On Sat, 2 Sept 2023 at 02:32, Michelle Lin <michelle.lint...@gmail.com> wrote:
>
> Currently, there is not a class to support the building of unified kernel
> images. Adding a uki.bbclass to support the creation of UKIs. This class calls
> the systemd Ukify tool, which will combine the kernel/initrd/stub components 
> to
> build the UKI. To sign the UKI (i.e. SecureBoot, TPM PCR signing), the 
> keys/cert
> files are to be specified in a separate configuration file, and the path to 
> the
> file is passed to the Ukify tool. UKIs are supported by UEFI and can improve
> security through predicted TPM PCR states, and reduce the build burden due to
> its single PE binary format.
>
> Signed-off-by: Michelle Lin <michelle.lint...@gmail.com>
> ---
>  meta/classes/uki.bbclass                 | 140 +++++++++++++++++++++++
>  meta/recipes-core/systemd/systemd_254.bb |  23 ++++
>  2 files changed, 163 insertions(+)
>  create mode 100644 meta/classes/uki.bbclass
>
> diff --git a/meta/classes/uki.bbclass b/meta/classes/uki.bbclass
> new file mode 100644
> index 0000000000..2eff387c75
> --- /dev/null
> +++ b/meta/classes/uki.bbclass
> @@ -0,0 +1,140 @@
> +#
> +# Unified kernel image (UKI) class
> +#
> +#
> +# This bbclass is designed to repack an Overlake image as a UKI, to be 
> booted on a qemuarm64 with SecureBoot
> +# signing and embedded with TPM PCR measurements.
> +#
> +# The UKI is composed by:
> +#   - an UEFI stub
> +#     The linux kernel can generate a UEFI stub, however the one from 
> systemd-boot can fetch
> +#     the command line from a separate section of the EFI application, 
> avoiding the need to
> +#     rebuild the kernel.
> +#   - the kernel
> +#   - an initramfs
> +#   - other metadata (e.g. PCR measurements)
> +#
> +#
> +#
> +
> +# List build time dependencies
> +DEPENDS += "systemd-native \
> +            sbsigntool-native \
> +            virtual/${TARGET_PREFIX}binutils \
> +            "
> +
> +REQUIRED_DISTRO_FEATURES += "usrmerge systemd"
> +
> +inherit features_check
> +require ../conf/image-uefi.conf
> +
> +INITRD_IMAGE ?= "core-image-minimal-initramfs"
> +
> +INITRD_LIVE ?= "${@ ('${DEPLOY_DIR_IMAGE}/' + d.getVar('INITRD_IMAGE') + 
> '-${MACHINE}.cpio.gz') if d.getVar('INITRD_IMAGE') else ''}"
> +
> +UKI_CONFIG_FILE ?= "${WORKDIR}/core-image-minimal-uki.conf"
> +UKI_FILENAME ?= "${@ 'UKI.signed.efi' if d.getVar('UKI_CONFIG_FILE') else 
> 'UKI.unsigned.efi'}"
> +
> +do_uki[depends] += " \
> +                        systemd-boot:do_deploy \
> +                        virtual/kernel:do_deploy \
> +                     "
> +
> +# INITRD_IMAGE is added to INITRD_LIVE, which we use to create our initrd, 
> so depend on it if it is set
> +# So we want to generate the initrd image if INITRD_IMAGE exists
> +do_uki[depends] += "${@ '${INITRD_IMAGE}:do_image_complete' if 
> d.getVar('INITRD_IMAGE') else ''}"
> +
> +# ensure that the build directory is empty everytime we generate a 
> newly-created uki
> +do_uki[cleandirs] = "${B}"
> +# influence the build directory at the start of the builds
> +do_uki[dirs] = "${B}"
> +
> +# we want to allow specifying files in SRC_URI, such as for signing the UKI
> +python () {
> +    d.delVarFlag("do_fetch","noexec")
> +    d.delVarFlag("do_unpack","noexec")
> +}
> +
> +# main task
> +python do_uki() {
> +    import glob
> +    import subprocess
> +
> +    # Construct the ukify command
> +    ukify_cmd = ("ukify build")
> +
> +    # Handle the creation of an initrd image by reading and concatenating 
> multiple cpio files.
> +    # If the INITRD_LIVE variable is defined and not empty, it opens the 
> necessary files, reads their contents,
> +    # and constructs a list.
> +    if d.getVar('INITRD_LIVE'):
> +        initrd_list = ""
> +        for cpio in d.getVar('INITRD_LIVE').split():
> +            # get a list of initrds
> +            initrd_list += cpio + ' '
> +
> +        ukify_cmd += " --initrd=%s" % initrd_list
> +    else:
> +        bb.fatal("ERROR - Required argument: INITRD")
> +
> +    deploy_dir_image = d.getVar('DEPLOY_DIR_IMAGE')
> +
> +    # Kernel
> +    if d.getVar('KERNEL_IMAGETYPE'):
> +        kernel = "%s/%s" % (deploy_dir_image, d.getVar('KERNEL_IMAGETYPE'))
> +        kernel_version = d.getVar('KERNEL_VERSION')
> +        if not os.path.exists(kernel):
> +            bb.fatal(f"ERROR: cannot find {kernel}.")
> +
> +        ukify_cmd += " --linux=%s --uname %s" % (kernel, kernel_version)
> +    else:
> +        bb.fatal("ERROR - Required argument: KERNEL")
> +
> +    # Architecture
> +    target_arch = d.getVar('EFI_ARCH')
> +    ukify_cmd += " --efi-arch %s" % target_arch
> +
> +    # Stub
> +    stub = "%s/linux%s.efi.stub" % (deploy_dir_image, target_arch)
> +    if not os.path.exists(stub):
> +        bb.fatal(f"ERROR: cannot find {stub}.")
> +    ukify_cmd += " --stub %s" % stub
> +
> +    # Add option for dtb
> +    if d.getVar('KERNEL_DEVICETREE'):
> +        first_dtb = d.getVar('KERNEL_DEVICETREE').split()[0]
> +        dtb_path = "%s/%s" % (deploy_dir_image, first_dtb)
> +
> +        if not os.path.exists(dtb_path):
> +            bb.fatal(f"ERROR: cannot find {dtb_path}.")
> +
> +        ukify_cmd += " --devicetree %s" % dtb_path

Hmm, I have not noticed this before. This doesn't look generic enough.
KERNEL_DEVICETREE can have several DT files and the first one is not
in any way special. It should not be picked up for the UKI image.
E.g. in our (meta-qcom / qcom-armv8a) case the KERNEL_DEVICETREE lists
dtb for all supported machines, ranging from the old dragonboard410c
up to the latest HDKs.

> +
> +    # Add option to pass a config file to sign the UKI.
> +    if os.path.exists(d.getVar('UKI_CONFIG_FILE')):
> +        ukify_cmd += " --config=%s" % d.getVar('UKI_CONFIG_FILE')
> +        ukify_cmd += " --tools=%s%s/lib/systemd/tools" % 
> (d.getVar("RECIPE_SYSROOT_NATIVE"), d.getVar("prefix"))
> +        bb.note("Pulling keys from config file")
> +    else:
> +        bb.note("Generating unsigned UKI")
> +
> +    # Custom UKI name
> +    output = " --output=%s" % d.getVar('UKI_FILENAME')
> +    ukify_cmd += " %s" % output
> +
> +    # Set env to determine where bitbake should look for dynamic libraries
> +    env = os.environ.copy() # get the env variables
> +    env['LD_LIBRARY_PATH'] = 
> d.expand("${RECIPE_SYSROOT_NATIVE}/usr/lib/systemd:${LD_LIBRARY_PATH}")
> +
> +    # Run the ukify command
> +    subprocess.check_call(ukify_cmd, env=env, shell=True)
> +}
> +
> +inherit deploy
> +
> +do_deploy () {
> +    # Copy generated UKI into DEPLOYDIR
> +       install ${B}/${UKI_FILENAME} ${DEPLOYDIR}
> +}
> +
> +addtask uki before do_deploy do_image after do_rootfs
> +addtask deploy before do_build after do_compile
> \ No newline at end of file
> diff --git a/meta/recipes-core/systemd/systemd_254.bb 
> b/meta/recipes-core/systemd/systemd_254.bb
> index 8d5cf13095..65f132abb8 100644
> --- a/meta/recipes-core/systemd/systemd_254.bb
> +++ b/meta/recipes-core/systemd/systemd_254.bb
> @@ -6,6 +6,9 @@ PE = "1"
>
>  DEPENDS = "intltool-native gperf-native libcap util-linux 
> python3-jinja2-native"
>
> +# The Ukify tool requires this module
> +DEPENDS:append:class-native = " python3-pefile-native"
> +
>  SECTION = "base/shell"
>
>  inherit useradd pkgconfig meson perlnative update-rc.d update-alternatives 
> qemu systemd gettext bash-completion manpages features_check
> @@ -18,6 +21,8 @@ REQUIRED_DISTRO_FEATURES += "usrmerge"
>  # that we don't build both udev and systemd in world builds.
>  REQUIRED_DISTRO_FEATURES += "systemd"
>
> +REQUIRED_DISTRO_FEATURES:class-native = ""
> +
>  SRC_URI += " \
>             file://touchscreen.rules \
>             file://00-create-volatile.conf \
> @@ -120,6 +125,8 @@ PACKAGECONFIG:remove:libc-musl = " \
>  # https://github.com/seccomp/libseccomp/issues/347
>  PACKAGECONFIG:remove:mipsarch = "seccomp"
>
> +PACKAGECONFIG:class-native = "serial-getty-generator openssl tpm2 efi"
> +
>  TARGET_CC_ARCH:append:libc-musl = " -D__UAPI_DEF_ETHHDR=0 
> -D_LARGEFILE64_SOURCE"
>
>  # Some of the dependencies are weak-style recommends - if not available at 
> runtime,
> @@ -260,6 +267,9 @@ EXTRA_OEMESON += "-Dkexec-path=${sbindir}/kexec \
>                    -Dloadkeys-path=${bindir}/loadkeys \
>                    -Dsetfont-path=${bindir}/setfont"
>
> +EXTRA_OEMESON:append:class-native = " -Dbootloader=true \
> +                                      -Dman=false \
> +                                    "
>  # The 60 seconds is watchdog's default vaule.
>  WATCHDOG_TIMEOUT ??= "60"
>
> @@ -380,6 +390,14 @@ do_install() {
>      fi
>  }
>
> +do_install:class-native() {
> +       meson_do_install
> +       install -d ${D}${bindir}
> +       install -m 0755 ${S}/src/ukify/ukify.py ${D}${bindir}/ukify
> +       install -d ${D}${prefix}/lib/systemd/tools
> +       install -m 0755 ${B}/systemd-measure ${D}${prefix}/lib/systemd/tools
> +}
> +
>  python populate_packages:prepend (){
>      systemdlibdir = d.getVar("rootlibdir")
>      do_split_packages(d, systemdlibdir, r'^lib(.*)\.so\.*', 'lib%s', 
> 'Systemd %s library', extra_depends='', allow_links=True)
> @@ -702,6 +720,9 @@ RRECOMMENDS:${PN} += "systemd-extra-utils \
>                        ${@bb.utils.contains('PACKAGECONFIG', 'logind', 
> 'pam-plugin-umask', '', d)} \
>  "
>
> +RRECOMMENDS:${PN}:class-native = ""
> +RDEPENDS:${PN}:class-native = ""
> +
>  INSANE_SKIP:${PN} += "dev-so libdir"
>  INSANE_SKIP:${PN}-dbg += "libdir"
>  INSANE_SKIP:${PN}-doc += " libdir"
> @@ -852,3 +873,5 @@ pkg_postinst:udev-hwdb () {
>  pkg_prerm:udev-hwdb () {
>         rm -f $D${sysconfdir}/udev/hwdb.bin
>  }
> +
> +BBCLASSEXTEND += "native"
> --
> 2.34.1
>
>
> 
>


-- 
With best wishes
Dmitry
-=-=-=-=-=-=-=-=-=-=-=-
Links: You receive all messages sent to this group.
View/Reply Online (#191364): 
https://lists.openembedded.org/g/openembedded-core/message/191364
Mute This Topic: https://lists.openembedded.org/mt/101106095/21656
Group Owner: openembedded-core+ow...@lists.openembedded.org
Unsubscribe: https://lists.openembedded.org/g/openembedded-core/unsub 
[arch...@mail-archive.com]
-=-=-=-=-=-=-=-=-=-=-=-

Reply via email to