diff --git a/conf/machine/x64.conf b/conf/machine/x64.conf index 14d2d8477..62bd7bb34 100644 --- a/conf/machine/x64.conf +++ b/conf/machine/x64.conf @@ -10,6 +10,8 @@ DEFAULTTUNE ?= "core2-64" require conf/machine/include/x86/tune-core2.inc require conf/machine/include/x86/x86-base.inc +MACHINE_FEATURES:append = " tpm2" + XSERVER = "\ ${XSERVER_X86_BASE} \ ${XSERVER_X86_EXT} \ diff --git a/docs/example.bb b/docs/example.bb new file mode 100644 index 000000000..fe14fad10 --- /dev/null +++ b/docs/example.bb @@ -0,0 +1,69 @@ +SUMMARY = "foobar - The example project" +DESCRIPTION = "\ +foobar is an example project, used when you need to communicate concepts to \ +developers." +HOMEPAGE = "https://github.com/ni/meta-nilrt" +SECTION = "test" +LICENSE = "MIT" +LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302" + + +# ============================================================================== +# RECIPE VARIABLES +# ============================================================================== + +# If the recipe sources are entirely within OE, we can set the package version +# to match the DISTRO_VERSION. +# PV = "${DISTRO_VERSION}" + + +# ============================================================================== +# SOURCE VARIABLES +# ============================================================================== + +SRC_URI = "\ + file://foo-file.1 \ + file://foo-file.2 \ + file://foo.initd \ +" + +S = "${UNPACKDIR}" + + +# ============================================================================== +# BBCLASSES +# ============================================================================== + +# UPDATE-RC.D +inherit update-rc.d +INITSCRIPT_PARAMS = "default" +INITSCRIPT = "foo" + + +# ============================================================================== +# TASKS +# ============================================================================== + +pkglibdir = "${libdir}/${BPN}" + +do_install () { + install -d ${D}${sysconfdir}/init.d + install foo.initd ${D}${sysconfdir}/init.d/foo + + install -d ${D}${pkglibdir} + install --mode=0755 foo-file.1 ${D}${pkglibdir}/foo-file.1 + install --mode=0744 foo-file.2 ${D}${pkglibdir}/foo-file.2 +} + + +# ============================================================================== +# PACKAGING +# ============================================================================== +# FOO +RDEPENDS:${PN} = "bash" + + +# ============================================================================== +# CLASS EXTENSIONS +# ============================================================================== +# BBCLASSEXTEND = "native" diff --git a/docs/styleguide.md b/docs/styleguide.md index 0cc01fc2c..59bdd87fc 100644 --- a/docs/styleguide.md +++ b/docs/styleguide.md @@ -138,61 +138,3 @@ At a minimum, you should add an additional message trailer declaring the [upstre #### .patch file names When bitbake applies `.patch` files to a recipe, it copies all `.patch` files into the recipe's workspace, then applies them in alphanumeric-order. In your PR, be mindful of how your `.patch` file is ordered versus the other files in the recipe. Keep in mind that some `.patch` files might come from other layers. - - -# Example Recipe -This is an example recipe for a package whose source is contained in the meta-nilrt layer. - -`.../foobar/` -``` -foobar/ -|- files/ -| |- foo-file.1 -| |- foo-file.2 -| \- foo.initd -\- foobar.bb -``` ----- -`.../foobar/foobar.bb` -``` -SUMMARY = "foobar - The example project" -DESCRIPTION = "\ -foobar is an example project, used when you need to communicate concepts to \ -developers." -HOMEPAGE = "https://github.com/ni/meta-nilrt" -SECTION = "test" -LICENSE = "MIT" -LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302" - - -PV = "1.0" - - -SRC_URI = "\ - file://foo-file.1 \ - file://foo-file.2 \ - file://foo.initd \ -" - -S = "${UNPACKDIR}" - - -inherit update-rc.d -INITSCRIPT_PARAMS = "default" -INITSCRIPT = "foo" - - -prefix_lib=${libdir}/${BPN} - -do_install () { - install -d ${D}${sysconfdir}/init.d - install foo.initd ${D}${sysconfdir}/init.d/foo - - install -d ${D}${prefix_lib} - install --mode=0755 foo-file.1 ${D}${prefix_lib}/foo-file.1 - install --mode=0744 foo-file.2 ${D}${prefix_lib}/foo-file.2 -} - - -RDEPENDS:${PN} = "bash" -``` \ No newline at end of file diff --git a/files/group b/files/group index 0ddf5444d..63ce8368d 100644 --- a/files/group +++ b/files/group @@ -6,6 +6,7 @@ ni:x:500: openvpn:x:499: niwscerts:x:498: # free space +clevis:x:405: krill:x:404: xrdp:x:403: arpwatch:x:402: diff --git a/files/passwd b/files/passwd index e2a3b33ff..9fde0b895 100644 --- a/files/passwd +++ b/files/passwd @@ -6,6 +6,7 @@ webserv:x:501:::: lvuser:x:500:::: openvpn:x:499:::: # free space +clevis:x:405:::: krill:x:404:::: xrdp:x:403:::: arpwatch:x:402:::: diff --git a/recipes-bsp/grub/grub-bootconf_%.bbappend b/recipes-bsp/grub/grub-bootconf_%.bbappend deleted file mode 100644 index 5bf585495..000000000 --- a/recipes-bsp/grub/grub-bootconf_%.bbappend +++ /dev/null @@ -1,26 +0,0 @@ -FILESEXTRAPATHS:prepend := "${THISDIR}/grub:" - -SRC_URI += " \ - file://grub.cfg \ - file://grubenv \ - file://grub.d \ -" - -PACKAGES:prepend = "${PN}-nilrt " - -FILES:${PN}-nilrt += "/boot/efi/nilrt/grub.cfg /boot/grub/grubenv /boot/bootimage.cfg.d/*.cfg" -CONFFILES:${PN}-nilrt += "/boot/efi/nilrt/grub.cfg /boot/grub/grubenv /boot/bootimage.cfg.d/*.cfg" - -do_install:append () { - - # Install NILRT grub.cfg - install -d ${D}/boot/efi/nilrt - install -m 0644 ${S}/grub.cfg ${D}/boot/efi/nilrt/grub.cfg - - # Install empty grubenv - install -d "${D}/boot/grub" - install -m 0644 "${S}/grubenv" "${D}/boot/grub/grubenv" - - install -d "${D}/boot/bootimage.cfg.d" - install -m 0644 ${S}/grub.d/* ${D}/boot/bootimage.cfg.d -} diff --git a/recipes-bsp/grub/grub-efi_2.%.bbappend b/recipes-bsp/grub/grub-efi_2.%.bbappend index 185a1fac8..574cf2a43 100644 --- a/recipes-bsp/grub/grub-efi_2.%.bbappend +++ b/recipes-bsp/grub/grub-efi_2.%.bbappend @@ -1,12 +1,37 @@ require grub-nilrt.inc -GRUB_BUILDIN += "smbios chain multiboot efi_uga font gfxterm gfxmenu terminal \ - minicmd iorw echo reboot terminfo loopback memdisk tar help serial \ - ls search_fs_uuid udf btrfs ntfs reiserfs xfs lvm ata \ - regexp probe" - -# Downstream NI-branch code quality is not yet ready to build with -Werror -CFLAGS:append = " -Wno-error" +GRUB_BUILDIN:append = " \ + ata \ + btrfs \ + chain \ + echo \ + efi_uga \ + font \ + gfxmenu \ + gfxterm \ + help \ + iorw \ + loopback \ + ls \ + lvm \ + memdisk \ + minicmd \ + multiboot \ + ntfs \ + probe \ + reboot \ + regexp \ + reiserfs \ + search_fs_uuid \ + serial \ + smbios \ + tar \ + terminal \ + terminfo \ + tpm \ + udf \ + xfs \ +" PACKAGES:prepend = "${PN}-nilrt " @@ -17,10 +42,13 @@ do_install:append:class-target() { # unchanged so that we may use it with USB provisioning tool # and other removable storage. ( - cd "${B}" - grub-mkimage -p /efi/nilrt -d ./grub-core/ \ - -O ${GRUB_TARGET}-efi -o ./${GRUB_IMAGE_PREFIX}nilrt-${GRUB_IMAGE} \ - ${GRUB_BUILDIN} + cd "${B}" + grub-mkimage \ + --prefix=/efi/nilrt \ + --directory=./grub-core/ \ + --format=${GRUB_TARGET}-efi \ + --output=./${GRUB_IMAGE_PREFIX}nilrt-${GRUB_IMAGE} \ + ${GRUB_BUILDIN} ) # Install NILRT grub image diff --git a/recipes-bsp/grub/grub-nilrt.inc b/recipes-bsp/grub/grub-nilrt.inc index b75e22b64..616963f4a 100644 --- a/recipes-bsp/grub/grub-nilrt.inc +++ b/recipes-bsp/grub/grub-nilrt.inc @@ -4,7 +4,6 @@ SRC_URI += "\ file://cmd-test-Add-bitwise-AND-document-the-feature.patch \ file://grub-advertise-NI-NILRT-over-GNU-GRUB.patch \ file://add-inbit-command-to-io-module.patch \ - file://grub.cfg \ file://cfg \ file://grub.d \ " diff --git a/recipes-bsp/grub/grub/grub-runmode-bootimage.cfg b/recipes-bsp/grub/grub/grub-runmode-bootimage.cfg index 7d5be7f1a..c87262d5e 100644 --- a/recipes-bsp/grub/grub/grub-runmode-bootimage.cfg +++ b/recipes-bsp/grub/grub/grub-runmode-bootimage.cfg @@ -2,6 +2,7 @@ set consoleparam='console=tty0 console=ttyS0,115200n8' set kernel_path='/runmode/bzImage' +set ramdisk_path='/runmode/ramdisk.xz' set otherbootargs="rootwait rw usbcore.usbfs_memory_mb=0 consoleblank=0 rcu_nocbs=all ibt=off " diff --git a/recipes-bsp/grub/grub/grub-safemode.cfg b/recipes-bsp/grub/grub/grub-safemode.cfg index 1a22231eb..e0b30180d 100644 --- a/recipes-bsp/grub/grub/grub-safemode.cfg +++ b/recipes-bsp/grub/grub/grub-safemode.cfg @@ -41,6 +41,7 @@ set sys_reset=false set system_manufacturer="" set smbios_bootmode=0 set smbios_tablelen=0 +set measure_on=true # Set the root variable to NI's bootfs partition search --set root --label nibootfs @@ -125,30 +126,33 @@ function setterminal { } function check_valid_os { - #safemode - for file in $safemode_files - do - if [ ! -f /.safe/$file ]; then - set valid_safemode=0 - break - fi - done - - #runmode - for file in $runmode_files - do - if [ ! -f /runmode/$file ]; then - set valid_runmode=0 - break - fi - done + # Safemode + if [ ! -d /.safe ]; then + set valid_safemode=0 + else + for file in $safemode_files + do + if [ ! -f /.safe/$file ]; then + set valid_safemode=0 + break + fi + done + fi - if [ $valid_safemode = 0 -a $valid_runmode = 0 ]; then - set fail_state=1 + # Runmode + if [ ! -d /runmode ]; then + set valid_runmode=0 + else + for file in $runmode_files + do + if [ ! -f /runmode/$file ]; then + set valid_runmode=0 + break + fi + done fi - # rootuuid is only generated at system provision - treat missing value as requiring recovery - if [ "$rootuuid" = "" ]; then + if [ $valid_safemode = 0 -a $valid_runmode = 0 ]; then set fail_state=1 fi } @@ -165,7 +169,15 @@ function boot_runmode { setconsoleparam setusbgadgetparam - linux $kernel_path root=PARTUUID=$rootuuid $otherbootargs $usb_gadget_args $kernellogparam $consoleparam $othbootargs sys_reset=$sys_reset + if [ -n "$ramdisk_path" ]; then + echo 'Loading Linux ...' + linux $kernel_path $otherbootargs $usb_gadget_args $kernellogparam $consoleparam $othbootargs sys_reset=$sys_reset + echo 'Loading initial ramdisk ...' + initrd $ramdisk_path + else + echo 'Loading Linux ...' + linux $kernel_path root=PARTUUID=$rootuuid $otherbootargs $usb_gadget_args $kernellogparam $consoleparam $othbootargs sys_reset=$sys_reset + fi } function boot_safemode { @@ -180,7 +192,9 @@ function boot_safemode { setconsoleparam setusbgadgetparam + echo 'Loading Linux ...' linux $kernel_path $rootfs_path $otherbootargs $usb_gadget_args $kernellogparam $consoleparam $othbootargs sys_reset=$sys_reset + echo 'Loading safemode ramdisk ...' initrd $ramdisk_path } diff --git a/recipes-bsp/grub/grub/grub.cfg b/recipes-bsp/grub/grub/grub.cfg deleted file mode 100644 index 0321f5ec8..000000000 --- a/recipes-bsp/grub/grub/grub.cfg +++ /dev/null @@ -1,30 +0,0 @@ -# NI Linux RT boot loader config - -set timeout=0 -set timeout_style="menu" - -set linux_console="console=tty0 console=ttyS0,115200n8" -set linux_verbosity="debug" -set linux_args="efi_no_storage_paranoia" - -set root_device_fslabel="" -probe --set root_device_fslabel --label "$root" - -set root_device_fsuuid="" -probe --set root_device_fsuuid --fs-uuid "$root" - -load_env -f /grub/grubenv - -set otherbootargs="rootwait rw usbcore.usbfs_memory_mb=0 consoleblank=0 rcu_nocbs=all " - -set usb_gadget_args="g_ether.idVendor=${USBVendorID} g_ether.idProduct=${USBProductID} g_ether.iProduct=${USBProduct}[${hostname}] g_ether.iSerialNumber=${SerialNum} g_ether.dev_addr=${usbgadgetethaddr} g_ether.bcdDevice=${USBDevice}" - -for cfg_d_file in /bootimage.cfg.d/*.cfg; do - source "$cfg_d_file" -done - -menuentry "NI Linux RT ($root_device_fslabel, $root_device_fsuuid)" { - linux /bzImage rootfstype=ramfs rauc.slot=$root_device_fslabel rauc.slot.uuid=$root_device_fsuuid $linux_console $linux_verbosity $linux_args $otherbootargs $usb_gadget_args - initrd /initrd.cpio.gz - boot -} diff --git a/recipes-core/base-files/base-files/x64/fstab b/recipes-core/base-files/base-files/x64/fstab index 5d57bf094..3877eac37 100644 --- a/recipes-core/base-files/base-files/x64/fstab +++ b/recipes-core/base-files/base-files/x64/fstab @@ -1,10 +1,11 @@ # stock fstab - you probably want to override this with a machine specific one -/dev/root / auto defaults 1 1 -proc /proc proc defaults 0 0 -devpts /dev/pts devpts mode=0620,gid=5 0 0 -tmpfs /run tmpfs mode=0755,nodev,nosuid,strictatime 0 0 -tmpfs /var/volatile tmpfs size=26% 0 0 +/dev/root / auto defaults 1 1 +proc /proc proc defaults 0 0 +devpts /dev/pts devpts mode=0620,gid=5 0 0 +tmpfs /run tmpfs mode=0755,nodev,nosuid,strictatime 0 0 +tmpfs /var/volatile tmpfs size=26% 0 0 +securityfs /sys/kernel/security securityfs defaults 0 0 # uncomment this if your device has a SD/MMC/Transflash slot #/dev/mmcblk0p1 /media/card auto defaults,sync,noauto 0 0 diff --git a/recipes-core/images/files/nilrt-base-system-image.postinst b/recipes-core/images/files/nilrt-base-system-image.postinst index e058840e6..94072aac3 100755 --- a/recipes-core/images/files/nilrt-base-system-image.postinst +++ b/recipes-core/images/files/nilrt-base-system-image.postinst @@ -70,11 +70,11 @@ awk '{print $2;}' < /proc/mounts | grep -q /boot || exit 1 /usr/sbin/setcap CAP_SYS_TIME+ep /mnt/userfs/sbin/hwclock.util-linux if [ "$arch" = "armv7l" ]; then - # safemodes older than 8.0 don't bind mount bootfs to /mnt/userfs/bootfs. - # So files that get extracted into /mnt/userfs/boot have to be moved into /boot. - if [ ! -f "$mount_point/linux_runmode.itb" ]; then - mv /mnt/userfs/boot/* "$mount_point" - fi + # The ITB is staged in boot/runmode/ inside the rootfs archive to prevent + # u-boot from seeing a partially-installed runmode if BSI extraction fails. + # Move it to its final /boot location now that all other files are in place. + mv "$mount_point/runmode/linux_runmode.itb" "$mount_point/linux_runmode.itb" + rmdir "$mount_point/runmode" 2>/dev/null || true elif [ "$arch" = "x86_64" ]; then kernel="/mnt/userfs/boot/tmp/runmode" # install the kernel diff --git a/recipes-core/images/nilrt-runmode-initramfs.bb b/recipes-core/images/nilrt-runmode-initramfs.bb new file mode 100644 index 000000000..4385442ee --- /dev/null +++ b/recipes-core/images/nilrt-runmode-initramfs.bb @@ -0,0 +1,67 @@ +DESCRIPTION = "A small initramfs for booting NILRT targets into runmode." + + +# ============================================================================== +# RECIPE VARIABLES +# ============================================================================== + +PV = "${DISTRO_VERSION}" + +require includes/nilrt-core-image.inc + + +# ============================================================================== +# SOFTWARE DISTRIBUTION +# ============================================================================== + +# Do not pollute the initrd image with rootfs features. +IMAGE_FEATURES = "" + +IMAGE_LINGUAS = "" + +IMAGE_INSTALL = "\ + ${ROOTFS_BOOTSTRAP_INSTALL} \ + packagegroup-ni-initramfs \ + init-nilrt-runmode-initramfs \ +" + +BAD_RECOMMENDATIONS += "\ + shared-mime-info \ + ca-certificates \ +" + +NO_RECOMMENDATIONS = "1" + +PACKAGE_EXCLUDE += "python-core python3-core" + +# 1024 MB = 1/2 the smallest x64 cRIO memory size +INITRAMFS_MAXSIZE = "1048578" + + +# ============================================================================== +# TASKS +# ============================================================================== + +bootimg_fixup () { + # Empty out /boot. The kernel and grub are added to the exterior + # image and not this ramdisk container. + rm -rf "${IMAGE_ROOTFS}/boot" +} + +IMAGE_PREPROCESS_COMMAND += " bootimg_fixup; " + + +# ============================================================================== +# DEPLOYMENT +# ============================================================================== + +IMAGE_FSTYPES = "cpio.xz" +IMAGE_NAME_SUFFIX = "" +XZ_COMPRESSION_LEVEL = "-e -9" +XZ_INTEGRITY_CHECK = "crc32" + +# Some BSPs use IMAGE_FSTYPES: which would override +# an assignment to IMAGE_FSTYPES so we need anon python +python () { + d.setVar("IMAGE_FSTYPES", d.getVar("INITRAMFS_FSTYPES")) +} diff --git a/recipes-core/images/nilrt-runmode-rootfs.bb b/recipes-core/images/nilrt-runmode-rootfs.bb index a78030576..4306280a5 100644 --- a/recipes-core/images/nilrt-runmode-rootfs.bb +++ b/recipes-core/images/nilrt-runmode-rootfs.bb @@ -1,8 +1,18 @@ DESCRIPTION = "NI Linux RT runmode rootfs archive" -SRC_URI += "\ - file://bootimage.ini \ -" + +# ============================================================================== +# RECIPE VARIABLES +# ============================================================================== + +DEPENDS += "nilrt-runmode-initramfs" + +PV = "${DISTRO_VERSION}" + + +# ============================================================================== +# SOFTWARE DISTRIBUTION +# ============================================================================== IMAGE_INSTALL = "\ packagegroup-ni-runmode \ @@ -12,38 +22,81 @@ IMAGE_INSTALL = "\ IMAGE_INSTALL:append:x64 = "\ nilrt-grub-runmode \ - " - -require includes/nilrt-image-base.inc -require includes/nilrt-xfce.inc -require includes/nilrt-proprietary.inc +" IMAGE_INSTALL_NODEPS += "\ ${NI_PROPRIETARY_COMMON_PACKAGES} \ ${NI_PROPRIETARY_RUNMODE_PACKAGES} \ " -# Ensure that rauc does not end up in this image. -PACKAGE_EXCLUDE += "rauc rauc-mark-good" -# on older NILRT distro flavors the kernel is installed in non-standard paths -# for backward compatibility -CUSTOM_KERNEL_PATH:x64 ?= "/boot/tmp/runmode" +# ============================================================================== +# IMAGE HERITAGE +# ============================================================================== + +require includes/nilrt-image-base.inc +require includes/nilrt-xfce.inc +require includes/nilrt-proprietary.inc + + +# ============================================================================== +# TASKS +# ============================================================================== + +# Install the bootimage.ini datafile. +install_bootimage () { + install -m 0644 \ + "${THISDIR}/files/bootimage.ini" \ + "${IMAGE_ROOTFS}/boot/runmode/bootimage.ini" + sed -i \ + "s/%component_version%/${BUILDNAME}/" \ + "${IMAGE_ROOTFS}/boot/runmode/bootimage.ini" +} +ROOTFS_POSTPROCESS_COMMAND += " install_bootimage; " + + +# FIXUP KERNEL # -bootimg_fixup_x64() { - install -m 0644 "${THISDIR}/files/bootimage.ini" "${IMAGE_ROOTFS}/boot/runmode/bootimage.ini" - sed -i "s/%component_version%/${BUILDNAME}/" "${IMAGE_ROOTFS}/boot/runmode/bootimage.ini" +RAMDISK_IMAGE = "nilrt-runmode-initramfs" +BOOT_TMP_PATH = "/boot/tmp" - # Postinst script is going to want this all in /boot/tmp/runmode - install -d `dirname "${IMAGE_ROOTFS}/${CUSTOM_KERNEL_PATH}"` - mv "${IMAGE_ROOTFS}/${KERNEL_IMAGEDEST}" "${IMAGE_ROOTFS}/${CUSTOM_KERNEL_PATH}" +# Install the kernel and ramdisk to a temporary path, so that the BSI postinst +# can move them later. +fixup_kernel_x64 () { + install -d "${IMAGE_ROOTFS}/${BOOT_TMP_PATH}" + # kernel + mv \ + "${IMAGE_ROOTFS}/${KERNEL_IMAGEDEST}" \ + "${IMAGE_ROOTFS}/${BOOT_TMP_PATH}/runmode" + # ramdisk + install -m 0644 \ + "${DEPLOY_DIR_IMAGE}/${RAMDISK_IMAGE}-${MACHINE}.cpio.xz" \ + "${IMAGE_ROOTFS}/${BOOT_TMP_PATH}/runmode/ramdisk.xz" } +fixup_kernel_x64[depends] += "${RAMDISK_IMAGE}:do_image_complete" +ROOTFS_POSTPROCESS_COMMAND:append:x64 = " fixup_kernel_x64; " -bootimg_fixup_arm() { - mv "${IMAGE_ROOTFS}/${KERNEL_IMAGEDEST}/fitImage" "${IMAGE_ROOTFS}/${KERNEL_IMAGEDEST}/linux_runmode.itb" + +# Give the ARMv7 runmode ITB a distinct name from the safemode ITB. +fixup_kernel_armv7a () { + # Stage the ITB under boot/runmode/ rather than directly in boot/. + # This prevents u-boot from attempting to boot a partially-extracted + # runmode installation: if BSI extraction fails (e.g. disk full), postinst + # never runs, so the ITB is never moved to its final /boot location and + # u-boot will correctly treat runmode as not installed. + install -d "${IMAGE_ROOTFS}/${KERNEL_IMAGEDEST}/runmode" + mv "${IMAGE_ROOTFS}/${KERNEL_IMAGEDEST}/fitImage" "${IMAGE_ROOTFS}/${KERNEL_IMAGEDEST}/runmode/linux_runmode.itb" + find ${IMAGE_ROOTFS}/${KERNEL_IMAGEDEST} -maxdepth 1 -type f \ + ! -name 'linux_runmode.itb' \ + -exec rm -f {} + } +ROOTFS_POSTPROCESS_COMMAND:append:armv7a = " fixup_kernel_armv7a; " + +# /FIXUP KERNEL # + -IMAGE_PREPROCESS_COMMAND:append:x64 = " bootimg_fixup_x64; " -IMAGE_PREPROCESS_COMMAND:append:xilinx-zynq = " bootimg_fixup_arm; " +# ============================================================================== +# DEPLOYMENT +# ============================================================================== IMAGE_FSTYPES += "squashfs ${NILRT_BSI_FSTYPE}" diff --git a/recipes-core/initrdscripts/files/init-nilrt-ramfs.sh b/recipes-core/initrdscripts/files/init-nilrt-ramfs.sh deleted file mode 100755 index e62b7f195..000000000 --- a/recipes-core/initrdscripts/files/init-nilrt-ramfs.sh +++ /dev/null @@ -1,339 +0,0 @@ -#!/bin/bash -set -euo pipefail - -# close stdin -exec 0<&- - -export PATH="/sbin:/bin:/usr/sbin:/usr/bin" - -umask 0022 - -mkdir -p /proc -mkdir -p /sys -mkdir -p /dev -mount -t proc proc /proc -mount -t sysfs sysfs /sys -mount -t devtmpfs devtmpfs /dev - -# redirect stderr and stdout to /dev/kmsg -exec 2>/dev/kmsg -exec 1>&2 - -function status () { - echo "initramfs: $*" || true -} - -function warn () { - echo "initramfs WARNING: $*" || true -} - -# prints to kernel log and screen -function error () { - echo "initramfs ERROR: $*" || true - - cat >/dev/tty0 || true < "/proc/sys/kernel/printk_devkmsg" -status "Set printk_devkmsg=on (previous: $ORIG_KMSG_CONFIG)" - -ARCH=$(uname -m) -status "Running init process on ARCH=$ARCH" - -# HACK: BIOS enables cstates when they should be disabled and this makes the -# processor frequency go bonkers on crio's 903x dual-core (not 9034) affecting -# performance of the restore-mode's rootfs unpacking. We disable all cstates -# except C0 in all cpu cores until we get a BIOS update with cstates disabled -if [ "$ARCH" == "x86_64" ]; then -( - shopt -s nullglob - for cstate_disable_file in /sys/devices/system/cpu/cpu*/cpuidle/state[^0]/disable; do - status "Setting cstate_disable_file=$cstate_disable_file" - echo 1 > "$cstate_disable_file" - done -) -fi - -# Asserts grep-safe fs identifiers -function assert_valid_fs_id() { - local name="$1" - local value="$2" - echo "$value" | egrep -q '^[a-zA-Z0-9\-]+$' || error "$name: Invalid fs label or UUID" -} - -function read_config_token() { - local filename="$1" - local token="$2" - local value="" - - if [ -e "$filename" ]; then - value=$(grep "^$token=" "$filename" | head -1 | cut -d"=" -f2) || value="" - fi - - echo "$value" -} - -function drop_config_token() { - local filename="$1" - local token="$2" - - if [ ! -e "$filename" ]; then - return - fi - - rm -f "$filename.new" - cp -p "$filename" "$filename.new" - - sed -i "/^""$token""=/d" "$filename.new" - - mv "$filename.new" "$filename" -} - -function read_file() { - local filename="$1" - local maxsize="$2" - local value="" - - if [ -e "$filename" ]; then - value=$(head -c "$maxsize" "$filename") || value="" - fi - - echo "$value" -} - -function read_sha256sum_file() { - read_file "$1" 65 -} - -# Parse kernel cmd line to find nibootX partition's fs label -current_niboot_fs_label=$(cat /proc/cmdline | tr " " "\n" | grep "^rauc.slot=" | head -1 | cut -d= -f2) -current_niboot_fs_uuid=$(cat /proc/cmdline | tr " " "\n" | grep "^rauc.slot.uuid=" | head -1 | cut -d= -f2) - -assert_valid_fs_id "rauc.slot" "$current_niboot_fs_label" -assert_valid_fs_id "rauc.slot.uuid" "$current_niboot_fs_uuid" - -# Root device which is detected asynchronously may not show up -# early, so continously polling for it until it is available -# or 10s timeout. -status "Waiting for root device" -slumber=10 -time_started=$SECONDS -while true; do - # Find boot dev nodes using a combination of fs label and UUID so that - # multiple NILRT installations may co-exist on the same system - current_niboot_part_device=$(lsblk -l -n -o NAME,LABEL,UUID | tr -s " " | egrep " $current_niboot_fs_label $current_niboot_fs_uuid\$" | head -1 | cut -d" " -f1) || true - - if [ -n "$current_niboot_part_device" ]; then - # Found root device - status "Root device detected." - break - fi - - time_elapsed=$((SECONDS - time_started)) - if [ $time_elapsed -ge $slumber ]; then - error "Root device not found. System is unbootable." - else - # Sleep for 10ms each trial - sleep 0.01 - fi -done - -current_boot_disk_device=$(lsblk -l -n -o PKNAME "/dev/$current_niboot_part_device") - -assert_valid_fs_id "current_boot_disk_device" "$current_boot_disk_device" - -# Find niuser partition dev on same disk that we booted -niboota_part_device=$(lsblk -l -n -o NAME,PKNAME,PARTLABEL | tr -s " " | egrep " $current_boot_disk_device niboota\$" | head -1 | cut -d" " -f1) -nibootb_part_device=$(lsblk -l -n -o NAME,PKNAME,PARTLABEL | tr -s " " | egrep " $current_boot_disk_device nibootb\$" | head -1 | cut -d" " -f1) -niuser_part_device=$(lsblk -l -n -o NAME,PKNAME,PARTLABEL | tr -s " " | egrep " $current_boot_disk_device niuser\$" | head -1 | cut -d" " -f1) - -status "Booting current_niboot_fs_label=$current_niboot_fs_label, current_niboot_fs_uuid=$current_niboot_fs_uuid, current_boot_disk_device=$current_boot_disk_device, current_niboot_part_device=$current_niboot_part_device, niuser_part_device=$niuser_part_device" - -# Populate /dev/niboot with references -mkdir /dev/niboot - -ln -sf "/dev/$niboota_part_device" /dev/niboot/niboota -ln -sf "/dev/$nibootb_part_device" /dev/niboot/nibootb -ln -sf "/dev/$niuser_part_device" /dev/niboot/niuser - -if [ "$current_niboot_part_device" == "$niboota_part_device" ]; then - ln -sf niboota /dev/niboot/niboot.current - ln -sf nibootb /dev/niboot/niboot.other -elif [ "$current_niboot_part_device" == "$nibootb_part_device" ]; then - ln -sf niboota /dev/niboot/niboot.other - ln -sf nibootb /dev/niboot/niboot.current -else - warn "Unrecognized current_niboot_part_device=$current_niboot_part_device" - ln -sf "/dev/$current_niboot_part_device" /dev/niboot/niboot.current -fi - -# Mount niboot.current -readonly B_MNT="/mnt/niboot.current" -mkdir "$B_MNT" -mount -o ro,sync,relatime "/dev/niboot/niboot.current" "$B_MNT" - -# Mount niuser -readonly U_MNT="/mnt/niuser" -mkdir "$U_MNT" - -readonly U_OVERLAY_CFG="$U_MNT/overlay/upper/etc/niboot/init-action.cfg" - -function mount_niuser_helper() { - mount -o rw,sync,relatime "/dev/niboot/niuser" "$U_MNT" -} - -do_format_niuser=false - -if mount_niuser_helper; then - if [ "$(read_config_token "$U_OVERLAY_CFG" reformat_niuser)" == "true" ]; then - status "init-action.cfg directed reformat of niuser" - umount "$U_MNT" - do_format_niuser=true - fi -else - status "Failed to mount niuser, reformatting" - do_format_niuser=true -fi - -if $do_format_niuser; then - mkfs.ext4 -q -F -L "niuser" "/dev/niboot/niuser" - - # Try to mount again - if ! mount_niuser_helper; then - error "Failed to mount niuser after re-creating file system" - fi -fi - -current_baserootfs_sha256sum=$(sha256sum "$B_MNT/baserootfs.squashfs" | cut -d" " -f1) - -do_reset_overlay=false - -# First check if we are explicitly directed to reset the overlay -if [ "$(read_config_token "$U_OVERLAY_CFG" reset_overlay)" == "true" ]; then - status "init-action.cfg directed reset of upper file system" - do_reset_overlay=true -else - # otherwise verify the baserootfs checksum matches - current_overlay_sha256sum=$(read_sha256sum_file "$U_MNT/overlay/lower.sha256sum") - if [ "$current_overlay_sha256sum" == "$current_baserootfs_sha256sum" ]; then - status "Current overlay matches baserootfs" - else - # Bad current overlay checksum, lets see if we can fall back to old overlay - old_overlay_sha256sum=$(read_sha256sum_file "$U_MNT/overlay.old/lower.sha256sum") - if [ "$old_overlay_sha256sum" == "$current_baserootfs_sha256sum" ]; then - status "Old overlay matches baserootfs, falling back" - - # Remove current overlay if it exists - # This is fail safe because the condition that got us here still hold: - # - do_reset_overlay will stay false, that's the default - # - current_overlay_sha256sum will become "" - # - old_overlay_sha256sum will stay the same - rm -Rf "$U_MNT/overlay" - - # Promote old overlay to current - mv "$U_MNT/overlay.old" "$U_MNT/overlay" - else - status "Resetting upper file system due to different baserootfs image" - do_reset_overlay=true - fi - fi -fi - -if $do_reset_overlay; then - # Demote current overlay to old if it exist, removing any old - # overlay which may be present - if [ -e "$U_MNT/overlay" ]; then - rm -Rf "$U_MNT/overlay.old" - mv "$U_MNT/overlay" "$U_MNT/overlay.old" - fi - - # Create new overlay with current baserootfs hash - # This is done as an atomic operation to facilitate recovery (above) - # after catastrophic failure like power loss - rm -Rf "$U_MNT/overlay.new" - mkdir "$U_MNT/overlay.new" - echo "$current_baserootfs_sha256sum" > "$U_MNT/overlay.new/lower.sha256sum" - mv "$U_MNT/overlay.new" "$U_MNT/overlay" -fi - -boot_safemode=false - -if [ "$(read_config_token "$U_OVERLAY_CFG" boot_safemode)" == "true" ]; then - status "Booting safemode, directed by user config" - drop_config_token "$U_OVERLAY_CFG" boot_safemode - boot_safemode=true -fi - -for file in /sys/bus/acpi/drivers/nirtfeatures/*/safe_mode; do - status=$(read_file "$file" 10) - if [ "$status" == "1" ]; then - status "Booting safemode, directed by firmware ($file = 1)" - boot_safemode=true - fi -done - -init_options="" - -if $boot_safemode; then - init_options="4" -fi - -for file in /sys/bus/acpi/drivers/nirtfeatures/*/ip_reset; do - status=$(read_file "$file" 10) - if [ "$status" == "1" ]; then - status "Reset network configuration, directed by firmware ($file = 1)" - if [ -e "$U_MNT/overlay/upper/var/lib/connman/" ]; then - find "$U_MNT/overlay/upper/var/lib/connman/" -mindepth 1 -maxdepth 1 -exec rm -Rf {} \; - fi - fi -done - -status "Create mount point for overlay" -mkdir -p "$U_MNT/overlay/lower" -mkdir -p "$U_MNT/overlay/upper" -mkdir -p "$U_MNT/overlay/work" -mkdir -p "$U_MNT/overlay/image" - -status "Mount lower filesystem" -mount -o ro -t squashfs "$B_MNT/baserootfs.squashfs" "$U_MNT/overlay/lower" - -status "Create overlay image" -mount -t overlay -o lowerdir="$U_MNT/overlay/lower,upperdir=$U_MNT/overlay/upper,workdir=$U_MNT/overlay/work" overlay "$U_MNT/overlay/image" - -status "Move boot" -mount --move "$B_MNT" "$U_MNT/overlay/image/boot" - -# Remove sync option from niuser mount in preparation for toggle -sync -mount -o remount,async "$U_MNT" - -status "Restore printk_devkmsg=$ORIG_KMSG_CONFIG" -echo "$ORIG_KMSG_CONFIG" > "/proc/sys/kernel/printk_devkmsg" - -status "Running switch_root to $U_MNT/overlay/image/" -exec switch_root "$U_MNT/overlay/image/" /sbin/init $init_options - diff --git a/recipes-core/initrdscripts/init-nilrt-ramfs.bb b/recipes-core/initrdscripts/init-nilrt-ramfs.bb deleted file mode 100644 index 9a3f97d23..000000000 --- a/recipes-core/initrdscripts/init-nilrt-ramfs.bb +++ /dev/null @@ -1,30 +0,0 @@ -SUMMARY = "NILRT initramfs init script" -LICENSE = "MIT" -LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302" -S = "${UNPACKDIR}" - -PV = "1.0" - - -SRC_URI = "\ - file://init-nilrt-ramfs.sh \ -" - -DEPENDS = "bash busybox util-linux ${PREFERRED_PROVIDER_virtual/kernel}" - -RDEPENDS:${PN} += " \ - bash \ - busybox \ - util-linux-lsblk \ - util-linux-switch-root \ - util-linux-mount \ - e2fsprogs-mke2fs \ -" - -do_install() { - install -m 0755 ${UNPACKDIR}/init-nilrt-ramfs.sh ${D}/init -} - -PACKAGE_ARCH = "${MACHINE_ARCH}" - -FILES:${PN} += "/init" diff --git a/recipes-core/initrdscripts/init-nilrt-runmode-initramfs.bb b/recipes-core/initrdscripts/init-nilrt-runmode-initramfs.bb new file mode 100644 index 000000000..5fdb9ce87 --- /dev/null +++ b/recipes-core/initrdscripts/init-nilrt-runmode-initramfs.bb @@ -0,0 +1,77 @@ +SUMMARY = "NILRT runmode initramfs init script" +HOMEPAGE = "https://github.com/ni/meta-nilrt" +SECTION = "core" +LICENSE = "MIT" +LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302" + + +# ============================================================================== +# RECIPE VARIABLES +# ============================================================================== + +DEPENDS = "bash busybox util-linux ${PREFERRED_PROVIDER_virtual/kernel}" + +PV = "${DISTRO_VERSION}" + + +# ============================================================================== +# SOURCE VARIABLES +# ============================================================================== + +SRC_URI = "\ + file://halt.sh \ + file://init.sh.in \ + file://kernel-vars.sh \ + file://reboot.sh \ +" + +S = "${UNPACKDIR}" + + +# ============================================================================== +# CLASSES +# ============================================================================== + +inherit update-alternatives + +ALTERNATIVES:${PN} = "halt reboot" +ALTERNATIVE_TARGET[halt] = "${sbindir}/halt" +ALTERNATIVE_TARGET[reboot] = "${sbindir}/reboot" +ALTERNATIVE_LINK_NAME[halt] = "halt.${PN}" +ALTERNATIVE_LINK_NAME[reboot] = "reboot.${PN}" + + +# ============================================================================== +# TASKS +# ============================================================================== + +do_compile() { + # Insert packaging variables into the init script. + sed <${S}/init.sh.in >${S}/init.sh \ + -e "s|@pkglibdir@|${libdir}/${BPN}|g" \ + -e "s|@sysconfdir@|${sysconfdir}|g" \ + -e "s|@sbindir@|${sbindir}|g" +} + +do_install() { + install -m 0744 ${S}/init.sh ${D}/init + install -D -m 0744 ${S}/halt.sh ${D}${sbindir}/halt.${BPN} + install -D -m 0744 ${S}/reboot.sh ${D}${sbindir}/reboot.${BPN} + install -D -m 0644 ${S}/kernel-vars.sh ${D}${libdir}/${BPN}/kernel-vars.sh +} + + +# ============================================================================== +# PACKAGING +# ============================================================================== + +PACKAGE_ARCH = "${MACHINE_ARCH}" + +FILES:${PN} += "/init" +RDEPENDS:${PN} += " \ + bash \ + busybox \ + util-linux-lsblk \ + util-linux-switch-root \ + util-linux-mount \ +" diff --git a/recipes-core/initrdscripts/init-nilrt-runmode-initramfs/halt.sh b/recipes-core/initrdscripts/init-nilrt-runmode-initramfs/halt.sh new file mode 100644 index 000000000..d87044767 --- /dev/null +++ b/recipes-core/initrdscripts/init-nilrt-runmode-initramfs/halt.sh @@ -0,0 +1,4 @@ +#!/bin/sh +sync +echo "Powering off..." +echo o > /proc/sysrq-trigger diff --git a/recipes-core/initrdscripts/init-nilrt-runmode-initramfs/init.sh.in b/recipes-core/initrdscripts/init-nilrt-runmode-initramfs/init.sh.in new file mode 100755 index 000000000..879972cd7 --- /dev/null +++ b/recipes-core/initrdscripts/init-nilrt-runmode-initramfs/init.sh.in @@ -0,0 +1,221 @@ +#!/bin/sh +# Copyright (c) 2026 Emerson T&M (NI). +# LICENSE: MIT +# +# PID 1 script for the NILRT runmode initramfs (ramdisk) image. +# This script should do basic setup of the kernel, find and mount the userfs, +# and then switch_root into it. +# +# If any step fails, it will print the error and give the user 10 seconds to +# press a key to enter a recovery shell. If no key is pressed, it will reboot +# to safemode. + + +# ============================================================================== +# CONSTANTS +# ============================================================================== + +RECOVERY_SHELL_TIMEOUT=10 + +LOG_CONTEXT="(initramfs)" + +NI_CRYPTDISKS="@sysconfdir@/init.d/ni-cryptdisks.sh" +NIROOTFS_INIT="/sbin/init" + + +# ============================================================================== +# EARLY SETUP +# ============================================================================== + +early_setup(){ + mount -t proc proc /proc || true + mount -t sysfs sysfs /sys || true + mount -t devtmpfs devtmpfs /dev || true + + export PATH="/sbin:/bin:/usr/sbin:/usr/bin" + umask 0022 +} +early_setup + + +# ============================================================================== +# IMPORTS +# ============================================================================== + +pkglibdir=@pkglibdir@ +. "$pkglibdir/kernel-vars.sh" || { + echo "(initramfs) WARNING: Failed to source kernel-vars.sh. Defaulting to VERBOSE output." +} + + +# ============================================================================== +# FUNCTIONS +# ============================================================================== + + +# Find and mount the encrypted drives. +decrypt_drives() { + # Find and mount the userfs partition. + if [ -x /etc/init.d/ni-cryptdisks.sh ]; then + log DEBUG "Running ni-cryptdisks start..." + /etc/init.d/ni-cryptdisks.sh start || { + log ERROR "Failed to unlock encrypted volumes" + return 1 + } + else + log INFO "/etc/init.d/ni-cryptdisks not found, skipping encryption setup" + fi +} + + +# Prints an error message, waits for user input, and enters a recovery shell if +# the user presses a key within the timeout. Otherwise, reboots the system. +enter_recovery_shell() { + echo "Entering recovery shell. Type 'reboot' to restart." >/dev/console || : + + # Unmount filesystems to give the recovery shell a clean environment + umount -a -r 2>/dev/null || true + sync + early_setup + + # Spawn recovery shells in a loop so exiting doesn't kill init + while true; do + /bin/sh /dev/console 2>&1 || true + echo "Shell exited. Respawning..." >/dev/console || : + sleep 1 + done +} + + +# Prints an error message, waits for a timeout, and reboots the system. +fail_and_reboot() { + set +e + + log ERROR "initramfs init/ failure." + echo "Press any key within $RECOVERY_SHELL_TIMEOUT seconds to enter recovery shell..." >/dev/console + + # Note: read -t is supported by busybox and bash, but not strictly POSIX + # We accept this since busybox is our target shell + # Redirect stdin explicitly and handle both success and timeout + response="" + if read -t $RECOVERY_SHELL_TIMEOUT response /dev/console 2>&1; then + log INFO "Key pressed, entering recovery shell..." + enter_recovery_shell + # Should never return from enter_recovery_shell (infinite loop) + while true; do sleep 1; done + fi + + # No key pressed, reboot using kernel sysrq + log INFO "Timeout expired, rebooting..." + sync + echo b > /proc/sysrq-trigger 2>/dev/null || true + + # Fallback: if sysrq didn't work, try reboot command + reboot 2>/dev/null || true + + # Last resort: loop forever to prevent kernel panic + while true; do sleep 1; done +} + + +switch_root_rootfs() { + log INFO "Setting up root filesystem..." + + decrypt_drives + + log DEBUG "Looking for nirootfs partition..." + nirootfs_path=$(blkid --label "nirootfs" --output device) || { + log ERROR "Failed to find nirootfs partition" + return 1 + } + log DEBUG "Found nirootfs at $nirootfs_path" + + mount "$nirootfs_path" /mnt || { + log ERROR "Failed to mount $nirootfs_path" + return 1 + } + + log DEBUG "Bind mounting virtual filesystems..." + mount --rbind /dev /mnt/dev || log WARNING "Failed to bind mount /dev" + mount --rbind /proc /mnt/proc || log WARNING "Failed to bind mount /proc" + mount --rbind /sys /mnt/sys || log WARNING "Failed to bind mount /sys" + + log DEBUG "Setting up /run in new root..." + # Ensure /run exists in the new root and mount tmpfs on it + mkdir -p /mnt/run || true + mount -t tmpfs tmpfs /mnt/run -o nosuid,nodev,mode=0755 || log WARNING "Failed to mount tmpfs on /mnt/run" + + log INFO "Switching to root filesystem..." + + if ! chroot /mnt test -x "$NIROOTFS_INIT"; then + log ERROR "Init binary $NIROOTFS_INIT not found or not executable in new root" + return 1 + fi + exec switch_root /mnt $NIROOTFS_INIT 5 || { + log ERROR "switch_root failed to exec $NIROOTFS_INIT" + return 1 + } + + # Should never reach here - if we do, exec failed + log ERROR "switch_root failed to exec" + return 1 +} + + +# Print output to the screen and kernel log, depending on the log level. +log() { + level="$1" + shift + msg="$*" + + if [ "$level" = "DEBUG" ] && [ "${DEBUG:-}" != "yes" ]; then + return + fi + if [ "$level" = "INFO" ] && [ "${VERBOSE:-}" != "yes" ]; then + return + fi + echo "$LOG_CONTEXT $level: $msg" >/dev/kmsg 2>/dev/null || true +} + + +# ============================================================================== +# GLOBALS +# ============================================================================== + +VERBOSE=${VERBOSE:-yes} +DEBUG=${DEBUG:-no} + +# If set, break into the recovery shell rather than proceed. +RD_BREAK=${RD_BREAK:-""} + + +# ============================================================================== +# MAIN +# ============================================================================== + +trap fail_and_reboot ERR + +log INFO "/init" + +if [ "$DEBUG" = "yes" ]; then + # Redirect kernel messages to the console so that the user can see them + echo on >"/proc/sys/kernel/printk_devkmsg" 2>/dev/null || true + VERBOSE=yes +fi + +if [ "$RD_BREAK" = "premount" ]; then + log INFO "RD_BREAK is set, entering recovery shell..." + enter_recovery_shell +fi + +if ! switch_root_rootfs; then + log ERROR "switch_root_rootfs failed" + fail_and_reboot +fi + +# If we somehow get here, something went very wrong +log ERROR "Reached end of init script unexpectedly" +enter_recovery_shell + +# Should never reach here +while true; do sleep 1; done diff --git a/recipes-core/initrdscripts/init-nilrt-runmode-initramfs/kernel-vars.sh b/recipes-core/initrdscripts/init-nilrt-runmode-initramfs/kernel-vars.sh new file mode 100644 index 000000000..4b3700fa2 --- /dev/null +++ b/recipes-core/initrdscripts/init-nilrt-runmode-initramfs/kernel-vars.sh @@ -0,0 +1,128 @@ +#!/bin/sh +# Copyright (c) 2026 Emerson +# SPDX-License-Identifier: MIT +# +# A helper script which will interrogate the value of ``/proc/cmdline`` and +# export shell variables to the NILRT runmode initramfs scripts. +#set -eu + + +# ============================================================================== +# CONSTANTS +# ============================================================================== + +CMDLINE_PATH="/proc/cmdline" +if ! [ -r "$CMDLINE_PATH" ]; then + echo "ERROR: Unable to read $CMDLINE_PATH" >&2 + exit 1 +fi + +CMDLINE="$(cat "$CMDLINE_PATH")" + + +# ============================================================================== +# FUNCTIONS +# ============================================================================== + + +# UTILITIES + +# Checks if a given key=value pair is present in the kernel command line. +# Returns either the key-value if found, or the provided default value if not. +_cmdline_get_key() { + key="$1" # The key to search for in the command line. + default="$2" # The value to return, if the key is not found. + + value="$(echo "$CMDLINE" | sed -n "s/.*\b${key}=\([^ ]*\).*/\1/p" || true)" + echo "${value:-$default}" +} + + +# Checks if a given word is present in the kernel command line. +# Returns 0 (success) if the word is found, or 1 (failure) if not. +_word_in_cmdline() { + word="$1" + echo "$CMDLINE" | grep -qw "$word" +} + + +# Check if **any** of the given words are present in the kernel command line. +# Returns 0 (success) if at least one word is found, or 1 (failure) otherwise. +_words_in_cmdline() { + for word in "$@"; do + if _word_in_cmdline "$word"; then + return 0 + fi + done + return 1 +} + + +# VARIABLE PARSING + +# Parse the value of the DEBUG variable. +parse_debug() { + DEBUG="" # default + # debian uses "debug" to enable debug logging. + # dracut uses "rd.debug" for the same purpose. + # systemd supports both. + if _words_in_cmdline "debug" "rd.debug"; then + DEBUG="yes"; return + fi +} + + +# Parse the value of the RD_BREAK variable. +parse_rd_break() { + RD_BREAK="" # default + # Note. We have to check for key-value first, because the _word functions + # will match the key portions as well. + + # debian supports keyvalue breaks for fine-grained control. + if [ "$(_cmdline_get_key "break" "NONE")" != "NONE" ]; then + RD_BREAK="$(_cmdline_get_key "break" "NONE")" + return + fi + # dracut also supports keyvalues in their break syntax. + if [ "$(_cmdline_get_key "rd.break" "NONE")" != "NONE" ]; then + RD_BREAK="$(_cmdline_get_key "rd.break" "NONE")" + return + fi + # debian, systemd, and dracut use these values generically + if _words_in_cmdline "rd.break" "break"; then + RD_BREAK="premount" + return + fi + +} + +# Parse the value of the VERBOSE variable. +parse_verbose() { + VERBOSE="" # default + # debian and systemd both use "quiet" to suppress verbose (INFO) logging. + if ! _word_in_cmdline "quiet"; then + VERBOSE="yes"; return + fi + # dracut uses rd.info + if _word_in_cmdline "rd.info"; then + VERBOSE="yes"; return + fi +} + + +# ============================================================================== +# MAIN +# ============================================================================== + +parse_debug +parse_rd_break +parse_verbose + + +# ============================================================================== +# EXPORTS +# ============================================================================== + +export VERBOSE +export DEBUG +export RD_BREAK diff --git a/recipes-core/initrdscripts/init-nilrt-runmode-initramfs/reboot.sh b/recipes-core/initrdscripts/init-nilrt-runmode-initramfs/reboot.sh new file mode 100644 index 000000000..317de1bd8 --- /dev/null +++ b/recipes-core/initrdscripts/init-nilrt-runmode-initramfs/reboot.sh @@ -0,0 +1,4 @@ +#!/bin/sh +sync +echo "Rebooting..." +echo b > /proc/sysrq-trigger diff --git a/recipes-core/initrdscripts/init-nilrt-runmode-initramfs/test/test_kernel-vars.sh b/recipes-core/initrdscripts/init-nilrt-runmode-initramfs/test/test_kernel-vars.sh new file mode 100644 index 000000000..bbf12ba1a --- /dev/null +++ b/recipes-core/initrdscripts/init-nilrt-runmode-initramfs/test/test_kernel-vars.sh @@ -0,0 +1,157 @@ +#!/bin/bash +# Unit tests for the ``kernel-vars.sh ``source script. +# This test script expects the ``kernel-vars.sh`` script to be in the same +# current working directory. + + +function test__debug() { + source ./kernel-vars.sh + + local DEBUG_CMDLINES=( + "debug" + "foo=bar debug baz=qux" + "rd.debug" + "foo=bar rd.debug baz=qux" + ) + local NODEBUG_CMDLINES=( + "foo bar" + "foo=bar quiet baz=qux" + ) + + for cmd in "${DEBUG_CMDLINES[@]}"; do + CMDLINE="$cmd" + parse_debug + if [ "$DEBUG" != "yes" ]; then + echo "Expected DEBUG=yes for cmdline: '$cmd', got '$DEBUG'" + return 1 + fi + done + for cmd in "${NODEBUG_CMDLINES[@]}"; do + CMDLINE="$cmd" + parse_debug + if [ "$DEBUG" != "" ]; then + echo "Expected DEBUG='' for cmdline: '$cmd', got '$DEBUG'" + return 1 + fi + done +} + + +function test__rd_break() { + source ./kernel-vars.sh + + local RD_BREAK_CMDLINES=( + "break" + "foo=bar break baz=qux" + "rd.break" + "foo=bar rd.break baz=qux" + "break=premount" + "foo=bar break=premount baz=qux" + ) + local NO_RD_BREAK_CMDLINES=( + "foo bar" + "foo=bar quiet baz=qux" + ) + + for cmd in "${RD_BREAK_CMDLINES[@]}"; do + CMDLINE="$cmd" + parse_rd_break + if [ "$RD_BREAK" != "premount" ]; then + echo "Expected RD_BREAK=premount for cmdline: '$cmd', got '$RD_BREAK'" + return 1 + fi + done + for cmd in "${NO_RD_BREAK_CMDLINES[@]}"; do + CMDLINE="$cmd" + parse_rd_break + if [ "$RD_BREAK" != "" ]; then + echo "Expected RD_BREAK='' for cmdline: '$cmd', got '$RD_BREAK'" + return 1 + fi + done + + # break key-values + CMDLINE="break=premount" + parse_rd_break + if [ "$RD_BREAK" != "premount" ]; then + echo "Expected RD_BREAK=premount for cmdline: '$CMDLINE', got '$RD_BREAK'" + return 1 + fi + CMDLINE="rd.break=postmount" + parse_rd_break + if [ "$RD_BREAK" != "postmount" ]; then + echo "Expected RD_BREAK=postmount for cmdline: '$CMDLINE', got '$RD_BREAK'" + return 1 + fi +} + +function test__verbose() { + source ./kernel-vars.sh + + local VERBOSE_CMDLINES=( + "foo bar" + "foo=bar baz=qux" + "verbose" + "foo=bar verbose baz=qux" + "rd.info" + "foo=bar rd.info baz=qux" + ) + local QUIET_CMDLINES=( + "quiet" + "foo=bar quiet baz=qux" + ) + + for cmd in "${VERBOSE_CMDLINES[@]}"; do + CMDLINE="$cmd" + parse_verbose + if [ "$VERBOSE" != "yes" ]; then + echo "Expected VERBOSE=yes for cmdline: '$cmd', got '$VERBOSE'" + return 1 + fi + done + for cmd in "${QUIET_CMDLINES[@]}"; do + CMDLINE="$cmd" + parse_verbose + if [ "$VERBOSE" != "" ]; then + echo "Expected VERBOSE='' for cmdline: '$cmd', got '$VERBOSE'" + return 1 + fi + done +} + + +function run_testcase() { + local testcase="$1" + + echo "Running testcase: $testcase" + $testcase + local rc=$? + if [ $rc -eq 0 ]; then + echo "[PASSED]" + else + echo "[FAILED]" + tc_failures=$((tc_failures + 1)) + fi +} + + +# ============================================================================== +# GLOBALS +# ============================================================================== + +tc_failures=0 + + +# ============================================================================== +# MAIN +# ============================================================================== + +run_testcase test__debug +run_testcase test__rd_break +run_testcase test__verbose + +if [ $tc_failures -eq 0 ]; then + echo "All testcases passed!" +else + echo "$tc_failures testcases failed." +fi diff --git a/recipes-core/ovmf/ovmf_%.bbappend b/recipes-core/ovmf/ovmf_%.bbappend new file mode 100644 index 000000000..813096f48 --- /dev/null +++ b/recipes-core/ovmf/ovmf_%.bbappend @@ -0,0 +1,5 @@ +do_deploy:class-target:append() { + install --mode 0644 -t ${DEPLOYDIR}/ \ + ${WORKDIR}/ovmf/ovmf.code.fd \ + ${WORKDIR}/ovmf/ovmf.vars.fd +} diff --git a/recipes-core/packagegroups/packagefeed-ni-core.bb b/recipes-core/packagegroups/packagefeed-ni-core.bb index 116ee0c74..314a54954 100644 --- a/recipes-core/packagegroups/packagefeed-ni-core.bb +++ b/recipes-core/packagegroups/packagefeed-ni-core.bb @@ -11,6 +11,7 @@ RDEPENDS:${PN} = "\ packagegroup-ni-base \ packagegroup-ni-contributors \ packagegroup-ni-crio \ + packagegroup-ni-initramfs \ packagegroup-ni-internal-deps \ packagegroup-ni-ptest-smoke \ packagegroup-ni-restoremode \ @@ -27,7 +28,6 @@ RDEPENDS:${PN}:append = "\ " RDEPENDS:${PN}:append:x64 = "\ - init-nilrt-ramfs \ nilrt-grub-runmode \ nilrt-grub-safemode \ packagegroup-core-x11 \ @@ -38,3 +38,9 @@ RDEPENDS:${PN}:append:x64 = "\ bolt \ onboard \ " + +# Image-specific packages kept separately so they are not accidentally +# installed via a packagegroup dependency into the wrong image. +RDEPENDS:${PN}:append:x64 = "\ + init-nilrt-runmode-initramfs \ +" diff --git a/recipes-core/packagegroups/packagefeed-ni-extra.bb b/recipes-core/packagegroups/packagefeed-ni-extra.bb index d02b494ba..09b9aa035 100644 --- a/recipes-core/packagegroups/packagefeed-ni-extra.bb +++ b/recipes-core/packagegroups/packagefeed-ni-extra.bb @@ -365,7 +365,6 @@ RDEPENDS:${PN} += "\ ccid \ ckermit \ cpufrequtils \ - cryptsetup \ daemonize \ daemontools \ dfu-util \ diff --git a/recipes-core/packagegroups/packagegroup-ni-base.bb b/recipes-core/packagegroups/packagegroup-ni-base.bb index 7f20c008c..110a834ee 100644 --- a/recipes-core/packagegroups/packagegroup-ni-base.bb +++ b/recipes-core/packagegroups/packagegroup-ni-base.bb @@ -97,11 +97,13 @@ RDEPENDS:${PN} += "\ RDEPENDS:${PN}:append:x64 = "\ packagegroup-kernel-modules-essential \ + packagegroup-ni-tpm \ dmidecode \ e2fsprogs \ e2fsprogs-mke2fs \ efibootmgr \ efivar \ + ni-device-encryption \ pstore-save \ " diff --git a/recipes-core/packagegroups/packagegroup-ni-initramfs.bb b/recipes-core/packagegroups/packagegroup-ni-initramfs.bb new file mode 100644 index 000000000..33b2dcf56 --- /dev/null +++ b/recipes-core/packagegroups/packagegroup-ni-initramfs.bb @@ -0,0 +1,56 @@ +SUMMARY = "Packages common to all NILRT initramfs images." +LICENSE = "MIT" + +PACKAGE_ARCH = "${MACHINE_ARCH}" + +inherit packagegroup + +RDEPENDS:${PN} += "\ + base-passwd \ + bash \ + bzip2 \ + coreutils \ + dosfstools \ + e2fsprogs \ + e2fsprogs-mke2fs \ + e2fsprogs-tune2fs \ + findutils \ + fw-printenv \ + gawk \ + gptfdisk \ + grep \ + kmod \ + ni-systemreplication \ + parted \ + procps \ + sed \ + sysvinit \ + tar \ + util-linux \ + util-linux-agetty \ + vim-tiny \ +" + +# TPM Interaction +RDEPENDS:${PN}:x64 += "\ + cryptsetup \ + libtss2-tcti-device \ + ni-device-encryption \ + tpm2-tools \ +" + +RDEPENDS:${PN}:append:x64 = "\ + dmidecode \ + efibootmgr \ + efivar \ + eudev \ + grub \ + grub-editenv \ + grub-efi \ + ni-smbios-helper \ +" + +RDEPENDS:${PN}:append:xilinx-zynq = "\ + mtd-utils \ + mtd-utils-ubifs \ +" diff --git a/recipes-core/packagegroups/packagegroup-ni-restoremode.bb b/recipes-core/packagegroups/packagegroup-ni-restoremode.bb index 35a143d6f..a8ea4e8fd 100644 --- a/recipes-core/packagegroups/packagegroup-ni-restoremode.bb +++ b/recipes-core/packagegroups/packagegroup-ni-restoremode.bb @@ -49,7 +49,6 @@ RDEPENDS:${PN}:append:xilinx-zynq = "\ " RRECOMMENDS:${PN}:x64 = "\ - kernel-module-tpm-tis \ kernel-module-atkbd \ kernel-module-hyperv-keyboard \ kernel-module-hv-storvsc \ diff --git a/recipes-core/packagegroups/packagegroup-ni-tpm.bb b/recipes-core/packagegroups/packagegroup-ni-tpm.bb index 25b0de2d6..aa7258fbc 100644 --- a/recipes-core/packagegroups/packagegroup-ni-tpm.bb +++ b/recipes-core/packagegroups/packagegroup-ni-tpm.bb @@ -10,6 +10,8 @@ PACKAGE_ARCH = "${MACHINE_ARCH}" inherit packagegroup RDEPENDS:${PN} = "\ - packagegroup-security-tpm2 \ + clevis \ + cryptsetup \ libtss2-tcti-device \ + tpm2-tools \ " diff --git a/recipes-kernel/linux/files/extra_x64.cfg b/recipes-kernel/linux/files/extra_x64.cfg index cbeef08e5..fe3bee3fa 100644 --- a/recipes-kernel/linux/files/extra_x64.cfg +++ b/recipes-kernel/linux/files/extra_x64.cfg @@ -1072,6 +1072,7 @@ CONFIG_HWMON_VID=m CONFIG_HWPOISON_INJECT=m CONFIG_HW_RANDOM_BA431=m CONFIG_HW_RANDOM_TIMERIOMEM=m +CONFIG_HW_RANDOM_TPM=m CONFIG_HW_RANDOM_XIPHERA=m CONFIG_HX711=m CONFIG_HYPERV_VSOCKETS=m @@ -1083,8 +1084,10 @@ CONFIG_I2C_AMD756=m CONFIG_I2C_AMD756_S4882=m CONFIG_I2C_AMD8111=m CONFIG_I2C_AMD_MP2=m +CONFIG_I2C_BOARDINFO=m CONFIG_I2C_CBUS_GPIO=m CONFIG_I2C_CHT_WC=m +CONFIG_I2C_COMPAT=m CONFIG_I2C_CP2615=m CONFIG_I2C_CROS_EC_TUNNEL=m CONFIG_I2C_DIOLAN_U2C=m @@ -2460,6 +2463,7 @@ CONFIG_RDS_RDMA=m CONFIG_RDS_TCP=m CONFIG_REALTEK_PHY=m CONFIG_REED_SOLOMON=m +CONFIG_REGMAP_I2C=m CONFIG_REGMAP_I3C=m CONFIG_REGMAP_SCCB=m CONFIG_REGMAP_SLIMBUS=m @@ -2510,6 +2514,7 @@ CONFIG_RT2800_LIB_MMIO=m CONFIG_RT2X00_LIB_MMIO=m CONFIG_RT2X00_LIB_PCI=m CONFIG_RT61PCI=m +CONFIG_RTC_I2C_AND_SPI=m CONFIG_RTL8180=m CONFIG_RTL8188EE=m CONFIG_RTL8192CE=m @@ -2656,6 +2661,7 @@ CONFIG_SCSI_WD719X=m CONFIG_SDIO_UART=m CONFIG_SDR_MAX2175=m CONFIG_SD_ADC_MODULATOR=m +CONFIG_SECURITYFS=m CONFIG_SEL3350_PLATFORM=m CONFIG_SENSEAIR_SUNRISE_CO2=m CONFIG_SENSIRION_SGP30=m @@ -3065,8 +3071,11 @@ CONFIG_TAHVO_USB=m CONFIG_TAP=m CONFIG_TARGET_CORE=m CONFIG_TCG_ATMEL=m +CONFIG_TCG_CRB=m CONFIG_TCG_INFINEON=m CONFIG_TCG_NSC=m +CONFIG_TCG_TIS=m +CONFIG_TCG_TIS_CORE=m CONFIG_TCG_TIS_I2C=m CONFIG_TCG_TIS_I2C_ATMEL=m CONFIG_TCG_TIS_I2C_CR50=m @@ -3076,6 +3085,7 @@ CONFIG_TCG_TIS_SPI=m CONFIG_TCG_TIS_ST33ZP24=m CONFIG_TCG_TIS_ST33ZP24_I2C=m CONFIG_TCG_TIS_ST33ZP24_SPI=m +CONFIG_TCG_TPM=m CONFIG_TCG_VTPM_PROXY=m CONFIG_TCG_XEN=m CONFIG_TCM_FC=m diff --git a/recipes-ni/ni-device-encryption/ni-device-encryption.bb b/recipes-ni/ni-device-encryption/ni-device-encryption.bb new file mode 100644 index 000000000..33acd998e --- /dev/null +++ b/recipes-ni/ni-device-encryption/ni-device-encryption.bb @@ -0,0 +1,77 @@ +SUMMARY = "Tools for encrypting NI devices." +DESCRIPTION = "\ +This package contains utilities for creating and using LUKS-encrypted root \ +partitions for NILRT. Installs the ni-cryptdisks.sh initscript.\ +" +HOMEPAGE = "https://github.com/ni/meta-nilrt" +SECTION = "test" +LICENSE = "MIT" +LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302" + + +# ============================================================================== +# SOURCES +# ============================================================================== + +SRC_URI = "\ + file://Makefile \ + file://bin/ \ + file://init/ \ + file://share/ \ +" + +S = "${UNPACKDIR}" + + +# ============================================================================== +# BBCLASSES +# ============================================================================== + +# UPDATE-RC.D +inherit update-rc.d +INITSCRIPT_PARAMS = "start 30 S ." +INITSCRIPT_NAME = "ni-cryptdisks.sh" + + +# ============================================================================== +# TASKS +# ============================================================================== + +EXTRA_OEMAKE += "DESTDIR=${D} VERSION=${PV}" + + +do_compile () { + oe_runmake all +} + + +do_install () { + oe_runmake install + + # Create a symlink to the package share directory from the prefix share, for compat w/ other packages + install -d ${D}${datadir} + ln -sf ../lib/${BPN}/share ${D}${datadir}/${BPN} + + # Install sbin symlink + install -d ${D}${sbindir} + ln -sf ../lib/${BPN}/bin/ni-reseal-luks.sh ${D}${sbindir}/ni-reseal-luks + + # Install initscript + install -d ${D}${sysconfdir}/init.d + install ${S}/init/ni-cryptdisks.sh ${D}${sysconfdir}/init.d/ni-cryptdisks.sh +} + + +# ============================================================================== +# PACKAGING +# ============================================================================== + +# NI-DEVICE-ENCRYPTION +RDEPENDS:${PN} = "\ + bash \ + clevis \ + cryptsetup \ + libdevmapper \ + util-linux-blkid \ + util-linux-logger \ +" diff --git a/recipes-ni/ni-device-encryption/ni-device-encryption/.gitignore b/recipes-ni/ni-device-encryption/ni-device-encryption/.gitignore new file mode 100644 index 000000000..eebccff16 --- /dev/null +++ b/recipes-ni/ni-device-encryption/ni-device-encryption/.gitignore @@ -0,0 +1,2 @@ +share/pkg.sh +init/ni-cryptdisks.sh diff --git a/recipes-ni/ni-device-encryption/ni-device-encryption/Makefile b/recipes-ni/ni-device-encryption/ni-device-encryption/Makefile new file mode 100644 index 000000000..8fbfef448 --- /dev/null +++ b/recipes-ni/ni-device-encryption/ni-device-encryption/Makefile @@ -0,0 +1,99 @@ +PACKAGE := ni-device-encryption + +DESCRIPTION := Tools for encrypting NI devices. +VERSION ?= dev + + +# SOURCE FILES + +srcdir = . +srcdatadir = $(srcdir)/share +srcbindir = $(srcdir)/bin + +SRC = \ + $(wildcard $(srcbindir)/*) \ + $(wildcard $(srcdatadir)/*) \ + $(srcdatadir)/pkg.sh \ + init/ni-cryptdisks.sh + + +# INSTALL DIRS + +prefix ?= /usr/local + +datadir ?= $(prefix)/share +libdir ?= $(prefix)/lib +sysconfdir ?= /etc + +pkgdatadir = $(datadir)/$(PACKAGE) +pkglibdir = $(libdir)/$(PACKAGE) + +.DEFAULT_GOAL := all + + +# ============================================================================== +# REAL TARGETS +# ============================================================================== + +$(PACKAGE).pc : + @{ \ + echo "Name: $(PACKAGE)"; \ + echo "Description: $(DESCRIPTION)"; \ + echo "Version: $(VERSION)"; \ + \ + echo "datadir=$(datadir)"; \ + echo "libdir=$(libdir)"; \ + echo "prefix=$(prefix)"; \ + \ + echo "pkgdatadir=$(pkgdatadir)"; \ + echo "pkglibdir=$(pkglibdir)"; \ + } >"$@" + + +$(srcdatadir)/pkg.sh : $(srcdatadir)/pkg.sh.in + sed $< >$@ \ + -e 's/@PACKAGE@/$(PACKAGE)/g' \ + -e 's/@VERSION@/$(VERSION)/g' + + +init/ni-cryptdisks.sh : init/ni-cryptdisks.sh.in + sed $< >$@ \ + -e 's%@pkglibdir@%$(pkglibdir)%g' + + +# ============================================================================== +# PHONY TARGETS +# ============================================================================== + +all : $(SRC) $(PACKAGE).pc +.PHONY : all + + +clean : + rm -f $(PACKAGE).pc + rm -f $(srcdatadir)/pkg.sh + rm -f init/ni-cryptdisks.sh +.PHONY : clean + + +install : all makeinstalldirs + install -m 0755 $(srcbindir)/* $(DESTDIR)$(pkglibdir)/bin/ + install -m 0644 $(srcdatadir)/*.sh $(DESTDIR)$(pkglibdir)/share/ + + # pkg-config + install -m 0644 $(PACKAGE).pc $(DESTDIR)$(datadir)/pkgconfig/$(PACKAGE).pc +.PHONY : install + + +makeinstalldirs : + mkdir -p $(DESTDIR)$(pkglibdir) + install -d $(DESTDIR)$(pkglibdir)/bin + install -d $(DESTDIR)$(pkglibdir)/share + install -d $(DESTDIR)$(datadir)/pkgconfig +.PHONY : makeinstalldirs + + +uninstall : + rm -rf $(DESTDIR)$(pkgdatadir) + rm -f $(DESTDIR)$(datadir)/pkgconfig/$(PACKAGE).pc +.PHONY : uninstall diff --git a/recipes-ni/ni-device-encryption/ni-device-encryption/bin/ni-reseal-luks.sh b/recipes-ni/ni-device-encryption/ni-device-encryption/bin/ni-reseal-luks.sh new file mode 100644 index 000000000..4e9f85548 --- /dev/null +++ b/recipes-ni/ni-device-encryption/ni-device-encryption/bin/ni-reseal-luks.sh @@ -0,0 +1,261 @@ +#!/bin/bash +# Copyright (c) 2026 Emerson T&M (NI). +# LICENSE: MIT +# +# When called, this utility precalculates the new PCR hashes for the safemode +# and runmode boot components (kernel, initramfs, grub config, et c.), and +# enrolls the new hashes as valid values to release the LUKS decryption keys +# for the niconfig and userfs partitions. +set -e + +# ============================================================================== +# CONSTANTS +# ============================================================================== + +declare -A EXITCODES=( + [OK]=0 + [ERR]=1 + [BADARGS]=2 + [BADENV]=3 + [IMPORT_ERROR]=4 +) + +PCR_BANK="sha256" +PCR_INDEXES="7" + +# ============================================================================== +# IMPORTS +# ============================================================================== + +SCRIPT_DIR="$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")" +source "$SCRIPT_DIR/../share/const_luks.sh" || exit ${EXITCODES[IMPORT_ERROR]} + + +# ============================================================================== +# FUNCTIONS +# ============================================================================== + + +# Checks that the script is being run in a suitable environment. +function _check_env() { + if [[ "$EUID" -ne 0 ]]; then + echo "This script must be run as root." >&2 + exit ${EXITCODES[BADENV]} + fi + if ! type tpm2_pcrread >/dev/null 2>&1; then + echo "tpm2-tools does not appear to be installed. Please install tpm2-tools and try again." >&2 + exit ${EXITCODES[BADENV]} + fi + if ! tpm2_pcrread sha256 >/dev/null 2>&1; then + echo "Unable to read PCR values from the TPM. Please ensure that the TPM is properly initialized and try again." >&2 + exit ${EXITCODES[BADENV]} + fi +} + + +function parse_args() { + local argv=("$@") + local positionals=() + + while [[ ${#argv[@]} -gt 0 ]]; do + case "${argv[0]}" in + -n|--dry-run) + opt_dry_run=true + ;; + -h|--help) + usage 1 + exit ${EXITCODES[OK]} + ;; + -y|--yes) + opt_yes=true + ;; + -*|--*) + echo "Unknown option: ${argv[0]}" >&2 + usage 2 + exit ${EXITCODES[BADARGS]} + ;; + *) + positionals+=("${argv[0]}") + ;; + esac + argv=("${argv[@]:1}") + done + + # Parse positional arguments + if [[ ${#positionals[@]} -gt 0 ]]; then + arg_keyfile="${positionals[0]}" + fi + + if $opt_dry_run; then + echo "Dry run mode enabled. No changes will be made." + fi +} + + +# Reseals a single LUKS keyslot with new PCR policies. +function _reseal_device_keyslot() { + local device="$1" + local keyslot="$2" + local master_keyfile="$3" + + echo "Resealing PCR state to LUKS keyslot $keyslot on device $device." + + # If the user has not provided a keyfile, try to reuse the clevis key in + # the keyslot. If there isn't one, we'll have to bail since we have no key. + if [ -z "$master_keyfile" ]; then + local keyfile=$(mktemp -p /dev/shm ni-reseal-luks.XXXXXX.keyfile) + chmod 0600 "$keyfile" + trap "shred -u $keyfile" EXIT + + clevis luks pass -d "$device" -s "$keyslot" >"$keyfile" || { + echo >&2 "ERROR: No master keyfile provided, but no existing clevis bind policy on $device, slot $keyslot. Unable to make an original seal without the master keyfile." + exit ${EXITCODES[ERR]} + } + else + local keyfile="$master_keyfile" + fi + + if $opt_dry_run; then + echo >&2 "Dry run. Skipping." + test -z "$master_keyfile" && shred -u "$keyfile" && trap - EXIT || : + return + fi + + + if [ -n "$master_keyfile" ]; then + # Remove the old TPM2 policy, if it exists. Clevis complains otherwise. + clevis luks unbind \ + -f \ + -d "$device" \ + -s "$keyslot" \ + tpm2 2>/dev/null || : + # Assign a new TPM2 policy to the keyslot. + clevis luks bind \ + -d "$device" \ + -s "$keyslot" \ + -k "$keyfile" \ + -y \ + tpm2 \ + "{ \"pcr_bank\": \"$PCR_BANK\", \"pcr_ids\": \"$PCR_INDEXES\" }" + else + clevis luks regen \ + -q \ + -d "$device" \ + -s "$keyslot" + fi + + # Clean up the tempkey, if we're using one + test -z "$master_keyfile" && shred -u "$keyfile" && trap - EXIT || : +} + + +# Reseals the specified LUKS device by enrolling new PCR policies for both the +# safemode and runmode keyslots. +function reseal_device() { + local device="$1" # e.g. the LUKS device path to encrypt + local keyfile="$2" # File path to a keyfile that can open the LUKS volume. + + # Let the user know where the key is coming from. + case $keyfile in + "") + echo "No master keyfile provided. Attempting to reseal using existing TPM policies." + ;; + "-") + # Read keyfile from stdin + echo "Reading keyfile from stdin..." + ;; + *) + if [ ! -f "$keyfile" ]; then + echo "ERROR: Provided keyfile '$keyfile' does not exist or is not a file." >&2 + exit ${EXITCODES[BADARGS]} + fi + echo "Using provided keyfile: $keyfile" + ;; + esac + + _reseal_device_keyslot "$device" "$LUKS_KEYSLOT_SAFEMODE" "$keyfile" + _reseal_device_keyslot "$device" "$LUKS_KEYSLOT_RUNMODE" "$keyfile" + + clevis luks list -d "$device" +} + + +# Prints usage information to the specified file descriptor (default: stderr). +function usage() { + local fp_out="${1:-2}" + + cat >&$fp_out </dev/null 2>&1 || { \ + log ERROR "Required tool '$tool' is not installed. Unable to continue."; + exit ${EXITCODES[BADENV]}; \ + } + done +} + + +# Populates the following global variables: +# - userfs_path +# - userfs_luks_path +# - niconfig_path +# - niconfig_luks_path +function find_partitions() { + userfs_path=$(blkid --label "$USERFS_PARTLABEL" --output device || :) + userfs_luks_path=$(blkid --label "$USERFS_PARTLABEL-luks" --output device || :) + niconfig_path=$(blkid --label "$NICONFIG_PARTLABEL" --output device || :) + niconfig_luks_path=$(blkid --label "$NICONFIG_PARTLABEL-luks" --output device || :) +} + + +# Logs a message with the specified log level. +# Log levels: +# - INFO: Informational messages (only printed if VERBOSE=yes) +# - WARN: Warning messages +# - ERROR: Error messages (also sent to syslog with "user.err" priority +function log() { + local level="$1" # log level (e.g. "INFO", "WARN", "ERROR") + local msg="${*:2}" # message text + + case "$level" in + INFO) + if [ "${VERBOSE:-}" = yes ]; then + logger -p user.info "ni-cryptdisks: INFO: $msg" || : + fi + ;; + WARN) + logger -p user.warning "ni-cryptdisks: WARN: $msg" || : + ;; + ERROR) + logger -p user.err "ni-cryptdisks: ERROR: $msg" || : + ;; + *) + logger -p user.notice "ni-cryptdisks: UNKNOWN: $msg" || : + ;; + esac +} + + +# Tries to find and mount LUKS-encrypted partitions to the system. +function start() { + find_partitions + + if [ -n "$userfs_luks_path" ]; then + echo "Opening LUKS partition $userfs_luks_path as $USERFS_PARTLABEL" + dmsetup ls --target crypt | grep -q "^$USERFS_PARTLABEL\s" || { + clevis luks unlock \ + -d "${userfs_luks_path}" \ + -n "$USERFS_PARTLABEL" + } + fi + + if [ -n "$niconfig_luks_path" ]; then + echo "Opening LUKS partition $niconfig_luks_path as $NICONFIG_PARTLABEL" + dmsetup ls --target crypt | grep -q "^$NICONFIG_PARTLABEL\s" || { + clevis luks unlock \ + -d "${niconfig_luks_path}" \ + -n "$NICONFIG_PARTLABEL" + } + fi +} + + +# Closes any open LUKS-encrypted partitions. +function stop() { + find_partitions + + if [ -n "$userfs_luks_path" ]; then + cryptsetup close "$USERFS_PARTLABEL" + if [ $? -eq 0 ]; then + echo "Closed LUKS partition $USERFS_PARTLABEL" + else + log WARN "Failed to close $USERFS_PARTLABEL" + fi + fi + + if [ -n "$niconfig_luks_path" ]; then + cryptsetup close "$NICONFIG_PARTLABEL" + if [ $? -eq 0]; then + echo "Closed LUKS partition $NICONFIG_PARTLABEL" + else + log WARN "Failed to close $NICONFIG_PARTLABEL" + fi + fi +} + + +# ============================================================================== +# GLOBALS +# ============================================================================== + +niconfig_luks_path="" # path to the LUKS version of the niconfig partition +niconfig_path="" # path to the niconfig partition +userfs_luks_path="" # path to the LUKS version of the userfs partition +userfs_path="" # path to the userfs partition + + +# ============================================================================== +# MAIN +# ============================================================================== + +check_env + +case "${1:-}" in + restart) stop && start ;; + start) start ;; + stop) stop ;; + *) echo "Usage: $0 {restart|start|stop}"; exit ${EXITCODES[BADARGS]};; +esac + +exit ${EXITCODES[OK]} diff --git a/recipes-ni/ni-device-encryption/ni-device-encryption/share/const_luks.sh b/recipes-ni/ni-device-encryption/ni-device-encryption/share/const_luks.sh new file mode 100644 index 000000000..036a4338a --- /dev/null +++ b/recipes-ni/ni-device-encryption/ni-device-encryption/share/const_luks.sh @@ -0,0 +1,4 @@ +# NI domain constants for LUKS encryption + +export LUKS_KEYSLOT_SAFEMODE=1 +export LUKS_KEYSLOT_RUNMODE=2 diff --git a/recipes-ni/ni-device-encryption/ni-device-encryption/share/pkg.sh.in b/recipes-ni/ni-device-encryption/ni-device-encryption/share/pkg.sh.in new file mode 100644 index 000000000..e5678bcf4 --- /dev/null +++ b/recipes-ni/ni-device-encryption/ni-device-encryption/share/pkg.sh.in @@ -0,0 +1,4 @@ +# Package variables for ni-device-encryption + +export PACKAGE=@PACKAGE@ +export VERSION=@VERSION@ diff --git a/recipes-security/clevis/clevis_21.bb b/recipes-security/clevis/clevis_21.bb new file mode 100644 index 000000000..b2cddf6da --- /dev/null +++ b/recipes-security/clevis/clevis_21.bb @@ -0,0 +1,67 @@ +SUMMARY = "Clevis - Automated Encryption Framework" +DESCRIPTION = "Clevis is a pluggable framework for automated decryption. It \ +can be used to provide automated decryption of data or even automated \ +unlocking of LUKS volumes." +HOMEPAGE = "https://github.com/latchset/clevis" +SECTION = "security" +LICENSE = "GPL-3.0-or-later" +LIC_FILES_CHKSUM = "\ + file://COPYING;md5=d32239bcb673463ab874e80d47fae504 \ + file://COPYING.openssl;md5=a78c00d154a43f35ef1dc1292a234c6d \ +" + + +DEPENDS = "\ + cryptsetup \ + cryptsetup-native \ + jansson \ + jose \ + keyutils-native \ +" + +SRC_URI = "\ + https://github.com/latchset/clevis/releases/download/v${PV}/${BP}.tar.xz \ +" +SRC_URI[sha256sum] = "a0388a544c77139dc751cdbf66bdd38fc29c43f9e81a1cdfd119c84109ffca3f" + + +# ============================================================================== +# BBCLASSES +# ============================================================================== + +# CONFIGURATION AND BUILD +inherit meson pkgconfig + +PACKAGECONFIG ??= "" +PACKAGECONFIG[docs] = ",, asciidoc-native" +PACKAGECONFIG[dracut] = ",, dracut, dracut" +PACKAGECONFIG[luks] = ",, luksmeta, cryptsetup jq" +PACKAGECONFIG[pkcs11] = ",, opensc-native, opensc" +PACKAGECONFIG[tpm2] = ",, tpm2-tools-native, tpm2-tools" +# TODO: Add support for systemd systems. +# initramfs-tools integration intentionally skipped due to no-support in OE. + + +inherit bash-completion + + +# PTESTING + +inherit ptest + +do_install_ptest () { + install -d ${D}${PTEST_PATH} + install -m 0744 ${S}/src/luks/tests/* ${D}${PTEST_PATH} + # TODO: more tests +} + +RDEPENDS:${PN}-ptest += " bash cryptsetup" +RRECOMMENDS:${PN}-ptest += " jq keyutils" + + +# ============================================================================== +# PACKAGING +# ============================================================================== +# clevis +FILES:${PN} += " ${libdir}/dracut/*" +RDEPENDS:${PN} += " bash tpm2-tools" diff --git a/recipes-security/clevis/clevis_21.bbappend b/recipes-security/clevis/clevis_21.bbappend new file mode 100644 index 000000000..19a9cc310 --- /dev/null +++ b/recipes-security/clevis/clevis_21.bbappend @@ -0,0 +1 @@ +PACKAGECONFIG = "docs dracut luks pkcs11 tpm2" diff --git a/recipes-security/jose/jose_14.bb b/recipes-security/jose/jose_14.bb new file mode 100644 index 000000000..78c684204 --- /dev/null +++ b/recipes-security/jose/jose_14.bb @@ -0,0 +1,33 @@ +SUMMARY = "Jose - C-language implementation of Javascript Object Signing and \ +Encryption" +DESCRIPTION = "José is a C-language implementation of the Javascript Object \ +Signing and Encryption standards. Specifically. José is extensively tested \ +against the RFC test vectors." +HOMEPAGE = "https://github.com/latchset/jose" +SECTION = "security" +LICENSE = "Apache-2.0" +LIC_FILES_CHKSUM = "\ + file://COPYING;md5=34400b68072d710fecd0a2940a0d1658 \ +" + +DEPENDS = "\ + openssl \ + jansson \ + zlib \ +" + + +SRC_URI = "\ + https://github.com/latchset/jose/releases/download/v14/jose-14.tar.xz \ +" +SRC_URI[sha256sum] = "cee329ef9fce97c4c025604a8d237092f619aaa9f6d35fdf9d8c9052bc1ff95b" + + +# ============================================================================== +# BBCLASSES +# ============================================================================== + +inherit meson pkgconfig + + +BBCLASSEXTEND = "native" diff --git a/recipes-support/luksmeta/luksmeta_10.bb b/recipes-support/luksmeta/luksmeta_10.bb new file mode 100644 index 000000000..663ea0951 --- /dev/null +++ b/recipes-support/luksmeta/luksmeta_10.bb @@ -0,0 +1,29 @@ +SUMMARY = "LUKSMeta" +DESCRIPTION = "Welcome to LUKSMeta! LUKSMeta is a simple library for storing \ +metadata in the LUKSv1 header. This library is licensed under the GNU LGPLv2+." +HOMEPAGE = "https://github.com/latchset/luksmeta" +SECTION = "security" +LICENSE = "LGPL-2.1-or-later" +LIC_FILES_CHKSUM = "\ + file://COPYING;md5=4e9dfcb21c14eb0c40ae8ba436d3bb7a \ +" + +DEPENDS = "\ + cryptsetup \ +" + + +SRC_URI = "\ + https://github.com/latchset/luksmeta/releases/download/v10/luksmeta-10.tar.bz2 \ +" +SRC_URI[sha256sum] = "a842538ba39680c8319c41dac0bcc082fe40fb43342561761925c0daa1a48f28" + + +inherit autotools pkgconfig + +# ============================================================================== +# PACKAGING +# ============================================================================== + + +BBCLASSEXTEND = "native"