1 ;; -*- fundamental -*- 2 ;; $Id: memdisk16.asm,v 1.3 2004/12/14 22:46:25 hpa Exp $ 3 ;; ----------------------------------------------------------------------- 4 ;; 5 ;; Copyright 1994-2004 H. Peter Anvin - All Rights Reserved 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 MY_CS equ 0x0800 ; Segment address to use 24 CS_BASE equ (MY_CS << 4) ; Corresponding address 25 26 ; Low memory bounce buffer 27 BOUNCE_SEG equ (MY_CS+0x1000) 28 29 %define DO_WBINVD 0 30 31 %define STACK_HEAP_SIZE (128*1024) 32 33 section .rodata align=16 34 section .data align=16 35 section .bss align=16 36 37 ;; ----------------------------------------------------------------------- 38 ;; Kernel image header 39 ;; ----------------------------------------------------------------------- 40 41 section .text ; Must be first in image 42 bits 16 43 44 00000000 00 cmdline times 497 db 0 ; We put the command line here 45 000001F1 00 setup_sects db 0 46 000001F2 0000 root_flags dw 0 47 000001F4 0000 syssize dw 0 48 000001F6 0000 swap_dev dw 0 49 000001F8 0000 ram_size dw 0 50 000001FA 0000 vid_mode dw 0 51 000001FC 0000 root_dev dw 0 52 000001FE 55AA boot_flag dw 0xAA55 53 54 00000200 EB2E _start: jmp short start 55 56 00000202 48647253 db "HdrS" ; Header signature 57 00000206 0302 dw 0x0203 ; Header version number 58 59 00000208 00000000 realmode_swtch dw 0, 0 ; default_switch, SETUPSEG 60 0000020C 0010 start_sys_seg dw 0x1000 ; obsolete 61 0000020E [00FE] version_ptr dw memdisk_version-0x200 ; version string ptr 62 00000210 00 type_of_loader db 0 ; Filled in by boot loader 63 00000211 01 loadflags db 1 ; Please load high 64 00000212 0000 setup_move_size dw 0 ; Unused 65 00000214 00001000 code32_start dd 0x100000 ; 32-bit start address 66 00000218 00000000 ramdisk_image dd 0 ; Loaded ramdisk image address 67 0000021C 00000000 ramdisk_size dd 0 ; Size of loaded ramdisk 68 00000220 00000000 bootsect_kludge dw 0, 0 69 00000224 0000 heap_end_ptr dw 0 70 00000226 0000 pad1 dw 0 71 00000228 00000000 cmd_line_ptr dd 0 ; Command line 72 0000022C FFFFFFFF ramdisk_max dd 0xffffffff ; Highest allowed ramdisk address 73 74 section .rodata 75 memdisk_version: 76 00000000 4D454D4449534B2033- db "MEMDISK ", VERSION, " ", DATE, 0 77 00000009 2E3039203078343165- 78 00000012 666662636100 79 80 ;; ----------------------------------------------------------------------- 81 ;; End kernel image header 82 ;; ----------------------------------------------------------------------- 83 84 ; 85 ; Move ourselves down into memory to reduce the risk of conflicts; 86 ; then canonicalize CS to match the other segments. 87 ; 88 section .text 89 bits 16 90 start: 91 00000230 B80008 mov ax,MY_CS 92 00000233 8EC0 mov es,ax 93 00000235 0FB60E[F101] movzx cx,byte [setup_sects] 94 0000023A 41 inc cx ; Add one for the boot sector 95 0000023B C1E107 shl cx,7 ; Convert to dwords 96 0000023E 31F6 xor si,si 97 00000240 31FF xor di,di 98 00000242 8EE6 mov fs,si ; fs <- 0 99 00000244 FC cld 100 00000245 F366A5 rep movsd 101 00000248 8ED8 mov ds,ax 102 0000024A 8ED0 mov ss,ax 103 0000024C 6631E4 xor esp,esp ; Stack at top of 64K segment 104 0000024F EA[5402]0008 jmp MY_CS:.next 105 .next: 106 107 ; 108 ; Copy the command line, if there is one 109 ; 110 copy_cmdline: 111 00000254 31FF xor di,di ; Bottom of our own segment (= "boot sector") 112 00000256 66A1[2802] mov eax,[cmd_line_ptr] 113 0000025A 6621C0 and eax,eax 114 0000025D 7417 jz .endcmd ; No command line 115 0000025F 89C6 mov si,ax 116 00000261 66C1E804 shr eax,4 ; Convert to segment 117 00000265 83E60F and si,0x000F ; Starting offset only 118 00000268 8EE8 mov gs,ax 119 0000026A B9F001 mov cx,496 ; Max number of bytes 120 .copycmd: 121 0000026D 65AC gs lodsb 122 0000026F 20C0 and al,al 123 00000271 7403 jz .endcmd 124 00000273 AA stosb 125 00000274 E2F7 loop .copycmd 126 .endcmd: 127 00000276 30C0 xor al,al 128 00000278 AA stosb 129 130 ; 131 ; Now jump to 32-bit code 132 ; 133 00000279 FB sti 134 0000027A E83D01 call init32 135 ; 136 ; When init32 returns, we have been set up, the new boot sector loaded, 137 ; and we should go and and run the newly loaded boot sector 138 ; 139 ; The setup function returns (in AL) the drive number which should be 140 ; put into DL 141 ; 142 0000027D 89C2 mov dx,ax 143 144 0000027F FA cli 145 00000280 6631F6 xor esi,esi ; No partition table involved 146 00000283 8EDE mov ds,si ; Make all the segments consistent 147 00000285 8EC6 mov es,si 148 00000287 8EE6 mov fs,si 149 00000289 8EEE mov gs,si 150 0000028B 8ED6 mov ss,si 151 0000028D 66BC007C0000 mov esp,0x7C00 ; Good place for SP to start out 152 00000293 EA007C0000 jmp 0:0x7C00 153 154 ; 155 ; We enter protected mode, set up a flat 32-bit environment, run rep movsd 156 ; and then exit. IMPORTANT: This code assumes cs == MY_CS. 157 ; 158 ; This code is probably excessively anal-retentive in its handling of 159 ; segments, but this stuff is painful enough as it is without having to rely 160 ; on everything happening "as it ought to." 161 ; 162 section .rodata 163 164 ; desc base, limit, flags 165 %macro desc 3 166 dd (%2 & 0xffff) | ((%1 & 0xffff) << 16) 167 dd (%1 & 0xff000000) | (%2 & 0xf0000) | ((%3 & 0xf0ff) << 8) | ((%1 & 0x00ff0000) >> 16) 168 %endmacro 169 170 align 8, db 0 171 00000018 2F00 call32_gdt: dw call32_gdt_size-1 ; Null descriptor - contains GDT 172 0000001A [18800000] .adj1: dd call32_gdt+CS_BASE ; pointer for LGDT instruction 173 0000001E 0000 dw 0 174 175 ; 0008: Code segment, use16, readable, dpl 0, base CS_BASE, 64K 176 desc CS_BASE, 0xffff, 0x009b 177 00000020 FFFF0080 <1> dd (%2 & 0xffff) | ((%1 & 0xffff) << 16) 178 00000024 009B0000 <1> dd (%1 & 0xff000000) | (%2 & 0xf0000) | ((%3 & 0xf0ff) << 8) | ((%1 & 0x00ff0000) >> 16) 179 180 ; 0010: Data segment, use16, read/write, dpl 0, base CS_BASE, 64K 181 desc CS_BASE, 0xffff, 0x0093 182 00000028 FFFF0080 <1> dd (%2 & 0xffff) | ((%1 & 0xffff) << 16) 183 0000002C 00930000 <1> dd (%1 & 0xff000000) | (%2 & 0xf0000) | ((%3 & 0xf0ff) << 8) | ((%1 & 0x00ff0000) >> 16) 184 185 ; 0018: Data segment, use16, read/write, dpl 0, base 0, 4G 186 desc 0, 0xfffff, 0x809b 187 00000030 FFFF0000 <1> dd (%2 & 0xffff) | ((%1 & 0xffff) << 16) 188 00000034 009B8F00 <1> dd (%1 & 0xff000000) | (%2 & 0xf0000) | ((%3 & 0xf0ff) << 8) | ((%1 & 0x00ff0000) >> 16) 189 190 ; 0020: Code segment, use32, read/write, dpl 0, base 0, 4G 191 desc 0, 0xfffff, 0xc09b 192 00000038 FFFF0000 <1> dd (%2 & 0xffff) | ((%1 & 0xffff) << 16) 193 0000003C 009BCF00 <1> dd (%1 & 0xff000000) | (%2 & 0xf0000) | ((%3 & 0xf0ff) << 8) | ((%1 & 0x00ff0000) >> 16) 194 195 ; 0028: Data segment, use32, read/write, dpl 0, base 0, 4G 196 desc 0, 0xfffff, 0xc093 197 00000040 FFFF0000 <1> dd (%2 & 0xffff) | ((%1 & 0xffff) << 16) 198 00000044 0093CF00 <1> dd (%1 & 0xff000000) | (%2 & 0xf0000) | ((%3 & 0xf0ff) << 8) | ((%1 & 0x00ff0000) >> 16) 199 200 call32_gdt_size: equ $-call32_gdt 201 202 00000048 4552524F523A204132- err_a20: db 'ERROR: A20 gate not responding!',13,10,0 203 00000051 302067617465206E6F- 204 0000005A 7420726573706F6E64- 205 00000063 696E67210D0A00 206 207 section .bss 208 alignb 4 209 00000000 SavedSSSP resd 1 ; Place to save SS:SP 210 00000004 Return resd 1 ; Return value 211 00000008 A20Test resw 1 ; Space to test A20 212 0000000A A20Tries resb 1 213 214 section .data 215 alignb 4 216 00000000 00000000 Target dd 0 ; Target address 217 00000004 2000 Target_Seg dw 20h ; Target CS 218 219 00000006 0000 A20Type dw 0 ; Default = unknown 220 221 section .text 222 bits 16 223 ; 224 ; Routines to enable and disable (yuck) A20. These routines are gathered 225 ; from tips from a couple of sources, including the Linux kernel and 226 ; http://www.x86.org/. The need for the delay to be as large as given here 227 ; is indicated by Donnie Barnes of RedHat, the problematic system being an 228 ; IBM ThinkPad 760EL. 229 ; 230 ; We typically toggle A20 twice for every 64K transferred. 231 ; 232 %define io_delay call _io_delay 233 %define IO_DELAY_PORT 80h ; Invalid port (we hope!) 234 %define disable_wait 32 ; How long to wait for a disable 235 236 %define A20_DUNNO 0 ; A20 type unknown 237 %define A20_NONE 1 ; A20 always on? 238 %define A20_BIOS 2 ; A20 BIOS enable 239 %define A20_KBC 3 ; A20 through KBC 240 %define A20_FAST 4 ; A20 through port 92h 241 242 align 2, db 0 243 00000298 [C302][C302][CD02]- A20List dw a20_dunno, a20_none, a20_bios, a20_kbc, a20_fast 244 0000029E [DE02][0303] 245 000002A2 [9403][9403][6603]- A20DList dw a20d_dunno, a20d_none, a20d_bios, a20d_kbc, a20d_fast 246 000002A8 [7703][6F03] 247 a20_adjust_cnt equ ($-A20List)/2 248 249 000002AC EE slow_out: out dx, al ; Fall through 250 251 000002AD E680 _io_delay: out IO_DELAY_PORT,al 252 000002AF E680 out IO_DELAY_PORT,al 253 000002B1 C3 ret 254 255 enable_a20: 256 000002B2 6660 pushad 257 000002B4 C606[0A00]FF mov byte [A20Tries],255 ; Times to try to make this work 258 259 try_enable_a20: 260 261 ; 262 ; Flush the caches 263 ; 264 %if DO_WBINVD 265 call try_wbinvd 266 %endif 267 268 ; 269 ; If the A20 type is known, jump straight to type 270 ; 271 000002B9 8B2E[0600] mov bp,[A20Type] 272 000002BD 01ED add bp,bp ; Convert to word offset 273 000002BF FFA6[9802] .adj4: jmp word [bp+A20List] 274 275 ; 276 ; First, see if we are on a system with no A20 gate 277 ; 278 a20_dunno: 279 a20_none: 280 000002C3 C606[0600]01 mov byte [A20Type], A20_NONE 281 000002C8 E86F00 call a20_test 282 000002CB 756A jnz a20_done 283 284 ; 285 ; Next, try the BIOS (INT 15h AX=2401h) 286 ; 287 a20_bios: 288 000002CD C606[0600]02 mov byte [A20Type], A20_BIOS 289 000002D2 B80124 mov ax,2401h 290 000002D5 9C pushf ; Some BIOSes muck with IF 291 000002D6 CD15 int 15h 292 000002D8 9D popf 293 294 000002D9 E85E00 call a20_test 295 000002DC 7559 jnz a20_done 296 297 ; 298 ; Enable the keyboard controller A20 gate 299 ; 300 a20_kbc: 301 000002DE B201 mov dl, 1 ; Allow early exit 302 000002E0 E8B600 call empty_8042 303 000002E3 7552 jnz a20_done ; A20 live, no need to use KBC 304 305 000002E5 C606[0600]03 mov byte [A20Type], A20_KBC ; Starting KBC command sequence 306 307 000002EA B0D1 mov al,0D1h ; Command write 308 000002EC E664 out 064h, al 309 000002EE E8A600 call empty_8042_uncond 310 311 000002F1 B0DF mov al,0DFh ; A20 on 312 000002F3 E660 out 060h, al 313 000002F5 E89F00 call empty_8042_uncond 314 315 ; Verify that A20 actually is enabled. Do that by 316 ; observing a word in low memory and the same word in 317 ; the HMA until they are no longer coherent. Note that 318 ; we don't do the same check in the disable case, because 319 ; we don't want to *require* A20 masking (SYSLINUX should 320 ; work fine without it, if the BIOS does.) 321 000002F8 51 .kbc_wait: push cx 322 000002F9 31C9 xor cx,cx 323 .kbc_wait_loop: 324 000002FB E83C00 call a20_test 325 000002FE 7536 jnz a20_done_pop 326 00000300 E2F9 loop .kbc_wait_loop 327 328 00000302 59 pop cx 329 ; 330 ; Running out of options here. Final attempt: enable the "fast A20 gate" 331 ; 332 a20_fast: 333 00000303 C606[0600]04 mov byte [A20Type], A20_FAST ; Haven't used the KBC yet 334 00000308 E492 in al, 092h 335 0000030A 0C02 or al,02h 336 0000030C 24FE and al,~01h ; Don't accidentally reset the machine! 337 0000030E E692 out 092h, al 338 339 00000310 51 .fast_wait: push cx 340 00000311 31C9 xor cx,cx 341 .fast_wait_loop: 342 00000313 E82400 call a20_test 343 00000316 751E jnz a20_done_pop 344 00000318 E2F9 loop .fast_wait_loop 345 346 0000031A 59 pop cx 347 348 ; 349 ; Oh bugger. A20 is not responding. Try frobbing it again; eventually give up 350 ; and report failure to the user. 351 ; 352 353 0000031B FE0E[0A00] dec byte [A20Tries] 354 0000031F 7598 jnz try_enable_a20 355 356 357 ; Error message time 358 00000321 BE[4800] mov si,err_a20 359 print_err: 360 00000324 AC lodsb 361 00000325 20C0 and al,al 362 00000327 7409 jz die 363 00000329 BB0700 mov bx,7 364 0000032C B40E mov ah,0xe 365 0000032E CD10 int 10h 366 00000330 EBF2 jmp print_err 367 368 369 die: 370 00000332 FB sti 371 00000333 F4 .hlt: hlt 372 00000334 EBFD jmp short .hlt 373 374 ; 375 ; A20 unmasked, proceed... 376 ; 377 00000336 59 a20_done_pop: pop cx 378 00000337 6661 a20_done: popad 379 00000339 C3 ret 380 381 ; 382 ; This routine tests if A20 is enabled (ZF = 0). This routine 383 ; must not destroy any register contents. 384 ; 385 a20_test: 386 0000033A 06 push es 387 0000033B 51 push cx 388 0000033C 50 push ax 389 0000033D B9FFFF mov cx,0FFFFh ; HMA = segment 0FFFFh 390 00000340 8EC1 mov es,cx 391 00000342 B92000 mov cx,32 ; Loop count 392 00000345 A1[0800] mov ax,[A20Test] 393 00000348 40 .a20_wait: inc ax 394 00000349 A3[0800] mov [A20Test],ax 395 0000034C E85EFF io_delay ; Serialize, and fix delay 396 0000034F 263B06[1880] cmp ax,[es:A20Test+CS_BASE+10h] 397 00000354 E1F2 loopz .a20_wait 398 00000356 58 .a20_done: pop ax 399 00000357 59 pop cx 400 00000358 07 pop es 401 00000359 C3 ret 402 403 disable_a20: 404 0000035A 6660 pushad 405 ; 406 ; Flush the caches 407 ; 408 %if DO_WBINVD 409 call try_wbinvd 410 %endif 411 412 0000035C 8B2E[0600] mov bp,[A20Type] 413 00000360 01ED add bp,bp ; Convert to word offset 414 00000362 FFA6[A202] .adj5: jmp word [bp+A20DList] 415 416 a20d_bios: 417 00000366 B80024 mov ax,2400h 418 00000369 9C pushf ; Some BIOSes muck with IF 419 0000036A CD15 int 15h 420 0000036C 9D popf 421 0000036D EB19 jmp short a20d_snooze 422 423 ; 424 ; Disable the "fast A20 gate" 425 ; 426 a20d_fast: 427 0000036F E492 in al, 092h 428 00000371 24FC and al,~03h 429 00000373 E692 out 092h, al 430 00000375 EB11 jmp short a20d_snooze 431 432 ; 433 ; Disable the keyboard controller A20 gate 434 ; 435 a20d_kbc: 436 00000377 E81D00 call empty_8042_uncond 437 0000037A B0D1 mov al,0D1h 438 0000037C E664 out 064h, al ; Command write 439 0000037E E81600 call empty_8042_uncond 440 00000381 B0DD mov al,0DDh ; A20 off 441 00000383 E660 out 060h, al 442 00000385 E80F00 call empty_8042_uncond 443 ; Wait a bit for it to take effect 444 a20d_snooze: 445 00000388 51 push cx 446 00000389 B92000 mov cx, disable_wait 447 0000038C E8ABFF .delayloop: call a20_test 448 0000038F 7402 jz .disabled 449 00000391 E2F9 loop .delayloop 450 00000393 59 .disabled: pop cx 451 a20d_dunno: 452 a20d_none: 453 00000394 6661 popad 454 00000396 C3 ret 455 456 ; 457 ; Routine to empty the 8042 KBC controller. If dl != 0 458 ; then we will test A20 in the loop and exit if A20 is 459 ; suddenly enabled. 460 ; 461 empty_8042_uncond: 462 00000397 30D2 xor dl,dl 463 empty_8042: 464 00000399 E89EFF call a20_test 465 0000039C 7404 jz .a20_on 466 0000039E 20D2 and dl,dl 467 000003A0 7517 jnz .done 468 000003A2 E808FF .a20_on: io_delay 469 000003A5 E464 in al, 064h ; Status port 470 000003A7 A801 test al,1 471 000003A9 7407 jz .no_output 472 000003AB E8FFFE io_delay 473 000003AE E460 in al, 060h ; Read input 474 000003B0 EBE7 jmp short empty_8042 475 .no_output: 476 000003B2 A802 test al,2 477 000003B4 75E3 jnz empty_8042 478 000003B6 E8F4FE io_delay 479 000003B9 C3 .done: ret 480 481 ; 482 ; Execute a WBINVD instruction if possible on this CPU 483 ; 484 %if DO_WBINVD 485 try_wbinvd: 486 wbinvd 487 ret 488 %endif 489 490 section .bss 491 0000000B alignb 4 492 0000000C PMESP resd 1 ; Protected mode %esp 493 494 section .idt nobits align=4096 495 alignb 4096 496 00000000 pm_idt resb 4096 ; Protected-mode IDT, followed by interrupt stubs 497 498 499 500 501 pm_entry: equ 0x100000 502 503 section .rodata 504 0000006A 00 align 4, db 0 505 call32_pmidt: 506 0000006C 0008 dw 8*256 ; Limit 507 0000006E [00800000] dd pm_idt+CS_BASE ; Address 508 509 call32_rmidt: 510 00000072 FFFF dw 0ffffh ; Limit 511 00000074 00000000 dd 0 ; Address 512 513 section .text 514 ; 515 ; This is the main entrypoint in this function 516 ; 517 init32: 518 000003BA 66BB[06840000] mov ebx,call32_call_start+CS_BASE ; Where to go in PM 519 520 call32_enter_pm: 521 000003C0 8CC8 mov ax,cs 522 000003C2 8ED8 mov ds,ax 523 000003C4 FA cli 524 000003C5 8926[0000] mov [SavedSSSP],sp 525 000003C9 8C16[0200] mov [SavedSSSP+2],ss 526 000003CD FC cld 527 000003CE E869FF call a20_test 528 000003D1 7503 jnz .a20ok 529 000003D3 E8DCFE call enable_a20 530 531 .a20ok: 532 000003D6 0F0116[1800] lgdt [call32_gdt] ; Set up GDT 533 000003DB 0F011E[6C00] lidt [call32_pmidt] ; Set up the IDT 534 000003E0 0F20C0 mov eax,cr0 535 000003E3 0C01 or al,1 536 000003E5 0F22C0 mov cr0,eax ; Enter protected mode 537 000003E8 66EA[F0830000]2000 jmp 20h:dword .in_pm+CS_BASE 538 539 bits 32 540 .in_pm: 541 000003F0 31C0 xor eax,eax ; Available for future use... 542 000003F2 8EE0 mov fs,eax 543 000003F4 8EE8 mov gs,eax 544 545 000003F6 B028 mov al,28h ; Set up data segments 546 000003F8 8EC0 mov es,eax 547 000003FA 8ED8 mov ds,eax 548 000003FC 8ED0 mov ss,eax 549 550 000003FE 8B25[0C800000] mov esp,[PMESP+CS_BASE] ; Load protmode %esp if available 551 00000404 FFE3 jmp ebx ; Go to where we need to go 552 553 ; 554 ; This is invoked before first dispatch of the 32-bit code, in 32-bit mode 555 ; 556 call32_call_start: 557 ; 558 ; Point the stack into low memory 559 ; We have: this segment, bounce buffer, then stack+heap 560 ; 561 00000406 BC00800400 mov esp, CS_BASE + 0x20000 + STACK_HEAP_SIZE 562 0000040B 83E4F0 and esp, ~0xf 563 564 ; 565 ; Set up the protmode IDT and the interrupt jump buffers 566 ; 567 0000040E BF[00800000] mov edi,pm_idt+CS_BASE 568 569 ; Form an interrupt gate descriptor 570 ; WARNING: This is broken if pm_idt crosses a 64K boundary; 571 ; however, it can't because of the alignment constraints. 572 00000413 BB[00880000] mov ebx,pm_idt+CS_BASE+8*256 573 00000418 B800EE2000 mov eax,0x0020ee00 574 0000041D 6693 xchg ax,bx 575 0000041F 31C9 xor ecx,ecx 576 00000421 FEC5 inc ch ; ecx <- 256 577 578 00000423 51 push ecx 579 .make_idt: 580 00000424 AB stosd 581 00000425 83C008 add eax,8 582 00000428 93 xchg eax,ebx 583 00000429 AB stosd 584 0000042A 93 xchg eax,ebx 585 0000042B E2F7 loop .make_idt 586 587 0000042D 59 pop ecx 588 589 ; Each entry in the interrupt jump buffer contains 590 ; the following instructions: 591 ; 592 ; 00000000 60 pushad 593 ; 00000001 B0xx mov al, 594 ; 00000003 E9xxxxxxxx jmp call32_handle_interrupt 595 596 0000042E B860B000E9 mov eax,0xe900b060 597 00000433 BB[D7840000] mov ebx,call32_handle_interrupt+CS_BASE 598 00000438 29FB sub ebx,edi 599 600 .make_ijb: 601 0000043A AB stosd 602 0000043B 284FFE sub [edi-2],cl ; Interrupt # 603 0000043E 93 xchg eax,ebx 604 0000043F 83E808 sub eax,8 605 00000442 AB stosd 606 00000443 93 xchg eax,ebx 607 00000444 E2F4 loop .make_ijb 608 609 ; Now everything is set up for interrupts... 610 611 00000446 6800800100 push dword (BOUNCE_SEG << 4) ; Bounce buffer address 612 0000044B 68[EA840000] push dword call32_syscall+CS_BASE ; Syscall entry point 613 00000450 FB sti ; Interrupts OK now 614 00000451 E8(00800F00) call pm_entry-CS_BASE ; Run the program... 615 616 ; ... on return ... 617 00000456 A3[04800000] mov [Return+CS_BASE],eax 618 619 ; ... fall through to call32_exit ... 620 621 call32_exit: 622 0000045B 66BB[A004] mov bx,call32_done ; Return to command loop 623 624 call32_enter_rm: 625 0000045F FA cli 626 00000460 FC cld 627 00000461 8925[0C800000] mov [PMESP+CS_BASE],esp ; Save exit %esp 628 00000467 31E4 xor esp,esp ; Make sure the high bits are zero 629 00000469 EA[70040000]0800 jmp 08h:.in_pm16 ; Return to 16-bit mode first 630 631 bits 16 632 .in_pm16: 633 00000470 B81000 mov ax,10h ; Real-mode-like segment 634 00000473 8EC0 mov es,ax 635 00000475 8ED8 mov ds,ax 636 00000477 8ED0 mov ss,ax 637 00000479 8EE0 mov fs,ax 638 0000047B 8EE8 mov gs,ax 639 640 0000047D 0F011E[7200] lidt [call32_rmidt] ; Real-mode IDT (rm needs no GDT) 641 00000482 0F20C0 mov eax,cr0 642 00000485 24FE and al,~1 643 00000487 0F22C0 mov cr0,eax 644 0000048A EA[8F04]0008 jmp MY_CS:.in_rm 645 646 .in_rm: ; Back in real mode 647 0000048F 8CC8 mov ax,cs ; Set up sane segments 648 00000491 8ED8 mov ds,ax 649 00000493 8EC0 mov es,ax 650 00000495 8EE0 mov fs,ax 651 00000497 8EE8 mov gs,ax 652 00000499 0FB226[0000] lss sp,[SavedSSSP] ; Restore stack 653 0000049E FFE3 jmp bx ; Go to whereever we need to go... 654 655 call32_done: 656 000004A0 E8B7FE call disable_a20 657 000004A3 FB sti 658 000004A4 A1[0400] mov ax,[Return] 659 000004A7 C3 ret 660 661 ; 662 ; 16-bit support code 663 ; 664 bits 16 665 666 ; 667 ; 16-bit interrupt-handling code 668 ; 669 call32_int_rm: 670 000004A8 9C pushf ; Flags on stack 671 000004A9 0E push cs ; Return segment 672 000004AA 68[B004] push word .cont ; Return address 673 000004AD 6652 push dword edx ; Segment:offset of IVT entry 674 000004AF CB retf ; Invoke IVT routine 675 .cont: ; ... on resume ... 676 000004B0 66BB[E8840000] mov ebx,call32_int_resume+CS_BASE 677 000004B6 E907FF jmp call32_enter_pm ; Go back to PM 678 679 ; 680 ; 16-bit system call handling code 681 ; 682 call32_sys_rm: 683 000004B9 0FA9 pop gs 684 000004BB 0FA1 pop fs 685 000004BD 07 pop es 686 000004BE 1F pop ds 687 000004BF 6661 popad 688 000004C1 669D popfd 689 000004C3 CB retf ; Invoke routine 690 .return: 691 000004C4 669C pushfd 692 000004C6 6660 pushad 693 000004C8 1E push ds 694 000004C9 06 push es 695 000004CA 0FA0 push fs 696 000004CC 0FA8 push gs 697 000004CE 66BB[39850000] mov ebx,call32_sys_resume+CS_BASE 698 000004D4 E9E9FE jmp call32_enter_pm 699 700 ; 701 ; 32-bit support code 702 ; 703 bits 32 704 705 ; 706 ; This is invoked on getting an interrupt in protected mode. At 707 ; this point, we need to context-switch to real mode and invoke 708 ; the interrupt routine. 709 ; 710 ; When this gets invoked, the registers are saved on the stack and 711 ; AL contains the register number. 712 ; 713 call32_handle_interrupt: 714 000004D7 0FB6C0 movzx eax,al 715 000004DA 31DB xor ebx,ebx ; Actually makes the code smaller 716 000004DC 8B1483 mov edx,[ebx+eax*4] ; Get the segment:offset of the routine 717 000004DF 66BB[A804] mov bx,call32_int_rm 718 000004E3 E977FFFFFF jmp call32_enter_rm ; Go to real mode 719 720 call32_int_resume: 721 000004E8 61 popad 722 000004E9 CF iret 723 724 ; 725 ; Syscall invocation. We manifest a structure on the real-mode stack, 726 ; containing the call32sys_t structure from as well as 727 ; the following entries (from low to high address): 728 ; - Target offset 729 ; - Target segment 730 ; - Return offset 731 ; - Return segment (== real mode cs) 732 ; - Return flags 733 ; 734 call32_syscall: 735 000004EA 9C pushfd ; Save IF among other things... 736 000004EB 60 pushad ; We only need to save some, but... 737 000004EC FC cld 738 739 000004ED 0FB73D[00800000] movzx edi,word [SavedSSSP+CS_BASE] 740 000004F4 0FB705[02800000] movzx eax,word [SavedSSSP+CS_BASE+2] 741 000004FB 83EF36 sub edi,54 ; Allocate 54 bytes 742 000004FE 66893D[00800000] mov [SavedSSSP+CS_BASE],di 743 00000505 C1E004 shl eax,4 744 00000508 01C7 add edi,eax ; Create linear address 745 746 0000050A 8B74242C mov esi,[esp+11*4] ; Source regs 747 0000050E 31C9 xor ecx,ecx 748 00000510 B10B mov cl,11 ; 44 bytes to copy 749 00000512 F3A5 rep movsd 750 751 00000514 0FB6442428 movzx eax,byte [esp+10*4] ; Interrupt number 752 ; ecx == 0 here; adding it to the EA makes the 753 ; encoding smaller 754 00000519 8B0481 mov eax,[ecx+eax*4] ; Get IVT entry 755 0000051C AB stosd ; Save in stack frame 756 0000051D B8[C4040008] mov eax,call32_sys_rm.return + (MY_CS << 16) ; Return seg:offs 757 00000522 AB stosd ; Save in stack frame 758 00000523 8B47F4 mov eax,[edi-12] ; Return flags 759 00000526 25D70C2000 and eax,0x200cd7 ; Mask (potentially) unsafe flags 760 0000052B 8947F4 mov [edi-12],eax ; Primary flags entry 761 0000052E 66AB stosw ; Return flags 762 763 00000530 66BB[B904] mov bx,call32_sys_rm 764 00000534 E926FFFFFF jmp call32_enter_rm ; Go to real mode 765 766 ; On return, the 44-byte return structure is on the 767 ; real-mode stack. 768 call32_sys_resume: 769 00000539 0FB735[00800000] movzx esi,word [SavedSSSP+CS_BASE] 770 00000540 0FB705[02800000] movzx eax,word [SavedSSSP+CS_BASE+2] 771 00000547 8B7C2430 mov edi,[esp+12*4] ; Dest regs 772 0000054B C1E004 shl eax,4 773 0000054E 01C6 add esi,eax ; Create linear address 774 00000550 21FF and edi,edi ; NULL pointer? 775 00000552 7502 jnz .do_copy 776 00000554 89F7 .no_copy: mov edi,esi ; Do a dummy copy-to-self 777 00000556 31C9 .do_copy: xor ecx,ecx 778 00000558 B10B mov cl,11 ; 44 bytes 779 0000055A F3A5 rep movsd ; Copy register block 780 781 0000055C 8305[00800000]2C add dword [SavedSSSP+CS_BASE],44 ; Remove from stack 782 783 00000563 61 popad 784 00000564 9D popfd 785 00000565 C3 ret ; Return to 32-bit program