[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[PATCH v2 3/7] setup: add support for native sector addressing w/ 512-by
From: |
Mihai Moldovan |
Subject: |
[PATCH v2 3/7] setup: add support for native sector addressing w/ 512-bytes lengths |
Date: |
Sun, 24 May 2020 14:25:13 +0200 |
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..781ad3fc0 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 */, 0 /* 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
- [PATCH v2 0/7] support >512b sector disks with old/buggy firmware, Mihai Moldovan, 2020/05/24
- [PATCH v2 4/7] grub-install: hook up --emu-512b to sector size autodetection in biosdisk, Mihai Moldovan, 2020/05/24
- [PATCH v2 1/7] biosdisk: autodetect hardware sector size (opt-in), Mihai Moldovan, 2020/05/24
- [PATCH v2 7/7] gpt: respect native sector size if set/detected, Mihai Moldovan, 2020/05/24
- [PATCH v2 6/7] diskfilter: write out currently scanned partition, Mihai Moldovan, 2020/05/24
- [PATCH v2 2/7] biosdisk: restore LBA mode after read/write failures, Mihai Moldovan, 2020/05/24
- [PATCH v2 5/7] docs/grub: document --emu-512b install option, Mihai Moldovan, 2020/05/24
- [PATCH v2 3/7] setup: add support for native sector addressing w/ 512-bytes lengths,
Mihai Moldovan <=
- Re: [PATCH v2 0/7] support >512b sector disks with old/buggy firmware, Mihai Moldovan, 2020/05/27