File uki.spec of Package uki

#
# spec file for package uki
#
# Copyright (c) 2025 SUSE LLC
#
# All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed
# upon. The license for this file, and modifications and additions to the
# file, is the same license as for the pristine package itself (unless the
# license for the pristine package is not an Open Source License, in which
# case the license is the MIT License). An "Open Source License" is a
# license that conforms to the Open Source Definition (Version 1.9)
# published by the Open Source Initiative.

# Please submit bugfixes or comments via https://bugs.opensuse.org/
#
# needsbinariesforbuild
# needssslcertforbuild


%global debug_package %{nil}

# Flavors conditions
# -------------------------
%define flavor @BUILD_FLAVOR@%{nil}
%if "%{flavor}" != ""
%define name_suffix -%{flavor}
%endif
# -------------------------
# Kernel name
# -------------------------
%ifarch %ix86 x86_64
%define ker_name vmlinuz
%endif
%ifarch ppc ppc64 ppc64le
%define ker_name vmlinux
%endif
%ifarch s390 s390x
%define ker_name image
%endif
%ifarch %arm
%define ker_name zImage
%endif
%ifarch aarch64 riscv64
%define ker_name Image
%endif
# -------------------------
# usefull variables
# -------------------------
%global uname %(ls /lib/modules | head -n 1)
%global kernel_version %(rpm -q --queryformat '%%{VERSION}' kernel-%{flavor})
%global initrd_pkg static-initrd-generic-%{flavor}-unsigned
%global initrd_version %(rpm -q --queryformat '%%{VERSION}' %{initrd_pkg} | sed 's|_k.*||')
%global initrd_release %(rpm -q --queryformat '%%{RELEASE}' %{initrd_pkg} | cut -d "." -f1)
%define etf /boot/efi
%define distro opensuse
%define efi_dir EFI/%{distro}
%define efi_path %{etf}/%{efi_dir}
%define efi_name uki-%{flavor}-%{initrd_version}-%{initrd_release}
%define url https://uapi-group.org/specifications/specs/unified_kernel_image
%define uki_install_dir %{kernel_module_directory}/%{uname}
%global cert_install %(if [ -f %{_sourcedir}/_projectcert.crt ]; then echo 1; else echo 0; fi)
%define cert_name uki-%{flavor}
%define cert_install_dir %{_datarootdir}/unified/certs
# -------------------------
# Module Kernel macro
# -------------------------
# TW is usrmerged
%if %{undefined usrmerged} && 0%{?suse_version} >= 1550
%define usrmerged 1
%endif

%if 0%{?usrmerged}
%define kernel_module_directory /usr/lib/modules
%else
%define kernel_module_directory /lib/modules
%endif

# source_rel is the package release string, without the rebuild counter
# generated by the build service. If the release string has a non-digit
# suffix, we keep that suffix and strip the rightmost digit component.
# This is used in KOTD builds: 2.1.g1234567 -> 2.g1234567
%define source_rel %(echo %release | sed -r 's/\\.[0-9]+($|\\.[^.]*[^.0-9][^.]*$)/\\1/')
# -------------------------

Name:           uki%{?name_suffix}
Version:        %{initrd_version}_%{initrd_release}_k%{kernel_version}
Release:        0
Summary:        Signed Unified Kernel Image
License:        Apache-2.0
URL:            %{url}
Group:          System/Kernel
Provides:       uki:%{cert_install_dir}
%if "%{flavor}" == ""
ExclusiveArch:  do_not_build
%else
ExclusiveArch:  x86_64
%endif
BuildRequires:  pesign-obs-integration
# -------------------------
# Needed to have the ukify tool
# -------------------------
BuildRequires:  systemd, systemd-experimental
BuildRequires:  %{python_module cryptography}
BuildRequires:  %{python_module pefile}
# -------------------------
BuildRequires:  %{initrd_pkg}
BuildRequires:  gptfdisk
BuildRequires:  kernel-%{flavor}
BuildRequires:  openssl
BuildRequires:  pesign
BuildRequires:  systemd-boot
BuildRequires:  tpm2.0-tools
BuildRequires:  udev
BuildRequires:  uki-tool >= 1.5.0
BuildRequires:  update-bootloader
BuildRoot:      %{_tmppath}/%{name}-%{version}-build
Provides:       %{name} = %{version}-%{source_rel}
Provides:       multiversion(kernel)

Requires:       uki-tool >= 1.5.0
# Needed in post to change the type of the partition
Requires:       gptfdisk
Requires:       shim >= 11
# Needed by systemd-pcrphase at boot
Requires:       pkgconfig(tss2-tcti-device)
# Install the UKI with the correct kernel version
Requires:       kernel-%{flavor} = %{kernel_version}

%description
Generic signed Unified Kernel Image based on kernel %{flavor}

