/* Some tools to handle 16:16 far pointers in memory */
struct __far_ptr {
- uint32_t __ptr;
+ union {
+ uint32_t ptr;
+ struct {
+ uint16_t offs, seg;
+ };
+ };
} __attribute__ ((packed));
typedef struct __far_ptr far_ptr_t;
static inline void *GET_PTR(far_ptr_t __fptr)
{
- return MK_PTR(__fptr.__ptr >> 16, __fptr.__ptr);
+ return MK_PTR(__fptr.seg, __fptr.offs);
}
static inline far_ptr_t FAR_PTR(void *__ptr)
{
far_ptr_t __fptr;
- __fptr.__ptr = (SEG(__ptr) << 16) + OFFS(__ptr);
+ __fptr.offs = OFFS(__ptr);
+ __fptr.seg = SEG(__ptr);
return __fptr;
}
CODEPAGE = cp865
# The targets to build in this directory...
-BTARGET = kwdhash.gen \
- extlinux.bin extlinux.bss extlinux.sys \
+BTARGET = kwdhash.gen pxelinux.0
+ #extlinux.bin extlinux.bss extlinux.sys \
ldlinux.bss ldlinux.sys ldlinux.bin \
- isolinux.bin isolinux-debug.bin
+ pxelinux.0 isolinux.bin isolinux-debug.bin
# All primary source files for the main syslinux files
NASMSRC := $(wildcard *.asm)
%ifndef _BIOS_INC
%define _BIOS_INC
+ global BIOS_fbm, BIOS_timer
absolute 4*1Eh ; In the interrupt table
fdctab equ $
*/
struct cache_struct* get_cache_block(struct device *dev, block_t block)
{
+ struct cache_struct *head = (struct cache_struct *)dev->cache_head;
+ struct cache_struct *last = head->prev;
/* let's find it from the end, 'cause the endest is the freshest */
- struct cache_struct *cs = dev->cache_head->prev;
- struct cache_struct *head, *last;
+ struct cache_struct *cs = head->prev;
int i;
-
static int total_read;
static int missed;
/* missed, so we need to load it */
if (i == dev->cache_entries) {
/* store it at the head of real cache */
- cs = dev->cache_head->next;
+ cs = head->next;
cs->block = block;
getoneblk(cs->data, block, dev->cache_block_size);
cs->next->prev = cs->prev;
/* add to just before head node */
- last = dev->cache_head->prev;
- head = dev->cache_head;
-
last->next = cs;
cs->prev = last;
head->prev = cs;
--- /dev/null
+#include <stdio.h>
+#include <string.h>
+#include <core.h>
+#include <pxe.h>
+#include <sys/cpu.h>
+
+void subnet_mask(void *data, int opt_len)
+{
+ if (opt_len != 4)
+ return;
+ Netmask = *(uint32_t *)data;
+}
+
+void router(void *data, int opt_len)
+{
+ if (opt_len != 4)
+ return;
+ Gateway = *(uint32_t *)data;
+}
+
+void dns_servers(void *data, int opt_len)
+{
+ int num = opt_len >> 2;
+ int i;
+
+ if (num > DNS_MAX_SERVERS)
+ num = DNS_MAX_SERVERS;
+
+ for (i = 0; i < num; i++) {
+ DNSServers[i] = *(uint32_t *)data;
+ data += 4;
+ }
+
+ /* NOT SURE FOR NOW */
+ LastDNSServer = OFFS_WRT(&DNSServers[num - 1], 0);
+}
+
+void local_domain(void *data, int opt_len)
+{
+ com32sys_t regs;
+ char *p = (char *)data + opt_len;
+ char end = *p;
+
+ memset(®s, 0, sizeof regs);
+ *p = '\0'; /* Zero-terminate option */
+ regs.esi.w[0] = OFFS_WRT(data, 0);
+ regs.edi.w[0] = OFFS_WRT(LocalDomain, 0);
+ call16(dns_mangle, ®s, NULL);
+ *p = end; /* Resotre ending byte */
+}
+
+void vendor_encaps(void *data, int opt_len)
+{
+ /* Only recongnize PXELINUX options */
+ parse_dhcp_options(data, opt_len, 208);
+}
+
+void option_overload(void *data, int opt_len)
+{
+ if (opt_len != 1)
+ return;
+ OverLoad = *(uint8_t *)data;
+}
+
+
+void server(void *data, int opt_len)
+{
+ uint32_t ip;
+
+ if (opt_len != 4)
+ return;
+
+ if (ServerIP)
+ return;
+
+ ip = *(uint32_t *)data;
+ if (ip_ok(ip))
+ ServerIP = ip;
+}
+
+void client_identifier(void *data, int opt_len)
+{
+ if (opt_len > MAC_MAX || opt_len < 2 ||
+ MACLen != (opt_len >> 8) ||
+ *(uint8_t *)data != MACType)
+ return;
+
+ opt_len --;
+ MACLen = opt_len & 0xff;
+ memcpy(MAC, data+1, opt_len);
+ MAC[opt_len] = 0;
+}
+
+void bootfile_name(void *data, int opt_len)
+{
+ strncpy(BootFile, data, opt_len);
+ BootFile[opt_len] = 0;
+}
+
+void uuid_client_identifier(void *data, int opt_len)
+{
+ int type = *(uint8_t *)data;
+ if (opt_len != 17 ||
+ (type | HaveUUID))
+ return;
+
+ HaveUUID = 1;
+ UUIDType = type;
+ memcpy(UUID, data+1, 16);
+ UUID[16] = 0;
+}
+
+void pxelinux_configfile(void *data, int opt_len)
+{
+ DHCPMagic |= 2;
+ strncpy(ConfigName, data, opt_len);
+ ConfigName[opt_len] = 0;
+}
+
+void pxelinux_pathprefix(void *data,int opt_len)
+{
+ DHCPMagic |= 4;
+ strncpy(PathPrefix, data, opt_len);
+ PathPrefix[opt_len] = 0;
+}
+
+void pxelinux_reboottime(void *data, int opt_len)
+{
+ if ((opt_len && 0xff) != 4)
+ return ;
+
+ RebootTime = ntohl(*(uint32_t *)data);
+ DHCPMagic |= 8; /* Got reboot time */
+}
+
+
+struct dhcp_options {
+ int opt_num;
+ void (*fun) (void *, int);
+};
+
+struct dhcp_options dhcp_opts[] = {
+ {1, subnet_mask},
+ {3, router},
+ {6, dns_servers},
+ {15, local_domain},
+ {43, vendor_encaps},
+ {52, option_overload},
+ {54, server},
+ {61, client_identifier},
+ {67, bootfile_name},
+ {97, uuid_client_identifier},
+ {209, pxelinux_configfile},
+ {210, pxelinux_pathprefix},
+ {211, pxelinux_reboottime}
+};
+
+/*
+ * Parse a sequence of DHCP options, pointed to by _option_;
+ * -- some DHCP servers leave option fields unterminated
+ * in violation of the spec.
+ *
+ * filter contains the minimum value for the option to recognize
+ * -- this is used to restrict parsing to PXELINUX-specific options only.
+ */
+void parse_dhcp_options(void *option, int size, int filter)
+{
+ uint8_t opt_num;
+ uint8_t opt_len;
+ uint8_t opt_filter = filter == 208 ? 208 : 0;
+ int opt_entries = sizeof(dhcp_opts) / sizeof(dhcp_opts[0]);
+ int i = 0;
+ char *p = option;
+ struct dhcp_options *opt;
+
+ if (opt_filter)
+ printf("***NOTE!:*** we hit a pxelinux-specific options\n");
+
+ while (size --) {
+ opt_num = *p++;
+
+ if (!size)
+ break;
+ if (opt_num == 0)
+ continue;
+ if (opt_num == 0xff)
+ break;
+
+ /* Anything else will have a lenght filed */
+ opt_len = *p++; /* c <- option lenght */
+ size = size - opt_len - 1;
+ if (size < 0)
+ break;
+ if (opt_num < opt_filter) { /* Is the option value valid */
+ option += opt_len; /* Try next */
+ continue;
+ }
+
+ opt = dhcp_opts;
+ for (i = 0; i < opt_entries; i++) {
+ if (opt_num == opt->opt_num) {
+ opt->fun(p, opt_len);
+ break;
+ }
+ opt ++;
+ }
+
+ /* parse next */
+ p += opt_len;
+ }
+}
+
+/*
+ *
+ ;
+ ; parse_dhcp
+ ;
+ ; Parse a DHCP packet. This includes dealing with "overloaded"
+ ; option fields (see RFC 2132, section 9.3)
+ ;
+ ; This should fill in the following global variables, if the
+ ; information is present:
+ ;
+ ; MyIP - client IP address
+ ; ServerIP - boot server IP address
+ ; Netmask - network mask
+ ; Gateway - default gateway router IP
+ ; BootFile - boot file name
+ ; DNSServers - DNS server IPs
+ ; LocalDomain - Local domain name
+ ; MACLen, MAC - Client identifier, if MACLen == 0
+ ;
+ ; This assumes the DHCP packet is in "trackbuf".
+ ;
+*/
+void parse_dhcp(int pkt_len)
+{
+ struct bootp_t *dhcp = (struct bootp_t *)trackbuf;
+ int opt_len;
+
+ OverLoad = 0;
+ if (ip_ok(dhcp->yip))
+ MyIP = dhcp->yip;
+
+ if (ip_ok(dhcp->sip))
+ ServerIP = dhcp->sip;
+
+ opt_len = (char *)dhcp + pkt_len - (char *)&dhcp->options;
+ if (opt_len && (dhcp->option_magic == BOOTP_OPTION_MAGIC))
+ parse_dhcp_options(&dhcp->options, opt_len, 0);
+
+ if (OverLoad & 1)
+ parse_dhcp_options(&dhcp->bootfile, 128, 0);
+ else if (dhcp->bootfile[0])
+ strcpy(BootFile, dhcp->bootfile);
+
+ if (OverLoad & 2)
+ parse_dhcp_options(dhcp->sname, 64, 0);
+}
+++ /dev/null
-#include <stdio.h>
-#include <string.h>
-#include "core.h"
-#include "disk.h"
-
-void read_sectors(char *buf, sector_t sector_num, int sectors)
-{
- com32sys_t regs;
- //static __lowmem char low_buf[65536];
- /* for safe, we use buf + (sectors << SECTOR_SHIFT) here */
- int high_addr = (buf + (sectors << SECTOR_SHIFT)) > (char *)0x100000;
-
- memset(®s, 0, sizeof regs);
- regs.eax.l = sector_num;
- regs.ebp.l = sectors;
-
- if (high_addr) {
- regs.es = SEG(core_xfer_buf);
- regs.ebx.w[0] = OFFS(core_xfer_buf);
- } else {
- regs.es = SEG(buf);
- regs.ebx.w[0] = OFFS(core_xfer_buf);
- }
-
- call16(getlinsec, ®s, NULL);
-
- if (high_addr)
- memcpy(buf, core_xfer_buf, sectors << SECTOR_SHIFT);
-}
-
-
-void getoneblk(char *buf, block_t block, int block_size)
-{
- int sec_per_block = block_size >> SECTOR_SHIFT;
-
- read_sectors(buf, block * sec_per_block, sec_per_block);
-}
-
-
#endif
return &disk;
}
+
+
+/*
+ * initialize the device structure
+ */
+struct device * device_init(uint8_t devno, bool cdrom, sector_t part_start,
+ uint16_t bsHeads, uint16_t bsSecPerTrack)
+{
+ static struct device dev;
+
+ dev.disk = disk_init(devno, cdrom, part_start, bsHeads, bsSecPerTrack);
+
+ /* for now, isolinux doesn't use cache */
+ if (!cdrom) {
+ /*
+ * FIX!! I can't use __lowmem here, 'cause it will cause the error:
+ * "auxseg/lowmem region collides with xfer_buf_seg".
+ *
+ * static __lowmem char cache_buf[65536];
+ */
+ dev.cache_data = core_cache_buf;
+ dev.cache_size = sizeof core_cache_buf;
+ } else
+ dev.cache_data = NULL;
+
+ return &dev;
+}
+
+
+/* debug function */
+void dump_dev(struct device *dev)
+{
+ printf("device type:%s\n", dev->disk->type ? "EDD" : "CHS");
+ printf("drive number: 0x%x\n", dev->disk->disk_number);
+ printf("cache_data: %p\n", dev->cache_data);
+ printf("cache_head: %p\n", dev->cache_head);
+ printf("cache_block_size: %d\n", dev->cache_block_size);
+ printf("cache_entries: %d\n", dev->cache_entries);
+ printf("cache_size: %d\n", dev->cache_size);
+}
;
; On return, DX contains the number of dots encountered.
;
+ global dns_mangle
dns_mangle:
push ax
push bx
endstruc
section .bss16
+ global LocalDomain, DNSServers
alignb 2
DNSSendBuf resb DNS_MAX_PACKET
DNSRecvBuf resb DNS_MAX_PACKET
.buffersize: dw DNS_MAX_PACKET ; Max packet size
.buffer: dw DNSRecvBuf, 0 ; off, seg of buffer
+ global LastDNSServer
LastDNSServer dw DNSServers
; Actual resolver function
; No segment assumptions permitted.
;
section .text16
+ global dns_resolv
dns_resolv:
push ds
push es
; fat.c
extern alloc_fill_dir, readdir
+ ; pxe.c
+ extern gendotquad
+
%endif ; EXTERN_INC
#include <stdbool.h>
#include <string.h>
#include "fs.h"
-#include "cache.h"
+//#include "cache.h"
/* The this fs pointer */
}
-/*
- * initialize the device structure
- */
-struct device * device_init(uint8_t devno, bool cdrom, sector_t part_start,
- uint16_t bsHeads, uint16_t bsSecPerTrack)
-{
- static struct device dev;
-
- dev.disk = disk_init(devno, cdrom, part_start, bsHeads, bsSecPerTrack);
-
- /* for now, isolinux doesn't use cache */
- if (!cdrom) {
- /*
- * FIX!! I can't use __lowmem here, 'cause it will cause the error:
- * "auxseg/lowmem region collides with xfer_buf_seg".
- *
- * static __lowmem char cache_buf[65536];
- */
- dev.cache_data = core_cache_buf;
- dev.cache_size = sizeof core_cache_buf;
- } else
- dev.cache_data = NULL;
-
- return &dev;
-}
-
-
-/* debug function */
-void dump_dev(struct device *dev)
-{
- printf("device type:%s\n", dev->disk->type ? "EDD" : "CHS");
- printf("drive number: 0x%x\n", dev->disk->disk_number);
- printf("cache_data: %p\n", dev->cache_data);
- printf("cache_head: %p\n", dev->cache_head);
- printf("cache_block_size: %d\n", dev->cache_block_size);
- printf("cache_entries: %d\n", dev->cache_entries);
- printf("cache_size: %d\n", dev->cache_size);
-}
-
/*
* it will do:
* set up the vfs fs structure;
/* set up the fs stucture */
fs.fs_name = ops->fs_name;
fs.fs_ops = ops;
- fs.fs_dev = device_init(regs->edx.b[0], regs->edx.b[1], regs->ecx.l,
+ if (1)//! strcmp(fs.fs_name, "pxe"))
+ fs.fs_dev = NULL;
+ else
+ fs.fs_dev = device_init(regs->edx.b[0], regs->edx.b[1], regs->ecx.l, \
regs->esi.w[0], regs->edi.w[0]);
this_fs = &fs;
blk_shift = fs.fs_ops->fs_init(&fs);
/* initialize the cache */
- if (fs.fs_dev->cache_data)
- cache_init(fs.fs_dev, blk_shift);
+ //if (fs.fs_dev && fs.fs_dev->cache_data)
+ // cache_init(fs.fs_dev, blk_shift);
}
/* diskio.c */
struct disk *disk_init(uint8_t, bool, sector_t, uint16_t, uint16_t);
+struct device *device_init(uint8_t, bool, sector_t, uint16_t, uint16_t);
#endif /* DISK_H */
/* the cache stuff */
char* cache_data;
- struct cache_struct* cache_head;
+ void* cache_head;
uint16_t cache_block_size;
uint16_t cache_entries;
uint32_t cache_size;
--- /dev/null
+
+/**
+* -----------------------------------------------------------------------
+*
+* Copyright 1999-2008 H. Peter Anvin - All Rights Reserved
+*
+* 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.
+*
+* -----------------------------------------------------------------------
+
+*
+* pxe.inc
+*
+* PXE opcodes
+*
+*/
+#ifndef PXE_H
+#define PXE_H
+
+
+#define PXENV_TFTP_OPEN 0x0020
+#define PXENV_TFTP_CLOSE 0x0021
+#define PXENV_TFTP_READ 0x0022
+#define PXENV_TFTP_READ_FILE 0x0023
+#define PXENV_TFTP_READ_FILE_PMODE 0x0024
+#define PXENV_TFTP_GET_FSIZE 0x0025
+
+#define PXENV_UDP_OPEN 0x0030
+#define PXENV_UDP_CLOSE 0x0031
+#define PXENV_UDP_READ 0x0032
+#define PXENV_UDP_WRITE 0x0033
+
+#define PXENV_START_UNDI 0x0000
+#define PXENV_UNDI_STARTUP 0x0001
+#define PXENV_UNDI_CLEANUP 0x0002
+#define PXENV_UNDI_INITIALIZE 0x0003
+#define PXENV_UNDI_RESET_NIC 0x0004
+#define PXENV_UNDI_SHUTDOWN 0x0005
+#define PXENV_UNDI_OPEN 0x0006
+#define PXENV_UNDI_CLOSE 0x0007
+#define PXENV_UNDI_TRANSMIT 0x0008
+#define PXENV_UNDI_SET_MCAST_ADDR 0x0009
+#define PXENV_UNDI_SET_STATION_ADDR 0x000A
+#define PXENV_UNDI_SET_PACKET_FILTER 0x000B
+#define PXENV_UNDI_GET_INFORMATION 0x000C
+#define PXENV_UNDI_GET_STATISTICS 0x000D
+#define PXENV_UNDI_CLEAR_STATISTICS 0x000E
+#define PXENV_UNDI_INITIATE_DIAGS 0x000F
+#define PXENV_UNDI_FORCE_INTERRUPT 0x0010
+#define PXENV_UNDI_GET_MCAST_ADDR 0x0011
+#define PXENV_UNDI_GET_NIC_TYPE 0x0012
+#define PXENV_UNDI_GET_IFACE_INFO 0x0013
+#define PXENV_UNDI_ISR 0x0014
+#define PXENV_STOP_UNDI 0x0015
+#define PXENV_UNDI_GET_STATE 0x0015
+
+#define PXENV_UNLOAD_STACK 0x0070
+#define PXENV_GET_CACHED_INFO 0x0071
+#define PXENV_RESTART_DHCP 0x0072
+#define PXENV_RESTART_TFTP 0x0073
+#define PXENV_MODE_SWITCH 0x0074
+#define PXENV_START_BASE 0x0075
+#define PXENV_STOP_BASE 0x0076
+
+/* gPXE extensions... */
+#define PXENV_FILE_OPEN 0x00e0
+#define PXENV_FILE_CLOSE 0x00e1
+#define PXENV_FILE_SELECT 0x00e2
+#define PXENV_FILE_READ 0x00e3
+#define PXENV_GET_FILE_SIZE 0x00e4
+#define PXENV_FILE_EXEC 0x00e5
+#define PXENV_FILE_API_CHECK 0x00e6
+
+/* Exit codes */
+#define PXENV_EXIT_SUCCESS 0x0000
+#define PXENV_EXIT_FAILURE 0x0001
+
+/* Status codes */
+#define PXENV_STATUS_SUCCESS 0x00
+#define PXENV_STATUS_FAILURE 0x01
+#define PXENV_STATUS_BAD_FUNC 0x02
+#define PXENV_STATUS_UNSUPPORTED 0x03
+#define PXENV_STATUS_KEEP_UNDI 0x04
+#define PXENV_STATUS_KEEP_ALL 0x05
+#define PXENV_STATUS_OUT_OF_RESOURCES 0x06
+#define PXENV_STATUS_ARP_TIMEOUT 0x11
+#define PXENV_STATUS_UDP_CLOSED 0x18
+#define PXENV_STATUS_UDP_OPEN 0x19
+#define PXENV_STATUS_TFTP_CLOSED 0x1a
+#define PXENV_STATUS_TFTP_OPEN 0x1b
+#define PXENV_STATUS_MCOPY_PROBLEM 0x20
+#define PXENV_STATUS_BIS_INTEGRITY_FAILURE 0x21
+#define PXENV_STATUS_BIS_VALIDATE_FAILURE 0x22
+#define PXENV_STATUS_BIS_INIT_FAILURE 0x23
+#define PXENV_STATUS_BIS_SHUTDOWN_FAILURE 0x24
+#define PXENV_STATUS_BIS_GBOA_FAILURE 0x25
+#define PXENV_STATUS_BIS_FREE_FAILURE 0x26
+#define PXENV_STATUS_BIS_GSI_FAILURE 0x27
+#define PXENV_STATUS_BIS_BAD_CKSUM 0x28
+#define PXENV_STATUS_TFTP_CANNOT_ARP_ADDRESS 0x30
+#define PXENV_STATUS_TFTP_OPEN_TIMEOUT 0x32
+
+#define PXENV_STATUS_TFTP_UNKNOWN_OPCODE 0x33
+#define PXENV_STATUS_TFTP_READ_TIMEOUT 0x35
+#define PXENV_STATUS_TFTP_ERROR_OPCODE 0x36
+#define PXENV_STATUS_TFTP_CANNOT_OPEN_CONNECTION 0x38
+#define PXENV_STATUS_TFTP_CANNOT_READ_FROM_CONNECTION 0x39
+#define PXENV_STATUS_TFTP_TOO_MANY_PACKAGES 0x3a
+#define PXENV_STATUS_TFTP_FILE_NOT_FOUND 0x3b
+#define PXENV_STATUS_TFTP_ACCESS_VIOLATION 0x3c
+#define PXENV_STATUS_TFTP_NO_MCAST_ADDRESS 0x3d
+#define PXENV_STATUS_TFTP_NO_FILESIZE 0x3e
+#define PXENV_STATUS_TFTP_INVALID_PACKET_SIZE 0x3f
+#define PXENV_STATUS_DHCP_TIMEOUT 0x51
+#define PXENV_STATUS_DHCP_NO_IP_ADDRESS 0x52
+#define PXENV_STATUS_DHCP_NO_BOOTFILE_NAME 0x53
+#define PXENV_STATUS_DHCP_BAD_IP_ADDRESS 0x54
+#define PXENV_STATUS_UNDI_INVALID_FUNCTION 0x60
+#define PXENV_STATUS_UNDI_MEDIATEST_FAILED 0x61
+#define PXENV_STATUS_UNDI_CANNOT_INIT_NIC_FOR_MCAST 0x62
+#define PXENV_STATUS_UNDI_CANNOT_INITIALIZE_NIC 0x63
+#define PXENV_STATUS_UNDI_CANNOT_INITIALIZE_PHY 0x64
+#define PXENV_STATUS_UNDI_CANNOT_READ_CONFIG_DATA 0x65
+#define PXENV_STATUS_UNDI_CANNOT_READ_INIT_DATA 0x66
+#define PXENV_STATUS_UNDI_BAD_MAC_ADDRESS 0x67
+#define PXENV_STATUS_UNDI_BAD_EEPROM_CHECKSUM 0x68
+#define PXENV_STATUS_UNDI_ERROR_SETTING_ISR 0x69
+#define PXENV_STATUS_UNDI_INVALID_STATE 0x6a
+#define PXENV_STATUS_UNDI_TRANSMIT_ERROR 0x6b
+#define PXENV_STATUS_UNDI_INVALID_PARAMETER 0x6c
+#define PXENV_STATUS_BSTRAP_PROMPT_MENU 0x74
+#define PXENV_STATUS_BSTRAP_MCAST_ADDR 0x76
+#define PXENV_STATUS_BSTRAP_MISSING_LIST 0x77
+#define PXENV_STATUS_BSTRAP_NO_RESPONSE 0x78
+#define PXENV_STATUS_BSTRAP_FILE_TOO_BIG 0x79
+#define PXENV_STATUS_BINL_CANCELED_BY_KEYSTROKE 0xa0
+#define PXENV_STATUS_BINL_NO_PXE_SERVER 0xa1
+#define PXENV_STATUS_NOT_AVAILABLE_IN_PMODE 0xa2
+#define PXENV_STATUS_NOT_AVAILABLE_IN_RMODE 0xa3
+#define PXENV_STATUS_BUSD_DEVICE_NOT_SUPPORTED 0xb0
+#define PXENV_STATUS_LOADER_NO_FREE_BASE_MEMORY 0xc0
+#define PXENV_STATUS_LOADER_NO_BC_ROMID 0xc1
+#define PXENV_STATUS_LOADER_BAD_BC_ROMID 0xc2
+#define PXENV_STATUS_LOADER_BAD_BC_RUNTIME_IMAGE 0xc3
+#define PXENV_STATUS_LOADER_NO_UNDI_ROMID 0xc4
+#define PXENV_STATUS_LOADER_BAD_UNDI_ROMID 0xc5
+#define PXENV_STATUS_LOADER_BAD_UNDI_DRIVER_IMAGE 0xc6
+#define PXENV_STATUS_LOADER_NO_PXE_STRUCT 0xc8
+#define PXENV_STATUS_LOADER_NO_PXENV_STRUCT 0xc9
+#define PXENV_STATUS_LOADER_UNDI_START 0xca
+#define PXENV_STATUS_LOADER_BC_START 0xcb
+
+
+
+
+/*
+ * some other defines
+ */
+#define PKTBUF_SIZE (65536 / MAX_OPEN)
+
+#define TFTP_BLOCKSIZE_LG2 9
+#define TFTP_BLOCKSIZE (1 << TFTP_BLOCKSIZE_LG2)
+#define PKTBUF_SEG 0x4000
+#define DNS_MAX_SERVERS 4
+
+#define is_digit(c) (((c) >= '0') && ((c) <= '9'))
+#define htons(x) ( ( ((x) & 0xff) << 8) + ( ((x) &0xff00) >> 8) )
+#define ntohs(x) htons(x)
+#define htonl(x) ( ( ((x) & 0xff) << 24) + ( ((x) & 0xff00) << 8 ) + \
+ ( ((x) & 0xff0000) >> 8 ) + ( ((x) & 0xff000000) >> 24) )
+#define ntohl(x) htonl(x)
+
+/*
+ * TFTP operation codes
+ */
+#define TFTP_RRQ htons(1) // Read rest
+#define TFTP_WRQ htons(2) // Write rest
+#define TFTP_DATA htons(3) // Data packet
+#define TFTP_ACK htons(4) // ACK packet
+#define TFTP_ERROR htons(5) // ERROR packet
+#define TFTP_OACK htons(6) // OACK packet
+
+/*
+ * TFTP error codes
+ */
+#define TFTP_EUNDEF htons(0) // Unspecified error
+#define TFTP_ENOTFOUND htons(1) // File not found
+#define TFTP_EACCESS htons(2) // Access violation
+#define TFTP_ENOSPACE htons(3) // Disk full
+#define TFTP_EBADOP htons(4) // Invalid TFTP operation
+#define TFTP_EBADID htons(5) // Unknown transfer
+#define TFTP_EEXISTS htons(6) // File exists
+#define TFTP_ENOUSER htons(7) // No such user
+#define TFTP_EOPTNEG htons(8) // Option negotiation failure
+
+#define BOOTP_OPTION_MAGIC htonl(0x63825363)
+
+
+/*
+ * structures
+ */
+struct bootp_t {
+ uint8_t opcode; /* BOOTP/DHCP "opcode" */
+ uint8_t hardware; /* ARP hreadware type */
+ uint8_t hardlen; /* Hardware address length */
+ uint8_t gatehops; /* Used by forwarders */
+ uint32_t ident; /* Transaction ID */
+ uint16_t seconds; /* Seconds elapsed */
+ uint16_t flags; /* Broadcast flags */
+ uint32_t cip; /* Cient IP */
+ uint32_t yip; /* "Your" IP */
+ uint32_t sip; /* Next Server IP */
+ uint32_t gip; /* Relay agent IP */
+ uint8_t macaddr[16]; /* Client MAC address */
+ uint8_t sname[64]; /* Server name (optional) */
+ char bootfile[128]; /* Boot file name */
+ uint32_t option_magic; /* Vendor option magic cookie */
+ uint8_t options[1260]; /* Vendor options */
+} __attribute__ ((packed));
+
+struct open_file_t {
+ uint16_t tftp_localport; /* Local port number (0=not in us)*/
+ uint16_t tftp_remoteport; /* Remote port number */
+ uint32_t tftp_remoteip; /* Remote IP address */
+ uint32_t tftp_filepos; /* bytes downloaded (includeing buffer) */
+ uint32_t tftp_filesize; /* Total file size(*) */
+ uint32_t tftp_blksize; /* Block size for this connection(*) */
+ uint16_t tftp_bytesleft; /* Unclaimed data bytes */
+ uint16_t tftp_lastpkt; /* Sequence number of last packet (NBO) */
+ uint16_t tftp_dataptr; /* Pointer to available data */
+ uint8_t tftp_goteof; /* 1 if the EOF packet received */
+ uint8_t tftp_unused[3]; /* Currently unused */
+ uint16_t tftp_pktbuf; /* Packet buffer offset */
+} __attribute__ ((packed));
+extern char Files[];
+
+struct pxe_udp_write_pkt {
+ uint16_t status;
+ uint32_t sip;
+ uint32_t gip;
+ uint16_t lport;
+ uint16_t rport;
+ uint16_t buffersize;
+ uint16_t buffer[2];
+} __attribute__ ((packed));
+
+struct pxe_udp_read_pkt {
+ uint16_t status;
+ uint32_t sip;
+ uint32_t dip;
+ uint16_t rport;
+ uint16_t lport;
+ uint16_t buffersize;
+ uint16_t buffer[2];
+} __attribute__ ((packed));
+
+struct pxe_bootp_query_pkt {
+ uint16_t status;
+ uint16_t packettype;
+ uint16_t buffersize;
+ uint16_t buffer[2];
+ uint16_t bufferlimit;
+} __attribute__ ((packed));
+
+struct pxe_udp_open_pkt {
+ uint16_t status;
+ uint32_t sip;
+} __attribute__ ((packed));
+
+struct gpxe_file_api_check {
+ uint16_t status;
+ uint16_t size;
+ uint32_t magic;
+ uint32_t provider;
+ uint32_t apimask;
+ uint32_t flags;
+} __attribute__ ((packed));
+
+struct gpxe_file_open {
+ uint16_t status;
+ uint16_t filehandle;
+ uint16_t filename[2];
+ uint32_t reserved;
+} __attribute__ ((packed));
+
+struct gpxe_get_file_size {
+ uint16_t status;
+ uint16_t filehandle;
+ uint32_t filesize;
+} __attribute__ ((packed));
+
+struct gpxe_file_read {
+ uint16_t status;
+ uint16_t filehandle;
+ uint16_t buffersize;
+ uint16_t buffer[2];
+} __attribute__ ((packed));
+
+/*
+ * externs
+ */
+extern uint32_t ServerIP;
+extern uint32_t MyIP;
+extern uint32_t Netmask;
+extern uint32_t Gateway;
+extern uint32_t ServerPort;
+
+#define MAC_MAX 32
+extern char MACStr[]; /* MAC address as a string */
+extern char MAC[]; /* Actual MAC address */
+extern char BOOTIFStr[]; /* Space for "BOOTIF=" */
+extern uint8_t MACLen; /* MAC address len */
+extern uint8_t MACType; /* MAC address type */
+
+
+extern uint8_t DHCPMagic;
+extern uint8_t OverLoad;
+extern uint32_t RebootTime;
+
+
+/* TFTP ACK packet */
+extern uint16_t ack_packet_buf[];
+
+extern void kaboom(void);
+extern void dns_mangle(void);
+extern char trackbuf[];
+extern char BootFile[];
+extern char PathPrefix[];
+extern char CurrentDirName[];
+extern char LocalDomain[];
+
+extern char packet_buf[];
+
+extern char IPOption[];
+extern char DotQuadBuf[];
+
+
+extern uint32_t DNSServers[];
+extern uint16_t LastDNSServer;
+
+extern char ConfigName[];
+
+extern uint16_t RealBaseMem;
+extern uint16_t APIVer;
+extern far_ptr_t PXEEntry;
+
+extern far_ptr_t InitStack;
+
+extern int HaveUUID;
+extern uint8_t UUIDType;
+extern char UUID[];
+
+extern volatile uint16_t BIOS_timer;
+
+
+/*
+ * functions
+ */
+int ip_ok(uint32_t);
+void parse_dhcp(int);
+void parse_dhcp_options(void *, int, int);
+
+
+
+
+#endif /* pxe.h */
err_noparm db 'Missing parameter in configuration file. Keyword: ',0
section .uibss
+ global KernelName
alignb 4
vk_size equ (vk_end + 3) & ~3
VKernelBuf: resb vk_size ; "Current" vkernel
--- /dev/null
+#include <stdio.h>
+#include <string.h>
+#include <core.h>
+#include <fs.h>
+#include <pxe.h>
+#include <sys/cpu.h>
+
+#define MAX_OPEN_LG2 5
+#define MAX_OPEN (1 << MAX_OPEN_LG2)
+#define FILENAME_MAX_LG2 7
+#define FILENAME_MAX (1 << FILENAME_MAX_LG2)
+
+#define MAX(a,b) (a > b ? a : b)
+
+#define GPXE 1
+#define USE_PXE_PROVIDED_STACK 0
+
+char *err_nopxe = "No !PXE or PXENV+ API found; we're dead...\n";
+char *err_pxefailed = "PXE API call failed, error ";
+char *err_udpinit = "Failed to initialize UDP stack\n";
+char *err_noconfig = "Unable to locate configuration file\n";
+char *err_damage = "TFTP server sent an incomprehesible reply\n";
+
+char *tftpprefix_msg = "TFTP prefix: ";
+char *get_packet_msg = "Getting cached packet ";
+
+uint16_t NextSocket = 49152;
+
+int has_gpxe;
+int HaveUUID = 0;
+uint8_t UUIDType;
+uint8_t uuid_dashes[] = {4, 2, 2, 2, 6, 0};
+
+uint16_t StructPtr[2];
+
+static const uint8_t TimeoutTable[] = {
+ 2, 2, 3, 3, 4, 5, 6, 7, 9, 10, 12, 15, 18, 21, 26, 31, 37, 44, 53, 64, 77,
+ 92, 110, 132, 159, 191, 229, 255, 255, 255, 255, 0
+};
+
+char *mode = "octet";
+char *tsize_str = "tsize";
+int tsize_len = 6; /* We should include the final null here */
+
+char *blksize_str = "blksize";
+int blksize_len = 8;
+
+char *asciidec = "1408";
+
+
+
+/*
+ * Allocate a local UDP port structure.
+ * return the socket pointer if success, or null if failure
+ *
+ */
+struct open_file_t* allocate_socket()
+{
+ extern uint16_t NextSocket;
+ uint16_t i = MAX_OPEN;
+ struct open_file_t *socket = (struct open_file_t *)&Files;
+
+ for (; i > 0; i--) {
+ if (*(uint16_t*)socket == 0)
+ break;
+ socket ++;
+ }
+
+ /* Not found */
+ if (i == 0)
+ return NULL;
+
+ /*
+ * 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.
+ *
+ */
+ i--;
+ NextSocket = ((NextSocket + 1) & ((1 << (13-MAX_OPEN_LG2))-1)) | 0xc000;
+ i <<= 13-MAX_OPEN_LG2;
+ i += NextSocket;
+ i = ntohs(i) ; /* convet to network byte order, little to big */
+ *(uint16_t*)socket = i; /* socket in use */
+
+ return socket;
+}
+
+/*
+ * free socket, socket in SI; return SI = 0, ZF = 1 for convenience
+ */
+void free_socket(struct open_file_t *file)
+{
+ /* tftp_pktbuf is not cleared */
+ memset(file, 0, sizeof(struct open_file_t) - 2);
+}
+
+/**
+ * Take a nubmer of bytes in memory and convert to lower-case hxeadecimal
+ *
+ * @param: dst, output buffer
+ * @param: src, input buffer
+ * @param: count, number of bytes
+ *
+ */
+void lchexbytes(char *dst, const void *src, int count)
+{
+ uint8_t half;
+ uint8_t c;
+ const uint8_t *s = src;
+
+ for(; count > 0; count--) {
+ c = *s++;
+ half = ((c >> 4) & 0x0f) + '0';
+ *dst++ = half > '9' ? (half + 'a' - '9' - 1) : half;
+
+ half = (c & 0x0f) + '0';
+ *dst++ = half > '9' ? (half + 'a' - '9' - 1) : half;
+ }
+}
+
+/**
+ * just like the lchexbytes, except to upper-case
+ *
+ */
+void uchexbytes(char *dst, const void *src, int count)
+{
+ uint8_t half;
+ uint8_t c;
+ const uint8_t *s = src;
+
+ for(; count > 0; count--) {
+ c = *s++;
+ half = ((c >> 4) & 0x0f) + '0';
+ *dst++ = half > '9' ? (half + 'A' - '9' - 1) : half;
+
+ half = (c & 0x0f) + '0';
+ *dst++ = half > '9' ? (half + 'A' - '9' - 1) : half;
+ }
+}
+
+
+
+
+/*
+ *
+ * Tests an IP address in EAX for validity; return with 0 for bad, 1 for good.
+ * We used to refuse class E, but class E addresses are likely to become
+ * assignable unicast addresses in the near future.
+ *
+ */
+int ip_ok(uint32_t ip)
+{
+ if (ip == -1) /* Refuse the all-one address */
+ goto bad;
+ if ((ip & 0xff) == 0) /* Refuse network zero */
+ goto bad;
+ if ((ip & 0xff) == 0xff) /* Refuse loopback */
+ goto bad;
+ if ((ip & 0xf0) == 0xe0) /* Refuse class D */
+ goto bad;
+
+ return 1;
+
+ bad:
+ return 0;
+}
+
+
+/*********************************************************************
+;
+; gendotquad
+;
+; Take an IP address (in network byte order) in EAX and
+; output a dotted quad string to ES:DI.
+; DI points to terminal null at end of string on exit.
+;
+ *********************************************************************/
+/**
+ * @param: dst, to store the converted ip string
+ * @param: ip, the ip address that needed convert.
+ *
+ * @return: the ip string length
+ */
+int gendotquad(char *dst, uint32_t ip)
+{
+ int part;
+ int i = 0, j;
+ char temp[4];
+ char *p = dst;
+
+ for (; i < 4; i++) {
+ j = 0;
+ part = ip & 0xff;
+ do {
+ temp[j++] = (part % 10) + '0';
+ }while(part /= 10);
+ for (; j > 0; j--)
+ *p++ = temp[j-1];
+ *p++ = '.';
+
+ ip >>= 8;
+ }
+ /* drop the last dot '.' and zero-terminate string*/
+ *(--p) = 0;
+
+ return p - dst;
+}
+
+/*
+ * parse the ip_str and return the ip address with *res.
+ * return the the string address after the ip string
+ *
+ */
+char *parse_dotquad(char *ip_str, uint32_t *res)
+{
+ char *p = ip_str;
+ int i = 0;
+ uint8_t part = 0;
+ uint32_t ip = 0;
+
+ for (; i < 4; i++) {
+ while (is_digit(*p)) {
+ part = part * 10 + *p - '0';
+ p++;
+ }
+ if (i != 3 && *p != '.')
+ return NULL;
+
+ ip = (ip << 8) | part;
+ part = 0;
+ p++;
+ }
+ p --;
+
+ *res = ip;
+ return p;
+}
+
+/*
+ * the ASM pxenv function wrapper, return 1 if error, or 0
+ *
+ */
+int pxe_call(int opcode, void *data)
+{
+ extern void pxenv(void);
+ com32sys_t in_regs, out_regs;
+
+#if 0
+ printf("pxe_call op %04x data %p\n", opcode, data);
+#endif
+
+ memset(&in_regs, 0, sizeof in_regs);
+
+ in_regs.ebx.w[0] = opcode;
+ in_regs.es = SEG(data);
+ in_regs.edi.w[0] = OFFS(data);
+ call16(pxenv, &in_regs, &out_regs);
+
+ return out_regs.eflags.l & EFLAGS_CF; /* CF SET for fail */
+}
+
+/**
+ *
+ * Send ACK packet. This is a common operation and so is worth canning.
+ *
+ * @param: file, TFTP block pointer
+ * @param: ack_num, Packet # to ack (network byte order)
+ *
+ */
+void ack_packet(struct open_file_t *file, uint16_t ack_num)
+{
+ int err;
+ static __lowmem struct pxe_udp_write_pkt uw_pkt;
+
+ /* Packet number to ack */
+ ack_packet_buf[1] = ack_num;
+ uw_pkt.lport = file->tftp_localport;
+ uw_pkt.rport = file->tftp_remoteport;
+ uw_pkt.sip = file->tftp_remoteip;
+ uw_pkt.gip = ((uw_pkt.sip ^ MyIP) & Netmask) ? Gateway : 0;
+ uw_pkt.buffer[0] = OFFS_WRT(ack_packet_buf, 0);
+ uw_pkt.buffer[1] = 0; /* seems SEG and OFFS stuff doesn't work here */
+ uw_pkt.buffersize = 4;
+
+ err = pxe_call(PXENV_UDP_WRITE, &uw_pkt);
+#if 0
+ printf("sent %s\n", err ? "FAILED" : "OK");
+#endif
+}
+
+
+/**
+ * Get a DHCP packet from the PXE stack into the trackbuf
+ *
+ * @param: type, packet type
+ * @return: buffer size
+ *
+ */
+int pxe_get_cached_info(int type)
+{
+ int err;
+ static __lowmem struct pxe_bootp_query_pkt bq_pkt;
+ printf(" %02x", type);
+
+ bq_pkt.status = 0;
+ bq_pkt.packettype = type;
+ bq_pkt.buffersize = 8192;
+ bq_pkt.buffer[0] = OFFS_WRT(trackbuf, 0);
+ bq_pkt.buffer[1] = 0;
+
+ err = pxe_call(PXENV_GET_CACHED_INFO, &bq_pkt);
+ if (err) {
+ printf("%s %04x\n", err_pxefailed, err);
+ call16(kaboom, NULL, NULL);
+ }
+
+ return bq_pkt.buffersize;
+}
+
+
+
+#if GPXE
+
+/*
+ * Return 1 if and only if the buffer pointed to by
+ * url is a URL (contains ://)
+ *
+ */
+int is_url(char *url)
+{
+
+ while (*url) {
+ if(! strncmp(url, "://", 3))
+ return 1;
+
+ url++;
+ }
+ return 0;
+}
+
+
+/*
+ * Return CF=0 if and only if the buffer pointed to by DS:SI is a URL
+ * (contains ://) *and* the gPXE extensions API is available. No
+ * registers modified.
+ */
+int is_gpxe(char *url)
+{
+ int err;
+ static __lowmem struct gpxe_file_api_check ac;
+ char *gpxe_warning_msg = "URL syntax, but gPXE extensions not detected, tring plain TFTP...\n";
+
+ if (! is_url(url))
+ return 0;
+
+ ac.size = 20;
+ ac.magic = 0x91d447b2;
+ /* If has_gpxe is greater than one, means the gpxe status is unknow */
+ while (has_gpxe > 1) {
+ err = pxe_call(PXENV_FILE_API_CHECK, &ac);
+ if (err || ac.magic != 0xe9c17b20)
+ printf("%s\n", gpxe_warning_msg);
+ else
+ has_gpxe = (~ac.provider & 0xffff) & 0x4b ? 0 : 1;
+
+ if (!has_gpxe)
+ printf("%s\n", gpxe_warning_msg);
+ }
+
+ if (has_gpxe == 1)
+ return 1;
+ else
+ return 0;
+}
+
+/**
+ * Get a fresh packet from a gPXE socket
+ * @param: file -> socket structure
+ *
+ */
+void get_packet_gpxe(struct open_file_t *file)
+{
+ static __lowmem struct gpxe_file_read fr;
+ int err;
+
+ while (1) {
+ fr.filehandle = file->tftp_remoteport;
+ fr.buffer[0] = file->tftp_pktbuf;
+ fr.buffer[1] = PKTBUF_SEG;
+ fr.buffersize = PKTBUF_SIZE;
+ err = pxe_call(PXENV_FILE_READ, &fr);
+ if (!err) /* successed */
+ break;
+
+ if (fr.status == PXENV_STATUS_TFTP_OPEN)
+ continue;
+ call16(kaboom, NULL, NULL);
+ }
+
+ file->tftp_bytesleft = fr.buffersize;
+ file->tftp_filepos += fr.buffersize;
+
+ if (file->tftp_bytesleft == 0)
+ file->tftp_filesize = file->tftp_filepos;
+
+ /* if we're done here, close the file */
+ if (file->tftp_filesize > file->tftp_filepos)
+ return;
+
+ /* Got EOF, close it */
+ file->tftp_goteof = 1;
+ pxe_call(PXENV_FILE_CLOSE, &fr);
+}
+#endif /* GPXE */
+
+
+/*
+ * mangle a filename pointed to by _src_ into a buffer pointed
+ * to by _dst_; ends on encountering any whitespace.
+ *
+ * The first four bytes of the manged name is the IP address of
+ * the download host, 0 for no host, or -1 for a gPXE URL.
+ *
+ */
+void pxe_mangle_name(char *dst, char *src)
+{
+ char *p = src;
+ uint32_t ip;
+ int i = 0;
+
+#if GPXE
+ if (is_url(src))
+ goto prefix_done;
+#endif
+
+ ip = ServerIP;
+ if (*p == 0)
+ goto noip;
+ if (! strncmp(p, "::", 2))
+ goto gotprefix;
+ else {
+ while (*p && strncmp(p, "::", 2))
+ p ++;
+ if (! *p)
+ goto noip;
+ /*
+ * we have a :: prefix of ip sort, it could be either a DNS
+ * name or dot-quad IP address. Try the dot-quad first.
+ */
+ p = src;
+ if ((p = parse_dotquad(p, &ip)) && !strncmp(p, "::", 2))
+ goto gotprefix;
+ else {
+#if 0
+ extern void dns_resolv(void);
+ call16(dns_resolv, regs, regs);
+ p = (char *)MK_PTR(regs->ds, regs->esi.w[0]);
+ ip = regs->eax.l;
+ if (! strncmp(p, "::", 2))
+ if (ip)
+ goto gotprefix;
+#endif
+ }
+ }
+ noip:
+ p = src;
+ ip = 0;
+ goto prefix_done;
+
+ gotprefix:
+ p += 2; /* skip double colon */
+
+ prefix_done:
+ *(uint32_t *)dst = ip;
+ dst += 4;
+ i = FILENAME_MAX - 5;
+
+ do {
+ if (*p <= ' ')
+ break;
+ *dst++ = *p++;
+ }while (i--);
+
+ i ++;
+ while (i) {
+ *dst++ = 0;
+ i --;
+ }
+
+#if 0
+ printf("the name before mangling: ");
+ dump16(src);
+ printf("the name after mangling: ");
+ dump16((char *)MK_PTR(regs->ds, regs->edi.w[0]));
+#endif
+}
+
+
+/*
+ *
+ ;
+ ; Get a fresh packet if the buffer is drained, and we haven't hit
+ ; EOF yet. The buffer should be filled immediately after draining!
+ ;
+ ; expects fs -> pktbuf_seg and ds:si -> socket structure
+ ;
+*/
+void fill_buffer(struct open_file_t *file)
+{
+ int err;
+ int last_pkt;
+ const uint8_t *timeout_ptr = TimeoutTable;
+ uint8_t timeout;
+ uint16_t buffersize;
+ uint16_t old_time;
+ void *data = NULL;
+ static __lowmem struct pxe_udp_read_pkt pkt;
+
+ if (file->tftp_bytesleft || file->tftp_goteof)
+ return;
+
+#if GPXE
+ if (file->tftp_localport == 0xffff) {
+ get_packet_gpxe(file);
+ return;
+ }
+#endif
+
+
+ /*
+ * Start by ACKing the previous packet; this should cause
+ * the next packet to be sent.
+ */
+ ack_again:
+ ack_packet(file, file->tftp_lastpkt);
+
+ timeout_ptr = TimeoutTable;
+ timeout = *timeout_ptr++;
+ old_time = BIOS_timer;
+ while (timeout) {
+ pkt.buffer[0] = file->tftp_pktbuf;
+ pkt.buffer[1] = PKTBUF_SEG;
+ pkt.buffersize = PKTBUF_SIZE;
+ pkt.sip = file->tftp_remoteip;
+ pkt.dip = MyIP;
+ pkt.rport = file->tftp_remoteport;
+ pkt.lport = file->tftp_localport;
+ err = pxe_call(PXENV_UDP_READ, &pkt);
+ if (err) {
+ if (BIOS_timer == old_time) {
+ printf(".");
+ continue;
+ }
+ printf("+");
+
+ timeout--; /* decrease one timer tick */
+ if (!timeout) {
+ timeout = *timeout_ptr++;
+ if (!timeout)
+ break;
+ }
+ continue;
+ }
+
+ if (pkt.buffersize < 4) /* Bad size for a DATA packet */
+ continue;
+
+ data = MK_PTR(PKTBUF_SEG, file->tftp_pktbuf);
+ if (*(uint16_t *)data != TFTP_DATA) /* Not a data packet */
+ continue;
+
+ /* If goes here, recevie OK, break */
+ break;
+ }
+
+ /* time runs out */
+ if (timeout == 0)
+ call16(kaboom, NULL, NULL);
+
+ last_pkt = file->tftp_lastpkt;
+ last_pkt = ntohs(last_pkt); /* Host byte order */
+ last_pkt++;
+ last_pkt = htons(last_pkt); /* Network byte order */
+ if (*(uint16_t *)(data + 2) != last_pkt) {
+ /*
+ * Wrong packet, ACK the packet and try again.
+ * This is presumably because the ACK got lost,
+ * so the server just resent the previous packet.
+ */
+#if 0
+ printf("Wrong packet, wanted %04x, got %04x\n", htons(last_pkt), htons(*(uint16_t *)(data+2)));
+#endif
+ goto ack_again;
+ }
+
+ /* It's the packet we want. We're also EOF if the size < blocksize */
+ file->tftp_lastpkt = last_pkt; /* Update last packet number */
+ buffersize = pkt.buffersize - 4; /* Skip TFTP header */
+ file->tftp_dataptr = file->tftp_pktbuf + 4;
+ file->tftp_filepos += buffersize;
+ file->tftp_bytesleft = buffersize;
+ if (buffersize < file->tftp_blksize) {
+ /* it's the last block, ACK packet immediately */
+ ack_packet(file, *(uint16_t *)(data + 2));
+
+ /* Make sure we know we are at end of file */
+ file->tftp_filesize = file->tftp_filepos;
+ file->tftp_goteof = 1;
+ }
+}
+
+
+/**
+ * getfssec: Get multiple clusters from a file, given the starting cluster.
+ * In this case, get multiple blocks from a specific TCP connection.
+ *
+ * @param: fs, the fs_info structure address, in pxe, we don't use this.
+ * @param: buf, buffer to store the read data
+ * @param: openfile, TFTP socket pointer
+ * @param: blocks, 512-byte block count; 0FFFFh = until end of file
+ *
+ * @return: the bytes read
+ *
+ */
+uint32_t pxe_getfssec(struct fs_info *fs, char *buf,
+ void *open_file, int blocks, int *have_more)
+{
+ struct open_file_t *file = (struct open_file_t *)open_file;
+ int count = blocks;
+ int chunk;
+ int bytes_read = 0;
+
+ sti();
+ fs = NULL; /* drop the compile warning message */
+
+ count <<= TFTP_BLOCKSIZE_LG2;
+ while (count) {
+ fill_buffer(file); /* If we have no 'fresh' buffer, get it */
+ if (! file->tftp_bytesleft)
+ break;
+
+ chunk = count;
+ if (chunk > file->tftp_bytesleft)
+ chunk = file->tftp_bytesleft;
+ file->tftp_bytesleft -= chunk;
+ memcpy(buf, MK_PTR(PKTBUF_SEG, file->tftp_dataptr), chunk);
+ file->tftp_dataptr += chunk;
+ buf += chunk;
+ bytes_read += chunk;
+ count -= chunk;
+ }
+
+
+ if (file->tftp_bytesleft || (file->tftp_filepos < file->tftp_filesize)) {
+ fill_buffer(file);
+ *have_more = 1;
+ } else if (file->tftp_goteof) {
+ /*
+ * The socket is closed and the buffer dranined
+ * close socket structure and re-init for next user
+ */
+ free_socket(file);
+ *have_more = 0;
+ }
+
+ return bytes_read;
+ }
+
+
+
+/*
+ * Fill the packet tail with the tftp informations then retures the lenght
+ */
+int fill_tail(char *dst)
+{
+ char *p = dst;
+ strcpy(p, mode);
+ p += strlen(mode) + 1;
+
+ strcpy(p, tsize_str);
+ p += strlen(tsize_str) + 1;
+
+ strcpy(p, "0");
+ p += 2;
+
+ strcpy(p, blksize_str);
+ p += strlen(blksize_str) + 1;
+
+ strcpy(p, asciidec);
+ p += strlen(asciidec) + 1;
+
+ return p - dst;
+}
+
+
+struct tftp_options {
+ char *str_ptr; /* string pointer */
+ int str_len; /* string lenght */
+ int offset; /* offset into socket structre */
+};
+struct tftp_options tftp_options[2];
+
+inline void init_options()
+{
+ tftp_options[0].str_ptr = tsize_str;
+ tftp_options[0].str_len = tsize_len;
+ tftp_options[0].offset = (int)&((struct open_file_t *)0)->tftp_filesize;
+
+ tftp_options[1].str_ptr = blksize_str;
+ tftp_options[1].str_len = blksize_len;
+ tftp_options[1].offset = (int)&((struct open_file_t *)0)->tftp_blksize;
+}
+
+/*
+ *
+ *
+ * searchdir:
+ *
+ * Open a TFTP connection to the server
+ *
+ * On entry:
+ * DS:DI = mangled filename
+ * If successful:
+ * ZF clear
+ * SI = socket pointer
+ * EAX = file length in bytes, or -1 if unknown
+ * If unsuccessful
+ * ZF set
+ *
+ */
+void pxe_searchdir(char *filename, struct file *file)
+{
+ char *buf = packet_buf;
+ char *p = filename;
+ char *options;
+ char *src, *dst;
+ char *data;
+ struct open_file_t *open_file;
+ static __lowmem struct pxe_udp_write_pkt uw_pkt;
+ static __lowmem struct pxe_udp_read_pkt ur_pkt;
+ static __lowmem struct gpxe_file_open fo;
+ static __lowmem struct gpxe_get_file_size gs;
+ struct tftp_options *tftp_opt = tftp_options;
+ int i = 0;
+ int tftp_opts = sizeof tftp_options / sizeof tftp_options[0];
+ int err;
+ int buffersize;
+ const uint8_t *timeout_ptr;
+ uint8_t timeout;
+ uint16_t oldtime;
+ uint16_t tid;
+ uint16_t opcode;
+ uint16_t blk_num;
+ uint32_t ip;
+ uint32_t filesize = 0;
+ uint32_t *data_ptr;
+
+ init_options();
+ open_file = allocate_socket();
+ if (!open_file)
+ goto done;
+
+ timeout_ptr = TimeoutTable; /* Reset timeout */
+
+ sendreq:
+ uw_pkt.buffer[0] = OFFS_WRT(buf, 0);
+ uw_pkt.buffer[1] = 0;
+ *(uint16_t *)buf = TFTP_RRQ; /* TFTP opcode */
+ buf += 2;
+
+ ip = *(uint32_t *)p; /* ip <- server override (if any) */
+ p += 4;
+ if (ip == 0) {
+ /* Have prefix */
+ strcpy(buf, PathPrefix);
+ buf += strlen(PathPrefix);
+ ip = ServerIP; /* Get the default server */
+ }
+
+ strcpy(buf, p); /* Copy the filename */
+ buf += strlen(p) + 1; /* advance the pointer, null char included */
+
+#if GPXE
+ if (is_gpxe(packet_buf + 2)) {
+ fo.status = PXENV_STATUS_BAD_FUNC;
+ fo.filename[0] = OFFS_WRT(packet_buf + 2, 0);
+ fo.filename[1] = 0;
+ err = pxe_call(PXENV_FILE_OPEN, &fo);
+ if (err)
+ goto done;
+
+ open_file->tftp_localport = -1;
+ open_file->tftp_remoteport = fo.filehandle;
+ gs.filehandle = fo.filehandle;
+
+#if 0
+ err = pxe_call(PXENV_GET_FILE_SIZE, &gs);
+ if (!err)
+ filesize = gs.filesize;
+ else
+#endif
+ filesize = -1;
+ open_file->tftp_filesize = -1;
+ goto done;
+ }
+#endif /* GPXE */
+
+ open_file->tftp_remoteip = ip;
+ tid = open_file->tftp_localport; /* TID(local port No) */
+ uw_pkt.sip = ip;
+ uw_pkt.gip = ((uw_pkt.sip ^ MyIP) & Netmask) ? Gateway : 0;
+ uw_pkt.lport = tid;
+ uw_pkt.rport = ServerPort;
+ buf += fill_tail(buf);
+ uw_pkt.buffersize = buf - packet_buf;
+ err = pxe_call(PXENV_UDP_WRITE, &uw_pkt);
+ if (err || uw_pkt.status != 0)
+ goto failure; /* In fact, the 'failure' target will not do a failure thing;
+ it will move on to the next timeout, then tries again until
+ _real_ time out */
+
+ /*
+ * Danger, Will Robinson! We need to support tiemout
+ * and retry lest we just lost a packet ...
+ */
+
+ /* Packet transmitted OK, now we need to receive */
+ timeout = *timeout_ptr++;
+ oldtime = BIOS_timer;
+ while (timeout) {
+ buf = packet_buf;
+ ur_pkt.buffer[0] = OFFS_WRT(buf, 0);
+ ur_pkt.buffer[1] = 0;
+ ur_pkt.buffersize = 2048;
+ ur_pkt.dip = MyIP;
+ ur_pkt.lport = tid;
+ err = pxe_call(PXENV_UDP_READ, &ur_pkt);
+ if (err) {
+ if (oldtime == BIOS_timer)
+ continue;
+ timeout --; /* Decrease one timer tick */
+ if (!timeout)
+ goto failure;
+ }
+
+ /* Make sure the packet actually came from the server */
+ if (ur_pkt.sip == open_file->tftp_remoteip)
+ break;
+ }
+
+ /* Got packet; reset timeout */
+ timeout_ptr = TimeoutTable;
+ open_file->tftp_remoteport = ur_pkt.rport;
+
+ /* filesize <- -1 == unknow */
+ open_file->tftp_filesize = -1;
+ /* Default blksize unless blksize option negotiated */
+ open_file->tftp_blksize = TFTP_BLOCKSIZE;
+ buffersize = ur_pkt.buffersize - 2; /* bytes after opcode */
+ if (buffersize < 0)
+ goto failure; /* Garbled reply */
+
+ /*
+ * Get the opcode type, and parse it
+ */
+ opcode = *(uint16_t *)packet_buf;
+ if (opcode == TFTP_ERROR) {
+ filesize = 0;
+ open_file = NULL;
+ goto done; /* ERROR reply; don't try again */
+ } else if (opcode == TFTP_DATA) {
+ /*
+ * If the server doesn't support any options, we'll get a
+ * DATA reply instead of OACK. Stash the data in the file
+ * buffer and go with the default value for all options...
+ *
+ * We got a DATA packet, meaning no options are
+ * suported. Save the data away and consider the
+ * length undefined, *unless* this is the only
+ * data packet...
+ */
+ buffersize -= 2;
+ if (buffersize < 0)
+ goto failure;
+ data = packet_buf + 2;
+ blk_num = *(uint16_t *)data;
+ data += 2;
+ if (blk_num != htons(1))
+ goto failure;
+ open_file->tftp_lastpkt = blk_num;
+ if (buffersize > TFTP_BLOCKSIZE)
+ goto err_reply; /* Corrupt */
+ else if (buffersize < TFTP_BLOCKSIZE) {
+ /*
+ * This is the final EOF packet, already...
+ * We know the filesize, but we also want to
+ * ack the packet and set the EOF flag.
+ */
+ open_file->tftp_filesize = buffersize;
+ open_file->tftp_goteof = 1;
+ ack_packet(open_file, blk_num);
+ }
+
+ open_file->tftp_bytesleft = buffersize;
+ open_file->tftp_dataptr = open_file->tftp_pktbuf;
+ memcpy(MK_PTR(PKTBUF_SEG, open_file->tftp_pktbuf), data, buffersize);
+ goto done;
+
+ } else if (opcode == TFTP_OACK) {
+ /*
+ * Now we need to parse the OACK packet to get the transfer
+ * and packet sizes.
+ */
+ if (!buffersize)
+ goto done; /* No options acked */
+
+ /*
+ * If we find an option which starts with a NUL byte,
+ * (a null option), we're either seeing garbage that some
+ * TFTP servers add to the end of the packet, or we have
+ * no clue how to parse the rest of the packet (what is
+ * an option name and what is a value?) In either case,
+ * discard the rest.
+ */
+ options = packet_buf + 2;
+ if (*options == 0)
+ goto done;
+
+ dst = src = options;
+ while (buffersize--) {
+ if (*src == 0)
+ break; /* found a final null */
+ *dst++ = *src++ | 0x20;
+ if (!buffersize)
+ goto done; /* found no final null */
+ }
+
+ /*
+ * Parse option pointed to by options; guaranteed to be null-terminated
+ */
+ p = options;
+ do {
+ tftp_opt = tftp_options;
+ for (i = 0; i < tftp_opts; i++) {
+ if (!strncmp(p, tftp_opt->str_ptr,tftp_opt->str_len))
+ break;
+ tftp_opt++;
+ }
+ if (i == tftp_opts)
+ goto err_reply; /* Non-negotitated option returned, no idea what it means ...*/
+
+ p += tftp_opt->str_len;
+
+ /* get the address of the filed that we want to write on */
+ data_ptr = (uint32_t *)((char *)file + tftp_opt->offset);
+ *data_ptr = 0;
+
+ /* do convert a number-string to decimal number, just like atoi */
+ while (buffersize--) {
+ if (*p == 0)
+ break; /* found a final null */
+ if (*p > '9')
+ goto err_reply; /* Not a decimal digit */
+ *data_ptr = *data_ptr * 10 + *p++ - '0';
+ }
+
+ }while (buffersize);
+
+ } else {
+ extern char tftp_proto_err[];
+ err_reply:
+
+ uw_pkt.rport = open_file->tftp_remoteport;
+ uw_pkt.buffer[0] = OFFS_WRT(tftp_proto_err, 0);
+ uw_pkt.buffer[1] = 0;
+ uw_pkt.buffersize = 24;
+ pxe_call(PXENV_UDP_WRITE, &uw_pkt);
+ printf("TFTP server sent an incomprehesible reply\n");
+ call16(kaboom, NULL, NULL);
+ }
+
+ filesize = open_file->tftp_filesize;
+
+ done:
+ if (!filesize)
+ free_socket(open_file);
+ file->file_len = filesize;
+ file->open_file = (void *)open_file;
+ return;
+
+ failure:
+ timeout_ptr++;
+ if (*timeout_ptr)
+ goto sendreq; /* Try again */
+}
+
+
+/*
+ * Store standard filename prefix
+ */
+void get_prefix(void)
+{
+ int len;
+ char *p;
+ char c;
+
+ if (DHCPMagic & 0x04) /* Did we get a path prefix option */
+ goto got_prefix;
+
+ strcpy(PathPrefix, BootFile);
+ len = strlen(PathPrefix);
+ p = &PathPrefix[len - 1];
+
+ while (len--) {
+ c = *p--;
+ c |= 0x20;
+
+ c = (c >= '0' && c <= '9') ||
+ (c >= 'a' && c <= 'z') ||
+ (c == '.' || c == '-');
+ if (!c)
+ break;
+ };
+
+ if (len < 0)
+ p --;
+
+ *(p + 2) = 0; /* Zero-terminate after delimiter */
+
+ got_prefix:
+ printf("%s%s\n", tftpprefix_msg, PathPrefix);
+ strcpy(CurrentDirName, PathPrefix);
+}
+
+ /**
+ * try to load a config file, if found, return 1, or return 0
+ *
+ */
+int try_load(com32sys_t *regs)
+{
+ extern char KernelName[];
+ char *config_name = (char *)MK_PTR(regs->ds, regs->edi.w[0]);
+
+ get_prefix();
+
+ printf("Trying to load: %-50s ", config_name);
+ pxe_mangle_name(KernelName, config_name);
+
+ regs->edi.w[0] = OFFS_WRT(KernelName, 0);
+ call16(core_open, regs, regs);
+ if (regs->eflags.l & EFLAGS_ZF) {
+ printf(" [FAILED]\n");
+ return 0;
+ } else {
+ printf(" [ OK ]\n");
+ return 1;
+ }
+}
+
+
+ /*
+ * load configuration file
+ *
+ */
+void pxe_load_config(com32sys_t *regs)
+{
+ extern void no_config(void);
+ char *cfgprefix = "pxelinux.cfg/";
+ char *default_str = "default";
+ char *config_file; /* Pointer to the variable suffix */
+ char *p;
+
+ uint8_t *uuid_ptr;
+
+ int tries = 8;
+ char *last;
+
+ if (DHCPMagic & 0x02) {
+ /* We got a DHCP option, try it first */
+ if (try_load(regs))
+ return;
+ }
+
+ memcpy(ConfigName, cfgprefix, strlen(cfgprefix));
+ config_file = ConfigName + strlen(cfgprefix);
+ /*
+ * Have to guess config file name ...
+ */
+
+ /* Try loading by UUID */
+ if (HaveUUID) {
+ uuid_ptr = uuid_dashes;
+ p = config_file;
+ while (*uuid_ptr) {
+ int len = *uuid_ptr;
+ char *src = UUID;
+
+ lchexbytes(p, src, len);
+ p += len * 2;
+ src += len;
+ uuid_ptr++;
+ *p++ = '-';
+ }
+ /* Remove last dash and zero-terminate */
+ *--p = '\0';
+ regs->edi.w[0] = OFFS_WRT(ConfigName, 0);
+ if (try_load(regs))
+ return;
+ }
+
+ /* Try loading by MAC address */
+ strcpy(config_file, MACStr);
+ regs->edi.w[0] = OFFS_WRT(ConfigName, 0);
+ if (try_load(regs))
+ return;
+
+#if 0
+ printf("MY IP: %p(%X)\n", MyIP, *(uint32_t *)MyIP);
+ #endif
+
+ /* Nope, try hexadecimal IP prefixes... */
+ uchexbytes(config_file, (uint8_t *)&MyIP, 4); /* Convet to hex string */
+ last = &config_file[8];
+ while (tries) {
+ *last = '\0'; /* Zero-terminate string */
+ regs->edi.w[0] = OFFS_WRT(ConfigName, 0);
+ if (try_load(regs))
+ return;
+ last--; /* Drop one character */
+ tries--;
+ };
+
+ /* Final attempt: "default" string */
+ strcpy(config_file, default_str);
+ regs->edi.w[0] = OFFS_WRT(ConfigName, 0);
+ if (try_load(regs))
+ return;
+
+ /* call16(no_config, NULL, NULL); */
+}
+
+
+
+/*
+ * Generate the botif string, and the hardware-based config string
+ */
+void make_bootif_string(void)
+{
+ char *bootif_str = "BOOTIF=";
+ uint8_t *src = &MACType; /* MACType just followed by MAC */
+ char *dst;
+ int i = MACLen + 1; /* MACType included */
+
+ strcpy(BOOTIFStr, bootif_str);
+ dst = strchr(BOOTIFStr, '\0');
+ for (; i > 0; i--) {
+ lchexbytes(dst, src, 1);
+ dst += 2;
+ src += 1;
+ *dst++ = '-';
+ }
+ *(dst - 1) = 0; /* Drop the last '-' and null-terminate string */
+
+#if 0
+ printf("%s\n", BOOTIFStr);
+#endif
+}
+/*
+ ;
+ ; genipopt
+ ;
+ ; Generate an ip=<client-ip>:<boot-server-ip>:<gw-ip>:<netmask>
+ ; option into IPOption based on a DHCP packet in trackbuf.
+ ; Assumes CS == DS == ES.
+ ;
+*/
+void genipopt(void)
+{
+ char *p = IPOption;
+ int ip_len;
+
+ strcpy(p, "ip=");
+ p += 3;
+
+ ip_len = gendotquad(p, MyIP);
+ p += ip_len;
+ *p++ = ':';
+
+ ip_len = gendotquad(p, ServerIP);
+ p += ip_len;
+ *p++ = ':';
+
+ ip_len = gendotquad(p, Gateway);
+ p += ip_len;
+ *p++ = ':';
+
+ ip_len = gendotquad(p, Netmask);
+}
+
+
+/* Generate ip= option and print the ip adress */
+void ip_init(void)
+{
+ int ip = MyIP;
+ char *myipaddr_msg = "My IP address seems to be ";
+
+ genipopt();
+
+ gendotquad(DotQuadBuf, ip);
+
+ ip = ntohl(ip);
+ printf("%s %08X %s\n", myipaddr_msg, ip, DotQuadBuf);
+
+ printf("%s\n", IPOption);
+}
+
+/*
+ * Validity check on possible !PXE structure in buf
+ * return 1 for success, 0 for failure.
+ *
+ */
+int is_pxe(char *buf)
+{
+ int i = buf[4];
+ uint8_t sum = 0;
+
+ if (memcmp(buf, "!PXE", 4) || i < 0x58)
+ return 0;
+
+ while (i--)
+ sum += *buf++;
+
+ if (sum == 0)
+ return 1;
+ else
+ return 0;
+}
+
+/**
+ * Just like is_pxe, it checks PXENV+ structure
+ *
+ */
+int is_pxenv(char *buf)
+{
+ int i = buf[8];
+ uint8_t sum = 0;
+
+ if (memcmp(buf, "PXENV+", 6) || i < 0x28)
+ return 0;
+
+ while (i--)
+ sum += *buf++;
+
+ if (sum == 0)
+ return 1;
+ else
+ return 0;
+}
+
+
+
+/*********************************************************************
+;
+; memory_scan_for_pxe_struct:
+; memory_scan_for_pxenv_struct:
+;
+; If none of the standard methods find the !PXE/PXENV+ structure,
+; look for it by scanning memory.
+;
+; On exit, if found:
+; ZF = 1, ES:BX -> !PXE structure
+; Otherwise:
+; ZF = 0
+;
+; Assumes DS == CS
+; Clobbers AX, BX, CX, DX, SI, ES
+;
+ ********************************************************************/
+
+inline int memory_scan(uint16_t seg, int (*func)(char *))
+{
+ while (seg < 0xA000) {
+ if (func(MK_PTR(seg, 0)))
+ return 1; /* found it */
+ seg++;
+ }
+ return 0;
+}
+
+int memory_scan_for_pxe_struct()
+{
+ extern uint16_t BIOS_fbm; /* Starting segment */
+ uint16_t seg = BIOS_fbm << (10 - 4);
+
+ return memory_scan(seg, is_pxe);
+}
+
+int memory_scan_for_pxenv_struct()
+{
+ uint16_t seg = 0x1000;
+
+ return memory_scan(seg, is_pxenv);
+}
+
+/*
+ * Find the !PXE structure; we search for the following, in order:
+ *
+ * a. !PXE structure as SS:[SP + 4]
+ * b. PXENV+ structure at [ES:BX]
+ * c. INT 1Ah AX=0x5650 -> PXENV+
+ * d. Search memory for !PXE
+ * e. Search memory for PXENV+
+ *
+ * If we find a PXENV+ structure, we try to find a !PXE structure from
+ * if if the API version is 2.1 or later
+ *
+ */
+void pxe_init()
+{
+ char plan = 'A';
+ uint16_t seg, off;
+ uint16_t code_seg, code_len;
+ uint16_t data_seg, data_len;
+ char *base = GET_PTR(InitStack);
+
+ /* Assume API version 2.1 */
+ APIVer = 0x201;
+
+ /* Plan A: !PXE structure as SS:[SP + 4] */
+ off = *(uint16_t *)(base + 48);
+ seg = *(uint16_t *)(base + 50);
+ if (is_pxe(MK_PTR(seg, off)))
+ goto have_pxe;
+
+ /* Plan B: PXENV+ structure at [ES:BX] */
+ plan++;
+ off = *(uint16_t *)(base + 24); /* Original BX */
+ seg = *(uint16_t *)(base + 4); /* Original ES */
+ if (is_pxenv(MK_PTR(seg, off)))
+ goto have_pxenv;
+
+ /*
+ * Plan C: PXENV+ structure via INT 1Ah AX=5650h
+ *
+ * for now, we just skip it since it must be run in Real mode
+ */
+ extern void plan_c(void);
+ plan++;
+#if 0
+ goto plan_D;
+ call16(plan_c, regs, regs);
+ if (((regs->eflags.l & EFLAGS_CF) == 0) && (regs->eax.w[0] == 0x564e)) {
+ seg = regs->es;
+ off = regs->ebx.w[0];
+ if (is_pxenv(MK_PTR(seg, off)))
+ goto have_pxenv;
+ }
+
+ plan_D:
+#endif
+ /* Plan D: !PXE memory scan */
+ plan++;
+ if (memory_scan_for_pxe_struct())
+ goto have_pxe;
+
+ /* Plan E: PXENV+ memory scan */
+ plan++;
+ if (memory_scan_for_pxenv_struct())
+ goto have_pxenv;
+
+ /* Found nothing at all !! */
+ printf("%s\n", err_nopxe);
+ call16(kaboom, NULL, NULL);
+
+ have_pxenv:
+ StructPtr[0] = off;
+ StructPtr[1] = seg;
+ base = MK_PTR(seg, off);
+ APIVer = *(uint16_t *)(base + 6);
+ printf("Found PXENV+ structure\nPXE API version is %04x\n", APIVer);
+
+ /* if the API version number is 0x0201 or higher, use the !PXE structure */
+ if (APIVer >= 0x201) {
+ if (*(char *)(base + 8) < 0x2c) { /* Length of PXENV+ structure in bytes */
+ if (memory_scan_for_pxe_struct())
+ goto have_pxe;
+ }
+
+ off = *(uint16_t *)(base + 0x28);
+ seg = *(uint16_t *)(base + 0x2a);
+ if (is_pxe(MK_PTR(seg, off)))
+ goto have_pxe;
+ /*
+ * Nope, !PXE structuremissing despite API 2.1+, or at least
+ * the pointer is missing. Do a last-ditch attempt to find it
+ */
+ if (memory_scan_for_pxe_struct())
+ goto have_pxe;
+ }
+
+ /* Otherwise, no dice, use PXENV+ structure */
+ data_len = *(uint16_t *)(base + 0x22); /* UNDI data len */
+ data_seg = *(uint16_t *)(base + 0x20); /* UNDI data seg */
+ code_len = *(uint16_t *)(base + 0x26); /* UNDI code len */
+ code_seg = *(uint16_t *)(base + 0x24); /* UNDI code seg */
+ PXEEntry = *(far_ptr_t *)(base + 0x0a); /* PXENV+ entry point */
+ printf("PXENV+ entry point found (we hope) at ");
+ goto have_entrypoint;
+
+ have_pxe:
+ StructPtr[0] = off;
+ StructPtr[1] = seg;
+ base = MK_PTR(seg, off);
+
+ data_len = *(uint16_t *)(base + 0x2e); /* UNDI data len */
+ data_seg = *(uint16_t *)(base + 0x28); /* UNDI data seg */
+ code_len = *(uint16_t *)(base + 0x36); /* UNDI code len */
+ code_seg = *(uint16_t *)(base + 0x30); /* UNDI code seg */
+ PXEEntry = *(far_ptr_t *)(base + 0x10); /* !PXE entry point */
+ printf("!PXE entry point found (we hope) at ");
+
+ have_entrypoint:
+ printf("%04X:%04X via plan %c\n", PXEEntry.seg, PXEEntry.offs, plan);
+ printf("UNDI code segment at %04X len %04X\n", code_seg, code_len);
+ code_seg = code_seg + ((code_len + 15) >> 4);
+
+ printf("UNDI data segment at %04X len %04X\n", data_seg, data_len);
+ data_seg = data_seg + ((data_len + 15) >> 4);
+
+
+ RealBaseMem = MAX(code_seg,data_seg) >> 6; /* Convert to kilobytes */
+ //regs->es = regs->ds; /* Restore the es segment */
+}
+
+/*
+ * Initialize UDP stack
+ *
+ */
+void udp_init(void)
+{
+ int err;
+ static __lowmem struct pxe_udp_open_pkt uo_pkt;
+ uo_pkt.sip = MyIP;
+ err = pxe_call(PXENV_UDP_OPEN, &uo_pkt);
+ if (err || uo_pkt.status) {
+ printf("%s", err_udpinit);
+ printf("%d\n", uo_pkt.status);
+ call16(kaboom, NULL, NULL);
+ }
+}
+
+
+/*
+ * Network-specific initialization
+ */
+void network_init()
+{
+ struct bootp_t *bp = (struct bootp_t *)trackbuf;
+ int pkt_len;
+
+ *LocalDomain = 0; /* No LocalDomain received */
+
+ /*
+ * Get the DHCP client identifiers (query info 1)
+ */
+ printf("%s", get_packet_msg);
+ pkt_len = pxe_get_cached_info(1);
+ parse_dhcp(pkt_len);
+ /*
+ ; We don't use flags from the request packet, so
+ ; this is a good time to initialize DHCPMagic...
+ ; Initialize it to 1 meaning we will accept options found;
+ ; in earlier versions of PXELINUX bit 0 was used to indicate
+ ; we have found option 208 with the appropriate magic number;
+ ; we no longer require that, but MAY want to re-introduce
+ ; it in the future for vendor encapsulated options.
+ */
+ *(char *)&DHCPMagic = 1;
+
+
+ /*
+ * Get the BOOTP/DHCP packet that brought us file (and an IP
+ * address). This lives in the DHCPACK packet (query info 2)
+ */
+ pkt_len = pxe_get_cached_info(2);
+ parse_dhcp(pkt_len);
+ /*
+ * 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
+ */
+ MACLen = bp->hardlen > 16 ? 0 : bp->hardlen;
+ MACType = bp->hardware;
+ memcpy(MAC, bp->macaddr, MACLen);
+
+
+ /*
+ * Get the boot file and other info. This lives in the CACHED_REPLY
+ * packet (query info 3)
+ */
+ pkt_len = pxe_get_cached_info(3);
+ parse_dhcp(pkt_len);
+
+ printf("\n");
+
+
+
+ make_bootif_string();
+ ip_init();
+
+ /*
+ * 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.
+ */
+ if ((DHCPMagic & 1) == 0)
+ DHCPMagic = 0;
+
+ udp_init();
+}
+
+/*
+ * Initialize pxe fs
+ *
+ */
+int pxe_fs_init(struct fs_info *fs)
+{
+ fs = NULL; /* drop the compile warning message */
+
+ /* do the pxe initialize */
+ pxe_init();
+
+ /* Network-specific initialization */
+ network_init();
+
+ return 0;
+}
+
+const struct fs_ops pxe_fs_ops = {
+ .fs_name = "pxe",
+ .fs_init = pxe_fs_init,
+ .searchdir = pxe_searchdir,
+ .getfssec = pxe_getfssec,
+ .mangle_name = pxe_mangle_name,
+ .unmangle_name = NULL,
+ .load_config = pxe_load_config
+};
; 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
+ global Files
alignb open_file_t_size
Files resb MAX_OPEN*open_file_t_size
alignb FILENAME_MAX
+ global BootFile, PathPrefix, DotQuadBuf, IPOption
BootFile resb 256 ; Boot file from DHCP packet
PathPrefix resb 256 ; Path prefix derived from boot file
DotQuadBuf resb 16 ; Buffer for dotted-quad IP address
IPOption resb 80 ; ip= option buffer
-InitStack resd 1 ; Pointer to reset stack (SS:SP)
PXEStack resd 1 ; Saved stack during PXE call
alignb 4
+ global DHCPMagic, OverLoad, RebootTime, APIVer, RealBaseMem
RebootTime resd 1 ; Reboot timeout, if set by option
StrucPtr resd 1 ; Pointer to PXENV+ or !PXE structure
APIVer resw 1 ; PXE API version found
DHCPMagic resb 1 ; PXELINUX magic flags
; The relative position of these fields matter!
+ global MACStr, MACLen, MACType, MAC, BOOTIFStr
MAC_MAX equ 32 ; Handle hardware addresses this long
MACLen resb 1 ; MAC address len
MACType resb 1 ; MAC address type
MACStr resb 3*(MAC_MAX+1) ; MAC address as a string
; The relative position of these fields matter!
+ global UUID
UUIDType resb 1 ; Type byte from DHCP option
UUID resb 16 ; UUID, from the PXE stack
UUIDNull resb 1 ; dhcp_copyoption zero-terminates
; BOOTP/DHCP packet buffer
section .bss16
+ global packet_buf
alignb 16
packet_buf resb 2048 ; Transfer packet
packet_buf_size equ $-packet_buf
call writestr_early
;
-; 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.
+; do fs initialize
;
- mov word [APIVer],0201h
+ extern pxe_fs_ops
+ mov eax,pxe_fs_ops
+ pm_call fs_init
+ jmp next
;
-; Now we need to find the !PXE structure.
-; We search for the following, in order:
-;
-; a. !PXE structure as SS:[SP+4]
-; b. PXENV+ structure at [ES:BX]
-; c. INT 1Ah AX=5650h -> PXENV+
-; d. Search memory for !PXE
-; e. Search memory for PXENV+
+; Plan C, because it must be in real mode, we set it here
;
-; If we find a PXENV+ structure, we try to find a !PXE structure from
-; it if the API version is 2.1 or later.
-;
- ; Plan A: !PXE structure as SS:[SP+4]
- lgs bp,[InitStack] ; GS:BP -> original stack
- les bx,[gs:bp+48]
- call is_pxe
- je have_pxe
-
- ; Plan B: PXENV+ structure at [ES:BX]
- inc byte [plan]
- mov bx,[gs:bp+24] ; Original BX
- mov es,[gs:bp+4] ; Original ES
- call is_pxenv
- je have_pxenv
-
+ global plan_c
+plan_c:
+
; Plan C: PXENV+ structure via INT 1Ah AX=5650h
- inc byte [plan]
mov ax, 5650h
%if USE_PXE_PROVIDED_STACK == 0
lss sp,[InitStack]
lss esp,[BaseStack]
%endif
sti ; Work around Etherboot bug
+ ret
- jc no_int1a
- cmp ax,564Eh
- jne no_int1a
-
- call is_pxenv
- je have_pxenv
-
-no_int1a:
- ; Plan D: !PXE memory scan
- inc byte [plan]
- call memory_scan_for_pxe_struct ; !PXE scan
- je have_pxe
-
- ; Plan E: PXENV+ memory scan
- inc byte [plan]
- call memory_scan_for_pxenv_struct ; PXENV+ scan
- je have_pxenv
-
- ; Found nothing at all!!
-no_pxe:
- mov si,err_nopxe
- call writestr_early
- jmp kaboom
-
-have_pxenv:
- mov [StrucPtr],bx
- mov [StrucPtr+2],es
-
- mov si,found_pxenv
- call writestr_early
-
- mov si,apiver_str
- call writestr_early
- mov ax,[es:bx+6]
- mov [APIVer],ax
- call writehex4
- call crlf
-
- cmp ax,0201h ; API version 2.1 or higher
- jb .old_api
- cmp byte [es:bx+8],2Ch ; Space for !PXE pointer?
- jb .pxescan
- les bx,[es:bx+28h] ; !PXE structure pointer
- call is_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.
-.pxescan:
- call memory_scan_for_pxe_struct
- je have_pxe
-
- ; Otherwise, no dice, use PXENV+ structure
-.old_api:
- les bx,[StrucPtr]
- push word [es:bx+22h] ; UNDI data len
- push word [es:bx+20h] ; UNDI data seg
- push word [es:bx+26h] ; UNDI code len
- push word [es:bx+24h] ; UNDI code seg
- push dword [es:bx+0Ah] ; PXENV+ entry point
-
- mov si,pxenventry_msg
- jmp have_entrypoint
-
-have_pxe:
- mov [StrucPtr],bx
- mov [StrucPtr+2],es
-
- push word [es:bx+2Eh] ; UNDI data len
- push word [es:bx+28h] ; UNDI data seg
- push word [es:bx+36h] ; UNDI code len
- push word [es:bx+30h] ; UNDI code seg
- push dword [es:bx+10h] ; !PXE entry point
-
- mov si,pxeentry_msg
-
-have_entrypoint:
- push cs
- pop es ; Restore CS == DS == ES
-
- call writestr_early ; !PXE or PXENV+ entry found
-
- pop dword [PXEEntry]
- mov ax,[PXEEntry+2]
- call writehex4
- mov al,':'
- call writechr
- mov ax,[PXEEntry]
- call writehex4
-
- mov si,viaplan_msg
- call writestr_early
-
- mov si,undi_code_msg
- call writestr_early
- pop ax ; UNDI code segment
- call writehex4
- xchg dx,ax
- mov si,len_msg
- call writestr_early
- pop ax ; UNDI code length
- call writehex4
- call crlf
- add ax,15
- shr ax,4
- add dx,ax ; DX = seg of end of UNDI code
-
- mov si,undi_data_msg
- call writestr_early
- pop ax ; UNDI data segment
- call writehex4
- xchg bx,ax
- mov si,len_msg
- call writestr_early
- pop ax ; UNDI data length
- call writehex4
- call crlf
- add ax,15
- shr ax,4
- add ax,bx ; AX = seg of end of UNDI data
-
- cmp ax,dx
- ja .data_on_top
- xchg ax,dx
-.data_on_top:
- ; Could we safely add 63 here before the shift?
- shr ax,6 ; Convert to kilobytes
- mov [RealBaseMem],ax
-
-
-;
-; Network-specific initialization
-;
- xor ax,ax
- mov [LocalDomain],al ; No LocalDomain received
-
-;
-; The DHCP client identifiers are best gotten from the DHCPREQUEST
-; packet (query info 1).
-;
-query_bootp_1:
- mov si,get_packet_msg
- call writestr_early
-
- mov dl,1
- call pxe_get_cached_info
- call parse_dhcp
-
- ; We don't use flags from the request packet, so
- ; this is a good time to initialize DHCPMagic...
- ; Initialize it to 1 meaning we will accept options found;
- ; in earlier versions of PXELINUX bit 0 was used to indicate
- ; we have found option 208 with the appropriate magic number;
- ; we no longer require that, but MAY want to re-introduce
- ; it in the future for vendor encapsulated options.
- mov byte [DHCPMagic],1
-
-;
-; 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_2:
- mov dl,2
- call pxe_get_cached_info
- 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]
- cmp cx,16
- jna .mac_ok
- xor cx,cx ; Bad hardware address length
-.mac_ok:
- mov [MACLen],cl
- mov al,[trackbuf+bootp.hardware]
- mov [MACType],al
- mov si,trackbuf+bootp.macaddr
- mov di,MAC
- rep movsb
-
-; Enable this if we really need to zero-pad this field...
-; mov cx,MAC+MAC_MAX+1
-; sub cx,di
-; xor ax,ax
-; rep stosb
-
-;
-; Now, get the boot file and other info. This lives in the CACHED_REPLY
-; packet (query info 3).
-;
-query_bootp_3:
- mov dl,3
- call pxe_get_cached_info
- call parse_dhcp ; Parse DHCP packet
- call crlf
-
-;
-; Generate the bootif string, and the hardware-based config string.
-;
-make_bootif_string:
- mov si,bootif_str
- mov di,BOOTIFStr
- mov cx,bootif_str_len
- rep movsb
-
- movzx cx,byte [MACLen]
- mov si,MACType
- inc cx
-.hexify_mac:
- push cx
- mov cl,1 ; CH == 0 already
- call lchexbytes
- mov al,'-'
- stosb
- pop cx
- loop .hexify_mac
- mov [di-1],cl ; Null-terminate and strip final dash
-;
-; Generate ip= option
-;
- call genipopt
-
-;
-; Print IP address
-;
- mov eax,[MyIP]
- mov di,DotQuadBuf
- push di
- call gendotquad ; This takes network byte order input
-
- 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_early
- call writehex8
- mov al,' '
- call writechr
- pop si ; DotQuadBuf
- call writestr_early
- call crlf
-
- mov si,IPOption
- call writestr_early
- 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_early
- jmp kaboom
-.success:
+next:
;
; Common initialization code
;
;
;
-; 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_early
- mov si,PathPrefix
- call writestr_early
- call crlf
-
- ; Set CurrentDirName
- push di
- mov si,PathPrefix
- mov di,CurrentDirName
- call strcpy
- pop di
-
-;
; Load configuration file
;
-find_config:
-
-;
-; Begin looking for configuration file
-;
-config_scan:
- test byte [DHCPMagic], 02h
- jz .no_option
-
- ; We got a DHCP option, try it first
- call .try
- jnz .success
-
-.no_option:
- mov di,ConfigName
- mov si,cfgprefix
- mov cx,cfgprefix_len
- rep movsb
-
- ; Have to guess config file name...
-
- ; Try loading by UUID.
- cmp byte [HaveUUID],0
- je .no_uuid
-
- push di
- mov bx,uuid_dashes
- mov si,UUID
-.gen_uuid:
- movzx cx,byte [bx]
- jcxz .done_uuid
- inc bx
- call lchexbytes
- mov al,'-'
- stosb
- jmp .gen_uuid
-.done_uuid:
- mov [di-1],cl ; Remove last dash and zero-terminate
- pop di
- call .try
- jnz .success
-.no_uuid:
-
- ; Try loading by MAC address
- push di
- mov si,MACStr
- call strcpy
- pop di
- call .try
- jnz .success
-
- ; Nope, try hexadecimal IP prefixes...
-.scan_ip:
- mov cx,4
- mov si,MyIP
- call uchexbytes ; Convert to hex string
-
- mov cx,8 ; Up to 8 attempts
-.tryagain:
- mov byte [di],0 ; Zero-terminate string
- call .try
- jnz .success
- dec di ; Drop one character
- loop .tryagain
-
- ; Final attempt: "default" string
- mov si,default_str ; "default" string
- call strcpy
- call .try
- jnz .success
-
- mov si,err_noconfig
- call writestr_early
- jmp kaboom
-
-.try:
- pusha
- mov si,trying_msg
- call writestr_early
- mov di,ConfigName
- mov si,di
- call writestr_early
- call crlf
- mov si,di
- mov di,KernelName ; Borrow this buffer for mangled name
- call mangle_name
- call open
- popa
- ret
-
-
-.success:
+ pm_call load_config
;
; Linux kernel loading code is common. However, we need to define
; kaboom: write a message and bail out. Wait for quite a while,
; or a user keypress, then do a hard reboot.
;
+ global no_config, kaboom
+; set the no_config kaboom here
+no_config:
+ mov si, err_noconfig
+ call writestr_early
kaboom:
RESET_STACK_AND_SEGS AX
.patch: mov si,bailmsg
mov word [BIOS_magic],0 ; Cold reboot
jmp 0F000h:0FFF0h ; Reset vector address
-;
-; memory_scan_for_pxe_struct:
-; memory_scan_for_pxenv_struct:
-;
-; If none of the standard methods find the !PXE/PXENV+ structure,
-; look for it by scanning memory.
-;
-; On exit, if found:
-; ZF = 1, ES:BX -> !PXE structure
-; Otherwise:
-; ZF = 0
-;
-; Assumes DS == CS
-; Clobbers AX, BX, CX, DX, SI, ES
-;
-memory_scan_for_pxe_struct:
- mov dx,is_pxe
- mov ax,[BIOS_fbm] ; Starting segment
- shl ax,(10-4) ; Kilobytes -> paragraphs
- jmp memory_scan_common
-
-memory_scan_for_pxenv_struct:
- mov ax,1000h ; Starting segment
- mov dx,is_pxenv
- ; fall through
-
-memory_scan_common:
- dec ax ; To skip inc ax
-.mismatch:
- inc ax
- cmp ax,0A000h-1 ; End of memory
- ja .not_found ; ZF = 0 on not found
- mov es,ax
- xor bx,bx
- call dx
- jne .mismatch
-.not_found:
- ret
-
-;
-; is_pxe:
-; Validity check on possible !PXE structure in ES:BX
-; is_pxenv:
-; Validity check on possible PXENV+ structure in ES:BX
-;
-; Return ZF = 1 on success
-;
-; Clobbers CX and SI
-;
-is_struc:
-.pxe:
- cmp dword [es:bx],'!PXE'
- jne .bad
- movzx cx,byte [es:bx+4]
- cmp cx,58h
- jae .checksum
- ret
-.pxenv:
- cmp dword [es:bx],'PXEN'
- jne .bad
- cmp word [es:bx+4],'V+'
- jne .bad
- movzx cx,[es:bx+8]
- cmp cx,28h
- jb .bad
-.checksum:
- push ax
- mov si,bx
- xor ax,ax
-.loop:
- es lodsb
- add ah,al
- loop .loop
- pop ax
-.bad:
- ret
-
-is_pxe equ is_struc.pxe
-is_pxenv equ is_struc.pxenv
-
-;
-; close_file:
-; Deallocates a file structure (pointer in SI)
-; Assumes CS == DS.
-;
-; XXX: We should check to see if this file is still open on the server
-; side and send a courtesy ERROR packet to the server.
-;
-close_file:
- and si,si
- jz .closed
- mov word [si],0 ; Not in use
-.closed: ret
-
-;
-; searchdir:
-;
-; Open a TFTP connection to the server
-;
-; On entry:
-; DS:DI = mangled filename
-; If successful:
-; ZF clear
-; SI = socket pointer
-; EAX = file length in bytes, or -1 if unknown
-; 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,TimeoutTable ; Reset timeout
-
-.sendreq: push ax ; [bp-2] - Timeout pointer
- 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
-%if GPXE
- mov si,packet_buf+2
- call is_gpxe
- jnc .gpxe
-%endif
-
- 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.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: mov bx,[bp-2]
- movzx bx,byte [bx]
- push bx ; [bp-10] - timeout in ticks
- push word [BIOS_timer] ; [bp-12]
-
-.pkt_loop: mov bx,[bp-8] ; TID
- mov di,packet_buf
- 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
- jnc .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]
- jnz .pkt_loop
- pop ax ; Adjust stack
- pop ax
- jmp .failure
-
-.got_packet:
- mov si,[bp-6] ; TFTP pointer
- mov bx,[bp-8] ; TID
-
- ; Make sure the packet actually came from the server
- ; This is technically not to the TFTP spec?
- mov eax,[si+tftp_remoteip]
- cmp [pxe_udp_read_pkt.sip],eax
- jne .no_packet
-
- ; Got packet - reset timeout
- mov word [bp-2],TimeoutTable
-
- 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
-
- movzx ecx,word [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
-
- ; If the server doesn't support any options, we'll get
- ; a DATA reply instead of OACK. Stash the data in
- ; the file buffer and go with the default value for
- ; all options...
- cmp ax, TFTP_DATA
- je .no_oack
-
- cmp ax, TFTP_OACK
- jne .err_reply ; Unknown packet type
-
- ; Now we need to parse the OACK packet to get the transfer
- ; and packet sizes.
- ; SI -> first byte of options; [E]CX -> byte count
-.parse_oack:
- jcxz .done_pkt ; 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]
-.got_file: ; SI->socket structure, EAX = size
- and eax,eax ; Set ZF depending on file size
- jz .error_si ; ZF = 1 need to free the socket
-.ret:
- leave ; SP <- BP, POP BP
- pop cx
- pop bx
- pop es
- ret
-
-
-.no_oack: ; We got a DATA packet, meaning no options are
- ; suported. Save the data away and consider the length
- ; undefined, *unless* this is the only data packet...
- mov bx,[bp-6] ; File pointer
- sub cx,2 ; Too short?
- jb .failure
- lodsw ; Block number
- cmp ax,htons(1)
- jne .failure
- mov [bx+tftp_lastpkt],ax
- cmp cx,TFTP_BLOCKSIZE
- ja .err_reply ; Corrupt...
- je .not_eof
- ; This was the final EOF packet, already...
- ; We know the filesize, but we also want to ack the
- ; packet and set the EOF flag.
- mov [bx+tftp_filesize],ecx
- mov byte [bx+tftp_goteof],1
- push si
- mov si,bx
- ; AX = htons(1) already
- call ack_packet
- pop si
-.not_eof:
- mov [bx+tftp_bytesleft],cx
- mov ax,pktbuf_seg
- push es
- mov es,ax
- mov di,tftp_pktbuf
- mov [bx+tftp_dataptr],di
- add cx,3
- shr cx,2
- rep movsd
- pop es
- jmp .done_pkt
-
-.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_damage
- call writestr_early
- jmp kaboom
-
-.bailnow:
- ; Immediate error - no retry
- mov word [bp-2],TimeoutTableEnd-1
-
-.failure: pop bx ; Junk
- pop bx
- pop si
- pop ax
- inc ax
- cmp ax,TimeoutTableEnd
- jb .sendreq ; Try again
-
-.error: mov si,bx ; Socket pointer
-.error_si: ; Socket pointer already in SI
- call free_socket ; ZF <- 1, SI <- 0
- jmp .ret
-
-
-%if GPXE
-.gpxe:
- push bx ; Socket pointer
- mov di,gpxe_file_open
- mov word [di],2 ; PXENV_STATUS_BAD_FUNC
- mov word [di+4],packet_buf+2 ; Completed URL
- mov [di+6],ds
- mov bx,PXENV_FILE_OPEN
- call pxenv
- pop si ; Socket pointer in SI
- jc .error_si
-
- mov ax,[di+2]
- mov word [si+tftp_localport],-1 ; gPXE URL
- mov [si+tftp_remoteport],ax
- mov di,gpxe_get_file_size
- mov [di+2],ax
-
-%if 0
- ; Disable this for now since gPXE doesn't always
- ; return valid information in PXENV_GET_FILE_SIZE
- mov bx,PXENV_GET_FILE_SIZE
- call pxenv
- mov eax,[di+4] ; File size
- jnc .oksize
-%endif
- or eax,-1 ; Size unknown
-.oksize:
- mov [si+tftp_filesize],eax
- jmp .got_file
-%endif ; GPXE
-
-;
-; 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,Files
- mov cx,MAX_OPEN
-.check: cmp word [bx], byte 0
- je .found
- add bx,open_file_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_OPEN_LG2))-1) | 0xC000
- mov [NextSocket],ax
- shl cx,13-MAX_OPEN_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_pktbuf >> 1 ; tftp_pktbuf is not cleared
- rep stosw
- popa
- pop es
- xor si,si
- ret
-
-;
-; parse_dotquad:
-; Read a dot-quad pathname in DS:SI and output an IP
-; address in EAX, with SI pointing to the first
-; nonmatching character.
-;
-; Return CF=1 on error.
-;
-; No segment assumptions permitted.
-;
-parse_dotquad:
- push cx
- mov cx,4
- xor eax,eax
-.parseloop:
- mov ch,ah
- mov ah,al
- lodsb
- sub al,'0'
- jb .notnumeric
- cmp al,9
- ja .notnumeric
- aad ; AL += 10 * AH; AH = 0;
- xchg ah,ch
- jmp .parseloop
-.notnumeric:
- cmp al,'.'-'0'
- pushf
- mov al,ah
- mov ah,ch
- xor ch,ch
- ror eax,8
- popf
- jne .error
- loop .parseloop
- jmp .done
-.error:
- loop .realerror ; If CX := 1 then we're done
- clc
- jmp .done
-.realerror:
- stc
-.done:
- dec si ; CF unchanged!
- pop cx
- ret
-
-;
-; is_url: Return CF=0 if and only if the buffer pointed to by
-; DS:SI is a URL (contains ://). No registers modified.
-;
-%if GPXE
-is_url:
- push si
- push eax
-.loop:
- mov eax,[si]
- inc si
- and al,al
- jz .not_url
- and eax,0FFFFFFh
- cmp eax,'://'
- jne .loop
-.done:
- ; CF=0 here
- pop eax
- pop si
- ret
-.not_url:
- stc
- jmp .done
-
-;
-; is_gpxe: Return CF=0 if and only if the buffer pointed to by
-; DS:SI is a URL (contains ://) *and* the gPXE extensions
-; API is available. No registers modified.
-;
-is_gpxe:
- call is_url
- jc .ret ; Not a URL, don't bother
-.again:
- cmp byte [HasGPXE],1
- ja .unknown
- ; CF=1 if not available (0),
- ; CF=0 if known available (1).
-.ret: ret
-
-.unknown:
- ; If we get here, the gPXE status is unknown.
- push es
- pushad
- push ds
- pop es
- mov di,gpxe_file_api_check
- mov bx,PXENV_FILE_API_CHECK ; BH = 0
- call pxenv
- jc .nogood
- cmp dword [di+4],0xe9c17b20
- jne .nogood
- mov ax,[di+12] ; Don't care about the upper half...
- not ax ; Set bits of *missing* functions...
- and ax,01001011b ; The functions we care about
- setz bh
- jz .done
-.nogood:
- mov si,gpxe_warning_msg
- call writestr_early
-.done:
- mov [HasGPXE],bh
- popad
- pop es
- jmp .again
-
- section .data16
-gpxe_warning_msg:
- db 'URL syntax, but gPXE extensions not detected, '
- db 'trying plain TFTP...', CR, LF, 0
-HasGPXE db -1 ; Unknown
- section .text16
-
-%endif
-
-;
-; mangle_name: Mangle a filename pointed to by DS:SI into a buffer pointed
-; to by ES:DI; ends on encountering any whitespace.
-; DI is preserved.
-;
-; 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.
-;
-; The first four bytes of the manged name is the IP address of
-; the download host, 0 for no host, or -1 for a gPXE URL.
-;
-; No segment assumptions permitted.
-;
-mangle_name:
- push di
-%if GPXE
- call is_url
- jc .not_url
- or eax,-1 ; It's a URL
- jmp .prefix_done
-.not_url:
-%endif ; GPXE
- push si
- mov eax,[cs:ServerIP]
- cmp byte [si],0
- je .noip ; Null filename?!?!
- cmp word [si],'::' ; Leading ::?
- je .gotprefix
-
-.more:
- inc si
- cmp byte [si],0
- je .noip
- cmp word [si],'::'
- jne .more
-
- ; We have a :: prefix of some sort, it could be either
- ; a DNS name or a dot-quad IP address. Try the dot-quad
- ; first...
-.here:
- pop si
- push si
- call parse_dotquad
- jc .notdq
- cmp word [si],'::'
- je .gotprefix
-.notdq:
- pop si
- push si
- call dns_resolv
- cmp word [si],'::'
- jne .noip
- and eax,eax
- jnz .gotprefix
-
-.noip:
- pop si
- xor eax,eax
- jmp .prefix_done
-
-.gotprefix:
- pop cx ; Adjust stack
- inc si ; Skip double colon
- inc si
-
-.prefix_done:
- stosd ; Save IP address prefix
- mov cx,FILENAME_MAX-5
-
-.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
- pop di
- ret ; Done
;
; unmangle_name: Does the opposite of mangle_name; converts a DOS-mangled
jz .noip
cmp eax,-1
jz .noip ; URL
- call gendotquad
+ pm_call gendotquad
mov ax,'::'
stosw
.noip:
;
; While we're at it, save and restore all registers.
;
+ global pxenv
pxenv:
pushfd
pushad
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
-
-;
-; getfssec: Get multiple clusters from a file, given the starting cluster.
-;
-; In this case, get multiple blocks from a specific TCP connection.
-;
-; On entry:
-; ES:BX -> Buffer
-; SI -> TFTP socket pointer
-; CX -> 512-byte block count; 0FFFFh = until end of file
-; On exit:
-; SI -> TFTP socket pointer (or 0 on EOF)
-; CF = 1 -> Hit EOF
-; ECX -> number of bytes actually read
-;
-getfssec:
- push eax
- push edi
- push bx
- push si
- push fs
- mov di,bx
- mov ax,pktbuf_seg
- mov fs,ax
-
- xor eax,eax
- movzx ecx,cx
- shl ecx,TFTP_BLOCKSIZE_LG2 ; Convert to bytes
- push ecx ; Initial request size
- jz .hit_eof ; Nothing to do?
-
-.need_more:
- call fill_buffer
- movzx eax,word [si+tftp_bytesleft]
- and ax,ax
- jz .hit_eof
-
- push ecx
- cmp ecx,eax
- jna .ok_size
- mov ecx,eax
-.ok_size:
- mov ax,cx ; EAX<31:16> == ECX<31:16> == 0
- mov bx,[si+tftp_dataptr]
- sub [si+tftp_bytesleft],cx
- xchg si,bx
- fs rep movsb ; Copy from packet buffer
- xchg si,bx
- mov [si+tftp_dataptr],bx
-
- pop ecx
- sub ecx,eax
- jnz .need_more
-
-.hit_eof:
- call fill_buffer
-
- pop eax ; Initial request amount
- xchg eax,ecx
- sub ecx,eax ; ... minus anything not gotten
-
- pop fs
- pop si
-
- ; Is there anything left of this?
- mov eax,[si+tftp_filesize]
- sub eax,[si+tftp_filepos]
- jnz .bytes_left
-
- cmp [si+tftp_bytesleft],ax ; AX == 0
- jne .bytes_left
-
- cmp byte [si+tftp_goteof],0
- je .done
- ; I'm 99% sure this can't happen, but...
- call fill_buffer ; Receive/ACK the EOF packet
-.done:
- ; The socket is closed and the buffer drained
- ; Close socket structure and re-init for next user
- call free_socket
- stc
- jmp .ret
-.bytes_left:
- clc
-.ret:
- pop bx
- pop edi
- pop eax
- ret
-
-;
-; Get a fresh packet if the buffer is drained, and we haven't hit
-; EOF yet. The buffer should be filled immediately after draining!
-;
-; expects fs -> pktbuf_seg and ds:si -> socket structure
-;
-fill_buffer:
- cmp word [si+tftp_bytesleft],0
- je .empty
- ret ; Otherwise, nothing to do
-
-.empty:
- push es
- pushad
- mov ax,ds
- mov es,ax
-
- ; Note: getting the EOF packet is not the same thing
- ; as tftp_filepos == tftp_filesize; if the EOF packet
- ; is empty the latter condition can be true without
- ; having gotten the official EOF.
- cmp byte [si+tftp_goteof],0
- jne .ret ; Already EOF
-
-%if GPXE
- cmp word [si+tftp_localport], -1
- jne .get_packet_tftp
- call get_packet_gpxe
- jmp .ret
-.get_packet_tftp:
-%endif ; GPXE
-
- ; TFTP code...
-.packet_loop:
- ; Start by ACKing the previous packet; this should cause the
- ; next packet to be sent.
- mov bx,TimeoutTable
-
-.send_ack: push bx ; <D> Retry pointer
- movzx cx,byte [bx] ; Timeout
-
- mov ax,[si+tftp_lastpkt]
- call ack_packet ; Send ACK
-
- ; We used to test the error code here, but sometimes
- ; PXE would return negative status even though we really
- ; did send the ACK. Now, just treat a failed send as
- ; a normally lost packet, and let it time out in due
- ; course of events.
-
-.send_ok: ; Now wait for packet.
- mov dx,[BIOS_timer] ; Get current time
-
-.wait_data: push cx ; <E> Timeout
- push dx ; <F> Old time
-
- mov bx,[si+tftp_pktbuf]
- mov [pxe_udp_read_pkt.buffer],bx
- mov [pxe_udp_read_pkt.buffer+2],fs
- mov [pxe_udp_read_pkt.buffersize],word PKTBUF_SIZE
- mov eax,[si+tftp_remoteip]
- mov [pxe_udp_read_pkt.sip],eax
- mov eax,[MyIP]
- mov [pxe_udp_read_pkt.dip],eax
- mov ax,[si+tftp_remoteport]
- mov [pxe_udp_read_pkt.rport],ax
- mov ax,[si+tftp_localport]
- mov [pxe_udp_read_pkt.lport],ax
- mov di,pxe_udp_read_pkt
- mov bx,PXENV_UDP_READ
- call pxenv
- jnc .recv_ok
-
- ; No packet, or receive failure
- mov dx,[BIOS_timer]
- pop ax ; <F> Old time
- pop cx ; <E> Timeout
- cmp ax,dx ; Same time -> don't advance timeout
- je .wait_data ; Same clock tick
- loop .wait_data ; Decrease timeout
-
- pop bx ; <D> Didn't get any, send another ACK
- inc bx
- cmp bx,TimeoutTableEnd
- jb .send_ack
- jmp kaboom ; Forget it...
-
-.recv_ok: pop dx ; <F>
- pop cx ; <E>
-
- cmp word [pxe_udp_read_pkt.buffersize],byte 4
- jb .wait_data ; Bad size for a DATA packet
-
- mov bx,[si+tftp_pktbuf]
- cmp word [fs:bx],TFTP_DATA ; Not a data packet?
- jne .wait_data ; Then wait for something else
-
- mov ax,[si+tftp_lastpkt]
- xchg ah,al ; Host byte order
- inc ax ; Which packet are we waiting for?
- xchg ah,al ; Network byte order
- cmp [fs:bx+2],ax
- je .right_packet
-
- ; Wrong packet, ACK the packet and then try again
- ; This is presumably because the ACK got lost,
- ; so the server just resent the previous packet
- mov ax,[fs:bx+2]
- call ack_packet
- jmp .send_ok ; Reset timeout
-
-.right_packet: ; It's the packet we want. We're also EOF if the
- ; size < blocksize
-
- pop cx ; <D> Don't need the retry count anymore
-
- mov [si+tftp_lastpkt],ax ; Update last packet number
-
- movzx ecx,word [pxe_udp_read_pkt.buffersize]
- sub cx,byte 4 ; Skip TFTP header
-
- ; Set pointer to data block
- lea ax,[bx+4] ; Data past TFTP header
- mov [si+tftp_dataptr],ax
-
- add [si+tftp_filepos],ecx
- mov [si+tftp_bytesleft],cx
-
- cmp cx,[si+tftp_blksize] ; Is it a full block?
- jb .last_block ; If not, it's EOF
-
-.ret:
- popad
- pop es
- ret
-
-
-.last_block: ; Last block - ACK packet immediately
- mov ax,[fs:bx+2]
- call ack_packet
-
- ; Make sure we know we are at end of file
- mov eax,[si+tftp_filepos]
- mov [si+tftp_filesize],eax
- mov byte [si+tftp_goteof],1
-
- jmp .ret
;
; TimeoutTable: list of timeouts (in 18.2 Hz timer ticks)
TimeoutTableEnd equ $
section .text16
-;
-; ack_packet:
-;
-; Send ACK packet. This is a common operation and so is worth canning.
-;
-; Entry:
-; SI = TFTP block
-; AX = Packet # to ack (network byte order)
-; Exit:
-; All registers preserved
-;
-; This function uses the pxe_udp_write_pkt but not the packet_buf.
-;
-ack_packet:
- pushad
- mov [ack_packet_buf+2],ax ; Packet number to ack
- mov ax,[si]
- mov [pxe_udp_write_pkt.lport],ax
- mov ax,[si+tftp_remoteport]
- mov [pxe_udp_write_pkt.rport],ax
- mov eax,[si+tftp_remoteip]
- mov [pxe_udp_write_pkt.sip],eax
- xor eax,[MyIP]
- and eax,[Netmask]
- jz .nogw
- mov eax,[Gateway]
-.nogw:
- mov [pxe_udp_write_pkt.gip],eax
- mov [pxe_udp_write_pkt.buffer],word ack_packet_buf
- mov [pxe_udp_write_pkt.buffersize], word 4
- mov di,pxe_udp_write_pkt
- mov bx,PXENV_UDP_WRITE
- call pxenv
- popad
- ret
-
-%if GPXE
-;
-; Get a fresh packet from a gPXE socket; expects fs -> pktbuf_seg
-; and ds:si -> socket structure
-;
-; Assumes CS == DS == ES.
-;
-get_packet_gpxe:
- mov di,gpxe_file_read
-
- mov ax,[si+tftp_remoteport] ; gPXE filehandle
- mov [di+2],ax
- mov ax,[si+tftp_pktbuf]
- mov [di+6],ax
- mov [si+tftp_dataptr],ax
- mov [di+8],fs
-
-.again:
- mov word [di+4],PKTBUF_SIZE
- mov bx,PXENV_FILE_READ
- call pxenv
- jnc .ok ; Got data or EOF
- cmp word [di],PXENV_STATUS_TFTP_OPEN ; == EWOULDBLOCK
- je .again
- jmp kaboom ; Otherwise error...
-
-.ok:
- movzx eax,word [di+4] ; Bytes read
- mov [si+tftp_bytesleft],ax ; Bytes in buffer
- add [si+tftp_filepos],eax ; Position in file
-
- and ax,ax ; EOF?
- mov eax,[si+tftp_filepos]
-
- jnz .got_stuff
-
- ; We got EOF here, make sure the upper layers know
- mov [si+tftp_filesize],eax
-.got_stuff:
- ; If we're done here, close the file
- cmp [si+tftp_filesize],eax
- ja .done ; Not EOF, there is still data...
-
- ; Reuse the previous [es:di] structure since the
- ; relevant fields are all the same
- mov byte [si+tftp_goteof],1
-
- mov bx,PXENV_FILE_CLOSE
- call pxenv
- ; Ignore return...
-.done:
- ret
-%endif ; GPXE
;
; unload_pxe:
pop es
ret
-;
-; gendotquad
-;
-; Take an IP address (in network byte order) in EAX and
-; output a dotted quad string to ES:DI.
-; DI points to terminal null at end of string on exit.
-;
-gendotquad:
- push eax
- push cx
- mov cx,4
-.genchar:
- push eax
- cmp al,10 ; < 10?
- jb .lt10 ; If so, skip first 2 digits
-
- cmp al,100 ; < 100
- jb .lt100 ; If so, skip first digit
-
- aam 100
- ; Now AH = 100-digit; AL = remainder
- add ah,'0'
- mov [es:di],ah
- inc di
-
-.lt100:
- aam 10
- ; Now AH = 10-digit; AL = remainder
- add ah,'0'
- mov [es:di],ah
- inc di
-
-.lt10:
- add al,'0'
- stosb
- mov al,'.'
- stosb
- pop eax
- ror eax,8 ; Move next char into LSB
- loop .genchar
- dec di
- mov [es:di], byte 0
- pop cx
- pop eax
- ret
-;
-; uchexbytes/lchexbytes
-;
-; Take a number of bytes in memory and convert to upper/lower-case
-; hexadecimal
-;
-; Input:
-; DS:SI = input bytes
-; ES:DI = output buffer
-; CX = number of bytes
-; Output:
-; DS:SI = first byte after
-; ES:DI = first byte after
-; CX = 0
-;
-; Trashes AX, DX
-;
-
-lchexbytes:
- mov dl,'a'-'9'-1
- jmp xchexbytes
-uchexbytes:
- mov dl,'A'-'9'-1
-xchexbytes:
-.loop:
- lodsb
- mov ah,al
- shr al,4
- call .outchar
- mov al,ah
- call .outchar
- loop .loop
- ret
-.outchar:
- and al,0Fh
- add al,'0'
- cmp al,'9'
- jna .done
- add al,dl
-.done:
- stosb
- ret
-
-;
-; pxe_get_cached_info
-;
-; Get a DHCP packet from the PXE stack into the trackbuf.
-;
-; Input:
-; DL = packet type
-; Output:
-; CX = buffer size
-;
-; Assumes CS == DS == ES.
-;
-pxe_get_cached_info:
- pushad
- mov al,' '
- call writechr
- mov al,dl
- call writehex2
- mov di,pxe_bootp_query_pkt
- push di
- xor ax,ax
- stosw ; Status
- movzx ax,dl
- stosw ; Packet type
- mov ax,trackbufsize
- stosw ; Buffer size
- mov ax,trackbuf
- stosw ; Buffer offset
- xor ax,ax
- stosw ; Buffer segment
- pop di ; DI -> parameter set
- mov bx,PXENV_GET_CACHED_INFO
- call pxenv
- jc .err
-
- popad
- mov cx,[pxe_bootp_query_pkt.buffersize]
- ret
-
-.err:
- mov si,err_pxefailed
- call writestr_early
- call writehex4
- call crlf
- jmp kaboom
-
- section .data16
-get_packet_msg db 'Getting cached packet', 0
-
- section .text16
-;
-; ip_ok
-;
-; Tests an IP address in EAX for validity; return with ZF=1 for bad.
-; We used to refuse class E, but class E addresses are likely to become
-; assignable unicast addresses in the near future.
-;
-ip_ok:
- push ax
- cmp eax,-1 ; Refuse the all-ones address
- jz .out
- and al,al ; Refuse network zero
- jz .out
- cmp al,127 ; Refuse loopback
- jz .out
- and al,0F0h
- cmp al,224 ; Refuse class D
-.out:
- pop ax
- ret
-
-;
-; parse_dhcp
-;
-; Parse a DHCP packet. This includes dealing with "overloaded"
-; option fields (see RFC 2132, section 9.3)
-;
-; This should fill in the following global variables, if the
-; information is present:
-;
-; MyIP - client IP address
-; ServerIP - boot server IP address
-; Netmask - network mask
-; Gateway - default gateway router IP
-; BootFile - boot file name
-; DNSServers - DNS server IPs
-; LocalDomain - Local domain name
-; MACLen, MAC - Client identifier, if MACLen == 0
-;
-; This assumes the DHCP packet is in "trackbuf" and the length
-; of the packet in in CX on entry.
-;
-
-parse_dhcp:
- mov byte [OverLoad],0 ; Assume no overload
- mov eax, [trackbuf+bootp.yip]
- call ip_ok
- jz .noyip
- mov [MyIP], eax
-.noyip:
- mov eax, [trackbuf+bootp.sip]
- and eax, eax
- call ip_ok
- jz .nosip
- mov [ServerIP], eax
-.nosip:
- sub cx, bootp.options
- jbe .nooptions
- mov si, trackbuf+bootp.option_magic
- lodsd
- cmp eax, BOOTP_OPTION_MAGIC
- jne .nooptions
- call parse_dhcp_options
-.nooptions:
- mov si, trackbuf+bootp.bootfile
- test byte [OverLoad],1
- jz .nofileoverload
- mov cx,128
- call parse_dhcp_options
- jmp short .parsed_file
-.nofileoverload:
- cmp byte [si], 0
- jz .parsed_file ; No bootfile name
- mov di,BootFile
- mov cx,32
- rep movsd
- xor al,al
- stosb ; Null-terminate
-.parsed_file:
- mov si, trackbuf+bootp.sname
- test byte [OverLoad],2
- jz .nosnameoverload
- mov cx,64
- call parse_dhcp_options
-.nosnameoverload:
- ret
-
-;
-; Parse a sequence of DHCP options, pointed to by DS:SI; the field
-; size is CX -- some DHCP servers leave option fields unterminated
-; in violation of the spec.
-;
-; For parse_some_dhcp_options, DH contains the minimum value for
-; the option to recognize -- this is used to restrict parsing to
-; PXELINUX-specific options only.
-;
-parse_dhcp_options:
- xor dx,dx
-
-parse_some_dhcp_options:
-.loop:
- and cx,cx
- jz .done
-
- lodsb
- dec cx
- jz .done ; Last byte; must be PAD, END or malformed
- cmp al, 0 ; PAD option
- je .loop
- cmp al,255 ; END option
- je .done
-
- ; Anything else will have a length field
- mov dl,al ; DL <- option number
- xor ax,ax
- lodsb ; AX <- option length
- dec cx
- sub cx,ax ; Decrement bytes left counter
- jb .done ; Malformed option: length > field size
-
- cmp dl,dh ; Is the option value valid?
- jb .opt_done
-
- mov bx,dhcp_option_list
-.find_option:
- cmp bx,dhcp_option_list_end
- jae .opt_done
- cmp dl,[bx]
- je .found_option
- add bx,3
- jmp .find_option
-.found_option:
- pushad
- call [bx+1]
- popad
-
-; Fall through
- ; Unknown option. Skip to the next one.
-.opt_done:
- add si,ax
- jmp .loop
-.done:
- ret
-
- section .data16
-dhcp_option_list:
- section .text16
-
-%macro dopt 2
- section .data16
- db %1
- dw dopt_%2
- section .text16
-dopt_%2:
-%endmacro
-
-;
-; Parse individual DHCP options. SI points to the option data and
-; AX to the option length. DL contains the option number.
-; All registers are saved around the routine.
-;
- dopt 1, subnet_mask
- mov ebx,[si]
- mov [Netmask],ebx
- ret
-
- dopt 3, router
- mov ebx,[si]
- mov [Gateway],ebx
- ret
-
- dopt 6, dns_servers
- mov cx,ax
- shr cx,2
- cmp cl,DNS_MAX_SERVERS
- jna .oklen
- mov cl,DNS_MAX_SERVERS
-.oklen:
- mov di,DNSServers
- rep movsd
- mov [LastDNSServer],di
- ret
-
- dopt 15, local_domain
- mov bx,si
- add bx,ax
- xor ax,ax
- xchg [bx],al ; Zero-terminate option
- mov di,LocalDomain
- call dns_mangle ; Convert to DNS label set
- mov [bx],al ; Restore ending byte
- ret
-
- dopt 43, vendor_encaps
- mov dh,208 ; Only recognize PXELINUX options
- mov cx,ax ; Length of option = max bytes to parse
- call parse_some_dhcp_options ; Parse recursive structure
- ret
-
- dopt 52, option_overload
- mov bl,[si]
- mov [OverLoad],bl
- ret
-
- dopt 54, server
- mov eax,[si]
- cmp dword [ServerIP],0
- jne .skip ; Already have a next server IP
- call ip_ok
- jz .skip
- mov [ServerIP],eax
-.skip: ret
-
- dopt 61, client_identifier
- cmp ax,MAC_MAX ; Too long?
- ja .skip
- cmp ax,2 ; Too short?
- jb .skip
- cmp [MACLen],ah ; Only do this if MACLen == 0
- jne .skip
- push ax
- lodsb ; Client identifier type
- cmp al,[MACType]
- pop ax
- jne .skip ; Client identifier is not a MAC
- dec ax
- mov [MACLen],al
- mov di,MAC
- jmp dhcp_copyoption
-.skip: ret
-
- dopt 67, bootfile_name
- mov di,BootFile
- jmp dhcp_copyoption
-
- dopt 97, uuid_client_identifier
- cmp ax,17 ; type byte + 16 bytes UUID
- jne .skip
- mov dl,[si] ; Must have type 0 == UUID
- or dl,[HaveUUID] ; Capture only the first instance
- jnz .skip
- mov byte [HaveUUID],1 ; Got UUID
- mov di,UUIDType
- jmp dhcp_copyoption
-.skip: ret
-
- dopt 209, pxelinux_configfile
- mov di,ConfigName
- or byte [DHCPMagic],2 ; Got config file
- jmp dhcp_copyoption
-
- dopt 210, pxelinux_pathprefix
- mov di,PathPrefix
- or byte [DHCPMagic],4 ; Got path prefix
- jmp dhcp_copyoption
-
- dopt 211, pxelinux_reboottime
- cmp al,4
- jne .done
- mov ebx,[si]
- xchg bl,bh ; Convert to host byte order
- rol ebx,16
- xchg bl,bh
- mov [RebootTime],ebx
- or byte [DHCPMagic],8 ; Got RebootTime
-.done: ret
-
- ; Common code for copying an option verbatim
- ; Copies the option into ES:DI and null-terminates it.
- ; Returns with AX=0 and SI past the option.
-dhcp_copyoption:
- xchg cx,ax ; CX <- option length
- rep movsb
- xchg cx,ax ; AX <- 0
- stosb ; Null-terminate
- ret
-
- section .data16
-dhcp_option_list_end:
- section .text16
-
- section .data16
-HaveUUID db 0
-uuid_dashes db 4,2,2,2,6,0 ; Bytes per UUID dashed section
- section .text16
-
-;
-; genipopt
-;
-; Generate an ip=<client-ip>:<boot-server-ip>:<gw-ip>:<netmask>
-; option into IPOption based on a DHCP packet in trackbuf.
-; Assumes CS == DS == ES.
-;
-genipopt:
- pushad
- mov di,IPOption
- mov eax,'ip='
- stosd
- dec di
- mov eax,[MyIP]
- call gendotquad
- mov al,':'
- stosb
- mov eax,[ServerIP]
- call gendotquad
- mov al,':'
- stosb
- mov eax,[Gateway]
- call gendotquad
- mov al,':'
- stosb
- mov eax,[Netmask]
- call gendotquad ; Zero-terminates its output
- popad
- ret
; -----------------------------------------------------------------------------
; Common modules
; PXE query packets partially filled in
;
section .bss16
+ global pxe_bootp_query_pkt, pxe_udp_write_pkt
+ global pxe_udp_open_pkt, pxe_udp_read_pkt
pxe_bootp_query_pkt:
.status: resw 1 ; Status
.packettype: resw 1 ; Boot server packet type
%if GPXE
section .data16
-
+ global gpxe_file_api_check
gpxe_file_api_check:
.status: dw 0 ; Status
.size: dw 20 ; Size in bytes
.flags: dd 0
section .bss16
+ global gpxe_file_read, gpxe_get_file_size
+ global gpxe_file_open
gpxe_file_open:
.status: resw 1 ; Status
section .data16
alignz 4
+ global BaseStack
BaseStack dd StackTop ; ESP of base stack
dw 0 ; SS of base stack
-NextSocket dw 49152 ; Counter for allocating socket numbers
KeepPXE db 0 ; Should PXE be kept around?
;
tftp_opts equ ($-tftp_opt_table)/6
;
-; Error packet to return on options negotiation error
+; Error packet to return on TFTP protocol error
+; Most of our errors are OACK parsing errors, so use that error code
;
-tftp_opt_err dw TFTP_ERROR ; ERROR packet
- dw TFTP_EOPTNEG ; ERROR 8: bad options
- db 'tsize option required', 0 ; Error message
-tftp_opt_err_len equ ($-tftp_opt_err)
+ global tftp_proto_err, tftp_proto_err_len
+tftp_proto_err dw TFTP_ERROR ; ERROR packet
+ dw TFTP_EOPTNEG ; ERROR 8: OACK error
+ db 'TFTP protocol error', 0 ; Error message
+tftp_proto_err_len equ ($-tftp_proto_err)
alignz 4
+ global ack_packet_buf
ack_packet_buf: dw TFTP_ACK, 0 ; TFTP ACK packet
;
; IP information (initialized to "unknown" values)
+ global MyIP, ServerIP, Netmask, Gateway, ServerPort
MyIP dd 0 ; My IP address
ServerIP dd 0 ; IP address of boot server
Netmask dd 0 ; Netmask of this subnet