#!/usr/bin/env bash
set -o nounset
set -o errexit
set -o errtrace
ktest_dir=$(dirname "$(readlink -f "$0")")
KTEST=$ktest_dir/ktest
. "$ktest_dir/lib/libktest.sh"
checkdep gcc
checkdep clang
checkdep make
checkdep bison
checkdep flex
checkdep bc
ktest_njobs=$(nproc)
ktest_precise=false
ktest_compiler="${CC:-gcc}"
ktest_skip_kernel_config=false
COVERAGE="" # doing code coverage?
MAKEARGS=()
DEPMOD=depmod
if ! which depmod > /dev/null; then
DEPMOD=/sbin/depmod
fi
usage()
{
echo "build-test-kernel: Run generic virtual machine tests"
echo "Usage: build-test-kernel cmd [options]"
ktest_usage_cmds
echo " oldconfig Run make oldconfig"
echo " config Run make nconfig"
echo
echo " options:"
ktest_usage_opts
echo
echo " options for build-test-kernel run:"
ktest_usage_run_opts
echo " -k
kernel source dir"
echo " -c enable coverage for this dir (only valid without -K)"
echo " -M extra arguments to be passed to make when building the kernel"
echo " -K keep existing kernel .config"
echo
ktest_usage_post
}
if [[ $# = 0 ]]; then
usage
exit 1
fi
#parse command and shift for rest of arg parsing
CMD="$1"
shift
while getopts "k:Pc:M:Kh${ktest_args}" arg; do
case $arg in
k)
ktest_kernel_source="$OPTARG"
;;
P)
ktest_precise=true
;;
c)
if [[ ! -d $OPTARG ]]; then
echo "$OPTARG must be a directory"
exit 1
fi
checkdep lcov
# Strip trailing / from directory name, substitute _ for /
OPTARG=$(echo "${OPTARG%/}"|tr / _)
MAKEARGS+=("GCOV_PROFILE_$OPTARG=y")
COVERAGE=1
;;
M)
MAKEARGS+=("$OPTARG")
;;
K)
ktest_skip_kernel_config=true
;;
h)
usage
exit 0
;;
esac
parse_ktest_arg $arg
done
shift $(( OPTIND - 1 ))
parse_args_post
# default parameters
[[ -z $ktest_kernel_source ]] && ktest_kernel_source="."
if [[ ! -d $ktest_kernel_source ]]; then
echo "kernel source directory $ktest_kernel_source does not exist"
exit 1
fi
ktest_kernel_source=$(readlink -e "$ktest_kernel_source")
ktest_kernel_build="$ktest_out/kernel_build.$ktest_arch"
mkdir -p "$ktest_kernel_build"
if [[ -n $CROSS_COMPILE ]]; then
checkdep "$ARCH_TRIPLE-gcc" "gcc-$ARCH_TRIPLE"
fi
run_ktest()
{
arg=$1
shift
"$KTEST" "$arg" $KTESTARGS "$@"
}
map_clang_version() {
case "$ktest_compiler" in
clang) echo "1" ;;
clang-[0-9]*) echo "-${ktest_compiler#clang-}" ;;
*) echo "" ;;
esac
}
do_make()
{
if [[ -n $CROSS_COMPILE ]]; then
export ARCH="$KERNEL_ARCH"
export CROSS_COMPILE="$ARCH_TRIPLE-"
fi
make --jobs="$ktest_njobs" \
--directory="$ktest_kernel_source" \
CC="$ktest_compiler" \
LLVM="$(map_clang_version)" \
O="$ktest_kernel_build" \
INSTALL_MOD_PATH="$ktest_kernel_binary" \
"${ktest_kernel_make_append[@]}" \
"${MAKEARGS[@]}" \
"$@"
}
new_config()
{
local kconfig="$ktest_kernel_build/.config"
local config_tool="$ktest_kernel_source/scripts/config"
if [[ ! -f $kconfig ]]; then
do_make allnoconfig
# Really undefine everything:
sed -i -e 's/\(CONFIG_.*\)=.*/# \1 is not set/' "$kconfig"
fi
}
kernel_opt()
{
local cmd=$1
local opt=$2
local kconfig="$ktest_kernel_build/.config"
local config_tool="$ktest_kernel_source/scripts/config"
local val=y
local ret=0
if [[ $opt =~ = ]]; then
local val=${opt#*=}
opt="${opt%=*}"
fi
case $cmd in
set)
"$config_tool" --file "$kconfig" --set-val "$opt" "$val"
;;
check)
local c=$("$config_tool" --file "$kconfig" -s "$opt")
[[ $c = undef ]] && c=n
if [[ $c != $val ]]; then
echo "Kernel config option $opt is $c; should be $val"
# If the current kernel doesn't have the option available,
# don't fail
if [[ $(cd $ktest_kernel_source; git grep "CONFIG_$opt") ]]; then
ret=1
fi
fi
;;
esac
if [[ $ret != 0 ]]; then
return $ret
fi
}
configure_kernel()
{
local kconfig="$ktest_kernel_build/.config"
if [[ -f "$kconfig" ]]; then
cp "$kconfig" "$kconfig".bak
fi
if [[ -z $ktest_kconfig_base ]]; then
if $ktest_precise; then
rm -f "$kconfig"
fi
new_config
else
cp "$ktest_kconfig_base" "$kconfig"
fi
log_verbose "kernel_config_require: ${ktest_kernel_config_require[@]} ${ktest_kernel_config_require_soft[@]}"
MAKEARGS+=("LOCALVERSION=-ktest")
for opt in "${ktest_kernel_config_require[@]}"; do
[[ -n $opt ]] && kernel_opt set "$opt"
done
for opt in "${ktest_kernel_config_require_soft[@]}"; do
[[ -n $opt ]] && kernel_opt set "$opt"
done
do_make olddefconfig
for opt in "${ktest_kernel_config_require[@]}"; do
[[ -n $opt ]] && kernel_opt check "$opt"
done
# Preserve timestamp if config didn't change:
if [[ -f "$kconfig".bak ]] && diff -q "$kconfig" "$kconfig".bak; then
mv "$kconfig".bak "$kconfig"
fi
}
build_kernel()
{
rm -rf "$ktest_kernel_binary"
mkdir -p "$ktest_kernel_binary"
if ! $ktest_skip_kernel_config; then
configure_kernel
fi
case $KERNEL_ARCH in
mips)
do_make -k vmlinuz
;;
*)
do_make -k
;;
esac
local BOOT=$ktest_kernel_build/arch/$KERNEL_ARCH/boot
case $ktest_arch in
x86*)
install -m0644 "$BOOT/bzImage" "$ktest_kernel_binary/vmlinuz"
;;
aarch64)
install -m0644 "$BOOT/Image" "$ktest_kernel_binary/vmlinuz"
;;
mips)
install -m0644 "$BOOT/vmlinux.strip" "$ktest_kernel_binary/vmlinuz"
#install -m0644 "$ktest_kernel_build/vmlinux" "$ktest_kernel_binary/vmlinuz"
;;
default)
echo "Don't know how to install kernel"
exit 1
;;
esac
install -m0644 "$ktest_kernel_build/vmlinux" "$ktest_kernel_binary/vmlinux"
install -m0644 "$ktest_kernel_build/.config" "$ktest_kernel_binary/config"
# if there weren't actually any modules selected, make modules_install gets
# confused:
touch "$ktest_kernel_build/modules.order"
touch "$ktest_kernel_build/modules.builtin"
do_make modules_install
local kernel_version=$(cat "$ktest_kernel_build/include/config/kernel.release")
$DEPMOD -b "$ktest_kernel_binary/" -v $kernel_version
}
cmd_run()
{
if [[ $# = 0 ]]; then
echo "build-test-kernel: missing test"
usage
exit 1
fi
ktest_test=$(realpath "$1")
shift
ktest_testargs="$@"
echo Running test $(basename "$ktest_test") on $(uname -n) at $(pwd)
parse_test_deps "$ktest_test"
if [[ -n $COVERAGE ]]; then
ktest_kernel_config_require+=(GCOV_KERNEL)
fi
run_quiet "building kernel" build_kernel
start_vm
}
cmd_boot()
{
cmd_run "$ktest_dir/boot.ktest"
}
cmd_oldconfig()
{
new_config
do_make oldconfig
}
cmd_config()
{
new_config
do_make nconfig "$@"
}
cmd_faddr2line()
{
./scripts/faddr2line "$ktest_kernel_build/vmlinux" $@
}
cmd_help()
{
usage
}
if [[ $(type -t "cmd_$CMD") == function ]]; then
CMD="cmd_$CMD"
elif [[ $(type -t "ktest_$CMD") == function ]]; then
CMD="ktest_$CMD"
else
usage
exit 1
fi
$CMD "$@"