File uki.spec of Package uki

#
# spec file for package uki
#
# Copyright (c) 2023 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}
%define name_suffix -%{flavor}
# -------------------------
# 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 %(echo "%{uname}" | cut -d '-' -f1)
%global initrd_pkg static-initrd-generic-unsigned
%global initrd_version %(rpm -q --queryformat '%{VERSION}' %{initrd_pkg} | sed 's|_k.*||')
%global initrd_release %(rpm -q --queryformat '%{RELEASE}' %{initrd_pkg})
%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 certs_name uki-%{flavor}
%define url https://uapi-group.org/specifications/specs/unified_kernel_image
%define uki_install_dir %{kernel_module_directory}/%{uname}
%define uki_extra_dir %{kernel_module_directory}/uki.extra.d/
%define cert_install_dir %{_datarootdir}/unified/certs
# -------------------------

# Includes macros
# -------------------------
%include %_sourcedir/kernel-spec-macros
# -------------------------

Name:           uki-%{flavor}
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:%{uki_extra_dir}
Provides:       uki:%{cert_install_dir}
Source1:        openSUSE-UEFI-CA-Certificate.crt
%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 pefile}
BuildRequires:  %{python_module cryptography}
# -------------------------
BuildRequires:  %{initrd_pkg} 
BuildRequires:  kernel-%{flavor}
BuildRequires:  udev, systemd-boot
BuildRequires:  uki-tool >= 0.3.0+15
BuildRequires:  pesign
BuildRequires:  openssl
BuildRequires:  tpm2.0-tools
BuildRequires:  gptfdisk
BuildRoot:      %{_tmppath}/%{name}-%{version}-build

Requires:       uki-tool >= 0.3.0+15
# 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 ./%{certs_name}.crt    
else
    echo "No _projectcert file in sources"
    # To be able to have a success localbuild
    cp %{SOURCE1} ./%{certs_name}.crt
fi
openssl x509 -in ./%{certs_name}.crt -outform DER -out ./%{certs_name}.der
# Get the static initrd from RPM.
rpm2cpio /.build.binaries/static-initrd-generic-unsigned*.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
install -d -m 0700 %{buildroot}%{uki_extra_dir}

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

%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
efi_file=$(basename %{uki_install_dir}/%{efi_name}.efi)
cert_file=$(basename %{cert_install_dir}/%{efi_name}-*.crt)
uname=$(ls /lib/modules | head -n 1)
entry_added=0
if [ $(command -v sdbootutil) ] && [ "$(sdbootutil bootloader)" == "systemd-boot" ]; then
  if (command -v sdbootutil > /dev/null 2>&1); then
    if uki-tool sdboot \
      --add \
      --uki ${efi_file} \
      --efi %{efi_dir} \
      --kerver %{uname}; 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 grub2 \
      --add \
      --uki %{uki_install_dir}/${efi_file} \
      --efi %{efi_dir} \
      --kerver %{uname}; 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
if [ $entry_added -eq 1 ]; then
    echo -e "\033[0;32mTo enroll the uki certificate key please run:\033[0m"
    echo -e "\033[0;32mmokutil \
--import %{cert_install_dir}/${cert_file}\033[0m"
fi

%preun
efi_file=$(basename %{uki_install_dir}/%{efi_name}.efi)
if [ $(command -v sdbootutil) ] && [ "$(sdbootutil bootloader)" == "systemd-boot" ]; then
  if (command -v sdbootutil > /dev/null 2>&1); then
    if uki-tool sdboot \
      --remove \
      --uki ${efi_file} \
      --efi %{efi_dir} \
      --kerver %{uname}; then
      echo "Entry for UKI has been removed from sdboot"
    else
      echo "Entry for UKI has not been removed from sdboot"
      exit 0
    fi
  fi 
else
  if (command -v grub2-mkconfig > /dev/null 2>&1); then
    if uki-tool grub2 \
      --remove \
      --uki %{uki_install_dir}/${efi_file} \
      --efi %{efi_dir} \
      --kerver %{uname}; then
        echo "Entry for UKI has been removed from grub2"
        [ -f %{efi_path}/${efi_file} ] && rm %{efi_path}/${efi_file}
    else
        echo "Entry for UKI has not been removed from grub2"
        exit 0
    fi
  fi
fi


%files
%defattr(-,root,root)
%{kernel_module_directory}/*/%{efi_name}.efi*
%dir %{uki_extra_dir}
%dir %{_datarootdir}/unified
%dir %{_datarootdir}/unified/certs
%{cert_install_dir}/%{efi_name}-*.crt

%changelog
openSUSE Build Service is sponsored by