diff options
author | Erwan Velu <erwanaliasr1@gmail.com> | 2012-05-29 21:45:47 +0200 |
---|---|---|
committer | Erwan Velu <erwanaliasr1@gmail.com> | 2012-05-29 21:45:47 +0200 |
commit | 39b84929805d8ea62a2876d1741c32a62fa29229 (patch) | |
tree | c308b28880719e13c505bef0731dfe6ea86eb62e | |
parent | 871650dd981da850bc05eae6de7471b3069926e9 (diff) | |
parent | 2c2c75abc360c4803902f3effa84a28746e34fde (diff) | |
download | syslinux-39b84929805d8ea62a2876d1741c32a62fa29229.tar.gz |
Merge branch 'master' of git://git.zytor.com/syslinux/syslinux
-rw-r--r-- | com32/include/dhcp.h | 40 | ||||
-rw-r--r-- | com32/lib/Makefile | 2 | ||||
-rw-r--r-- | com32/lib/com32.ld | 33 | ||||
-rw-r--r-- | com32/lib/dhcppack.c | 166 | ||||
-rw-r--r-- | com32/lib/dhcpunpack.c | 116 | ||||
-rw-r--r-- | com32/modules/Makefile | 3 | ||||
-rw-r--r-- | com32/modules/pxechn.c | 1122 | ||||
-rw-r--r-- | com32/tools/Makefile | 2 | ||||
-rw-r--r-- | com32/tools/include/tools/le_byteshift.h | 70 | ||||
-rw-r--r-- | com32/tools/relocs.c | 312 | ||||
-rw-r--r-- | core/syslinux.ld | 9 | ||||
-rw-r--r-- | diag/mbr/README | 4 | ||||
-rw-r--r-- | diag/mbr/handoff.S | 4 | ||||
-rw-r--r-- | doc/pxechn.txt | 94 |
14 files changed, 1847 insertions, 130 deletions
diff --git a/com32/include/dhcp.h b/com32/include/dhcp.h new file mode 100644 index 00000000..afef9242 --- /dev/null +++ b/com32/include/dhcp.h @@ -0,0 +1,40 @@ +#ifndef DHCP_H +#define DHCP_H + +#include <inttypes.h> + +struct dhcp_option { + void *data; + int len; +}; + +struct dhcp_packet { + uint8_t op; /* 0 */ + uint8_t htype; /* 1 */ + uint8_t hlen; /* 2 */ + uint8_t hops; /* 3 */ + uint32_t xid; /* 4 */ + uint16_t secs; /* 8 */ + uint16_t flags; /* 10 */ + uint32_t ciaddr; /* 12 */ + uint32_t yiaddr; /* 16 */ + uint32_t siaddr; /* 20 */ + uint32_t giaddr; /* 24 */ + uint8_t chaddr[16]; /* 28 */ + uint8_t sname[64]; /* 44 */ + uint8_t file[128]; /* 108 */ + uint32_t magic; /* 236 */ + uint8_t options[4]; /* 240 */ +}; + +#define DHCP_VENDOR_MAGIC 0x63825363 + +int dhcp_pack_packet(void *packet, size_t *len, + const struct dhcp_option opt[256]); + +int dhcp_unpack_packet(const void *packet, size_t len, + struct dhcp_option opt[256]); + +#endif /* DHCP_H */ + + diff --git a/com32/lib/Makefile b/com32/lib/Makefile index 62a322ab..eace321b 100644 --- a/com32/lib/Makefile +++ b/com32/lib/Makefile @@ -31,7 +31,7 @@ LIBOBJS = \ skipspace.o \ chrreplace.o \ bufprintf.o \ - inet.o \ + inet.o dhcppack.o dhcpunpack.o \ strreplace.o \ \ lmalloc.o lstrdup.o \ diff --git a/com32/lib/com32.ld b/com32/lib/com32.ld index 37ee46cf..008e4ceb 100644 --- a/com32/lib/com32.ld +++ b/com32/lib/com32.ld @@ -36,36 +36,23 @@ SECTIONS .rodata1 : { *(.rodata1) } __rodata_end = .; - /* Ensure the __preinit_array_start label is properly aligned. We - could instead move the label definition inside the section, but - the linker would then create the section even if it turns out to - be empty, which isn't pretty. */ + /* + * The difference betwee .ctors/.dtors and .init_array/.fini_array + * is the ordering, but we don't use prioritization for libcom32, so + * just lump them all together and hope that's okay. + */ . = ALIGN(4); - .preinit_array : { - PROVIDE (__preinit_array_start = .); - *(.preinit_array) - PROVIDE (__preinit_array_end = .); - } - .init_array : { - PROVIDE (__init_array_start = .); - *(.init_array) - PROVIDE (__init_array_end = .); - } - .fini_array : { - PROVIDE (__fini_array_start = .); - *(.fini_array) - PROVIDE (__fini_array_end = .); - } .ctors : { PROVIDE (__ctors_start = .); - KEEP (*(SORT(.ctors.*))) - KEEP (*(.ctors)) + KEEP (*(SORT(.preinit_array*))) + KEEP (*(SORT(.init_array*))) + KEEP (*(SORT(.ctors*))) PROVIDE (__ctors_end = .); } .dtors : { PROVIDE (__dtors_start = .); - KEEP (*(SORT(.dtors.*))) - KEEP (*(.dtors)) + KEEP (*(SORT(.fini_array*))) + KEEP (*(SORT(.dtors*))) PROVIDE (__dtors_end = .); } diff --git a/com32/lib/dhcppack.c b/com32/lib/dhcppack.c new file mode 100644 index 00000000..a08583c4 --- /dev/null +++ b/com32/lib/dhcppack.c @@ -0,0 +1,166 @@ +#include <stdlib.h> +#include <errno.h> +#include <string.h> +// #include <arpa/inet.h> +#include <netinet/in.h> + +// #include "dhcp.h" +#include <dhcp.h> + +/* + * Pack DHCP options into an option field, without overload support. + * On return, len contains the number of active bytes, and the full + * field is zero-padded. + * + * Options which are successfully placed have their length zeroed out. + */ +static int dhcp_pack_field_zero(void *field, size_t *len, + struct dhcp_option opt[256]) +{ + int i; + size_t xlen, plen; + const uint8_t *p; + uint8_t *q = field; + size_t spc = *len; + int err = 0; + + if (!*len) + return ENOSPC; + + for (i = 1; i < 255; i++) { + if (opt[i].len < 0) + continue; + + /* We need to handle the 0 case as well as > 255 */ + if (opt[i].len <= 255) + xlen = opt[i].len + 2; + else + xlen = opt[i].len + 2*((opt[i].len+254)/255); + + p = opt[i].data; + + if (xlen >= spc) { + /* This option doesn't fit... */ + err++; + continue; + } + + xlen = opt[i].len; + do { + *q++ = i; + *q++ = plen = xlen > 255 ? 255 : xlen; + if (plen) + memcpy(q, p, plen); + q += plen; + p += plen; + spc -= plen+2; + xlen -= plen; + } while (xlen); + + opt[i].len = -1; + } + + *q++ = 255; /* End marker */ + memset(q, 0, spc); /* Zero-pad the rest of the field */ + + *len = xlen = q - (uint8_t *)field; + return err; +} + +/* + * Pack DHCP options into an option field, without overload support. + * On return, len contains the number of active bytes, and the full + * field is zero-padded. + * + * Use this to encode encapsulated option fields. + */ +int dhcp_pack_field(void *field, size_t *len, + struct dhcp_option opt[256]) +{ + struct dhcp_option ox[256]; + + memcpy(ox, opt, sizeof ox); + return dhcp_pack_field_zero(field, len, ox); +} + +/* + * Pack DHCP options into a packet. + * Apply overloading if (and only if) the "file" or "sname" option + * doesn't fit in the respective dedicated fields. + */ +int dhcp_pack_packet(void *packet, size_t *len, + const struct dhcp_option opt[256]) +{ + struct dhcp_packet *pkt = packet; + size_t spc = *len; + uint8_t overload; + struct dhcp_option ox[256]; + uint8_t *q; + int err; + + if (spc < sizeof(struct dhcp_packet)) + return ENOSPC; /* Buffer impossibly small */ + + pkt->magic = htonl(DHCP_VENDOR_MAGIC); + + memcpy(ox, opt, sizeof ox); + + /* Figure out if we should do overloading or not */ + overload = 0; + + if (opt[67].len > 128) + overload |= 1; + else + ox[67].len = -1; + + if (opt[66].len > 64) + overload |= 2; + else + ox[66].len = -1; + + /* Kill any passed-in overload option */ + ox[52].len = -1; + + q = pkt->options; + spc -= 240; + + /* Force option 53 (DHCP packet type) first */ + if (ox[53].len == 1) { + *q++ = 53; + *q++ = 1; + *q++ = *(uint8_t *)ox[53].data; + spc -= 3; + ox[53].len = -1; + } + + /* Follow with the overload option, if applicable */ + if (overload) { + *q++ = 52; + *q++ = 1; + *q++ = overload; + spc -= 3; + } + + err = dhcp_pack_field_zero(q, &spc, ox); + *len = spc + (q-(uint8_t *)packet); + + if (overload & 1) { + spc = 128; + err = dhcp_pack_field_zero(pkt->file, &spc, ox); + } else { + memset(pkt->file, 0, 128); + if (opt[67].len > 0) + memcpy(pkt->file, opt[67].data, opt[67].len); + } + + if (overload & 2) { + spc = 64; + err = dhcp_pack_field_zero(pkt->sname, &spc, ox); + } else { + memset(pkt->sname, 0, 64); + if (opt[66].len > 0) + memcpy(pkt->sname, opt[66].data, opt[66].len); + } + + return err; +} diff --git a/com32/lib/dhcpunpack.c b/com32/lib/dhcpunpack.c new file mode 100644 index 00000000..248173a8 --- /dev/null +++ b/com32/lib/dhcpunpack.c @@ -0,0 +1,116 @@ +#define _GNU_SOURCE /* For strnlen() */ +#include <stdlib.h> +#include <errno.h> +#include <string.h> +// #include <arpa/inet.h> +#include <netinet/in.h> + +// #include "dhcp.h" +#include <dhcp.h> + +/* + * Unpack DHCP options from a field. Assumes opt is pre-initalized + * (to all zero in the common case.) + */ +int dhcp_unpack_field(const void *field, size_t len, + struct dhcp_option opt[256]) +{ + const uint8_t *p = field; + int err = 0; + + while (len > 1) { + uint8_t op; + size_t xlen; + + op = *p++; len--; + if (op == 0) + continue; + else if (op == 255) + break; + + xlen = *p++; len--; + if (xlen > len) + break; + if (opt[op].len < 0) + opt[op].len = 0; + if (xlen) { + opt[op].data = realloc(opt[op].data, + opt[op].len + xlen + 1); + if (!opt[op].data) { + err = ENOMEM; + continue; + } + memcpy((char *)opt[op].data + opt[op].len, p, xlen); + opt[op].len += xlen; + /* Null-terminate as a courtesy to users */ + *((char *)opt[op].data + opt[op].len) = 0; + p += xlen; + len -= xlen; + } + } + + return err; +} + +/* + * Unpack a DHCP packet, with overload support. Do not use this + * to unpack an encapsulated option set. + */ +int dhcp_unpack_packet(const void *packet, size_t len, + struct dhcp_option opt[256]) +{ + const struct dhcp_packet *pkt = packet; + int err; + uint8_t overload; + int i; + + if (len < 240 || pkt->magic != htonl(DHCP_VENDOR_MAGIC)) + return EINVAL; /* Bogus packet */ + + for (i = 0; i < 256; i++) { + opt[i].len = -1; /* Option not present */ + opt[i].data = NULL; + } + + err = dhcp_unpack_field(pkt->options, len-240, opt); + + overload = 0; + if (opt[52].len == 1) { + overload = *(uint8_t *)opt[52].data; + free(opt[52].data); + opt[52].len = -1; + opt[52].data = NULL; + } + + if (overload & 1) { + err |= dhcp_unpack_field(pkt->file, 128, opt); + } else { + opt[67].len = strnlen((const char *)pkt->file, 128); + if (opt[67].len) { + opt[67].data = malloc(opt[67].len + 1); + if (opt[67].data) { + memcpy(opt[67].data, pkt->file, opt[67].len); + *((char *)opt[67].data + opt[67].len) = 0; + } else { + err |= ENOMEM; + } + } + } + + if (overload & 2) { + err |= dhcp_unpack_field(pkt->sname, 64, opt); + } else { + opt[66].len = strnlen((const char *)pkt->sname, 64); + if (opt[66].len) { + opt[66].data = malloc(opt[66].len + 1); + if (opt[66].data) { + memcpy(opt[66].data, pkt->file, opt[66].len); + *((char *)opt[66].data + opt[66].len) = 0; + } else { + err |= ENOMEM; + } + } + } + + return err; +} diff --git a/com32/modules/Makefile b/com32/modules/Makefile index 1b2854f5..d8861c48 100644 --- a/com32/modules/Makefile +++ b/com32/modules/Makefile @@ -23,7 +23,8 @@ MODULES = config.c32 ethersel.c32 dmitest.c32 cpuidtest.c32 \ disk.c32 pcitest.c32 elf.c32 linux.c32 reboot.c32 pmload.c32 \ meminfo.c32 sdi.c32 sanboot.c32 ifcpu64.c32 vesainfo.c32 \ kbdmap.c32 cmd.c32 vpdtest.c32 host.c32 ls.c32 gpxecmd.c32 \ - ifcpu.c32 cpuid.c32 cat.c32 pwd.c32 ifplop.c32 zzjson.c32 whichsys.c32 + ifcpu.c32 cpuid.c32 cat.c32 pwd.c32 ifplop.c32 zzjson.c32 \ + whichsys.c32 pxechn.c32 TESTFILES = diff --git a/com32/modules/pxechn.c b/com32/modules/pxechn.c new file mode 100644 index 00000000..3f9ebd32 --- /dev/null +++ b/com32/modules/pxechn.c @@ -0,0 +1,1122 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2010-2012 Gene Cumm - All Rights Reserved + * + * Portions from chain.c: + * Copyright 2003-2009 H. Peter Anvin - All Rights Reserved + * Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin + * Significant portions copyright (C) 2010 Shao Miller + * [partition iteration, GPT, "fs"] + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, Inc., 53 Temple Place Ste 330, + * Boston MA 02111-1307, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +/* + * pxechn.c + * + * PXE Chain Loader; Chain load to another PXE network boot program + * that may be on another host. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <consoles.h> +#include <console.h> +#include <errno.h> +#include <string.h> +#include <syslinux/config.h> +#include <syslinux/loadfile.h> +#include <syslinux/bootrm.h> +#include <syslinux/video.h> +#include <com32.h> +#include <stdint.h> +#include <syslinux/pxe.h> +#include <sys/gpxe.h> +#include <unistd.h> +#include <getkey.h> +#include <dhcp.h> +#include <limits.h> + + +#define PXECHN_DEBUG 1 + +typedef union { + uint64_t q; + uint32_t l[2]; + uint16_t w[4]; + uint8_t b[8]; +} reg64_t; + +#define dprintf0(f, ...) ((void)0) + +#if (PXECHN_DEBUG > 0) +# define dpressanykey pressanykey +# define dprintf printf +# define dprint_pxe_bootp_t print_pxe_bootp_t +# define dprint_pxe_vendor_blk print_pxe_vendor_blk +# define dprint_pxe_vendor_raw print_pxe_vendor_raw +#else +# define dpressanykey(tm) ((void)0) +# define dprintf(f, ...) ((void)0) +# define dprint_pxe_bootp_t(p, l) ((void)0) +# define dprint_pxe_vendor_blk(p, l) ((void)0) +# define dprint_pxe_vendor_raw(p, l) ((void)0) +#endif + +#define dprintf_opt_cp dprintf0 +#define dprintf_opt_inj dprintf0 +#define dprintf_pc_pa dprintf +#define dprintf_pc_so_s dprintf0 + +#define t_PXENV_RESTART_TFTP t_PXENV_TFTP_READ_FILE + +#define STACK_SPLIT 11 + +/* same as pxelinux.asm REBOOT_TIME */ +#define REBOOT_TIME 300 + +#define NUM_DHCP_OPTS 256 +#define DHCP_OPT_LEN_MAX 256 +#define PXE_VENDOR_RAW_PRN_MAX 0x7F +#define PXECHN_HOST_LEN 256 /* 63 bytes per label; 255 max total */ + +#define PXECHN_NUM_PKT_TYPE 3 +#define PXECHN_NUM_PKT_AVAIL 2*PXECHN_NUM_PKT_TYPE +#define PXECHN_PKT_TYPE_START PXENV_PACKET_TYPE_DHCP_DISCOVER + +#define PXECHN_FORCE_PKT1 0x80000000 +#define PXECHN_FORCE_PKT2 0x40000000 +#define PXECHN_FORCE_ALL (PXECHN_FORCE_PKT1 | PXECHN_FORCE_PKT2) +#define PXECHN_FORCE_ALL_1 0 +#define STRASINT_str ('s' + (('t' + ('r' << 8)) << 8)) + +#define min(a,b) (((a) < (b)) ? (a) : (b)) + +const char app_name_str[] = "pxechn.c32"; + +struct pxelinux_opt { + char *fn; /* Filename as passed to us */ + in_addr_t fip; /* fn's IP component */ + char *fp; /* fn's path component */ + in_addr_t gip; /* giaddr; Gateway/DHCP relay */ + uint32_t force; + uint32_t wait; /* Additional decision to wait before boot */ + int32_t wds; /* WDS option/level */ + struct dhcp_option p[PXECHN_NUM_PKT_AVAIL]; + /* original _DHCP_DISCOVER, _DHCP_ACK, _CACHED_REPLY then modified packets */ + char host[PXECHN_HOST_LEN]; + struct dhcp_option opts[PXECHN_NUM_PKT_TYPE][NUM_DHCP_OPTS]; + char p_unpacked[PXECHN_NUM_PKT_TYPE]; +}; + + +/* from chain.c */ +struct data_area { + void *data; + addr_t base; + addr_t size; +}; + +/* From chain.c */ +static inline void error(const char *msg) +{ + fputs(msg, stderr); +} + +/* From chain.c */ +static void do_boot(struct data_area *data, int ndata, + struct syslinux_rm_regs *regs) +{ + uint16_t *const bios_fbm = (uint16_t *) 0x413; + addr_t dosmem = *bios_fbm << 10; /* Technically a low bound */ + struct syslinux_memmap *mmap; + struct syslinux_movelist *mlist = NULL; + addr_t endimage; + int i; + + mmap = syslinux_memory_map(); + + if (!mmap) { + error("Cannot read system memory map\n"); + return; + } + + endimage = 0; + for (i = 0; i < ndata; i++) { + if (data[i].base + data[i].size > endimage) + endimage = data[i].base + data[i].size; + } + if (endimage > dosmem) + goto too_big; + + for (i = 0; i < ndata; i++) { + if (syslinux_add_movelist(&mlist, data[i].base, + (addr_t) data[i].data, data[i].size)) + goto enomem; + } + + + /* Tell the shuffler not to muck with this area... */ + syslinux_add_memmap(&mmap, endimage, 0xa0000 - endimage, SMT_RESERVED); + + /* Force text mode */ + syslinux_force_text_mode(); + + fputs("Booting...\n", stdout); + syslinux_shuffle_boot_rm(mlist, mmap, 3, regs); + error("Chainboot failed!\n"); + return; + +too_big: + error("Loader file too large\n"); + return; + +enomem: + error("Out of memory\n"); + return; +} + +void usage(void) +{ + printf("USAGE:\n" + " %s [OPTIONS]... _new-nbp_\n" + " %s -r _new-nbp_ (calls PXE stack PXENV_RESTART_TFTP)\n" + "OPTIONS:\n" + " [-c config] [-g gateway] [-p prefix] [-t reboot] [-u] [-w] [-W]" + " [-o opt.ty=val]\n\n", + app_name_str, app_name_str); +} + +void pxe_error(int ierr, const char *evt, const char *msg) +{ + if (msg) + printf("%s", msg); + else if (evt) + printf("Error while %s: ", evt); + printf("%d:%s\n", ierr, strerror(ierr)); +} + +int pressanykey(clock_t tm) { + int inc; + + printf("Press any key to continue. "); + inc = get_key(stdin, tm); + puts(""); + return inc; +} + +int dhcp_find_opt(pxe_bootp_t *p, size_t len, uint8_t opt) +{ + int rv = -1; + int i, vlen, oplen; + uint8_t *d; + uint32_t magic; + + if (!p) { + dprintf(" packet pointer is null\n"); + return rv; + } + vlen = len - ((void *)&(p->vendor) - (void *)p); + d = p->vendor.d; + magic = ntohl(*((uint32_t *)d)); + if (magic != VM_RFC1048) /* Invalid DHCP packet */ + vlen = 0; + for (i = 4; i < vlen; i++) { + if (d[i] == opt) { + dprintf("\n @%03X-%2d\n", i, d[i]); + rv = i; + break; + } + if (d[i] == ((NUM_DHCP_OPTS) - 1)) /* End of list */ + break; + if (d[i]) { /* Skip padding */ + oplen = d[++i]; + i += oplen; + } + } + return rv; +} + +void print_pxe_vendor_raw(pxe_bootp_t *p, size_t len) +{ + int i, vlen; + + if (!p) { + printf(" packet pointer is null\n"); + return; + } + vlen = len - ((void *)&(p->vendor) - (void *)p); + if (vlen > PXE_VENDOR_RAW_PRN_MAX) + vlen = PXE_VENDOR_RAW_PRN_MAX; + dprintf(" rawLen = %d", vlen); + for (i = 0; i < vlen; i++) { + if ((i & 0xf) == 0) + printf("\n %04X:", i); + printf(" %02X", p->vendor.d[i]); + } + printf("\n"); +} + +void print_pxe_vendor_blk(pxe_bootp_t *p, size_t len) +{ + int i, vlen, oplen, j; + uint8_t *d; + uint32_t magic; + if (!p) { + printf(" packet pointer is null\n"); + return; + } + vlen = len - ((void *)&(p->vendor) - (void *)p); + printf(" Vendor Data: Len=%d", vlen); + d = p->vendor.d; + magic = ntohl(*((uint32_t *)d)); + printf(" Magic: %08X", ntohl(*((uint32_t *)d))); + if (magic != VM_RFC1048) /* Invalid DHCP packet */ + vlen = 0; + for (i = 4; i < vlen; i++) { + if (d[i]) /* Skip the padding */ + printf("\n @%03X-%3d", i, d[i]); + if (d[i] == ((NUM_DHCP_OPTS) - 1)) /* End of list */ + break; + if (d[i]) { + oplen = d[++i]; + printf(" l=%3d:", oplen); + for (j = (++i + oplen); i < vlen && i < j; i++) { + printf(" %02X", d[i]); + } + i--; + } + } + printf("\n"); +} + +void print_pxe_bootp_t(pxe_bootp_t *p, size_t len) +{ + if (!p || len <= 0) { + printf(" packet pointer is null\n"); + return; + } + printf(" op:%02X hw:%02X hl:%02X gh:%02X id:%08X se:%04X f:%04X" + " cip:%08X\n", p->opcode, p->Hardware, p->Hardlen, p->Gatehops, + ntohl(p->ident), ntohs(p->seconds), ntohs(p->Flags), ntohl(p->cip)); + printf(" yip:%08X sip:%08X gip:%08X", + ntohl(p->yip), ntohl(p->sip), ntohl(p->gip)); + printf(" caddr-%02X:%02X:%02X:%02X:%02X:%02X\n", p->CAddr[0], + p->CAddr[1], p->CAddr[2], p->CAddr[3], p->CAddr[4], p->CAddr[5]); + printf(" sName: '%s'\n", p->Sname); + printf(" bootfile: '%s'\n", p->bootfile); + dprint_pxe_vendor_blk(p, len); +} + +void pxe_set_regs(struct syslinux_rm_regs *regs) +{ + com32sys_t tregs; + + regs->ip = 0x7C00; + /* Plan A uses SS:[SP + 4] */ + /* sdi->pxe.stack is a usable pointer, not something that can be nicely + and reliably split to SS:SP without causing issues */ + tregs.eax.l = 0x000A; + __intcall(0x22, &tregs, &tregs); + regs->ss = tregs.fs; + regs->esp.l = tregs.esi.w[0] + sizeof(tregs); + /* Plan B uses [ES:BX] */ + regs->es = tregs.es; + regs->ebx = tregs.ebx; + dprintf("\nsp:%04x ss:%04x es:%04x bx:%04x\n", regs->esp.w[0], + regs->ss, regs->es, regs->ebx.w[0]); + /* Zero out everything else just to be sure */ + regs->cs = regs->ds = regs->fs = regs->gs = 0; + regs->eax.l = regs->ecx.l = regs->edx.l = 0; +} + +int hostlen_limit(int len) +{ + return min(len, ((PXECHN_HOST_LEN) - 1)); +} + +//FIXME: To a library +/* Parse a filename into an IPv4 address and filename pointer + * returns Based on the interpretation of fn + * 0 regular file name + * 1 in format IP::FN + * 2 TFTP URL + * 3 HTTP URL + * 4 FTP URL + * 3 + 2^30 HTTPS URL + * -1 if fn is another URL type + */ +int pxechn_parse_fn(char fn[], in_addr_t *fip, char *host, char *fp[]) +{ + in_addr_t tip = 0; + char *csep, *ssep, *hsep; /* Colon, Slash separator positions */ + int hlen, plen; /* Hostname, protocol length */ + int rv = 0; + + csep = strchr(fn, ':'); + if (csep) { + if (csep[1] == ':') { /* assume IP::FN */ + *fp = &csep[2]; + rv = 1; + if (fn[0] != ':') { + hlen = hostlen_limit(csep - fn); + memcpy(host, fn, hlen); + host[hlen] = 0; + } + } else if ((csep[1] == '/') && (csep[2] == '/')) { + /* URL: proto://host:port/path/file */ + /* proto://[user[:passwd]@]host[:port]/path/file */ + ssep = strchr(csep + 3, '/'); + if (ssep) { + hlen = hostlen_limit(ssep - (csep + 3)); + *fp = ssep + 1; + } else { + hlen = hostlen_limit(strlen(csep + 3)); + } + memcpy(host, (csep + 3), hlen); + host[hlen] = 0; + plen = csep - fn; + if (strncmp(fn, "tftp", plen) == 0) + rv = 2; + else if (strncmp(fn, "http", plen) == 0) + rv = 3; + else if (strncmp(fn, "ftp", plen) == 0) + rv = 4; + else if (strncmp(fn, "https", plen) == 0) + rv = 3 + ( 1 << 30 ); + else + rv = -1; + } else { + csep = NULL; + } + } + if (!csep) { + *fp = fn; + } + if (host[0]) { + hsep = strchr(host, '@'); + if (!hsep) + hsep = host; + tip = pxe_dns(hsep); + } + if (tip != 0) + *fip = tip; + dprintf0(" host '%s'\n fp '%s'\n fip %08x\n", host, *fp, ntohl(*fip)); + return rv; +} + +void pxechn_opt_free(struct dhcp_option *opt) +{ + free(opt->data); + opt->len = -1; +} + +void pxechn_fill_pkt(struct pxelinux_opt *pxe, int ptype) +{ + int rv = -1; + int p1, p2; + if ((ptype < 0) || (ptype > PXECHN_NUM_PKT_TYPE)) + rv = -2; + p1 = ptype - PXECHN_PKT_TYPE_START; + p2 = p1 + PXECHN_NUM_PKT_TYPE; + if ((rv >= -1) && (!pxe_get_cached_info(ptype, + (void **)&(pxe->p[p1].data), (size_t *)&(pxe->p[p1].len)))) { + pxe->p[p2].data = malloc(2048); + if (pxe->p[p2].data) { + memcpy(pxe->p[p2].data, pxe->p[p1].data, pxe->p[p1].len); + pxe->p[p2].len = pxe->p[p1].len; + rv = 0; + dprint_pxe_bootp_t((pxe_bootp_t *)(pxe->p[p1].data), pxe->p[p1].len); + dpressanykey(INT_MAX); + } else { + printf("%s: ERROR: Unable to malloc() for second packet\n", app_name_str); + } + } else { + printf("%s: ERROR: Unable to retrieve first packet\n", app_name_str); + } + if (rv <= -1) { + pxechn_opt_free(&pxe->p[p1]); + } +} + +void pxechn_init(struct pxelinux_opt *pxe) +{ + /* Init for paranoia */ + pxe->fn = NULL; + pxe->fp = NULL; + pxe->force = 0; + pxe->wait = 0; + pxe->gip = 0; + pxe->wds = 0; + pxe->host[0] = 0; + pxe->host[((NUM_DHCP_OPTS) - 1)] = 0; + for (int j = 0; j < PXECHN_NUM_PKT_TYPE; j++){ + for (int i = 0; i < NUM_DHCP_OPTS; i++) { + pxe->opts[j][i].data = NULL; + pxe->opts[j][i].len = -1; + } + pxe->p_unpacked[j] = 0; + pxe->p[j].data = NULL; + pxe->p[j+PXECHN_NUM_PKT_TYPE].data = NULL; + pxe->p[j].len = 0; + pxe->p[j+PXECHN_NUM_PKT_TYPE].len = 0; + } + pxechn_fill_pkt(pxe, PXENV_PACKET_TYPE_CACHED_REPLY); +} + +int pxechn_to_hex(char i) +{ + if (i >= '0' && i <= '9') + return (i - '0'); + if (i >= 'A' && i <= 'F') + return (i - 'A' + 10); + if (i >= 'a' && i <= 'f') + return (i - 'a' + 10); + if (i == 0) + return -1; + return -2; +} + +int pxechn_parse_2bhex(char ins[]) +{ + int ret = -2; + int n0 = -3, n1 = -3; + /* NULL pointer */ + if (!ins) { + ret = -1; + /* pxechn_to_hex can handle the NULL character by returning -1 and + breaking the execution of the statement chain */ + } else if (((n0 = pxechn_to_hex(ins[0])) >= 0) + && ((n1 = pxechn_to_hex(ins[1])) >= 0)) { + ret = (n0 * 16) + n1; + } else if (n0 == -1) { /* Leading NULL char */ + ret = -1; + } + return ret; +} + +int pxechn_optnum_ok(int optnum) +{ + if ((optnum > 0) && (optnum < ((NUM_DHCP_OPTS) - 1))) + return 1; + return 0; +} + +int pxechn_optnum_ok_notres(int optnum) +{ + if ((optnum <= 0) && (optnum >= ((NUM_DHCP_OPTS) - 1))) + return 0; + switch(optnum){ + case 66: case 67: + return 0; + break; + default: return 1; + } +} + +int pxechn_optlen_ok(int optlen) +{ + if ((optlen >= 0) && (optlen < ((DHCP_OPT_LEN_MAX) - 1))) + return 1; + return 0; +} + +int pxechn_setopt(struct dhcp_option *opt, void *data, int len) +{ + void *p; + if (!opt || !data) + return -1; + if (len < 0) { + return -3; + } + p = realloc(opt->data, len); + if (!p && len) { /* Allow for len=0 */ + pxechn_opt_free(opt); + return -2; + } + opt->data = p; + memcpy(opt->data, data, len); + opt->len = len; + return len; +} + +int pxechn_setopt_str(struct dhcp_option *opt, void *data) +{ + return pxechn_setopt(opt, data, strnlen(data, DHCP_OPT_LEN_MAX)); +} + +int pxechn_parse_int(char *data, char istr[], int tlen) +{ + int terr = errno; + + if ((tlen == 1) || (tlen == 2) || (tlen == 4)) { + errno = 0; + uint32_t optval = strtoul(istr, NULL, 0); + if (errno) + return -3; + errno = terr; + switch(tlen){ + case 1: + if (optval & 0xFFFFFF00) + return -4; + break; + case 2: + if (optval & 0xFFFF0000) + return -4; + optval = htons(optval); + break; + case 4: + optval = htonl(optval); + break; + } + memcpy(data, &optval, tlen); + } else if (tlen == 8) { + errno = 0; + uint64_t optval = strtoull(istr, NULL, 0); + if (errno) + return -3; + errno = terr; + optval = htonq(optval); + memcpy(data, &optval, tlen); + } else { + return -2; + } + return tlen; +} + +int pxechn_parse_hex_sep(char *data, char istr[], char sep) +{ + int len = 0; + int ipos = 0, ichar; + + if (!data || !istr) + return -1; + while ((istr[ipos]) && (len < DHCP_OPT_LEN_MAX)) { + dprintf(" %02X%02X", *((int *)(istr + ipos)) & 0xFF, *((int *)(istr + ipos +1)) & 0xFF); + ichar = pxechn_parse_2bhex(istr + ipos); + if (ichar >=0) { + data[len++] = ichar; + } else { + return -EINVAL; + } + if (!istr[ipos+2]){ + ipos += 2; + } else if (istr[ipos+2] != sep) { + return -(EINVAL + 1); + } else { + ipos += 3; + } + } + return len; +} + +int pxechn_parse_opttype(char istr[], int optnum) +{ + char *pos; + int tlen, type, tmask; + + if (!istr) + return -1; + pos = strchr(istr, '='); + if (!pos) + return -2; + if (istr[0] != '.') { + if (!pxechn_optnum_ok(optnum)) + return -3; + return -3; /* do lookup here */ + } else { + tlen = pos - istr - 1; + if ((tlen < 1) || (tlen > 4)) + return -4; + tmask = 0xFFFFFFFF >> (8 * (4 - tlen)); + type = (*(int*)(istr + 1)) & tmask; + } + return type; +} + +int pxechn_parse_setopt(struct dhcp_option opts[], struct dhcp_option *iopt, + char istr[]) +{ + int rv = 0, optnum, opttype; + char *cpos = NULL, *pos; + + if (!opts || !iopt || !(iopt->data)) + return -1; + if (!istr || !istr[0]) + return -2; + // -EINVAL; + optnum = strtoul(istr, &cpos, 0); + if (!pxechn_optnum_ok(optnum)) + return -3; + pos = strchr(cpos, '='); + if (!pos) + return -4; + opttype = pxechn_parse_opttype(cpos, optnum); + pos++; + switch(opttype) { + case 'b': + iopt->len = pxechn_parse_int(iopt->data, pos, 1); + break; + case 'l': + iopt->len = pxechn_parse_int(iopt->data, pos, 4); + break; + case 'q': + iopt->len = pxechn_parse_int(iopt->data, pos, 8); + break; + case 's': + case STRASINT_str: + iopt->len = strlen(pos); + if (iopt->len > DHCP_OPT_LEN_MAX) + iopt->len = DHCP_OPT_LEN_MAX; + memcpy(iopt->data, pos, iopt->len); + dprintf_pc_so_s("s.len=%d\trv=%d\n", iopt->len, rv); + break; + case 'w': + iopt->len = pxechn_parse_int(iopt->data, pos, 2); + break; + case 'x': + iopt->len = pxechn_parse_hex_sep(iopt->data, pos, ':'); + break; + default: + return -6; + break; + } + if (pxechn_optlen_ok(iopt->len)) { + rv = pxechn_setopt(&(opts[optnum]), (void *)(iopt->data), iopt->len); + } + if((opttype == 's') || (opttype == STRASINT_str)) + dprintf_pc_so_s("rv=%d\n", rv); + return rv; +} + +int pxechn_parse_force(const char istr[]) +{ + uint32_t rv = 0; + char *pos; + int terr = errno; + + errno = 0; + rv = strtoul(istr, &pos, 0); + if ((istr == pos ) || ((rv == ULONG_MAX) && (errno))) + rv = 0; + errno = terr; + return rv; +} + +int pxechn_uuid_set(struct pxelinux_opt *pxe) +{ + int ret = 0; + + if (!pxe->p_unpacked[0]) + ret = dhcp_unpack_packet((pxe_bootp_t *)(pxe->p[0].data), + pxe->p[0].len, pxe->opts[0]); + if (ret) { + error("Could not unpack packet\n"); + return -ret; /* dhcp_unpack_packet always returns positive errors */ + } + + if (pxe->opts[0][97].len >= 0 ) + pxechn_setopt(&(pxe->opts[2][97]), pxe->opts[0][97].data, pxe->opts[0][97].len); + return 1; + return 0; +} + +int pxechn_parse_args(int argc, char *argv[], struct pxelinux_opt *pxe, + struct dhcp_option opts[]) +{ + int arg, optnum, rv = 0; + char *p = NULL; + const char optstr[] = "c:f:g:o:p:t:uwW"; + struct dhcp_option iopt; + + if (pxe->p[5].data) + pxe->fip = ( (pxe_bootp_t *)(pxe->p[5].data) )->sip; + else + pxe->fip = 0; + /* Fill */ + pxe->fn = argv[0]; + pxechn_parse_fn(pxe->fn, &(pxe->fip), pxe->host, &(pxe->fp)); + pxechn_setopt_str(&(opts[67]), pxe->fp); + pxechn_setopt_str(&(opts[66]), pxe->host); + iopt.data = malloc(DHCP_OPT_LEN_MAX); + iopt.len = 0; + while ((rv >= 0) && (arg = getopt(argc, argv, optstr)) >= 0) { + dprintf_pc_pa(" Got arg '%c'/'%c' addr %08X val %s\n", arg == '?' ? optopt : arg, arg, (unsigned int)optarg, optarg ? optarg : ""); + switch(arg) { + case 'c': /* config */ + pxechn_setopt_str(&(opts[209]), optarg); + break; + case 'f': /* force */ + pxe->force = pxechn_parse_force(optarg); + break; + case 'g': /* gateway/DHCP relay */ + pxe->gip = pxe_dns(optarg); + break; + case 'n': /* native */ + break; + case 'o': /* option */ + rv = pxechn_parse_setopt(opts, &iopt, optarg); + break; + case 'p': /* prefix */ + pxechn_setopt_str(&(opts[210]), optarg); + break; + case 't': /* timeout */ + optnum = strtoul(optarg, &p, 0); + if (p != optarg) { + optnum = htonl(optnum); + pxechn_setopt(&(opts[211]), (void *)(&optnum), 4); + } else { + rv = -3; + } + break; + case 'u': /* UUID: copy option 97 from packet 1 if present */ + pxechn_uuid_set(pxe); + break; + case 'w': /* wait */ + pxe->wait = 1; + break; + case 'W': /* WDS */ + pxe->wds = 1; + break; + case '?': + rv = -'?'; + default: + break; + } + if (rv >= 0) /* Clear it since getopt() doesn't guarentee it */ + optarg = NULL; + } + if (iopt.data) + pxechn_opt_free(&iopt); +/* FIXME: consider reordering the application of parsed command line options + such that the new nbp may be at the end */ + if (rv >= 0) { + rv = 0; + } else if (arg != '?') { + printf("Invalid argument for -%c: %s\n", arg, optarg); + } + dprintf("pxechn_parse_args rv=%d\n", rv); + return rv; +} + +int pxechn_args(int argc, char *argv[], struct pxelinux_opt *pxe) +{ + pxe_bootp_t *bootp0, *bootp1; + int ret = 0; + struct dhcp_option *opts; + + opts = pxe->opts[2]; + /* Start filling packet #1 */ + bootp0 = (pxe_bootp_t *)(pxe->p[2].data); + bootp1 = (pxe_bootp_t *)(pxe->p[5].data); + + ret = dhcp_unpack_packet(bootp0, pxe->p[2].len, opts); + if (ret) { + error("Could not unpack packet\n"); + return -ret; + } + pxe->p_unpacked[2] = 1; + pxe->gip = bootp1->gip; + + ret = pxechn_parse_args(argc, argv, pxe, opts); + if (ret) + return ret; + bootp1->sip = pxe->fip; + bootp1->gip = pxe->gip; + + ret = dhcp_pack_packet(bootp1, (size_t *)&(pxe->p[5].len), opts); + if (ret) { + error("Could not pack packet\n"); + return -ret; /* dhcp_pack_packet always returns positive errors */ + } + return ret; +} + +/* dhcp_pkt2pxe: Copy packet to PXE's BC data for a ptype packet + * Input: + * p Packet data to copy + * len length of data to copy + * ptype Packet type to overwrite + */ +int dhcp_pkt2pxe(pxe_bootp_t *p, size_t len, int ptype) +{ + com32sys_t reg; + t_PXENV_GET_CACHED_INFO *ci; + void *cp; + int rv = -1; + + if (!(ci = lzalloc(sizeof(t_PXENV_GET_CACHED_INFO)))){ + dprintf("Unable to lzalloc() for PXE call structure\n"); + rv = 1; + goto ret; + } + ci->Status = PXENV_STATUS_FAILURE; + ci->PacketType = ptype; + memset(®, 0, sizeof(reg)); + reg.eax.w[0] = 0x0009; + reg.ebx.w[0] = PXENV_GET_CACHED_INFO; + reg.edi.w[0] = OFFS(ci); + reg.es = SEG(ci); + __intcall(0x22, ®, ®); + + if (ci->Status != PXENV_STATUS_SUCCESS) { + dprintf("PXE Get Cached Info failed: %d\n", ci->Status); + rv = 2; + goto ret; + } + + cp = MK_PTR(ci->Buffer.seg, ci->Buffer.offs); + if (!(memcpy(cp, p, len))) { + dprintf("Failed to copy packet\n"); + rv = 3; + goto ret; + } +ret: + lfree(ci); + return rv; +} + +int pxechn_mergeopt(struct pxelinux_opt *pxe, int d, int s) +{ + int ret = 0, i; + + if ((d >= PXECHN_NUM_PKT_TYPE) || (s >= PXECHN_NUM_PKT_TYPE) + || (d < 0) || (s < 0)) { + return -2; + } + if (!pxe->p_unpacked[s]) + ret = dhcp_unpack_packet(pxe->p[s].data, pxe->p[s].len, pxe->opts[s]); + if (ret) { + error("Could not unpack packet for merge\n"); + printf("Error %d (%d)\n", ret, EINVAL); + if (ret == EINVAL) { + if (pxe->p[s].len < 240) + printf("Packet %d is too short: %d (240)\n", s, pxe->p[s].len); + else if (((const struct dhcp_packet *)(pxe->p[s].data))->magic != htonl(DHCP_VENDOR_MAGIC)) + printf("Packet %d has no magic\n", s); + else + error("Unknown EINVAL error\n"); + } else { + error("Unknown error\n"); + } + return -ret; + } + for (i = 0; i < NUM_DHCP_OPTS; i++) { + if (pxe->opts[d][i].len <= -1) { + if (pxe->opts[s][i].len >= 0) + pxechn_setopt(&(pxe->opts[d][i]), pxe->opts[s][i].data, pxe->opts[s][i].len); + } + } + return 0; +} + +/* pxechn: Chainload to new PXE file ourselves + * Input: + * argc Count of arguments passed + * argv Values of arguments passed + * Returns 0 on success (which should never happen) + * 1 on loadfile() error + * 2 if DHCP Option 52 (Option Overload) used file field + * -1 on usage error + */ +int pxechn(int argc, char *argv[]) +{ + struct pxelinux_opt pxe; + pxe_bootp_t* p[(2 * PXECHN_NUM_PKT_TYPE)]; + int rv = 0; + int i; + struct data_area file; + struct syslinux_rm_regs regs; + + pxechn_init(&pxe); + for (i = 0; i < (2 * PXECHN_NUM_PKT_TYPE); i++) { + p[i] = (pxe_bootp_t *)(pxe.p[i].data); + } + + /* Parse arguments and patch packet 1 */ + rv = pxechn_args(argc, argv, &pxe); + dpressanykey(INT_MAX); + if (rv) + goto ret; + pxe_set_regs(®s); + /* Load the file late; it's the most time-expensive operation */ + printf("%s: Attempting to load '%s': ", app_name_str, pxe.fn); + if (loadfile(pxe.fn, &file.data, &file.size)) { + pxe_error(errno, NULL, NULL); + rv = -2; + goto ret; + } + puts("loaded."); + /* we'll be shuffling to the standard location of 7C00h */ + file.base = 0x7C00; + if ((pxe.wds) || + ((pxe.force) && ((pxe.force & (~PXECHN_FORCE_ALL)) == 0))) { + printf("Forcing behavior %08X\n", pxe.force); + // P2 is the same as P3 if no PXE server present. + if ((pxe.wds) || + (pxe.force & PXECHN_FORCE_PKT2)) { + pxechn_fill_pkt(&pxe, PXENV_PACKET_TYPE_DHCP_ACK); + rv = pxechn_mergeopt(&pxe, 2, 1); + if (rv) { + dprintf("Merge Option returned %d\n", rv); + } + rv = dhcp_pack_packet(p[5], (size_t *)&(pxe.p[5].len), pxe.opts[2]); + rv = dhcp_pkt2pxe(p[5], pxe.p[5].len, PXENV_PACKET_TYPE_DHCP_ACK); + } + if (pxe.force & PXECHN_FORCE_PKT1) { + puts("Unimplemented force option utilized"); + } + } + rv = dhcp_pkt2pxe(p[5], pxe.p[5].len, PXENV_PACKET_TYPE_CACHED_REPLY); + dprint_pxe_bootp_t(p[5], pxe.p[5].len); + if ((pxe.wds) || + ((pxe.force) && ((pxe.force & (~PXECHN_FORCE_ALL)) == 0))) { + // printf("Forcing behavior %08X\n", pxe.force); + // P2 is the same as P3 if no PXE server present. + if ((pxe.wds) || + (pxe.force & PXECHN_FORCE_PKT2)) { + rv = dhcp_pkt2pxe(p[5], pxe.p[5].len, PXENV_PACKET_TYPE_DHCP_ACK); + } + } else if (pxe.force) { + printf("FORCE: bad argument %08X\n", pxe.force); + } + printf("\n...Ready to boot:\n"); + if (pxe.wait) { + pressanykey(INT_MAX); + } else { + dpressanykey(INT_MAX); + } + if (true) { + puts(" Attempting to boot..."); + do_boot(&file, 1, ®s); + } + /* If failed, copy backup back in and abort */ + dhcp_pkt2pxe(p[2], pxe.p[2].len, PXENV_PACKET_TYPE_CACHED_REPLY); + if (pxe.force && ((pxe.force & (~PXECHN_FORCE_ALL)) == 0)) { + if (pxe.force & PXECHN_FORCE_PKT2) { + rv = dhcp_pkt2pxe(p[1], pxe.p[1].len, PXENV_PACKET_TYPE_DHCP_ACK); + } + } +ret: + return rv; +} + +/* pxe_restart: Restart the PXE environment with a new PXE file + * Input: + * ifn Name of file to chainload to in a format PXELINUX understands + * This must strictly be TFTP or relative file + */ +int pxe_restart(char *ifn) +{ + int rv = 0; + struct pxelinux_opt pxe; + com32sys_t reg; + t_PXENV_RESTART_TFTP *pxep; /* PXENV callback Parameter */ + + pxe.fn = ifn; + pxechn_fill_pkt(&pxe, PXENV_PACKET_TYPE_CACHED_REPLY); + if (pxe.p[5].data) + pxe.fip = ( (pxe_bootp_t *)(pxe.p[5].data) )->sip; + else + pxe.fip = 0; + rv = pxechn_parse_fn(pxe.fn, &(pxe.fip), pxe.host, &(pxe.fp)); + if ((rv > 2) || (rv < 0)) { + printf("%s: ERROR: Unparsable filename argument: '%s'\n\n", app_name_str, pxe.fn); + goto ret; + } + printf(" Attempting to boot '%s'...\n\n", pxe.fn); + memset(®, 0, sizeof reg); + if (sizeof(t_PXENV_TFTP_READ_FILE) <= __com32.cs_bounce_size) { + pxep = __com32.cs_bounce; + memset(pxep, 0, sizeof(t_PXENV_RESTART_TFTP)); + } else if (!(pxep = lzalloc(sizeof(t_PXENV_RESTART_TFTP)))){ + dprintf("Unable to lzalloc() for PXE call structure\n"); + goto ret; + } + pxep->Status = PXENV_STATUS_SUCCESS; /* PXENV_STATUS_FAILURE */ + strcpy((char *)pxep->FileName, ifn); + pxep->BufferSize = 0x8000; + pxep->Buffer = (void *)0x7c00; + pxep->ServerIPAddress = pxe.fip; + dprintf("FN='%s' %08X %08X %08X %08X\n\n", (char *)pxep->FileName, + pxep->ServerIPAddress, (unsigned int)pxep, + pxep->BufferSize, (unsigned int)pxep->Buffer); + dprintf("PXENV_RESTART_TFTP status %d\n", pxep->Status); + reg.eax.w[0] = 0x0009; + reg.ebx.w[0] = PXENV_RESTART_TFTP; + reg.edi.w[0] = OFFS(pxep); + reg.es = SEG(pxep); + + __intcall(0x22, ®, ®); + + printf("PXENV_RESTART_TFTP returned %d\n", pxep->Status); + if (pxep != __com32.cs_bounce) + lfree(pxep); + +ret: + return rv; +} + +/* pxechn_gpxe: Use gPXE to chainload a new NBP + * Input: + * argc Count of arguments passed + * argv Values of arguments passed + * Returns 0 on success (which should never happen) + * 1 on loadfile() error + * -1 on usage error + */ +//FIXME:Implement +int pxechn_gpxe(int argc, char *argv[]) +{ + int rv = 0; + struct pxelinux_opt pxe; + + if (argc) { + printf("%s\n", argv[0]); + pxechn_args(argc, argv, &pxe); + } + return rv; +} + +int main(int argc, char *argv[]) +{ + int rv= -1; + int err; + const struct syslinux_version *sv; + + /* Initialization */ + err = errno; + console_ansi_raw(); /* sets errno = 9 (EBADF) */ + /* printf("%d %d\n", err, errno); */ + errno = err; + sv = syslinux_version(); + if (sv->filesystem != SYSLINUX_FS_PXELINUX) { + printf("%s: May only run in PXELINUX\n", app_name_str); + argc = 1; /* prevents further processing to boot */ + } + if (argc == 2) { + if ((strcasecmp(argv[1], "-h") == 0) || ((strcmp(argv[1], "-?") == 0)) + || (strcasecmp(argv[1], "--help") == 0)) { + argc = 1; + } else { + rv = pxechn(argc - 1, &argv[1]); + } + } else if (argc >= 3) { + if ((strcmp(argv[1], "-r") == 0)) { + if (argc == 3) + rv = pxe_restart(argv[2]); + } else { + rv = pxechn(argc - 1, &argv[1]); + } + } + if (rv <= -1 ) { + usage(); + rv = 1; + } + return rv; +} diff --git a/com32/tools/Makefile b/com32/tools/Makefile index 7badabd2..0161baf1 100644 --- a/com32/tools/Makefile +++ b/com32/tools/Makefile @@ -15,6 +15,8 @@ include $(MAKEDIR)/build.mk BINS = relocs +INCLUDES += -I./include + all : $(BINS) relocs : relocs.o diff --git a/com32/tools/include/tools/le_byteshift.h b/com32/tools/include/tools/le_byteshift.h new file mode 100644 index 00000000..c99d45a6 --- /dev/null +++ b/com32/tools/include/tools/le_byteshift.h @@ -0,0 +1,70 @@ +#ifndef _TOOLS_LE_BYTESHIFT_H +#define _TOOLS_LE_BYTESHIFT_H + +#include <linux/types.h> + +static inline __u16 __get_unaligned_le16(const __u8 *p) +{ + return p[0] | p[1] << 8; +} + +static inline __u32 __get_unaligned_le32(const __u8 *p) +{ + return p[0] | p[1] << 8 | p[2] << 16 | p[3] << 24; +} + +static inline __u64 __get_unaligned_le64(const __u8 *p) +{ + return (__u64)__get_unaligned_le32(p + 4) << 32 | + __get_unaligned_le32(p); +} + +static inline void __put_unaligned_le16(__u16 val, __u8 *p) +{ + *p++ = val; + *p++ = val >> 8; +} + +static inline void __put_unaligned_le32(__u32 val, __u8 *p) +{ + __put_unaligned_le16(val >> 16, p + 2); + __put_unaligned_le16(val, p); +} + +static inline void __put_unaligned_le64(__u64 val, __u8 *p) +{ + __put_unaligned_le32(val >> 32, p + 4); + __put_unaligned_le32(val, p); +} + +static inline __u16 get_unaligned_le16(const void *p) +{ + return __get_unaligned_le16((const __u8 *)p); +} + +static inline __u32 get_unaligned_le32(const void *p) +{ + return __get_unaligned_le32((const __u8 *)p); +} + +static inline __u64 get_unaligned_le64(const void *p) +{ + return __get_unaligned_le64((const __u8 *)p); +} + +static inline void put_unaligned_le16(__u16 val, void *p) +{ + __put_unaligned_le16(val, p); +} + +static inline void put_unaligned_le32(__u32 val, void *p) +{ + __put_unaligned_le32(val, p); +} + +static inline void put_unaligned_le64(__u64 val, void *p) +{ + __put_unaligned_le64(val, p); +} + +#endif /* _TOOLS_LE_BYTESHIFT_H */ diff --git a/com32/tools/relocs.c b/com32/tools/relocs.c index 24742060..f06af6e4 100644 --- a/com32/tools/relocs.c +++ b/com32/tools/relocs.c @@ -1,6 +1,3 @@ -/* - * This file is taken from the Linux kernel and is distributed under GPL v2. - */ #include <stdio.h> #include <stdarg.h> #include <stdlib.h> @@ -13,12 +10,16 @@ #define USE_BSD #include <endian.h> #include <regex.h> -#include <sys/types.h> +#include <tools/le_byteshift.h> + +static void die(char *fmt, ...); #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) static Elf32_Ehdr ehdr; static unsigned long reloc_count, reloc_idx; static unsigned long *relocs; +static unsigned long reloc16_count, reloc16_idx; +static unsigned long *relocs16; struct section { Elf32_Shdr shdr; @@ -29,60 +30,89 @@ struct section { }; static struct section *secs; -static void die(char *fmt, ...) -{ - va_list ap; - va_start(ap, fmt); - vfprintf(stderr, fmt, ap); - va_end(ap); - exit(1); -} +enum symtype { + S_ABS, + S_REL, + S_SEG, + S_LIN, + S_NSYMTYPES +}; +static const char * const sym_regex_kernel[S_NSYMTYPES] = { /* - * Following symbols have been audited. Don't warn user about + * Following symbols have been audited. There values are constant and do + * not change if bzImage is loaded at a different physical address than + * the address for which it has been compiled. Don't warn user about * absolute relocations present w.r.t these symbols. */ + [S_ABS] = + "^(__.*_len|__.*_dwords)$", -/* True absolute relocations */ +/* + * These symbols are known to be relative, even if the linker marks them + * as absolute (typically defined outside any section in the linker script.) + */ + [S_REL] = + "^(__.*_start|__.*_end|_end|_[se](text|data))$", +}; -static const char safe_abs_regex[] = -"^(__.*_len|__.*_dwords)$"; -static regex_t safe_abs_regex_c; -static int is_safe_abs_reloc(const char *sym_name) -{ - return !regexec(&safe_abs_regex_c, sym_name, 0, NULL, 0); -} +static const char * const sym_regex_realmode[S_NSYMTYPES] = { +/* + * These are 16-bit segment symbols when compiling 16-bit code. + */ + [S_SEG] = + "^real_mode_seg$", -/* These are relative even though the linker marks them absolute */ +/* + * These are offsets belonging to segments, as opposed to linear addresses, + * when compiling 16-bit code. + */ + [S_LIN] = + "^pa_", +}; -static const char safe_rel_regex[] = -"^(__.*_start|__.*_end|_end|_[se](text|data))$"; -static regex_t safe_rel_regex_c; +static const char * const *sym_regex; -static int is_safe_rel_reloc(const char *sym_name) +static regex_t sym_regex_c[S_NSYMTYPES]; +static int is_reloc(enum symtype type, const char *sym_name) { - return !regexec(&safe_rel_regex_c, sym_name, 0, NULL, 0); + return sym_regex[type] && + !regexec(&sym_regex_c[type], sym_name, 0, NULL, 0); } -static void regex_init(void) +static void regex_init(int use_real_mode) { - char errbuf[128]; - int err; + char errbuf[128]; + int err; + int i; - err = regcomp(&safe_abs_regex_c, safe_abs_regex, - REG_EXTENDED|REG_NOSUB); - if (err) { - regerror(err, &safe_abs_regex_c, errbuf, sizeof errbuf); - die("%s", errbuf); - } + if (use_real_mode) + sym_regex = sym_regex_realmode; + else + sym_regex = sym_regex_kernel; - err = regcomp(&safe_rel_regex_c, safe_rel_regex, - REG_EXTENDED|REG_NOSUB); - if (err) { - regerror(err, &safe_rel_regex_c, errbuf, sizeof errbuf); - die("%s", errbuf); - } + for (i = 0; i < S_NSYMTYPES; i++) { + if (!sym_regex[i]) + continue; + + err = regcomp(&sym_regex_c[i], sym_regex[i], + REG_EXTENDED|REG_NOSUB); + + if (err) { + regerror(err, &sym_regex_c[i], errbuf, sizeof errbuf); + die("%s", errbuf); + } + } +} + +static void die(char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + exit(1); } static const char *sym_type(unsigned type) @@ -153,13 +183,16 @@ static const char *rel_type(unsigned type) REL_TYPE(R_386_RELATIVE), REL_TYPE(R_386_GOTOFF), REL_TYPE(R_386_GOTPC), + REL_TYPE(R_386_8), + REL_TYPE(R_386_PC8), + REL_TYPE(R_386_16), + REL_TYPE(R_386_PC16), #undef REL_TYPE }; - const char *name = NULL; - if (type < ARRAY_SIZE(type_name)) + const char *name = "unknown type rel type name"; + if (type < ARRAY_SIZE(type_name) && type_name[type]) { name = type_name[type]; - if (!name) - name = "unknown"; + } return name; } @@ -189,7 +222,7 @@ static const char *sym_name(const char *sym_strtab, Elf32_Sym *sym) name = sym_strtab + sym->st_name; } else { - name = sec_name(secs[sym->st_shndx].shdr.sh_name); + name = sec_name(sym->st_shndx); } return name; } @@ -428,7 +461,7 @@ static void print_absolute_symbols(void) printf("\n"); } -static int print_absolute_relocs(FILE *f) +static void print_absolute_relocs(void) { int i, printed = 0; @@ -472,17 +505,18 @@ static int print_absolute_relocs(FILE *f) * Before warning check if this absolute symbol * relocation is harmless. */ - if (is_safe_abs_reloc(name) || - is_safe_rel_reloc(name)) + if (is_reloc(S_ABS, name) || is_reloc(S_REL, name)) continue; if (!printed) { - fprintf(f, "Unknown absolute relocations present\n"); - fprintf(f, "Offset Info Type Sym.Value Sym.Name\n"); + printf("WARNING: Absolute relocations" + " present\n"); + printf("Offset Info Type Sym.Value " + "Sym.Name\n"); printed = 1; } - fprintf(f, "%08x %08x %10s %08x %s\n", + printf("%08x %08x %10s %08x %s\n", rel->r_offset, rel->r_info, rel_type(ELF32_R_TYPE(rel->r_info)), @@ -492,12 +526,11 @@ static int print_absolute_relocs(FILE *f) } if (printed) - fputc('\n', f); - - return printed; + printf("\n"); } -static void walk_relocs(void (*visit)(Elf32_Rel *rel, Elf32_Sym *sym)) +static void walk_relocs(void (*visit)(Elf32_Rel *rel, Elf32_Sym *sym), + int use_real_mode) { int i; /* Walk through the relocations */ @@ -522,31 +555,71 @@ static void walk_relocs(void (*visit)(Elf32_Rel *rel, Elf32_Sym *sym)) Elf32_Rel *rel; Elf32_Sym *sym; unsigned r_type; + const char *symname; + int shn_abs; + rel = &sec->reltab[j]; sym = &sh_symtab[ELF32_R_SYM(rel->r_info)]; r_type = ELF32_R_TYPE(rel->r_info); - /* Don't visit relocations to absolute symbols */ - if (sym->st_shndx == SHN_ABS && - !is_safe_rel_reloc(sym_name(sym_strtab, sym))) - continue; + + shn_abs = sym->st_shndx == SHN_ABS; switch (r_type) { case R_386_NONE: case R_386_PC32: + case R_386_PC16: + case R_386_PC8: case R_386_GOTPC: case R_386_GOTOFF: case R_386_GOT32: case R_386_PLT32: - /* Relative relocations don't need to - be adjusted */ + /* + * NONE can be ignored and and PC relative + * relocations don't need to be adjusted. + */ + break; + + case R_386_16: + symname = sym_name(sym_strtab, sym); + if (!use_real_mode) + goto bad; + if (shn_abs) { + if (is_reloc(S_ABS, symname)) + break; + else if (!is_reloc(S_SEG, symname)) + goto bad; + } else { + if (is_reloc(S_LIN, symname)) + goto bad; + else + break; + } + visit(rel, sym); break; + case R_386_32: - /* Visit relocations that need adjustment */ + symname = sym_name(sym_strtab, sym); + if (shn_abs) { + if (is_reloc(S_ABS, symname)) + break; + else if (!is_reloc(S_REL, symname)) + goto bad; + } else { + if (use_real_mode && + !is_reloc(S_LIN, symname)) + break; + } visit(rel, sym); break; default: die("Unsupported relocation type: %s (%d)\n", rel_type(r_type), r_type); + break; + bad: + symname = sym_name(sym_strtab, sym); + die("Invalid %s %s relocation: %s\n", + shn_abs ? "absolute" : "relative", + rel_type(r_type), symname); } } } @@ -554,8 +627,12 @@ static void walk_relocs(void (*visit)(Elf32_Rel *rel, Elf32_Sym *sym)) static void count_reloc(Elf32_Rel *rel, Elf32_Sym *sym) { - (void)rel; (void)sym; - reloc_count += 1; + (void)sym; + + if (ELF32_R_TYPE(rel->r_info) == R_386_16) + reloc16_count++; + else + reloc_count++; } static void collect_reloc(Elf32_Rel *rel, Elf32_Sym *sym) @@ -563,7 +640,10 @@ static void collect_reloc(Elf32_Rel *rel, Elf32_Sym *sym) (void)sym; /* Remember the address that needs to be adjusted. */ - relocs[reloc_idx++] = rel->r_offset; + if (ELF32_R_TYPE(rel->r_info) == R_386_16) + relocs16[reloc16_idx++] = rel->r_offset; + else + relocs[reloc_idx++] = rel->r_offset; } static int cmp_relocs(const void *va, const void *vb) @@ -573,23 +653,41 @@ static int cmp_relocs(const void *va, const void *vb) return (*a == *b)? 0 : (*a > *b)? 1 : -1; } -static void emit_relocs(int as_text) +static int write32(unsigned int v, FILE *f) +{ + unsigned char buf[4]; + + put_unaligned_le32(v, buf); + return fwrite(buf, 1, 4, f) == 4 ? 0 : -1; +} + +static void emit_relocs(int as_text, int use_real_mode) { int i; /* Count how many relocations I have and allocate space for them. */ reloc_count = 0; - walk_relocs(count_reloc); + walk_relocs(count_reloc, use_real_mode); relocs = malloc(reloc_count * sizeof(relocs[0])); if (!relocs) { die("malloc of %d entries for relocs failed\n", reloc_count); } + + relocs16 = malloc(reloc16_count * sizeof(relocs[0])); + if (!relocs16) { + die("malloc of %d entries for relocs16 failed\n", + reloc16_count); + } /* Collect up the relocations */ reloc_idx = 0; - walk_relocs(collect_reloc); + walk_relocs(collect_reloc, use_real_mode); + + if (reloc16_count && !use_real_mode) + die("Segment relocations found but --realmode not specified\n"); /* Order the relocations for more efficient processing */ qsort(relocs, reloc_count, sizeof(relocs[0]), cmp_relocs); + qsort(relocs16, reloc16_count, sizeof(relocs16[0]), cmp_relocs); /* Print the relocations */ if (as_text) { @@ -598,61 +696,83 @@ static void emit_relocs(int as_text) */ printf(".section \".data.reloc\",\"a\"\n"); printf(".balign 4\n"); - for (i = 0; i < reloc_count; i++) { - printf("\t .long 0x%08lx\n", relocs[i]); + if (use_real_mode) { + printf("\t.long %lu\n", reloc16_count); + for (i = 0; i < reloc16_count; i++) + printf("\t.long 0x%08lx\n", relocs16[i]); + printf("\t.long %lu\n", reloc_count); + for (i = 0; i < reloc_count; i++) { + printf("\t.long 0x%08lx\n", relocs[i]); + } + } else { + /* Print a stop */ + printf("\t.long 0x%08lx\n", (unsigned long)0); + for (i = 0; i < reloc_count; i++) { + printf("\t.long 0x%08lx\n", relocs[i]); + } } + printf("\n"); } else { - unsigned char buf[4]; - /* Now print each relocation */ - for (i = 0; i < reloc_count; i++) { - buf[0] = (relocs[i] >> 0) & 0xff; - buf[1] = (relocs[i] >> 8) & 0xff; - buf[2] = (relocs[i] >> 16) & 0xff; - buf[3] = (relocs[i] >> 24) & 0xff; - fwrite(buf, 4, 1, stdout); + if (use_real_mode) { + write32(reloc16_count, stdout); + for (i = 0; i < reloc16_count; i++) + write32(relocs16[i], stdout); + write32(reloc_count, stdout); + + /* Now print each relocation */ + for (i = 0; i < reloc_count; i++) + write32(relocs[i], stdout); + } else { + /* Print a stop */ + write32(0, stdout); + + /* Now print each relocation */ + for (i = 0; i < reloc_count; i++) { + write32(relocs[i], stdout); + } } - /* Print a stop */ - memset(buf, 0, sizeof buf); - fwrite(buf, 4, 1, stdout); } } static void usage(void) { - die("relocs [--abs-syms |--abs-relocs | --text] vmlinux\n"); + die("relocs [--abs-syms|--abs-relocs|--text|--realmode] vmlinux\n"); } int main(int argc, char **argv) { int show_absolute_syms, show_absolute_relocs; - int as_text; + int as_text, use_real_mode; const char *fname; FILE *fp; int i; - int err = 0; show_absolute_syms = 0; show_absolute_relocs = 0; as_text = 0; + use_real_mode = 0; fname = NULL; for (i = 1; i < argc; i++) { char *arg = argv[i]; if (*arg == '-') { - if (strcmp(argv[1], "--abs-syms") == 0) { + if (strcmp(arg, "--abs-syms") == 0) { show_absolute_syms = 1; continue; } - - if (strcmp(argv[1], "--abs-relocs") == 0) { + if (strcmp(arg, "--abs-relocs") == 0) { show_absolute_relocs = 1; continue; } - else if (strcmp(argv[1], "--text") == 0) { + if (strcmp(arg, "--text") == 0) { as_text = 1; continue; } + if (strcmp(arg, "--realmode") == 0) { + use_real_mode = 1; + continue; + } } else if (!fname) { fname = arg; @@ -663,10 +783,7 @@ int main(int argc, char **argv) if (!fname) { usage(); } - - - regex_init(); - + regex_init(use_real_mode); fp = fopen(fname, "r"); if (!fp) { die("Cannot open %s: %s\n", @@ -682,10 +799,9 @@ int main(int argc, char **argv) return 0; } if (show_absolute_relocs) { - print_absolute_relocs(stdout); + print_absolute_relocs(); return 0; } - err = print_absolute_relocs(stderr); - emit_relocs(as_text); - return err; + emit_relocs(as_text, use_real_mode); + return 0; } diff --git a/core/syslinux.ld b/core/syslinux.ld index 40a01394..11adbcb8 100644 --- a/core/syslinux.ld +++ b/core/syslinux.ld @@ -280,8 +280,9 @@ SECTIONS __ctors_lma = __ctors_vma + __text_lma - __text_vma; .ctors : AT(__ctors_lma) { __ctors_start = .; - KEEP (*(SORT(.ctors.*))) - KEEP (*(.ctors)) + KEEP (*(SORT(.preinit_array*))) + KEEP (*(SORT(.init_array*))) + KEEP (*(SORT(.ctors*))) __ctors_end = .; } @@ -289,8 +290,8 @@ SECTIONS __dtors_lma = __dtors_vma + __text_lma - __text_vma; .dtors : AT(__dtors_lma) { __dtors_start = .; - KEEP (*(SORT(.dtors.*))) - KEEP (*(.dtors)) + KEEP (*(SORT(.fini_array*))) + KEEP (*(SORT(.dtors*))) __dtors_end = .; } diff --git a/diag/mbr/README b/diag/mbr/README index fb7a7dd8..96b67c6c 100644 --- a/diag/mbr/README +++ b/diag/mbr/README @@ -5,11 +5,13 @@ handoff.bin Show the data that the BIOS/MBR hands off to an MBR/VBR. +++ USAGE +++ +NOTE: in the examples, mbr.bin, /dev/hda and /dev/hda1 are used as generic representations. + Writing out an MBR is straight forward (it is assumed below that /dev/hda is the target raw device and /dev/hda1 is the target partition): dd conv=notrunc bs=440 count=1 if=mbr.bin of=/dev/hda -Writing a VBR to match Syslinux requires more work as it must have a jump and be offset into the partition: +Writing a VBR to match Syslinux requires more work as it must have a jump and be offset into the partition (and as a result the code must be compaible with this offset): echo -en "\0353\0130\0220" |dd conv=notrunc bs=1 count=3 of=/dev/hda1 dd conv=notrunc bs=2 count=210 seek=45 if=mbr.bin of=/dev/hda1 diff --git a/diag/mbr/handoff.S b/diag/mbr/handoff.S index 7af3fdeb..ab8582b7 100644 --- a/diag/mbr/handoff.S +++ b/diag/mbr/handoff.S @@ -43,11 +43,11 @@ * Install instructions (assuming your target is /dev/dev; file or block device): * * MBR: - * dd conv=notrunc bs=440 count=1 if=mbr_ho.bin of=/dev/dev + * dd conv=notrunc bs=440 count=1 if=handoff.bin of=/dev/dev * * VBR/PBR (should work for FAT12/16/32, ext[234]fs, btrfs): * echo -en "\0353\0130\0220" |dd conv=notrunc bs=1 count=3 of=/dev/dev - * dd conv=notrunc bs=2 count=210 seek=45 if=mbr_ho.bin of=/dev/dev + * dd conv=notrunc bs=2 count=210 seek=45 if=handoff.bin of=/dev/dev */ // #define DEBUG_MARKER1 /* Insert markers in binary */ diff --git a/doc/pxechn.txt b/doc/pxechn.txt new file mode 100644 index 00000000..a09bbe2b --- /dev/null +++ b/doc/pxechn.txt @@ -0,0 +1,94 @@ += pxechn.c32 = +:doctype: manpage +:author: Gene Cumm +:email: gene.cumm@gmail.com +:revdate: 2012-05-27 + + +== NAME == +pxechn.c32 - Chainboot to new NBP + + +== SYNOPSIS == +*pxechn.c32* [-h | --help | -?] +*pxechn.c32* -r 'FILE' +*pxechn.c32* 'FILE' ['OPTIONS'] + + +== DESCRIPTION == +Chainboot to a new NBP (Network Boot Program) 'FILE' with options to adjust PXE packet #3 (PXENV_PACKET_TYPE_CACHED_REPLY) to alter end behavior. 'FILE' may be a filename, an IP::FN ( 192.168.1.1::path/to/file.0 ), or URL. 'FILE' is parsed to adjust the DHCP 'sname' field/option 66 and 'file' field/option 67. +// but these may be override-able in the future. + + +== OPTIONS == +*-c* 'CONFIG':: + PXELINUX config file (DHCP Option 209). + +// *-f* 'MOD':: +// Force behavior specified by 'MOD' +// +// *-g* 'HOST':: +// Set DHCP gateway/relay. Parsed by pxe_dns(). +// +*-h*, *--help*, *-?*:: + Print usage information; invalid options will also cause this. + +// *-n*:: +// Use native methods, ignoring underlying gPXE/iPXE. +// +// *-N*:: +// Use non-native methods to utilize gPXE/iPXE (if available). +// +*-o* 'OPT.TYPE=VALUE':: + Specify a generic option. 'OPT' is in 'DECIMAL INPUT' format (below). 'TYPE' specifies the output type and input syntax. 'b'yte, 'w'ord(2B), 'l'ong(4B), 'q'uad(8B), character 's'tring and colon-separated he'x' string (case insensitive; bytes must have 2 digits and each byte must be separated). byte, word, long and quad input values must meet criteria for 'DECIMAL INPUT' + +*-p* 'PATH':: + PXELINUX path (DHCP Option 210). + +*-r*:: + Call the PXE stack with PXENV_RESTART_TFTP. _Must_ be the only option and before 'FILE'. + +*-t* 'SECONDS':: + PXELINUX timeout (DHCP Option 211). + +// *-u*:: +// Copy UUID (Option 97) if found in packet #1 + +*-w*:: + wait after loading before booting for user input. + +*-W*:: + Enable WDS (Windows Deployment Services) - specific options. + + +== DECIMAL INPUT == +All parameters that are defaulted to decimal format are processed by *strtoul*(3) with a base of 0 which allows alternate formats and finds a suitable non-space separating character. + + +== EXAMPLES == +pxechn.c32 http://myhost.dom.loc/path/nbp.0 -c myconfig + Load nbp.0 and set PXELINUX config (option 209). + +pxechn.c32 gpxelinux.0 -p http://172.16.23.1/tftp/ -w -c myconfig -o 15.s=domain.loc -o 6.x=0A:01:01:02:ac:17:4D:Ec - + Load gpxelinux.0 from the current directory, set prefix, wait to execute, set first config, set the domain name and 2 domain name servers (case mixed to show insensitivity; 10.1.1.2 and 172.23.77.236). + +pxechn.c32 gpxelinux.0 -p http://172.16.23.1/tftp/ -w -X A012345678 -x 197:00d0de00 +pxechn.c32 gpxelinux.0 -p http://172.16.23.1/tftp/ -w -X A012:3456:78 -x 197:00-d0-de-00 + Both of these are equivalent. Load gpxelinux.0 (relative to the current directory and not altering sname/option 66), set the PXELINUX path prefix, wait after loading, set option 160 to 0x12 0x34 0x56 0x78, and option 197 to 0x00 0xD0 0xDE 0x00. + + +== NOTES == +Please note that some NBPs may ignore packet #3 by either not examining it at all or by issuing its own DHCP DISCOVER/REQUEST, negating all DHCP field/option modifications by pxechn.c32. + +URL specifications in 'FILE' that include user/password before the host will currently cause the siaddr field to not be set properly. + +The non-space constraint is due to how Syslinux variants parse the command line as of 2012-01-12. + + +== AUTHOR == +{author} <{email}> + + +== COPYRIGHT == +Copyright \(C) 2012 {author}. Free use of this software is granted under +the terms of the GNU General Public License (GPL). |