/* PXE unload sequences */
const uint8_t new_api_unload[] = {
- PXENV_UDP_CLOSE, PXENV_UNDI_SHUTDOWN,
+ PXENV_UDP_CLOSE, PXENV_UNDI_SHUTDOWN,
PXENV_UNLOAD_STACK, PXENV_STOP_UNDI, 0
};
const uint8_t old_api_unload[] = {
/*
* Allocate a local UDP port structure.
* return the socket pointer if success, or null if failure
- *
+ *
*/
static struct open_file_t *allocate_socket(void)
{
int i;
struct open_file_t *socket = Files;
uint16_t nextport;
-
+
for (i = 0; i < MAX_OPEN; i++) {
if (!socket->tftp_localport)
break;
socket++;
}
-
+
if (i == MAX_OPEN)
return NULL;
}
/**
- * Take a nubmer of bytes in memory and convert to lower-case hxeadecimal
+ * Take a nubmer of bytes in memory and convert to lower-case hxeadecimal
*
* @param: dst, output buffer
* @param: src, input buffer
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;
}
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;
}
int i = 0, j;
char temp[4];
char *p = dst;
-
+
for (; i < 4; i++) {
j = 0;
part = ip & 0xff;
for (; j > 0; j--)
*p++ = temp[j-1];
*p++ = '.';
-
+
ip >>= 8;
}
/* drop the last dot '.' and zero-terminate string*/
*(--p) = 0;
-
+
return p - dst;
}
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 != '.')
+ if (i != 3 && *p != '.')
return NULL;
-
+
ip = (ip << 8) | part;
part = 0;
p++;
}
p --;
-
+
*res = ip;
- return p;
+ 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 regs;
-
+
#if 0
printf("pxe_call op %04x data %p\n", opcode, data);
#endif
- memset(®s, 0, sizeof regs);
+ memset(®s, 0, sizeof regs);
regs.ebx.w[0] = opcode;
regs.es = SEG(data);
regs.edi.w[0] = OFFS(data);
call16(pxenv, ®s, ®s);
-
+
return regs.eflags.l & EFLAGS_CF; /* CF SET if fail */
}
err_buf.err_num = errnum;
memcpy(err_buf.err_msg, errstr, len);
err_buf.err_msg[len] = '\0';
-
+
udp_write.src_port = file->tftp_localport;
udp_write.dst_port = file->tftp_remoteport;
udp_write.ip = file->tftp_remoteip;
/* If something goes wrong, there is nothing we can do, anyway... */
pxe_call(PXENV_UDP_WRITE, &udp_write);
-}
+}
/**
*
* @param: file, TFTP block pointer
* @param: ack_num, Packet # to ack (network byte order)
- *
+ *
*/
static void ack_packet(struct open_file_t *file, uint16_t ack_num)
{
int err;
static __lowmem uint16_t ack_packet_buf[2];
- static __lowmem struct s_PXENV_UDP_WRITE udp_write;
-
+ static __lowmem struct s_PXENV_UDP_WRITE udp_write;
+
/* Packet number to ack */
ack_packet_buf[0] = TFTP_ACK;
ack_packet_buf[1] = ack_num;
*
* @param: type, packet type
* @return: buffer size
- *
+ *
*/
static int pxe_get_cached_info(int type)
{
int err;
static __lowmem struct s_PXENV_GET_CACHED_INFO get_cached_info;
printf(" %02x", type);
-
+
get_cached_info.Status = 0;
get_cached_info.PacketType = type;
get_cached_info.BufferSize = 8192;
printf("PXE API call failed, error %04x\n", err);
kaboom();
}
-
+
return get_cached_info.BufferSize;
}
-
+
#if GPXE
/*
- * Return 1 if and only if the buffer pointed to by
- * url is a URL (contains ://)
- *
+ * Return true if and only if the buffer pointed to by
+ * url is a URL -- it must contain :// and it must be the
+ * first colon.
*/
-static int is_url(const char *url)
+static inline bool is_url(const char *url)
{
- while (*url) {
- if (!strncmp(url, "://", 3))
- return 1;
-
- url++;
- }
- return 0;
+ const char *p = strchr(url, ':');
+
+ return p && p[1] == '/' && p[2] == '/';
}
/*
- * 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
+ * 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.
*/
-static bool is_gpxe(char *url)
+static bool is_gpxe(const char *url)
{
static bool already;
- const char gpxe_warning_msg[] =
- "URL syntax, but gPXE extensions not detected, tring plain TFTP...\n";
-
+
if (!is_url(url))
return false;
-
+
if (!has_gpxe && !already) {
- printf("%s\n", gpxe_warning_msg);
+ fputs("URL syntax, but gPXE extensions not detected, tring plain TFTP...\n", stdout);
already = true;
}
-
+
return has_gpxe;
}
{
static __lowmem struct s_PXENV_FILE_READ file_read;
int err;
-
+
while (1) {
file_read.FileHandle = file->tftp_remoteport;
file_read.Buffer.offs = file->tftp_pktbuf;
kaboom();
}
+ file->tftp_dataptr = file->tftp_pktbuf;
file->tftp_bytesleft = file_read.BufferSize;
file->tftp_filepos += file_read.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)
+ if (file->tftp_filesize > file->tftp_filepos)
return;
/* Got EOF, close it */
const char *p = src;
uint32_t ip = server_ip;
int i = 0;
-
+
#if GPXE
if (is_url(src)) {
ip = -1;
}
}
}
-
+
store:
- *(uint32_t *)dst = ip;
+ *(uint32_t *)dst = ip;
dst += 4;
i = FILENAME_MAX - 5;
/*
* Does the opposite of mangle_name; converts a DOS-mangled
- * filename to the conventional representation. This is
+ * filename to the conventional representation. This is
* needed for the BOOT_IMAGE= parameter for the kernel.
*/
static char *pxe_unmangle_name(char *dst, const char *src)
{
uint32_t ip = *(uint32_t *)src;
int ip_len = 0;
-
+
if (ip != 0 && ip != -1) {
ip_len = gendotquad(dst, *(uint32_t *)src);
dst += ip_len;
src += 4;
return stpcpy(dst, src);
}
-
+
/*
* Get a fresh packet if the buffer is drained, and we haven't hit
* EOF yet. The buffer should be filled immediately after draining!
uint32_t oldtime;
void *data = NULL;
static __lowmem struct s_PXENV_UDP_READ udp_read;
-
+
if (file->tftp_bytesleft || file->tftp_goteof)
return;
* Start by ACKing the previous packet; this should cause
* the next packet to be sent.
*/
- ack_again:
+ ack_again:
ack_packet(file, file->tftp_lastpkt);
-
- timeout_ptr = TimeoutTable;
+
+ timeout_ptr = TimeoutTable;
timeout = *timeout_ptr++;
oldtime = jiffies();
while (timeout) {
}
continue;
}
-
+
if (udp_read.buffer_size < 4) /* Bad size for a DATA packet */
- continue;
+ 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)
kaboom();
-
+
last_pkt = file->tftp_lastpkt;
last_pkt = ntohs(last_pkt); /* Host byte order */
last_pkt++;
#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 = udp_read.buffer_size - 4; /* Skip TFTP header */
* @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
*
*/
-static uint32_t pxe_getfssec(struct file *gfile, char *buf,
+static uint32_t pxe_getfssec(struct file *gfile, char *buf,
int blocks, bool *have_more)
{
struct open_file_t *file = gfile->open_file;
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;
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 drained; the caller will
* call close_file and therefore free the socket.
*/
*have_more = 0;
}
-
+
return bytes_read;
}
-
+
/*
* Fill the packet tail with the tftp informations then retures the lenght
static int fill_tail(char *dst)
{
static const char tail[] = "octet\0""tsize\0""0\0""blksize\0""1408";
-
+
memcpy(dst, tail, sizeof tail);
return sizeof tail;
}
static __lowmem struct s_PXENV_UDP_WRITE udp_write;
static __lowmem struct s_PXENV_UDP_READ udp_read;
static __lowmem struct s_PXENV_FILE_OPEN file_open;
- static __lowmem struct s_PXENV_GET_FILE_SIZE get_file_size;
const struct tftp_options *tftp_opt;
int i = 0;
int err;
uint16_t blk_num;
uint32_t ip;
uint32_t opdata, *opdata_ptr;
-
+
open_file = allocate_socket();
if (!open_file) {
file->file_len = 0;
file->open_file = NULL;
return;
}
-
+
timeout_ptr = TimeoutTable; /* Reset timeout */
sendreq:
udp_write.buffer.seg = 0;
*(uint16_t *)buf = TFTP_RRQ; /* TFTP opcode */
buf += 2;
-
+
ip = *(uint32_t *)p; /* ip <- server override (if any) */
p += 4;
if (ip == 0) {
strcpy(buf, p); /* Copy the filename */
buf += strlen(p) + 1; /* advance the pointer, null char included */
-
+
#if GPXE
if (is_gpxe(packet_buf + 2)) {
file_open.Status = PXENV_STATUS_BAD_FUNC;
- file_open.FileName.offs = OFFS_WRT(packet_buf + 2, 0);
- file_open.FileName.seg = 0;
+ file_open.FileName.offs = OFFS(packet_buf + 2);
+ file_open.FileName.seg = SEG(packet_buf + 2);
err = pxe_call(PXENV_FILE_OPEN, &file_open);
- if (err)
+ if (err)
goto done;
-
+
open_file->tftp_localport = -1;
open_file->tftp_remoteport = file_open.FileHandle;
- get_file_size.FileHandle = file_open.FileHandle;
-
-#if 0
- err = pxe_call(PXENV_GET_FILE_SIZE, &get_file_size);
- if (!err)
- open_file->tftp_filesize = get_file_size.FileSize;
- else
-#endif
- open_file->tftp_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) */
udp_write.ip = ip;
udp_write.buffer_size = buf - packet_buf;
err = pxe_call(PXENV_UDP_WRITE, &udp_write);
if (err || udp_write.status != 0)
- goto failure; /*
+ 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
* 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 = jiffies();
if (udp_read.src_ip == open_file->tftp_remoteip)
break;
}
-
+
/* Got packet; reset timeout */
timeout_ptr = TimeoutTable;
open_file->tftp_remoteport = udp_read.s_port;
-
+
/* filesize <- -1 == unknown */
open_file->tftp_filesize = -1;
/* Default blksize unless blksize option negotiated */
buffersize = udp_read.buffer_size - 2; /* bytes after opcode */
if (buffersize < 0)
goto failure; /* Garbled reply */
-
+
/*
- * Get the opcode type, and parse it
+ * Get the opcode type, and parse it
*/
opcode = *(uint16_t *)packet_buf;
switch (opcode) {
* 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...
+ * suported. Save the data away and consider the
+ * length undefined, *unless* this is the only
+ * data packet...
*/
buffersize -= 2;
if (buffersize < 0)
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
+ * We know the filesize, but we also want to
* ack the packet and set the EOF flag.
*/
open_file->tftp_filesize = buffersize;
break;
case TFTP_OACK:
- /*
+ /*
* Now we need to parse the OACK packet to get the transfer
* and packet sizes.
*/
options = packet_buf + 2;
p = options;
-
+
while (buffersize) {
char *opt = p;
if (!buffersize)
break; /* No option data */
- /*
+ /*
* Parse option pointed to by options; guaranteed to be
* null-terminated
*/
if (i == tftp_nopts)
goto err_reply; /* Non-negotitated option returned,
no idea what it means ...*/
-
+
/* get the address of the filed that we want to write on */
opdata_ptr = (uint32_t *)((char *)open_file + tftp_opt->offset);
opdata = 0;
-
+
/* do convert a number-string to decimal number, just like atoi */
while (buffersize--) {
uint8_t d = *p++;
*opdata_ptr = opdata;
}
break;
-
+
default:
printf("TFTP unknown opcode %d\n", ntohs(opcode));
goto err_reply;
}
-
+
done:
if (!open_file->tftp_filesize) {
free_socket(open_file);
tftp_error(open_file, TFTP_EOPTNEG, "TFTP protocol error");
printf("TFTP server sent an incomprehesible reply\n");
kaboom();
-
+
failure:
timeout_ptr++;
if (*timeout_ptr)
goto sendreq; /* Try again */
}
-
+
/*
* Store standard filename prefix
int len;
char *p;
char c;
-
+
if (DHCPMagic & 0x04) /* Did we get a path prefix option */
goto got_prefix;
-
+
strcpy(path_prefix, boot_file);
len = strlen(path_prefix);
- p = &path_prefix[len - 1];
+ p = &path_prefix[len - 1];
while (len--) {
c = *p--;
c |= 0x20;
-
- c = (c >= '0' && c <= '9') ||
- (c >= 'a' && c <= 'z') ||
+
+ 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("TFTP prefix: %s\n", path_prefix);
strcpy(CurrentDirName, path_prefix);
/*
* try to load a config file, if found, return 1, or return 0
*
- */
+ */
static int try_load(char *config_name)
{
com32sys_t regs;
-
+
printf("Trying to load: %-50s ", config_name);
pxe_mangle_name(KernelName, config_name);
-
+
memset(®s, 0, sizeof regs);
regs.edi.w[0] = OFFS_WRT(KernelName, 0);
call16(core_open, ®s, ®s);
const char *default_str = "default";
char *config_file;
char *last;
- char *p;
+ char *p;
uint8_t *uuid_ptr;
int tries = 8;
-
+
get_prefix();
if (DHCPMagic & 0x02) {
/* We got a DHCP option, try it first */
if (try_load(boot_file))
return 0;
}
-
+
/*
* Have to guess config file name ...
- */
+ */
memcpy(ConfigName, cfgprefix, strlen(cfgprefix));
config_file = ConfigName + strlen(cfgprefix);
-
+
/* Try loading by UUID */
if (have_uuid) {
uuid_ptr = uuid_dashes;
while (*uuid_ptr) {
int len = *uuid_ptr;
char *src = uuid;
-
+
lchexbytes(p, src, len);
p += len * 2;
src += len;
strcpy(config_file, MAC_str);
if (try_load(ConfigName))
return 0;
-
+
/* Nope, try hexadecimal IP prefixes... */
uchexbytes(config_file, (uint8_t *)&MyIP, 4); /* Convet to hex string */
last = &config_file[8];
last--; /* Drop one character */
tries--;
};
-
+
/* Final attempt: "default" string */
strcpy(config_file, default_str);
if (try_load(ConfigName))
}
/*
- * Generate the botif string, and the hardware-based config string
+ * Generate the botif string, and the hardware-based config string
*/
static void make_bootif_string(void)
{
char *src = mac;
char *dst = MAC_str;
int i = MAC_len + 1;
-
+
*(uint8_t *)src++ = MAC_type;
memcpy(src, MAC, MAC_len);
src = mac;
{
char *p = IPOption;
int ip_len;
-
+
strcpy(p, "ip=");
p += 3;
-
+
ip_len = gendotquad(p, MyIP);
p += ip_len;
*p++ = ':';
ip_len = gendotquad(p, gate_way);
p += ip_len;
*p++ = ':';
-
+
ip_len = gendotquad(p, net_mask);
}
-
+
/* Generate ip= option and print the ip adress */
static void ip_init(void)
{
uint32_t ip = MyIP;
-
+
genipopt();
gendotquad(dot_quad_buf, ip);
-
+
ip = ntohl(ip);
printf("My IP address seems to be %08X %s\n", ip, dot_quad_buf);
printf("%s\n", IPOption);
const uint8_t *p = buf;
int i = pxenv->length;
uint8_t sum = 0;
-
+
/* The pxeptr field isn't present in old versions */
if (i < offsetof(struct pxenv_t, pxeptr) ||
memcmp(pxenv->signature, "PXENV+", 6))
return sum == 0;
}
-
+
/*
}
return NULL;
}
-
+
static const struct pxe_t *memory_scan_for_pxe_struct(void)
{
extern uint16_t BIOS_fbm; /* Starting segment */
return memory_scan(BIOS_fbm << 10, is_pxe);
}
-
+
static const struct pxenv_t *memory_scan_for_pxenv_struct(void)
{
return memory_scan(0x10000, is_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
+ * If we find a PXENV+ structure, we try to find a !PXE structure from
+ * if if the API version is 2.1 or later
*
*/
static int pxe_init(void)
/* 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);
pxe = MK_PTR(seg, off);
- if (is_pxe(pxe))
+ if (is_pxe(pxe))
goto have_pxe;
/* Plan B: PXENV+ structure at [ES:BX] */
plan++;
if ((pxe = memory_scan_for_pxe_struct()))
goto have_pxe;
-
+
/* Plan E: PXENV+ memory scan */
plan++;
if ((pxenv = memory_scan_for_pxenv_struct()))
/* Found nothing at all !! */
printf("No !PXE or PXENV+ API found; we're dead...\n");
kaboom();
-
+
have_pxenv:
APIVer = pxenv->version;
printf("Found PXENV+ structure\nPXE API version is %04x\n", APIVer);
if (pxenv->length >= sizeof(struct pxenv_t)) {
pxe = GET_PTR(pxenv->pxeptr);
if (is_pxe(pxe))
- goto have_pxe;
+ goto 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
}
APIVer = 0x200; /* PXENV+ only, assume version 2.00 */
}
-
+
/* Otherwise, no dice, use PXENV+ structure */
data_len = pxenv->undidatasize;
data_seg = pxenv->undidataseg;
real_base_mem = max(code_seg, data_seg) >> 6; /* Convert to kilobytes */
return 0;
-}
+}
/*
* See if we have gPXE
}
/*
- * Initialize UDP stack
+ * Initialize UDP stack
*
*/
static void udp_init(void)
kaboom();
}
}
-
-
+
+
/*
* Network-specific initialization
- */
+ */
static void network_init(void)
-{
+{
struct bootp_t *bp = (struct bootp_t *)trackbuf;
int pkt_len;
*LocalDomain = 0; /* No LocalDomain received */
-
+
/*
* Get the DHCP client identifiers (query info 1)
*/
* 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
+ * 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
*/
MAC_len = bp->hardlen > 16 ? 0 : bp->hardlen;
MAC_type = bp->hardware;
- memcpy(MAC, bp->macaddr, MAC_len);
+ memcpy(MAC, bp->macaddr, MAC_len);
/*
* 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);
+ parse_dhcp(pkt_len);
printf("\n");
make_bootif_string();
static int pxe_fs_init(struct fs_info *fs)
{
(void)fs; /* drop the compile warning message */
-
+
/* Initialize the Files structure */
files_init();
}
/*
- * This function unloads the PXE and UNDI stacks and
+ * This function unloads the PXE and UNDI stacks and
* unclaims the memory.
*/
void unload_pxe(void)
static __lowmem struct s_PXENV_UNLOAD_STACK unload_stack;
pxe_idle_cleanup();
-
+
if (KeepPXE) {
/*
* We want to keep PXE around, but still we should reset
if (err || unload_stack.Status != PXENV_STATUS_SUCCESS)
goto cant_free;
}
-
+
flag = 0xff00;
if (real_base_mem <= BIOS_fbm) /* Santiy check */
goto cant_free;
flag ++;
-
+
/* Check that PXE actually unhooked the INT 0x1A chain */
int_addr = (int)MK_PTR(*(uint16_t *)(4*0x1a+2), *(uint16_t *)(4*0x1a));
int_addr >>= 10;
BIOS_fbm = real_base_mem;
return;
}
-
+
cant_free:
- printf("Failed to free base memory error %04x-%08x\n",
+ printf("Failed to free base memory error %04x-%08x\n",
flag, *(uint32_t *)(4 * 0x1a));
return;
}
-
-
+
+
const struct fs_ops pxe_fs_ops = {
.fs_name = "pxe",
.fs_flags = FS_NODEV,