pkg: fedora: Add new package build scripts for building patched kernels

These scripts use the kernel-ark repository that upstream Fedora uses too to
build their kernels.
This commit is contained in:
Dorian Stoll 2023-05-13 20:46:57 +02:00
parent 5ef44c41d7
commit 5dffa283ee
No known key found for this signature in database
GPG key ID: F1DACD02C619442A
8 changed files with 353 additions and 2 deletions

View file

@ -1,2 +1,4 @@
surface.key
surface.crt
secureboot/MOK.key
secureboot/MOK.crt
kernel-ark
out

View file

@ -0,0 +1,171 @@
#!/usr/bin/env python3
import argparse
import functools
import operator
import os
import shutil
import subprocess
import time
def system(cmd: str) -> None:
subprocess.run(cmd, shell=True, check=True)
parser = argparse.ArgumentParser(usage="Build a patched Fedora kernel")
parser.add_argument(
"--package-name",
help="The name of the patched package (e.g. foo -> kernel-foo).",
required=True,
)
parser.add_argument(
"--package-tag",
help="The upstream tag to build.",
required=True,
)
parser.add_argument(
"--package-release",
help="The release suffix of the modified package.",
required=True,
)
parser.add_argument(
"--ark-dir",
help="The local path to the kernel-ark repository.",
default="kernel-ark",
)
parser.add_argument(
"--ark-url",
help="The remote path to the kernel-ark repository.",
default="https://gitlab.com/cki-project/kernel-ark",
)
parser.add_argument(
"--patch",
help="Applies a patch to the kernel source.",
action="append",
nargs="+",
)
parser.add_argument(
"--config",
help="Applies a KConfig fragment to the kernel source.",
action="append",
nargs="+",
)
parser.add_argument(
"--file",
help="Copy a file into the RPM buildroot.",
action="append",
nargs="+",
)
parser.add_argument(
"--buildopts",
help="Enable or disable options of the kernel spec file.",
action="append",
nargs="+",
)
parser.add_argument(
"--outdir",
help="The directory where the built RPM files will be saved.",
default="out",
)
args = parser.parse_args()
patches = [] if not args.patch else functools.reduce(operator.add, args.patch)
configs = [] if not args.config else functools.reduce(operator.add, args.config)
files = [] if not args.file else functools.reduce(operator.add, args.file)
buildopts = [] if not args.buildopts else functools.reduce(operator.add, args.buildopts)
# Make paths absolute.
patches = [os.path.realpath(x) for x in patches]
configs = [os.path.realpath(x) for x in configs]
files = [os.path.realpath(x) for x in files]
outdir = os.path.realpath(args.outdir)
# Clone the kernel-ark repository if it doesn't exist.
if not os.path.exists(args.ark_dir):
system("git clone '%s' '%s'" % (args.ark_url, args.ark_dir))
os.chdir(args.ark_dir)
# Check out the requested tag.
system("git fetch --tags")
system("git clean -dfx")
system("git checkout -b 'build/%s'" % time.time())
system("git reset --hard '%s'" % args.package_tag)
# Apply patches
for patch in patches:
system("git am '%s'" % patch)
# Copy files
for file in files:
shutil.copy(file, "redhat/fedora_files/")
# Apply config options
#
# The format that the kernel-ark tree expects is a bit different from
# a standard kernel config. Every option is split into a single file
# named after that config.
#
# Example:
# $ cat redhat/configs/common/generic/CONFIG_PCI
# CONFIG_PCI=y
#
# This supposedly makes things easier for Red Hat developers,
# but it also ends up being really annoying for us.
for config in configs:
with open(config) as f:
lines = f.readlines()
# Filter out comments, this means only selecting lines that look like:
# - CONFIG_FOO=b
# - # CONFIG_FOO is not set
for line in lines:
enable = line.startswith("CONFIG_")
disable = line.startswith("# CONFIG_")
if not enable and not disable:
continue
NAME = ""
if enable:
NAME = line.split("=")[0]
elif disable:
NAME = line[2:].split(" ")[0]
print("Applying %s" % line.rstrip("\n"))
with open("redhat/configs/custom-overrides/generic/%s" % NAME, "w") as f:
f.write(line)
system("git add redhat/configs/custom-overrides/generic")
system("git commit -m 'Merge %s config'" % args.package_name)
cmd = []
cmd.append("make")
cmd.append("dist-rpms")
cmd.append("SPECPACKAGE_NAME='kernel-%s'" % args.package_name)
cmd.append("DISTLOCALVERSION='.%s'" % args.package_name)
cmd.append("BUILD='%s'" % args.package_release)
if len(buildopts) > 0:
cmd.append("BUILDOPTS='%s'" % " ".join(buildopts))
# Build RPMS
system(" ".join(cmd))
# Copy built RPMS to output directory
os.makedirs(outdir, exist_ok=True)
system("cp -r redhat/rpm/RPMS/* '%s'" % outdir)

