image: add the implementation to support the secure initramfs

The secure initramfs is used to load the rootfs based on information
from the kernel command line, parse the FIT image encapsulating the
rootfs and perform other operations such as authenticating the rootfs.

Tracked-by: FEAT-29
Closes: PCF-1893
Signed-off-by: Mohamed-nour Toumi <mohamed.toumi_ext@softathome.com>
This commit is contained in:
mohamed-nour Toumi 2025-02-03 10:15:27 +01:00 committed by Petr Štetiar
parent f626416bb4
commit b8ba3aecc3
No known key found for this signature in database
GPG Key ID: 58EE120F30CC02D3
5 changed files with 276 additions and 11 deletions

View File

@ -70,6 +70,25 @@ menu "Target Images"
Generate separate initrd.cpio instead of embedding it.
This is useful for generating images with a dedicated
ramdisk e.g. in U-Boot's uImage and uImage.FIT formats.
menuconfig TARGET_SECURE_INITRAMFS
bool "pivoting initramfs"
help
Build a pivoting initramfs archive the kernel can start.
It will be used to set things up before a rootfs is started,
like authenticate a rootfs image.
choice
prompt "Compression"
depends on TARGET_SECURE_INITRAMFS
help
Select initramfs compression mode.
config TARGET_SECURE_INITRAMFS_COMPRESSION_NONE
bool "none"
config TARGET_SECURE_INITRAMFS_COMPRESSION_GZIP
bool "gzip"
endchoice
comment "Root filesystem archives"

View File

@ -3,6 +3,10 @@
IMAGE_KERNEL = $(word 1,$^)
IMAGE_ROOTFS = $(word 2,$^)
define get_initramfs_compression
$(firstword $(foreach compression,NONE GZIP BZIP2 LZMA LZO XZ LZ4 ZSTD,$(if $($1$(compression)),$(compression) ,)))
endef
define ModelNameLimit16
$(shell printf %.16s "$(word 2, $(subst _, ,$(1)))")
endef

View File

