cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

functions.sh (7520B)


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