1 ;; -*- fundamental -*- 2 ;; ----------------------------------------------------------------------- 3 ;; 4 ;; Copyright 1994-2008 H. Peter Anvin - All Rights Reserved 5 ;; Copyright 2009 Intel Corporation; author: H. Peter Anvin 6 ;; 7 ;; This program is free software; you can redistribute it and/or modify 8 ;; it under the terms of the GNU General Public License as published by 9 ;; the Free Software Foundation, Inc., 53 Temple Place Ste 330, 10 ;; Boston MA 02111-1307, USA; either version 2 of the License, or 11 ;; (at your option) any later version; incorporated herein by reference. 12 ;; 13 ;; ----------------------------------------------------------------------- 14 15 ;; 16 ;; init16.asm 17 ;; 18 ;; Routine to initialize and to trampoline into 32-bit 19 ;; protected memory. This code is derived from bcopy32.inc and 20 ;; com32.inc in the main SYSLINUX distribution. 21 ;; 22 23 %include '../version.gen' 1 <1> %define VERSION 6.04 2 <1> %define VERSION_STR "6.04" 3 <1> %define VERSION_MAJOR 6 4 <1> %define VERSION_MINOR 4 5 <1> %define YEAR 2015 6 <1> %define YEAR_STR "2015" 24 25 MY_CS equ 0x0800 ; Segment address to use 26 CS_BASE equ (MY_CS << 4) ; Corresponding address 27 28 ; Low memory bounce buffer 29 BOUNCE_SEG equ (MY_CS+0x1000) 30 31 %define DO_WBINVD 0 32 33 section .rodata align=16 34 section .data align=16 35 section .bss align=16 36 section .stack align=16 nobits 37 00000000 stack resb 512 38 stack_end equ $ 39 40 ;; ----------------------------------------------------------------------- 41 ;; Kernel image header 42 ;; ----------------------------------------------------------------------- 43 44 section .text ; Must be first in image 45 bits 16 46 47 00000000 00 cmdline times 497 db 0 ; We put the command line here 48 000001F1 00 setup_sects db 0 49 000001F2 0000 root_flags dw 0 50 000001F4 0000 syssize dw 0 51 000001F6 0000 swap_dev dw 0 52 000001F8 0000 ram_size dw 0 53 000001FA 0000 vid_mode dw 0 54 000001FC 0000 root_dev dw 0 55 000001FE 55AA boot_flag dw 0xAA55 56 57 00000200 EB3E _start: jmp short start 58 59 00000202 48647253 db "HdrS" ; Header signature 60 00000206 0302 dw 0x0203 ; Header version number 61 62 00000208 00000000 realmode_swtch dw 0, 0 ; default_switch, SETUPSEG 63 0000020C 0010 start_sys_seg dw 0x1000 ; obsolete 64 0000020E [00FE] version_ptr dw memdisk_version-0x200 ; version string ptr 65 00000210 00 type_of_loader db 0 ; Filled in by boot loader 66 00000211 01 loadflags db 1 ; Please load high 67 00000212 0000 setup_move_size dw 0 ; Unused 68 00000214 00001000 code32_start dd 0x100000 ; 32-bit start address 69 00000218 00000000 ramdisk_image dd 0 ; Loaded ramdisk image address 70 0000021C 00000000 ramdisk_size dd 0 ; Size of loaded ramdisk 71 00000220 00000000 bootsect_kludge dw 0, 0 72 00000224 0000 heap_end_ptr dw 0 73 00000226 0000 pad1 dw 0 74 00000228 00000000 cmd_line_ptr dd 0 ; Command line 75 0000022C FFFFFFFF ramdisk_max dd 0xffffffff ; Highest allowed ramdisk address 76 77 ; 78 ; These fields aren't real setup fields, they're poked in by the 79 ; 32-bit code. 80 ; 81 00000230 00000000 b_esdi dd 0 ; ES:DI for boot sector invocation 82 00000234 00000000 b_edx dd 0 ; EDX for boot sector invocation 83 00000238 00000000 b_sssp dd 0 ; SS:SP on boot sector invocation 84 0000023C 00000000 b_csip dd 0 ; CS:IP on boot sector invocation 85 86 section .rodata 87 memdisk_version: 88 00000000 4D454D4449534B2036- db "MEMDISK ", VERSION_STR, " ", DATE, 0 88 00000009 2E303420707265322A- 88 00000012 00 89 90 ;; ----------------------------------------------------------------------- 91 ;; End kernel image header 92 ;; ----------------------------------------------------------------------- 93 94 ; 95 ; Move ourselves down into memory to reduce the risk of conflicts; 96 ; then canonicalize CS to match the other segments. 97 ; 98 section .text 99 bits 16 100 start: 101 00000240 B80008 mov ax,MY_CS 102 00000243 8EC0 mov es,ax 103 00000245 0FB60E[F101] movzx cx,byte [setup_sects] 104 0000024A 41 inc cx ; Add one for the boot sector 105 0000024B C1E107 shl cx,7 ; Convert to dwords 106 0000024E 31F6 xor si,si 107 00000250 31FF xor di,di 108 00000252 8EE6 mov fs,si ; fs <- 0 109 00000254 FC cld 110 00000255 F366A5 rep movsd 111 00000258 8ED8 mov ds,ax 112 0000025A 8ED0 mov ss,ax 113 0000025C 66BC[00020000] mov esp,stack_end 114 00000262 EA[6702]0008 jmp MY_CS:.next 115 .next: 116 117 ; 118 ; Copy the command line, if there is one 119 ; 120 copy_cmdline: 121 00000267 31FF xor di,di ; Bottom of our own segment (= "boot sector") 122 00000269 66A1[2802] mov eax,[cmd_line_ptr] 123 0000026D 6621C0 and eax,eax 124 00000270 7417 jz .endcmd ; No command line 125 00000272 89C6 mov si,ax 126 00000274 66C1E804 shr eax,4 ; Convert to segment 127 00000278 83E60F and si,0x000F ; Starting offset only 128 0000027B 8EE8 mov gs,ax 129 0000027D B9F001 mov cx,496 ; Max number of bytes 130 .copycmd: 131 00000280 65AC gs lodsb 132 00000282 20C0 and al,al 133 00000284 7403 jz .endcmd 134 00000286 AA stosb 135 00000287 E2F7 loop .copycmd 136 .endcmd: 137 00000289 30C0 xor al,al 138 0000028B AA stosb 139 140 ; 141 ; Now jump to 32-bit code 142 ; 143 0000028C FB sti 144 0000028D E87501 call init32 145 ; 146 ; When init32 returns, we have been set up, the new boot sector loaded, 147 ; and we should go and and run the newly loaded boot sector. 148 ; 149 ; The setup function will have poked values into the setup area. 150 ; 151 00000290 2E660FB73E[3002] movzx edi,word [cs:b_esdi] 152 00000297 2E8E06[3202] mov es,word [cs:b_esdi+2] 153 0000029C 2E668B16[3402] mov edx,[cs:b_edx] 154 155 000002A2 FA cli 156 000002A3 6631F6 xor esi,esi ; No partition table involved 157 000002A6 8EDE mov ds,si ; Make all the segments consistent 158 000002A8 8EE6 mov fs,si 159 000002AA 8EEE mov gs,si 160 000002AC 2E0FB226[3802] lss sp,[cs:b_sssp] 161 000002B2 660FB7E4 movzx esp,sp 162 000002B6 2EFF2E[3C02] jmp far [cs:b_csip] 163 164 ; 165 ; We enter protected mode, set up a flat 32-bit environment, run rep movsd 166 ; and then exit. IMPORTANT: This code assumes cs == MY_CS. 167 ; 168 ; This code is probably excessively anal-retentive in its handling of 169 ; segments, but this stuff is painful enough as it is without having to rely 170 ; on everything happening "as it ought to." 171 ; 172 DummyTSS equ 0x580 ; Hopefully safe place in low mmoery 173 174 section .data 175 176 ; desc base, limit, flags 177 %macro desc 3 178 dd (%2 & 0xffff) | ((%1 & 0xffff) << 16) 179 dd (%1 & 0xff000000) | (%2 & 0xf0000) | ((%3 & 0xf0ff) << 8) | ((%1 & 0x00ff0000) >> 16) 180 %endmacro 181 182 align 8, db 0 183 00000000 2F00 call32_gdt: dw call32_gdt_size-1 ; Null descriptor - contains GDT 184 00000002 [00800000] .adj1: dd call32_gdt+CS_BASE ; pointer for LGDT instruction 185 00000006 0000 dw 0 186 187 ; 0008: Dummy TSS to make Intel VT happy 188 ; Should never be actually accessed... 189 desc DummyTSS, 103, 0x8089 189 00000008 67008005 <1> dd (%2 & 0xffff) | ((%1 & 0xffff) << 16) 189 0000000C 00898000 <1> dd (%1 & 0xff000000) | (%2 & 0xf0000) | ((%3 & 0xf0ff) << 8) | ((%1 & 0x00ff0000) >> 16) 190 191 ; 0010: Code segment, use16, readable, dpl 0, base CS_BASE, 64K 192 desc CS_BASE, 0xffff, 0x009b 192 00000010 FFFF0080 <1> dd (%2 & 0xffff) | ((%1 & 0xffff) << 16) 192 00000014 009B0000 <1> dd (%1 & 0xff000000) | (%2 & 0xf0000) | ((%3 & 0xf0ff) << 8) | ((%1 & 0x00ff0000) >> 16) 193 194 ; 0018: Data segment, use16, read/write, dpl 0, base CS_BASE, 64K 195 desc CS_BASE, 0xffff, 0x0093 195 00000018 FFFF0080 <1> dd (%2 & 0xffff) | ((%1 & 0xffff) << 16) 195 0000001C 00930000 <1> dd (%1 & 0xff000000) | (%2 & 0xf0000) | ((%3 & 0xf0ff) << 8) | ((%1 & 0x00ff0000) >> 16) 196 197 ; 0020: Code segment, use32, read/write, dpl 0, base 0, 4G 198 desc 0, 0xfffff, 0xc09b 198 00000020 FFFF0000 <1> dd (%2 & 0xffff) | ((%1 & 0xffff) << 16) 198 00000024 009BCF00 <1> dd (%1 & 0xff000000) | (%2 & 0xf0000) | ((%3 & 0xf0ff) << 8) | ((%1 & 0x00ff0000) >> 16) 199 200 ; 0028: Data segment, use32, read/write, dpl 0, base 0, 4G 201 desc 0, 0xfffff, 0xc093 201 00000028 FFFF0000 <1> dd (%2 & 0xffff) | ((%1 & 0xffff) << 16) 201 0000002C 0093CF00 <1> dd (%1 & 0xff000000) | (%2 & 0xf0000) | ((%3 & 0xf0ff) << 8) | ((%1 & 0x00ff0000) >> 16) 202 203 call32_gdt_size: equ $-call32_gdt 204 205 00000030 4552524F523A204132- err_a20: db 'ERROR: A20 gate not responding!',13,10,0 205 00000039 302067617465206E6F- 205 00000042 7420726573706F6E64- 205 0000004B 696E67210D0A00 206 207 section .bss 208 alignb 4 209 00000000 Return resd 1 ; Return value 210 00000004 SavedSP resw 1 ; Place to save SP 211 00000006 A20Tries resb 1 212 213 section .data 214 00000052 00 align 4, db 0 215 00000054 00000000 Target dd 0 ; Target address 216 00000058 2000 Target_Seg dw 20h ; Target CS 217 218 0000005A 0000 A20Type dw 0 ; Default = unknown 219 220 section .text 221 bits 16 222 ; 223 ; Routines to enable and disable (yuck) A20. These routines are gathered 224 ; from tips from a couple of sources, including the Linux kernel and 225 ; http://www.x86.org/. The need for the delay to be as large as given here 226 ; is indicated by Donnie Barnes of RedHat, the problematic system being an 227 ; IBM ThinkPad 760EL. 228 ; 229 ; We typically toggle A20 twice for every 64K transferred. 230 ; 231 %define io_delay call _io_delay 232 %define IO_DELAY_PORT 80h ; Invalid port (we hope!) 233 %define disable_wait 32 ; How long to wait for a disable 234 235 %define A20_DUNNO 0 ; A20 type unknown 236 %define A20_NONE 1 ; A20 always on? 237 %define A20_BIOS 2 ; A20 BIOS enable 238 %define A20_KBC 3 ; A20 through KBC 239 %define A20_FAST 4 ; A20 through port 92h 240 241 000002BB 00 align 2, db 0 242 000002BC [E702][E702][F102]- A20List dw a20_dunno, a20_none, a20_bios, a20_kbc, a20_fast 242 000002C2 [0203][2E03] 243 000002C6 [DF03][DF03][AA03]- A20DList dw a20d_dunno, a20d_none, a20d_bios, a20d_kbc, a20d_fast 243 000002CC [BB03][B303] 244 a20_adjust_cnt equ ($-A20List)/2 245 246 000002D0 EE slow_out: out dx, al ; Fall through 247 248 000002D1 E680 _io_delay: out IO_DELAY_PORT,al 249 000002D3 E680 out IO_DELAY_PORT,al 250 000002D5 C3 ret 251 252 enable_a20: 253 000002D6 6660 pushad 254 000002D8 C606[0600]FF mov byte [A20Tries],255 ; Times to try to make this work 255 256 try_enable_a20: 257 258 ; 259 ; Flush the caches 260 ; 261 %if DO_WBINVD 262 call try_wbinvd 263 %endif 264 265 ; 266 ; If the A20 type is known, jump straight to type 267 ; 268 000002DD 8B2E[5A00] mov bp,[A20Type] 269 000002E1 01ED add bp,bp ; Convert to word offset 270 000002E3 FFA6[BC02] .adj4: jmp word [bp+A20List] 271 272 ; 273 ; First, see if we are on a system with no A20 gate 274 ; 275 a20_dunno: 276 a20_none: 277 000002E7 C606[5A00]01 mov byte [A20Type], A20_NONE 278 000002EC E87600 call a20_test 279 000002EF 7571 jnz a20_done 280 281 ; 282 ; Next, try the BIOS (INT 15h AX=2401h) 283 ; 284 a20_bios: 285 000002F1 C606[5A00]02 mov byte [A20Type], A20_BIOS 286 000002F6 B80124 mov ax,2401h 287 000002F9 9C pushf ; Some BIOSes muck with IF 288 000002FA CD15 int 15h 289 000002FC 9D popf 290 291 000002FD E86500 call a20_test 292 00000300 7560 jnz a20_done 293 294 ; 295 ; Enable the keyboard controller A20 gate 296 ; 297 a20_kbc: 298 00000302 B201 mov dl, 1 ; Allow early exit 299 00000304 E8DD00 call empty_8042 300 00000307 7559 jnz a20_done ; A20 live, no need to use KBC 301 302 00000309 C606[5A00]03 mov byte [A20Type], A20_KBC ; Starting KBC command sequence 303 304 0000030E B0D1 mov al,0D1h ; Write output port 305 00000310 E664 out 064h, al 306 00000312 E8CD00 call empty_8042_uncond 307 308 00000315 B0DF mov al,0DFh ; A20 on 309 00000317 E660 out 060h, al 310 00000319 E8C600 call empty_8042_uncond 311 312 ; Apparently the UHCI spec assumes that A20 toggle 313 ; ends with a null command (assumed to be for sychronization?) 314 ; Put it here to see if it helps anything... 315 0000031C B0FF mov al,0FFh ; Null command 316 0000031E E664 out 064h, al 317 00000320 E8BF00 call empty_8042_uncond 318 319 ; Verify that A20 actually is enabled. Do that by 320 ; observing a word in low memory and the same word in 321 ; the HMA until they are no longer coherent. Note that 322 ; we don't do the same check in the disable case, because 323 ; we don't want to *require* A20 masking (SYSLINUX should 324 ; work fine without it, if the BIOS does.) 325 00000323 51 .kbc_wait: push cx 326 00000324 31C9 xor cx,cx 327 .kbc_wait_loop: 328 00000326 E83C00 call a20_test 329 00000329 7536 jnz a20_done_pop 330 0000032B E2F9 loop .kbc_wait_loop 331 332 0000032D 59 pop cx 333 ; 334 ; Running out of options here. Final attempt: enable the "fast A20 gate" 335 ; 336 a20_fast: 337 0000032E C606[5A00]04 mov byte [A20Type], A20_FAST ; Haven't used the KBC yet 338 00000333 E492 in al, 092h 339 00000335 0C02 or al,02h 340 00000337 24FE and al,~01h ; Don't accidentally reset the machine! 341 00000339 E692 out 092h, al 342 343 0000033B 51 .fast_wait: push cx 344 0000033C 31C9 xor cx,cx 345 .fast_wait_loop: 346 0000033E E82400 call a20_test 347 00000341 751E jnz a20_done_pop 348 00000343 E2F9 loop .fast_wait_loop 349 350 00000345 59 pop cx 351 352 ; 353 ; Oh bugger. A20 is not responding. Try frobbing it again; eventually give up 354 ; and report failure to the user. 355 ; 356 357 00000346 FE0E[0600] dec byte [A20Tries] 358 0000034A 7591 jnz try_enable_a20 359 360 361 ; Error message time 362 0000034C BE[3000] mov si,err_a20 363 print_err: 364 0000034F AC lodsb 365 00000350 20C0 and al,al 366 00000352 7409 jz die 367 00000354 BB0700 mov bx,7 368 00000357 B40E mov ah,0xe 369 00000359 CD10 int 10h 370 0000035B EBF2 jmp print_err 371 372 373 die: 374 0000035D FB sti 375 0000035E F4 .hlt: hlt 376 0000035F EBFD jmp short .hlt 377 378 ; 379 ; A20 unmasked, proceed... 380 ; 381 00000361 59 a20_done_pop: pop cx 382 00000362 6661 a20_done: popad 383 00000364 C3 ret 384 385 ; 386 ; This routine tests if A20 is enabled (ZF = 0). This routine 387 ; must not destroy any register contents. 388 ; 389 390 ; This is the INT 1Fh vector, which is standard PCs is used by the 391 ; BIOS when the screen is in graphics mode. Even if it is, it points to 392 ; data, not code, so it should be safe enough to fiddle with. 393 A20Test equ (1Fh*4) 394 395 a20_test: 396 00000365 1E push ds 397 00000366 06 push es 398 00000367 51 push cx 399 00000368 6650 push eax 400 0000036A 31C0 xor ax,ax 401 0000036C 8ED8 mov ds,ax ; DS == 0 402 0000036E 48 dec ax 403 0000036F 8EC0 mov es,ax ; ES == 0FFFFh 404 00000371 B92000 mov cx,32 ; Loop count 405 00000374 66A17C00 mov eax,[A20Test] 406 00000378 26663B068C00 cmp eax,[es:A20Test+10h] 407 0000037E 7518 jne .a20_done 408 00000380 6650 push eax 409 .a20_wait: 410 00000382 6640 inc eax 411 00000384 66A37C00 mov [A20Test],eax 412 00000388 E846FF io_delay 413 0000038B 26663B068C00 cmp eax,[es:A20Test+10h] 414 00000391 E1EF loopz .a20_wait 415 00000393 668F067C00 pop dword [A20Test] ; Restore original value 416 .a20_done: 417 00000398 6658 pop eax 418 0000039A 59 pop cx 419 0000039B 07 pop es 420 0000039C 1F pop ds 421 0000039D C3 ret 422 423 disable_a20: 424 0000039E 6660 pushad 425 ; 426 ; Flush the caches 427 ; 428 %if DO_WBINVD 429 call try_wbinvd 430 %endif 431 432 000003A0 8B2E[5A00] mov bp,[A20Type] 433 000003A4 01ED add bp,bp ; Convert to word offset 434 000003A6 FFA6[C602] .adj5: jmp word [bp+A20DList] 435 436 a20d_bios: 437 000003AA B80024 mov ax,2400h 438 000003AD 9C pushf ; Some BIOSes muck with IF 439 000003AE CD15 int 15h 440 000003B0 9D popf 441 000003B1 EB20 jmp short a20d_snooze 442 443 ; 444 ; Disable the "fast A20 gate" 445 ; 446 a20d_fast: 447 000003B3 E492 in al, 092h 448 000003B5 24FC and al,~03h 449 000003B7 E692 out 092h, al 450 000003B9 EB18 jmp short a20d_snooze 451 452 ; 453 ; Disable the keyboard controller A20 gate 454 ; 455 a20d_kbc: 456 000003BB E82400 call empty_8042_uncond 457 458 000003BE B0D1 mov al,0D1h 459 000003C0 E664 out 064h, al ; Write output port 460 000003C2 E81D00 call empty_8042_uncond 461 462 000003C5 B0DD mov al,0DDh ; A20 off 463 000003C7 E660 out 060h, al 464 000003C9 E81600 call empty_8042_uncond 465 466 000003CC B0FF mov al,0FFh ; Null command/synchronization 467 000003CE E664 out 064h, al 468 000003D0 E80F00 call empty_8042_uncond 469 470 ; Wait a bit for it to take effect 471 a20d_snooze: 472 000003D3 51 push cx 473 000003D4 B92000 mov cx, disable_wait 474 000003D7 E88BFF .delayloop: call a20_test 475 000003DA 7402 jz .disabled 476 000003DC E2F9 loop .delayloop 477 000003DE 59 .disabled: pop cx 478 a20d_dunno: 479 a20d_none: 480 000003DF 6661 popad 481 000003E1 C3 ret 482 483 ; 484 ; Routine to empty the 8042 KBC controller. If dl != 0 485 ; then we will test A20 in the loop and exit if A20 is 486 ; suddenly enabled. 487 ; 488 empty_8042_uncond: 489 000003E2 30D2 xor dl,dl 490 empty_8042: 491 000003E4 E87EFF call a20_test 492 000003E7 7404 jz .a20_on 493 000003E9 20D2 and dl,dl 494 000003EB 7517 jnz .done 495 000003ED E8E1FE .a20_on: io_delay 496 000003F0 E464 in al, 064h ; Status port 497 000003F2 A801 test al,1 498 000003F4 7407 jz .no_output 499 000003F6 E8D8FE io_delay 500 000003F9 E460 in al, 060h ; Read input 501 000003FB EBE7 jmp short empty_8042 502 .no_output: 503 000003FD A802 test al,2 504 000003FF 75E3 jnz empty_8042 505 00000401 E8CDFE io_delay 506 00000404 C3 .done: ret 507 508 ; 509 ; Execute a WBINVD instruction if possible on this CPU 510 ; 511 %if DO_WBINVD 512 try_wbinvd: 513 wbinvd 514 ret 515 %endif 516 517 section .bss 518 00000007 alignb 4 519 00000008 PMESP resd 1 ; Protected mode %esp 520 521 section .idt nobits align=4096 522 alignb 4096 523 00000000 pm_idt resb 4096 ; Protected-mode IDT, followed by interrupt stubs 524 525 526 527 528 pm_entry: equ 0x100000 529 530 section .rodata 531 00000013 00 align 2, db 0 532 call32_rmidt: 533 00000014 FFFF dw 0ffffh ; Limit 534 00000016 00000000 dd 0 ; Address 535 536 section .data 537 alignb 2 538 call32_pmidt: 539 0000005C 0008 dw 8*256 ; Limit 540 0000005E 00000000 dd 0 ; Address (entered later) 541 542 section .text 543 ; 544 ; This is the main entrypoint in this function 545 ; 546 init32: 547 00000405 BB[6304] mov bx,call32_call_start ; Where to go in PM 548 549 ; 550 ; Enter protected mode. BX contains the entry point relative to the 551 ; real-mode CS. 552 ; 553 call32_enter_pm: 554 00000408 8CC8 mov ax,cs 555 0000040A 8ED8 mov ds,ax 556 0000040C 660FB7E8 movzx ebp,ax 557 00000410 66C1E504 shl ebp,4 ; EBP <- CS_BASE 558 00000414 660FB7DB movzx ebx,bx 559 00000418 6601EB add ebx,ebp ; entry point += CS_BASE 560 0000041B FA cli 561 0000041C 8926[0400] mov [SavedSP],sp 562 00000420 FC cld 563 00000421 E8B2FE call enable_a20 564 00000424 C606[0D00]89 mov byte [call32_gdt+8+5],89h ; Mark TSS unbusy 565 00000429 660F0116[0000] o32 lgdt [call32_gdt] ; Set up GDT 566 0000042F 660F011E[5C00] o32 lidt [call32_pmidt] ; Set up IDT 567 00000435 0F20C0 mov eax,cr0 568 00000438 0C01 or al,1 569 0000043A 0F22C0 mov cr0,eax ; Enter protected mode 570 0000043D 66EA[45840000]2000 jmp 20h:strict dword .in_pm+CS_BASE 571 .pm_jmp equ $-6 572 573 574 bits 32 575 .in_pm: 576 00000445 31C0 xor eax,eax ; Available for future use... 577 00000447 8EE0 mov fs,eax 578 00000449 8EE8 mov gs,eax 579 0000044B 0F00D0 lldt ax 580 581 0000044E B028 mov al,28h ; Set up data segments 582 00000450 8EC0 mov es,eax 583 00000452 8ED8 mov ds,eax 584 00000454 8ED0 mov ss,eax 585 586 00000456 B008 mov al,08h 587 00000458 0F00D8 ltr ax 588 589 0000045B 8BA5[08000000] mov esp,[ebp+PMESP] ; Load protmode %esp if available 590 00000461 FFE3 jmp ebx ; Go to where we need to go 591 592 ; 593 ; This is invoked before first dispatch of the 32-bit code, in 32-bit mode 594 ; 595 call32_call_start: 596 ; 597 ; Set up a temporary stack in the bounce buffer; 598 ; start32.S will override this to point us to the real 599 ; high-memory stack. 600 ; 601 00000463 BC00800200 mov esp, (BOUNCE_SEG << 4) + 0x10000 602 603 00000468 68[DA840000] push dword call32_enter_rm.rm_jmp+CS_BASE 604 0000046D 68[3F840000] push dword call32_enter_pm.pm_jmp+CS_BASE 605 00000472 68[00020000] push dword stack_end ; RM size 606 00000477 68[00800000] push dword call32_gdt+CS_BASE 607 0000047C 68[1C850000] push dword call32_handle_interrupt+CS_BASE 608 00000481 6800800000 push dword CS_BASE ; Segment base 609 00000486 6800800100 push dword (BOUNCE_SEG << 4) ; Bounce buffer address 610 0000048B 68[2F850000] push dword call32_syscall+CS_BASE ; Syscall entry point 611 612 00000490 E8(00800F00) call pm_entry-CS_BASE ; Run the program... 613 614 ; ... fall through to call32_exit ... 615 616 call32_exit: 617 00000495 66BB[EE04] mov bx,call32_done ; Return to command loop 618 619 call32_enter_rm: 620 ; Careful here... the PM code may have relocated the 621 ; entire RM code, so we need to figure out exactly 622 ; where we are executing from. If the PM code has 623 ; relocated us, it *will* have adjusted the GDT to 624 ; match, though. 625 00000499 E800000000 call .here 626 0000049E 5D .here: pop ebp 627 0000049F 81ED[9E040000] sub ebp,.here 628 000004A5 0F018D[5C000000] o32 sidt [ebp+call32_pmidt] 629 000004AC FA cli 630 000004AD FC cld 631 000004AE 89A5[08000000] mov [ebp+PMESP],esp ; Save exit %esp 632 000004B4 31E4 xor esp,esp ; Make sure the high bits are zero 633 000004B6 EA[BD040000]1000 jmp 10h:.in_pm16 ; Return to 16-bit mode first 634 635 bits 16 636 .in_pm16: 637 000004BD B81800 mov ax,18h ; Real-mode-like segment 638 000004C0 8EC0 mov es,ax 639 000004C2 8ED8 mov ds,ax 640 000004C4 8ED0 mov ss,ax 641 000004C6 8EE0 mov fs,ax 642 000004C8 8EE8 mov gs,ax 643 644 000004CA 0F011E[1400] lidt [call32_rmidt] ; Real-mode IDT (rm needs no GDT) 645 000004CF 0F20C0 mov eax,cr0 646 000004D2 24FE and al,~1 647 000004D4 0F22C0 mov cr0,eax 648 000004D7 EA[DC04]0008 jmp MY_CS:.in_rm 649 .rm_jmp equ $-2 650 651 .in_rm: ; Back in real mode 652 000004DC 8CC8 mov ax,cs 653 000004DE 8ED8 mov ds,ax 654 000004E0 8EC0 mov es,ax 655 000004E2 8EE0 mov fs,ax 656 000004E4 8EE8 mov gs,ax 657 000004E6 8ED0 mov ss,ax 658 000004E8 8B26[0400] mov sp,[SavedSP] ; Restore stack 659 000004EC FFE3 jmp bx ; Go to whereever we need to go... 660 661 call32_done: 662 000004EE E8ADFE call disable_a20 663 000004F1 FB sti 664 000004F2 C3 ret 665 666 ; 667 ; 16-bit support code 668 ; 669 bits 16 670 671 ; 672 ; 16-bit interrupt-handling code 673 ; 674 call32_int_rm: 675 000004F3 9C pushf ; Flags on stack 676 000004F4 0E push cs ; Return segment 677 000004F5 68[FB04] push word .cont ; Return address 678 000004F8 6652 push dword edx ; Segment:offset of IVT entry 679 000004FA CB retf ; Invoke IVT routine 680 .cont: ; ... on resume ... 681 000004FB BB[2D05] mov bx,call32_int_resume 682 000004FE E907FF jmp call32_enter_pm ; Go back to PM 683 684 ; 685 ; 16-bit system call handling code 686 ; 687 call32_sys_rm: 688 00000501 0FA9 pop gs 689 00000503 0FA1 pop fs 690 00000505 07 pop es 691 00000506 1F pop ds 692 00000507 6661 popad 693 00000509 669D popfd 694 0000050B CB retf ; Invoke routine 695 .return: 696 0000050C 669C pushfd 697 0000050E 6660 pushad 698 00000510 1E push ds 699 00000511 06 push es 700 00000512 0FA0 push fs 701 00000514 0FA8 push gs 702 00000516 BB[8705] mov bx,call32_sys_resume 703 00000519 E9ECFE jmp call32_enter_pm 704 705 ; 706 ; 32-bit support code 707 ; 708 bits 32 709 710 ; 711 ; This is invoked on getting an interrupt in protected mode. At 712 ; this point, we need to context-switch to real mode and invoke 713 ; the interrupt routine. 714 ; 715 ; When this gets invoked, the registers are saved on the stack and 716 ; AL contains the register number. 717 ; 718 call32_handle_interrupt: 719 0000051C 0FB6C0 movzx eax,al 720 0000051F 31DB xor ebx,ebx ; Actually makes the code smaller 721 00000521 8B1483 mov edx,[ebx+eax*4] ; Get the segment:offset of the routine 722 00000524 66BB[F304] mov bx,call32_int_rm 723 00000528 E96CFFFFFF jmp call32_enter_rm ; Go to real mode 724 725 call32_int_resume: 726 0000052D 61 popad 727 0000052E CF iret 728 729 ; 730 ; Syscall invocation. We manifest a structure on the real-mode stack, 731 ; containing the call32sys_t structure from as well as 732 ; the following entries (from low to high address): 733 ; - Target offset 734 ; - Target segment 735 ; - Return offset 736 ; - Return segment (== real mode cs) 737 ; - Return flags 738 ; 739 call32_syscall: 740 0000052F 9C pushfd ; Save IF among other things... 741 00000530 60 pushad ; We only need to save some, but... 742 00000531 FC cld 743 00000532 E800000000 call .here 744 00000537 5D .here: pop ebp 745 00000538 81ED[37050000] sub ebp,.here 746 747 0000053E 0FB7BD[04000000] movzx edi,word [ebp+SavedSP] 748 00000545 83EF36 sub edi,54 ; Allocate 54 bytes 749 00000548 6689BD[04000000] mov [ebp+SavedSP],di 750 0000054F 01EF add edi,ebp ; Create linear address 751 752 00000551 8B74242C mov esi,[esp+11*4] ; Source regs 753 00000555 31C9 xor ecx,ecx 754 00000557 B10B mov cl,11 ; 44 bytes to copy 755 00000559 F3A5 rep movsd 756 757 0000055B 0FB6442428 movzx eax,byte [esp+10*4] ; Interrupt number 758 ; ecx == 0 here; adding it to the EA makes the 759 ; encoding smaller 760 00000560 8B0481 mov eax,[ecx+eax*4] ; Get IVT entry 761 00000563 AB stosd ; Save in stack frame 762 00000564 66B8[0C05] mov ax,call32_sys_rm.return ; Return offset 763 00000568 66AB stosw ; Save in stack frame 764 0000056A 89E8 mov eax,ebp 765 0000056C C1E804 shr eax,4 ; Return segment 766 0000056F 66AB stosw ; Save in stack frame 767 00000571 8B47F4 mov eax,[edi-12] ; Return flags 768 00000574 25D70C2000 and eax,0x200cd7 ; Mask (potentially) unsafe flags 769 00000579 8947F4 mov [edi-12],eax ; Primary flags entry 770 0000057C 66AB stosw ; Return flags 771 772 0000057E 66BB[0105] mov bx,call32_sys_rm 773 00000582 E912FFFFFF jmp call32_enter_rm ; Go to real mode 774 775 ; On return, the 44-byte return structure is on the 776 ; real-mode stack. call32_enter_pm will leave ebp 777 ; pointing to the real-mode base. 778 call32_sys_resume: 779 00000587 0FB7B5[04000000] movzx esi,word [ebp+SavedSP] 780 0000058E 8B7C2430 mov edi,[esp+12*4] ; Dest regs 781 00000592 01EE add esi,ebp ; Create linear address 782 00000594 21FF and edi,edi ; NULL pointer? 783 00000596 7502 jnz .do_copy 784 00000598 89F7 .no_copy: mov edi,esi ; Do a dummy copy-to-self 785 0000059A 31C9 .do_copy: xor ecx,ecx 786 0000059C B10B mov cl,11 ; 44 bytes 787 0000059E F3A5 rep movsd ; Copy register block 788 789 000005A0 668385[04000000]2C add word [ebp+SavedSP],44 ; Remove from stack 790 791 000005A8 61 popad 792 000005A9 9D popfd 793 000005AA C3 ret ; Return to 32-bit program