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(®s, 0, sizeof regs); 118 regs.ebx.w[0] = opcode; 119 regs.es = SEG(data); 120 regs.edi.w[0] = OFFS(data); 121 call16(pxenv, ®s, ®s); 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