mirror of
https://git.launchpad.net/livecd-rootfs
synced 2026-02-17 15:33:32 +00:00
Add architecture-specific boot configurators that translate the debian-cd boot shell scripts (boot-amd64, boot-arm64, boot-ppc64el, boot-riscv64, boot-s390x) into Python. The package uses a class hierarchy: - BaseBootConfigurator: abstract base with common functionality - GrubBootConfigurator: shared GRUB config generation - UEFIBootConfigurator: UEFI-specific shim/ESP handling - Architecture classes: AMD64, ARM64, PPC64EL, RISCV64, S390X A factory function make_boot_configurator_for_arch() creates the appropriate configurator for each architecture. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
207 lines
7.0 KiB
Python
207 lines
7.0 KiB
Python
"""IBM S/390 architecture boot configuration."""
|
|
|
|
import pathlib
|
|
import shutil
|
|
import struct
|
|
|
|
from .base import BaseBootConfigurator
|
|
|
|
|
|
README_dot_boot = """\
|
|
About the S/390 installation CD
|
|
===============================
|
|
|
|
It is possible to "boot" the installation system off this CD using
|
|
the files provided in the /boot directory.
|
|
|
|
Although you can boot the installer from this CD, the installation
|
|
itself is *not* actually done from the CD. Once the initrd is loaded,
|
|
the installer will ask you to configure your network connection and
|
|
uses the network-console component to allow you to continue the
|
|
installation over SSH. The rest of the installation is done over the
|
|
network: all installer components and Debian packages are retrieved
|
|
from a mirror.
|
|
|
|
Instead of SSH, one can also use the ASCII terminal available in HMC.
|
|
|
|
Exporting full .iso contents (including the hidden .disk directory)
|
|
allows one to use the result as a valid mirror for installation.
|
|
"""
|
|
|
|
ubuntu_dot_exec = """\
|
|
/* REXX EXEC TO IPL Ubuntu for */
|
|
/* z Systems FROM THE VM READER. */
|
|
/* */
|
|
'CP CLOSE RDR'
|
|
'PURGE RDR ALL'
|
|
'SPOOL PUNCH * RDR'
|
|
'PUNCH KERNEL UBUNTU * (NOHEADER'
|
|
'PUNCH PARMFILE UBUNTU * (NOHEADER'
|
|
'PUNCH INITRD UBUNTU * (NOHEADER'
|
|
'CHANGE RDR ALL KEEP NOHOLD'
|
|
'CP IPL 000C CLEAR'
|
|
"""
|
|
|
|
ubuntu_dot_ins = """\
|
|
* Ubuntu for IBM Z (default kernel)
|
|
kernel.ubuntu 0x00000000
|
|
initrd.off 0x0001040c
|
|
initrd.siz 0x00010414
|
|
parmfile.ubuntu 0x00010480
|
|
initrd.ubuntu 0x01000000
|
|
"""
|
|
|
|
|
|
def gen_s390_cd_kernel(
|
|
kernel: pathlib.Path, initrd: pathlib.Path, cmdline: str, outfile: pathlib.Path
|
|
) -> None:
|
|
"""Generate a bootable S390X CD kernel image.
|
|
|
|
This is a Python translation of gen-s390-cd-kernel.pl from debian-cd.
|
|
It creates a bootable image for S/390 architecture by combining kernel,
|
|
initrd, and boot parameters in a specific format.
|
|
"""
|
|
# Calculate sizes
|
|
initrd_size = initrd.stat().st_size
|
|
|
|
# The initrd is placed at a fixed offset of 16 MiB
|
|
initrd_offset = 0x1000000
|
|
|
|
# Calculate total boot image size (rounded up to 4K blocks)
|
|
boot_size = ((initrd_offset + initrd_size) >> 12) + 1
|
|
boot_size = boot_size << 12
|
|
|
|
# Validate cmdline length (max 896 bytes)
|
|
if len(cmdline) >= 896:
|
|
raise ValueError(f"Kernel commandline too long ({len(cmdline)} bytes)")
|
|
|
|
# Create output file and fill with zeros
|
|
with outfile.open("wb") as out_fh:
|
|
# Fill entire file with zeros
|
|
out_fh.write(b"\x00" * boot_size)
|
|
|
|
# Copy kernel to offset 0
|
|
out_fh.seek(0)
|
|
with kernel.open("rb") as kernel_fh:
|
|
out_fh.write(kernel_fh.read())
|
|
|
|
# Copy initrd to offset 0x1000000 (16 MiB)
|
|
out_fh.seek(initrd_offset)
|
|
with initrd.open("rb") as initrd_fh:
|
|
out_fh.write(initrd_fh.read())
|
|
|
|
# Write boot loader control value at offset 4
|
|
# This tells the S/390 boot loader where to find the kernel
|
|
out_fh.seek(4)
|
|
out_fh.write(struct.pack("!I", 0x80010000))
|
|
|
|
# Write kernel command line at offset 0x10480
|
|
out_fh.seek(0x10480)
|
|
out_fh.write(cmdline.encode("utf-8"))
|
|
|
|
# Write initrd parameters
|
|
# Initrd offset at 0x1040C
|
|
out_fh.seek(0x1040C)
|
|
out_fh.write(struct.pack("!I", initrd_offset))
|
|
|
|
# Initrd size at 0x10414
|
|
out_fh.seek(0x10414)
|
|
out_fh.write(struct.pack("!I", initrd_size))
|
|
|
|
|
|
class S390XBootConfigurator(BaseBootConfigurator):
|
|
"""Boot setup for IBM S/390 architecture."""
|
|
|
|
def mkisofs_opts(self) -> list[str | pathlib.Path]:
|
|
"""Return mkisofs options for S390X."""
|
|
return [
|
|
"-J",
|
|
"-no-emul-boot",
|
|
"-b",
|
|
"boot/ubuntu.ikr",
|
|
]
|
|
|
|
def extract_files(self) -> None:
|
|
"""Set up boot files for S390X."""
|
|
self.logger.log("extracting S390X boot files")
|
|
boot_dir = self.iso_root.joinpath("boot")
|
|
boot_dir.mkdir(parents=True, exist_ok=True)
|
|
|
|
# Copy static .ins & exec scripts, docs from data directory
|
|
self.iso_root.joinpath("README.boot").write_text(README_dot_boot)
|
|
boot_dir.joinpath("ubuntu.exec").write_text(ubuntu_dot_exec)
|
|
boot_dir.joinpath("ubuntu.ins").write_text(ubuntu_dot_ins)
|
|
|
|
# Move kernel image to the name used in .ins & exec scripts
|
|
kernel_src = self.iso_root.joinpath("casper", "vmlinuz")
|
|
kernel_dst = boot_dir.joinpath("kernel.ubuntu")
|
|
kernel_src.replace(kernel_dst)
|
|
|
|
# Move initrd to the name used in .ins & exec scripts
|
|
initrd_src = self.iso_root.joinpath("casper", "initrd")
|
|
initrd_dst = boot_dir.joinpath("initrd.ubuntu")
|
|
initrd_src.replace(initrd_dst)
|
|
|
|
# Compute initrd offset & size, store in files used by .ins & exec scripts
|
|
# Offset is always 0x1000000 (16 MiB)
|
|
initrd_offset_file = boot_dir.joinpath("initrd.off")
|
|
with initrd_offset_file.open("wb") as f:
|
|
f.write(struct.pack("!I", 0x1000000))
|
|
|
|
# Size is the actual size of the initrd
|
|
initrd_size = initrd_dst.stat().st_size
|
|
initrd_size_file = boot_dir.joinpath("initrd.siz")
|
|
with initrd_size_file.open("wb") as f:
|
|
f.write(struct.pack("!I", initrd_size))
|
|
|
|
# Compute cmdline, store in parmfile used by .ins & exec scripts
|
|
parmfile = boot_dir.joinpath("parmfile.ubuntu")
|
|
with parmfile.open("w") as f:
|
|
f.write(" --- ")
|
|
|
|
# Generate secondary top-level ubuntu.ins file
|
|
# This transforms lines not starting with * by prepending "boot/"
|
|
ubuntu_ins_src = boot_dir.joinpath("ubuntu.ins")
|
|
ubuntu_ins_dst = self.iso_root.joinpath("ubuntu.ins")
|
|
if ubuntu_ins_src.exists():
|
|
self.logger.run(
|
|
["sed", "-e", "s,^[^*],boot/&,g", ubuntu_ins_src],
|
|
stdout=ubuntu_ins_dst.open("w"),
|
|
check=True,
|
|
)
|
|
|
|
# Generate QEMU-KVM boot image using gen_s390_cd_kernel
|
|
cmdline = parmfile.read_text().strip()
|
|
ikr_file = boot_dir.joinpath("ubuntu.ikr")
|
|
gen_s390_cd_kernel(kernel_dst, initrd_dst, cmdline, ikr_file)
|
|
|
|
# Extract bootloader signing certificate
|
|
installed_pem = pathlib.Path("/usr/lib/s390-tools/stage3.pem")
|
|
squashfs_root = self.iso_root.joinpath("squashfs-root")
|
|
squashfs_path = self.iso_root.joinpath(
|
|
"casper", "ubuntu-server-minimal.squashfs"
|
|
)
|
|
|
|
if squashfs_path.exists():
|
|
self.logger.run(
|
|
[
|
|
"unsquashfs",
|
|
"-no-xattrs",
|
|
"-i",
|
|
"-d",
|
|
squashfs_root,
|
|
squashfs_path,
|
|
installed_pem,
|
|
],
|
|
check=True,
|
|
)
|
|
|
|
# Move certificate to iso root
|
|
cert_src = squashfs_root.joinpath(str(installed_pem).lstrip("/"))
|
|
cert_dst = self.iso_root.joinpath("ubuntu.pem")
|
|
if cert_src.exists():
|
|
cert_src.replace(cert_dst)
|
|
|
|
# Clean up squashfs extraction
|
|
shutil.rmtree(squashfs_root)
|