📄 pxelinux.asm
字号:
;; 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 = filename; If successful:; ZF clear; SI = socket pointer; DX:AX = file length in bytes; If unsuccessful; ZF set;searchdir: push es mov ax,ds mov es,ax mov si,di push bp mov bp,sp call allocate_socket jz .error 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 push bx ; [bp-6] - TFTP block mov bx,[bx] push bx ; [bp-8] - TID (local port no) mov eax,[ServerIP] ; Server IP mov [pxe_udp_write_pkt.status],byte 0 mov [pxe_udp_write_pkt.sip],eax mov eax,[UseGW] 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 di,packet_buf mov [pxe_udp_write_pkt.buffer],di mov ax,TFTP_RRQ ; TFTP opcode stosw push si ; Add common prefix mov si,PathPrefix call strcpy dec di pop si call strcpy ; Filename 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,[ServerIP] cmp [pxe_udp_read_pkt.sip],eax ; This is technically not to the TFTP spec? jne .no_packet mov [si+tftp_remoteip],eax ; 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 jnz .get_opt_name ; Not end of packet ; ZF == 1 ; Success, done!.done_pkt: pop si ; Junk pop si ; We want the packet ptr in SI mov eax,[si+tftp_filesize] cmp eax,-1 jz .no_tsize mov edx,eax shr edx,16 ; DX:AX == EAX and eax,eax ; Set ZF depending on file size pop bp ; Junk pop bp ; Junk (retry counter) jz .error_si ; ZF = 1 need to free the socket pop bp pop es ret.no_tsize:.err_reply: ; Option negotiation error. Send ERROR reply. ; ServerIP and gateway are already programmed in mov si,[bp-6] mov ax,[si+tftp_remoteport] mov word [pxe_udp_write_pkt.rport],ax mov word [pxe_udp_write_pkt.buffer],tftp_opt_err mov word [pxe_udp_write_pkt.buffersize],tftp_opt_err_len mov di,pxe_udp_write_pkt mov bx,PXENV_UDP_WRITE call pxenv ; Write an error message and explode mov si,err_oldtftp call writestr jmp kaboom.bailnow: mov word [bp-2],1 ; Immediate error - no retry.failure: pop bx ; Junk pop bx pop si pop ax dec ax ; Retry counter jnz .sendreq ; Try again.error: mov si,bx ; Socket pointer.error_si: ; Socket pointer already in SI call free_socket ; ZF <- 1, SI <- 0 pop bp pop es ret;; allocate_socket: Allocate a local UDP port structure;; If successful:; ZF set; BX = socket pointer; If unsuccessful:; ZF clear;allocate_socket: push cx mov bx,Sockets mov cx,MAX_SOCKETS.check: cmp word [bx], byte 0 je .found add bx,tftp_port_t_size loop .check xor cx,cx ; ZF = 1 pop cx ret ; Allocate a socket number. Socket numbers are made ; guaranteed unique by including the socket slot number ; (inverted, because we use the loop counter cx); add a ; counter value to keep the numbers from being likely to ; get immediately reused. ; ; The NextSocket variable also contains the top two bits ; set. This generates a value in the range 49152 to ; 57343..found: dec cx push ax mov ax,[NextSocket] inc ax and ax,((1 << (13-MAX_SOCKETS_LG2))-1) | 0xC000 mov [NextSocket],ax shl cx,13-MAX_SOCKETS_LG2 add cx,ax ; ZF = 0 xchg ch,cl ; Convert to network byte order mov [bx],cx ; Socket in use pop ax pop cx ret;; Free socket: socket in SI; return SI = 0, ZF = 1 for convenience;free_socket: push es pusha xor ax,ax mov es,ax mov di,si mov cx,tftp_clear_words rep stosw popa pop es xor si,si ret;; strcpy: Copy DS:SI -> ES:DI up to and including a null byte;strcpy: push ax.loop: lodsb stosb and al,al jnz .loop pop ax ret;; writechr: Write a single character in AL to the console without; mangling any registers. This does raw console writes,; since some PXE BIOSes seem to interfere regular console I/O.;writechr: push ds push cs pop ds call write_serial ; write to serial port if needed pushfd pushad mov bh,[BIOS_page] push ax mov ah,03h ; Read cursor position int 10h pop ax cmp al,8 je .bs cmp al,13 je .cr cmp al,10 je .lf push dx mov bh,[BIOS_page] mov bl,07h ; White on black mov cx,1 ; One only mov ah,09h ; Write char and attribute int 10h pop dx inc dl cmp dl,[VidCols] jna .curxyok xor dl,dl.lf: inc dh cmp dh,[VidRows] ja .scroll.curxyok: mov bh,[BIOS_page] mov ah,02h ; Set cursor position int 10h .ret: popad popfd pop ds ret.scroll: dec dh mov bh,[BIOS_page] mov ah,02h int 10h mov ax,0601h ; Scroll up one line mov bh,[ScrollAttribute] xor cx,cx mov dx,[ScreenSize] ; The whole screen int 10h jmp short .ret.cr: xor dl,dl jmp short .curxyok.bs: sub dl,1 jnc .curxyok mov dl,[VidCols] sub dh,1 jnc .curxyok xor dh,dh jmp short .curxyok;; mangle_name: Mangle a filename pointed to by DS:SI into a buffer pointed; to by ES:DI; ends on encountering any whitespace.;; This verifies that a filename is < FILENAME_MAX characters; and doesn't contain whitespace, and zero-pads the output buffer,; so "repe cmpsb" can do a compare.;mangle_name: mov cx,FILENAME_MAX-1.mn_loop: lodsb cmp al,' ' ; If control or space, end jna .mn_end stosb loop .mn_loop.mn_end: inc cx ; At least one null byte xor ax,ax ; Zero-fill name rep stosb ; Doesn't do anything if CX=0 ret ; Done;; unmangle_name: Does the opposite of mangle_name; converts a DOS-mangled; filename to the conventional representation. This is needed; for the BOOT_IMAGE= parameter for the kernel.; NOTE: A 13-byte buffer is mandatory, even if the string is; known to be shorter.;; DS:SI -> input mangled file name; ES:DI -> output buffer;; On return, DI points to the first byte after the output name,; which is set to a null byte.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -