1#!/bin/bash 2# SPDX-License-Identifier: GPL-2.0 3# Copyright (C) 2018 Joe Lawrence <joe.lawrence@redhat.com> 4 5# Shell functions for the rest of the scripts. 6 7MAX_RETRIES=600 8RETRY_INTERVAL=".1" # seconds 9 10# log(msg) - write message to kernel log 11# msg - insightful words 12function log() { 13 echo "$1" > /dev/kmsg 14} 15 16# skip(msg) - testing can't proceed 17# msg - explanation 18function skip() { 19 log "SKIP: $1" 20 echo "SKIP: $1" >&2 21 exit 4 22} 23 24# die(msg) - game over, man 25# msg - dying words 26function die() { 27 log "ERROR: $1" 28 echo "ERROR: $1" >&2 29 exit 1 30} 31 32# set_dynamic_debug() - setup kernel dynamic debug 33# TODO - push and pop this config? 34function set_dynamic_debug() { 35 cat << EOF > /sys/kernel/debug/dynamic_debug/control 36file kernel/livepatch/* +p 37func klp_try_switch_task -p 38EOF 39} 40 41# loop_until(cmd) - loop a command until it is successful or $MAX_RETRIES, 42# sleep $RETRY_INTERVAL between attempts 43# cmd - command and its arguments to run 44function loop_until() { 45 local cmd="$*" 46 local i=0 47 while true; do 48 eval "$cmd" && return 0 49 [[ $((i++)) -eq $MAX_RETRIES ]] && return 1 50 sleep $RETRY_INTERVAL 51 done 52} 53 54function assert_mod() { 55 local mod="$1" 56 57 modprobe --dry-run "$mod" &>/dev/null 58} 59 60function is_livepatch_mod() { 61 local mod="$1" 62 63 if [[ $(modinfo "$mod" | awk '/^livepatch:/{print $NF}') == "Y" ]]; then 64 return 0 65 fi 66 67 return 1 68} 69 70function __load_mod() { 71 local mod="$1"; shift 72 73 local msg="% modprobe $mod $*" 74 log "${msg%% }" 75 ret=$(modprobe "$mod" "$@" 2>&1) 76 if [[ "$ret" != "" ]]; then 77 die "$ret" 78 fi 79 80 # Wait for module in sysfs ... 81 loop_until '[[ -e "/sys/module/$mod" ]]' || 82 die "failed to load module $mod" 83} 84 85 86# load_mod(modname, params) - load a kernel module 87# modname - module name to load 88# params - module parameters to pass to modprobe 89function load_mod() { 90 local mod="$1"; shift 91 92 assert_mod "$mod" || 93 skip "unable to load module ${mod}, verify CONFIG_TEST_LIVEPATCH=m and run self-tests as root" 94 95 is_livepatch_mod "$mod" && 96 die "use load_lp() to load the livepatch module $mod" 97 98 __load_mod "$mod" "$@" 99} 100 101# load_lp_nowait(modname, params) - load a kernel module with a livepatch 102# but do not wait on until the transition finishes 103# modname - module name to load 104# params - module parameters to pass to modprobe 105function load_lp_nowait() { 106 local mod="$1"; shift 107 108 assert_mod "$mod" || 109 skip "unable to load module ${mod}, verify CONFIG_TEST_LIVEPATCH=m and run self-tests as root" 110 111 is_livepatch_mod "$mod" || 112 die "module $mod is not a livepatch" 113 114 __load_mod "$mod" "$@" 115 116 # Wait for livepatch in sysfs ... 117 loop_until '[[ -e "/sys/kernel/livepatch/$mod" ]]' || 118 die "failed to load module $mod (sysfs)" 119} 120 121# load_lp(modname, params) - load a kernel module with a livepatch 122# modname - module name to load 123# params - module parameters to pass to modprobe 124function load_lp() { 125 local mod="$1"; shift 126 127 load_lp_nowait "$mod" "$@" 128 129 # Wait until the transition finishes ... 130 loop_until 'grep -q '^0$' /sys/kernel/livepatch/$mod/transition' || 131 die "failed to complete transition" 132} 133 134# load_failing_mod(modname, params) - load a kernel module, expect to fail 135# modname - module name to load 136# params - module parameters to pass to modprobe 137function load_failing_mod() { 138 local mod="$1"; shift 139 140 local msg="% modprobe $mod $*" 141 log "${msg%% }" 142 ret=$(modprobe "$mod" "$@" 2>&1) 143 if [[ "$ret" == "" ]]; then 144 die "$mod unexpectedly loaded" 145 fi 146 log "$ret" 147} 148 149# unload_mod(modname) - unload a kernel module 150# modname - module name to unload 151function unload_mod() { 152 local mod="$1" 153 154 # Wait for module reference count to clear ... 155 loop_until '[[ $(cat "/sys/module/$mod/refcnt") == "0" ]]' || 156 die "failed to unload module $mod (refcnt)" 157 158 log "% rmmod $mod" 159 ret=$(rmmod "$mod" 2>&1) 160 if [[ "$ret" != "" ]]; then 161 die "$ret" 162 fi 163 164 # Wait for module in sysfs ... 165 loop_until '[[ ! -e "/sys/module/$mod" ]]' || 166 die "failed to unload module $mod (/sys/module)" 167} 168 169# unload_lp(modname) - unload a kernel module with a livepatch 170# modname - module name to unload 171function unload_lp() { 172 unload_mod "$1" 173} 174 175# disable_lp(modname) - disable a livepatch 176# modname - module name to unload 177function disable_lp() { 178 local mod="$1" 179 180 log "% echo 0 > /sys/kernel/livepatch/$mod/enabled" 181 echo 0 > /sys/kernel/livepatch/"$mod"/enabled 182 183 # Wait until the transition finishes and the livepatch gets 184 # removed from sysfs... 185 loop_until '[[ ! -e "/sys/kernel/livepatch/$mod" ]]' || 186 die "failed to disable livepatch $mod" 187} 188 189# set_pre_patch_ret(modname, pre_patch_ret) 190# modname - module name to set 191# pre_patch_ret - new pre_patch_ret value 192function set_pre_patch_ret { 193 local mod="$1"; shift 194 local ret="$1" 195 196 log "% echo $ret > /sys/module/$mod/parameters/pre_patch_ret" 197 echo "$ret" > /sys/module/"$mod"/parameters/pre_patch_ret 198 199 # Wait for sysfs value to hold ... 200 loop_until '[[ $(cat "/sys/module/$mod/parameters/pre_patch_ret") == "$ret" ]]' || 201 die "failed to set pre_patch_ret parameter for $mod module" 202} 203 204# check_result() - verify dmesg output 205# TODO - better filter, out of order msgs, etc? 206function check_result { 207 local expect="$*" 208 local result 209 210 result=$(dmesg | grep -v 'tainting' | grep -e 'livepatch:' -e 'test_klp' | sed 's/^\[[ 0-9.]*\] //') 211 212 if [[ "$expect" == "$result" ]] ; then 213 echo "ok" 214 else 215 echo -e "not ok\n\n$(diff -upr --label expected --label result <(echo "$expect") <(echo "$result"))\n" 216 die "livepatch kselftest(s) failed" 217 fi 218} 219