diff options
author | Joshua Watt <JPEWhacker@gmail.com> | 2023-08-30 08:20:17 -0600 |
---|---|---|
committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2023-09-02 11:47:50 +0100 |
commit | 93388921ab6fa78658461eefbf0b7beae5a52a9d (patch) | |
tree | cf5c32d3c8621b43cb2aa0e8cc73ed687e50028e | |
parent | 9d9c7d4b0bb8fe7681496804b7afe8fc2d967419 (diff) | |
download | poky-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.py | 3 | ||||
-rw-r--r-- | scripts/lib/wic/partition.py | 1 | ||||
-rw-r--r-- | 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 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 | ||