PXE: use ddprintf macro
[syslinux.git] / core / fs / pxe / pxe.c
blob526323a4f3d663d2589a7d06ec057cbc66135eb1
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 <fcntl.h>
9 #include <sys/cpu.h>
10 #include "pxe.h"
11 #include "thread.h"
12 #include "url.h"
13 #include "tftp.h"
14 #include <net.h>
16 __lowmem t_PXENV_UNDI_GET_INFORMATION pxe_undi_info;
17 __lowmem t_PXENV_UNDI_GET_IFACE_INFO pxe_undi_iface;
19 static uint16_t real_base_mem; /* Amount of DOS memory after freeing */
21 uint8_t MAC[MAC_MAX]; /* Actual MAC address */
22 uint8_t MAC_len; /* MAC address len */
23 uint8_t MAC_type; /* MAC address type */
25 char boot_file[256]; /* From DHCP */
26 char path_prefix[256]; /* From DHCP */
28 static bool has_gpxe;
29 static uint32_t gpxe_funcs;
30 bool have_uuid = false;
33 * Allocate a local UDP port structure and assign it a local port number.
34 * Return the inode pointer if success, or null if failure
36 static struct inode *allocate_socket(struct fs_info *fs)
38 struct inode *inode = alloc_inode(fs, 0, sizeof(struct pxe_pvt_inode));
40 if (!inode) {
41 malloc_error("socket structure");
42 } else {
43 inode->mode = DT_REG; /* No other types relevant for PXE */
46 return inode;
49 void free_socket(struct inode *inode)
51 struct pxe_pvt_inode *socket = PVT(inode);
53 free(socket->tftp_pktbuf); /* If we allocated a buffer, free it now */
54 free_inode(inode);
57 static void pxe_close_file(struct file *file)
59 struct inode *inode = file->inode;
60 struct pxe_pvt_inode *socket = PVT(inode);
62 if (!inode)
63 return;
65 if (!socket->tftp_goteof) {
66 socket->ops->close(inode);
69 free_socket(inode);
73 * Tests an IP address in _ip_ for validity; return with 0 for bad, 1 for good.
74 * We used to refuse class E, but class E addresses are likely to become
75 * assignable unicast addresses in the near future.
78 bool ip_ok(uint32_t ip)
80 uint8_t ip_hi = (uint8_t)ip; /* First octet of the ip address */
82 if (ip == 0xffffffff || /* Refuse the all-ones address */
83 ip_hi == 0 || /* Refuse network zero */
84 ip_hi == 127 || /* Refuse the loopback network */
85 (ip_hi & 240) == 224) /* Refuse class D */
86 return false;
88 return true;
93 * Take an IP address (in network byte order) in _ip_ and
94 * output a dotted quad string to _dst_, returns the length
95 * of the dotted quad ip string.
98 static int gendotquad(char *dst, uint32_t ip)
100 return sprintf(dst, "%u.%u.%u.%u",
101 ((const uint8_t *)&ip)[0],
102 ((const uint8_t *)&ip)[1],
103 ((const uint8_t *)&ip)[2],
104 ((const uint8_t *)&ip)[3]);
108 * the ASM pxenv function wrapper, return 1 if error, or 0
111 __export int pxe_call(int opcode, void *data)
113 static DECLARE_INIT_SEMAPHORE(pxe_sem, 1);
114 extern void pxenv(void);
115 com32sys_t regs;
117 sem_down(&pxe_sem, 0);
119 #if 0
120 dprintf("pxe_call op %04x data %p\n", opcode, data);
121 #endif
123 memset(&regs, 0, sizeof regs);
124 regs.ebx.w[0] = opcode;
125 regs.es = SEG(data);
126 regs.edi.w[0] = OFFS(data);
127 call16(pxenv, &regs, &regs);
129 sem_up(&pxe_sem);
131 return regs.eflags.l & EFLAGS_CF; /* CF SET if fail */
135 * Get a DHCP packet from the PXE stack into a lowmem buffer
137 * @param: type, packet type
138 * @return: buffer size
141 static int pxe_get_cached_info(int type, void *buf, size_t bufsiz)
143 int err;
144 static __lowmem struct s_PXENV_GET_CACHED_INFO get_cached_info;
145 ddprintf(" %02x", type);
147 memset(&get_cached_info, 0, sizeof get_cached_info);
148 get_cached_info.PacketType = type;
149 get_cached_info.BufferSize = bufsiz;
150 get_cached_info.Buffer = FAR_PTR(buf);
151 err = pxe_call(PXENV_GET_CACHED_INFO, &get_cached_info);
152 if (err) {
153 ddprintf("PXE API call failed, error %04x\n", err);
154 kaboom();
157 return get_cached_info.BufferSize;
161 * mangle a filename pointed to by _src_ into a buffer pointed
162 * to by _dst_; ends on encountering any whitespace.
164 * This deliberately does not attempt to do any conversion of
165 * pathname separators.
168 static void pxe_mangle_name(char *dst, const char *src)
170 size_t len = FILENAME_MAX-1;
172 while (len-- && not_whitespace(*src))
173 *dst++ = *src++;
175 *dst = '\0';
179 * Read a single character from the specified pxe inode.
180 * Very useful for stepping through http streams and
181 * parsing their headers.
183 int pxe_getc(struct inode *inode)
185 struct pxe_pvt_inode *socket = PVT(inode);
186 unsigned char byte;
188 while (!socket->tftp_bytesleft) {
189 if (socket->tftp_goteof)
190 return -1;
192 socket->ops->fill_buffer(inode);
195 byte = *socket->tftp_dataptr;
196 socket->tftp_bytesleft -= 1;
197 socket->tftp_dataptr += 1;
199 return byte;
203 * Get a fresh packet if the buffer is drained, and we haven't hit
204 * EOF yet. The buffer should be filled immediately after draining!
206 static void fill_buffer(struct inode *inode)
208 struct pxe_pvt_inode *socket = PVT(inode);
209 if (socket->tftp_bytesleft || socket->tftp_goteof)
210 return;
212 return socket->ops->fill_buffer(inode);
217 * getfssec: Get multiple clusters from a file, given the starting cluster.
218 * In this case, get multiple blocks from a specific TCP connection.
220 * @param: fs, the fs_info structure address, in pxe, we don't use this.
221 * @param: buf, buffer to store the read data
222 * @param: openfile, TFTP socket pointer
223 * @param: blocks, 512-byte block count; 0FFFFh = until end of file
225 * @return: the bytes read
228 static uint32_t pxe_getfssec(struct file *file, char *buf,
229 int blocks, bool *have_more)
231 struct inode *inode = file->inode;
232 struct pxe_pvt_inode *socket = PVT(inode);
233 int count = blocks;
234 int chunk;
235 int bytes_read = 0;
237 count <<= TFTP_BLOCKSIZE_LG2;
238 while (count) {
239 fill_buffer(inode); /* If we have no 'fresh' buffer, get it */
240 if (!socket->tftp_bytesleft)
241 break;
243 chunk = count;
244 if (chunk > socket->tftp_bytesleft)
245 chunk = socket->tftp_bytesleft;
246 socket->tftp_bytesleft -= chunk;
247 memcpy(buf, socket->tftp_dataptr, chunk);
248 socket->tftp_dataptr += chunk;
249 buf += chunk;
250 bytes_read += chunk;
251 count -= chunk;
255 if (socket->tftp_bytesleft || (socket->tftp_filepos < inode->size)) {
256 fill_buffer(inode);
257 *have_more = 1;
258 } else if (socket->tftp_goteof) {
260 * The socket is closed and the buffer drained; the caller will
261 * call close_file and therefore free the socket.
263 *have_more = 0;
266 return bytes_read;
270 * Assign an IP address to a URL
272 static void url_set_ip(struct url_info *url)
274 url->ip = 0;
275 if (url->host)
276 url->ip = dns_resolv(url->host);
277 if (!url->ip)
278 url->ip = IPInfo.serverip;
282 * Open the specified connection
284 * @param:filename, the file we wanna open
286 * @out: open_file_t structure, stores in file->open_file
287 * @out: the lenght of this file, stores in file->file_len
290 static void __pxe_searchdir(const char *filename, int flags, struct file *file);
291 extern uint16_t PXERetry;
293 static void pxe_searchdir(const char *filename, int flags, struct file *file)
295 int i = PXERetry;
297 do {
298 dprintf("PXE: file = %p, retries left = %d: ", file, i);
299 __pxe_searchdir(filename, flags, file);
300 dprintf("%s\n", file->inode ? "ok" : "failed");
301 } while (!file->inode && i--);
303 static void __pxe_searchdir(const char *filename, int flags, struct file *file)
305 struct fs_info *fs = file->fs;
306 struct inode *inode;
307 char fullpath[2*FILENAME_MAX];
308 #if GPXE
309 char urlsave[2*FILENAME_MAX];
310 #endif
311 struct url_info url;
312 const struct url_scheme *us = NULL;
313 int redirect_count = 0;
314 bool found_scheme = false;
316 inode = file->inode = NULL;
318 while (filename) {
319 if (redirect_count++ > 5)
320 break;
322 strlcpy(fullpath, filename, sizeof fullpath);
323 #if GPXE
324 strcpy(urlsave, fullpath);
325 #endif
326 parse_url(&url, fullpath);
327 if (url.type == URL_SUFFIX) {
328 snprintf(fullpath, sizeof fullpath, "%s%s", fs->cwd_name, filename);
329 #if GPXE
330 strcpy(urlsave, fullpath);
331 #endif
332 parse_url(&url, fullpath);
335 inode = allocate_socket(fs);
336 if (!inode)
337 return; /* Allocation failure */
339 url_set_ip(&url);
341 filename = NULL;
342 found_scheme = false;
343 for (us = url_schemes; us->name; us++) {
344 if (!strcmp(us->name, url.scheme)) {
345 if ((flags & ~us->ok_flags & OK_FLAGS_MASK) == 0)
346 us->open(&url, flags, inode, &filename);
347 found_scheme = true;
348 break;
352 /* filename here is set on a redirect */
355 if (!found_scheme) {
356 #if GPXE
357 /* No URL scheme found, hand it to GPXE */
358 gpxe_open(inode, urlsave);
359 #endif
362 if (inode->size) {
363 file->inode = inode;
364 file->inode->mode = (flags & O_DIRECTORY) ? DT_DIR : DT_REG;
365 } else {
366 free_socket(inode);
369 return;
374 * Store standard filename prefix
376 static void get_prefix(void)
378 int len;
379 char *p;
380 char c;
382 if (!(DHCPMagic & 0x04)) {
383 /* No path prefix option, derive from boot file */
385 strlcpy(path_prefix, boot_file, sizeof path_prefix);
386 len = strlen(path_prefix);
387 p = &path_prefix[len - 1];
389 while (len--) {
390 c = *p--;
391 c |= 0x20;
393 c = (c >= '0' && c <= '9') ||
394 (c >= 'a' && c <= 'z') ||
395 (c == '.' || c == '-');
396 if (!c)
397 break;
400 if (len < 0)
401 p --;
403 *(p + 2) = 0; /* Zero-terminate after delimiter */
406 ddprintf("TFTP prefix: %s\n", path_prefix);
408 if (url_type(path_prefix) == URL_SUFFIX) {
410 * Construct a ::-style TFTP path.
412 * We may have moved out of the root directory at the time
413 * this function is invoked, but to maintain compatibility
414 * with versions of Syslinux < 5.00, path_prefix must be
415 * relative to "::".
417 p = strdup(path_prefix);
418 if (!p)
419 return;
421 snprintf(path_prefix, sizeof path_prefix, "::%s", p);
422 free(p);
425 chdir(path_prefix);
429 * realpath for PXE
431 static size_t pxe_realpath(struct fs_info *fs, char *dst, const char *src,
432 size_t bufsize)
434 return snprintf(dst, bufsize, "%s%s",
435 url_type(src) == URL_SUFFIX ? fs->cwd_name : "", src);
439 * chdir for PXE
441 static int pxe_chdir(struct fs_info *fs, const char *src)
443 /* The cwd for PXE is just a text prefix */
444 enum url_type path_type = url_type(src);
446 if (path_type == URL_SUFFIX)
447 strlcat(fs->cwd_name, src, sizeof fs->cwd_name);
448 else
449 strlcpy(fs->cwd_name, src, sizeof fs->cwd_name);
450 return 0;
452 dprintf("cwd = \"%s\"\n", fs->cwd_name);
453 return 0;
456 static int pxe_chdir_start(void)
458 get_prefix();
459 return 0;
462 /* Load the config file, return -1 if failed, or 0 */
463 static int pxe_open_config(struct com32_filedata *filedata)
465 const char *cfgprefix = "pxelinux.cfg/";
466 const char *default_str = "default";
467 char *config_file;
468 char *last;
469 int tries = 8;
471 chdir(path_prefix);
472 if (DHCPMagic & 0x02) {
473 /* We got a DHCP option, try it first */
474 if (open_file(ConfigName, O_RDONLY, filedata) >= 0)
475 return 0;
479 * Have to guess config file name ...
481 config_file = stpcpy(ConfigName, cfgprefix);
483 /* Try loading by UUID */
484 if (sysappend_strings[SYSAPPEND_SYSUUID]) {
485 strcpy(config_file, sysappend_strings[SYSAPPEND_SYSUUID]+8);
486 if (open_file(ConfigName, O_RDONLY, filedata) >= 0)
487 return 0;
490 /* Try loading by MAC address */
491 strcpy(config_file, sysappend_strings[SYSAPPEND_BOOTIF]+7);
492 if (open_file(ConfigName, O_RDONLY, filedata) >= 0)
493 return 0;
495 /* Nope, try hexadecimal IP prefixes... */
496 sprintf(config_file, "%08X", ntohl(IPInfo.myip));
497 last = &config_file[8];
498 while (tries) {
499 *last = '\0'; /* Zero-terminate string */
500 if (open_file(ConfigName, O_RDONLY, filedata) >= 0)
501 return 0;
502 last--; /* Drop one character */
503 tries--;
506 /* Final attempt: "default" string */
507 strcpy(config_file, default_str);
508 if (open_file(ConfigName, O_RDONLY, filedata) >= 0)
509 return 0;
511 ddprintf("%-68s\n", "Unable to locate configuration file");
512 kaboom();
516 * Generate the bootif string.
518 static void make_bootif_string(void)
520 static char bootif_str[7+3*(MAC_MAX+1)];
521 const uint8_t *src;
522 char *dst = bootif_str;
523 int i;
525 dst += sprintf(dst, "BOOTIF=%02x", MAC_type);
526 src = MAC;
527 for (i = MAC_len; i; i--)
528 dst += sprintf(dst, "-%02x", *src++);
530 sysappend_strings[SYSAPPEND_BOOTIF] = bootif_str;
534 * Generate an ip=<client-ip>:<boot-server-ip>:<gw-ip>:<netmask>
535 * option into IPOption based on DHCP information in IPInfo.
538 static void genipopt(void)
540 static char ip_option[3+4*16];
541 const uint32_t *v = &IPInfo.myip;
542 char *p;
543 int i;
545 p = stpcpy(ip_option, "ip=");
547 for (i = 0; i < 4; i++) {
548 p += gendotquad(p, *v++);
549 *p++ = ':';
551 *--p = '\0';
553 sysappend_strings[SYSAPPEND_IP] = ip_option;
557 /* Generate ip= option and print the ip adress */
558 static void ip_init(void)
560 uint32_t ip = IPInfo.myip;
561 char dot_quad_buf[16];
563 genipopt();
564 gendotquad(dot_quad_buf, ip);
566 ip = ntohl(ip);
567 ddprintf("My IP address seems to be %08X %s\n", ip, dot_quad_buf);
571 * Validity check on possible !PXE structure in buf
572 * return 1 for success, 0 for failure.
575 static int is_pxe(const void *buf)
577 const struct pxe_t *pxe = buf;
578 const uint8_t *p = buf;
579 int i = pxe->structlength;
580 uint8_t sum = 0;
582 if (i < sizeof(struct pxe_t) ||
583 memcmp(pxe->signature, "!PXE", 4))
584 return 0;
586 while (i--)
587 sum += *p++;
589 return sum == 0;
593 * Just like is_pxe, it checks PXENV+ structure
596 static int is_pxenv(const void *buf)
598 const struct pxenv_t *pxenv = buf;
599 const uint8_t *p = buf;
600 int i = pxenv->length;
601 uint8_t sum = 0;
603 /* The pxeptr field isn't present in old versions */
604 if (i < offsetof(struct pxenv_t, pxeptr) ||
605 memcmp(pxenv->signature, "PXENV+", 6))
606 return 0;
608 while (i--)
609 sum += *p++;
611 return sum == 0;
617 * memory_scan_for_pxe_struct:
618 * memory_scan_for_pxenv_struct:
620 * If none of the standard methods find the !PXE/PXENV+ structure,
621 * look for it by scanning memory.
623 * return the corresponding pxe structure if found, or NULL;
625 static const void *memory_scan(uintptr_t start, int (*func)(const void *))
627 const char *ptr;
629 /* Scan each 16 bytes of conventional memory before the VGA region */
630 for (ptr = (const char *)start; ptr < (const char *)0xA0000; ptr += 16) {
631 if (func(ptr))
632 return ptr; /* found it! */
633 ptr += 16;
635 return NULL;
638 static const struct pxe_t *memory_scan_for_pxe_struct(void)
640 uint16_t start = bios_fbm(); /* Starting segment */
642 return memory_scan(start << 10, is_pxe);
645 static const struct pxenv_t *memory_scan_for_pxenv_struct(void)
647 return memory_scan(0x10000, is_pxenv);
651 * Find the !PXE structure; we search for the following, in order:
653 * a. !PXE structure as SS:[SP + 4]
654 * b. PXENV+ structure at [ES:BX]
655 * c. INT 1Ah AX=0x5650 -> PXENV+
656 * d. Search memory for !PXE
657 * e. Search memory for PXENV+
659 * If we find a PXENV+ structure, we try to find a !PXE structure from
660 * if if the API version is 2.1 or later
663 static int pxe_init(bool quiet)
665 extern void pxe_int1a(void);
666 char plan = 'A';
667 uint16_t seg, off;
668 uint16_t code_seg, code_len;
669 uint16_t data_seg, data_len;
670 const char *base = GET_PTR(InitStack);
671 com32sys_t regs;
672 const char *type;
673 const struct pxenv_t *pxenv;
674 const struct pxe_t *pxe;
676 /* Assume API version 2.1 */
677 APIVer = 0x201;
679 /* Plan A: !PXE structure as SS:[SP + 4] */
680 off = *(const uint16_t *)(base + 48);
681 seg = *(const uint16_t *)(base + 50);
682 pxe = MK_PTR(seg, off);
683 if (is_pxe(pxe))
684 goto have_pxe;
686 /* Plan B: PXENV+ structure at [ES:BX] */
687 plan++;
688 off = *(const uint16_t *)(base + 24); /* Original BX */
689 seg = *(const uint16_t *)(base + 4); /* Original ES */
690 pxenv = MK_PTR(seg, off);
691 if (is_pxenv(pxenv))
692 goto have_pxenv;
694 /* Plan C: PXENV+ structure via INT 1Ah AX=5650h */
695 plan++;
696 memset(&regs, 0, sizeof regs);
697 regs.eax.w[0] = 0x5650;
698 call16(pxe_int1a, &regs, &regs);
699 if (!(regs.eflags.l & EFLAGS_CF) && (regs.eax.w[0] == 0x564e)) {
700 pxenv = MK_PTR(regs.es, regs.ebx.w[0]);
701 if (is_pxenv(pxenv))
702 goto have_pxenv;
705 /* Plan D: !PXE memory scan */
706 plan++;
707 if ((pxe = memory_scan_for_pxe_struct()))
708 goto have_pxe;
710 /* Plan E: PXENV+ memory scan */
711 plan++;
712 if ((pxenv = memory_scan_for_pxenv_struct()))
713 goto have_pxenv;
715 /* Found nothing at all !! */
716 if (!quiet)
717 ddprintf("No !PXE or PXENV+ API found; we're dead...\n");
718 return -1;
720 have_pxenv:
721 APIVer = pxenv->version;
722 if (!quiet)
723 ddprintf("Found PXENV+ structure\nPXE API version is %04x\n", APIVer);
725 /* if the API version number is 0x0201 or higher, use the !PXE structure */
726 if (APIVer >= 0x201) {
727 if (pxenv->length >= sizeof(struct pxenv_t)) {
728 pxe = GET_PTR(pxenv->pxeptr);
729 if (is_pxe(pxe))
730 goto have_pxe;
732 * Nope, !PXE structure missing despite API 2.1+, or at least
733 * the pointer is missing. Do a last-ditch attempt to find it
735 if ((pxe = memory_scan_for_pxe_struct()))
736 goto have_pxe;
738 APIVer = 0x200; /* PXENV+ only, assume version 2.00 */
741 /* Otherwise, no dice, use PXENV+ structure */
742 data_len = pxenv->undidatasize;
743 data_seg = pxenv->undidataseg;
744 code_len = pxenv->undicodesize;
745 code_seg = pxenv->undicodeseg;
746 PXEEntry = pxenv->rmentry;
747 type = "PXENV+";
748 goto have_entrypoint;
750 have_pxe:
751 data_len = pxe->seg[PXE_Seg_UNDIData].size;
752 data_seg = pxe->seg[PXE_Seg_UNDIData].sel;
753 code_len = pxe->seg[PXE_Seg_UNDICode].size;
754 code_seg = pxe->seg[PXE_Seg_UNDICode].sel;
755 PXEEntry = pxe->entrypointsp;
756 type = "!PXE";
758 have_entrypoint:
759 if (!quiet) {
760 ddprintf("%s entry point found (we hope) at %04X:%04X via plan %c\n",
761 type, PXEEntry.seg, PXEEntry.offs, plan);
762 ddprintf("UNDI code segment at %04X len %04X\n", code_seg, code_len);
763 ddprintf("UNDI data segment at %04X len %04X\n", data_seg, data_len);
766 code_seg = code_seg + ((code_len + 15) >> 4);
767 data_seg = data_seg + ((data_len + 15) >> 4);
769 real_base_mem = max(code_seg, data_seg) >> 6; /* Convert to kilobytes */
771 probe_undi();
773 return 0;
777 * See if we have gPXE
779 static void gpxe_init(void)
781 int err;
782 static __lowmem struct s_PXENV_FILE_API_CHECK api_check;
784 if (APIVer >= 0x201) {
785 api_check.Size = sizeof api_check;
786 api_check.Magic = 0x91d447b2;
787 err = pxe_call(PXENV_FILE_API_CHECK, &api_check);
788 if (!err && api_check.Magic == 0xe9c17b20)
789 gpxe_funcs = api_check.APIMask;
792 /* Necessary functions for us to use the gPXE file API */
793 has_gpxe = (~gpxe_funcs & 0x4b) == 0;
797 * Network-specific initialization
799 static void network_init(void)
801 int pkt_len;
802 struct bootp_t *bp;
803 const size_t dhcp_max_packet = 4096;
805 bp = lmalloc(dhcp_max_packet);
806 if (!bp) {
807 ddprintf("Out of low memory\n");
808 kaboom();
811 *LocalDomain = 0; /* No LocalDomain received */
814 * Get the DHCP client identifiers (query info 1)
816 ddprintf("Getting cached packet ");
817 pkt_len = pxe_get_cached_info(1, bp, dhcp_max_packet);
818 parse_dhcp(bp, pkt_len);
820 * We don't use flags from the request packet, so
821 * this is a good time to initialize DHCPMagic...
822 * Initialize it to 1 meaning we will accept options found;
823 * in earlier versions of PXELINUX bit 0 was used to indicate
824 * we have found option 208 with the appropriate magic number;
825 * we no longer require that, but MAY want to re-introduce
826 * it in the future for vendor encapsulated options.
828 *(char *)&DHCPMagic = 1;
831 * Get the BOOTP/DHCP packet that brought us file (and an IP
832 * address). This lives in the DHCPACK packet (query info 2)
834 pkt_len = pxe_get_cached_info(2, bp, dhcp_max_packet);
835 parse_dhcp(bp, pkt_len);
837 * Save away MAC address (assume this is in query info 2. If this
838 * turns out to be problematic it might be better getting it from
839 * the query info 1 packet
841 MAC_len = bp->hardlen > 16 ? 0 : bp->hardlen;
842 MAC_type = bp->hardware;
843 memcpy(MAC, bp->macaddr, MAC_len);
846 * Get the boot file and other info. This lives in the CACHED_REPLY
847 * packet (query info 3)
849 pkt_len = pxe_get_cached_info(3, bp, dhcp_max_packet);
850 parse_dhcp(bp, pkt_len);
851 ddprintf("\n");
853 lfree(bp);
855 make_bootif_string();
856 /* If DMI and DHCP disagree, which one should we set? */
857 if (have_uuid)
858 sysappend_set_uuid(uuid);
859 ip_init();
861 /* print_sysappend(); */
863 * Check to see if we got any PXELINUX-specific DHCP options; in particular,
864 * if we didn't get the magic enable, do not recognize any other options.
866 if ((DHCPMagic & 1) == 0)
867 DHCPMagic = 0;
869 net_core_init();
873 * Initialize pxe fs
876 static int pxe_fs_init(struct fs_info *fs)
878 (void)fs; /* drop the compile warning message */
880 /* Prepare for handling pxe interrupts */
881 pxe_init_isr();
883 /* This block size is actually arbitrary... */
884 fs->sector_shift = fs->block_shift = TFTP_BLOCKSIZE_LG2;
885 fs->sector_size = fs->block_size = 1 << TFTP_BLOCKSIZE_LG2;
887 /* Find the PXE stack */
888 if (pxe_init(false))
889 kaboom();
891 /* See if we also have a gPXE stack */
892 gpxe_init();
894 /* Network-specific initialization */
895 network_init();
897 /* Initialize network-card-specific idle handling */
898 pxe_idle_init();
900 /* Our name for the root */
901 strcpy(fs->cwd_name, "::");
903 return 0;
907 * Look to see if we are on an EFI CSM system. Some EFI
908 * CSM systems put the BEV stack in low memory, which means
909 * a return to the PXE stack will crash the system. However,
910 * INT 18h works reliably, so in that case hack the stack and
911 * point the "return address" to an INT 18h instruction.
913 * Hack the stack instead of the much simpler "just invoke INT 18h
914 * if we want to reset", so that chainloading other NBPs will work.
916 * This manipulates the real-mode InitStack directly. It relies on this
917 * *not* being a currently active stack, i.e. the former
918 * USE_PXE_PROVIDED_STACK no longer works.
920 * XXX: Disable this until we can find a better way to discriminate
921 * between BIOSes that are broken on BEV return and BIOSes which are
922 * broken on INT 18h. Keying on the EFI CSM turns out to cause more
923 * problems than it solves.
925 extern far_ptr_t InitStack;
927 struct efi_struct {
928 uint32_t magic;
929 uint8_t csum;
930 uint8_t len;
931 } __attribute__((packed));
932 #define EFI_MAGIC (('$' << 24)+('E' << 16)+('F' << 8)+'I')
934 static inline bool is_efi(const struct efi_struct *efi)
937 * We don't verify the checksum, because it seems some CSMs leave
938 * it at zero, sigh...
940 return (efi->magic == EFI_MAGIC) && (efi->len >= 83);
943 #if 0
944 static void install_int18_hack(void)
946 static const uint8_t int18_hack[] =
948 0xcd, 0x18, /* int $0x18 */
949 0xea, 0xf0, 0xff, 0x00, 0xf0, /* ljmpw $0xf000,$0xfff0 */
950 0xf4 /* hlt */
952 uint16_t *retcode;
954 retcode = GET_PTR(*(far_ptr_t *)((char *)GET_PTR(InitStack) + 44));
956 /* Don't do this if the return already points to int $0x18 */
957 if (*retcode != 0x18cd) {
958 uint32_t efi_ptr;
959 bool efi = false;
961 for (efi_ptr = 0xe0000 ; efi_ptr < 0x100000 ; efi_ptr += 16) {
962 if (is_efi((const struct efi_struct *)efi_ptr)) {
963 efi = true;
964 break;
968 if (efi) {
969 uint8_t *src = GET_PTR(InitStack);
970 uint8_t *dst = src - sizeof int18_hack;
972 memmove(dst, src, 52);
973 memcpy(dst+52, int18_hack, sizeof int18_hack);
974 InitStack.offs -= sizeof int18_hack;
976 /* Clobber the return address */
977 *(uint16_t *)(dst+44) = OFFS_WRT(dst+52, InitStack.seg);
978 *(uint16_t *)(dst+46) = InitStack.seg;
982 #endif
985 * This function unloads the PXE and UNDI stacks and
986 * unclaims the memory.
988 __export void unload_pxe(uint16_t flags)
990 /* PXE unload sequences */
992 * iPXE does:
993 * UNDI_SHUTDOWN, UNDI_CLEANUP, STOP_UNDI
994 * Older Syslinux did:
995 * UDP_CLOSE, UNDI_SHUTDOWN, UNLOAD_STACK, STOP_UNDI/UNDI_CLEANUP
997 static const uint8_t new_api_unload[] = {
998 PXENV_UNDI_SHUTDOWN, PXENV_UNLOAD_STACK, PXENV_STOP_UNDI, 0
1000 static const uint8_t old_api_unload[] = {
1001 PXENV_UNDI_SHUTDOWN, PXENV_UNLOAD_STACK, PXENV_UNDI_CLEANUP, 0
1004 unsigned int api;
1005 const uint8_t *api_ptr;
1006 int err;
1007 size_t int_addr;
1008 static __lowmem union {
1009 struct s_PXENV_UNDI_SHUTDOWN undi_shutdown;
1010 struct s_PXENV_UNLOAD_STACK unload_stack;
1011 struct s_PXENV_STOP_UNDI stop_undi;
1012 struct s_PXENV_UNDI_CLEANUP undi_cleanup;
1013 uint16_t Status; /* All calls have this as the first member */
1014 } unload_call;
1016 dprintf("Called unload_pxe()...\n");
1017 dprintf("FBM before unload = %d\n", bios_fbm());
1019 err = reset_pxe();
1021 dprintf("FBM after reset_pxe = %d, err = %d\n", bios_fbm(), err);
1023 /* If we want to keep PXE around, we still need to reset it */
1024 if (flags || err)
1025 return;
1027 dprintf("APIVer = %04x\n", APIVer);
1029 api_ptr = APIVer >= 0x0200 ? new_api_unload : old_api_unload;
1030 while((api = *api_ptr++)) {
1031 dprintf("PXE call %04x\n", api);
1032 memset(&unload_call, 0, sizeof unload_call);
1033 err = pxe_call(api, &unload_call);
1034 if (err || unload_call.Status != PXENV_STATUS_SUCCESS) {
1035 ddprintf("PXE unload API call %04x failed: 0x%x\n",
1036 api, unload_call.Status);
1037 goto cant_free;
1041 api = 0xff00;
1042 if (real_base_mem <= bios_fbm()) { /* Sanity check */
1043 dprintf("FBM %d < real_base_mem %d\n", bios_fbm(), real_base_mem);
1044 goto cant_free;
1046 api++;
1048 /* Check that PXE actually unhooked the INT 0x1A chain */
1049 int_addr = (size_t)GET_PTR(*(far_ptr_t *)(4 * 0x1a));
1050 int_addr >>= 10;
1051 if (int_addr >= real_base_mem || int_addr < bios_fbm()) {
1052 set_bios_fbm(real_base_mem);
1053 dprintf("FBM after unload_pxe = %d\n", bios_fbm());
1054 return;
1057 dprintf("Can't free FBM, real_base_mem = %d, "
1058 "FBM = %d, INT 1A = %08x (%d)\n",
1059 real_base_mem, bios_fbm(),
1060 *(uint32_t *)(4 * 0x1a), int_addr);
1062 cant_free:
1063 ddprintf("Failed to free base memory error %04x-%08x (%d/%dK)\n",
1064 api, *(uint32_t *)(4 * 0x1a), bios_fbm(), real_base_mem);
1065 return;
1068 static int pxe_readdir(struct file *file, struct dirent *dirent)
1070 struct inode *inode = file->inode;
1071 struct pxe_pvt_inode *socket = PVT(inode);
1073 if (socket->ops->readdir)
1074 return socket->ops->readdir(inode, dirent);
1075 else
1076 return -1; /* No such operation */
1079 const struct fs_ops pxe_fs_ops = {
1080 .fs_name = "pxe",
1081 .fs_flags = FS_NODEV,
1082 .fs_init = pxe_fs_init,
1083 .searchdir = pxe_searchdir,
1084 .chdir = pxe_chdir,
1085 .realpath = pxe_realpath,
1086 .getfssec = pxe_getfssec,
1087 .close_file = pxe_close_file,
1088 .mangle_name = pxe_mangle_name,
1089 .chdir_start = pxe_chdir_start,
1090 .open_config = pxe_open_config,
1091 .readdir = pxe_readdir,