Home | History | Annotate | Download | only in pxe
      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 <sys/cpu.h>
      8 #include "pxe.h"
      9 #include "thread.h"
     10 #include "url.h"
     11 #include "tftp.h"
     12 #include <net.h>
     13 
     14 __lowmem t_PXENV_UNDI_GET_INFORMATION pxe_undi_info;
     15 __lowmem t_PXENV_UNDI_GET_IFACE_INFO  pxe_undi_iface;
     16 
     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 */
     20 
     21 char boot_file[256];		   /* From DHCP */
     22 char path_prefix[256];		   /* From DHCP */
     23 
     24 bool have_uuid = false;
     25 
     26 /*
     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
     29  */
     30 static struct inode *allocate_socket(struct fs_info *fs)
     31 {
     32     struct inode *inode = alloc_inode(fs, 0, sizeof(struct pxe_pvt_inode));
     33 
     34     if (!inode) {
     35 	malloc_error("socket structure");
     36     } else {
     37 	inode->mode = DT_REG;	/* No other types relevant for PXE */
     38     }
     39 
     40     return inode;
     41 }
     42 
     43 void free_socket(struct inode *inode)
     44 {
     45     struct pxe_pvt_inode *socket = PVT(inode);
     46 
     47     free(socket->tftp_pktbuf);	/* If we allocated a buffer, free it now */
     48     free_inode(inode);
     49 }
     50 
     51 static void pxe_close_file(struct file *file)
     52 {
     53     struct inode *inode = file->inode;
     54     struct pxe_pvt_inode *socket = PVT(inode);
     55 
     56     if (!inode)
     57 	return;
     58 
     59     if (!socket->tftp_goteof) {
     60 	socket->ops->close(inode);
     61     }
     62 
     63     free_socket(inode);
     64 }
     65 
     66 /*
     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.
     70  *
     71  */
     72 bool ip_ok(uint32_t ip)
     73 {
     74     uint8_t ip_hi = (uint8_t)ip; /* First octet of the ip address */
     75 
     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;
     81 
     82     return true;
     83 }
     84 
     85 
     86 /*
     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.
     90  *
     91  */
     92 static int gendotquad(char *dst, uint32_t ip)
     93 {
     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]);
     99 }
    100 
    101 /*
    102  * the ASM pxenv function wrapper, return 1 if error, or 0
    103  *
    104  */
    105 __export int pxe_call(int opcode, void *data)
    106 {
    107     static DECLARE_INIT_SEMAPHORE(pxe_sem, 1);
    108     extern void pxenv(void);
    109     com32sys_t regs;
    110 
    111     sem_down(&pxe_sem, 0);
    112 
    113 #if 0
    114     dprintf("pxe_call op %04x data %p\n", opcode, data);
    115 #endif
    116 
    117     memset(&regs, 0, sizeof regs);
    118     regs.ebx.w[0] = opcode;
    119     regs.es       = SEG(data);
    120     regs.edi.w[0] = OFFS(data);
    121     call16(pxenv, &regs, &regs);
    122 
    123     sem_up(&pxe_sem);
    124 
    125     return regs.eflags.l & EFLAGS_CF;  /* CF SET if fail */
    126 }
    127 
    128 /*
    129  * mangle a filename pointed to by _src_ into a buffer pointed
    130  * to by _dst_; ends on encountering any whitespace.
    131  *
    132  * This deliberately does not attempt to do any conversion of
    133  * pathname separators.
    134  *
    135  */
    136 static void pxe_mangle_name(char *dst, const char *src)
    137 {
    138     size_t len = FILENAME_MAX-1;
    139 
    140     while (len-- && not_whitespace(*src))
    141 	*dst++ = *src++;
    142 
    143     *dst = '\0';
    144 }
    145 
    146 /*
    147  * Read a single character from the specified pxe inode.
    148  * Very useful for stepping through http streams and
    149  * parsing their headers.
    150  */
    151 int pxe_getc(struct inode *inode)
    152 {
    153     struct pxe_pvt_inode *socket = PVT(inode);
    154     unsigned char byte;
    155 
    156     while (!socket->tftp_bytesleft) {
    157 	if (socket->tftp_goteof)
    158 	    return -1;
    159 
    160 	socket->ops->fill_buffer(inode);
    161     }
    162 
    163     byte = *socket->tftp_dataptr;
    164     socket->tftp_bytesleft -= 1;
    165     socket->tftp_dataptr += 1;
    166 
    167     return byte;
    168 }
    169 
    170 /*
    171  * Get a fresh packet if the buffer is drained, and we haven't hit
    172  * EOF yet.  The buffer should be filled immediately after draining!
    173  */
    174 static void fill_buffer(struct inode *inode)
    175 {
    176     struct pxe_pvt_inode *socket = PVT(inode);
    177     if (socket->tftp_bytesleft || socket->tftp_goteof)
    178         return;
    179 
    180     return socket->ops->fill_buffer(inode);
    181 }
    182 
    183 
    184 /**
    185  * getfssec: Get multiple clusters from a file, given the starting cluster.
    186  * In this case, get multiple blocks from a specific TCP connection.
    187  *
    188  * @param: fs, the fs_info structure address, in pxe, we don't use this.
    189  * @param: buf, buffer to store the read data
    190  * @param: openfile, TFTP socket pointer
    191  * @param: blocks, 512-byte block count; 0FFFFh = until end of file
    192  *
    193  * @return: the bytes read
    194  *
    195  */
    196 static uint32_t pxe_getfssec(struct file *file, char *buf,
    197 			     int blocks, bool *have_more)
    198 {
    199     struct inode *inode = file->inode;
    200     struct pxe_pvt_inode *socket = PVT(inode);
    201     int count = blocks;
    202     int chunk;
    203     int bytes_read = 0;
    204 
    205     count <<= TFTP_BLOCKSIZE_LG2;
    206     while (count) {
    207         fill_buffer(inode); /* If we have no 'fresh' buffer, get it */
    208         if (!socket->tftp_bytesleft)
    209             break;
    210 
    211         chunk = count;
    212         if (chunk > socket->tftp_bytesleft)
    213             chunk = socket->tftp_bytesleft;
    214         socket->tftp_bytesleft -= chunk;
    215         memcpy(buf, socket->tftp_dataptr, chunk);
    216 	socket->tftp_dataptr += chunk;
    217         buf += chunk;
    218         bytes_read += chunk;
    219         count -= chunk;
    220     }
    221 
    222 
    223     if (socket->tftp_bytesleft || (socket->tftp_filepos < inode->size)) {
    224 	fill_buffer(inode);
    225         *have_more = 1;
    226     } else if (socket->tftp_goteof) {
    227         /*
    228          * The socket is closed and the buffer drained; the caller will
    229 	 * call close_file and therefore free the socket.
    230          */
    231         *have_more = 0;
    232     }
    233 
    234     return bytes_read;
    235 }
    236 
    237 /*
    238  * Assign an IP address to a URL
    239  */
    240 static void url_set_ip(struct url_info *url)
    241 {
    242     url->ip = 0;
    243     if (url->host)
    244 	url->ip = dns_resolv(url->host);
    245     if (!url->ip)
    246 	url->ip = IPInfo.serverip;
    247 }
    248 
    249 /**
    250  * Open the specified connection
    251  *
    252  * @param:filename, the file we wanna open
    253  *
    254  * @out: open_file_t structure, stores in file->open_file
    255  * @out: the lenght of this file, stores in file->file_len
    256  *
    257  */
    258 static void __pxe_searchdir(const char *filename, int flags, struct file *file);
    259 extern uint16_t PXERetry;
    260 
    261 static void pxe_searchdir(const char *filename, int flags, struct file *file)
    262 {
    263     int i = PXERetry;
    264 
    265     do {
    266 	dprintf("PXE: file = %p, retries left = %d: ", file, i);
    267 	__pxe_searchdir(filename, flags, file);
    268 	dprintf("%s\n", file->inode ? "ok" : "failed");
    269     } while (!file->inode && i--);
    270 }
    271 static void __pxe_searchdir(const char *filename, int flags, struct file *file)
    272 {
    273     struct fs_info *fs = file->fs;
    274     struct inode *inode;
    275     char fullpath[2*FILENAME_MAX];
    276 #if GPXE
    277     char urlsave[2*FILENAME_MAX];
    278 #endif
    279     struct url_info url;
    280     const struct url_scheme *us = NULL;
    281     int redirect_count = 0;
    282     bool found_scheme = false;
    283 
    284     inode = file->inode = NULL;
    285 
    286     while (filename) {
    287 	if (redirect_count++ > 5)
    288 	    break;
    289 
    290 	strlcpy(fullpath, filename, sizeof fullpath);
    291 #if GPXE
    292 	strcpy(urlsave, fullpath);
    293 #endif
    294 	parse_url(&url, fullpath);
    295 	if (url.type == URL_SUFFIX) {
    296 	    snprintf(fullpath, sizeof fullpath, "%s%s", fs->cwd_name, filename);
    297 #if GPXE
    298 	    strcpy(urlsave, fullpath);
    299 #endif
    300 	    parse_url(&url, fullpath);
    301 	}
    302 
    303 	inode = allocate_socket(fs);
    304 	if (!inode)
    305 	    return;			/* Allocation failure */
    306 
    307 	url_set_ip(&url);
    308 
    309 	filename = NULL;
    310 	found_scheme = false;
    311 	for (us = url_schemes; us->name; us++) {
    312 	    if (!strcmp(us->name, url.scheme)) {
    313 		if ((flags & ~us->ok_flags & OK_FLAGS_MASK) == 0)
    314 		    us->open(&url, flags, inode, &filename);
    315 		found_scheme = true;
    316 		break;
    317 	    }
    318 	}
    319 
    320 	/* filename here is set on a redirect */
    321     }
    322 
    323     if (!found_scheme) {
    324 #if GPXE
    325 	/* No URL scheme found, hand it to GPXE */
    326 	gpxe_open(inode, urlsave);
    327 #endif
    328     }
    329 
    330     if (inode->size) {
    331 	file->inode = inode;
    332 	file->inode->mode = (flags & O_DIRECTORY) ? DT_DIR : DT_REG;
    333     } else {
    334         free_socket(inode);
    335     }
    336 
    337     return;
    338 }
    339 
    340 
    341 /*
    342  * Store standard filename prefix
    343  */
    344 static void get_prefix(void)
    345 {
    346     int len;
    347     char *p;
    348     char c;
    349 
    350     if (!(DHCPMagic & 0x04)) {
    351 	/* No path prefix option, derive from boot file */
    352 
    353 	strlcpy(path_prefix, boot_file, sizeof path_prefix);
    354 	len = strlen(path_prefix);
    355 	p = &path_prefix[len - 1];
    356 
    357 	while (len--) {
    358 	    c = *p--;
    359 	    c |= 0x20;
    360 
    361 	    c = (c >= '0' && c <= '9') ||
    362 		(c >= 'a' && c <= 'z') ||
    363 		(c == '.' || c == '-');
    364 	    if (!c)
    365 		break;
    366 	};
    367 
    368 	if (len < 0)
    369 	    p --;
    370 
    371 	*(p + 2) = 0;                /* Zero-terminate after delimiter */
    372     }
    373 
    374     ddprintf("TFTP prefix: %s\n", path_prefix);
    375 
    376     if (url_type(path_prefix) == URL_SUFFIX) {
    377 	/*
    378 	 * Construct a ::-style TFTP path.
    379 	 *
    380 	 * We may have moved out of the root directory at the time
    381 	 * this function is invoked, but to maintain compatibility
    382 	 * with versions of Syslinux < 5.00, path_prefix must be
    383 	 * relative to "::".
    384 	 */
    385 	p = strdup(path_prefix);
    386 	if (!p)
    387 	    return;
    388 
    389 	snprintf(path_prefix, sizeof path_prefix, "::%s", p);
    390 	free(p);
    391     }
    392 
    393     chdir(path_prefix);
    394 }
    395 
    396 /*
    397  * realpath for PXE
    398  */
    399 static size_t pxe_realpath(struct fs_info *fs, char *dst, const char *src,
    400 			   size_t bufsize)
    401 {
    402     return snprintf(dst, bufsize, "%s%s",
    403 		    url_type(src) == URL_SUFFIX ? fs->cwd_name : "", src);
    404 }
    405 
    406 /*
    407  * chdir for PXE
    408  */
    409 static int pxe_chdir(struct fs_info *fs, const char *src)
    410 {
    411     /* The cwd for PXE is just a text prefix */
    412     enum url_type path_type = url_type(src);
    413 
    414     if (path_type == URL_SUFFIX)
    415 	strlcat(fs->cwd_name, src, sizeof fs->cwd_name);
    416     else
    417 	strlcpy(fs->cwd_name, src, sizeof fs->cwd_name);
    418     return 0;
    419 
    420     dprintf("cwd = \"%s\"\n", fs->cwd_name);
    421     return 0;
    422 }
    423 
    424 static int pxe_chdir_start(void)
    425 {
    426 	get_prefix();
    427 	return 0;
    428 }
    429 
    430 /* Load the config file, return -1 if failed, or 0 */
    431 static int pxe_open_config(struct com32_filedata *filedata)
    432 {
    433     const char *cfgprefix = "pxelinux.cfg/";
    434     const char *default_str = "default";
    435     char *config_file;
    436     char *last;
    437     int tries = 8;
    438 
    439     chdir(path_prefix);
    440     if (DHCPMagic & 0x02) {
    441         /* We got a DHCP option, try it first */
    442 	if (open_file(ConfigName, O_RDONLY, filedata) >= 0)
    443 	    return 0;
    444     }
    445 
    446     /*
    447      * Have to guess config file name ...
    448      */
    449     config_file = stpcpy(ConfigName, cfgprefix);
    450 
    451     /* Try loading by UUID */
    452     if (sysappend_strings[SYSAPPEND_SYSUUID]) {
    453 	strcpy(config_file, sysappend_strings[SYSAPPEND_SYSUUID]+8);
    454 	if (open_file(ConfigName, O_RDONLY, filedata) >= 0)
    455             return 0;
    456     }
    457 
    458     /* Try loading by MAC address */
    459     strcpy(config_file, sysappend_strings[SYSAPPEND_BOOTIF]+7);
    460     if (open_file(ConfigName, O_RDONLY, filedata) >= 0)
    461         return 0;
    462 
    463     /* Nope, try hexadecimal IP prefixes... */
    464     sprintf(config_file, "%08X", ntohl(IPInfo.myip));
    465     last = &config_file[8];
    466     while (tries) {
    467         *last = '\0';        /* Zero-terminate string */
    468 	if (open_file(ConfigName, O_RDONLY, filedata) >= 0)
    469             return 0;
    470         last--;           /* Drop one character */
    471         tries--;
    472     };
    473 
    474     /* Final attempt: "default" string */
    475     strcpy(config_file, default_str);
    476     if (open_file(ConfigName, O_RDONLY, filedata) >= 0)
    477         return 0;
    478 
    479     ddprintf("%-68s\n", "Unable to locate configuration file");
    480     kaboom();
    481 }
    482 
    483 /*
    484  * Generate the bootif string.
    485  */
    486 static void make_bootif_string(void)
    487 {
    488     static char bootif_str[7+3*(MAC_MAX+1)];
    489     const uint8_t *src;
    490     char *dst = bootif_str;
    491     int i;
    492 
    493     dst += sprintf(dst, "BOOTIF=%02x", MAC_type);
    494     src = MAC;
    495     for (i = MAC_len; i; i--)
    496 	dst += sprintf(dst, "-%02x", *src++);
    497 
    498     sysappend_strings[SYSAPPEND_BOOTIF] = bootif_str;
    499 }
    500 
    501 /*
    502  * Generate an ip=<client-ip>:<boot-server-ip>:<gw-ip>:<netmask>
    503  * option into IPOption based on DHCP information in IPInfo.
    504  *
    505  */
    506 static void genipopt(void)
    507 {
    508     static char ip_option[3+4*16];
    509     const uint32_t *v = &IPInfo.myip;
    510     char *p;
    511     int i;
    512 
    513     p = stpcpy(ip_option, "ip=");
    514 
    515     for (i = 0; i < 4; i++) {
    516 	p += gendotquad(p, *v++);
    517 	*p++ = ':';
    518     }
    519     *--p = '\0';
    520 
    521     sysappend_strings[SYSAPPEND_IP] = ip_option;
    522 }
    523 
    524 
    525 /* Generate ip= option and print the ip adress */
    526 static void ip_init(void)
    527 {
    528     uint32_t ip = IPInfo.myip;
    529     char dot_quad_buf[16];
    530 
    531     genipopt();
    532     gendotquad(dot_quad_buf, ip);
    533 
    534     ip = ntohl(ip);
    535     ddprintf("My IP address seems to be %08X %s\n", ip, dot_quad_buf);
    536 }
    537 
    538 /*
    539  * Network-specific initialization
    540  */
    541 static void network_init(void)
    542 {
    543     net_parse_dhcp();
    544 
    545     make_bootif_string();
    546     /* If DMI and DHCP disagree, which one should we set? */
    547     if (have_uuid)
    548 	sysappend_set_uuid(uuid);
    549     ip_init();
    550 
    551     /* print_sysappend(); */
    552     /*
    553      * Check to see if we got any PXELINUX-specific DHCP options; in particular,
    554      * if we didn't get the magic enable, do not recognize any other options.
    555      */
    556     if ((DHCPMagic & 1) == 0)
    557         DHCPMagic = 0;
    558 
    559     net_core_init();
    560 }
    561 
    562 /*
    563  * Initialize pxe fs
    564  *
    565  */
    566 static int pxe_fs_init(struct fs_info *fs)
    567 {
    568     (void)fs;    /* drop the compile warning message */
    569 
    570     /* Prepare for handling pxe interrupts */
    571     pxe_init_isr();
    572 
    573     /* This block size is actually arbitrary... */
    574     fs->sector_shift = fs->block_shift = TFTP_BLOCKSIZE_LG2;
    575     fs->sector_size  = fs->block_size  = 1 << TFTP_BLOCKSIZE_LG2;
    576 
    577     /* Find the PXE stack */
    578     if (pxe_init(false))
    579 	kaboom();
    580 
    581     /* See if we also have a gPXE stack */
    582     gpxe_init();
    583 
    584     /* Network-specific initialization */
    585     network_init();
    586 
    587     /* Initialize network-card-specific idle handling */
    588     pxe_idle_init();
    589 
    590     /* Our name for the root */
    591     strcpy(fs->cwd_name, "::");
    592 
    593     return 0;
    594 }
    595 
    596 /*
    597  * Look to see if we are on an EFI CSM system.  Some EFI
    598  * CSM systems put the BEV stack in low memory, which means
    599  * a return to the PXE stack will crash the system.  However,
    600  * INT 18h works reliably, so in that case hack the stack and
    601  * point the "return address" to an INT 18h instruction.
    602  *
    603  * Hack the stack instead of the much simpler "just invoke INT 18h
    604  * if we want to reset", so that chainloading other NBPs will work.
    605  *
    606  * This manipulates the real-mode InitStack directly.  It relies on this
    607  * *not* being a currently active stack, i.e. the former
    608  * USE_PXE_PROVIDED_STACK no longer works.
    609  *
    610  * XXX: Disable this until we can find a better way to discriminate
    611  * between BIOSes that are broken on BEV return and BIOSes which are
    612  * broken on INT 18h.  Keying on the EFI CSM turns out to cause more
    613  * problems than it solves.
    614  */
    615 extern far_ptr_t InitStack;
    616 
    617 struct efi_struct {
    618     uint32_t magic;
    619     uint8_t  csum;
    620     uint8_t  len;
    621 } __attribute__((packed));
    622 #define EFI_MAGIC (('$' << 24)+('E' << 16)+('F' << 8)+'I')
    623 
    624 static inline bool is_efi(const struct efi_struct *efi)
    625 {
    626     /*
    627      * We don't verify the checksum, because it seems some CSMs leave
    628      * it at zero, sigh...
    629      */
    630     return (efi->magic == EFI_MAGIC) && (efi->len >= 83);
    631 }
    632 
    633 #if 0
    634 static void install_int18_hack(void)
    635 {
    636     static const uint8_t int18_hack[] =
    637     {
    638 	0xcd, 0x18,			/* int $0x18 */
    639 	0xea, 0xf0, 0xff, 0x00, 0xf0,	/* ljmpw $0xf000,$0xfff0 */
    640 	0xf4				/* hlt */
    641     };
    642     uint16_t *retcode;
    643 
    644     retcode = GET_PTR(*(far_ptr_t *)((char *)GET_PTR(InitStack) + 44));
    645 
    646     /* Don't do this if the return already points to int $0x18 */
    647     if (*retcode != 0x18cd) {
    648 	uint32_t efi_ptr;
    649 	bool efi = false;
    650 
    651 	for (efi_ptr = 0xe0000 ; efi_ptr < 0x100000 ; efi_ptr += 16) {
    652 	    if (is_efi((const struct efi_struct *)efi_ptr)) {
    653 		efi = true;
    654 		break;
    655 	    }
    656 	}
    657 
    658 	if (efi) {
    659 	    uint8_t *src = GET_PTR(InitStack);
    660 	    uint8_t *dst = src - sizeof int18_hack;
    661 
    662 	    memmove(dst, src, 52);
    663 	    memcpy(dst+52, int18_hack, sizeof int18_hack);
    664 	    InitStack.offs -= sizeof int18_hack;
    665 
    666 	    /* Clobber the return address */
    667 	    *(uint16_t *)(dst+44) = OFFS_WRT(dst+52, InitStack.seg);
    668 	    *(uint16_t *)(dst+46) = InitStack.seg;
    669 	}
    670     }
    671 }
    672 #endif
    673 
    674 static int pxe_readdir(struct file *file, struct dirent *dirent)
    675 {
    676     struct inode *inode = file->inode;
    677     struct pxe_pvt_inode *socket = PVT(inode);
    678 
    679     if (socket->ops->readdir)
    680 	return socket->ops->readdir(inode, dirent);
    681     else
    682 	return -1;		/* No such operation */
    683 }
    684 
    685 const struct fs_ops pxe_fs_ops = {
    686     .fs_name       = "pxe",
    687     .fs_flags      = FS_NODEV,
    688     .fs_init       = pxe_fs_init,
    689     .searchdir     = pxe_searchdir,
    690     .chdir         = pxe_chdir,
    691     .realpath      = pxe_realpath,
    692     .getfssec      = pxe_getfssec,
    693     .close_file    = pxe_close_file,
    694     .mangle_name   = pxe_mangle_name,
    695     .chdir_start   = pxe_chdir_start,
    696     .open_config   = pxe_open_config,
    697     .readdir	   = pxe_readdir,
    698     .fs_uuid       = NULL,
    699 };
    700