1 /* ----------------------------------------------------------------------- *
3 * Copyright 2010-2012 Gene Cumm - All Rights Reserved
5 * Portions from chain.c:
6 * Copyright 2003-2009 H. Peter Anvin - All Rights Reserved
7 * Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin
8 * Significant portions copyright (C) 2010 Shao Miller
9 * [partition iteration, GPT, "fs"]
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation, Inc., 53 Temple Place Ste 330,
14 * Boston MA 02111-1307, USA; either version 2 of the License, or
15 * (at your option) any later version; incorporated herein by reference.
17 * ----------------------------------------------------------------------- */
22 * PXE Chain Loader; Chain load to another PXE network boot program
23 * that may be on another host.
32 #include <syslinux/config.h>
33 #include <syslinux/loadfile.h>
34 #include <syslinux/bootrm.h>
35 #include <syslinux/video.h>
38 #include <syslinux/pxe.h>
47 # define PXECHN_DEBUG 1
49 # define PXECHN_DEBUG 0
59 #define dprintf0(f, ...) ((void)0)
62 # if (PXECHN_DEBUG > 0)
63 # define dprintf printf
65 # define dprintf(f, ...) ((void)0)
69 #if (PXECHN_DEBUG > 0)
70 # define dpressanykey pressanykey
71 # define dprint_pxe_bootp_t print_pxe_bootp_t
72 # define dprint_pxe_vendor_blk print_pxe_vendor_blk
73 # define dprint_pxe_vendor_raw print_pxe_vendor_raw
75 # define dpressanykey(tm) ((void)0)
76 # define dprint_pxe_bootp_t(p, l) ((void)0)
77 # define dprint_pxe_vendor_blk(p, l) ((void)0)
78 # define dprint_pxe_vendor_raw(p, l) ((void)0)
81 #define dprintf_opt_cp dprintf0
82 #define dprintf_opt_inj dprintf0
83 #define dprintf_pc_pa dprintf
84 #define dprintf_pc_so_s dprintf0
86 #define t_PXENV_RESTART_TFTP t_PXENV_TFTP_READ_FILE
88 #define STACK_SPLIT 11
90 /* same as pxelinux.asm REBOOT_TIME */
91 #define REBOOT_TIME 300
93 #define NUM_DHCP_OPTS 256
94 #define DHCP_OPT_LEN_MAX 256
95 #define PXE_VENDOR_RAW_PRN_MAX 0x7F
96 #define PXECHN_HOST_LEN 256 /* 63 bytes per label; 255 max total */
98 #define PXECHN_NUM_PKT_TYPE 3
99 #define PXECHN_NUM_PKT_AVAIL 2*PXECHN_NUM_PKT_TYPE
100 #define PXECHN_PKT_TYPE_START PXENV_PACKET_TYPE_DHCP_DISCOVER
102 #define PXECHN_FORCE_PKT1 0x80000000
103 #define PXECHN_FORCE_PKT2 0x40000000
104 #define PXECHN_FORCE_ALL (PXECHN_FORCE_PKT1 | PXECHN_FORCE_PKT2)
105 #define PXECHN_FORCE_ALL_1 0
106 #define STRASINT_str ('s' + (('t' + ('r' << 8)) << 8))
108 #define min(a,b) (((a) < (b)) ? (a) : (b))
110 const char app_name_str[] = "pxechn.c32";
112 struct pxelinux_opt {
113 char *fn; /* Filename as passed to us */
114 in_addr_t fip; /* fn's IP component */
115 char *fp; /* fn's path component */
116 in_addr_t gip; /* giaddr; Gateway/DHCP relay */
118 uint32_t wait; /* Additional decision to wait before boot */
119 int32_t wds; /* WDS option/level */
120 struct dhcp_option p[PXECHN_NUM_PKT_AVAIL];
121 /* original _DHCP_DISCOVER, _DHCP_ACK, _CACHED_REPLY then modified packets */
122 char host[PXECHN_HOST_LEN];
123 struct dhcp_option opts[PXECHN_NUM_PKT_TYPE][NUM_DHCP_OPTS];
124 char p_unpacked[PXECHN_NUM_PKT_TYPE];
136 static inline void error(const char *msg)
142 static void do_boot(struct data_area *data, int ndata,
143 struct syslinux_rm_regs *regs)
145 uint16_t *const bios_fbm = (uint16_t *) 0x413;
146 addr_t dosmem = *bios_fbm << 10; /* Technically a low bound */
147 struct syslinux_memmap *mmap;
148 struct syslinux_movelist *mlist = NULL;
152 mmap = syslinux_memory_map();
155 error("Cannot read system memory map\n");
160 for (i = 0; i < ndata; i++) {
161 if (data[i].base + data[i].size > endimage)
162 endimage = data[i].base + data[i].size;
164 if (endimage > dosmem)
167 for (i = 0; i < ndata; i++) {
168 if (syslinux_add_movelist(&mlist, data[i].base,
169 (addr_t) data[i].data, data[i].size))
174 /* Tell the shuffler not to muck with this area... */
175 syslinux_add_memmap(&mmap, endimage, 0xa0000 - endimage, SMT_RESERVED);
177 /* Force text mode */
178 syslinux_force_text_mode();
180 fputs("Booting...\n", stdout);
181 syslinux_shuffle_boot_rm(mlist, mmap, 3, regs);
182 error("Chainboot failed!\n");
186 error("Loader file too large\n");
190 error("Out of memory\n");
197 " %s [OPTIONS]... _new-nbp_\n"
198 " %s -r _new-nbp_ (calls PXE stack PXENV_RESTART_TFTP)\n"
200 " [-c config] [-g gateway] [-p prefix] [-t reboot] [-u] [-w] [-W]"
201 " [-o opt.ty=val]\n\n",
202 app_name_str, app_name_str);
205 void pxe_error(int ierr, const char *evt, const char *msg)
210 printf("Error while %s: ", evt);
211 printf("%d:%s\n", ierr, strerror(ierr));
214 int pressanykey(clock_t tm) {
217 printf("Press any key to continue. ");
218 inc = get_key(stdin, tm);
223 int dhcp_find_opt(pxe_bootp_t *p, size_t len, uint8_t opt)
231 dprintf(" packet pointer is null\n");
234 vlen = len - ((void *)&(p->vendor) - (void *)p);
236 magic = ntohl(*((uint32_t *)d));
237 if (magic != VM_RFC1048) /* Invalid DHCP packet */
239 for (i = 4; i < vlen; i++) {
241 dprintf("\n @%03X-%2d\n", i, d[i]);
245 if (d[i] == ((NUM_DHCP_OPTS) - 1)) /* End of list */
247 if (d[i]) { /* Skip padding */
255 void print_pxe_vendor_raw(pxe_bootp_t *p, size_t len)
260 printf(" packet pointer is null\n");
263 vlen = len - ((void *)&(p->vendor) - (void *)p);
264 if (vlen > PXE_VENDOR_RAW_PRN_MAX)
265 vlen = PXE_VENDOR_RAW_PRN_MAX;
266 dprintf(" rawLen = %d", vlen);
267 for (i = 0; i < vlen; i++) {
269 printf("\n %04X:", i);
270 printf(" %02X", p->vendor.d[i]);
275 void print_pxe_vendor_blk(pxe_bootp_t *p, size_t len)
277 int i, vlen, oplen, j;
281 printf(" packet pointer is null\n");
284 vlen = len - ((void *)&(p->vendor) - (void *)p);
285 printf(" Vendor Data: Len=%d", vlen);
287 magic = ntohl(*((uint32_t *)d));
288 printf(" Magic: %08X", ntohl(*((uint32_t *)d)));
289 if (magic != VM_RFC1048) /* Invalid DHCP packet */
291 for (i = 4; i < vlen; i++) {
292 if (d[i]) /* Skip the padding */
293 printf("\n @%03X-%3d", i, d[i]);
294 if (d[i] == ((NUM_DHCP_OPTS) - 1)) /* End of list */
298 printf(" l=%3d:", oplen);
299 for (j = (++i + oplen); i < vlen && i < j; i++) {
300 printf(" %02X", d[i]);
308 void print_pxe_bootp_t(pxe_bootp_t *p, size_t len)
310 if (!p || len <= 0) {
311 printf(" packet pointer is null\n");
314 printf(" op:%02X hw:%02X hl:%02X gh:%02X id:%08X se:%04X f:%04X"
315 " cip:%08X\n", p->opcode, p->Hardware, p->Hardlen, p->Gatehops,
316 ntohl(p->ident), ntohs(p->seconds), ntohs(p->Flags), ntohl(p->cip));
317 printf(" yip:%08X sip:%08X gip:%08X",
318 ntohl(p->yip), ntohl(p->sip), ntohl(p->gip));
319 printf(" caddr-%02X:%02X:%02X:%02X:%02X:%02X\n", p->CAddr[0],
320 p->CAddr[1], p->CAddr[2], p->CAddr[3], p->CAddr[4], p->CAddr[5]);
321 printf(" sName: '%s'\n", p->Sname);
322 printf(" bootfile: '%s'\n", p->bootfile);
323 dprint_pxe_vendor_blk(p, len);
326 void pxe_set_regs(struct syslinux_rm_regs *regs)
331 /* Plan A uses SS:[SP + 4] */
332 /* sdi->pxe.stack is a usable pointer, not something that can be nicely
333 and reliably split to SS:SP without causing issues */
334 tregs.eax.l = 0x000A;
335 __intcall(0x22, &tregs, &tregs);
337 regs->esp.l = tregs.esi.w[0] + sizeof(tregs);
338 /* Plan B uses [ES:BX] */
340 regs->ebx = tregs.ebx;
341 dprintf("\nsp:%04x ss:%04x es:%04x bx:%04x\n", regs->esp.w[0],
342 regs->ss, regs->es, regs->ebx.w[0]);
343 /* Zero out everything else just to be sure */
344 regs->cs = regs->ds = regs->fs = regs->gs = 0;
345 regs->eax.l = regs->ecx.l = regs->edx.l = 0;
348 int hostlen_limit(int len)
350 return min(len, ((PXECHN_HOST_LEN) - 1));
353 //FIXME: To a library
354 /* Parse a filename into an IPv4 address and filename pointer
355 * returns Based on the interpretation of fn
356 * 0 regular file name
362 * -1 if fn is another URL type
364 int pxechn_parse_fn(char fn[], in_addr_t *fip, char *host, char *fp[])
367 char *csep, *ssep, *hsep; /* Colon, Slash separator positions */
368 int hlen, plen; /* Hostname, protocol length */
371 csep = strchr(fn, ':');
373 if (csep[1] == ':') { /* assume IP::FN */
377 hlen = hostlen_limit(csep - fn);
378 memcpy(host, fn, hlen);
381 } else if ((csep[1] == '/') && (csep[2] == '/')) {
382 /* URL: proto://host:port/path/file */
383 /* proto://[user[:passwd]@]host[:port]/path/file */
384 ssep = strchr(csep + 3, '/');
386 hlen = hostlen_limit(ssep - (csep + 3));
389 hlen = hostlen_limit(strlen(csep + 3));
391 memcpy(host, (csep + 3), hlen);
394 if (strncmp(fn, "tftp", plen) == 0)
396 else if (strncmp(fn, "http", plen) == 0)
398 else if (strncmp(fn, "ftp", plen) == 0)
400 else if (strncmp(fn, "https", plen) == 0)
401 rv = 3 + ( 1 << 30 );
412 hsep = strchr(host, '@');
419 dprintf0(" host '%s'\n fp '%s'\n fip %08x\n", host, *fp, ntohl(*fip));
423 void pxechn_opt_free(struct dhcp_option *opt)
429 void pxechn_fill_pkt(struct pxelinux_opt *pxe, int ptype)
433 if ((ptype < 0) || (ptype > PXECHN_NUM_PKT_TYPE))
435 p1 = ptype - PXECHN_PKT_TYPE_START;
436 p2 = p1 + PXECHN_NUM_PKT_TYPE;
437 if ((rv >= -1) && (!pxe_get_cached_info(ptype,
438 (void **)&(pxe->p[p1].data), (size_t *)&(pxe->p[p1].len)))) {
439 pxe->p[p2].data = malloc(2048);
440 if (pxe->p[p2].data) {
441 memcpy(pxe->p[p2].data, pxe->p[p1].data, pxe->p[p1].len);
442 pxe->p[p2].len = pxe->p[p1].len;
444 dprint_pxe_bootp_t((pxe_bootp_t *)(pxe->p[p1].data), pxe->p[p1].len);
445 dpressanykey(INT_MAX);
447 printf("%s: ERROR: Unable to malloc() for second packet\n", app_name_str);
450 printf("%s: ERROR: Unable to retrieve first packet\n", app_name_str);
453 pxechn_opt_free(&pxe->p[p1]);
457 void pxechn_init(struct pxelinux_opt *pxe)
459 /* Init for paranoia */
467 pxe->host[((NUM_DHCP_OPTS) - 1)] = 0;
468 for (int j = 0; j < PXECHN_NUM_PKT_TYPE; j++){
469 for (int i = 0; i < NUM_DHCP_OPTS; i++) {
470 pxe->opts[j][i].data = NULL;
471 pxe->opts[j][i].len = -1;
473 pxe->p_unpacked[j] = 0;
474 pxe->p[j].data = NULL;
475 pxe->p[j+PXECHN_NUM_PKT_TYPE].data = NULL;
477 pxe->p[j+PXECHN_NUM_PKT_TYPE].len = 0;
479 pxechn_fill_pkt(pxe, PXENV_PACKET_TYPE_CACHED_REPLY);
482 int pxechn_to_hex(char i)
484 if (i >= '0' && i <= '9')
486 if (i >= 'A' && i <= 'F')
487 return (i - 'A' + 10);
488 if (i >= 'a' && i <= 'f')
489 return (i - 'a' + 10);
495 int pxechn_parse_2bhex(char ins[])
498 int n0 = -3, n1 = -3;
502 /* pxechn_to_hex can handle the NULL character by returning -1 and
503 breaking the execution of the statement chain */
504 } else if (((n0 = pxechn_to_hex(ins[0])) >= 0)
505 && ((n1 = pxechn_to_hex(ins[1])) >= 0)) {
506 ret = (n0 * 16) + n1;
507 } else if (n0 == -1) { /* Leading NULL char */
513 int pxechn_optnum_ok(int optnum)
515 if ((optnum > 0) && (optnum < ((NUM_DHCP_OPTS) - 1)))
520 int pxechn_optnum_ok_notres(int optnum)
522 if ((optnum <= 0) && (optnum >= ((NUM_DHCP_OPTS) - 1)))
532 int pxechn_optlen_ok(int optlen)
534 if ((optlen >= 0) && (optlen < ((DHCP_OPT_LEN_MAX) - 1)))
539 int pxechn_setopt(struct dhcp_option *opt, void *data, int len)
547 p = realloc(opt->data, len);
548 if (!p && len) { /* Allow for len=0 */
549 pxechn_opt_free(opt);
553 memcpy(opt->data, data, len);
558 int pxechn_setopt_str(struct dhcp_option *opt, void *data)
560 return pxechn_setopt(opt, data, strnlen(data, DHCP_OPT_LEN_MAX));
563 int pxechn_parse_int(char *data, char istr[], int tlen)
567 if ((tlen == 1) || (tlen == 2) || (tlen == 4)) {
569 uint32_t optval = strtoul(istr, NULL, 0);
575 if (optval & 0xFFFFFF00)
579 if (optval & 0xFFFF0000)
581 optval = htons(optval);
584 optval = htonl(optval);
587 memcpy(data, &optval, tlen);
588 } else if (tlen == 8) {
590 uint64_t optval = strtoull(istr, NULL, 0);
594 optval = htonq(optval);
595 memcpy(data, &optval, tlen);
602 int pxechn_parse_hex_sep(char *data, char istr[], char sep)
609 while ((istr[ipos]) && (len < DHCP_OPT_LEN_MAX)) {
610 dprintf(" %02X%02X", *((int *)(istr + ipos)) & 0xFF, *((int *)(istr + ipos +1)) & 0xFF);
611 ichar = pxechn_parse_2bhex(istr + ipos);
619 } else if (istr[ipos+2] != sep) {
620 return -(EINVAL + 1);
628 int pxechn_parse_opttype(char istr[], int optnum)
631 int tlen, type, tmask;
635 pos = strchr(istr, '=');
638 if (istr[0] != '.') {
639 if (!pxechn_optnum_ok(optnum))
641 return -3; /* do lookup here */
643 tlen = pos - istr - 1;
644 if ((tlen < 1) || (tlen > 4))
646 tmask = 0xFFFFFFFF >> (8 * (4 - tlen));
647 type = (*(int*)(istr + 1)) & tmask;
652 int pxechn_parse_setopt(struct dhcp_option opts[], struct dhcp_option *iopt,
655 int rv = 0, optnum, opttype;
656 char *cpos = NULL, *pos;
658 if (!opts || !iopt || !(iopt->data))
660 if (!istr || !istr[0])
663 optnum = strtoul(istr, &cpos, 0);
664 if (!pxechn_optnum_ok(optnum))
666 pos = strchr(cpos, '=');
669 opttype = pxechn_parse_opttype(cpos, optnum);
673 iopt->len = pxechn_parse_int(iopt->data, pos, 1);
676 iopt->len = pxechn_parse_int(iopt->data, pos, 4);
679 iopt->len = pxechn_parse_int(iopt->data, pos, 8);
683 iopt->len = strlen(pos);
684 if (iopt->len > DHCP_OPT_LEN_MAX)
685 iopt->len = DHCP_OPT_LEN_MAX;
686 memcpy(iopt->data, pos, iopt->len);
687 dprintf_pc_so_s("s.len=%d\trv=%d\n", iopt->len, rv);
690 iopt->len = pxechn_parse_int(iopt->data, pos, 2);
693 iopt->len = pxechn_parse_hex_sep(iopt->data, pos, ':');
699 if (pxechn_optlen_ok(iopt->len)) {
700 rv = pxechn_setopt(&(opts[optnum]), (void *)(iopt->data), iopt->len);
702 if((opttype == 's') || (opttype == STRASINT_str))
703 dprintf_pc_so_s("rv=%d\n", rv);
707 int pxechn_parse_force(const char istr[])
714 rv = strtoul(istr, &pos, 0);
715 if ((istr == pos ) || ((rv == ULONG_MAX) && (errno)))
721 int pxechn_uuid_set(struct pxelinux_opt *pxe)
725 if (!pxe->p_unpacked[0])
726 ret = dhcp_unpack_packet((pxe_bootp_t *)(pxe->p[0].data),
727 pxe->p[0].len, pxe->opts[0]);
729 error("Could not unpack packet\n");
730 return -ret; /* dhcp_unpack_packet always returns positive errors */
733 if (pxe->opts[0][97].len >= 0 )
734 pxechn_setopt(&(pxe->opts[2][97]), pxe->opts[0][97].data, pxe->opts[0][97].len);
739 int pxechn_parse_args(int argc, char *argv[], struct pxelinux_opt *pxe,
740 struct dhcp_option opts[])
742 int arg, optnum, rv = 0;
744 const char optstr[] = "c:f:g:o:p:t:uwW";
745 struct dhcp_option iopt;
748 pxe->fip = ( (pxe_bootp_t *)(pxe->p[5].data) )->sip;
753 pxechn_parse_fn(pxe->fn, &(pxe->fip), pxe->host, &(pxe->fp));
754 pxechn_setopt_str(&(opts[67]), pxe->fp);
755 pxechn_setopt_str(&(opts[66]), pxe->host);
756 iopt.data = malloc(DHCP_OPT_LEN_MAX);
758 while ((rv >= 0) && (arg = getopt(argc, argv, optstr)) >= 0) {
759 dprintf_pc_pa(" Got arg '%c'/'%c' addr %08X val %s\n", arg == '?' ? optopt : arg, arg, (unsigned int)optarg, optarg ? optarg : "");
761 case 'c': /* config */
762 pxechn_setopt_str(&(opts[209]), optarg);
764 case 'f': /* force */
765 pxe->force = pxechn_parse_force(optarg);
767 case 'g': /* gateway/DHCP relay */
768 pxe->gip = pxe_dns(optarg);
770 case 'n': /* native */
772 case 'o': /* option */
773 rv = pxechn_parse_setopt(opts, &iopt, optarg);
775 case 'p': /* prefix */
776 pxechn_setopt_str(&(opts[210]), optarg);
778 case 't': /* timeout */
779 optnum = strtoul(optarg, &p, 0);
781 optnum = htonl(optnum);
782 pxechn_setopt(&(opts[211]), (void *)(&optnum), 4);
787 case 'u': /* UUID: copy option 97 from packet 1 if present */
788 pxechn_uuid_set(pxe);
801 if (rv >= 0) /* Clear it since getopt() doesn't guarentee it */
805 pxechn_opt_free(&iopt);
806 /* FIXME: consider reordering the application of parsed command line options
807 such that the new nbp may be at the end */
810 } else if (arg != '?') {
811 printf("Invalid argument for -%c: %s\n", arg, optarg);
813 dprintf("pxechn_parse_args rv=%d\n", rv);
817 int pxechn_args(int argc, char *argv[], struct pxelinux_opt *pxe)
819 pxe_bootp_t *bootp0, *bootp1;
821 struct dhcp_option *opts;
824 /* Start filling packet #1 */
825 bootp0 = (pxe_bootp_t *)(pxe->p[2].data);
826 bootp1 = (pxe_bootp_t *)(pxe->p[5].data);
828 ret = dhcp_unpack_packet(bootp0, pxe->p[2].len, opts);
830 error("Could not unpack packet\n");
833 pxe->p_unpacked[2] = 1;
834 pxe->gip = bootp1->gip;
836 ret = pxechn_parse_args(argc, argv, pxe, opts);
839 bootp1->sip = pxe->fip;
840 bootp1->gip = pxe->gip;
842 ret = dhcp_pack_packet(bootp1, (size_t *)&(pxe->p[5].len), opts);
844 error("Could not pack packet\n");
845 return -ret; /* dhcp_pack_packet always returns positive errors */
850 /* dhcp_pkt2pxe: Copy packet to PXE's BC data for a ptype packet
852 * p Packet data to copy
853 * len length of data to copy
854 * ptype Packet type to overwrite
856 int dhcp_pkt2pxe(pxe_bootp_t *p, size_t len, int ptype)
859 t_PXENV_GET_CACHED_INFO *ci;
863 if (!(ci = lzalloc(sizeof(t_PXENV_GET_CACHED_INFO)))){
864 dprintf("Unable to lzalloc() for PXE call structure\n");
868 ci->Status = PXENV_STATUS_FAILURE;
869 ci->PacketType = ptype;
870 memset(®, 0, sizeof(reg));
871 reg.eax.w[0] = 0x0009;
872 reg.ebx.w[0] = PXENV_GET_CACHED_INFO;
873 reg.edi.w[0] = OFFS(ci);
875 __intcall(0x22, ®, ®);
877 if (ci->Status != PXENV_STATUS_SUCCESS) {
878 dprintf("PXE Get Cached Info failed: %d\n", ci->Status);
883 cp = MK_PTR(ci->Buffer.seg, ci->Buffer.offs);
884 if (!(memcpy(cp, p, len))) {
885 dprintf("Failed to copy packet\n");
894 int pxechn_mergeopt(struct pxelinux_opt *pxe, int d, int s)
898 if ((d >= PXECHN_NUM_PKT_TYPE) || (s >= PXECHN_NUM_PKT_TYPE)
899 || (d < 0) || (s < 0)) {
902 if (!pxe->p_unpacked[s])
903 ret = dhcp_unpack_packet(pxe->p[s].data, pxe->p[s].len, pxe->opts[s]);
905 error("Could not unpack packet for merge\n");
906 printf("Error %d (%d)\n", ret, EINVAL);
908 if (pxe->p[s].len < 240)
909 printf("Packet %d is too short: %d (240)\n", s, pxe->p[s].len);
910 else if (((const struct dhcp_packet *)(pxe->p[s].data))->magic != htonl(DHCP_VENDOR_MAGIC))
911 printf("Packet %d has no magic\n", s);
913 error("Unknown EINVAL error\n");
915 error("Unknown error\n");
919 for (i = 0; i < NUM_DHCP_OPTS; i++) {
920 if (pxe->opts[d][i].len <= -1) {
921 if (pxe->opts[s][i].len >= 0)
922 pxechn_setopt(&(pxe->opts[d][i]), pxe->opts[s][i].data, pxe->opts[s][i].len);
928 /* pxechn: Chainload to new PXE file ourselves
930 * argc Count of arguments passed
931 * argv Values of arguments passed
932 * Returns 0 on success (which should never happen)
933 * 1 on loadfile() error
934 * 2 if DHCP Option 52 (Option Overload) used file field
937 int pxechn(int argc, char *argv[])
939 struct pxelinux_opt pxe;
940 pxe_bootp_t* p[(2 * PXECHN_NUM_PKT_TYPE)];
943 struct data_area file;
944 struct syslinux_rm_regs regs;
947 for (i = 0; i < (2 * PXECHN_NUM_PKT_TYPE); i++) {
948 p[i] = (pxe_bootp_t *)(pxe.p[i].data);
951 /* Parse arguments and patch packet 1 */
952 rv = pxechn_args(argc, argv, &pxe);
953 dpressanykey(INT_MAX);
957 /* Load the file late; it's the most time-expensive operation */
958 printf("%s: Attempting to load '%s': ", app_name_str, pxe.fn);
959 if (loadfile(pxe.fn, &file.data, &file.size)) {
960 pxe_error(errno, NULL, NULL);
965 /* we'll be shuffling to the standard location of 7C00h */
968 ((pxe.force) && ((pxe.force & (~PXECHN_FORCE_ALL)) == 0))) {
969 printf("Forcing behavior %08X\n", pxe.force);
970 // P2 is the same as P3 if no PXE server present.
972 (pxe.force & PXECHN_FORCE_PKT2)) {
973 pxechn_fill_pkt(&pxe, PXENV_PACKET_TYPE_DHCP_ACK);
974 rv = pxechn_mergeopt(&pxe, 2, 1);
976 dprintf("Merge Option returned %d\n", rv);
978 rv = dhcp_pack_packet(p[5], (size_t *)&(pxe.p[5].len), pxe.opts[2]);
979 rv = dhcp_pkt2pxe(p[5], pxe.p[5].len, PXENV_PACKET_TYPE_DHCP_ACK);
981 if (pxe.force & PXECHN_FORCE_PKT1) {
982 puts("Unimplemented force option utilized");
985 rv = dhcp_pkt2pxe(p[5], pxe.p[5].len, PXENV_PACKET_TYPE_CACHED_REPLY);
986 dprint_pxe_bootp_t(p[5], pxe.p[5].len);
988 ((pxe.force) && ((pxe.force & (~PXECHN_FORCE_ALL)) == 0))) {
989 // printf("Forcing behavior %08X\n", pxe.force);
990 // P2 is the same as P3 if no PXE server present.
992 (pxe.force & PXECHN_FORCE_PKT2)) {
993 rv = dhcp_pkt2pxe(p[5], pxe.p[5].len, PXENV_PACKET_TYPE_DHCP_ACK);
995 } else if (pxe.force) {
996 printf("FORCE: bad argument %08X\n", pxe.force);
998 printf("\n...Ready to boot:\n");
1000 pressanykey(INT_MAX);
1002 dpressanykey(INT_MAX);
1005 puts(" Attempting to boot...");
1006 do_boot(&file, 1, ®s);
1008 /* If failed, copy backup back in and abort */
1009 dhcp_pkt2pxe(p[2], pxe.p[2].len, PXENV_PACKET_TYPE_CACHED_REPLY);
1010 if (pxe.force && ((pxe.force & (~PXECHN_FORCE_ALL)) == 0)) {
1011 if (pxe.force & PXECHN_FORCE_PKT2) {
1012 rv = dhcp_pkt2pxe(p[1], pxe.p[1].len, PXENV_PACKET_TYPE_DHCP_ACK);
1019 /* pxe_restart: Restart the PXE environment with a new PXE file
1021 * ifn Name of file to chainload to in a format PXELINUX understands
1022 * This must strictly be TFTP or relative file
1024 int pxe_restart(char *ifn)
1027 struct pxelinux_opt pxe;
1029 t_PXENV_RESTART_TFTP *pxep; /* PXENV callback Parameter */
1032 pxechn_fill_pkt(&pxe, PXENV_PACKET_TYPE_CACHED_REPLY);
1034 pxe.fip = ( (pxe_bootp_t *)(pxe.p[5].data) )->sip;
1037 rv = pxechn_parse_fn(pxe.fn, &(pxe.fip), pxe.host, &(pxe.fp));
1038 if ((rv > 2) || (rv < 0)) {
1039 printf("%s: ERROR: Unparsable filename argument: '%s'\n\n", app_name_str, pxe.fn);
1042 printf(" Attempting to boot '%s'...\n\n", pxe.fn);
1043 memset(®, 0, sizeof reg);
1044 if (sizeof(t_PXENV_TFTP_READ_FILE) <= __com32.cs_bounce_size) {
1045 pxep = __com32.cs_bounce;
1046 memset(pxep, 0, sizeof(t_PXENV_RESTART_TFTP));
1047 } else if (!(pxep = lzalloc(sizeof(t_PXENV_RESTART_TFTP)))){
1048 dprintf("Unable to lzalloc() for PXE call structure\n");
1051 pxep->Status = PXENV_STATUS_SUCCESS; /* PXENV_STATUS_FAILURE */
1052 strcpy((char *)pxep->FileName, ifn);
1053 pxep->BufferSize = 0x8000;
1054 pxep->Buffer = (void *)0x7c00;
1055 pxep->ServerIPAddress = pxe.fip;
1056 dprintf("FN='%s' %08X %08X %08X %08X\n\n", (char *)pxep->FileName,
1057 pxep->ServerIPAddress, (unsigned int)pxep,
1058 pxep->BufferSize, (unsigned int)pxep->Buffer);
1059 dprintf("PXENV_RESTART_TFTP status %d\n", pxep->Status);
1060 reg.eax.w[0] = 0x0009;
1061 reg.ebx.w[0] = PXENV_RESTART_TFTP;
1062 reg.edi.w[0] = OFFS(pxep);
1065 __intcall(0x22, ®, ®);
1067 printf("PXENV_RESTART_TFTP returned %d\n", pxep->Status);
1068 if (pxep != __com32.cs_bounce)
1075 /* pxechn_gpxe: Use gPXE to chainload a new NBP
1077 * argc Count of arguments passed
1078 * argv Values of arguments passed
1079 * Returns 0 on success (which should never happen)
1080 * 1 on loadfile() error
1084 int pxechn_gpxe(int argc, char *argv[])
1087 struct pxelinux_opt pxe;
1090 printf("%s\n", argv[0]);
1091 pxechn_args(argc, argv, &pxe);
1096 int main(int argc, char *argv[])
1100 const struct syslinux_version *sv;
1102 /* Initialization */
1104 console_ansi_raw(); /* sets errno = 9 (EBADF) */
1105 /* printf("%d %d\n", err, errno); */
1107 sv = syslinux_version();
1108 if (sv->filesystem != SYSLINUX_FS_PXELINUX) {
1109 printf("%s: May only run in PXELINUX\n", app_name_str);
1110 argc = 1; /* prevents further processing to boot */
1113 if ((strcasecmp(argv[1], "-h") == 0) || ((strcmp(argv[1], "-?") == 0))
1114 || (strcasecmp(argv[1], "--help") == 0)) {
1117 rv = pxechn(argc - 1, &argv[1]);
1119 } else if (argc >= 3) {
1120 if ((strcmp(argv[1], "-r") == 0)) {
1122 rv = pxe_restart(argv[2]);
1124 rv = pxechn(argc - 1, &argv[1]);