%prep
# Get the certificates used to sign
if test -e %{_sourcedir}/_projectcert.crt ; then
    cp %{_sourcedir}/_projectcert.crt ./%{cert_name}.crt
    openssl x509 -in ./%{cert_name}.crt -outform DER -out ./%{cert_name}.der
fi
# Get the static initrd from RPM.
rpm2cpio /.build.binaries/%{initrd_pkg}.rpm | cpio -idmv

%build
staticinitrd="./usr/share/initrd/$(basename ./usr/share/initrd/static-initrd-generic-*.unsigned)"
if [ ! -f "$staticinitrd" ]; then
    echo "No static initrd extracted"
    exit 1
fi
uki-tool create \
    -k %{uname} \
    -i "$staticinitrd" \
    -c "rw quiet systemd.show_status=1 console=ttyS0,115200 console=tty0"\
    -n %{efi_name}.efi \
    -o .

%install
export BRP_PESIGN_FILES="%{uki_install_dir}/%{efi_name}.efi"
install -d -m 0700 %{buildroot}%{uki_install_dir}/
install -m 0755 ./%{efi_name}.efi \
    %{buildroot}%{uki_install_dir}/%{efi_name}.efi
install -d -m 0700 %{buildroot}%{uki_install_dir}/%{efi_name}.efi.extra.d

%if %{cert_install}
install -d -m 0700 %{buildroot}%{cert_install_dir}
fpr=$(openssl x509 -sha1 -fingerprint -inform DER -noout -in ./%{cert_name}.der \
    | cut -c 18- | cut -d ":" -f 1,2,3,4 | sed 's/://g')
install -m 644 ./%{cert_name}.der \
    %{buildroot}%{cert_install_dir}/%{efi_name}-${fpr}.crt
%endif

%posttrans
# Change type of the file system partition from 8300 to 8304
dev_name=$(df -h / | tail -1 | cut -d ' ' -f1)
dev_part=${dev_name::-1}
dev_no=${dev_name: -1}
if [[ $dev_no && $dev_part ]] &&\
   [[ -b "${dev_part}${dev_no}" ]]; then
    echo "Change partition type of ${dev_part}${dev_no} in 8304"
    sgdisk -t $dev_no:8304 $dev_part || true
fi
# Add menuentry to bootloaders
entry_added=0
if [ $(command -v sdbootutil) ] && [ "$(sdbootutil bootloader)" == "systemd-boot" ]; then
  if (command -v sdbootutil > /dev/null 2>&1); then
    if uki-tool -q sdboot \
      --add \
      --all-ukis \
      --efi %{efi_dir}; then
        echo "Entry for UKI has been added to sdboot"
        entry_added=1
    else
        echo "Entry for UKI has not been added to sdboot"
        exit 0
    fi
  fi
else
  if (command -v grub2-mkconfig > /dev/null 2>&1); then
    if uki-tool -q grub2 \
      --add \
      --all-ukis \
      --efi %{efi_dir}; then
        echo "Entry for UKI has been added to Grub"
        entry_added=1
    else
        echo "Entry for UKI hasn't been added to Grub"
        exit 0
    fi
  fi
fi
cert_file=$(basename %{cert_install_dir}/%{efi_name}-*.crt)
cert_file_p="%{cert_install_dir}/${cert_file}"
if test "$entry_added" = "1" -a -f ${cert_file_p} ; then
    echo -e "\033[0;32mTo enroll the uki certificate key please run:\033[0m"
    echo -e "\033[0;32mmokutil \
--import ${cert_file_p}\033[0m"
fi

%preun
if [ $(command -v sdbootutil) ] && [ "$(sdbootutil bootloader)" == "systemd-boot" ]; then
  if uki-tool -q sdboot \
    --remove \
    --all-ukis \
    --efi %{efi_dir}; then
    echo "Entry for UKI has been removed from sdboot"
  else
    echo "Entry for UKI has not been removed from sdboot"
    exit 0
  fi
else
  if (command -v grub2-mkconfig > /dev/null 2>&1); then
    if uki-tool -q grub2 \
      --remove \
      --all-ukis \
      --efi %{efi_dir}; then
        echo "Entries for UKI has been removed from grub2"
    else
        echo "Entries for UKI has not been removed from grub2"
        exit 0
    fi
  fi
fi

%files
%defattr(-,root,root)
%attr(0644, root, root) %{kernel_module_directory}/%{uname}/%{efi_name}.efi
%{kernel_module_directory}/%{uname}/%{efi_name}.efi.extra.d
%if %{cert_install}
%dir %{_datarootdir}/unified
%dir %{_datarootdir}/unified/certs
%{cert_install_dir}/%{efi_name}-*.crt
%endif

%changelog
openSUSE Build Service is sponsored by