root/core/pxelinux.asm
| Revision 5a8a7bf7cb8b312b9d5bf28cc17ecb4c410be2c6, 12.5 KB (checked in by Gene Cumm <gene.cumm@…>, 18 months ago) | |
|---|---|
|
|
| Line | |
|---|---|
| 1 | ; -*- fundamental -*- (asm-mode sucks) |
| 2 | ; **************************************************************************** |
| 3 | ; |
| 4 | ; pxelinux.asm |
| 5 | ; |
| 6 | ; A program to boot Linux kernels off a TFTP server using the Intel PXE |
| 7 | ; network booting API. It is based on the SYSLINUX boot loader for |
| 8 | ; MS-DOS floppies. |
| 9 | ; |
| 10 | ; Copyright 1994-2009 H. Peter Anvin - All Rights Reserved |
| 11 | ; Copyright 2009 Intel Corporation; author: H. Peter Anvin |
| 12 | ; |
| 13 | ; This program is free software; you can redistribute it and/or modify |
| 14 | ; it under the terms of the GNU General Public License as published by |
| 15 | ; the Free Software Foundation, Inc., 53 Temple Place Ste 330, |
| 16 | ; Boston MA 02111-1307, USA; either version 2 of the License, or |
| 17 | ; (at your option) any later version; incorporated herein by reference. |
| 18 | ; |
| 19 | ; **************************************************************************** |
| 20 | |
| 21 | %define IS_PXELINUX 1 |
| 22 | %include "head.inc" |
| 23 | %include "pxe.inc" |
| 24 | |
| 25 | ; gPXE extensions support |
| 26 | %define GPXE 1 |
| 27 | |
| 28 | ; |
| 29 | ; Some semi-configurable constants... change on your own risk. |
| 30 | ; |
| 31 | my_id equ pxelinux_id |
| 32 | NULLFILE equ 0 ; Zero byte == null file name |
| 33 | NULLOFFSET equ 0 ; Position in which to look |
| 34 | REBOOT_TIME equ 5*60 ; If failure, time until full reset |
| 35 | %assign HIGHMEM_SLOP 128*1024 ; Avoid this much memory near the top |
| 36 | TFTP_BLOCKSIZE_LG2 equ 9 ; log2(bytes/block) |
| 37 | TFTP_BLOCKSIZE equ (1 << TFTP_BLOCKSIZE_LG2) |
| 38 | |
| 39 | SECTOR_SHIFT equ TFTP_BLOCKSIZE_LG2 |
| 40 | SECTOR_SIZE equ TFTP_BLOCKSIZE |
| 41 | |
| 42 | ; |
| 43 | ; The following structure is used for "virtual kernels"; i.e. LILO-style |
| 44 | ; option labels. The options we permit here are `kernel' and `append |
| 45 | ; Since there is no room in the bottom 64K for all of these, we |
| 46 | ; stick them in high memory and copy them down before we need them. |
| 47 | ; |
| 48 | struc vkernel |
| 49 | vk_vname: resb FILENAME_MAX ; Virtual name **MUST BE FIRST!** |
| 50 | vk_rname: resb FILENAME_MAX ; Real name |
| 51 | vk_ipappend: resb 1 ; "IPAPPEND" flag |
| 52 | vk_type: resb 1 ; Type of file |
| 53 | vk_appendlen: resw 1 |
| 54 | alignb 4 |
| 55 | vk_append: resb max_cmd_len+1 ; Command line |
| 56 | alignb 4 |
| 57 | vk_end: equ $ ; Should be <= vk_size |
| 58 | endstruc |
| 59 | |
| 60 | |
| 61 | ; --------------------------------------------------------------------------- |
| 62 | ; BEGIN CODE |
| 63 | ; --------------------------------------------------------------------------- |
| 64 | |
| 65 | ; |
| 66 | ; Memory below this point is reserved for the BIOS and the MBR |
| 67 | ; |
| 68 | section .earlybss |
| 69 | global trackbuf |
| 70 | trackbufsize equ 8192 |
| 71 | trackbuf resb trackbufsize ; Track buffer goes here |
| 72 | ; ends at 2800h |
| 73 | |
| 74 | ; These fields save information from before the time |
| 75 | ; .bss is zeroed... must be in .earlybss |
| 76 | global InitStack |
| 77 | InitStack resd 1 |
| 78 | |
| 79 | section .bss16 |
| 80 | alignb FILENAME_MAX |
| 81 | PXEStack resd 1 ; Saved stack during PXE call |
| 82 | |
| 83 | alignb 4 |
| 84 | global DHCPMagic, RebootTime, APIVer |
| 85 | RebootTime resd 1 ; Reboot timeout, if set by option |
| 86 | StrucPtr resw 2 ; Pointer to PXENV+ or !PXE structure |
| 87 | APIVer resw 1 ; PXE API version found |
| 88 | LocalBootType resw 1 ; Local boot return code |
| 89 | DHCPMagic resb 1 ; PXELINUX magic flags |
| 90 | |
| 91 | section .text16 |
| 92 | StackBuf equ STACK_TOP-44 ; Base of stack if we use our own |
| 93 | StackHome equ StackBuf |
| 94 | |
| 95 | ; PXE loads the whole file, but assume it can't be more |
| 96 | ; than (384-31)K in size. |
| 97 | MaxLMA equ 384*1024 |
| 98 | |
| 99 | ; |
| 100 | ; Primary entry point. |
| 101 | ; |
| 102 | bootsec equ $ |
| 103 | _start: |
| 104 | jmp 0:_start1 ; Canonicalize the address and skip |
| 105 | ; the patch header |
| 106 | |
| 107 | ; |
| 108 | ; Patch area for adding hardwired DHCP options |
| 109 | ; |
| 110 | align 4 |
| 111 | |
| 112 | hcdhcp_magic dd 0x2983c8ac ; Magic number |
| 113 | hcdhcp_len dd 7*4 ; Size of this structure |
| 114 | hcdhcp_flags dd 0 ; Reserved for the future |
| 115 | ; Parameters to be parsed before the ones from PXE |
| 116 | bdhcp_offset dd 0 ; Offset (entered by patcher) |
| 117 | bdhcp_len dd 0 ; Length (entered by patcher) |
| 118 | ; Parameters to be parsed *after* the ones from PXE |
| 119 | adhcp_offset dd 0 ; Offset (entered by patcher) |
| 120 | adhcp_len dd 0 ; Length (entered by patcher) |
| 121 | |
| 122 | _start1: |
| 123 | pushfd ; Paranoia... in case of return to PXE |
| 124 | pushad ; ... save as much state as possible |
| 125 | push ds |
| 126 | push es |
| 127 | push fs |
| 128 | push gs |
| 129 | |
| 130 | cld ; Copy upwards |
| 131 | xor ax,ax |
| 132 | mov ds,ax |
| 133 | mov es,ax |
| 134 | |
| 135 | %if 0 ; debugging code only... not intended for production use |
| 136 | ; Clobber the stack segment, to test for specific pathologies |
| 137 | mov di,STACK_BASE |
| 138 | mov cx,STACK_LEN >> 1 |
| 139 | mov ax,0xf4f4 |
| 140 | rep stosw |
| 141 | |
| 142 | ; Clobber the tail of the 64K segment, too |
| 143 | extern __bss1_end |
| 144 | mov di,__bss1_end |
| 145 | sub cx,di ; CX = 0 previously |
| 146 | shr cx,1 |
| 147 | rep stosw |
| 148 | %endif |
| 149 | |
| 150 | ; That is all pushed onto the PXE stack. Save the pointer |
| 151 | ; to it and switch to an internal stack. |
| 152 | mov [InitStack],sp |
| 153 | mov [InitStack+2],ss |
| 154 | |
| 155 | lss esp,[BaseStack] |
| 156 | sti ; Stack set up and ready |
| 157 | ; |
| 158 | ; Move the hardwired DHCP options (if present) to a safe place... |
| 159 | ; |
| 160 | bdhcp_copy: |
| 161 | mov cx,[bdhcp_len] |
| 162 | mov ax,trackbufsize/2 |
| 163 | jcxz .none |
| 164 | cmp cx,ax |
| 165 | jbe .oksize |
| 166 | mov cx,ax |
| 167 | mov [bdhcp_len],ax |
| 168 | .oksize: |
| 169 | mov eax,[bdhcp_offset] |
| 170 | add eax,_start |
| 171 | mov si,ax |
| 172 | and si,000Fh |
| 173 | shr eax,4 |
| 174 | push ds |
| 175 | mov ds,ax |
| 176 | mov di,trackbuf |
| 177 | add cx,3 |
| 178 | shr cx,2 |
| 179 | rep movsd |
| 180 | pop ds |
| 181 | .none: |
| 182 | |
| 183 | adhcp_copy: |
| 184 | mov cx,[adhcp_len] |
| 185 | mov ax,trackbufsize/2 |
| 186 | jcxz .none |
| 187 | cmp cx,ax |
| 188 | jbe .oksize |
| 189 | mov cx,ax |
| 190 | mov [adhcp_len],ax |
| 191 | .oksize: |
| 192 | mov eax,[adhcp_offset] |
| 193 | add eax,_start |
| 194 | mov si,ax |
| 195 | and si,000Fh |
| 196 | shr eax,4 |
| 197 | push ds |
| 198 | mov ds,ax |
| 199 | mov di,trackbuf+trackbufsize/2 |
| 200 | add cx,3 |
| 201 | shr cx,2 |
| 202 | rep movsd |
| 203 | pop ds |
| 204 | .none: |
| 205 | |
| 206 | ; |
| 207 | ; Initialize screen (if we're using one) |
| 208 | ; |
| 209 | %include "init.inc" |
| 210 | |
| 211 | ; |
| 212 | ; Tell the user we got this far |
| 213 | ; |
| 214 | mov si,syslinux_banner |
| 215 | call writestr_early |
| 216 | |
| 217 | mov si,copyright_str |
| 218 | call writestr_early |
| 219 | |
| 220 | ; |
| 221 | ; do fs initialize |
| 222 | ; |
| 223 | mov eax,ROOT_FS_OPS |
| 224 | xor ebp,ebp |
| 225 | pm_call fs_init |
| 226 | |
| 227 | section .rodata |
| 228 | alignz 4 |
| 229 | ROOT_FS_OPS: |
| 230 | extern pxe_fs_ops |
| 231 | dd pxe_fs_ops |
| 232 | dd 0 |
| 233 | |
| 234 | |
| 235 | section .text16 |
| 236 | ; |
| 237 | ; Initialize the idle mechanism |
| 238 | ; |
| 239 | call reset_idle |
| 240 | |
| 241 | ; |
| 242 | ; Now we're all set to start with our *real* business. First load the |
| 243 | ; configuration file (if any) and parse it. |
| 244 | ; |
| 245 | ; In previous versions I avoided using 32-bit registers because of a |
| 246 | ; rumour some BIOSes clobbered the upper half of 32-bit registers at |
| 247 | ; random. I figure, though, that if there are any of those still left |
| 248 | ; they probably won't be trying to install Linux on them... |
| 249 | ; |
| 250 | ; The code is still ripe with 16-bitisms, though. Not worth the hassle |
| 251 | ; to take'm out. In fact, we may want to put them back if we're going |
| 252 | ; to boot ELKS at some point. |
| 253 | ; |
| 254 | |
| 255 | ; |
| 256 | ; Linux kernel loading code is common. However, we need to define |
| 257 | ; a couple of helper macros... |
| 258 | ; |
| 259 | |
| 260 | ; Unload PXE stack |
| 261 | %define HAVE_UNLOAD_PREP |
| 262 | %macro UNLOAD_PREP 0 |
| 263 | pm_call unload_pxe |
| 264 | %endmacro |
| 265 | |
| 266 | ; |
| 267 | ; Load configuration file |
| 268 | ; |
| 269 | pm_call pm_load_config |
| 270 | jz no_config_file |
| 271 | |
| 272 | ; |
| 273 | ; Now we have the config file open. Parse the config file and |
| 274 | ; run the user interface. |
| 275 | ; |
| 276 | %include "ui.inc" |
| 277 | |
| 278 | ; |
| 279 | ; Boot to the local disk by returning the appropriate PXE magic. |
| 280 | ; AX contains the appropriate return code. |
| 281 | ; |
| 282 | local_boot: |
| 283 | push cs |
| 284 | pop ds |
| 285 | mov [LocalBootType],ax |
| 286 | call vgaclearmode |
| 287 | mov si,localboot_msg |
| 288 | call writestr_early |
| 289 | ; Restore the environment we were called with |
| 290 | pm_call reset_pxe |
| 291 | call cleanup_hardware |
| 292 | lss sp,[InitStack] |
| 293 | pop gs |
| 294 | pop fs |
| 295 | pop es |
| 296 | pop ds |
| 297 | popad |
| 298 | mov ax,[cs:LocalBootType] |
| 299 | cmp ax,-1 ; localboot -1 == INT 18h |
| 300 | je .int18 |
| 301 | popfd |
| 302 | retf ; Return to PXE |
| 303 | .int18: |
| 304 | popfd |
| 305 | int 18h |
| 306 | jmp 0F000h:0FFF0h |
| 307 | hlt |
| 308 | |
| 309 | ; |
| 310 | ; kaboom: write a message and bail out. Wait for quite a while, |
| 311 | ; or a user keypress, then do a hard reboot. |
| 312 | ; |
| 313 | ; Note: use BIOS_timer here; we may not have jiffies set up. |
| 314 | ; |
| 315 | global kaboom |
| 316 | kaboom: |
| 317 | RESET_STACK_AND_SEGS AX |
| 318 | .patch: mov si,bailmsg |
| 319 | call writestr_early ; Returns with AL = 0 |
| 320 | .drain: call pollchar |
| 321 | jz .drained |
| 322 | call getchar |
| 323 | jmp short .drain |
| 324 | .drained: |
| 325 | mov edi,[RebootTime] |
| 326 | mov al,[DHCPMagic] |
| 327 | and al,09h ; Magic+Timeout |
| 328 | cmp al,09h |
| 329 | je .time_set |
| 330 | mov edi,REBOOT_TIME |
| 331 | .time_set: |
| 332 | mov cx,18 |
| 333 | .wait1: push cx |
| 334 | mov ecx,edi |
| 335 | .wait2: mov dx,[BIOS_timer] |
| 336 | .wait3: call pollchar |
| 337 | jnz .keypress |
| 338 | call do_idle |
| 339 | cmp dx,[BIOS_timer] |
| 340 | je .wait3 |
| 341 | loop .wait2,ecx |
| 342 | mov al,'.' |
| 343 | call writechr |
| 344 | pop cx |
| 345 | loop .wait1 |
| 346 | .keypress: |
| 347 | call crlf |
| 348 | mov word [BIOS_magic],0 ; Cold reboot |
| 349 | jmp 0F000h:0FFF0h ; Reset vector address |
| 350 | |
| 351 | |
| 352 | ; |
| 353 | ; pxenv |
| 354 | ; |
| 355 | ; This is the main PXENV+/!PXE entry point, using the PXENV+ |
| 356 | ; calling convention. This is a separate local routine so |
| 357 | ; we can hook special things from it if necessary. In particular, |
| 358 | ; some PXE stacks seem to not like being invoked from anything but |
| 359 | ; the initial stack, so humour it. |
| 360 | ; |
| 361 | ; While we're at it, save and restore all registers. |
| 362 | ; |
| 363 | global pxenv |
| 364 | pxenv: |
| 365 | pushfd |
| 366 | pushad |
| 367 | |
| 368 | ; We may be removing ourselves from memory |
| 369 | cmp bx,0073h ; PXENV_RESTART_TFTP |
| 370 | jz .disable_timer |
| 371 | cmp bx,00E5h ; gPXE PXENV_FILE_EXEC |
| 372 | jnz .store_stack |
| 373 | |
| 374 | .disable_timer: |
| 375 | call timer_cleanup |
| 376 | |
| 377 | .store_stack: |
| 378 | mov [cs:PXEStack],sp |
| 379 | mov [cs:PXEStack+2],ss |
| 380 | lss sp,[cs:InitStack] |
| 381 | |
| 382 | ; Pre-clear the Status field |
| 383 | mov word [es:di],cs |
| 384 | |
| 385 | ; This works either for the PXENV+ or the !PXE calling |
| 386 | ; convention, as long as we ignore CF (which is redundant |
| 387 | ; with AX anyway.) |
| 388 | push es |
| 389 | push di |
| 390 | push bx |
| 391 | .jump: call 0:0 |
| 392 | add sp,6 |
| 393 | mov [cs:PXEStatus],ax |
| 394 | |
| 395 | lss sp,[cs:PXEStack] |
| 396 | |
| 397 | mov bp,sp |
| 398 | and ax,ax |
| 399 | setnz [bp+32] ; If AX != 0 set CF on return |
| 400 | |
| 401 | ; This clobbers the AX return, but we already saved it into |
| 402 | ; the PXEStatus variable. |
| 403 | popad |
| 404 | |
| 405 | ; If the call failed, it could return. |
| 406 | cmp bx,0073h |
| 407 | jz .enable_timer |
| 408 | cmp bx,00E5h |
| 409 | jnz .pop_flags |
| 410 | |
| 411 | .enable_timer: |
| 412 | call timer_init |
| 413 | |
| 414 | .pop_flags: |
| 415 | popfd ; Restore flags (incl. IF, DF) |
| 416 | ret |
| 417 | |
| 418 | ; Must be after function def due to NASM bug |
| 419 | global PXEEntry |
| 420 | PXEEntry equ pxenv.jump+1 |
| 421 | |
| 422 | section .bss16 |
| 423 | alignb 2 |
| 424 | PXEStatus resb 2 |
| 425 | |
| 426 | |
| 427 | section .text16 |
| 428 | ; |
| 429 | ; Invoke INT 1Ah on the PXE stack. This is used by the "Plan C" method |
| 430 | ; for finding the PXE entry point. |
| 431 | ; |
| 432 | global pxe_int1a |
| 433 | pxe_int1a: |
| 434 | mov [cs:PXEStack],sp |
| 435 | mov [cs:PXEStack+2],ss |
| 436 | lss sp,[cs:InitStack] |
| 437 | |
| 438 | int 1Ah ; May trash registers |
| 439 | |
| 440 | lss sp,[cs:PXEStack] |
| 441 | ret |
| 442 | |
| 443 | ; |
| 444 | ; Special unload for gPXE: this switches the InitStack from |
| 445 | ; gPXE to the ROM PXE stack. |
| 446 | ; |
| 447 | %if GPXE |
| 448 | global gpxe_unload |
| 449 | gpxe_unload: |
| 450 | mov bx,PXENV_FILE_EXIT_HOOK |
| 451 | mov di,pxe_file_exit_hook |
| 452 | call pxenv |
| 453 | jc .plain |
| 454 | |
| 455 | ; Now we actually need to exit back to gPXE, which will |
| 456 | ; give control back to us on the *new* "original stack"... |
| 457 | pushfd |
| 458 | push ds |
| 459 | push es |
| 460 | mov [PXEStack],sp |
| 461 | mov [PXEStack+2],ss |
| 462 | lss sp,[InitStack] |
| 463 | pop gs |
| 464 | pop fs |
| 465 | pop es |
| 466 | pop ds |
| 467 | popad |
| 468 | popfd |
| 469 | xor ax,ax |
| 470 | retf |
| 471 | .resume: |
| 472 | cli |
| 473 | |
| 474 | ; gPXE will have a stack frame looking much like our |
| 475 | ; InitStack, except it has a magic cookie at the top, |
| 476 | ; and the segment registers are in reverse order. |
| 477 | pop eax |
| 478 | pop ax |
| 479 | pop bx |
| 480 | pop cx |
| 481 | pop dx |
| 482 | push ax |
| 483 | push bx |
| 484 | push cx |
| 485 | push dx |
| 486 | mov [cs:InitStack],sp |
| 487 | mov [cs:InitStack+2],ss |
| 488 | lss sp,[cs:PXEStack] |
| 489 | pop es |
| 490 | pop ds |
| 491 | popfd |
| 492 | |
| 493 | .plain: |
| 494 | ret |
| 495 | |
| 496 | section .data16 |
| 497 | alignz 4 |
| 498 | pxe_file_exit_hook: |
| 499 | .status: dw 0 |
| 500 | .offset: dw gpxe_unload.resume |
| 501 | .seg: dw 0 |
| 502 | %endif |
| 503 | |
| 504 | section .text16 |
| 505 | |
| 506 | ; ----------------------------------------------------------------------------- |
| 507 | ; Common modules |
| 508 | ; ----------------------------------------------------------------------------- |
| 509 | |
| 510 | %include "common.inc" ; Universal modules |
| 511 | %include "writestr.inc" ; String output |
| 512 | writestr_early equ writestr |
| 513 | %include "writehex.inc" ; Hexadecimal output |
| 514 | %include "rawcon.inc" ; Console I/O w/o using the console functions |
| 515 | |
| 516 | ; ----------------------------------------------------------------------------- |
| 517 | ; Begin data section |
| 518 | ; ----------------------------------------------------------------------------- |
| 519 | |
| 520 | section .data16 |
| 521 | |
| 522 | copyright_str db ' Copyright (C) 1994-' |
| 523 | asciidec YEAR |
| 524 | db ' H. Peter Anvin et al', CR, LF, 0 |
| 525 | err_bootfailed db CR, LF, 'Boot failed: press a key to retry, or wait for reset...', CR, LF, 0 |
| 526 | bailmsg equ err_bootfailed |
| 527 | localboot_msg db 'Booting from local disk...', CR, LF, 0 |
| 528 | syslinux_banner db CR, LF, MY_NAME, ' ', VERSION_STR, ' ', DATE_STR, ' ', 0 |
| 529 | |
| 530 | ; |
| 531 | ; Config file keyword table |
| 532 | ; |
| 533 | %include "keywords.inc" |
| 534 | |
| 535 | ; |
| 536 | ; Extensions to search for (in *forward* order). |
| 537 | ; (.bs and .bss16 are disabled for PXELINUX, since they are not supported) |
| 538 | ; |
| 539 | alignz 4 |
| 540 | exten_table: db '.cbt' ; COMBOOT (specific) |
| 541 | db '.0', 0, 0 ; PXE bootstrap program |
| 542 | db '.com' ; COMBOOT (same as DOS) |
| 543 | db '.c32' ; COM32 |
| 544 | exten_table_end: |
| 545 | dd 0, 0 ; Need 8 null bytes here |
| 546 | |
| 547 | ; |
| 548 | ; Misc initialized (data) variables |
| 549 | ; |
| 550 | section .data16 |
| 551 | global KeepPXE |
| 552 | KeepPXE db 0 ; Should PXE be kept around? |
| 553 | |
| 554 | ; |
| 555 | ; IP information. Note that the field are in the same order as the |
| 556 | ; Linux kernel expects in the ip= option. |
| 557 | ; |
| 558 | section .bss16 |
| 559 | alignb 4 |
| 560 | global IPInfo |
| 561 | IPInfo: |
| 562 | .IPv4 resd 1 ; IPv4 information |
| 563 | .MyIP resd 1 ; My IP address |
| 564 | .ServerIP resd 1 |
| 565 | .GatewayIP resd 1 |
| 566 | .Netmask resd 1 |
Note: See TracBrowser
for help on using the browser.
