0001
0002
0003
0004
0005
0006
0007 MAX_RETRIES=600
0008 RETRY_INTERVAL=".1"
0009
0010
0011 ksft_skip=4
0012
0013
0014
0015 function log() {
0016 echo "$1" > /dev/kmsg
0017 }
0018
0019
0020
0021 function skip() {
0022 log "SKIP: $1"
0023 echo "SKIP: $1" >&2
0024 exit $ksft_skip
0025 }
0026
0027
0028 function is_root() {
0029 uid=$(id -u)
0030 if [ $uid -ne 0 ]; then
0031 echo "skip all tests: must be run as root" >&2
0032 exit $ksft_skip
0033 fi
0034 }
0035
0036
0037
0038 function die() {
0039 log "ERROR: $1"
0040 echo "ERROR: $1" >&2
0041 exit 1
0042 }
0043
0044
0045 function save_dmesg() {
0046 SAVED_DMESG=$(mktemp --tmpdir -t klp-dmesg-XXXXXX)
0047 dmesg > "$SAVED_DMESG"
0048 }
0049
0050
0051 function cleanup_dmesg_file() {
0052 rm -f "$SAVED_DMESG"
0053 }
0054
0055 function push_config() {
0056 DYNAMIC_DEBUG=$(grep '^kernel/livepatch' /sys/kernel/debug/dynamic_debug/control | \
0057 awk -F'[: ]' '{print "file " $1 " line " $2 " " $4}')
0058 FTRACE_ENABLED=$(sysctl --values kernel.ftrace_enabled)
0059 }
0060
0061 function pop_config() {
0062 if [[ -n "$DYNAMIC_DEBUG" ]]; then
0063 echo -n "$DYNAMIC_DEBUG" > /sys/kernel/debug/dynamic_debug/control
0064 fi
0065 if [[ -n "$FTRACE_ENABLED" ]]; then
0066 sysctl kernel.ftrace_enabled="$FTRACE_ENABLED" &> /dev/null
0067 fi
0068 }
0069
0070 function set_dynamic_debug() {
0071 cat <<-EOF > /sys/kernel/debug/dynamic_debug/control
0072 file kernel/livepatch/* +p
0073 func klp_try_switch_task -p
0074 EOF
0075 }
0076
0077 function set_ftrace_enabled() {
0078 local can_fail=0
0079 if [[ "$1" == "--fail" ]] ; then
0080 can_fail=1
0081 shift
0082 fi
0083
0084 local err=$(sysctl -q kernel.ftrace_enabled="$1" 2>&1)
0085 local result=$(sysctl --values kernel.ftrace_enabled)
0086
0087 if [[ "$result" != "$1" ]] ; then
0088 if [[ $can_fail -eq 1 ]] ; then
0089 echo "livepatch: $err" > /dev/kmsg
0090 return
0091 fi
0092
0093 skip "failed to set kernel.ftrace_enabled = $1"
0094 fi
0095
0096 echo "livepatch: kernel.ftrace_enabled = $result" > /dev/kmsg
0097 }
0098
0099 function cleanup() {
0100 pop_config
0101 cleanup_dmesg_file
0102 }
0103
0104
0105
0106
0107
0108 function setup_config() {
0109 is_root
0110 push_config
0111 set_dynamic_debug
0112 set_ftrace_enabled 1
0113 trap cleanup EXIT INT TERM HUP
0114 }
0115
0116
0117
0118
0119 function loop_until() {
0120 local cmd="$*"
0121 local i=0
0122 while true; do
0123 eval "$cmd" && return 0
0124 [[ $((i++)) -eq $MAX_RETRIES ]] && return 1
0125 sleep $RETRY_INTERVAL
0126 done
0127 }
0128
0129 function assert_mod() {
0130 local mod="$1"
0131
0132 modprobe --dry-run "$mod" &>/dev/null
0133 }
0134
0135 function is_livepatch_mod() {
0136 local mod="$1"
0137
0138 if [[ $(modinfo "$mod" | awk '/^livepatch:/{print $NF}') == "Y" ]]; then
0139 return 0
0140 fi
0141
0142 return 1
0143 }
0144
0145 function __load_mod() {
0146 local mod="$1"; shift
0147
0148 local msg="% modprobe $mod $*"
0149 log "${msg%% }"
0150 ret=$(modprobe "$mod" "$@" 2>&1)
0151 if [[ "$ret" != "" ]]; then
0152 die "$ret"
0153 fi
0154
0155
0156 loop_until '[[ -e "/sys/module/$mod" ]]' ||
0157 die "failed to load module $mod"
0158 }
0159
0160
0161
0162
0163
0164 function load_mod() {
0165 local mod="$1"; shift
0166
0167 assert_mod "$mod" ||
0168 skip "unable to load module ${mod}, verify CONFIG_TEST_LIVEPATCH=m and run self-tests as root"
0169
0170 is_livepatch_mod "$mod" &&
0171 die "use load_lp() to load the livepatch module $mod"
0172
0173 __load_mod "$mod" "$@"
0174 }
0175
0176
0177
0178
0179
0180 function load_lp_nowait() {
0181 local mod="$1"; shift
0182
0183 assert_mod "$mod" ||
0184 skip "unable to load module ${mod}, verify CONFIG_TEST_LIVEPATCH=m and run self-tests as root"
0185
0186 is_livepatch_mod "$mod" ||
0187 die "module $mod is not a livepatch"
0188
0189 __load_mod "$mod" "$@"
0190
0191
0192 loop_until '[[ -e "/sys/kernel/livepatch/$mod" ]]' ||
0193 die "failed to load module $mod (sysfs)"
0194 }
0195
0196
0197
0198
0199 function load_lp() {
0200 local mod="$1"; shift
0201
0202 load_lp_nowait "$mod" "$@"
0203
0204
0205 loop_until 'grep -q '^0$' /sys/kernel/livepatch/$mod/transition' ||
0206 die "failed to complete transition"
0207 }
0208
0209
0210
0211
0212 function load_failing_mod() {
0213 local mod="$1"; shift
0214
0215 local msg="% modprobe $mod $*"
0216 log "${msg%% }"
0217 ret=$(modprobe "$mod" "$@" 2>&1)
0218 if [[ "$ret" == "" ]]; then
0219 die "$mod unexpectedly loaded"
0220 fi
0221 log "$ret"
0222 }
0223
0224
0225
0226 function unload_mod() {
0227 local mod="$1"
0228
0229
0230 loop_until '[[ $(cat "/sys/module/$mod/refcnt") == "0" ]]' ||
0231 die "failed to unload module $mod (refcnt)"
0232
0233 log "% rmmod $mod"
0234 ret=$(rmmod "$mod" 2>&1)
0235 if [[ "$ret" != "" ]]; then
0236 die "$ret"
0237 fi
0238
0239
0240 loop_until '[[ ! -e "/sys/module/$mod" ]]' ||
0241 die "failed to unload module $mod (/sys/module)"
0242 }
0243
0244
0245
0246 function unload_lp() {
0247 unload_mod "$1"
0248 }
0249
0250
0251
0252 function disable_lp() {
0253 local mod="$1"
0254
0255 log "% echo 0 > /sys/kernel/livepatch/$mod/enabled"
0256 echo 0 > /sys/kernel/livepatch/"$mod"/enabled
0257
0258
0259
0260 loop_until '[[ ! -e "/sys/kernel/livepatch/$mod" ]]' ||
0261 die "failed to disable livepatch $mod"
0262 }
0263
0264
0265
0266
0267 function set_pre_patch_ret {
0268 local mod="$1"; shift
0269 local ret="$1"
0270
0271 log "% echo $ret > /sys/module/$mod/parameters/pre_patch_ret"
0272 echo "$ret" > /sys/module/"$mod"/parameters/pre_patch_ret
0273
0274
0275 loop_until '[[ $(cat "/sys/module/$mod/parameters/pre_patch_ret") == "$ret" ]]' ||
0276 die "failed to set pre_patch_ret parameter for $mod module"
0277 }
0278
0279 function start_test {
0280 local test="$1"
0281
0282 save_dmesg
0283 echo -n "TEST: $test ... "
0284 log "===== TEST: $test ====="
0285 }
0286
0287
0288
0289 function check_result {
0290 local expect="$*"
0291 local result
0292
0293
0294
0295
0296
0297 result=$(dmesg | comm --nocheck-order -13 "$SAVED_DMESG" - | \
0298 grep -e 'livepatch:' -e 'test_klp' | \
0299 grep -v '\(tainting\|taints\) kernel' | \
0300 sed 's/^\[[ 0-9.]*\] //')
0301
0302 if [[ "$expect" == "$result" ]] ; then
0303 echo "ok"
0304 else
0305 echo -e "not ok\n\n$(diff -upr --label expected --label result <(echo "$expect") <(echo "$result"))\n"
0306 die "livepatch kselftest(s) failed"
0307 fi
0308
0309 cleanup_dmesg_file
0310 }