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; 31my_id equ pxelinux_id 32NULLFILE equ 0 ; Zero byte == null file name 33NULLOFFSET equ 0 ; Position in which to look 34REBOOT_TIME equ 5*60 ; If failure, time until full reset 35%assign HIGHMEM_SLOP 128*1024 ; Avoid this much memory near the top 36TFTP_BLOCKSIZE_LG2 equ 9 ; log2(bytes/block) 37TFTP_BLOCKSIZE equ (1 << TFTP_BLOCKSIZE_LG2) 38 39SECTOR_SHIFT equ TFTP_BLOCKSIZE_LG2 40SECTOR_SIZE equ TFTP_BLOCKSIZE 41 42; --------------------------------------------------------------------------- 43; BEGIN CODE 44; --------------------------------------------------------------------------- 45 46; 47; Memory below this point is reserved for the BIOS and the MBR 48; 49 section .earlybss 50 global trackbuf 51trackbufsize equ 8192 52trackbuf resb trackbufsize ; Track buffer goes here 53 ; ends at 2800h 54 55 ; These fields save information from before the time 56 ; .bss is zeroed... must be in .earlybss 57 global InitStack 58InitStack resd 1 59 60 section .bss16 61 alignb FILENAME_MAX 62PXEStack resd 1 ; Saved stack during PXE call 63 64 alignb 4 65 global DHCPMagic, RebootTime, BIOSName 66RebootTime resd 1 ; Reboot timeout, if set by option 67LocalBootType resw 1 ; Local boot return code 68DHCPMagic resb 1 ; PXELINUX magic flags 69BIOSName resw 1 ; Dummy variable - always 0 70 71 section .text16 72 global StackBuf 73StackBuf equ STACK_TOP-44 ; Base of stack if we use our own 74StackHome equ StackBuf 75 76 ; PXE loads the whole file, but assume it can't be more 77 ; than (384-31)K in size. 78MaxLMA equ 384*1024 79 80; 81; Primary entry point. 82; 83bootsec equ $ 84_start: 85 jmp 0:_start1 ; Canonicalize the address and skip 86 ; the patch header 87 88; 89; Patch area for adding hardwired DHCP options 90; 91 align 4 92 93hcdhcp_magic dd 0x2983c8ac ; Magic number 94hcdhcp_len dd 7*4 ; Size of this structure 95hcdhcp_flags dd 0 ; Reserved for the future 96 global bdhcp_len, adhcp_len 97 ; Parameters to be parsed before the ones from PXE 98bdhcp_offset dd 0 ; Offset (entered by patcher) 99bdhcp_len dd 0 ; Length (entered by patcher) 100 ; Parameters to be parsed *after* the ones from PXE 101adhcp_offset dd 0 ; Offset (entered by patcher) 102adhcp_len dd 0 ; Length (entered by patcher) 103 104_start1: 105 pushfd ; Paranoia... in case of return to PXE 106 pushad ; ... save as much state as possible 107 push ds 108 push es 109 push fs 110 push gs 111 112 cld ; Copy upwards 113 xor ax,ax 114 mov ds,ax 115 mov es,ax 116 117%if 0 ; debugging code only... not intended for production use 118 ; Clobber the stack segment, to test for specific pathologies 119 mov di,STACK_BASE 120 mov cx,STACK_LEN >> 1 121 mov ax,0xf4f4 122 rep stosw 123 124 ; Clobber the tail of the 64K segment, too 125 extern __bss1_end 126 mov di,__bss1_end 127 sub cx,di ; CX = 0 previously 128 shr cx,1 129 rep stosw 130%endif 131 132 ; That is all pushed onto the PXE stack. Save the pointer 133 ; to it and switch to an internal stack. 134 mov [InitStack],sp 135 mov [InitStack+2],ss 136 137 lss esp,[BaseStack] 138 sti ; Stack set up and ready 139 140; 141; Initialize screen (if we're using one) 142; 143%include "init.inc" 144 145; 146; Tell the user we got this far 147; 148 mov si,syslinux_banner 149 call writestr_early 150 151 mov si,copyright_str 152 call writestr_early 153 154; 155; do fs initialize 156; 157 mov eax,ROOT_FS_OPS 158 xor ebp,ebp 159 pm_call pm_fs_init 160 161 section .rodata 162 alignz 4 163ROOT_FS_OPS: 164 extern pxe_fs_ops 165 dd pxe_fs_ops 166 dd 0 167 168 169 section .text16 170; 171; Initialize the idle mechanism 172; 173 extern reset_idle 174 pm_call reset_idle 175 176; 177; Now we're all set to start with our *real* business. 178; 179; In previous versions I avoided using 32-bit registers because of a 180; rumour some BIOSes clobbered the upper half of 32-bit registers at 181; random. I figure, though, that if there are any of those still left 182; they probably won't be trying to install Linux on them... 183; 184; The code is still ripe with 16-bitisms, though. Not worth the hassle 185; to take'm out. In fact, we may want to put them back if we're going 186; to boot ELKS at some point. 187; 188 189; 190; Linux kernel loading code is common. However, we need to define 191; a couple of helper macros... 192; 193 194; Unload PXE stack 195%define HAVE_UNLOAD_PREP 196%macro UNLOAD_PREP 0 197 pm_call unload_pxe 198%endmacro 199 200; 201; Jump to 32-bit ELF space 202; 203 pm_call load_env32 204 jmp kaboom ; load_env32() shouldn't return. If it does, then kaboom! 205 206print_hello: 207enter_command: 208auto_boot: 209 pm_call hello 210 211; 212; Save hardwired DHCP options. This is done before the C environment 213; is initialized, so it has to be done in assembly. 214; 215%define MAX_DHCP_OPTS 4096 216 bits 32 217 218 section .savedata 219 global bdhcp_data, adhcp_data 220bdhcp_data: resb MAX_DHCP_OPTS 221adhcp_data: resb MAX_DHCP_OPTS 222 223 section .textnr 224pm_save_data: 225 mov eax,MAX_DHCP_OPTS 226 movzx ecx,word [bdhcp_len] 227 cmp ecx,eax 228 jna .oksize 229 mov ecx,eax 230 mov [bdhcp_len],ax 231.oksize: 232 mov esi,[bdhcp_offset] 233 add esi,_start 234 mov edi,bdhcp_data 235 add ecx,3 236 shr ecx,2 237 rep movsd 238 239adhcp_copy: 240 movzx ecx,word [adhcp_len] 241 cmp ecx,eax 242 jna .oksize 243 mov ecx,eax 244 mov [adhcp_len],ax 245.oksize: 246 mov esi,[adhcp_offset] 247 add esi,_start 248 mov edi,adhcp_data 249 add ecx,3 250 shr ecx,2 251 rep movsd 252 ret 253 254 bits 16 255 256; As core/ui.inc used to be included here in core/pxelinux.asm, and it's no 257; longer used, its global variables that were previously used by 258; core/pxelinux.asm are now declared here. 259 section .bss16 260 alignb 4 261Kernel_EAX resd 1 262Kernel_SI resw 1 263 264 section .bss16 265 alignb 4 266ThisKbdTo resd 1 ; Temporary holder for KbdTimeout 267ThisTotalTo resd 1 ; Temporary holder for TotalTimeout 268KernelExtPtr resw 1 ; During search, final null pointer 269FuncFlag resb 1 ; Escape sequences received from keyboard 270KernelType resb 1 ; Kernel type, from vkernel, if known 271 global KernelName 272KernelName resb FILENAME_MAX ; Mangled name for kernel 273 274 section .text16 275; 276; COM32 vestigial data structure 277; 278%include "com32.inc" 279 280 section .text16 281 global local_boot16:function hidden 282local_boot16: 283 mov [LocalBootType],ax 284 lss sp,[InitStack] 285 pop gs 286 pop fs 287 pop es 288 pop ds 289 popad 290 mov ax,[cs:LocalBootType] 291 cmp ax,-1 ; localboot -1 == INT 18h 292 je .int18 293 popfd 294 retf ; Return to PXE 295.int18: 296 popfd 297 int 18h 298 jmp 0F000h:0FFF0h 299 hlt 300 301; 302; kaboom: write a message and bail out. Wait for quite a while, 303; or a user keypress, then do a hard reboot. 304; 305; Note: use BIOS_timer here; we may not have jiffies set up. 306; 307 global kaboom 308kaboom: 309 RESET_STACK_AND_SEGS AX 310.patch: mov si,bailmsg 311 call writestr_early ; Returns with AL = 0 312.drain: call pollchar 313 jz .drained 314 call getchar 315 jmp short .drain 316.drained: 317 mov edi,[RebootTime] 318 mov al,[DHCPMagic] 319 and al,09h ; Magic+Timeout 320 cmp al,09h 321 je .time_set 322 mov edi,REBOOT_TIME 323.time_set: 324 mov cx,18 325.wait1: push cx 326 mov ecx,edi 327.wait2: mov dx,[BIOS_timer] 328.wait3: call pollchar 329 jnz .keypress 330 pm_call __idle 331 cmp dx,[BIOS_timer] 332 je .wait3 333 loop .wait2,ecx 334 mov al,'.' 335 pm_call pm_writechr 336 pop cx 337 loop .wait1 338.keypress: 339 pm_call crlf 340 mov word [BIOS_magic],0 ; Cold reboot 341 jmp 0F000h:0FFF0h ; Reset vector address 342 343; 344; pxenv 345; 346; This is the main PXENV+/!PXE entry point, using the PXENV+ 347; calling convention. This is a separate local routine so 348; we can hook special things from it if necessary. In particular, 349; some PXE stacks seem to not like being invoked from anything but 350; the initial stack, so humour it. 351; 352; While we're at it, save and restore all registers. 353; 354 global pxenv 355pxenv: 356 pushfd 357 pushad 358 359 ; We may be removing ourselves from memory 360 cmp bx,PXENV_RESTART_TFTP 361 jz .disable_timer 362 cmp bx,PXENV_FILE_EXEC 363 jnz .store_stack 364 365.disable_timer: 366 call bios_timer_cleanup 367 368.store_stack: 369 pushf 370 cli 371 inc word [cs:PXEStackLock] 372 jnz .skip1 373 pop bp 374 mov [cs:PXEStack],sp 375 mov [cs:PXEStack+2],ss 376 lss sp,[cs:InitStack] 377 push bp 378.skip1: 379 popf 380 381 ; Pre-clear the Status field 382 mov word [es:di],cs 383 384 ; This works either for the PXENV+ or the !PXE calling 385 ; convention, as long as we ignore CF (which is redundant 386 ; with AX anyway.) 387 push es 388 push di 389 push bx 390.jump: call 0:0 391 add sp,6 392 mov [cs:PXEStatus],ax 393 394 pushf 395 cli 396 dec word [cs:PXEStackLock] 397 jns .skip2 398 pop bp 399 lss sp,[cs:PXEStack] 400 push bp 401.skip2: 402 popf 403 404 mov bp,sp 405 and ax,ax 406 setnz [bp+32] ; If AX != 0 set CF on return 407 408 ; This clobbers the AX return, but we already saved it into 409 ; the PXEStatus variable. 410 popad 411 412 ; If the call failed, it could return. 413 cmp bx,PXENV_RESTART_TFTP 414 jz .enable_timer 415 cmp bx,PXENV_FILE_EXEC 416 jnz .pop_flags 417 418.enable_timer: 419 call timer_init 420 421.pop_flags: 422 popfd ; Restore flags (incl. IF, DF) 423 ret 424 425; Must be after function def due to NASM bug 426 global PXEEntry 427PXEEntry equ pxenv.jump+1 428 429; 430; The PXEStackLock keeps us from switching stacks if we take an interrupt 431; (which ends up calling pxenv) while we are already on the PXE stack. 432; It will be -1 normally, 0 inside a PXE call, and a positive value 433; inside a *nested* PXE call. 434; 435 section .data16 436 alignb 2 437PXEStackLock dw -1 438 439 section .bss16 440 alignb 2 441PXEStatus resb 2 442 443 section .text16 444; 445; Invoke INT 1Ah on the PXE stack. This is used by the "Plan C" method 446; for finding the PXE entry point. 447; 448 global pxe_int1a 449pxe_int1a: 450 mov [cs:PXEStack],sp 451 mov [cs:PXEStack+2],ss 452 lss sp,[cs:InitStack] 453 454 int 1Ah ; May trash registers 455 456 lss sp,[cs:PXEStack] 457 ret 458 459; 460; Special unload for gPXE: this switches the InitStack from 461; gPXE to the ROM PXE stack. 462; 463%if GPXE 464 global gpxe_unload 465gpxe_unload: 466 mov bx,PXENV_FILE_EXIT_HOOK 467 mov di,pxe_file_exit_hook 468 call pxenv 469 jc .plain 470 471 ; Now we actually need to exit back to gPXE, which will 472 ; give control back to us on the *new* "original stack"... 473 pushfd 474 push ds 475 push es 476 mov [PXEStack],sp 477 mov [PXEStack+2],ss 478 lss sp,[InitStack] 479 pop gs 480 pop fs 481 pop es 482 pop ds 483 popad 484 popfd 485 xor ax,ax 486 retf 487.resume: 488 cli 489 490 ; gPXE will have a stack frame looking much like our 491 ; InitStack, except it has a magic cookie at the top, 492 ; and the segment registers are in reverse order. 493 pop eax 494 pop ax 495 pop bx 496 pop cx 497 pop dx 498 push ax 499 push bx 500 push cx 501 push dx 502 mov [cs:InitStack],sp 503 mov [cs:InitStack+2],ss 504 lss sp,[cs:PXEStack] 505 pop es 506 pop ds 507 popfd 508 509.plain: 510 ret 511 512writestr_early: 513 pm_call pm_writestr 514 ret 515 516pollchar: 517 pm_call pm_pollchar 518 ret 519 520getchar: 521 pm_call pm_getchar 522 ret 523 524 section .data16 525 alignz 4 526pxe_file_exit_hook: 527.status: dw 0 528.offset: dw gpxe_unload.resume 529.seg: dw 0 530%endif 531 532 section .text16 533 534; ----------------------------------------------------------------------------- 535; PXE modules 536; ----------------------------------------------------------------------------- 537 538%if IS_LPXELINUX 539%include "pxeisr.inc" 540%endif 541 542; ----------------------------------------------------------------------------- 543; Common modules 544; ----------------------------------------------------------------------------- 545 546%include "common.inc" ; Universal modules 547 548; ----------------------------------------------------------------------------- 549; Begin data section 550; ----------------------------------------------------------------------------- 551 552 section .data16 553 554 global copyright_str, syslinux_banner 555copyright_str db 'Copyright (C) 1994-' 556 asciidec YEAR 557 db ' H. Peter Anvin et al', CR, LF, 0 558err_bootfailed db CR, LF, 'Boot failed: press a key to retry, or wait for reset...', CR, LF, 0 559bailmsg equ err_bootfailed 560localboot_msg db 'Booting from local disk...', CR, LF, 0 561syslinux_banner db CR, LF, MY_NAME, ' ', VERSION_STR, ' ', MY_TYPE, ' ' 562 db DATE_STR, ' ', 0 563 564; 565; Misc initialized (data) variables 566; 567 section .data16 568 global KeepPXE 569KeepPXE db 0 ; Should PXE be kept around? 570 571 section .bss16 572 global OrigFDCTabPtr 573OrigFDCTabPtr resd 1 ; Keep bios_cleanup_hardware() honest 574