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