diff options
Diffstat (limited to 'core/pxelinux.asm')
-rw-r--r-- | core/pxelinux.asm | 573 |
1 files changed, 573 insertions, 0 deletions
diff --git a/core/pxelinux.asm b/core/pxelinux.asm new file mode 100644 index 0000000..a2543df --- /dev/null +++ b/core/pxelinux.asm @@ -0,0 +1,573 @@ +; -*- fundamental -*- (asm-mode sucks) +; **************************************************************************** +; +; pxelinux.asm +; +; A program to boot Linux kernels off a TFTP server using the Intel PXE +; network booting API. It is based on the SYSLINUX boot loader for +; MS-DOS floppies. +; +; Copyright 1994-2009 H. Peter Anvin - All Rights Reserved +; Copyright 2009 Intel Corporation; author: H. Peter Anvin +; +; This program is free software; you can redistribute it and/or modify +; it under the terms of the GNU General Public License as published by +; the Free Software Foundation, Inc., 53 Temple Place Ste 330, +; Boston MA 02111-1307, USA; either version 2 of the License, or +; (at your option) any later version; incorporated herein by reference. +; +; **************************************************************************** + +%define IS_PXELINUX 1 +%include "head.inc" +%include "pxe.inc" + +; gPXE extensions support +%define GPXE 1 + +; +; Some semi-configurable constants... change on your own risk. +; +my_id equ pxelinux_id +NULLFILE equ 0 ; Zero byte == null file name +NULLOFFSET equ 0 ; Position in which to look +REBOOT_TIME equ 5*60 ; If failure, time until full reset +%assign HIGHMEM_SLOP 128*1024 ; Avoid this much memory near the top +TFTP_BLOCKSIZE_LG2 equ 9 ; log2(bytes/block) +TFTP_BLOCKSIZE equ (1 << TFTP_BLOCKSIZE_LG2) + +SECTOR_SHIFT equ TFTP_BLOCKSIZE_LG2 +SECTOR_SIZE equ TFTP_BLOCKSIZE + +; --------------------------------------------------------------------------- +; BEGIN CODE +; --------------------------------------------------------------------------- + +; +; Memory below this point is reserved for the BIOS and the MBR +; + section .earlybss + global trackbuf +trackbufsize equ 8192 +trackbuf resb trackbufsize ; Track buffer goes here + ; ends at 2800h + + ; These fields save information from before the time + ; .bss is zeroed... must be in .earlybss + global InitStack +InitStack resd 1 + + section .bss16 + alignb FILENAME_MAX +PXEStack resd 1 ; Saved stack during PXE call + + alignb 4 + global DHCPMagic, RebootTime, BIOSName +RebootTime resd 1 ; Reboot timeout, if set by option +LocalBootType resw 1 ; Local boot return code +DHCPMagic resb 1 ; PXELINUX magic flags +BIOSName resw 1 ; Dummy variable - always 0 + + section .text16 + global StackBuf +StackBuf equ STACK_TOP-44 ; Base of stack if we use our own +StackHome equ StackBuf + + ; PXE loads the whole file, but assume it can't be more + ; than (384-31)K in size. +MaxLMA equ 384*1024 + +; +; Primary entry point. +; +bootsec equ $ +_start: + jmp 0:_start1 ; Canonicalize the address and skip + ; the patch header + +; +; Patch area for adding hardwired DHCP options +; + align 4 + +hcdhcp_magic dd 0x2983c8ac ; Magic number +hcdhcp_len dd 7*4 ; Size of this structure +hcdhcp_flags dd 0 ; Reserved for the future + global bdhcp_len, adhcp_len + ; Parameters to be parsed before the ones from PXE +bdhcp_offset dd 0 ; Offset (entered by patcher) +bdhcp_len dd 0 ; Length (entered by patcher) + ; Parameters to be parsed *after* the ones from PXE +adhcp_offset dd 0 ; Offset (entered by patcher) +adhcp_len dd 0 ; Length (entered by patcher) + +_start1: + pushfd ; Paranoia... in case of return to PXE + pushad ; ... save as much state as possible + push ds + push es + push fs + push gs + + cld ; Copy upwards + xor ax,ax + mov ds,ax + mov es,ax + +%if 0 ; debugging code only... not intended for production use + ; Clobber the stack segment, to test for specific pathologies + mov di,STACK_BASE + mov cx,STACK_LEN >> 1 + mov ax,0xf4f4 + rep stosw + + ; Clobber the tail of the 64K segment, too + extern __bss1_end + mov di,__bss1_end + sub cx,di ; CX = 0 previously + shr cx,1 + rep stosw +%endif + + ; That is all pushed onto the PXE stack. Save the pointer + ; to it and switch to an internal stack. + mov [InitStack],sp + mov [InitStack+2],ss + + lss esp,[BaseStack] + sti ; Stack set up and ready + +; +; Initialize screen (if we're using one) +; +%include "init.inc" + +; +; Tell the user we got this far +; + mov si,syslinux_banner + call writestr_early + + mov si,copyright_str + call writestr_early + +; +; do fs initialize +; + mov eax,ROOT_FS_OPS + xor ebp,ebp + pm_call pm_fs_init + + section .rodata + alignz 4 +ROOT_FS_OPS: + extern pxe_fs_ops + dd pxe_fs_ops + dd 0 + + + section .text16 +; +; Initialize the idle mechanism +; + extern reset_idle + pm_call reset_idle + +; +; Now we're all set to start with our *real* business. +; +; In previous versions I avoided using 32-bit registers because of a +; rumour some BIOSes clobbered the upper half of 32-bit registers at +; random. I figure, though, that if there are any of those still left +; they probably won't be trying to install Linux on them... +; +; The code is still ripe with 16-bitisms, though. Not worth the hassle +; to take'm out. In fact, we may want to put them back if we're going +; to boot ELKS at some point. +; + +; +; Linux kernel loading code is common. However, we need to define +; a couple of helper macros... +; + +; Unload PXE stack +%define HAVE_UNLOAD_PREP +%macro UNLOAD_PREP 0 + pm_call unload_pxe +%endmacro + +; +; Jump to 32-bit ELF space +; + pm_call load_env32 + jmp kaboom ; load_env32() shouldn't return. If it does, then kaboom! + +print_hello: +enter_command: +auto_boot: + pm_call hello + +; +; Save hardwired DHCP options. This is done before the C environment +; is initialized, so it has to be done in assembly. +; +%define MAX_DHCP_OPTS 4096 + bits 32 + + section .savedata + global bdhcp_data, adhcp_data +bdhcp_data: resb MAX_DHCP_OPTS +adhcp_data: resb MAX_DHCP_OPTS + + section .textnr +pm_save_data: + mov eax,MAX_DHCP_OPTS + movzx ecx,word [bdhcp_len] + cmp ecx,eax + jna .oksize + mov ecx,eax + mov [bdhcp_len],ax +.oksize: + mov esi,[bdhcp_offset] + add esi,_start + mov edi,bdhcp_data + add ecx,3 + shr ecx,2 + rep movsd + +adhcp_copy: + movzx ecx,word [adhcp_len] + cmp ecx,eax + jna .oksize + mov ecx,eax + mov [adhcp_len],ax +.oksize: + mov esi,[adhcp_offset] + add esi,_start + mov edi,adhcp_data + add ecx,3 + shr ecx,2 + rep movsd + ret + + bits 16 + +; As core/ui.inc used to be included here in core/pxelinux.asm, and it's no +; longer used, its global variables that were previously used by +; core/pxelinux.asm are now declared here. + section .bss16 + alignb 4 +Kernel_EAX resd 1 +Kernel_SI resw 1 + + section .bss16 + alignb 4 +ThisKbdTo resd 1 ; Temporary holder for KbdTimeout +ThisTotalTo resd 1 ; Temporary holder for TotalTimeout +KernelExtPtr resw 1 ; During search, final null pointer +FuncFlag resb 1 ; Escape sequences received from keyboard +KernelType resb 1 ; Kernel type, from vkernel, if known + global KernelName +KernelName resb FILENAME_MAX ; Mangled name for kernel + + section .text16 +; +; COM32 vestigial data structure +; +%include "com32.inc" + + section .text16 + global local_boot16:function hidden +local_boot16: + mov [LocalBootType],ax + lss sp,[InitStack] + pop gs + pop fs + pop es + pop ds + popad + mov ax,[cs:LocalBootType] + cmp ax,-1 ; localboot -1 == INT 18h + je .int18 + popfd + retf ; Return to PXE +.int18: + popfd + int 18h + jmp 0F000h:0FFF0h + hlt + +; +; kaboom: write a message and bail out. Wait for quite a while, +; or a user keypress, then do a hard reboot. +; +; Note: use BIOS_timer here; we may not have jiffies set up. +; + global kaboom +kaboom: + RESET_STACK_AND_SEGS AX +.patch: mov si,bailmsg + call writestr_early ; Returns with AL = 0 +.drain: call pollchar + jz .drained + call getchar + jmp short .drain +.drained: + mov edi,[RebootTime] + mov al,[DHCPMagic] + and al,09h ; Magic+Timeout + cmp al,09h + je .time_set + mov edi,REBOOT_TIME +.time_set: + mov cx,18 +.wait1: push cx + mov ecx,edi +.wait2: mov dx,[BIOS_timer] +.wait3: call pollchar + jnz .keypress + pm_call __idle + cmp dx,[BIOS_timer] + je .wait3 + loop .wait2,ecx + mov al,'.' + pm_call pm_writechr + pop cx + loop .wait1 +.keypress: + pm_call crlf + mov word [BIOS_magic],0 ; Cold reboot + jmp 0F000h:0FFF0h ; Reset vector address + +; +; pxenv +; +; This is the main PXENV+/!PXE entry point, using the PXENV+ +; calling convention. This is a separate local routine so +; we can hook special things from it if necessary. In particular, +; some PXE stacks seem to not like being invoked from anything but +; the initial stack, so humour it. +; +; While we're at it, save and restore all registers. +; + global pxenv +pxenv: + pushfd + pushad + + ; We may be removing ourselves from memory + cmp bx,PXENV_RESTART_TFTP + jz .disable_timer + cmp bx,PXENV_FILE_EXEC + jnz .store_stack + +.disable_timer: + call bios_timer_cleanup + +.store_stack: + pushf + cli + inc word [cs:PXEStackLock] + jnz .skip1 + pop bp + mov [cs:PXEStack],sp + mov [cs:PXEStack+2],ss + lss sp,[cs:InitStack] + push bp +.skip1: + popf + + ; Pre-clear the Status field + mov word [es:di],cs + + ; This works either for the PXENV+ or the !PXE calling + ; convention, as long as we ignore CF (which is redundant + ; with AX anyway.) + push es + push di + push bx +.jump: call 0:0 + add sp,6 + mov [cs:PXEStatus],ax + + pushf + cli + dec word [cs:PXEStackLock] + jns .skip2 + pop bp + lss sp,[cs:PXEStack] + push bp +.skip2: + popf + + mov bp,sp + and ax,ax + setnz [bp+32] ; If AX != 0 set CF on return + + ; This clobbers the AX return, but we already saved it into + ; the PXEStatus variable. + popad + + ; If the call failed, it could return. + cmp bx,PXENV_RESTART_TFTP + jz .enable_timer + cmp bx,PXENV_FILE_EXEC + jnz .pop_flags + +.enable_timer: + call timer_init + +.pop_flags: + popfd ; Restore flags (incl. IF, DF) + ret + +; Must be after function def due to NASM bug + global PXEEntry +PXEEntry equ pxenv.jump+1 + +; +; The PXEStackLock keeps us from switching stacks if we take an interrupt +; (which ends up calling pxenv) while we are already on the PXE stack. +; It will be -1 normally, 0 inside a PXE call, and a positive value +; inside a *nested* PXE call. +; + section .data16 + alignb 2 +PXEStackLock dw -1 + + section .bss16 + alignb 2 +PXEStatus resb 2 + + section .text16 +; +; Invoke INT 1Ah on the PXE stack. This is used by the "Plan C" method +; for finding the PXE entry point. +; + global pxe_int1a +pxe_int1a: + mov [cs:PXEStack],sp + mov [cs:PXEStack+2],ss + lss sp,[cs:InitStack] + + int 1Ah ; May trash registers + + lss sp,[cs:PXEStack] + ret + +; +; Special unload for gPXE: this switches the InitStack from +; gPXE to the ROM PXE stack. +; +%if GPXE + global gpxe_unload +gpxe_unload: + mov bx,PXENV_FILE_EXIT_HOOK + mov di,pxe_file_exit_hook + call pxenv + jc .plain + + ; Now we actually need to exit back to gPXE, which will + ; give control back to us on the *new* "original stack"... + pushfd + push ds + push es + mov [PXEStack],sp + mov [PXEStack+2],ss + lss sp,[InitStack] + pop gs + pop fs + pop es + pop ds + popad + popfd + xor ax,ax + retf +.resume: + cli + + ; gPXE will have a stack frame looking much like our + ; InitStack, except it has a magic cookie at the top, + ; and the segment registers are in reverse order. + pop eax + pop ax + pop bx + pop cx + pop dx + push ax + push bx + push cx + push dx + mov [cs:InitStack],sp + mov [cs:InitStack+2],ss + lss sp,[cs:PXEStack] + pop es + pop ds + popfd + +.plain: + ret + +writestr_early: + pm_call pm_writestr + ret + +pollchar: + pm_call pm_pollchar + ret + +getchar: + pm_call pm_getchar + ret + + section .data16 + alignz 4 +pxe_file_exit_hook: +.status: dw 0 +.offset: dw gpxe_unload.resume +.seg: dw 0 +%endif + + section .text16 + +; ----------------------------------------------------------------------------- +; PXE modules +; ----------------------------------------------------------------------------- + +%if IS_LPXELINUX +%include "pxeisr.inc" +%endif + +; ----------------------------------------------------------------------------- +; Common modules +; ----------------------------------------------------------------------------- + +%include "common.inc" ; Universal modules + +; ----------------------------------------------------------------------------- +; Begin data section +; ----------------------------------------------------------------------------- + + section .data16 + + global copyright_str, syslinux_banner +copyright_str db 'Copyright (C) 1994-' + asciidec YEAR + db ' H. Peter Anvin et al', CR, LF, 0 +err_bootfailed db CR, LF, 'Boot failed: press a key to retry, or wait for reset...', CR, LF, 0 +bailmsg equ err_bootfailed +localboot_msg db 'Booting from local disk...', CR, LF, 0 +syslinux_banner db CR, LF, MY_NAME, ' ', VERSION_STR, ' ', MY_TYPE, ' ' + db DATE_STR, ' ', 0 + +; +; Misc initialized (data) variables +; + section .data16 + global KeepPXE +KeepPXE db 0 ; Should PXE be kept around? + + section .bss16 + global OrigFDCTabPtr +OrigFDCTabPtr resd 1 ; Keep bios_cleanup_hardware() honest |