assembler/qemu: gpt support for grub legacy
The GRUB2 bootloader in legacy mode, i.e. non-EFI mode, consists of
several stages. The fist one place in the in the Master Boot Record
of the disk will load and execute the next, second stage, consisting
of core modules and the grub kernel. The first bit is also known as
'boot' and the second as 'core'. When the 'MBR' partition layout is
being used, there is a gap between the Master Boot Record (MBR) and
the first partition (for historical and performance reasons). The
core image normally is placed into this gap (call the MBR gap).
When the partition layout is 'gpt' there is no standard gap that can
be used, instead a special partition ("BIOS boot" [1]) needs to be
created that can store the grub2 core image. Additionally, the 'boot'
image need to modified to point the sector of that partition. The
core image itself also needs to be modified with the information of
the location its own second sector. The location of the pointers
were taken from the grub2 source ([2] at commit [3]). For the 'boot'
image it is 'GRUB_BOOT_MACHINE_KERNEL_SECTOR' (0x5c) from 'pc/boot.h'
and for the core image "0x200 - GRUB_BOOT_MACHINE_LIST_SIZE (12)" to
be found in 'pc/diskboot.S'.
[1] https://en.wikipedia.org/wiki/BIOS_boot_partition
[2] https://github.com/rhboot/grub2
[3] 2a2e10c1b39672de3d5da037a50d5c371f49b40d
This commit is contained in:
parent
c83019a264
commit
814cb4eb80
1 changed files with 63 additions and 5 deletions
|
|
@ -5,6 +5,7 @@ import json
|
|||
import os
|
||||
import socket
|
||||
import shutil
|
||||
import struct
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
|
|
@ -269,6 +270,14 @@ class PartitionTable:
|
|||
return part
|
||||
return None
|
||||
|
||||
def find_bios_boot_partition(self) -> Partition:
|
||||
"""Find the BIOS-boot Partition"""
|
||||
bb_type = "21686148-6449-6E6F-744E-656564454649"
|
||||
for part in self.partitions:
|
||||
if part.type.upper() == bb_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
|
||||
|
|
@ -394,6 +403,51 @@ def grub2_write_core_prep_part(core_f: BinaryIO,
|
|||
shutil.copyfileobj(core_f, image_f)
|
||||
|
||||
|
||||
def grub2_write_core_bios_boot(core_f: BinaryIO,
|
||||
image_f: BinaryIO,
|
||||
pt: PartitionTable):
|
||||
"""Write the core to the bios boot partition"""
|
||||
bb = pt.find_bios_boot_partition()
|
||||
if bb is None:
|
||||
raise ValueError("BIOS-boot partition missing")
|
||||
core_size = os.fstat(core_f.fileno()).st_size
|
||||
if bb.size_in_bytes < core_size:
|
||||
raise ValueError("BIOS-boot partition too small")
|
||||
|
||||
image_f.seek(bb.start_in_bytes)
|
||||
shutil.copyfileobj(core_f, image_f)
|
||||
|
||||
# The core image needs to know from where to load its
|
||||
# second sector so that information needs to be embedded
|
||||
# into the image itself at the right location, i.e.
|
||||
# the "sector start parameter" ("size .long 2, 0"):
|
||||
# 0x200 - GRUB_BOOT_MACHINE_LIST_SIZE (12) = 0x1F4 = 500
|
||||
image_f.seek(bb.start_in_bytes + 500)
|
||||
image_f.write(struct.pack("<Q", bb.start + 1))
|
||||
|
||||
# Additionally, write the location (in sectors) of
|
||||
# the grub core image, into the boot image, so the
|
||||
# latter can find the former. To exact location is
|
||||
# taken from grub2's "boot.S"":
|
||||
# GRUB_BOOT_MACHINE_KERNEL_SECTOR 0x5c (= 92)
|
||||
image_f.seek(0x5c)
|
||||
image_f.write(struct.pack("<Q", bb.start))
|
||||
|
||||
|
||||
def grub2_partition_id(pt: PartitionTable):
|
||||
"""grub2 partition identifier for the partition table"""
|
||||
|
||||
label2grub = {
|
||||
"dos": "msdos",
|
||||
"gpt": "gpt"
|
||||
}
|
||||
|
||||
if pt.label not in label2grub:
|
||||
raise ValueError(f"Unknown partition type: {pt.label}")
|
||||
|
||||
return label2grub[pt.label]
|
||||
|
||||
|
||||
def install_grub2(image: str, pt: PartitionTable, options):
|
||||
"""Install grub2 to image"""
|
||||
platform = options.get("platform", "i386-pc")
|
||||
|
|
@ -421,7 +475,10 @@ def install_grub2(image: str, pt: PartitionTable, options):
|
|||
else:
|
||||
modules = []
|
||||
|
||||
modules += ["part_msdos"]
|
||||
if pt.label == "dos":
|
||||
modules += ["part_msdos"]
|
||||
elif pt.label == "gpt":
|
||||
modules += ["part_gpt"]
|
||||
|
||||
# modules: grubs needs to access the filesystems of /boot/grub2
|
||||
fs_type = boot_part.fs_type or "unknown"
|
||||
|
|
@ -434,10 +491,8 @@ def install_grub2(image: str, pt: PartitionTable, options):
|
|||
raise ValueError(f"unknown boot filesystem type: '{fs_type}'")
|
||||
|
||||
# identify the partition containing boot for grub2
|
||||
if pt.label == "dos":
|
||||
partid = "msdos" + str(boot_part.index + 1)
|
||||
else:
|
||||
raise ValueError(f"unsupported partition type: '{pt.label}'")
|
||||
partid = grub2_partition_id(pt) + str(boot_part.index + 1)
|
||||
print(f"grub2 prefix {partid}")
|
||||
|
||||
# now created the core image
|
||||
subprocess.run(["grub2-mkimage",
|
||||
|
|
@ -462,6 +517,9 @@ def install_grub2(image: str, pt: PartitionTable, options):
|
|||
if platform == "powerpc-ieee1275":
|
||||
# write the core to the PrEP partition
|
||||
grub2_write_core_prep_part(core_f, image_f, pt)
|
||||
elif pt.label == "gpt":
|
||||
# gpt requires a bios-boot partition
|
||||
grub2_write_core_bios_boot(core_f, image_f, pt)
|
||||
else:
|
||||
# embed the core in the MBR gap
|
||||
grub2_write_core_mbrgap(core_f, image_f, pt)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue