mirror of
https://git.launchpad.net/livecd-rootfs
synced 2026-02-20 08:53:44 +00:00
Merge branch 'fix-boot-paths' into ubuntu/master
This commit is contained in:
commit
92c29ecbf2
2
debian/changelog
vendored
2
debian/changelog
vendored
@ -10,6 +10,8 @@ livecd-rootfs (26.04.20) UNRELEASED; urgency=medium
|
||||
[ Michael Hudson-Doyle ]
|
||||
* 030-ubuntu-live-system-seed.binary: do not run if there is no layer to
|
||||
install the system, in particular on arm64.
|
||||
* Fix some path confusion in the new isobuilder.boot package and refactor
|
||||
grub config generation to be more string based.
|
||||
|
||||
-- Michael Hudson-Doyle <michael.hudson@ubuntu.com> Thu, 19 Feb 2026 19:55:26 +1300
|
||||
|
||||
|
||||
@ -17,6 +17,7 @@ def make_boot_configurator_for_arch(
|
||||
arch: str,
|
||||
logger: "Logger",
|
||||
apt_state: "AptStateManager",
|
||||
workdir: pathlib.Path,
|
||||
iso_root: pathlib.Path,
|
||||
) -> "BaseBootConfigurator":
|
||||
"""Factory function to create boot configurator for a specific architecture."""
|
||||
@ -24,23 +25,23 @@ def make_boot_configurator_for_arch(
|
||||
case "amd64":
|
||||
from .amd64 import AMD64BootConfigurator
|
||||
|
||||
return AMD64BootConfigurator(logger, apt_state, iso_root)
|
||||
return AMD64BootConfigurator(logger, apt_state, workdir, iso_root)
|
||||
case "arm64":
|
||||
from .arm64 import ARM64BootConfigurator
|
||||
|
||||
return ARM64BootConfigurator(logger, apt_state, iso_root)
|
||||
return ARM64BootConfigurator(logger, apt_state, workdir, iso_root)
|
||||
case "ppc64el":
|
||||
from .ppc64el import PPC64ELBootConfigurator
|
||||
|
||||
return PPC64ELBootConfigurator(logger, apt_state, iso_root)
|
||||
return PPC64ELBootConfigurator(logger, apt_state, workdir, iso_root)
|
||||
case "riscv64":
|
||||
from .riscv64 import RISCV64BootConfigurator
|
||||
|
||||
return RISCV64BootConfigurator(logger, apt_state, iso_root)
|
||||
return RISCV64BootConfigurator(logger, apt_state, workdir, iso_root)
|
||||
case "s390x":
|
||||
from .s390x import S390XBootConfigurator
|
||||
|
||||
return S390XBootConfigurator(logger, apt_state, iso_root)
|
||||
return S390XBootConfigurator(logger, apt_state, workdir, iso_root)
|
||||
case _:
|
||||
raise ValueError(f"Unsupported architecture: {arch}")
|
||||
|
||||
|
||||
@ -3,8 +3,9 @@
|
||||
import pathlib
|
||||
import shutil
|
||||
|
||||
from .uefi import UEFIBootConfigurator
|
||||
from .base import default_kernel_params
|
||||
from .grub import copy_grub_modules
|
||||
from .uefi import UEFIBootConfigurator
|
||||
|
||||
|
||||
CALAMARES_PROJECTS = ["kubuntu", "lubuntu"]
|
||||
@ -51,16 +52,13 @@ class AMD64BootConfigurator(UEFIBootConfigurator):
|
||||
opts.extend(
|
||||
[
|
||||
"--grub2-mbr",
|
||||
self.grub_dir.joinpath("usr/lib/grub/i386-pc/boot_hybrid.img"),
|
||||
self.scratch.joinpath("boot_hybrid.img"),
|
||||
]
|
||||
)
|
||||
|
||||
# ## Set up the mkisofs options for UEFI boot.
|
||||
opts.extend(self.get_uefi_mkisofs_opts())
|
||||
|
||||
# ## Add cd-boot-tree to the ISO
|
||||
opts.append(str(self.boot_tree))
|
||||
|
||||
return opts
|
||||
|
||||
def extract_files(self) -> None:
|
||||
@ -71,115 +69,113 @@ class AMD64BootConfigurator(UEFIBootConfigurator):
|
||||
|
||||
# AMD64-specific: Add BIOS/legacy boot files
|
||||
with self.logger.logged("adding BIOS/legacy boot files"):
|
||||
self.download_and_extract_package("grub-pc-bin", self.grub_dir)
|
||||
grub_pc_pkg_dir = self.scratch.joinpath("grub-pc-pkg")
|
||||
self.download_and_extract_package("grub-pc-bin", grub_pc_pkg_dir)
|
||||
|
||||
grub_boot_dir = self.boot_tree.joinpath("boot", "grub", "i386-pc")
|
||||
grub_boot_dir = self.iso_root.joinpath("boot", "grub", "i386-pc")
|
||||
grub_boot_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
src_grub_dir = self.grub_dir.joinpath("usr", "lib", "grub", "i386-pc")
|
||||
src_grub_dir = grub_pc_pkg_dir.joinpath("usr", "lib", "grub", "i386-pc")
|
||||
|
||||
shutil.copy(src_grub_dir.joinpath("eltorito.img"), grub_boot_dir)
|
||||
shutil.copy(src_grub_dir.joinpath("boot_hybrid.img"), self.scratch)
|
||||
|
||||
self.copy_grub_modules(
|
||||
src_grub_dir, grub_boot_dir, ["*.mod", "*.lst", "*.o"]
|
||||
copy_grub_modules(
|
||||
grub_pc_pkg_dir,
|
||||
self.iso_root,
|
||||
"i386-pc",
|
||||
["*.mod", "*.lst", "*.o"],
|
||||
)
|
||||
|
||||
def generate_grub_config(self) -> None:
|
||||
"""Generate grub.cfg and loopback.cfg for the boot tree."""
|
||||
boot_grub_dir = self.boot_tree.joinpath("boot", "grub")
|
||||
boot_grub_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
grub_cfg = boot_grub_dir.joinpath("grub.cfg")
|
||||
def generate_grub_config(self) -> str:
|
||||
"""Generate grub.cfg content for AMD64."""
|
||||
result = self.grub_header()
|
||||
|
||||
if self.project == "ubuntu-mini-iso":
|
||||
self.write_grub_header(grub_cfg)
|
||||
with grub_cfg.open("a") as f:
|
||||
f.write(
|
||||
"""menuentry "Choose an Ubuntu version to install" {
|
||||
result += """\
|
||||
menuentry "Choose an Ubuntu version to install" {
|
||||
set gfxpayload=keep
|
||||
linux /casper/vmlinuz iso-chooser-menu ip=dhcp ---
|
||||
initrd /casper/initrd
|
||||
linux /casper/vmlinuz iso-chooser-menu ip=dhcp ---
|
||||
initrd /casper/initrd
|
||||
}
|
||||
"""
|
||||
)
|
||||
return
|
||||
return result
|
||||
|
||||
# Generate grub.cfg
|
||||
kernel_params = default_kernel_params(self.project)
|
||||
|
||||
# Write common GRUB header
|
||||
self.write_grub_header(grub_cfg)
|
||||
|
||||
# Main menu entry
|
||||
with grub_cfg.open("a") as f:
|
||||
f.write(
|
||||
f"""menuentry "Try or Install {self.humanproject}" {{
|
||||
result += f"""\
|
||||
menuentry "Try or Install {self.humanproject}" {{
|
||||
set gfxpayload=keep
|
||||
linux /casper/vmlinuz {kernel_params}
|
||||
initrd /casper/initrd
|
||||
linux /casper/vmlinuz {kernel_params}
|
||||
initrd /casper/initrd
|
||||
}}
|
||||
"""
|
||||
)
|
||||
|
||||
# All but server get safe-graphics mode
|
||||
if self.project != "ubuntu-server":
|
||||
with grub_cfg.open("a") as f:
|
||||
f.write(
|
||||
f"""menuentry "{self.humanproject} (safe graphics)" {{
|
||||
result += f"""\
|
||||
menuentry "{self.humanproject} (safe graphics)" {{
|
||||
set gfxpayload=keep
|
||||
linux /casper/vmlinuz nomodeset {kernel_params}
|
||||
initrd /casper/initrd
|
||||
linux /casper/vmlinuz nomodeset {kernel_params}
|
||||
initrd /casper/initrd
|
||||
}}
|
||||
"""
|
||||
)
|
||||
|
||||
# ubiquity based projects get OEM mode
|
||||
if "maybe-ubiquity" in kernel_params:
|
||||
oem_kernel_params = kernel_params.replace(
|
||||
"maybe-ubiquity", "only-ubiquity oem-config/enable=true"
|
||||
)
|
||||
with grub_cfg.open("a") as f:
|
||||
f.write(
|
||||
f"""menuentry "OEM install (for manufacturers)" {{
|
||||
result += f"""\
|
||||
menuentry "OEM install (for manufacturers)" {{
|
||||
set gfxpayload=keep
|
||||
linux /casper/vmlinuz {oem_kernel_params}
|
||||
initrd /casper/initrd
|
||||
linux /casper/vmlinuz {oem_kernel_params}
|
||||
initrd /casper/initrd
|
||||
}}
|
||||
"""
|
||||
)
|
||||
|
||||
# Calamares-based projects get OEM mode
|
||||
if self.project in CALAMARES_PROJECTS:
|
||||
with grub_cfg.open("a") as f:
|
||||
f.write(
|
||||
f"""menuentry "OEM install (for manufacturers)" {{
|
||||
result += f"""\
|
||||
menuentry "OEM install (for manufacturers)" {{
|
||||
set gfxpayload=keep
|
||||
linux /casper/vmlinuz {kernel_params} oem-config/enable=true
|
||||
initrd /casper/initrd
|
||||
linux /casper/vmlinuz {kernel_params} oem-config/enable=true
|
||||
initrd /casper/initrd
|
||||
}}
|
||||
"""
|
||||
)
|
||||
|
||||
# Currently only server is built with HWE, hence no safe-graphics/OEM
|
||||
if self.hwe:
|
||||
with grub_cfg.open("a") as f:
|
||||
f.write(
|
||||
f"""menuentry "{self.humanproject} with the HWE kernel" {{
|
||||
result += f"""\
|
||||
menuentry "{self.humanproject} with the HWE kernel" {{
|
||||
set gfxpayload=keep
|
||||
linux /casper/hwe-vmlinuz {kernel_params}
|
||||
initrd /casper/hwe-initrd
|
||||
linux /casper/hwe-vmlinuz {kernel_params}
|
||||
initrd /casper/hwe-initrd
|
||||
}}
|
||||
"""
|
||||
)
|
||||
|
||||
# Create the loopback config, based on the main config
|
||||
with grub_cfg.open("r") as f:
|
||||
content = f.read()
|
||||
# UEFI Entries (wrapped in grub_platform check for dual BIOS/UEFI support)
|
||||
uefi_menu_entries = self.uefi_menu_entries()
|
||||
|
||||
# sed: delete from line 1 to menu_color_highlight, delete from
|
||||
# grub_platform to end and replace '---' with
|
||||
# 'iso-scan/filename=${iso_path} ---' in lines with 'linux'
|
||||
lines = content.split("\n")
|
||||
result += f"""\
|
||||
grub_platform
|
||||
if [ "$grub_platform" = "efi" ]; then
|
||||
{uefi_menu_entries}\
|
||||
fi
|
||||
"""
|
||||
|
||||
return result
|
||||
|
||||
@staticmethod
|
||||
def generate_loopback_config(grub_content: str) -> str:
|
||||
"""Derive loopback.cfg from grub.cfg content.
|
||||
|
||||
Strips the header (up to menu_color_highlight) and the UEFI
|
||||
trailer (from grub_platform to end), and adds iso-scan/filename
|
||||
to linux lines.
|
||||
"""
|
||||
lines = grub_content.split("\n")
|
||||
start_idx = 0
|
||||
for i, line in enumerate(lines):
|
||||
if "menu_color_highlight" in line:
|
||||
@ -202,16 +198,19 @@ class AMD64BootConfigurator(UEFIBootConfigurator):
|
||||
for line in loopback_lines
|
||||
]
|
||||
|
||||
loopback_cfg = boot_grub_dir.joinpath("loopback.cfg")
|
||||
with loopback_cfg.open("w") as f:
|
||||
f.write("\n".join(loopback_lines))
|
||||
return "\n".join(loopback_lines)
|
||||
|
||||
# UEFI Entries (wrapped in grub_platform check for dual BIOS/UEFI support)
|
||||
with grub_cfg.open("a") as f:
|
||||
f.write("grub_platform\n")
|
||||
f.write('if [ "$grub_platform" = "efi" ]; then\n')
|
||||
|
||||
self.write_uefi_menu_entries(grub_cfg)
|
||||
|
||||
with grub_cfg.open("a") as f:
|
||||
f.write("fi\n")
|
||||
def make_bootable(
|
||||
self,
|
||||
project: str,
|
||||
capproject: str,
|
||||
subarch: str,
|
||||
hwe: bool,
|
||||
) -> None:
|
||||
"""Make the ISO bootable, including generating loopback.cfg."""
|
||||
super().make_bootable(project, capproject, subarch, hwe)
|
||||
grub_cfg = self.iso_root.joinpath("boot", "grub", "grub.cfg")
|
||||
grub_content = grub_cfg.read_text()
|
||||
self.iso_root.joinpath("boot", "grub", "loopback.cfg").write_text(
|
||||
self.generate_loopback_config(grub_content)
|
||||
)
|
||||
|
||||
@ -26,7 +26,6 @@ class ARM64BootConfigurator(UEFIBootConfigurator):
|
||||
opts.extend(self.get_uefi_mkisofs_opts())
|
||||
# ARM64-specific: partition cylinder alignment
|
||||
opts.extend(["-partition_cyl_align", "all"])
|
||||
opts.append(self.boot_tree)
|
||||
return opts
|
||||
|
||||
def extract_files(self) -> None:
|
||||
@ -34,19 +33,15 @@ class ARM64BootConfigurator(UEFIBootConfigurator):
|
||||
with self.logger.logged("extracting ARM64 boot files"):
|
||||
self.extract_uefi_files()
|
||||
|
||||
def generate_grub_config(self) -> None:
|
||||
def generate_grub_config(self) -> str:
|
||||
"""Generate grub.cfg for ARM64."""
|
||||
kernel_params = default_kernel_params(self.project)
|
||||
|
||||
grub_cfg = self.grub_dir.joinpath("grub.cfg")
|
||||
|
||||
# Write common GRUB header
|
||||
self.write_grub_header(grub_cfg)
|
||||
result = self.grub_header()
|
||||
|
||||
# ARM64-specific: Snapdragon workarounds
|
||||
with grub_cfg.open("a") as f:
|
||||
f.write(
|
||||
"""set cmdline=
|
||||
result += f"""\
|
||||
set cmdline=
|
||||
smbios --type 4 --get-string 5 --set proc_version
|
||||
regexp "Snapdragon.*" "$proc_version"
|
||||
if [ $? = 0 ]; then
|
||||
@ -59,16 +54,14 @@ if [ $? = 0 ]; then
|
||||
fi
|
||||
|
||||
menuentry "Try or Install {self.humanproject}" {{
|
||||
\tset gfxpayload=keep
|
||||
\tlinux\t/casper/vmlinuz $cmdline {kernel_params} console=tty0
|
||||
\tinitrd\t/casper/initrd
|
||||
set gfxpayload=keep
|
||||
linux /casper/vmlinuz $cmdline {kernel_params} console=tty0
|
||||
initrd /casper/initrd
|
||||
}}
|
||||
"""
|
||||
)
|
||||
|
||||
# HWE kernel option if available
|
||||
self.write_hwe_menu_entry(
|
||||
grub_cfg,
|
||||
result += self.hwe_menu_entry(
|
||||
"vmlinuz",
|
||||
f"{kernel_params} console=tty0",
|
||||
extra_params="$cmdline ",
|
||||
@ -78,4 +71,6 @@ menuentry "Try or Install {self.humanproject}" {{
|
||||
# but it's not actually set anywhere in the grub.cfg, so we omit it here
|
||||
|
||||
# UEFI Entries (ARM64 is UEFI-only, no grub_platform check needed)
|
||||
self.write_uefi_menu_entries(grub_cfg)
|
||||
result += self.uefi_menu_entries()
|
||||
|
||||
return result
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
"""Base classes and helper functions for boot configuration."""
|
||||
|
||||
import pathlib
|
||||
import shutil
|
||||
import subprocess
|
||||
import tempfile
|
||||
from abc import ABC, abstractmethod
|
||||
@ -34,16 +33,13 @@ class BaseBootConfigurator(ABC):
|
||||
self,
|
||||
logger: Logger,
|
||||
apt_state: AptStateManager,
|
||||
workdir: pathlib.Path,
|
||||
iso_root: pathlib.Path,
|
||||
) -> None:
|
||||
self.logger = logger
|
||||
self.apt_state = apt_state
|
||||
self.iso_root = iso_root
|
||||
|
||||
def create_dirs(self, workdir):
|
||||
self.scratch = workdir.joinpath("boot-stuff")
|
||||
self.scratch.mkdir(exist_ok=True)
|
||||
self.boot_tree = self.scratch.joinpath("cd-boot-tree")
|
||||
self.iso_root = iso_root
|
||||
|
||||
def download_and_extract_package(
|
||||
self, pkg_name: str, target_dir: pathlib.Path
|
||||
@ -65,14 +61,6 @@ class BaseBootConfigurator(ABC):
|
||||
dpkg_proc.stdout.close()
|
||||
tar_proc.communicate()
|
||||
|
||||
def copy_grub_modules(
|
||||
self, src_dir: pathlib.Path, dest_dir: pathlib.Path, extensions: list[str]
|
||||
) -> None:
|
||||
"""Copy GRUB module files matching given extensions from src to dest."""
|
||||
for ext in extensions:
|
||||
for file in src_dir.glob(ext):
|
||||
shutil.copy(file, dest_dir)
|
||||
|
||||
@abstractmethod
|
||||
def extract_files(self) -> None:
|
||||
"""Download and extract bootloader packages to the boot tree.
|
||||
@ -95,7 +83,6 @@ class BaseBootConfigurator(ABC):
|
||||
|
||||
def make_bootable(
|
||||
self,
|
||||
workdir: pathlib.Path,
|
||||
project: str,
|
||||
capproject: str,
|
||||
subarch: str,
|
||||
@ -106,6 +93,6 @@ class BaseBootConfigurator(ABC):
|
||||
self.humanproject = capproject.replace("-", " ")
|
||||
self.subarch = subarch
|
||||
self.hwe = hwe
|
||||
self.create_dirs(workdir)
|
||||
self.scratch.mkdir(exist_ok=True)
|
||||
with self.logger.logged("configuring boot"):
|
||||
self.extract_files()
|
||||
|
||||
@ -7,17 +7,31 @@ from abc import abstractmethod
|
||||
from .base import BaseBootConfigurator
|
||||
|
||||
|
||||
def copy_grub_common_files_to_boot_tree(
|
||||
grub_dir: pathlib.Path, boot_tree: pathlib.Path
|
||||
) -> None:
|
||||
fonts_dir = boot_tree.joinpath("boot", "grub", "fonts")
|
||||
def copy_grub_common_files(grub_pkg_dir: pathlib.Path, iso_root: pathlib.Path) -> None:
|
||||
fonts_dir = iso_root.joinpath("boot", "grub", "fonts")
|
||||
fonts_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
src = grub_dir.joinpath("usr", "share", "grub", "unicode.pf2")
|
||||
src = grub_pkg_dir.joinpath("usr", "share", "grub", "unicode.pf2")
|
||||
dst = fonts_dir.joinpath("unicode.pf2")
|
||||
shutil.copy(src, dst)
|
||||
|
||||
|
||||
def copy_grub_modules(
|
||||
grub_pkg_dir: pathlib.Path,
|
||||
iso_root: pathlib.Path,
|
||||
grub_target: str,
|
||||
patterns: list[str],
|
||||
) -> None:
|
||||
"""Copy GRUB module files matching given patterns from src to dest."""
|
||||
src_dir = grub_pkg_dir.joinpath("usr", "lib", "grub", grub_target)
|
||||
dest_dir = iso_root.joinpath("boot", "grub", grub_target)
|
||||
dest_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
for pat in patterns:
|
||||
for file in src_dir.glob(pat):
|
||||
shutil.copy(file, dest_dir)
|
||||
|
||||
|
||||
class GrubBootConfigurator(BaseBootConfigurator):
|
||||
"""Base class for architectures that use GRUB (all except S390X).
|
||||
|
||||
@ -25,79 +39,66 @@ class GrubBootConfigurator(BaseBootConfigurator):
|
||||
Subclasses must implement generate_grub_config().
|
||||
"""
|
||||
|
||||
def create_dirs(self, workdir):
|
||||
super().create_dirs(workdir)
|
||||
self.grub_dir = self.boot_tree.joinpath("grub")
|
||||
|
||||
def setup_grub_common_files(self) -> None:
|
||||
"""Copy common GRUB files (fonts, etc.) to boot tree."""
|
||||
copy_grub_common_files_to_boot_tree(self.grub_dir, self.boot_tree)
|
||||
|
||||
def write_grub_header(
|
||||
self, grub_cfg: pathlib.Path, include_loadfont: bool = True
|
||||
) -> None:
|
||||
"""Write common GRUB config header (timeout, colors).
|
||||
def grub_header(self, include_loadfont: bool = True) -> str:
|
||||
"""Return common GRUB config header (timeout, colors).
|
||||
|
||||
Args:
|
||||
grub_cfg: Path to grub.cfg file
|
||||
include_loadfont: Whether to include 'loadfont unicode'
|
||||
(not needed for RISC-V)
|
||||
"""
|
||||
with grub_cfg.open("a") as f:
|
||||
f.write("set timeout=30\n\n")
|
||||
if include_loadfont:
|
||||
f.write("loadfont unicode\n\n")
|
||||
f.write(
|
||||
"""set menu_color_normal=white/black
|
||||
result = "set timeout=30\n\n"
|
||||
if include_loadfont:
|
||||
result += "loadfont unicode\n\n"
|
||||
result += """\
|
||||
set menu_color_normal=white/black
|
||||
set menu_color_highlight=black/light-gray
|
||||
|
||||
"""
|
||||
)
|
||||
return result
|
||||
|
||||
def write_hwe_menu_entry(
|
||||
def hwe_menu_entry(
|
||||
self,
|
||||
grub_cfg: pathlib.Path,
|
||||
kernel_name: str,
|
||||
kernel_params: str,
|
||||
extra_params: str = "",
|
||||
) -> None:
|
||||
"""Write HWE kernel menu entry if HWE is enabled.
|
||||
) -> str:
|
||||
"""Return HWE kernel menu entry if HWE is enabled.
|
||||
|
||||
Args:
|
||||
grub_cfg: Path to grub.cfg file
|
||||
kernel_name: Kernel binary name (vmlinuz or vmlinux)
|
||||
kernel_params: Kernel parameters to append
|
||||
extra_params: Additional parameters (e.g., console=tty0, $cmdline)
|
||||
"""
|
||||
if self.hwe:
|
||||
with grub_cfg.open("a") as f:
|
||||
f.write(
|
||||
f"""menuentry "{self.humanproject} with the HWE kernel" {{
|
||||
\tset gfxpayload=keep
|
||||
\tlinux\t/casper/hwe-{kernel_name} {extra_params}{kernel_params}
|
||||
\tinitrd\t/casper/hwe-initrd
|
||||
if not self.hwe:
|
||||
return ""
|
||||
return f"""\
|
||||
menuentry "{self.humanproject} with the HWE kernel" {{
|
||||
set gfxpayload=keep
|
||||
linux /casper/hwe-{kernel_name} {extra_params}{kernel_params}
|
||||
initrd /casper/hwe-initrd
|
||||
}}
|
||||
"""
|
||||
)
|
||||
|
||||
@abstractmethod
|
||||
def generate_grub_config(self) -> None:
|
||||
"""Generate grub.cfg configuration file.
|
||||
def generate_grub_config(self) -> str:
|
||||
"""Generate grub.cfg content.
|
||||
|
||||
Each GRUB-based architecture must implement this to create its
|
||||
specific GRUB configuration.
|
||||
Each GRUB-based architecture must implement this to return the
|
||||
GRUB configuration.
|
||||
"""
|
||||
...
|
||||
|
||||
def make_bootable(
|
||||
self,
|
||||
workdir: pathlib.Path,
|
||||
project: str,
|
||||
capproject: str,
|
||||
subarch: str,
|
||||
hwe: bool,
|
||||
) -> None:
|
||||
"""Make the ISO bootable by extracting files and generating GRUB config."""
|
||||
super().make_bootable(workdir, project, capproject, subarch, hwe)
|
||||
super().make_bootable(project, capproject, subarch, hwe)
|
||||
with self.logger.logged("generating grub config"):
|
||||
self.generate_grub_config()
|
||||
content = self.generate_grub_config()
|
||||
grub_dir = self.iso_root.joinpath("boot", "grub")
|
||||
grub_dir.mkdir(parents=True, exist_ok=True)
|
||||
grub_dir.joinpath("grub.cfg").write_text(content)
|
||||
|
||||
@ -3,7 +3,11 @@
|
||||
import pathlib
|
||||
import shutil
|
||||
|
||||
from .grub import GrubBootConfigurator
|
||||
from .grub import (
|
||||
copy_grub_common_files,
|
||||
copy_grub_modules,
|
||||
GrubBootConfigurator,
|
||||
)
|
||||
from .base import default_kernel_params
|
||||
|
||||
|
||||
@ -12,28 +16,26 @@ class PPC64ELBootConfigurator(GrubBootConfigurator):
|
||||
|
||||
def mkisofs_opts(self) -> list[str | pathlib.Path]:
|
||||
"""Return mkisofs options for PPC64EL."""
|
||||
# Add cd-boot-tree to the ISO
|
||||
return [self.boot_tree]
|
||||
return []
|
||||
|
||||
def extract_files(self) -> None:
|
||||
"""Download and extract bootloader packages for PPC64EL."""
|
||||
self.logger.log("extracting PPC64EL boot files")
|
||||
|
||||
grub_pkg_dir = self.scratch.joinpath("grub-pkg")
|
||||
|
||||
# Download and extract bootloader packages
|
||||
self.download_and_extract_package("grub2-common", self.grub_dir)
|
||||
self.download_and_extract_package("grub-ieee1275-bin", self.grub_dir)
|
||||
self.download_and_extract_package("grub2-common", grub_pkg_dir)
|
||||
self.download_and_extract_package("grub-ieee1275-bin", grub_pkg_dir)
|
||||
|
||||
# Add common files for GRUB to tree
|
||||
self.setup_grub_common_files()
|
||||
copy_grub_common_files(grub_pkg_dir, self.iso_root)
|
||||
|
||||
# Add IEEE1275 ppc boot files
|
||||
ppc_dir = self.boot_tree.joinpath("ppc")
|
||||
ppc_dir.mkdir(parents=True, exist_ok=True)
|
||||
ppc_dir = self.iso_root.joinpath("ppc")
|
||||
ppc_dir.mkdir()
|
||||
|
||||
grub_boot_dir = self.boot_tree.joinpath("boot", "grub", "powerpc-ieee1275")
|
||||
grub_boot_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
src_grub_dir = self.grub_dir.joinpath("usr", "lib", "grub", "powerpc-ieee1275")
|
||||
src_grub_dir = grub_pkg_dir.joinpath("usr", "lib", "grub", "powerpc-ieee1275")
|
||||
|
||||
# Copy bootinfo.txt to ppc directory
|
||||
shutil.copy(
|
||||
@ -43,33 +45,30 @@ class PPC64ELBootConfigurator(GrubBootConfigurator):
|
||||
# Copy eltorito.elf to boot/grub as powerpc.elf
|
||||
shutil.copy(
|
||||
src_grub_dir.joinpath("eltorito.elf"),
|
||||
self.boot_tree.joinpath("boot", "grub", "powerpc.elf"),
|
||||
self.iso_root.joinpath("boot", "grub", "powerpc.elf"),
|
||||
)
|
||||
|
||||
# Copy GRUB modules
|
||||
self.copy_grub_modules(src_grub_dir, grub_boot_dir, ["*.mod", "*.lst"])
|
||||
copy_grub_modules(
|
||||
grub_pkg_dir, self.iso_root, "powerpc-ieee1275", ["*.mod", "*.lst"]
|
||||
)
|
||||
|
||||
def generate_grub_config(self) -> None:
|
||||
def generate_grub_config(self) -> str:
|
||||
"""Generate grub.cfg for PPC64EL."""
|
||||
kernel_params = default_kernel_params(self.project)
|
||||
|
||||
grub_cfg = self.grub_dir.joinpath("grub.cfg")
|
||||
|
||||
# Write common GRUB header
|
||||
self.write_grub_header(grub_cfg)
|
||||
result = self.grub_header()
|
||||
|
||||
# Main menu entry
|
||||
with grub_cfg.open("a") as f:
|
||||
f.write(
|
||||
f"""menuentry "Try or Install {self.humanproject}" {{
|
||||
\tset gfxpayload=keep
|
||||
\tlinux\t/casper/vmlinux quiet {kernel_params}
|
||||
\tinitrd\t/casper/initrd
|
||||
result += f"""\
|
||||
menuentry "Try or Install {self.humanproject}" {{
|
||||
set gfxpayload=keep
|
||||
linux /casper/vmlinux quiet {kernel_params}
|
||||
initrd /casper/initrd
|
||||
}}
|
||||
"""
|
||||
)
|
||||
|
||||
# HWE kernel option if available
|
||||
self.write_hwe_menu_entry(
|
||||
grub_cfg, "vmlinux", kernel_params, extra_params="quiet "
|
||||
)
|
||||
result += self.hwe_menu_entry("vmlinux", kernel_params, extra_params="quiet ")
|
||||
|
||||
return result
|
||||
|
||||
@ -3,35 +3,31 @@
|
||||
import pathlib
|
||||
import shutil
|
||||
|
||||
from .grub import GrubBootConfigurator
|
||||
from .grub import GrubBootConfigurator, copy_grub_common_files, copy_grub_modules
|
||||
|
||||
|
||||
def copy_unsigned_monolithic_grub_to_boot_tree(
|
||||
grub_dir: pathlib.Path, efi_suffix: str, grub_target: str, boot_tree: pathlib.Path
|
||||
def copy_unsigned_monolithic_grub(
|
||||
grub_pkg_dir: pathlib.Path,
|
||||
efi_suffix: str,
|
||||
grub_target: str,
|
||||
iso_root: pathlib.Path,
|
||||
) -> None:
|
||||
efi_boot_dir = boot_tree.joinpath("EFI", "boot")
|
||||
efi_boot_dir = iso_root.joinpath("EFI", "boot")
|
||||
efi_boot_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
shutil.copy(
|
||||
grub_dir.joinpath(
|
||||
grub_pkg_dir.joinpath(
|
||||
"usr",
|
||||
"lib",
|
||||
"grub",
|
||||
f"{grub_target}-efi",
|
||||
grub_target,
|
||||
"monolithic",
|
||||
f"gcd{efi_suffix}.efi",
|
||||
),
|
||||
efi_boot_dir.joinpath(f"boot{efi_suffix}.efi"),
|
||||
)
|
||||
|
||||
grub_boot_dir = boot_tree.joinpath("boot", "grub", f"{grub_target}-efi")
|
||||
grub_boot_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
src_grub_dir = grub_dir.joinpath("usr", "lib", "grub", f"{grub_target}-efi")
|
||||
for mod_file in src_grub_dir.glob("*.mod"):
|
||||
shutil.copy(mod_file, grub_boot_dir)
|
||||
for lst_file in src_grub_dir.glob("*.lst"):
|
||||
shutil.copy(lst_file, grub_boot_dir)
|
||||
copy_grub_modules(grub_pkg_dir, iso_root, grub_target, ["*.mod", "*.lst"])
|
||||
|
||||
|
||||
class RISCV64BootConfigurator(GrubBootConfigurator):
|
||||
@ -74,16 +70,19 @@ class RISCV64BootConfigurator(GrubBootConfigurator):
|
||||
self.logger.log("extracting RISC-V64 boot files")
|
||||
u_boot_dir = self.scratch.joinpath("u-boot-sifive")
|
||||
|
||||
grub_pkg_dir = self.scratch.joinpath("grub-pkg")
|
||||
|
||||
# Download and extract bootloader packages
|
||||
self.download_and_extract_package("grub2-common", self.grub_dir)
|
||||
self.download_and_extract_package("grub-efi-riscv64-bin", self.grub_dir)
|
||||
self.download_and_extract_package("grub-efi-riscv64-unsigned", self.grub_dir)
|
||||
self.download_and_extract_package("grub2-common", grub_pkg_dir)
|
||||
self.download_and_extract_package("grub-efi-riscv64-bin", grub_pkg_dir)
|
||||
self.download_and_extract_package("grub-efi-riscv64-unsigned", grub_pkg_dir)
|
||||
self.download_and_extract_package("u-boot-sifive", u_boot_dir)
|
||||
|
||||
# Add GRUB to tree
|
||||
self.setup_grub_common_files()
|
||||
copy_unsigned_monolithic_grub_to_boot_tree(
|
||||
self.grub_dir, "riscv64", "riscv64", self.boot_tree
|
||||
copy_grub_common_files(grub_pkg_dir, self.iso_root)
|
||||
|
||||
copy_unsigned_monolithic_grub(
|
||||
grub_pkg_dir, "riscv64", "riscv64-efi", self.iso_root
|
||||
)
|
||||
|
||||
# Extract DTBs to tree
|
||||
@ -107,22 +106,14 @@ class RISCV64BootConfigurator(GrubBootConfigurator):
|
||||
)
|
||||
|
||||
# Copy DTBs if they exist
|
||||
dtb_dir = self.boot_tree.joinpath("dtb")
|
||||
dtb_dir = self.iso_root.joinpath("dtb")
|
||||
dtb_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
firmware_dir = kernel_layer.joinpath("usr", "lib", "firmware")
|
||||
device_tree_files = list(firmware_dir.glob("*/device-tree/*"))
|
||||
|
||||
if device_tree_files:
|
||||
for dtb_file in device_tree_files:
|
||||
if dtb_file.is_file():
|
||||
shutil.copy(dtb_file, dtb_dir)
|
||||
|
||||
# Clean up kernel layer
|
||||
shutil.rmtree(kernel_layer)
|
||||
|
||||
# Copy tree contents to live-media rootfs
|
||||
self.logger.run(["cp", "-aT", self.boot_tree, self.iso_root], check=True)
|
||||
for dtb_file in firmware_dir.glob("*/device-tree/*"):
|
||||
if dtb_file.is_file():
|
||||
shutil.copy(dtb_file, dtb_dir)
|
||||
|
||||
# Create ESP image with GRUB and dtbs
|
||||
efi_img = self.scratch.joinpath("efi.img")
|
||||
@ -131,41 +122,34 @@ class RISCV64BootConfigurator(GrubBootConfigurator):
|
||||
)
|
||||
|
||||
# Add EFI files to ESP
|
||||
efi_dir = self.boot_tree.joinpath("EFI")
|
||||
efi_dir = self.iso_root.joinpath("EFI")
|
||||
self.logger.run(["mcopy", "-s", "-i", efi_img, efi_dir, "::/."], check=True)
|
||||
|
||||
# Add DTBs to ESP
|
||||
self.logger.run(["mcopy", "-s", "-i", efi_img, dtb_dir, "::/."], check=True)
|
||||
|
||||
def generate_grub_config(self) -> None:
|
||||
def generate_grub_config(self) -> str:
|
||||
"""Generate grub.cfg for RISC-V64."""
|
||||
grub_dir = self.iso_root.joinpath("boot", "grub")
|
||||
grub_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
grub_cfg = grub_dir.joinpath("grub.cfg")
|
||||
|
||||
# Write GRUB header (without loadfont for RISC-V)
|
||||
self.write_grub_header(grub_cfg, include_loadfont=False)
|
||||
result = self.grub_header(include_loadfont=False)
|
||||
|
||||
# Main menu entry
|
||||
with grub_cfg.open("a") as f:
|
||||
f.write(
|
||||
f"""menuentry "Try or Install {self.humanproject}" {{
|
||||
\tset gfxpayload=keep
|
||||
\tlinux\t/casper/vmlinux efi=debug sysctl.kernel.watchdog_thresh=60 ---
|
||||
\tinitrd\t/casper/initrd
|
||||
result += f"""\
|
||||
menuentry "Try or Install {self.humanproject}" {{
|
||||
set gfxpayload=keep
|
||||
linux /casper/vmlinux efi=debug sysctl.kernel.watchdog_thresh=60 ---
|
||||
initrd /casper/initrd
|
||||
}}
|
||||
"""
|
||||
)
|
||||
|
||||
# HWE kernel option if available
|
||||
self.write_hwe_menu_entry(
|
||||
grub_cfg,
|
||||
result += self.hwe_menu_entry(
|
||||
"vmlinux",
|
||||
"---",
|
||||
extra_params="efi=debug sysctl.kernel.watchdog_thresh=60 ",
|
||||
)
|
||||
|
||||
return result
|
||||
|
||||
def post_process_iso(self, iso_path: pathlib.Path) -> None:
|
||||
"""Add GPT partitions with U-Boot for SiFive Unmatched board.
|
||||
|
||||
|
||||
@ -4,29 +4,31 @@ import pathlib
|
||||
import shutil
|
||||
|
||||
from ..builder import Logger
|
||||
from .grub import GrubBootConfigurator
|
||||
from .grub import copy_grub_common_files, GrubBootConfigurator
|
||||
|
||||
|
||||
def copy_signed_shim_grub_to_boot_tree(
|
||||
shim_dir: pathlib.Path,
|
||||
grub_dir: pathlib.Path,
|
||||
def copy_signed_shim_grub(
|
||||
shim_pkg_dir: pathlib.Path,
|
||||
grub_pkg_dir: pathlib.Path,
|
||||
efi_suffix: str,
|
||||
grub_target: str,
|
||||
boot_tree: pathlib.Path,
|
||||
iso_root: pathlib.Path,
|
||||
) -> None:
|
||||
efi_boot_dir = boot_tree.joinpath("EFI", "boot")
|
||||
efi_boot_dir = iso_root.joinpath("EFI", "boot")
|
||||
efi_boot_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
shutil.copy(
|
||||
shim_dir.joinpath("usr", "lib", "shim", f"shim{efi_suffix}.efi.signed.latest"),
|
||||
shim_pkg_dir.joinpath(
|
||||
"usr", "lib", "shim", f"shim{efi_suffix}.efi.signed.latest"
|
||||
),
|
||||
efi_boot_dir.joinpath(f"boot{efi_suffix}.efi"),
|
||||
)
|
||||
shutil.copy(
|
||||
shim_dir.joinpath("usr", "lib", "shim", f"mm{efi_suffix}.efi"),
|
||||
shim_pkg_dir.joinpath("usr", "lib", "shim", f"mm{efi_suffix}.efi"),
|
||||
efi_boot_dir.joinpath(f"mm{efi_suffix}.efi"),
|
||||
)
|
||||
shutil.copy(
|
||||
grub_dir.joinpath(
|
||||
grub_pkg_dir.joinpath(
|
||||
"usr",
|
||||
"lib",
|
||||
"grub",
|
||||
@ -36,10 +38,10 @@ def copy_signed_shim_grub_to_boot_tree(
|
||||
efi_boot_dir.joinpath(f"grub{efi_suffix}.efi"),
|
||||
)
|
||||
|
||||
grub_boot_dir = boot_tree.joinpath("boot", "grub", f"{grub_target}-efi")
|
||||
grub_boot_dir = iso_root.joinpath("boot", "grub", f"{grub_target}-efi")
|
||||
grub_boot_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
src_grub_dir = grub_dir.joinpath("usr", "lib", "grub", f"{grub_target}-efi")
|
||||
src_grub_dir = grub_pkg_dir.joinpath("usr", "lib", "grub", f"{grub_target}-efi")
|
||||
for mod_file in src_grub_dir.glob("*.mod"):
|
||||
shutil.copy(mod_file, grub_boot_dir)
|
||||
for lst_file in src_grub_dir.glob("*.lst"):
|
||||
@ -47,10 +49,10 @@ def copy_signed_shim_grub_to_boot_tree(
|
||||
|
||||
|
||||
def create_eltorito_esp_image(
|
||||
logger: Logger, boot_tree: pathlib.Path, target_file: pathlib.Path
|
||||
logger: Logger, iso_root: pathlib.Path, target_file: pathlib.Path
|
||||
) -> None:
|
||||
logger.log("creating El Torito ESP image")
|
||||
efi_dir = boot_tree.joinpath("EFI")
|
||||
efi_dir = iso_root.joinpath("EFI")
|
||||
|
||||
# Calculate size: du -s --apparent-size --block-size=1024 + 1024
|
||||
result = logger.run(
|
||||
@ -84,10 +86,6 @@ class UEFIBootConfigurator(GrubBootConfigurator):
|
||||
grub_target: str = ""
|
||||
arch: str = ""
|
||||
|
||||
def create_dirs(self, workdir):
|
||||
super().create_dirs(workdir)
|
||||
self.shim_dir = self.boot_tree.joinpath("shim")
|
||||
|
||||
def get_uefi_grub_packages(self) -> list[str]:
|
||||
"""Return list of UEFI GRUB packages to download."""
|
||||
return [
|
||||
@ -98,40 +96,42 @@ class UEFIBootConfigurator(GrubBootConfigurator):
|
||||
|
||||
def extract_uefi_files(self) -> None:
|
||||
"""Extract common UEFI files to boot tree."""
|
||||
|
||||
shim_pkg_dir = self.scratch.joinpath("shim-pkg")
|
||||
grub_pkg_dir = self.scratch.joinpath("grub-pkg")
|
||||
|
||||
# Download UEFI packages
|
||||
self.download_and_extract_package("shim-signed", self.shim_dir)
|
||||
self.download_and_extract_package("shim-signed", shim_pkg_dir)
|
||||
for pkg in self.get_uefi_grub_packages():
|
||||
self.download_and_extract_package(pkg, self.grub_dir)
|
||||
self.download_and_extract_package(pkg, grub_pkg_dir)
|
||||
|
||||
# Add common files for GRUB to tree
|
||||
self.setup_grub_common_files()
|
||||
copy_grub_common_files(grub_pkg_dir, self.iso_root)
|
||||
|
||||
# Add EFI GRUB to tree
|
||||
copy_signed_shim_grub_to_boot_tree(
|
||||
self.shim_dir,
|
||||
self.grub_dir,
|
||||
copy_signed_shim_grub(
|
||||
shim_pkg_dir,
|
||||
grub_pkg_dir,
|
||||
self.efi_suffix,
|
||||
self.grub_target,
|
||||
self.boot_tree,
|
||||
self.iso_root,
|
||||
)
|
||||
|
||||
# Create ESP image for El-Torito catalog and hybrid boot
|
||||
create_eltorito_esp_image(
|
||||
self.logger, self.boot_tree, self.scratch.joinpath("cd-boot-efi.img")
|
||||
self.logger, self.iso_root, self.scratch.joinpath("cd-boot-efi.img")
|
||||
)
|
||||
|
||||
def write_uefi_menu_entries(self, grub_cfg: pathlib.Path) -> None:
|
||||
"""Write UEFI firmware menu entries."""
|
||||
with grub_cfg.open("a") as f:
|
||||
f.write(
|
||||
"""menuentry 'Boot from next volume' {
|
||||
\texit 1
|
||||
def uefi_menu_entries(self) -> str:
|
||||
"""Return UEFI firmware menu entries."""
|
||||
return """\
|
||||
menuentry 'Boot from next volume' {
|
||||
exit 1
|
||||
}
|
||||
menuentry 'UEFI Firmware Settings' {
|
||||
\tfwsetup
|
||||
fwsetup
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
def get_uefi_mkisofs_opts(self) -> list[str | pathlib.Path]:
|
||||
"""Return common UEFI mkisofs options."""
|
||||
|
||||
@ -298,10 +298,10 @@ class ISOBuilder:
|
||||
self.arch,
|
||||
self.logger,
|
||||
self.apt_state,
|
||||
self.workdir,
|
||||
self.iso_root,
|
||||
)
|
||||
configurator.make_bootable(
|
||||
self.workdir,
|
||||
project,
|
||||
capproject,
|
||||
subarch,
|
||||
@ -341,9 +341,9 @@ class ISOBuilder:
|
||||
self.arch,
|
||||
self.logger,
|
||||
self.apt_state,
|
||||
self.workdir,
|
||||
self.iso_root,
|
||||
)
|
||||
configurator.create_dirs(self.workdir)
|
||||
mkisofs_opts = configurator.mkisofs_opts()
|
||||
cmd: list[str | pathlib.Path] = ["xorriso"]
|
||||
if self.arch == "riscv64":
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user