All of lore.kernel.org
 help / color / mirror / Atom feed
* [OE-core][PATCH] wic: Add gpt-hybrid partition layout
@ 2023-08-30 14:20 Joshua Watt
  0 siblings, 0 replies; only message in thread
From: Joshua Watt @ 2023-08-30 14:20 UTC (permalink / raw)
  To: openembedded-core; +Cc: Joshua Watt

Add support for formatting a disk with a hybrid MBR & GPT partition
scheme. In this scheme, the primary partitioning method is GPT, but a
valid MBR header is also written than can point to a subset of the GPT
partitions on the disk (any partitions marked with the `--mbr` flag will
be included in this MBR). The primary purpose of this method is to allow
for SoCs that can only find a bootloader in an MBR partition to use GPT
once the bootloader is running. As an example, older versions of the
Raspberry Pi firmware can only parse MBR partitions to find a kernel (or
other bootloader like u-boot), but once those have booted GPT partitions
can be used.

In addition to the partitions annotated with the `--mbr`, a "protective"
GPT partition of type 0xEE is added, as the existence of such a
partition is the indication to tooling that this a hybrid MBR and that
the GPT partition table should be parsed instead.

Signed-off-by: Joshua Watt <JPEWhacker@gmail.com>
---
 scripts/lib/wic/ksparser.py              |  3 +-
 scripts/lib/wic/partition.py             |  1 +
 scripts/lib/wic/plugins/imager/direct.py | 82 +++++++++++++++++++-----
 3 files changed, 69 insertions(+), 17 deletions(-)

diff --git a/scripts/lib/wic/ksparser.py b/scripts/lib/wic/ksparser.py
index 667b2ff9c33..7ef3dc83ddc 100644
--- a/scripts/lib/wic/ksparser.py
+++ b/scripts/lib/wic/ksparser.py
@@ -188,11 +188,12 @@ class KickStart():
         part.add_argument('--uuid')
         part.add_argument('--fsuuid')
         part.add_argument('--no-fstab-update', action='store_true')
+        part.add_argument('--mbr', action='store_true')
 
         bootloader = subparsers.add_parser('bootloader')
         bootloader.add_argument('--append')
         bootloader.add_argument('--configfile')
-        bootloader.add_argument('--ptable', choices=('msdos', 'gpt'),
+        bootloader.add_argument('--ptable', choices=('msdos', 'gpt', 'gpt-hybrid'),
                                 default='msdos')
         bootloader.add_argument('--timeout', type=int)
         bootloader.add_argument('--source')
diff --git a/scripts/lib/wic/partition.py b/scripts/lib/wic/partition.py
index f11c393df51..b1a2306dd12 100644
--- a/scripts/lib/wic/partition.py
+++ b/scripts/lib/wic/partition.py
@@ -60,6 +60,7 @@ class Partition():
         self.has_fstab = False
         self.update_fstab_in_rootfs = False
         self.hidden = args.hidden
+        self.mbr = args.mbr
 
         self.lineno = lineno
         self.source_file = ""
diff --git a/scripts/lib/wic/plugins/imager/direct.py b/scripts/lib/wic/plugins/imager/direct.py
index 55347f5480a..9b619e41c11 100644
--- a/scripts/lib/wic/plugins/imager/direct.py
+++ b/scripts/lib/wic/plugins/imager/direct.py
@@ -342,7 +342,7 @@ class PartitionedImage():
         # generate parition and filesystem UUIDs
         for part in self.partitions:
             if not part.uuid and part.use_uuid:
-                if self.ptable_format == 'gpt':
+                if self.ptable_format in ('gpt', 'gpt-hybrid'):
                     part.uuid = str(uuid.uuid4())
                 else: # msdos partition table
                     part.uuid = '%08x-%02d' % (self.identifier, part.realnum)
@@ -398,6 +398,10 @@ class PartitionedImage():
                 raise WicError("setting custom partition type is not " \
                                "implemented for msdos partitions")
 
+            if part.mbr and self.ptable_format != 'gpt-hybrid':
+                raise WicError("Partition may only be included in MBR with " \
+                               "a gpt-hybrid partition table")
+
             # Get the disk where the partition is located
             self.numpart += 1
             if not part.no_table:
@@ -406,7 +410,7 @@ class PartitionedImage():
             if self.numpart == 1:
                 if self.ptable_format == "msdos":
                     overhead = MBR_OVERHEAD
-                elif self.ptable_format == "gpt":
+                elif self.ptable_format in ("gpt", "gpt-hybrid"):
                     overhead = GPT_OVERHEAD
 
                 # Skip one sector required for the partitioning scheme overhead
@@ -490,7 +494,7 @@ class PartitionedImage():
         # Once all the partitions have been layed out, we can calculate the
         # minumim disk size
         self.min_size = self.offset
-        if self.ptable_format == "gpt":
+        if self.ptable_format in ("gpt", "gpt-hybrid"):
             self.min_size += GPT_OVERHEAD
 
         self.min_size *= self.sector_size
@@ -511,22 +515,38 @@ class PartitionedImage():
 
         return exec_native_cmd(cmd, self.native_sysroot)
 
+    def _write_identifier(self, device, identifier):
+        logger.debug("Set disk identifier %x", identifier)
+        with open(device, 'r+b') as img:
+            img.seek(0x1B8)
+            img.write(identifier.to_bytes(4, 'little'))
+
+    def _make_disk(self, device, ptable_format, min_size):
+        logger.debug("Creating sparse file %s", device)
+        with open(device, 'w') as sparse:
+            os.ftruncate(sparse.fileno(), min_size)
+
+        logger.debug("Initializing partition table for %s", device)
+        exec_native_cmd("parted -s %s mklabel %s" % (device, ptable_format),
+                        self.native_sysroot)
+
+
     def create(self):
-        logger.debug("Creating sparse file %s", self.path)
-        with open(self.path, 'w') as sparse:
-            os.ftruncate(sparse.fileno(), self.min_size)
+        self._make_disk(self.path,
+                        "gpt" if self.ptable_format == "gpt-hybrid" else self.ptable_format,
+                        self.min_size)
 
-        logger.debug("Initializing partition table for %s", self.path)
-        exec_native_cmd("parted -s %s mklabel %s" %
-                        (self.path, self.ptable_format), self.native_sysroot)
+        self._write_identifier(self.path, self.identifier)
 
-        logger.debug("Set disk identifier %x", self.identifier)
-        with open(self.path, 'r+b') as img:
-            img.seek(0x1B8)
-            img.write(self.identifier.to_bytes(4, 'little'))
+        if self.ptable_format == "gpt-hybrid":
+            mbr_path = self.path + ".mbr"
+            self._make_disk(mbr_path, "msdos", self.min_size)
+            self._write_identifier(mbr_path, self.identifier)
 
         logger.debug("Creating partitions")
 
+        hybrid_mbr_part_num = 0
+
         for part in self.partitions:
             if part.num == 0:
                 continue
@@ -571,7 +591,14 @@ class PartitionedImage():
             self._create_partition(self.path, part.type,
                                    parted_fs_type, part.start, part.size_sec)
 
-            if self.ptable_format == "gpt" and (part.part_name or part.label):
+            if self.ptable_format == "gpt-hybrid" and part.mbr:
+                hybrid_mbr_part_num += 1
+                if hybrid_mbr_part_num > 4:
+                    raise WicError("Extended MBR partitions are not supported in hybrid MBR")
+                self._create_partition(mbr_path, "primary",
+                                       parted_fs_type, part.start, part.size_sec)
+
+            if self.ptable_format in ("gpt", "gpt-hybrid") and (part.part_name or part.label):
                 partition_label = part.part_name if part.part_name else part.label
                 logger.debug("partition %d: set name to %s",
                              part.num, partition_label)
@@ -586,7 +613,7 @@ class PartitionedImage():
                                          (part.num, part.part_type,
                                           self.path), self.native_sysroot)
 
