• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  */
allocate_socket(struct fs_info * fs)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 
free_socket(struct inode * inode)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 
pxe_close_file(struct file * file)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  */
ip_ok(uint32_t ip)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  */
gendotquad(char * dst,uint32_t ip)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  */
pxe_call(int opcode,void * data)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  */
pxe_mangle_name(char * dst,const char * src)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  */
pxe_getc(struct inode * inode)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  */
fill_buffer(struct inode * inode)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  */
pxe_getfssec(struct file * file,char * buf,int blocks,bool * have_more)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  */
url_set_ip(struct url_info * url)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 
pxe_searchdir(const char * filename,int flags,struct file * file)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 }
__pxe_searchdir(const char * filename,int flags,struct file * file)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  */
get_prefix(void)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  */
pxe_realpath(struct fs_info * fs,char * dst,const char * src,size_t bufsize)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  */
pxe_chdir(struct fs_info * fs,const char * src)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 
pxe_chdir_start(void)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 */
pxe_open_config(struct com32_filedata * filedata)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  */
make_bootif_string(void)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  */
genipopt(void)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 */
ip_init(void)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  */
network_init(void)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  */
pxe_fs_init(struct fs_info * fs)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 
is_efi(const struct efi_struct * efi)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 
pxe_readdir(struct file * file,struct dirent * dirent)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