From: Emanuele Giuseppe Esposito <eesposit@xxxxxxxxxx> redhat/kernel.spec: add uki_addons to create UKI kernel cmdline addons Upstream Status: RHEL-Only The folder redhat/uki_addons will contain all addons configs specifying the UKI kernel cmdline addons to be created in the next build. An addon config is simply a .addon plain text file, where any line is taken as kernel cmdline, except for the ones starting with '#', which will be automatically ignored. redhat/scripts/uki_addons.py will take care of parsing all configs and folders in redhat/uki_addons and call 'ukify' to create the actual addons. The output addon filename will be a concatenation of all folders in redhat/uki_addons that are part of the addon config path. The folder hierarchy inside of redhat/uki_addons is similar to redhat/configs: $distro/$UKI_NAME/%arch. It is also possible to add .sbat to all the generated addons, by populating redhat/uki_addons/$distro/$UKI_NAME/%arch/sbat/sbat.conf. Syntax is same as the addons config. At build time, Makefile will create a tar.gz archive (uki_addons.tar.gz) containing all the files in redhat/uki_addons. It will then passed to the kernel specfile that will extract the addons from it and generate the UKI kernel cmdline addons. Signed-off-by: Emanuele Giuseppe Esposito <eesposit@xxxxxxxxxx> diff --git a/redhat/Makefile b/redhat/Makefile index blahblah..blahblah 100644 --- a/redhat/Makefile +++ b/redhat/Makefile @@ -685,6 +685,7 @@ sources-rh: $(TARBALL) $(KABI_TARBALL) $(KABIDW_TARBALL) generate-testpatch-tmp @sed -e "s/%%SPECKVERSION%%/$(SPECKVERSION)/" \ -e "s/%%SPECKPATCHLEVEL%%/$(SPECKPATCHLEVEL)/" \ scripts/gating/rpminspect.yaml > $(SOURCES)/rpminspect.yaml + @tar -czf uki_addons.tar.gz uki_addons @cp scripts/kernel-tools/kvm_stat.logrotate \ keys/rhel*.x509 \ keys/nvidia*.x509 \ @@ -695,6 +696,7 @@ sources-rh: $(TARBALL) $(KABI_TARBALL) $(KABIDW_TARBALL) generate-testpatch-tmp scripts/filtermods.py \ scripts/mod/mod-denylist.sh \ scripts/mod/mod-sign.sh \ + scripts/uki_addons.py \ configs/flavors \ configs/generate_all_configs.sh \ configs/merge.py \ @@ -703,6 +705,7 @@ sources-rh: $(TARBALL) $(KABI_TARBALL) $(KABIDW_TARBALL) generate-testpatch-tmp README.rst \ kernel-local \ dracut-virt.conf \ + uki_addons.tar.gz \ $(SOURCES)/ @cat $$(ls -1 $(SPECPACKAGE_NAME).changelog-* | sort -t '.' -k 3 -n -r) \ > $(SOURCES)/kernel.changelog diff --git a/redhat/kernel.spec.template b/redhat/kernel.spec.template index blahblah..blahblah 100644 --- a/redhat/kernel.spec.template +++ b/redhat/kernel.spec.template @@ -790,6 +790,8 @@ BuildRequires: binutils BuildRequires: lvm2 BuildRequires: systemd-boot-unsigned # For systemd-stub and systemd-pcrphase +BuildRequires: systemd-ukify +# For UKI kernel cmdline addons BuildRequires: systemd-udev >= 252-1 # For TPM operations in UKI initramfs BuildRequires: tpm2-tools @@ -919,6 +921,9 @@ Source86: dracut-virt.conf Source87: flavors +Source151: uki_addons.py +Source152: uki_addons.tar.gz + Source100: rheldup3.x509 Source101: rhelkpatch1.x509 Source102: nvidiagpuoot001.x509 @@ -1539,6 +1544,11 @@ Provides: kernel-%{?1:%{1}-}uname-r = %{KVERREL}%{uname_suffix %{?1:+%{1}}}\ Requires: kernel%{?1:-%{1}}-modules-core-uname-r = %{KVERREL}%{uname_suffix %{?1:+%{1}}}\ Requires(pre): %{kernel_prereq}\ Requires(pre): systemd >= 254-1\ +%package %{?1:%{1}-}uki-virt-addons\ +Summary: %{variant_summary} unified kernel image addons for virtual machines\ +Provides: installonlypkg(kernel)\ +Requires: kernel%{?1:-%{1}}-uki-virt = %{specrpmversion}-%{release}\ +Requires(pre): systemd >= 254-1\ %endif\ %endif\ %if %{with_gcov}\ @@ -1678,31 +1688,49 @@ input and output, etc. %if %{with_up} && %{with_debug} && %{with_efiuki} %description debug-uki-virt Prebuilt debug unified kernel image for virtual machines. + +%description debug-uki-virt-addons +Prebuilt debug unified kernel image addons for virtual machines. %endif %if %{with_up_base} && %{with_efiuki} %description uki-virt Prebuilt default unified kernel image for virtual machines. + +%description uki-virt-addons +Prebuilt default unified kernel image addons for virtual machines. %endif %if %{with_arm64_16k} && %{with_debug} && %{with_efiuki} %description 16k-debug-uki-virt Prebuilt 16k debug unified kernel image for virtual machines. + +%description 16k-debug-uki-virt-addons +Prebuilt 16k debug unified kernel image addons for virtual machines. %endif %if %{with_arm64_16k_base} && %{with_efiuki} %description 16k-uki-virt Prebuilt 16k unified kernel image for virtual machines. + +%description 16k-uki-virt-addons +Prebuilt 16k unified kernel image addons for virtual machines. %endif %if %{with_arm64_64k} && %{with_debug} && %{with_efiuki} %description 64k-debug-uki-virt Prebuilt 64k debug unified kernel image for virtual machines. + +%description 64k-debug-uki-virt-addons +Prebuilt 64k debug unified kernel image addons for virtual machines. %endif %if %{with_arm64_64k_base} && %{with_efiuki} %description 64k-uki-virt Prebuilt 64k unified kernel image for virtual machines. + +%description 64k-uki-virt-addons +Prebuilt 64k unified kernel image addons for virtual machines. %endif %if %{with_ipaclones} @@ -2551,6 +2579,12 @@ BuildKernel() { --kernel-cmdline 'console=tty0 console=ttyS0' \ $KernelUnifiedImage + tar -xvf %{SOURCE152} -C $KernelUnifiedImageDir + KernelAddonsDir="$KernelUnifiedImageDir/uki_addons" + KernelAddonsDirOut="$RPM_BUILD_ROOT/usr/lib/linux/extra.d/$KernelVer" + mkdir -p $KernelAddonsDirOut + python3 %{SOURCE151} $KernelAddonsDir $KernelAddonsDirOut virt %{primary_target} %{_target_cpu} + %if %{signkernel} %{log_msg "Sign the EFI UKI kernel"} %pesign -s -i $KernelUnifiedImage -o $KernelUnifiedImage.tmp -a %{secureboot_ca_0} -c %{secureboot_key_0} -n %{pesign_name_0} @@ -2563,9 +2597,17 @@ BuildKernel() { fi mv $KernelUnifiedImage.signed $KernelUnifiedImage + for addon in "$KernelAddonsDirOut"/*; do + %pesign -s -i $addon -o $addon.signed -a %{secureboot_ca_1} -c %{secureboot_key_1} -n %{pesign_name_1} + rm -f $addon + mv $addon.signed $addon + done + # signkernel %endif + rm -rf $KernelUnifiedImageDir/uki_addons + # with_efiuki %endif @@ -3860,6 +3902,8 @@ fi\ /lib/modules/%{KVERREL}%{?3:+%{3}}/modules.builtin*\ %attr(0644, root, root) /lib/modules/%{KVERREL}%{?3:+%{3}}/%{?-k:%{-k*}}%{!?-k:vmlinuz}-virt.efi\ %ghost /%{image_install_path}/efi/EFI/Linux/%{?-k:%{-k*}}%{!?-k:*}-%{KVERREL}%{?3:+%{3}}.efi\ +%{expand:%%files %{?3:%{3}-}uki-virt-addons}\ +/usr/lib/linux/extra.d/%{KVERREL}%{?3:+%{3}}/*.addon.efi\ %endif\ %endif\ %if %{?3:1} %{!?3:0}\ diff --git a/redhat/scripts/uki_addons.py b/redhat/scripts/uki_addons.py new file mode 100644 index blahblah..blahblah 100644 --- /dev/null +++ b/redhat/scripts/uki_addons.py @@ -0,0 +1,162 @@ +#!/usr/bin/env python3 +# +# This script recursively inspects a given uki directory, and creates an addon +# for each file found. +# +# Usage: python uki_addons.py input_dir out_dir uki distro arch +# +# This tool requires the systemd-ukify and systemd-boot packages. +# +# Addon file +#----------- +# Each addon terminates with .addon +# Each addon contains only two types of lines: +# Lines beginning with '#' are description and thus ignored +# All other lines are command line to be added. +# The name of the end resulting addon is taken from the folder hierarchy. +# For example, and addon in virt/rhel/x86_64/hello.addon will result in +# an UKI addon file generated in out_dir called hello-virt.rhel.x86_64.addon.efi +# +# The common folder, present in any folder in redhat/uki_addons (except the leaf folders) +# is used as place for default addons when the same addon is not defined deep +# in the hierarchy. For example, if we define test.addon (text: test1) in +# redhat/uki_addons/common and another test.addon (text: test2) in +# redhat/uki_addons/virt/common, any other uki except virt will have a test.addon.efi +# with text "test1", and virt will have a test.addon.efi with "test2" +# +# sbat.conf +#---------- +# This file is containing the sbat string for *all* addons being created. +# This file is optional, but when used has to be put in out_dir/sbat/sbat.conf. +# It follows the same syntax as the addon files, meaning '#' is comment and +# the rest is taken as sbat string and feed to ukify. +# +# +# How to extend the script and kernel.spec with a new arch or uki or distro +#-------------------------------------------------------------------------- +# A new distro has to be added by creating the folder in redhat/uki_addons. +# See above to how the directory hierarchy in redhat/uki_addons is expected to be. +# After that, if the distro is a different arch from the one already supported, +# one needs to extend the %define with_efiuki in kernel.spec.template. +# If a new UKI has to be created with a different name from the existing ones, +# the logic to create the addons and call this script has to be implemented too +# in kernel.spec.template. As an example, see how the 'virt' UKI addons are +# created. + +import os +import sys +import collections +import subprocess + + +UKIFY_PATH = '/usr/lib/systemd/ukify' +SBAT_RELATIVE_PATH = 'sbat/sbat.conf' + +def usage(err): + print(f'Usage: {os.path.basename(__file__)} input_dir output_dir uki distro arch') + print(f'Error:{err}') + sys.exit(1) + +def check_clean_arguments(in_dir, out_dir): + # Remove end '/' + if in_dir[-1:] == '/': + in_dir = in_dir[:-1] + if out_dir[-1:] == '/': + out_dir = out_dir[:-1] + if not os.path.isdir(in_dir): + usage(f'in_dir {in_dir} is not a dir, or does not exist!') + if not os.path.isdir(out_dir): + usage(f'out_dir_dir {out_dir} is not a dir, or does not exist!') + return in_dir, out_dir + +UKICmdlineAddon = collections.namedtuple('UKICmdlineAddon', ['name', 'cmdline']) +uki_addons_list = [] +uki_addons = {} +addon_sbat_string = None + +def parse_addon_conf(path, rstrip=True): + with open(path, 'r') as addons: + lines = addons.readlines() + cmdline = '' + for l in lines: + l = l.lstrip() + if not l: + continue + if l[0] == '#': + continue + if rstrip: + l = l.rstrip() + ' ' + cmdline += l + if cmdline == '': + return '' + return cmdline + +def parse_all_addons_folder(in_dir): + global addon_sbat_string + sbat_loc = os.path.join(in_dir, SBAT_RELATIVE_PATH) + + for el in os.listdir(in_dir): + el_path = os.path.join(in_dir, el) + # file found: save its path, replacing the old one + if os.path.isfile(el_path): + if el.endswith('.addon'): + uki_addons[el] = el_path + + if os.path.isfile(sbat_loc): + # override sbat with the most specific one found + addon_sbat_string = parse_addon_conf(sbat_loc, rstrip=False) + +def recursively_find_addons(in_dir, i, folder_list): + # end of recursion, leaf directory. Search all addons here + if i == len(folder_list): + parse_all_addons_folder(in_dir) + return + + # first, check for common folder + common_dir = os.path.join(in_dir, 'common') + if os.path.exists(common_dir): + parse_all_addons_folder(common_dir) + + # second, check if there is a match with the searched folder + folder_dir = os.path.join(in_dir, folder_list[i]) + if os.path.exists(folder_dir): + recursively_find_addons(folder_dir, i+1, folder_list) + +def parse_in_dir(in_dir, uki_name, distro, arch): + recursively_find_addons(in_dir, 0, [uki_name, distro, arch]) + + for addon_name, addon_path in uki_addons.items(): + addon_name = addon_name.replace(".addon","") + addon_full_name = f'{addon_name}-{uki_name}.{distro}.{arch}.addon.efi' + cmdline = parse_addon_conf(addon_path) + if cmdline: + uki_addons_list.append(UKICmdlineAddon(addon_full_name, cmdline)) + +def create_addons(out_dir): + for uki_addon in uki_addons_list: + out_path = os.path.join(out_dir, uki_addon.name) + cmd = [ + f'{UKIFY_PATH}', 'build', + f'--cmdline="{uki_addon.cmdline}"', + f'--output={out_path}'] + if addon_sbat_string: + cmd.append('--sbat="' + addon_sbat_string.rstrip() +'"') + + subprocess.check_call(cmd, text=True) + +if __name__ == "__main__": + argc = len(sys.argv) - 1 + if argc != 5: + usage('too few or too many parameters!') + + in_dir = sys.argv[1] + out_dir = sys.argv[2] + uki_name = sys.argv[3] + distro = sys.argv[4] + arch = sys.argv[5] + + in_dir, out_dir = check_clean_arguments(in_dir, out_dir) + parse_in_dir(in_dir, uki_name, distro, arch) + create_addons(out_dir) + + -- https://gitlab.com/cki-project/kernel-ark/-/merge_requests/2917 -- _______________________________________________ kernel mailing list -- kernel@xxxxxxxxxxxxxxxxxxxxxxx To unsubscribe send an email to kernel-leave@xxxxxxxxxxxxxxxxxxxxxxx Fedora Code of Conduct: https://docs.fedoraproject.org/en-US/project/code-of-conduct/ List Guidelines: https://fedoraproject.org/wiki/Mailing_list_guidelines List Archives: https://lists.fedoraproject.org/archives/list/kernel@xxxxxxxxxxxxxxxxxxxxxxx Do not reply to spam, report it: https://pagure.io/fedora-infrastructure/new_issue