xref: /OK3568_Linux_fs/kernel/tools/testing/selftests/livepatch/functions.sh (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun#!/bin/bash
2*4882a593Smuzhiyun# SPDX-License-Identifier: GPL-2.0
3*4882a593Smuzhiyun# Copyright (C) 2018 Joe Lawrence <joe.lawrence@redhat.com>
4*4882a593Smuzhiyun
5*4882a593Smuzhiyun# Shell functions for the rest of the scripts.
6*4882a593Smuzhiyun
7*4882a593SmuzhiyunMAX_RETRIES=600
8*4882a593SmuzhiyunRETRY_INTERVAL=".1"	# seconds
9*4882a593Smuzhiyun
10*4882a593Smuzhiyun# Kselftest framework requirement - SKIP code is 4
11*4882a593Smuzhiyunksft_skip=4
12*4882a593Smuzhiyun
13*4882a593Smuzhiyun# log(msg) - write message to kernel log
14*4882a593Smuzhiyun#	msg - insightful words
15*4882a593Smuzhiyunfunction log() {
16*4882a593Smuzhiyun	echo "$1" > /dev/kmsg
17*4882a593Smuzhiyun}
18*4882a593Smuzhiyun
19*4882a593Smuzhiyun# skip(msg) - testing can't proceed
20*4882a593Smuzhiyun#	msg - explanation
21*4882a593Smuzhiyunfunction skip() {
22*4882a593Smuzhiyun	log "SKIP: $1"
23*4882a593Smuzhiyun	echo "SKIP: $1" >&2
24*4882a593Smuzhiyun	exit $ksft_skip
25*4882a593Smuzhiyun}
26*4882a593Smuzhiyun
27*4882a593Smuzhiyun# root test
28*4882a593Smuzhiyunfunction is_root() {
29*4882a593Smuzhiyun	uid=$(id -u)
30*4882a593Smuzhiyun	if [ $uid -ne 0 ]; then
31*4882a593Smuzhiyun		echo "skip all tests: must be run as root" >&2
32*4882a593Smuzhiyun		exit $ksft_skip
33*4882a593Smuzhiyun	fi
34*4882a593Smuzhiyun}
35*4882a593Smuzhiyun
36*4882a593Smuzhiyun# die(msg) - game over, man
37*4882a593Smuzhiyun#	msg - dying words
38*4882a593Smuzhiyunfunction die() {
39*4882a593Smuzhiyun	log "ERROR: $1"
40*4882a593Smuzhiyun	echo "ERROR: $1" >&2
41*4882a593Smuzhiyun	exit 1
42*4882a593Smuzhiyun}
43*4882a593Smuzhiyun
44*4882a593Smuzhiyun# save existing dmesg so we can detect new content
45*4882a593Smuzhiyunfunction save_dmesg() {
46*4882a593Smuzhiyun	SAVED_DMESG=$(mktemp --tmpdir -t klp-dmesg-XXXXXX)
47*4882a593Smuzhiyun	dmesg > "$SAVED_DMESG"
48*4882a593Smuzhiyun}
49*4882a593Smuzhiyun
50*4882a593Smuzhiyun# cleanup temporary dmesg file from save_dmesg()
51*4882a593Smuzhiyunfunction cleanup_dmesg_file() {
52*4882a593Smuzhiyun	rm -f "$SAVED_DMESG"
53*4882a593Smuzhiyun}
54*4882a593Smuzhiyun
55*4882a593Smuzhiyunfunction push_config() {
56*4882a593Smuzhiyun	DYNAMIC_DEBUG=$(grep '^kernel/livepatch' /sys/kernel/debug/dynamic_debug/control | \
57*4882a593Smuzhiyun			awk -F'[: ]' '{print "file " $1 " line " $2 " " $4}')
58*4882a593Smuzhiyun	FTRACE_ENABLED=$(sysctl --values kernel.ftrace_enabled)
59*4882a593Smuzhiyun}
60*4882a593Smuzhiyun
61*4882a593Smuzhiyunfunction pop_config() {
62*4882a593Smuzhiyun	if [[ -n "$DYNAMIC_DEBUG" ]]; then
63*4882a593Smuzhiyun		echo -n "$DYNAMIC_DEBUG" > /sys/kernel/debug/dynamic_debug/control
64*4882a593Smuzhiyun	fi
65*4882a593Smuzhiyun	if [[ -n "$FTRACE_ENABLED" ]]; then
66*4882a593Smuzhiyun		sysctl kernel.ftrace_enabled="$FTRACE_ENABLED" &> /dev/null
67*4882a593Smuzhiyun	fi
68*4882a593Smuzhiyun}
69*4882a593Smuzhiyun
70*4882a593Smuzhiyunfunction set_dynamic_debug() {
71*4882a593Smuzhiyun        cat <<-EOF > /sys/kernel/debug/dynamic_debug/control
72*4882a593Smuzhiyun		file kernel/livepatch/* +p
73*4882a593Smuzhiyun		func klp_try_switch_task -p
74*4882a593Smuzhiyun		EOF
75*4882a593Smuzhiyun}
76*4882a593Smuzhiyun
77*4882a593Smuzhiyunfunction set_ftrace_enabled() {
78*4882a593Smuzhiyun	result=$(sysctl -q kernel.ftrace_enabled="$1" 2>&1 && \
79*4882a593Smuzhiyun		 sysctl kernel.ftrace_enabled 2>&1)
80*4882a593Smuzhiyun	echo "livepatch: $result" > /dev/kmsg
81*4882a593Smuzhiyun}
82*4882a593Smuzhiyun
83*4882a593Smuzhiyunfunction cleanup() {
84*4882a593Smuzhiyun	pop_config
85*4882a593Smuzhiyun	cleanup_dmesg_file
86*4882a593Smuzhiyun}
87*4882a593Smuzhiyun
88*4882a593Smuzhiyun# setup_config - save the current config and set a script exit trap that
89*4882a593Smuzhiyun#		 restores the original config.  Setup the dynamic debug
90*4882a593Smuzhiyun#		 for verbose livepatching output and turn on
91*4882a593Smuzhiyun#		 the ftrace_enabled sysctl.
92*4882a593Smuzhiyunfunction setup_config() {
93*4882a593Smuzhiyun	is_root
94*4882a593Smuzhiyun	push_config
95*4882a593Smuzhiyun	set_dynamic_debug
96*4882a593Smuzhiyun	set_ftrace_enabled 1
97*4882a593Smuzhiyun	trap cleanup EXIT INT TERM HUP
98*4882a593Smuzhiyun}
99*4882a593Smuzhiyun
100*4882a593Smuzhiyun# loop_until(cmd) - loop a command until it is successful or $MAX_RETRIES,
101*4882a593Smuzhiyun#		    sleep $RETRY_INTERVAL between attempts
102*4882a593Smuzhiyun#	cmd - command and its arguments to run
103*4882a593Smuzhiyunfunction loop_until() {
104*4882a593Smuzhiyun	local cmd="$*"
105*4882a593Smuzhiyun	local i=0
106*4882a593Smuzhiyun	while true; do
107*4882a593Smuzhiyun		eval "$cmd" && return 0
108*4882a593Smuzhiyun		[[ $((i++)) -eq $MAX_RETRIES ]] && return 1
109*4882a593Smuzhiyun		sleep $RETRY_INTERVAL
110*4882a593Smuzhiyun	done
111*4882a593Smuzhiyun}
112*4882a593Smuzhiyun
113*4882a593Smuzhiyunfunction assert_mod() {
114*4882a593Smuzhiyun	local mod="$1"
115*4882a593Smuzhiyun
116*4882a593Smuzhiyun	modprobe --dry-run "$mod" &>/dev/null
117*4882a593Smuzhiyun}
118*4882a593Smuzhiyun
119*4882a593Smuzhiyunfunction is_livepatch_mod() {
120*4882a593Smuzhiyun	local mod="$1"
121*4882a593Smuzhiyun
122*4882a593Smuzhiyun	if [[ $(modinfo "$mod" | awk '/^livepatch:/{print $NF}') == "Y" ]]; then
123*4882a593Smuzhiyun		return 0
124*4882a593Smuzhiyun	fi
125*4882a593Smuzhiyun
126*4882a593Smuzhiyun	return 1
127*4882a593Smuzhiyun}
128*4882a593Smuzhiyun
129*4882a593Smuzhiyunfunction __load_mod() {
130*4882a593Smuzhiyun	local mod="$1"; shift
131*4882a593Smuzhiyun
132*4882a593Smuzhiyun	local msg="% modprobe $mod $*"
133*4882a593Smuzhiyun	log "${msg%% }"
134*4882a593Smuzhiyun	ret=$(modprobe "$mod" "$@" 2>&1)
135*4882a593Smuzhiyun	if [[ "$ret" != "" ]]; then
136*4882a593Smuzhiyun		die "$ret"
137*4882a593Smuzhiyun	fi
138*4882a593Smuzhiyun
139*4882a593Smuzhiyun	# Wait for module in sysfs ...
140*4882a593Smuzhiyun	loop_until '[[ -e "/sys/module/$mod" ]]' ||
141*4882a593Smuzhiyun		die "failed to load module $mod"
142*4882a593Smuzhiyun}
143*4882a593Smuzhiyun
144*4882a593Smuzhiyun
145*4882a593Smuzhiyun# load_mod(modname, params) - load a kernel module
146*4882a593Smuzhiyun#	modname - module name to load
147*4882a593Smuzhiyun#	params  - module parameters to pass to modprobe
148*4882a593Smuzhiyunfunction load_mod() {
149*4882a593Smuzhiyun	local mod="$1"; shift
150*4882a593Smuzhiyun
151*4882a593Smuzhiyun	assert_mod "$mod" ||
152*4882a593Smuzhiyun		skip "unable to load module ${mod}, verify CONFIG_TEST_LIVEPATCH=m and run self-tests as root"
153*4882a593Smuzhiyun
154*4882a593Smuzhiyun	is_livepatch_mod "$mod" &&
155*4882a593Smuzhiyun		die "use load_lp() to load the livepatch module $mod"
156*4882a593Smuzhiyun
157*4882a593Smuzhiyun	__load_mod "$mod" "$@"
158*4882a593Smuzhiyun}
159*4882a593Smuzhiyun
160*4882a593Smuzhiyun# load_lp_nowait(modname, params) - load a kernel module with a livepatch
161*4882a593Smuzhiyun#			but do not wait on until the transition finishes
162*4882a593Smuzhiyun#	modname - module name to load
163*4882a593Smuzhiyun#	params  - module parameters to pass to modprobe
164*4882a593Smuzhiyunfunction load_lp_nowait() {
165*4882a593Smuzhiyun	local mod="$1"; shift
166*4882a593Smuzhiyun
167*4882a593Smuzhiyun	assert_mod "$mod" ||
168*4882a593Smuzhiyun		skip "unable to load module ${mod}, verify CONFIG_TEST_LIVEPATCH=m and run self-tests as root"
169*4882a593Smuzhiyun
170*4882a593Smuzhiyun	is_livepatch_mod "$mod" ||
171*4882a593Smuzhiyun		die "module $mod is not a livepatch"
172*4882a593Smuzhiyun
173*4882a593Smuzhiyun	__load_mod "$mod" "$@"
174*4882a593Smuzhiyun
175*4882a593Smuzhiyun	# Wait for livepatch in sysfs ...
176*4882a593Smuzhiyun	loop_until '[[ -e "/sys/kernel/livepatch/$mod" ]]' ||
177*4882a593Smuzhiyun		die "failed to load module $mod (sysfs)"
178*4882a593Smuzhiyun}
179*4882a593Smuzhiyun
180*4882a593Smuzhiyun# load_lp(modname, params) - load a kernel module with a livepatch
181*4882a593Smuzhiyun#	modname - module name to load
182*4882a593Smuzhiyun#	params  - module parameters to pass to modprobe
183*4882a593Smuzhiyunfunction load_lp() {
184*4882a593Smuzhiyun	local mod="$1"; shift
185*4882a593Smuzhiyun
186*4882a593Smuzhiyun	load_lp_nowait "$mod" "$@"
187*4882a593Smuzhiyun
188*4882a593Smuzhiyun	# Wait until the transition finishes ...
189*4882a593Smuzhiyun	loop_until 'grep -q '^0$' /sys/kernel/livepatch/$mod/transition' ||
190*4882a593Smuzhiyun		die "failed to complete transition"
191*4882a593Smuzhiyun}
192*4882a593Smuzhiyun
193*4882a593Smuzhiyun# load_failing_mod(modname, params) - load a kernel module, expect to fail
194*4882a593Smuzhiyun#	modname - module name to load
195*4882a593Smuzhiyun#	params  - module parameters to pass to modprobe
196*4882a593Smuzhiyunfunction load_failing_mod() {
197*4882a593Smuzhiyun	local mod="$1"; shift
198*4882a593Smuzhiyun
199*4882a593Smuzhiyun	local msg="% modprobe $mod $*"
200*4882a593Smuzhiyun	log "${msg%% }"
201*4882a593Smuzhiyun	ret=$(modprobe "$mod" "$@" 2>&1)
202*4882a593Smuzhiyun	if [[ "$ret" == "" ]]; then
203*4882a593Smuzhiyun		die "$mod unexpectedly loaded"
204*4882a593Smuzhiyun	fi
205*4882a593Smuzhiyun	log "$ret"
206*4882a593Smuzhiyun}
207*4882a593Smuzhiyun
208*4882a593Smuzhiyun# unload_mod(modname) - unload a kernel module
209*4882a593Smuzhiyun#	modname - module name to unload
210*4882a593Smuzhiyunfunction unload_mod() {
211*4882a593Smuzhiyun	local mod="$1"
212*4882a593Smuzhiyun
213*4882a593Smuzhiyun	# Wait for module reference count to clear ...
214*4882a593Smuzhiyun	loop_until '[[ $(cat "/sys/module/$mod/refcnt") == "0" ]]' ||
215*4882a593Smuzhiyun		die "failed to unload module $mod (refcnt)"
216*4882a593Smuzhiyun
217*4882a593Smuzhiyun	log "% rmmod $mod"
218*4882a593Smuzhiyun	ret=$(rmmod "$mod" 2>&1)
219*4882a593Smuzhiyun	if [[ "$ret" != "" ]]; then
220*4882a593Smuzhiyun		die "$ret"
221*4882a593Smuzhiyun	fi
222*4882a593Smuzhiyun
223*4882a593Smuzhiyun	# Wait for module in sysfs ...
224*4882a593Smuzhiyun	loop_until '[[ ! -e "/sys/module/$mod" ]]' ||
225*4882a593Smuzhiyun		die "failed to unload module $mod (/sys/module)"
226*4882a593Smuzhiyun}
227*4882a593Smuzhiyun
228*4882a593Smuzhiyun# unload_lp(modname) - unload a kernel module with a livepatch
229*4882a593Smuzhiyun#	modname - module name to unload
230*4882a593Smuzhiyunfunction unload_lp() {
231*4882a593Smuzhiyun	unload_mod "$1"
232*4882a593Smuzhiyun}
233*4882a593Smuzhiyun
234*4882a593Smuzhiyun# disable_lp(modname) - disable a livepatch
235*4882a593Smuzhiyun#	modname - module name to unload
236*4882a593Smuzhiyunfunction disable_lp() {
237*4882a593Smuzhiyun	local mod="$1"
238*4882a593Smuzhiyun
239*4882a593Smuzhiyun	log "% echo 0 > /sys/kernel/livepatch/$mod/enabled"
240*4882a593Smuzhiyun	echo 0 > /sys/kernel/livepatch/"$mod"/enabled
241*4882a593Smuzhiyun
242*4882a593Smuzhiyun	# Wait until the transition finishes and the livepatch gets
243*4882a593Smuzhiyun	# removed from sysfs...
244*4882a593Smuzhiyun	loop_until '[[ ! -e "/sys/kernel/livepatch/$mod" ]]' ||
245*4882a593Smuzhiyun		die "failed to disable livepatch $mod"
246*4882a593Smuzhiyun}
247*4882a593Smuzhiyun
248*4882a593Smuzhiyun# set_pre_patch_ret(modname, pre_patch_ret)
249*4882a593Smuzhiyun#	modname - module name to set
250*4882a593Smuzhiyun#	pre_patch_ret - new pre_patch_ret value
251*4882a593Smuzhiyunfunction set_pre_patch_ret {
252*4882a593Smuzhiyun	local mod="$1"; shift
253*4882a593Smuzhiyun	local ret="$1"
254*4882a593Smuzhiyun
255*4882a593Smuzhiyun	log "% echo $ret > /sys/module/$mod/parameters/pre_patch_ret"
256*4882a593Smuzhiyun	echo "$ret" > /sys/module/"$mod"/parameters/pre_patch_ret
257*4882a593Smuzhiyun
258*4882a593Smuzhiyun	# Wait for sysfs value to hold ...
259*4882a593Smuzhiyun	loop_until '[[ $(cat "/sys/module/$mod/parameters/pre_patch_ret") == "$ret" ]]' ||
260*4882a593Smuzhiyun		die "failed to set pre_patch_ret parameter for $mod module"
261*4882a593Smuzhiyun}
262*4882a593Smuzhiyun
263*4882a593Smuzhiyunfunction start_test {
264*4882a593Smuzhiyun	local test="$1"
265*4882a593Smuzhiyun
266*4882a593Smuzhiyun	save_dmesg
267*4882a593Smuzhiyun	echo -n "TEST: $test ... "
268*4882a593Smuzhiyun	log "===== TEST: $test ====="
269*4882a593Smuzhiyun}
270*4882a593Smuzhiyun
271*4882a593Smuzhiyun# check_result() - verify dmesg output
272*4882a593Smuzhiyun#	TODO - better filter, out of order msgs, etc?
273*4882a593Smuzhiyunfunction check_result {
274*4882a593Smuzhiyun	local expect="$*"
275*4882a593Smuzhiyun	local result
276*4882a593Smuzhiyun
277*4882a593Smuzhiyun	# Note: when comparing dmesg output, the kernel log timestamps
278*4882a593Smuzhiyun	# help differentiate repeated testing runs.  Remove them with a
279*4882a593Smuzhiyun	# post-comparison sed filter.
280*4882a593Smuzhiyun
281*4882a593Smuzhiyun	result=$(dmesg | comm --nocheck-order -13 "$SAVED_DMESG" - | \
282*4882a593Smuzhiyun		 grep -e 'livepatch:' -e 'test_klp' | \
283*4882a593Smuzhiyun		 grep -v '\(tainting\|taints\) kernel' | \
284*4882a593Smuzhiyun		 sed 's/^\[[ 0-9.]*\] //')
285*4882a593Smuzhiyun
286*4882a593Smuzhiyun	if [[ "$expect" == "$result" ]] ; then
287*4882a593Smuzhiyun		echo "ok"
288*4882a593Smuzhiyun	else
289*4882a593Smuzhiyun		echo -e "not ok\n\n$(diff -upr --label expected --label result <(echo "$expect") <(echo "$result"))\n"
290*4882a593Smuzhiyun		die "livepatch kselftest(s) failed"
291*4882a593Smuzhiyun	fi
292*4882a593Smuzhiyun
293*4882a593Smuzhiyun	cleanup_dmesg_file
294*4882a593Smuzhiyun}
295