summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMorten Linderud <morten@linderud.pw>2023-10-20 22:04:26 +0200
committerMorten Linderud <morten@linderud.pw>2023-10-20 22:04:26 +0200
commitdfc30b91ea5d29c183bc5a1cfd5cadf297728253 (patch)
tree099ed856d49a5ca29f0ed0ace8e74acfe6e1c50b
parent8ea759c229245fc7056bc843f25283ccc488233f (diff)
parent73d806fa91586ff932a2586ecca4a8d625ae2d27 (diff)
Merge remote-tracking branch 'origin/merge-requests/187'
* origin/merge-requests/187: tests: add test case for kver_zimage Get kernel version from generic EFI zboot image for bash completion Get kernel version from generic EFI zboot image
-rw-r--r--functions73
-rw-r--r--shell/bash-completion165
-rw-r--r--test/cases/functions.bats76
-rw-r--r--test/helpers/common.bash79
4 files changed, 376 insertions, 17 deletions
diff --git a/functions b/functions
index 17e1ee5..a0fe637 100644
--- a/functions
+++ b/functions
@@ -210,9 +210,71 @@ detect_compression() {
return
fi
+ read -rd '' bytes < <(od -An -j0x04 -t c -N4 "$1" | tr -dc '[:alnum:]')
+ if [[ "$bytes" == 'zimg' ]]; then
+ echo 'zimg'
+ return
+ fi
+
# out of ideas, assuming uncompressed
}
+kver_zimage() {
+ # Generic EFI zboot added since kernel 6.1
+ # https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/firmware/efi/libstub/Makefile.zboot?h=v6.1
+ # https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/firmware/efi/libstub/zboot-header.S?h=v6.1
+
+ local kver='' reader start size comp_type
+
+ # Reading 4 bytes from address 0x08 is the starting offset of compressed data
+ start="$(od -An -j0x08 -t u4 -N4 "$1" | tr -dc '[:alnum:]')"
+
+ # Reading 4 bytes from address 0x0c is the size of compressed data,
+ # but it needs to be corrected according to the compressed type.
+ size="$(od -An -j0x0c -t u4 -N4 "$1" | tr -dc '[:alnum:]')"
+
+ # Read 36 bytes (before 0x3c) from address 0x18,
+ # which is a nul-terminated string representing the compressed type.
+ read -rd '' comp_type < <(od -An -j0x18 -t a -N32 "$1" | sed 's/ nul//g' | tr -dc '[:alnum:]')
+
+ [[ "$start" =~ ^[0-9]+$ ]] || return 1
+ [[ "$size" =~ ^[0-9]+$ ]] || return 1
+
+ case "$comp_type" in
+ 'gzip')
+ reader='zcat'
+ ;;
+ 'lz4')
+ reader='lz4cat'
+ size="$((size + 4))"
+ ;;
+ 'lzma')
+ reader='xzcat'
+ size="$((size + 4))"
+ ;;
+ 'lzo')
+ reader="lzop -d"
+ size="$((size + 4))"
+ ;;
+ 'xzkern')
+ reader='xzcat'
+ size="$((size + 4))"
+ ;;
+ 'zstd22')
+ reader='zstdcat'
+ size="$((size + 4))"
+ ;;
+ *)
+ reader="$comp_type"
+ size="$((size + 4))"
+ ;;
+ esac
+
+ read -r _ _ kver _ < <(dd if="$1" bs=1 count="$size" skip="$start" 2>/dev/null | $reader - | grep -m1 -aoE 'Linux version .(\.[-[:alnum:]+]+)+')
+
+ printf '%s' "$kver"
+}
+
kver_generic() {
# For unknown architectures, we can try to grep the uncompressed or gzipped
# image for the boot banner.
@@ -223,6 +285,17 @@ kver_generic() {
# Loosely grep for `linux_banner`:
# https://elixir.bootlin.com/linux/v5.7.2/source/init/version.c#L46
local kver='' reader='cat'
+ local comp_type=''
+
+ comp_type="$(detect_compression "$1")"
+
+ if [[ "$comp_type" == 'zimg' ]]; then
+ # Generic EFI zboot image
+ kver_zimage "$1"
+ return 0
+ elif [[ "$comp_type" == 'gzip' ]]; then
+ reader='zcat'
+ fi
[[ "$(detect_compression "$1")" == 'gzip' ]] && reader='zcat'
diff --git a/shell/bash-completion b/shell/bash-completion
index 80ceef9..850c4b9 100644
--- a/shell/bash-completion
+++ b/shell/bash-completion
@@ -1,12 +1,169 @@
#!/usr/bin/env bash
# SPDX-License-Identifier: GPL-2.0-only
+_detect_compression() {
+ local bytes
+
+ bytes="$(od -An -t x1 -N6 "$1" | tr -dc '[:alnum:]')"
+ case "$bytes" in
+ 'fd377a585a00')
+ echo 'xz'
+ return
+ ;;
+ esac
+
+ bytes="$(od -An -t x1 -N4 "$1" | tr -dc '[:alnum:]')"
+ if [[ "$bytes" == '894c5a4f' ]]; then
+ echo 'lzop'
+ return
+ fi
+
+ bytes="$(od -An -t x2 -N2 "$1" | tr -dc '[:alnum:]')"
+ if [[ "$bytes" == '8b1f' ]]; then
+ echo 'gzip'
+ return
+ fi
+
+ bytes="$(od -An -t x4 -N4 "$1" | tr -dc '[:alnum:]')"
+ case "$bytes" in
+ '184d2204')
+ error 'Newer lz4 stream format detected! This may not boot!'
+ echo 'lz4'
+ return
+ ;;
+ '184c2102')
+ echo 'lz4 -l'
+ return
+ ;;
+ 'fd2fb528')
+ echo 'zstd'
+ return
+ ;;
+ esac
+
+ bytes="$(od -An -c -N3 "$1" | tr -dc '[:alnum:]')"
+ if [[ "$bytes" == 'BZh' ]]; then
+ echo 'bzip2'
+ return
+ fi
+
+ # lzma detection sucks and there's really no good way to
+ # do it without reading large portions of the stream. this
+ # check is good enough for GNU tar, apparently, so it's good
+ # enough for me.
+ bytes="$(od -An -t x1 -N3 "$1" | tr -dc '[:alnum:]')"
+ if [[ "$bytes" == '5d0000' ]]; then
+ echo 'lzma'
+ return
+ fi
+
+ read -rd '' bytes < <(od -An -j0x04 -t c -N4 "$1" | tr -dc '[:alnum:]')
+ if [[ "$bytes" == 'zimg' ]]; then
+ echo 'zimg'
+ return
+ fi
+
+ # out of ideas, assuming uncompressed
+}
+
+_kver_zimage() {
+ # Generic EFI zboot added since kernel 6.1
+ # https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/firmware/efi/libstub/Makefile.zboot?h=v6.1
+ # https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/firmware/efi/libstub/zboot-header.S?h=v6.1
+
+ local kver='' reader start size comp_type
+
+ # Reading 4 bytes from address 0x08 is the starting offset of compressed data
+ start="$(od -An -j0x08 -t u4 -N4 "$1" | tr -dc '[:alnum:]')"
+
+ # Reading 4 bytes from address 0x0c is the size of compressed data,
+ # but it needs to be corrected according to the compressed type.
+ size="$(od -An -j0x0c -t u4 -N4 "$1" | tr -dc '[:alnum:]')"
+
+ # Read 36 bytes (before 0x3c) from address 0x18,
+ # which is a nul-terminated string representing the compressed type.
+ read -rd '' comp_type < <(od -An -j0x18 -t a -N32 "$1" | sed 's/ nul//g' | tr -dc '[:alnum:]')
+
+ [[ "$start" =~ ^[0-9]+$ ]] || return 1
+ [[ "$size" =~ ^[0-9]+$ ]] || return 1
+
+ case "$comp_type" in
+ 'gzip')
+ reader='zcat'
+ ;;
+ 'lz4')
+ reader='lz4cat'
+ size="$((size + 4))"
+ ;;
+ 'lzma')
+ reader='xzcat'
+ size="$((size + 4))"
+ ;;
+ 'lzo')
+ reader="lzop -d"
+ size="$((size + 4))"
+ ;;
+ 'xzkern')
+ reader='xzcat'
+ size="$((size + 4))"
+ ;;
+ 'zstd22')
+ reader='zstdcat'
+ size="$((size + 4))"
+ ;;
+ *)
+ reader="$comp_type"
+ size="$((size + 4))"
+ ;;
+ esac
+
+ read -r _ _ kver _ < <(dd if="$1" bs=1 count="$size" skip="$start" 2>/dev/null | $reader - | grep -m1 -aoE 'Linux version .(\.[-[:alnum:]+]+)+')
+
+ printf '%s' "$kver"
+}
+
+_detect_generic_kver() {
+ # For unknown architectures, we can try to grep the uncompressed or gzipped
+ # image for the boot banner.
+ # This should work at least for ARM when run on /boot/Image, or RISC-V on
+ # gzipped /boot/vmlinuz-linuz. On other architectures it may be worth trying
+ # rather than bailing, and inform the user if none was found.
+
+ # Loosely grep for `linux_banner`:
+ # https://elixir.bootlin.com/linux/v5.7.2/source/init/version.c#L46
+ local kver='' reader='cat'
+ local comp_type=''
+
+ comp_type="$(_detect_compression "$1")"
+
+ if [[ "$comp_type" == 'zimg' ]]; then
+ # Generic EFI zboot image
+ _kver_zimage "$1"
+ return 0
+ elif [[ "$comp_type" == 'gzip' ]]; then
+ reader='zcat'
+ fi
+
+ [[ "$(_detect_compression "$1")" == 'gzip' ]] && reader='zcat'
+
+ read -r _ _ kver _ < <($reader "$1" | grep -m1 -aoE 'Linux version .(\.[-[:alnum:]+]+)+')
+
+ printf '%s' "$kver"
+}
+
_detect_kver() {
local kver_validator='^[[:digit:]]+(\.[[:digit:]]+)+' offset
- offset="$(od -An -j0x20E -dN2 "$1")" || return
- read -r kver _ < \
- <(dd if="$1" bs=1 count=127 skip=$(( offset + 0x200 )) 2>/dev/null)
- [[ "$kver" =~ $kver_validator ]] && printf '%s' "$kver"
+ local arch
+
+ arch="$(uname -m)"
+ if [[ $arch == @(i?86|x86_64) ]]; then
+ offset="$(od -An -j0x20E -dN2 "$1")" || return
+ read -r kver _ < \
+ <(dd if="$1" bs=1 count=127 skip=$(( offset + 0x200 )) 2>/dev/null)
+ [[ "$kver" =~ $kver_validator ]] && printf '%s' "$kver"
+ else
+ _detect_generic_kver "$1"
+ fi
}
_lsinitcpio() {
diff --git a/test/cases/functions.bats b/test/cases/functions.bats
index 43e3b9d..c1f4e8b 100644
--- a/test/cases/functions.bats
+++ b/test/cases/functions.bats
@@ -12,28 +12,28 @@ setup() {
}
@test "detect_compression bzip2" {
- local tmp_img
+ local tmp_img=''
tmp_img="$(__gen_test_image 'bzip2')"
run detect_compression "$tmp_img"
assert_output "bzip2"
}
@test "detect_compression cat" {
- local tmp_img
+ local tmp_img=''
tmp_img="$(__gen_test_image 'cat')"
run detect_compression "$tmp_img"
assert_output ""
}
@test "detect_compression gzip" {
- local tmp_img
+ local tmp_img=''
tmp_img="$(__gen_test_image 'gzip')"
run detect_compression "$tmp_img"
assert_output "gzip"
}
@test "detect_compression lz4" {
- local tmp_img
+ local tmp_img=''
tmp_img="$(__gen_test_image 'lz4')"
run detect_compression "$tmp_img"
assert_output --partial "==> ERROR: Newer lz4 stream format detected! This may not boot!"
@@ -41,21 +41,21 @@ setup() {
}
@test "detect_compression lz4 (legacy)" {
- local tmp_img
+ local tmp_img=''
tmp_img="$(__gen_test_image 'lz4' '-l')"
run detect_compression "$tmp_img"
assert_output "lz4 -l"
}
@test "detect_compression lzma" {
- local tmp_img
+ local tmp_img=''
tmp_img="$(__gen_test_image 'lzma')"
run detect_compression "$tmp_img"
assert_output "lzma"
}
@test "detect_compression lzop" {
- local tmp_img
+ local tmp_img=''
__check_binary "lzop"
tmp_img="$(__gen_test_image 'lzop')"
run detect_compression "$tmp_img"
@@ -63,27 +63,83 @@ setup() {
}
@test "detect_compression xz" {
- local tmp_img
+ local tmp_img=''
tmp_img="$(__gen_test_image 'xz' '--check=crc32')"
run detect_compression "$tmp_img"
assert_output "xz"
}
@test "detect_compression zstd" {
- local tmp_img
+ local tmp_img=''
tmp_img="$(__gen_test_image 'zstd' '-T0')"
run detect_compression "$tmp_img"
assert_output "zstd"
}
+@test "detect_compression zimg" {
+ local tmp_img=''
+
+ tmp_img="$(__gen_test_image 'zimg')"
+ run detect_compression "$tmp_img"
+ assert_output "zimg"
+}
+
@test "kver_x86" {
- local kernel_ver tmp_knl
+ local kernel_ver='' tmp_knl=''
kernel_ver="6.0.9-arch1-1 #1 SMP PREEMPT_DYNAMIC Wed, 16 Nov 2022 17:01:17 +0000 x86_64 GNU/Linux"
tmp_knl=$(__gen_test_kernel "$kernel_ver")
run kver_x86 "$tmp_knl"
assert_output "6.0.9-arch1-1"
}
+@test "kver_zimage gzip" {
+ local kernel_ver='' tmp_knl='' tmp_img=''
+ kernel_ver="Linux version 6.1.0-rc5-5 #1 SMP Sat, 17 Dec 2022 05:05:29 +0000 loongarch64 GNU/Linux"
+ tmp_img="$(__gen_test_zboot_kernel "$kernel_ver" 'gzip')"
+ run kver_zimage "$tmp_img"
+ assert_output "6.1.0-rc5-5"
+}
+
+@test "kver_zimage lz4" {
+ local kernel_ver='' tmp_knl='' tmp_img=''
+ kernel_ver="Linux version 6.1.0-rc5-5 #1 SMP Sat, 17 Dec 2022 05:05:29 +0000 loongarch64 GNU/Linux"
+ tmp_img="$(__gen_test_zboot_kernel "$kernel_ver" 'lz4')"
+ run kver_zimage "$tmp_img"
+ assert_output "6.1.0-rc5-5"
+}
+
+@test "kver_zimage lzma" {
+ local kernel_ver='' tmp_knl='' tmp_img=''
+ kernel_ver="Linux version 6.1.0-rc5-5 #1 SMP Sat, 17 Dec 2022 05:05:29 +0000 loongarch64 GNU/Linux"
+ tmp_img="$(__gen_test_zboot_kernel "$kernel_ver" 'lzma')"
+ run kver_zimage "$tmp_img"
+ assert_output "6.1.0-rc5-5"
+}
+
+@test "kver_zimage lzo" {
+ local kernel_ver='' tmp_knl='' tmp_img=''
+ kernel_ver="Linux version 6.1.0-rc5-5 #1 SMP Sat, 17 Dec 2022 05:05:29 +0000 loongarch64 GNU/Linux"
+ tmp_img="$(__gen_test_zboot_kernel "$kernel_ver" 'lzo')"
+ run kver_zimage "$tmp_img"
+ assert_output "6.1.0-rc5-5"
+}
+
+@test "kver_zimage xz" {
+ local kernel_ver='' tmp_knl='' tmp_img=''
+ kernel_ver="Linux version 6.1.0-rc5-5 #1 SMP Sat, 17 Dec 2022 05:05:29 +0000 loongarch64 GNU/Linux"
+ tmp_img="$(__gen_test_zboot_kernel "$kernel_ver" 'xzkern')"
+ run kver_zimage "$tmp_img"
+ assert_output "6.1.0-rc5-5"
+}
+
+@test "kver_zimage zstd" {
+ local kernel_ver='' tmp_knl='' tmp_img=''
+ kernel_ver="Linux version 6.1.0-arch1-2 #1 SMP Sat, 17 Dec 2022 05:05:29 +0000 loongarch64 GNU/Linux"
+ tmp_img="$(__gen_test_zboot_kernel "$kernel_ver" 'zstd22')"
+ run kver_zimage "$tmp_img"
+ assert_output "6.1.0-arch1-2"
+}
+
@test "add_binary script" {
local tmp_bin BUILDROOT="${BATS_RUN_TMPDIR}/buildroot.${BATS_TEST_NAME}/" interpreter="/usr/local/${BATS_TEST_NAME}.${RANDOM}" _optquiet=1
diff --git a/test/helpers/common.bash b/test/helpers/common.bash
index b6b2a22..fc954db 100644
--- a/test/helpers/common.bash
+++ b/test/helpers/common.bash
@@ -9,8 +9,15 @@ __gen_test_image() {
tmp_file="$(mktemp --tmpdir="$BATS_RUN_TMPDIR" tmp_file.XXXXXX)"
trap '{ rm -f -- "$tmp_img"; }' EXIT
trap '{ rm -f -- "$tmp_file"; }' EXIT
- echo "this is a test file" >"$tmp_file"
- bsdtar -cf - "$tmp_file" | "${compress_opts[@]}" >"$tmp_img"
+ echo "this is a test file" > "$tmp_file"
+
+ if [[ ${compress_opts[0]} == "zimg" ]]; then
+ bsdtar -cf - "$tmp_file" > "$tmp_img"
+ # write "zimg" into image at 0x04
+ printf "zimg" | dd of="$tmp_img" seek=$((0x4)) bs=1 count=4 status=none conv=notrunc
+ else
+ bsdtar -cf - "$tmp_file" | "${compress_opts[@]}" > "$tmp_img"
+ fi
rm -f -- "$tmp_file"
echo "$tmp_img"
}
@@ -31,7 +38,73 @@ __gen_test_kernel() {
echo "$tmp_knl"
}
-__check_binary() {
+# Generates a temporary zboot dummy kernel, with the passed string as kernel version and specified compression type
+__gen_test_zboot_kernel() {
+ local kernel_ver="$1"
+ local comp_type="$2"
+ local tmp_img tmp_file
+ local num count size start
+ local compress_opts
+
+ tmp_img="$(mktemp --tmpdir="$BATS_RUN_TMPDIR" tmp_img.XXXXXX)"
+ tmp_file="$(mktemp --tmpdir="$BATS_RUN_TMPDIR" tmp_file.XXXXXX)"
+
+ # generate image with random size between 256 to 1024, 4 bytes align
+ num=$(head -n 10 /dev/urandom | cksum | awk -F ' ' '{print $1}')
+ count=$((num%192*4+512))
+ dd if=/dev/zero of="$tmp_img" count="$count" bs=1 status=none
+
+ trap '{ rm -f -- "$tmp_img"; }' EXIT
+ trap '{ rm -f -- "$tmp_file"; }' EXIT
+
+ case $comp_type in
+ gzip)
+ compress_opts=("gzip")
+ ;;
+ lz4)
+ compress_opts=("lz4")
+ ;;
+ lzma)
+ compress_opts=("lzma")
+ ;;
+ lzo)
+ compress_opts=("lzop")
+ ;;
+ xzkern)
+ compress_opts=("xz" "--check=crc32")
+ ;;
+ zstd22)
+ compress_opts=("zstd" "-T0")
+ ;;
+ *)
+ echo "Compress type is not supported"
+ return 1
+ ;;
+ esac
+
+ echo "$kernel_ver" | "${compress_opts[@]}" > "$tmp_file"
+ cat "$tmp_file" >> "$tmp_img"
+ size=$(stat -c %s "$tmp_file")
+
+ # write "zimg" into image at 0x04
+ printf "zimg" | dd of="$tmp_img" seek="$((0x4))" bs=1 count=4 status=none conv=notrunc
+
+ # write COMP_TYPE string into image at 0x18
+ printf '%s' "$comp_type" | dd of="$tmp_img" seek="$((0x18))" bs=1 count=36 status=none conv=notrunc
+
+ # write compress data start offset at 0x08
+ start=$(printf "%08x" "$count")
+ echo -n -e "\\x${start:6:2}\\x${start:4:2}\\x${start:2:2}\\x${start:0:2}" | dd of="$tmp_img" seek=$((0x8)) bs=1 count=4 status=none conv=notrunc
+
+ # write compress data size offset at 0x0c
+ size=$(printf "%08x\n" "$size")
+ echo -n -e "\\x${size:6:2}\\x${size:4:2}\\x${size:2:2}\\x${size:0:2}" | dd of="$tmp_img" seek=$((0xc)) bs=1 count=4 status=none conv=notrunc
+
+ rm -f -- "$tmp_file"
+ echo "$tmp_img"
+}
+
+__check_binary(){
local binary="$1"
if ! command -v "${binary}" &>/dev/null; then
skip "${binary} not installed"