patches/genimage: add gpt-protective-first option for hybrid MBR

In a hybrid MBR the conventional layout (used by gdisk and others) is:

  MBR[0] = 0xEE  (GPT protective, covers whole disk)
  MBR[1..3] = user-selected GPT partitions with legacy type codes

genimage currently appends the 0xEE entry after the data partitions,
producing the opposite order.  This is fine for most platforms, but
breaks firmware that detects GPT by inspecting only MBR entry 0: seeing
a non-0xEE type there it falls into pure-MBR mode and cannot look up
GPT partitions by name.

The TF-A partition driver (drivers/partition/partition.c) exhibits
exactly this behaviour: load_mbr_header() copies entry[0] and checks
type == 0xEE to select between MBR and GPT mode.  The MediaTek MT7622
platform (BananaPi BPI-R64) is a concrete example where this matters.

On the other hand, Raspberry Pi firmware scans all MBR slots for a
bootable FAT32 partition.  If slot 0 holds a 0xEE protective entry the
firmware switches to GPT detection, looks for an EFI System Partition
UUID, and reports "no bootable partitions" when none is found.

Add an opt-in hdimage option `gpt-protective-first = true` that places
the 0xEE entry at slot 0 and shifts data partitions to slots 1..3.
The default (false) preserves the existing genimage behaviour so that
platforms like Raspberry Pi continue to work without any changes.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
This commit is contained in:
Joachim Wiberg
2026-03-10 12:11:10 +01:00
parent 35ba2ca37f
commit 784b911253
@@ -0,0 +1,133 @@
From 7f50533b9e57f5e7d1e4dd47c3f43e2d6a9c8b1a Mon Sep 17 00:00:00 2001
From: Joachim Wiberg <troglobit@gmail.com>
Date: Sun, 8 Mar 2026 04:33:56 +0100
Subject: [PATCH] hdimage: add gpt-protective-first option for hybrid MBR
Organization: Wires
In a hybrid MBR the conventional layout (used by gdisk and others) is:
MBR[0] = 0xEE (GPT protective, covers whole disk)
MBR[1..3] = user-selected GPT partitions with legacy type codes
genimage currently appends the 0xEE entry after the data partitions,
producing the opposite order. This is fine for most platforms, but
breaks firmware that detects GPT by inspecting only MBR entry 0: seeing
a non-0xEE type there it falls into pure-MBR mode and cannot look up
GPT partitions by name.
The TF-A partition driver (drivers/partition/partition.c) exhibits
exactly this behaviour: load_mbr_header() copies entry[0] and checks
type == 0xEE to select between MBR and GPT mode. The MediaTek MT7622
platform (BananaPi BPI-R64) is a concrete example where this matters.
On the other hand, Raspberry Pi firmware scans all MBR slots for a
bootable FAT32 partition. If slot 0 holds a 0xEE protective entry the
firmware switches to GPT detection, looks for an EFI System Partition
UUID, and reports "no bootable partitions" when none is found.
Add an opt-in hdimage option `gpt-protective-first = true` that places
the 0xEE entry at slot 0 and shifts data partitions to slots 1..3.
The default (false) preserves the existing genimage behaviour so that
platforms like Raspberry Pi continue to work without any changes.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
---
README.rst | 10 ++++++++++
image-hd.c | 27 ++++++++++++++++++++++-----
2 files changed, 32 insertions(+), 5 deletions(-)
diff --git a/README.rst b/README.rst
index 9a95f75..f45fb93 100644
--- a/README.rst
+++ b/README.rst
@@ -480,6 +480,16 @@ Options:
placed at 512 bytes (sector 1). Defaults to 1024 bytes (sector 2).
:gpt-no-backup: Boolean. If true, then the backup partition table at the end of
the image is not written.
+:gpt-protective-first: Boolean. If true, then for ``hybrid`` partition tables, the GPT
+ protective entry (type ``0xEE``) is placed at MBR slot 0 and
+ data partitions fill slots 1..3. This matches the layout
+ produced by tools such as gdisk and is required by firmware
+ that identifies GPT disks by inspecting only MBR entry 0 —
+ for example the TF-A partition driver used on MediaTek MT7622
+ (BananaPi BPI-R64). When false (the default), the 0xEE entry
+ is placed after the data partitions, which is compatible with
+ firmware that scans MBR slots for a bootable partition (e.g.
+ Raspberry Pi).
:disk-uuid: UUID string used as disk id in GPT partitioning. Defaults to a
random value.
:fill: If this is set to true, then the image file will be filled
diff --git a/image-hd.c b/image-hd.c
index 274c6ac..610d9d1 100644
--- a/image-hd.c
+++ b/image-hd.c
@@ -46,6 +46,7 @@ struct hdimage {
int table_type;
unsigned long long gpt_location;
cfg_bool_t gpt_no_backup;
+ cfg_bool_t gpt_protective_first;
cfg_bool_t fill;
unsigned long long file_size;
};
@@ -152,6 +153,26 @@ 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));
+ /*
+ * For hybrid MBR, optionally write the GPT protective entry (0xEE) at
+ * slot 0 first when gpt-protective-first = true. This matches the
+ * conventional layout used by tools such as gdisk and is required by
+ * firmware that detects GPT by inspecting only MBR[0] (e.g. the TF-A
+ * partition driver on MediaTek MT7622). Leave slot 0 free by default
+ * so that firmware scanning for a bootable MBR partition (e.g.
+ * Raspberry Pi) can find data partitions at slots 0..2.
+ */
+ if (hd->table_type == TYPE_HYBRID && hd->gpt_protective_first) {
+ struct mbr_partition_entry *entry = &mbr.part_entry[0];
+
+ entry->boot = 0x00;
+ entry->partition_type = 0xee;
+ entry->relative_sectors = 1;
+ entry->total_sectors = hd->gpt_location / 512 + GPT_SECTORS - 2;
+ hdimage_setup_chs(entry, 0);
+ i = 1;
+ }
+
list_for_each_entry(part, partitions, list) {
struct mbr_partition_entry *entry;
@@ -176,13 +197,13 @@ static int hdimage_insert_mbr(struct image *image, struct list_head *partitions)
i++;
}
- if (hd->table_type == TYPE_HYBRID) {
+ /* For hybrid MBR without gpt-protective-first, append 0xEE after data partitions. */
+ if (hd->table_type == TYPE_HYBRID && !hd->gpt_protective_first) {
struct mbr_partition_entry *entry;
entry = &mbr.part_entry[i];
entry->boot = 0x00;
-
entry->partition_type = 0xee;
entry->relative_sectors = 1;
entry->total_sectors = hd->gpt_location / 512 + GPT_SECTORS - 2;
@@ -978,6 +999,7 @@ static int hdimage_setup(struct image *image, cfg_t *cfg)
table_type = cfg_getstr(cfg, "partition-table-type");
hd->gpt_location = cfg_getint_suffix(cfg, "gpt-location");
hd->gpt_no_backup = cfg_getbool(cfg, "gpt-no-backup");
+ hd->gpt_protective_first = cfg_getbool(cfg, "gpt-protective-first");
hd->fill = cfg_getbool(cfg, "fill");
if (is_block_device(imageoutfile(image))) {
@@ -1227,6 +1249,7 @@ static cfg_opt_t hdimage_opts[] = {
CFG_BOOL("gpt", cfg_false, CFGF_NODEFAULT),
CFG_STR("gpt-location", NULL, CFGF_NONE),
CFG_BOOL("gpt-no-backup", cfg_false, CFGF_NONE),
+ CFG_BOOL("gpt-protective-first", cfg_false, CFGF_NONE),
CFG_BOOL("fill", cfg_false, CFGF_NONE),
CFG_END()
};
--
2.43.0