diff options
author | Michal Soltys <soltys@ziu.info> | 2010-08-22 13:44:01 +0200 |
---|---|---|
committer | Michal Soltys <soltys@ziu.info> | 2010-09-28 09:32:52 +0200 |
commit | b4ba30102958f77e6bef1354459a06416408c2d7 (patch) | |
tree | 44f46a9f9c3fcd504daa32f1ddc44a2dbf8c3414 /com32/chain | |
parent | e0b353a198457ada5f2579fda0a77e1853137bd7 (diff) | |
download | syslinux-b4ba30102958f77e6bef1354459a06416408c2d7.tar.gz |
chain.c: Split chain into smaller files
new file: chain.h
new file: common.h
new file: mangle.c
new file: mangle.h
new file: options.c
new file: options.h
new file: utility.c
new file: utility.h
Signed-off-by: Michal Soltys <soltys@ziu.info>
Diffstat (limited to 'com32/chain')
-rw-r--r-- | com32/chain/Makefile | 2 | ||||
-rw-r--r-- | com32/chain/chain.c | 788 | ||||
-rw-r--r-- | com32/chain/chain.h | 50 | ||||
-rw-r--r-- | com32/chain/common.h | 9 | ||||
-rw-r--r-- | com32/chain/mangle.c | 318 | ||||
-rw-r--r-- | com32/chain/mangle.h | 19 | ||||
-rw-r--r-- | com32/chain/options.c | 336 | ||||
-rw-r--r-- | com32/chain/options.h | 10 | ||||
-rw-r--r-- | com32/chain/partiter.c | 12 | ||||
-rw-r--r-- | com32/chain/partiter.h | 6 | ||||
-rw-r--r-- | com32/chain/utility.c | 114 | ||||
-rw-r--r-- | com32/chain/utility.h | 15 |
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: */ |