From b089f2e97849ae9600f3eecdf6e381f5c990d84d Mon Sep 17 00:00:00 2001 From: Thomas Schmitt Date: Sat, 10 Apr 2010 18:50:59 +0200 Subject: [PATCH] New bit1 of iso_write_opts_set_system_area() options. New inner call make_isolinux_mbr() produces a bootable System Area from an ISOLINUX mbr/isohdp[fp]x*.bin file and an ISOLINUX El Torito bootable image (isolinux.bin). --- libisofs/ecma119.c | 2 +- libisofs/ecma119.h | 3 + libisofs/libisofs.h | 5 + libisofs/make_isohybrid_mbr.c | 223 +++++++++++++++++++++++++++++++++- libisofs/system_area.c | 22 ++++ 5 files changed, 252 insertions(+), 3 deletions(-) diff --git a/libisofs/ecma119.c b/libisofs/ecma119.c index 8682c5f..2620fda 100644 --- a/libisofs/ecma119.c +++ b/libisofs/ecma119.c @@ -2162,7 +2162,7 @@ int iso_write_opts_set_system_area(IsoWriteOpts *opts, char data[32768], } memcpy(opts->system_area_data, data, 32768); } - opts->system_area_options = options & 1; + opts->system_area_options = options & 3; return ISO_SUCCESS; } diff --git a/libisofs/ecma119.h b/libisofs/ecma119.h index c74c0bf..f5ff273 100644 --- a/libisofs/ecma119.h +++ b/libisofs/ecma119.h @@ -465,6 +465,9 @@ struct ecma119_image * end of the ISO image. Assumed are 63 secs/hed, 255 head/cyl. * (GRUB protective msdos label.) * This works with and without system_area_data. + * bit1= apply isohybrid MBR patching to the system area. + * This works only with system_area_data plus ISOLINUX boot image + * and only if not bit0 is set. */ int system_area_options; diff --git a/libisofs/libisofs.h b/libisofs/libisofs.h index b3d743c..33ac9ef 100644 --- a/libisofs/libisofs.h +++ b/libisofs/libisofs.h @@ -1697,6 +1697,11 @@ int iso_write_opts_set_fifo_size(IsoWriteOpts *opts, size_t fifo_size); * This means to patch bytes 446 to 512 of the system area so * that one partition is defined which begins at the second * 512-byte block of the image and ends where the image ends. + * This works with and without system_area_data. + * bit1= apply isohybrid MBR patching to the system area. + * This works only with system area data from SYSLINUX plus an + * ISOLINUX boot image (see iso_image_set_boot_image()) and + * only if not bit0 is set. * @param flag * bit0 = invalidate any attached system area data. Same as data == NULL * @return diff --git a/libisofs/make_isohybrid_mbr.c b/libisofs/make_isohybrid_mbr.c index cb67cc7..88ef2a9 100644 --- a/libisofs/make_isohybrid_mbr.c +++ b/libisofs/make_isohybrid_mbr.c @@ -10,6 +10,7 @@ /* for gettimeofday() */ #include + /* This code stems from syslinux-3.72/utils/isohybrid, a perl script under GPL which is Copyright 2002-2008 H. Peter Anvin. @@ -31,7 +32,7 @@ license from above stem licenses, typically from LGPL. In case its generosity is needed, here is the 2-clause BSD license: make_isohybrid_mbr.c is copyright 2002-2008 H. Peter Anvin - and 2008-2009 Thomas Schmitt + and 2008-2010 Thomas Schmitt 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. @@ -51,8 +52,9 @@ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + /* A helper function. One could replace it by one or two macros. */ -static int lsb_to_buf(char **wpt, int value, int bits, int flag) +static int lsb_to_buf(char **wpt, uint32_t value, int bits, int flag) { int b; @@ -61,6 +63,11 @@ static int lsb_to_buf(char **wpt, int value, int bits, int flag) return (1); } + +/* ====================================================================== */ +/* Deprecated Function */ +/* ====================================================================== */ + /* * Create a MBR for an isohybrid enabled ISOLINUX boot image. * @@ -228,3 +235,215 @@ int make_isohybrid_mbr(int bin_lba, int *img_blocks, char *mbr, int flag) return (1); } + +/* ====================================================================== */ +/* The New MBR Producer */ +/* ====================================================================== */ + +/* The following prescription by H.Peter Anvin is actually a slightly + generalized version of the algorithm in deprecated function + make_isohybrid_mbr(). To be applied to externally provided System Area + data. + + So provisorily i consider the functions lba512chs_to_buf() and + make_isolinux_mbr() as our further contribution the existing code base + under the existing license. This would yield: + copyright 2002-2010 H. Peter Anvin + and 2008-2010 Thomas Schmitt + under "either the LGPL or the MIT/ISC/2-clause BSD licenses" + + Before release i will ask hpa for his explicit ok. +*/ +/* + +From hpa@zytor.com Thu Apr 1 08:32:52 2010 +Date: Wed, 31 Mar 2010 14:53:51 -0700 +From: H. Peter Anvin +To: For discussion of Syslinux and tftp-hpa +Cc: Thomas Schmitt +Subject: Re: [syslinux] port syslinux isohybrid perl script to C + +[...] + +> Currently i lack of blob and prescriptions. + +The blobs are available in the Syslinux build tree under the names: + +mbr/isohdp[fp]x*.bin + +The default probably should be mbr/isohdppx.bin, but it's ultimately up +to the user. + +User definable parameters: + +-> MBR ID (default random 32-bit number, + or preserved from previous instance) +-> Sector count (default 32, range 1-63) +-> Head count (default 64, range 1-256) +-> Partition offset (default 0, range 0-64) +-> Partition number (default 1, range 1-4) +-> Filesystem type (default 0x17, range 1-255) + +Note: the filesystem type is largely arbitrary, in theory it can be any +value other than 0x00, 0x05, 0x0f, 0x85, 0xee, or 0xef. 0x17 ("Windows +IFS Hidden") seems safeish, some people believe 0x83 (Linux) is better. + +Here is the prescriptions for how to install it: + +All numbers are littleendian. "word" means 16 bits, "dword" means 32 +bits, "qword" means 64 bits. + +Common subroutine LBA_to_CHS(): + s = (lba % sector_count) + 1 + t = (lba / sector_count) + h = (t % head_count) + c = (t / head_count) + + if (c >= 1024): + c = 1023 + h = head_count + s = sector_count + + s = s | ((c & 0x300) >> 2) + c = c & 0xff + + write byte h + write byte s + write byte c + +Main: + Pad image_size to a multiple of sector_count*head_count + Use the input file unmodified for bytes 0..431 + write qword boot_lba # Offset 432 + write dword mbr_id # Offset 440 + write word 0 # Offset 444 + + # Offset 446 + For each partition entry 1..4: + if this_partition != partition_number: + write 16 zero bytes + else: + write byte 0x80 + write LBA_to_CHS(partition_offset) + write byte filesystem_type + write LBA_to_CHS(image_size-1) + write dword partition_offset + write dword image_size + + # Offset 510 + write word 0xaa55 + + Use the input file unmodified for bytes 512..32767 + (pad with zero as necessary) + +[...] + + -hpa +*/ + + +static +int lba512chs_to_buf(char **wpt, off_t lba, int head_count, int sector_count) +{ + int s, t, h, c; + + s = (lba % sector_count) + 1; + t = (lba / sector_count); + h = (t % head_count); + c = (t / head_count); + if (c >= 1024) { + c = 1023; + h = head_count; /* >>> not -1 ? Limits head_count to 255 */ + s = sector_count; + } + s = s | ((c & 0x300) >> 2); + c = c & 0xff; + (*((unsigned char **) wpt))[0] = h; + (*((unsigned char **) wpt))[1] = s; + (*((unsigned char **) wpt))[2] = c; + (*wpt)+= 3; + return(1); +} + + +/* + * @param flag bit0= make own random MBR Id from current time + */ +int make_isolinux_mbr(int32_t *img_blocks, uint32_t boot_lba, + uint32_t mbr_id, int head_count, int sector_count, + int part_offset, int part_number, int fs_type, + uint8_t *buf, int flag) +{ + uint32_t spc, id, part, nominal_part_size; + off_t hd_img_blocks, hd_boot_lba; + char *wpt; + /* For generating a weak random number */ + struct timeval tv; + struct timezone tz; + + /* Pad image_size to a multiple of sector_count*head_count + */ + spc = head_count * sector_count; + hd_img_blocks = ((off_t) *img_blocks) * (off_t) 4; + if (hd_img_blocks % spc) { + hd_img_blocks += spc - (hd_img_blocks % spc); + *img_blocks = hd_img_blocks / 4 + !!(hd_img_blocks % 4); + } + + wpt = (char *) buf + 432; + + /* write qword boot_lba # Offset 432 + */ + hd_boot_lba = ((off_t) boot_lba) * (off_t) 4; + lsb_to_buf(&wpt, hd_boot_lba & 0xffffffff, 32, 0); + lsb_to_buf(&wpt, hd_boot_lba >> 32, 32, 0); + + /* write dword mbr_id # Offset 440 + (here some 32-bit random value with no crypto strength) + */ + if (flag & 1) { + gettimeofday(&tv, &tz); + id = 0xffffffff & (tv.tv_sec ^ (tv.tv_usec * 2000)); + lsb_to_buf(&wpt, id, 32, 0); + } + + /* write word 0 # Offset 444 + */ + lsb_to_buf(&wpt, 0, 16, 0); + + /* # Offset 446 + */ + for (part = 1 ; part <= 4; part++) { + if (part != part_number) { + /* if this_partition != partition_number: write 16 zero bytes */ + memset(wpt, 0, 16); + wpt+= 16; + continue; + } + /* write byte 0x80 + write LBA_to_CHS(partition_offset) + write byte filesystem_type + write LBA_to_CHS(image_size-1) + write dword partition_offset + write dword image_size + */ + lsb_to_buf(&wpt, 0x80, 8, 0); + lba512chs_to_buf(&wpt, part_offset, head_count, sector_count); + lsb_to_buf(&wpt, fs_type, 8, 0); + lba512chs_to_buf(&wpt, hd_img_blocks - 1, head_count, sector_count); + lsb_to_buf(&wpt, part_offset, 32, 0); + if (hd_img_blocks - (off_t) part_offset > (off_t) 0xffffffff) + nominal_part_size = 0xffffffff; + else + nominal_part_size = hd_img_blocks - (off_t) part_offset; + lsb_to_buf(&wpt, nominal_part_size, 32, 0); + } + + /* write word 0xaa55 # Offset 510 + */ + lsb_to_buf(&wpt, 0xaa55, 16, 0); + + return(1); +} + + diff --git a/libisofs/system_area.c b/libisofs/system_area.c index 79fe939..24beaee 100644 --- a/libisofs/system_area.c +++ b/libisofs/system_area.c @@ -23,6 +23,15 @@ */ int make_isohybrid_mbr(int bin_lba, int *img_blocks, char *mbr, int flag); +/* + * The New ISOLINUX MBR Producer. + * Be cautious with changing parameters. Only few combinations are tested. + * + */ +int make_isolinux_mbr(int32_t *img_blocks, uint32_t boot_lba, + uint32_t mbr_id, int head_count, int sector_count, + int part_offset, int part_number, int fs_type, + uint8_t *buf, int flag); /* This is the gesture of grub-mkisofs --protective-msdos-label as explained by @@ -41,6 +50,7 @@ int make_isohybrid_mbr(int bin_lba, int *img_blocks, char *mbr, int flag); same number. See also http://en.wikipedia.org/wiki/Master_boot_record */ +static int make_grub_msdos_label(int img_blocks, uint8_t *buf, int flag) { uint8_t *wpt; @@ -141,6 +151,18 @@ int iso_write_system_area(Ecma119Image *t, uint8_t *buf) ret = make_grub_msdos_label(img_blocks, buf, 0); if (ret != 1) /* error should never happen */ return ISO_ASSERT_FAILURE; + } else if(t->system_area_options & 2) { + /* Patch externally provided system area as isohybrid MBR */ + if (t->catalog == NULL || t->system_area_data == NULL) { + /* isohybrid makes only sense together with ISOLINUX boot image + and externally provided System Area. + */ + return ISO_ISOLINUX_CANT_PATCH; + } + ret = make_isolinux_mbr(&img_blocks, t->bootimg->sections[0].block, + (uint32_t) 0, 64, 32, 0, 1, 0x17, buf, 1); + if (ret != 1) + return ret; } return ISO_SUCCESS; }