pxe: Remove stray debugging message
[syslinux.git] / core / fs / pxe / pxe.c
blob89a7b58481d9902e81e5e2a02b19ed14609f241f
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 <fcntl.h>
8 #include <sys/cpu.h>
9 #include <lwip/api.h>
10 #include <lwip/dns.h>
11 #include <lwip/tcpip.h>
12 #include <lwip/opt.h>
13 #include "pxe.h"
14 #include "thread.h"
15 #include "url.h"
16 #include "tftp.h"
18 __lowmem t_PXENV_UNDI_GET_INFORMATION pxe_undi_info;
19 __lowmem t_PXENV_UNDI_GET_IFACE_INFO pxe_undi_iface;
21 static uint16_t real_base_mem; /* Amount of DOS memory after freeing */
23 uint8_t MAC[MAC_MAX]; /* Actual MAC address */
24 uint8_t MAC_len; /* MAC address len */
25 uint8_t MAC_type; /* MAC address type */
27 char boot_file[256]; /* From DHCP */
28 char path_prefix[256]; /* From DHCP */
30 static bool has_gpxe;
31 static uint32_t gpxe_funcs;
32 bool have_uuid = false;
34 static struct url_scheme {
35 const char *name;
36 void (*open)(struct url_info *, int, struct inode *, const char **);
37 int ok_flags;
38 } url_schemes[] = {
39 { "tftp", tftp_open, 0 },
40 { "http", http_open, O_DIRECTORY },
41 { "ftp", ftp_open, O_DIRECTORY },
42 { NULL, NULL, 0 },
44 #define OK_FLAGS_MASK (O_DIRECTORY|O_WRONLY)
47 * Allocate a local UDP port structure and assign it a local port number.
48 * Return the inode pointer if success, or null if failure
50 static struct inode *allocate_socket(struct fs_info *fs)
52 struct inode *inode = alloc_inode(fs, 0, sizeof(struct pxe_pvt_inode));
54 if (!inode) {
55 malloc_error("socket structure");
56 } else {
57 inode->mode = DT_REG; /* No other types relevant for PXE */
60 return inode;
63 void free_socket(struct inode *inode)
65 struct pxe_pvt_inode *socket = PVT(inode);
67 free(socket->tftp_pktbuf); /* If we allocated a buffer, free it now */
68 free_inode(inode);
71 static void pxe_close_file(struct file *file)
73 struct inode *inode = file->inode;
74 struct pxe_pvt_inode *socket = PVT(inode);
76 if (!socket->tftp_goteof) {
77 socket->ops->close(inode);
80 free_socket(inode);
84 * Tests an IP address in _ip_ for validity; return with 0 for bad, 1 for good.
85 * We used to refuse class E, but class E addresses are likely to become
86 * assignable unicast addresses in the near future.
89 bool ip_ok(uint32_t ip)
91 uint8_t ip_hi = (uint8_t)ip; /* First octet of the ip address */
93 if (ip == 0xffffffff || /* Refuse the all-ones address */
94 ip_hi == 0 || /* Refuse network zero */
95 ip_hi == 127 || /* Refuse the loopback network */
96 (ip_hi & 240) == 224) /* Refuse class D */
97 return false;
99 return true;
104 * Take an IP address (in network byte order) in _ip_ and
105 * output a dotted quad string to _dst_, returns the length
106 * of the dotted quad ip string.
109 static int gendotquad(char *dst, uint32_t ip)
111 return sprintf(dst, "%u.%u.%u.%u",
112 ((const uint8_t *)&ip)[0],
113 ((const uint8_t *)&ip)[1],
114 ((const uint8_t *)&ip)[2],
115 ((const uint8_t *)&ip)[3]);
119 * the ASM pxenv function wrapper, return 1 if error, or 0
122 int pxe_call(int opcode, void *data)
124 static DECLARE_INIT_SEMAPHORE(pxe_sem, 1);
125 extern void pxenv(void);
126 com32sys_t regs;
128 sem_down(&pxe_sem, 0);
130 #if 0
131 dprintf("pxe_call op %04x data %p\n", opcode, data);
132 #endif
134 memset(&regs, 0, sizeof regs);
135 regs.ebx.w[0] = opcode;
136 regs.es = SEG(data);
137 regs.edi.w[0] = OFFS(data);
138 call16(pxenv, &regs, &regs);
140 sem_up(&pxe_sem);
142 return regs.eflags.l & EFLAGS_CF; /* CF SET if fail */
146 * Get a DHCP packet from the PXE stack into the trackbuf
148 * @param: type, packet type
149 * @return: buffer size
152 static int pxe_get_cached_info(int type)
154 int err;
155 static __lowmem struct s_PXENV_GET_CACHED_INFO get_cached_info;
156 printf(" %02x", type);
158 memset(&get_cached_info, 0, sizeof get_cached_info);
159 get_cached_info.PacketType = type;
160 get_cached_info.BufferSize = 8192;
161 get_cached_info.Buffer = FAR_PTR(trackbuf);
162 err = pxe_call(PXENV_GET_CACHED_INFO, &get_cached_info);
163 if (err) {
164 printf("PXE API call failed, error %04x\n", err);
165 kaboom();
168 return get_cached_info.BufferSize;
172 * mangle a filename pointed to by _src_ into a buffer pointed
173 * to by _dst_; ends on encountering any whitespace.
175 * This deliberately does not attempt to do any conversion of
176 * pathname separators.
179 static void pxe_mangle_name(char *dst, const char *src)
181 size_t len = FILENAME_MAX-1;
183 while (len-- && not_whitespace(*src))
184 *dst++ = *src++;
186 *dst = '\0';
190 * Read a single character from the specified pxe inode.
191 * Very useful for stepping through http streams and
192 * parsing their headers.
194 int pxe_getc(struct inode *inode)
196 struct pxe_pvt_inode *socket = PVT(inode);
197 unsigned char byte;
199 while (!socket->tftp_bytesleft) {
200 if (socket->tftp_goteof)
201 return -1;
203 socket->ops->fill_buffer(inode);
206 byte = *socket->tftp_dataptr;
207 socket->tftp_bytesleft -= 1;
208 socket->tftp_dataptr += 1;
210 return byte;
214 * Get a fresh packet if the buffer is drained, and we haven't hit
215 * EOF yet. The buffer should be filled immediately after draining!
217 static void fill_buffer(struct inode *inode)
219 struct pxe_pvt_inode *socket = PVT(inode);
220 if (socket->tftp_bytesleft || socket->tftp_goteof)
221 return;
223 return socket->ops->fill_buffer(inode);
228 * getfssec: Get multiple clusters from a file, given the starting cluster.
229 * In this case, get multiple blocks from a specific TCP connection.
231 * @param: fs, the fs_info structure address, in pxe, we don't use this.
232 * @param: buf, buffer to store the read data
233 * @param: openfile, TFTP socket pointer
234 * @param: blocks, 512-byte block count; 0FFFFh = until end of file
236 * @return: the bytes read
239 static uint32_t pxe_getfssec(struct file *file, char *buf,
240 int blocks, bool *have_more)
242 struct inode *inode = file->inode;
243 struct pxe_pvt_inode *socket = PVT(inode);
244 int count = blocks;
245 int chunk;
246 int bytes_read = 0;
248 count <<= TFTP_BLOCKSIZE_LG2;
249 while (count) {
250 fill_buffer(inode); /* If we have no 'fresh' buffer, get it */
251 if (!socket->tftp_bytesleft)
252 break;
254 chunk = count;
255 if (chunk > socket->tftp_bytesleft)
256 chunk = socket->tftp_bytesleft;
257 socket->tftp_bytesleft -= chunk;
258 memcpy(buf, socket->tftp_dataptr, chunk);
259 socket->tftp_dataptr += chunk;
260 buf += chunk;
261 bytes_read += chunk;
262 count -= chunk;
266 if (socket->tftp_bytesleft || (socket->tftp_filepos < inode->size)) {
267 fill_buffer(inode);
268 *have_more = 1;
269 } else if (socket->tftp_goteof) {
271 * The socket is closed and the buffer drained; the caller will
272 * call close_file and therefore free the socket.
274 *have_more = 0;
277 return bytes_read;
281 * Assign an IP address to a URL
283 static void url_set_ip(struct url_info *url)
285 url->ip = 0;
286 if (url->host)
287 url->ip = dns_resolv(url->host);
288 if (!url->ip)
289 url->ip = IPInfo.serverip;
293 * Open the specified connection
295 * @param:filename, the file we wanna open
297 * @out: open_file_t structure, stores in file->open_file
298 * @out: the lenght of this file, stores in file->file_len
301 static void __pxe_searchdir(const char *filename, int flags, struct file *file);
302 extern uint16_t PXERetry;
304 static void pxe_searchdir(const char *filename, int flags, struct file *file)
306 int i = PXERetry;
308 do {
309 dprintf("PXE: file = %p, retries left = %d: ", file, i);
310 __pxe_searchdir(filename, flags, file);
311 dprintf("%s\n", file->inode ? "ok" : "failed");
312 } while (!file->inode && i--);
314 static void __pxe_searchdir(const char *filename, int flags, struct file *file)
316 struct fs_info *fs = file->fs;
317 struct inode *inode;
318 char fullpath[2*FILENAME_MAX];
319 #if GPXE
320 char urlsave[2*FILENAME_MAX];
321 #endif
322 struct url_info url;
323 const struct url_scheme *us = NULL;
324 int redirect_count = 0;
325 bool found_scheme = false;
327 inode = file->inode = NULL;
329 while (filename) {
330 if (redirect_count++ > 5)
331 break;
333 strlcpy(fullpath, filename, sizeof fullpath);
334 #if GPXE
335 strcpy(urlsave, fullpath);
336 #endif
337 parse_url(&url, fullpath);
338 if (url.type == URL_SUFFIX) {
339 snprintf(fullpath, sizeof fullpath, "%s%s", fs->cwd_name, filename);
340 #if GPXE
341 strcpy(urlsave, fullpath);
342 #endif
343 parse_url(&url, fullpath);
346 inode = allocate_socket(fs);
347 if (!inode)
348 return; /* Allocation failure */
350 url_set_ip(&url);
352 filename = NULL;
353 found_scheme = false;
354 for (us = url_schemes; us->name; us++) {
355 if (!strcmp(us->name, url.scheme)) {
356 if ((flags & ~us->ok_flags & OK_FLAGS_MASK) == 0)
357 us->open(&url, flags, inode, &filename);
358 found_scheme = true;
359 break;
363 /* filename here is set on a redirect */
366 if (!found_scheme) {
367 #if GPXE
368 /* No URL scheme found, hand it to GPXE */
369 gpxe_open(inode, urlsave);
370 #endif
373 if (inode->size) {
374 file->inode = inode;
375 file->inode->mode = (flags & O_DIRECTORY) ? DT_DIR : DT_REG;
376 } else {
377 free_socket(inode);
380 return;
385 * Store standard filename prefix
387 static void get_prefix(void)
389 int len;
390 char *p;
391 char c;
393 if (!(DHCPMagic & 0x04)) {
394 /* No path prefix option, derive from boot file */
396 strlcpy(path_prefix, boot_file, sizeof path_prefix);
397 len = strlen(path_prefix);
398 p = &path_prefix[len - 1];
400 while (len--) {
401 c = *p--;
402 c |= 0x20;
404 c = (c >= '0' && c <= '9') ||
405 (c >= 'a' && c <= 'z') ||
406 (c == '.' || c == '-');
407 if (!c)
408 break;
411 if (len < 0)
412 p --;
414 *(p + 2) = 0; /* Zero-terminate after delimiter */
417 printf("TFTP prefix: %s\n", path_prefix);
418 chdir(path_prefix);
422 * realpath for PXE
424 static size_t pxe_realpath(struct fs_info *fs, char *dst, const char *src,
425 size_t bufsize)
427 return snprintf(dst, bufsize, "%s%s",
428 url_type(src) == URL_SUFFIX ? fs->cwd_name : "", src);
432 * chdir for PXE
434 static int pxe_chdir(struct fs_info *fs, const char *src)
436 /* The cwd for PXE is just a text prefix */
437 if (url_type(src) == URL_SUFFIX)
438 strlcat(fs->cwd_name, src, sizeof fs->cwd_name);
439 else
440 strlcpy(fs->cwd_name, src, sizeof fs->cwd_name);
442 dprintf("cwd = \"%s\"\n", fs->cwd_name);
443 return 0;
447 * try to load a config file, if found, return 1, or return 0
450 static int try_load(char *config_name)
452 com32sys_t regs;
454 printf("Trying to load: %-50s ", config_name);
455 pxe_mangle_name(KernelName, config_name);
457 memset(&regs, 0, sizeof regs);
458 regs.edi.w[0] = OFFS_WRT(KernelName, 0);
459 call16(core_open, &regs, &regs);
460 if (regs.eflags.l & EFLAGS_ZF) {
461 strcpy(ConfigName, KernelName);
462 printf("\r");
463 return 0;
464 } else {
465 printf("ok\n");
466 return 1;
471 /* Load the config file, return 1 if failed, or 0 */
472 static int pxe_load_config(void)
474 const char *cfgprefix = "pxelinux.cfg/";
475 const char *default_str = "default";
476 char *config_file;
477 char *last;
478 int tries = 8;
480 get_prefix();
481 if (DHCPMagic & 0x02) {
482 /* We got a DHCP option, try it first */
483 if (try_load(ConfigName))
484 return 0;
488 * Have to guess config file name ...
490 config_file = stpcpy(ConfigName, cfgprefix);
492 /* Try loading by UUID */
493 if (sysappend_strings[SYSAPPEND_SYSUUID]) {
494 strcpy(config_file, sysappend_strings[SYSAPPEND_SYSUUID]+8);
495 if (try_load(ConfigName))
496 return 0;
499 /* Try loading by MAC address */
500 strcpy(config_file, sysappend_strings[SYSAPPEND_BOOTIF]+7);
501 if (try_load(ConfigName))
502 return 0;
504 /* Nope, try hexadecimal IP prefixes... */
505 sprintf(config_file, "%08X", ntohl(IPInfo.myip));
506 last = &config_file[8];
507 while (tries) {
508 *last = '\0'; /* Zero-terminate string */
509 if (try_load(ConfigName))
510 return 0;
511 last--; /* Drop one character */
512 tries--;
515 /* Final attempt: "default" string */
516 strcpy(config_file, default_str);
517 if (try_load(ConfigName))
518 return 0;
520 printf("%-68s\n", "Unable to locate configuration file");
521 kaboom();
525 * Generate the bootif string.
527 static void make_bootif_string(void)
529 static char bootif_str[7+3*(MAC_MAX+1)];
530 const uint8_t *src;
531 char *dst = bootif_str;
532 int i;
534 dst += sprintf(dst, "BOOTIF=%02x", MAC_type);
535 src = MAC;
536 for (i = MAC_len; i; i--)
537 dst += sprintf(dst, "-%02x", *src++);
539 sysappend_strings[SYSAPPEND_BOOTIF] = bootif_str;
543 * Generate an ip=<client-ip>:<boot-server-ip>:<gw-ip>:<netmask>
544 * option into IPOption based on a DHCP packet in trackbuf.
547 static void genipopt(void)
549 static char ip_option[3+4*16];
550 const uint32_t *v = &IPInfo.myip;
551 char *p;
552 int i;
554 p = stpcpy(ip_option, "ip=");
556 for (i = 0; i < 4; i++) {
557 p += gendotquad(p, *v++);
558 *p++ = ':';
560 *--p = '\0';
562 sysappend_strings[SYSAPPEND_IP] = ip_option;
566 /* Generate ip= option and print the ip adress */
567 static void ip_init(void)
569 uint32_t ip = IPInfo.myip;
570 char dot_quad_buf[16];
572 genipopt();
573 gendotquad(dot_quad_buf, ip);
575 ip = ntohl(ip);
576 printf("My IP address seems to be %08X %s\n", ip, dot_quad_buf);
580 * Validity check on possible !PXE structure in buf
581 * return 1 for success, 0 for failure.
584 static int is_pxe(const void *buf)
586 const struct pxe_t *pxe = buf;
587 const uint8_t *p = buf;
588 int i = pxe->structlength;
589 uint8_t sum = 0;
591 if (i < sizeof(struct pxe_t) ||
592 memcmp(pxe->signature, "!PXE", 4))
593 return 0;
595 while (i--)
596 sum += *p++;
598 return sum == 0;
602 * Just like is_pxe, it checks PXENV+ structure
605 static int is_pxenv(const void *buf)
607 const struct pxenv_t *pxenv = buf;
608 const uint8_t *p = buf;
609 int i = pxenv->length;
610 uint8_t sum = 0;
612 /* The pxeptr field isn't present in old versions */
613 if (i < offsetof(struct pxenv_t, pxeptr) ||
614 memcmp(pxenv->signature, "PXENV+", 6))
615 return 0;
617 while (i--)
618 sum += *p++;
620 return sum == 0;
626 * memory_scan_for_pxe_struct:
627 * memory_scan_for_pxenv_struct:
629 * If none of the standard methods find the !PXE/PXENV+ structure,
630 * look for it by scanning memory.
632 * return the corresponding pxe structure if found, or NULL;
634 static const void *memory_scan(uintptr_t start, int (*func)(const void *))
636 const char *ptr;
638 /* Scan each 16 bytes of conventional memory before the VGA region */
639 for (ptr = (const char *)start; ptr < (const char *)0xA0000; ptr += 16) {
640 if (func(ptr))
641 return ptr; /* found it! */
642 ptr += 16;
644 return NULL;
647 static const struct pxe_t *memory_scan_for_pxe_struct(void)
649 extern uint16_t BIOS_fbm; /* Starting segment */
651 return memory_scan(BIOS_fbm << 10, is_pxe);
654 static const struct pxenv_t *memory_scan_for_pxenv_struct(void)
656 return memory_scan(0x10000, is_pxenv);
660 * Find the !PXE structure; we search for the following, in order:
662 * a. !PXE structure as SS:[SP + 4]
663 * b. PXENV+ structure at [ES:BX]
664 * c. INT 1Ah AX=0x5650 -> PXENV+
665 * d. Search memory for !PXE
666 * e. Search memory for PXENV+
668 * If we find a PXENV+ structure, we try to find a !PXE structure from
669 * if if the API version is 2.1 or later
672 static int pxe_init(bool quiet)
674 extern void pxe_int1a(void);
675 char plan = 'A';
676 uint16_t seg, off;
677 uint16_t code_seg, code_len;
678 uint16_t data_seg, data_len;
679 const char *base = GET_PTR(InitStack);
680 com32sys_t regs;
681 const char *type;
682 const struct pxenv_t *pxenv;
683 const struct pxe_t *pxe;
685 /* Assume API version 2.1 */
686 APIVer = 0x201;
688 /* Plan A: !PXE structure as SS:[SP + 4] */
689 off = *(const uint16_t *)(base + 48);
690 seg = *(const uint16_t *)(base + 50);
691 pxe = MK_PTR(seg, off);
692 if (is_pxe(pxe))
693 goto have_pxe;
695 /* Plan B: PXENV+ structure at [ES:BX] */
696 plan++;
697 off = *(const uint16_t *)(base + 24); /* Original BX */
698 seg = *(const uint16_t *)(base + 4); /* Original ES */
699 pxenv = MK_PTR(seg, off);
700 if (is_pxenv(pxenv))
701 goto have_pxenv;
703 /* Plan C: PXENV+ structure via INT 1Ah AX=5650h */
704 plan++;
705 memset(&regs, 0, sizeof regs);
706 regs.eax.w[0] = 0x5650;
707 call16(pxe_int1a, &regs, &regs);
708 if (!(regs.eflags.l & EFLAGS_CF) && (regs.eax.w[0] == 0x564e)) {
709 pxenv = MK_PTR(regs.es, regs.ebx.w[0]);
710 if (is_pxenv(pxenv))
711 goto have_pxenv;
714 /* Plan D: !PXE memory scan */
715 plan++;
716 if ((pxe = memory_scan_for_pxe_struct()))
717 goto have_pxe;
719 /* Plan E: PXENV+ memory scan */
720 plan++;
721 if ((pxenv = memory_scan_for_pxenv_struct()))
722 goto have_pxenv;
724 /* Found nothing at all !! */
725 if (!quiet)
726 printf("No !PXE or PXENV+ API found; we're dead...\n");
727 return -1;
729 have_pxenv:
730 APIVer = pxenv->version;
731 if (!quiet)
732 printf("Found PXENV+ structure\nPXE API version is %04x\n", APIVer);
734 /* if the API version number is 0x0201 or higher, use the !PXE structure */
735 if (APIVer >= 0x201) {
736 if (pxenv->length >= sizeof(struct pxenv_t)) {
737 pxe = GET_PTR(pxenv->pxeptr);
738 if (is_pxe(pxe))
739 goto have_pxe;
741 * Nope, !PXE structure missing despite API 2.1+, or at least
742 * the pointer is missing. Do a last-ditch attempt to find it
744 if ((pxe = memory_scan_for_pxe_struct()))
745 goto have_pxe;
747 APIVer = 0x200; /* PXENV+ only, assume version 2.00 */
750 /* Otherwise, no dice, use PXENV+ structure */
751 data_len = pxenv->undidatasize;
752 data_seg = pxenv->undidataseg;
753 code_len = pxenv->undicodesize;
754 code_seg = pxenv->undicodeseg;
755 PXEEntry = pxenv->rmentry;
756 type = "PXENV+";
757 goto have_entrypoint;
759 have_pxe:
760 data_len = pxe->seg[PXE_Seg_UNDIData].size;
761 data_seg = pxe->seg[PXE_Seg_UNDIData].sel;
762 code_len = pxe->seg[PXE_Seg_UNDICode].size;
763 code_seg = pxe->seg[PXE_Seg_UNDICode].sel;
764 PXEEntry = pxe->entrypointsp;
765 type = "!PXE";
767 have_entrypoint:
768 if (!quiet) {
769 printf("%s entry point found (we hope) at %04X:%04X via plan %c\n",
770 type, PXEEntry.seg, PXEEntry.offs, plan);
771 printf("UNDI code segment at %04X len %04X\n", code_seg, code_len);
772 printf("UNDI data segment at %04X len %04X\n", data_seg, data_len);
775 code_seg = code_seg + ((code_len + 15) >> 4);
776 data_seg = data_seg + ((data_len + 15) >> 4);
778 real_base_mem = max(code_seg, data_seg) >> 6; /* Convert to kilobytes */
780 /* Probe UNDI information */
781 pxe_call(PXENV_UNDI_GET_INFORMATION, &pxe_undi_info);
782 pxe_call(PXENV_UNDI_GET_IFACE_INFO, &pxe_undi_iface);
784 printf("UNDI: baseio %04x int %d MTU %d type %d \"%s\" flags 0x%x\n",
785 pxe_undi_info.BaseIo, pxe_undi_info.IntNumber,
786 pxe_undi_info.MaxTranUnit, pxe_undi_info.HwType,
787 pxe_undi_iface.IfaceType, pxe_undi_iface.ServiceFlags);
789 return 0;
793 * See if we have gPXE
795 static void gpxe_init(void)
797 int err;
798 static __lowmem struct s_PXENV_FILE_API_CHECK api_check;
800 if (APIVer >= 0x201) {
801 api_check.Size = sizeof api_check;
802 api_check.Magic = 0x91d447b2;
803 err = pxe_call(PXENV_FILE_API_CHECK, &api_check);
804 if (!err && api_check.Magic == 0xe9c17b20)
805 gpxe_funcs = api_check.APIMask;
808 /* Necessary functions for us to use the gPXE file API */
809 has_gpxe = (~gpxe_funcs & 0x4b) == 0;
813 * Network-specific initialization
815 static void network_init(void)
817 struct bootp_t *bp = (struct bootp_t *)trackbuf;
818 int err;
819 int pkt_len;
820 int i;
822 *LocalDomain = 0; /* No LocalDomain received */
825 * Get the DHCP client identifiers (query info 1)
827 printf("Getting cached packet ");
828 pkt_len = pxe_get_cached_info(1);
829 parse_dhcp(pkt_len);
831 * We don't use flags from the request packet, so
832 * this is a good time to initialize DHCPMagic...
833 * Initialize it to 1 meaning we will accept options found;
834 * in earlier versions of PXELINUX bit 0 was used to indicate
835 * we have found option 208 with the appropriate magic number;
836 * we no longer require that, but MAY want to re-introduce
837 * it in the future for vendor encapsulated options.
839 *(char *)&DHCPMagic = 1;
842 * Get the BOOTP/DHCP packet that brought us file (and an IP
843 * address). This lives in the DHCPACK packet (query info 2)
845 pkt_len = pxe_get_cached_info(2);
846 parse_dhcp(pkt_len);
848 * Save away MAC address (assume this is in query info 2. If this
849 * turns out to be problematic it might be better getting it from
850 * the query info 1 packet
852 MAC_len = bp->hardlen > 16 ? 0 : bp->hardlen;
853 MAC_type = bp->hardware;
854 memcpy(MAC, bp->macaddr, MAC_len);
857 * Get the boot file and other info. This lives in the CACHED_REPLY
858 * packet (query info 3)
860 pkt_len = pxe_get_cached_info(3);
861 parse_dhcp(pkt_len);
862 printf("\n");
864 make_bootif_string();
865 /* If DMI and DHCP disagree, which one should we set? */
866 if (have_uuid)
867 sysappend_set_uuid(uuid);
868 ip_init();
869 /* print_sysappend(); */
870 http_bake_cookies();
873 * Check to see if we got any PXELINUX-specific DHCP options; in particular,
874 * if we didn't get the magic enable, do not recognize any other options.
876 if ((DHCPMagic & 1) == 0)
877 DHCPMagic = 0;
879 /* Initialize lwip */
880 tcpip_init(NULL, NULL);
882 /* Start up the undi driver for lwip */
883 err = undiif_start(IPInfo.myip, IPInfo.netmask, IPInfo.gateway);
884 if (err) {
885 printf("undiif driver failed to start: %d\n", err);
886 kaboom();
889 for (i = 0; i < DNS_MAX_SERVERS; i++) {
890 /* Transfer the DNS information to lwip */
891 dns_setserver(i, (struct ip_addr *)&dns_server[i]);
896 * Initialize pxe fs
899 static int pxe_fs_init(struct fs_info *fs)
901 (void)fs; /* drop the compile warning message */
903 /* Prepare for handling pxe interrupts */
904 pxe_init_isr();
906 /* This block size is actually arbitrary... */
907 fs->sector_shift = fs->block_shift = TFTP_BLOCKSIZE_LG2;
908 fs->sector_size = fs->block_size = 1 << TFTP_BLOCKSIZE_LG2;
910 /* Find the PXE stack */
911 if (pxe_init(false))
912 kaboom();
914 /* See if we also have a gPXE stack */
915 gpxe_init();
917 /* Network-specific initialization */
918 network_init();
920 /* Initialize network-card-specific idle handling */
921 pxe_idle_init();
923 /* Our name for the root */
924 strcpy(fs->cwd_name, "::");
926 return 0;
930 * Look to see if we are on an EFI CSM system. Some EFI
931 * CSM systems put the BEV stack in low memory, which means
932 * a return to the PXE stack will crash the system. However,
933 * INT 18h works reliably, so in that case hack the stack and
934 * point the "return address" to an INT 18h instruction.
936 * Hack the stack instead of the much simpler "just invoke INT 18h
937 * if we want to reset", so that chainloading other NBPs will work.
939 * This manipulates the real-mode InitStack directly. It relies on this
940 * *not* being a currently active stack, i.e. the former
941 * USE_PXE_PROVIDED_STACK no longer works.
943 * XXX: Disable this until we can find a better way to discriminate
944 * between BIOSes that are broken on BEV return and BIOSes which are
945 * broken on INT 18h. Keying on the EFI CSM turns out to cause more
946 * problems than it solves.
948 extern far_ptr_t InitStack;
950 struct efi_struct {
951 uint32_t magic;
952 uint8_t csum;
953 uint8_t len;
954 } __attribute__((packed));
955 #define EFI_MAGIC (('$' << 24)+('E' << 16)+('F' << 8)+'I')
957 static inline bool is_efi(const struct efi_struct *efi)
960 * We don't verify the checksum, because it seems some CSMs leave
961 * it at zero, sigh...
963 return (efi->magic == EFI_MAGIC) && (efi->len >= 83);
966 #if 0
967 static void install_int18_hack(void)
969 static const uint8_t int18_hack[] =
971 0xcd, 0x18, /* int $0x18 */
972 0xea, 0xf0, 0xff, 0x00, 0xf0, /* ljmpw $0xf000,$0xfff0 */
973 0xf4 /* hlt */
975 uint16_t *retcode;
977 retcode = GET_PTR(*(far_ptr_t *)((char *)GET_PTR(InitStack) + 44));
979 /* Don't do this if the return already points to int $0x18 */
980 if (*retcode != 0x18cd) {
981 uint32_t efi_ptr;
982 bool efi = false;
984 for (efi_ptr = 0xe0000 ; efi_ptr < 0x100000 ; efi_ptr += 16) {
985 if (is_efi((const struct efi_struct *)efi_ptr)) {
986 efi = true;
987 break;
991 if (efi) {
992 uint8_t *src = GET_PTR(InitStack);
993 uint8_t *dst = src - sizeof int18_hack;
995 memmove(dst, src, 52);
996 memcpy(dst+52, int18_hack, sizeof int18_hack);
997 InitStack.offs -= sizeof int18_hack;
999 /* Clobber the return address */
1000 *(uint16_t *)(dst+44) = OFFS_WRT(dst+52, InitStack.seg);
1001 *(uint16_t *)(dst+46) = InitStack.seg;
1005 #endif
1008 * This function unloads the PXE and UNDI stacks and
1009 * unclaims the memory.
1011 void unload_pxe(void)
1013 /* PXE unload sequences */
1015 * iPXE does:
1016 * UNDI_SHUTDOWN, UNDI_CLEANUP, STOP_UNDI
1017 * Older Syslinux did:
1018 * UDP_CLOSE, UNDI_SHUTDOWN, UNLOAD_STACK, STOP_UNDI/UNDI_CLEANUP
1020 static const uint8_t new_api_unload[] = {
1021 PXENV_UNDI_SHUTDOWN, PXENV_UNLOAD_STACK, PXENV_STOP_UNDI, 0
1023 static const uint8_t old_api_unload[] = {
1024 PXENV_UNDI_SHUTDOWN, PXENV_UNLOAD_STACK, PXENV_UNDI_CLEANUP, 0
1027 unsigned int api;
1028 const uint8_t *api_ptr;
1029 int err;
1030 size_t int_addr;
1031 static __lowmem union {
1032 struct s_PXENV_UNDI_SHUTDOWN undi_shutdown;
1033 struct s_PXENV_UNLOAD_STACK unload_stack;
1034 struct s_PXENV_STOP_UNDI stop_undi;
1035 struct s_PXENV_UNDI_CLEANUP undi_cleanup;
1036 uint16_t Status; /* All calls have this as the first member */
1037 } unload_call;
1039 dprintf("Called unload_pxe()...\n");
1040 dprintf("FBM before unload = %d\n", BIOS_fbm);
1042 err = reset_pxe();
1044 /* If we want to keep PXE around, we still need to reset it */
1045 if (KeepPXE || err)
1046 return;
1048 dprintf("APIVer = %04x\n", APIVer);
1050 api_ptr = APIVer >= 0x0200 ? new_api_unload : old_api_unload;
1051 while((api = *api_ptr++)) {
1052 dprintf("PXE call %04x\n", api);
1053 memset(&unload_call, 0, sizeof unload_call);
1054 err = pxe_call(api, &unload_call);
1055 if (err || unload_call.Status != PXENV_STATUS_SUCCESS) {
1056 printf("PXE unload API call %04x failed: 0x%x\n",
1057 api, unload_call.Status);
1058 goto cant_free;
1062 api = 0xff00;
1063 if (real_base_mem <= BIOS_fbm) { /* Sanity check */
1064 dprintf("FBM %d < real_base_mem %d\n", BIOS_fbm, real_base_mem);
1065 goto cant_free;
1067 api++;
1069 /* Check that PXE actually unhooked the INT 0x1A chain */
1070 int_addr = (size_t)GET_PTR(*(far_ptr_t *)(4 * 0x1a));
1071 int_addr >>= 10;
1072 if (int_addr >= real_base_mem || int_addr < BIOS_fbm) {
1073 BIOS_fbm = real_base_mem;
1074 dprintf("FBM after unload_pxe = %d\n", BIOS_fbm);
1075 return;
1078 dprintf("Can't free FBM, real_base_mem = %d, "
1079 "FBM = %d, INT 1A = %08x (%d)\n",
1080 real_base_mem, BIOS_fbm,
1081 *(uint32_t *)(4 * 0x1a), int_addr);
1083 cant_free:
1084 printf("Failed to free base memory error %04x-%08x (%d/%dK)\n",
1085 api, *(uint32_t *)(4 * 0x1a), BIOS_fbm, real_base_mem);
1086 return;
1089 static int pxe_readdir(struct file *file, struct dirent *dirent)
1091 struct inode *inode = file->inode;
1092 struct pxe_pvt_inode *socket = PVT(inode);
1094 if (socket->ops->readdir)
1095 return socket->ops->readdir(inode, dirent);
1096 else
1097 return -1; /* No such operation */
1100 const struct fs_ops pxe_fs_ops = {
1101 .fs_name = "pxe",
1102 .fs_flags = FS_NODEV,
1103 .fs_init = pxe_fs_init,
1104 .searchdir = pxe_searchdir,
1105 .chdir = pxe_chdir,
1106 .realpath = pxe_realpath,
1107 .getfssec = pxe_getfssec,
1108 .close_file = pxe_close_file,
1109 .mangle_name = pxe_mangle_name,
1110 .load_config = pxe_load_config,
1111 .readdir = pxe_readdir,