📄 pxelinux.asm
字号:
xchg ah,al ; Convert to host byte order ror eax,16 ; (BSWAP doesn't work on 386) xchg ah,al mov si,myipaddr_msg call writestr call writehex8 mov al,' ' call writechr pop si ; DotQuadBuf call writestr call crlf mov si,IPOption call writestr call crlf;; Check to see if we got any PXELINUX-specific DHCP options; in particular,; if we didn't get the magic enable, do not recognize any other options.;check_dhcp_magic: test byte [DHCPMagic], 1 ; If we didn't get the magic enable... jnz .got_magic mov byte [DHCPMagic], 0 ; If not, kill all other options.got_magic: ;; Initialize UDP stack;udp_init: mov eax,[MyIP] mov [pxe_udp_open_pkt.sip],eax mov di,pxe_udp_open_pkt mov bx,PXENV_UDP_OPEN call pxenv jc .failed cmp word [pxe_udp_open_pkt.status], byte 0 je .success.failed: mov si,err_udpinit call writestr jmp kaboom.success:;; Common initialization code;%include "init.inc"%include "cpuinit.inc";; 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.;;; Store standard filename prefix;prefix: test byte [DHCPMagic], 04h ; Did we get a path prefix option jnz .got_prefix mov si,BootFile mov di,PathPrefix cld call strcpy mov cx,di sub cx,PathPrefix+1 std lea si,[di-2] ; Skip final null!.find_alnum: lodsb or al,20h cmp al,'.' ; Count . or - as alphanum je .alnum cmp al,'-' je .alnum cmp al,'0' jb .notalnum cmp al,'9' jbe .alnum cmp al,'a' jb .notalnum cmp al,'z' ja .notalnum.alnum: loop .find_alnum dec si.notalnum: mov byte [si+2],0 ; Zero-terminate after delimiter cld.got_prefix: mov si,tftpprefix_msg call writestr mov si,PathPrefix call writestr call crlf;; Load configuration file;find_config:;; Begin looking for configuration file;config_scan: mov di,ConfigServer xor eax,eax stosd ; The config file is always from the server test byte [DHCPMagic], 02h jz .no_option ; We got a DHCP option, try it first mov si,trying_msg call writestr ; mov di,ConfigName ; - already the case mov si,di call writestr call crlf mov di,ConfigServer call open jnz .success.no_option: mov di,ConfigName mov si,cfgprefix mov cx,cfgprefix_len rep movsb ; Try loading by MAC address ; Have to guess config file name push di mov si,MACStr mov cx,(3*17+1)/2 rep movsw mov si,trying_msg call writestr mov di,ConfigName mov si,di call writestr call crlf mov di,ConfigServer call open pop di jnz .success.scan_ip: mov cx,8 mov eax,[MyIP] xchg ah,al ; Convert to host byte order ror eax,16 xchg ah,al.hexify_loop: rol eax,4 push eax and al,0Fh cmp al,10 jae .high.low: add al,'0' jmp short .char.high: add al,'A'-10.char: stosb pop eax loop .hexify_loop mov cx,9 ; Up to 9 attempts.tryagain: mov byte [di],0 cmp cx,byte 1 jne .not_default pusha mov si,default_str mov cx,default_len rep movsb ; Copy "default" string popa.not_default: pusha mov si,trying_msg call writestr mov di,ConfigName mov si,di call writestr call crlf mov di,ConfigServer call open popa jnz .success dec di loop .tryagain jmp no_config_file.success:;; Now we have the config file open. Parse the config file and; run the user interface.;%include "ui.inc";; Linux kernel loading code is common. However, we need to define; a couple of helper macros...;; Handle "ipappend" option%define HAVE_SPECIAL_APPEND%macro SPECIAL_APPEND 0 test byte [IPAppend],01h ; ip= jz .noipappend1 mov si,IPOption mov cx,[IPOptionLen] rep movsb mov al,' ' stosb.noipappend1: test byte [IPAppend],02h jz .noipappend2 mov si,BOOTIFStr call strcpy mov byte [es:di-1],' ' ; Replace null with space.noipappend2:%endmacro; Unload PXE stack%define HAVE_UNLOAD_PREP%macro UNLOAD_PREP 0 call unload_pxe%endmacro%include "runkernel.inc";; COMBOOT-loading code;%include "comboot.inc"%include "com32.inc"%include "cmdline.inc";; Boot sector loading code;%include "bootsect.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 ; Restore the environment we were called with lss sp,[InitStack] pop gs pop fs pop es pop ds popad mov ax,[cs:LocalBootType] popfd retf ; Return to PXE;; Abort loading code;%include "abort.inc";; kaboom: write a message and bail out. Wait for quite a while,; or a user keypress, then do a hard reboot.;kaboom: mov ax,cs mov es,ax mov ds,ax lss esp,[BaseStack] sti.patch: mov si,bailmsg call writestr ; 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 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;; memory_scan_for_pxe_struct:;; If none of the standard methods find the !PXE structure, look for it; by scanning memory.;; On exit, if found:; CF = 0, ES:BX -> !PXE structure; Otherwise CF = 1, all registers saved; memory_scan_for_pxe_struct: push ds pusha mov ax,cs mov ds,ax mov si,trymempxe_msg call writestr mov ax,[BIOS_fbm] ; Starting segment shl ax,(10-4) ; Kilobytes -> paragraphs; mov ax,01000h ; Start to look here dec ax ; To skip inc ax.mismatch: inc ax cmp ax,0A000h ; End of memory jae .not_found call writehex4 mov si,fourbs_msg call writestr mov es,ax mov edx,[es:0] cmp edx,'!PXE' jne .mismatch movzx cx,byte [es:4] ; Length of structure cmp cl,08h ; Minimum length jb .mismatch push ax xor ax,ax xor si,si.checksum: es lodsb add ah,al loop .checksum pop ax jnz .mismatch ; Checksum must == 0.found: mov bp,sp xor bx,bx mov [bp+8],bx ; Save BX into stack frame (will be == 0) mov ax,es call writehex4 call crlf popa pop ds clc ret.not_found: mov si,notfound_msg call writestr popa pop ds stc ret;; memory_scan_for_pxenv_struct:;; If none of the standard methods find the PXENV+ structure, look for it; by scanning memory.;; On exit, if found:; CF = 0, ES:BX -> PXENV+ structure; Otherwise CF = 1, all registers saved; memory_scan_for_pxenv_struct: pusha mov si,trymempxenv_msg call writestr; mov ax,[BIOS_fbm] ; Starting segment; shl ax,(10-4) ; Kilobytes -> paragraphs mov ax,01000h ; Start to look here dec ax ; To skip inc ax.mismatch: inc ax cmp ax,0A000h ; End of memory jae .not_found mov es,ax mov edx,[es:0] cmp edx,'PXEN' jne .mismatch mov dx,[es:4] cmp dx,'V+' jne .mismatch movzx cx,byte [es:8] ; Length of structure cmp cl,26h ; Minimum length jb .mismatch xor ax,ax xor si,si.checksum: es lodsb add ah,al loop .checksum and ah,ah jnz .mismatch ; Checksum must == 0.found: mov bp,sp mov [bp+8],bx ; Save BX into stack frame mov ax,bx call writehex4 call crlf clc ret.not_found: mov si,notfound_msg call writestr popad stc ret;; searchdir:;; Open a TFTP connection to the server ;; On entry:; DS:DI = mangled filename; If successful:; ZF clear; SI = socket pointer; DX:AX = file length in bytes; If unsuccessful; ZF set;searchdir: push es push bx push cx mov ax,ds mov es,ax mov si,di push bp mov bp,sp call allocate_socket jz .ret mov ax,PKT_RETRY ; Retry counter mov word [PktTimeout],PKT_TIMEOUT ; Initial timeout .sendreq: push ax ; [bp-2] - Retry counter push si ; [bp-4] - File name mov di,packet_buf mov [pxe_udp_write_pkt.buffer],di mov ax,TFTP_RRQ ; TFTP opcode stosw lodsd ; EAX <- server override (if any) and eax,eax jnz .noprefix ; No prefix, and we have the server push si ; Add common prefix mov si,PathPrefix call strcpy dec di pop si mov eax,[ServerIP] ; Get default server.noprefix: call strcpy ; Filename mov [bx+tftp_remoteip],eax push bx ; [bp-6] - TFTP block mov bx,[bx] push bx ; [bp-8] - TID (local port no) mov [pxe_udp_write_pkt.status],byte 0 mov [pxe_udp_write_pkt.sip],eax ; Now figure out the gateway xor eax,[MyIP] and eax,[Netmask] jz .nogwneeded mov eax,[Gateway].nogwneeded: mov [pxe_udp_write_pkt.gip],eax mov [pxe_udp_write_pkt.lport],bx mov ax,[ServerPort] mov [pxe_udp_write_pkt.rport],ax mov si,tftp_tail mov cx,tftp_tail_len rep movsb sub di,packet_buf ; Get packet size mov [pxe_udp_write_pkt.buffersize],di mov di,pxe_udp_write_pkt mov bx,PXENV_UDP_WRITE call pxenv jc .failure cmp word [pxe_udp_write_pkt.status],byte 0 jne .failure ; ; Danger, Will Robinson! We need to support timeout ; and retry lest we just lost a packet... ; ; Packet transmitted OK, now we need to receive.getpacket: push word [PktTimeout] ; [bp-10] push word [BIOS_timer] ; [bp-12].pkt_loop: mov bx,[bp-8] ; TID mov di,packet_buf mov word [pxe_udp_read_pkt.status],0 mov [pxe_udp_read_pkt.buffer],di mov [pxe_udp_read_pkt.buffer+2],ds mov word [pxe_udp_read_pkt.buffersize],packet_buf_size mov eax,[MyIP] mov [pxe_udp_read_pkt.dip],eax mov [pxe_udp_read_pkt.lport],bx mov di,pxe_udp_read_pkt mov bx,PXENV_UDP_READ call pxenv and ax,ax jz .got_packet ; Wait for packet.no_packet: mov dx,[BIOS_timer] cmp dx,[bp-12] je .pkt_loop mov [bp-12],dx dec word [bp-10] ; Timeout jnz .pkt_loop pop ax ; Adjust stack pop ax shl word [PktTimeout],1 ; Exponential backoff jmp .failure .got_packet: mov si,[bp-6] ; TFTP pointer mov bx,[bp-8] ; TID mov eax,[si+tftp_remoteip] cmp [pxe_udp_read_pkt.sip],eax ; This is technically not to the TFTP spec? jne .no_packet ; Got packet - reset timeout mov word [PktTimeout],PKT_TIMEOUT pop ax ; Adjust stack pop ax mov ax,[pxe_udp_read_pkt.rport] mov [si+tftp_remoteport],ax ; filesize <- -1 == unknown mov dword [si+tftp_filesize], -1 ; Default blksize unless blksize option negotiated mov word [si+tftp_blksize], TFTP_BLOCKSIZE mov cx,[pxe_udp_read_pkt.buffersize] sub cx,2 ; CX <- bytes after opcode jb .failure ; Garbled reply mov si,packet_buf lodsw cmp ax, TFTP_ERROR je .bailnow ; ERROR reply: don't try again cmp ax, TFTP_OACK jne .no_tsize ; Now we need to parse the OACK packet to get the transfer ; size. SI -> first byte of options; CX -> byte count.parse_oack: jcxz .no_tsize ; No options acked.get_opt_name: mov di,si mov bx,si.opt_name_loop: lodsb and al,al jz .got_opt_name or al,20h ; Convert to lowercase stosb loop .opt_name_loop ; We ran out, and no final null jmp .err_reply.got_opt_name: ; si -> option value dec cx ; bytes left in pkt jz .err_reply ; Option w/o value ; Parse option pointed to by bx; guaranteed to be ; null-terminated. push cx push si mov si,bx ; -> option name mov bx,tftp_opt_table mov cx,tftp_opts.opt_loop: push cx push si mov di,[bx] ; Option pointer mov cx,[bx+2] ; Option len repe cmpsb pop si pop cx je .get_value ; OK, known option add bx,6 loop .opt_loop pop si pop cx jmp .err_reply ; Non-negotiated option returned.get_value: pop si ; si -> option value pop cx ; cx -> bytes left in pkt mov bx,[bx+4] ; Pointer to data target add bx,[bp-6] ; TFTP socket pointer xor eax,eax xor edx,edx.value_loop: lodsb and al,al jz .got_value sub al,'0' cmp al, 9 ja .err_reply ; Not a decimal digit imul edx,10 add edx,eax mov [bx],edx loop .value_loop ; Ran out before final null, accept anyway jmp short .done_pkt.got_value: dec cx
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -