Merge remote-tracking branch 'hdt/master'
[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 #define PXECHN_DEBUG 1
47
48 typedef union {
49     uint64_t q;
50     uint32_t l[2];
51     uint16_t w[4];
52     uint8_t b[8];
53 } reg64_t;
54
55 #define dprintf0(f, ...)                ((void)0)
56
57 #if (PXECHN_DEBUG > 0)
58 #  define dpressanykey                  pressanykey
59 #  define dprintf                       printf
60 #  define dprint_pxe_bootp_t            print_pxe_bootp_t
61 #  define dprint_pxe_vendor_blk         print_pxe_vendor_blk
62 #  define dprint_pxe_vendor_raw         print_pxe_vendor_raw
63 #else
64 #  define dpressanykey(tm)              ((void)0)
65 #  define dprintf(f, ...)               ((void)0)
66 #  define dprint_pxe_bootp_t(p, l)      ((void)0)
67 #  define dprint_pxe_vendor_blk(p, l)   ((void)0)
68 #  define dprint_pxe_vendor_raw(p, l)   ((void)0)
69 #endif
70
71 #define dprintf_opt_cp          dprintf0
72 #define dprintf_opt_inj         dprintf0
73 #define dprintf_pc_pa           dprintf
74 #define dprintf_pc_so_s         dprintf0
75
76 #define t_PXENV_RESTART_TFTP    t_PXENV_TFTP_READ_FILE
77
78 #define STACK_SPLIT     11
79
80 /* same as pxelinux.asm REBOOT_TIME */
81 #define REBOOT_TIME     300
82
83 #define NUM_DHCP_OPTS           256
84 #define DHCP_OPT_LEN_MAX        256
85 #define PXE_VENDOR_RAW_PRN_MAX  0x7F
86 #define PXECHN_HOST_LEN         256     /* 63 bytes per label; 255 max total */
87
88 #define PXECHN_NUM_PKT_TYPE     3
89 #define PXECHN_NUM_PKT_AVAIL    2*PXECHN_NUM_PKT_TYPE
90 #define PXECHN_PKT_TYPE_START   PXENV_PACKET_TYPE_DHCP_DISCOVER
91
92 #define PXECHN_FORCE_PKT1       0x80000000
93 #define PXECHN_FORCE_PKT2       0x40000000
94 #define PXECHN_FORCE_ALL        (PXECHN_FORCE_PKT1 | PXECHN_FORCE_PKT2)
95 #define PXECHN_FORCE_ALL_1      0
96 #define STRASINT_str            ('s' + (('t' + ('r' << 8)) << 8))
97
98 #define min(a,b) (((a) < (b)) ? (a) : (b))
99
100 const char app_name_str[] = "pxechn.c32";
101
102 struct pxelinux_opt {
103     char *fn;   /* Filename as passed to us */
104     in_addr_t fip;      /* fn's IP component */
105     char *fp;   /* fn's path component */
106     in_addr_t gip;      /* giaddr; Gateway/DHCP relay */
107     uint32_t force;
108     uint32_t wait;      /* Additional decision to wait before boot */
109     int32_t wds;        /* WDS option/level */
110     struct dhcp_option p[PXECHN_NUM_PKT_AVAIL];
111         /* original _DHCP_DISCOVER, _DHCP_ACK, _CACHED_REPLY then modified packets */
112     char host[PXECHN_HOST_LEN];
113     struct dhcp_option opts[PXECHN_NUM_PKT_TYPE][NUM_DHCP_OPTS];
114     char p_unpacked[PXECHN_NUM_PKT_TYPE];
115 };
116
117
118 /* from chain.c */
119 struct data_area {
120     void *data;
121     addr_t base;
122     addr_t size;
123 };
124
125 /* From chain.c */
126 static inline void error(const char *msg)
127 {
128     fputs(msg, stderr);
129 }
130
131 /* From chain.c */
132 static void do_boot(struct data_area *data, int ndata,
133                     struct syslinux_rm_regs *regs)
134 {
135     uint16_t *const bios_fbm = (uint16_t *) 0x413;
136     addr_t dosmem = *bios_fbm << 10;    /* Technically a low bound */
137     struct syslinux_memmap *mmap;
138     struct syslinux_movelist *mlist = NULL;
139     addr_t endimage;
140     int i;
141
142     mmap = syslinux_memory_map();
143
144     if (!mmap) {
145         error("Cannot read system memory map\n");
146         return;
147     }
148
149     endimage = 0;
150     for (i = 0; i < ndata; i++) {
151         if (data[i].base + data[i].size > endimage)
152             endimage = data[i].base + data[i].size;
153     }
154     if (endimage > dosmem)
155         goto too_big;
156
157     for (i = 0; i < ndata; i++) {
158         if (syslinux_add_movelist(&mlist, data[i].base,
159                                   (addr_t) data[i].data, data[i].size))
160             goto enomem;
161     }
162
163
164     /* Tell the shuffler not to muck with this area... */
165     syslinux_add_memmap(&mmap, endimage, 0xa0000 - endimage, SMT_RESERVED);
166
167     /* Force text mode */
168     syslinux_force_text_mode();
169
170     fputs("Booting...\n", stdout);
171     syslinux_shuffle_boot_rm(mlist, mmap, 3, regs);
172     error("Chainboot failed!\n");
173     return;
174
175 too_big:
176     error("Loader file too large\n");
177     return;
178
179 enomem:
180     error("Out of memory\n");
181     return;
182 }
183
184 void usage(void)
185 {
186     printf("USAGE:\n"
187         "    %s [OPTIONS]... _new-nbp_\n"
188         "    %s -r _new-nbp_    (calls PXE stack PXENV_RESTART_TFTP)\n"
189         "OPTIONS:\n"
190         "    [-c config] [-g gateway] [-p prefix] [-t reboot] [-u] [-w] [-W]"
191         " [-o opt.ty=val]\n\n",
192         app_name_str, app_name_str);
193 }
194
195 void pxe_error(int ierr, const char *evt, const char *msg)
196 {
197     if (msg)
198         printf("%s", msg);
199     else if (evt)
200         printf("Error while %s: ", evt);
201     printf("%d:%s\n", ierr, strerror(ierr));
202 }
203
204 int pressanykey(clock_t tm) {
205     int inc;
206
207     printf("Press any key to continue. ");
208     inc = get_key(stdin, tm);
209     puts("");
210     return inc;
211 }
212
213 int dhcp_find_opt(pxe_bootp_t *p, size_t len, uint8_t opt)
214 {
215     int rv = -1;
216     int i, vlen, oplen;
217     uint8_t *d;
218     uint32_t magic;
219
220     if (!p) {
221         dprintf("  packet pointer is null\n");
222         return rv;
223     }
224     vlen = len - ((void *)&(p->vendor) - (void *)p);
225     d = p->vendor.d;
226     magic = ntohl(*((uint32_t *)d));
227     if (magic != VM_RFC1048)    /* Invalid DHCP packet */
228         vlen = 0;
229     for (i = 4; i < vlen; i++) {
230         if (d[i] == opt) {
231             dprintf("\n    @%03X-%2d\n", i, d[i]);
232             rv = i;
233             break;
234         }
235         if (d[i] == ((NUM_DHCP_OPTS) - 1))      /* End of list */
236             break;
237         if (d[i]) {             /* Skip padding */
238             oplen = d[++i];
239             i += oplen;
240         }
241     }
242     return rv;
243 }
244
245 void print_pxe_vendor_raw(pxe_bootp_t *p, size_t len)
246 {
247     int i, vlen;
248
249     if (!p) {
250         printf("  packet pointer is null\n");
251         return;
252     }
253     vlen = len - ((void *)&(p->vendor) - (void *)p);
254     if (vlen > PXE_VENDOR_RAW_PRN_MAX)
255         vlen = PXE_VENDOR_RAW_PRN_MAX;
256     dprintf("  rawLen = %d", vlen);
257     for (i = 0; i < vlen; i++) {
258         if ((i & 0xf) == 0)
259             printf("\n  %04X:", i);
260         printf(" %02X", p->vendor.d[i]);
261     }
262     printf("\n");
263 }
264
265 void print_pxe_vendor_blk(pxe_bootp_t *p, size_t len)
266 {
267     int i, vlen, oplen, j;
268     uint8_t *d;
269     uint32_t magic;
270     if (!p) {
271         printf("  packet pointer is null\n");
272         return;
273     }
274     vlen = len - ((void *)&(p->vendor) - (void *)p);
275     printf("  Vendor Data:    Len=%d", vlen);
276     d = p->vendor.d;
277     magic = ntohl(*((uint32_t *)d));
278     printf("    Magic: %08X", ntohl(*((uint32_t *)d)));
279     if (magic != VM_RFC1048)    /* Invalid DHCP packet */
280         vlen = 0;
281     for (i = 4; i < vlen; i++) {
282         if (d[i])       /* Skip the padding */
283             printf("\n    @%03X-%3d", i, d[i]);
284         if (d[i] == ((NUM_DHCP_OPTS) - 1))      /* End of list */
285             break;
286         if (d[i]) {
287             oplen = d[++i];
288             printf(" l=%3d:", oplen);
289             for (j = (++i + oplen); i < vlen && i < j; i++) {
290                 printf(" %02X", d[i]);
291             }
292             i--;
293         }
294     }
295     printf("\n");
296 }
297
298 void print_pxe_bootp_t(pxe_bootp_t *p, size_t len)
299 {
300     if (!p || len <= 0) {
301         printf("  packet pointer is null\n");
302         return;
303     }
304     printf("  op:%02X  hw:%02X  hl:%02X  gh:%02X  id:%08X se:%04X f:%04X"
305         "  cip:%08X\n", p->opcode, p->Hardware, p->Hardlen, p->Gatehops,
306         ntohl(p->ident), ntohs(p->seconds), ntohs(p->Flags), ntohl(p->cip));
307     printf("  yip:%08X  sip:%08X  gip:%08X",
308         ntohl(p->yip), ntohl(p->sip), ntohl(p->gip));
309     printf("  caddr-%02X:%02X:%02X:%02X:%02X:%02X\n", p->CAddr[0],
310         p->CAddr[1], p->CAddr[2], p->CAddr[3], p->CAddr[4], p->CAddr[5]);
311     printf("  sName: '%s'\n", p->Sname);
312     printf("  bootfile: '%s'\n", p->bootfile);
313     dprint_pxe_vendor_blk(p, len);
314 }
315
316 void pxe_set_regs(struct syslinux_rm_regs *regs)
317 {
318     com32sys_t tregs;
319
320     regs->ip = 0x7C00;
321     /* Plan A uses SS:[SP + 4] */
322     /* sdi->pxe.stack is a usable pointer, not something that can be nicely
323        and reliably split to SS:SP without causing issues */
324     tregs.eax.l = 0x000A;
325     __intcall(0x22, &tregs, &tregs);
326     regs->ss = tregs.fs;
327     regs->esp.l = tregs.esi.w[0] + sizeof(tregs);
328     /* Plan B uses [ES:BX] */
329     regs->es = tregs.es;
330     regs->ebx = tregs.ebx;
331     dprintf("\nsp:%04x    ss:%04x    es:%04x    bx:%04x\n", regs->esp.w[0],
332         regs->ss, regs->es, regs->ebx.w[0]);
333     /* Zero out everything else just to be sure */
334     regs->cs = regs->ds = regs->fs = regs->gs = 0;
335     regs->eax.l = regs->ecx.l = regs->edx.l = 0;
336 }
337
338 int hostlen_limit(int len)
339 {
340     return min(len, ((PXECHN_HOST_LEN) - 1));
341 }
342
343 //FIXME: To a library
344 /* Parse a filename into an IPv4 address and filename pointer
345  *      returns Based on the interpretation of fn
346  *              0 regular file name
347  *              1 in format IP::FN
348  *              2 TFTP URL
349  *              3 HTTP URL
350  *              4 FTP URL
351  *              3 + 2^30 HTTPS URL
352  *              -1 if fn is another URL type
353  */
354 int pxechn_parse_fn(char fn[], in_addr_t *fip, char *host, char *fp[])
355 {
356     in_addr_t tip = 0;
357     char *csep, *ssep, *hsep;   /* Colon, Slash separator positions */
358     int hlen, plen;     /* Hostname, protocol length */
359     int rv = 0;
360
361     csep = strchr(fn, ':');
362     if (csep) {
363         if (csep[1] == ':') {   /* assume IP::FN */
364             *fp = &csep[2];
365             rv = 1;
366             if (fn[0] != ':') {
367                 hlen = hostlen_limit(csep - fn);
368                 memcpy(host, fn, hlen);
369                 host[hlen] = 0;
370             }
371         } else if ((csep[1] == '/') && (csep[2] == '/')) {
372                 /* URL: proto://host:port/path/file */
373                 /* proto://[user[:passwd]@]host[:port]/path/file */
374             ssep = strchr(csep + 3, '/');
375             if (ssep) {
376                 hlen = hostlen_limit(ssep - (csep + 3));
377                 *fp = ssep + 1;
378             } else {
379                 hlen = hostlen_limit(strlen(csep + 3));
380             }
381             memcpy(host, (csep + 3), hlen);
382             host[hlen] = 0;
383             plen = csep - fn;
384             if (strncmp(fn, "tftp", plen) == 0)
385                 rv = 2;
386             else if (strncmp(fn, "http", plen) == 0)
387                 rv = 3;
388             else if (strncmp(fn, "ftp", plen) == 0)
389                 rv = 4;
390             else if (strncmp(fn, "https", plen) == 0)
391                 rv = 3 + ( 1 << 30 );
392             else
393                 rv = -1;
394         } else {
395             csep = NULL;
396         }
397     }
398     if (!csep) {
399         *fp = fn;
400     }
401     if (host[0]) {
402         hsep = strchr(host, '@');
403         if (!hsep)
404             hsep = host;
405         tip = pxe_dns(hsep);
406     }
407     if (tip != 0)
408         *fip = tip;
409     dprintf0("  host '%s'\n  fp   '%s'\n  fip  %08x\n", host, *fp, ntohl(*fip));
410     return rv;
411 }
412
413 void pxechn_opt_free(struct dhcp_option *opt)
414 {
415     free(opt->data);
416     opt->len = -1;
417 }
418
419 void pxechn_fill_pkt(struct pxelinux_opt *pxe, int ptype)
420 {
421     int rv = -1;
422     int p1, p2;
423     if ((ptype < 0) || (ptype > PXECHN_NUM_PKT_TYPE))
424         rv = -2;
425     p1 = ptype - PXECHN_PKT_TYPE_START;
426     p2 = p1 + PXECHN_NUM_PKT_TYPE;
427     if ((rv >= -1) && (!pxe_get_cached_info(ptype,
428             (void **)&(pxe->p[p1].data), (size_t *)&(pxe->p[p1].len)))) {
429         pxe->p[p2].data = malloc(2048);
430         if (pxe->p[p2].data) {
431             memcpy(pxe->p[p2].data, pxe->p[p1].data, pxe->p[p1].len);
432             pxe->p[p2].len = pxe->p[p1].len;
433             rv = 0;
434             dprint_pxe_bootp_t((pxe_bootp_t *)(pxe->p[p1].data), pxe->p[p1].len);
435             dpressanykey(INT_MAX);
436         } else {
437             printf("%s: ERROR: Unable to malloc() for second packet\n", app_name_str);
438         }
439     } else {
440         printf("%s: ERROR: Unable to retrieve first packet\n", app_name_str);
441     }
442     if (rv <= -1) {
443         pxechn_opt_free(&pxe->p[p1]);
444     }
445 }
446
447 void pxechn_init(struct pxelinux_opt *pxe)
448 {
449     /* Init for paranoia */
450     pxe->fn = NULL;
451     pxe->fp = NULL;
452     pxe->force = 0;
453     pxe->wait = 0;
454     pxe->gip = 0;
455     pxe->wds = 0;
456     pxe->host[0] = 0;
457     pxe->host[((NUM_DHCP_OPTS) - 1)] = 0;
458     for (int j = 0; j < PXECHN_NUM_PKT_TYPE; j++){
459         for (int i = 0; i < NUM_DHCP_OPTS; i++) {
460             pxe->opts[j][i].data = NULL;
461             pxe->opts[j][i].len = -1;
462         }
463         pxe->p_unpacked[j] = 0;
464         pxe->p[j].data = NULL;
465         pxe->p[j+PXECHN_NUM_PKT_TYPE].data = NULL;
466         pxe->p[j].len = 0;
467         pxe->p[j+PXECHN_NUM_PKT_TYPE].len = 0;
468     }
469     pxechn_fill_pkt(pxe, PXENV_PACKET_TYPE_CACHED_REPLY);
470 }
471
472 int pxechn_to_hex(char i)
473 {
474     if (i >= '0' && i <= '9')
475         return (i - '0');
476     if (i >= 'A' && i <= 'F')
477         return (i - 'A' + 10);
478     if (i >= 'a' && i <= 'f')
479         return (i - 'a' + 10);
480     if (i == 0)
481         return -1;
482     return -2;
483 }
484
485 int pxechn_parse_2bhex(char ins[])
486 {
487     int ret = -2;
488     int n0 = -3, n1 = -3;
489     /* NULL pointer */
490     if (!ins) {
491         ret = -1;
492     /* pxechn_to_hex can handle the NULL character by returning -1 and
493        breaking the execution of the statement chain */
494     } else if (((n0 = pxechn_to_hex(ins[0])) >= 0)
495             && ((n1 = pxechn_to_hex(ins[1])) >= 0)) {
496         ret = (n0 * 16) + n1;
497     } else if (n0 == -1) {      /* Leading NULL char */
498         ret = -1;
499     }
500     return ret;
501 }
502
503 int pxechn_optnum_ok(int optnum)
504 {
505     if ((optnum > 0) && (optnum < ((NUM_DHCP_OPTS) - 1)))
506         return 1;
507     return 0;
508 }
509
510 int pxechn_optnum_ok_notres(int optnum)
511 {
512     if ((optnum <= 0) && (optnum >= ((NUM_DHCP_OPTS) - 1)))
513         return 0;
514     switch(optnum){
515     case 66: case 67:
516         return 0;
517         break;
518     default:    return 1;
519     }
520 }
521
522 int pxechn_optlen_ok(int optlen)
523 {
524     if ((optlen >= 0) && (optlen < ((DHCP_OPT_LEN_MAX) - 1)))
525         return 1;
526     return 0;
527 }
528
529 int pxechn_setopt(struct dhcp_option *opt, void *data, int len)
530 {
531     void *p;
532     if (!opt || !data)
533         return -1;
534     if (len < 0) {
535         return -3;
536     }
537     p = realloc(opt->data, len);
538     if (!p && len) {    /* Allow for len=0 */
539         pxechn_opt_free(opt);
540         return -2;
541     }
542     opt->data = p;
543     memcpy(opt->data, data, len);
544     opt->len = len;
545     return len;
546 }
547
548 int pxechn_setopt_str(struct dhcp_option *opt, void *data)
549 {
550     return pxechn_setopt(opt, data, strnlen(data, DHCP_OPT_LEN_MAX));
551 }
552
553 int pxechn_parse_int(char *data, char istr[], int tlen)
554 {
555     int terr = errno;
556
557     if ((tlen == 1) || (tlen == 2) || (tlen == 4)) {
558         errno = 0;
559         uint32_t optval = strtoul(istr, NULL, 0);
560         if (errno)
561             return -3;
562         errno = terr;
563         switch(tlen){
564         case  1:
565             if (optval & 0xFFFFFF00)
566                 return -4;
567             break;
568         case  2:
569             if (optval & 0xFFFF0000)
570                 return -4;
571             optval = htons(optval);
572             break;
573         case  4:
574             optval = htonl(optval);
575             break;
576         }
577         memcpy(data, &optval, tlen);
578     } else if (tlen == 8) {
579         errno = 0;
580         uint64_t optval = strtoull(istr, NULL, 0);
581         if (errno)
582             return -3;
583         errno = terr;
584         optval = htonq(optval);
585         memcpy(data, &optval, tlen);
586     } else {
587         return -2;
588     }
589     return tlen;
590 }
591
592 int pxechn_parse_hex_sep(char *data, char istr[], char sep)
593 {
594     int len = 0;
595     int ipos = 0, ichar;
596     
597     if (!data || !istr)
598         return -1;
599     while ((istr[ipos]) && (len < DHCP_OPT_LEN_MAX)) {
600         dprintf(" %02X%02X", *((int *)(istr + ipos)) & 0xFF, *((int *)(istr + ipos +1)) & 0xFF);
601         ichar = pxechn_parse_2bhex(istr + ipos);
602         if (ichar >=0) {
603             data[len++] = ichar;
604         } else {
605             return -EINVAL;
606         }
607         if (!istr[ipos+2]){
608             ipos += 2;
609         } else if (istr[ipos+2] != sep) {
610             return -(EINVAL + 1);
611         } else {
612             ipos += 3;
613         }
614     }
615     return len;
616 }
617
618 int pxechn_parse_opttype(char istr[], int optnum)
619 {
620     char *pos;
621     int tlen, type, tmask;
622
623     if (!istr)
624         return -1;
625     pos = strchr(istr, '=');
626     if (!pos)
627         return -2;
628     if (istr[0] != '.') {
629         if (!pxechn_optnum_ok(optnum))
630             return -3;
631         return -3;      /* do lookup here */
632     } else {
633         tlen = pos - istr - 1;
634         if ((tlen < 1) || (tlen > 4))
635             return -4;
636         tmask = 0xFFFFFFFF >> (8 * (4 - tlen));
637         type = (*(int*)(istr + 1)) & tmask;
638     }
639     return type;
640 }
641
642 int pxechn_parse_setopt(struct dhcp_option opts[], struct dhcp_option *iopt,
643                         char istr[])
644 {
645     int rv = 0, optnum, opttype;
646     char *cpos = NULL, *pos;
647
648     if (!opts || !iopt || !(iopt->data))
649         return -1;
650     if (!istr || !istr[0])
651         return -2;
652     // -EINVAL;
653     optnum = strtoul(istr, &cpos, 0);
654     if (!pxechn_optnum_ok(optnum))
655         return -3;
656     pos = strchr(cpos, '=');
657     if (!pos)
658         return -4;
659     opttype = pxechn_parse_opttype(cpos, optnum);
660     pos++;
661     switch(opttype) {
662     case 'b':
663         iopt->len = pxechn_parse_int(iopt->data, pos, 1);
664         break;
665     case 'l':
666         iopt->len = pxechn_parse_int(iopt->data, pos, 4);
667         break;
668     case 'q':
669         iopt->len = pxechn_parse_int(iopt->data, pos, 8);
670         break;
671     case 's':
672     case STRASINT_str:
673         iopt->len = strlen(pos);
674         if (iopt->len > DHCP_OPT_LEN_MAX)
675             iopt->len = DHCP_OPT_LEN_MAX;
676         memcpy(iopt->data, pos, iopt->len);
677         dprintf_pc_so_s("s.len=%d\trv=%d\n", iopt->len, rv);
678         break;
679     case 'w':
680         iopt->len = pxechn_parse_int(iopt->data, pos, 2);
681         break;
682     case 'x':
683         iopt->len = pxechn_parse_hex_sep(iopt->data, pos, ':');
684         break;
685     default:
686         return -6;
687         break;
688     }
689     if (pxechn_optlen_ok(iopt->len)) {
690         rv = pxechn_setopt(&(opts[optnum]), (void *)(iopt->data), iopt->len);
691     }
692     if((opttype == 's') || (opttype == STRASINT_str))
693         dprintf_pc_so_s("rv=%d\n", rv);
694     return rv;
695 }
696
697 int pxechn_parse_force(const char istr[])
698 {
699     uint32_t rv = 0;
700     char *pos;
701     int terr = errno;
702
703     errno = 0;
704     rv = strtoul(istr, &pos, 0);
705     if ((istr == pos ) || ((rv == ULONG_MAX) && (errno)))
706         rv = 0;
707     errno = terr;
708     return rv;
709 }
710
711 int pxechn_uuid_set(struct pxelinux_opt *pxe)
712 {
713     int ret = 0;
714
715     if (!pxe->p_unpacked[0])
716         ret = dhcp_unpack_packet((pxe_bootp_t *)(pxe->p[0].data),
717                                  pxe->p[0].len, pxe->opts[0]);
718     if (ret) {
719         error("Could not unpack packet\n");
720         return -ret;    /* dhcp_unpack_packet always returns positive errors */
721     }
722
723     if (pxe->opts[0][97].len >= 0 )
724         pxechn_setopt(&(pxe->opts[2][97]), pxe->opts[0][97].data, pxe->opts[0][97].len);
725         return 1;
726     return 0;
727 }
728
729 int pxechn_parse_args(int argc, char *argv[], struct pxelinux_opt *pxe,
730                          struct dhcp_option opts[])
731 {
732     int arg, optnum, rv = 0;
733     char *p = NULL;
734     const char optstr[] = "c:f:g:o:p:t:uwW";
735     struct dhcp_option iopt;
736
737     if (pxe->p[5].data)
738         pxe->fip = ( (pxe_bootp_t *)(pxe->p[5].data) )->sip;
739     else
740         pxe->fip = 0;
741     /* Fill */
742     pxe->fn = argv[0];
743     pxechn_parse_fn(pxe->fn, &(pxe->fip), pxe->host, &(pxe->fp));
744     pxechn_setopt_str(&(opts[67]), pxe->fp);
745     pxechn_setopt_str(&(opts[66]), pxe->host);
746     iopt.data = malloc(DHCP_OPT_LEN_MAX);
747     iopt.len = 0;
748     while ((rv >= 0) && (arg = getopt(argc, argv, optstr)) >= 0) {
749         dprintf_pc_pa("  Got arg '%c'/'%c' addr %08X val %s\n", arg == '?' ? optopt : arg, arg, (unsigned int)optarg, optarg ? optarg : "");
750         switch(arg) {
751         case 'c':       /* config */
752             pxechn_setopt_str(&(opts[209]), optarg);
753             break;
754         case 'f':       /* force */
755             pxe->force = pxechn_parse_force(optarg);
756             break;
757         case 'g':       /* gateway/DHCP relay */
758             pxe->gip = pxe_dns(optarg);
759             break;
760         case 'n':       /* native */
761             break;
762         case 'o':       /* option */
763             rv = pxechn_parse_setopt(opts, &iopt, optarg);
764             break;
765         case 'p':       /* prefix */
766             pxechn_setopt_str(&(opts[210]), optarg);
767             break;
768         case 't':       /* timeout */
769             optnum = strtoul(optarg, &p, 0);
770             if (p != optarg) {
771                 optnum = htonl(optnum);
772                 pxechn_setopt(&(opts[211]), (void *)(&optnum), 4);
773             } else {
774                 rv = -3;
775             }
776             break;
777         case 'u':       /* UUID: copy option 97 from packet 1 if present */
778             pxechn_uuid_set(pxe);
779             break;
780         case 'w':       /* wait */
781             pxe->wait = 1;
782             break;
783         case 'W':       /* WDS */
784             pxe->wds = 1;
785             break;
786         case '?':
787             rv = -'?';
788         default:
789             break;
790         }
791         if (rv >= 0)    /* Clear it since getopt() doesn't guarentee it */
792             optarg = NULL;
793     }
794     if (iopt.data)
795         pxechn_opt_free(&iopt);
796 /* FIXME: consider reordering the application of parsed command line options
797        such that the new nbp may be at the end */
798     if (rv >= 0) {
799         rv = 0;
800     } else if (arg != '?') {
801         printf("Invalid argument for -%c: %s\n", arg, optarg);
802     }
803     dprintf("pxechn_parse_args rv=%d\n", rv);
804     return rv;
805 }
806
807 int pxechn_args(int argc, char *argv[], struct pxelinux_opt *pxe)
808 {
809     pxe_bootp_t *bootp0, *bootp1;
810     int ret = 0;
811     struct dhcp_option *opts;
812
813     opts = pxe->opts[2];
814     /* Start filling packet #1 */
815     bootp0 = (pxe_bootp_t *)(pxe->p[2].data);
816     bootp1 = (pxe_bootp_t *)(pxe->p[5].data);
817
818     ret = dhcp_unpack_packet(bootp0, pxe->p[2].len, opts);
819     if (ret) {
820         error("Could not unpack packet\n");
821         return -ret;
822     }
823     pxe->p_unpacked[2] = 1;
824     pxe->gip = bootp1->gip;
825
826     ret = pxechn_parse_args(argc, argv, pxe, opts);
827     if (ret)
828         return ret;
829     bootp1->sip = pxe->fip;
830     bootp1->gip = pxe->gip;
831
832     ret = dhcp_pack_packet(bootp1, (size_t *)&(pxe->p[5].len), opts);
833     if (ret) {
834         error("Could not pack packet\n");
835         return -ret;    /* dhcp_pack_packet always returns positive errors */
836     }
837     return ret;
838 }
839
840 /* dhcp_pkt2pxe: Copy packet to PXE's BC data for a ptype packet
841  *      Input:
842  *      p       Packet data to copy
843  *      len     length of data to copy
844  *      ptype   Packet type to overwrite
845  */
846 int dhcp_pkt2pxe(pxe_bootp_t *p, size_t len, int ptype)
847 {
848     com32sys_t reg;
849     t_PXENV_GET_CACHED_INFO *ci;
850     void *cp;
851     int rv = -1;
852
853     if (!(ci = lzalloc(sizeof(t_PXENV_GET_CACHED_INFO)))){
854         dprintf("Unable to lzalloc() for PXE call structure\n");
855         rv = 1;
856         goto ret;
857     }
858     ci->Status = PXENV_STATUS_FAILURE;
859     ci->PacketType = ptype;
860     memset(&reg, 0, sizeof(reg));
861     reg.eax.w[0] = 0x0009;
862     reg.ebx.w[0] = PXENV_GET_CACHED_INFO;
863     reg.edi.w[0] = OFFS(ci);
864     reg.es = SEG(ci);
865     __intcall(0x22, &reg, &reg);
866
867     if (ci->Status != PXENV_STATUS_SUCCESS) {
868         dprintf("PXE Get Cached Info failed: %d\n", ci->Status);
869         rv = 2;
870         goto ret;
871     }
872
873     cp = MK_PTR(ci->Buffer.seg, ci->Buffer.offs);
874     if (!(memcpy(cp, p, len))) {
875         dprintf("Failed to copy packet\n");
876         rv = 3;
877         goto ret;
878     }
879 ret:
880     lfree(ci);
881    return rv;
882 }
883
884 int pxechn_mergeopt(struct pxelinux_opt *pxe, int d, int s)
885 {
886     int ret = 0, i;
887
888     if ((d >= PXECHN_NUM_PKT_TYPE) || (s >= PXECHN_NUM_PKT_TYPE) 
889             || (d < 0) || (s < 0)) {
890         return -2;
891     }
892     if (!pxe->p_unpacked[s])
893         ret = dhcp_unpack_packet(pxe->p[s].data, pxe->p[s].len, pxe->opts[s]);
894     if (ret) {
895         error("Could not unpack packet for merge\n");
896         printf("Error %d (%d)\n", ret, EINVAL);
897         if (ret == EINVAL) {
898             if (pxe->p[s].len < 240)
899                 printf("Packet %d is too short: %d (240)\n", s, pxe->p[s].len);
900             else if (((const struct dhcp_packet *)(pxe->p[s].data))->magic != htonl(DHCP_VENDOR_MAGIC))
901                 printf("Packet %d has no magic\n", s);
902             else
903                 error("Unknown EINVAL error\n");
904         } else {
905             error("Unknown error\n");
906         }
907         return -ret;
908     }
909     for (i = 0; i < NUM_DHCP_OPTS; i++) {
910         if (pxe->opts[d][i].len <= -1) {
911             if (pxe->opts[s][i].len >= 0)
912                 pxechn_setopt(&(pxe->opts[d][i]), pxe->opts[s][i].data, pxe->opts[s][i].len);
913         }
914     }
915     return 0;
916 }
917
918 /* pxechn: Chainload to new PXE file ourselves
919  *      Input:
920  *      argc    Count of arguments passed
921  *      argv    Values of arguments passed
922  *      Returns 0 on success (which should never happen)
923  *              1 on loadfile() error
924  *              2 if DHCP Option 52 (Option Overload) used file field
925  *              -1 on usage error
926  */
927 int pxechn(int argc, char *argv[])
928 {
929     struct pxelinux_opt pxe;
930     pxe_bootp_t* p[(2 * PXECHN_NUM_PKT_TYPE)];
931     int rv = 0;
932     int i;
933     struct data_area file;
934     struct syslinux_rm_regs regs;
935
936     pxechn_init(&pxe);
937     for (i = 0; i < (2 * PXECHN_NUM_PKT_TYPE); i++) {
938         p[i] = (pxe_bootp_t *)(pxe.p[i].data);
939     }
940
941     /* Parse arguments and patch packet 1 */
942     rv = pxechn_args(argc, argv, &pxe);
943     dpressanykey(INT_MAX);
944     if (rv)
945         goto ret;
946     pxe_set_regs(&regs);
947     /* Load the file late; it's the most time-expensive operation */
948     printf("%s: Attempting to load '%s': ", app_name_str, pxe.fn);
949     if (loadfile(pxe.fn, &file.data, &file.size)) {
950         pxe_error(errno, NULL, NULL);
951         rv = -2;
952         goto ret;
953     }
954     puts("loaded.");
955     /* we'll be shuffling to the standard location of 7C00h */
956     file.base = 0x7C00;
957     if ((pxe.wds) || 
958             ((pxe.force) && ((pxe.force & (~PXECHN_FORCE_ALL)) == 0))) {
959         printf("Forcing behavior %08X\n", pxe.force);
960         // P2 is the same as P3 if no PXE server present.
961         if ((pxe.wds) ||
962                 (pxe.force & PXECHN_FORCE_PKT2)) {
963             pxechn_fill_pkt(&pxe, PXENV_PACKET_TYPE_DHCP_ACK);
964             rv = pxechn_mergeopt(&pxe, 2, 1);
965             if (rv) {
966                 dprintf("Merge Option returned %d\n", rv);
967             }
968             rv = dhcp_pack_packet(p[5], (size_t *)&(pxe.p[5].len), pxe.opts[2]);
969             rv = dhcp_pkt2pxe(p[5], pxe.p[5].len, PXENV_PACKET_TYPE_DHCP_ACK);
970         }
971         if (pxe.force & PXECHN_FORCE_PKT1) {
972             puts("Unimplemented force option utilized");
973         }
974     }
975     rv = dhcp_pkt2pxe(p[5], pxe.p[5].len, PXENV_PACKET_TYPE_CACHED_REPLY);
976     dprint_pxe_bootp_t(p[5], pxe.p[5].len);
977     if ((pxe.wds) ||
978             ((pxe.force) && ((pxe.force & (~PXECHN_FORCE_ALL)) == 0))) {
979         // printf("Forcing behavior %08X\n", pxe.force);
980         // P2 is the same as P3 if no PXE server present.
981         if ((pxe.wds) ||
982                 (pxe.force & PXECHN_FORCE_PKT2)) {
983             rv = dhcp_pkt2pxe(p[5], pxe.p[5].len, PXENV_PACKET_TYPE_DHCP_ACK);
984         }
985     } else if (pxe.force) {
986         printf("FORCE: bad argument %08X\n", pxe.force);
987     }
988     printf("\n...Ready to boot:\n");
989     if (pxe.wait) {
990         pressanykey(INT_MAX);
991     } else {
992         dpressanykey(INT_MAX);
993     }
994     if (true) {
995         puts("  Attempting to boot...");
996         do_boot(&file, 1, &regs);
997     }
998     /* If failed, copy backup back in and abort */
999     dhcp_pkt2pxe(p[2], pxe.p[2].len, PXENV_PACKET_TYPE_CACHED_REPLY);
1000     if (pxe.force && ((pxe.force & (~PXECHN_FORCE_ALL)) == 0)) {
1001         if (pxe.force & PXECHN_FORCE_PKT2) {
1002             rv = dhcp_pkt2pxe(p[1], pxe.p[1].len, PXENV_PACKET_TYPE_DHCP_ACK);
1003         }
1004     }
1005 ret:
1006     return rv;
1007 }
1008
1009 /* pxe_restart: Restart the PXE environment with a new PXE file
1010  *      Input:
1011  *      ifn     Name of file to chainload to in a format PXELINUX understands
1012  *              This must strictly be TFTP or relative file
1013  */
1014 int pxe_restart(char *ifn)
1015 {
1016     int rv = 0;
1017     struct pxelinux_opt pxe;
1018     com32sys_t reg;
1019     t_PXENV_RESTART_TFTP *pxep; /* PXENV callback Parameter */
1020
1021     pxe.fn = ifn;
1022     pxechn_fill_pkt(&pxe, PXENV_PACKET_TYPE_CACHED_REPLY);
1023     if (pxe.p[5].data)
1024         pxe.fip = ( (pxe_bootp_t *)(pxe.p[5].data) )->sip;
1025     else
1026         pxe.fip = 0;
1027     rv = pxechn_parse_fn(pxe.fn, &(pxe.fip), pxe.host, &(pxe.fp));
1028     if ((rv > 2) || (rv < 0)) {
1029         printf("%s: ERROR: Unparsable filename argument: '%s'\n\n", app_name_str, pxe.fn);
1030         goto ret;
1031     }
1032     printf("  Attempting to boot '%s'...\n\n", pxe.fn);
1033     memset(&reg, 0, sizeof reg);
1034     if (sizeof(t_PXENV_TFTP_READ_FILE) <= __com32.cs_bounce_size) {
1035         pxep = __com32.cs_bounce;
1036         memset(pxep, 0, sizeof(t_PXENV_RESTART_TFTP));
1037     } else if (!(pxep = lzalloc(sizeof(t_PXENV_RESTART_TFTP)))){
1038         dprintf("Unable to lzalloc() for PXE call structure\n");
1039         goto ret;
1040     }
1041     pxep->Status = PXENV_STATUS_SUCCESS;        /* PXENV_STATUS_FAILURE */
1042     strcpy((char *)pxep->FileName, ifn);
1043     pxep->BufferSize = 0x8000;
1044     pxep->Buffer = (void *)0x7c00;
1045     pxep->ServerIPAddress = pxe.fip;
1046     dprintf("FN='%s'  %08X %08X %08X %08X\n\n", (char *)pxep->FileName,
1047         pxep->ServerIPAddress, (unsigned int)pxep,
1048         pxep->BufferSize, (unsigned int)pxep->Buffer);
1049     dprintf("PXENV_RESTART_TFTP status %d\n", pxep->Status);
1050     reg.eax.w[0] = 0x0009;
1051     reg.ebx.w[0] = PXENV_RESTART_TFTP;
1052     reg.edi.w[0] = OFFS(pxep);
1053     reg.es = SEG(pxep);
1054
1055     __intcall(0x22, &reg, &reg);
1056
1057     printf("PXENV_RESTART_TFTP returned %d\n", pxep->Status);
1058     if (pxep != __com32.cs_bounce)
1059         lfree(pxep);
1060
1061 ret:
1062     return rv;
1063 }
1064
1065 /* pxechn_gpxe: Use gPXE to chainload a new NBP
1066  *      Input:
1067  *      argc    Count of arguments passed
1068  *      argv    Values of arguments passed
1069  *      Returns 0 on success (which should never happen)
1070  *              1 on loadfile() error
1071  *              -1 on usage error
1072  */
1073 //FIXME:Implement
1074 int pxechn_gpxe(int argc, char *argv[])
1075 {
1076     int rv = 0;
1077     struct pxelinux_opt pxe;
1078
1079     if (argc) {
1080         printf("%s\n", argv[0]);
1081         pxechn_args(argc, argv, &pxe);
1082     }
1083     return rv;
1084 }
1085
1086 int main(int argc, char *argv[])
1087 {
1088     int rv= -1;
1089     int err;
1090     const struct syslinux_version *sv;
1091
1092     /* Initialization */
1093     err = errno;
1094     console_ansi_raw(); /* sets errno = 9 (EBADF) */
1095     /* printf("%d %d\n", err, errno); */
1096     errno = err;
1097     sv = syslinux_version();
1098     if (sv->filesystem != SYSLINUX_FS_PXELINUX) {
1099         printf("%s: May only run in PXELINUX\n", app_name_str);
1100         argc = 1;       /* prevents further processing to boot */
1101     }
1102     if (argc == 2) {
1103         if ((strcasecmp(argv[1], "-h") == 0) || ((strcmp(argv[1], "-?") == 0))
1104                 || (strcasecmp(argv[1], "--help") == 0)) {
1105             argc = 1;
1106         } else {
1107             rv = pxechn(argc - 1, &argv[1]);
1108         }
1109     } else if (argc >= 3) {
1110         if ((strcmp(argv[1], "-r") == 0)) {
1111             if (argc == 3)
1112                 rv = pxe_restart(argv[2]);
1113         } else {
1114             rv = pxechn(argc - 1, &argv[1]);
1115         }
1116     }
1117     if (rv <= -1 ) {
1118         usage();
1119         rv = 1;
1120     }
1121     return rv;
1122 }