Home | History | Annotate | Download | only in core
      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 ;   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
     51 trackbufsize	equ 8192
     52 trackbuf	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
     58 InitStack	resd 1
     59 
     60 		section .bss16
     61 		alignb FILENAME_MAX
     62 PXEStack	resd 1			; Saved stack during PXE call
     63 
     64 		alignb 4
     65                 global DHCPMagic, RebootTime, BIOSName
     66 RebootTime	resd 1			; Reboot timeout, if set by option
     67 LocalBootType	resw 1			; Local boot return code
     68 DHCPMagic	resb 1			; PXELINUX magic flags
     69 BIOSName	resw 1			; Dummy variable - always 0
     70 
     71 		section .text16
     72 		global StackBuf
     73 StackBuf	equ STACK_TOP-44	; Base of stack if we use our own
     74 StackHome	equ StackBuf
     75 
     76 		; PXE loads the whole file, but assume it can't be more
     77 		; than (384-31)K in size.
     78 MaxLMA		equ 384*1024
     79 
     80 ;
     81 ; Primary entry point.
     82 ;
     83 bootsec		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 
     93 hcdhcp_magic	dd 0x2983c8ac		; Magic number
     94 hcdhcp_len	dd 7*4			; Size of this structure
     95 hcdhcp_flags	dd 0			; Reserved for the future
     96 		global bdhcp_len, adhcp_len
     97 		; Parameters to be parsed before the ones from PXE
     98 bdhcp_offset	dd 0			; Offset (entered by patcher)
     99 bdhcp_len	dd 0			; Length (entered by patcher)
    100 		; Parameters to be parsed *after* the ones from PXE
    101 adhcp_offset	dd 0			; Offset (entered by patcher)
    102 adhcp_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
    163 ROOT_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 
    206 print_hello:
    207 enter_command:
    208 auto_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
    220 bdhcp_data:	resb MAX_DHCP_OPTS
    221 adhcp_data:	resb MAX_DHCP_OPTS
    222 
    223 		section .textnr
    224 pm_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 
    239 adhcp_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
    261 Kernel_EAX	resd 1
    262 Kernel_SI	resw 1
    263 
    264 		section .bss16
    265 		alignb 4
    266 ThisKbdTo	resd 1			; Temporary holder for KbdTimeout
    267 ThisTotalTo	resd 1			; Temporary holder for TotalTimeout
    268 KernelExtPtr	resw 1			; During search, final null pointer
    269 FuncFlag	resb 1			; Escape sequences received from keyboard
    270 KernelType	resb 1			; Kernel type, from vkernel, if known
    271 		global KernelName
    272 KernelName	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
    282 local_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
    308 kaboom:
    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
    355 pxenv:
    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
    427 PXEEntry	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
    437 PXEStackLock	dw -1
    438 
    439 		section .bss16
    440 		alignb 2
    441 PXEStatus	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
    449 pxe_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
    465 gpxe_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 
    512 writestr_early:
    513 		pm_call pm_writestr
    514 		ret
    515 
    516 pollchar:
    517 		pm_call pm_pollchar
    518 		ret
    519 
    520 getchar:
    521 		pm_call pm_getchar
    522 		ret
    523 
    524 		section .data16
    525 		alignz 4
    526 pxe_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
    555 copyright_str   db 'Copyright (C) 1994-'
    556 		asciidec YEAR
    557 		db ' H. Peter Anvin et al', CR, LF, 0
    558 err_bootfailed	db CR, LF, 'Boot failed: press a key to retry, or wait for reset...', CR, LF, 0
    559 bailmsg		equ err_bootfailed
    560 localboot_msg	db 'Booting from local disk...', CR, LF, 0
    561 syslinux_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
    569 KeepPXE		db 0			; Should PXE be kept around?
    570 
    571 		section .bss16
    572 		global OrigFDCTabPtr
    573 OrigFDCTabPtr	resd 1			; Keep bios_cleanup_hardware() honest
    574