summaryrefslogtreecommitdiff
path: root/com32/lib
diff options
context:
space:
mode:
Diffstat (limited to 'com32/lib')
-rw-r--r--com32/lib/Makefile9
-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/lib/pci/scan.c6
-rw-r--r--com32/lib/strreplace.c58
-rw-r--r--com32/lib/syslinux/disk.c534
-rw-r--r--com32/lib/syslinux/load_linux.c49
-rw-r--r--com32/lib/syslinux/setup_data.c47
9 files changed, 989 insertions, 29 deletions
diff --git a/com32/lib/Makefile b/com32/lib/Makefile
index 105c2bd6..5ab1fac4 100644
--- a/com32/lib/Makefile
+++ b/com32/lib/Makefile
@@ -31,7 +31,8 @@ LIBOBJS = \
skipspace.o \
chrreplace.o \
bufprintf.o \
- inet.o \
+ inet.o dhcppack.o dhcpunpack.o \
+ strreplace.o \
\
lmalloc.o lstrdup.o \
\
@@ -126,7 +127,11 @@ LIBOBJS = \
syslinux/setadv.o \
\
syslinux/video/fontquery.o syslinux/video/forcetext.o \
- syslinux/video/reportmode.o
+ syslinux/video/reportmode.o \
+ \
+ syslinux/disk.o \
+ \
+ syslinux/setup_data.o
# These are the objects which are also imported into the core
LIBCOREOBJS = \
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/lib/pci/scan.c b/com32/lib/pci/scan.c
index e0974f98..fe00fc25 100644
--- a/com32/lib/pci/scan.c
+++ b/com32/lib/pci/scan.c
@@ -579,14 +579,14 @@ void free_pci_domain(struct pci_domain *domain)
free(func->dev_info);
free(func);
}
- free(slot);
}
+ free(slot);
}
- free(bus);
}
+ free(bus);
}
- free(domain);
}
+ free(domain);
}
}
diff --git a/com32/lib/strreplace.c b/com32/lib/strreplace.c
new file mode 100644
index 00000000..d59efe0e
--- /dev/null
+++ b/com32/lib/strreplace.c
@@ -0,0 +1,58 @@
+/* ----------------------------------------------------------------------- *
+ *
+ * Copyright 2011 Erwan Velu - All Rights Reserved
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom
+ * the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall
+ * be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * -----------------------------------------------------------------------
+ */
+
+#include <string.h>
+#include <stdlib.h>
+
+char *strreplace(const char *string, const char *string_to_replace,
+ const char *string_to_insert)
+{
+ char *token = NULL;
+ char *out = NULL;
+
+ size_t slen, srlen, silen;
+
+ token = strstr(string, string_to_replace);
+ if (!token)
+ return strdup(string);
+
+ slen = strlen(string);
+ srlen = strlen(string_to_replace);
+ silen = strlen(string_to_insert);
+
+ out = malloc(slen - srlen + silen + 1);
+ if (!out)
+ return NULL;
+
+ memcpy(out, string, token - string);
+ memcpy(out + (token - string), string_to_insert, silen);
+ memcpy(out + (token - string) + silen, token + srlen,
+ slen - srlen - (token - string) + 1);
+
+ return out;
+}
diff --git a/com32/lib/syslinux/disk.c b/com32/lib/syslinux/disk.c
new file mode 100644
index 00000000..d6409af6
--- /dev/null
+++ b/com32/lib/syslinux/disk.c
@@ -0,0 +1,534 @@
+/* ----------------------------------------------------------------------- *
+ *
+ * Copyright 2003-2009 H. Peter Anvin - All Rights Reserved
+ * Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin
+ * Copyright (C) 2010 Shao Miller
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom
+ * the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall
+ * be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * ----------------------------------------------------------------------- */
+
+/**
+ * @file disk.c
+ *
+ * Deal with disks and partitions
+ */
+
+#include <dprintf.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslinux/disk.h>
+
+/**
+ * Call int 13h, but with retry on failure. Especially floppies need this.
+ *
+ * @v inreg CPU register settings upon INT call
+ * @v outreg CPU register settings returned by INT call
+ * @ret (int) 0 upon success, -1 upon failure
+ */
+int disk_int13_retry(const com32sys_t * inreg, com32sys_t * outreg)
+{
+ int retry = 6; /* Number of retries */
+ com32sys_t tmpregs;
+
+ if (!outreg)
+ outreg = &tmpregs;
+
+ while (retry--) {
+ __intcall(0x13, inreg, outreg);
+ if (!(outreg->eflags.l & EFLAGS_CF))
+ return 0; /* CF=0, OK */
+ }
+
+ return -1; /* Error */
+}
+
+/**
+ * Query disk parameters and EBIOS availability for a particular disk.
+ *
+ * @v disk The INT 0x13 disk drive number to process
+ * @v diskinfo The structure to save the queried params to
+ * @ret (int) 0 upon success, -1 upon failure
+ */
+int disk_get_params(int disk, struct disk_info *const diskinfo)
+{
+ static com32sys_t inreg, outreg;
+ struct disk_ebios_eparam *eparam = __com32.cs_bounce;
+
+ memset(diskinfo, 0, sizeof *diskinfo);
+ diskinfo->disk = disk;
+ diskinfo->bps = SECTOR;
+
+ /* Get EBIOS support */
+ memset(&inreg, 0, sizeof inreg);
+ inreg.eax.b[1] = 0x41;
+ inreg.ebx.w[0] = 0x55aa;
+ inreg.edx.b[0] = disk;
+ inreg.eflags.b[0] = 0x3; /* CF set */
+
+ __intcall(0x13, &inreg, &outreg);
+
+ if (!(outreg.eflags.l & EFLAGS_CF) &&
+ outreg.ebx.w[0] == 0xaa55 && (outreg.ecx.b[0] & 1)) {
+ diskinfo->ebios = 1;
+ }
+
+ /* Get extended disk parameters if ebios == 1 */
+ if (diskinfo->ebios) {
+ memset(&inreg, 0, sizeof inreg);
+ inreg.eax.b[1] = 0x48;
+ inreg.edx.b[0] = disk;
+ inreg.esi.w[0] = OFFS(eparam);
+ inreg.ds = SEG(eparam);
+
+ memset(eparam, 0, sizeof *eparam);
+ eparam->len = sizeof *eparam;
+
+ __intcall(0x13, &inreg, &outreg);
+
+ if (!(outreg.eflags.l & EFLAGS_CF)) {
+ diskinfo->lbacnt = eparam->lbacnt;
+ if (eparam->bps)
+ diskinfo->bps = eparam->bps;
+ /*
+ * don't think about using geometry data returned by
+ * 48h, as it can differ from 08h a lot ...
+ */
+ }
+ }
+ /*
+ * Get disk parameters the old way - really only useful for hard
+ * disks, but if we have a partitioned floppy it's actually our best
+ * chance...
+ */
+ memset(&inreg, 0, sizeof inreg);
+ inreg.eax.b[1] = 0x08;
+ inreg.edx.b[0] = disk;
+
+ __intcall(0x13, &inreg, &outreg);
+
+ if (outreg.eflags.l & EFLAGS_CF)
+ return diskinfo->ebios ? 0 : -1;
+
+ diskinfo->spt = 0x3f & outreg.ecx.b[0];
+ diskinfo->head = 1 + outreg.edx.b[1];
+ diskinfo->cyl = 1 + (outreg.ecx.b[1] | ((outreg.ecx.b[0] & 0xc0u) << 2));
+
+ if (diskinfo->spt)
+ diskinfo->cbios = 1; /* Valid geometry */
+ else {
+ diskinfo->head = 1;
+ diskinfo->spt = 1;
+ diskinfo->cyl = 1;
+ }
+
+ if (!diskinfo->lbacnt)
+ diskinfo->lbacnt = diskinfo->cyl * diskinfo->head * diskinfo->spt;
+
+ return 0;
+}
+
+/**
+ * Get disk block(s) and return a malloc'd buffer.
+ *
+ * @v diskinfo The disk drive to read from
+ * @v lba The logical block address to begin reading at
+ * @v count The number of sectors to read
+ * @ret data An allocated buffer with the read data
+ *
+ * Uses the disk number and information from diskinfo. Read count sectors
+ * from drive, starting at lba. Return a new buffer, or NULL upon failure.
+ */
+void *disk_read_sectors(const struct disk_info *const diskinfo, uint64_t lba,
+ uint8_t count)
+{
+ com32sys_t inreg;
+ struct disk_ebios_dapa *dapa = __com32.cs_bounce;
+ void *buf = (char *)__com32.cs_bounce + diskinfo->bps;
+ void *data;
+ uint32_t maxcnt;
+
+ maxcnt = (__com32.cs_bounce_size - diskinfo->bps) / diskinfo->bps;
+ if (!count || count > maxcnt || lba + count > diskinfo->lbacnt)
+ return NULL;
+
+ memset(&inreg, 0, sizeof inreg);
+
+ if (diskinfo->ebios) {
+ dapa->len = sizeof(*dapa);
+ dapa->count = count;
+ dapa->off = OFFS(buf);
+ dapa->seg = SEG(buf);
+ dapa->lba = lba;
+
+ inreg.esi.w[0] = OFFS(dapa);
+ inreg.ds = SEG(dapa);
+ inreg.edx.b[0] = diskinfo->disk;
+ inreg.eax.b[1] = 0x42; /* Extended read */
+ } else {
+ unsigned int c, h, s, t;
+ /*
+ * if we passed lba + count check and we get here, that means that
+ * lbacnt was calculated from chs geometry (or faked from 1/1/1), thus
+ * 32bits are perfectly enough and lbacnt corresponds to cylinder
+ * boundary
+ */
+ s = lba % diskinfo->spt;
+ t = lba / diskinfo->spt;
+ h = t % diskinfo->head;
+ c = t / diskinfo->head;
+
+ inreg.eax.b[0] = count;
+ inreg.eax.b[1] = 0x02; /* Read */
+ inreg.ecx.b[1] = c;
+ inreg.ecx.b[0] = ((c & 0x300) >> 2) | (s+1);
+ inreg.edx.b[1] = h;
+ inreg.edx.b[0] = diskinfo->disk;
+ inreg.ebx.w[0] = OFFS(buf);
+ inreg.es = SEG(buf);
+ }
+
+ if (disk_int13_retry(&inreg, NULL))
+ return NULL;
+
+ data = malloc(count * diskinfo->bps);
+ if (data)
+ memcpy(data, buf, count * diskinfo->bps);
+ return data;
+}
+
+/**
+ * Write disk block(s).
+ *
+ * @v diskinfo The disk drive to write to
+ * @v lba The logical block address to begin writing at
+ * @v data The data to write
+ * @v count The number of sectors to write
+ * @ret (int) 0 upon success, -1 upon failure
+ *
+ * Uses the disk number and information from diskinfo.
+ * Write sector(s) to a disk drive, starting at lba.
+ */
+int disk_write_sectors(const struct disk_info *const diskinfo, uint64_t lba,
+ const void *data, uint8_t count)
+{
+ com32sys_t inreg;
+ struct disk_ebios_dapa *dapa = __com32.cs_bounce;
+ void *buf = (char *)__com32.cs_bounce + diskinfo->bps;
+ uint32_t maxcnt;
+
+ maxcnt = (__com32.cs_bounce_size - diskinfo->bps) / diskinfo->bps;
+ if (!count || count > maxcnt || lba + count > diskinfo->lbacnt)
+ return -1;
+
+ memcpy(buf, data, count * diskinfo->bps);
+ memset(&inreg, 0, sizeof inreg);
+
+ if (diskinfo->ebios) {
+ dapa->len = sizeof(*dapa);
+ dapa->count = count;
+ dapa->off = OFFS(buf);
+ dapa->seg = SEG(buf);
+ dapa->lba = lba;
+
+ inreg.esi.w[0] = OFFS(dapa);
+ inreg.ds = SEG(dapa);
+ inreg.edx.b[0] = diskinfo->disk;
+ inreg.eax.b[1] = 0x43; /* Extended write */
+ } else {
+ unsigned int c, h, s, t;
+ /*
+ * if we passed lba + count check and we get here, that means that
+ * lbacnt was calculated from chs geometry (or faked from 1/1/1), thus
+ * 32bits are perfectly enough and lbacnt corresponds to cylinder
+ * boundary
+ */
+ s = lba % diskinfo->spt;
+ t = lba / diskinfo->spt;
+ h = t % diskinfo->head;
+ c = t / diskinfo->head;
+
+ inreg.eax.b[0] = count;
+ inreg.eax.b[1] = 0x03; /* Write */
+ inreg.ecx.b[1] = c;
+ inreg.ecx.b[0] = ((c & 0x300) >> 2) | (s+1);
+ inreg.edx.b[1] = h;
+ inreg.edx.b[0] = diskinfo->disk;
+ inreg.ebx.w[0] = OFFS(buf);
+ inreg.es = SEG(buf);
+ }
+
+ if (disk_int13_retry(&inreg, NULL))
+ return -1;
+
+ return 0; /* ok */
+}
+
+/**
+ * Write disk blocks and verify they were written.
+ *
+ * @v diskinfo The disk drive to write to
+ * @v lba The logical block address to begin writing at
+ * @v buf The data to write
+ * @v count The number of sectors to write
+ * @ret rv 0 upon success, -1 upon failure
+ *
+ * Uses the disk number and information from diskinfo.
+ * Writes sectors to a disk drive starting at lba, then reads them back
+ * to verify they were written correctly.
+ */
+int disk_write_verify_sectors(const struct disk_info *const diskinfo,
+ uint64_t lba, const void *buf, uint8_t count)
+{
+ char *rb;
+ int rv;
+
+ rv = disk_write_sectors(diskinfo, lba, buf, count);
+ if (rv)
+ return rv; /* Write failure */
+ rb = disk_read_sectors(diskinfo, lba, count);
+ if (!rb)
+ return -1; /* Readback failure */
+ rv = memcmp(buf, rb, count * diskinfo->bps);
+ free(rb);
+ return rv ? -1 : 0;
+}
+
+/**
+ * Dump info about a DOS partition entry
+ *
+ * @v part The 16-byte partition entry to examine
+ */
+void disk_dos_part_dump(const struct disk_dos_part_entry *const part)
+{
+ (void)part;
+ dprintf("Partition status _____ : 0x%.2x\n"
+ "Partition CHS start\n"
+ " Cylinder ___________ : 0x%.4x (%u)\n"
+ " Head _______________ : 0x%.2x (%u)\n"
+ " Sector _____________ : 0x%.2x (%u)\n"
+ "Partition type _______ : 0x%.2x\n"
+ "Partition CHS end\n"
+ " Cylinder ___________ : 0x%.4x (%u)\n"
+ " Head _______________ : 0x%.2x (%u)\n"
+ " Sector _____________ : 0x%.2x (%u)\n"
+ "Partition LBA start __ : 0x%.8x (%u)\n"
+ "Partition LBA count __ : 0x%.8x (%u)\n"
+ "-------------------------------\n",
+ part->active_flag,
+ chs_cylinder(part->start),
+ chs_cylinder(part->start),
+ chs_head(part->start),
+ chs_head(part->start),
+ chs_sector(part->start),
+ chs_sector(part->start),
+ part->ostype,
+ chs_cylinder(part->end),
+ chs_cylinder(part->end),
+ chs_head(part->end),
+ chs_head(part->end),
+ chs_sector(part->end),
+ chs_sector(part->end),
+ part->start_lba, part->start_lba, part->length, part->length);
+}
+
+/* Trivial error message output */
+static inline void error(const char *msg)
+{
+ fputs(msg, stderr);
+}
+
+/**
+ * This walk-map effectively reverses the little-endian
+ * portions of a GPT disk/partition GUID for a string representation.
+ * There might be a better header for this...
+ */
+static const char guid_le_walk_map[] = {
+ 3, -1, -1, -1, 0,
+ 5, -1, 0,
+ 3, -1, 0,
+ 2, 1, 0,
+ 1, 1, 1, 1, 1, 1
+};
+
+/**
+ * Fill a buffer with a textual GUID representation.
+ *
+ * @v buf Points to a minimum array of 37 chars
+ * @v id The GUID to represent as text
+ *
+ * The buffer must be >= char[37] and will be populated
+ * with an ASCII NUL C string terminator.
+ * Example: 11111111-2222-3333-4444-444444444444
+ * Endian: LLLLLLLL-LLLL-LLLL-BBBB-BBBBBBBBBBBB
+ */
+void guid_to_str(char *buf, const struct guid *const id)
+{
+ unsigned int i = 0;
+ const char *walker = (const char *)id;
+
+ while (i < sizeof(guid_le_walk_map)) {
+ walker += guid_le_walk_map[i];
+ if (!guid_le_walk_map[i])
+ *buf = '-';
+ else {
+ *buf = ((*walker & 0xF0) >> 4) + '0';
+ if (*buf > '9')
+ *buf += 'A' - '9' - 1;
+ buf++;
+ *buf = (*walker & 0x0F) + '0';
+ if (*buf > '9')
+ *buf += 'A' - '9' - 1;
+ }
+ buf++;
+ i++;
+ }
+ *buf = 0;
+}
+
+/**
+ * Create a GUID structure from a textual GUID representation.
+ *
+ * @v buf Points to a GUID string to parse
+ * @v id Points to a GUID to be populated
+ * @ret (int) Returns 0 upon success, -1 upon failure
+ *
+ * The input buffer must be >= 32 hexadecimal chars and be
+ * terminated with an ASCII NUL. Returns non-zero on failure.
+ * Example: 11111111-2222-3333-4444-444444444444
+ * Endian: LLLLLLLL-LLLL-LLLL-BBBB-BBBBBBBBBBBB
+ */
+int str_to_guid(const char *buf, struct guid *const id)
+{
+ char guid_seq[sizeof(struct guid) * 2];
+ unsigned int i = 0;
+ char *walker = (char *)id;
+
+ while (*buf && i < sizeof(guid_seq)) {
+ switch (*buf) {
+ /* Skip these three characters */
+ case '{':
+ case '}':
+ case '-':
+ break;
+ default:
+ /* Copy something useful to the temp. sequence */
+ if ((*buf >= '0') && (*buf <= '9'))
+ guid_seq[i] = *buf - '0';
+ else if ((*buf >= 'A') && (*buf <= 'F'))
+ guid_seq[i] = *buf - 'A' + 10;
+ else if ((*buf >= 'a') && (*buf <= 'f'))
+ guid_seq[i] = *buf - 'a' + 10;
+ else {
+ /* Or not */
+ error("Illegal character in GUID!\n");
+ return -1;
+ }
+ i++;
+ }
+ buf++;
+ }
+ /* Check for insufficient valid characters */
+ if (i < sizeof(guid_seq)) {
+ error("Too few GUID characters!\n");
+ return -1;
+ }
+ buf = guid_seq;
+ i = 0;
+ while (i < sizeof(guid_le_walk_map)) {
+ if (!guid_le_walk_map[i])
+ i++;
+ walker += guid_le_walk_map[i];
+ *walker = *buf << 4;
+ buf++;
+ *walker |= *buf;
+ buf++;
+ i++;
+ }
+ return 0;
+}
+
+/**
+ * Display GPT partition details.
+ *
+ * @v gpt_part The GPT partition entry to display
+ */
+void disk_gpt_part_dump(const struct disk_gpt_part_entry *const gpt_part)
+{
+ unsigned int i;
+ char guid_text[37];
+
+ dprintf("----------------------------------\n"
+ "GPT part. LBA first __ : 0x%.16llx\n"
+ "GPT part. LBA last ___ : 0x%.16llx\n"
+ "GPT part. attribs ____ : 0x%.16llx\n"
+ "GPT part. name _______ : '",
+ gpt_part->lba_first, gpt_part->lba_last, gpt_part->attribs);
+ for (i = 0; i < sizeof(gpt_part->name); i++) {
+ if (gpt_part->name[i])
+ dprintf("%c", gpt_part->name[i]);
+ }
+ dprintf("'");
+ guid_to_str(guid_text, &gpt_part->type);
+ dprintf("GPT part. type GUID __ : {%s}\n", guid_text);
+ guid_to_str(guid_text, &gpt_part->uid);
+ dprintf("GPT part. unique ID __ : {%s}\n", guid_text);
+}
+
+/**
+ * Display GPT header details.
+ *
+ * @v gpt The GPT header to display
+ */
+void disk_gpt_header_dump(const struct disk_gpt_header *const gpt)
+{
+ char guid_text[37];
+
+ printf("GPT sig ______________ : '%8.8s'\n"
+ "GPT major revision ___ : 0x%.4x\n"
+ "GPT minor revision ___ : 0x%.4x\n"
+ "GPT header size ______ : 0x%.8x\n"
+ "GPT header checksum __ : 0x%.8x\n"
+ "GPT reserved _________ : '%4.4s'\n"
+ "GPT LBA current ______ : 0x%.16llx\n"
+ "GPT LBA alternative __ : 0x%.16llx\n"
+ "GPT LBA first usable _ : 0x%.16llx\n"
+ "GPT LBA last usable __ : 0x%.16llx\n"
+ "GPT LBA part. table __ : 0x%.16llx\n"
+ "GPT partition count __ : 0x%.8x\n"
+ "GPT partition size ___ : 0x%.8x\n"
+ "GPT part. table chksum : 0x%.8x\n",
+ gpt->sig,
+ gpt->rev.fields.major,
+ gpt->rev.fields.minor,
+ gpt->hdr_size,
+ gpt->chksum,
+ gpt->reserved1,
+ gpt->lba_cur,
+ gpt->lba_alt,
+ gpt->lba_first_usable,
+ gpt->lba_last_usable,
+ gpt->lba_table, gpt->part_count, gpt->part_size, gpt->table_chksum);
+ guid_to_str(guid_text, &gpt->disk_guid);
+ printf("GPT disk GUID ________ : {%s}\n", guid_text);
+}
diff --git a/com32/lib/syslinux/load_linux.c b/com32/lib/syslinux/load_linux.c
index 45cd6965..856141f8 100644
--- a/com32/lib/syslinux/load_linux.c
+++ b/com32/lib/syslinux/load_linux.c
@@ -38,6 +38,7 @@
#include <inttypes.h>
#include <string.h>
#include <minmax.h>
+#include <errno.h>
#include <suffix_number.h>
#include <syslinux/align.h>
#include <syslinux/linux.h>
@@ -180,13 +181,16 @@ static int map_initramfs(struct syslinux_movelist **fraglist,
}
int syslinux_boot_linux(void *kernel_buf, size_t kernel_size,
- struct initramfs *initramfs, char *cmdline)
+ struct initramfs *initramfs,
+ struct setup_data *setup_data,
+ char *cmdline)
{
struct linux_header hdr, *whdr;
size_t real_mode_size, prot_mode_size;
addr_t real_mode_base, prot_mode_base;
addr_t irf_size;
size_t cmdline_size, cmdline_offset;
+ struct setup_data *sdp;
struct syslinux_rm_regs regs;
struct syslinux_movelist *fraglist = NULL;
struct syslinux_memmap *mmap = NULL;
@@ -449,6 +453,49 @@ int syslinux_boot_linux(void *kernel_buf, size_t kernel_size,
}
}
+ if (setup_data) {
+ uint64_t *prev_ptr = &whdr->setup_data;
+
+ for (sdp = setup_data->next; sdp != setup_data; sdp = sdp->next) {
+ struct syslinux_memmap *ml;
+ const addr_t align_mask = 15; /* Header is 16 bytes */
+ addr_t best_addr = 0;
+ size_t size = sdp->hdr.len + sizeof(sdp->hdr);
+
+ if (!sdp->data || !sdp->hdr.len)
+ continue;
+
+ if (hdr.version < 0x0209) {
+ /* Setup data not supported */
+ errno = ENXIO; /* Kind of arbitrary... */
+ goto bail;
+ }
+
+ for (ml = amap; ml->type != SMT_END; ml = ml->next) {
+ addr_t adj_start = (ml->start + align_mask) & ~align_mask;
+ addr_t adj_end = ml->next->start & ~align_mask;
+
+ if (ml->type == SMT_FREE && adj_end - adj_start >= size)
+ best_addr = (adj_end - size) & ~align_mask;
+ }
+
+ if (!best_addr)
+ goto bail;
+
+ *prev_ptr = best_addr;
+ prev_ptr = &sdp->hdr.next;
+
+ if (syslinux_add_memmap(&amap, best_addr, size, SMT_ALLOC))
+ goto bail;
+ if (syslinux_add_movelist(&fraglist, best_addr,
+ (addr_t)&sdp->hdr, sizeof sdp->hdr))
+ goto bail;
+ if (syslinux_add_movelist(&fraglist, best_addr + sizeof sdp->hdr,
+ (addr_t)sdp->data, sdp->hdr.len))
+ goto bail;
+ }
+ }
+
/* Set up the registers on entry */
memset(&regs, 0, sizeof regs);
regs.es = regs.ds = regs.ss = regs.fs = regs.gs = real_mode_base >> 4;
diff --git a/com32/lib/syslinux/setup_data.c b/com32/lib/syslinux/setup_data.c
new file mode 100644
index 00000000..a36c5b61
--- /dev/null
+++ b/com32/lib/syslinux/setup_data.c
@@ -0,0 +1,47 @@
+#include <stdlib.h>
+#include <syslinux/linux.h>
+#include <syslinux/loadfile.h>
+
+struct setup_data *setup_data_init(void)
+{
+ struct setup_data *setup_data;
+
+ setup_data = zalloc(sizeof(*setup_data));
+ if (!setup_data)
+ return NULL;
+
+ setup_data->prev = setup_data->next = setup_data;
+ return setup_data;
+}
+
+int setup_data_add(struct setup_data *head, uint32_t type,
+ const void *data, size_t data_len)
+{
+ struct setup_data *setup_data;
+
+ setup_data = zalloc(sizeof(*setup_data));
+ if (!setup_data)
+ return -1;
+
+ setup_data->data = data;
+ setup_data->hdr.len = data_len;
+ setup_data->hdr.type = type;
+ setup_data->prev = head->prev;
+ setup_data->next = head;
+ head->prev->next = setup_data;
+ head->prev = setup_data;
+
+ return 0;
+}
+
+int setup_data_load(struct setup_data *head, uint32_t type,
+ const char *filename)
+{
+ void *data;
+ size_t len;
+
+ if (loadfile(filename, &data, &len))
+ return -1;
+
+ return setup_data_add(head, type, data, len);
+}