Skip to content

Commit

Permalink
image-hd: add support for overridding optional fields of the MBR
Browse files Browse the repository at this point in the history
The 'holes' property allows to describe holes in an image, for example
to allow writing an image that spans accross the MBR partition
table. The image-hd code creates a fake partition spanning from 440
bytes to 512 bytes to protect the area used by the partition table.

However, it turns out that the Amlogic SoCs have a bootloader that is
precisely written accross the partition table, with some bits before
the partition table and some bits after. But those Amlogic SoCs use
the first 444 bytes, instead of just the first 440 bytes (see [1]).

This is possible because the first 2 fields of the MBR are optional
fields (see [2]). The first optional field is the disk signature (4
bytes), the second optional field is the "copy protect" field (2
bytes).

Therefore, to write a bootloader image for the Amlogic SoCs, we need
to write something like:

image fip/u-boot.bin.sd.bin {
        file {
                holes = {"(444; 512)"}
        }
}

But that isn't accepted because it overlaps with the fake MBR
partition that starts at offset 440.

In order to allow this, the present commit adds a new
'mbr-skip-optionals' property, which tells genimage that we would like
to skip writing those optional fields, allowing the full 446 bytes to
be used, as is needed for Amlogic SoC bootloader images.

Implementation note: we would have preferred to write:

	mbr_data += sizeof(struct mbr_tail_optionals);

but genimage has chosen to not allow arithmetic on void* pointers, so
instead we have chosen to use:

	mbr_data = &mbr.part_entry[0];

when adjusting the start of the MBR data that have to be inserted
into the image.

This commit also adds two test cases for this new feature:

- One positive test case, where we verify that we can write an image
  where the hole is (444, 512) and the mbr-skip-optionals = "true"
  option is passed

- One negative test case, where we verify that we are not allowed to
  write an image where the hole is (444, 512) when mbr-skip-optionals
  = "false".

[1] http://docs.khadas.com/products/sbc/vim3/development/create-bootable-tf-card
[2] https://wiki.osdev.org/MBR_(x86)

Co-Developed-by: Romain Naour <[email protected]>
Signed-off-by: Thomas Petazzoni <[email protected]>
  • Loading branch information
tpetazzoni committed Jul 14, 2024
1 parent 00009af commit 4fe428f
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 5 deletions.
1 change: 1 addition & 0 deletions Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ EXTRA_DIST += \
test/hdimage7.config \
test/hdimage7.fdisk \
test/hole.config \
test/hole-skip-optionals.config \
test/hdimage-hybrid.config \
test/hdimage-hybrid.fdisk \
test/hdimage-fail1.config \
Expand Down
3 changes: 3 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,9 @@ Options:
up to the end of the last partition. This might make the file
bigger. This is necessary if the image will be processed by
such tools as libvirt, libguestfs or parted.
:mbr-skip-optionals: Boolean. If true, then the 6 bytes of optional fields in the MBR
are skipped, allowing an image occupying the first 446 bytes
instead of just the first 440 bytes to be written.

iso
***
Expand Down
41 changes: 36 additions & 5 deletions image-hd.c
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ struct hdimage {
unsigned long long gpt_location;
cfg_bool_t gpt_no_backup;
cfg_bool_t fill;
cfg_bool_t mbr_skip_optionals;
unsigned long long file_size;
};

Expand All @@ -64,9 +65,14 @@ struct mbr_partition_entry {
} __attribute__((packed));
ct_assert(sizeof(struct mbr_partition_entry) == 16);

