From: Michal Soltys Date: Sun, 15 Aug 2010 21:00:59 +0000 (+0200) Subject: chain.c: move file mangling outside main() X-Git-Tag: syslinux-4.06-pre3~3^2~77 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=2905564db097f2fbf5f5fb28fdc059f2eaa9378e;p=profile%2Fivi%2Fsyslinux.git chain.c: move file mangling outside main() All the file mangling code has been moved to mangle_*() family of functions, currently: mangle_isolinux() mangle_grublegacy() mangle_grldr() mangle_drmk() They will be further moved to separate file. Also, file and sector loading routines are now near each other. Signed-off-by: Michal Soltys --- diff --git a/com32/chain/chain.c b/com32/chain/chain.c index 6fcdf03..c7f7521 100644 --- a/com32/chain/chain.c +++ b/com32/chain/chain.c @@ -673,6 +673,223 @@ 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 mangle_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.loadfile); + + 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 & ~3]; + *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 mangle_grldr(const struct part_iter *_iter, struct syslinux_rm_regs *_regs) +{ + _regs->edx.b[1] = _iter->index - 1; + return 0; +} + +/* + * Legacy grub's stage2 chainloading + */ +static int mangle_grublegacy(const struct part_iter *_iter, struct data_area *_data, struct syslinux_rm_regs *_regs) +{ + /* 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= 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= is not a supported stage2 GRUB Legacy binary.\n"); + goto bail; + } + + /* jump 0x200 bytes into the loadfile */ + _regs->ip = 0x200; + + /* + * 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 = _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 mangle_drmk(struct data_area *_data, struct syslinux_rm_regs *_regs) +{ + /* + * 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. + */ + + int tsize = (_data->size + 19) & 0xfffffff0; + _regs->ss = _regs->fs = _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 */ + _regs->ds = (tsize >> 4) + (opt.seg - 2); + /* "Patch" into tail of the new space */ + *(int *)(_data->data + tsize - 4) = 0x0000003f; + + return 0; +bail: + return -1; +} int main(int argc, char *argv[]) { @@ -684,7 +901,7 @@ int main(int argc, char *argv[]) struct syslinux_rm_regs regs; struct data_area data[3]; - int ndata = 0; + int ndata = 0, fidx = -1, sidx = -1; addr_t load_base; openconsole(&dev_null_r, &dev_stdcon_w); @@ -707,268 +924,83 @@ int main(int argc, char *argv[]) } /* Get disk/part iterator matching user supplied options */ - if(find_dp(&cur_part)) goto bail; /* DOS kernels want the drive number in BL instead of DL. Indulge them. */ regs.ebx.b[0] = regs.edx.b[0] = cur_part->di.disk; + /* Do hide / unhide if appropriate */ if (opt.hide) hide_unhide(cur_part); - /* Do the actual chainloading */ + /* Load file and bs/mbr */ + load_base = opt.seg ? (opt.seg << 4) : 0x7c00; if (opt.loadfile) { fputs("Loading the boot file...\n", stdout); if (loadfile(opt.loadfile, &data[ndata].data, &data[ndata].size)) { - error("Failed to load the boot file.\n"); + error("Couldn't read the boot file.\n"); goto bail; } - data[ndata].base = load_base; file_area = (void *)data[ndata].data; + data[ndata].base = load_base; load_base = 0x7c00; /* If we also load a boot sector */ + fidx = ndata; + ndata++; + } - /* 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) */ - if (opt.isolinux) { - const union syslinux_derivative_info *sdi; - sdi = syslinux_derivative_info(); - - if (sdi->c.filesystem == SYSLINUX_FS_ISOLINUX) { - unsigned char *isolinux_bin; - uint32_t *checksum, *chkhead, *chktail; - uint32_t file_lba = 0; - /* 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[ndata].data; - - /* Get LBA address of bootfile */ - file_lba = get_file_lba(opt.loadfile); - - 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[ndata].size; - - /* Calculate checksum */ - checksum = (uint32_t *) & isolinux_bin[20]; - chkhead = (uint32_t *) & isolinux_bin[64]; - chktail = (uint32_t *) & isolinux_bin[data[ndata].size & ~3]; - *checksum = 0; - while (chkhead < chktail) - *checksum += *chkhead++; - - /* - * Deal with possible fractional dword at the end; - * this *should* never happen... - */ - if (data[ndata].size & 3) { - uint32_t xword = 0; - memcpy(&xword, chkhead, data[ndata].size & 3); - *checksum += xword; - } - } else { - error - ("The isolinux= option is only valid when run from ISOLINUX\n"); - goto bail; - } + if (!opt.loadfile || data[0].base >= 0x7c00 + SECTOR) { + if (!(data[ndata].data = disk_read_sectors(&cur_part->di, cur_part->start_lba, 1))) { + error("Couldn't read the boot sector or mbr.\n"); + goto bail; } - /* - * GRLDR of GRUB4DOS wants the partition number in DH: - * -1: whole drive (default) - * 0-3: primary partitions - * 4-*: logical partitions - */ - if (opt.grldr) - regs.edx.b[1] = cur_part->index - 1; - - /* - * Legacy grub's stage2 chainloading - */ - if (opt.grub) { - /* 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[ndata].size < sizeof(struct grub_stage2_patch_area)) { - error - ("The file specified by grub= is too small to be stage2 of GRUB Legacy.\n"); - goto bail; - } - - stage2 = data[ndata].data; + sect_area = (void *)data[ndata].data; + data[ndata].base = load_base; + data[ndata].size = SECTOR; + sidx = ndata; + ndata++; + } - /* - * 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= is not a supported stage2 GRUB Legacy binary\n"); - goto bail; - } + /* Mangle file area */ - /* jump 0x200 bytes into the loadfile */ - regs.ip = 0x200; - - /* - * 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 = cur_part->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); - } - } + if (opt.isolinux && mangle_isolinux(data + fidx)) + goto bail; - /* - * Dell's DRMK chainloading. - */ - if (opt.drmk) { - /* 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. - */ - int tsize = (data[ndata].size + 19) & 0xfffffff0; - regs.ss = regs.fs = regs.gs = 0; /* Used before initialized */ - if (!realloc(data[ndata].data, tsize)) { - error("Failed to realloc for DRMK\n"); - goto bail; - } - data[ndata].size = tsize; - /* ds:[bp+28] must be 0x0000003f */ - regs.ds = (tsize >> 4) + (opt.seg - 2); - /* "Patch" into tail of the new space */ - *(int *)(data[ndata].data + tsize - 4) = 0x0000003f; - } + if (opt.grldr && mangle_grldr(cur_part, ®s)) + goto bail; - ndata++; - } + if (opt.grub && mangle_grublegacy(cur_part, data + fidx, ®s)) + goto bail; - if (!opt.loadfile || data[0].base >= 0x7c00 + SECTOR) { - /* Actually read the boot sector or mbr */ - if (!(data[ndata].data = disk_read_sectors(&cur_part->di, cur_part->start_lba, 1))) { - error("Couldn't read boot sector or mbr.\n"); - goto bail; - } else - sect_area = (void *)data[ndata].data; + if (opt.drmk && mangle_drmk(data + fidx, ®s)) + goto bail; - data[ndata].size = SECTOR; - data[ndata].base = load_base; - /* - * 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. - */ - if (cur_part->index && opt.cmldr) { - memcpy((char *)data[ndata].data + 3, cmldr_signature, - sizeof cmldr_signature); - } + /* Mangle bs/mbr area */ - /* - * Modify the hidden sectors (partition offset) copy in memory; - * this modifies the field used by FAT and NTFS filesystems, and - * possibly other boot loaders which use the same format. - */ - if (cur_part->index && opt.sethidden) { - *(uint32_t *) ((char *)data[ndata].data + 28) = cur_part->start_lba; - } + /* + * 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. + */ + if (cur_part->index && opt.cmldr) { + memcpy((char *)data[sidx].data + 3, cmldr_signature, + sizeof cmldr_signature); + } - ndata++; + /* + * Modify the hidden sectors (partition offset) copy in memory; + * this modifies the field used by FAT and NTFS filesystems, and + * possibly other boot loaders which use the same format. + */ + if (cur_part->index && opt.sethidden) { + *(uint32_t *) ((char *)data[sidx].data + 28) = cur_part->start_lba; } + if (cur_part->index) { if (cur_part->type == typegpt) { /* Do GPT hand-over, if applicable (as per syslinux/doc/gpt.txt) */