Adding upstream version 4.01+dfsg.
[syslinux-debian/hramrach.git] / core / fs / pxe / pxe.c
blob25bd094dbfbb38d0cd30b53f97b1bf2558c7335a
1 #include <dprintf.h>
2 #include <stdio.h>
3 #include <string.h>
4 #include <core.h>
5 #include <fs.h>
6 #include <minmax.h>
7 #include <sys/cpu.h>
8 #include "pxe.h"
10 #define GPXE 1
12 static uint16_t real_base_mem; /* Amount of DOS memory after freeing */
14 uint8_t MAC[MAC_MAX]; /* Actual MAC address */
15 uint8_t MAC_len; /* MAC address len */
16 uint8_t MAC_type; /* MAC address type */
18 char __bss16 BOOTIFStr[7+3*(MAC_MAX+1)];
19 #define MAC_str (BOOTIFStr+7) /* The actual hardware address */
20 char __bss16 SYSUUIDStr[8+32+5];
21 #define UUID_str (SYSUUIDStr+8) /* The actual UUID */
23 char boot_file[256]; /* From DHCP */
24 char path_prefix[256]; /* From DHCP */
25 char dot_quad_buf[16];
27 static bool has_gpxe;
28 static uint32_t gpxe_funcs;
29 bool have_uuid = false;
31 /* Common receive buffer */
32 static __lowmem char packet_buf[PKTBUF_SIZE] __aligned(16);
34 const uint8_t TimeoutTable[] = {
35 2, 2, 3, 3, 4, 5, 6, 7, 9, 10, 12, 15, 18, 21, 26, 31, 37, 44,
36 53, 64, 77, 92, 110, 132, 159, 191, 229, 255, 255, 255, 255, 0
39 struct tftp_options {
40 const char *str_ptr; /* string pointer */
41 size_t offset; /* offset into socket structre */
44 #define IFIELD(x) offsetof(struct inode, x)
45 #define PFIELD(x) (offsetof(struct inode, pvt) + \
46 offsetof(struct pxe_pvt_inode, x))
48 static const struct tftp_options tftp_options[] =
50 { "tsize", IFIELD(size) },
51 { "blksize", PFIELD(tftp_blksize) },
53 static const int tftp_nopts = sizeof tftp_options / sizeof tftp_options[0];
55 static void tftp_error(struct inode *file, uint16_t errnum,
56 const char *errstr);
59 * Allocate a local UDP port structure and assign it a local port number.
60 * Return the inode pointer if success, or null if failure
62 static struct inode *allocate_socket(struct fs_info *fs)
64 struct inode *inode = alloc_inode(fs, 0, sizeof(struct pxe_pvt_inode));
66 if (!inode) {
67 malloc_error("socket structure");
68 } else {
69 struct pxe_pvt_inode *socket = PVT(inode);
70 socket->tftp_localport = get_port();
71 inode->mode = DT_REG; /* No other types relevant for PXE */
74 return inode;
77 static void free_socket(struct inode *inode)
79 struct pxe_pvt_inode *socket = PVT(inode);
81 free_port(socket->tftp_localport);
82 free_inode(inode);
85 #if GPXE
86 static void gpxe_close_file(struct inode *inode)
88 struct pxe_pvt_inode *socket = PVT(inode);
89 static __lowmem struct s_PXENV_FILE_CLOSE file_close;
91 file_close.FileHandle = socket->tftp_remoteport;
92 pxe_call(PXENV_FILE_CLOSE, &file_close);
94 #endif
96 static void pxe_close_file(struct file *file)
98 struct inode *inode = file->inode;
99 struct pxe_pvt_inode *socket = PVT(inode);
101 if (!socket->tftp_goteof) {
102 #if GPXE
103 if (socket->tftp_localport == 0xffff) {
104 gpxe_close_file(inode);
105 } else
106 #endif
107 if (socket->tftp_localport != 0) {
108 tftp_error(inode, 0, "No error, file close");
112 free_socket(inode);
116 * Take a nubmer of bytes in memory and convert to lower-case hxeadecimal
118 * @param: dst, output buffer
119 * @param: src, input buffer
120 * @param: count, number of bytes
123 static void lchexbytes(char *dst, const void *src, int count)
125 uint8_t half;
126 uint8_t c;
127 const uint8_t *s = src;
129 for(; count > 0; count--) {
130 c = *s++;
131 half = ((c >> 4) & 0x0f) + '0';
132 *dst++ = half > '9' ? (half + 'a' - '9' - 1) : half;
134 half = (c & 0x0f) + '0';
135 *dst++ = half > '9' ? (half + 'a' - '9' - 1) : half;
140 * just like the lchexbytes, except to upper-case
143 static void uchexbytes(char *dst, const void *src, int count)
145 uint8_t half;
146 uint8_t c;
147 const uint8_t *s = src;
149 for(; count > 0; count--) {
150 c = *s++;
151 half = ((c >> 4) & 0x0f) + '0';
152 *dst++ = half > '9' ? (half + 'A' - '9' - 1) : half;
154 half = (c & 0x0f) + '0';
155 *dst++ = half > '9' ? (half + 'A' - '9' - 1) : half;
160 * Parse a single hexadecimal byte, which must be complete (two
161 * digits). This is used in URL parsing.
163 static int hexbyte(const char *p)
165 if (!is_hex(p[0]) || !is_hex(p[1]))
166 return -1;
167 else
168 return (hexval(p[0]) << 4) + hexval(p[1]);
172 * Tests an IP address in _ip_ for validity; return with 0 for bad, 1 for good.
173 * We used to refuse class E, but class E addresses are likely to become
174 * assignable unicast addresses in the near future.
177 bool ip_ok(uint32_t ip)
179 uint8_t ip_hi = (uint8_t)ip; /* First octet of the ip address */
181 if (ip == 0xffffffff || /* Refuse the all-ones address */
182 ip_hi == 0 || /* Refuse network zero */
183 ip_hi == 127 || /* Refuse the loopback network */
184 (ip_hi & 240) == 224) /* Refuse class D */
185 return false;
187 return true;
192 * Take an IP address (in network byte order) in _ip_ and
193 * output a dotted quad string to _dst_, returns the length
194 * of the dotted quad ip string.
197 static int gendotquad(char *dst, uint32_t ip)
199 int part;
200 int i = 0, j;
201 char temp[4];
202 char *p = dst;
204 for (; i < 4; i++) {
205 j = 0;
206 part = ip & 0xff;
207 do {
208 temp[j++] = (part % 10) + '0';
209 }while(part /= 10);
210 for (; j > 0; j--)
211 *p++ = temp[j-1];
212 *p++ = '.';
214 ip >>= 8;
216 /* drop the last dot '.' and zero-terminate string*/
217 *(--p) = 0;
219 return p - dst;
223 * parse the ip_str and return the ip address with *res.
224 * return the the string address after the ip string
227 static const char *parse_dotquad(const char *ip_str, uint32_t *res)
229 const char *p = ip_str;
230 uint8_t part = 0;
231 uint32_t ip = 0;
232 int i;
234 for (i = 0; i < 4; i++) {
235 while (is_digit(*p)) {
236 part = part * 10 + *p - '0';
237 p++;
239 if (i != 3 && *p != '.')
240 return NULL;
242 ip = (ip << 8) | part;
243 part = 0;
244 p++;
246 p--;
248 *res = ip;
249 return p;
253 * the ASM pxenv function wrapper, return 1 if error, or 0
256 int pxe_call(int opcode, void *data)
258 extern void pxenv(void);
259 com32sys_t regs;
261 #if 0
262 printf("pxe_call op %04x data %p\n", opcode, data);
263 #endif
265 memset(&regs, 0, sizeof regs);
266 regs.ebx.w[0] = opcode;
267 regs.es = SEG(data);
268 regs.edi.w[0] = OFFS(data);
269 call16(pxenv, &regs, &regs);
271 return regs.eflags.l & EFLAGS_CF; /* CF SET if fail */
275 * Send an ERROR packet. This is used to terminate a connection.
277 * @inode: Inode structure
278 * @errnum: Error number (network byte order)
279 * @errstr: Error string (included in packet)
281 static void tftp_error(struct inode *inode, uint16_t errnum,
282 const char *errstr)
284 static __lowmem struct {
285 uint16_t err_op;
286 uint16_t err_num;
287 char err_msg[64];
288 } __packed err_buf;
289 static __lowmem struct s_PXENV_UDP_WRITE udp_write;
290 int len = min(strlen(errstr), sizeof(err_buf.err_msg)-1);
291 struct pxe_pvt_inode *socket = PVT(inode);
293 err_buf.err_op = TFTP_ERROR;
294 err_buf.err_num = errnum;
295 memcpy(err_buf.err_msg, errstr, len);
296 err_buf.err_msg[len] = '\0';
298 udp_write.src_port = socket->tftp_localport;
299 udp_write.dst_port = socket->tftp_remoteport;
300 udp_write.ip = socket->tftp_remoteip;
301 udp_write.gw = gateway(udp_write.ip);
302 udp_write.buffer = FAR_PTR(&err_buf);
303 udp_write.buffer_size = 4 + len + 1;
305 /* If something goes wrong, there is nothing we can do, anyway... */
306 pxe_call(PXENV_UDP_WRITE, &udp_write);
311 * Send ACK packet. This is a common operation and so is worth canning.
313 * @param: inode, Inode pointer
314 * @param: ack_num, Packet # to ack (network byte order)
317 static void ack_packet(struct inode *inode, uint16_t ack_num)
319 int err;
320 static __lowmem uint16_t ack_packet_buf[2];
321 static __lowmem struct s_PXENV_UDP_WRITE udp_write;
322 struct pxe_pvt_inode *socket = PVT(inode);
324 /* Packet number to ack */
325 ack_packet_buf[0] = TFTP_ACK;
326 ack_packet_buf[1] = ack_num;
327 udp_write.src_port = socket->tftp_localport;
328 udp_write.dst_port = socket->tftp_remoteport;
329 udp_write.ip = socket->tftp_remoteip;
330 udp_write.gw = gateway(udp_write.ip);
331 udp_write.buffer = FAR_PTR(ack_packet_buf);
332 udp_write.buffer_size = 4;
334 err = pxe_call(PXENV_UDP_WRITE, &udp_write);
335 #if 0
336 printf("sent %s\n", err ? "FAILED" : "OK");
337 #endif
342 * Get a DHCP packet from the PXE stack into the trackbuf
344 * @param: type, packet type
345 * @return: buffer size
348 static int pxe_get_cached_info(int type)
350 int err;
351 static __lowmem struct s_PXENV_GET_CACHED_INFO get_cached_info;
352 printf(" %02x", type);
354 get_cached_info.Status = 0;
355 get_cached_info.PacketType = type;
356 get_cached_info.BufferSize = 8192;
357 get_cached_info.Buffer = FAR_PTR(trackbuf);
358 err = pxe_call(PXENV_GET_CACHED_INFO, &get_cached_info);
359 if (err) {
360 printf("PXE API call failed, error %04x\n", err);
361 kaboom();
364 return get_cached_info.BufferSize;
369 * Return the type of pathname passed.
371 enum pxe_path_type {
372 PXE_RELATIVE, /* No :: or URL */
373 PXE_HOMESERVER, /* Starting with :: */
374 PXE_TFTP, /* host:: */
375 PXE_URL_TFTP, /* tftp:// */
376 PXE_URL, /* Absolute URL syntax */
379 static enum pxe_path_type pxe_path_type(const char *str)
381 const char *p;
383 p = str;
385 while (1) {
386 switch (*p) {
387 case ':':
388 if (p[1] == ':') {
389 if (p == str)
390 return PXE_HOMESERVER;
391 else
392 return PXE_TFTP;
393 } else if (p > str && p[1] == '/' && p[2] == '/') {
394 if (!strncasecmp(str, "tftp://", 7))
395 return PXE_URL_TFTP;
396 else
397 return PXE_URL;
400 /* else fall through */
401 case '/': case '!': case '@': case '#': case '%':
402 case '^': case '&': case '*': case '(': case ')':
403 case '[': case ']': case '{': case '}': case '\\':
404 case '|': case '=': case '`': case '~': case '\'':
405 case '\"': case ';': case '>': case '<': case '?':
406 case '\0':
407 /* Any of these characters terminate the colon search */
408 return PXE_RELATIVE;
409 default:
410 break;
412 p++;
416 #if GPXE
419 * Get a fresh packet from a gPXE socket
420 * @param: inode -> Inode pointer
423 static void get_packet_gpxe(struct inode *inode)
425 struct pxe_pvt_inode *socket = PVT(inode);
426 static __lowmem struct s_PXENV_FILE_READ file_read;
427 int err;
429 while (1) {
430 file_read.FileHandle = socket->tftp_remoteport;
431 file_read.Buffer = FAR_PTR(packet_buf);
432 file_read.BufferSize = PKTBUF_SIZE;
433 err = pxe_call(PXENV_FILE_READ, &file_read);
434 if (!err) /* successed */
435 break;
437 if (file_read.Status != PXENV_STATUS_TFTP_OPEN)
438 kaboom();
441 memcpy(socket->tftp_pktbuf, packet_buf, file_read.BufferSize);
443 socket->tftp_dataptr = socket->tftp_pktbuf;
444 socket->tftp_bytesleft = file_read.BufferSize;
445 socket->tftp_filepos += file_read.BufferSize;
447 if (socket->tftp_bytesleft == 0)
448 inode->size = socket->tftp_filepos;
450 /* if we're done here, close the file */
451 if (inode->size > socket->tftp_filepos)
452 return;
454 /* Got EOF, close it */
455 socket->tftp_goteof = 1;
456 gpxe_close_file(inode);
458 #endif /* GPXE */
462 * mangle a filename pointed to by _src_ into a buffer pointed
463 * to by _dst_; ends on encountering any whitespace.
466 static void pxe_mangle_name(char *dst, const char *src)
468 size_t len = FILENAME_MAX-1;
470 while (len-- && not_whitespace(*src))
471 *dst++ = *src++;
473 *dst = '\0';
477 * Get a fresh packet if the buffer is drained, and we haven't hit
478 * EOF yet. The buffer should be filled immediately after draining!
480 static void fill_buffer(struct inode *inode)
482 int err;
483 int last_pkt;
484 const uint8_t *timeout_ptr;
485 uint8_t timeout;
486 uint16_t buffersize;
487 uint32_t oldtime;
488 void *data = NULL;
489 static __lowmem struct s_PXENV_UDP_READ udp_read;
490 struct pxe_pvt_inode *socket = PVT(inode);
492 if (socket->tftp_bytesleft || socket->tftp_goteof)
493 return;
495 #if GPXE
496 if (socket->tftp_localport == 0xffff) {
497 get_packet_gpxe(inode);
498 return;
500 #endif
503 * Start by ACKing the previous packet; this should cause
504 * the next packet to be sent.
506 timeout_ptr = TimeoutTable;
507 timeout = *timeout_ptr++;
508 oldtime = jiffies();
510 ack_again:
511 ack_packet(inode, socket->tftp_lastpkt);
513 while (timeout) {
514 udp_read.buffer = FAR_PTR(packet_buf);
515 udp_read.buffer_size = PKTBUF_SIZE;
516 udp_read.src_ip = socket->tftp_remoteip;
517 udp_read.dest_ip = IPInfo.myip;
518 udp_read.s_port = socket->tftp_remoteport;
519 udp_read.d_port = socket->tftp_localport;
520 err = pxe_call(PXENV_UDP_READ, &udp_read);
521 if (err) {
522 uint32_t now = jiffies();
524 if (now-oldtime >= timeout) {
525 oldtime = now;
526 timeout = *timeout_ptr++;
527 if (!timeout)
528 break;
530 continue;
533 if (udp_read.buffer_size < 4) /* Bad size for a DATA packet */
534 continue;
536 data = packet_buf;
537 if (*(uint16_t *)data != TFTP_DATA) /* Not a data packet */
538 continue;
540 /* If goes here, recevie OK, break */
541 break;
544 /* time runs out */
545 if (timeout == 0)
546 kaboom();
548 last_pkt = socket->tftp_lastpkt;
549 last_pkt = ntohs(last_pkt); /* Host byte order */
550 last_pkt++;
551 last_pkt = htons(last_pkt); /* Network byte order */
552 if (*(uint16_t *)(data + 2) != last_pkt) {
554 * Wrong packet, ACK the packet and try again.
555 * This is presumably because the ACK got lost,
556 * so the server just resent the previous packet.
558 #if 0
559 printf("Wrong packet, wanted %04x, got %04x\n", \
560 htons(last_pkt), htons(*(uint16_t *)(data+2)));
561 #endif
562 goto ack_again;
565 /* It's the packet we want. We're also EOF if the size < blocksize */
566 socket->tftp_lastpkt = last_pkt; /* Update last packet number */
567 buffersize = udp_read.buffer_size - 4; /* Skip TFTP header */
568 memcpy(socket->tftp_pktbuf, packet_buf + 4, buffersize);
569 socket->tftp_dataptr = socket->tftp_pktbuf;
570 socket->tftp_filepos += buffersize;
571 socket->tftp_bytesleft = buffersize;
572 if (buffersize < socket->tftp_blksize) {
573 /* it's the last block, ACK packet immediately */
574 ack_packet(inode, *(uint16_t *)(data + 2));
576 /* Make sure we know we are at end of file */
577 inode->size = socket->tftp_filepos;
578 socket->tftp_goteof = 1;
584 * getfssec: Get multiple clusters from a file, given the starting cluster.
585 * In this case, get multiple blocks from a specific TCP connection.
587 * @param: fs, the fs_info structure address, in pxe, we don't use this.
588 * @param: buf, buffer to store the read data
589 * @param: openfile, TFTP socket pointer
590 * @param: blocks, 512-byte block count; 0FFFFh = until end of file
592 * @return: the bytes read
595 static uint32_t pxe_getfssec(struct file *file, char *buf,
596 int blocks, bool *have_more)
598 struct inode *inode = file->inode;
599 struct pxe_pvt_inode *socket = PVT(inode);
600 int count = blocks;
601 int chunk;
602 int bytes_read = 0;
604 count <<= TFTP_BLOCKSIZE_LG2;
605 while (count) {
606 fill_buffer(inode); /* If we have no 'fresh' buffer, get it */
607 if (!socket->tftp_bytesleft)
608 break;
610 chunk = count;
611 if (chunk > socket->tftp_bytesleft)
612 chunk = socket->tftp_bytesleft;
613 socket->tftp_bytesleft -= chunk;
614 memcpy(buf, socket->tftp_dataptr, chunk);
615 socket->tftp_dataptr += chunk;
616 buf += chunk;
617 bytes_read += chunk;
618 count -= chunk;
622 if (socket->tftp_bytesleft || (socket->tftp_filepos < inode->size)) {
623 fill_buffer(inode);
624 *have_more = 1;
625 } else if (socket->tftp_goteof) {
627 * The socket is closed and the buffer drained; the caller will
628 * call close_file and therefore free the socket.
630 *have_more = 0;
633 return bytes_read;
637 * Open a TFTP connection to the server
639 * @param:filename, the file we wanna open
641 * @out: open_file_t structure, stores in file->open_file
642 * @out: the lenght of this file, stores in file->file_len
645 static void pxe_searchdir(const char *filename, struct file *file)
647 struct fs_info *fs = file->fs;
648 struct inode *inode;
649 struct pxe_pvt_inode *socket;
650 char *buf;
651 const char *np;
652 char *p;
653 char *options;
654 char *data;
655 static __lowmem struct s_PXENV_UDP_WRITE udp_write;
656 static __lowmem struct s_PXENV_UDP_READ udp_read;
657 static __lowmem struct s_PXENV_FILE_OPEN file_open;
658 static const char rrq_tail[] = "octet\0""tsize\0""0\0""blksize\0""1408";
659 static __lowmem char rrq_packet_buf[2+2*FILENAME_MAX+sizeof rrq_tail];
660 const struct tftp_options *tftp_opt;
661 int i = 0;
662 int err;
663 int buffersize;
664 int rrq_len;
665 const uint8_t *timeout_ptr;
666 uint32_t timeout;
667 uint32_t oldtime;
668 uint16_t tid;
669 uint16_t opcode;
670 uint16_t blk_num;
671 uint32_t ip = 0;
672 uint32_t opdata, *opdata_ptr;
673 enum pxe_path_type path_type;
674 char fullpath[2*FILENAME_MAX];
675 uint16_t server_port = TFTP_PORT; /* TFTP server port */
677 inode = file->inode = NULL;
679 buf = rrq_packet_buf;
680 *(uint16_t *)buf = TFTP_RRQ; /* TFTP opcode */
681 buf += 2;
683 path_type = pxe_path_type(filename);
684 if (path_type == PXE_RELATIVE) {
685 snprintf(fullpath, sizeof fullpath, "%s%s", fs->cwd_name, filename);
686 path_type = pxe_path_type(filename = fullpath);
689 switch (path_type) {
690 case PXE_RELATIVE: /* Really shouldn't happen... */
691 case PXE_URL:
692 buf = stpcpy(buf, filename);
693 ip = IPInfo.serverip; /* Default server */
694 break;
696 case PXE_HOMESERVER:
697 buf = stpcpy(buf, filename+2);
698 ip = IPInfo.serverip;
699 break;
701 case PXE_TFTP:
702 np = strchr(filename, ':');
703 buf = stpcpy(buf, np+2);
704 if (parse_dotquad(filename, &ip) != np)
705 ip = dns_resolv(filename);
706 break;
708 case PXE_URL_TFTP:
709 np = filename + 7;
710 while (*np && *np != '/' && *np != ':')
711 np++;
712 if (np > filename + 7) {
713 if (parse_dotquad(filename + 7, &ip) != np)
714 ip = dns_resolv(filename + 7);
716 if (*np == ':') {
717 np++;
718 server_port = 0;
719 while (*np >= '0' && *np <= '9')
720 server_port = server_port * 10 + *np++ - '0';
721 server_port = server_port ? htons(server_port) : TFTP_PORT;
723 if (*np == '/')
724 np++; /* Do *NOT* eat more than one slash here... */
726 * The ; is because of a quirk in the TFTP URI spec (RFC
727 * 3617); it is to be followed by TFTP modes, which we just ignore.
729 while (*np && *np != ';') {
730 int v;
731 if (*np == '%' && (v = hexbyte(np+1)) > 0) {
732 *buf++ = v;
733 np += 3;
734 } else {
735 *buf++ = *np++;
738 *buf = '\0';
739 break;
742 if (!ip)
743 return; /* No server */
745 buf++; /* Point *past* the final NULL */
746 memcpy(buf, rrq_tail, sizeof rrq_tail);
747 buf += sizeof rrq_tail;
749 rrq_len = buf - rrq_packet_buf;
751 inode = allocate_socket(fs);
752 if (!inode)
753 return; /* Allocation failure */
754 socket = PVT(inode);
756 #if GPXE
757 if (path_type == PXE_URL) {
758 if (has_gpxe) {
759 file_open.Status = PXENV_STATUS_BAD_FUNC;
760 file_open.FileName = FAR_PTR(rrq_packet_buf + 2);
761 err = pxe_call(PXENV_FILE_OPEN, &file_open);
762 if (err)
763 goto done;
765 socket->tftp_localport = -1;
766 socket->tftp_remoteport = file_open.FileHandle;
767 inode->size = -1;
768 goto done;
769 } else {
770 static bool already = false;
771 if (!already) {
772 printf("URL syntax, but gPXE extensions not detected, "
773 "trying plain TFTP...\n");
774 already = true;
778 #endif /* GPXE */
780 timeout_ptr = TimeoutTable; /* Reset timeout */
782 sendreq:
783 timeout = *timeout_ptr++;
784 if (!timeout)
785 return; /* No file available... */
786 oldtime = jiffies();
788 socket->tftp_remoteip = ip;
789 tid = socket->tftp_localport; /* TID(local port No) */
790 udp_write.buffer = FAR_PTR(rrq_packet_buf);
791 udp_write.ip = ip;
792 udp_write.gw = gateway(udp_write.ip);
793 udp_write.src_port = tid;
794 udp_write.dst_port = server_port;
795 udp_write.buffer_size = rrq_len;
796 pxe_call(PXENV_UDP_WRITE, &udp_write);
798 /* If the WRITE call fails, we let the timeout take care of it... */
800 wait_pkt:
801 for (;;) {
802 buf = packet_buf;
803 udp_read.status = 0;
804 udp_read.buffer = FAR_PTR(buf);
805 udp_read.buffer_size = PKTBUF_SIZE;
806 udp_read.dest_ip = IPInfo.myip;
807 udp_read.d_port = tid;
808 err = pxe_call(PXENV_UDP_READ, &udp_read);
809 if (err || udp_read.status) {
810 uint32_t now = jiffies();
811 if (now - oldtime >= timeout)
812 goto sendreq;
813 } else {
814 /* Make sure the packet actually came from the server */
815 if (udp_read.src_ip == socket->tftp_remoteip)
816 break;
820 socket->tftp_remoteport = udp_read.s_port;
822 /* filesize <- -1 == unknown */
823 inode->size = -1;
824 /* Default blksize unless blksize option negotiated */
825 socket->tftp_blksize = TFTP_BLOCKSIZE;
826 buffersize = udp_read.buffer_size - 2; /* bytes after opcode */
827 if (buffersize < 0)
828 goto wait_pkt; /* Garbled reply */
831 * Get the opcode type, and parse it
833 opcode = *(uint16_t *)packet_buf;
834 switch (opcode) {
835 case TFTP_ERROR:
836 inode->size = 0;
837 break; /* ERROR reply; don't try again */
839 case TFTP_DATA:
841 * If the server doesn't support any options, we'll get a
842 * DATA reply instead of OACK. Stash the data in the file
843 * buffer and go with the default value for all options...
845 * We got a DATA packet, meaning no options are
846 * suported. Save the data away and consider the
847 * length undefined, *unless* this is the only
848 * data packet...
850 buffersize -= 2;
851 if (buffersize < 0)
852 goto wait_pkt;
853 data = packet_buf + 2;
854 blk_num = *(uint16_t *)data;
855 data += 2;
856 if (blk_num != htons(1))
857 goto wait_pkt;
858 socket->tftp_lastpkt = blk_num;
859 if (buffersize > TFTP_BLOCKSIZE)
860 goto err_reply; /* Corrupt */
861 else if (buffersize < TFTP_BLOCKSIZE) {
863 * This is the final EOF packet, already...
864 * We know the filesize, but we also want to
865 * ack the packet and set the EOF flag.
867 inode->size = buffersize;
868 socket->tftp_goteof = 1;
869 ack_packet(inode, blk_num);
872 socket->tftp_bytesleft = buffersize;
873 socket->tftp_dataptr = socket->tftp_pktbuf;
874 memcpy(socket->tftp_pktbuf, data, buffersize);
875 break;
877 case TFTP_OACK:
879 * Now we need to parse the OACK packet to get the transfer
880 * and packet sizes.
883 options = packet_buf + 2;
884 p = options;
886 while (buffersize) {
887 const char *opt = p;
890 * If we find an option which starts with a NUL byte,
891 * (a null option), we're either seeing garbage that some
892 * TFTP servers add to the end of the packet, or we have
893 * no clue how to parse the rest of the packet (what is
894 * an option name and what is a value?) In either case,
895 * discard the rest.
897 if (!*opt)
898 goto done;
900 while (buffersize) {
901 if (!*p)
902 break; /* Found a final null */
903 *p++ |= 0x20;
904 buffersize--;
906 if (!buffersize)
907 break; /* Unterminated option */
909 /* Consume the terminal null */
910 p++;
911 buffersize--;
913 if (!buffersize)
914 break; /* No option data */
917 * Parse option pointed to by options; guaranteed to be
918 * null-terminated
920 tftp_opt = tftp_options;
921 for (i = 0; i < tftp_nopts; i++) {
922 if (!strcmp(opt, tftp_opt->str_ptr))
923 break;
924 tftp_opt++;
926 if (i == tftp_nopts)
927 goto err_reply; /* Non-negotitated option returned,
928 no idea what it means ...*/
930 /* get the address of the filed that we want to write on */
931 opdata_ptr = (uint32_t *)((char *)inode + tftp_opt->offset);
932 opdata = 0;
934 /* do convert a number-string to decimal number, just like atoi */
935 while (buffersize--) {
936 uint8_t d = *p++;
937 if (d == '\0')
938 break; /* found a final null */
939 d -= '0';
940 if (d > 9)
941 goto err_reply; /* Not a decimal digit */
942 opdata = opdata*10 + d;
944 *opdata_ptr = opdata;
946 break;
948 default:
949 printf("TFTP unknown opcode %d\n", ntohs(opcode));
950 goto err_reply;
953 done:
954 if (!inode->size) {
955 free_socket(inode);
956 return;
958 file->inode = inode;
959 return;
961 err_reply:
962 /* Build the TFTP error packet */
963 tftp_error(inode, TFTP_EOPTNEG, "TFTP protocol error");
964 printf("TFTP server sent an incomprehesible reply\n");
965 kaboom();
970 * Store standard filename prefix
972 static void get_prefix(void)
974 int len;
975 char *p;
976 char c;
978 if (!(DHCPMagic & 0x04)) {
979 /* No path prefix option, derive from boot file */
981 strlcpy(path_prefix, boot_file, sizeof path_prefix);
982 len = strlen(path_prefix);
983 p = &path_prefix[len - 1];
985 while (len--) {
986 c = *p--;
987 c |= 0x20;
989 c = (c >= '0' && c <= '9') ||
990 (c >= 'a' && c <= 'z') ||
991 (c == '.' || c == '-');
992 if (!c)
993 break;
996 if (len < 0)
997 p --;
999 *(p + 2) = 0; /* Zero-terminate after delimiter */
1002 printf("TFTP prefix: %s\n", path_prefix);
1003 chdir(path_prefix);
1007 * realpath for PXE
1009 static size_t pxe_realpath(struct fs_info *fs, char *dst, const char *src,
1010 size_t bufsize)
1012 enum pxe_path_type path_type = pxe_path_type(src);
1014 return snprintf(dst, bufsize, "%s%s",
1015 path_type == PXE_RELATIVE ? fs->cwd_name : "", src);
1019 * chdir for PXE
1021 static int pxe_chdir(struct fs_info *fs, const char *src)
1023 /* The cwd for PXE is just a text prefix */
1024 enum pxe_path_type path_type = pxe_path_type(src);
1026 if (path_type == PXE_RELATIVE)
1027 strlcat(fs->cwd_name, src, sizeof fs->cwd_name);
1028 else
1029 strlcpy(fs->cwd_name, src, sizeof fs->cwd_name);
1031 dprintf("cwd = \"%s\"\n", fs->cwd_name);
1032 return 0;
1036 * try to load a config file, if found, return 1, or return 0
1039 static int try_load(char *config_name)
1041 com32sys_t regs;
1043 printf("Trying to load: %-50s ", config_name);
1044 pxe_mangle_name(KernelName, config_name);
1046 memset(&regs, 0, sizeof regs);
1047 regs.edi.w[0] = OFFS_WRT(KernelName, 0);
1048 call16(core_open, &regs, &regs);
1049 if (regs.eflags.l & EFLAGS_ZF) {
1050 strcpy(ConfigName, KernelName);
1051 printf("\r");
1052 return 0;
1053 } else {
1054 printf("ok\n");
1055 return 1;
1060 /* Load the config file, return 1 if failed, or 0 */
1061 static int pxe_load_config(void)
1063 const char *cfgprefix = "pxelinux.cfg/";
1064 const char *default_str = "default";
1065 char *config_file;
1066 char *last;
1067 int tries = 8;
1069 get_prefix();
1070 if (DHCPMagic & 0x02) {
1071 /* We got a DHCP option, try it first */
1072 if (try_load(ConfigName))
1073 return 0;
1077 * Have to guess config file name ...
1079 config_file = stpcpy(ConfigName, cfgprefix);
1081 /* Try loading by UUID */
1082 if (have_uuid) {
1083 strcpy(config_file, UUID_str);
1084 if (try_load(ConfigName))
1085 return 0;
1088 /* Try loading by MAC address */
1089 strcpy(config_file, MAC_str);
1090 if (try_load(ConfigName))
1091 return 0;
1093 /* Nope, try hexadecimal IP prefixes... */
1094 uchexbytes(config_file, (uint8_t *)&IPInfo.myip, 4);
1095 last = &config_file[8];
1096 while (tries) {
1097 *last = '\0'; /* Zero-terminate string */
1098 if (try_load(ConfigName))
1099 return 0;
1100 last--; /* Drop one character */
1101 tries--;
1104 /* Final attempt: "default" string */
1105 strcpy(config_file, default_str);
1106 if (try_load(ConfigName))
1107 return 0;
1109 printf("%-68s\n", "Unable to locate configuration file");
1110 kaboom();
1114 * Generate the bootif string.
1116 static void make_bootif_string(void)
1118 const uint8_t *src;
1119 char *dst = BOOTIFStr;
1120 int i;
1122 dst += sprintf(dst, "BOOTIF=%02x", MAC_type);
1123 src = MAC;
1124 for (i = MAC_len; i; i--)
1125 dst += sprintf(dst, "-%02x", *src++);
1128 * Generate the SYSUUID string, if we have one...
1130 static void make_sysuuid_string(void)
1132 static const uint8_t uuid_dashes[] = {4, 2, 2, 2, 6, 0};
1133 const uint8_t *src = uuid;
1134 const uint8_t *uuid_ptr = uuid_dashes;
1135 char *dst;
1137 SYSUUIDStr[0] = '\0'; /* If nothing there... */
1139 /* Try loading by UUID */
1140 if (have_uuid) {
1141 dst = stpcpy(SYSUUIDStr, "SYSUUID=");
1143 while (*uuid_ptr) {
1144 int len = *uuid_ptr;
1146 lchexbytes(dst, src, len);
1147 dst += len * 2;
1148 src += len;
1149 uuid_ptr++;
1150 *dst++ = '-';
1152 /* Remove last dash and zero-terminate */
1153 *--dst = '\0';
1158 * Generate an ip=<client-ip>:<boot-server-ip>:<gw-ip>:<netmask>
1159 * option into IPOption based on a DHCP packet in trackbuf.
1162 char __bss16 IPOption[3+4*16];
1164 static void genipopt(void)
1166 char *p = IPOption;
1167 const uint32_t *v = &IPInfo.myip;
1168 int i;
1170 p = stpcpy(p, "ip=");
1172 for (i = 0; i < 4; i++) {
1173 p += gendotquad(p, *v++);
1174 *p++ = ':';
1176 *--p = '\0';
1180 /* Generate ip= option and print the ip adress */
1181 static void ip_init(void)
1183 uint32_t ip = IPInfo.myip;
1185 genipopt();
1186 gendotquad(dot_quad_buf, ip);
1188 ip = ntohl(ip);
1189 printf("My IP address seems to be %08X %s\n", ip, dot_quad_buf);
1193 * Print the IPAPPEND strings, in order
1195 extern const uint16_t IPAppends[];
1196 extern const char numIPAppends[];
1198 static void print_ipappend(void)
1200 size_t i;
1202 for (i = 0; i < (size_t)numIPAppends; i++) {
1203 const char *p = (const char *)(size_t)IPAppends[i];
1204 if (*p)
1205 printf("%s\n", p);
1210 * Validity check on possible !PXE structure in buf
1211 * return 1 for success, 0 for failure.
1214 static int is_pxe(const void *buf)
1216 const struct pxe_t *pxe = buf;
1217 const uint8_t *p = buf;
1218 int i = pxe->structlength;
1219 uint8_t sum = 0;
1221 if (i < sizeof(struct pxe_t) ||
1222 memcmp(pxe->signature, "!PXE", 4))
1223 return 0;
1225 while (i--)
1226 sum += *p++;
1228 return sum == 0;
1232 * Just like is_pxe, it checks PXENV+ structure
1235 static int is_pxenv(const void *buf)
1237 const struct pxenv_t *pxenv = buf;
1238 const uint8_t *p = buf;
1239 int i = pxenv->length;
1240 uint8_t sum = 0;
1242 /* The pxeptr field isn't present in old versions */
1243 if (i < offsetof(struct pxenv_t, pxeptr) ||
1244 memcmp(pxenv->signature, "PXENV+", 6))
1245 return 0;
1247 while (i--)
1248 sum += *p++;
1250 return sum == 0;
1256 * memory_scan_for_pxe_struct:
1257 * memory_scan_for_pxenv_struct:
1259 * If none of the standard methods find the !PXE/PXENV+ structure,
1260 * look for it by scanning memory.
1262 * return the corresponding pxe structure if found, or NULL;
1264 static const void *memory_scan(uintptr_t start, int (*func)(const void *))
1266 const char *ptr;
1268 /* Scan each 16 bytes of conventional memory before the VGA region */
1269 for (ptr = (const char *)start; ptr < (const char *)0xA0000; ptr += 16) {
1270 if (func(ptr))
1271 return ptr; /* found it! */
1272 ptr += 16;
1274 return NULL;
1277 static const struct pxe_t *memory_scan_for_pxe_struct(void)
1279 extern uint16_t BIOS_fbm; /* Starting segment */
1281 return memory_scan(BIOS_fbm << 10, is_pxe);
1284 static const struct pxenv_t *memory_scan_for_pxenv_struct(void)
1286 return memory_scan(0x10000, is_pxenv);
1290 * Find the !PXE structure; we search for the following, in order:
1292 * a. !PXE structure as SS:[SP + 4]
1293 * b. PXENV+ structure at [ES:BX]
1294 * c. INT 1Ah AX=0x5650 -> PXENV+
1295 * d. Search memory for !PXE
1296 * e. Search memory for PXENV+
1298 * If we find a PXENV+ structure, we try to find a !PXE structure from
1299 * if if the API version is 2.1 or later
1302 static int pxe_init(bool quiet)
1304 extern void pxe_int1a(void);
1305 char plan = 'A';
1306 uint16_t seg, off;
1307 uint16_t code_seg, code_len;
1308 uint16_t data_seg, data_len;
1309 const char *base = GET_PTR(InitStack);
1310 com32sys_t regs;
1311 const char *type;
1312 const struct pxenv_t *pxenv;
1313 const struct pxe_t *pxe;
1315 /* Assume API version 2.1 */
1316 APIVer = 0x201;
1318 /* Plan A: !PXE structure as SS:[SP + 4] */
1319 off = *(const uint16_t *)(base + 48);
1320 seg = *(const uint16_t *)(base + 50);
1321 pxe = MK_PTR(seg, off);
1322 if (is_pxe(pxe))
1323 goto have_pxe;
1325 /* Plan B: PXENV+ structure at [ES:BX] */
1326 plan++;
1327 off = *(const uint16_t *)(base + 24); /* Original BX */
1328 seg = *(const uint16_t *)(base + 4); /* Original ES */
1329 pxenv = MK_PTR(seg, off);
1330 if (is_pxenv(pxenv))
1331 goto have_pxenv;
1333 /* Plan C: PXENV+ structure via INT 1Ah AX=5650h */
1334 plan++;
1335 memset(&regs, 0, sizeof regs);
1336 regs.eax.w[0] = 0x5650;
1337 call16(pxe_int1a, &regs, &regs);
1338 if (!(regs.eflags.l & EFLAGS_CF) && (regs.eax.w[0] == 0x564e)) {
1339 pxenv = MK_PTR(regs.es, regs.ebx.w[0]);
1340 if (is_pxenv(pxenv))
1341 goto have_pxenv;
1344 /* Plan D: !PXE memory scan */
1345 plan++;
1346 if ((pxe = memory_scan_for_pxe_struct()))
1347 goto have_pxe;
1349 /* Plan E: PXENV+ memory scan */
1350 plan++;
1351 if ((pxenv = memory_scan_for_pxenv_struct()))
1352 goto have_pxenv;
1354 /* Found nothing at all !! */
1355 if (!quiet)
1356 printf("No !PXE or PXENV+ API found; we're dead...\n");
1357 return -1;
1359 have_pxenv:
1360 APIVer = pxenv->version;
1361 if (!quiet)
1362 printf("Found PXENV+ structure\nPXE API version is %04x\n", APIVer);
1364 /* if the API version number is 0x0201 or higher, use the !PXE structure */
1365 if (APIVer >= 0x201) {
1366 if (pxenv->length >= sizeof(struct pxenv_t)) {
1367 pxe = GET_PTR(pxenv->pxeptr);
1368 if (is_pxe(pxe))
1369 goto have_pxe;
1371 * Nope, !PXE structure missing despite API 2.1+, or at least
1372 * the pointer is missing. Do a last-ditch attempt to find it
1374 if ((pxe = memory_scan_for_pxe_struct()))
1375 goto have_pxe;
1377 APIVer = 0x200; /* PXENV+ only, assume version 2.00 */
1380 /* Otherwise, no dice, use PXENV+ structure */
1381 data_len = pxenv->undidatasize;
1382 data_seg = pxenv->undidataseg;
1383 code_len = pxenv->undicodesize;
1384 code_seg = pxenv->undicodeseg;
1385 PXEEntry = pxenv->rmentry;
1386 type = "PXENV+";
1387 goto have_entrypoint;
1389 have_pxe:
1390 data_len = pxe->seg[PXE_Seg_UNDIData].size;
1391 data_seg = pxe->seg[PXE_Seg_UNDIData].sel;
1392 code_len = pxe->seg[PXE_Seg_UNDICode].size;
1393 code_seg = pxe->seg[PXE_Seg_UNDICode].sel;
1394 PXEEntry = pxe->entrypointsp;
1395 type = "!PXE";
1397 have_entrypoint:
1398 if (!quiet) {
1399 printf("%s entry point found (we hope) at %04X:%04X via plan %c\n",
1400 type, PXEEntry.seg, PXEEntry.offs, plan);
1401 printf("UNDI code segment at %04X len %04X\n", code_seg, code_len);
1402 printf("UNDI data segment at %04X len %04X\n", data_seg, data_len);
1405 code_seg = code_seg + ((code_len + 15) >> 4);
1406 data_seg = data_seg + ((data_len + 15) >> 4);
1408 real_base_mem = max(code_seg, data_seg) >> 6; /* Convert to kilobytes */
1410 return 0;
1414 * See if we have gPXE
1416 static void gpxe_init(void)
1418 int err;
1419 static __lowmem struct s_PXENV_FILE_API_CHECK api_check;
1421 if (APIVer >= 0x201) {
1422 api_check.Size = sizeof api_check;
1423 api_check.Magic = 0x91d447b2;
1424 err = pxe_call(PXENV_FILE_API_CHECK, &api_check);
1425 if (!err && api_check.Magic == 0xe9c17b20)
1426 gpxe_funcs = api_check.APIMask;
1429 /* Necessary functions for us to use the gPXE file API */
1430 has_gpxe = (~gpxe_funcs & 0x4b) == 0;
1434 * Initialize UDP stack
1437 static void udp_init(void)
1439 int err;
1440 static __lowmem struct s_PXENV_UDP_OPEN udp_open;
1441 udp_open.src_ip = IPInfo.myip;
1442 err = pxe_call(PXENV_UDP_OPEN, &udp_open);
1443 if (err || udp_open.status) {
1444 printf("Failed to initialize UDP stack ");
1445 printf("%d\n", udp_open.status);
1446 kaboom();
1452 * Network-specific initialization
1454 static void network_init(void)
1456 struct bootp_t *bp = (struct bootp_t *)trackbuf;
1457 int pkt_len;
1459 *LocalDomain = 0; /* No LocalDomain received */
1462 * Get the DHCP client identifiers (query info 1)
1464 printf("Getting cached packet ");
1465 pkt_len = pxe_get_cached_info(1);
1466 parse_dhcp(pkt_len);
1468 * We don't use flags from the request packet, so
1469 * this is a good time to initialize DHCPMagic...
1470 * Initialize it to 1 meaning we will accept options found;
1471 * in earlier versions of PXELINUX bit 0 was used to indicate
1472 * we have found option 208 with the appropriate magic number;
1473 * we no longer require that, but MAY want to re-introduce
1474 * it in the future for vendor encapsulated options.
1476 *(char *)&DHCPMagic = 1;
1479 * Get the BOOTP/DHCP packet that brought us file (and an IP
1480 * address). This lives in the DHCPACK packet (query info 2)
1482 pkt_len = pxe_get_cached_info(2);
1483 parse_dhcp(pkt_len);
1485 * Save away MAC address (assume this is in query info 2. If this
1486 * turns out to be problematic it might be better getting it from
1487 * the query info 1 packet
1489 MAC_len = bp->hardlen > 16 ? 0 : bp->hardlen;
1490 MAC_type = bp->hardware;
1491 memcpy(MAC, bp->macaddr, MAC_len);
1494 * Get the boot file and other info. This lives in the CACHED_REPLY
1495 * packet (query info 3)
1497 pkt_len = pxe_get_cached_info(3);
1498 parse_dhcp(pkt_len);
1499 printf("\n");
1501 make_bootif_string();
1502 make_sysuuid_string();
1503 ip_init();
1504 print_ipappend();
1507 * Check to see if we got any PXELINUX-specific DHCP options; in particular,
1508 * if we didn't get the magic enable, do not recognize any other options.
1510 if ((DHCPMagic & 1) == 0)
1511 DHCPMagic = 0;
1513 udp_init();
1517 * Initialize pxe fs
1520 static int pxe_fs_init(struct fs_info *fs)
1522 (void)fs; /* drop the compile warning message */
1524 /* This block size is actually arbitrary... */
1525 fs->sector_shift = fs->block_shift = TFTP_BLOCKSIZE_LG2;
1526 fs->sector_size = fs->block_size = 1 << TFTP_BLOCKSIZE_LG2;
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 /* Find the PXE stack */
1533 if (pxe_init(false))
1534 kaboom();
1536 /* See if we also have a gPXE stack */
1537 gpxe_init();
1539 /* Network-specific initialization */
1540 network_init();
1542 /* Initialize network-card-specific idle handling */
1543 pxe_idle_init();
1545 /* Our name for the root */
1546 strcpy(fs->cwd_name, "::");
1548 return 0;
1552 * Look to see if we are on an EFI CSM system. Some EFI
1553 * CSM systems put the BEV stack in low memory, which means
1554 * a return to the PXE stack will crash the system. However,
1555 * INT 18h works reliably, so in that case hack the stack and
1556 * point the "return address" to an INT 18h instruction.
1558 * Hack the stack instead of the much simpler "just invoke INT 18h
1559 * if we want to reset", so that chainloading other NBPs will work.
1561 * This manipulates the real-mode InitStack directly. It relies on this
1562 * *not* being a currently active stack, i.e. the former
1563 * USE_PXE_PROVIDED_STACK no longer works.
1565 extern far_ptr_t InitStack;
1567 struct efi_struct {
1568 uint32_t magic;
1569 uint8_t csum;
1570 uint8_t len;
1571 } __attribute__((packed));
1572 #define EFI_MAGIC (('$' << 24)+('E' << 16)+('F' << 8)+'I')
1574 static inline bool is_efi(const struct efi_struct *efi)
1577 * We don't verify the checksum, because it seems some CSMs leave
1578 * it at zero, sigh...
1580 return (efi->magic == EFI_MAGIC) && (efi->len >= 83);
1583 static void install_efi_csm_hack(void)
1585 static const uint8_t efi_csm_hack[] =
1587 0xcd, 0x18, /* int $0x18 */
1588 0xea, 0xf0, 0xff, 0x00, 0xf0, /* ljmpw $0xf000,$0xfff0 */
1589 0xf4 /* hlt */
1591 uint16_t *retcode;
1593 retcode = GET_PTR(*(far_ptr_t *)((char *)GET_PTR(InitStack) + 44));
1595 /* Don't do this if the return already points to int $0x18 */
1596 if (*retcode != 0x18cd) {
1597 uint32_t efi_ptr;
1598 bool efi = false;
1600 for (efi_ptr = 0xe0000 ; efi_ptr < 0x100000 ; efi_ptr += 16) {
1601 if (is_efi((const struct efi_struct *)efi_ptr)) {
1602 efi = true;
1603 break;
1607 if (efi) {
1608 uint8_t *src = GET_PTR(InitStack);
1609 uint8_t *dst = src - sizeof efi_csm_hack;
1611 memmove(dst, src, 52);
1612 memcpy(dst+52, efi_csm_hack, sizeof efi_csm_hack);
1613 InitStack.offs -= sizeof efi_csm_hack;
1615 /* Clobber the return address */
1616 *(uint16_t *)(dst+44) = OFFS_WRT(dst+52, InitStack.seg);
1617 *(uint16_t *)(dst+46) = InitStack.seg;
1622 int reset_pxe(void)
1624 static __lowmem struct s_PXENV_UDP_CLOSE udp_close;
1625 extern void gpxe_unload(void);
1626 int err = 0;
1628 pxe_idle_cleanup();
1630 pxe_call(PXENV_UDP_CLOSE, &udp_close);
1632 if (gpxe_funcs & 0x80) {
1633 /* gPXE special unload implemented */
1634 call16(gpxe_unload, &zero_regs, NULL);
1636 /* Locate the actual vendor stack... */
1637 err = pxe_init(true);
1640 install_efi_csm_hack();
1641 return err;
1645 * This function unloads the PXE and UNDI stacks and
1646 * unclaims the memory.
1648 void unload_pxe(void)
1650 /* PXE unload sequences */
1651 static const uint8_t new_api_unload[] = {
1652 PXENV_UNDI_SHUTDOWN, PXENV_UNLOAD_STACK, PXENV_STOP_UNDI, 0
1654 static const uint8_t old_api_unload[] = {
1655 PXENV_UNDI_SHUTDOWN, PXENV_UNLOAD_STACK, PXENV_UNDI_CLEANUP, 0
1658 unsigned int api;
1659 const uint8_t *api_ptr;
1660 int err;
1661 size_t int_addr;
1662 static __lowmem union {
1663 struct s_PXENV_UNDI_SHUTDOWN undi_shutdown;
1664 struct s_PXENV_UNLOAD_STACK unload_stack;
1665 struct s_PXENV_STOP_UNDI stop_undi;
1666 struct s_PXENV_UNDI_CLEANUP undi_cleanup;
1667 uint16_t Status; /* All calls have this as the first member */
1668 } unload_call;
1670 dprintf("FBM before unload = %d\n", BIOS_fbm);
1672 err = reset_pxe();
1674 dprintf("FBM after reset_pxe = %d, err = %d\n", BIOS_fbm, err);
1676 /* If we want to keep PXE around, we still need to reset it */
1677 if (KeepPXE || err)
1678 return;
1680 dprintf("APIVer = %04x\n", APIVer);
1682 api_ptr = APIVer >= 0x0200 ? new_api_unload : old_api_unload;
1683 while((api = *api_ptr++)) {
1684 dprintf("PXE call %04x\n", api);
1685 memset(&unload_call, 0, sizeof unload_call);
1686 err = pxe_call(api, &unload_call);
1687 if (err || unload_call.Status != PXENV_STATUS_SUCCESS) {
1688 dprintf("PXE unload API call %04x failed\n", api);
1689 goto cant_free;
1693 api = 0xff00;
1694 if (real_base_mem <= BIOS_fbm) { /* Sanity check */
1695 dprintf("FBM %d < real_base_mem %d\n", BIOS_fbm, real_base_mem);
1696 goto cant_free;
1698 api++;
1700 /* Check that PXE actually unhooked the INT 0x1A chain */
1701 int_addr = (size_t)GET_PTR(*(far_ptr_t *)(4 * 0x1a));
1702 int_addr >>= 10;
1703 if (int_addr >= real_base_mem || int_addr < BIOS_fbm) {
1704 BIOS_fbm = real_base_mem;
1705 dprintf("FBM after unload_pxe = %d\n", BIOS_fbm);
1706 return;
1709 dprintf("Can't free FBM, real_base_mem = %d, "
1710 "FBM = %d, INT 1A = %08x (%d)\n",
1711 real_base_mem, BIOS_fbm,
1712 *(uint32_t *)(4 * 0x1a), int_addr);
1714 cant_free:
1715 printf("Failed to free base memory error %04x-%08x (%d/%dK)\n",
1716 api, *(uint32_t *)(4 * 0x1a), BIOS_fbm, real_base_mem);
1717 return;
1720 const struct fs_ops pxe_fs_ops = {
1721 .fs_name = "pxe",
1722 .fs_flags = FS_NODEV,
1723 .fs_init = pxe_fs_init,
1724 .searchdir = pxe_searchdir,
1725 .chdir = pxe_chdir,
1726 .realpath = pxe_realpath,
1727 .getfssec = pxe_getfssec,
1728 .close_file = pxe_close_file,
1729 .mangle_name = pxe_mangle_name,
1730 .load_config = pxe_load_config,