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 in_addr_t sip
; /* siaddr: Next Server IP Address */
121 struct dhcp_option p
[PXECHN_NUM_PKT_AVAIL
];
122 /* original _DHCP_DISCOVER, _DHCP_ACK, _CACHED_REPLY then modified packets */
123 char host
[PXECHN_HOST_LEN
];
124 struct dhcp_option opts
[PXECHN_NUM_PKT_TYPE
][NUM_DHCP_OPTS
];
125 char p_unpacked
[PXECHN_NUM_PKT_TYPE
];
137 static inline void error(const char *msg
)
143 static void do_boot(struct data_area
*data
, int ndata
,
144 struct syslinux_rm_regs
*regs
)
146 uint16_t *const bios_fbm
= (uint16_t *) 0x413;
147 addr_t dosmem
= *bios_fbm
<< 10; /* Technically a low bound */
148 struct syslinux_memmap
*mmap
;
149 struct syslinux_movelist
*mlist
= NULL
;
153 mmap
= syslinux_memory_map();
156 error("Cannot read system memory map\n");
161 for (i
= 0; i
< ndata
; i
++) {
162 if (data
[i
].base
+ data
[i
].size
> endimage
)
163 endimage
= data
[i
].base
+ data
[i
].size
;
165 if (endimage
> dosmem
)
168 for (i
= 0; i
< ndata
; i
++) {
169 if (syslinux_add_movelist(&mlist
, data
[i
].base
,
170 (addr_t
) data
[i
].data
, data
[i
].size
))
175 /* Tell the shuffler not to muck with this area... */
176 syslinux_add_memmap(&mmap
, endimage
, 0xa0000 - endimage
, SMT_RESERVED
);
178 /* Force text mode */
179 syslinux_force_text_mode();
181 fputs("Booting...\n", stdout
);
182 syslinux_shuffle_boot_rm(mlist
, mmap
, 3, regs
);
183 error("Chainboot failed!\n");
187 error("Loader file too large\n");
191 error("Out of memory\n");
198 " %s [OPTIONS]... _new-nbp_\n"
199 " %s -r _new-nbp_ (calls PXE stack PXENV_RESTART_TFTP)\n"
201 " [-c config] [-g gateway] [-p prefix] [-t reboot] [-u] [-w] [-W]"
202 " [-o opt.ty=val]\n\n",
203 app_name_str
, app_name_str
);
206 void pxe_error(int ierr
, const char *evt
, const char *msg
)
211 printf("Error while %s: ", evt
);
212 printf("%d:%s\n", ierr
, strerror(ierr
));
215 int pressanykey(clock_t tm
) {
218 printf("Press any key to continue. ");
219 inc
= get_key(stdin
, tm
);
224 int dhcp_find_opt(pxe_bootp_t
*p
, size_t len
, uint8_t opt
)
232 dprintf(" packet pointer is null\n");
235 vlen
= len
- ((void *)&(p
->vendor
) - (void *)p
);
237 magic
= ntohl(*((uint32_t *)d
));
238 if (magic
!= VM_RFC1048
) /* Invalid DHCP packet */
240 for (i
= 4; i
< vlen
; i
++) {
242 dprintf("\n @%03X-%2d\n", i
, d
[i
]);
246 if (d
[i
] == ((NUM_DHCP_OPTS
) - 1)) /* End of list */
248 if (d
[i
]) { /* Skip padding */
256 void print_pxe_vendor_raw(pxe_bootp_t
*p
, size_t len
)
261 printf(" packet pointer is null\n");
264 vlen
= len
- ((void *)&(p
->vendor
) - (void *)p
);
265 if (vlen
> PXE_VENDOR_RAW_PRN_MAX
)
266 vlen
= PXE_VENDOR_RAW_PRN_MAX
;
267 dprintf(" rawLen = %d", vlen
);
268 for (i
= 0; i
< vlen
; i
++) {
270 printf("\n %04X:", i
);
271 printf(" %02X", p
->vendor
.d
[i
]);
276 void print_pxe_vendor_blk(pxe_bootp_t
*p
, size_t len
)
278 int i
, vlen
, oplen
, j
;
282 printf(" packet pointer is null\n");
285 vlen
= len
- ((void *)&(p
->vendor
) - (void *)p
);
286 printf(" Vendor Data: Len=%d", vlen
);
288 magic
= ntohl(*((uint32_t *)d
));
289 printf(" Magic: %08X", ntohl(*((uint32_t *)d
)));
290 if (magic
!= VM_RFC1048
) /* Invalid DHCP packet */
292 for (i
= 4; i
< vlen
; i
++) {
293 if (d
[i
]) /* Skip the padding */
294 printf("\n @%03X-%3d", i
, d
[i
]);
295 if (d
[i
] == ((NUM_DHCP_OPTS
) - 1)) /* End of list */
299 printf(" l=%3d:", oplen
);
300 for (j
= (++i
+ oplen
); i
< vlen
&& i
< j
; i
++) {
301 printf(" %02X", d
[i
]);
309 void print_pxe_bootp_t(pxe_bootp_t
*p
, size_t len
)
311 if (!p
|| len
<= 0) {
312 printf(" packet pointer is null\n");
315 printf(" op:%02X hw:%02X hl:%02X gh:%02X id:%08X se:%04X f:%04X"
316 " cip:%08X\n", p
->opcode
, p
->Hardware
, p
->Hardlen
, p
->Gatehops
,
317 ntohl(p
->ident
), ntohs(p
->seconds
), ntohs(p
->Flags
), ntohl(p
->cip
));
318 printf(" yip:%08X sip:%08X gip:%08X",
319 ntohl(p
->yip
), ntohl(p
->sip
), ntohl(p
->gip
));
320 printf(" caddr-%02X:%02X:%02X:%02X:%02X:%02X\n", p
->CAddr
[0],
321 p
->CAddr
[1], p
->CAddr
[2], p
->CAddr
[3], p
->CAddr
[4], p
->CAddr
[5]);
322 printf(" sName: '%s'\n", p
->Sname
);
323 printf(" bootfile: '%s'\n", p
->bootfile
);
324 dprint_pxe_vendor_blk(p
, len
);
327 void pxe_set_regs(struct syslinux_rm_regs
*regs
)
332 /* Plan A uses SS:[SP + 4] */
333 /* sdi->pxe.stack is a usable pointer, not something that can be nicely
334 and reliably split to SS:SP without causing issues */
335 tregs
.eax
.l
= 0x000A;
336 __intcall(0x22, &tregs
, &tregs
);
338 regs
->esp
.l
= tregs
.esi
.w
[0] + sizeof(tregs
);
339 /* Plan B uses [ES:BX] */
341 regs
->ebx
= tregs
.ebx
;
342 dprintf("\nsp:%04x ss:%04x es:%04x bx:%04x\n", regs
->esp
.w
[0],
343 regs
->ss
, regs
->es
, regs
->ebx
.w
[0]);
344 /* Zero out everything else just to be sure */
345 regs
->cs
= regs
->ds
= regs
->fs
= regs
->gs
= 0;
346 regs
->eax
.l
= regs
->ecx
.l
= regs
->edx
.l
= 0;
349 int hostlen_limit(int len
)
351 return min(len
, ((PXECHN_HOST_LEN
) - 1));
354 //FIXME: To a library
355 /* Parse a filename into an IPv4 address and filename pointer
356 * returns Based on the interpretation of fn
357 * 0 regular file name
363 * -1 if fn is another URL type
365 int pxechn_parse_fn(char fn
[], in_addr_t
*fip
, char *host
, char *fp
[])
368 char *csep
, *ssep
, *hsep
; /* Colon, Slash separator positions */
369 int hlen
, plen
; /* Hostname, protocol length */
372 csep
= strchr(fn
, ':');
374 if (csep
[1] == ':') { /* assume IP::FN */
378 hlen
= hostlen_limit(csep
- fn
);
379 memcpy(host
, fn
, hlen
);
382 } else if ((csep
[1] == '/') && (csep
[2] == '/')) {
383 /* URL: proto://host:port/path/file */
384 /* proto://[user[:passwd]@]host[:port]/path/file */
385 ssep
= strchr(csep
+ 3, '/');
387 hlen
= hostlen_limit(ssep
- (csep
+ 3));
390 hlen
= hostlen_limit(strlen(csep
+ 3));
392 memcpy(host
, (csep
+ 3), hlen
);
395 if (strncmp(fn
, "tftp", plen
) == 0)
397 else if (strncmp(fn
, "http", plen
) == 0)
399 else if (strncmp(fn
, "ftp", plen
) == 0)
401 else if (strncmp(fn
, "https", plen
) == 0)
402 rv
= 3 + ( 1 << 30 );
413 hsep
= strchr(host
, '@');
420 dprintf0(" host '%s'\n fp '%s'\n fip %08x\n", host
, *fp
, ntohl(*fip
));
424 void pxechn_opt_free(struct dhcp_option
*opt
)
430 void pxechn_fill_pkt(struct pxelinux_opt
*pxe
, int ptype
)
434 if ((ptype
< 0) || (ptype
> PXECHN_NUM_PKT_TYPE
))
436 p1
= ptype
- PXECHN_PKT_TYPE_START
;
437 p2
= p1
+ PXECHN_NUM_PKT_TYPE
;
438 if ((rv
>= -1) && (!pxe_get_cached_info(ptype
,
439 (void **)&(pxe
->p
[p1
].data
), (size_t *)&(pxe
->p
[p1
].len
)))) {
440 pxe
->p
[p2
].data
= malloc(2048);
441 if (pxe
->p
[p2
].data
) {
442 memcpy(pxe
->p
[p2
].data
, pxe
->p
[p1
].data
, pxe
->p
[p1
].len
);
443 pxe
->p
[p2
].len
= pxe
->p
[p1
].len
;
445 dprint_pxe_bootp_t((pxe_bootp_t
*)(pxe
->p
[p1
].data
), pxe
->p
[p1
].len
);
446 dpressanykey(INT_MAX
);
448 printf("%s: ERROR: Unable to malloc() for second packet\n", app_name_str
);
451 printf("%s: ERROR: Unable to retrieve first packet\n", app_name_str
);
454 pxechn_opt_free(&pxe
->p
[p1
]);
458 void pxechn_init(struct pxelinux_opt
*pxe
)
460 /* Init for paranoia */
469 pxe
->host
[((NUM_DHCP_OPTS
) - 1)] = 0;
470 for (int j
= 0; j
< PXECHN_NUM_PKT_TYPE
; j
++){
471 for (int i
= 0; i
< NUM_DHCP_OPTS
; i
++) {
472 pxe
->opts
[j
][i
].data
= NULL
;
473 pxe
->opts
[j
][i
].len
= -1;
475 pxe
->p_unpacked
[j
] = 0;
476 pxe
->p
[j
].data
= NULL
;
477 pxe
->p
[j
+PXECHN_NUM_PKT_TYPE
].data
= NULL
;
479 pxe
->p
[j
+PXECHN_NUM_PKT_TYPE
].len
= 0;
481 pxechn_fill_pkt(pxe
, PXENV_PACKET_TYPE_CACHED_REPLY
);
484 int pxechn_to_hex(char i
)
486 if (i
>= '0' && i
<= '9')
488 if (i
>= 'A' && i
<= 'F')
489 return (i
- 'A' + 10);
490 if (i
>= 'a' && i
<= 'f')
491 return (i
- 'a' + 10);
497 int pxechn_parse_2bhex(char ins
[])
500 int n0
= -3, n1
= -3;
504 /* pxechn_to_hex can handle the NULL character by returning -1 and
505 breaking the execution of the statement chain */
506 } else if (((n0
= pxechn_to_hex(ins
[0])) >= 0)
507 && ((n1
= pxechn_to_hex(ins
[1])) >= 0)) {
508 ret
= (n0
* 16) + n1
;
509 } else if (n0
== -1) { /* Leading NULL char */
515 int pxechn_optnum_ok(int optnum
)
517 if ((optnum
> 0) && (optnum
< ((NUM_DHCP_OPTS
) - 1)))
522 int pxechn_optnum_ok_notres(int optnum
)
524 if ((optnum
<= 0) && (optnum
>= ((NUM_DHCP_OPTS
) - 1)))
534 int pxechn_optlen_ok(int optlen
)
536 if ((optlen
>= 0) && (optlen
< ((DHCP_OPT_LEN_MAX
) - 1)))
541 int pxechn_setopt(struct dhcp_option
*opt
, void *data
, int len
)
549 p
= realloc(opt
->data
, len
);
550 if (!p
&& len
) { /* Allow for len=0 */
551 pxechn_opt_free(opt
);
555 memcpy(opt
->data
, data
, len
);
560 int pxechn_setopt_str(struct dhcp_option
*opt
, void *data
)
562 return pxechn_setopt(opt
, data
, strnlen(data
, DHCP_OPT_LEN_MAX
));
565 int pxechn_parse_int(char *data
, char istr
[], int tlen
)
569 if ((tlen
== 1) || (tlen
== 2) || (tlen
== 4)) {
571 uint32_t optval
= strtoul(istr
, NULL
, 0);
577 if (optval
& 0xFFFFFF00)
581 if (optval
& 0xFFFF0000)
583 optval
= htons(optval
);
586 optval
= htonl(optval
);
589 memcpy(data
, &optval
, tlen
);
590 } else if (tlen
== 8) {
592 uint64_t optval
= strtoull(istr
, NULL
, 0);
596 optval
= htonq(optval
);
597 memcpy(data
, &optval
, tlen
);
604 int pxechn_parse_hex_sep(char *data
, char istr
[], char sep
)
611 while ((istr
[ipos
]) && (len
< DHCP_OPT_LEN_MAX
)) {
612 dprintf(" %02X%02X", *((int *)(istr
+ ipos
)) & 0xFF, *((int *)(istr
+ ipos
+1)) & 0xFF);
613 ichar
= pxechn_parse_2bhex(istr
+ ipos
);
621 } else if (istr
[ipos
+2] != sep
) {
622 return -(EINVAL
+ 1);
630 int pxechn_parse_opttype(char istr
[], int optnum
)
633 int tlen
, type
, tmask
;
637 pos
= strchr(istr
, '=');
640 if (istr
[0] != '.') {
641 if (!pxechn_optnum_ok(optnum
))
643 return -3; /* do lookup here */
645 tlen
= pos
- istr
- 1;
646 if ((tlen
< 1) || (tlen
> 4))
648 tmask
= 0xFFFFFFFF >> (8 * (4 - tlen
));
649 type
= (*(int*)(istr
+ 1)) & tmask
;
654 int pxechn_parse_setopt(struct dhcp_option opts
[], struct dhcp_option
*iopt
,
657 int rv
= 0, optnum
, opttype
;
658 char *cpos
= NULL
, *pos
;
660 if (!opts
|| !iopt
|| !(iopt
->data
))
662 if (!istr
|| !istr
[0])
665 optnum
= strtoul(istr
, &cpos
, 0);
666 if (!pxechn_optnum_ok(optnum
))
668 pos
= strchr(cpos
, '=');
671 opttype
= pxechn_parse_opttype(cpos
, optnum
);
675 iopt
->len
= pxechn_parse_int(iopt
->data
, pos
, 1);
678 iopt
->len
= pxechn_parse_int(iopt
->data
, pos
, 4);
681 iopt
->len
= pxechn_parse_int(iopt
->data
, pos
, 8);
685 iopt
->len
= strlen(pos
);
686 if (iopt
->len
> DHCP_OPT_LEN_MAX
)
687 iopt
->len
= DHCP_OPT_LEN_MAX
;
688 memcpy(iopt
->data
, pos
, iopt
->len
);
689 dprintf_pc_so_s("s.len=%d\trv=%d\n", iopt
->len
, rv
);
692 iopt
->len
= pxechn_parse_int(iopt
->data
, pos
, 2);
695 iopt
->len
= pxechn_parse_hex_sep(iopt
->data
, pos
, ':');
701 if (pxechn_optlen_ok(iopt
->len
)) {
702 rv
= pxechn_setopt(&(opts
[optnum
]), (void *)(iopt
->data
), iopt
->len
);
704 if((opttype
== 's') || (opttype
== STRASINT_str
))
705 dprintf_pc_so_s("rv=%d\n", rv
);
709 int pxechn_parse_force(const char istr
[])
716 rv
= strtoul(istr
, &pos
, 0);
717 if ((istr
== pos
) || ((rv
== ULONG_MAX
) && (errno
)))
723 int pxechn_uuid_set(struct pxelinux_opt
*pxe
)
727 if (!pxe
->p_unpacked
[0])
728 ret
= dhcp_unpack_packet((pxe_bootp_t
*)(pxe
->p
[0].data
),
729 pxe
->p
[0].len
, pxe
->opts
[0]);
731 error("Could not unpack packet\n");
732 return -ret
; /* dhcp_unpack_packet always returns positive errors */
735 if (pxe
->opts
[0][97].len
>= 0 )
736 pxechn_setopt(&(pxe
->opts
[2][97]), pxe
->opts
[0][97].data
, pxe
->opts
[0][97].len
);
741 int pxechn_parse_args(int argc
, char *argv
[], struct pxelinux_opt
*pxe
,
742 struct dhcp_option opts
[])
744 int arg
, optnum
, rv
= 0;
746 const char optstr
[] = "c:f:g:o:p:St:uwW";
747 struct dhcp_option iopt
;
750 pxe
->fip
= ( (pxe_bootp_t
*)(pxe
->p
[5].data
) )->sip
;
755 pxechn_parse_fn(pxe
->fn
, &(pxe
->fip
), pxe
->host
, &(pxe
->fp
));
756 pxechn_setopt_str(&(opts
[67]), pxe
->fp
);
757 pxechn_setopt_str(&(opts
[66]), pxe
->host
);
758 iopt
.data
= malloc(DHCP_OPT_LEN_MAX
);
760 while ((rv
>= 0) && (arg
= getopt(argc
, argv
, optstr
)) >= 0) {
761 dprintf_pc_pa(" Got arg '%c'/'%c' addr %08X val %s\n", arg
== '?' ? optopt
: arg
, arg
, (unsigned int)optarg
, optarg
? optarg
: "");
763 case 'c': /* config */
764 pxechn_setopt_str(&(opts
[209]), optarg
);
766 case 'f': /* force */
767 pxe
->force
= pxechn_parse_force(optarg
);
769 case 'g': /* gateway/DHCP relay */
770 pxe
->gip
= pxe_dns(optarg
);
772 case 'n': /* native */
774 case 'o': /* option */
775 rv
= pxechn_parse_setopt(opts
, &iopt
, optarg
);
777 case 'p': /* prefix */
778 pxechn_setopt_str(&(opts
[210]), optarg
);
780 case 'S': /* sip from sName */
783 case 't': /* timeout */
784 optnum
= strtoul(optarg
, &p
, 0);
786 optnum
= htonl(optnum
);
787 pxechn_setopt(&(opts
[211]), (void *)(&optnum
), 4);
792 case 'u': /* UUID: copy option 97 from packet 1 if present */
793 pxechn_uuid_set(pxe
);
806 if (rv
>= 0) /* Clear it since getopt() doesn't guarentee it */
810 pxechn_opt_free(&iopt
);
811 /* FIXME: consider reordering the application of parsed command line options
812 such that the new nbp may be at the end */
815 } else if (arg
!= '?') {
816 printf("Invalid argument for -%c: %s\n", arg
, optarg
);
818 dprintf("pxechn_parse_args rv=%d\n", rv
);
822 int pxechn_args(int argc
, char *argv
[], struct pxelinux_opt
*pxe
)
824 pxe_bootp_t
*bootp0
, *bootp1
;
826 struct dhcp_option
*opts
;
830 /* Start filling packet #1 */
831 bootp0
= (pxe_bootp_t
*)(pxe
->p
[2].data
);
832 bootp1
= (pxe_bootp_t
*)(pxe
->p
[5].data
);
834 ret
= dhcp_unpack_packet(bootp0
, pxe
->p
[2].len
, opts
);
836 error("Could not unpack packet\n");
839 pxe
->p_unpacked
[2] = 1;
840 pxe
->gip
= bootp1
->gip
;
842 ret
= pxechn_parse_args(argc
, argv
, pxe
, opts
);
845 if (pxe
->sip
> 0xFFFFFF) { /* a real IPv4 address */
846 bootp1
->sip
= pxe
->sip
;
847 } else if ((pxe
->sip
== 1)
848 && (opts
[66].len
> 0)){
850 if (strnlen(opts
[66].data
, opts
[66].len
) == (size_t)opts
[66].len
) {
851 str
= malloc(opts
[66].len
+ 1);
853 memcpy(str
, opts
[66].data
, opts
[66].len
);
854 str
[opts
[66].len
] = 0;
860 bootp1
->sip
= pxe_dns(str
);
861 if (str
!= opts
[66].data
)
864 bootp1
->sip
= pxe
->fip
;
867 bootp1
->sip
= pxe
->fip
;
869 bootp1
->gip
= pxe
->gip
;
871 ret
= dhcp_pack_packet(bootp1
, (size_t *)&(pxe
->p
[5].len
), opts
);
873 error("Could not pack packet\n");
874 return -ret
; /* dhcp_pack_packet always returns positive errors */
879 /* dhcp_pkt2pxe: Copy packet to PXE's BC data for a ptype packet
881 * p Packet data to copy
882 * len length of data to copy
883 * ptype Packet type to overwrite
885 int dhcp_pkt2pxe(pxe_bootp_t
*p
, size_t len
, int ptype
)
887 t_PXENV_GET_CACHED_INFO
*ci
;
891 if (!(ci
= lzalloc(sizeof(t_PXENV_GET_CACHED_INFO
)))){
892 dprintf("Unable to lzalloc() for PXE call structure\n");
896 ci
->Status
= PXENV_STATUS_FAILURE
;
897 ci
->PacketType
= ptype
;
898 pxe_call(PXENV_GET_CACHED_INFO
, ci
);
900 if (ci
->Status
!= PXENV_STATUS_SUCCESS
) {
901 dprintf("PXE Get Cached Info failed: %d\n", ci
->Status
);
906 cp
= MK_PTR(ci
->Buffer
.seg
, ci
->Buffer
.offs
);
907 if (!(memcpy(cp
, p
, len
))) {
908 dprintf("Failed to copy packet\n");
917 int pxechn_mergeopt(struct pxelinux_opt
*pxe
, int d
, int s
)
921 if ((d
>= PXECHN_NUM_PKT_TYPE
) || (s
>= PXECHN_NUM_PKT_TYPE
)
922 || (d
< 0) || (s
< 0)) {
925 if (!pxe
->p_unpacked
[s
])
926 ret
= dhcp_unpack_packet(pxe
->p
[s
].data
, pxe
->p
[s
].len
, pxe
->opts
[s
]);
928 error("Could not unpack packet for merge\n");
929 printf("Error %d (%d)\n", ret
, EINVAL
);
931 if (pxe
->p
[s
].len
< 240)
932 printf("Packet %d is too short: %d (240)\n", s
, pxe
->p
[s
].len
);
933 else if (((const struct dhcp_packet
*)(pxe
->p
[s
].data
))->magic
!= htonl(DHCP_VENDOR_MAGIC
))
934 printf("Packet %d has no magic\n", s
);
936 error("Unknown EINVAL error\n");
938 error("Unknown error\n");
942 for (i
= 0; i
< NUM_DHCP_OPTS
; i
++) {
943 if (pxe
->opts
[d
][i
].len
<= -1) {
944 if (pxe
->opts
[s
][i
].len
>= 0)
945 pxechn_setopt(&(pxe
->opts
[d
][i
]), pxe
->opts
[s
][i
].data
, pxe
->opts
[s
][i
].len
);
951 /* pxechn: Chainload to new PXE file ourselves
953 * argc Count of arguments passed
954 * argv Values of arguments passed
955 * Returns 0 on success (which should never happen)
956 * 1 on loadfile() error
957 * 2 if DHCP Option 52 (Option Overload) used file field
960 int pxechn(int argc
, char *argv
[])
962 struct pxelinux_opt pxe
;
963 pxe_bootp_t
* p
[(2 * PXECHN_NUM_PKT_TYPE
)];
966 struct data_area file
;
967 struct syslinux_rm_regs regs
;
970 for (i
= 0; i
< (2 * PXECHN_NUM_PKT_TYPE
); i
++) {
971 p
[i
] = (pxe_bootp_t
*)(pxe
.p
[i
].data
);
974 /* Parse arguments and patch packet 1 */
975 rv
= pxechn_args(argc
, argv
, &pxe
);
976 dpressanykey(INT_MAX
);
980 /* Load the file late; it's the most time-expensive operation */
981 printf("%s: Attempting to load '%s': ", app_name_str
, pxe
.fn
);
982 if (loadfile(pxe
.fn
, &file
.data
, &file
.size
)) {
983 pxe_error(errno
, NULL
, NULL
);
988 /* we'll be shuffling to the standard location of 7C00h */
991 ((pxe
.force
) && ((pxe
.force
& (~PXECHN_FORCE_ALL
)) == 0))) {
992 printf("Forcing behavior %08X\n", pxe
.force
);
993 // P2 is the same as P3 if no PXE server present.
995 (pxe
.force
& PXECHN_FORCE_PKT2
)) {
996 pxechn_fill_pkt(&pxe
, PXENV_PACKET_TYPE_DHCP_ACK
);
997 rv
= pxechn_mergeopt(&pxe
, 2, 1);
999 dprintf("Merge Option returned %d\n", rv
);
1001 rv
= dhcp_pack_packet(p
[5], (size_t *)&(pxe
.p
[5].len
), pxe
.opts
[2]);
1002 rv
= dhcp_pkt2pxe(p
[5], pxe
.p
[5].len
, PXENV_PACKET_TYPE_DHCP_ACK
);
1004 if (pxe
.force
& PXECHN_FORCE_PKT1
) {
1005 puts("Unimplemented force option utilized");
1008 rv
= dhcp_pkt2pxe(p
[5], pxe
.p
[5].len
, PXENV_PACKET_TYPE_CACHED_REPLY
);
1009 dprint_pxe_bootp_t(p
[5], pxe
.p
[5].len
);
1011 ((pxe
.force
) && ((pxe
.force
& (~PXECHN_FORCE_ALL
)) == 0))) {
1012 // printf("Forcing behavior %08X\n", pxe.force);
1013 // P2 is the same as P3 if no PXE server present.
1015 (pxe
.force
& PXECHN_FORCE_PKT2
)) {
1016 rv
= dhcp_pkt2pxe(p
[5], pxe
.p
[5].len
, PXENV_PACKET_TYPE_DHCP_ACK
);
1018 } else if (pxe
.force
) {
1019 printf("FORCE: bad argument %08X\n", pxe
.force
);
1021 printf("\n...Ready to boot:\n");
1023 pressanykey(INT_MAX
);
1025 dpressanykey(INT_MAX
);
1028 puts(" Attempting to boot...");
1029 do_boot(&file
, 1, ®s
);
1031 /* If failed, copy backup back in and abort */
1032 dhcp_pkt2pxe(p
[2], pxe
.p
[2].len
, PXENV_PACKET_TYPE_CACHED_REPLY
);
1033 if (pxe
.force
&& ((pxe
.force
& (~PXECHN_FORCE_ALL
)) == 0)) {
1034 if (pxe
.force
& PXECHN_FORCE_PKT2
) {
1035 rv
= dhcp_pkt2pxe(p
[1], pxe
.p
[1].len
, PXENV_PACKET_TYPE_DHCP_ACK
);
1042 /* pxe_restart: Restart the PXE environment with a new PXE file
1044 * ifn Name of file to chainload to in a format PXELINUX understands
1045 * This must strictly be TFTP or relative file
1047 int pxe_restart(char *ifn
)
1050 struct pxelinux_opt pxe
;
1051 t_PXENV_RESTART_TFTP
*pxep
; /* PXENV callback Parameter */
1054 pxechn_fill_pkt(&pxe
, PXENV_PACKET_TYPE_CACHED_REPLY
);
1056 pxe
.fip
= ( (pxe_bootp_t
*)(pxe
.p
[5].data
) )->sip
;
1059 rv
= pxechn_parse_fn(pxe
.fn
, &(pxe
.fip
), pxe
.host
, &(pxe
.fp
));
1060 if ((rv
> 2) || (rv
< 0)) {
1061 printf("%s: ERROR: Unparsable filename argument: '%s'\n\n", app_name_str
, pxe
.fn
);
1064 printf(" Attempting to boot '%s'...\n\n", pxe
.fn
);
1065 if (!(pxep
= lzalloc(sizeof(t_PXENV_RESTART_TFTP
)))){
1066 dprintf("Unable to lzalloc() for PXE call structure\n");
1069 pxep
->Status
= PXENV_STATUS_SUCCESS
; /* PXENV_STATUS_FAILURE */
1070 strcpy((char *)pxep
->FileName
, ifn
);
1071 pxep
->BufferSize
= 0x8000;
1072 pxep
->Buffer
= (void *)0x7c00;
1073 pxep
->ServerIPAddress
= pxe
.fip
;
1074 dprintf("FN='%s' %08X %08X %08X %08X\n\n", (char *)pxep
->FileName
,
1075 pxep
->ServerIPAddress
, (unsigned int)pxep
,
1076 pxep
->BufferSize
, (unsigned int)pxep
->Buffer
);
1077 dprintf("PXENV_RESTART_TFTP status %d\n", pxep
->Status
);
1079 pxe_call(PXENV_RESTART_TFTP
, pxep
);
1081 printf("PXENV_RESTART_TFTP returned %d\n", pxep
->Status
);
1088 /* pxechn_gpxe: Use gPXE to chainload a new NBP
1090 * argc Count of arguments passed
1091 * argv Values of arguments passed
1092 * Returns 0 on success (which should never happen)
1093 * 1 on loadfile() error
1097 int pxechn_gpxe(int argc
, char *argv
[])
1100 struct pxelinux_opt pxe
;
1103 printf("%s\n", argv
[0]);
1104 pxechn_args(argc
, argv
, &pxe
);
1109 int main(int argc
, char *argv
[])
1113 const struct syslinux_version
*sv
;
1115 /* Initialization */
1117 console_ansi_raw(); /* sets errno = 9 (EBADF) */
1118 /* printf("%d %d\n", err, errno); */
1120 sv
= syslinux_version();
1121 if (sv
->filesystem
!= SYSLINUX_FS_PXELINUX
) {
1122 printf("%s: May only run in PXELINUX\n", app_name_str
);
1123 argc
= 1; /* prevents further processing to boot */
1126 if ((strcasecmp(argv
[1], "-h") == 0) || ((strcmp(argv
[1], "-?") == 0))
1127 || (strcasecmp(argv
[1], "--help") == 0)) {
1130 rv
= pxechn(argc
- 1, &argv
[1]);
1132 } else if (argc
>= 3) {
1133 if ((strcmp(argv
[1], "-r") == 0)) {
1135 rv
= pxe_restart(argv
[2]);
1137 rv
= pxechn(argc
- 1, &argv
[1]);