From 1da71ebbb40206bba1781bf7d8a2d499a8aaee94 Mon Sep 17 00:00:00 2001 From: Christian Kellner Date: Sun, 22 Dec 2019 17:21:00 +0100 Subject: [PATCH] assembler/qemu: support for ppc64le (open firmware) Introduce support for ppc64le (Open Firmware). The main difference to x86 legacy, i.e. non-efi, is that no stage 1 is required because the core image is stored on a special 'PReP' partition, which must be marked as bootable. The firmware then looks for that partition and directly loads the core from there and executes it. Introduce a `platform` parameter for the grub installer code which controls various platform depended aspects, including a) the path for the modules, b) what modules are compiled into the core, c) if the boot image is written to the MBR and 4) where to write the core image, i.e. mbr-gap or PReP partition. --- assemblers/org.osbuild.qemu | 76 +++++++++++++++++++++++++++++-------- 1 file changed, 61 insertions(+), 15 deletions(-) diff --git a/assemblers/org.osbuild.qemu b/assemblers/org.osbuild.qemu index 1aab59ad..0e5fdb4f 100755 --- a/assemblers/org.osbuild.qemu +++ b/assemblers/org.osbuild.qemu @@ -257,6 +257,18 @@ class PartitionTable: # fallback to the root partition return self.partition_containing_root() + def find_prep_partition(self) -> Partition: + """Find the PReP partition'""" + if self.label == "dos": + prep_type = "41" + elif self.label == "gpt": + prep_type = "9E1A2D38-C612-4316-AA26-8B49521E5A8B" + + for part in self.partitions: + if part.type.upper() == prep_type: + return part + return None + def write_to(self, target, sync=True): """Write the partition table to disk""" # generate the command for sfdisk to create the table @@ -353,8 +365,29 @@ def grub2_write_core_mbrgap(core_f: BinaryIO, shutil.copyfileobj(core_f, image_f) +def grub2_write_core_prep_part(core_f: BinaryIO, + image_f: BinaryIO, + pt: PartitionTable): + """Write the core to the prep partition""" + # On ppc64le with Open Firmware a special partition called + # 'PrEP partition' is used the store the grub2 core; the + # firmware looks for this partition and directly loads and + # executes the core form it. + prep_part = pt.find_prep_partition() + if prep_part is None: + raise ValueError("PrEP partition missing") + + core_size = os.fstat(core_f.fileno()).st_size + assert core_size < prep_part.size_in_bytes - 512 + image_f.seek(prep_part.start_in_bytes) + shutil.copyfileobj(core_f, image_f) + + def install_grub2(image: str, pt: PartitionTable, options): """Install grub2 to image""" + platform = options.get("platform", "i386-pc") + + boot_path = f"/usr/lib/grub/{platform}/boot.img" core_path = "/var/tmp/grub2-core.img" # Create the level-2 & 3 stages of the bootloader, aka the core @@ -369,8 +402,15 @@ def install_grub2(image: str, pt: PartitionTable, options): # find the partition containing /boot/grub2 boot_part = pt.partition_containing_boot() - # modules: access the disk and read the partition table - modules = ["biosdisk", "part_msdos"] + # modules: access the disk and read the partition table: + # on x86 'biosdisk' is used to access the disk, on ppc64le + # with "Open Firmware" the latter is directly loading core + if platform == "i386-pc": + modules = ["biosdisk"] + else: + modules = [] + + modules += ["part_msdos"] # modules: grubs needs to access the filesystems of /boot/grub2 fs_type = boot_part.fs_type or "unknown" @@ -391,27 +431,33 @@ def install_grub2(image: str, pt: PartitionTable, options): # now created the core image subprocess.run(["grub2-mkimage", "--verbose", - "--directory", "/usr/lib/grub/i386-pc", - "--format", "i386-pc", + "--directory", f"/usr/lib/grub/{platform}", "--prefix", f"(,{partid})/boot/grub2", + "--format", platform, "--compression", "auto", - "--output", grub2_core] + + "--output", core_path] + modules, check=True) with open(image, "rb+") as image_f: - # Install the level-1 bootloader into the start of the MBR - # The purpose of this is simply to jump into the level-2 bootloader. - with open("/usr/lib/grub/i386-pc/boot.img", "rb") as boot_f: - # The boot.img file is 512 bytes, but we must only copy the first 440 - # bytes, as these contain the bootstrapping code. The rest of the - # first sector contains the partition table, and must not be - # overwritten. - image_f.write(boot_f.read(440)) + if platform == "i386-pc": + # On x86, install the level-1 bootloader into the start of the MBR + # The purpose of this is simply to jump into the stage-2 bootloader. + # On ppc64le & Open Firmware stage-2 is loaded by the firmware + with open(boot_path, "rb") as boot_f: + # The boot.img file is 512 bytes, but we must only copy the first 440 + # bytes, as these contain the bootstrapping code. The rest of the + # first sector contains the partition table, and must not be + # overwritten. + image_f.write(boot_f.read(440)) with open(core_path, "rb") as core_f: - # Embed the core in the gap between the MBR and the fist partition - grub2_write_core_mbrgap(core_f, image_f, pt) + if platform == "powerpc-ieee1275": + # write the core to the PrEP partition + grub2_write_core_prep_part(core_f, image_f, pt) + else: + # embed the core in the MBR gap + grub2_write_core_mbrgap(core_f, image_f, pt) def main(tree, output_dir, options, loop_client):