Hopefully create a branch and not destroy the svn repository.
[reactos.git] / boot / freeldr / freeldr / arch / i386 / arch.S
1 /*
2 * FreeLoader
3 * Copyright (C) 1998-2002 Brian Palmer <brianp@sginet.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 .text
21 .code16
22
23 #define ASM
24 #include <arch.h>
25 #include <multiboot.h>
26
27
28 EXTERN(RealEntryPoint)
29
30 cli
31
32 /* Setup segment registers */
33 xorw %ax,%ax
34 movw %ax,%ds
35 movw %ax,%es
36 movw %ax,%fs
37 movw %ax,%gs
38 movw %ax,%ss
39 /* Setup a stack */
40 movw stack16,%sp
41
42 sti
43
44 /* Init pmode */
45 call switch_to_prot
46
47 .code32
48
49 /* Zero BootDrive and BootPartition */
50 xorl %eax,%eax
51 movl %eax,(_BootDrive)
52 movl %eax,(_BootPartition)
53
54 /* Store the boot drive */
55 movb %dl,(_BootDrive)
56
57 /* Store the boot partition */
58 movb %dh,(_BootPartition)
59
60 /* GO! */
61 pushl %eax
62 call _BootMain
63
64 call switch_to_real
65 .code16
66
67 int $0x19
68
69 /* We should never get here */
70 stop:
71 jmp stop
72 nop
73 nop
74
75 /*
76 * Switches the processor to protected mode
77 * it destroys eax
78 */
79 EXTERN(switch_to_prot)
80
81 .code16
82
83 cli /* None of these */
84
85 /* We don't know what values are currently */
86 /* in the segment registers. So we are */
87 /* going to reload them with sane values. */
88 /* Of course CS has to already be valid. */
89 /* We are currently in real-mode so we */
90 /* need real-mode segment values. */
91 xorw %ax,%ax
92 movw %ax,%ds
93 movw %ax,%es
94 movw %ax,%fs
95 movw %ax,%gs
96 movw %ax,%ss
97
98 /* Get the return address off the stack */
99 popw (code32ret)
100
101 /* Save 16-bit stack pointer */
102 movw %sp,stack16
103
104 /* Load the GDT */
105 lgdt gdtptr
106 /* Load the IDT */
107 lidt i386idtptr
108
109 /* Enable Protected Mode */
110 mov %cr0,%eax
111 orl $CR0_PE_SET,%eax
112 mov %eax,%cr0
113
114 /* Clear prefetch queue & correct CS */
115 ljmp $PMODE_CS, $inpmode
116
117
118 .code32
119
120 inpmode:
121 /* Setup segment selectors */
122 movw $PMODE_DS,%ax
123 movw %ax,%ds
124 movw %ax,%es
125 movw %ax,%fs
126 movw %ax,%gs
127 movw %ax,%ss
128 movl stack32,%esp
129
130 /* Put the return address back onto the stack */
131 pushl (code32ret)
132
133 /* Now return in p-mode! */
134 ret
135
136 /*
137 * Switches the processor back to real mode
138 * it destroys eax
139 */
140 EXTERN(switch_to_real)
141
142 .code32
143
144 /* We don't know what values are currently */
145 /* in the segment registers. So we are */
146 /* going to reload them with sane values. */
147 /* Of course CS has to already be valid. */
148 /* We are currently in protected-mode so we */
149 /* need protected-mode segment values. */
150 movw $PMODE_DS,%ax
151 movw %ax,%ds
152 movw %ax,%es
153 movw %ax,%fs
154 movw %ax,%gs
155 movw %ax,%ss
156
157 /* Get the return address off the stack */
158 popl (code16ret)
159
160 /* Save 32-bit stack pointer */
161 movl %esp,stack32
162
163 /* jmp to 16-bit segment to set the limit correctly */
164 ljmp $RMODE_CS, $switch_to_real16
165
166 switch_to_real16:
167 .code16
168
169 /* Restore segment registers to correct limit */
170 movw $RMODE_DS,%ax
171 movw %ax,%ds
172 movw %ax,%es
173 movw %ax,%fs
174 movw %ax,%gs
175 movw %ax,%ss
176
177 /* Disable Protected Mode */
178 mov %cr0,%eax
179 andl $CR0_PE_CLR,%eax
180 mov %eax,%cr0
181
182 /* Clear prefetch queue & correct CS */
183 ljmp $0, $inrmode
184
185 inrmode:
186 movw %cs,%ax
187 movw %ax,%ds
188 movw %ax,%es
189 movw %ax,%fs
190 movw %ax,%gs
191 movw %ax,%ss
192
193 /* Clear out the high 16-bits of ESP */
194 /* This is needed because I have one */
195 /* machine that hangs when booted to dos if */
196 /* anything other than 0x0000 is in the high */
197 /* 16-bits of ESP. Even though real-mode */
198 /* code should only use SP and not ESP. */
199 xorl %esp,%esp
200
201 movw stack16,%sp
202
203 /* Put the return address back onto the stack */
204 pushw (code16ret)
205
206 /* Load IDTR with real mode value */
207 lidt rmode_idtptr
208
209 sti /* These are ok now */
210
211 /* Now return in r-mode! */
212 ret
213
214
215 /*
216 * Needed for enabling the a20 address line
217 */
218 .code16
219 empty_8042:
220 .word 0x00eb,0x00eb // jmp $+2, jmp $+2
221 inb $0x64,%al
222 cmp $0xff, %al // legacy-free machine without keyboard
223 jz empty_8042_ret // controllers on Intel Macs read back 0xFF
224 testb $0x02,%al
225 jnz empty_8042
226 empty_8042_ret:
227 ret
228
229 /*
230 * Enable the A20 address line (to allow access to over 1mb)
231 */
232 EXTERN(_EnableA20)
233 .code32
234
235 pushal
236
237 call switch_to_real
238 .code16
239
240 call empty_8042
241 movb $0xD1,%al // command write
242 outb %al,$0x64
243 call empty_8042
244 mov $0xDF,%al // A20 on
245 out %al,$0x60
246 call empty_8042
247 call switch_to_prot
248 .code32
249
250 popal
251
252 ret
253
254 /*
255 * Disable the A20 address line
256 */
257 EXTERN(_DisableA20)
258 .code32
259
260 pushal
261
262 call switch_to_real
263 .code16
264
265 call empty_8042
266 movb $0xD1,%al // command write
267 outb %al,$0x64
268 call empty_8042
269 mov $0xDD,%al // A20 off
270 out %al,$0x60
271 call empty_8042
272 call switch_to_prot
273 .code32
274
275 popal
276
277 ret
278
279 /* Multiboot support
280 *
281 * Allows freeldr to be loaded as a "multiboot kernel" by
282 * other boot loaders like Grub
283 */
284
285 #define MB_INFO_SIZE 90
286 #define MB_INFO_FLAGS_OFFSET 0
287 #define MB_INFO_BOOT_DEVICE_OFFSET 12
288 #define MB_INFO_COMMAND_LINE_OFFSET 16
289 #define CMDLINE_SIZE 256
290
291 /*
292 * We want to execute at 0x8000 (to be compatible with bootsector
293 * loading), but Grub only allows loading of multiboot kernels
294 * above 1MB. So we let Grub load us there and then relocate
295 * ourself to 0x8000
296 */
297 #define FREELDR_BASE 0x8000
298 #define INITIAL_BASE 0x200000
299
300 /* Align 32 bits boundary */
301 .align 4
302
303 /* Multiboot header */
304 MultibootHeader:
305 /* magic */
306 .long MULTIBOOT_HEADER_MAGIC
307 /* flags */
308 .long MULTIBOOT_HEADER_FLAGS
309 /* checksum */
310 .long -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS)
311 /* header_addr */
312 .long INITIAL_BASE + MultibootHeader - FREELDR_BASE
313 /* load_addr */
314 .long INITIAL_BASE
315 /* load_end_addr */
316 .long INITIAL_BASE + __bss_start__ - FREELDR_BASE
317 /* bss_end_addr */
318 .long INITIAL_BASE + __bss_end__ - FREELDR_BASE
319 /* entry_addr */
320 .long INITIAL_BASE + MultibootEntry - FREELDR_BASE
321
322 MultibootEntry:
323 cli /* Even after setting up the our IDT below we are
324 * not ready to handle hardware interrupts (no entries
325 * in IDT), so there's no sti here. Interrupts will be
326 * enabled in due time */
327
328 /* Although the multiboot spec says we should be called with the
329 * segment registers set to 4GB flat mode, let's be sure and set up
330 * our own */
331 lgdt gdtptrhigh + INITIAL_BASE - FREELDR_BASE
332 /* Reload segment selectors */
333 ljmp $PMODE_CS, $(mb1 + INITIAL_BASE - FREELDR_BASE)
334 mb1:
335 movw $PMODE_DS,%dx
336 movw %dx,%ds
337 movw %dx,%es
338
339 /* Check for valid multiboot signature */
340 cmpl $MULTIBOOT_BOOTLOADER_MAGIC,%eax
341 jne mbfail
342
343 /* Store multiboot info in a safe place */
344 movl %ebx,%esi
345 movl $(mb_info + INITIAL_BASE - FREELDR_BASE),%edi
346 movl $MB_INFO_SIZE,%ecx
347 rep movsb
348
349 /* Save commandline */
350 movl MB_INFO_FLAGS_OFFSET(%ebx),%edx
351 testl $MB_INFO_FLAG_COMMAND_LINE,MB_INFO_FLAGS_OFFSET(%ebx)
352 jz mb3
353 movl MB_INFO_COMMAND_LINE_OFFSET(%ebx),%esi
354 movl $(cmdline + INITIAL_BASE - FREELDR_BASE),%edi
355 movl $CMDLINE_SIZE,%ecx
356 mb2: lodsb
357 stosb
358 testb %al,%al
359 jz mb3
360 dec %ecx
361 jnz mb2
362 mb3:
363
364 /* Copy to low mem */
365 movl $INITIAL_BASE,%esi
366 movl $FREELDR_BASE,%edi
367 movl $(__bss_end__ - FREELDR_BASE),%ecx
368 addl $3,%ecx
369 shrl $2,%ecx
370 rep movsl
371
372 /* Load the GDT and IDT */
373 lgdt gdtptr
374 lidt i386idtptr
375
376 /* Clear prefetch queue & correct CS,
377 * jump to low mem */
378 ljmp $PMODE_CS, $mb4
379 mb4:
380 /* Reload segment selectors */
381 movw $PMODE_DS,%dx
382 movw %dx,%ds
383 movw %dx,%es
384 movw %dx,%fs
385 movw %dx,%gs
386 movw %dx,%ss
387 movl $STACK32ADDR,%esp
388
389 movl $mb_info,%ebx
390 /* See if the boot device was passed in */
391 movl MB_INFO_FLAGS_OFFSET(%ebx),%edx
392 testl $MB_INFO_FLAG_BOOT_DEVICE,%edx
393 jz mb5
394 /* Retrieve boot device info */
395 movl MB_INFO_BOOT_DEVICE_OFFSET(%ebx),%eax
396 shrl $16,%eax
397 incb %al
398 movb %al,_BootPartition
399 movb %ah,_BootDrive
400 jmp mb6
401 mb5: /* No boot device known, assume first partition of first harddisk */
402 movb $0x80,_BootDrive
403 movb $1,_BootPartition
404 mb6:
405 /* Check for command line */
406 mov $cmdline,%eax
407 testl $MB_INFO_FLAG_COMMAND_LINE,MB_INFO_FLAGS_OFFSET(%ebx)
408 jnz mb7
409 xorl %eax,%eax
410 mb7:
411
412 /* GO! */
413 pushl %eax
414 call _BootMain
415
416 mbfail: call switch_to_real
417 .code16
418 int $0x19
419 mbstop: jmp mbstop /* We should never get here */
420
421 .code32
422
423 /* 16-bit stack pointer */
424 stack16:
425 .word STACK16ADDR
426
427 /* 32-bit stack pointer */
428 stack32:
429 .long STACK32ADDR
430
431 /* 16-bit return address */
432 code16ret:
433 .long 0
434
435 /* 32-bit return address */
436 code32ret:
437 .long 0
438
439
440 .p2align 2 /* force 4-byte alignment */
441 gdt:
442 /* NULL Descriptor */
443 .word 0x0000
444 .word 0x0000
445 .word 0x0000
446 .word 0x0000
447
448 /* 32-bit flat CS */
449 .word 0xFFFF
450 .word 0x0000
451 .word 0x9A00
452 .word 0x00CF
453
454 /* 32-bit flat DS */
455 .word 0xFFFF
456 .word 0x0000
457 .word 0x9200
458 .word 0x00CF
459
460 /* 16-bit real mode CS */
461 .word 0xFFFF
462 .word 0x0000
463 .word 0x9E00
464 .word 0x0000
465
466 /* 16-bit real mode DS */
467 .word 0xFFFF
468 .word 0x0000
469 .word 0x9200
470 .word 0x0000
471
472 /* GDT table pointer */
473 gdtptr:
474 .word 0x27 /* Limit */
475 .long gdt /* Base Address */
476
477 /* Initial GDT table pointer for multiboot */
478 gdtptrhigh:
479 .word 0x27 /* Limit */
480 .long gdt + INITIAL_BASE - FREELDR_BASE /* Base Address */
481
482 /* Real-mode IDT pointer */
483 rmode_idtptr:
484 .word 0x3ff /* Limit */
485 .long 0 /* Base Address */
486
487 mb_info:
488 .fill MB_INFO_SIZE, 1, 0
489
490 cmdline:
491 .fill CMDLINE_SIZE, 1, 0
492
493 EXTERN(_BootDrive)
494 .long 0
495
496 EXTERN(_BootPartition)
497 .long 0