diff options
Diffstat (limited to 'contrib/syslinux-4.02/core/pxelinux.asm')
-rw-r--r-- | contrib/syslinux-4.02/core/pxelinux.asm | 544 |
1 files changed, 544 insertions, 0 deletions
diff --git a/contrib/syslinux-4.02/core/pxelinux.asm b/contrib/syslinux-4.02/core/pxelinux.asm new file mode 100644 index 0000000..8084ac9 --- /dev/null +++ b/contrib/syslinux-4.02/core/pxelinux.asm @@ -0,0 +1,544 @@ +; -*- 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 + +; +; The following structure is used for "virtual kernels"; i.e. LILO-style +; option labels. The options we permit here are `kernel' and `append +; Since there is no room in the bottom 64K for all of these, we +; stick them in high memory and copy them down before we need them. +; + struc vkernel +vk_vname: resb FILENAME_MAX ; Virtual name **MUST BE FIRST!** +vk_rname: resb FILENAME_MAX ; Real name +vk_ipappend: resb 1 ; "IPAPPEND" flag +vk_type: resb 1 ; Type of file +vk_appendlen: resw 1 + alignb 4 +vk_append: resb max_cmd_len+1 ; Command line + alignb 4 +vk_end: equ $ ; Should be <= vk_size + endstruc + + +; --------------------------------------------------------------------------- +; 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, APIVer +RebootTime resd 1 ; Reboot timeout, if set by option +StrucPtr resw 2 ; Pointer to PXENV+ or !PXE structure +APIVer resw 1 ; PXE API version found +LocalBootType resw 1 ; Local boot return code +DHCPMagic resb 1 ; PXELINUX magic flags + + section .text16 +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 + ; 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 +; +; Move the hardwired DHCP options (if present) to a safe place... +; +bdhcp_copy: + mov cx,[bdhcp_len] + mov ax,trackbufsize/2 + jcxz .none + cmp cx,ax + jbe .oksize + mov cx,ax + mov [bdhcp_len],ax +.oksize: + mov eax,[bdhcp_offset] + add eax,_start + mov si,ax + and si,000Fh + shr eax,4 + push ds + mov ds,ax + mov di,trackbuf + add cx,3 + shr cx,2 + rep movsd + pop ds +.none: + +adhcp_copy: + mov cx,[adhcp_len] + mov ax,trackbufsize/2 + jcxz .none + cmp cx,ax + jbe .oksize + mov cx,ax + mov [adhcp_len],ax +.oksize: + mov eax,[adhcp_offset] + add eax,_start + mov si,ax + and si,000Fh + shr eax,4 + push ds + mov ds,ax + mov di,trackbuf+trackbufsize/2 + add cx,3 + shr cx,2 + rep movsd + pop ds +.none: + +; +; 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 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 +; + call reset_idle + +; +; Now we're all set to start with our *real* business. First load the +; configuration file (if any) and parse it. +; +; 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. +; + +; +; Load configuration file +; + pm_call load_config + +; +; 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 + +; +; Now we have the config file open. Parse the config file and +; run the user interface. +; +%include "ui.inc" + +; +; Boot to the local disk by returning the appropriate PXE magic. +; AX contains the appropriate return code. +; +local_boot: + push cs + pop ds + mov [LocalBootType],ax + call vgaclearmode + mov si,localboot_msg + call writestr_early + ; Restore the environment we were called with + pm_call reset_pxe + call cleanup_hardware + 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 + call do_idle + cmp dx,[BIOS_timer] + je .wait3 + loop .wait2,ecx + mov al,'.' + call writechr + pop cx + loop .wait1 +.keypress: + 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 + + mov [cs:PXEStack],sp + mov [cs:PXEStack+2],ss + lss sp,[cs:InitStack] + + ; 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 + + lss sp,[cs:PXEStack] + + 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 + popfd ; Restore flags (incl. IF, DF) + ret + +; Must be after function def due to NASM bug + global PXEEntry +PXEEntry equ pxenv.jump+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 + + section .data16 + alignz 4 +pxe_file_exit_hook: +.status: dw 0 +.offset: dw gpxe_unload.resume +.seg: dw 0 +%endif + + section .text16 + +; ----------------------------------------------------------------------------- +; Common modules +; ----------------------------------------------------------------------------- + +%include "common.inc" ; Universal modules +%include "writestr.inc" ; String output +writestr_early equ writestr +%include "writehex.inc" ; Hexadecimal output +%include "rawcon.inc" ; Console I/O w/o using the console functions + +; ----------------------------------------------------------------------------- +; Begin data section +; ----------------------------------------------------------------------------- + + section .data16 + +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, ' ', DATE_STR, ' ', 0 + +; +; Config file keyword table +; +%include "keywords.inc" + +; +; Extensions to search for (in *forward* order). +; (.bs and .bss16 are disabled for PXELINUX, since they are not supported) +; + alignz 4 +exten_table: db '.cbt' ; COMBOOT (specific) + db '.0', 0, 0 ; PXE bootstrap program + db '.com' ; COMBOOT (same as DOS) + db '.c32' ; COM32 +exten_table_end: + dd 0, 0 ; Need 8 null bytes here + +; +; Misc initialized (data) variables +; + section .data16 + global KeepPXE +KeepPXE db 0 ; Should PXE be kept around? + +; +; IP information. Note that the field are in the same order as the +; Linux kernel expects in the ip= option. +; + section .bss16 + alignb 4 + global IPInfo +IPInfo: +.IPv4 resd 1 ; IPv4 information +.MyIP resd 1 ; My IP address +.ServerIP resd 1 +.GatewayIP resd 1 +.Netmask resd 1 |