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 <[email protected]> --- 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 -- _______________________________________________ meta-intel mailing list [email protected] https://lists.yoctoproject.org/listinfo/meta-intel