View file

@ -0,0 +1,111 @@
#!/usr/bin/env python3
import subprocess
import sys
from pathlib import Path
#####################################################################
##
## The name of the modified kernel package.
##
PACKAGE_NAME = "surface"
##
## https://gitlab.com/cki-project/kernel-ark/-/tags
##
## Fedora tags: kernel-X.Y.Z
## Upstream tags: vX.Y.Z
##
PACKAGE_TAG = "kernel-6.3.6-0"
##
## The release number of the modified kernel package.
## e.g. 300 for kernel-6.3.1-300.fc38.foo
##
PACKAGE_RELEASE = "1"
##
## Build options for configuring which parts of the kernel package are enabled.
##
## We disable all userspace components because we only want the kernel + modules.
## We also don't care too much about debug info or UKI.
##
## To list the available options, run make dist-full-help in the kernel-ark tree.
##
KERNEL_BUILDOPTS = "+up +baseonly -debuginfo -doc -headers -efiuki"
#####################################################################
# The directory where this script is saved.
script = Path(sys.argv[0]).resolve().parent
# The root of the linux-surface repository.
linux_surface = script / ".." / ".." / ".."
# Determine the major version of the kernel.
kernel_version = PACKAGE_TAG.split("-")[1]
kernel_major = ".".join(kernel_version.split(".")[:2])
# Determine the patches directory and config file.
patches = linux_surface / "patches" / kernel_major
config = linux_surface / "configs" / ("surface-%s.config" % kernel_major)
sb_cert = script / "secureboot" / "MOK.crt"
sb_key = script / "secureboot" / "MOK.key"
# Check if the major version is supported.
if not patches.exists() or not config.exists():
print("ERROR: Could not find patches / configs for kernel %s!" % kernel_major)
sys.exit(1)
# Check if Secure Boot keys are available.
sb_avail = sb_cert.exists() and sb_key.exists()
# If we are building without secureboot, require user input to continue.
if not sb_avail:
print("")
print("Secure Boot keys were not configured! Using Red Hat testkeys.")
print("The compiled kernel will not boot with Secure Boot enabled!")
print("")
input("Press any key to continue")
# Expand globs
surface_patches = list(patches.glob("*.patch"))
cmd = []
cmd += [script / "build-ark.py"]
cmd += ["--package-name", PACKAGE_NAME]
cmd += ["--package-tag", PACKAGE_TAG]
cmd += ["--package-release", PACKAGE_RELEASE]
cmd += ["--patch"] + surface_patches
cmd += ["--config", config]
cmd += ["--buildopts", KERNEL_BUILDOPTS]
local_patches = list((script / "patches").glob("*.patch"))
local_configs = list((script / "configs").glob("*.config"))
local_files = list((script / "files").glob("*"))
if len(local_patches) > 0:
cmd += ["--patch"] + local_patches
if len(local_configs) > 0:
cmd += ["--config"] + local_configs
if len(local_files) > 0:
cmd += ["--file"] + local_files
if sb_avail:
sb_patches = list((script / "secureboot").glob("*.patch"))
sb_configs = list((script / "secureboot").glob("*.config"))
if len(sb_patches) > 0:
cmd += ["--patch"] + sb_patches
if len(sb_configs) > 0:
cmd += ["--config"] + sb_configs
cmd += ["--file", sb_cert, sb_key]
subprocess.run(cmd, check=True)

