162306a36Sopenharmony_ci#!/bin/sh
262306a36Sopenharmony_ci# SPDX-License-Identifier: GPL-2.0
362306a36Sopenharmony_ci#
462306a36Sopenharmony_ci# This reads tests.txt for the list of LKDTM tests to invoke. Any marked
562306a36Sopenharmony_ci# with a leading "#" are skipped. The rest of the line after the
662306a36Sopenharmony_ci# test name is either the text to look for in dmesg for a "success",
762306a36Sopenharmony_ci# or the rationale for why a test is marked to be skipped.
862306a36Sopenharmony_ci#
962306a36Sopenharmony_ciset -e
1062306a36Sopenharmony_ciTRIGGER=/sys/kernel/debug/provoke-crash/DIRECT
1162306a36Sopenharmony_ciCLEAR_ONCE=/sys/kernel/debug/clear_warn_once
1262306a36Sopenharmony_ciKSELFTEST_SKIP_TEST=4
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci# Verify we have LKDTM available in the kernel.
1562306a36Sopenharmony_ciif [ ! -r $TRIGGER ] ; then
1662306a36Sopenharmony_ci	/sbin/modprobe -q lkdtm || true
1762306a36Sopenharmony_ci	if [ ! -r $TRIGGER ] ; then
1862306a36Sopenharmony_ci		echo "Cannot find $TRIGGER (missing CONFIG_LKDTM?)"
1962306a36Sopenharmony_ci	else
2062306a36Sopenharmony_ci		echo "Cannot write $TRIGGER (need to run as root?)"
2162306a36Sopenharmony_ci	fi
2262306a36Sopenharmony_ci	# Skip this test
2362306a36Sopenharmony_ci	exit $KSELFTEST_SKIP_TEST
2462306a36Sopenharmony_cifi
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci# Figure out which test to run from our script name.
2762306a36Sopenharmony_citest=$(basename $0 .sh)
2862306a36Sopenharmony_ci# Look up details about the test from master list of LKDTM tests.
2962306a36Sopenharmony_ciline=$(grep -E '^#?'"$test"'\b' tests.txt)
3062306a36Sopenharmony_ciif [ -z "$line" ]; then
3162306a36Sopenharmony_ci	echo "Skipped: missing test '$test' in tests.txt"
3262306a36Sopenharmony_ci	exit $KSELFTEST_SKIP_TEST
3362306a36Sopenharmony_cifi
3462306a36Sopenharmony_ci# Check that the test is known to LKDTM.
3562306a36Sopenharmony_ciif ! grep -E -q '^'"$test"'$' "$TRIGGER" ; then
3662306a36Sopenharmony_ci	echo "Skipped: test '$test' missing in $TRIGGER!"
3762306a36Sopenharmony_ci	exit $KSELFTEST_SKIP_TEST
3862306a36Sopenharmony_cifi
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci# Extract notes/expected output from test list.
4162306a36Sopenharmony_citest=$(echo "$line" | cut -d" " -f1)
4262306a36Sopenharmony_ciif echo "$line" | grep -q ' ' ; then
4362306a36Sopenharmony_ci	expect=$(echo "$line" | cut -d" " -f2-)
4462306a36Sopenharmony_cielse
4562306a36Sopenharmony_ci	expect=""
4662306a36Sopenharmony_cifi
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci# If the test is commented out, report a skip
4962306a36Sopenharmony_ciif echo "$test" | grep -q '^#' ; then
5062306a36Sopenharmony_ci	test=$(echo "$test" | cut -c2-)
5162306a36Sopenharmony_ci	if [ -z "$expect" ]; then
5262306a36Sopenharmony_ci		expect="crashes entire system"
5362306a36Sopenharmony_ci	fi
5462306a36Sopenharmony_ci	echo "Skipping $test: $expect"
5562306a36Sopenharmony_ci	exit $KSELFTEST_SKIP_TEST
5662306a36Sopenharmony_cifi
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci# If no expected output given, assume an Oops with back trace is success.
5962306a36Sopenharmony_cirepeat=1
6062306a36Sopenharmony_ciif [ -z "$expect" ]; then
6162306a36Sopenharmony_ci	expect="call trace:"
6262306a36Sopenharmony_cielse
6362306a36Sopenharmony_ci	if echo "$expect" | grep -q '^repeat:' ; then
6462306a36Sopenharmony_ci		repeat=$(echo "$expect" | cut -d' ' -f1 | cut -d: -f2)
6562306a36Sopenharmony_ci		expect=$(echo "$expect" | cut -d' ' -f2-)
6662306a36Sopenharmony_ci	fi
6762306a36Sopenharmony_cifi
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci# Prepare log for report checking
7062306a36Sopenharmony_ciLOG=$(mktemp --tmpdir -t lkdtm-log-XXXXXX)
7162306a36Sopenharmony_ciDMESG=$(mktemp --tmpdir -t lkdtm-dmesg-XXXXXX)
7262306a36Sopenharmony_cicleanup() {
7362306a36Sopenharmony_ci	rm -f "$LOG" "$DMESG"
7462306a36Sopenharmony_ci}
7562306a36Sopenharmony_citrap cleanup EXIT
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci# Reset WARN_ONCE counters so we trip it each time this runs.
7862306a36Sopenharmony_ciif [ -w $CLEAR_ONCE ] ; then
7962306a36Sopenharmony_ci	echo 1 > $CLEAR_ONCE
8062306a36Sopenharmony_cifi
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci# Save existing dmesg so we can detect new content below
8362306a36Sopenharmony_cidmesg > "$DMESG"
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci# Since the kernel is likely killing the process writing to the trigger
8662306a36Sopenharmony_ci# file, it must not be the script's shell itself. i.e. we cannot do:
8762306a36Sopenharmony_ci#     echo "$test" >"$TRIGGER"
8862306a36Sopenharmony_ci# Instead, use "cat" to take the signal. Since the shell will yell about
8962306a36Sopenharmony_ci# the signal that killed the subprocess, we must ignore the failure and
9062306a36Sopenharmony_ci# continue. However we don't silence stderr since there might be other
9162306a36Sopenharmony_ci# useful details reported there in the case of other unexpected conditions.
9262306a36Sopenharmony_cifor i in $(seq 1 $repeat); do
9362306a36Sopenharmony_ci	echo "$test" | cat >"$TRIGGER" || true
9462306a36Sopenharmony_cidone
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci# Record and dump the results
9762306a36Sopenharmony_cidmesg | comm --nocheck-order -13 "$DMESG" - > "$LOG" || true
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_cicat "$LOG"
10062306a36Sopenharmony_ci# Check for expected output
10162306a36Sopenharmony_ciif grep -E -qi "$expect" "$LOG" ; then
10262306a36Sopenharmony_ci	echo "$test: saw '$expect': ok"
10362306a36Sopenharmony_ci	exit 0
10462306a36Sopenharmony_cielse
10562306a36Sopenharmony_ci	if grep -E -qi XFAIL: "$LOG" ; then
10662306a36Sopenharmony_ci		echo "$test: saw 'XFAIL': [SKIP]"
10762306a36Sopenharmony_ci		exit $KSELFTEST_SKIP_TEST
10862306a36Sopenharmony_ci	else
10962306a36Sopenharmony_ci		echo "$test: missing '$expect': [FAIL]"
11062306a36Sopenharmony_ci		exit 1
11162306a36Sopenharmony_ci	fi
11262306a36Sopenharmony_cifi
113