-            if part.uuid and self.ptable_format == "gpt":
+            if part.uuid and self.ptable_format in ("gpt", "gpt-hybrid"):
                 logger.debug("partition %d: set UUID to %s",
                              part.num, part.uuid)
                 exec_native_cmd("sgdisk --partition-guid=%d:%s %s" % \
@@ -594,12 +621,16 @@ class PartitionedImage():
                                 self.native_sysroot)
 
             if part.active:
-                flag_name = "legacy_boot" if self.ptable_format == 'gpt' else "boot"
+                flag_name = "legacy_boot" if self.ptable_format in ('gpt', 'gpt-hybrid') else "boot"
                 logger.debug("Set '%s' flag for partition '%s' on disk '%s'",
                              flag_name, part.num, self.path)
                 exec_native_cmd("parted -s %s set %d %s on" % \
                                 (self.path, part.num, flag_name),
                                 self.native_sysroot)
+                if self.ptable_format == 'gpt-hybrid' and part.mbr:
+                    exec_native_cmd("parted -s %s set %d %s on" % \
+                                    (mbr_path, hybrid_mbr_part_num, "boot"),
+                                    self.native_sysroot)
             if part.system_id:
                 exec_native_cmd("sfdisk --part-type %s %s %s" % \
                                 (self.path, part.num, part.system_id),
@@ -612,6 +643,25 @@ class PartitionedImage():
                                 (self.path, part.num),
                                 self.native_sysroot)
 
+        if self.ptable_format == "gpt-hybrid":
+            # Write a protective GPT partition
+            hybrid_mbr_part_num += 1
+            if hybrid_mbr_part_num > 4:
+                raise WicError("Extended MBR partitions are not supported in hybrid MBR")
+
+            # parted cannot directly create a protective GPT partition, so
+            # create with an arbitrary type, then change it to the correct type
+            # with sfdisk
+            self._create_partition(mbr_path, "primary", "fat32", 1, GPT_OVERHEAD)
+            exec_native_cmd("sfdisk --part-type %s %d 0xee" % (mbr_path, hybrid_mbr_part_num),
+                            self.native_sysroot)
+
+            # Copy hybrid MBR
+            with open(mbr_path, "rb") as mbr_file:
+                with open(self.path, "r+b") as image_file:
+                    mbr = mbr_file.read(512)
+                    image_file.write(mbr)
+
     def cleanup(self):
         pass
 
-- 
2.34.1



^ permalink raw reply related	[flat|nested] only message in thread

only message in thread, other threads:[~2023-08-30 14:20 UTC | newest]

Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2023-08-30 14:20 [OE-core][PATCH] wic: Add gpt-hybrid partition layout Joshua Watt

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.