1a2818ee4SJoe Lawrence#!/bin/bash 2a2818ee4SJoe Lawrence# SPDX-License-Identifier: GPL-2.0 3a2818ee4SJoe Lawrence# Copyright (C) 2018 Joe Lawrence <joe.lawrence@redhat.com> 4a2818ee4SJoe Lawrence 5a2818ee4SJoe Lawrence# Shell functions for the rest of the scripts. 6a2818ee4SJoe Lawrence 7a2818ee4SJoe LawrenceMAX_RETRIES=600 8a2818ee4SJoe LawrenceRETRY_INTERVAL=".1" # seconds 9*ff1b80ecSSong LiuKLP_SYSFS_DIR="/sys/kernel/livepatch" 10a2818ee4SJoe Lawrence 1105564c29SShuah Khan# Kselftest framework requirement - SKIP code is 4 1205564c29SShuah Khanksft_skip=4 1305564c29SShuah Khan 14a2818ee4SJoe Lawrence# log(msg) - write message to kernel log 15a2818ee4SJoe Lawrence# msg - insightful words 16a2818ee4SJoe Lawrencefunction log() { 17a2818ee4SJoe Lawrence echo "$1" > /dev/kmsg 18a2818ee4SJoe Lawrence} 19a2818ee4SJoe Lawrence 20527d37e9SJoe Lawrence# skip(msg) - testing can't proceed 21527d37e9SJoe Lawrence# msg - explanation 22527d37e9SJoe Lawrencefunction skip() { 23527d37e9SJoe Lawrence log "SKIP: $1" 24527d37e9SJoe Lawrence echo "SKIP: $1" >&2 2505564c29SShuah Khan exit $ksft_skip 2605564c29SShuah Khan} 2705564c29SShuah Khan 2805564c29SShuah Khan# root test 2905564c29SShuah Khanfunction is_root() { 3005564c29SShuah Khan uid=$(id -u) 3105564c29SShuah Khan if [ $uid -ne 0 ]; then 3205564c29SShuah Khan echo "skip all tests: must be run as root" >&2 3305564c29SShuah Khan exit $ksft_skip 3405564c29SShuah Khan fi 35527d37e9SJoe Lawrence} 36527d37e9SJoe Lawrence 37a2818ee4SJoe Lawrence# die(msg) - game over, man 38a2818ee4SJoe Lawrence# msg - dying words 39a2818ee4SJoe Lawrencefunction die() { 40a2818ee4SJoe Lawrence log "ERROR: $1" 41a2818ee4SJoe Lawrence echo "ERROR: $1" >&2 42a2818ee4SJoe Lawrence exit 1 43a2818ee4SJoe Lawrence} 44a2818ee4SJoe Lawrence 452eeb0d45SJoe Lawrence# save existing dmesg so we can detect new content 462eeb0d45SJoe Lawrencefunction save_dmesg() { 472eeb0d45SJoe Lawrence SAVED_DMESG=$(mktemp --tmpdir -t klp-dmesg-XXXXXX) 482eeb0d45SJoe Lawrence dmesg > "$SAVED_DMESG" 492eeb0d45SJoe Lawrence} 502eeb0d45SJoe Lawrence 512eeb0d45SJoe Lawrence# cleanup temporary dmesg file from save_dmesg() 522eeb0d45SJoe Lawrencefunction cleanup_dmesg_file() { 532eeb0d45SJoe Lawrence rm -f "$SAVED_DMESG" 542eeb0d45SJoe Lawrence} 552eeb0d45SJoe Lawrence 5635c9e74cSJoe Lawrencefunction push_config() { 57fbb01c52SJoe Lawrence DYNAMIC_DEBUG=$(grep '^kernel/livepatch' /sys/kernel/debug/dynamic_debug/control | \ 58fbb01c52SJoe Lawrence awk -F'[: ]' '{print "file " $1 " line " $2 " " $4}') 598c666d2aSJoe Lawrence FTRACE_ENABLED=$(sysctl --values kernel.ftrace_enabled) 60fbb01c52SJoe Lawrence} 61fbb01c52SJoe Lawrence 6235c9e74cSJoe Lawrencefunction pop_config() { 63fbb01c52SJoe Lawrence if [[ -n "$DYNAMIC_DEBUG" ]]; then 64fbb01c52SJoe Lawrence echo -n "$DYNAMIC_DEBUG" > /sys/kernel/debug/dynamic_debug/control 65fbb01c52SJoe Lawrence fi 668c666d2aSJoe Lawrence if [[ -n "$FTRACE_ENABLED" ]]; then 678c666d2aSJoe Lawrence sysctl kernel.ftrace_enabled="$FTRACE_ENABLED" &> /dev/null 688c666d2aSJoe Lawrence fi 69fbb01c52SJoe Lawrence} 70fbb01c52SJoe Lawrence 71a2818ee4SJoe Lawrencefunction set_dynamic_debug() { 72fbb01c52SJoe Lawrence cat <<-EOF > /sys/kernel/debug/dynamic_debug/control 73a2818ee4SJoe Lawrence file kernel/livepatch/* +p 74a2818ee4SJoe Lawrence func klp_try_switch_task -p 75a2818ee4SJoe Lawrence EOF 76a2818ee4SJoe Lawrence} 77a2818ee4SJoe Lawrence 788c666d2aSJoe Lawrencefunction set_ftrace_enabled() { 794327b9eaSDavid Vernet local can_fail=0 804327b9eaSDavid Vernet if [[ "$1" == "--fail" ]] ; then 814327b9eaSDavid Vernet can_fail=1 824327b9eaSDavid Vernet shift 834327b9eaSDavid Vernet fi 844327b9eaSDavid Vernet 854327b9eaSDavid Vernet local err=$(sysctl -q kernel.ftrace_enabled="$1" 2>&1) 864327b9eaSDavid Vernet local result=$(sysctl --values kernel.ftrace_enabled) 874327b9eaSDavid Vernet 884327b9eaSDavid Vernet if [[ "$result" != "$1" ]] ; then 894327b9eaSDavid Vernet if [[ $can_fail -eq 1 ]] ; then 90857300b7SJoe Lawrence echo "livepatch: $err" | sed 's#/proc/sys/kernel/#kernel.#' > /dev/kmsg 914327b9eaSDavid Vernet return 924327b9eaSDavid Vernet fi 934327b9eaSDavid Vernet 944327b9eaSDavid Vernet skip "failed to set kernel.ftrace_enabled = $1" 954327b9eaSDavid Vernet fi 964327b9eaSDavid Vernet 974327b9eaSDavid Vernet echo "livepatch: kernel.ftrace_enabled = $result" > /dev/kmsg 988c666d2aSJoe Lawrence} 998c666d2aSJoe Lawrence 1002eeb0d45SJoe Lawrencefunction cleanup() { 1012eeb0d45SJoe Lawrence pop_config 1022eeb0d45SJoe Lawrence cleanup_dmesg_file 1032eeb0d45SJoe Lawrence} 1042eeb0d45SJoe Lawrence 10535c9e74cSJoe Lawrence# setup_config - save the current config and set a script exit trap that 10635c9e74cSJoe Lawrence# restores the original config. Setup the dynamic debug 1078c666d2aSJoe Lawrence# for verbose livepatching output and turn on 1088c666d2aSJoe Lawrence# the ftrace_enabled sysctl. 10935c9e74cSJoe Lawrencefunction setup_config() { 11005564c29SShuah Khan is_root 11135c9e74cSJoe Lawrence push_config 11235c9e74cSJoe Lawrence set_dynamic_debug 1138c666d2aSJoe Lawrence set_ftrace_enabled 1 1142eeb0d45SJoe Lawrence trap cleanup EXIT INT TERM HUP 11535c9e74cSJoe Lawrence} 11635c9e74cSJoe Lawrence 117a2818ee4SJoe Lawrence# loop_until(cmd) - loop a command until it is successful or $MAX_RETRIES, 118a2818ee4SJoe Lawrence# sleep $RETRY_INTERVAL between attempts 119a2818ee4SJoe Lawrence# cmd - command and its arguments to run 120a2818ee4SJoe Lawrencefunction loop_until() { 121a2818ee4SJoe Lawrence local cmd="$*" 122a2818ee4SJoe Lawrence local i=0 123a2818ee4SJoe Lawrence while true; do 124a2818ee4SJoe Lawrence eval "$cmd" && return 0 125a2818ee4SJoe Lawrence [[ $((i++)) -eq $MAX_RETRIES ]] && return 1 126a2818ee4SJoe Lawrence sleep $RETRY_INTERVAL 127a2818ee4SJoe Lawrence done 128a2818ee4SJoe Lawrence} 129a2818ee4SJoe Lawrence 130527d37e9SJoe Lawrencefunction assert_mod() { 131527d37e9SJoe Lawrence local mod="$1" 132527d37e9SJoe Lawrence 133527d37e9SJoe Lawrence modprobe --dry-run "$mod" &>/dev/null 134527d37e9SJoe Lawrence} 135527d37e9SJoe Lawrence 136a2818ee4SJoe Lawrencefunction is_livepatch_mod() { 137a2818ee4SJoe Lawrence local mod="$1" 138a2818ee4SJoe Lawrence 139a2818ee4SJoe Lawrence if [[ $(modinfo "$mod" | awk '/^livepatch:/{print $NF}') == "Y" ]]; then 140a2818ee4SJoe Lawrence return 0 141a2818ee4SJoe Lawrence fi 142a2818ee4SJoe Lawrence 143a2818ee4SJoe Lawrence return 1 144a2818ee4SJoe Lawrence} 145a2818ee4SJoe Lawrence 146a2818ee4SJoe Lawrencefunction __load_mod() { 147a2818ee4SJoe Lawrence local mod="$1"; shift 148a2818ee4SJoe Lawrence 149fbb76d57SJoe Lawrence local msg="% modprobe $mod $*" 150a2818ee4SJoe Lawrence log "${msg%% }" 151fbb76d57SJoe Lawrence ret=$(modprobe "$mod" "$@" 2>&1) 152a2818ee4SJoe Lawrence if [[ "$ret" != "" ]]; then 153a2818ee4SJoe Lawrence die "$ret" 154a2818ee4SJoe Lawrence fi 155a2818ee4SJoe Lawrence 156a2818ee4SJoe Lawrence # Wait for module in sysfs ... 157a2818ee4SJoe Lawrence loop_until '[[ -e "/sys/module/$mod" ]]' || 158a2818ee4SJoe Lawrence die "failed to load module $mod" 159a2818ee4SJoe Lawrence} 160a2818ee4SJoe Lawrence 161a2818ee4SJoe Lawrence 162a2818ee4SJoe Lawrence# load_mod(modname, params) - load a kernel module 163a2818ee4SJoe Lawrence# modname - module name to load 164a2818ee4SJoe Lawrence# params - module parameters to pass to modprobe 165a2818ee4SJoe Lawrencefunction load_mod() { 166a2818ee4SJoe Lawrence local mod="$1"; shift 167a2818ee4SJoe Lawrence 168527d37e9SJoe Lawrence assert_mod "$mod" || 169527d37e9SJoe Lawrence skip "unable to load module ${mod}, verify CONFIG_TEST_LIVEPATCH=m and run self-tests as root" 170527d37e9SJoe Lawrence 171a2818ee4SJoe Lawrence is_livepatch_mod "$mod" && 172a2818ee4SJoe Lawrence die "use load_lp() to load the livepatch module $mod" 173a2818ee4SJoe Lawrence 174fbb76d57SJoe Lawrence __load_mod "$mod" "$@" 175a2818ee4SJoe Lawrence} 176a2818ee4SJoe Lawrence 177a2818ee4SJoe Lawrence# load_lp_nowait(modname, params) - load a kernel module with a livepatch 178a2818ee4SJoe Lawrence# but do not wait on until the transition finishes 179a2818ee4SJoe Lawrence# modname - module name to load 180a2818ee4SJoe Lawrence# params - module parameters to pass to modprobe 181a2818ee4SJoe Lawrencefunction load_lp_nowait() { 182a2818ee4SJoe Lawrence local mod="$1"; shift 183a2818ee4SJoe Lawrence 184527d37e9SJoe Lawrence assert_mod "$mod" || 185527d37e9SJoe Lawrence skip "unable to load module ${mod}, verify CONFIG_TEST_LIVEPATCH=m and run self-tests as root" 186527d37e9SJoe Lawrence 187a2818ee4SJoe Lawrence is_livepatch_mod "$mod" || 188a2818ee4SJoe Lawrence die "module $mod is not a livepatch" 189a2818ee4SJoe Lawrence 190fbb76d57SJoe Lawrence __load_mod "$mod" "$@" 191a2818ee4SJoe Lawrence 192a2818ee4SJoe Lawrence # Wait for livepatch in sysfs ... 193a2818ee4SJoe Lawrence loop_until '[[ -e "/sys/kernel/livepatch/$mod" ]]' || 194a2818ee4SJoe Lawrence die "failed to load module $mod (sysfs)" 195a2818ee4SJoe Lawrence} 196a2818ee4SJoe Lawrence 197a2818ee4SJoe Lawrence# load_lp(modname, params) - load a kernel module with a livepatch 198a2818ee4SJoe Lawrence# modname - module name to load 199a2818ee4SJoe Lawrence# params - module parameters to pass to modprobe 200a2818ee4SJoe Lawrencefunction load_lp() { 201a2818ee4SJoe Lawrence local mod="$1"; shift 202a2818ee4SJoe Lawrence 203fbb76d57SJoe Lawrence load_lp_nowait "$mod" "$@" 204a2818ee4SJoe Lawrence 205a2818ee4SJoe Lawrence # Wait until the transition finishes ... 206a2818ee4SJoe Lawrence loop_until 'grep -q '^0$' /sys/kernel/livepatch/$mod/transition' || 207a2818ee4SJoe Lawrence die "failed to complete transition" 208a2818ee4SJoe Lawrence} 209a2818ee4SJoe Lawrence 210a2818ee4SJoe Lawrence# load_failing_mod(modname, params) - load a kernel module, expect to fail 211a2818ee4SJoe Lawrence# modname - module name to load 212a2818ee4SJoe Lawrence# params - module parameters to pass to modprobe 213a2818ee4SJoe Lawrencefunction load_failing_mod() { 214a2818ee4SJoe Lawrence local mod="$1"; shift 215a2818ee4SJoe Lawrence 216fbb76d57SJoe Lawrence local msg="% modprobe $mod $*" 217a2818ee4SJoe Lawrence log "${msg%% }" 218fbb76d57SJoe Lawrence ret=$(modprobe "$mod" "$@" 2>&1) 219a2818ee4SJoe Lawrence if [[ "$ret" == "" ]]; then 220a2818ee4SJoe Lawrence die "$mod unexpectedly loaded" 221a2818ee4SJoe Lawrence fi 222a2818ee4SJoe Lawrence log "$ret" 223a2818ee4SJoe Lawrence} 224a2818ee4SJoe Lawrence 225a2818ee4SJoe Lawrence# unload_mod(modname) - unload a kernel module 226a2818ee4SJoe Lawrence# modname - module name to unload 227a2818ee4SJoe Lawrencefunction unload_mod() { 228a2818ee4SJoe Lawrence local mod="$1" 229a2818ee4SJoe Lawrence 230a2818ee4SJoe Lawrence # Wait for module reference count to clear ... 231a2818ee4SJoe Lawrence loop_until '[[ $(cat "/sys/module/$mod/refcnt") == "0" ]]' || 232a2818ee4SJoe Lawrence die "failed to unload module $mod (refcnt)" 233a2818ee4SJoe Lawrence 234a2818ee4SJoe Lawrence log "% rmmod $mod" 235a2818ee4SJoe Lawrence ret=$(rmmod "$mod" 2>&1) 236a2818ee4SJoe Lawrence if [[ "$ret" != "" ]]; then 237a2818ee4SJoe Lawrence die "$ret" 238a2818ee4SJoe Lawrence fi 239a2818ee4SJoe Lawrence 240a2818ee4SJoe Lawrence # Wait for module in sysfs ... 241a2818ee4SJoe Lawrence loop_until '[[ ! -e "/sys/module/$mod" ]]' || 242a2818ee4SJoe Lawrence die "failed to unload module $mod (/sys/module)" 243a2818ee4SJoe Lawrence} 244a2818ee4SJoe Lawrence 245a2818ee4SJoe Lawrence# unload_lp(modname) - unload a kernel module with a livepatch 246a2818ee4SJoe Lawrence# modname - module name to unload 247a2818ee4SJoe Lawrencefunction unload_lp() { 248a2818ee4SJoe Lawrence unload_mod "$1" 249a2818ee4SJoe Lawrence} 250a2818ee4SJoe Lawrence 251a2818ee4SJoe Lawrence# disable_lp(modname) - disable a livepatch 252a2818ee4SJoe Lawrence# modname - module name to unload 253a2818ee4SJoe Lawrencefunction disable_lp() { 254a2818ee4SJoe Lawrence local mod="$1" 255a2818ee4SJoe Lawrence 256a2818ee4SJoe Lawrence log "% echo 0 > /sys/kernel/livepatch/$mod/enabled" 257a2818ee4SJoe Lawrence echo 0 > /sys/kernel/livepatch/"$mod"/enabled 258a2818ee4SJoe Lawrence 259a2818ee4SJoe Lawrence # Wait until the transition finishes and the livepatch gets 260a2818ee4SJoe Lawrence # removed from sysfs... 261a2818ee4SJoe Lawrence loop_until '[[ ! -e "/sys/kernel/livepatch/$mod" ]]' || 262a2818ee4SJoe Lawrence die "failed to disable livepatch $mod" 263a2818ee4SJoe Lawrence} 264a2818ee4SJoe Lawrence 265a2818ee4SJoe Lawrence# set_pre_patch_ret(modname, pre_patch_ret) 266a2818ee4SJoe Lawrence# modname - module name to set 267a2818ee4SJoe Lawrence# pre_patch_ret - new pre_patch_ret value 268a2818ee4SJoe Lawrencefunction set_pre_patch_ret { 269a2818ee4SJoe Lawrence local mod="$1"; shift 270a2818ee4SJoe Lawrence local ret="$1" 271a2818ee4SJoe Lawrence 272a2818ee4SJoe Lawrence log "% echo $ret > /sys/module/$mod/parameters/pre_patch_ret" 273a2818ee4SJoe Lawrence echo "$ret" > /sys/module/"$mod"/parameters/pre_patch_ret 274a2818ee4SJoe Lawrence 275a2818ee4SJoe Lawrence # Wait for sysfs value to hold ... 276a2818ee4SJoe Lawrence loop_until '[[ $(cat "/sys/module/$mod/parameters/pre_patch_ret") == "$ret" ]]' || 277a2818ee4SJoe Lawrence die "failed to set pre_patch_ret parameter for $mod module" 278a2818ee4SJoe Lawrence} 279a2818ee4SJoe Lawrence 2802eeb0d45SJoe Lawrencefunction start_test { 2812eeb0d45SJoe Lawrence local test="$1" 2822eeb0d45SJoe Lawrence 2832eeb0d45SJoe Lawrence save_dmesg 2842eeb0d45SJoe Lawrence echo -n "TEST: $test ... " 2853fd9bd8bSJoe Lawrence log "===== TEST: $test =====" 2862eeb0d45SJoe Lawrence} 2872eeb0d45SJoe Lawrence 288a2818ee4SJoe Lawrence# check_result() - verify dmesg output 289a2818ee4SJoe Lawrence# TODO - better filter, out of order msgs, etc? 290a2818ee4SJoe Lawrencefunction check_result { 291a2818ee4SJoe Lawrence local expect="$*" 292a2818ee4SJoe Lawrence local result 293a2818ee4SJoe Lawrence 2942eeb0d45SJoe Lawrence # Note: when comparing dmesg output, the kernel log timestamps 2952eeb0d45SJoe Lawrence # help differentiate repeated testing runs. Remove them with a 2962eeb0d45SJoe Lawrence # post-comparison sed filter. 2972eeb0d45SJoe Lawrence 298884ee754SMiroslav Benes result=$(dmesg | comm --nocheck-order -13 "$SAVED_DMESG" - | \ 299c401088fSJoe Lawrence grep -e 'livepatch:' -e 'test_klp' | \ 300c401088fSJoe Lawrence grep -v '\(tainting\|taints\) kernel' | \ 3012eeb0d45SJoe Lawrence sed 's/^\[[ 0-9.]*\] //') 302a2818ee4SJoe Lawrence 303a2818ee4SJoe Lawrence if [[ "$expect" == "$result" ]] ; then 304a2818ee4SJoe Lawrence echo "ok" 305a2818ee4SJoe Lawrence else 306a2818ee4SJoe Lawrence echo -e "not ok\n\n$(diff -upr --label expected --label result <(echo "$expect") <(echo "$result"))\n" 307a2818ee4SJoe Lawrence die "livepatch kselftest(s) failed" 308a2818ee4SJoe Lawrence fi 3092eeb0d45SJoe Lawrence 3102eeb0d45SJoe Lawrence cleanup_dmesg_file 311a2818ee4SJoe Lawrence} 312*ff1b80ecSSong Liu 313*ff1b80ecSSong Liu# check_sysfs_rights(modname, rel_path, expected_rights) - check sysfs 314*ff1b80ecSSong Liu# path permissions 315*ff1b80ecSSong Liu# modname - livepatch module creating the sysfs interface 316*ff1b80ecSSong Liu# rel_path - relative path of the sysfs interface 317*ff1b80ecSSong Liu# expected_rights - expected access rights 318*ff1b80ecSSong Liufunction check_sysfs_rights() { 319*ff1b80ecSSong Liu local mod="$1"; shift 320*ff1b80ecSSong Liu local rel_path="$1"; shift 321*ff1b80ecSSong Liu local expected_rights="$1"; shift 322*ff1b80ecSSong Liu 323*ff1b80ecSSong Liu local path="$KLP_SYSFS_DIR/$mod/$rel_path" 324*ff1b80ecSSong Liu local rights=$(/bin/stat --format '%A' "$path") 325*ff1b80ecSSong Liu if test "$rights" != "$expected_rights" ; then 326*ff1b80ecSSong Liu die "Unexpected access rights of $path: $expected_rights vs. $rights" 327*ff1b80ecSSong Liu fi 328*ff1b80ecSSong Liu} 329*ff1b80ecSSong Liu 330*ff1b80ecSSong Liu# check_sysfs_value(modname, rel_path, expected_value) - check sysfs value 331*ff1b80ecSSong Liu# modname - livepatch module creating the sysfs interface 332*ff1b80ecSSong Liu# rel_path - relative path of the sysfs interface 333*ff1b80ecSSong Liu# expected_value - expected value read from the file 334*ff1b80ecSSong Liufunction check_sysfs_value() { 335*ff1b80ecSSong Liu local mod="$1"; shift 336*ff1b80ecSSong Liu local rel_path="$1"; shift 337*ff1b80ecSSong Liu local expected_value="$1"; shift 338*ff1b80ecSSong Liu 339*ff1b80ecSSong Liu local path="$KLP_SYSFS_DIR/$mod/$rel_path" 340*ff1b80ecSSong Liu local value=`cat $path` 341*ff1b80ecSSong Liu if test "$value" != "$expected_value" ; then 342*ff1b80ecSSong Liu die "Unexpected value in $path: $expected_value vs. $value" 343*ff1b80ecSSong Liu fi 344*ff1b80ecSSong Liu} 345