dmi: check both the AC and ID flags at the same time
[syslinux.git] / core / fs / pxe / pxe.c
bloba8f38559ee0fc1a5cc97eef67819797710601942
1 #include <dprintf.h>
2 #include <stdio.h>
3 #include <string.h>
4 #include <core.h>
5 #include <fs.h>
6 #include <fcntl.h>
7 #include <x86/cpu.h>
8 #include "core_pxe.h"
9 #include "thread.h"
10 #include "url.h"
11 #include "tftp.h"
12 #include <net.h>
14 __lowmem t_PXENV_UNDI_GET_INFORMATION pxe_undi_info;
15 __lowmem t_PXENV_UNDI_GET_IFACE_INFO pxe_undi_iface;
17 uint8_t MAC[MAC_MAX]; /* Actual MAC address */
18 uint8_t MAC_len; /* MAC address len */
19 uint8_t MAC_type; /* MAC address type */
21 char boot_file[256]; /* From DHCP */
22 char path_prefix[256]; /* From DHCP */
24 bool have_uuid = false;
27 * Allocate a local UDP port structure and assign it a local port number.
28 * Return the inode pointer if success, or null if failure
30 static struct inode *allocate_socket(struct fs_info *fs)
32 struct inode *inode = alloc_inode(fs, 0, sizeof(struct pxe_pvt_inode));
34 if (!inode) {
35 malloc_error("socket structure");
36 } else {
37 inode->mode = DT_REG; /* No other types relevant for PXE */
40 return inode;
43 void free_socket(struct inode *inode)
45 struct pxe_pvt_inode *socket = PVT(inode);
47 free(socket->tftp_pktbuf); /* If we allocated a buffer, free it now */
48 free_inode(inode);
51 static void pxe_close_file(struct file *file)
53 struct inode *inode = file->inode;
54 struct pxe_pvt_inode *socket = PVT(inode);
56 if (!inode)
57 return;
59 if (!socket->tftp_goteof) {
60 socket->ops->close(inode);
63 free_socket(inode);
67 * Tests an IP address in _ip_ for validity; return with 0 for bad, 1 for good.
68 * We used to refuse class E, but class E addresses are likely to become
69 * assignable unicast addresses in the near future.
72 bool ip_ok(uint32_t ip)
74 uint8_t ip_hi = (uint8_t)ip; /* First octet of the ip address */
76 if (ip == 0xffffffff || /* Refuse the all-ones address */
77 ip_hi == 0 || /* Refuse network zero */
78 ip_hi == 127 || /* Refuse the loopback network */
79 (ip_hi & 240) == 224) /* Refuse class D */
80 return false;
82 return true;
87 * Take an IP address (in network byte order) in _ip_ and
88 * output a dotted quad string to _dst_, returns the length
89 * of the dotted quad ip string.
92 static int gendotquad(char *dst, uint32_t ip)
94 return sprintf(dst, "%u.%u.%u.%u",
95 ((const uint8_t *)&ip)[0],
96 ((const uint8_t *)&ip)[1],
97 ((const uint8_t *)&ip)[2],
98 ((const uint8_t *)&ip)[3]);
102 * mangle a filename pointed to by _src_ into a buffer pointed
103 * to by _dst_; ends on encountering any whitespace.
105 * This deliberately does not attempt to do any conversion of
106 * pathname separators.
109 static void pxe_mangle_name(char *dst, const char *src)
111 size_t len = FILENAME_MAX-1;
113 while (len-- && not_whitespace(*src))
114 *dst++ = *src++;
116 *dst = '\0';
120 * Read a single character from the specified pxe inode.
121 * Very useful for stepping through http streams and
122 * parsing their headers.
124 int pxe_getc(struct inode *inode)
126 struct pxe_pvt_inode *socket = PVT(inode);
127 unsigned char byte;
129 while (!socket->tftp_bytesleft) {
130 if (socket->tftp_goteof)
131 return -1;
133 socket->ops->fill_buffer(inode);
136 byte = *socket->tftp_dataptr;
137 socket->tftp_bytesleft -= 1;
138 socket->tftp_dataptr += 1;
140 return byte;
144 * Get a fresh packet if the buffer is drained, and we haven't hit
145 * EOF yet. The buffer should be filled immediately after draining!
147 static void fill_buffer(struct inode *inode)
149 struct pxe_pvt_inode *socket = PVT(inode);
150 if (socket->tftp_bytesleft || socket->tftp_goteof)
151 return;
153 return socket->ops->fill_buffer(inode);
158 * getfssec: Get multiple clusters from a file, given the starting cluster.
159 * In this case, get multiple blocks from a specific TCP connection.
161 * @param: fs, the fs_info structure address, in pxe, we don't use this.
162 * @param: buf, buffer to store the read data
163 * @param: openfile, TFTP socket pointer
164 * @param: blocks, 512-byte block count; 0FFFFh = until end of file
166 * @return: the bytes read
169 static uint32_t pxe_getfssec(struct file *file, char *buf,
170 int blocks, bool *have_more)
172 struct inode *inode = file->inode;
173 struct pxe_pvt_inode *socket = PVT(inode);
174 int count = blocks;
175 int chunk;
176 int bytes_read = 0;
178 count <<= TFTP_BLOCKSIZE_LG2;
179 while (count) {
180 fill_buffer(inode); /* If we have no 'fresh' buffer, get it */
181 if (!socket->tftp_bytesleft)
182 break;
184 chunk = count;
185 if (chunk > socket->tftp_bytesleft)
186 chunk = socket->tftp_bytesleft;
187 socket->tftp_bytesleft -= chunk;
188 memcpy(buf, socket->tftp_dataptr, chunk);
189 socket->tftp_dataptr += chunk;
190 buf += chunk;
191 bytes_read += chunk;
192 count -= chunk;
196 if (socket->tftp_bytesleft || (socket->tftp_filepos < inode->size)) {
197 fill_buffer(inode);
198 *have_more = 1;
199 } else if (socket->tftp_goteof) {
201 * The socket is closed and the buffer drained; the caller will
202 * call close_file and therefore free the socket.
204 *have_more = 0;
207 return bytes_read;
211 * Assign an IP address to a URL
213 __export int url_set_ip(struct url_info *url)
215 int err = -ntohs(TFTP_OK);
217 url->ip = 0;
218 if (url->host && url->host[0]) {
219 url->ip = pxe_dns(url->host);
220 if (!url->ip)
221 err = -ntohs(TFTP_ERESOLVE);
224 /* Note: default to the server IP on resolve failure */
225 if (!url->ip) {
226 url->ip = IPInfo.serverip;
227 if (!url->ip)
228 err = -ntohs(TFTP_NONETWORK);
231 return err;
235 * Open the specified connection
237 * @param:filename, the file we wanna open
239 * @out: open_file_t structure, stores in file->open_file
240 * @out: the lenght of this file, stores in file->file_len
243 static void __pxe_searchdir(const char *filename, int flags, struct file *file);
244 extern uint16_t PXERetry;
246 static void pxe_searchdir(const char *filename, int flags, struct file *file)
248 int i = PXERetry;
250 do {
251 dprintf("PXE: file = %p, retries left = %d: ", file, i);
252 __pxe_searchdir(filename, flags, file);
253 dprintf("%s\n", file->inode ? "ok" : "failed");
254 } while (!file->inode && i--);
256 static void __pxe_searchdir(const char *filename, int flags, struct file *file)
258 struct fs_info *fs = file->fs;
259 struct inode *inode;
260 char fullpath[2*FILENAME_MAX];
261 #if GPXE
262 char urlsave[2*FILENAME_MAX];
263 #endif
264 struct url_info url;
265 const struct url_scheme *us = NULL;
266 int redirect_count = 0;
267 bool found_scheme = false;
269 inode = file->inode = NULL;
271 while (filename) {
272 if (redirect_count++ > 5)
273 break;
275 strlcpy(fullpath, filename, sizeof fullpath);
276 #if GPXE
277 strcpy(urlsave, fullpath);
278 #endif
279 parse_url(&url, fullpath);
280 if (url.type == URL_SUFFIX) {
281 snprintf(fullpath, sizeof fullpath, "%s%s", fs->cwd_name, filename);
282 #if GPXE
283 strcpy(urlsave, fullpath);
284 #endif
285 parse_url(&url, fullpath);
288 inode = allocate_socket(fs);
289 if (!inode)
290 return; /* Allocation failure */
292 url_set_ip(&url);
294 filename = NULL;
295 found_scheme = false;
296 for (us = url_schemes; us->name; us++) {
297 if (!strcmp(us->name, url.scheme)) {
298 if ((flags & ~us->ok_flags & OK_FLAGS_MASK) == 0)
299 us->open(&url, flags, inode, &filename);
300 found_scheme = true;
301 break;
305 /* filename here is set on a redirect */
308 if (!found_scheme) {
309 #if GPXE
310 /* No URL scheme found, hand it to GPXE */
311 gpxe_open(inode, urlsave);
312 #endif
315 if (inode->size) {
316 file->inode = inode;
317 file->inode->mode = (flags & O_DIRECTORY) ? DT_DIR : DT_REG;
318 } else {
319 free_socket(inode);
322 return;
327 * Store standard filename prefix
329 static void get_prefix(void)
331 int len;
332 char *p;
333 char c;
335 if (!(DHCPMagic & 0x04)) {
336 /* No path prefix option, derive from boot file */
338 strlcpy(path_prefix, boot_file, sizeof path_prefix);
339 len = strlen(path_prefix);
340 p = &path_prefix[len - 1];
342 while (len--) {
343 c = *p--;
344 c |= 0x20;
346 c = (c >= '0' && c <= '9') ||
347 (c >= 'a' && c <= 'z') ||
348 (c == '.' || c == '-');
349 if (!c)
350 break;
353 if (len < 0)
354 p --;
356 *(p + 2) = 0; /* Zero-terminate after delimiter */
359 ddprintf("TFTP prefix: %s\n", path_prefix);
361 if (url_type(path_prefix) == URL_SUFFIX) {
363 * Construct a ::-style TFTP path.
365 * We may have moved out of the root directory at the time
366 * this function is invoked, but to maintain compatibility
367 * with versions of Syslinux < 5.00, path_prefix must be
368 * relative to "::".
370 p = strdup(path_prefix);
371 if (!p)
372 return;
374 snprintf(path_prefix, sizeof path_prefix, "::%s", p);
375 free(p);
378 chdir(path_prefix);
382 * realpath for PXE
384 static size_t pxe_realpath(struct fs_info *fs, char *dst, const char *src,
385 size_t bufsize)
387 return snprintf(dst, bufsize, "%s%s",
388 url_type(src) == URL_SUFFIX ? fs->cwd_name : "", src);
392 * chdir for PXE
394 static int pxe_chdir(struct fs_info *fs, const char *src)
396 /* The cwd for PXE is just a text prefix */
397 enum url_type path_type = url_type(src);
399 if (path_type == URL_SUFFIX)
400 strlcat(fs->cwd_name, src, sizeof fs->cwd_name);
401 else
402 strlcpy(fs->cwd_name, src, sizeof fs->cwd_name);
403 return 0;
405 dprintf("cwd = \"%s\"\n", fs->cwd_name);
406 return 0;
409 static int pxe_chdir_start(void)
411 get_prefix();
412 return 0;
415 /* Load the config file, return -1 if failed, or 0 */
416 static int pxe_open_config(struct com32_filedata *filedata)
418 const char *cfgprefix = "pxelinux.cfg/";
419 const char *default_str = "default";
420 char *config_file;
421 char *last;
422 int tries = 8;
424 chdir(path_prefix);
425 if (DHCPMagic & 0x02) {
426 /* We got a DHCP option, try it first */
427 if (open_file(ConfigName, O_RDONLY, filedata) >= 0)
428 return 0;
432 * Have to guess config file name ...
434 config_file = stpcpy(ConfigName, cfgprefix);
436 /* Try loading by UUID */
437 if (sysappend_strings[SYSAPPEND_SYSUUID]) {
438 strcpy(config_file, sysappend_strings[SYSAPPEND_SYSUUID]+8);
439 if (open_file(ConfigName, O_RDONLY, filedata) >= 0)
440 return 0;
443 /* Try loading by MAC address */
444 strcpy(config_file, sysappend_strings[SYSAPPEND_BOOTIF]+7);
445 if (open_file(ConfigName, O_RDONLY, filedata) >= 0)
446 return 0;
448 /* Nope, try hexadecimal IP prefixes... */
449 sprintf(config_file, "%08X", ntohl(IPInfo.myip));
450 last = &config_file[8];
451 while (tries) {
452 *last = '\0'; /* Zero-terminate string */
453 if (open_file(ConfigName, O_RDONLY, filedata) >= 0)
454 return 0;
455 last--; /* Drop one character */
456 tries--;
459 /* Final attempt: "default" string */
460 strcpy(config_file, default_str);
461 if (open_file(ConfigName, O_RDONLY, filedata) >= 0)
462 return 0;
464 ddprintf("%-68s\n", "Unable to locate configuration file");
465 kaboom();
469 * Generate the bootif string.
471 static void make_bootif_string(void)
473 static char bootif_str[7+3*(MAC_MAX+1)];
474 const uint8_t *src;
475 char *dst = bootif_str;
476 int i;
478 dst += sprintf(dst, "BOOTIF=%02x", MAC_type);
479 src = MAC;
480 for (i = MAC_len; i; i--)
481 dst += sprintf(dst, "-%02x", *src++);
483 sysappend_strings[SYSAPPEND_BOOTIF] = bootif_str;
487 * Generate an ip=<client-ip>:<boot-server-ip>:<gw-ip>:<netmask>
488 * option into IPOption based on DHCP information in IPInfo.
491 static void genipopt(void)
493 static char ip_option[3+4*16];
494 const uint32_t *v = &IPInfo.myip;
495 char *p;
496 int i;
498 p = stpcpy(ip_option, "ip=");
500 for (i = 0; i < 4; i++) {
501 p += gendotquad(p, *v++);
502 *p++ = ':';
504 *--p = '\0';
506 sysappend_strings[SYSAPPEND_IP] = ip_option;
510 /* Generate ip= option and print the ip adress */
511 static void ip_init(void)
513 uint32_t ip = IPInfo.myip;
514 char dot_quad_buf[16];
516 genipopt();
517 gendotquad(dot_quad_buf, ip);
519 ip = ntohl(ip);
520 ddprintf("My IP address seems to be %08X %s\n", ip, dot_quad_buf);
524 * Network-specific initialization
526 static void network_init(void)
528 net_parse_dhcp();
530 make_bootif_string();
531 /* If DMI and DHCP disagree, which one should we set? */
532 if (have_uuid)
533 sysappend_set_uuid(uuid);
534 ip_init();
536 /* print_sysappend(); */
538 * Check to see if we got any PXELINUX-specific DHCP options; in particular,
539 * if we didn't get the magic enable, do not recognize any other options.
541 if ((DHCPMagic & 1) == 0)
542 DHCPMagic = 0;
544 net_core_init();
548 * Initialize pxe fs
551 static int pxe_fs_init(struct fs_info *fs)
553 (void)fs; /* drop the compile warning message */
555 /* Prepare for handling pxe interrupts */
556 pxe_init_isr();
558 /* This block size is actually arbitrary... */
559 fs->sector_shift = fs->block_shift = TFTP_BLOCKSIZE_LG2;
560 fs->sector_size = fs->block_size = 1 << TFTP_BLOCKSIZE_LG2;
562 /* Find the PXE stack */
563 if (pxe_init(false))
564 kaboom();
566 /* See if we also have a gPXE stack */
567 gpxe_init();
569 /* Network-specific initialization */
570 network_init();
572 /* Initialize network-card-specific idle handling */
573 pxe_idle_init();
575 /* Our name for the root */
576 strcpy(fs->cwd_name, "::");
578 return 0;
582 * Look to see if we are on an EFI CSM system. Some EFI
583 * CSM systems put the BEV stack in low memory, which means
584 * a return to the PXE stack will crash the system. However,
585 * INT 18h works reliably, so in that case hack the stack and
586 * point the "return address" to an INT 18h instruction.
588 * Hack the stack instead of the much simpler "just invoke INT 18h
589 * if we want to reset", so that chainloading other NBPs will work.
591 * This manipulates the real-mode InitStack directly. It relies on this
592 * *not* being a currently active stack, i.e. the former
593 * USE_PXE_PROVIDED_STACK no longer works.
595 * XXX: Disable this until we can find a better way to discriminate
596 * between BIOSes that are broken on BEV return and BIOSes which are
597 * broken on INT 18h. Keying on the EFI CSM turns out to cause more
598 * problems than it solves.
600 extern far_ptr_t InitStack;
602 struct efi_struct {
603 uint32_t magic;
604 uint8_t csum;
605 uint8_t len;
606 } __attribute__((packed));
607 #define EFI_MAGIC (('$' << 24)+('E' << 16)+('F' << 8)+'I')
609 static inline bool is_efi(const struct efi_struct *efi)
612 * We don't verify the checksum, because it seems some CSMs leave
613 * it at zero, sigh...
615 return (efi->magic == EFI_MAGIC) && (efi->len >= 83);
618 #if 0
619 static void install_int18_hack(void)
621 static const uint8_t int18_hack[] =
623 0xcd, 0x18, /* int $0x18 */
624 0xea, 0xf0, 0xff, 0x00, 0xf0, /* ljmpw $0xf000,$0xfff0 */
625 0xf4 /* hlt */
627 uint16_t *retcode;
629 retcode = GET_PTR(*(far_ptr_t *)((char *)GET_PTR(InitStack) + 44));
631 /* Don't do this if the return already points to int $0x18 */
632 if (*retcode != 0x18cd) {
633 uint32_t efi_ptr;
634 bool efi = false;
636 for (efi_ptr = 0xe0000 ; efi_ptr < 0x100000 ; efi_ptr += 16) {
637 if (is_efi((const struct efi_struct *)efi_ptr)) {
638 efi = true;
639 break;
643 if (efi) {
644 uint8_t *src = GET_PTR(InitStack);
645 uint8_t *dst = src - sizeof int18_hack;
647 memmove(dst, src, 52);
648 memcpy(dst+52, int18_hack, sizeof int18_hack);
649 InitStack.offs -= sizeof int18_hack;
651 /* Clobber the return address */
652 *(uint16_t *)(dst+44) = OFFS_WRT(dst+52, InitStack.seg);
653 *(uint16_t *)(dst+46) = InitStack.seg;
657 #endif
659 static int pxe_readdir(struct file *file, struct dirent *dirent)
661 struct inode *inode = file->inode;
662 struct pxe_pvt_inode *socket = PVT(inode);
664 if (socket->ops->readdir)
665 return socket->ops->readdir(inode, dirent);
666 else
667 return -1; /* No such operation */
670 const struct fs_ops pxe_fs_ops = {
671 .fs_name = "pxe",
672 .fs_flags = FS_NODEV,
673 .fs_init = pxe_fs_init,
674 .searchdir = pxe_searchdir,
675 .chdir = pxe_chdir,
676 .realpath = pxe_realpath,
677 .getfssec = pxe_getfssec,
678 .close_file = pxe_close_file,
679 .mangle_name = pxe_mangle_name,
680 .chdir_start = pxe_chdir_start,
681 .open_config = pxe_open_config,
682 .readdir = pxe_readdir,
683 .fs_uuid = NULL,