summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJoshua Watt <JPEWhacker@gmail.com>2023-08-30 08:20:17 -0600
committerRichard Purdie <richard.purdie@linuxfoundation.org>2023-09-02 11:47:50 +0100
commit93388921ab6fa78658461eefbf0b7beae5a52a9d (patch)
treecf5c32d3c8621b43cb2aa0e8cc73ed687e50028e
parent9d9c7d4b0bb8fe7681496804b7afe8fc2d967419 (diff)
downloadpoky-93388921ab6fa78658461eefbf0b7beae5a52a9d.tar.gz
wic: Add gpt-hybrid partition layout
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. (From OE-Core rev: e50e4c2a5ada6947b3503ca4d8e9c30d359e8a5d) Signed-off-by: Joshua Watt <JPEWhacker@gmail.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
-rw-r--r--scripts/lib/wic/ksparser.py3
-rw-r--r--scripts/lib/wic/partition.py1
-rw-r--r--scripts/lib/wic/plugins/imager/direct.py82
3 files changed, 69 insertions, 17 deletions
diff --git a/scripts/lib/wic/ksparser.py b/scripts/lib/wic/ksparser.py
index 667b2ff9c3..7ef3dc83dd 100644
--- a/scripts/lib/wic/ksparser.py
+++ b/scripts/lib/wic/ksparser.py
@@ -188,11 +188,12 @@ class KickStart():
188 part.add_argument('--uuid') 188 part.add_argument('--uuid')
189 part.add_argument('--fsuuid') 189 part.add_argument('--fsuuid')
190 part.add_argument('--no-fstab-update', action='store_true') 190 part.add_argument('--no-fstab-update', action='store_true')
191 part.add_argument('--mbr', action='store_true')
191 192
192 bootloader = subparsers.add_parser('bootloader') 193 bootloader = subparsers.add_parser('bootloader')
193 bootloader.add_argument('--append') 194 bootloader.add_argument('--append')
194 bootloader.add_argument('--configfile') 195 bootloader.add_argument('--configfile')
195 bootloader.add_argument('--ptable', choices=('msdos', 'gpt'), 196 bootloader.add_argument('--ptable', choices=('msdos', 'gpt', 'gpt-hybrid'),
196 default='msdos') 197 default='msdos')
197 bootloader.add_argument('--timeout', type=int) 198 bootloader.add_argument('--timeout', type=int)
198 bootloader.add_argument('--source') 199 bootloader.add_argument('--source')
diff --git a/scripts/lib/wic/partition.py b/scripts/lib/wic/partition.py
index f11c393df5..b1a2306dd1 100644
--- a/scripts/lib/wic/partition.py
+++ b/scripts/lib/wic/partition.py
@@ -60,6 +60,7 @@ class Partition():
60 self.has_fstab = False 60 self.has_fstab = False
61 self.update_fstab_in_rootfs = False 61 self.update_fstab_in_rootfs = False
62 self.hidden = args.hidden 62 self.hidden = args.hidden
63 self.mbr = args.mbr
63 64
64 self.lineno = lineno 65 self.lineno = lineno
65 self.source_file = "" 66 self.source_file = ""
diff --git a/scripts/lib/wic/plugins/imager/direct.py b/scripts/lib/wic/plugins/imager/direct.py
index 55347f5480..9b619e41c1 100644
--- a/scripts/lib/wic/plugins/imager/direct.py
+++ b/scripts/lib/wic/plugins/imager/direct.py
@@ -342,7 +342,7 @@ class PartitionedImage():
342 # generate parition and filesystem UUIDs 342 # generate parition and filesystem UUIDs
343 for part in self.partitions: 343 for part in self.partitions:
344 if not part.uuid and part.use_uuid: 344 if not part.uuid and part.use_uuid:
345 if self.ptable_format == 'gpt': 345 if self.ptable_format in ('gpt', 'gpt-hybrid'):
346 part.uuid = str(uuid.uuid4()) 346 part.uuid = str(uuid.uuid4())
347 else: # msdos partition table 347 else: # msdos partition table
348 part.uuid = '%08x-%02d' % (self.identifier, part.realnum) 348 part.uuid = '%08x-%02d' % (self.identifier, part.realnum)
@@ -398,6 +398,10 @@ class PartitionedImage():
398 raise WicError("setting custom partition type is not " \ 398 raise WicError("setting custom partition type is not " \
399 "implemented for msdos partitions") 399 "implemented for msdos partitions")
400 400
401 if part.mbr and self.ptable_format != 'gpt-hybrid':
402 raise WicError("Partition may only be included in MBR with " \
403 "a gpt-hybrid partition table")
404
401 # Get the disk where the partition is located 405 # Get the disk where the partition is located
402 self.numpart += 1 406 self.numpart += 1
403 if not part.no_table: 407 if not part.no_table:
@@ -406,7 +410,7 @@ class PartitionedImage():
406 if self.numpart == 1: 410 if self.numpart == 1:
407 if self.ptable_format == "msdos": 411 if self.ptable_format == "msdos":
408 overhead = MBR_OVERHEAD 412 overhead = MBR_OVERHEAD
409 elif self.ptable_format == "gpt": 413 elif self.ptable_format in ("gpt", "gpt-hybrid"):
410 overhead = GPT_OVERHEAD 414 overhead = GPT_OVERHEAD
411 415
412 # Skip one sector required for the partitioning scheme overhead 416 # Skip one sector required for the partitioning scheme overhead
@@ -490,7 +494,7 @@ class PartitionedImage():
490 # Once all the partitions have been layed out, we can calculate the 494 # Once all the partitions have been layed out, we can calculate the
491 # minumim disk size 495 # minumim disk size
492 self.min_size = self.offset 496 self.min_size = self.offset
493 if self.ptable_format == "gpt": 497 if self.ptable_format in ("gpt", "gpt-hybrid"):
494 self.min_size += GPT_OVERHEAD 498 self.min_size += GPT_OVERHEAD
495 499
496 self.min_size *= self.sector_size 500 self.min_size *= self.sector_size
@@ -511,22 +515,38 @@ class PartitionedImage():
511 515
512 return exec_native_cmd(cmd, self.native_sysroot) 516 return exec_native_cmd(cmd, self.native_sysroot)
513 517
518 def _write_identifier(self, device, identifier):
519 logger.debug("Set disk identifier %x", identifier)
520 with open(device, 'r+b') as img:
521 img.seek(0x1B8)
522 img.write(identifier.to_bytes(4, 'little'))
523
524 def _make_disk(self, device, ptable_format, min_size):
525 logger.debug("Creating sparse file %s", device)
526 with open(device, 'w') as sparse:
527 os.ftruncate(sparse.fileno(), min_size)
528
529 logger.debug("Initializing partition table for %s", device)
530 exec_native_cmd("parted -s %s mklabel %s" % (device, ptable_format),
531 self.native_sysroot)
532
533
514 def create(self): 534 def create(self):
515 logger.debug("Creating sparse file %s", self.path) 535 self._make_disk(self.path,
516 with open(self.path, 'w') as sparse: 536 "gpt" if self.ptable_format == "gpt-hybrid" else self.ptable_format,
517 os.ftruncate(sparse.fileno(), self.min_size) 537 self.min_size)
518 538
519 logger.debug("Initializing partition table for %s", self.path) 539 self._write_identifier(self.path, self.identifier)
520 exec_native_cmd("parted -s %s mklabel %s" %
521 (self.path, self.ptable_format), self.native_sysroot)
522 540
523 logger.debug("Set disk identifier %x", self.identifier) 541 if self.ptable_format == "gpt-hybrid":
524 with open(self.path, 'r+b') as img: 542 mbr_path = self.path + ".mbr"
525 img.seek(0x1B8) 543 self._make_disk(mbr_path, "msdos", self.min_size)
526 img.write(self.identifier.to_bytes(4, 'little')) 544 self._write_identifier(mbr_path, self.identifier)
527 545
528 logger.debug("Creating partitions") 546 logger.debug("Creating partitions")
529 547
548 hybrid_mbr_part_num = 0
549
530 for part in self.partitions: 550 for part in self.partitions:
531 if part.num == 0: 551 if part.num == 0:
532 continue 552 continue
@@ -571,7 +591,14 @@ class PartitionedImage():
571 self._create_partition(self.path, part.type, 591 self._create_partition(self.path, part.type,
572 parted_fs_type, part.start, part.size_sec) 592 parted_fs_type, part.start, part.size_sec)
573 593
574 if self.ptable_format == "gpt" and (part.part_name or part.label): 594 if self.ptable_format == "gpt-hybrid" and part.mbr:
595 hybrid_mbr_part_num += 1
596 if hybrid_mbr_part_num > 4:
597 raise WicError("Extended MBR partitions are not supported in hybrid MBR")
598 self._create_partition(mbr_path, "primary",
599 parted_fs_type, part.start, part.size_sec)
600
601 if self.ptable_format in ("gpt", "gpt-hybrid") and (part.part_name or part.label):
575 partition_label = part.part_name if part.part_name else part.label 602 partition_label = part.part_name if part.part_name else part.label
576 logger.debug("partition %d: set name to %s", 603 logger.debug("partition %d: set name to %s",
577 part.num, partition_label) 604 part.num, partition_label)
@@ -586,7 +613,7 @@ class PartitionedImage():
586 (part.num, part.part_type, 613 (part.num, part.part_type,
587 self.path), self.native_sysroot) 614 self.path), self.native_sysroot)
588 615
589 if part.uuid and self.ptable_format == "gpt": 616 if part.uuid and self.ptable_format in ("gpt", "gpt-hybrid"):
590 logger.debug("partition %d: set UUID to %s", 617 logger.debug("partition %d: set UUID to %s",
591 part.num, part.uuid) 618 part.num, part.uuid)
592 exec_native_cmd("sgdisk --partition-guid=%d:%s %s" % \ 619 exec_native_cmd("sgdisk --partition-guid=%d:%s %s" % \
@@ -594,12 +621,16 @@ class PartitionedImage():
594 self.native_sysroot) 621 self.native_sysroot)
595 622
596 if part.active: 623 if part.active:
597 flag_name = "legacy_boot" if self.ptable_format == 'gpt' else "boot" 624 flag_name = "legacy_boot" if self.ptable_format in ('gpt', 'gpt-hybrid') else "boot"
598 logger.debug("Set '%s' flag for partition '%s' on disk '%s'", 625 logger.debug("Set '%s' flag for partition '%s' on disk '%s'",
599 flag_name, part.num, self.path) 626 flag_name, part.num, self.path)
600 exec_native_cmd("parted -s %s set %d %s on" % \ 627 exec_native_cmd("parted -s %s set %d %s on" % \
601 (self.path, part.num, flag_name), 628 (self.path, part.num, flag_name),
602 self.native_sysroot) 629 self.native_sysroot)
630 if self.ptable_format == 'gpt-hybrid' and part.mbr:
631 exec_native_cmd("parted -s %s set %d %s on" % \
632 (mbr_path, hybrid_mbr_part_num, "boot"),
633 self.native_sysroot)
603 if part.system_id: 634 if part.system_id:
604 exec_native_cmd("sfdisk --part-type %s %s %s" % \ 635 exec_native_cmd("sfdisk --part-type %s %s %s" % \
605 (self.path, part.num, part.system_id), 636 (self.path, part.num, part.system_id),
@@ -612,6 +643,25 @@ class PartitionedImage():
612 (self.path, part.num), 643 (self.path, part.num),
613 self.native_sysroot) 644 self.native_sysroot)
614 645
646 if self.ptable_format == "gpt-hybrid":
647 # Write a protective GPT partition
648 hybrid_mbr_part_num += 1
649 if hybrid_mbr_part_num > 4:
650 raise WicError("Extended MBR partitions are not supported in hybrid MBR")
651
652 # parted cannot directly create a protective GPT partition, so
653 # create with an arbitrary type, then change it to the correct type
654 # with sfdisk
655 self._create_partition(mbr_path, "primary", "fat32", 1, GPT_OVERHEAD)
656 exec_native_cmd("sfdisk --part-type %s %d 0xee" % (mbr_path, hybrid_mbr_part_num),
657 self.native_sysroot)
658
659 # Copy hybrid MBR
660 with open(mbr_path, "rb") as mbr_file:
661 with open(self.path, "r+b") as image_file:
662 mbr = mbr_file.read(512)
663 image_file.write(mbr)
664
615 def cleanup(self): 665 def cleanup(self):
616 pass 666 pass
617 667