aboutsummaryrefslogtreecommitdiff
path: root/core/pxelinux.asm
diff options
context:
space:
mode:
Diffstat (limited to 'core/pxelinux.asm')
-rw-r--r--core/pxelinux.asm573
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