Merging upstream version 5.01+dfsg.
[syslinux-debian/hramrach.git] / core / fs / pxe / pxe.c
blobae44cffef6c8eebc5e1fe92c59723e73c5b18485
1 #include <dprintf.h>
2 #include <stdio.h>
3 #include <string.h>
4 #include <core.h>
5 #include <bios.h>
6 #include <fs.h>
7 #include <minmax.h>
8 #include <sys/cpu.h>
9 #include "pxe.h"
11 #define GPXE 1
13 static uint16_t real_base_mem; /* Amount of DOS memory after freeing */
15 uint8_t MAC[MAC_MAX]; /* Actual MAC address */
16 uint8_t MAC_len; /* MAC address len */
17 uint8_t MAC_type; /* MAC address type */
19 char __bss16 BOOTIFStr[7+3*(MAC_MAX+1)];
20 #define MAC_str (BOOTIFStr+7) /* The actual hardware address */
21 char __bss16 SYSUUIDStr[8+32+5];
22 #define UUID_str (SYSUUIDStr+8) /* The actual UUID */
24 char boot_file[256]; /* From DHCP */
25 char path_prefix[256]; /* From DHCP */
26 char dot_quad_buf[16];
28 static bool has_gpxe;
29 static uint32_t gpxe_funcs;
30 bool have_uuid = false;
32 /* Common receive buffer */
33 static __lowmem char packet_buf[PKTBUF_SIZE] __aligned(16);
35 const uint8_t TimeoutTable[] = {
36 2, 2, 3, 3, 4, 5, 6, 7, 9, 10, 12, 15, 18, 21, 26, 31, 37, 44,
37 53, 64, 77, 92, 110, 132, 159, 191, 229, 255, 255, 255, 255, 0
40 struct tftp_options {
41 const char *str_ptr; /* string pointer */
42 size_t offset; /* offset into socket structre */
45 #define IFIELD(x) offsetof(struct inode, x)
46 #define PFIELD(x) (offsetof(struct inode, pvt) + \
47 offsetof(struct pxe_pvt_inode, x))
49 static const struct tftp_options tftp_options[] =
51 { "tsize", IFIELD(size) },
52 { "blksize", PFIELD(tftp_blksize) },
54 static const int tftp_nopts = sizeof tftp_options / sizeof tftp_options[0];
56 static void tftp_error(struct inode *file, uint16_t errnum,
57 const char *errstr);
60 * Allocate a local UDP port structure and assign it a local port number.
61 * Return the inode pointer if success, or null if failure
63 static struct inode *allocate_socket(struct fs_info *fs)
65 struct inode *inode = alloc_inode(fs, 0, sizeof(struct pxe_pvt_inode));
67 if (!inode) {
68 malloc_error("socket structure");
69 } else {
70 struct pxe_pvt_inode *socket = PVT(inode);
71 socket->tftp_localport = get_port();
72 inode->mode = DT_REG; /* No other types relevant for PXE */
75 return inode;
78 static void free_socket(struct inode *inode)
80 struct pxe_pvt_inode *socket = PVT(inode);
82 free_port(socket->tftp_localport);
83 free_inode(inode);
86 #if GPXE
87 static void gpxe_close_file(struct inode *inode)
89 struct pxe_pvt_inode *socket = PVT(inode);
90 static __lowmem struct s_PXENV_FILE_CLOSE file_close;
92 file_close.FileHandle = socket->tftp_remoteport;
93 pxe_call(PXENV_FILE_CLOSE, &file_close);
95 #endif
97 static void pxe_close_file(struct file *file)
99 struct inode *inode = file->inode;
100 struct pxe_pvt_inode *socket = PVT(inode);
102 if (!socket->tftp_goteof) {
103 #if GPXE
104 if (socket->tftp_localport == 0xffff) {
105 gpxe_close_file(inode);
106 } else
107 #endif
108 if (socket->tftp_localport != 0) {
109 tftp_error(inode, 0, "No error, file close");
113 free_socket(inode);
117 * Take a nubmer of bytes in memory and convert to lower-case hxeadecimal
119 * @param: dst, output buffer
120 * @param: src, input buffer
121 * @param: count, number of bytes
124 static void lchexbytes(char *dst, const void *src, int count)
126 uint8_t half;
127 uint8_t c;
128 const uint8_t *s = src;
130 for(; count > 0; count--) {
131 c = *s++;
132 half = ((c >> 4) & 0x0f) + '0';
133 *dst++ = half > '9' ? (half + 'a' - '9' - 1) : half;
135 half = (c & 0x0f) + '0';
136 *dst++ = half > '9' ? (half + 'a' - '9' - 1) : half;
141 * just like the lchexbytes, except to upper-case
144 static void uchexbytes(char *dst, const void *src, int count)
146 uint8_t half;
147 uint8_t c;
148 const uint8_t *s = src;
150 for(; count > 0; count--) {
151 c = *s++;
152 half = ((c >> 4) & 0x0f) + '0';
153 *dst++ = half > '9' ? (half + 'A' - '9' - 1) : half;
155 half = (c & 0x0f) + '0';
156 *dst++ = half > '9' ? (half + 'A' - '9' - 1) : half;
161 * Parse a single hexadecimal byte, which must be complete (two
162 * digits). This is used in URL parsing.
164 static int hexbyte(const char *p)
166 if (!is_hex(p[0]) || !is_hex(p[1]))
167 return -1;
168 else
169 return (hexval(p[0]) << 4) + hexval(p[1]);
173 * Tests an IP address in _ip_ for validity; return with 0 for bad, 1 for good.
174 * We used to refuse class E, but class E addresses are likely to become
175 * assignable unicast addresses in the near future.
178 bool ip_ok(uint32_t ip)
180 uint8_t ip_hi = (uint8_t)ip; /* First octet of the ip address */
182 if (ip == 0xffffffff || /* Refuse the all-ones address */
183 ip_hi == 0 || /* Refuse network zero */
184 ip_hi == 127 || /* Refuse the loopback network */
185 (ip_hi & 240) == 224) /* Refuse class D */
186 return false;
188 return true;
193 * Take an IP address (in network byte order) in _ip_ and
194 * output a dotted quad string to _dst_, returns the length
195 * of the dotted quad ip string.
198 static int gendotquad(char *dst, uint32_t ip)
200 int part;
201 int i = 0, j;
202 char temp[4];
203 char *p = dst;
205 for (; i < 4; i++) {
206 j = 0;
207 part = ip & 0xff;
208 do {
209 temp[j++] = (part % 10) + '0';
210 }while(part /= 10);
211 for (; j > 0; j--)
212 *p++ = temp[j-1];
213 *p++ = '.';
215 ip >>= 8;
217 /* drop the last dot '.' and zero-terminate string*/
218 *(--p) = 0;
220 return p - dst;
224 * parse the ip_str and return the ip address with *res.
225 * return the the string address after the ip string
228 static const char *parse_dotquad(const char *ip_str, uint32_t *res)
230 const char *p = ip_str;
231 uint8_t part = 0;
232 uint32_t ip = 0;
233 int i;
235 for (i = 0; i < 4; i++) {
236 while (is_digit(*p)) {
237 part = part * 10 + *p - '0';
238 p++;
240 if (i != 3 && *p != '.')
241 return NULL;
243 ip = (ip << 8) | part;
244 part = 0;
245 p++;
247 p--;
249 *res = htonl(ip);
250 return p;
254 * the ASM pxenv function wrapper, return 1 if error, or 0
257 __export int pxe_call(int opcode, void *data)
259 extern void pxenv(void);
260 com32sys_t regs;
262 #if 0
263 printf("pxe_call op %04x data %p\n", opcode, data);
264 #endif
266 memset(&regs, 0, sizeof regs);
267 regs.ebx.w[0] = opcode;
268 regs.es = SEG(data);
269 regs.edi.w[0] = OFFS(data);
270 call16(pxenv, &regs, &regs);
272 return regs.eflags.l & EFLAGS_CF; /* CF SET if fail */
276 * Send an ERROR packet. This is used to terminate a connection.
278 * @inode: Inode structure
279 * @errnum: Error number (network byte order)
280 * @errstr: Error string (included in packet)
282 static void tftp_error(struct inode *inode, uint16_t errnum,
283 const char *errstr)
285 static __lowmem struct {
286 uint16_t err_op;
287 uint16_t err_num;
288 char err_msg[64];
289 } __packed err_buf;
290 static __lowmem struct s_PXENV_UDP_WRITE udp_write;
291 int len = min(strlen(errstr), sizeof(err_buf.err_msg)-1);
292 struct pxe_pvt_inode *socket = PVT(inode);
294 err_buf.err_op = TFTP_ERROR;
295 err_buf.err_num = errnum;
296 memcpy(err_buf.err_msg, errstr, len);
297 err_buf.err_msg[len] = '\0';
299 udp_write.src_port = socket->tftp_localport;
300 udp_write.dst_port = socket->tftp_remoteport;
301 udp_write.ip = socket->tftp_remoteip;
302 udp_write.gw = gateway(udp_write.ip);
303 udp_write.buffer = FAR_PTR(&err_buf);
304 udp_write.buffer_size = 4 + len + 1;
306 /* If something goes wrong, there is nothing we can do, anyway... */
307 pxe_call(PXENV_UDP_WRITE, &udp_write);
312 * Send ACK packet. This is a common operation and so is worth canning.
314 * @param: inode, Inode pointer
315 * @param: ack_num, Packet # to ack (network byte order)
318 static void ack_packet(struct inode *inode, uint16_t ack_num)
320 int err;
321 static __lowmem uint16_t ack_packet_buf[2];
322 static __lowmem struct s_PXENV_UDP_WRITE udp_write;
323 struct pxe_pvt_inode *socket = PVT(inode);
325 /* Packet number to ack */
326 ack_packet_buf[0] = TFTP_ACK;
327 ack_packet_buf[1] = ack_num;
328 udp_write.src_port = socket->tftp_localport;
329 udp_write.dst_port = socket->tftp_remoteport;
330 udp_write.ip = socket->tftp_remoteip;
331 udp_write.gw = gateway(udp_write.ip);
332 udp_write.buffer = FAR_PTR(ack_packet_buf);
333 udp_write.buffer_size = 4;
335 err = pxe_call(PXENV_UDP_WRITE, &udp_write);
336 (void)err;
337 #if 0
338 printf("sent %s\n", err ? "FAILED" : "OK");
339 #endif
344 * Get a DHCP packet from the PXE stack into a lowmem buffer
346 * @param: type, packet type
347 * @return: buffer size
350 static int pxe_get_cached_info(int type, void *buf, size_t bufsiz)
352 int err;
353 static __lowmem struct s_PXENV_GET_CACHED_INFO get_cached_info;
354 printf(" %02x", type);
356 get_cached_info.Status = 0;
357 get_cached_info.PacketType = type;
358 get_cached_info.BufferSize = bufsiz;
359 get_cached_info.Buffer = FAR_PTR(buf);
360 err = pxe_call(PXENV_GET_CACHED_INFO, &get_cached_info);
361 if (err) {
362 printf("PXE API call failed, error %04x\n", err);
363 kaboom();
366 return get_cached_info.BufferSize;
371 * Return the type of pathname passed.
373 enum pxe_path_type {
374 PXE_RELATIVE, /* No :: or URL */
375 PXE_HOMESERVER, /* Starting with :: */
376 PXE_TFTP, /* host:: */
377 PXE_URL_TFTP, /* tftp:// */
378 PXE_URL, /* Absolute URL syntax */
381 static enum pxe_path_type pxe_path_type(const char *str)
383 const char *p;
385 p = str;
387 while (1) {
388 switch (*p) {
389 case ':':
390 if (p[1] == ':') {
391 if (p == str)
392 return PXE_HOMESERVER;
393 else
394 return PXE_TFTP;
395 } else if (p > str && p[1] == '/' && p[2] == '/') {
396 if (!strncasecmp(str, "tftp://", 7))
397 return PXE_URL_TFTP;
398 else
399 return PXE_URL;
402 /* else fall through */
403 case '/': case '!': case '@': case '#': case '%':
404 case '^': case '&': case '*': case '(': case ')':
405 case '[': case ']': case '{': case '}': case '\\':
406 case '|': case '=': case '`': case '~': case '\'':
407 case '\"': case ';': case '>': case '<': case '?':
408 case '\0':
409 /* Any of these characters terminate the colon search */
410 return PXE_RELATIVE;
411 default:
412 break;
414 p++;
418 #if GPXE
421 * Get a fresh packet from a gPXE socket
422 * @param: inode -> Inode pointer
425 static void get_packet_gpxe(struct inode *inode)
427 struct pxe_pvt_inode *socket = PVT(inode);
428 static __lowmem struct s_PXENV_FILE_READ file_read;
429 int err;
431 while (1) {
432 file_read.FileHandle = socket->tftp_remoteport;
433 file_read.Buffer = FAR_PTR(packet_buf);
434 file_read.BufferSize = PKTBUF_SIZE;
435 err = pxe_call(PXENV_FILE_READ, &file_read);
436 if (!err) /* successed */
437 break;
439 if (file_read.Status != PXENV_STATUS_TFTP_OPEN)
440 kaboom();
443 memcpy(socket->tftp_pktbuf, packet_buf, file_read.BufferSize);
445 socket->tftp_dataptr = socket->tftp_pktbuf;
446 socket->tftp_bytesleft = file_read.BufferSize;
447 socket->tftp_filepos += file_read.BufferSize;
449 if (socket->tftp_bytesleft == 0)
450 inode->size = socket->tftp_filepos;
452 /* if we're done here, close the file */
453 if (inode->size > socket->tftp_filepos)
454 return;
456 /* Got EOF, close it */
457 socket->tftp_goteof = 1;
458 gpxe_close_file(inode);
460 #endif /* GPXE */
464 * mangle a filename pointed to by _src_ into a buffer pointed
465 * to by _dst_; ends on encountering any whitespace.
468 static void pxe_mangle_name(char *dst, const char *src)
470 size_t len = FILENAME_MAX-1;
472 while (len-- && not_whitespace(*src))
473 *dst++ = *src++;
475 *dst = '\0';
479 * Get a fresh packet if the buffer is drained, and we haven't hit
480 * EOF yet. The buffer should be filled immediately after draining!
482 static void fill_buffer(struct inode *inode)
484 int err;
485 int last_pkt;
486 const uint8_t *timeout_ptr;
487 uint8_t timeout;
488 uint16_t buffersize;
489 uint32_t oldtime;
490 void *data = NULL;
491 static __lowmem struct s_PXENV_UDP_READ udp_read;
492 struct pxe_pvt_inode *socket = PVT(inode);
494 if (socket->tftp_bytesleft || socket->tftp_goteof)
495 return;
497 #if GPXE
498 if (socket->tftp_localport == 0xffff) {
499 get_packet_gpxe(inode);
500 return;
502 #endif
505 * Start by ACKing the previous packet; this should cause
506 * the next packet to be sent.
508 timeout_ptr = TimeoutTable;
509 timeout = *timeout_ptr++;
510 oldtime = jiffies();
512 ack_again:
513 ack_packet(inode, socket->tftp_lastpkt);
515 while (timeout) {
516 udp_read.buffer = FAR_PTR(packet_buf);
517 udp_read.buffer_size = PKTBUF_SIZE;
518 udp_read.src_ip = socket->tftp_remoteip;
519 udp_read.dest_ip = IPInfo.myip;
520 udp_read.s_port = socket->tftp_remoteport;
521 udp_read.d_port = socket->tftp_localport;
522 err = pxe_call(PXENV_UDP_READ, &udp_read);
523 if (err) {
524 uint32_t now = jiffies();
526 if (now-oldtime >= timeout) {
527 oldtime = now;
528 timeout = *timeout_ptr++;
529 if (!timeout)
530 break;
532 continue;
535 if (udp_read.buffer_size < 4) /* Bad size for a DATA packet */
536 continue;
538 data = packet_buf;
539 if (*(uint16_t *)data != TFTP_DATA) /* Not a data packet */
540 continue;
542 /* If goes here, recevie OK, break */
543 break;
546 /* time runs out */
547 if (timeout == 0)
548 kaboom();
550 last_pkt = socket->tftp_lastpkt;
551 last_pkt = ntohs(last_pkt); /* Host byte order */
552 last_pkt++;
553 last_pkt = htons(last_pkt); /* Network byte order */
554 if (*(uint16_t *)(data + 2) != last_pkt) {
556 * Wrong packet, ACK the packet and try again.
557 * This is presumably because the ACK got lost,
558 * so the server just resent the previous packet.
560 #if 0
561 printf("Wrong packet, wanted %04x, got %04x\n", \
562 htons(last_pkt), htons(*(uint16_t *)(data+2)));
563 #endif
564 goto ack_again;
567 /* It's the packet we want. We're also EOF if the size < blocksize */
568 socket->tftp_lastpkt = last_pkt; /* Update last packet number */
569 buffersize = udp_read.buffer_size - 4; /* Skip TFTP header */
570 memcpy(socket->tftp_pktbuf, packet_buf + 4, buffersize);
571 socket->tftp_dataptr = socket->tftp_pktbuf;
572 socket->tftp_filepos += buffersize;
573 socket->tftp_bytesleft = buffersize;
574 if (buffersize < socket->tftp_blksize) {
575 /* it's the last block, ACK packet immediately */
576 ack_packet(inode, *(uint16_t *)(data + 2));
578 /* Make sure we know we are at end of file */
579 inode->size = socket->tftp_filepos;
580 socket->tftp_goteof = 1;
586 * getfssec: Get multiple clusters from a file, given the starting cluster.
587 * In this case, get multiple blocks from a specific TCP connection.
589 * @param: fs, the fs_info structure address, in pxe, we don't use this.
590 * @param: buf, buffer to store the read data
591 * @param: openfile, TFTP socket pointer
592 * @param: blocks, 512-byte block count; 0FFFFh = until end of file
594 * @return: the bytes read
597 static uint32_t pxe_getfssec(struct file *file, char *buf,
598 int blocks, bool *have_more)
600 struct inode *inode = file->inode;
601 struct pxe_pvt_inode *socket = PVT(inode);
602 int count = blocks;
603 int chunk;
604 int bytes_read = 0;
606 count <<= TFTP_BLOCKSIZE_LG2;
607 while (count) {
608 fill_buffer(inode); /* If we have no 'fresh' buffer, get it */
609 if (!socket->tftp_bytesleft)
610 break;
612 chunk = count;
613 if (chunk > socket->tftp_bytesleft)
614 chunk = socket->tftp_bytesleft;
615 socket->tftp_bytesleft -= chunk;
616 memcpy(buf, socket->tftp_dataptr, chunk);
617 socket->tftp_dataptr += chunk;
618 buf += chunk;
619 bytes_read += chunk;
620 count -= chunk;
624 if (socket->tftp_bytesleft || (socket->tftp_filepos < inode->size)) {
625 fill_buffer(inode);
626 *have_more = 1;
627 } else if (socket->tftp_goteof) {
629 * The socket is closed and the buffer drained; the caller will
630 * call close_file and therefore free the socket.
632 *have_more = 0;
635 return bytes_read;
639 * Open a TFTP connection to the server
641 * @param:filename, the file we wanna open
643 * @out: open_file_t structure, stores in file->open_file
644 * @out: the lenght of this file, stores in file->file_len
647 static void __pxe_searchdir(const char *filename, struct file *file);
648 extern uint16_t PXERetry;
650 static void pxe_searchdir(const char *filename, struct file *file)
652 int i = PXERetry;
654 do {
655 dprintf("PXE: file = %p, retries left = %d: ", file, i);
656 __pxe_searchdir(filename, file);
657 dprintf("%s\n", file->inode ? "ok" : "failed");
658 } while (!file->inode && i--);
661 static void __pxe_searchdir(const char *filename, struct file *file)
663 struct fs_info *fs = file->fs;
664 struct inode *inode;
665 struct pxe_pvt_inode *socket;
666 char *buf;
667 const char *np;
668 char *p;
669 char *options;
670 char *data;
671 static __lowmem struct s_PXENV_UDP_WRITE udp_write;
672 static __lowmem struct s_PXENV_UDP_READ udp_read;
673 static __lowmem struct s_PXENV_FILE_OPEN file_open;
674 static const char rrq_tail[] = "octet\0""tsize\0""0\0""blksize\0""1408";
675 static __lowmem char rrq_packet_buf[2+2*FILENAME_MAX+sizeof rrq_tail];
676 const struct tftp_options *tftp_opt;
677 int i = 0;
678 int err;
679 int buffersize;
680 int rrq_len;
681 const uint8_t *timeout_ptr;
682 uint32_t timeout;
683 uint32_t oldtime;
684 uint16_t tid;
685 uint16_t opcode;
686 uint16_t blk_num;
687 uint32_t ip = 0;
688 uint32_t opdata, *opdata_ptr;
689 enum pxe_path_type path_type;
690 char fullpath[2*FILENAME_MAX];
691 uint16_t server_port = TFTP_PORT; /* TFTP server port */
693 inode = file->inode = NULL;
695 buf = rrq_packet_buf;
696 *(uint16_t *)buf = TFTP_RRQ; /* TFTP opcode */
697 buf += 2;
699 path_type = pxe_path_type(filename);
700 if (path_type == PXE_RELATIVE) {
701 snprintf(fullpath, sizeof fullpath, "%s%s", fs->cwd_name, filename);
702 path_type = pxe_path_type(filename = fullpath);
705 switch (path_type) {
706 case PXE_RELATIVE: /* Really shouldn't happen... */
707 case PXE_URL:
708 buf = stpcpy(buf, filename);
709 ip = IPInfo.serverip; /* Default server */
710 break;
712 case PXE_HOMESERVER:
713 buf = stpcpy(buf, filename+2);
714 ip = IPInfo.serverip;
715 break;
717 case PXE_TFTP:
718 np = strchr(filename, ':');
719 buf = stpcpy(buf, np+2);
720 if (parse_dotquad(filename, &ip) != np)
721 ip = dns_resolv(filename);
722 break;
724 case PXE_URL_TFTP:
725 np = filename + 7;
726 while (*np && *np != '/' && *np != ':')
727 np++;
728 if (np > filename + 7) {
729 if (parse_dotquad(filename + 7, &ip) != np)
730 ip = dns_resolv(filename + 7);
732 if (*np == ':') {
733 np++;
734 server_port = 0;
735 while (*np >= '0' && *np <= '9')
736 server_port = server_port * 10 + *np++ - '0';
737 server_port = server_port ? htons(server_port) : TFTP_PORT;
739 if (*np == '/')
740 np++; /* Do *NOT* eat more than one slash here... */
742 * The ; is because of a quirk in the TFTP URI spec (RFC
743 * 3617); it is to be followed by TFTP modes, which we just ignore.
745 while (*np && *np != ';') {
746 int v;
747 if (*np == '%' && (v = hexbyte(np+1)) > 0) {
748 *buf++ = v;
749 np += 3;
750 } else {
751 *buf++ = *np++;
754 *buf = '\0';
755 break;
758 buf++; /* Point *past* the final NULL */
759 memcpy(buf, rrq_tail, sizeof rrq_tail);
760 buf += sizeof rrq_tail;
762 rrq_len = buf - rrq_packet_buf;
764 inode = allocate_socket(fs);
765 if (!inode)
766 return; /* Allocation failure */
767 socket = PVT(inode);
769 #if GPXE
770 if (path_type == PXE_URL) {
771 if (has_gpxe) {
772 file_open.Status = PXENV_STATUS_BAD_FUNC;
773 file_open.FileName = FAR_PTR(rrq_packet_buf + 2);
774 err = pxe_call(PXENV_FILE_OPEN, &file_open);
775 if (err)
776 goto done;
778 socket->tftp_localport = -1;
779 socket->tftp_remoteport = file_open.FileHandle;
780 inode->size = -1;
781 goto done;
782 } else {
783 static bool already = false;
784 if (!already) {
785 printf("URL syntax, but gPXE extensions not detected, "
786 "trying plain TFTP...\n");
787 already = true;
791 #endif /* GPXE */
793 if (!ip)
794 goto done; /* No server */
796 timeout_ptr = TimeoutTable; /* Reset timeout */
798 sendreq:
799 timeout = *timeout_ptr++;
800 if (!timeout) {
801 free_socket(inode);
802 return; /* No file available... */
804 oldtime = jiffies();
806 socket->tftp_remoteip = ip;
807 tid = socket->tftp_localport; /* TID(local port No) */
808 udp_write.buffer = FAR_PTR(rrq_packet_buf);
809 udp_write.ip = ip;
810 udp_write.gw = gateway(udp_write.ip);
811 udp_write.src_port = tid;
812 udp_write.dst_port = server_port;
813 udp_write.buffer_size = rrq_len;
814 pxe_call(PXENV_UDP_WRITE, &udp_write);
816 /* If the WRITE call fails, we let the timeout take care of it... */
818 wait_pkt:
819 for (;;) {
820 buf = packet_buf;
821 udp_read.status = 0;
822 udp_read.buffer = FAR_PTR(buf);
823 udp_read.buffer_size = PKTBUF_SIZE;
824 udp_read.dest_ip = IPInfo.myip;
825 udp_read.d_port = tid;
826 err = pxe_call(PXENV_UDP_READ, &udp_read);
827 if (err || udp_read.status) {
828 uint32_t now = jiffies();
829 if (now - oldtime >= timeout)
830 goto sendreq;
831 } else {
832 /* Make sure the packet actually came from the server */
833 if (udp_read.src_ip == socket->tftp_remoteip)
834 break;
838 socket->tftp_remoteport = udp_read.s_port;
840 /* filesize <- -1 == unknown */
841 inode->size = -1;
842 /* Default blksize unless blksize option negotiated */
843 socket->tftp_blksize = TFTP_BLOCKSIZE;
844 buffersize = udp_read.buffer_size - 2; /* bytes after opcode */
845 if (buffersize < 0)
846 goto wait_pkt; /* Garbled reply */
849 * Get the opcode type, and parse it
851 opcode = *(uint16_t *)packet_buf;
852 switch (opcode) {
853 case TFTP_ERROR:
854 inode->size = 0;
855 break; /* ERROR reply; don't try again */
857 case TFTP_DATA:
859 * If the server doesn't support any options, we'll get a
860 * DATA reply instead of OACK. Stash the data in the file
861 * buffer and go with the default value for all options...
863 * We got a DATA packet, meaning no options are
864 * suported. Save the data away and consider the
865 * length undefined, *unless* this is the only
866 * data packet...
868 buffersize -= 2;
869 if (buffersize < 0)
870 goto wait_pkt;
871 data = packet_buf + 2;
872 blk_num = *(uint16_t *)data;
873 data += 2;
874 if (blk_num != htons(1))
875 goto wait_pkt;
876 socket->tftp_lastpkt = blk_num;
877 if (buffersize > TFTP_BLOCKSIZE)
878 goto err_reply; /* Corrupt */
879 else if (buffersize < TFTP_BLOCKSIZE) {
881 * This is the final EOF packet, already...
882 * We know the filesize, but we also want to
883 * ack the packet and set the EOF flag.
885 inode->size = buffersize;
886 socket->tftp_goteof = 1;
887 ack_packet(inode, blk_num);
890 socket->tftp_bytesleft = buffersize;
891 socket->tftp_dataptr = socket->tftp_pktbuf;
892 memcpy(socket->tftp_pktbuf, data, buffersize);
893 break;
895 case TFTP_OACK:
897 * Now we need to parse the OACK packet to get the transfer
898 * and packet sizes.
901 options = packet_buf + 2;
902 p = options;
904 while (buffersize) {
905 const char *opt = p;
908 * If we find an option which starts with a NUL byte,
909 * (a null option), we're either seeing garbage that some
910 * TFTP servers add to the end of the packet, or we have
911 * no clue how to parse the rest of the packet (what is
912 * an option name and what is a value?) In either case,
913 * discard the rest.
915 if (!*opt)
916 goto done;
918 while (buffersize) {
919 if (!*p)
920 break; /* Found a final null */
921 *p++ |= 0x20;
922 buffersize--;
924 if (!buffersize)
925 break; /* Unterminated option */
927 /* Consume the terminal null */
928 p++;
929 buffersize--;
931 if (!buffersize)
932 break; /* No option data */
935 * Parse option pointed to by options; guaranteed to be
936 * null-terminated
938 tftp_opt = tftp_options;
939 for (i = 0; i < tftp_nopts; i++) {
940 if (!strcmp(opt, tftp_opt->str_ptr))
941 break;
942 tftp_opt++;
944 if (i == tftp_nopts)
945 goto err_reply; /* Non-negotitated option returned,
946 no idea what it means ...*/
948 /* get the address of the filed that we want to write on */
949 opdata_ptr = (uint32_t *)((char *)inode + tftp_opt->offset);
950 opdata = 0;
952 /* do convert a number-string to decimal number, just like atoi */
953 while (buffersize--) {
954 uint8_t d = *p++;
955 if (d == '\0')
956 break; /* found a final null */
957 d -= '0';
958 if (d > 9)
959 goto err_reply; /* Not a decimal digit */
960 opdata = opdata*10 + d;
962 *opdata_ptr = opdata;
964 break;
966 default:
967 printf("TFTP unknown opcode %d\n", ntohs(opcode));
968 goto err_reply;
971 done:
972 if (!inode->size) {
973 free_socket(inode);
974 return;
976 file->inode = inode;
977 return;
979 err_reply:
980 /* Build the TFTP error packet */
981 tftp_error(inode, TFTP_EOPTNEG, "TFTP protocol error");
982 printf("TFTP server sent an incomprehesible reply\n");
983 kaboom();
988 * Store standard filename prefix
990 static void get_prefix(void)
992 int len;
993 char *p;
994 char c;
996 if (!(DHCPMagic & 0x04)) {
997 /* No path prefix option, derive from boot file */
999 strlcpy(path_prefix, boot_file, sizeof path_prefix);
1000 len = strlen(path_prefix);
1001 p = &path_prefix[len - 1];
1003 while (len--) {
1004 c = *p--;
1005 c |= 0x20;
1007 c = (c >= '0' && c <= '9') ||
1008 (c >= 'a' && c <= 'z') ||
1009 (c == '.' || c == '-');
1010 if (!c)
1011 break;
1014 if (len < 0)
1015 p --;
1017 *(p + 2) = 0; /* Zero-terminate after delimiter */
1020 printf("TFTP prefix: %s\n", path_prefix);
1021 chdir(path_prefix);
1025 * realpath for PXE
1027 static size_t pxe_realpath(struct fs_info *fs, char *dst, const char *src,
1028 size_t bufsize)
1030 enum pxe_path_type path_type = pxe_path_type(src);
1032 return snprintf(dst, bufsize, "%s%s",
1033 path_type == PXE_RELATIVE ? fs->cwd_name : "", src);
1037 * chdir for PXE
1039 static int pxe_chdir(struct fs_info *fs, const char *src)
1041 /* The cwd for PXE is just a text prefix */
1042 enum pxe_path_type path_type = pxe_path_type(src);
1044 if (path_type == PXE_RELATIVE)
1045 strlcat(fs->cwd_name, src, sizeof fs->cwd_name);
1046 else
1047 strlcpy(fs->cwd_name, src, sizeof fs->cwd_name);
1049 dprintf("cwd = \"%s\"\n", fs->cwd_name);
1050 return 0;
1053 static int pxe_chdir_start(void)
1055 get_prefix();
1056 return 0;
1059 /* Load the config file, return -1 if failed, or 0 */
1060 static int pxe_open_config(struct com32_filedata *filedata)
1062 const char *cfgprefix = "pxelinux.cfg/";
1063 const char *default_str = "default";
1064 char *config_file;
1065 char *last;
1066 int tries = 8;
1068 if (DHCPMagic & 0x02) {
1069 /* We got a DHCP option, try it first */
1070 if (open_file(ConfigName, filedata) >= 0)
1071 return 0;
1075 * Have to guess config file name ...
1077 config_file = stpcpy(ConfigName, cfgprefix);
1079 /* Try loading by UUID */
1080 if (have_uuid) {
1081 strcpy(config_file, UUID_str);
1082 if (open_file(ConfigName, filedata) >= 0)
1083 return 0;
1086 /* Try loading by MAC address */
1087 strcpy(config_file, MAC_str);
1088 if (open_file(ConfigName, filedata) >= 0)
1089 return 0;
1091 /* Nope, try hexadecimal IP prefixes... */
1092 uchexbytes(config_file, (uint8_t *)&IPInfo.myip, 4);
1093 last = &config_file[8];
1094 while (tries) {
1095 *last = '\0'; /* Zero-terminate string */
1096 if (open_file(ConfigName, filedata) >= 0)
1097 return 0;
1098 last--; /* Drop one character */
1099 tries--;
1102 /* Final attempt: "default" string */
1103 strcpy(config_file, default_str);
1104 if (open_file(ConfigName, filedata) >= 0)
1105 return 0;
1107 printf("%-68s\n", "Unable to locate configuration file");
1108 kaboom();
1112 * Generate the bootif string.
1114 static void make_bootif_string(void)
1116 const uint8_t *src;
1117 char *dst = BOOTIFStr;
1118 int i;
1120 dst += sprintf(dst, "BOOTIF=%02x", MAC_type);
1121 src = MAC;
1122 for (i = MAC_len; i; i--)
1123 dst += sprintf(dst, "-%02x", *src++);
1126 * Generate the SYSUUID string, if we have one...
1128 static void make_sysuuid_string(void)
1130 static const uint8_t uuid_dashes[] = {4, 2, 2, 2, 6, 0};
1131 const uint8_t *src = uuid;
1132 const uint8_t *uuid_ptr = uuid_dashes;
1133 char *dst;
1135 SYSUUIDStr[0] = '\0'; /* If nothing there... */
1137 /* Try loading by UUID */
1138 if (have_uuid) {
1139 dst = stpcpy(SYSUUIDStr, "SYSUUID=");
1141 while (*uuid_ptr) {
1142 int len = *uuid_ptr;
1144 lchexbytes(dst, src, len);
1145 dst += len * 2;
1146 src += len;
1147 uuid_ptr++;
1148 *dst++ = '-';
1150 /* Remove last dash and zero-terminate */
1151 *--dst = '\0';
1156 * Generate an ip=<client-ip>:<boot-server-ip>:<gw-ip>:<netmask>
1157 * option into IPOption based on DHCP information in IPInfo.
1160 char __bss16 IPOption[3+4*16];
1162 static void genipopt(void)
1164 char *p = IPOption;
1165 const uint32_t *v = &IPInfo.myip;
1166 int i;
1168 p = stpcpy(p, "ip=");
1170 for (i = 0; i < 4; i++) {
1171 p += gendotquad(p, *v++);
1172 *p++ = ':';
1174 *--p = '\0';
1178 /* Generate ip= option and print the ip adress */
1179 static void ip_init(void)
1181 uint32_t ip = IPInfo.myip;
1183 genipopt();
1184 gendotquad(dot_quad_buf, ip);
1186 ip = ntohl(ip);
1187 printf("My IP address seems to be %08X %s\n", ip, dot_quad_buf);
1191 * Print the IPAPPEND strings, in order
1193 static void print_ipappend(void)
1195 size_t i;
1197 for (i = 0; i < (size_t)numIPAppends; i++) {
1198 const char *p = (const char *)(size_t)IPAppends[i];
1199 if (*p)
1200 printf("%s\n", p);
1205 * Validity check on possible !PXE structure in buf
1206 * return 1 for success, 0 for failure.
1209 static int is_pxe(const void *buf)
1211 const struct pxe_t *pxe = buf;
1212 const uint8_t *p = buf;
1213 int i = pxe->structlength;
1214 uint8_t sum = 0;
1216 if (i < sizeof(struct pxe_t) ||
1217 memcmp(pxe->signature, "!PXE", 4))
1218 return 0;
1220 while (i--)
1221 sum += *p++;
1223 return sum == 0;
1227 * Just like is_pxe, it checks PXENV+ structure
1230 static int is_pxenv(const void *buf)
1232 const struct pxenv_t *pxenv = buf;
1233 const uint8_t *p = buf;
1234 int i = pxenv->length;
1235 uint8_t sum = 0;
1237 /* The pxeptr field isn't present in old versions */
1238 if (i < offsetof(struct pxenv_t, pxeptr) ||
1239 memcmp(pxenv->signature, "PXENV+", 6))
1240 return 0;
1242 while (i--)
1243 sum += *p++;
1245 return sum == 0;
1251 * memory_scan_for_pxe_struct:
1252 * memory_scan_for_pxenv_struct:
1254 * If none of the standard methods find the !PXE/PXENV+ structure,
1255 * look for it by scanning memory.
1257 * return the corresponding pxe structure if found, or NULL;
1259 static const void *memory_scan(uintptr_t start, int (*func)(const void *))
1261 const char *ptr;
1263 /* Scan each 16 bytes of conventional memory before the VGA region */
1264 for (ptr = (const char *)start; ptr < (const char *)0xA0000; ptr += 16) {
1265 if (func(ptr))
1266 return ptr; /* found it! */
1267 ptr += 16;
1269 return NULL;
1272 static const struct pxe_t *memory_scan_for_pxe_struct(void)
1274 uint16_t start = bios_fbm(); /* Starting segment */
1276 return memory_scan(start << 10, is_pxe);
1279 static const struct pxenv_t *memory_scan_for_pxenv_struct(void)
1281 return memory_scan(0x10000, is_pxenv);
1285 * Find the !PXE structure; we search for the following, in order:
1287 * a. !PXE structure as SS:[SP + 4]
1288 * b. PXENV+ structure at [ES:BX]
1289 * c. INT 1Ah AX=0x5650 -> PXENV+
1290 * d. Search memory for !PXE
1291 * e. Search memory for PXENV+
1293 * If we find a PXENV+ structure, we try to find a !PXE structure from
1294 * if if the API version is 2.1 or later
1297 static int pxe_init(bool quiet)
1299 extern void pxe_int1a(void);
1300 char plan = 'A';
1301 uint16_t seg, off;
1302 uint16_t code_seg, code_len;
1303 uint16_t data_seg, data_len;
1304 const char *base = GET_PTR(InitStack);
1305 com32sys_t regs;
1306 const char *type;
1307 const struct pxenv_t *pxenv;
1308 const struct pxe_t *pxe;
1310 /* Assume API version 2.1 */
1311 APIVer = 0x201;
1313 /* Plan A: !PXE structure as SS:[SP + 4] */
1314 off = *(const uint16_t *)(base + 48);
1315 seg = *(const uint16_t *)(base + 50);
1316 pxe = MK_PTR(seg, off);
1317 if (is_pxe(pxe))
1318 goto have_pxe;
1320 /* Plan B: PXENV+ structure at [ES:BX] */
1321 plan++;
1322 off = *(const uint16_t *)(base + 24); /* Original BX */
1323 seg = *(const uint16_t *)(base + 4); /* Original ES */
1324 pxenv = MK_PTR(seg, off);
1325 if (is_pxenv(pxenv))
1326 goto have_pxenv;
1328 /* Plan C: PXENV+ structure via INT 1Ah AX=5650h */
1329 plan++;
1330 memset(&regs, 0, sizeof regs);
1331 regs.eax.w[0] = 0x5650;
1332 call16(pxe_int1a, &regs, &regs);
1333 if (!(regs.eflags.l & EFLAGS_CF) && (regs.eax.w[0] == 0x564e)) {
1334 pxenv = MK_PTR(regs.es, regs.ebx.w[0]);
1335 if (is_pxenv(pxenv))
1336 goto have_pxenv;
1339 /* Plan D: !PXE memory scan */
1340 plan++;
1341 if ((pxe = memory_scan_for_pxe_struct()))
1342 goto have_pxe;
1344 /* Plan E: PXENV+ memory scan */
1345 plan++;
1346 if ((pxenv = memory_scan_for_pxenv_struct()))
1347 goto have_pxenv;
1349 /* Found nothing at all !! */
1350 if (!quiet)
1351 printf("No !PXE or PXENV+ API found; we're dead...\n");
1352 return -1;
1354 have_pxenv:
1355 APIVer = pxenv->version;
1356 if (!quiet)
1357 printf("Found PXENV+ structure\nPXE API version is %04x\n", APIVer);
1359 /* if the API version number is 0x0201 or higher, use the !PXE structure */
1360 if (APIVer >= 0x201) {
1361 if (pxenv->length >= sizeof(struct pxenv_t)) {
1362 pxe = GET_PTR(pxenv->pxeptr);
1363 if (is_pxe(pxe))
1364 goto have_pxe;
1366 * Nope, !PXE structure missing despite API 2.1+, or at least
1367 * the pointer is missing. Do a last-ditch attempt to find it
1369 if ((pxe = memory_scan_for_pxe_struct()))
1370 goto have_pxe;
1372 APIVer = 0x200; /* PXENV+ only, assume version 2.00 */
1375 /* Otherwise, no dice, use PXENV+ structure */
1376 data_len = pxenv->undidatasize;
1377 data_seg = pxenv->undidataseg;
1378 code_len = pxenv->undicodesize;
1379 code_seg = pxenv->undicodeseg;
1380 PXEEntry = pxenv->rmentry;
1381 type = "PXENV+";
1382 goto have_entrypoint;
1384 have_pxe:
1385 data_len = pxe->seg[PXE_Seg_UNDIData].size;
1386 data_seg = pxe->seg[PXE_Seg_UNDIData].sel;
1387 code_len = pxe->seg[PXE_Seg_UNDICode].size;
1388 code_seg = pxe->seg[PXE_Seg_UNDICode].sel;
1389 PXEEntry = pxe->entrypointsp;
1390 type = "!PXE";
1392 have_entrypoint:
1393 if (!quiet) {
1394 printf("%s entry point found (we hope) at %04X:%04X via plan %c\n",
1395 type, PXEEntry.seg, PXEEntry.offs, plan);
1396 printf("UNDI code segment at %04X len %04X\n", code_seg, code_len);
1397 printf("UNDI data segment at %04X len %04X\n", data_seg, data_len);
1400 code_seg = code_seg + ((code_len + 15) >> 4);
1401 data_seg = data_seg + ((data_len + 15) >> 4);
1403 real_base_mem = max(code_seg, data_seg) >> 6; /* Convert to kilobytes */
1405 return 0;
1409 * See if we have gPXE
1411 static void gpxe_init(void)
1413 int err;
1414 static __lowmem struct s_PXENV_FILE_API_CHECK api_check;
1416 if (APIVer >= 0x201) {
1417 api_check.Size = sizeof api_check;
1418 api_check.Magic = 0x91d447b2;
1419 err = pxe_call(PXENV_FILE_API_CHECK, &api_check);
1420 if (!err && api_check.Magic == 0xe9c17b20)
1421 gpxe_funcs = api_check.APIMask;
1424 /* Necessary functions for us to use the gPXE file API */
1425 has_gpxe = (~gpxe_funcs & 0x4b) == 0;
1429 * Initialize UDP stack
1432 static void udp_init(void)
1434 int err;
1435 static __lowmem struct s_PXENV_UDP_OPEN udp_open;
1436 udp_open.src_ip = IPInfo.myip;
1437 err = pxe_call(PXENV_UDP_OPEN, &udp_open);
1438 if (err || udp_open.status) {
1439 printf("Failed to initialize UDP stack ");
1440 printf("%d\n", udp_open.status);
1441 kaboom();
1447 * Network-specific initialization
1449 static void network_init(void)
1451 int pkt_len;
1452 struct bootp_t *bp;
1453 const size_t dhcp_max_packet = 4096;
1455 bp = lmalloc(dhcp_max_packet);
1456 if (!bp) {
1457 printf("Out of low memory\n");
1458 kaboom();
1461 *LocalDomain = 0; /* No LocalDomain received */
1464 * Get the DHCP client identifiers (query info 1)
1466 printf("Getting cached packet ");
1467 pkt_len = pxe_get_cached_info(1, bp, dhcp_max_packet);
1468 parse_dhcp(bp, pkt_len);
1470 * We don't use flags from the request packet, so
1471 * this is a good time to initialize DHCPMagic...
1472 * Initialize it to 1 meaning we will accept options found;
1473 * in earlier versions of PXELINUX bit 0 was used to indicate
1474 * we have found option 208 with the appropriate magic number;
1475 * we no longer require that, but MAY want to re-introduce
1476 * it in the future for vendor encapsulated options.
1478 *(char *)&DHCPMagic = 1;
1481 * Get the BOOTP/DHCP packet that brought us file (and an IP
1482 * address). This lives in the DHCPACK packet (query info 2)
1484 pkt_len = pxe_get_cached_info(2, bp, dhcp_max_packet);
1485 parse_dhcp(bp, pkt_len);
1487 * Save away MAC address (assume this is in query info 2. If this
1488 * turns out to be problematic it might be better getting it from
1489 * the query info 1 packet
1491 MAC_len = bp->hardlen > 16 ? 0 : bp->hardlen;
1492 MAC_type = bp->hardware;
1493 memcpy(MAC, bp->macaddr, MAC_len);
1496 * Get the boot file and other info. This lives in the CACHED_REPLY
1497 * packet (query info 3)
1499 pkt_len = pxe_get_cached_info(3, bp, dhcp_max_packet);
1500 parse_dhcp(bp, pkt_len);
1501 printf("\n");
1503 lfree(bp);
1505 make_bootif_string();
1506 make_sysuuid_string();
1507 ip_init();
1508 print_ipappend();
1511 * Check to see if we got any PXELINUX-specific DHCP options; in particular,
1512 * if we didn't get the magic enable, do not recognize any other options.
1514 if ((DHCPMagic & 1) == 0)
1515 DHCPMagic = 0;
1517 udp_init();
1521 * Initialize pxe fs
1524 static int pxe_fs_init(struct fs_info *fs)
1526 (void)fs; /* drop the compile warning message */
1528 /* This block size is actually arbitrary... */
1529 fs->sector_shift = fs->block_shift = TFTP_BLOCKSIZE_LG2;
1530 fs->sector_size = fs->block_size = 1 << TFTP_BLOCKSIZE_LG2;
1532 /* This block size is actually arbitrary... */
1533 fs->sector_shift = fs->block_shift = TFTP_BLOCKSIZE_LG2;
1534 fs->sector_size = fs->block_size = 1 << TFTP_BLOCKSIZE_LG2;
1536 /* Find the PXE stack */
1537 if (pxe_init(false))
1538 kaboom();
1540 /* See if we also have a gPXE stack */
1541 gpxe_init();
1543 /* Network-specific initialization */
1544 network_init();
1546 /* Initialize network-card-specific idle handling */
1547 pxe_idle_init();
1549 /* Our name for the root */
1550 strcpy(fs->cwd_name, "::");
1552 return 0;
1556 * Look to see if we are on an EFI CSM system. Some EFI
1557 * CSM systems put the BEV stack in low memory, which means
1558 * a return to the PXE stack will crash the system. However,
1559 * INT 18h works reliably, so in that case hack the stack and
1560 * point the "return address" to an INT 18h instruction.
1562 * Hack the stack instead of the much simpler "just invoke INT 18h
1563 * if we want to reset", so that chainloading other NBPs will work.
1565 * This manipulates the real-mode InitStack directly. It relies on this
1566 * *not* being a currently active stack, i.e. the former
1567 * USE_PXE_PROVIDED_STACK no longer works.
1569 * XXX: Disable this until we can find a better way to discriminate
1570 * between BIOSes that are broken on BEV return and BIOSes which are
1571 * broken on INT 18h. Keying on the EFI CSM turns out to cause more
1572 * problems than it solves.
1574 extern far_ptr_t InitStack;
1576 struct efi_struct {
1577 uint32_t magic;
1578 uint8_t csum;
1579 uint8_t len;
1580 } __attribute__((packed));
1581 #define EFI_MAGIC (('$' << 24)+('E' << 16)+('F' << 8)+'I')
1583 static inline bool is_efi(const struct efi_struct *efi)
1586 * We don't verify the checksum, because it seems some CSMs leave
1587 * it at zero, sigh...
1589 return (efi->magic == EFI_MAGIC) && (efi->len >= 83);
1592 static void install_int18_hack(void)
1594 #if 0
1595 static const uint8_t int18_hack[] =
1597 0xcd, 0x18, /* int $0x18 */
1598 0xea, 0xf0, 0xff, 0x00, 0xf0, /* ljmpw $0xf000,$0xfff0 */
1599 0xf4 /* hlt */
1601 uint16_t *retcode;
1603 retcode = GET_PTR(*(far_ptr_t *)((char *)GET_PTR(InitStack) + 44));
1605 /* Don't do this if the return already points to int $0x18 */
1606 if (*retcode != 0x18cd) {
1607 uint32_t efi_ptr;
1608 bool efi = false;
1610 for (efi_ptr = 0xe0000 ; efi_ptr < 0x100000 ; efi_ptr += 16) {
1611 if (is_efi((const struct efi_struct *)efi_ptr)) {
1612 efi = true;
1613 break;
1617 if (efi) {
1618 uint8_t *src = GET_PTR(InitStack);
1619 uint8_t *dst = src - sizeof int18_hack;
1621 memmove(dst, src, 52);
1622 memcpy(dst+52, int18_hack, sizeof int18_hack);
1623 InitStack.offs -= sizeof int18_hack;
1625 /* Clobber the return address */
1626 *(uint16_t *)(dst+44) = OFFS_WRT(dst+52, InitStack.seg);
1627 *(uint16_t *)(dst+46) = InitStack.seg;
1630 #endif
1633 int reset_pxe(void)
1635 static __lowmem struct s_PXENV_UDP_CLOSE udp_close;
1636 extern void gpxe_unload(void);
1637 int err = 0;
1639 pxe_idle_cleanup();
1641 pxe_call(PXENV_UDP_CLOSE, &udp_close);
1643 if (gpxe_funcs & 0x80) {
1644 /* gPXE special unload implemented */
1645 call16(gpxe_unload, &zero_regs, NULL);
1647 /* Locate the actual vendor stack... */
1648 err = pxe_init(true);
1651 install_int18_hack();
1652 return err;
1656 * This function unloads the PXE and UNDI stacks and
1657 * unclaims the memory.
1659 __export void unload_pxe(uint16_t flags)
1661 /* PXE unload sequences */
1662 static const uint8_t new_api_unload[] = {
1663 PXENV_UNDI_SHUTDOWN, PXENV_UNLOAD_STACK, PXENV_STOP_UNDI, 0
1665 static const uint8_t old_api_unload[] = {
1666 PXENV_UNDI_SHUTDOWN, PXENV_UNLOAD_STACK, PXENV_UNDI_CLEANUP, 0
1669 unsigned int api;
1670 const uint8_t *api_ptr;
1671 int err;
1672 size_t int_addr;
1673 static __lowmem union {
1674 struct s_PXENV_UNDI_SHUTDOWN undi_shutdown;
1675 struct s_PXENV_UNLOAD_STACK unload_stack;
1676 struct s_PXENV_STOP_UNDI stop_undi;
1677 struct s_PXENV_UNDI_CLEANUP undi_cleanup;
1678 uint16_t Status; /* All calls have this as the first member */
1679 } unload_call;
1681 dprintf("FBM before unload = %d\n", bios_fbm());
1683 err = reset_pxe();
1685 dprintf("FBM after reset_pxe = %d, err = %d\n", bios_fbm(), err);
1687 /* If we want to keep PXE around, we still need to reset it */
1688 if (flags || err)
1689 return;
1691 dprintf("APIVer = %04x\n", APIVer);
1693 api_ptr = APIVer >= 0x0200 ? new_api_unload : old_api_unload;
1694 while((api = *api_ptr++)) {
1695 dprintf("PXE call %04x\n", api);
1696 memset(&unload_call, 0, sizeof unload_call);
1697 err = pxe_call(api, &unload_call);
1698 if (err || unload_call.Status != PXENV_STATUS_SUCCESS) {
1699 dprintf("PXE unload API call %04x failed\n", api);
1700 goto cant_free;
1704 api = 0xff00;
1705 if (real_base_mem <= bios_fbm()) { /* Sanity check */
1706 dprintf("FBM %d < real_base_mem %d\n", bios_fbm(), real_base_mem);
1707 goto cant_free;
1709 api++;
1711 /* Check that PXE actually unhooked the INT 0x1A chain */
1712 int_addr = (size_t)GET_PTR(*(far_ptr_t *)(4 * 0x1a));
1713 int_addr >>= 10;
1714 if (int_addr >= real_base_mem || int_addr < bios_fbm()) {
1715 set_bios_fbm(real_base_mem);
1716 dprintf("FBM after unload_pxe = %d\n", bios_fbm());
1717 return;
1720 dprintf("Can't free FBM, real_base_mem = %d, "
1721 "FBM = %d, INT 1A = %08x (%d)\n",
1722 real_base_mem, bios_fbm(),
1723 *(uint32_t *)(4 * 0x1a), int_addr);
1725 cant_free:
1726 printf("Failed to free base memory error %04x-%08x (%d/%dK)\n",
1727 api, *(uint32_t *)(4 * 0x1a), bios_fbm(), real_base_mem);
1728 return;
1731 const struct fs_ops pxe_fs_ops = {
1732 .fs_name = "pxe",
1733 .fs_flags = FS_NODEV,
1734 .fs_init = pxe_fs_init,
1735 .searchdir = pxe_searchdir,
1736 .chdir = pxe_chdir,
1737 .realpath = pxe_realpath,
1738 .getfssec = pxe_getfssec,
1739 .close_file = pxe_close_file,
1740 .mangle_name = pxe_mangle_name,
1741 .chdir_start = pxe_chdir_start,
1742 .open_config = pxe_open_config,