@ -48,6 +48,7 @@ IMG_PREFIX_VERCODE:=$(if $(CONFIG_VERSION_CODE_FILENAMES),$(call sanitize,$(VERS
IMG_PREFIX:=$(VERSION_DIST_SANITIZED)-$(IMG_PREFIX_VERNUM)$(IMG_PREFIX_VERCODE)$(IMG_PREFIX_EXTRA)$(BOARD)-$(SUBTARGET)
IMG_ROOTFS:=$(IMG_PREFIX)-rootfs
IMG_COMBINED:=$(IMG_PREFIX)-combined
IMG_SECURE_INITRAMFS:=$(IMG_PREFIX)-secure-initramfs$(shell $(TOPDIR)/scripts/gen-secure-initramfs.sh show-extension $(call get_initramfs_compression,CONFIG_TARGET_SECURE_INITRAMFS_COMPRESSION_))
ifeq ($(DUMP),)
IMG_PART_SIGNATURE:=$(shell echo $(SOURCE_DATE_EPOCH)$(LINUX_VERMAGIC) | $(MKHASH) md5 | cut -b1-8)
IMG_PART_DISKGUID:=$(shell echo $(SOURCE_DATE_EPOCH)$(LINUX_VERMAGIC) | $(MKHASH) md5 | sed -E 's/(.{8})(.{4})(.{4})(.{4})(.{10})../\1-\2-\3-\4-\500/')
@ -156,6 +157,15 @@ define prepare_generic_squashfs
$(STAGING_DIR_HOST)/bin/padjffs2 $(1) 4 8 16 64 128 256
endef
define Image/SecureInitramfs
UNSTRIPPED_FOLDER=$(STAGING_DIR_ROOT) \
BINARIES_PATH=$(CONFIG_TARGET_INIT_PATH) \
$(TOPDIR)/scripts/gen-secure-initramfs.sh build \
$(call get_initramfs_compression,CONFIG_TARGET_SECURE_INITRAMFS_COMPRESSION_) \
$(BIN_DIR)/$(IMG_SECURE_INITRAMFS) \
$(STAGING_DIR_IMAGE)/initramfs $(TARGET_DIR)
endef
define Image/BuildKernel/Initramfs
$(call Image/Build/Initramfs)
endef
@ -971,6 +981,7 @@ define BuildImage
$(call Image/BuildKernel)
$(if $(CONFIG_TARGET_ROOTFS_INITRAMFS),$(if $(IB),,$(call Image/BuildKernel/Initramfs)))
$(call Image/InstallKernel)
$(if $(CONFIG_TARGET_SECURE_INITRAMFS),$(call Image/SecureInitramfs))
$(foreach device,$(TARGET_DEVICES),$(call Device,$(device)))

View File

@ -45,6 +45,26 @@ else
endef
endif
# Convenience function to only enable selected initramfs compression mode
# $1: compression variable prefix (e.g. "CONFIG_TARGET_INITRAMFS_COMPRESSION_")
# $2: output file
define Kernel/SetInitramfsCompressions
{ \
$(if $(CONFIG_TARGET_INITRAMFS_COMPRESSION_NONE), \
echo "CONFIG_INITRAMFS_COMPRESSION_NONE=y" >> $(2);, \
echo "# CONFIG_INITRAMFS_COMPRESSION_NONE is not set" >> $(2); ) \
$(foreach ALGO,GZIP BZIP2 LZMA LZO XZ LZ4 ZSTD, \
$(if $(CONFIG_TARGET_INITRAMFS_COMPRESSION_$(ALGO)), \
echo "CONFIG_INITRAMFS_COMPRESSION_$(ALGO)=y" >> $(2); $\, \
echo "# CONFIG_INITRAMFS_COMPRESSION_$(ALGO) is not set" >> $(2); $\) \
$(if $(CONFIG_TARGET_INITRAMFS_COMPRESSION_$(ALGO)), \
echo "CONFIG_RD_$(ALGO)=y" >> $(2); $\, \
echo "# CONFIG_RD_$(ALGO) is not set" >> $(2); $\) \
) \
}
endef
ifeq ($(CONFIG_TARGET_ROOTFS_INITRAMFS),y)
ifeq ($(CONFIG_TARGET_ROOTFS_INITRAMFS_SEPARATE),y)
define Kernel/SetInitramfs/PreConfigure
@ -86,28 +106,31 @@ ifeq ($(CONFIG_TARGET_ROOTFS_INITRAMFS),y)
$(if $(CONFIG_TARGET_INITRAMFS_FORCE), \
echo "CONFIG_INITRAMFS_FORCE=y" >> $(2)/.config;, \
echo "# CONFIG_INITRAMFS_FORCE is not set" >> $(2)/.config;)) \
$(if $(CONFIG_TARGET_INITRAMFS_COMPRESSION_NONE), \
echo "CONFIG_INITRAMFS_COMPRESSION_NONE=y" >> $(2)/.config;, \
echo "# CONFIG_INITRAMFS_COMPRESSION_NONE is not set" >> $(2)/.config; ) \
$(foreach ALGO,GZIP BZIP2 LZMA LZO XZ LZ4 ZSTD, \
$(if $(CONFIG_TARGET_INITRAMFS_COMPRESSION_$(ALGO)), \
echo "CONFIG_INITRAMFS_COMPRESSION_$(ALGO)=y" >> $(2)/.config; $\, \
echo "# CONFIG_INITRAMFS_COMPRESSION_$(ALGO) is not set" >> $(2)/.config; $\) \
$(if $(CONFIG_TARGET_INITRAMFS_COMPRESSION_$(ALGO)), \
echo "CONFIG_RD_$(ALGO)=y" >> $(2)/.config; $\, \
echo "# CONFIG_RD_$(ALGO) is not set" >> $(2)/.config; $\) \
) \
$(call Kernel/SetInitramfsCompressions,CONFIG_TARGET_INITRAMFS_COMPRESSION_,$(LINUX_DIR)/.config); \
}
endef
else
endif
ifeq ($(CONFIG_TARGET_SECURE_INITRAMFS),y)
define Kernel/SetNoInitramfs/CustomSupportInitramfs
echo 'CONFIG_BLK_DEV_INITRD=y' >> $(LINUX_DIR)/.config.set
echo 'CONFIG_DECOMPRESS_GZIP=y' >> $(LINUX_DIR)/.config.set
echo 'CONFIG_RD_GZIP=y' >> $(LINUX_DIR)/.config.set
endef
else
define Kernel/SetNoInitramfs/CustomSupportInitramfs
endef
endif
define Kernel/SetNoInitramfs
mv $(LINUX_DIR)/.config.set $(LINUX_DIR)/.config.old
grep -v INITRAMFS $(LINUX_DIR)/.config.old > $(LINUX_DIR)/.config.set
echo 'CONFIG_INITRAMFS_SOURCE=""' >> $(LINUX_DIR)/.config.set
echo '# CONFIG_INITRAMFS_FORCE is not set' >> $(LINUX_DIR)/.config.set
echo "# CONFIG_INITRAMFS_PRESERVE_MTIME is not set" >> $(LINUX_DIR)/.config.set
$(call Kernel/SetNoInitramfs/CustomSupportInitramfs)
endef
define Kernel/Configure/Default

208
scripts/gen-secure-initramfs.sh Executable file
View File

@ -0,0 +1,208 @@
#!/bin/bash
# SPDX-License-Identifier: BSD-2-Clause-Patent
#
# SPDX-FileCopyrightText: Copyright (c) 2025 SoftAtHome
#
TMP_FOLDER="initramfs.tmp"
MODES="help show-extension build"
function usage {
echo -e "Usage:
$0 MODE COMPRESSION [OUT_FILE] [IN_FOLDER] [ROOTFS_FOLDER]\n\
\n\
MODE should be one of $MODES.\n\
COMPRESSION should be one of GZIP BZIP2 LZMA LZO XZ LZ4 ZSTD.\n\
Additional parameters are only needed in 'build' mode
'build' mode will also expect environment variables:
- UNSTRIPPED_FOLDER: a staging folder containing unstripped binaries for objdump
- BINARIES_PATH: the target PATH variable
"
exit $1
}
function help {
usage 0
}
function show-extension {
echo ".cpio$EXTENSION"
}
function build {
OUT_FILE="$1"
IN_FOLDER="$2"
ROOTFS_FOLDER="$3"
[ -e `dirname $OUT_FILE` ] && [ -e "$IN_FOLDER" ] && [ -e "$ROOTFS_FOLDER" ] && [ -n "$UNSTRIPPED_FOLDER" ] && [ -n "$BINARIES_PATH" ] || usage 1
INITRAMFS_BINARIES_SEARCH_PATH="$BINARIES_PATH:/lib:/lib64:/usr/lib:/usr/lib64"
rm -rf "$TMP_FOLDER"
mkdir "$TMP_FOLDER"
import_files_tree
import_files_list
package_final_file
}
function parse_compression {
case "$1" in
NONE)
EXTENSION=""
COMPRESSOR="tee"
;;
GZIP)
EXTENSION=".gz"
COMPRESSOR="gzip -c -"
;;
BZIP2)
EXTENSION=".bz2"
COMPRESSOR="bzip2 -c -"
;;
LZMA)
EXTENSION=".lzma"
COMPRESSOR="lzma -c -"
;;
LZO)
EXTENSION=".lzo"
COMPRESSOR="lzop -c -"
;;
XZ)
EXTENSION=".xz"
COMPRESSOR="xz -c -"
;;
LZ4)
EXTENSION=".lz4"
COMPRESSOR="lz4 -c -"
;;
ZSTD)
EXTENSION=".zstd"
COMPRESSOR="zstd -c -"
;;
*)
usage 1;;
esac
}
function import_files_tree {
cp -r $IN_FOLDER/initramfs-files/* $TMP_FOLDER/
}
# $1 is the file to find, it should be a binary/lib to search in path
# Only first hit is returned
function find_binary {
while read -r search_dir; do
if [ -e "$ROOTFS_FOLDER$search_dir/$1" ] || [ -L "$ROOTFS_FOLDER$search_dir/$1" ]; then
echo "$search_dir/$1"
return 0
fi
done < <(echo "$INITRAMFS_BINARIES_SEARCH_PATH" | sed -r 's/:/\n/g')
echo "Error: could not find required binary $1" >&2
exit 1
}
function import_file_symlink {
import_file_other "$1"
target=`readlink "$ROOTFS_FOLDER$1"`
[[ "$target" == /* ]] || target=`dirname "$1"`/$target
import_file "$target"
}
function import_file_binary {
import_file_other "$1"
deps=`objdump -p "$UNSTRIPPED_FOLDER$1" | grep NEEDED | sed -r 's/\s*NEEDED\s*//'`
[ -z "$deps" ] && return 0
while read -r dependency; do
import_file `find_binary "$dependency"`
done < <(echo "$deps")
}
function import_file_other {
mkdir -p `dirname $TMP_FOLDER$1`
cp -pPTr "$ROOTFS_FOLDER$1" "$TMP_FOLDER$1"
}
function import_file {
[ -e "$TMP_FOLDER$1" ] && return 0
# Here it both checks the file exists, and expand any wildcard pattern, so we need the ls command
cd "$ROOTFS_FOLDER"; file=`ls --format=single-column ./$1 | head -n 1 | sed -E 's,\./,,'`; cd - > /dev/null
[ -z "$file" ] && echo "Could not find $1" >&2 && exit 1
input_file="$ROOTFS_FOLDER$file"
if [ -L "$input_file" ]; then
file_type=symlink
elif objdump -f "$input_file" > /dev/null; then
file_type=binary
else
file_type=other
fi
import_file_$file_type "$file"
}
function find_optional_kernel_module {
local name="$1"
local relpath
# Work under ROOTFS_FOLDER, search entire lib/modules tree
relpath=$(cd "$ROOTFS_FOLDER" && \
find lib/modules -type f -name "$name" 2>/dev/null | head -n 1)
if [ -z "$relpath" ]; then
echo "Optional kernel module $name not found, skipping" >&2
return 1
fi
# Prepend leading slash to make it absolute relative to ROOTFS
echo "/$relpath"
return 0
}
function import_files_list {
while read -r file; do
# Skip empty lines and comments
[ -z "$file" ] && continue
[[ "$file" =~ ^# ]] && continue
# Optional kernel module: syntax "(dm-mod.ko)"
if [[ "$file" =~ ^\((.+\.ko)\)$ ]]; then
local modname="${BASH_REMATCH[1]}"
local modpath
if modpath=$(find_optional_kernel_module "$modname"); then
# Only import if found
import_file "$modpath"
fi
# Always continue: optional, so never fatal
continue
fi
# Normal behavior for all other entries:
# - absolute path: use as-is
# - otherwise: search via find_binary
if [[ "$file" == /* ]]; then
import_file "$file"
else
file=$(find_binary "$file")
import_file "$file"
fi
done < <(cat "$IN_FOLDER/initramfs-files-list.txt"; \
find "$ROOTFS_FOLDER/lib" -name 'ld-*' -printf '/lib/%P\n')
}
function package_final_file {
cd $TMP_FOLDER
find . | cpio -o --quiet -H newc --owner "+0:+0" | $COMPRESSOR > "$OUT_FILE"
cd - > /dev/null
}
mode=$1
echo "$MODES" | grep -wq "$mode" || usage 1
shift
parse_compression "$1"
shift
$mode $@