301f311ef8d14275bd70fa43712422061867df30
[profile/ivi/syslinux.git] / com32 / modules / pxechn.c
1 /* ----------------------------------------------------------------------- *
2  *
3  *   Copyright 2010-2012 Gene Cumm - All Rights Reserved
4  *
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"]
10  *
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.
16  *
17  * ----------------------------------------------------------------------- */
18
19 /*
20  * pxechn.c
21  *
22  * PXE Chain Loader; Chain load to another PXE network boot program
23  * that may be on another host.
24  */
25
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <consoles.h>
29 #include <console.h>
30 #include <errno.h>
31 #include <string.h>
32 #include <syslinux/config.h>
33 #include <syslinux/loadfile.h>
34 #include <syslinux/bootrm.h>
35 #include <syslinux/video.h>
36 #include <com32.h>
37 #include <stdint.h>
38 #include <syslinux/pxe.h>
39 #include <sys/gpxe.h>
40 #include <unistd.h>
41 #include <getkey.h>
42 #include <dhcp.h>
43 #include <limits.h>
44
45
46 #ifdef DEBUG
47 #  define PXECHN_DEBUG 1
48 #else
49 #  define PXECHN_DEBUG 0
50 #endif
51
52 typedef union {
53     uint64_t q;
54     uint32_t l[2];
55     uint16_t w[4];
56     uint8_t b[8];
57 } reg64_t;
58
59 #define dprintf0(f, ...)                ((void)0)
60
61 #ifndef dprintf
62 #  if (PXECHN_DEBUG > 0)
63 #    define dprintf                     printf
64 #  else
65 #    define dprintf(f, ...)             ((void)0)
66 #  endif
67 #endif
68
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
74 #else
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)
79 #endif
80
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
85
86 #define t_PXENV_RESTART_TFTP    t_PXENV_TFTP_READ_FILE
87
88 #define STACK_SPLIT     11
89
90 /* same as pxelinux.asm REBOOT_TIME */
91 #define REBOOT_TIME     300
92
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 */
97
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
101
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))
107
108 #define min(a,b) (((a) < (b)) ? (a) : (b))
109
110 const char app_name_str[] = "pxechn.c32";
111
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 */
117     uint32_t force;
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];
125 };
126
127
128 /* from chain.c */
129 struct data_area {
130     void *data;
131     addr_t base;
132     addr_t size;
133 };
134
135 /* From chain.c */
136 static inline void error(const char *msg)
137 {
138     fputs(msg, stderr);
139 }
140
141 /* From chain.c */
142 static void do_boot(struct data_area *data, int ndata,
143                     struct syslinux_rm_regs *regs)
144 {
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;
149     addr_t endimage;
150     int i;
151
152     mmap = syslinux_memory_map();
153
154     if (!mmap) {
155         error("Cannot read system memory map\n");
156         return;
157     }
158
159     endimage = 0;
160     for (i = 0; i < ndata; i++) {
161         if (data[i].base + data[i].size > endimage)
162             endimage = data[i].base + data[i].size;
163     }
164     if (endimage > dosmem)
165         goto too_big;
166
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))
170             goto enomem;
171     }
172
173
174     /* Tell the shuffler not to muck with this area... */
175     syslinux_add_memmap(&mmap, endimage, 0xa0000 - endimage, SMT_RESERVED);
176
177     /* Force text mode */
178     syslinux_force_text_mode();
179
180     fputs("Booting...\n", stdout);
181     syslinux_shuffle_boot_rm(mlist, mmap, 3, regs);
182     error("Chainboot failed!\n");
183     return;
184
185 too_big:
186     error("Loader file too large\n");
187     return;
188
189 enomem:
190     error("Out of memory\n");
191     return;
192 }
193
194 void usage(void)
195 {
196     printf("USAGE:\n"
197         "    %s [OPTIONS]... _new-nbp_\n"
198         "    %s -r _new-nbp_    (calls PXE stack PXENV_RESTART_TFTP)\n"
199         "OPTIONS:\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);
203 }
204
205 void pxe_error(int ierr, const char *evt, const char *msg)
206 {
207     if (msg)
208         printf("%s", msg);
209     else if (evt)
210         printf("Error while %s: ", evt);
211     printf("%d:%s\n", ierr, strerror(ierr));
212 }
213
214 int pressanykey(clock_t tm) {
215     int inc;
216
217     printf("Press any key to continue. ");
218     inc = get_key(stdin, tm);
219     puts("");
220     return inc;
221 }
222
223 int dhcp_find_opt(pxe_bootp_t *p, size_t len, uint8_t opt)
224 {
225     int rv = -1;
226     int i, vlen, oplen;
227     uint8_t *d;
228     uint32_t magic;
229
230     if (!p) {
231         dprintf("  packet pointer is null\n");
232         return rv;
233     }
234     vlen = len - ((void *)&(p->vendor) - (void *)p);
235     d = p->vendor.d;
236     magic = ntohl(*((uint32_t *)d));
237     if (magic != VM_RFC1048)    /* Invalid DHCP packet */
238         vlen = 0;
239     for (i = 4; i < vlen; i++) {
240         if (d[i] == opt) {
241             dprintf("\n    @%03X-%2d\n", i, d[i]);
242             rv = i;
243             break;
244         }
245         if (d[i] == ((NUM_DHCP_OPTS) - 1))      /* End of list */
246             break;
247         if (d[i]) {             /* Skip padding */
248             oplen = d[++i];
249             i += oplen;
250         }
251     }
252     return rv;
253 }
254
255 void print_pxe_vendor_raw(pxe_bootp_t *p, size_t len)
256 {
257     int i, vlen;
258
259     if (!p) {
260         printf("  packet pointer is null\n");
261         return;
262     }
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++) {
268         if ((i & 0xf) == 0)
269             printf("\n  %04X:", i);
270         printf(" %02X", p->vendor.d[i]);
271     }
272     printf("\n");
273 }
274
275 void print_pxe_vendor_blk(pxe_bootp_t *p, size_t len)
276 {
277     int i, vlen, oplen, j;
278     uint8_t *d;
279     uint32_t magic;
280     if (!p) {
281         printf("  packet pointer is null\n");
282         return;
283     }
284     vlen = len - ((void *)&(p->vendor) - (void *)p);
285     printf("  Vendor Data:    Len=%d", vlen);
286     d = p->vendor.d;
287     magic = ntohl(*((uint32_t *)d));
288     printf("    Magic: %08X", ntohl(*((uint32_t *)d)));
289     if (magic != VM_RFC1048)    /* Invalid DHCP packet */
290         vlen = 0;
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 */
295             break;
296         if (d[i]) {
297             oplen = d[++i];
298             printf(" l=%3d:", oplen);
299             for (j = (++i + oplen); i < vlen && i < j; i++) {
300                 printf(" %02X", d[i]);
301             }
302             i--;
303         }
304     }
305     printf("\n");
306 }
307
308 void print_pxe_bootp_t(pxe_bootp_t *p, size_t len)
309 {
310     if (!p || len <= 0) {
311         printf("  packet pointer is null\n");
312         return;
313     }
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);
324 }
325
326 void pxe_set_regs(struct syslinux_rm_regs *regs)
327 {
328     com32sys_t tregs;
329
330     regs->ip = 0x7C00;
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);
336     regs->ss = tregs.fs;
337     regs->esp.l = tregs.esi.w[0] + sizeof(tregs);
338     /* Plan B uses [ES:BX] */
339     regs->es = tregs.es;
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;
346 }
347
348 int hostlen_limit(int len)
349 {
350     return min(len, ((PXECHN_HOST_LEN) - 1));
351 }
352
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
357  *              1 in format IP::FN
358  *              2 TFTP URL
359  *              3 HTTP URL
360  *              4 FTP URL
361  *              3 + 2^30 HTTPS URL
362  *              -1 if fn is another URL type
363  */
364 int pxechn_parse_fn(char fn[], in_addr_t *fip, char *host, char *fp[])
365 {
366     in_addr_t tip = 0;
367     char *csep, *ssep, *hsep;   /* Colon, Slash separator positions */
368     int hlen, plen;     /* Hostname, protocol length */
369     int rv = 0;
370
371     csep = strchr(fn, ':');
372     if (csep) {
373         if (csep[1] == ':') {   /* assume IP::FN */
374             *fp = &csep[2];
375             rv = 1;
376             if (fn[0] != ':') {
377                 hlen = hostlen_limit(csep - fn);
378                 memcpy(host, fn, hlen);
379                 host[hlen] = 0;
380             }
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, '/');
385             if (ssep) {
386                 hlen = hostlen_limit(ssep - (csep + 3));
387                 *fp = ssep + 1;
388             } else {
389                 hlen = hostlen_limit(strlen(csep + 3));
390             }
391             memcpy(host, (csep + 3), hlen);
392             host[hlen] = 0;
393             plen = csep - fn;
394             if (strncmp(fn, "tftp", plen) == 0)
395                 rv = 2;
396             else if (strncmp(fn, "http", plen) == 0)
397                 rv = 3;
398             else if (strncmp(fn, "ftp", plen) == 0)
399                 rv = 4;
400             else if (strncmp(fn, "https", plen) == 0)
401                 rv = 3 + ( 1 << 30 );
402             else
403                 rv = -1;
404         } else {
405             csep = NULL;
406         }
407     }
408     if (!csep) {
409         *fp = fn;
410     }
411     if (host[0]) {
412         hsep = strchr(host, '@');
413         if (!hsep)
414             hsep = host;
415         tip = pxe_dns(hsep);
416     }
417     if (tip != 0)
418         *fip = tip;
419     dprintf0("  host '%s'\n  fp   '%s'\n  fip  %08x\n", host, *fp, ntohl(*fip));
420     return rv;
421 }
422
423 void pxechn_opt_free(struct dhcp_option *opt)
424 {
425     free(opt->data);
426     opt->len = -1;
427 }
428
429 void pxechn_fill_pkt(struct pxelinux_opt *pxe, int ptype)
430 {
431     int rv = -1;
432     int p1, p2;
433     if ((ptype < 0) || (ptype > PXECHN_NUM_PKT_TYPE))
434         rv = -2;
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;
443             rv = 0;
444             dprint_pxe_bootp_t((pxe_bootp_t *)(pxe->p[p1].data), pxe->p[p1].len);
445             dpressanykey(INT_MAX);
446         } else {
447             printf("%s: ERROR: Unable to malloc() for second packet\n", app_name_str);
448         }
449     } else {
450         printf("%s: ERROR: Unable to retrieve first packet\n", app_name_str);
451     }
452     if (rv <= -1) {
453         pxechn_opt_free(&pxe->p[p1]);
454     }
455 }
456
457 void pxechn_init(struct pxelinux_opt *pxe)
458 {
459     /* Init for paranoia */
460     pxe->fn = NULL;
461     pxe->fp = NULL;
462     pxe->force = 0;
463     pxe->wait = 0;
464     pxe->gip = 0;
465     pxe->wds = 0;
466     pxe->host[0] = 0;
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;
472         }
473         pxe->p_unpacked[j] = 0;
474         pxe->p[j].data = NULL;
475         pxe->p[j+PXECHN_NUM_PKT_TYPE].data = NULL;
476         pxe->p[j].len = 0;
477         pxe->p[j+PXECHN_NUM_PKT_TYPE].len = 0;
478     }
479     pxechn_fill_pkt(pxe, PXENV_PACKET_TYPE_CACHED_REPLY);
480 }
481
482 int pxechn_to_hex(char i)
483 {
484     if (i >= '0' && i <= '9')
485         return (i - '0');
486     if (i >= 'A' && i <= 'F')
487         return (i - 'A' + 10);
488     if (i >= 'a' && i <= 'f')
489         return (i - 'a' + 10);
490     if (i == 0)
491         return -1;
492     return -2;
493 }
494
495 int pxechn_parse_2bhex(char ins[])
496 {
497     int ret = -2;
498     int n0 = -3, n1 = -3;
499     /* NULL pointer */
500     if (!ins) {
501         ret = -1;
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 */
508         ret = -1;
509     }
510     return ret;
511 }
512
513 int pxechn_optnum_ok(int optnum)
514 {
515     if ((optnum > 0) && (optnum < ((NUM_DHCP_OPTS) - 1)))
516         return 1;
517     return 0;
518 }
519
520 int pxechn_optnum_ok_notres(int optnum)
521 {
522     if ((optnum <= 0) && (optnum >= ((NUM_DHCP_OPTS) - 1)))
523         return 0;
524     switch(optnum){
525     case 66: case 67:
526         return 0;
527         break;
528     default:    return 1;
529     }
530 }
531
532 int pxechn_optlen_ok(int optlen)
533 {
534     if ((optlen >= 0) && (optlen < ((DHCP_OPT_LEN_MAX) - 1)))
535         return 1;
536     return 0;
537 }
538
539 int pxechn_setopt(struct dhcp_option *opt, void *data, int len)
540 {
541     void *p;
542     if (!opt || !data)
543         return -1;
544     if (len < 0) {
545         return -3;
546     }
547     p = realloc(opt->data, len);
548     if (!p && len) {    /* Allow for len=0 */
549         pxechn_opt_free(opt);
550         return -2;
551     }
552     opt->data = p;
553     memcpy(opt->data, data, len);
554     opt->len = len;
555     return len;
556 }
557
558 int pxechn_setopt_str(struct dhcp_option *opt, void *data)
559 {
560     return pxechn_setopt(opt, data, strnlen(data, DHCP_OPT_LEN_MAX));
561 }
562
563 int pxechn_parse_int(char *data, char istr[], int tlen)
564 {
565     int terr = errno;
566
567     if ((tlen == 1) || (tlen == 2) || (tlen == 4)) {
568         errno = 0;
569         uint32_t optval = strtoul(istr, NULL, 0);
570         if (errno)
571             return -3;
572         errno = terr;
573         switch(tlen){
574         case  1:
575             if (optval & 0xFFFFFF00)
576                 return -4;
577             break;
578         case  2:
579             if (optval & 0xFFFF0000)
580                 return -4;
581             optval = htons(optval);
582             break;
583         case  4:
584             optval = htonl(optval);
585             break;
586         }
587         memcpy(data, &optval, tlen);
588     } else if (tlen == 8) {
589         errno = 0;
590         uint64_t optval = strtoull(istr, NULL, 0);
591         if (errno)
592             return -3;
593         errno = terr;
594         optval = htonq(optval);
595         memcpy(data, &optval, tlen);
596     } else {
597         return -2;
598     }
599     return tlen;
600 }
601
602 int pxechn_parse_hex_sep(char *data, char istr[], char sep)
603 {
604     int len = 0;
605     int ipos = 0, ichar;
606     
607     if (!data || !istr)
608         return -1;
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);
612         if (ichar >=0) {
613             data[len++] = ichar;
614         } else {
615             return -EINVAL;
616         }
617         if (!istr[ipos+2]){
618             ipos += 2;
619         } else if (istr[ipos+2] != sep) {
620             return -(EINVAL + 1);
621         } else {
622             ipos += 3;
623         }
624     }
625     return len;
626 }
627
628 int pxechn_parse_opttype(char istr[], int optnum)
629 {
630     char *pos;
631     int tlen, type, tmask;
632
633     if (!istr)
634         return -1;
635     pos = strchr(istr, '=');
636     if (!pos)
637         return -2;
638     if (istr[0] != '.') {
639         if (!pxechn_optnum_ok(optnum))
640             return -3;
641         return -3;      /* do lookup here */
642     } else {
643         tlen = pos - istr - 1;
644         if ((tlen < 1) || (tlen > 4))
645             return -4;
646         tmask = 0xFFFFFFFF >> (8 * (4 - tlen));
647         type = (*(int*)(istr + 1)) & tmask;
648     }
649     return type;
650 }
651
652 int pxechn_parse_setopt(struct dhcp_option opts[], struct dhcp_option *iopt,
653                         char istr[])
654 {
655     int rv = 0, optnum, opttype;
656     char *cpos = NULL, *pos;
657
658     if (!opts || !iopt || !(iopt->data))
659         return -1;
660     if (!istr || !istr[0])
661         return -2;
662     // -EINVAL;
663     optnum = strtoul(istr, &cpos, 0);
664     if (!pxechn_optnum_ok(optnum))
665         return -3;
666     pos = strchr(cpos, '=');
667     if (!pos)
668         return -4;
669     opttype = pxechn_parse_opttype(cpos, optnum);
670     pos++;
671     switch(opttype) {
672     case 'b':
673         iopt->len = pxechn_parse_int(iopt->data, pos, 1);
674         break;
675     case 'l':
676         iopt->len = pxechn_parse_int(iopt->data, pos, 4);
677         break;
678     case 'q':
679         iopt->len = pxechn_parse_int(iopt->data, pos, 8);
680         break;
681     case 's':
682     case STRASINT_str:
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);
688         break;
689     case 'w':
690         iopt->len = pxechn_parse_int(iopt->data, pos, 2);
691         break;
692     case 'x':
693         iopt->len = pxechn_parse_hex_sep(iopt->data, pos, ':');
694         break;
695     default:
696         return -6;
697         break;
698     }
699     if (pxechn_optlen_ok(iopt->len)) {
700         rv = pxechn_setopt(&(opts[optnum]), (void *)(iopt->data), iopt->len);
701     }
702     if((opttype == 's') || (opttype == STRASINT_str))
703         dprintf_pc_so_s("rv=%d\n", rv);
704     return rv;
705 }
706
707 int pxechn_parse_force(const char istr[])
708 {
709     uint32_t rv = 0;
710     char *pos;
711     int terr = errno;
712
713     errno = 0;
714     rv = strtoul(istr, &pos, 0);
715     if ((istr == pos ) || ((rv == ULONG_MAX) && (errno)))
716         rv = 0;
717     errno = terr;
718     return rv;
719 }
720
721 int pxechn_uuid_set(struct pxelinux_opt *pxe)
722 {
723     int ret = 0;
724
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]);
728     if (ret) {
729         error("Could not unpack packet\n");
730         return -ret;    /* dhcp_unpack_packet always returns positive errors */
731     }
732
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);
735         return 1;
736     return 0;
737 }
738
739 int pxechn_parse_args(int argc, char *argv[], struct pxelinux_opt *pxe,
740                          struct dhcp_option opts[])
741 {
742     int arg, optnum, rv = 0;
743     char *p = NULL;
744     const char optstr[] = "c:f:g:o:p:t:uwW";
745     struct dhcp_option iopt;
746
747     if (pxe->p[5].data)
748         pxe->fip = ( (pxe_bootp_t *)(pxe->p[5].data) )->sip;
749     else
750         pxe->fip = 0;
751     /* Fill */
752     pxe->fn = argv[0];
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);
757     iopt.len = 0;
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 : "");
760         switch(arg) {
761         case 'c':       /* config */
762             pxechn_setopt_str(&(opts[209]), optarg);
763             break;
764         case 'f':       /* force */
765             pxe->force = pxechn_parse_force(optarg);
766             break;
767         case 'g':       /* gateway/DHCP relay */
768             pxe->gip = pxe_dns(optarg);
769             break;
770         case 'n':       /* native */
771             break;
772         case 'o':       /* option */
773             rv = pxechn_parse_setopt(opts, &iopt, optarg);
774             break;
775         case 'p':       /* prefix */
776             pxechn_setopt_str(&(opts[210]), optarg);
777             break;
778         case 't':       /* timeout */
779             optnum = strtoul(optarg, &p, 0);
780             if (p != optarg) {
781                 optnum = htonl(optnum);
782                 pxechn_setopt(&(opts[211]), (void *)(&optnum), 4);
783             } else {
784                 rv = -3;
785             }
786             break;
787         case 'u':       /* UUID: copy option 97 from packet 1 if present */
788             pxechn_uuid_set(pxe);
789             break;
790         case 'w':       /* wait */
791             pxe->wait = 1;
792             break;
793         case 'W':       /* WDS */
794             pxe->wds = 1;
795             break;
796         case '?':
797             rv = -'?';
798         default:
799             break;
800         }
801         if (rv >= 0)    /* Clear it since getopt() doesn't guarentee it */
802             optarg = NULL;
803     }
804     if (iopt.data)
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 */
808     if (rv >= 0) {
809         rv = 0;
810     } else if (arg != '?') {
811         printf("Invalid argument for -%c: %s\n", arg, optarg);
812     }
813     dprintf("pxechn_parse_args rv=%d\n", rv);
814     return rv;
815 }
816
817 int pxechn_args(int argc, char *argv[], struct pxelinux_opt *pxe)
818 {
819     pxe_bootp_t *bootp0, *bootp1;
820     int ret = 0;
821     struct dhcp_option *opts;
822
823     opts = pxe->opts[2];
824     /* Start filling packet #1 */
825     bootp0 = (pxe_bootp_t *)(pxe->p[2].data);
826     bootp1 = (pxe_bootp_t *)(pxe->p[5].data);
827
828     ret = dhcp_unpack_packet(bootp0, pxe->p[2].len, opts);
829     if (ret) {
830         error("Could not unpack packet\n");
831         return -ret;
832     }
833     pxe->p_unpacked[2] = 1;
834     pxe->gip = bootp1->gip;
835
836     ret = pxechn_parse_args(argc, argv, pxe, opts);
837     if (ret)
838         return ret;
839     bootp1->sip = pxe->fip;
840     bootp1->gip = pxe->gip;
841
842     ret = dhcp_pack_packet(bootp1, (size_t *)&(pxe->p[5].len), opts);
843     if (ret) {
844         error("Could not pack packet\n");
845         return -ret;    /* dhcp_pack_packet always returns positive errors */
846     }
847     return ret;
848 }
849
850 /* dhcp_pkt2pxe: Copy packet to PXE's BC data for a ptype packet
851  *      Input:
852  *      p       Packet data to copy
853  *      len     length of data to copy
854  *      ptype   Packet type to overwrite
855  */
856 int dhcp_pkt2pxe(pxe_bootp_t *p, size_t len, int ptype)
857 {
858     com32sys_t reg;
859     t_PXENV_GET_CACHED_INFO *ci;
860     void *cp;
861     int rv = -1;
862
863     if (!(ci = lzalloc(sizeof(t_PXENV_GET_CACHED_INFO)))){
864         dprintf("Unable to lzalloc() for PXE call structure\n");
865         rv = 1;
866         goto ret;
867     }
868     ci->Status = PXENV_STATUS_FAILURE;
869     ci->PacketType = ptype;
870     memset(&reg, 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);
874     reg.es = SEG(ci);
875     __intcall(0x22, &reg, &reg);
876
877     if (ci->Status != PXENV_STATUS_SUCCESS) {
878         dprintf("PXE Get Cached Info failed: %d\n", ci->Status);
879         rv = 2;
880         goto ret;
881     }
882
883     cp = MK_PTR(ci->Buffer.seg, ci->Buffer.offs);
884     if (!(memcpy(cp, p, len))) {
885         dprintf("Failed to copy packet\n");
886         rv = 3;
887         goto ret;
888     }
889 ret:
890     lfree(ci);
891    return rv;
892 }
893
894 int pxechn_mergeopt(struct pxelinux_opt *pxe, int d, int s)
895 {
896     int ret = 0, i;
897
898     if ((d >= PXECHN_NUM_PKT_TYPE) || (s >= PXECHN_NUM_PKT_TYPE) 
899             || (d < 0) || (s < 0)) {
900         return -2;
901     }
902     if (!pxe->p_unpacked[s])
903         ret = dhcp_unpack_packet(pxe->p[s].data, pxe->p[s].len, pxe->opts[s]);
904     if (ret) {
905         error("Could not unpack packet for merge\n");
906         printf("Error %d (%d)\n", ret, EINVAL);
907         if (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);
912             else
913                 error("Unknown EINVAL error\n");
914         } else {
915             error("Unknown error\n");
916         }
917         return -ret;
918     }
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);
923         }
924     }
925     return 0;
926 }
927
928 /* pxechn: Chainload to new PXE file ourselves
929  *      Input:
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
935  *              -1 on usage error
936  */
937 int pxechn(int argc, char *argv[])
938 {
939     struct pxelinux_opt pxe;
940     pxe_bootp_t* p[(2 * PXECHN_NUM_PKT_TYPE)];
941     int rv = 0;
942     int i;
943     struct data_area file;
944     struct syslinux_rm_regs regs;
945
946     pxechn_init(&pxe);
947     for (i = 0; i < (2 * PXECHN_NUM_PKT_TYPE); i++) {
948         p[i] = (pxe_bootp_t *)(pxe.p[i].data);
949     }
950
951     /* Parse arguments and patch packet 1 */
952     rv = pxechn_args(argc, argv, &pxe);
953     dpressanykey(INT_MAX);
954     if (rv)
955         goto ret;
956     pxe_set_regs(&regs);
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);
961         rv = -2;
962         goto ret;
963     }
964     puts("loaded.");
965     /* we'll be shuffling to the standard location of 7C00h */
966     file.base = 0x7C00;
967     if ((pxe.wds) || 
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.
971         if ((pxe.wds) ||
972                 (pxe.force & PXECHN_FORCE_PKT2)) {
973             pxechn_fill_pkt(&pxe, PXENV_PACKET_TYPE_DHCP_ACK);
974             rv = pxechn_mergeopt(&pxe, 2, 1);
975             if (rv) {
976                 dprintf("Merge Option returned %d\n", rv);
977             }
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);
980         }
981         if (pxe.force & PXECHN_FORCE_PKT1) {
982             puts("Unimplemented force option utilized");
983         }
984     }
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);
987     if ((pxe.wds) ||
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.
991         if ((pxe.wds) ||
992                 (pxe.force & PXECHN_FORCE_PKT2)) {
993             rv = dhcp_pkt2pxe(p[5], pxe.p[5].len, PXENV_PACKET_TYPE_DHCP_ACK);
994         }
995     } else if (pxe.force) {
996         printf("FORCE: bad argument %08X\n", pxe.force);
997     }
998     printf("\n...Ready to boot:\n");
999     if (pxe.wait) {
1000         pressanykey(INT_MAX);
1001     } else {
1002         dpressanykey(INT_MAX);
1003     }
1004     if (true) {
1005         puts("  Attempting to boot...");
1006         do_boot(&file, 1, &regs);
1007     }
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);
1013         }
1014     }
1015 ret:
1016     return rv;
1017 }
1018
1019 /* pxe_restart: Restart the PXE environment with a new PXE file
1020  *      Input:
1021  *      ifn     Name of file to chainload to in a format PXELINUX understands
1022  *              This must strictly be TFTP or relative file
1023  */
1024 int pxe_restart(char *ifn)
1025 {
1026     int rv = 0;
1027     struct pxelinux_opt pxe;
1028     com32sys_t reg;
1029     t_PXENV_RESTART_TFTP *pxep; /* PXENV callback Parameter */
1030
1031     pxe.fn = ifn;
1032     pxechn_fill_pkt(&pxe, PXENV_PACKET_TYPE_CACHED_REPLY);
1033     if (pxe.p[5].data)
1034         pxe.fip = ( (pxe_bootp_t *)(pxe.p[5].data) )->sip;
1035     else
1036         pxe.fip = 0;
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);
1040         goto ret;
1041     }
1042     printf("  Attempting to boot '%s'...\n\n", pxe.fn);
1043     memset(&reg, 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");
1049         goto ret;
1050     }
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);
1063     reg.es = SEG(pxep);
1064
1065     __intcall(0x22, &reg, &reg);
1066
1067     printf("PXENV_RESTART_TFTP returned %d\n", pxep->Status);
1068     if (pxep != __com32.cs_bounce)
1069         lfree(pxep);
1070
1071 ret:
1072     return rv;
1073 }
1074
1075 /* pxechn_gpxe: Use gPXE to chainload a new NBP
1076  *      Input:
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
1081  *              -1 on usage error
1082  */
1083 //FIXME:Implement
1084 int pxechn_gpxe(int argc, char *argv[])
1085 {
1086     int rv = 0;
1087     struct pxelinux_opt pxe;
1088
1089     if (argc) {
1090         printf("%s\n", argv[0]);
1091         pxechn_args(argc, argv, &pxe);
1092     }
1093     return rv;
1094 }
1095
1096 int main(int argc, char *argv[])
1097 {
1098     int rv= -1;
1099     int err;
1100     const struct syslinux_version *sv;
1101
1102     /* Initialization */
1103     err = errno;
1104     console_ansi_raw(); /* sets errno = 9 (EBADF) */
1105     /* printf("%d %d\n", err, errno); */
1106     errno = err;
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 */
1111     }
1112     if (argc == 2) {
1113         if ((strcasecmp(argv[1], "-h") == 0) || ((strcmp(argv[1], "-?") == 0))
1114                 || (strcasecmp(argv[1], "--help") == 0)) {
1115             argc = 1;
1116         } else {
1117             rv = pxechn(argc - 1, &argv[1]);
1118         }
1119     } else if (argc >= 3) {
1120         if ((strcmp(argv[1], "-r") == 0)) {
1121             if (argc == 3)
1122                 rv = pxe_restart(argv[2]);
1123         } else {
1124             rv = pxechn(argc - 1, &argv[1]);
1125         }
1126     }
1127     if (rv <= -1 ) {
1128         usage();
1129         rv = 1;
1130     }
1131     return rv;
1132 }