aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorErwan Velu <erwanaliasr1@gmail.com>2012-05-29 21:45:47 +0200
committerErwan Velu <erwanaliasr1@gmail.com>2012-05-29 21:45:47 +0200
commit39b84929805d8ea62a2876d1741c32a62fa29229 (patch)
treec308b28880719e13c505bef0731dfe6ea86eb62e
parent871650dd981da850bc05eae6de7471b3069926e9 (diff)
parent2c2c75abc360c4803902f3effa84a28746e34fde (diff)
downloadsyslinux-39b84929805d8ea62a2876d1741c32a62fa29229.tar.gz
Merge branch 'master' of git://git.zytor.com/syslinux/syslinux
-rw-r--r--com32/include/dhcp.h40
-rw-r--r--com32/lib/Makefile2
-rw-r--r--com32/lib/com32.ld33
-rw-r--r--com32/lib/dhcppack.c166
-rw-r--r--com32/lib/dhcpunpack.c116
-rw-r--r--com32/modules/Makefile3
-rw-r--r--com32/modules/pxechn.c1122
-rw-r--r--com32/tools/Makefile2
-rw-r--r--com32/tools/include/tools/le_byteshift.h70
-rw-r--r--com32/tools/relocs.c312
-rw-r--r--core/syslinux.ld9
-rw-r--r--diag/mbr/README4
-rw-r--r--diag/mbr/handoff.S4
-rw-r--r--doc/pxechn.txt94
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(&reg, 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, &reg, &reg);
+
+ 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(&regs);
+ /* 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, &regs);
+ }
+ /* 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(&reg, 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, &reg, &reg);
+
+ 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).