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