mirror of
https://github.com/linuxboot/heads.git
synced 2024-12-20 21:43:11 +00:00
Blob jail: Add zstd-decompress, decompress more complex archives
Debian 12's initrd by default now consists of an uncompressed cpio archive containing microcode, followed by a zstd-compressed cpio archive. inject_firmware.sh only supported gzip-compressed cpio, so it could not extract /init from this archive. Add zstd-decompress to decompress zstd streams (uncompressed size is about 180 KB). Add unpack_initramfs.sh which is able to decompress uncompressed, gzip, or zstd archives, with multiple segments, much like the Linux kernel itself does. Use unpack_initramfs.sh to extract /init for blob jail. Don't compress the new archive segment containing firmware and the updated /init. Signed-off-by: Jonathon Hall <jonathon.hall@puri.sm>
This commit is contained in:
parent
3e6eac9ffd
commit
1bf8331ffb
1
Makefile
1
Makefile
@ -500,6 +500,7 @@ bin_modules-$(CONFIG_TPM2_TOOLS) += tpm2-tools
|
|||||||
bin_modules-$(CONFIG_BASH) += bash
|
bin_modules-$(CONFIG_BASH) += bash
|
||||||
bin_modules-$(CONFIG_POWERPC_UTILS) += powerpc-utils
|
bin_modules-$(CONFIG_POWERPC_UTILS) += powerpc-utils
|
||||||
bin_modules-$(CONFIG_IOPORT) += ioport
|
bin_modules-$(CONFIG_IOPORT) += ioport
|
||||||
|
bin_modules-$(CONFIG_ZSTD) += zstd
|
||||||
|
|
||||||
$(foreach m, $(bin_modules-y), \
|
$(foreach m, $(bin_modules-y), \
|
||||||
$(call map,initrd_bin_add,$(call bins,$m)) \
|
$(call map,initrd_bin_add,$(call bins,$m)) \
|
||||||
|
@ -20,6 +20,8 @@ CONFIG_PCIUTILS=y
|
|||||||
CONFIG_POPT=y
|
CONFIG_POPT=y
|
||||||
CONFIG_QRENCODE=y
|
CONFIG_QRENCODE=y
|
||||||
CONFIG_TPMTOTP=y
|
CONFIG_TPMTOTP=y
|
||||||
|
# zstd-decompress - for blob jail, needed to extract /init from zstd cpio archive
|
||||||
|
CONFIG_ZSTD=y
|
||||||
|
|
||||||
CONFIG_CAIRO=y
|
CONFIG_CAIRO=y
|
||||||
CONFIG_FBWHIPTAIL=y
|
CONFIG_FBWHIPTAIL=y
|
||||||
|
@ -36,7 +36,8 @@ ORIG_INITRD="$1"
|
|||||||
INITRD_ROOT="/tmp/inject_firmware_initrd_root"
|
INITRD_ROOT="/tmp/inject_firmware_initrd_root"
|
||||||
rm -rf "$INITRD_ROOT" || true
|
rm -rf "$INITRD_ROOT" || true
|
||||||
mkdir "$INITRD_ROOT"
|
mkdir "$INITRD_ROOT"
|
||||||
gunzip <"$ORIG_INITRD" | (cd "$INITRD_ROOT"; cpio -i init 2>/dev/null)
|
# Unpack just 'init' from the original initrd
|
||||||
|
unpack_initramfs.sh "$ORIG_INITRD" "$INITRD_ROOT" init
|
||||||
|
|
||||||
# Copy the firmware into the initrd
|
# Copy the firmware into the initrd
|
||||||
for f in $(cbfs -l | grep firmware); do
|
for f in $(cbfs -l | grep firmware); do
|
||||||
@ -50,6 +51,7 @@ done
|
|||||||
# awk will happily pass through a binary file, so look for the match we want
|
# awk will happily pass through a binary file, so look for the match we want
|
||||||
# before modifying init to ensure it's a shell script and not an ELF, etc.
|
# before modifying init to ensure it's a shell script and not an ELF, etc.
|
||||||
if ! grep -E -q '^exec run-init .*\$\{rootmnt\}' "$INITRD_ROOT/init"; then
|
if ! grep -E -q '^exec run-init .*\$\{rootmnt\}' "$INITRD_ROOT/init"; then
|
||||||
|
WARN "Can't apply firmware blob jail, unknown init script"
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@ -79,11 +81,14 @@ awk -e "$AWK_INSERT_CP" "$INITRD_ROOT/init" >"$INITRD_ROOT/init_fw"
|
|||||||
mv "$INITRD_ROOT/init_fw" "$INITRD_ROOT/init"
|
mv "$INITRD_ROOT/init_fw" "$INITRD_ROOT/init"
|
||||||
chmod a+x "$INITRD_ROOT/init"
|
chmod a+x "$INITRD_ROOT/init"
|
||||||
|
|
||||||
# Pad the original initrd to 512 byte blocks, the last gzip blob is often not
|
# Pad the original initrd to 512 byte blocks. Uncompressed cpio contents must
|
||||||
# padded. (If it is not gzip-compressed, we would already have failed above.)
|
# be 4-byte aligned, and anecdotally gzip frames might not be padded by dracut.
|
||||||
|
# Linux ignores zeros between archive segments, so any extra padding is not
|
||||||
|
# harmful.
|
||||||
FW_INITRD="/tmp/inject_firmware_initrd.cpio.gz"
|
FW_INITRD="/tmp/inject_firmware_initrd.cpio.gz"
|
||||||
dd if="$ORIG_INITRD" of="$FW_INITRD" bs=512 conv=sync status=none
|
dd if="$ORIG_INITRD" of="$FW_INITRD" bs=512 conv=sync status=none
|
||||||
# Pack up the new contents and append to the initrd
|
# Pack up the new contents and append to the initrd. Don't spend time
|
||||||
(cd "$INITRD_ROOT"; find . | cpio -o -H newc) | gzip >>"$FW_INITRD"
|
# compressing this.
|
||||||
|
(cd "$INITRD_ROOT"; find . | cpio -o -H newc) >>"$FW_INITRD"
|
||||||
# Use this initrd
|
# Use this initrd
|
||||||
echo "$FW_INITRD"
|
echo "$FW_INITRD"
|
||||||
|
110
initrd/bin/unpack_initramfs.sh
Executable file
110
initrd/bin/unpack_initramfs.sh
Executable file
@ -0,0 +1,110 @@
|
|||||||
|
#! /bin/bash
|
||||||
|
set -e -o pipefail
|
||||||
|
|
||||||
|
. /etc/functions
|
||||||
|
|
||||||
|
# Unpack a Linux initramfs archive.
|
||||||
|
#
|
||||||
|
# In general, the initramfs archive is one or more cpio archives, optionally
|
||||||
|
# compressed, concatenated together. Uncompressed and compressed segments can
|
||||||
|
# exist in the same file. Zero bytes between segments are skipped. To properly
|
||||||
|
# unpack such an archive, all segments must be unpacked.
|
||||||
|
#
|
||||||
|
# This script unpacks such an archive, but with a limitation that once a
|
||||||
|
# compressed segment is reached, no more segments can be read. This works for
|
||||||
|
# common initrds on x86, where the microcode must be stored in an initial
|
||||||
|
# uncompressed segment, followed by the "real" initramfs content which is
|
||||||
|
# usually in one compressed segment.
|
||||||
|
#
|
||||||
|
# The limitation comes from gunzip/unzstd, there's no way to prevent them from
|
||||||
|
# consuming trailing data or tell us the member/frame length. The script
|
||||||
|
# succeeds with whatever was extracted, since this is used to extract particular
|
||||||
|
# files and boot can proceed as long as those files were found.
|
||||||
|
|
||||||
|
INITRAMFS_ARCHIVE="$1"
|
||||||
|
DEST_DIR="$2"
|
||||||
|
shift
|
||||||
|
shift
|
||||||
|
# rest of args go to cpio, can specify filename patterns
|
||||||
|
CPIO_ARGS=("$@")
|
||||||
|
|
||||||
|
# Consume zero bytes, the first nonzero byte read (if any) is repeated on stdout
|
||||||
|
consume_zeros() {
|
||||||
|
next_byte='00'
|
||||||
|
while [ "$next_byte" = "00" ]; do
|
||||||
|
# if we reach EOF, next_byte becomes empty (dd does not fail)
|
||||||
|
next_byte="$(dd bs=1 count=1 status=none | xxd -p | tr -d ' ')"
|
||||||
|
done
|
||||||
|
# if we finished due to nonzero byte (not EOF), then carry that byte
|
||||||
|
if [ -n "$next_byte" ]; then
|
||||||
|
echo -n "$next_byte" | xxd -p -r
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
unpack_cpio() {
|
||||||
|
(cd "$dest_dir"; cpio -i "${CPIO_ARGS[@]}" 2>/dev/null)
|
||||||
|
}
|
||||||
|
|
||||||
|
# unpack the first segment of an archive, then write the rest to another file
|
||||||
|
unpack_first_segment() {
|
||||||
|
unpack_archive="$1"
|
||||||
|
dest_dir="$2"
|
||||||
|
rest_archive="$3"
|
||||||
|
|
||||||
|
mkdir -p "$dest_dir"
|
||||||
|
|
||||||
|
# peek the beginning of the file to determine what type of content is next
|
||||||
|
magic="$(dd if="$unpack_archive" bs=6 count=1 status=none | xxd -p)"
|
||||||
|
|
||||||
|
# read this segment of the archive, then write the rest to the next file
|
||||||
|
(
|
||||||
|
# Magic values correspond to Linux init/initramfs.c (zero, cpio) and
|
||||||
|
# lib/decompress.c (gzip)
|
||||||
|
case "$magic" in
|
||||||
|
00*)
|
||||||
|
# Skip zero bytes and copy the first nonzero byte
|
||||||
|
consume_zeros
|
||||||
|
# Copy the remaining data
|
||||||
|
cat
|
||||||
|
;;
|
||||||
|
303730373031*|303730373032*) # plain cpio
|
||||||
|
# Unpack the plain cpio, this stops reading after the trailer
|
||||||
|
unpack_cpio
|
||||||
|
# Copy the remaining data
|
||||||
|
cat
|
||||||
|
;;
|
||||||
|
1f8b*|1f9e*) # gzip
|
||||||
|
# gunzip won't stop when reaching the end of the gzipped member,
|
||||||
|
# so we can't read another segment after this. We can't
|
||||||
|
# reasonably determine the member length either, this requires
|
||||||
|
# walking all the compressed blocks.
|
||||||
|
gunzip | unpack_cpio
|
||||||
|
;;
|
||||||
|
28b5*) # zstd
|
||||||
|
# Like gunzip, this will not stop when reaching the end of the
|
||||||
|
# frame, and determining the frame length requires walking all
|
||||||
|
# of its blocks.
|
||||||
|
(zstd-decompress -d || true) | unpack_cpio
|
||||||
|
;;
|
||||||
|
*) # unknown
|
||||||
|
die "Can't decompress initramfs archive, unknown type: $magic"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
) <"$unpack_archive" >"$rest_archive"
|
||||||
|
|
||||||
|
orig_size="$(stat -c %s "$unpack_archive")"
|
||||||
|
rest_size="$(stat -c %s "$rest_archive")"
|
||||||
|
DEBUG "archive segment $magic: $((orig_size - rest_size)) bytes"
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUG "Unpacking $INITRAMFS_ARCHIVE to $DEST_DIR"
|
||||||
|
|
||||||
|
next_archive="$INITRAMFS_ARCHIVE"
|
||||||
|
rest_archive="/tmp/unpack_initramfs_rest"
|
||||||
|
|
||||||
|
# Break when there is no remaining data
|
||||||
|
while [ -s "$next_archive" ]; do
|
||||||
|
unpack_first_segment "$next_archive" "$DEST_DIR" "$rest_archive"
|
||||||
|
next_archive="/tmp/unpack_initramfs_next"
|
||||||
|
mv "$rest_archive" "$next_archive"
|
||||||
|
done
|
20
modules/zstd
Normal file
20
modules/zstd
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
modules-$(CONFIG_ZSTD) += zstd
|
||||||
|
|
||||||
|
zstd_version := 1.5.5
|
||||||
|
zstd_dir := zstd-$(zstd_version)
|
||||||
|
zstd_tar := zstd-$(zstd_version).tar.gz
|
||||||
|
zstd_url := https://github.com/facebook/zstd/releases/download/v$(zstd_version)/$(zstd_tar)
|
||||||
|
zstd_hash := 9c4396cc829cfae319a6e2615202e82aad41372073482fce286fac78646d3ee4
|
||||||
|
|
||||||
|
zstd_configure := true
|
||||||
|
|
||||||
|
# Only the decompressor is built and installed, to be able to read zstd-compressed
|
||||||
|
# initramfs archives.
|
||||||
|
zstd_target := \
|
||||||
|
$(MAKE_JOBS) $(CROSS_TOOLS) -C programs CFLAGS="-g0 -Os" \
|
||||||
|
HAVE_ZLIB=0 \
|
||||||
|
HAVE_LZMA=0 \
|
||||||
|
HAVE_LZ4=0 \
|
||||||
|
zstd-decompress
|
||||||
|
|
||||||
|
zstd_output := programs/zstd-decompress
|
Loading…
Reference in New Issue
Block a user