[syslinux] [PATCH] chain.c32: support chainloading GRUB2 core.img

Gert Hulselmans gerth at zytor.com
Tue Dec 21 12:56:55 PST 2010


Gert Hulselmans wrote:
> Here is a patch that makes it possible to chainload GRUB2 core.img form
> chain.c32.
>
> It reuses the grub= parameter (used for chainloading GRUB Legacy stage2):
> - both loaded at 0x8000
> - start execution at offset 0x200 (0x8200 in memory)
>
> GRUB2 allows to specify another "GRUB home dir" than the standard
> /boot/grub
> GRUB2 doesn't allow to change the configfile 'grub.cfg' name itself (I
> can't find it uncompressed).
>
> Usage with GRUB2: chain.c32 grub=/boot/grub2/core.img grub2dir=/boot/grub2
> hd1,6
>
> Usage with GRUB Legacy: chain.c32 grub=/boot/grub/stage2
> grubcfg=/boot/grub/grub.lst hd0,2
>
> Remarks are welcome.
> Testers too.
>
> diff is also attached.
>
> - Gert Hulselmans
>

I found a grub2 (v1.98) source code file that describes more fields than I
found in the source code files which I used to write the previous patch:

  kern/i386/pc/startup.S

The structures have some additional fields now, but fundamentally, nothing
changed.

I also tested the GRUB Legacy stage2 with this code, and it still works
like before the GRUB2 specific code was added.


Would it be useful to check for the version number of core.img of GRUB2
like we do for GRUB Legacy detection or is it overkill?

 Values for GRUB2:
  - grub2_core_img->compat_version_major = 4;
  - grub2_core_img->compat_version_major = 0;

- Gert Hulselmans


diff -urp syslinux-4.03/com32/modules/chain.c
syslinux-4.03-chain-grub2/com32/modules/chain.c
--- syslinux-4.03/com32/modules/chain.c	2010-10-20 21:25:38.000000000 +0200
+++ syslinux-4.03-chain-grub2/com32/modules/chain.c	2010-12-21
20:18:28.947116001 +0100
@@ -82,10 +82,14 @@
  *
  * grub=<loader>
  *	same as seg=0x800 file=<loader> & jumping to seg 0x820,
- *	used with GRUB Legacy stage2 files.
+ *	used with GRUB Legacy stage2 files and GRUB2 core.img files.
  *
  * grubcfg=<filename>
- *	set an alternative config filename in stage2 of Grub Legacy,
+ *	set an alternative config filename in stage2 of GRUB Legacy.
+ *	only applicable in combination with "grub=<loader>".
+ *
+ * grub2dir=<dirname>
+ *	set an alternative directory name in core.img of GRUB2.
  *	only applicable in combination with "grub=<loader>".
  *
  * grldr=<loader>
@@ -134,6 +138,7 @@ static struct options {
     bool grub;
     bool grldr;
     const char *grubcfg;
+    const char *grub2dir;
     bool swap;
     bool hide;
     bool sethidden;
@@ -1283,8 +1288,9 @@ Options: file=<loader>      Load and exe
          msdos=<loader>     Load MS-DOS 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\
+         grub=<loader>      Load GRUB Legacy stage2 or GRUB2 core.img\n\
          grubcfg=<filename> Set alternative config filename for GRUB
Legacy\n\
+         grub2dir=<dirname> Set alternative directory name for GRUB2\n\
          grldr=<loader>     Load GRUB4DOS grldr\n\
          seg=<segment>      Jump to <seg>:0000, instead of 0000:7C00\n\
          swap               Swap drive numbers, if bootdisk is not
fd0/hd0\n\
@@ -1364,6 +1370,8 @@ int main(int argc, char *argv[])
 	    opt.grub = true;
 	} else if (!strncmp(argv[i], "grubcfg=", 8)) {
 	    opt.grubcfg = argv[i] + 8;
+	} else if (!strncmp(argv[i], "grub2dir=", 9)) {
+	    opt.grub2dir = argv[i] + 9;
 	} else if (!strncmp(argv[i], "grldr=", 6)) {
 	    opt.loadfile = argv[i] + 6;
 	    opt.grldr = true;
@@ -1412,6 +1420,11 @@ int main(int argc, char *argv[])
 	goto bail;
     }

+    if (opt.grub2dir && !opt.grub) {
+	error("grub2dir=<dirname> must be used together with grub=<loader>.\n");
+	goto bail;
+    }
+
     if (opt.seg) {
 	regs.es = regs.cs = regs.ss = regs.ds = regs.fs = regs.gs = opt.seg;
     } else {
@@ -1603,16 +1616,16 @@ int main(int argc, char *argv[])
 	}

 	if (opt.grub) {
-	    /* Layout of stage2 file (from byte 0x0 to 0x270) */
+	    /* Layout of stage2 file of GRUB Legacy (from byte 0x0 to 0x270) */
 	    struct grub_stage2_patch_area {
-		/* 0x0 to 0x205 */
+		/* 0x0 - 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 */
+		/* 0x208 - 0x20b: install_partition variable */
 		struct {
 		    /* 0x208: sub-partition in sub-partition part2 */
 		    uint8_t part3;
@@ -1624,83 +1637,161 @@ int main(int argc, char *argv[])
 		    uint8_t drive;
 		} __attribute__ ((packed)) install_partition;

-		/* 0x20c: deprecated (historical reason only) */
+		/* 0x20c - 0x20f: 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) */
+		/* 0x212 - 0x216: version string (will normally be 0.97) */
 		char version_string[5];
-		/* 0x217: config filename */
+		/* 0x217 - 0x26f: config filename */
 		char config_file[89];
-		/* 0x270: start of code (after jump from 0x200) */
-		char codestart[1];
-	    } __attribute__ ((packed)) *stage2;
+	    } __attribute__ ((packed)) *grub_legacy_stage2;

