Merge branch 'master' into chaindev
authorMichal Soltys <soltys@ziu.info>
Mon, 25 Oct 2010 08:27:23 +0000 (10:27 +0200)
committerMichal Soltys <soltys@ziu.info>
Mon, 25 Oct 2010 08:27:23 +0000 (10:27 +0200)
Conflicts:
com32/modules/chain.c

Changes to DRMK imported into commented-out section
of com32/chain/mangle.c

Signed-off-by: Michal Soltys <soltys@ziu.info>
1  2 
com32/chain/mangle.c

index b7645cc,0000000..e4d4be3
mode 100644,000000..100644
--- /dev/null
@@@ -1,541 -1,0 +1,558 @@@
-     /* ds:[bp+28] must be 0x0000003f */
 +#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 "options.h"
 +#include "utility.h"
 +#include "partiter.h"
 +#include "mangle.h"
 +
 +static const char cmldr_signature[8] = "cmdcons";
 +
 +/* Create boot info table: needed when you want to chainload
 + * another version of ISOLINUX (or another bootlaoder that needs
 + * the -boot-info-table switch of mkisofs)
 + * (will only work when run from ISOLINUX)
 + */
 +int manglef_isolinux(struct data_area *data)
 +{
 +    const union syslinux_derivative_info *sdi;
 +    unsigned char *isolinux_bin;
 +    uint32_t *checksum, *chkhead, *chktail;
 +    uint32_t file_lba = 0;
 +
 +    if (!(opt.file && opt.isolinux))
 +      return 0;
 +
 +    sdi = syslinux_derivative_info();
 +
 +    if (sdi->c.filesystem != SYSLINUX_FS_ISOLINUX) {
 +      error ("The isolinux= option is only valid when run from ISOLINUX.\n");
 +      goto bail;
 +    }
 +
 +    /* Boot info table info (integers in little endian format)
 +
 +       Offset Name         Size      Meaning
 +       8      bi_pvd       4 bytes   LBA of primary volume descriptor
 +       12     bi_file      4 bytes   LBA of boot file
 +       16     bi_length    4 bytes   Boot file length in bytes
 +       20     bi_csum      4 bytes   32-bit checksum
 +       24     bi_reserved  40 bytes  Reserved
 +
 +       The 32-bit checksum is the sum of all the 32-bit words in the
 +       boot file starting at byte offset 64. All linear block
 +       addresses (LBAs) are given in CD sectors (normally 2048 bytes).
 +
 +       LBA of primary volume descriptor should already be set to 16.
 +       */
 +
 +    isolinux_bin = (unsigned char *)data->data;
 +
 +    /* Get LBA address of bootfile */
 +    file_lba = get_file_lba(opt.file);
 +
 +    if (file_lba == 0) {
 +      error("Failed to find LBA offset of the boot file\n");
 +      goto bail;
 +    }
 +    /* Set it */
 +    *((uint32_t *) & isolinux_bin[12]) = file_lba;
 +
 +    /* Set boot file length */
 +    *((uint32_t *) & isolinux_bin[16]) = data->size;
 +
 +    /* Calculate checksum */
 +    checksum = (uint32_t *) & isolinux_bin[20];
 +    chkhead = (uint32_t *) & isolinux_bin[64];
 +    chktail = (uint32_t *) & isolinux_bin[data->size & ~3u];
 +    *checksum = 0;
 +    while (chkhead < chktail)
 +      *checksum += *chkhead++;
 +
 +    /*
 +     * Deal with possible fractional dword at the end;
 +     * this *should* never happen...
 +     */
 +    if (data->size & 3) {
 +      uint32_t xword = 0;
 +      memcpy(&xword, chkhead, data->size & 3);
 +      *checksum += xword;
 +    }
 +    return 0;
 +bail:
 +    return -1;
 +}
 +
 +/*
 + * Legacy grub's stage2 chainloading
 + */
 +int manglef_grub(const struct part_iter *iter, struct data_area *data)
 +{
 +    /* Layout of stage2 file (from byte 0x0 to 0x270) */
 +    struct grub_stage2_patch_area {
 +      /* 0x0 to 0x205 */
 +      char unknown[0x206];
 +      /* 0x206: compatibility version number major */
 +      uint8_t compat_version_major;
 +      /* 0x207: compatibility version number minor */
 +      uint8_t compat_version_minor;
 +
 +      /* 0x208: install_partition variable */
 +      struct {
 +          /* 0x208: sub-partition in sub-partition part2 */
 +          uint8_t part3;
 +          /* 0x209: sub-partition in top-level partition */
 +          uint8_t part2;
 +          /* 0x20a: top-level partiton number */
 +          uint8_t part1;
 +          /* 0x20b: BIOS drive number (must be 0) */
 +          uint8_t drive;
 +      } __attribute__ ((packed)) install_partition;
 +
 +      /* 0x20c: deprecated (historical reason only) */
 +      uint32_t saved_entryno;
 +      /* 0x210: stage2_ID: will always be STAGE2_ID_STAGE2 = 0 in stage2 */
 +      uint8_t stage2_id;
 +      /* 0x211: force LBA */
 +      uint8_t force_lba;
 +      /* 0x212: version string (will probably be 0.97) */
 +      char version_string[5];
 +      /* 0x217: config filename */
 +      char config_file[89];
 +      /* 0x270: start of code (after jump from 0x200) */
 +      char codestart[1];
 +    } __attribute__ ((packed)) *stage2;
 +
 +    if (!(opt.file && opt.grub))
 +      return 0;
 +
 +    if (data->size < sizeof(struct grub_stage2_patch_area)) {
 +      error("The file specified by grub=<loader> is too small to be stage2 of GRUB Legacy.\n");
 +      goto bail;
 +    }
 +    stage2 = data->data;
 +
 +    /*
 +     * Check the compatibility version number to see if we loaded a real
 +     * stage2 file or a stage2 file that we support.
 +     */
 +    if (stage2->compat_version_major != 3
 +          || stage2->compat_version_minor != 2) {
 +      error("The file specified by grub=<loader> is not a supported stage2 GRUB Legacy binary.\n");
 +      goto bail;
 +    }
 +
 +    /*
 +     * GRUB Legacy wants the partition number in the install_partition
 +     * variable, located at offset 0x208 of stage2.
 +     * When GRUB Legacy is loaded, it is located at memory address 0x8208.
 +     *
 +     * It looks very similar to the "boot information format" of the
 +     * Multiboot specification:
 +     *   http://www.gnu.org/software/grub/manual/multiboot/multiboot.html#Boot-information-format
 +     *
 +     *   0x208 = part3: sub-partition in sub-partition part2
 +     *   0x209 = part2: sub-partition in top-level partition
 +     *   0x20a = part1: top-level partition number
 +     *   0x20b = drive: BIOS drive number (must be 0)
 +     *
 +     * GRUB Legacy doesn't store the BIOS drive number at 0x20b, but at
 +     * another location.
 +     *
 +     * Partition numbers always start from zero.
 +     * Unused partition bytes must be set to 0xFF.
 +     *
 +     * We only care about top-level partition, so we only need to change
 +     * "part1" to the appropriate value:
 +     *   -1:   whole drive (default) (-1 = 0xFF)
 +     *   0-3:  primary partitions
 +     *   4-*:  logical partitions
 +     */
 +    stage2->install_partition.part1 = (uint8_t)(iter->index - 1);
 +
 +    /*
 +     * Grub Legacy reserves 89 bytes (from 0x8217 to 0x826f) for the
 +     * config filename. The filename passed via grubcfg= will overwrite
 +     * the default config filename "/boot/grub/menu.lst".
 +     */
 +    if (opt.grubcfg) {
 +      if (strlen(opt.grubcfg) > sizeof(stage2->config_file) - 1) {
 +          error ("The config filename length can't exceed 88 characters.\n");
 +          goto bail;
 +      }
 +
 +      strcpy((char *)stage2->config_file, opt.grubcfg);
 +    }
 +
 +    return 0;
 +bail:
 +    return -1;
 +}
 +#if 0
 +/*
 + * Dell's DRMK chainloading.
 + */
 +int manglef_drmk(struct data_area *data)
 +{
 +    /*
 +     * DRMK entry is different than MS-DOS/PC-DOS
 +     * A new size, aligned to 16 bytes to ease use of ds:[bp+28].
 +     * We only really need 4 new, usable bytes at the end.
 +     */
 +
 +    if (!(opt.file && opt.drmk))
 +      return 0;
 +
 +    uint32_t tsize = (data->size + 19) & 0xfffffff0;
++    const union syslinux_derivative_info *sdi;
++    uint64_t fs_lba;
++
++    sdi = syslinux_derivative_info();
++    /* We should lookup the Syslinux partition offset and use it */
++    fs_lba = *sdi->disk.partoffset;
++
++    /*
++     * fs_lba should be verified against the disk as some DRMK
++     * variants will check and fail if it does not match
++     */
++    dprintf("  fs_lba offset is %d\n", fs_lba);
++    /* DRMK only uses a DWORD */
++    if (fs_lba > 0xffffffff) {
++      error("LBA very large; Only using lower 32 bits; DRMK will probably fail\n");
++    }
 +    opt.regs.ss = opt.regs.fs = opt.regs.gs = 0;      /* Used before initialized */
 +    if (!realloc(data->data, tsize)) {
 +      error("Failed to realloc for DRMK.\n");
 +      goto bail;
 +    }
 +    data->size = tsize;
-     *(uint32_t *)((char*)data->data + tsize - 4) = 0x0000003f;
++    /* ds:bp is assumed by DRMK to be the boot sector */
++    /* offset 28 is the FAT HiddenSectors value */
 +    opt.regs.ds = (uint16_t)((tsize >> 4) + (opt.fseg - 2));
 +    /* "Patch" into tail of the new space */
++    *(uint32_t *)((char*)data->data + tsize - 4) = (uint32_t)fs_lba;
 +
 +    return 0;
 +bail:
 +    return -1;
 +}
 +#endif
 +/* Adjust BPB common function */
 +static int mangle_bpb(const struct part_iter *iter, struct data_area *data)
 +{
 +    unsigned int off;
 +    int type = bpb_detect(data->data);
 +
 +    /* BPB: hidden sectors 32bit*/
 +    if (type >= bpbV34) {
 +      if (iter->start_lba < ~0u)
 +          *(uint32_t *) ((char *)data->data + 0x1c) = (uint32_t)iter->start_lba;
 +      else
 +          /* won't really help much, but ... */
 +          *(uint32_t *) ((char *)data->data + 0x1c) = ~0u;
 +    }
 +    /* BPB: hidden sectors 16bit*/
 +    if (bpbV30 <= type && type <= bpbV32) {
 +      if (iter->start_lba < 0xFFFF)
 +          *(uint16_t *) ((char *)data->data + 0x1c) = (uint16_t)iter->start_lba;
 +      else
 +          /* won't really help much, but ... */
 +          *(uint16_t *) ((char *)data->data + 0x1c) = (uint16_t)~0u;
 +    }
 +    /* BPB: legacy geometry */
 +    if (type >= bpbV30) {
 +      if (iter->di.cbios)
 +          *(uint32_t *)((char *)data->data + 0x18) = (uint32_t)((iter->di.head << 16) | iter->di.spt);
 +      else {
 +          if (iter->di.disk & 0x80)
 +              *(uint32_t *)((char *)data->data + 0x18) = 0x00FF003F;
 +          else
 +              *(uint32_t *)((char *)data->data + 0x18) = 0x00020012;
 +      }
 +    }
 +    /* BPB: drive */
 +    if (drvoff_detect(type, &off)) {
 +      *(uint8_t *)((char *)data->data + off) = (uint8_t)
 +          (opt.swap ? iter->di.disk & 0x80 : iter->di.disk);
 +    }
 +
 +    return 0;
 +}
 +
 +/*
 + * Adjust BPB of a BPB-compatible file
 + */
 +int manglef_bpb(const struct part_iter *iter, struct data_area *data)
 +{
 +    if (!(opt.file && opt.filebpb))
 +      return 0;
 +
 +    return mangle_bpb(iter, data);
 +}
 +
 +/*
 + * Adjust BPB of a sector
 + */
 +int mangles_bpb(const struct part_iter *iter, struct data_area *data)
 +{
 +    if (!(opt.sect && opt.setbpb))
 +      return 0;
 +
 +    return mangle_bpb(iter, data);
 +}
 +
 +/*
 + * This function performs full BPB patching, analogously to syslinux's
 + * native BSS.
 + */
 +int manglesf_bss(struct data_area *sec, struct data_area *fil)
 +{
 +    int type1, type2;
 +    unsigned int cnt = 0;
 +
 +    if (!(opt.sect && opt.file && opt.bss))
 +      return 0;
 +
 +    type1 = bpb_detect(fil->data);
 +    type2 = bpb_detect(sec->data);
 +
 +    if (!type1 || !type2) {
 +      error("Couldn't determine the BPB type for option 'bss'.\n");
 +      goto bail;
 +    }
 +    if (type1 != type2) {
 +      error("Option 'bss' can't be used,\n"
 +              "when a sector and a file have incompatible BPBs.\n");
 +      goto bail;
 +    }
 +
 +    /* Copy common 2.0 data */
 +    memcpy((char *)fil->data + 0x0B, (char *)sec->data + 0x0B, 0x0D);
 +
 +    /* Copy 3.0+ data */
 +    if (type1 <= bpbV30) {
 +      cnt = 0x06;
 +    } else if (type1 <= bpbV32) {
 +      cnt = 0x08;
 +    } else if (type1 <= bpbV34) {
 +      cnt = 0x0C;
 +    } else if (type1 <= bpbV40) {
 +      cnt = 0x2E;
 +    } else if (type1 <= bpbVNT) {
 +      cnt = 0x3C;
 +    } else if (type1 <= bpbV70) {
 +      cnt = 0x42;
 +    }
 +    memcpy((char *)fil->data + 0x18, (char *)sec->data + 0x18, cnt);
 +
 +    return 0;
 +bail:
 +    return -1;
 +}
 +
 +/*
 + * Save sector.
 + */
 +int mangles_save(const struct part_iter *iter, const struct data_area *data, void *org)
 +{
 +    if (!(opt.sect && opt.save))
 +      return 0;
 +
 +    if (memcmp(org, data->data, data->size)) {
 +      if (disk_write_sectors(&iter->di, iter->start_lba, data->data, 1)) {
 +          error("Cannot write the updated sector.\n");
 +          goto bail;
 +      }
 +      /* function can be called again */
 +      memcpy(org, data->data, data->size);
 +    }
 +
 +    return 0;
 +bail:
 +    return -1;
 +}
 +
 +/*
 + * To boot the Recovery Console of Windows NT/2K/XP we need to write
 + * the string "cmdcons\0" to memory location 0000:7C03.
 + * Memory location 0000:7C00 contains the bootsector of the partition.
 + */
 +int mangles_cmldr(struct data_area *data)
 +{
 +    if (!(opt.sect && opt.cmldr))
 +      return 0;
 +
 +    memcpy((char *)data->data + 3, cmldr_signature, sizeof(cmldr_signature));
 +    return 0;
 +}
 +
 +/* Set common registers */
 +int mangler_common(const struct part_iter *iter)
 +{
 +    /* Set initial registry values */
 +    if (opt.file) {
 +      opt.regs.cs = opt.regs.ds = opt.regs.ss = (uint16_t)opt.fseg;
 +      opt.regs.ip = (uint16_t)opt.fip;
 +    } else {
 +      opt.regs.cs = opt.regs.ds = opt.regs.ss = (uint16_t)opt.sseg;
 +      opt.regs.ip = (uint16_t)opt.sip;
 +    }
 +
 +    if (opt.regs.ip == 0x7C00 && !opt.regs.cs)
 +      opt.regs.esp.l = 0x7C00;
 +
 +    /* DOS kernels want the drive number in BL instead of DL. Indulge them. */
 +    opt.regs.ebx.b[0] = opt.regs.edx.b[0] = (uint8_t)iter->di.disk;
 +
 +    return 0;
 +}
 +
 +/* ds:si & ds:bp */
 +int mangler_handover(const struct part_iter *iter, const struct data_area *data)
 +{
 +    if (opt.sect && opt.file && opt.maps && !opt.hptr) {
 +      opt.regs.esi.l = opt.regs.ebp.l = opt.soff;
 +      opt.regs.ds = (uint16_t)opt.sseg;
 +      opt.regs.eax.l = 0;
 +    } else if (opt.hand) {
 +      /* base is really 0x7be */
 +      opt.regs.esi.l = opt.regs.ebp.l = data->base;
 +      opt.regs.ds = 0;
 +      if (iter->type == typegpt)
 +          opt.regs.eax.l = 0x54504721;        /* '!GPT' */
 +      else
 +          opt.regs.eax.l = 0;
 +    }
 +
 +    return 0;
 +}
 +
 +/*
 + * GRLDR of GRUB4DOS wants the partition number in DH:
 + * -1:   whole drive (default)
 + * 0-3:  primary partitions
 + * 4-*:  logical partitions
 + */
 +int mangler_grldr(const struct part_iter *iter)
 +{
 +    if (opt.grldr)
 +      opt.regs.edx.b[1] = (uint8_t)(iter->index - 1);
 +
 +    return 0;
 +}
 +
 +static int mpe_sethide(struct part_iter *miter, struct part_iter *iter)
 +{
 +    struct disk_dos_part_entry *mdp, *dp;
 +    static const uint16_t mask =
 +      (1 << 0x01) | (1 << 0x04) | (1 << 0x06) |
 +      (1 << 0x07) | (1 << 0x0b) | (1 << 0x0c) | (1 << 0x0e);
 +    uint8_t t;
 +
 +    mdp = (struct disk_dos_part_entry *)miter->record;
 +    dp = (struct disk_dos_part_entry *)iter->record;
 +    t = dp->ostype;
 +
 +    if ((t <= 0x1f) && ((mask >> (t & ~0x10u)) & 1)) {
 +      /* It's a hideable partition type */
 +      if (miter->index == iter->index || opt.hide & 4)
 +          t &= (uint8_t)(~0x10u);     /* unhide */
 +      else
 +          t |= 0x10u; /* hide */
 +    }
 +    if (dp->ostype != t) {
 +      dp->ostype = t;
 +      /*
 +       * the type of the partition being booted has to be adjusted in
 +       * the match iterator (miter) as well
 +       */
 +      if (miter->index == iter->index) {
 +          mdp->ostype = t;
 +      }
 +      return -1;
 +    }
 +    return 0;
 +}
 +
 +static int mpe_setchs(const struct disk_info *di,
 +                   struct disk_dos_part_entry *dp,
 +                   uint32_t lba1)
 +{
 +    uint32_t ochs1, ochs2;
 +
 +    ochs1 = *(uint32_t *)dp->start;
 +    ochs2 = *(uint32_t *)dp->end;
 +
 +    *(uint32_t *)dp->start =
 +      lba2chs(di, lba1, l2c_cadd) |
 +      (*(uint32_t *)dp->start & 0xFF000000);
 +
 +    *(uint32_t *)dp->end =
 +      lba2chs(di, lba1 + dp->length - 1, l2c_cadd) |
 +      (*(uint32_t *)dp->end & 0xFF000000);
 +
 +    return
 +      *(uint32_t *)dp->start != ochs1 ||
 +      *(uint32_t *)dp->end != ochs2;
 +}
 +
 +int manglepe_mbrchshide(struct part_iter *miter)
 +{
 +    int wb = 0, werr = 0;
 +    struct part_iter *iter = NULL;
 +    struct disk_dos_part_entry *dp;
 +    int ridx;
 +
 +    if (!(opt.mbrchs || opt.hide))
 +      return 0;
 +
 +    if (miter->type != typedos) {
 +      error("Partition entry mangling ('[un]hide[all]', 'mbrchs')\n"
 +            "is meaningful only for legacy partition scheme.\n");
 +      return -1;
 +    }
 +    if (opt.hide &&
 +          ((miter->index < 1 && opt.hide < 4) || /* try to hide a disk */
 +           (miter->index > 4 && opt.hide == 1))) /* try to hide a part when limited to pri */
 +      error("WARNING: It's impossible to hide the selected partition (or you selected a disk).\n");
 +
 +    if (!(iter = pi_begin(&miter->di, 1)))  /* turn on stepall */
 +      return -1;
 +
 +    while (!pi_next(&iter) && !werr && (opt.hide & 2 || opt.mbrchs)) {
 +      ridx = iter->rawindex;
 +      dp = (struct disk_dos_part_entry *)iter->record;
 +
 +      if (dp->ostype) {
 +          if (opt.hide & 2 || (opt.hide & 1 && ridx <= 4)) {
 +              wb |= mpe_sethide(miter, iter);
 +          }
 +          if (opt.mbrchs) {
 +              wb |= mpe_setchs(&iter->di, dp, (uint32_t)iter->start_lba);
 +              if (ridx > 4)
 +                  wb |= mpe_setchs(&iter->di, dp + 1, iter->sub.dos.nebr_lba);
 +          }
 +      }
 +
 +      if (ridx >= 4 && wb && !werr) {
 +          werr |= disk_write_sectors(&iter->di, iter->sub.dos.cebr_lba, iter->data, 1);
 +          wb = 0;
 +      }
 +    }
 +    /* last write */
 +    if (wb && !werr)
 +      werr |= disk_write_sectors(&miter->di, iter->sub.dos.cebr_lba, iter->data, 1);
 +
 +    pi_del(&iter);
 +    if (werr)
 +      error("WARNING: failed to write E/MBR for partition\n"
 +            "mangling options ('[un]hide[all]', 'mbrchs').\n");
 +    return 0;
 +}
 +
 +/* vim: set ts=8 sts=4 sw=4 noet: */