aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--com32/chain/Makefile2
-rw-r--r--com32/chain/chain.c788
-rw-r--r--com32/chain/chain.h50
-rw-r--r--com32/chain/common.h9
-rw-r--r--com32/chain/mangle.c318
-rw-r--r--com32/chain/mangle.h19
-rw-r--r--com32/chain/options.c336
-rw-r--r--com32/chain/options.h10
-rw-r--r--com32/chain/partiter.c12
-rw-r--r--com32/chain/partiter.h6
-rw-r--r--com32/chain/utility.c114
-rw-r--r--com32/chain/utility.h15
12 files changed, 891 insertions, 788 deletions
diff --git a/com32/chain/Makefile b/com32/chain/Makefile
index 337775c7..054c7681 100644
--- a/com32/chain/Makefile
+++ b/com32/chain/Makefile
@@ -15,7 +15,7 @@
topdir = ../..
include ../MCONFIG
-OBJS = chain.o partiter.o
+OBJS = chain.o partiter.o utility.o options.o mangle.o
GCCEXTRA = -Wextra -Wconversion -pedantic -Wno-error -DDEBUG
all: chain.c32
diff --git a/com32/chain/chain.c b/com32/chain/chain.c
index 5af6c152..d1939ecd 100644
--- a/com32/chain/chain.c
+++ b/com32/chain/chain.c
@@ -34,83 +34,22 @@
#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"
-/* used in checks, whenever addresses supplied by user are sane */
+struct options opt;
-#define ADDRMAX 0x9EFFFu
-#define ADDRMIN 0x500u
-
-static const char cmldr_signature[8] = "cmdcons";
static int fixed_cnt;
-static struct options {
- unsigned int fseg;
- unsigned int foff;
- unsigned int fip;
- unsigned int sseg;
- unsigned int soff;
- unsigned int sip;
- unsigned int drvoff;
- 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;
- bool hide;
- bool sethid;
- bool setgeo;
- bool setdrv;
- bool sect;
- bool save;
- bool filebpb;
- bool warn;
- uint16_t keeppxe;
- struct syslinux_rm_regs regs;
-} opt;
-
-struct data_area {
- void *data;
- addr_t base;
- addr_t size;
-};
-
-static 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));
-}
-
-static void error(const char *msg)
-{
- fputs(msg, stderr);
-}
-
-static int no_ov(const struct data_area *a, const struct data_area *b)
+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;
+ a->base + a->size > b->base &&
+ b->base + b->size > a->base;
}
static int is_phys(uint8_t sdifs)
@@ -400,377 +339,6 @@ out:
free(mbr);
}
-static 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;
-}
-
-/* Convert string seg:off:ip values into numerical seg:off:ip ones */
-
-static int soi_s2n(char *ptr, unsigned int *seg,
- unsigned int *off,
- unsigned int *ip)
-{
- unsigned int segval = 0, offval = 0, ipval = 0, val;
- char *p;
-
- segval = strtoul(ptr, &p, 0);
- if (*p == ':')
- offval = strtoul(p+1, &p, 0);
- if (*p == ':')
- 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)
-{
- static const char *const usage[] = { "\
-Usage:\n\
- chain.c32 [options]\n\
- chain.c32 {fd|hd}<disk> [<partition>] [options]\n\
- chain.c32 mbr{:|=}<id> [<partition>] [options]\n\
- chain.c32 guid{:|=}<guid> [<partition>] [options]\n\
- chain.c32 label{:|=}<label> [<partition>] [options]\n\
- chain.c32 boot{,| }[<partition>] [options]\n\
- chain.c32 fs [options]\n\
-\nOptions ('no' prefix specify defaulti value):\n\
- file=<loader> Load and execute file\n\
- seg=<s[:o[:i]]> Load file at <s:o>, jump to <s:i>\n\
- nofilebpb Treat file in memory as BPB compatible\n\
- sect[=<s[:o[:i]]>] Load sector at <s:o>, jump to <s:i>\n\
- - defaults to 0:0x7C00:0x7C00\n\
- maps Map loaded sector into real memory\n\
- nosethid[den] Set BPB's hidden sectors field\n\
- nosetgeo Set BPB's sectors per track and heads fields\n\
- nosetdrv[@<off>] Set BPB's drive unit field at <o>\n\
- - <off> defaults to autodetection\n\
- - only 0x24 and 0x40 are accepted\n\
- nosetbpb Enable set{hid,geo,drv}\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 Hide primary partitions, unhide selected partition\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\
-", "\
-\nComposite options:\n\
- isolinux=<loader> Load another version of ISOLINUX\n\
- ntldr=<loader> Load Windows NTLDR, SETUPLDR.BIN or BOOTMGR\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\
-\nPlease see doc/chain.txt for the detailed documentation.\n"
- };
- error(usage[0]);
- error("Press any key...\n");
- wait_key();
- error(usage[1]);
-}
-
-static int 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))
- goto bail;
- } 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.sethid = true;
- opt.setgeo = true;
- opt.setdrv = true;
- opt.drvoff = 0x24;
- /* 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.sethid = true;
- opt.setgeo = true;
- opt.setdrv = true;
- opt.drvoff = 0x24;
- /* 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 = 0x9000;
- opt.soff = 0;
- opt.sip = 0;
- opt.file = argv[i] + 8;
- opt.sethid = true;
- opt.setgeo = true;
- opt.setdrv = true;
- opt.drvoff = ~0u;
- /* 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 = 0x9000;
- opt.soff = 0;
- opt.sip = 0;
- opt.file = argv[i] + v;
- opt.sethid = true;
- opt.setgeo = true;
- opt.setdrv = true;
- opt.drvoff = ~0u;
- /* 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.sethid = true;
- opt.setgeo = true;
- opt.setdrv = true;
- opt.drvoff = ~0u;
- /* 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], "hide")) {
- opt.hide = true;
- } else if (!strcmp(argv[i], "nohide")) {
- opt.hide = false;
- } else if (!strcmp(argv[i], "sethid") ||
- !strcmp(argv[i], "sethidden")) {
- opt.sethid = true;
- } else if (!strcmp(argv[i], "nosethid") ||
- !strcmp(argv[i], "nosethidden")) {
- opt.sethid = false;
- } else if (!strcmp(argv[i], "setgeo")) {
- opt.setgeo = true;
- } else if (!strcmp(argv[i], "nosetgeo")) {
- opt.setgeo = false;
- } else if (!strncmp(argv[i], "setdrv",6)) {
- if (!argv[i][6])
- v = ~0u; /* autodetect */
- else if (argv[i][6] == '@' ||
- argv[i][6] == '=' ||
- argv[i][6] == ':') {
- v = strtoul(argv[i] + 7, NULL, 0);
- if (!(v == 0x24 || v == 0x40)) {
- error("Invalid 'setdrv' offset.\n");
- goto bail;
- }
- } else {
- error("Invalid 'setdrv' specification.\n");
- goto bail;
- }
- opt.setdrv = true;
- opt.drvoff = v;
- } else if (!strcmp(argv[i], "nosetdrv")) {
- opt.setdrv = false;
- } else if (!strcmp(argv[i], "setbpb")) {
- opt.setdrv = true;
- opt.drvoff = ~0u;
- opt.setgeo = true;
- opt.sethid = true;
- } else if (!strcmp(argv[i], "nosetbpb")) {
- opt.setdrv = false;
- opt.setgeo = false;
- opt.sethid = 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))
- goto bail;
- if ((opt.sseg << 4) + opt.soff + SECTOR - 1 > ADDRMAX) {
- error("Arguments of 'sect=' are invalid - resulting address too big.\n");
- goto bail;
- }
- }
- opt.sect = true;
- } else if (!strcmp(argv[i], "nosect")) {
- opt.sect = false;
- } else if (!strcmp(argv[i], "save")) {
- opt.save = true;
- } else if (!strcmp(argv[i], "nosave")) {
- opt.save = false;
- } else if (!strcmp(argv[i], "filebpb")) {
- opt.filebpb = true;
- } else if (!strcmp(argv[i], "nofilebpb")) {
- opt.filebpb = false;
- } else if (!strcmp(argv[i], "warn")) {
- opt.warn = true;
- } else if (!strcmp(argv[i], "nowarn")) {
- opt.warn = 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];
- p = strchr(opt.drivename, ',');
- 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 ((!opt.maps || !opt.sect) && !opt.file) {
- error("You have to load something.\n");
- goto bail;
- }
-
- if (opt.filebpb && !opt.file) {
- error("Option 'filebpb' requires file.\n");
- goto bail;
- }
-
- return 0;
-bail:
- return -1;
-}
-
int find_dp(struct part_iter **_iter)
{
struct part_iter *iter;
@@ -888,256 +456,6 @@ bail:
return -1;
}
-/* 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)
- */
-static 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;
-
- 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;
-}
-
-/*
- * GRLDR of GRUB4DOS wants the partition number in DH:
- * -1: whole drive (default)
- * 0-3: primary partitions
- * 4-*: logical partitions
- */
-static int manglef_grldr(const struct part_iter *iter)
-{
- opt.regs.edx.b[1] = (uint8_t)(iter->index - 1);
- return 0;
-}
-
-/*
- * Legacy grub's stage2 chainloading
- */
-static 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 (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;
-}
-
-/*
- * Dell's DRMK chainloading.
- */
-static 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.
- */
-
- uint32_t tsize = (data->size + 19) & 0xfffffff0;
- 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+28] must be 0x0000003f */
- opt.regs.ds = (uint16_t)((tsize >> 4) + (opt.fseg - 2));
- /* "Patch" into tail of the new space */
- *(uint32_t *)((char*)data->data + tsize - 4) = 0x0000003f;
-
- return 0;
-bail:
- return -1;
-}
-
-static uint32_t lba2chs(const struct disk_info *di, uint64_t lba)
-{
- uint32_t c, h, s, t;
-
- if (di->cbios) {
- if (lba >= di->cyl * di->head * di->sect) {
- s = di->sect;
- h = di->head - 1;
- c = di->cyl - 1;
- goto out;
- }
- s = ((uint32_t)lba % di->sect) + 1;
- t = (uint32_t)lba / di->sect;
- h = t % di->head;
- c = t / di->head;
- } else
- goto fallback;
-
-out:
- return h | (s << 8) | ((c & 0x300) << 6) | ((c & 0xFF) << 16);
-
-fallback:
- if (di->disk & 0x80)
- return 0x00FFFFFE; /* 1023/63/254 */
- else
- /* FIXME ?
- * this is mostly "useful" with partitioned floppy,
- * maybe stick to 2.88mb ?
- */
- return 0x004F1201; /* 79/18/1 */
-#if 0
- return 0x004F2401; /* 79/36/1 */
-#endif
-}
-
static int setup_handover(const struct part_iter *iter,
struct data_area *data)
{
@@ -1218,86 +536,6 @@ bail:
return -1;
}
-static int manglef_bpb(const struct part_iter *iter, struct data_area *data)
-{
- /* BPB: hidden sectors */
- if (opt.sethid) {
- 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: legacy geometry */
- if (opt.setgeo) {
- if (iter->di.cbios)
- *(uint32_t *)((char *)data->data + 0x18) = (uint32_t)((iter->di.head << 16) | iter->di.sect);
- else {
- if (iter->di.disk & 0x80)
- *(uint32_t *)((char *)data->data + 0x18) = 0x00FF003F;
- else
- *(uint32_t *)((char *)data->data + 0x18) = 0x00020012;
- }
- }
-
- /* BPB: drive */
- if (opt.setdrv)
- *(uint8_t *)((char *)data->data + opt.drvoff) = (uint8_t)
- (opt.swap ? iter->di.disk & 0x80 : iter->di.disk);
-
- return 0;
-}
-
-static int try_mangles_bpb(const struct part_iter *iter, struct data_area *data)
-{
- void *cmp_buf = NULL;
-
- if (!(opt.setdrv || opt.setgeo || opt.sethid))
- return 0;
-
-#if 0
- /* Turn this off for now. It's hard to find a reason to
- * BPB-mangle sector 0 of whatever there is, but it's
- * "potentially" useful (fixing fdd's sector ?).
- */
- if (!iter->index)
- return 0;
-#endif
-
- if (!(cmp_buf = malloc(data->size))) {
- error("Could not allocate sector-compare buffer.\n");
- goto bail;
- }
-
- memcpy(cmp_buf, data->data, data->size);
-
- manglef_bpb(iter, data);
-
- if (opt.save && memcmp(cmp_buf, data->data, data->size)) {
- if (disk_write_verify_sector(&iter->di, iter->start_lba, data->data)) {
- error("Cannot write updated boot sector.\n");
- goto bail;
- }
- }
-
- free(cmp_buf);
- 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.
- */
-static int mangles_cmldr(struct data_area *data)
-{
- memcpy((char *)data->data + 3, cmldr_signature, sizeof(cmldr_signature));
- return 0;
-}
-
int setdrv_auto(const struct part_iter *iter)
{
int a, b;
@@ -1408,7 +646,7 @@ int main(int argc, char *argv[])
data[ndata].size = SECTOR;
data[ndata].base = (opt.sseg << 4) + opt.soff;
- if (opt.file && opt.maps && !no_ov(data + fidx, data + ndata)) {
+ if (opt.file && opt.maps && overlap(data + fidx, data + ndata)) {
error("WARNING: The sector won't be loaded, as it would conflict with the boot file.\n");
} else {
if (!(data[ndata].data = disk_read_sectors(&iter->di, iter->start_lba, 1))) {
@@ -1429,8 +667,8 @@ int main(int argc, char *argv[])
hand_area = (void *)data[ndata].data;
/* Verify possible conflicts */
- if ( ( fidx >= 0 && !no_ov(data + fidx, data + ndata)) ||
- ( sidx >= 0 && opt.maps && !no_ov(data + sidx, data + ndata)) ) {
+ if ( ( fidx >= 0 && overlap(data + fidx, data + ndata)) ||
+ ( sidx >= 0 && opt.maps && overlap(data + sidx, data + ndata)) ) {
error("WARNING: Handover area won't be prepared,\n"
"as it would conflict with the boot file and/or the sector.\n");
} else {
@@ -1470,8 +708,10 @@ int main(int argc, char *argv[])
if (opt.grub && manglef_grub(iter, data + fidx))
goto bail;
+#if 0
if (opt.drmk && manglef_drmk(data + fidx))
goto bail;
+#endif
if (opt.filebpb && manglef_bpb(iter, data + fidx))
goto bail;
diff --git a/com32/chain/chain.h b/com32/chain/chain.h
new file mode 100644
index 00000000..7a63022a
--- /dev/null
+++ b/com32/chain/chain.h
@@ -0,0 +1,50 @@
+#ifndef _COM32_CHAIN_CHAIN_H
+#define _COM32_CHAIN_CHAIN_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;
+ unsigned int drvoff;
+ 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;
+ bool hide;
+ bool sethid;
+ bool setgeo;
+ bool setdrv;
+ bool sect;
+ bool save;
+ bool filebpb;
+ bool warn;
+ uint16_t keeppxe;
+ struct syslinux_rm_regs regs;
+};
+
+struct data_area {
+ void *data;
+ addr_t base;
+ addr_t size;
+};
+
+extern struct options opt;
+
+#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..5aea2f68
--- /dev/null
+++ b/com32/chain/mangle.c
@@ -0,0 +1,318 @@
+#include <com32.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <syslinux/config.h>
+#include "common.h"
+#include "chain.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;
+
+ 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;
+}
+
+/*
+ * GRLDR of GRUB4DOS wants the partition number in DH:
+ * -1: whole drive (default)
+ * 0-3: primary partitions
+ * 4-*: logical partitions
+ */
+int manglef_grldr(const struct part_iter *iter)
+{
+ opt.regs.edx.b[1] = (uint8_t)(iter->index - 1);
+ return 0;
+}
+
+/*
+ * 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 (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;
+}
+
+/*
+ * Adjust BPB of a BPB-compatible file
+ */
+int manglef_bpb(const struct part_iter *iter, struct data_area *data)
+{
+ /* BPB: hidden sectors */
+ if (opt.sethid) {
+ 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: legacy geometry */
+ if (opt.setgeo) {
+ if (iter->di.cbios)
+ *(uint32_t *)((char *)data->data + 0x18) = (uint32_t)((iter->di.head << 16) | iter->di.sect);
+ else {
+ if (iter->di.disk & 0x80)
+ *(uint32_t *)((char *)data->data + 0x18) = 0x00FF003F;
+ else
+ *(uint32_t *)((char *)data->data + 0x18) = 0x00020012;
+ }
+ }
+
+ /* BPB: drive */
+ if (opt.setdrv)
+ *(uint8_t *)((char *)data->data + opt.drvoff) = (uint8_t)
+ (opt.swap ? iter->di.disk & 0x80 : iter->di.disk);
+
+ return 0;
+}
+
+/*
+ * Adjust BPB of a sector
+ */
+int try_mangles_bpb(const struct part_iter *iter, struct data_area *data)
+{
+ void *cmp_buf = NULL;
+
+ if (!(opt.setdrv || opt.setgeo || opt.sethid))
+ return 0;
+
+#if 0
+ /* Turn this off for now. It's hard to find a reason to
+ * BPB-mangle sector 0 of whatever there is, but it's
+ * "potentially" useful (fixing fdd's sector ?).
+ */
+ if (!iter->index)
+ return 0;
+#endif
+
+ if (!(cmp_buf = malloc(data->size))) {
+ error("Could not allocate sector-compare buffer.\n");
+ goto bail;
+ }
+
+ memcpy(cmp_buf, data->data, data->size);
+
+ manglef_bpb(iter, data);
+
+ if (opt.save && memcmp(cmp_buf, data->data, data->size)) {
+ if (disk_write_verify_sector(&iter->di, iter->start_lba, data->data)) {
+ error("Cannot write updated boot sector.\n");
+ goto bail;
+ }
+ }
+
+ free(cmp_buf);
+ 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)
+{
+ memcpy((char *)data->data + 3, cmldr_signature, sizeof(cmldr_signature));
+ return 0;
+}
+
+#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.
+ */
+
+ uint32_t tsize = (data->size + 19) & 0xfffffff0;
+ 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+28] must be 0x0000003f */
+ opt.regs.ds = (uint16_t)((tsize >> 4) + (opt.fseg - 2));
+ /* "Patch" into tail of the new space */
+ *(uint32_t *)((char*)data->data + tsize - 4) = 0x0000003f;
+
+ return 0;
+bail:
+ return -1;
+}
+#endif
+
+/* 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..71a347ae
--- /dev/null
+++ b/com32/chain/mangle.h
@@ -0,0 +1,19 @@
+#ifndef _COM32_CHAIN_MANGLE_H
+#define _COM32_CHAIN_MANGLE_H
+
+#include "chain.h"
+#include "partiter.h"
+
+int manglef_isolinux(struct data_area *data);
+int manglef_grldr(const struct part_iter *iter);
+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 try_mangles_bpb(const struct part_iter *iter, struct data_area *data);
+int mangles_cmldr(struct data_area *data);
+#if 0
+int manglef_drmk(struct data_area *data);
+#endif
+
+#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..efb563d7
--- /dev/null
+++ b/com32/chain/options.c
@@ -0,0 +1,336 @@
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include "common.h"
+#include "chain.h"
+#include "utility.h"
+#include "options.h"
+
+int soi_s2n(char *ptr, unsigned int *seg,
+ unsigned int *off,
+ unsigned int *ip)
+{
+ unsigned int segval = 0, offval = 0, ipval = 0, val;
+ char *p;
+
+ segval = strtoul(ptr, &p, 0);
+ if (*p == ':')
+ offval = strtoul(p+1, &p, 0);
+ if (*p == ':')
+ 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;
+}
+
+void usage(void)
+{
+ static const char *const usage[] = { "\
+Usage:\n\
+ chain.c32 [options]\n\
+ chain.c32 {fd|hd}<disk> [<partition>] [options]\n\
+ chain.c32 mbr{:|=}<id> [<partition>] [options]\n\
+ chain.c32 guid{:|=}<guid> [<partition>] [options]\n\
+ chain.c32 label{:|=}<label> [<partition>] [options]\n\
+ chain.c32 boot{,| }[<partition>] [options]\n\
+ chain.c32 fs [options]\n\
+\nOptions ('no' prefix specify defaulti value):\n\
+ file=<loader> Load and execute file\n\
+ seg=<s[:o[:i]]> Load file at <s:o>, jump to <s:i>\n\
+ nofilebpb Treat file in memory as BPB compatible\n\
+ sect[=<s[:o[:i]]>] Load sector at <s:o>, jump to <s:i>\n\
+ - defaults to 0:0x7C00:0x7C00\n\
+ maps Map loaded sector into real memory\n\
+ nosethid[den] Set BPB's hidden sectors field\n\
+ nosetgeo Set BPB's sectors per track and heads fields\n\
+ nosetdrv[@<off>] Set BPB's drive unit field at <o>\n\
+ - <off> defaults to autodetection\n\
+ - only 0x24 and 0x40 are accepted\n\
+ nosetbpb Enable set{hid,geo,drv}\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 Hide primary partitions, unhide selected partition\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\
+", "\
+\nComposite options:\n\
+ isolinux=<loader> Load another version of ISOLINUX\n\
+ ntldr=<loader> Load Windows NTLDR, SETUPLDR.BIN or BOOTMGR\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\
+\nPlease see doc/chain.txt for the detailed documentation.\n"
+ };
+ error(usage[0]);
+ error("Press any key...\n");
+ wait_key();
+ error(usage[1]);
+}
+
+int 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))
+ goto bail;
+ } 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.sethid = true;
+ opt.setgeo = true;
+ opt.setdrv = true;
+ opt.drvoff = 0x24;
+ /* 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.sethid = true;
+ opt.setgeo = true;
+ opt.setdrv = true;
+ opt.drvoff = 0x24;
+ /* 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 = 0x9000;
+ opt.soff = 0;
+ opt.sip = 0;
+ opt.file = argv[i] + 8;
+ opt.sethid = true;
+ opt.setgeo = true;
+ opt.setdrv = true;
+ opt.drvoff = ~0u;
+ /* 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 = 0x9000;
+ opt.soff = 0;
+ opt.sip = 0;
+ opt.file = argv[i] + v;
+ opt.sethid = true;
+ opt.setgeo = true;
+ opt.setdrv = true;
+ opt.drvoff = ~0u;
+ /* 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.sethid = true;
+ opt.setgeo = true;
+ opt.setdrv = true;
+ opt.drvoff = ~0u;
+ /* 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], "hide")) {
+ opt.hide = true;
+ } else if (!strcmp(argv[i], "nohide")) {
+ opt.hide = false;
+ } else if (!strcmp(argv[i], "sethid") ||
+ !strcmp(argv[i], "sethidden")) {
+ opt.sethid = true;
+ } else if (!strcmp(argv[i], "nosethid") ||
+ !strcmp(argv[i], "nosethidden")) {
+ opt.sethid = false;
+ } else if (!strcmp(argv[i], "setgeo")) {
+ opt.setgeo = true;
+ } else if (!strcmp(argv[i], "nosetgeo")) {
+ opt.setgeo = false;
+ } else if (!strncmp(argv[i], "setdrv",6)) {
+ if (!argv[i][6])
+ v = ~0u; /* autodetect */
+ else if (argv[i][6] == '@' ||
+ argv[i][6] == '=' ||
+ argv[i][6] == ':') {
+ v = strtoul(argv[i] + 7, NULL, 0);
+ if (!(v == 0x24 || v == 0x40)) {
+ error("Invalid 'setdrv' offset.\n");
+ goto bail;
+ }
+ } else {
+ error("Invalid 'setdrv' specification.\n");
+ goto bail;
+ }
+ opt.setdrv = true;
+ opt.drvoff = v;
+ } else if (!strcmp(argv[i], "nosetdrv")) {
+ opt.setdrv = false;
+ } else if (!strcmp(argv[i], "setbpb")) {
+ opt.setdrv = true;
+ opt.drvoff = ~0u;
+ opt.setgeo = true;
+ opt.sethid = true;
+ } else if (!strcmp(argv[i], "nosetbpb")) {
+ opt.setdrv = false;
+ opt.setgeo = false;
+ opt.sethid = 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))
+ goto bail;
+ if ((opt.sseg << 4) + opt.soff + SECTOR - 1 > ADDRMAX) {
+ error("Arguments of 'sect=' are invalid - resulting address too big.\n");
+ goto bail;
+ }
+ }
+ opt.sect = true;
+ } else if (!strcmp(argv[i], "nosect")) {
+ opt.sect = false;
+ } else if (!strcmp(argv[i], "save")) {
+ opt.save = true;
+ } else if (!strcmp(argv[i], "nosave")) {
+ opt.save = false;
+ } else if (!strcmp(argv[i], "filebpb")) {
+ opt.filebpb = true;
+ } else if (!strcmp(argv[i], "nofilebpb")) {
+ opt.filebpb = false;
+ } else if (!strcmp(argv[i], "warn")) {
+ opt.warn = true;
+ } else if (!strcmp(argv[i], "nowarn")) {
+ opt.warn = 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];
+ p = strchr(opt.drivename, ',');
+ 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 ((!opt.maps || !opt.sect) && !opt.file) {
+ error("You have to load something.\n");
+ goto bail;
+ }
+
+ if (opt.filebpb && !opt.file) {
+ error("Option 'filebpb' requires file.\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..e400eca5
--- /dev/null
+++ b/com32/chain/options.h
@@ -0,0 +1,10 @@
+#ifndef _COM32_CHAIN_OPTIONS_H
+#define _COM32_CHAIN_OPTIONS_H
+
+int soi_s2n(char *ptr, unsigned int *seg, unsigned int *off, unsigned int *ip);
+void usage(void);
+int 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
index 6934e8df..2859694a 100644
--- a/com32/chain/partiter.c
+++ b/com32/chain/partiter.c
@@ -39,7 +39,9 @@
#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)
@@ -48,11 +50,6 @@
/* this is chosen to follow how many sectors disklib can read at once */
#define MAXGPTPTSIZE (255u*SECTOR)
-static void error(const char *msg)
-{
- fputs(msg, stderr);
-}
-
/* forwards */
static int iter_ctor(struct part_iter *, va_list *);
@@ -94,11 +91,6 @@ static int inv_type(const void *type)
}
#endif
-static int guid_is0(const struct guid *guid)
-{
- return !*(const uint64_t *)guid && !*((const uint64_t *)guid+1);
-}
-
/**
* iter_ctor() - common iterator initialization
* @iter: iterator pointer
diff --git a/com32/chain/partiter.h b/com32/chain/partiter.h
index f32543e6..d00d6099 100644
--- a/com32/chain/partiter.h
+++ b/com32/chain/partiter.h
@@ -1,8 +1,8 @@
/* ----------------------------------------------------------------------- *
*
* Copyright 2003-2010 H. Peter Anvin - All Rights Reserved
- * Copyright 2010 Shao Miller
* 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
@@ -33,8 +33,8 @@
* Provides disk / partition iteration.
*/
-#ifndef _SYSLINUX_PARTITER_H
-#define _SYSLINUX_PARTITER_H
+#ifndef _COM32_CHAIN_PARTITER_H
+#define _COM32_CHAIN_PARTITER_H
#include <stdint.h>
#include <syslinux/disk.h>
diff --git a/com32/chain/utility.c b/com32/chain/utility.c
new file mode 100644
index 00000000..9b978a61
--- /dev/null
+++ b/com32/chain/utility.c
@@ -0,0 +1,114 @@
+#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"
+
+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));
+}
+
+uint32_t lba2chs(const struct disk_info *di, uint64_t lba)
+{
+ uint32_t c, h, s, t;
+
+ if (di->cbios) {
+ if (lba >= di->cyl * di->head * di->sect) {
+ s = di->sect;
+ h = di->head - 1;
+ c = di->cyl - 1;
+ goto out;
+ }
+ s = ((uint32_t)lba % di->sect) + 1;
+ t = (uint32_t)lba / di->sect;
+ h = t % di->head;
+ c = t / di->head;
+ } else
+ goto fallback;
+
+out:
+ return h | (s << 8) | ((c & 0x300) << 6) | ((c & 0xFF) << 16);
+
+fallback:
+ if (di->disk & 0x80)
+ return 0x00FFFFFE; /* 1023/63/254 */
+ else
+ /*
+ * adjust ?
+ * this is somewhat "useful" with partitioned floppy,
+ * maybe stick to 2.88mb ?
+ */
+ return 0x004F1201; /* 79/18/1 */
+#if 0
+ return 0x004F2401; /* 79/36/1 */
+#endif
+}
+
+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;
+}
+
diff --git a/com32/chain/utility.h b/com32/chain/utility.h
new file mode 100644
index 00000000..b77f633b
--- /dev/null
+++ b/com32/chain/utility.h
@@ -0,0 +1,15 @@
+#ifndef _COM32_CHAIN_UTILITY_H
+#define _COM32_CHAIN_UTILITY_H
+
+#include <stdint.h>
+#include <syslinux/disk.h>
+
+void error(const char *msg);
+int guid_is0(const struct guid *guid);
+void wait_key(void);
+uint32_t lba2chs(const struct disk_info *di, uint64_t lba);
+uint32_t get_file_lba(const char *filename);
+
+#endif
+
+/* vim: set ts=8 sts=4 sw=4 noet: */