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