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