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