View file

@ -0,0 +1,7 @@
##
## Config options specific to Fedora
##
# The build fails because this is not enabled in the config set for RHEL,
# but enabled automatically by one of our patches.
CONFIG_VIDEO_V4L2_SUBDEV_API=y

View file

View file

@ -0,0 +1,60 @@
From 67f8052f553191686b1224b5598d00ff33d38608 Mon Sep 17 00:00:00 2001
From: Dorian Stoll <dorian.stoll@tmsp.io>
Date: Sat, 13 May 2023 16:39:50 +0200
Subject: [PATCH] Use a custom key and certificate for Secure Boot signing
Signed-off-by: Dorian Stoll <dorian.stoll@tmsp.io>
---
redhat/kernel.spec.template | 15 +++++++++------
1 file changed, 9 insertions(+), 6 deletions(-)
diff --git a/redhat/kernel.spec.template b/redhat/kernel.spec.template
index 51f43b21b018..76d1ad8e2818 100644
--- a/redhat/kernel.spec.template
+++ b/redhat/kernel.spec.template
@@ -703,6 +703,7 @@ BuildRequires: system-sb-certs
%ifarch x86_64 aarch64
BuildRequires: nss-tools
BuildRequires: pesign >= 0.10-4
+BuildRequires: sbsigntools
%endif
%endif
%endif
@@ -762,6 +763,13 @@ Source1: Makefile.rhelver
%define signing_key_filename kernel-signing-s390.cer
%endif
+%ifarch x86_64 aarch64
+
+Source7001: MOK.key
+Source7002: MOK.crt
+
+%endif
+
%if %{?released_kernel}
Source10: redhatsecurebootca5.cer
@@ -1860,9 +1868,7 @@ BuildKernel() {
fi
%ifarch x86_64 aarch64
- %pesign -s -i $SignImage -o vmlinuz.tmp -a %{secureboot_ca_0} -c %{secureboot_key_0} -n %{pesign_name_0}
- %pesign -s -i vmlinuz.tmp -o vmlinuz.signed -a %{secureboot_ca_1} -c %{secureboot_key_1} -n %{pesign_name_1}
- rm vmlinuz.tmp
+ sbsign --key %{SOURCE7001} --cert %{SOURCE7002} --output vmlinuz.signed $SignImage
%endif
%ifarch s390x ppc64le
if [ -x /usr/bin/rpm-sign ]; then
@@ -2393,9 +2399,6 @@ BuildKernel() {
# Red Hat UEFI Secure Boot CA cert, which can be used to authenticate the kernel
mkdir -p $RPM_BUILD_ROOT%{_datadir}/doc/kernel-keys/$KernelVer
%ifarch x86_64 aarch64
- install -m 0644 %{secureboot_ca_0} $RPM_BUILD_ROOT%{_datadir}/doc/kernel-keys/$KernelVer/kernel-signing-ca-20200609.cer
- install -m 0644 %{secureboot_ca_1} $RPM_BUILD_ROOT%{_datadir}/doc/kernel-keys/$KernelVer/kernel-signing-ca-20140212.cer
- ln -s kernel-signing-ca-20200609.cer $RPM_BUILD_ROOT%{_datadir}/doc/kernel-keys/$KernelVer/kernel-signing-ca.cer
%else
install -m 0644 %{secureboot_ca_0} $RPM_BUILD_ROOT%{_datadir}/doc/kernel-keys/$KernelVer/kernel-signing-ca.cer
%endif
--
2.40.1