In order to successfully boot with non-512-bytes sector disks and buggy/older system firmware, core.img must be written in a special way.
This means that: - each part of core.img that needs to be directly addressable MUST start on a hardware sector - the addressing must be native-sector-size-based - lengths are still based on 512-bytes blocks. We can get such a layout by padding data out a bit, like this: - write the first 512-bytes block to the start of a native sector (this already is implicitly true through the embedding routine) - potentially leave a gap up until the next native sector (i.e., zero-fill) - write the next 512-bytes block - write the next 126 512-bytes blocks, since data is read-in in 127 512-bytes blocks at a time, inherited from buggy "Phoenix BIOS" system firmware that was not able to read more data at a time - potentially leave a gap until the next native sector - repeat writing blocks as specified above until reaching core.img's end - DON'T add useless padding at the end. This is all optional and comes with a new switch to grub-install: --emu-512b It also: - only works when embedding core.img (sorry, no blocklists support) - requires MBR- or GPT-embedding (sorry, no BtrFS- or zfs-embedding support) - will only work for x86 BIOS targets (because the SPARC code is so different in certain aspects that I cannot meaningfully generalize it). Additionally, this commit fixes a small SPARC issue as a drive-by: SPARC does not support Reed-Solomon Codes and the code for generating them was properly #ifdef'd out, but the maximum needed sectors was still uselessly doubled to accomodate RS. From now on, only x86 BIOS targets will reserve space for RS codes (unless disabled, of course). --- grub-core/disk/ldm.c | 10 +- grub-core/fs/btrfs.c | 9 +- grub-core/fs/zfs/zfs.c | 9 +- grub-core/partmap/gpt.c | 44 ++++- grub-core/partmap/msdos.c | 30 +++- include/grub/emu/hostdisk.h | 3 +- include/grub/fs.h | 3 +- include/grub/partition.h | 3 +- include/grub/util/install.h | 4 +- util/grub-install.c | 18 +- util/grub-setup.c | 10 +- util/setup.c | 349 +++++++++++++++++++++++++++++++++--- 12 files changed, 449 insertions(+), 43 deletions(-) diff --git a/grub-core/disk/ldm.c b/grub-core/disk/ldm.c index 2a22d2d6c..47fda7a39 100644 --- a/grub-core/disk/ldm.c +++ b/grub-core/disk/ldm.c @@ -954,7 +954,8 @@ grub_err_t grub_util_ldm_embed (struct grub_disk *disk, unsigned int *nsectors, unsigned int max_nsectors, grub_embed_type_t embed_type, - grub_disk_addr_t **sectors) + grub_disk_addr_t **sectors, + int emu_512b) { struct grub_diskfilter_pv *pv = NULL; struct grub_diskfilter_vg *vg; @@ -964,6 +965,13 @@ grub_util_ldm_embed (struct grub_disk *disk, unsigned int *nsectors, if (embed_type != GRUB_EMBED_PCBIOS) return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "LDM currently supports only PC-BIOS embedding"); + + if (emu_512b) + { + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "512 bytes sector length emulation is not implemented for LDM-embedding"); + } + if (disk->partition) return grub_error (GRUB_ERR_BUG, "disk isn't LDM"); pv = grub_diskfilter_get_pv_from_disk (disk, &vg); diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c index 63f9657a6..3949ea1a0 100644 --- a/grub-core/fs/btrfs.c +++ b/grub-core/fs/btrfs.c @@ -2151,7 +2151,8 @@ grub_btrfs_embed (grub_device_t device __attribute__ ((unused)), unsigned int *nsectors, unsigned int max_nsectors, grub_embed_type_t embed_type, - grub_disk_addr_t **sectors) + grub_disk_addr_t **sectors, + int emu_512b) { unsigned i; @@ -2159,6 +2160,12 @@ grub_btrfs_embed (grub_device_t device __attribute__ ((unused)), return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "BtrFS currently supports only PC-BIOS embedding"); + if (emu_512b) + { + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "512 bytes sector length emulation is not implemented for BtrFS-embedding"); + } + if (64 * 2 - 1 < *nsectors) return grub_error (GRUB_ERR_OUT_OF_RANGE, N_("your core.img is unusually large. " diff --git a/grub-core/fs/zfs/zfs.c b/grub-core/fs/zfs/zfs.c index b5e10fd0b..3b4c65f56 100644 --- a/grub-core/fs/zfs/zfs.c +++ b/grub-core/fs/zfs/zfs.c @@ -4323,7 +4323,8 @@ grub_zfs_embed (grub_device_t device __attribute__ ((unused)), unsigned int *nsectors, unsigned int max_nsectors, grub_embed_type_t embed_type, - grub_disk_addr_t **sectors) + grub_disk_addr_t **sectors, + int emu_512b) { unsigned i; @@ -4331,6 +4332,12 @@ grub_zfs_embed (grub_device_t device __attribute__ ((unused)), return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "ZFS currently supports only PC-BIOS embedding"); + if (emu_512b) + { + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "512 bytes sector length emulation is not implemented for zfs-embedding"); + } + if ((VDEV_BOOT_SIZE >> GRUB_DISK_SECTOR_BITS) < *nsectors) return grub_error (GRUB_ERR_OUT_OF_RANGE, N_("your core.img is unusually large. " diff --git a/grub-core/partmap/gpt.c b/grub-core/partmap/gpt.c index 103f6796f..4b968529a 100644 --- a/grub-core/partmap/gpt.c +++ b/grub-core/partmap/gpt.c @@ -169,8 +169,13 @@ static grub_err_t gpt_partition_map_embed (struct grub_disk *disk, unsigned int *nsectors, unsigned int max_nsectors, grub_embed_type_t embed_type, - grub_disk_addr_t **sectors) + grub_disk_addr_t **sectors, + int emu_512b) { + struct gpt_partition_map_embed_ctx orig_ctx = { + .start = 0, + .len = 0 + }; struct gpt_partition_map_embed_ctx ctx = { .start = 0, .len = 0 @@ -186,6 +191,43 @@ gpt_partition_map_embed (struct grub_disk *disk, unsigned int *nsectors, if (err) return err; + orig_ctx = ctx; + if (emu_512b) + { + unsigned int emu_sec_factor_bits = disk->log_sector_size + - GRUB_DISK_SECTOR_BITS; + /* + * Make sure the start is HW-sector-aligned and modify the partition size + * accordingly. + */ + + /* + * Align and truncate to physical sector (always less or equal to current + * value. + */ + ctx.start = ctx.start >> emu_sec_factor_bits; + ctx.start = ctx.start << emu_sec_factor_bits; + + if (ctx.start != orig_ctx.start) + { + /* + * ctx.start is unaligned, so add a physical sector factor and align + * and truncate again. + */ + ctx.start = orig_ctx.start + ((1U) << emu_sec_factor_bits); + + ctx.start = ctx.start >> emu_sec_factor_bits; + ctx.start = ctx.start << emu_sec_factor_bits; + + ctx.len -= (ctx.start - orig_ctx.start); + } + } + + /* + * N.B.: ctx can either be an aligned version or just its original value from + * this point on, depending on whether emu_512b was requested or not. + */ + if (ctx.len == 0) return grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("this GPT partition label contains no BIOS Boot Partition;" diff --git a/grub-core/partmap/msdos.c b/grub-core/partmap/msdos.c index 7b8e45076..314bd8afd 100644 --- a/grub-core/partmap/msdos.c +++ b/grub-core/partmap/msdos.c @@ -236,7 +236,8 @@ static grub_err_t pc_partition_map_embed (struct grub_disk *disk, unsigned int *nsectors, unsigned int max_nsectors, grub_embed_type_t embed_type, - grub_disk_addr_t **sectors) + grub_disk_addr_t **sectors, + int emu_512b) { grub_disk_addr_t end = ~0ULL; struct grub_msdos_partition_mbr mbr; @@ -246,6 +247,13 @@ pc_partition_map_embed (struct grub_disk *disk, unsigned int *nsectors, grub_disk_addr_t lastaddr = 1; grub_disk_addr_t ext_offset = 0; grub_disk_addr_t offset = 0; + unsigned int emu_sec_factor = (1U) << (disk->log_sector_size + - GRUB_DISK_SECTOR_BITS); + + if (!emu_512b) + { + emu_sec_factor = 1; + } if (embed_type != GRUB_EMBED_PCBIOS) return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, @@ -326,22 +334,23 @@ pc_partition_map_embed (struct grub_disk *disk, unsigned int *nsectors, break; } - if (end >= *nsectors + 1) + if (end >= *nsectors + emu_sec_factor) { unsigned i, j; char *embed_signature_check; unsigned int orig_nsectors, avail_nsectors; orig_nsectors = *nsectors; - *nsectors = end - 1; + *nsectors = end - emu_sec_factor; avail_nsectors = *nsectors; + if (*nsectors > max_nsectors) *nsectors = max_nsectors; *sectors = grub_malloc (*nsectors * sizeof (**sectors)); if (!*sectors) return grub_errno; for (i = 0; i < *nsectors; i++) - (*sectors)[i] = 1 + i; + (*sectors)[i] = emu_sec_factor + i; /* Check for software that is already using parts of the embedding * area. @@ -362,6 +371,15 @@ pc_partition_map_embed (struct grub_disk *disk, unsigned int *nsectors, continue; grub_util_warn (_(message_warn[embed_signatures[j].type]), (*sectors)[i], embed_signatures[j].name); + + if (emu_512b) + { + return grub_error (GRUB_ERR_OUT_OF_RANGE, + N_("leaving gaps for other software in the " + "embedding area is not supported in 512 " + "bytes emulation mode")); + } + avail_nsectors--; if (avail_nsectors < *nsectors) *nsectors = avail_nsectors; @@ -390,12 +408,12 @@ pc_partition_map_embed (struct grub_disk *disk, unsigned int *nsectors, return GRUB_ERR_NONE; } - if (end <= 1) + if (end <= emu_sec_factor) return grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("this msdos-style partition label has no " "post-MBR gap; embedding won't be possible")); - if (*nsectors > 62) + if (*nsectors > (63 - emu_sec_factor)) return grub_error (GRUB_ERR_OUT_OF_RANGE, N_("your core.img is unusually large. " "It won't fit in the embedding area")); diff --git a/include/grub/emu/hostdisk.h b/include/grub/emu/hostdisk.h index e006f0b38..e70d474e8 100644 --- a/include/grub/emu/hostdisk.h +++ b/include/grub/emu/hostdisk.h @@ -51,7 +51,8 @@ grub_err_t grub_util_ldm_embed (struct grub_disk *disk, unsigned int *nsectors, unsigned int max_nsectors, grub_embed_type_t embed_type, - grub_disk_addr_t **sectors); + grub_disk_addr_t **sectors, + int emu_512b); #endif const char * grub_hostdisk_os_dev_to_grub_drive (const char *os_dev, int add); diff --git a/include/grub/fs.h b/include/grub/fs.h index 302e48d4b..a3f72b6fa 100644 --- a/include/grub/fs.h +++ b/include/grub/fs.h @@ -88,7 +88,8 @@ struct grub_fs grub_err_t (*fs_embed) (grub_device_t device, unsigned int *nsectors, unsigned int max_nsectors, grub_embed_type_t embed_type, - grub_disk_addr_t **sectors); + grub_disk_addr_t **sectors, + int emu_512b); /* Whether this filesystem reserves first sector for DOS-style boot. */ int reserved_first_sector; diff --git a/include/grub/partition.h b/include/grub/partition.h index 7adb7ec6e..2a6f5e6d1 100644 --- a/include/grub/partition.h +++ b/include/grub/partition.h @@ -55,7 +55,8 @@ struct grub_partition_map grub_err_t (*embed) (struct grub_disk *disk, unsigned int *nsectors, unsigned int max_nsectors, grub_embed_type_t embed_type, - grub_disk_addr_t **sectors); + grub_disk_addr_t **sectors, + int emu_512b); #endif }; typedef struct grub_partition_map *grub_partition_map_t; diff --git a/include/grub/util/install.h b/include/grub/util/install.h index 2631b1074..daaef1725 100644 --- a/include/grub/util/install.h +++ b/include/grub/util/install.h @@ -193,13 +193,13 @@ grub_util_bios_setup (const char *dir, const char *boot_file, const char *core_file, const char *dest, int force, int fs_probe, int allow_floppy, - int add_rs_codes); + int add_rs_codes, int emu_512b); void grub_util_sparc_setup (const char *dir, const char *boot_file, const char *core_file, const char *dest, int force, int fs_probe, int allow_floppy, - int add_rs_codes); + int add_rs_codes, int emu_512b); char * grub_install_get_image_targets_string (void); diff --git a/util/grub-install.c b/util/grub-install.c index 8970b73aa..57ef3ef33 100644 --- a/util/grub-install.c +++ b/util/grub-install.c @@ -79,6 +79,7 @@ static char *label_color; static char *label_bgcolor; static char *product_version; static int add_rs_codes = 1; +static int emu_512b = 0; enum { @@ -105,6 +106,7 @@ enum OPTION_DISK_MODULE, OPTION_NO_BOOTSECTOR, OPTION_NO_RS_CODES, + OPTION_EMU_512B, OPTION_MACPPC_DIRECTORY, OPTION_LABEL_FONT, OPTION_LABEL_COLOR, @@ -224,6 +226,10 @@ argp_parser (int key, char *arg, struct argp_state *state) add_rs_codes = 0; return 0; + case OPTION_EMU_512B: + emu_512b = 1; + return 0; + case OPTION_DEBUG: verbosity++; return 0; @@ -285,6 +291,10 @@ static struct argp_option options[] = { {"no-rs-codes", OPTION_NO_RS_CODES, 0, 0, N_("Do not apply any reed-solomon codes when embedding core.img. " "This option is only available on x86 BIOS targets."), 0}, + {"emu-512b", OPTION_EMU_512B, 0, 0, + N_("Use native-sector-size addressing, but emulate 512-bytes sector lengths when embedding the core image. " + "Workaround for broken firmware. " + "This option is only available on x86 BIOS targets."), 0}, {"debug", OPTION_DEBUG, 0, OPTION_HIDDEN, 0, 2}, {"no-floppy", OPTION_NO_FLOPPY, 0, OPTION_HIDDEN, 0, 2}, @@ -1704,7 +1714,7 @@ main (int argc, char *argv[]) "boot.img"); grub_install_copy_file (boot_img_src, boot_img, 1); - grub_util_info ("%sgrub-bios-setup %s %s %s %s %s --directory='%s' --device-map='%s' '%s'", + grub_util_info ("%sgrub-bios-setup %s %s %s %s %s %s --directory='%s' --device-map='%s' '%s'", /* TRANSLATORS: This is a prefix in the log to indicate that usually a command would be executed but due to an option was skipped. */ install_bootsector ? "" : _("NOT RUNNING: "), @@ -1713,6 +1723,7 @@ main (int argc, char *argv[]) force ? "--force " : "", !fs_probe ? "--skip-fs-probe" : "", !add_rs_codes ? "--no-rs-codes" : "", + emu_512b ? "--emu-512b" : "", platdir, device_map, install_device); @@ -1721,7 +1732,8 @@ main (int argc, char *argv[]) if (install_bootsector) grub_util_bios_setup (platdir, "boot.img", "core.img", install_drive, force, - fs_probe, allow_floppy, add_rs_codes); + fs_probe, allow_floppy, add_rs_codes, + emu_512b); break; } case GRUB_INSTALL_PLATFORM_SPARC64_IEEE1275: @@ -1748,7 +1760,7 @@ main (int argc, char *argv[]) grub_util_sparc_setup (platdir, "boot.img", "core.img", install_drive, force, fs_probe, allow_floppy, - 0 /* unused */ ); + 0 /* unused */, 8 /* unused */); break; } diff --git a/util/grub-setup.c b/util/grub-setup.c index 42b98ad3c..8297436df 100644 --- a/util/grub-setup.c +++ b/util/grub-setup.c @@ -95,6 +95,9 @@ static struct argp_option options[] = { {"no-rs-codes", NO_RS_CODES_KEY, 0, 0, N_("Do not apply any reed-solomon codes when embedding core.img. " "This option is only available on x86 BIOS targets."), 0}, + {"emu-512b", 'e', 0, 0, + N_("Use native-sector-size addressing, but emulate 512 bytes sector lengths when embedding the core image. " + "Workaround for broken firmware."), 0}, { 0, 0, 0, 0, 0, 0 } }; @@ -135,6 +138,7 @@ struct arguments int allow_floppy; char *device; int add_rs_codes; + int emu_512b; }; static error_t @@ -194,6 +198,10 @@ argp_parser (int key, char *arg, struct argp_state *state) arguments->add_rs_codes = 0; break; + case 'e': + arguments->emu_512b = 1; + break; + case ARGP_KEY_ARG: if (state->arg_num == 0) arguments->device = xstrdup(arg); @@ -315,7 +323,7 @@ main (int argc, char *argv[]) arguments.core_file ? : DEFAULT_CORE_FILE, dest_dev, arguments.force, arguments.fs_probe, arguments.allow_floppy, - arguments.add_rs_codes); + arguments.add_rs_codes, arguments.emu_512b); /* Free resources. */ grub_fini_all (); diff --git a/util/setup.c b/util/setup.c index 3be88aae1..0574a826a 100644 --- a/util/setup.c +++ b/util/setup.c @@ -254,7 +254,8 @@ SETUP (const char *dir, const char *boot_file, const char *core_file, const char *dest, int force, int fs_probe, int allow_floppy, - int add_rs_codes __attribute__ ((unused))) /* unused on sparc64 */ + int add_rs_codes __attribute__ ((unused)), /* unused on sparc64 */ + int emu_512b) { char *core_path; char *boot_img, *core_img, *boot_path; @@ -267,6 +268,14 @@ SETUP (const char *dir, bl.first_sector = (grub_disk_addr_t) -1; +#ifdef GRUB_SETUP_SPARC64 + if (emu_512b) + { + grub_util_error ("%s", _("512 bytes sector length emulation mode" + "is currently not available on SPARC platforms")); + } +#endif + #ifdef GRUB_SETUP_BIOS bl.current_segment = GRUB_BOOT_I386_PC_KERNEL_SEG + (GRUB_DISK_SECTOR_SIZE >> 4); @@ -408,6 +417,25 @@ SETUP (const char *dir, int i; grub_fs_t fs; unsigned int nsec, maxsec; + unsigned int emu_sec_factor = (1U) << (dest_dev->disk->log_sector_size + - GRUB_DISK_SECTOR_BITS); + grub_uint64_t fill = 0; + + /* + * If the block doesn't end on physical sector size, calculate emulated + * sector count to fill up to physical sector size. + */ + fill = emu_sec_factor - (0x7f % emu_sec_factor); + + if (emu_512b) + { + /* Sanity checks. */ + if ((int)(emu_sec_factor) < 1) + { + grub_util_error ("%s", _("invalid hardware sector size; " + "embedding won't be possible")); + } + } grub_partition_iterate (dest_dev->disk, identify_partmap, &ctx); @@ -501,19 +529,7 @@ SETUP (const char *dir, goto unable_to_embed; } - nsec = core_sectors; - - if (add_rs_codes) - maxsec = 2 * core_sectors; - else - maxsec = core_sectors; - -#ifdef GRUB_SETUP_BIOS - if (maxsec > ((0x78000 - GRUB_KERNEL_I386_PC_LINK_ADDR) - >> GRUB_DISK_SECTOR_BITS)) - maxsec = ((0x78000 - GRUB_KERNEL_I386_PC_LINK_ADDR) - >> GRUB_DISK_SECTOR_BITS); -#endif + nsec = maxsec = core_sectors; #ifdef GRUB_SETUP_SPARC64 /* @@ -525,15 +541,66 @@ SETUP (const char *dir, maxsec += 2; #endif + if (emu_512b) + { + grub_int64_t read_blocks_quot = (nsec - 1) / 0x7f; + grub_int64_t read_blocks_rem = (nsec - 1) % 0x7f; + + /* + * We want to fill/pad each complete 127-sectors-block, but not the + * last one. + * + * To do so, we calculate the number of blocks (complete or incomplete) + * and subtract one. + */ + grub_int64_t emu_blocks_fill = read_blocks_quot + (!!read_blocks_rem) + - 1; + + if (emu_blocks_fill >= 0) + { + nsec = nsec + /* + * Padding to fill first HW sector - keep + * boot.img/diskboot.img isolated! + * + * N.B.: calculation is safe as long as the grub-internal + * sector size is smaller than or equal to the hardware + * sector size. + * We checked for this previously. + */ + + (emu_sec_factor - 1) + /* + * Pad each completed(!) 127-sectors-block to a full + * hardware sector, so that each block starts on a proper + * hardware sector. + */ + + ((unsigned int)(emu_blocks_fill) * fill); + maxsec = nsec; + } + } + +#ifdef GRUB_SETUP_BIOS + /* + * SPARC currently does not support RS codes. + */ + if (add_rs_codes) + maxsec *= 2; + + if (maxsec > ((0x78000 - GRUB_KERNEL_I386_PC_LINK_ADDR) + >> GRUB_DISK_SECTOR_BITS)) + maxsec = ((0x78000 - GRUB_KERNEL_I386_PC_LINK_ADDR) + >> GRUB_DISK_SECTOR_BITS); +#endif + if (is_ldm) err = grub_util_ldm_embed (dest_dev->disk, &nsec, maxsec, - GRUB_EMBED_PCBIOS, §ors); + GRUB_EMBED_PCBIOS, §ors, emu_512b); else if (ctx.dest_partmap) err = ctx.dest_partmap->embed (dest_dev->disk, &nsec, maxsec, - GRUB_EMBED_PCBIOS, §ors); + GRUB_EMBED_PCBIOS, §ors, emu_512b); else err = fs->fs_embed (dest_dev, &nsec, maxsec, - GRUB_EMBED_PCBIOS, §ors); + GRUB_EMBED_PCBIOS, §ors, emu_512b); if (!err && nsec < core_sectors) { err = grub_error (GRUB_ERR_OUT_OF_RANGE, @@ -563,9 +630,150 @@ SETUP (const char *dir, } bl.block = bl.first_block; - for (i = 0; i < nsec; i++) - save_blocklists (sectors[i] + grub_partition_get_start (ctx.container), - 0, GRUB_DISK_SECTOR_SIZE, &bl); + { + grub_disk_addr_t last_hw_sector = 0; + unsigned int block_pos = 0; + int first_sec = 0; + int block_end = 0; + + for (i = 0; i < nsec; i++) + { + grub_disk_addr_t sector = sectors[i] + + grub_partition_get_start (ctx.container); + + if (emu_512b) + { + /* + * When booting off a disk, grub reads in core.img in chunks of + * 127 blocks, which tradionally are 512 bytes long. The block + * length is inherited from old, buggy "Phoenix BIOS" system + * firmare that didn't support reading more data at a time. + * + * The nifty (or scary, depending on your point of view) + * consequence of that is that only each 127th sector is + * directly addressed, while the others sectors are read-in + * via a length parameter based on 512-bytes blocks. + * + * For buggy firmware that is not aware of anything but + * 512-bytes-sector-hardware, but always assumes (and returns) + * 512-bytes blocks, sector *addressing* is based on the native + * hardware sector size (because this parameter is just passed + * to the drive directly), while the length is 512-bytes based. + * + * In 512-bytes-emulation mode, we hence have to modify the + * (internal) blocklist to use native sector adressing, like + * this: + * - put the first 512-bytes block at the start of a physical + * sector (implicitly true through the embedding routine), + * since is the code reading in additional data + * - put the second 512-bytes block at the start of the next + * physical sector (i.e., leaving a gap if necessary) + * - put the next 126 512-bytes blocks right after the + * previous (i.e., no gap) + * - put the next 512-bytes block at the start of the next + * possible physical sector (i.e., leaving a gap) + * - put the next 126 ... + * + * This eventually leads to: + * - correct native-sector-addressing sector addresses at the + * start of each block that NEEDS to be directly addressed + * - fake, incrementing sector addresses for the next 126 + * 512-bytes blocks (which shouldn't hurt since they are + * not used in any meaningful way) + * - implicitly generating a new blocklist after 127 + * 512-bytes blocks because the next native sector address + * will be smaller than the previously written fake sector + * address. + * + * Even more eventually, this scheme should lead to grub + * correctly reading in all data of its core.img file and + * successful booting. + */ + if (0 == i) + { + first_sec = 1; + last_hw_sector = sector / emu_sec_factor; + } + else + { + /* Sanity check. */ + grub_disk_addr_t prev_sector = sectors[i - 1] + + grub_partition_get_start (ctx.container); + + if (1 != (sector - prev_sector)) + { + grub_util_error ("%s", _("embedding non-contiguous " + "ranges is not supported in " + "512-bytes sector emulation " + "mode")); + } + + /* Must be somewhere within a block. */ + ++block_pos; + + if (block_end) + { + /* + * Previously been at a block end, recalculate HW sector. + * This will automatically create a new blocklist if necessary. + */ + last_hw_sector = sector / emu_sec_factor; + + /* Reset block end marker. */ + block_end = 0; + } + else + { + ++last_hw_sector; + } + + if (0x7f == block_pos) + { + /* Last emulated sector in this block. */ + block_end = 1; + } + } + } + else + { + last_hw_sector = sector; + } + + save_blocklists (last_hw_sector, 0, GRUB_DISK_SECTOR_SIZE, &bl); + + if (emu_512b) + { + /* + * N.B.: advancing i here past nsec is safe as long as we don't + * use it - in the worst case, the loop will terminate + * right away. + */ + if (first_sec) + { + /* + * Skip over the next logical sectors to reach the next HW + * sector. + * + * N.B.: decrease by one due to the implicit increment at + * the end of the loop! + */ + i += (emu_sec_factor - 1); + + /* Reset first sector counter to not jump again next time. */ + first_sec = 0; + } + + if (block_end) + { + /* Likewise, if necessary, but with a different offset. */ + i += fill; + + /* Reset position counter within a block. */ + block_pos = 0; + } + } + } + } /* Make sure that the last blocklist is a terminator. */ if (bl.block == bl.first_block) @@ -641,10 +849,97 @@ SETUP (const char *dir, } /* Write the core image onto the disk. */ - for (i = 0; i < nsec; i++) - grub_disk_write (dest_dev->disk, sectors[i], 0, - GRUB_DISK_SECTOR_SIZE, - core_img + i * GRUB_DISK_SECTOR_SIZE); + char *null_sector = xmalloc (GRUB_DISK_SECTOR_SIZE); + memset (null_sector, 0, GRUB_DISK_SECTOR_SIZE); + + { + int fill_null = 0; + unsigned int block_pos = 0; + int first_hw_sec = 0; + int block_end = 0; + char *core_img_location = core_img; + + for (i = 0; i < nsec; i++) + { + grub_disk_addr_t sector = sectors[i]; + char *read_location = core_img_location; + + /* + * For a description of what we're doing here in 512-bytes-emulation + * mode, refer to the blocklist writing section. + * + * The magic in this part is that we have to add some padding to + * regions that will NOT contain data from core.img. + * For simplicity's sake, we'll pad those regions with zero-filled + * 512-bytes blocks. + */ + if (emu_512b) + { + /* Set flag when processing first HW sector. */ + if (0 == i) + { + first_hw_sec = 1; + } + else + { + /* Reset, completely handled first HW sector. */ + if (emu_sec_factor == i) + { + first_hw_sec = 0; + fill_null = 0; + } + + /* Reset, handled full block, up to HW sector end. */ + if ((0x7f + fill) == block_pos) + { + block_end = 0; + fill_null = 0; + block_pos = 0; + } + + ++block_pos; + + if (0x7f == block_pos) + { + /* Last emulated sector in this block. */ + block_end = 1; + } + } + } + + if (fill_null) + { + read_location = null_sector; + } + + grub_util_info ("writing to (virtual) disk sector %" PRIuGRUB_UINT64_T, sectors[i]); + grub_disk_write (dest_dev->disk, + sector, 0, + GRUB_DISK_SECTOR_SIZE, + read_location); + + /* Advance core_img_location if we actually used data from it. */ + if (!fill_null) + { + core_img_location += GRUB_DISK_SECTOR_SIZE; + } + + if (emu_512b) + { + /* + * Note that fill_null will be reset once reaching the HW + * sector end in the first part of the iteration loop. + */ + if ((first_hw_sec) || (block_end)) + { + /* Zero-fill rest of HW sector. */ + fill_null = 1; + } + } + } + } + + grub_free (null_sector); #endif #ifdef GRUB_SETUP_SPARC64 @@ -678,6 +973,12 @@ unable_to_embed: grub_util_error ("%s", _("embedding is not possible, but this is required for " "RAID and LVM install")); + if (emu_512b) + { + grub_util_error ("%s", _("512-bytess-emulation only available when " + "embedding")); + } + { grub_fs_t fs; fs = grub_fs_probe (root_dev); -- 2.25.1 _______________________________________________ Grub-devel mailing list Grub-devel@gnu.org https://lists.gnu.org/mailman/listinfo/grub-devel