📄 pxelinux.asm
字号:
; -*- fundamental -*- (asm-mode sucks); $Id$; ****************************************************************************;; 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 (C) 1994-2005 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 "macros.inc"%include "config.inc"%include "kernel.inc"%include "bios.inc"%include "tracers.inc"%include "pxe.inc"%include "layout.inc";; Some semi-configurable constants... change on your own risk.;my_id equ pxelinux_idFILENAME_MAX_LG2 equ 7 ; log2(Max filename size Including final null)FILENAME_MAX equ (1 << FILENAME_MAX_LG2)NULLFILE equ 0 ; Zero byte == null file nameNULLOFFSET equ 4 ; Position in which to lookREBOOT_TIME equ 5*60 ; If failure, time until full reset%assign HIGHMEM_SLOP 128*1024 ; Avoid this much memory near the topMAX_OPEN_LG2 equ 5 ; log2(Max number of open sockets)MAX_OPEN equ (1 << MAX_OPEN_LG2)PKTBUF_SIZE equ (65536/MAX_OPEN) ; Per-socket packet buffer sizeTFTP_PORT equ htons(69) ; Default TFTP port PKT_RETRY equ 6 ; Packet transmit retry countPKT_TIMEOUT equ 12 ; Initial timeout, timer ticks @ 55 ms; Desired TFTP block size; For Ethernet MTU is normally 1500. Unfortunately there seems to; be a fair number of networks with "substandard" MTUs which break.; The code assumes TFTP_LARGEBLK <= 2K.TFTP_MTU equ 1472TFTP_LARGEBLK equ (TFTP_MTU-20-8-4) ; MTU - IP hdr - UDP hdr - TFTP hdr; Standard TFTP block sizeTFTP_BLOCKSIZE_LG2 equ 9 ; log2(bytes/block)TFTP_BLOCKSIZE equ (1 << TFTP_BLOCKSIZE_LG2)%assign USE_PXE_PROVIDED_STACK 1 ; Use stack provided by PXE?SECTOR_SHIFT equ TFTP_BLOCKSIZE_LG2SECTOR_SIZE equ TFTP_BLOCKSIZE;; This is what we need to do when idle;%define HAVE_IDLE 1 ; idle is not a noop%macro RESET_IDLE 0 call reset_idle%endmacro%macro DO_IDLE 0 call check_for_arp%endmacro;; TFTP operation codes;TFTP_RRQ equ htons(1) ; Read requestTFTP_WRQ equ htons(2) ; Write requestTFTP_DATA equ htons(3) ; Data packetTFTP_ACK equ htons(4) ; ACK packetTFTP_ERROR equ htons(5) ; ERROR packetTFTP_OACK equ htons(6) ; OACK packet;; TFTP error codes;TFTP_EUNDEF equ htons(0) ; Unspecified errorTFTP_ENOTFOUND equ htons(1) ; File not foundTFTP_EACCESS equ htons(2) ; Access violationTFTP_ENOSPACE equ htons(3) ; Disk fullTFTP_EBADOP equ htons(4) ; Invalid TFTP operationTFTP_EBADID equ htons(5) ; Unknown transferTFTP_EEXISTS equ htons(6) ; File existsTFTP_ENOUSER equ htons(7) ; No such userTFTP_EOPTNEG equ htons(8) ; Option negotiation failure;; 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 at vk_seg:0000 and copy them down before we need them.; struc vkernelvk_vname: resb FILENAME_MAX ; Virtual name **MUST BE FIRST!**vk_rname: resb FILENAME_MAX ; Real namevk_ipappend: resb 1 ; "IPAPPEND" flag resb 1 ; Padvk_appendlen: resw 1 alignb 4vk_append: resb max_cmd_len+1 ; Command line alignb 4vk_end: equ $ ; Should be <= vk_size endstruc;; Segment assignments in the bottom 640K; 0000h - main code/data segment (and BIOS segment);real_mode_seg equ 4000hvk_seg equ 3000h ; Virtual kernelsxfer_buf_seg equ 2000h ; Bounce buffer for I/O to high mempktbuf_seg equ 1000h ; Packet buffers segmentscomboot_seg equ real_mode_seg ; COMBOOT image loading zone;; BOOTP/DHCP packet pattern; struc bootp_t bootp:.opcode resb 1 ; BOOTP/DHCP "opcode".hardware resb 1 ; ARP hardware type.hardlen resb 1 ; Hardware address length.gatehops resb 1 ; Used by forwarders.ident resd 1 ; Transaction ID.seconds resw 1 ; Seconds elapsed.flags resw 1 ; Broadcast flags.cip resd 1 ; Client IP.yip resd 1 ; "Your" IP.sip resd 1 ; Next server IP.gip resd 1 ; Relay agent IP.macaddr resb 16 ; Client MAC address.sname resb 64 ; Server name (optional).bootfile resb 128 ; Boot file name.option_magic resd 1 ; Vendor option magic cookie.options resb 1260 ; Vendor options endstruc BOOTP_OPTION_MAGIC equ htonl(0x63825363) ; See RFC 2132;; TFTP connection data structure. Each one of these corresponds to a local; UDP port. The size of this structure must be a power of 2.; HBO = host byte order; NBO = network byte order; (*) = written by options negotiation code, must be dword sized; struc open_file_ttftp_localport resw 1 ; Local port number (0 = not in use)tftp_remoteport resw 1 ; Remote port numbertftp_remoteip resd 1 ; Remote IP addresstftp_filepos resd 1 ; Bytes downloaded (including buffer)tftp_filesize resd 1 ; Total file size(*)tftp_blksize resd 1 ; Block size for this connection(*)tftp_bytesleft resw 1 ; Unclaimed data bytestftp_lastpkt resw 1 ; Sequence number of last packet (NBO)tftp_dataptr resw 1 ; Pointer to available data resw 2 ; Currently unusued ; At end since it should not be zeroed on socked closetftp_pktbuf resw 1 ; Packet buffer offset endstruc%ifndef DEPEND%if (open_file_t_size & (open_file_t_size-1))%error "open_file_t is not a power of 2"%endif%endif; ---------------------------------------------------------------------------; BEGIN CODE; ---------------------------------------------------------------------------;; Memory below this point is reserved for the BIOS and the MBR; section .earlybsstrackbufsize equ 8192trackbuf resb trackbufsize ; Track buffer goes heregetcbuf resb trackbufsize ; ends at 4800h ; Put some large buffers here, before RBFG_brainfuck, ; where we can still carefully control the address ; assignments... alignb open_file_t_sizeFiles resb MAX_OPEN*open_file_t_size alignb FILENAME_MAXBootFile resb 256 ; Boot file from DHCP packetConfigServer resd 1 ; Null prefix for mangled config nameConfigName resb 256-4 ; Configuration file from DHCP optionPathPrefix resb 256 ; Path prefix derived from boot fileDotQuadBuf resb 16 ; Buffer for dotted-quad IP addressIPOption resb 80 ; ip= option bufferInitStack resd 1 ; Pointer to reset stack (SS:SP)PXEStack resd 1 ; Saved stack during PXE call; Warning here: RBFG build 22 randomly overwrites memory location; [0x5680,0x576c), possibly more. It seems that it gets confused and; screws up the pointer to its own internal packet buffer and starts; writing a received ARP packet into low memory.RBFG_brainfuck resb 0E00h section .latebss alignb 4RebootTime resd 1 ; Reboot timeout, if set by optionStrucPtr resd 1 ; Pointer to PXENV+ or !PXE structureAPIVer resw 1 ; PXE API version foundIPOptionLen resw 1 ; Length of IPOptionIdleTimer resw 1 ; Time to check for ARP?LocalBootType resw 1 ; Local boot return codePktTimeout resw 1 ; Timeout for current packetRealBaseMem resw 1 ; Amount of DOS memory after freeingOverLoad resb 1 ; Set if DHCP packet uses "overloading"; The relative position of these fields matter!MACLen resb 1 ; MAC address lenMACType resb 1 ; MAC address typeMAC resb 16 ; Actual MAC addressBOOTIFStr resb 7 ; Space for "BOOTIF="MACStr resb 3*17 ; MAC address as a string;; PXE packets which don't need static initialization; alignb 4pxe_unload_stack_pkt:.status: resw 1 ; Status.reserved: resw 10 ; Reservedpxe_unload_stack_pkt_len equ $-pxe_unload_stack_pkt alignb 16 ; BOOTP/DHCP packet buffer alignb 16packet_buf resb 2048 ; Transfer packetpacket_buf_size equ $-packet_buf;; Constants for the xfer_buf_seg;; The xfer_buf_seg is also used to store message file buffers. We; need two trackbuffers (text and graphics), plus a work buffer; for the graphics decompressor.;xbs_textbuf equ 0 ; Also hard-coded, do not changexbs_vgabuf equ trackbufsizexbs_vgatmpbuf equ 2*trackbufsize section .text ; ; PXELINUX needs more BSS than the other derivatives; ; therefore we relocate it from 7C00h on startup. ;StackBuf equ $ ; Base of stack if we use our own;; Primary entry point.;bootsec equ $_start: pushfd ; Paranoia... in case of return to PXE pushad ; ... save as much state as possible push ds push es push fs push gs xor ax,ax mov ds,ax mov es,ax %ifndef DEPEND%if TEXT_START != 0x7c00 ; This is uglier than it should be, but works around ; some NASM 0.98.38 bugs. mov di,section..bcopy32.start add di,__bcopy_size-4 lea si,[di-(TEXT_START-7C00h)] lea cx,[di-(TEXT_START-4)] shr cx,2 std ; Overlapping areas, copy backwards rep movsd%endif%endif jmp 0:_start1 ; Canonicalize address_start1: mov bp,sp les bx,[bp+48] ; ES:BX -> !PXE or PXENV+ structure ; 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%if USE_PXE_PROVIDED_STACK ; Apparently some platforms go bonkers if we ; set up our own stack... mov [BaseStack],sp mov [BaseStack+4],ss%endif cli ; Paranoia lss esp,[BaseStack] sti ; Stack set up and ready cld ; Copy upwards;; Initialize screen (if we're using one); ; Now set up screen parameters call adjust_screen ; Wipe the F-key area mov al,NULLFILE mov di,FKeyName mov cx,10*(1 << FILENAME_MAX_LG2) push es ; Save ES -> PXE structure push ds ; ES <- DS pop es rep stosb pop es ; Restore ES;; Tell the user we got this far; mov si,syslinux_banner call writestr mov si,copyright_str call writestr;; Assume API version 2.1, in case we find the !PXE structure without; finding the PXENV+ structure. This should really look at the Base; Code ROM ID structure in have_pxe, but this is adequate for now --; if we have !PXE, we have to be 2.1 or higher, and we don't care; about higher versions than that.; mov word [APIVer],0201h;; Now we need to find the !PXE structure. It's *supposed* to be pointed; to by SS:[SP+4], but support INT 1Ah, AX=5650h method as well.; FIX: ES:BX should point to the PXENV+ structure on entry as well.; We should make that the second test, and not trash ES:BX...; cmp dword [es:bx], '!PXE' je have_pxe ; Uh-oh, not there... try plan B mov ax, 5650h%if USE_PXE_PROVIDED_STACK == 0 lss sp,[InitStack]%endif int 1Ah ; May trash regs%if USE_PXE_PROVIDED_STACK == 0 lss esp,[BaseStack]%endif jc no_pxe cmp ax,564Eh jne no_pxe ; Okay, that gave us the PXENV+ structure, find !PXE ; structure from that (if available) cmp dword [es:bx], 'PXEN' jne no_pxe cmp word [es:bx+4], 'V+' je have_pxenv ; Nothing there either. Last-ditch: scan memory call memory_scan_for_pxe_struct ; !PXE scan jnc have_pxe call memory_scan_for_pxenv_struct ; PXENV+ scan jnc have_pxenvno_pxe: mov si,err_nopxe call writestr jmp kaboomhave_pxenv: mov [StrucPtr],bx mov [StrucPtr+2],es mov si,found_pxenv call writestr mov si,apiver_str call writestr mov ax,[es:bx+6] mov [APIVer],ax call writehex4 call crlf cmp ax,0201h ; API version 2.1 or higher jb old_api mov si,bx mov ax,es les bx,[es:bx+28h] ; !PXE structure pointer cmp dword [es:bx],'!PXE' je have_pxe ; Nope, !PXE structure missing despite API 2.1+, or at least ; the pointer is missing. Do a last-ditch attempt to find it. call memory_scan_for_pxe_struct jnc have_pxe ; Otherwise, no dice, use PXENV+ structure mov bx,si mov es,axold_api: ; Need to use a PXENV+ structure mov si,using_pxenv_msg call writestr mov eax,[es:bx+0Ah] ; PXE RM API mov [PXENVEntry],eax mov si,undi_data_msg call writestr mov ax,[es:bx+20h] call writehex4 call crlf mov si,undi_data_len_msg call writestr mov ax,[es:bx+22h] call writehex4 call crlf mov si,undi_code_msg call writestr mov ax,[es:bx+24h] call writehex4 call crlf mov si,undi_code_len_msg call writestr mov ax,[es:bx+26h] call writehex4 call crlf ; Compute base memory size from PXENV+ structure xor esi,esi movzx eax,word [es:bx+20h] ; UNDI data seg cmp ax,[es:bx+24h] ; UNDI code seg ja .use_data mov ax,[es:bx+24h] mov si,[es:bx+26h] jmp short .combine.use_data: mov si,[es:bx+22h].combine: shl eax,4 add eax,esi shr eax,10 ; Convert to kilobytes mov [RealBaseMem],ax mov si,pxenventry_msg call writestr mov ax,[PXENVEntry+2] call writehex4 mov al,':' call writechr mov ax,[PXENVEntry] call writehex4 call crlf jmp have_entrypointhave_pxe: mov [StrucPtr],bx mov [StrucPtr+2],es mov eax,[es:bx+10h] mov [PXEEntry],eax mov si,undi_data_msg call writestr mov eax,[es:bx+2Ah] call writehex8 call crlf mov si,undi_data_len_msg call writestr mov ax,[es:bx+2Eh] call writehex4 call crlf mov si,undi_code_msg call writestr mov ax,[es:bx+32h] call writehex8 call crlf mov si,undi_code_len_msg call writestr mov ax,[es:bx+36h] call writehex4 call crlf ; Compute base memory size from !PXE structure xor esi,esi mov eax,[es:bx+2Ah] cmp eax,[es:bx+32h] ja .use_data mov eax,[es:bx+32h] mov si,[es:bx+36h] jmp short .combine.use_data: mov si,[es:bx+2Eh].combine: add eax,esi shr eax,10 mov [RealBaseMem],ax mov si,pxeentry_msg call writestr mov ax,[PXEEntry+2] call writehex4 mov al,':' call writechr mov ax,[PXEEntry] call writehex4 call crlfhave_entrypoint: push cs pop es ; Restore CS == DS == ES;; Network-specific initialization; xor ax,ax mov [LocalDomain],al ; No LocalDomain received;; Now attempt to get the BOOTP/DHCP packet that brought us life (and an IP; address). This lives in the DHCPACK packet (query info 2).;query_bootp: mov di,pxe_bootp_query_pkt_2 mov bx,PXENV_GET_CACHED_INFO call pxenv push word [pxe_bootp_query_pkt_2.status] jc .pxe_err1 cmp ax,byte 0 je .pxe_ok.pxe_err1: mov di,pxe_bootp_size_query_pkt mov bx,PXENV_GET_CACHED_INFO call pxenv jc .pxe_err.pxe_size: mov ax,[pxe_bootp_size_query_pkt.buffersize] call writehex4 call crlf.pxe_err: mov si,err_pxefailed call writestr call writehex4 mov al, ' ' call writechr pop ax ; Status call writehex4 call crlf jmp kaboom ; We're dead.pxe_ok: pop cx ; Forget status mov cx,[pxe_bootp_query_pkt_2.buffersize] call parse_dhcp ; Parse DHCP packet;; Save away MAC address (assume this is in query info 2. If this; turns out to be problematic it might be better getting it from; the query info 1 packet.);.save_mac: movzx cx,byte [trackbuf+bootp.hardlen] mov [MACLen],cl mov al,[trackbuf+bootp.hardware] mov [MACType],al mov si,trackbuf+bootp.macaddr mov di,MAC push cx rep movsb mov cx,MAC+16 sub cx,di xor ax,ax rep stosb mov si,bootif_str mov di,BOOTIFStr mov cx,bootif_str_len rep movsb pop cx mov si,MACType inc cx mov bx,hextbl_lower.hexify_mac: lodsb mov ah,al shr al,4 xlatb stosb mov al,ah and al,0Fh xlatb stosb mov al,'-' stosb loop .hexify_mac mov [di-1],byte 0 ; Null-terminate and strip final colon;; Now, get the boot file and other info. This lives in the CACHED_REPLY; packet (query info 3).; mov [pxe_bootp_size_query_pkt.packettype], byte 3 mov di,pxe_bootp_query_pkt_3 mov bx,PXENV_GET_CACHED_INFO call pxenv push word [pxe_bootp_query_pkt_3.status] jc .pxe_err1 cmp ax,byte 0 jne .pxe_err1 ; Packet loaded OK... pop cx ; Forget status mov cx,[pxe_bootp_query_pkt_3.buffersize] call parse_dhcp ; Parse DHCP packet;; Generate ip= option; call genipopt;; Print IP address; mov eax,[MyIP] mov di,DotQuadBuf push di call gendotquad ; This takes network byte order input
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -