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