struct mbr_tail {
struct mbr_tail_optionals {
uint32_t disk_signature;
uint16_t copy_protect;
} __attribute__((packed));
ct_assert(sizeof(struct mbr_tail_optionals) == 6);

struct mbr_tail {
struct mbr_tail_optionals optionals;
struct mbr_partition_entry part_entry[4];
uint16_t boot_signature;
} __attribute__((packed));
Expand Down Expand Up @@ -141,6 +147,9 @@ static int hdimage_insert_mbr(struct image *image, struct list_head *partitions)
struct hdimage *hd = image->handler_priv;
struct mbr_tail mbr;
struct partition *part;
unsigned long long mbr_offset;
const void *mbr_data;
size_t mbr_size;
int ret, i = 0;

if (hd->table_type == TYPE_HYBRID) {
Expand All @@ -150,7 +159,7 @@ static int hdimage_insert_mbr(struct image *image, struct list_head *partitions)
}

memset(&mbr, 0, sizeof(mbr));
memcpy(&mbr.disk_signature, &hd->disksig, sizeof(hd->disksig));
memcpy(&mbr.optionals.disk_signature, &hd->disksig, sizeof(hd->disksig));

list_for_each_entry(part, partitions, list) {
struct mbr_partition_entry *entry;
Expand Down Expand Up @@ -192,7 +201,16 @@ static int hdimage_insert_mbr(struct image *image, struct list_head *partitions)

mbr.boot_signature = htole16(0xaa55);

ret = insert_data(image, &mbr, imageoutfile(image), sizeof(mbr), 440);
mbr_offset = 440;
mbr_size = sizeof(mbr);
mbr_data = &mbr;
if (hd->mbr_skip_optionals) {
mbr_offset += sizeof(struct mbr_tail_optionals);
mbr_size -= sizeof(struct mbr_tail_optionals);
mbr_data = &mbr.part_entry[0];
}

ret = insert_data(image, mbr_data, imageoutfile(image), mbr_size, mbr_offset);
if (ret) {
if (hd->table_type == TYPE_HYBRID) {
image_error(image, "failed to write hybrid MBR\n");
Expand Down Expand Up @@ -968,6 +986,7 @@ static int hdimage_setup(struct image *image, cfg_t *cfg)
hd->gpt_location = cfg_getint_suffix(cfg, "gpt-location");
hd->gpt_no_backup = cfg_getbool(cfg, "gpt-no-backup");
hd->fill = cfg_getbool(cfg, "fill");
hd->mbr_skip_optionals = cfg_getbool(cfg, "mbr-skip-optionals");

if (is_block_device(imageoutfile(image))) {
if (image->size) {
Expand Down Expand Up @@ -1029,8 +1048,19 @@ static int hdimage_setup(struct image *image, cfg_t *cfg)
}

if (hd->table_type != TYPE_NONE) {
struct partition *mbr = fake_partition("[MBR]", 512 - sizeof(struct mbr_tail),
sizeof(struct mbr_tail));
struct partition *mbr;
unsigned long long mbr_offset;
unsigned long long mbr_size;

mbr_offset = 512 - sizeof(struct mbr_tail);
mbr_size = sizeof(struct mbr_tail);

if (hd->mbr_skip_optionals) {
mbr_offset += sizeof(struct mbr_tail_optionals);
mbr_size -= sizeof(struct mbr_tail_optionals);
}

mbr = fake_partition("[MBR]", mbr_offset, mbr_size);

list_add_tail(&mbr->list, &image->partitions);
now = partition_end(mbr);
Expand Down Expand Up @@ -1215,6 +1245,7 @@ static cfg_opt_t hdimage_opts[] = {
CFG_STR("gpt-location", NULL, CFGF_NONE),
CFG_BOOL("gpt-no-backup", cfg_false, CFGF_NONE),
CFG_BOOL("fill", cfg_false, CFGF_NONE),
CFG_BOOL("mbr-skip-optionals", cfg_false, CFGF_NONE),
CFG_END()
};

Expand Down
14 changes: 14 additions & 0 deletions test/hdimage.test
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,20 @@ test_expect_success "bootloader-hole5" "
setup_gpt_files &&
OFFSET=128K run_genimage hole.config"

# Test the mbr-skip-optionals
test_expect_success "bootloader-hole6" "
rm -rf input &&
mkdir input &&
truncate -s 100K input/bootloader.img &&
MBR_SKIP_OPTIONALS=true run_genimage hole-skip-optionals.config"

# Test the mbr-skip-optionals
test_expect_success "bootloader-hole7" "
rm -rf input &&
mkdir input &&
truncate -s 100K input/bootloader.img &&
MBR_SKIP_OPTIONALS=false test_must_fail run_genimage hole-skip-optionals.config"

test_expect_success hexdump "hdimage no-partition" "
dd if=/dev/zero bs=1 count=100 | tr '\000' '\377' > input/block1.img &&
dd if=/dev/zero bs=1 count=50 | tr '\000' '\252' > input/block2.img &&
Expand Down
17 changes: 17 additions & 0 deletions test/hole-skip-optionals.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
image test.hole {
hdimage {
mbr-skip-optionals = "${MBR_SKIP_OPTIONALS}"
}

partition bootloader {
in-partition-table = false
offset = 0
image = "bootloader.img"
}
}

image bootloader.img {
file {
holes = "(444; 512)"
}
}

0 comments on commit 4fe428f

Please sign in to comment.