summaryrefslogtreecommitdiff
path: root/com32/chain
diff options
context:
space:
mode:
Diffstat (limited to 'com32/chain')
-rw-r--r--com32/chain/Makefile42
-rw-r--r--com32/chain/chain.c658
-rw-r--r--com32/chain/chain.h14
-rw-r--r--com32/chain/common.h9
-rw-r--r--com32/chain/mangle.c618
-rw-r--r--com32/chain/mangle.h32
-rw-r--r--com32/chain/options.c376
-rw-r--r--com32/chain/options.h47
-rw-r--r--com32/chain/partiter.c805
-rw-r--r--com32/chain/partiter.h107
-rw-r--r--com32/chain/utility.c214
-rw-r--r--com32/chain/utility.h30
12 files changed, 2952 insertions, 0 deletions
diff --git a/com32/chain/Makefile b/com32/chain/Makefile
new file mode 100644
index 00000000..9d398a85
--- /dev/null
+++ b/com32/chain/Makefile
@@ -0,0 +1,42 @@
+## -----------------------------------------------------------------------
+##
+## Copyright 2001-2010 H. Peter Anvin - All Rights Reserved
+## Copyright 2010 Michal Soltys
+##
+## 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.
+##
+## -----------------------------------------------------------------------
+
+
+topdir = ../..
+MAKEDIR = $(topdir)/mk
+include $(MAKEDIR)/com32.mk
+
+OBJS = chain.o partiter.o utility.o options.o mangle.o
+
+all: chain.c32
+
+chain.elf: $(OBJS) $(LIBS) $(C_LIBS)
+ $(LD) $(LDFLAGS) -o $@ $^
+
+%.o: %.c
+ $(CC) $(MAKEDEPS) $(CFLAGS) $(CHAINEXTOPT) -c -o $@ $<
+
+tidy dist:
+ rm -f *.o *.lo *.a *.lst *.elf .*.d *.tmp
+
+clean: tidy
+ rm -f *.lnx
+
+spotless: clean
+ rm -f *.lss *.c32 *.com
+ rm -f *~ \#*
+
+install:
+
+
+-include .*.d
diff --git a/com32/chain/chain.c b/com32/chain/chain.c
new file mode 100644
index 00000000..30153c4d
--- /dev/null
+++ b/com32/chain/chain.c
@@ -0,0 +1,658 @@
+/* ----------------------------------------------------------------------- *
+ *
+ * Copyright 2003-2009 H. Peter Anvin - All Rights Reserved
+ * Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin
+ * Copyright 2010 Shao Miller
+ * Copyright 2010 Michal Soltys
+ *
+ * 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.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * Please see doc/chain.txt for the detailed documentation.
+ */
+
+#include <com32.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <console.h>
+#include <consoles.h>
+#include <minmax.h>
+#include <stdbool.h>
+#include <dprintf.h>
+#include <errno.h>
+#include <unistd.h>
+#include <syslinux/loadfile.h>
+#include <syslinux/bootrm.h>
+#include <syslinux/config.h>
+#include <syslinux/disk.h>
+#include <syslinux/video.h>
+#include "common.h"
+#include "chain.h"
+#include "utility.h"
+#include "options.h"
+#include "partiter.h"
+#include "mangle.h"
+
+static int fixed_cnt = 128; /* see comments in main() */
+
+static int overlap(const struct data_area *a, const struct data_area *b)
+{
+ return
+ a->base + a->size > b->base &&
+ b->base + b->size > a->base;
+}
+
+static int is_phys(uint8_t sdifs)
+{
+ return
+ sdifs == SYSLINUX_FS_SYSLINUX ||
+ sdifs == SYSLINUX_FS_EXTLINUX ||
+ sdifs == SYSLINUX_FS_ISOLINUX;
+}
+
+/*
+ * Search for a specific drive, based on the MBR signature.
+ * Return drive and iterator at 0th position.
+ */
+static int find_by_sig(uint32_t mbr_sig,
+ struct part_iter **_boot_part)
+{
+ struct part_iter *boot_part = NULL;
+ struct disk_info diskinfo;
+ int drive;
+
+ for (drive = 0x80; drive < 0x80 + fixed_cnt; drive++) {
+ if (disk_get_params(drive, &diskinfo))
+ continue; /* Drive doesn't exist */
+ if (!(boot_part = pi_begin(&diskinfo, 0)))
+ continue;
+ /* Check for a MBR disk */
+ if (boot_part->type != typedos) {
+ pi_del(&boot_part);
+ continue;
+ }
+ if (boot_part->sub.dos.disk_sig == mbr_sig) {
+ goto ok;
+ }
+ }
+ drive = -1;
+ok:
+ *_boot_part = boot_part;
+ return drive;
+}
+
+/*
+ * Search for a specific drive/partition, based on the GPT GUID.
+ * Return drive and iterator at proper position.
+ */
+static int find_by_guid(const struct guid *gpt_guid,
+ struct part_iter **_boot_part)
+{
+ struct part_iter *boot_part = NULL;
+ struct disk_info diskinfo;
+ int drive;
+
+ for (drive = 0x80; drive < 0x80 + fixed_cnt; drive++) {
+ if (disk_get_params(drive, &diskinfo))
+ continue; /* Drive doesn't exist */
+ if (!(boot_part = pi_begin(&diskinfo, 0)))
+ continue;
+ /* Check for a GPT disk */
+ if (boot_part->type != typegpt) {
+ pi_del(&boot_part);
+ continue;
+ }
+ /* Check for a matching GPT disk guid */
+ if (!memcmp(&boot_part->sub.gpt.disk_guid, gpt_guid, sizeof(*gpt_guid))) {
+ goto ok;
+ }
+ /* disk guid doesn't match, maybe partition guid will */
+ while (!pi_next(&boot_part)) {
+ if (!memcmp(&boot_part->sub.gpt.part_guid, gpt_guid, sizeof(*gpt_guid)))
+ goto ok;
+ }
+ }
+ drive = -1;
+ok:
+ *_boot_part = boot_part;
+ return drive;
+}
+
+/*
+ * Search for a specific drive/partition, based on the GPT label.
+ * Return drive and iterator at proper position.
+ */
+static int find_by_label(const char *label, struct part_iter **_boot_part)
+{
+ struct part_iter *boot_part = NULL;
+ struct disk_info diskinfo;
+ int drive;
+
+ for (drive = 0x80; drive < 0x80 + fixed_cnt; drive++) {
+ if (disk_get_params(drive, &diskinfo))
+ continue; /* Drive doesn't exist */
+ if (!(boot_part = pi_begin(&diskinfo, 0)))
+ continue;
+ /* Check for a GPT disk */
+ if (!(boot_part->type == typegpt)) {
+ pi_del(&boot_part);
+ continue;
+ }
+ /* Check for a matching partition */
+ while (!pi_next(&boot_part)) {
+ if (!strcmp(label, boot_part->sub.gpt.part_label))
+ goto ok;
+ }
+ }
+ drive = -1;
+ok:
+ *_boot_part = boot_part;
+ return drive;
+}
+
+static void do_boot(struct data_area *data, int ndata)
+{
+ uint16_t *const bios_fbm = (uint16_t *) 0x413;
+ addr_t dosmem = (addr_t)(*bios_fbm << 10); /* Technically a low bound */
+ struct syslinux_memmap *mmap;
+ struct syslinux_movelist *mlist = NULL;
+ addr_t endimage;
+ uint8_t driveno = opt.regs.edx.b[0];
+ uint8_t swapdrive = driveno & 0x80;
+ 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;
+ }
+
+ if (opt.swap && driveno != swapdrive) {
+ static const uint8_t swapstub_master[] = {
+ /* The actual swap code */
+ 0x53, /* 00: push bx */
+ 0x0f, 0xb6, 0xda, /* 01: movzx bx,dl */
+ 0x2e, 0x8a, 0x57, 0x60, /* 04: mov dl,[cs:bx+0x60] */
+ 0x5b, /* 08: pop bx */
+ 0xea, 0, 0, 0, 0, /* 09: jmp far 0:0 */
+ 0x90, 0x90, /* 0E: nop; nop */
+ /* Code to install this in the right location */
+ /* Entry with DS = CS; ES = SI = 0; CX = 256 */
+ 0x26, 0x66, 0x8b, 0x7c, 0x4c, /* 10: mov edi,[es:si+4*0x13] */
+ 0x66, 0x89, 0x3e, 0x0a, 0x00, /* 15: mov [0x0A],edi */
+ 0x26, 0x8b, 0x3e, 0x13, 0x04, /* 1A: mov di,[es:0x413] */
+ 0x4f, /* 1F: dec di */
+ 0x26, 0x89, 0x3e, 0x13, 0x04, /* 20: mov [es:0x413],di */
+ 0x66, 0xc1, 0xe7, 0x16, /* 25: shl edi,16+6 */
+ 0x26, 0x66, 0x89, 0x7c, 0x4c, /* 29: mov [es:si+4*0x13],edi */
+ 0x66, 0xc1, 0xef, 0x10, /* 2E: shr edi,16 */
+ 0x8e, 0xc7, /* 32: mov es,di */
+ 0x31, 0xff, /* 34: xor di,di */
+ 0xf3, 0x66, 0xa5, /* 36: rep movsd */
+ 0xbe, 0, 0, /* 39: mov si,0 */
+ 0xbf, 0, 0, /* 3C: mov di,0 */
+ 0x8e, 0xde, /* 3F: mov ds,si */
+ 0x8e, 0xc7, /* 41: mov es,di */
+ 0x66, 0xb9, 0, 0, 0, 0, /* 43: mov ecx,0 */
+ 0x66, 0xbe, 0, 0, 0, 0, /* 49: mov esi,0 */
+ 0x66, 0xbf, 0, 0, 0, 0, /* 4F: mov edi,0 */
+ 0xea, 0, 0, 0, 0, /* 55: jmp 0:0 */
+ /* pad out to segment boundary */
+ 0x90, 0x90, /* 5A: ... */
+ 0x90, 0x90, 0x90, 0x90, /* 5C: ... */
+ };
+ static uint8_t swapstub[1024];
+ uint8_t *p;
+
+ /* Note: we can't rely on either INT 13h nor the dosmem
+ vector to be correct at this stage, so we have to use an
+ installer stub to put things in the right place.
+ Round the installer location to a 1K boundary so the only
+ possible overlap is the identity mapping. */
+ endimage = (endimage + 1023u) & ~1023u;
+
+ /* Create swap stub */
+ memcpy(swapstub, swapstub_master, sizeof swapstub_master);
+ *(uint16_t *) & swapstub[0x3a] = opt.regs.ds;
+ *(uint16_t *) & swapstub[0x3d] = opt.regs.es;
+ *(uint32_t *) & swapstub[0x45] = opt.regs.ecx.l;
+ *(uint32_t *) & swapstub[0x4b] = opt.regs.esi.l;
+ *(uint32_t *) & swapstub[0x51] = opt.regs.edi.l;
+ *(uint16_t *) & swapstub[0x56] = opt.regs.ip;
+ *(uint16_t *) & swapstub[0x58] = opt.regs.cs;
+ p = &swapstub[sizeof swapstub_master];
+
+ /* Mapping table; start out with identity mapping everything */
+ for (i = 0; i < 256; i++)
+ p[i] = (uint8_t)i;
+
+ /* And the actual swap */
+ p[driveno] = swapdrive;
+ p[swapdrive] = driveno;
+
+ /* Adjust registers */
+ opt.regs.ds = opt.regs.cs = (uint16_t)(endimage >> 4);
+ opt.regs.esi.l = opt.regs.es = 0;
+ opt.regs.ecx.l = sizeof swapstub >> 2;
+ opt.regs.ip = 0x10; /* Installer offset */
+ opt.regs.ebx.b[0] = opt.regs.edx.b[0] = swapdrive;
+
+ if (syslinux_add_movelist(&mlist, endimage, (addr_t) swapstub,
+ sizeof swapstub))
+ goto enomem;
+
+ endimage += sizeof swapstub;
+ }
+
+ /* 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, opt.keeppxe, &opt.regs);
+ error("Chainboot failed!\n");
+ return;
+
+too_big:
+ error("Loader file too large\n");
+ return;
+
+enomem:
+ error("Out of memory\n");
+ return;
+}
+
+int find_dp(struct part_iter **_iter)
+{
+ struct part_iter *iter = NULL;
+ struct disk_info diskinfo;
+ struct guid gpt_guid;
+ uint64_t fs_lba;
+ int drive, hd, partition;
+ const union syslinux_derivative_info *sdi;
+
+ sdi = syslinux_derivative_info();
+
+ if (!strncmp(opt.drivename, "mbr", 3)) {
+ if (find_by_sig(strtoul(opt.drivename + 4, NULL, 0), &iter) < 0) {
+ error("Unable to find requested MBR signature.\n");
+ goto bail;
+ }
+ } else if (!strncmp(opt.drivename, "guid", 4)) {
+ if (str_to_guid(opt.drivename + 5, &gpt_guid))
+ goto bail;
+ if (find_by_guid(&gpt_guid, &iter) < 0) {
+ error("Unable to find requested GPT disk or partition by guid.\n");
+ goto bail;
+ }
+ } else if (!strncmp(opt.drivename, "label", 5)) {
+ if (!opt.drivename[6]) {
+ error("No label specified.\n");
+ goto bail;
+ }
+ if (find_by_label(opt.drivename + 6, &iter) < 0) {
+ error("Unable to find requested GPT partition by label.\n");
+ goto bail;
+ }
+ } else if ((opt.drivename[0] == 'h' || opt.drivename[0] == 'f') &&
+ opt.drivename[1] == 'd') {
+ hd = opt.drivename[0] == 'h' ? 0x80 : 0;
+ opt.drivename += 2;
+ drive = hd | strtol(opt.drivename, NULL, 0);
+
+ if (disk_get_params(drive, &diskinfo))
+ goto bail;
+ /* this will start iteration over FDD, possibly raw */
+ if (!(iter = pi_begin(&diskinfo, 0)))
+ goto bail;
+
+ } else if (!strcmp(opt.drivename, "boot") || !strcmp(opt.drivename, "fs")) {
+ if (!is_phys(sdi->c.filesystem)) {
+ error("When syslinux is not booted from physical disk (or its emulation),\n"
+ "'boot' and 'fs' are meaningless.\n");
+ goto bail;
+ }
+ /* offsets match, but in case it changes in the future */
+ if (sdi->c.filesystem == SYSLINUX_FS_ISOLINUX) {
+ drive = sdi->iso.drive_number;
+ fs_lba = *sdi->iso.partoffset;
+ } else {
+ drive = sdi->disk.drive_number;
+ fs_lba = *sdi->disk.partoffset;
+ }
+ if (disk_get_params(drive, &diskinfo))
+ goto bail;
+ /* this will start iteration over disk emulation, possibly raw */
+ if (!(iter = pi_begin(&diskinfo, 0)))
+ goto bail;
+
+ /* 'fs' => we should lookup the syslinux partition number and use it */
+ if (!strcmp(opt.drivename, "fs")) {
+ while (!pi_next(&iter)) {
+ if (iter->start_lba == fs_lba)
+ break;
+ }
+ /* broken part structure or other problems */
+ if (iter->status) {
+ error("Can't find myself on the drive I booted from.\n");
+ goto bail;
+ }
+ }
+ } else {
+ error("Unparsable drive specification.\n");
+ goto bail;
+ }
+ /* main options done - only thing left is explicit partition specification,
+ * if we're still at the disk stage with the iterator AND user supplied
+ * partition number (including disk pseudo-partition).
+ */
+ if (!iter->index && opt.partition) {
+ partition = strtol(opt.partition, NULL, 0);
+ /* search for matching part#, including disk */
+ do {
+ if (iter->index == partition)
+ break;
+ } while (!pi_next(&iter));
+ if (iter->status) {
+ error("Requested disk / partition combination not found.\n");
+ goto bail;
+ }
+ }
+
+ if (!(iter->di.disk & 0x80) && iter->index) {
+ error("WARNING: Partitions on floppy devices may not work.\n");
+ }
+
+ *_iter = iter;
+
+ return 0;
+
+bail:
+ pi_del(&iter);
+ return -1;
+}
+
+static int setup_handover(const struct part_iter *iter,
+ struct data_area *data)
+{
+ struct disk_dos_part_entry *ha;
+ uint32_t synth_size;
+ uint32_t *plen;
+
+ if (!iter->index) { /* implies typeraw or non-iterated */
+ uint32_t len;
+ /* RAW handover protocol */
+ synth_size = sizeof(struct disk_dos_part_entry);
+ ha = malloc(synth_size);
+ if (!ha) {
+ error("Could not build RAW hand-over record!\n");
+ goto bail;
+ }
+ len = ~0u;
+ if (iter->length < len)
+ len = (uint32_t)iter->length;
+ lba2chs(&ha->start, &iter->di, 0, l2c_cadd);
+ lba2chs(&ha->end, &iter->di, len - 1, l2c_cadd);
+ ha->active_flag = 0x80;
+ ha->ostype = 0xDA; /* "Non-FS Data", anything is good here though ... */
+ ha->start_lba = 0;
+ ha->length = len;
+ } else if (iter->type == typegpt) {
+ /* GPT handover protocol */
+ synth_size = sizeof(struct disk_dos_part_entry) +
+ sizeof(uint32_t) + (uint32_t)iter->sub.gpt.pe_size;
+ ha = malloc(synth_size);
+ if (!ha) {
+ error("Could not build GPT hand-over record!\n");
+ goto bail;
+ }
+ lba2chs(&ha->start, &iter->di, iter->start_lba, l2c_cadd);
+ lba2chs(&ha->end, &iter->di, iter->start_lba + iter->length - 1, l2c_cadd);
+ ha->active_flag = 0x80;
+ ha->ostype = 0xED;
+ /* All bits set by default */
+ ha->start_lba = ~0u;
+ ha->length = ~0u;
+ /* If these fit the precision, pass them on */
+ if (iter->start_lba < ha->start_lba)
+ ha->start_lba = (uint32_t)iter->start_lba;
+ if (iter->length < ha->length)
+ ha->length = (uint32_t)iter->length;
+ /* Next comes the GPT partition record length */
+ plen = (uint32_t *) (ha + 1);
+ plen[0] = (uint32_t)iter->sub.gpt.pe_size;
+ /* Next comes the GPT partition record copy */
+ memcpy(plen + 1, iter->record, plen[0]);
+#ifdef DEBUG
+ dprintf("GPT handover:\n");
+ disk_dos_part_dump(ha);
+ disk_gpt_part_dump((struct disk_gpt_part_entry *)(plen + 1));
+#endif
+ } else if (iter->type == typedos) {
+ /* MBR handover protocol */
+ synth_size = sizeof(struct disk_dos_part_entry);
+ ha = malloc(synth_size);
+ if (!ha) {
+ error("Could not build MBR hand-over record!\n");
+ goto bail;
+ }
+ memcpy(ha, iter->record, synth_size);
+ /* make sure these match bios imaginations and are ebr agnostic */
+ lba2chs(&ha->start, &iter->di, iter->start_lba, l2c_cadd);
+ lba2chs(&ha->end, &iter->di, iter->start_lba + iter->length - 1, l2c_cadd);
+ ha->start_lba = (uint32_t)iter->start_lba;
+ ha->length = (uint32_t)iter->length;
+
+#ifdef DEBUG
+ dprintf("MBR handover:\n");
+ disk_dos_part_dump(ha);
+#endif
+ } else {
+ /* shouldn't ever happen */
+ goto bail;
+ }
+
+ data->base = 0x7be;
+ data->size = synth_size;
+ data->data = (void *)ha;
+
+ return 0;
+bail:
+ return -1;
+}
+
+int main(int argc, char *argv[])
+{
+ struct part_iter *iter = NULL;
+ void *sbck = NULL;
+ struct data_area fdat, hdat, sdat, data[3];
+ int ndata = 0;
+
+ console_ansi_raw();
+
+ memset(&fdat, 0, sizeof(fdat));
+ memset(&hdat, 0, sizeof(hdat));
+ memset(&sdat, 0, sizeof(sdat));
+
+ opt_set_defs();
+ if (opt_parse_args(argc, argv))
+ goto bail;
+
+#if 0
+ /* Get max fixed disk number */
+ fixed_cnt = *(uint8_t *)(0x475);
+
+ /*
+ * hmm, looks like we can't do that -
+ * some bioses/vms just set it to 1
+ * and go on living happily
+ * any better options than hardcoded 0x80 - 0xFF ?
+ */
+#endif
+
+ /* Get disk/part iterator matching user supplied options */
+ if (find_dp(&iter))
+ goto bail;
+
+ /* Perform initial partition entry mangling */
+ if (manglepe_fixchs(iter))
+ goto bail;
+ if (manglepe_hide(iter))
+ goto bail;
+
+ /* Load the boot file */
+ if (opt.file) {
+ fdat.base = (opt.fseg << 4) + opt.foff;
+
+ if (loadfile(opt.file, &fdat.data, &fdat.size)) {
+ error("Couldn't read the boot file.\n");
+ goto bail;
+ }
+ if (fdat.base + fdat.size - 1 > ADDRMAX) {
+ error("The boot file is too big to load at this address.\n");
+ goto bail;
+ }
+ }
+
+ /* Load the sector */
+ if (opt.sect) {
+ sdat.base = (opt.sseg << 4) + opt.soff;
+ sdat.size = iter->di.bps;
+
+ if (sdat.base + sdat.size - 1 > ADDRMAX) {
+ error("The sector cannot be loaded at such high address.\n");
+ goto bail;
+ }
+ if (!(sdat.data = disk_read_sectors(&iter->di, iter->start_lba, 1))) {
+ error("Couldn't read the sector.\n");
+ goto bail;
+ }
+ if (opt.save) {
+ if (!(sbck = malloc(sdat.size))) {
+ error("Couldn't allocate cmp-buf for option 'save'.\n");
+ goto bail;
+ }
+ memcpy(sbck, sdat.data, sdat.size);
+ }
+ if (opt.file && opt.maps && overlap(&fdat, &sdat)) {
+ error("WARNING: The sector won't be mmapped, as it would conflict with the boot file.\n");
+ opt.maps = false;
+ }
+ }
+
+ /* Prep the handover */
+ if (opt.hand) {
+ if (setup_handover(iter, &hdat))
+ goto bail;
+ /* Verify possible conflicts */
+ if ( ( opt.file && overlap(&fdat, &hdat)) ||
+ ( opt.maps && overlap(&sdat, &hdat)) ) {
+ error("WARNING: Handover area won't be prepared,\n"
+ "as it would conflict with the boot file and/or the sector.\n");
+ opt.hand = false;
+ }
+ }
+
+ /* Adjust registers */
+
+ mangler_init(iter);
+ mangler_handover(iter, &hdat);
+ mangler_grldr(iter);
+
+ /* Patching functions */
+
+ if (manglef_isolinux(&fdat))
+ goto bail;
+
+ if (manglef_grub(iter, &fdat))
+ goto bail;
+#if 0
+ if (manglef_drmk(&fdat))
+ goto bail;
+#endif
+ if (manglef_bpb(iter, &fdat))
+ goto bail;
+
+ if (mangles_bpb(iter, &sdat))
+ goto bail;
+
+ if (mangles_save(iter, &sdat, sbck))
+ goto bail;
+
+ if (manglesf_bss(&sdat, &fdat))
+ goto bail;
+
+ /* This *must* be after BPB saving or copying */
+ if (mangles_cmldr(&sdat))
+ goto bail;
+
+ /*
+ * Prepare boot-time mmap data. We should to it here, as manglers could
+ * potentially alter some of the data.
+ */
+
+ if (opt.file)
+ memcpy(data + ndata++, &fdat, sizeof(fdat));
+ if (opt.maps)
+ memcpy(data + ndata++, &sdat, sizeof(sdat));
+ if (opt.hand)
+ memcpy(data + ndata++, &hdat, sizeof(hdat));
+
+#ifdef DEBUG
+ printf("iter->di dsk, bps: %X, %u\niter->di lbacnt, C*H*S: %"PRIu64", %u\n"
+ "iter->di C, H, S: %u, %u, %u\n",
+ iter->di.disk, iter->di.bps,
+ iter->di.lbacnt, iter->di.cyl * iter->di.head * iter->di.spt,
+ iter->di.cyl, iter->di.head, iter->di.spt);
+ printf("iter idx: %d\n", iter->index);
+ printf("iter lba: %"PRIu64"\n", iter->start_lba);
+ if (opt.hand)
+ printf("hand lba: %u\n",
+ ((struct disk_dos_part_entry *)hdat.data)->start_lba);
+#endif
+
+ if (opt.warn) {
+ puts("Press any key to continue booting...");
+ wait_key();
+ }
+
+ if (ndata && !opt.brkchain) /* boot only if we actually chainload */
+ do_boot(data, ndata);
+ else
+ error("Service-only run completed, exiting.\n");
+bail:
+ pi_del(&iter);
+ /* Free allocated areas */
+ free(fdat.data);
+ free(sdat.data);
+ free(hdat.data);
+ free(sbck);
+ return 255;
+}
+
+/* vim: set ts=8 sts=4 sw=4 noet: */
diff --git a/com32/chain/chain.h b/com32/chain/chain.h
new file mode 100644
index 00000000..fc481bc6
--- /dev/null
+++ b/com32/chain/chain.h
@@ -0,0 +1,14 @@
+#ifndef _COM32_CHAIN_CHAIN_H
+#define _COM32_CHAIN_CHAIN_H
+
+#include <syslinux/movebits.h>
+
+struct data_area {
+ void *data;
+ addr_t base;
+ addr_t size;
+};
+
+#endif
+
+/* vim: set ts=8 sts=4 sw=4 noet: */
diff --git a/com32/chain/common.h b/com32/chain/common.h
new file mode 100644
index 00000000..b170a732
--- /dev/null
+++ b/com32/chain/common.h
@@ -0,0 +1,9 @@
+#ifndef _COM32_CHAIN_COMMON_H
+#define _COM32_CHAIN_COMMON_H
+
+#define ADDRMAX 0x9EFFFu
+#define ADDRMIN 0x500u
+
+#endif
+
+/* vim: set ts=8 sts=4 sw=4 noet: */
diff --git a/com32/chain/mangle.c b/com32/chain/mangle.c
new file mode 100644
index 00000000..8358106e
--- /dev/null
+++ b/com32/chain/mangle.c
@@ -0,0 +1,618 @@
+#include <com32.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <dprintf.h>
+#include <syslinux/config.h>
+#include "common.h"
+#include "chain.h"
+#include "options.h"
+#include "utility.h"
+#include "partiter.h"
+#include "mangle.h"
+
+static const char cmldr_signature[8] = "cmdcons";
+
+/* Create boot info table: needed when you want to chainload
+ * another version of ISOLINUX (or another bootlaoder that needs
+ * the -boot-info-table switch of mkisofs)
+ * (will only work when run from ISOLINUX)
+ */
+int manglef_isolinux(struct data_area *data)
+{
+ const union syslinux_derivative_info *sdi;
+ unsigned char *isolinux_bin;
+ uint32_t *checksum, *chkhead, *chktail;
+ uint32_t file_lba = 0;
+
+ if (!(opt.file && opt.isolinux))
+ return 0;
+
+ sdi = syslinux_derivative_info();
+
+ if (sdi->c.filesystem != SYSLINUX_FS_ISOLINUX) {
+ error ("The isolinux= option is only valid when run from ISOLINUX.\n");
+ goto bail;
+ }
+
+ /* Boot info table info (integers in little endian format)
+
+ Offset Name Size Meaning
+ 8 bi_pvd 4 bytes LBA of primary volume descriptor
+ 12 bi_file 4 bytes LBA of boot file
+ 16 bi_length 4 bytes Boot file length in bytes
+ 20 bi_csum 4 bytes 32-bit checksum
+ 24 bi_reserved 40 bytes Reserved
+
+ The 32-bit checksum is the sum of all the 32-bit words in the
+ boot file starting at byte offset 64. All linear block
+ addresses (LBAs) are given in CD sectors (normally 2048 bytes).
+
+ LBA of primary volume descriptor should already be set to 16.
+ */
+
+ isolinux_bin = (unsigned char *)data->data;
+
+ /* Get LBA address of bootfile */
+ file_lba = get_file_lba(opt.file);
+
+ if (file_lba == 0) {
+ error("Failed to find LBA offset of the boot file\n");
+ goto bail;
+ }
+ /* Set it */
+ *((uint32_t *) & isolinux_bin[12]) = file_lba;
+
+ /* Set boot file length */
+ *((uint32_t *) & isolinux_bin[16]) = data->size;
+
+ /* Calculate checksum */
+ checksum = (uint32_t *) & isolinux_bin[20];
+ chkhead = (uint32_t *) & isolinux_bin[64];
+ chktail = (uint32_t *) & isolinux_bin[data->size & ~3u];
+ *checksum = 0;
+ while (chkhead < chktail)
+ *checksum += *chkhead++;
+
+ /*
+ * Deal with possible fractional dword at the end;
+ * this *should* never happen...
+ */
+ if (data->size & 3) {
+ uint32_t xword = 0;
+ memcpy(&xword, chkhead, data->size & 3);
+ *checksum += xword;
+ }
+ return 0;
+bail:
+ return -1;
+}
+
+/*
+ * Legacy grub's stage2 chainloading
+ */
+int manglef_grub(const struct part_iter *iter, struct data_area *data)
+{
+ /* Layout of stage2 file (from byte 0x0 to 0x270) */
+ struct grub_stage2_patch_area {
+ /* 0x0 to 0x205 */
+ char unknown[0x206];
+ /* 0x206: compatibility version number major */
+ uint8_t compat_version_major;
+ /* 0x207: compatibility version number minor */
+ uint8_t compat_version_minor;
+
+ /* 0x208: install_partition variable */
+ struct {
+ /* 0x208: sub-partition in sub-partition part2 */
+ uint8_t part3;
+ /* 0x209: sub-partition in top-level partition */
+ uint8_t part2;
+ /* 0x20a: top-level partiton number */
+ uint8_t part1;
+ /* 0x20b: BIOS drive number (must be 0) */
+ uint8_t drive;
+ } __attribute__ ((packed)) install_partition;
+
+ /* 0x20c: deprecated (historical reason only) */
+ uint32_t saved_entryno;
+ /* 0x210: stage2_ID: will always be STAGE2_ID_STAGE2 = 0 in stage2 */
+ uint8_t stage2_id;
+ /* 0x211: force LBA */
+ uint8_t force_lba;
+ /* 0x212: version string (will probably be 0.97) */
+ char version_string[5];
+ /* 0x217: config filename */
+ char config_file[89];
+ /* 0x270: start of code (after jump from 0x200) */
+ char codestart[1];
+ } __attribute__ ((packed)) *stage2;
+
+ if (!(opt.file && opt.grub))
+ return 0;
+
+ if (data->size < sizeof(struct grub_stage2_patch_area)) {
+ error("The file specified by grub=<loader> is too small to be stage2 of GRUB Legacy.\n");
+ goto bail;
+ }
+ stage2 = data->data;
+
+ /*
+ * Check the compatibility version number to see if we loaded a real
+ * stage2 file or a stage2 file that we support.
+ */
+ if (stage2->compat_version_major != 3
+ || stage2->compat_version_minor != 2) {
+ error("The file specified by grub=<loader> is not a supported stage2 GRUB Legacy binary.\n");
+ goto bail;
+ }
+
+ /*
+ * GRUB Legacy wants the partition number in the install_partition
+ * variable, located at offset 0x208 of stage2.
+ * When GRUB Legacy is loaded, it is located at memory address 0x8208.
+ *
+ * It looks very similar to the "boot information format" of the
+ * Multiboot specification:
+ * http://www.gnu.org/software/grub/manual/multiboot/multiboot.html#Boot-information-format
+ *
+ * 0x208 = part3: sub-partition in sub-partition part2
+ * 0x209 = part2: sub-partition in top-level partition
+ * 0x20a = part1: top-level partition number
+ * 0x20b = drive: BIOS drive number (must be 0)
+ *
+ * GRUB Legacy doesn't store the BIOS drive number at 0x20b, but at
+ * another location.
+ *
+ * Partition numbers always start from zero.
+ * Unused partition bytes must be set to 0xFF.
+ *
+ * We only care about top-level partition, so we only need to change
+ * "part1" to the appropriate value:
+ * -1: whole drive (default) (-1 = 0xFF)
+ * 0-3: primary partitions
+ * 4-*: logical partitions
+ */
+ stage2->install_partition.part1 = (uint8_t)(iter->index - 1);
+
+ /*
+ * Grub Legacy reserves 89 bytes (from 0x8217 to 0x826f) for the
+ * config filename. The filename passed via grubcfg= will overwrite
+ * the default config filename "/boot/grub/menu.lst".
+ */
+ if (opt.grubcfg) {
+ if (strlen(opt.grubcfg) > sizeof(stage2->config_file) - 1) {
+ error ("The config filename length can't exceed 88 characters.\n");
+ goto bail;
+ }
+
+ strcpy((char *)stage2->config_file, opt.grubcfg);
+ }
+
+ return 0;
+bail:
+ return -1;
+}
+#if 0
+/*
+ * Dell's DRMK chainloading.
+ */
+int manglef_drmk(struct data_area *data)
+{
+ /*
+ * DRMK entry is different than MS-DOS/PC-DOS
+ * A new size, aligned to 16 bytes to ease use of ds:[bp+28].
+ * We only really need 4 new, usable bytes at the end.
+ */
+
+ if (!(opt.file && opt.drmk))
+ return 0;
+
+ uint32_t tsize = (data->size + 19) & 0xfffffff0;
+ const union syslinux_derivative_info *sdi;
+ uint64_t fs_lba;
+
+ sdi = syslinux_derivative_info();
+ /* We should lookup the Syslinux partition offset and use it */
+ fs_lba = *sdi->disk.partoffset;
+
+ /*
+ * fs_lba should be verified against the disk as some DRMK
+ * variants will check and fail if it does not match
+ */
+ dprintf(" fs_lba offset is %d\n", fs_lba);
+ /* DRMK only uses a DWORD */
+ if (fs_lba > 0xffffffff) {
+ error("LBA very large; Only using lower 32 bits; DRMK will probably fail\n");
+ }
+ opt.regs.ss = opt.regs.fs = opt.regs.gs = 0; /* Used before initialized */
+ if (!realloc(data->data, tsize)) {
+ error("Failed to realloc for DRMK.\n");
+ goto bail;
+ }
+ data->size = tsize;
+ /* ds:bp is assumed by DRMK to be the boot sector */
+ /* offset 28 is the FAT HiddenSectors value */
+ opt.regs.ds = (uint16_t)((tsize >> 4) + (opt.fseg - 2));
+ /* "Patch" into tail of the new space */
+ *(uint32_t *)((char*)data->data + tsize - 4) = (uint32_t)fs_lba;
+
+ return 0;
+bail:
+ return -1;
+}
+#endif
+/* Adjust BPB common function */
+static int mangle_bpb(const struct part_iter *iter, struct data_area *data, const char *tag)
+{
+ unsigned int off;
+ int type = bpb_detect(data->data, tag);
+
+ /* BPB: hidden sectors 32bit*/
+ if (type >= bpbV34) {
+ if (iter->start_lba < ~0u)
+ *(uint32_t *) ((char *)data->data + 0x1c) = (uint32_t)iter->start_lba;
+ else
+ /* won't really help much, but ... */
+ *(uint32_t *) ((char *)data->data + 0x1c) = ~0u;
+ }
+ /* BPB: hidden sectors 16bit*/
+ if (bpbV30 <= type && type <= bpbV32) {
+ if (iter->start_lba < 0xFFFF)
+ *(uint16_t *) ((char *)data->data + 0x1c) = (uint16_t)iter->start_lba;
+ else
+ /* won't really help much, but ... */
+ *(uint16_t *) ((char *)data->data + 0x1c) = (uint16_t)~0u;
+ }
+ /* BPB: legacy geometry */
+ if (type >= bpbV30) {
+ if (iter->di.cbios)
+ *(uint32_t *)((char *)data->data + 0x18) = (uint32_t)((iter->di.head << 16) | iter->di.spt);
+ else {
+ if (iter->di.disk & 0x80)
+ *(uint32_t *)((char *)data->data + 0x18) = 0x00FF003F;
+ else
+ *(uint32_t *)((char *)data->data + 0x18) = 0x00020012;
+ }
+ }
+ /* BPB: drive */
+ if (drvoff_detect(type, &off)) {
+ *(uint8_t *)((char *)data->data + off) = (uint8_t)
+ (opt.swap ? iter->di.disk & 0x80 : iter->di.disk);
+ }
+
+ return 0;
+}
+
+/*
+ * Adjust BPB of a BPB-compatible file
+ */
+int manglef_bpb(const struct part_iter *iter, struct data_area *data)
+{
+ if (!(opt.file && opt.filebpb))
+ return 0;
+
+ return mangle_bpb(iter, data, "file");
+}
+
+/*
+ * Adjust BPB of a sector
+ */
+int mangles_bpb(const struct part_iter *iter, struct data_area *data)
+{
+ if (!(opt.sect && opt.setbpb))
+ return 0;
+
+ return mangle_bpb(iter, data, "sect");
+}
+
+/*
+ * This function performs full BPB patching, analogously to syslinux's
+ * native BSS.
+ */
+int manglesf_bss(struct data_area *sec, struct data_area *fil)
+{
+ int type1, type2;
+ unsigned int cnt = 0;
+
+ if (!(opt.sect && opt.file && opt.bss))
+ return 0;
+
+ type1 = bpb_detect(fil->data, "bss/file");
+ type2 = bpb_detect(sec->data, "bss/sect");
+
+ if (!type1 || !type2) {
+ error("Couldn't determine the BPB type for option 'bss'.\n");
+ goto bail;
+ }
+ if (type1 != type2) {
+ error("Option 'bss' can't be used,\n"
+ "when a sector and a file have incompatible BPBs.\n");
+ goto bail;
+ }
+
+ /* Copy common 2.0 data */
+ memcpy((char *)fil->data + 0x0B, (char *)sec->data + 0x0B, 0x0D);
+
+ /* Copy 3.0+ data */
+ if (type1 <= bpbV30) {
+ cnt = 0x06;
+ } else if (type1 <= bpbV32) {
+ cnt = 0x08;
+ } else if (type1 <= bpbV34) {
+ cnt = 0x0C;
+ } else if (type1 <= bpbV40) {
+ cnt = 0x2E;
+ } else if (type1 <= bpbVNT) {
+ cnt = 0x3C;
+ } else if (type1 <= bpbV70) {
+ cnt = 0x42;
+ }
+ memcpy((char *)fil->data + 0x18, (char *)sec->data + 0x18, cnt);
+
+ return 0;
+bail:
+ return -1;
+}
+
+/*
+ * Save sector.
+ */
+int mangles_save(const struct part_iter *iter, const struct data_area *data, void *org)
+{
+ if (!(opt.sect && opt.save))
+ return 0;
+
+ if (memcmp(org, data->data, data->size)) {
+ if (disk_write_sectors(&iter->di, iter->start_lba, data->data, 1)) {
+ error("Cannot write the updated sector.\n");
+ goto bail;
+ }
+ /* function can be called again */
+ memcpy(org, data->data, data->size);
+ }
+
+ return 0;
+bail:
+ return -1;
+}
+
+/*
+ * To boot the Recovery Console of Windows NT/2K/XP we need to write
+ * the string "cmdcons\0" to memory location 0000:7C03.
+ * Memory location 0000:7C00 contains the bootsector of the partition.
+ */
+int mangles_cmldr(struct data_area *data)
+{
+ if (!(opt.sect && opt.cmldr))
+ return 0;
+
+ memcpy((char *)data->data + 3, cmldr_signature, sizeof(cmldr_signature));
+ return 0;
+}
+
+/* Set common registers */
+int mangler_init(const struct part_iter *iter)
+{
+ /* Set initial registry values */
+ if (opt.file) {
+ opt.regs.cs = opt.regs.ds = opt.regs.ss = (uint16_t)opt.fseg;
+ opt.regs.ip = (uint16_t)opt.fip;
+ } else {
+ opt.regs.cs = opt.regs.ds = opt.regs.ss = (uint16_t)opt.sseg;
+ opt.regs.ip = (uint16_t)opt.sip;
+ }
+
+ if (opt.regs.ip == 0x7C00 && !opt.regs.cs)
+ opt.regs.esp.l = 0x7C00;
+
+ /* DOS kernels want the drive number in BL instead of DL. Indulge them. */
+ opt.regs.ebx.b[0] = opt.regs.edx.b[0] = (uint8_t)iter->di.disk;
+
+ return 0;
+}
+
+/* ds:si & ds:bp */
+int mangler_handover(const struct part_iter *iter, const struct data_area *data)
+{
+ if (opt.file && opt.maps && !opt.hptr) {
+ opt.regs.esi.l = opt.regs.ebp.l = opt.soff;
+ opt.regs.ds = (uint16_t)opt.sseg;
+ opt.regs.eax.l = 0;
+ } else if (opt.hand) {
+ /* base is really 0x7be */
+ opt.regs.esi.l = opt.regs.ebp.l = data->base;
+ opt.regs.ds = 0;
+ if (iter->index && iter->type == typegpt) /* must be iterated and GPT */
+ opt.regs.eax.l = 0x54504721; /* '!GPT' */
+ else
+ opt.regs.eax.l = 0;
+ }
+
+ return 0;
+}
+
+/*
+ * GRLDR of GRUB4DOS wants the partition number in DH:
+ * -1: whole drive (default)
+ * 0-3: primary partitions
+ * 4-*: logical partitions
+ */
+int mangler_grldr(const struct part_iter *iter)
+{
+ if (opt.grldr)
+ opt.regs.edx.b[1] = (uint8_t)(iter->index - 1);
+
+ return 0;
+}
+
+/*
+ * try to copy values from temporary iterator, if positions match
+ */
+static void push_embr(struct part_iter *diter, struct part_iter *siter)
+{
+ if (diter->sub.dos.cebr_lba == siter->sub.dos.cebr_lba &&
+ diter->di.disk == siter->di.disk) {
+ memcpy(diter->data, siter->data, sizeof(struct disk_dos_mbr));
+ }
+}
+
+static int mpe_sethide(struct part_iter *iter, struct part_iter *miter)
+{
+ struct disk_dos_part_entry *dp;
+ static const uint16_t mask =
+ (1 << 0x01) | (1 << 0x04) | (1 << 0x06) |
+ (1 << 0x07) | (1 << 0x0b) | (1 << 0x0c) | (1 << 0x0e);
+ uint8_t t;
+
+ dp = (struct disk_dos_part_entry *)iter->record;
+ t = dp->ostype;
+
+ if ((t <= 0x1f) && ((mask >> (t & ~0x10u)) & 1)) {
+ /* It's a hideable partition type */
+ if (miter->index == iter->index || opt.hide & 4)
+ t &= (uint8_t)(~0x10u); /* unhide */
+ else
+ t |= 0x10u; /* hide */
+ }
+ if (dp->ostype != t) {
+ dp->ostype = t;
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * miter - iterator we match against
+ * hide bits meaning:
+ * ..| - enable (1) / disable (0)
+ * .|. - all (1) / pri (0)
+ * |.. - unhide (1) / hide (0)
+ */
+int manglepe_hide(struct part_iter *miter)
+{
+ int wb = 0, werr = 0;
+ struct part_iter *iter = NULL;
+ struct disk_dos_part_entry *dp;
+ int ridx;
+
+ if (!opt.hide)
+ return 0;
+
+ if (miter->type != typedos) {
+ error("Options '*hide*' is meaningful only for legacy partition scheme.\n");
+ return -1;
+ }
+
+ if (miter->index < 1)
+ error("WARNING: It's impossible to unhide a disk.\n");
+
+ if (miter->index > 4 && !(opt.hide & 2))
+ error("WARNING: your partition is beyond mbr, so it can't be unhidden without '*hideall'.\n");
+
+ if (!(iter = pi_begin(&miter->di, 1))) /* turn stepall on */
+ return -1;
+
+ while (!pi_next(&iter) && !werr) {
+ ridx = iter->rawindex;
+ if (!(opt.hide & 2) && ridx > 4)
+ break; /* skip when we're constrained to pri only */
+
+ dp = (struct disk_dos_part_entry *)iter->record;
+ if (dp->ostype)
+ wb |= mpe_sethide(iter, miter);
+
+ if (ridx >= 4 && wb && !werr) {
+ push_embr(miter, iter);
+ werr |= disk_write_sectors(&iter->di, iter->sub.dos.cebr_lba, iter->data, 1);
+ wb = 0;
+ }
+ }
+
+ if (iter->status > PI_DONE)
+ goto bail;
+
+ /* last write */
+ if (wb && !werr) {
+ push_embr(miter, iter);
+ werr |= disk_write_sectors(&iter->di, iter->sub.dos.cebr_lba, iter->data, 1);
+ }
+ if (werr)
+ error("WARNING: failed to write E/MBR during '*hide*'\n");
+
+bail:
+ pi_del(&iter);
+ return 0;
+}
+
+static int mpe_setchs(const struct disk_info *di,
+ struct disk_dos_part_entry *dp,
+ uint32_t lba1)
+{
+ uint32_t ochs1, ochs2;
+
+ ochs1 = *(uint32_t *)dp->start;
+ ochs2 = *(uint32_t *)dp->end;
+
+ lba2chs(&dp->start, di, lba1, l2c_cadd);
+ lba2chs(&dp->end, di, lba1 + dp->length - 1, l2c_cadd);
+
+ return
+ *(uint32_t *)dp->start != ochs1 ||
+ *(uint32_t *)dp->end != ochs2;
+}
+
+/*
+ * miter - iterator we match against
+ */
+int manglepe_fixchs(struct part_iter *miter)
+{
+ int wb = 0, werr = 0;
+ struct part_iter *iter = NULL;
+ struct disk_dos_part_entry *dp;
+ int ridx;
+
+ if (!opt.fixchs)
+ return 0;
+
+ if (miter->type != typedos) {
+ error("Options 'fixchs' is meaningful only for legacy partition scheme.\n");
+ return -1;
+ }
+
+ if (!(iter = pi_begin(&miter->di, 1))) /* turn stepall on */
+ return -1;
+
+ while (!pi_next(&iter) && !werr) {
+ ridx = iter->rawindex;
+ dp = (struct disk_dos_part_entry *)iter->record;
+
+ wb |= mpe_setchs(&iter->di, dp, (uint32_t)iter->start_lba);
+ if (ridx > 4)
+ wb |= mpe_setchs(&iter->di, dp + 1, iter->sub.dos.nebr_lba);
+
+ if (ridx >= 4 && wb && !werr) {
+ push_embr(miter, iter);
+ werr |= disk_write_sectors(&iter->di, iter->sub.dos.cebr_lba, iter->data, 1);
+ wb = 0;
+ }
+ }
+
+ if (iter->status > PI_DONE)
+ goto bail;
+
+ /* last write */
+ if (wb && !werr) {
+ push_embr(miter, iter);
+ werr |= disk_write_sectors(&iter->di, iter->sub.dos.cebr_lba, iter->data, 1);
+ }
+ if (werr)
+ error("WARNING: failed to write E/MBR during 'fixchs'\n");
+
+bail:
+ pi_del(&iter);
+ return 0;
+}
+
+/* vim: set ts=8 sts=4 sw=4 noet: */
diff --git a/com32/chain/mangle.h b/com32/chain/mangle.h
new file mode 100644
index 00000000..bcefea3b
--- /dev/null
+++ b/com32/chain/mangle.h
@@ -0,0 +1,32 @@
+#ifndef _COM32_CHAIN_MANGLE_H
+#define _COM32_CHAIN_MANGLE_H
+
+#include "chain.h"
+#include "partiter.h"
+
+/* file's manglers */
+int manglef_isolinux(struct data_area *data);
+int manglef_grub(const struct part_iter *iter, struct data_area *data);
+int manglef_bpb(const struct part_iter *iter, struct data_area *data);
+/* int manglef_drmk(struct data_area *data);*/
+
+/* sector's manglers */
+int mangles_bpb(const struct part_iter *iter, struct data_area *data);
+int mangles_save(const struct part_iter *iter, const struct data_area *data, void *org);
+int mangles_cmldr(struct data_area *data);
+
+/* sector + file's manglers */
+int manglesf_bss(struct data_area *sec, struct data_area *fil);
+
+/* registers' manglers */
+int mangler_init(const struct part_iter *iter);
+int mangler_handover(const struct part_iter *iter, const struct data_area *data);
+int mangler_grldr(const struct part_iter *iter);
+
+/* partition layout's manglers */
+int manglepe_fixchs(struct part_iter *miter);
+int manglepe_hide(struct part_iter *miter);
+
+#endif
+
+/* vim: set ts=8 sts=4 sw=4 noet: */
diff --git a/com32/chain/options.c b/com32/chain/options.c
new file mode 100644
index 00000000..658a45ca
--- /dev/null
+++ b/com32/chain/options.c
@@ -0,0 +1,376 @@
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include "common.h"
+#include "chain.h"
+#include "utility.h"
+#include "options.h"
+
+struct options opt;
+
+static int soi_s2n(char *ptr, unsigned int *seg,
+ unsigned int *off,
+ unsigned int *ip,
+ unsigned int def)
+{
+ unsigned int segval = 0, offval, ipval, val;
+ char *p;
+
+ offval = def;
+ ipval = def;
+
+ segval = strtoul(ptr, &p, 0);
+ if (p[0] == ':' && p[1] && p[1] != ':')
+ offval = strtoul(p+1, &p, 0);
+ if (p[0] == ':' && p[1] && p[1] != ':')
+ ipval = strtoul(p+1, NULL, 0);
+
+ val = (segval << 4) + offval;
+
+ if (val < ADDRMIN || val > ADDRMAX) {
+ error("Invalid seg:off:* address specified..\n");
+ goto bail;
+ }
+
+ val = (segval << 4) + ipval;
+
+ if (ipval > 0xFFFE || val < ADDRMIN || val > ADDRMAX) {
+ error("Invalid seg:*:ip address specified.\n");
+ goto bail;
+ }
+
+ if (seg)
+ *seg = segval;
+ if (off)
+ *off = offval;
+ if (ip)
+ *ip = ipval;
+
+ return 0;
+bail:
+ return -1;
+}
+
+static void usage(void)
+{
+ unsigned int i;
+ static const char key[] = "Press any key...\n";
+ static const char *const usage[] = {
+"\
+Usage:\n\
+ chain.c32 [options]\n\
+ chain.c32 {fd|hd}<disk#>{,| }[<part#>] [options]\n\
+ chain.c32 mbr{:|=}<id>{,| }[<part#>] [options]\n\
+ chain.c32 guid{:|=}<guid>{,| }[<part#>] [options]\n\
+ chain.c32 label{:|=}<label> [<part#>] [options]\n\
+ chain.c32 boot{,| }[<part#>] [options]\n\
+ chain.c32 fs [options]\n\
+", "\
+\nOptions ('no' prefix specifies default value):\n\
+ sect[=<s[:o[:i]]>] Load sector at <s:o>, jump to <s:i>\n\
+ - defaults to 0:0x7C00:0x7C00\n\
+ - ommited o/i values default to 0\n\
+ maps Map loaded sector into real memory\n\
+ nosetbpb Fix BPB fields in loaded sector\n\
+ nofilebpb Apply 'setbpb' to loaded file\n\
+ nosave Write adjusted sector back to disk\n\
+ hand Prepare handover area\n\
+ nohptr Force ds:si and ds:bp to point to handover area\n\
+ noswap Swap drive numbers, if bootdisk is not fd0/hd0\n\
+ nohide Disable all hide variations (also the default)\n\
+ hide Hide primary partitions, unhide selected partition\n\
+ hideall Hide *all* partitions, unhide selected partition\n\
+ unhide Unhide primary partitions\n\
+ unhideall Unhide *all* partitions\n\
+ nofixchs Walk *all* partitions and fix E/MBRs' chs values\n\
+ nokeeppxe Keep the PXE and UNDI stacks in memory (PXELINUX)\n\
+ nowarn Wait for a keypress to continue chainloading\n\
+ - useful to see emited warnings\n\
+ nobreak Actually perform the chainloading\n\
+", "\
+\nOptions continued ...\n\
+ file=<file> Load and execute <file>\n\
+ seg=<s[:o[:i]]> Load file at <s:o>, jump to <s:i>\n\
+ - defaults to 0:0x7C00:0x7C00\n\
+ - ommited o/i values default to 0\n\
+ isolinux=<loader> Load another version of ISOLINUX\n\
+ ntldr=<loader> Load Windows NTLDR, SETUPLDR.BIN or BOOTMGR\n\
+ reactos=<loader> Load ReactOS's loader\n\
+ cmldr=<loader> Load Recovery Console of Windows NT/2K/XP/2003\n\
+ freedos=<loader> Load FreeDOS KERNEL.SYS\n\
+ msdos=<loader> Load MS-DOS 2.xx - 6.xx IO.SYS\n\
+ msdos7=<loader> Load MS-DOS 7+ IO.SYS\n\
+ pcdos=<loader> Load PC-DOS IBMBIO.COM\n\
+ drmk=<loader> Load DRMK DELLBIO.BIN\n\
+ grub=<loader> Load GRUB Legacy stage2\n\
+ grubcfg=<filename> Set alternative config filename for GRUB Legacy\n\
+ grldr=<loader> Load GRUB4DOS grldr\n\
+ bss=<filename> Emulate syslinux's BSS\n\
+ bs=<filename> Emulate syslinux's BS\n\
+\nPlease see doc/chain.txt for the detailed documentation.\n\
+"
+ };
+ for (i = 0; i < sizeof(usage)/sizeof(usage[0]); i++) {
+ if (i) {
+ error(key);
+ wait_key();
+ }
+ error(usage[i]);
+ }
+}
+
+void opt_set_defs(void)
+{
+ memset(&opt, 0, sizeof(opt));
+ opt.sect = true; /* by def. load sector */
+ opt.maps = true; /* by def. map sector */
+ opt.hand = true; /* by def. prepare handover */
+ opt.brkchain = false; /* by def. do chainload */
+ opt.foff = opt.soff = opt.fip = opt.sip = 0x7C00;
+ opt.drivename = "boot";
+#ifdef DEBUG
+ opt.warn = true;
+#endif
+}
+
+int opt_parse_args(int argc, char *argv[])
+{
+ int i;
+ unsigned int v;
+ char *p;
+
+ for (i = 1; i < argc; i++) {
+ if (!strncmp(argv[i], "file=", 5)) {
+ opt.file = argv[i] + 5;
+ } else if (!strcmp(argv[i], "nofile")) {
+ opt.file = NULL;
+ } else if (!strncmp(argv[i], "seg=", 4)) {
+ if (soi_s2n(argv[i] + 4, &opt.fseg, &opt.foff, &opt.fip, 0))
+ goto bail;
+ } else if (!strncmp(argv[i], "bss=", 4)) {
+ opt.file = argv[i] + 4;
+ opt.bss = true;
+ opt.maps = false;
+ opt.setbpb = true;
+ /* opt.save = true; */
+ } else if (!strncmp(argv[i], "bs=", 3)) {
+ opt.file = argv[i] + 3;
+ opt.sect = false;
+ opt.filebpb = true;
+ } else if (!strncmp(argv[i], "isolinux=", 9)) {
+ opt.file = argv[i] + 9;
+ opt.isolinux = true;
+ opt.hand = false;
+ opt.sect = false;
+ } else if (!strncmp(argv[i], "ntldr=", 6)) {
+ opt.fseg = 0x2000; /* NTLDR wants this address */
+ opt.foff = 0;
+ opt.fip = 0;
+ opt.file = argv[i] + 6;
+ opt.setbpb = true;
+ /* opt.save = true; */
+ opt.hand = false;
+ } else if (!strncmp(argv[i], "reactos=", 8)) {
+ /*
+ * settings based on commit
+ * ad4cf1470977f648ee1dd45e97939589ccb0393c
+ * note, conflicts with:
+ * http://reactos.freedoors.org/Reactos%200.3.13/ReactOS-0.3.13-REL-src/boot/freeldr/notes.txt
+ */
+ opt.fseg = 0;
+ opt.foff = 0x8000;
+ opt.fip = 0x8100;
+ opt.file = argv[i] + 8;
+ opt.setbpb = true;
+ /* opt.save = true; */
+ opt.hand = false;
+ } else if (!strncmp(argv[i], "cmldr=", 6)) {
+ opt.fseg = 0x2000; /* CMLDR wants this address */
+ opt.foff = 0;
+ opt.fip = 0;
+ opt.file = argv[i] + 6;
+ opt.cmldr = true;
+ opt.setbpb = true;
+ /* opt.save = true; */
+ opt.hand = false;
+ } else if (!strncmp(argv[i], "freedos=", 8)) {
+ opt.fseg = 0x60; /* FREEDOS wants this address */
+ opt.foff = 0;
+ opt.fip = 0;
+ opt.sseg = 0x1FE0;
+ opt.file = argv[i] + 8;
+ opt.setbpb = true;
+ /* opt.save = true; */
+ opt.hand = false;
+ } else if ( (v = 6, !strncmp(argv[i], "msdos=", v) ||
+ !strncmp(argv[i], "pcdos=", v)) ||
+ (v = 7, !strncmp(argv[i], "msdos7=", v)) ) {
+ opt.fseg = 0x70; /* MS-DOS 2.00 .. 6.xx wants this address */
+ opt.foff = 0;
+ opt.fip = v == 7 ? 0x200 : 0; /* MS-DOS 7.0+ wants this ip */
+ opt.sseg = 0x8000;
+ opt.file = argv[i] + v;
+ opt.setbpb = true;
+ /* opt.save = true; */
+ opt.hand = false;
+ } else if (!strncmp(argv[i], "drmk=", 5)) {
+ opt.fseg = 0x70; /* DRMK wants this address */
+ opt.foff = 0;
+ opt.fip = 0;
+ opt.sseg = 0x2000;
+ opt.soff = 0;
+ opt.sip = 0;
+ opt.file = argv[i] + 5;
+ /* opt.drmk = true; */
+ opt.setbpb = true;
+ /* opt.save = true; */
+ opt.hand = false;
+ } else if (!strncmp(argv[i], "grub=", 5)) {
+ opt.fseg = 0x800; /* stage2 wants this address */
+ opt.foff = 0;
+ opt.fip = 0x200;
+ opt.file = argv[i] + 5;
+ opt.grub = true;
+ opt.hand = false;
+ opt.sect = false;
+ } else if (!strncmp(argv[i], "grubcfg=", 8)) {
+ opt.grubcfg = argv[i] + 8;
+ } else if (!strncmp(argv[i], "grldr=", 6)) {
+ opt.file = argv[i] + 6;
+ opt.grldr = true;
+ opt.hand = false;
+ opt.sect = false;
+ } else if (!strcmp(argv[i], "keeppxe")) {
+ opt.keeppxe = 3;
+ } else if (!strcmp(argv[i], "nokeeppxe")) {
+ opt.keeppxe = 0;
+ } else if (!strcmp(argv[i], "maps")) {
+ opt.maps = true;
+ } else if (!strcmp(argv[i], "nomaps")) {
+ opt.maps = false;
+ } else if (!strcmp(argv[i], "hand")) {
+ opt.hand = true;
+ } else if (!strcmp(argv[i], "nohand")) {
+ opt.hand = false;
+ } else if (!strcmp(argv[i], "hptr")) {
+ opt.hptr = true;
+ } else if (!strcmp(argv[i], "nohptr")) {
+ opt.hptr = false;
+ } else if (!strcmp(argv[i], "swap")) {
+ opt.swap = true;
+ } else if (!strcmp(argv[i], "noswap")) {
+ opt.swap = false;
+ } else if (!strcmp(argv[i], "nohide")) {
+ opt.hide = 0;
+ } else if (!strcmp(argv[i], "hide")) {
+ opt.hide = 1; /* 001b */
+ } else if (!strcmp(argv[i], "hideall")) {
+ opt.hide = 2; /* 010b */
+ } else if (!strcmp(argv[i], "unhide")) {
+ opt.hide = 5; /* 101b */
+ } else if (!strcmp(argv[i], "unhideall")) {
+ opt.hide = 6; /* 110b */
+ } else if (!strcmp(argv[i], "setbpb")) {
+ opt.setbpb = true;
+ } else if (!strcmp(argv[i], "nosetbpb")) {
+ opt.setbpb = false;
+ } else if (!strcmp(argv[i], "filebpb")) {
+ opt.filebpb = true;
+ } else if (!strcmp(argv[i], "nofilebpb")) {
+ opt.filebpb = false;
+ } else if (!strncmp(argv[i], "sect=", 5) ||
+ !strcmp(argv[i], "sect")) {
+ if (argv[i][4]) {
+ if (soi_s2n(argv[i] + 5, &opt.sseg, &opt.soff, &opt.sip, 0))
+ goto bail;
+ }
+ opt.sect = true;
+ } else if (!strcmp(argv[i], "nosect")) {
+ opt.sect = false;
+ opt.maps = false;
+ } else if (!strcmp(argv[i], "save")) {
+ opt.save = true;
+ } else if (!strcmp(argv[i], "nosave")) {
+ opt.save = false;
+ } else if (!strcmp(argv[i], "fixchs")) {
+ opt.fixchs = true;
+ } else if (!strcmp(argv[i], "nofixchs")) {
+ opt.fixchs = false;
+ } else if (!strcmp(argv[i], "warn")) {
+ opt.warn = true;
+ } else if (!strcmp(argv[i], "nowarn")) {
+ opt.warn = false;
+ } else if (!strcmp(argv[i], "nobreak")) {
+ opt.brkchain = false;
+ } else if (!strcmp(argv[i], "break")) {
+ opt.brkchain = true;
+ opt.file = NULL;
+ opt.maps = false;
+ opt.hand = false;
+ } else if (((argv[i][0] == 'h' || argv[i][0] == 'f')
+ && argv[i][1] == 'd')
+ || !strncmp(argv[i], "mbr:", 4)
+ || !strncmp(argv[i], "mbr=", 4)
+ || !strncmp(argv[i], "guid:", 5)
+ || !strncmp(argv[i], "guid=", 5)
+ || !strncmp(argv[i], "label:", 6)
+ || !strncmp(argv[i], "label=", 6)
+ || !strcmp(argv[i], "boot")
+ || !strncmp(argv[i], "boot,", 5)
+ || !strcmp(argv[i], "fs")) {
+ opt.drivename = argv[i];
+ if (strncmp(argv[i], "label", 5))
+ p = strchr(opt.drivename, ',');
+ else
+ p = NULL;
+ if (p) {
+ *p = '\0';
+ opt.partition = p + 1;
+ } else if (argv[i + 1] && argv[i + 1][0] >= '0'
+ && argv[i + 1][0] <= '9') {
+ opt.partition = argv[++i];
+ }
+ } else {
+ usage();
+ goto bail;
+ }
+ }
+
+ if (opt.grubcfg && !opt.grub) {
+ error("grubcfg=<filename> must be used together with grub=<loader>.\n");
+ goto bail;
+ }
+
+#if 0
+ if ((!opt.maps || !opt.sect) && !opt.file) {
+ error("You have to load something.\n");
+ goto bail;
+ }
+#endif
+
+ if (opt.filebpb && !opt.file) {
+ error("Option 'filebpb' requires a file.\n");
+ goto bail;
+ }
+
+ if (opt.save && !opt.sect) {
+ error("Option 'save' requires a sector.\n");
+ goto bail;
+ }
+
+ if (opt.setbpb && !opt.sect) {
+ error("Option 'setbpb' requires a sector.\n");
+ goto bail;
+ }
+
+ if (opt.maps && !opt.sect) {
+ error("Option 'maps' requires a sector.\n");
+ goto bail;
+ }
+
+ return 0;
+bail:
+ return -1;
+}
+
+/* vim: set ts=8 sts=4 sw=4 noet: */
diff --git a/com32/chain/options.h b/com32/chain/options.h
new file mode 100644
index 00000000..4493ef1f
--- /dev/null
+++ b/com32/chain/options.h
@@ -0,0 +1,47 @@
+#ifndef _COM32_CHAIN_OPTIONS_H
+#define _COM32_CHAIN_OPTIONS_H
+
+#include <stdint.h>
+#include <syslinux/bootrm.h>
+
+struct options {
+ unsigned int fseg;
+ unsigned int foff;
+ unsigned int fip;
+ unsigned int sseg;
+ unsigned int soff;
+ unsigned int sip;
+ const char *drivename;
+ const char *partition;
+ const char *file;
+ const char *grubcfg;
+ bool isolinux;
+ bool cmldr;
+ bool drmk;
+ bool grub;
+ bool grldr;
+ bool maps;
+ bool hand;
+ bool hptr;
+ bool swap;
+ int hide;
+ bool sect;
+ bool save;
+ bool bss;
+ bool setbpb;
+ bool filebpb;
+ bool fixchs;
+ bool warn;
+ bool brkchain;
+ uint16_t keeppxe;
+ struct syslinux_rm_regs regs;
+};
+
+extern struct options opt;
+
+void opt_set_defs(void);
+int opt_parse_args(int argc, char *argv[]);
+
+#endif
+
+/* vim: set ts=8 sts=4 sw=4 noet: */
diff --git a/com32/chain/partiter.c b/com32/chain/partiter.c
new file mode 100644
index 00000000..1acd1958
--- /dev/null
+++ b/com32/chain/partiter.c
@@ -0,0 +1,805 @@
+/* ----------------------------------------------------------------------- *
+ *
+ * Copyright 2003-2010 H. Peter Anvin - All Rights Reserved
+ * Copyright 2010 Shao Miller
+ * Copyright 2010 Michal Soltys
+ *
+ * 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.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * partiter.c
+ *
+ * Provides disk / partition iteration.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <zlib.h>
+#include <syslinux/disk.h>
+#include "common.h"
+#include "partiter.h"
+#include "utility.h"
+
+#define ost_is_ext(type) ((type) == 0x05 || (type) == 0x0F || (type) == 0x85)
+#define ost_is_nondata(type) (ost_is_ext(type) || (type) == 0x00)
+#define sane(s,l) ((s)+(l) > (s))
+
+/* forwards */
+
+static int iter_ctor(struct part_iter *, va_list *);
+static int iter_dos_ctor(struct part_iter *, va_list *);
+static int iter_gpt_ctor(struct part_iter *, va_list *);
+static void iter_dtor(struct part_iter *);
+static struct part_iter *pi_dos_next(struct part_iter *);
+static struct part_iter *pi_gpt_next(struct part_iter *);
+static struct part_iter *pi_raw_next(struct part_iter *);
+
+static struct itertype types[] = {
+ [0] = {
+ .ctor = &iter_dos_ctor,
+ .dtor = &iter_dtor,
+ .next = &pi_dos_next,
+}, [1] = {
+ .ctor = &iter_gpt_ctor,
+ .dtor = &iter_dtor,
+ .next = &pi_gpt_next,
+}, [2] = {
+ .ctor = &iter_ctor,
+ .dtor = &iter_dtor,
+ .next = &pi_raw_next,
+}};
+
+const struct itertype * const typedos = types;
+const struct itertype * const typegpt = types+1;
+const struct itertype * const typeraw = types+2;
+
+#ifdef DEBUG
+static int inv_type(const void *type)
+{
+ int i, cnt = sizeof(types)/sizeof(types[0]);
+ for (i = 0; i < cnt; i++) {
+ if (type == types + i)
+ return 0;
+ }
+ return -1;
+}
+#endif
+
+/**
+ * iter_ctor() - common iterator initialization
+ * @iter: iterator pointer
+ * @args(0): disk_info structure used for disk functions
+ * @args(1): stepall modifier
+ *
+ * Second and further arguments are passed as a pointer to va_list
+ **/
+static int iter_ctor(struct part_iter *iter, va_list *args)
+{
+ const struct disk_info *di = va_arg(*args, const struct disk_info *);
+ int stepall = va_arg(*args, int);
+
+#ifdef DEBUG
+ if (!di)
+ return -1;
+#endif
+
+ memcpy(&iter->di, di, sizeof(struct disk_info));
+ iter->stepall = stepall;
+ iter->index0 = -1;
+ iter->length = di->lbacnt;
+
+ return 0;
+}
+
+/**
+ * iter_dtor() - common iterator cleanup
+ * @iter: iterator pointer
+ *
+ **/
+static void iter_dtor(struct part_iter *iter)
+{
+ free(iter->data);
+}
+
+/**
+ * iter_dos_ctor() - MBR/EBR iterator specific initialization
+ * @iter: iterator pointer
+ * @args(0): disk_info structure used for disk functions
+ * @args(1): pointer to buffer with loaded valid MBR
+ *
+ * Second and further arguments are passed as a pointer to va_list.
+ * This function only makes rudimentary checks. If user uses
+ * pi_new(), he/she is responsible for doing proper sanity checks.
+ **/
+static int iter_dos_ctor(struct part_iter *iter, va_list *args)
+{
+ const struct disk_dos_mbr *mbr;
+
+ /* uses args(0) */
+ if (iter_ctor(iter, args))
+ return -1;
+
+ mbr = va_arg(*args, const struct disk_dos_mbr *);
+
+#ifdef DEBUG
+ if (!mbr)
+ goto bail;
+#endif
+
+ if (!(iter->data = malloc(sizeof(struct disk_dos_mbr))))
+ goto bail;
+
+ memcpy(iter->data, mbr, sizeof(struct disk_dos_mbr));
+
+ iter->sub.dos.bebr_index0 = -1;
+ iter->sub.dos.disk_sig = mbr->disk_sig;
+
+ return 0;
+bail:
+ iter->type->dtor(iter);
+ return -1;
+}
+
+/**
+ * iter_gpt_ctor() - GPT iterator specific initialization
+ * @iter: iterator pointer
+ * @args(0): ptr to disk_info structure
+ * @args(1): ptr to buffer with GPT header
+ * @args(2): ptr to buffer with GPT partition list
+ *
+ * Second and further arguments are passed as a pointer to va_list.
+ * This function only makes rudimentary checks. If user uses
+ * pi_new(), he/she is responsible for doing proper sanity checks.
+ **/
+static int iter_gpt_ctor(struct part_iter *iter, va_list *args)
+{
+ uint64_t siz;
+ const struct disk_gpt_header *gpth;
+ const struct disk_gpt_part_entry *gptl;
+
+ /* uses args(0) */
+ if (iter_ctor(iter, args))
+ return -1;
+
+ gpth = va_arg(*args, const struct disk_gpt_header *);
+ gptl = va_arg(*args, const struct disk_gpt_part_entry *);
+
+#ifdef DEBUG
+ if (!gpth || !gptl)
+ goto bail;
+#endif
+
+ siz = (uint64_t)gpth->part_count * gpth->part_size;
+
+#ifdef DEBUG
+ if (!siz || (siz + iter->di.bps - 1) / iter->di.bps > 255u ||
+ gpth->part_size < sizeof(struct disk_gpt_part_entry)) {
+ goto bail;
+ }
+#endif
+
+ if (!(iter->data = malloc((size_t)siz)))
+ goto bail;
+
+ memcpy(iter->data, gptl, (size_t)siz);
+
+ iter->sub.gpt.pe_count = (int)gpth->part_count;
+ iter->sub.gpt.pe_size = (int)gpth->part_size;
+ iter->sub.gpt.ufirst = gpth->lba_first_usable;
+ iter->sub.gpt.ulast = gpth->lba_last_usable;
+
+ memcpy(&iter->sub.gpt.disk_guid, &gpth->disk_guid, sizeof(struct guid));
+
+ return 0;
+bail:
+ iter->type->dtor(iter);
+ return -1;
+}
+
+/* Logical partition must be sane, meaning:
+ * - must be data or empty
+ * - must have non-0 start and length
+ * - values must not wrap around 32bit
+ * - must be inside current EBR frame
+ */
+
+static int notsane_logical(const struct part_iter *iter)
+{
+ const struct disk_dos_part_entry *dp;
+ uint32_t end_log;
+
+ dp = ((struct disk_dos_mbr *)iter->data)->table;
+
+ if (!dp[0].ostype)
+ return 0;
+
+ if (ost_is_ext(dp[0].ostype)) {
+ error("1st EBR entry must be data or empty.\n");
+ return -1;
+ }
+
+ end_log = dp[0].start_lba + dp[0].length;
+
+ if (!dp[0].start_lba ||
+ !dp[0].length ||
+ !sane(dp[0].start_lba, dp[0].length) ||
+ end_log > iter->sub.dos.ebr_size) {
+
+ error("Insane logical partition.\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Extended partition must be sane, meaning:
+ * - must be extended or empty
+ * - must have non-0 start and length
+ * - values must not wrap around 32bit
+ * - must be inside base EBR frame
+ */
+
+static int notsane_extended(const struct part_iter *iter)
+{
+ const struct disk_dos_part_entry *dp;
+ uint32_t end_ebr;
+
+ dp = ((struct disk_dos_mbr *)iter->data)->table;
+
+ if (!dp[1].ostype)
+ return 0;
+
+ if (!ost_is_nondata(dp[1].ostype)) {
+ error("2nd EBR entry must be extended or empty.\n");
+ return -1;
+ }
+
+ end_ebr = dp[1].start_lba + dp[1].length;
+
+ if (!dp[1].start_lba ||
+ !dp[1].length ||
+ !sane(dp[1].start_lba, dp[1].length) ||
+ end_ebr > iter->sub.dos.bebr_size) {
+
+ error("Insane extended partition.\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Primary partition must be sane, meaning:
+ * - must have non-0 start and length
+ * - values must not wrap around 32bit
+ */
+
+static int notsane_primary(const struct part_iter *iter)
+{
+ const struct disk_dos_part_entry *dp;
+ dp = ((struct disk_dos_mbr *)iter->data)->table + iter->index0;
+
+ if (!dp->ostype)
+ return 0;
+
+ if (!dp->start_lba ||
+ !dp->length ||
+ !sane(dp->start_lba, dp->length) ||
+ dp->start_lba + dp->length > iter->di.lbacnt) {
+ error("Insane primary (MBR) partition.\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int notsane_gpt(const struct part_iter *iter)
+{
+ const struct disk_gpt_part_entry *gp;
+ gp = (const struct disk_gpt_part_entry *)
+ (iter->data + iter->index0 * iter->sub.gpt.pe_size);
+
+ if (guid_is0(&gp->type))
+ return 0;
+
+ if (gp->lba_first < iter->sub.gpt.ufirst ||
+ gp->lba_last > iter->sub.gpt.ulast) {
+ error("Insane GPT partition.\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int pi_dos_next_mbr(struct part_iter *iter, uint32_t *lba,
+ struct disk_dos_part_entry **_dp)
+{
+ struct disk_dos_part_entry *dp;
+
+ while (++iter->index0 < 4) {
+ dp = ((struct disk_dos_mbr *)iter->data)->table + iter->index0;
+
+ if (notsane_primary(iter)) {
+ iter->status = PI_INSANE;
+ goto bail;
+ }
+
+ if (ost_is_ext(dp->ostype)) {
+ if (iter->sub.dos.bebr_index0 >= 0) {
+ error("You have more than 1 extended partition.\n");
+ iter->status = PI_INSANE;
+ goto bail;
+ }
+ /* record base EBR index */
+ iter->sub.dos.bebr_index0 = iter->index0;
+ }
+ if (!ost_is_nondata(dp->ostype) || iter->stepall) {
+ *lba = dp->start_lba;
+ *_dp = dp;
+ break;
+ }
+ }
+
+ return 0;
+bail:
+ return -1;
+}
+
+static int prep_base_ebr(struct part_iter *iter)
+{
+ struct disk_dos_part_entry *dp;
+
+ if (iter->sub.dos.bebr_index0 < 0) /* if we don't have base extended partition at all */
+ return -1;
+ else if (!iter->sub.dos.bebr_start) { /* if not initialized yet */
+ dp = ((struct disk_dos_mbr *)iter->data)->table + iter->sub.dos.bebr_index0;
+
+ iter->sub.dos.bebr_start = dp->start_lba;
+ iter->sub.dos.bebr_size = dp->length;
+
+ iter->sub.dos.ebr_start = 0;
+ iter->sub.dos.ebr_size = iter->sub.dos.bebr_size;
+
+ iter->sub.dos.cebr_lba = 0;
+ iter->sub.dos.nebr_lba = iter->sub.dos.bebr_start;
+
+ iter->index0--;
+ }
+ return 0;
+}
+
+static int pi_dos_next_ebr(struct part_iter *iter, uint32_t *lba,
+ struct disk_dos_part_entry **_dp)
+{
+ struct disk_dos_part_entry *dp;
+
+ if (prep_base_ebr(iter)) {
+ iter->status = PI_DONE;
+ return -1;
+ }
+
+ while (++iter->index0 < 1024 && iter->sub.dos.nebr_lba) {
+ free(iter->data);
+ if (!(iter->data =
+ disk_read_sectors(&iter->di, iter->sub.dos.nebr_lba, 1))) {
+ error("Couldn't load EBR.\n");
+ iter->status = PI_ERRLOAD;
+ return -1;
+ }
+
+ if (notsane_logical(iter) || notsane_extended(iter)) {
+ iter->status = PI_INSANE;
+ return -1;
+ }
+
+ dp = ((struct disk_dos_mbr *)iter->data)->table;
+
+ iter->sub.dos.cebr_lba = iter->sub.dos.nebr_lba;
+
+ /* setup next frame values */
+ if (dp[1].ostype) {
+ iter->sub.dos.ebr_start = dp[1].start_lba;
+ iter->sub.dos.ebr_size = dp[1].length;
+ iter->sub.dos.nebr_lba = iter->sub.dos.bebr_start + dp[1].start_lba;
+ } else {
+ iter->sub.dos.ebr_start = 0;
+ iter->sub.dos.ebr_size = 0;
+ iter->sub.dos.nebr_lba = 0;
+ }
+
+ if (!dp[0].ostype)
+ iter->sub.dos.skipcnt++;
+
+ if (dp[0].ostype || iter->stepall) {
+ *lba = iter->sub.dos.cebr_lba + dp[0].start_lba;
+ *_dp = dp;
+ return 0;
+ }
+ /*
+ * This way it's possible to continue, if some crazy soft left a "hole"
+ * - EBR with a valid extended partition without a logical one. In
+ * such case, linux will not reserve a number for such hole - so we
+ * don't increase index0. If stepall flag is set, we will never reach
+ * this place.
+ */
+ }
+ iter->status = PI_DONE;
+ return -1;
+}
+
+static struct part_iter *pi_dos_next(struct part_iter *iter)
+{
+ uint32_t start_lba = 0;
+ struct disk_dos_part_entry *dos_part = NULL;
+
+ if (iter->status)
+ goto bail;
+
+ /* look for primary partitions */
+ if (iter->index0 < 4 &&
+ pi_dos_next_mbr(iter, &start_lba, &dos_part))
+ goto bail;
+
+ /* look for logical partitions */
+ if (iter->index0 >= 4 &&
+ pi_dos_next_ebr(iter, &start_lba, &dos_part))
+ goto bail;
+
+ /*
+ * note special index handling, if we have stepall set -
+ * this is made to keep index consistent with non-stepall
+ * iterators
+ */
+
+ if (iter->index0 >= 4 && !dos_part->ostype)
+ iter->index = -1;
+ else
+ iter->index = iter->index0 - iter->sub.dos.skipcnt + 1;
+ iter->rawindex = iter->index0 + 1;
+ iter->start_lba = start_lba;
+ iter->length = dos_part->length;
+ iter->record = (char *)dos_part;
+
+#ifdef DEBUG
+ disk_dos_part_dump(dos_part);
+#endif
+
+ return iter;
+bail:
+ return NULL;
+}
+
+static void gpt_conv_label(struct part_iter *iter)
+{
+ const struct disk_gpt_part_entry *gp;
+ const int16_t *orig_lab;
+
+ gp = (const struct disk_gpt_part_entry *)
+ (iter->data + iter->index0 * iter->sub.gpt.pe_size);
+ orig_lab = (const int16_t *)gp->name;
+
+ /* caveat: this is very crude conversion */
+ for (int i = 0; i < PI_GPTLABSIZE/2; i++) {
+ iter->sub.gpt.part_label[i] = (char)orig_lab[i];
+ }
+ iter->sub.gpt.part_label[PI_GPTLABSIZE/2] = 0;
+}
+
+static struct part_iter *pi_gpt_next(struct part_iter *iter)
+{
+ const struct disk_gpt_part_entry *gpt_part = NULL;
+
+ if (iter->status)
+ goto bail;
+
+ while (++iter->index0 < iter->sub.gpt.pe_count) {
+ gpt_part = (const struct disk_gpt_part_entry *)
+ (iter->data + iter->index0 * iter->sub.gpt.pe_size);
+
+ if (notsane_gpt(iter)) {
+ iter->status = PI_INSANE;
+ goto bail;
+ }
+
+ if (!guid_is0(&gpt_part->type) || iter->stepall)
+ break;
+ }
+ /* no more partitions ? */
+ if (iter->index0 == iter->sub.gpt.pe_count) {
+ iter->status = PI_DONE;
+ goto bail;
+ }
+ /* gpt_part is guaranteed to be valid here */
+ iter->index = iter->index0 + 1;
+ iter->rawindex = iter->index0 + 1;
+ iter->start_lba = gpt_part->lba_first;
+ iter->length = gpt_part->lba_last - gpt_part->lba_first + 1;
+ iter->record = (char *)gpt_part;
+ memcpy(&iter->sub.gpt.part_guid, &gpt_part->uid, sizeof(struct guid));
+ gpt_conv_label(iter);
+
+#ifdef DEBUG
+ disk_gpt_part_dump(gpt_part);
+#endif
+
+ return iter;
+bail:
+ return NULL;
+}
+
+static struct part_iter *pi_raw_next(struct part_iter *iter)
+{
+ iter->status = PI_DONE;
+ return NULL;
+}
+
+static int check_crc(uint32_t crc_match, const uint8_t *buf, unsigned int siz)
+{
+ uint32_t crc;
+
+ crc = crc32(0, NULL, 0);
+ crc = crc32(crc, buf, siz);
+
+ return crc_match != crc;
+}
+
+static int gpt_check_hdr_crc(const struct disk_info * const diskinfo, struct disk_gpt_header **_gh)
+{
+ struct disk_gpt_header *gh = *_gh;
+ uint64_t lba_alt;
+ uint32_t hold_crc32;
+
+ hold_crc32 = gh->chksum;
+ gh->chksum = 0;
+ if (check_crc(hold_crc32, (const uint8_t *)gh, gh->hdr_size)) {
+ error("WARNING: Primary GPT header checksum invalid.\n");
+ /* retry with backup */
+ lba_alt = gh->lba_alt;
+ free(gh);
+ if (!(gh = *_gh = disk_read_sectors(diskinfo, lba_alt, 1))) {
+ error("Couldn't read backup GPT header.\n");
+ return -1;
+ }
+ hold_crc32 = gh->chksum;
+ gh->chksum = 0;
+ if (check_crc(hold_crc32, (const uint8_t *)gh, gh->hdr_size)) {
+ error("Secondary GPT header checksum invalid.\n");
+ return -1;
+ }
+ }
+ /* restore old checksum */
+ gh->chksum = hold_crc32;
+
+ return 0;
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ * Following functions are for users to call.
+ * ----------------------------------------------------------------------------
+ */
+
+
+int pi_next(struct part_iter **_iter)
+{
+ struct part_iter *iter;
+
+ if(!_iter || !*_iter)
+ return 0;
+ iter = *_iter;
+#ifdef DEBUG
+ if (inv_type(iter->type)) {
+ error("This is not a valid iterator.\n");
+ return 0;
+ }
+#endif
+ if ((iter = iter->type->next(iter))) {
+ *_iter = iter;
+ }
+ return (*_iter)->status;
+}
+
+/**
+ * pi_new() - get new iterator
+ * @itertype: iterator type
+ * @...: variable arguments passed to ctors
+ *
+ * Variable arguments depend on the type. Please see functions:
+ * iter_gpt_ctor() and iter_dos_ctor() for details.
+ **/
+struct part_iter *pi_new(const struct itertype *type, ...)
+{
+ int badctor = 0;
+ struct part_iter *iter = NULL;
+ va_list ap;
+
+ va_start(ap, type);
+
+#ifdef DEBUG
+ if (inv_type(type)) {
+ error("Unknown iterator requested.\n");
+ goto bail;
+ }
+#endif
+
+ if (!(iter = malloc(sizeof(struct part_iter)))) {
+ error("Couldn't allocate memory for the iterator.\n");
+ goto bail;
+ }
+
+ memset(iter, 0, sizeof(struct part_iter));
+ iter->type = type;
+
+ if (type->ctor(iter, &ap)) {
+ badctor = -1;
+ error("Cannot initialize the iterator.\n");
+ goto bail;
+ }
+
+bail:
+ va_end(ap);
+ if (badctor) {
+ free(iter);
+ iter = NULL;
+ }
+ return iter;
+}
+
+/**
+ * pi_del() - delete iterator
+ * @iter: iterator double pointer
+ *
+ **/
+
+void pi_del(struct part_iter **_iter)
+{
+ struct part_iter *iter;
+
+ if(!_iter || !*_iter)
+ return;
+ iter = *_iter;
+
+#ifdef DEBUG
+ if (inv_type(iter->type)) {
+ error("This is not a valid iterator.\n");
+ return;
+ }
+#endif
+
+ iter->type->dtor(iter);
+ free(iter);
+ *_iter = NULL;
+}
+
+/**
+ * pi_begin() - check disk, validate, and get proper iterator
+ * @di: diskinfo struct pointer
+ *
+ * This function checks the disk for GPT or legacy partition table and allocates
+ * an appropriate iterator.
+ **/
+struct part_iter *pi_begin(const struct disk_info *di, int stepall)
+{
+ int setraw = 0;
+ struct part_iter *iter = NULL;
+ struct disk_dos_mbr *mbr = NULL;
+ struct disk_gpt_header *gpth = NULL;
+ struct disk_gpt_part_entry *gptl = NULL;
+
+ /* Read MBR */
+ if (!(mbr = disk_read_sectors(di, 0, 1))) {
+ error("Couldn't read first disk sector.\n");
+ goto bail;
+ }
+
+ setraw = -1;
+
+ /* Check for MBR magic*/
+ if (mbr->sig != disk_mbr_sig_magic) {
+ error("No MBR magic.\n");
+ goto bail;
+ }
+
+ /* Check for GPT protective MBR */
+ if (mbr->table[0].ostype == 0xEE) {
+ if (!(gpth = disk_read_sectors(di, 1, 1))) {
+ error("Couldn't read potential GPT header.\n");
+ goto bail;
+ }
+ }
+
+ if (gpth && gpth->rev.uint32 == 0x00010000 &&
+ !memcmp(gpth->sig, disk_gpt_sig_magic, sizeof(disk_gpt_sig_magic))) {
+ /* looks like GPT v1.0 */
+ uint64_t gpt_loff; /* offset to GPT partition list in sectors */
+ uint64_t gpt_lsiz; /* size of GPT partition list in bytes */
+ uint64_t gpt_lcnt; /* size of GPT partition in sectors */
+#ifdef DEBUG
+ puts("Looks like a GPT v1.0 disk.");
+ disk_gpt_header_dump(gpth);
+#endif
+ /* Verify checksum, fallback to backup, then bail if invalid */
+ if (gpt_check_hdr_crc(di, &gpth))
+ goto bail;
+
+ gpt_loff = gpth->lba_table;
+ gpt_lsiz = (uint64_t)gpth->part_size * gpth->part_count;
+ gpt_lcnt = (gpt_lsiz + di->bps - 1) / di->bps;
+
+ /*
+ * disk_read_sectors allows reading of max 255 sectors, so we use
+ * it as a sanity check base. EFI doesn't specify max (AFAIK).
+ * Apart from that, some extensive sanity checks.
+ */
+ if (!gpt_loff || !gpt_lsiz || gpt_lcnt > 255u ||
+ gpth->lba_first_usable > gpth->lba_last_usable ||
+ !sane(gpt_loff, gpt_lcnt) ||
+ gpt_loff + gpt_lcnt > gpth->lba_first_usable ||
+ !sane(gpth->lba_last_usable, gpt_lcnt) ||
+ gpth->lba_last_usable + gpt_lcnt >= gpth->lba_alt ||
+ gpth->lba_alt >= di->lbacnt ||
+ gpth->part_size < sizeof(struct disk_gpt_part_entry)) {
+ error("Invalid GPT header's values.\n");
+ goto bail;
+ }
+ if (!(gptl = disk_read_sectors(di, gpt_loff, (uint8_t)gpt_lcnt))) {
+ error("Couldn't read GPT partition list.\n");
+ goto bail;
+ }
+ /* Check array checksum(s). */
+ if (check_crc(gpth->table_chksum, (const uint8_t *)gptl, (unsigned int)gpt_lsiz)) {
+ error("WARNING: GPT partition list checksum invalid, trying backup.\n");
+ free(gptl);
+ /* secondary array directly precedes secondary header */
+ if (!(gptl = disk_read_sectors(di, gpth->lba_alt - gpt_lcnt, (uint8_t)gpt_lcnt))) {
+ error("Couldn't read backup GPT partition list.\n");
+ goto bail;
+ }
+ if (check_crc(gpth->table_chksum, (const uint8_t *)gptl, (unsigned int)gpt_lsiz)) {
+ error("Backup GPT partition list checksum invalid.\n");
+ goto bail;
+ }
+ }
+ /* allocate iterator and exit */
+ iter = pi_new(typegpt, di, stepall, gpth, gptl);
+ } else {
+ /* looks like MBR */
+ iter = pi_new(typedos, di, stepall, mbr);
+ }
+
+ setraw = 0;
+bail:
+ if (setraw) {
+ error("WARNING: treating disk as raw.\n");
+ iter = pi_new(typeraw, di, stepall);
+ }
+ free(mbr);
+ free(gpth);
+ free(gptl);
+
+ return iter;
+}
+
+/* vim: set ts=8 sts=4 sw=4 noet: */
diff --git a/com32/chain/partiter.h b/com32/chain/partiter.h
new file mode 100644
index 00000000..7deeb534
--- /dev/null
+++ b/com32/chain/partiter.h
@@ -0,0 +1,107 @@
+/* ----------------------------------------------------------------------- *
+ *
+ * Copyright 2003-2010 H. Peter Anvin - All Rights Reserved
+ * Copyright 2010 Michal Soltys
+ * Copyright 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.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * partiter.h
+ *
+ * Provides disk / partition iteration.
+ */
+
+#ifndef _COM32_CHAIN_PARTITER_H
+#define _COM32_CHAIN_PARTITER_H
+
+#include <stdint.h>
+#include <syslinux/disk.h>
+
+#define PI_ERRLOAD 3
+#define PI_INSANE 2
+#define PI_DONE 1
+#define PI_OK 0
+
+struct itertype;
+struct part_iter;
+
+struct itertype {
+ int (*ctor)(struct part_iter *, va_list *);
+ void (*dtor)(struct part_iter *);
+ struct part_iter *(*next) (struct part_iter *);
+};
+
+#define PI_GPTLABSIZE ((int)sizeof(((struct disk_gpt_part_entry *)0)->name))
+
+struct part_iter {
+ const struct itertype *type;
+ char *data;
+ char *record;
+ uint64_t start_lba;
+ uint64_t length;
+ int index;
+ int rawindex;
+ struct disk_info di;
+ int stepall;
+ int status;
+ /* internal */
+ int index0;
+ union _sub {
+ struct _dos {
+ uint32_t disk_sig;
+ uint32_t nebr_lba;
+ uint32_t cebr_lba;
+ /* internal */
+ uint32_t ebr_start;
+ uint32_t ebr_size;
+ uint32_t bebr_start;
+ uint32_t bebr_size;
+ int bebr_index0;
+ int skipcnt;
+ } dos;
+ struct _gpt {
+ struct guid disk_guid;
+ struct guid part_guid;
+ char part_label[PI_GPTLABSIZE/2+1];
+ int pe_count;
+ int pe_size;
+ uint64_t ufirst;
+ uint64_t ulast;
+ } gpt;
+ } sub;
+};
+
+extern const struct itertype * const typedos;
+extern const struct itertype * const typegpt;
+extern const struct itertype * const typeraw;
+
+struct part_iter *pi_begin(const struct disk_info *, int stepall);
+struct part_iter *pi_new(const struct itertype *, ...);
+void pi_del(struct part_iter **);
+int pi_next(struct part_iter **);
+
+#endif
+
+/* vim: set ts=8 sts=4 sw=4 noet: */
diff --git a/com32/chain/utility.c b/com32/chain/utility.c
new file mode 100644
index 00000000..fb59551b
--- /dev/null
+++ b/com32/chain/utility.c
@@ -0,0 +1,214 @@
+#include <com32.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+#include <syslinux/disk.h>
+#include "utility.h"
+
+static const char *bpbtypes[] = {
+ [0] = "unknown",
+ [1] = "2.0",
+ [2] = "3.0",
+ [3] = "3.2",
+ [4] = "3.4",
+ [5] = "4.0",
+ [6] = "8.0 (NT+)",
+ [7] = "7.0",
+};
+
+void error(const char *msg)
+{
+ fputs(msg, stderr);
+}
+
+int guid_is0(const struct guid *guid)
+{
+ return !*(const uint64_t *)guid && !*((const uint64_t *)guid + 1);
+}
+
+void wait_key(void)
+{
+ int cnt;
+ char junk;
+
+ /* drain */
+ do {
+ errno = 0;
+ cnt = read(0, &junk, 1);
+ } while (cnt > 0 || (cnt < 0 && errno == EAGAIN));
+
+ /* wait */
+ do {
+ errno = 0;
+ cnt = read(0, &junk, 1);
+ } while (!cnt || (cnt < 0 && errno == EAGAIN));
+}
+
+void lba2chs(disk_chs *dst, const struct disk_info *di, uint64_t lba, uint32_t mode)
+{
+ uint32_t c, h, s, t;
+ uint32_t cs, hs, ss;
+
+ /*
+ * Not much reason here, but if we have no valid CHS geometry, we assume
+ * "typical" ones to have something to return.
+ */
+ if (di->cbios) {
+ cs = di->cyl;
+ hs = di->head;
+ ss = di->spt;
+ if (mode == l2c_cadd && cs < 1024 && di->lbacnt > cs*hs*ss)
+ cs++;
+ else if (mode == l2c_cmax)
+ cs = 1024;
+ } else {
+ if (di->disk & 0x80) {
+ cs = 1024;
+ hs = 255;
+ ss = 63;
+ } else {
+ cs = 80;
+ hs = 2;
+ ss = 18;
+ }
+ }
+
+ if (lba >= cs*hs*ss) {
+ s = ss;
+ h = hs - 1;
+ c = cs - 1;
+ } else {
+ s = ((uint32_t)lba % ss) + 1;
+ t = (uint32_t)lba / ss;
+ h = t % hs;
+ c = t / hs;
+ }
+
+ (*dst)[0] = h;
+ (*dst)[1] = s | ((c & 0x300) >> 2);
+ (*dst)[2] = c;
+}
+
+uint32_t get_file_lba(const char *filename)
+{
+ com32sys_t inregs;
+ uint32_t lba;
+
+ /* Start with clean registers */
+ memset(&inregs, 0, sizeof(com32sys_t));
+
+ /* Put the filename in the bounce buffer */
+ strlcpy(__com32.cs_bounce, filename, __com32.cs_bounce_size);
+
+ /* Call comapi_open() which returns a structure pointer in SI
+ * to a structure whose first member happens to be the LBA.
+ */
+ inregs.eax.w[0] = 0x0006;
+ inregs.esi.w[0] = OFFS(__com32.cs_bounce);
+ inregs.es = SEG(__com32.cs_bounce);
+ __com32.cs_intcall(0x22, &inregs, &inregs);
+
+ if ((inregs.eflags.l & EFLAGS_CF) || inregs.esi.w[0] == 0) {
+ return 0; /* Filename not found */
+ }
+
+ /* Since the first member is the LBA, we simply cast */
+ lba = *((uint32_t *) MK_PTR(inregs.ds, inregs.esi.w[0]));
+
+ /* Clean the registers for the next call */
+ memset(&inregs, 0, sizeof(com32sys_t));
+
+ /* Put the filename in the bounce buffer */
+ strlcpy(__com32.cs_bounce, filename, __com32.cs_bounce_size);
+
+ /* Call comapi_close() to free the structure */
+ inregs.eax.w[0] = 0x0008;
+ inregs.esi.w[0] = OFFS(__com32.cs_bounce);
+ inregs.es = SEG(__com32.cs_bounce);
+ __com32.cs_intcall(0x22, &inregs, &inregs);
+
+ return lba;
+}
+
+/* drive offset detection */
+int drvoff_detect(int type, unsigned int *off)
+{
+ if (bpbV40 <= type && type <= bpbVNT) {
+ *off = 0x24;
+ } else if (type == bpbV70) {
+ *off = 0x40;
+ } else
+ return 0;
+
+ return -1;
+}
+
+/*
+ * heuristics could certainly be improved
+ */
+int bpb_detect(const uint8_t *sec, const char *tag)
+{
+ int a, b, c, jmp = -1, rev = 0;
+
+ /* media descriptor check */
+ if ((sec[0x15] & 0xF0) != 0xF0)
+ goto out;
+
+ if (sec[0] == 0xEB) /* jump short */
+ jmp = 2 + *(int8_t *)(sec + 1);
+ else if (sec[0] == 0xE9) /* jump near */
+ jmp = 3 + *(int16_t *)(sec + 1);
+
+ if (jmp < 0) /* no boot code at all ? */
+ goto nocode;
+
+ /* sanity */
+ if (jmp < 0x18 || jmp > 0x1F0)
+ goto out;
+
+ /* detect by jump */
+ if (jmp >= 0x18 && jmp < 0x1E)
+ rev = bpbV20;
+ else if (jmp >= 0x1E && jmp < 0x20)
+ rev = bpbV30;
+ else if (jmp >= 0x20 && jmp < 0x24)
+ rev = bpbV32;
+ else if (jmp >= 0x24 && jmp < 0x46)
+ rev = bpbV34;
+
+ /* TODO: some better V2 - V3.4 checks ? */
+
+ if (rev)
+ goto out;
+ /*
+ * BPB info:
+ * 2.0 == 0x0B - 0x17
+ * 3.0 == 2.0 + 0x18 - 0x1D
+ * 3.2 == 3.0 + 0x1E - 0x1F
+ * 3.4 ==!2.0 + 0x18 - 0x23
+ * 4.0 == 3.4 + 0x24 - 0x45
+ * NT ==~3.4 + 0x24 - 0x53
+ * 7.0 == 3.4 + 0x24 - 0x59
+ */
+
+nocode:
+ a = memcmp(sec + 0x03, "NTFS", 4);
+ b = memcmp(sec + 0x36, "FAT", 3);
+ c = memcmp(sec + 0x52, "FAT", 3); /* ext. DOS 7+ bs */
+
+ if ((sec[0x26] & 0xFE) == 0x28 && !b) {
+ rev = bpbV40;
+ } else if (sec[0x26] == 0x80 && !a) {
+ rev = bpbVNT;
+ } else if ((sec[0x42] & 0xFE) == 0x28 && !c) {
+ rev = bpbV70;
+ }
+
+out:
+ printf("BPB detection (%s): %s\n", tag, bpbtypes[rev]);
+ return rev;
+}
+
+/* vim: set ts=8 sts=4 sw=4 noet: */
diff --git a/com32/chain/utility.h b/com32/chain/utility.h
new file mode 100644
index 00000000..8a08be71
--- /dev/null
+++ b/com32/chain/utility.h
@@ -0,0 +1,30 @@
+#ifndef _COM32_CHAIN_UTILITY_H
+#define _COM32_CHAIN_UTILITY_H
+
+#include <stdint.h>
+#include <syslinux/disk.h>
+
+#define bpbUNK 0
+#define bpbV20 1
+#define bpbV30 2
+#define bpbV32 3
+#define bpbV34 4
+#define bpbV40 5
+#define bpbVNT 6
+#define bpbV70 7
+
+#define l2c_cnul 0
+#define l2c_cadd 1
+#define l2c_cmax 2
+
+void error(const char *msg);
+int guid_is0(const struct guid *guid);
+void wait_key(void);
+void lba2chs(disk_chs *dst, const struct disk_info *di, uint64_t lba, uint32_t mode);
+uint32_t get_file_lba(const char *filename);
+int drvoff_detect(int type, unsigned int *off);
+int bpb_detect(const uint8_t *bpb, const char *tag);
+
+#endif
+
+/* vim: set ts=8 sts=4 sw=4 noet: */