[meta-intel] [PATCH RFC 4/4] classes: Add uefi-comboapp bbclass

California Sullivan california.l.sullivan at intel.com
Fri Jun 9 18:30:56 PDT 2017


This bbclass pulls in the portion of code that builds a UEFI combo
application from meta-refkit to meta-intel. The combo app contains an
EFI stub from systemd, which allows you to boot a kernel directly, a
kernel, an initramfs, and a command line.

This class is intended to be used as an EFI_PROVIDER or a target for
wic, and does not include the entire _dsk type image creation seen in
meta-refkit.

Signed-off-by: California Sullivan <california.l.sullivan at intel.com>
---
 classes/uefi-comboapp.bbclass | 196 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 196 insertions(+)
 create mode 100644 classes/uefi-comboapp.bbclass

diff --git a/classes/uefi-comboapp.bbclass b/classes/uefi-comboapp.bbclass
new file mode 100644
index 0000000..f0aced3
--- /dev/null
+++ b/classes/uefi-comboapp.bbclass
@@ -0,0 +1,196 @@
+# This class brings a more generic version of the UEFI combo app from refkit to meta-intel.
+# It uses a combo file, containing kernel, initramfs and
+# command line, presented to the BIOS as UEFI application, by prepending
+# it with the efi stub obtained from systemd-boot.
+# Needed to use native python libraries
+
+inherit pythonnative
+
+# Don't add syslinux or build an ISO
+PCBIOS_forcevariable = "0"
+NOISO_forcevariable  = "1"
+
+# We use the initrd as the source of our initramfs
+INITRD_IMAGE ??= "core-image-minimal-initramfs"
+
+do_uefiapp[depends] += " \
+                         systemd-boot:do_deploy \
+                         virtual/kernel:do_deploy \
+                         ${INITRD_IMAGE}:do_image_complete \
+                       "
+
+# Always ensure that the INITRD_IMAGE gets added to the initramfs .cpio.
+# This needs to be done even when the actual .dsk image format is inactive,
+# because the .cpio file gets copied into the rootfs, and that rootfs
+# must be consistent regardless of the image format. This became relevant
+# when adding swupd bundle support, because there virtual images
+# without active .dsk are used to generate the rootfs for other
+# images with .dsk format.
+INITRD_LIVE_append = " ${@ ('${DEPLOY_DIR_IMAGE}/' + d.getVar('INITRD_IMAGE', expand=True) + '-${MACHINE}.cpio.gz') if d.getVar('INITRD_IMAGE', True) else ''}"
+
+PACKAGES = " "
+EXCLUDE_FROM_WORLD = "1"
+
+inherit deploy
+
+# UEFIAPP_BUILDERS variable is used to create one or more combo apps.
+# The following format is used: "UUID1;extra-cmdline1;suffix1 UUID2:extra-cmdline2:suffix2" and so on
+# The extra command line and suffix fields are not necessary, but without the suffix field a combo app may get overwritten
+# To reduce complication, the extra commandline section can currently only add one value
+UEFIAPP_BUILDERS ??= "${REMOVABLE_MEDIA_ROOTFS_PARTUUID_VALUE};;"
+
+# The image does without traditional bootloader.
+# In its place, instead, it uses a single UEFI executable binary, which 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
+#   - the initramfs
+#   There is a catch: all of these binary components must have the same word size as the BIOS:
+#   either 32 or 64 bit.
+python do_uefiapp() {
+    import string, shutil, glob
+    from subprocess import check_call
+
+    uefiapp_partuuids = []
+    uefiapp_cmdlines  = []
+    uefiapp_suffixes  = []
+
+    uefiapp_buildstrings = d.getVar('UEFIAPP_BUILDERS', True)
+
+    if uefiapp_buildstrings is "":
+        bb.fatal("UEFIAPP_BUILDERS is empty")
+
+    for buildstring in uefiapp_buildstrings.split():
+        sections = buildstring.split(';')
+        if len(sections) < 3:
+            uefiapp_suffixes.append("")
+        else:
+            uefiapp_suffixes.append(sections[2])
+        if len(sections) < 2:
+            uefiapp_cmdlines.append("")
+        else:
+            uefiapp_cmdlines.append(sections[1])
+        uefiapp_partuuids.append(sections[0])
+
+    if os.path.exists(d.expand('${B}/initrd')):
+        os.remove(d.expand('${B}/initrd'))
+    # initrd is a concatenation of compressed cpio archives
+    # (initramfs, microcode, etc.)
+    with open(d.expand('${B}/initrd'), 'wb') as dst:
+        for cpio in d.getVar('INITRD_LIVE', True).split():
+            with open(cpio, 'rb') as src:
+                dst.write(src.read())
+    with open(d.expand('${B}/machine.txt'), 'w') as f:
+        f.write(d.expand('${MACHINE}'))
+    if '64' in d.getVar('MACHINE', True):
+        executable = 'bootx64.efi'
+    else:
+        executable = 'bootia32.efi'
+
+    def generate_app(partuuid, cmdline, suffix):
+        with open(d.expand('${B}/cmdline' + suffix + '.txt'), 'w') as f:
+            f.write(d.expand('${APPEND} root=PARTUUID=%s %s' % \
+                             (partuuid, cmdline)))
+        check_call(d.expand('objcopy ' +
+                          '--add-section .osrel=${B}/machine.txt ' +
+                              '--change-section-vma  .osrel=0x20000 ' +
+                          '--add-section .cmdline=${B}/cmdline' + suffix + '.txt ' +
+                              '--change-section-vma .cmdline=0x30000 ' +
+                          '--add-section .linux=${DEPLOY_DIR_IMAGE}/bzImage ' +
+                              '--change-section-vma .linux=0x40000 ' +
+                          '--add-section .initrd=${B}/initrd ' +
+                              '--change-section-vma .initrd=0x3000000 ' +
+                          glob.glob(d.expand('${DEPLOY_DIR_IMAGE}/linux*.efi.stub'))[0] +
+                          ' ${B}/' + executable + suffix
+                          ).split())
+        if not os.path.exists(d.expand('${DEPLOYDIR}/EFI' + suffix + '/BOOT')):
+            os.makedirs(d.expand('${DEPLOYDIR}/EFI' + suffix + '/BOOT'))
+        shutil.copyfile(d.expand('${B}/' + executable + suffix), d.expand('${DEPLOYDIR}/EFI' + suffix + '/BOOT/' + executable))
+
+    for i in range(len(uefiapp_partuuids)):
+        generate_app(uefiapp_partuuids[i], uefiapp_cmdlines[i], uefiapp_suffixes[i])
+}
+
+DEPLOYDIR = "${WORKDIR}/uefiapp-${PN}"
+SSTATETASKS += "do_uefiapp"
+do_uefiapp[dirs] = "${DEPLOYDIR} ${B}"
+do_uefiapp[vardeps] += " APPEND UEFIAPP_BUILDERS"
+do_uefiapp[sstate-inputdirs] = "${DEPLOYDIR}"
+do_uefiapp[sstate-outputdirs] = "${DEPLOY_DIR_IMAGE}/${IMAGE_NAME}-uefiapp"
+
+addtask do_uefiapp before do_rootfs
+
+python do_uefiapp_setscene () {
+    sstate_setscene(d)
+}
+
+uefiapp_sign() {
+    if [ -f ${UEFIAPP_SIGNING_KEY} ] && [ -f ${UEFIAPP_SIGNING_CERT} ]; then
+        for i in `find ${DEPLOYDIR} -name '*.efi'`; do
+            sbsign --key ${UEFIAPP_SIGNING_KEY} --cert ${UEFIAPP_SIGNING_CERT} $i
+            sbverify --cert ${UEFIAPP_SIGNING_CERT} $i.signed
+            mv $i.signed $i
+        done
+    fi
+}
+
+uefiapp_deploy() {
+    # Let's make sure that only what is needed stays in the /boot dir
+    rm -rf ${IMAGE_ROOTFS}/boot/*
+    cp  --preserve=timestamps -r ${DEPLOYDIR}/* ${IMAGE_ROOTFS}/boot/
+    chown -R root:root ${IMAGE_ROOTFS}/boot
+}
+
+addtask do_uefiapp_setscene
+
+# Re-run do_rootfs (and signing) if the key content changes. The name is irrelevant.
+# Also checks that the variables are set at parse time instead of failing during image building.
+do_rootfs[vardeps] += '${@bb.utils.contains('IMAGE_FEATURES','secureboot','UEFIAPP_SIGNING_CERT_HASH UEFIAPP_SIGNING_KEY_HASH','',d)}'
+python () {
+    import os
+    import hashlib
+
+    if bb.utils.contains('IMAGE_FEATURES', 'secureboot', True, False, d):
+        for varname in ('UEFIAPP_SIGNING_CERT', 'UEFIAPP_SIGNING_KEY'):
+            filename = d.getVar(varname)
+            if filename is None:
+                bb.fatal('%s is not set.' % varname)
+            if not os.path.isfile(filename):
+                bb.fatal('%s=%s is not a file.' % (varname, filename))
+            with open(filename, 'rb') as f:
+                data = f.read()
+            hash = hashlib.sha256(data).hexdigest()
+            d.setVar('%s_HASH' % varname, hash)
+
+            # Must reparse and thus rehash on file changes.
+            bb.parse.mark_dependency(d, filename)
+}
+
+do_rootfs[depends] += '${@bb.utils.contains('IMAGE_FEATURES','secureboot','sbsigntool-native:do_populate_sysroot','',d)}'
+
+efi_hddimg_populate() {
+    DEST=$1
+    cp  --preserve=timestamps -r ${DEPLOYDIR}/* ${DEST}/
+}
+
+build_efi_cfg() {
+    # The command line is built into the combo app, so this is a null op
+    :
+}
+
+populate_kernel_append() {
+        # The kernel and initrd are built into the app, so we don't need these
+    if [ -f $dest/initrd ]; then
+        rm $dest/initrd
+    fi
+    if [ -f $dest/vmlinuz ]; then
+        rm $dest/vmlinuz
+    fi
+}
+
+IMAGE_FEATURES[validitems] += "secureboot"
+ROOTFS_POSTPROCESS_COMMAND += " ${@bb.utils.contains('IMAGE_FEATURES','secureboot','uefiapp_sign;','',d)} "
+ROOTFS_POSTPROCESS_COMMAND += " uefiapp_deploy; "
-- 
2.5.5



More information about the meta-intel mailing list