18c2ecf20Sopenharmony_ci#!/bin/bash 28c2ecf20Sopenharmony_ci# SPDX-License-Identifier: GPL-2.0 38c2ecf20Sopenharmony_ci# Copyright (C) 2018 Joe Lawrence <joe.lawrence@redhat.com> 48c2ecf20Sopenharmony_ci 58c2ecf20Sopenharmony_ci# Shell functions for the rest of the scripts. 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ciMAX_RETRIES=600 88c2ecf20Sopenharmony_ciRETRY_INTERVAL=".1" # seconds 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci# Kselftest framework requirement - SKIP code is 4 118c2ecf20Sopenharmony_ciksft_skip=4 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci# log(msg) - write message to kernel log 148c2ecf20Sopenharmony_ci# msg - insightful words 158c2ecf20Sopenharmony_cifunction log() { 168c2ecf20Sopenharmony_ci echo "$1" > /dev/kmsg 178c2ecf20Sopenharmony_ci} 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci# skip(msg) - testing can't proceed 208c2ecf20Sopenharmony_ci# msg - explanation 218c2ecf20Sopenharmony_cifunction skip() { 228c2ecf20Sopenharmony_ci log "SKIP: $1" 238c2ecf20Sopenharmony_ci echo "SKIP: $1" >&2 248c2ecf20Sopenharmony_ci exit $ksft_skip 258c2ecf20Sopenharmony_ci} 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci# root test 288c2ecf20Sopenharmony_cifunction is_root() { 298c2ecf20Sopenharmony_ci uid=$(id -u) 308c2ecf20Sopenharmony_ci if [ $uid -ne 0 ]; then 318c2ecf20Sopenharmony_ci echo "skip all tests: must be run as root" >&2 328c2ecf20Sopenharmony_ci exit $ksft_skip 338c2ecf20Sopenharmony_ci fi 348c2ecf20Sopenharmony_ci} 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci# die(msg) - game over, man 378c2ecf20Sopenharmony_ci# msg - dying words 388c2ecf20Sopenharmony_cifunction die() { 398c2ecf20Sopenharmony_ci log "ERROR: $1" 408c2ecf20Sopenharmony_ci echo "ERROR: $1" >&2 418c2ecf20Sopenharmony_ci exit 1 428c2ecf20Sopenharmony_ci} 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci# save existing dmesg so we can detect new content 458c2ecf20Sopenharmony_cifunction save_dmesg() { 468c2ecf20Sopenharmony_ci SAVED_DMESG=$(mktemp --tmpdir -t klp-dmesg-XXXXXX) 478c2ecf20Sopenharmony_ci dmesg > "$SAVED_DMESG" 488c2ecf20Sopenharmony_ci} 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci# cleanup temporary dmesg file from save_dmesg() 518c2ecf20Sopenharmony_cifunction cleanup_dmesg_file() { 528c2ecf20Sopenharmony_ci rm -f "$SAVED_DMESG" 538c2ecf20Sopenharmony_ci} 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cifunction push_config() { 568c2ecf20Sopenharmony_ci DYNAMIC_DEBUG=$(grep '^kernel/livepatch' /sys/kernel/debug/dynamic_debug/control | \ 578c2ecf20Sopenharmony_ci awk -F'[: ]' '{print "file " $1 " line " $2 " " $4}') 588c2ecf20Sopenharmony_ci FTRACE_ENABLED=$(sysctl --values kernel.ftrace_enabled) 598c2ecf20Sopenharmony_ci} 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cifunction pop_config() { 628c2ecf20Sopenharmony_ci if [[ -n "$DYNAMIC_DEBUG" ]]; then 638c2ecf20Sopenharmony_ci echo -n "$DYNAMIC_DEBUG" > /sys/kernel/debug/dynamic_debug/control 648c2ecf20Sopenharmony_ci fi 658c2ecf20Sopenharmony_ci if [[ -n "$FTRACE_ENABLED" ]]; then 668c2ecf20Sopenharmony_ci sysctl kernel.ftrace_enabled="$FTRACE_ENABLED" &> /dev/null 678c2ecf20Sopenharmony_ci fi 688c2ecf20Sopenharmony_ci} 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cifunction set_dynamic_debug() { 718c2ecf20Sopenharmony_ci cat <<-EOF > /sys/kernel/debug/dynamic_debug/control 728c2ecf20Sopenharmony_ci file kernel/livepatch/* +p 738c2ecf20Sopenharmony_ci func klp_try_switch_task -p 748c2ecf20Sopenharmony_ci EOF 758c2ecf20Sopenharmony_ci} 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_cifunction set_ftrace_enabled() { 788c2ecf20Sopenharmony_ci result=$(sysctl -q kernel.ftrace_enabled="$1" 2>&1 && \ 798c2ecf20Sopenharmony_ci sysctl kernel.ftrace_enabled 2>&1) 808c2ecf20Sopenharmony_ci echo "livepatch: $result" > /dev/kmsg 818c2ecf20Sopenharmony_ci} 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cifunction cleanup() { 848c2ecf20Sopenharmony_ci pop_config 858c2ecf20Sopenharmony_ci cleanup_dmesg_file 868c2ecf20Sopenharmony_ci} 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci# setup_config - save the current config and set a script exit trap that 898c2ecf20Sopenharmony_ci# restores the original config. Setup the dynamic debug 908c2ecf20Sopenharmony_ci# for verbose livepatching output and turn on 918c2ecf20Sopenharmony_ci# the ftrace_enabled sysctl. 928c2ecf20Sopenharmony_cifunction setup_config() { 938c2ecf20Sopenharmony_ci is_root 948c2ecf20Sopenharmony_ci push_config 958c2ecf20Sopenharmony_ci set_dynamic_debug 968c2ecf20Sopenharmony_ci set_ftrace_enabled 1 978c2ecf20Sopenharmony_ci trap cleanup EXIT INT TERM HUP 988c2ecf20Sopenharmony_ci} 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci# loop_until(cmd) - loop a command until it is successful or $MAX_RETRIES, 1018c2ecf20Sopenharmony_ci# sleep $RETRY_INTERVAL between attempts 1028c2ecf20Sopenharmony_ci# cmd - command and its arguments to run 1038c2ecf20Sopenharmony_cifunction loop_until() { 1048c2ecf20Sopenharmony_ci local cmd="$*" 1058c2ecf20Sopenharmony_ci local i=0 1068c2ecf20Sopenharmony_ci while true; do 1078c2ecf20Sopenharmony_ci eval "$cmd" && return 0 1088c2ecf20Sopenharmony_ci [[ $((i++)) -eq $MAX_RETRIES ]] && return 1 1098c2ecf20Sopenharmony_ci sleep $RETRY_INTERVAL 1108c2ecf20Sopenharmony_ci done 1118c2ecf20Sopenharmony_ci} 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_cifunction assert_mod() { 1148c2ecf20Sopenharmony_ci local mod="$1" 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci modprobe --dry-run "$mod" &>/dev/null 1178c2ecf20Sopenharmony_ci} 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_cifunction is_livepatch_mod() { 1208c2ecf20Sopenharmony_ci local mod="$1" 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci if [[ $(modinfo "$mod" | awk '/^livepatch:/{print $NF}') == "Y" ]]; then 1238c2ecf20Sopenharmony_ci return 0 1248c2ecf20Sopenharmony_ci fi 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci return 1 1278c2ecf20Sopenharmony_ci} 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_cifunction __load_mod() { 1308c2ecf20Sopenharmony_ci local mod="$1"; shift 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci local msg="% modprobe $mod $*" 1338c2ecf20Sopenharmony_ci log "${msg%% }" 1348c2ecf20Sopenharmony_ci ret=$(modprobe "$mod" "$@" 2>&1) 1358c2ecf20Sopenharmony_ci if [[ "$ret" != "" ]]; then 1368c2ecf20Sopenharmony_ci die "$ret" 1378c2ecf20Sopenharmony_ci fi 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci # Wait for module in sysfs ... 1408c2ecf20Sopenharmony_ci loop_until '[[ -e "/sys/module/$mod" ]]' || 1418c2ecf20Sopenharmony_ci die "failed to load module $mod" 1428c2ecf20Sopenharmony_ci} 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci# load_mod(modname, params) - load a kernel module 1468c2ecf20Sopenharmony_ci# modname - module name to load 1478c2ecf20Sopenharmony_ci# params - module parameters to pass to modprobe 1488c2ecf20Sopenharmony_cifunction load_mod() { 1498c2ecf20Sopenharmony_ci local mod="$1"; shift 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci assert_mod "$mod" || 1528c2ecf20Sopenharmony_ci skip "unable to load module ${mod}, verify CONFIG_TEST_LIVEPATCH=m and run self-tests as root" 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci is_livepatch_mod "$mod" && 1558c2ecf20Sopenharmony_ci die "use load_lp() to load the livepatch module $mod" 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci __load_mod "$mod" "$@" 1588c2ecf20Sopenharmony_ci} 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci# load_lp_nowait(modname, params) - load a kernel module with a livepatch 1618c2ecf20Sopenharmony_ci# but do not wait on until the transition finishes 1628c2ecf20Sopenharmony_ci# modname - module name to load 1638c2ecf20Sopenharmony_ci# params - module parameters to pass to modprobe 1648c2ecf20Sopenharmony_cifunction load_lp_nowait() { 1658c2ecf20Sopenharmony_ci local mod="$1"; shift 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci assert_mod "$mod" || 1688c2ecf20Sopenharmony_ci skip "unable to load module ${mod}, verify CONFIG_TEST_LIVEPATCH=m and run self-tests as root" 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci is_livepatch_mod "$mod" || 1718c2ecf20Sopenharmony_ci die "module $mod is not a livepatch" 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci __load_mod "$mod" "$@" 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci # Wait for livepatch in sysfs ... 1768c2ecf20Sopenharmony_ci loop_until '[[ -e "/sys/kernel/livepatch/$mod" ]]' || 1778c2ecf20Sopenharmony_ci die "failed to load module $mod (sysfs)" 1788c2ecf20Sopenharmony_ci} 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci# load_lp(modname, params) - load a kernel module with a livepatch 1818c2ecf20Sopenharmony_ci# modname - module name to load 1828c2ecf20Sopenharmony_ci# params - module parameters to pass to modprobe 1838c2ecf20Sopenharmony_cifunction load_lp() { 1848c2ecf20Sopenharmony_ci local mod="$1"; shift 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci load_lp_nowait "$mod" "$@" 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci # Wait until the transition finishes ... 1898c2ecf20Sopenharmony_ci loop_until 'grep -q '^0$' /sys/kernel/livepatch/$mod/transition' || 1908c2ecf20Sopenharmony_ci die "failed to complete transition" 1918c2ecf20Sopenharmony_ci} 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci# load_failing_mod(modname, params) - load a kernel module, expect to fail 1948c2ecf20Sopenharmony_ci# modname - module name to load 1958c2ecf20Sopenharmony_ci# params - module parameters to pass to modprobe 1968c2ecf20Sopenharmony_cifunction load_failing_mod() { 1978c2ecf20Sopenharmony_ci local mod="$1"; shift 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci local msg="% modprobe $mod $*" 2008c2ecf20Sopenharmony_ci log "${msg%% }" 2018c2ecf20Sopenharmony_ci ret=$(modprobe "$mod" "$@" 2>&1) 2028c2ecf20Sopenharmony_ci if [[ "$ret" == "" ]]; then 2038c2ecf20Sopenharmony_ci die "$mod unexpectedly loaded" 2048c2ecf20Sopenharmony_ci fi 2058c2ecf20Sopenharmony_ci log "$ret" 2068c2ecf20Sopenharmony_ci} 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci# unload_mod(modname) - unload a kernel module 2098c2ecf20Sopenharmony_ci# modname - module name to unload 2108c2ecf20Sopenharmony_cifunction unload_mod() { 2118c2ecf20Sopenharmony_ci local mod="$1" 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci # Wait for module reference count to clear ... 2148c2ecf20Sopenharmony_ci loop_until '[[ $(cat "/sys/module/$mod/refcnt") == "0" ]]' || 2158c2ecf20Sopenharmony_ci die "failed to unload module $mod (refcnt)" 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci log "% rmmod $mod" 2188c2ecf20Sopenharmony_ci ret=$(rmmod "$mod" 2>&1) 2198c2ecf20Sopenharmony_ci if [[ "$ret" != "" ]]; then 2208c2ecf20Sopenharmony_ci die "$ret" 2218c2ecf20Sopenharmony_ci fi 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci # Wait for module in sysfs ... 2248c2ecf20Sopenharmony_ci loop_until '[[ ! -e "/sys/module/$mod" ]]' || 2258c2ecf20Sopenharmony_ci die "failed to unload module $mod (/sys/module)" 2268c2ecf20Sopenharmony_ci} 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci# unload_lp(modname) - unload a kernel module with a livepatch 2298c2ecf20Sopenharmony_ci# modname - module name to unload 2308c2ecf20Sopenharmony_cifunction unload_lp() { 2318c2ecf20Sopenharmony_ci unload_mod "$1" 2328c2ecf20Sopenharmony_ci} 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci# disable_lp(modname) - disable a livepatch 2358c2ecf20Sopenharmony_ci# modname - module name to unload 2368c2ecf20Sopenharmony_cifunction disable_lp() { 2378c2ecf20Sopenharmony_ci local mod="$1" 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci log "% echo 0 > /sys/kernel/livepatch/$mod/enabled" 2408c2ecf20Sopenharmony_ci echo 0 > /sys/kernel/livepatch/"$mod"/enabled 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci # Wait until the transition finishes and the livepatch gets 2438c2ecf20Sopenharmony_ci # removed from sysfs... 2448c2ecf20Sopenharmony_ci loop_until '[[ ! -e "/sys/kernel/livepatch/$mod" ]]' || 2458c2ecf20Sopenharmony_ci die "failed to disable livepatch $mod" 2468c2ecf20Sopenharmony_ci} 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci# set_pre_patch_ret(modname, pre_patch_ret) 2498c2ecf20Sopenharmony_ci# modname - module name to set 2508c2ecf20Sopenharmony_ci# pre_patch_ret - new pre_patch_ret value 2518c2ecf20Sopenharmony_cifunction set_pre_patch_ret { 2528c2ecf20Sopenharmony_ci local mod="$1"; shift 2538c2ecf20Sopenharmony_ci local ret="$1" 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci log "% echo $ret > /sys/module/$mod/parameters/pre_patch_ret" 2568c2ecf20Sopenharmony_ci echo "$ret" > /sys/module/"$mod"/parameters/pre_patch_ret 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci # Wait for sysfs value to hold ... 2598c2ecf20Sopenharmony_ci loop_until '[[ $(cat "/sys/module/$mod/parameters/pre_patch_ret") == "$ret" ]]' || 2608c2ecf20Sopenharmony_ci die "failed to set pre_patch_ret parameter for $mod module" 2618c2ecf20Sopenharmony_ci} 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_cifunction start_test { 2648c2ecf20Sopenharmony_ci local test="$1" 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci save_dmesg 2678c2ecf20Sopenharmony_ci echo -n "TEST: $test ... " 2688c2ecf20Sopenharmony_ci log "===== TEST: $test =====" 2698c2ecf20Sopenharmony_ci} 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci# check_result() - verify dmesg output 2728c2ecf20Sopenharmony_ci# TODO - better filter, out of order msgs, etc? 2738c2ecf20Sopenharmony_cifunction check_result { 2748c2ecf20Sopenharmony_ci local expect="$*" 2758c2ecf20Sopenharmony_ci local result 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci # Note: when comparing dmesg output, the kernel log timestamps 2788c2ecf20Sopenharmony_ci # help differentiate repeated testing runs. Remove them with a 2798c2ecf20Sopenharmony_ci # post-comparison sed filter. 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci result=$(dmesg | comm --nocheck-order -13 "$SAVED_DMESG" - | \ 2828c2ecf20Sopenharmony_ci grep -e 'livepatch:' -e 'test_klp' | \ 2838c2ecf20Sopenharmony_ci grep -v '\(tainting\|taints\) kernel' | \ 2848c2ecf20Sopenharmony_ci sed 's/^\[[ 0-9.]*\] //') 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci if [[ "$expect" == "$result" ]] ; then 2878c2ecf20Sopenharmony_ci echo "ok" 2888c2ecf20Sopenharmony_ci else 2898c2ecf20Sopenharmony_ci echo -e "not ok\n\n$(diff -upr --label expected --label result <(echo "$expect") <(echo "$result"))\n" 2908c2ecf20Sopenharmony_ci die "livepatch kselftest(s) failed" 2918c2ecf20Sopenharmony_ci fi 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci cleanup_dmesg_file 2948c2ecf20Sopenharmony_ci} 295