1/*
2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
5 * 1.0 of the CDDL.
6 *
7 * A full copy of the text of the CDDL should have accompanied this
8 * source.  A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
10 */
11/*
12 * Copyright 2016 Toomas Soome <tsoome@me.com>
13 */
14
15/*
16 * Relocate is needed to support loading code which has to be located
17 * below 1MB, as both BTX and loader are using low memory area.
18 *
19 * Relocate and start loaded code. Since loaded code may need to be
20 * placed in an already occupied memory area, the code is moved to a safe
21 * memory area and then btx __exec will be called with physical pointer
22 * to this area. __exec will set the pointer to %eax and call *%eax,
23 * so that on entry, we have the new "base" address in %eax.
24 *
25 * Relocate will first set up and load new safe GDT to shut down BTX,
26 * then loaded code will be relocated to final memory location,
27 * then machine will be switched from 32-bit protected mode to 16-bit
28 * protected mode following by switch to real mode with A20 enabled or
29 * disabled. Finally the loaded code will be started and it will take
30 * over the whole system.
31 *
32 * For now, the known "safe" memory area for relocate is 0x600,
33 * the actual "free" memory is supposed to start from 0x500, leaving
34 * first 0x100 bytes in reserve. As relocate code+data is very small,
35 * it will leave enough space to set up boot blocks to 0:7c00 or load
36 * linux kernel below 1MB space.
37 */
38/*
39 * segment selectors
40 */
41		.set SEL_SCODE,0x8
42		.set SEL_SDATA,0x10
43		.set SEL_RCODE,0x18
44		.set SEL_RDATA,0x20
45
46		.p2align	4
47		.globl relocater
48relocater:
49		cli
50		/*
51		 * set up GDT from new location
52		 */
53		movl	%eax, %esi		/* our base address */
54		add	$(relocater.1-relocater), %eax
55		jmp	*%eax
56relocater.1:
57		/* set up jump */
58		lea	(relocater.2-relocater)(%esi), %eax
59		movl	%eax, (jump_vector-relocater) (%esi)
60
61		/* set up gdt */
62		lea	(gdt-relocater) (%esi), %eax
63		movl	%eax, (gdtaddr-relocater) (%esi)
64
65		/* load gdt */
66		lgdt	(gdtdesc - relocater) (%esi)
67		lidt	(idt-relocater) (%esi)
68
69		/* update cs */
70		ljmp *(jump_vector-relocater) (%esi)
71
72		.code32
73relocater.2:
74		xorl	%eax, %eax
75		movb	$SEL_SDATA, %al
76		movw	%ax, %ss
77		movw	%ax, %ds
78		movw	%ax, %es
79		movw	%ax, %fs
80		movw	%ax, %gs
81		movl	%cr0, %eax		/* disable paging */
82		andl	$~0x80000000,%eax
83		movl	%eax, %cr0
84		xorl	%ecx, %ecx		/* flush TLB */
85		movl	%ecx, %cr3
86		cld
87/*
88 * relocate data loop. load source, dest and size from
89 * relocater_data[i], 0 value will stop the loop.
90 * registers used for move: %esi, %edi, %ecx.
91 * %ebx to keep base
92 * %edx for relocater_data offset
93 */
94		movl	%esi, %ebx		/* base address */
95		xorl	%edx, %edx
96loop.1:
97		movl	(relocater_data-relocater)(%ebx, %edx, 4), %eax
98		testl	%eax, %eax
99		jz	loop.2
100		movl	(relocater_data-relocater)(%ebx, %edx, 4), %esi
101		inc	%edx
102		movl	(relocater_data-relocater)(%ebx, %edx, 4), %edi
103		inc	%edx
104		movl	(relocater_data-relocater)(%ebx, %edx, 4), %ecx
105		inc	%edx
106		rep
107		movsb
108		jmp	loop.1
109loop.2:
110		movl	%ebx, %esi		/* restore esi */
111		/*
112		 * data is relocated, switch to 16-bit mode
113		 */
114		lea	(relocater.3-relocater)(%esi), %eax
115		movl	%eax, (jump_vector-relocater) (%esi)
116		movl	$SEL_RCODE, %eax
117		movl	%eax, (jump_vector-relocater+4) (%esi)
118
119		ljmp *(jump_vector-relocater) (%esi)
120relocater.3:
121		.code16
122
123		movw	$SEL_RDATA, %ax
124		movw	%ax, %ds
125		movw	%ax, %es
126		movw	%ax, %fs
127		movw	%ax, %gs
128		movw	%ax, %ss
129		lidt	(idt-relocater) (%esi)
130		lea	(relocater.4-relocater)(%esi), %eax
131		movl	%eax, (jump_vector-relocater) (%esi)
132		xorl	%eax, %eax
133		movl	%eax, (jump_vector-relocater+4) (%esi)
134		/* clear PE */
135		movl	%cr0, %eax
136		dec	%al
137		movl	%eax, %cr0
138		ljmp *(jump_vector-relocater) (%esi)
139relocater.4:
140		xorw	%ax, %ax
141		movw	%ax, %ds
142		movw	%ax, %es
143		movw	%ax, %fs
144		movw	%ax, %gs
145		movw	%ax, %ss
146		/*
147		 * set real mode irq offsets
148		 */
149		movw	$0x7008,%bx
150		in $0x21,%al			# Save master
151		push %ax			#  IMR
152		in $0xa1,%al			# Save slave
153		push %ax			#  IMR
154		movb $0x11,%al			# ICW1 to
155		outb %al,$0x20			#  master,
156		outb %al,$0xa0			#  slave
157		movb %bl,%al			# ICW2 to
158		outb %al,$0x21			#  master
159		movb %bh,%al			# ICW2 to
160		outb %al,$0xa1			#  slave
161		movb $0x4,%al			# ICW3 to
162		outb %al,$0x21			#  master
163		movb $0x2,%al			# ICW3 to
164		outb %al,$0xa1			#  slave
165		movb $0x1,%al			# ICW4 to
166		outb %al,$0x21			#  master,
167		outb %al,$0xa1			#  slave
168		pop %ax				# Restore slave
169		outb %al,$0xa1			#  IMR
170		pop %ax				# Restore master
171		outb %al,$0x21			#  IMR
172						# done
173		/*
174		 * Should A20 be left enabled?
175		 */
176		/* movw imm16, %ax */
177		.byte	0xb8
178		.globl	relocator_a20_enabled
179relocator_a20_enabled:
180		.word	0
181		test	%ax, %ax
182		jnz	a20_done
183
184		movw	$0xa00, %ax
185		movw	%ax, %sp
186		movw	%ax, %bp
187
188		/* Disable A20 */
189		movw	$0x2400, %ax
190		int	$0x15
191#		jnc	a20_done
192
193		call	a20_check_state
194		testb	%al, %al
195		jz	a20_done
196
197		inb	$0x92
198		andb	$(~0x03), %al
199		outb	$0x92
200		jmp	a20_done
201
202a20_check_state:
203		movw	$100, %cx
2041:
205		xorw	%ax, %ax
206		movw	%ax, %ds
207		decw	%ax
208		movw	%ax, %es
209		xorw	%ax, %ax
210		movw	$0x8000, %ax
211		movw	%ax, %si
212		addw	$0x10, %ax
213		movw	%ax, %di
214		movb	%ds:(%si), %dl
215		movb	%es:(%di), %al
216		movb	%al, %dh
217		decb	%dh
218		movb	%dh, %ds:(%si)
219		outb	%al, $0x80
220		outb	%al, $0x80
221		movb	%es:(%di), %dh
222		subb	%dh, %al
223		xorb	$1, %al
224		movb	%dl, %ds:(%si)
225		testb	%al, %al
226		jz	a20_done
227		loop	1b
228		ret
229a20_done:
230		/*
231		 * set up registers
232		 */
233		/* movw imm16, %ax. */
234		.byte	0xb8
235		.globl	relocator_ds
236relocator_ds:	.word	0
237		movw	%ax, %ds
238
239		/* movw imm16, %ax. */
240		.byte	0xb8
241		.globl	relocator_es
242relocator_es:	.word	0
243		movw	%ax, %es
244
245		/* movw imm16, %ax. */
246		.byte	0xb8
247		.globl	relocator_fs
248relocator_fs:	.word	0
249		movw	%ax, %fs
250
251		/* movw imm16, %ax. */
252		.byte	0xb8
253		.globl	relocator_gs
254relocator_gs:	.word	0
255		movw	%ax, %gs
256
257		/* movw imm16, %ax. */
258		.byte	0xb8
259		.globl	relocator_ss
260relocator_ss:	.word	0
261		movw	%ax, %ss
262
263		/* movw imm16, %ax. */
264		.byte	0xb8
265		.globl	relocator_sp
266relocator_sp:	.word	0
267		movzwl	%ax, %esp
268
269		/* movw imm32, %eax. */
270		.byte	0x66, 0xb8
271		.globl	relocator_esi
272relocator_esi:	.long	0
273		movl	%eax, %esi
274
275		/* movw imm32, %edx. */
276		.byte	0x66, 0xba
277		.globl	relocator_edx
278relocator_edx:	.long	0
279
280		/* movw imm32, %ebx. */
281		.byte	0x66, 0xbb
282		.globl	relocator_ebx
283relocator_ebx:	.long	0
284
285		/* movw imm32, %eax. */
286		.byte	0x66, 0xb8
287		.globl	relocator_eax
288relocator_eax:	.long	0
289
290		/* movw imm32, %ebp. */
291		.byte	0x66, 0xbd
292		.globl	relocator_ebp
293relocator_ebp:	.long	0
294
295		sti
296		.byte 0xea			 /* ljmp */
297		.globl relocator_ip
298relocator_ip:
299		.word 0
300		.globl relocator_cs
301relocator_cs:
302		.word 0
303
304/* GDT to reset BTX */
305		.code32
306		.p2align	4
307jump_vector:	.long	0
308		.long	SEL_SCODE
309
310gdt:		.word 0x0, 0x0			/* null entry */
311		.byte 0x0, 0x0, 0x0, 0x0
312		.word 0xffff, 0x0		/* SEL_SCODE */
313		.byte 0x0, 0x9a, 0xcf, 0x0
314		.word 0xffff, 0x0		/* SEL_SDATA */
315		.byte 0x0, 0x92, 0xcf, 0x0
316		.word 0xffff, 0x0		/* SEL_RCODE */
317		.byte 0x0, 0x9a, 0x0f, 0x0
318		.word 0xffff, 0x0		/* SEL_RDATA */
319		.byte 0x0, 0x92, 0x0f, 0x0
320gdt.1:
321
322gdtdesc:	.word gdt.1 - gdt - 1		/* limit */
323gdtaddr:	.long 0				/* base */
324
325idt:		.word 0x3ff
326		.long 0
327
328		.globl relocater_data
329
330/* reserve space for 3 entries */
331relocater_data:
332		.long 0			/* src */
333		.long 0			/* dest */
334		.long 0			/* size */
335		.long 0			/* src */
336		.long 0			/* dest */
337		.long 0			/* size */
338		.long 0			/* src */
339		.long 0			/* dest */
340		.long 0			/* size */
341		.long 0
342
343		.globl relocater_size
344relocater_size:
345		.long relocater_size-relocater
346