-	    if (data[ndata].size < sizeof(struct grub_stage2_patch_area)) {
-		error
-		    ("The file specified by grub=<loader> is to small to be stage2 of
GRUB Legacy.\n");
-		goto bail;
-	    }
+	    /* Layout of GRUB2 core.img file (from byte 0x0 to 0x25c) */
+	    struct grub2_core_img_patch_area {
+		/* 0x0 - 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 - 0x20b: GRUB2 total module size */
+		uint32_t grub2_total_module_size;
+		/* 0x20c - 0x20f: GRUB2 kernel image size */
+		uint32_t grub2_kernel_image_size;
+		/* 0x210 - 0x213: GRUB2 compressed size */
+		uint32_t grub2_compressed_size;
+		/* 0x214 - 0x217: msdos partition number  */
+		uint32_t install_dos_part;
+		/* 0x218 - 0x21b: BSD partition number */
+		uint32_t install_bsd_part;
+		/* 0x21c - 0x25b: GRUB2 directory name */
+		char grub2dir[0x40];

-	    stage2 = data[ndata].data;
+		/* 0x25c - 0x267: Multiboot header */
+		struct {
+		    /* 0x25c - 0x25f: magic number = 0x1badb002 */
+		    uint32_t magic;
+		    /* 0x260 - 0x263: flags */
+		    uint32_t flags;
+		    /* 0x264 - 0x267: checksum */
+		    uint32_t checksum;
+		    /* 0x268 - 0x26b: header address */
+		    uint32_t header_addr;
+		    /* 0x26c - 0x26f: load address */
+		    uint32_t load_addr;
+		    /* 0x270 - 0x273: load end address */
+		    uint32_t load_end_addr;
+		    /* 0x274 - 0x277: bss end address */
+		    uint32_t bss_end_addr;
+		    /* 0x278 - 0x27b: entry address */
+		    uint32_t entry_addr;
+		} __attribute__ ((packed)) multiboot_header;
+
+	    } __attribute__ ((packed)) *grub2_core_img;

-	    /*
-	     * 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) {
+	    /* check if the loaded file is at least 4 kiB */
+	    if (data[ndata].size < 0x1000) {
 		error
-		    ("The file specified by grub=<loader> is not a supported stage2
GRUB Legacy binary\n");
+		    ("The file specified by grub=<loader> is to small to be stage2 of
GRUB Legacy\nor core.img of GRUB2.\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 = whichpart - 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) {
+	    grub2_core_img = data[ndata].data;
+
+	    /* Do we have core.img of GRUB2 ? */
+	    if (grub2_core_img->multiboot_header.magic == 0x1badb002) {
+		/* Check multiboot header: sum must be zero (32-bit unsigned)  */
+		if ((uint32_t) (grub2_core_img->multiboot_header.magic
+		    + grub2_core_img->multiboot_header.flags
+		    + grub2_core_img->multiboot_header.checksum) != 0) {
+			error
+			    ("The file specified by grub=<loader> is not a valid GRUB2
core.img binary.\n");
+			goto bail;
+		}
+
+		/*
+		 *   -1:   whole drive (default) (-1 = 0xffffffff)
+		 *   0-3:  primary partitions
+		 *   4-*:  logical partitions
+		 */
+		grub2_core_img->install_dos_part = ((uint32_t) whichpart) - 1;
+
+		/*
+		 * GRUB2 reserves 63 bytes (0x21c - 0x25b) for the GRUB2 directory.
+		 * The filename passed via grub2dir= will overwrite the default
+		 * GRUB2 directory "/boot/grub/".
+		 */
+		if (opt.grub2dir) {
+		    if (strlen(opt.grub2dir) > sizeof(grub2_core_img->grub2dir) - 1) {
+			error
+			    ("The config filename length can't exceed 63 characters.\n");
+			goto bail;
+		    }
+		    strcpy((char *)grub2_core_img->grub2dir, opt.grub2dir);
+		}
+	    } else {
+		grub_legacy_stage2 = data[ndata].data;
+
+		/* Do we have stage2 of GRUB legacy ?
+		 *
+		 * Check the compatibility version number to see if we loaded a real
+		 * stage2 file or a stage2 file that we support.
+		 */
+		if (grub_legacy_stage2->compat_version_major != 3
+		    || grub_legacy_stage2->compat_version_minor != 2) {
 		    error
-			("The config filename length can't exceed 88 characters.\n");
+			("The file specified by grub=<loader> is not a supported GRUB Legacy
stage2 binary\n");
 		    goto bail;
 		}

-		strcpy((char *)stage2->config_file, opt.grubcfg);
+		/*
+		 * 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
+		 */
+		grub_legacy_stage2->install_partition.part1 = whichpart - 1;
+
+		/*
+		 * Grub Legacy reserves 89 bytes (from 0x217 to 0x26f) 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(grub_legacy_stage2->config_file) -
1) {
+			error
+			    ("The config filename length can't exceed 88 characters.\n");
+			goto bail;
+		    }
+
+		    strcpy((char *)grub_legacy_stage2->config_file, opt.grubcfg);
+		}
 	    }
 	}
-------------- next part --------------
A non-text attachment was scrubbed...
Name: chain-grub2.diff
Type: text/x-patch
Size: 11219 bytes
Desc: not available
URL: <http://www.zytor.com/pipermail/syslinux/attachments/20101221/d8ac22f0/attachment.bin>


More information about the Syslinux mailing list