From nobody Tue May 13 10:08:56 2025
Received: from mail-wm1-f73.google.com (mail-wm1-f73.google.com
 [209.85.128.73])
	(using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits))
	(No client certificate requested)
	by smtp.subspace.kernel.org (Postfix) with ESMTPS id 577771F8686
	for <linux-kernel@vger.kernel.org>; Wed,  8 Jan 2025 11:46:28 +0000 (UTC)
Authentication-Results: smtp.subspace.kernel.org;
 arc=none smtp.client-ip=209.85.128.73
ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116;
	t=1736336790; cv=none;
 b=nFnoR+vJQTV1tUeIN1+QiD41Ty48XISa7OaY1QTfhLRCP7yXfxc4kZM8LTelTl/hPzSXygFPDPF0D8wFHKHVuVuEmFaNkvXArqfbSSbgmoEN/KReVzx2L9GjbTLhdACs2wCOUEHRh6Ej2IpN6OfRCqnPT1tTXhXoJ3xDkUms0Gg=
ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org;
	s=arc-20240116; t=1736336790; c=relaxed/simple;
	bh=1RhNgAyBYwnY1lmaJpGPMJ1Z7wtx/NBVUARf1zLmbsk=;
	h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From:
	 To:Cc:Content-Type;
 b=rQ+DERLrOqYshGnf80UoXd50lQcTNLitATMv9kagtoUSq7owPs2pLVm70eFUb0DFk1BZTRfrr2I9r2eNeABY8hu+VHUegYEhx0EKjsegMSaJZU2GdgBSFY644ZEnKb3hUpKhk1AcfOIzmeuoBTNoRf/jhfBNZjQH5fjI4i9RNPI=
ARC-Authentication-Results: i=1; smtp.subspace.kernel.org;
 dmarc=pass (p=reject dis=none) header.from=google.com;
 spf=pass smtp.mailfrom=flex--vdonnefort.bounces.google.com;
 dkim=pass (2048-bit key) header.d=google.com header.i=@google.com
 header.b=CqNHt+pq; arc=none smtp.client-ip=209.85.128.73
Authentication-Results: smtp.subspace.kernel.org;
 dmarc=pass (p=reject dis=none) header.from=google.com
Authentication-Results: smtp.subspace.kernel.org;
 spf=pass smtp.mailfrom=flex--vdonnefort.bounces.google.com
Authentication-Results: smtp.subspace.kernel.org;
	dkim=pass (2048-bit key) header.d=google.com header.i=@google.com
 header.b="CqNHt+pq"
Received: by mail-wm1-f73.google.com with SMTP id
 5b1f17b1804b1-4361fc2b2d6so53254265e9.3
        for <linux-kernel@vger.kernel.org>;
 Wed, 08 Jan 2025 03:46:28 -0800 (PST)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
        d=google.com; s=20230601; t=1736336787; x=1736941587;
 darn=vger.kernel.org;
        h=cc:to:from:subject:message-id:references:mime-version:in-reply-to
         :date:from:to:cc:subject:date:message-id:reply-to;
        bh=QbQ/1nIXXwGxfAoTE/TC8PTd45WcqY21w+/Qgwgx3q8=;
        b=CqNHt+pqbudjHDDufGveXXccFI1x+sOI2bg+52j0fRrIGLZwmYGUUuwe7JcLkfTBat
         BT9wf5TCW+3tnsoq/6iMIy3nctsH2gQGZDVoSQKDP/N9BdrNiM/yrp0omoWslPcKDXv5
         hvfLj2KU+B9vbJTsRegZ5rJkGcGiNUKJAENwiLbEe5U/GX8B5mzmSr++YXC33l7asbmg
         eYJ8N3scNOZwHFSqXcyNUHMGlTEn7gWOH6ZiGO2YPO8Fwwo4/LIok/2xRCviOkximklS
         QtT56IzIoAxi27LnIMiP02unserwAAuzZw1YPYJykZTBggZc6+1poYPaH00pknCQIBc4
         q/6A==
X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
        d=1e100.net; s=20230601; t=1736336787; x=1736941587;
        h=cc:to:from:subject:message-id:references:mime-version:in-reply-to
         :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to;
        bh=QbQ/1nIXXwGxfAoTE/TC8PTd45WcqY21w+/Qgwgx3q8=;
        b=f3AstD6js84EXa6cxaY9jFOY8vesB94qkd880dbLhKAxfRVyZvzZAJyG+xD1rq9kE7
         OzlJ57sQ8XRXxBIGOVQaQkWd8sQDl0No0EfIaJyDC72lVOwrj29Ynbt7Pm0sJc6Bjtx+
         5Qy4Bt5+5bClcDNagy7/vo5xmmQiaFHJV38vx0aQXPe+lUqvgXETRVjaGWweTog+VNma
         PezUfPX/zBWBruQpUe5TxfeTpj8wm80hiLov8A+U7uyRCFiqsRbFIyntT0tekZP2D+po
         FyhkCxgBg9KpEWFhghJ6ZI9DyjR0HYdCSt3xo4c7gygelxrFCUqMVzKwuBxauMX9mlv6
         8h1g==
X-Forwarded-Encrypted: i=1;
 AJvYcCVPDeFKY0m+oMyrKa8c5brgsrojZDNuIzrP4rhY9lXkD5d6S389h7B9dAEtxa8KilVExTDrwL0+Dyz5HNQ=@vger.kernel.org
X-Gm-Message-State: AOJu0Yy14Hjl8Z7euV/4esgDovHYElkOnZ1ou2Alhtz/Dw5NfV6qdg3J
	bsSpVyzhWSo2Uz81le8wVnpJznWmVJ/IF+U9/pxuGUiXVJcA7DO6VolxYZI8hDAylg+BE8B5ai3
	nPRpB1cqmSXGddJJE0w==
X-Google-Smtp-Source: 
 AGHT+IGmtxuQJ5R7JETfm1bouxcxdHJ+CL6MhsFC4RluXUZnyO+yUdelUMuFT12Ogzt7f0uODwAj8yN9o62sPdo3
X-Received: from wmbbh17.prod.google.com
 ([2002:a05:600c:3d11:b0:436:3ea:c491])
 (user=vdonnefort job=prod-delivery.src-stubby-dispatcher) by
 2002:a05:600c:1c98:b0:434:a04f:2557 with SMTP id
 5b1f17b1804b1-436e266e802mr19136005e9.4.1736336787077;
 Wed, 08 Jan 2025 03:46:27 -0800 (PST)
Date: Wed,  8 Jan 2025 11:45:36 +0000
In-Reply-To: <20250108114536.627715-1-vdonnefort@google.com>
Precedence: bulk
X-Mailing-List: linux-kernel@vger.kernel.org
List-Id: <linux-kernel.vger.kernel.org>
List-Subscribe: <mailto:linux-kernel+subscribe@vger.kernel.org>
List-Unsubscribe: <mailto:linux-kernel+unsubscribe@vger.kernel.org>
Mime-Version: 1.0
References: <20250108114536.627715-1-vdonnefort@google.com>
X-Mailer: git-send-email 2.47.1.613.gc27f4b7a9f-goog
Message-ID: <20250108114536.627715-13-vdonnefort@google.com>
Subject: [PATCH v2 12/12] KVM: arm64: Add kselftest for tracefs hyp tracefs
From: Vincent Donnefort <vdonnefort@google.com>
To: rostedt@goodmis.org, mhiramat@kernel.org, mathieu.desnoyers@efficios.com,
	linux-trace-kernel@vger.kernel.org, maz@kernel.org, oliver.upton@linux.dev,
	joey.gouly@arm.com, suzuki.poulose@arm.com, yuzenghui@huawei.com
Cc: kvmarm@lists.linux.dev, linux-arm-kernel@lists.infradead.org,
	jstultz@google.com, qperret@google.com, will@kernel.org,
	kernel-team@android.com, linux-kernel@vger.kernel.org,
	Vincent Donnefort <vdonnefort@google.com>
Content-Transfer-Encoding: quoted-printable
Content-Type: text/plain; charset="utf-8"

Add a test to validate the newly introduced tracefs interface for the
pKVM hypervisor. This test covers the usage of extended timestamp and
coherence of the tracing clock.

Signed-off-by: Vincent Donnefort <vdonnefort@google.com>

diff --git a/arch/arm64/include/asm/kvm_asm.h b/arch/arm64/include/asm/kvm_=
asm.h
index 784f6b3ae892..60c42661d179 100644
--- a/arch/arm64/include/asm/kvm_asm.h
+++ b/arch/arm64/include/asm/kvm_asm.h
@@ -86,6 +86,7 @@ enum __kvm_host_smccc_func {
 	__KVM_HOST_SMCCC_FUNC___pkvm_reset_tracing,
 	__KVM_HOST_SMCCC_FUNC___pkvm_swap_reader_tracing,
 	__KVM_HOST_SMCCC_FUNC___pkvm_enable_event,
+	__KVM_HOST_SMCCC_FUNC___pkvm_selftest_event,
 };
=20
 #define DECLARE_KVM_VHE_SYM(sym)	extern char sym[]
diff --git a/arch/arm64/include/asm/kvm_hypevents.h b/arch/arm64/include/as=
m/kvm_hypevents.h
index 0b98a87a1250..25d8e23a3cc6 100644
--- a/arch/arm64/include/asm/kvm_hypevents.h
+++ b/arch/arm64/include/asm/kvm_hypevents.h
@@ -28,4 +28,14 @@ HYP_EVENT(hyp_exit,
 	),
 	HE_PRINTK(" ")
 );
+
+#ifdef CONFIG_PKVM_SELFTESTS
+HYP_EVENT(selftest,
+	  HE_PROTO(void),
+	  HE_STRUCT(),
+	  HE_ASSIGN(),
+	  HE_PRINTK(" ")
+);
 #endif
+
+#endif /* __ARM64_KVM_HYPEVENTS_H_ */
diff --git a/arch/arm64/kvm/Kconfig b/arch/arm64/kvm/Kconfig
index ead632ad01b4..fff9d24d7771 100644
--- a/arch/arm64/kvm/Kconfig
+++ b/arch/arm64/kvm/Kconfig
@@ -46,6 +46,7 @@ menuconfig KVM
 config NVHE_EL2_DEBUG
 	bool "Debug mode for non-VHE EL2 object"
 	depends on KVM
+	select PKVM_SELFTESTS
 	help
 	  Say Y here to enable the debug mode for the non-VHE KVM EL2 object.
 	  Failure reports will BUG() in the hypervisor. This is intended for
@@ -83,4 +84,13 @@ config PTDUMP_STAGE2_DEBUGFS
=20
 	  If in doubt, say N.
=20
+config PKVM_SELFTESTS
+	bool "Protected KVM hypervisor selftests"
+	depends on KVM
+	default n
+	help
+	  Say Y here to enable pKVM hypervisor testing infrastructure.
+
+	  If unsure, say N.
+
 endif # VIRTUALIZATION
diff --git a/arch/arm64/kvm/hyp/nvhe/hyp-main.c b/arch/arm64/kvm/hyp/nvhe/h=
yp-main.c
index 1920af496952..406b456d09c8 100644
--- a/arch/arm64/kvm/hyp/nvhe/hyp-main.c
+++ b/arch/arm64/kvm/hyp/nvhe/hyp-main.c
@@ -436,6 +436,19 @@ static void handle___pkvm_enable_event(struct kvm_cpu_=
context *host_ctxt)
 	cpu_reg(host_ctxt, 1) =3D __pkvm_enable_event(id, enable);
 }
=20
+static void handle___pkvm_selftest_event(struct kvm_cpu_context *host_ctxt)
+{
+	int smc_ret =3D SMCCC_RET_NOT_SUPPORTED, ret =3D -EOPNOTSUPP;
+
+#ifdef CONFIG_PKVM_SELFTESTS
+	trace_selftest();
+	smc_ret =3D SMCCC_RET_SUCCESS;
+	ret =3D 0;
+#endif
+	cpu_reg(host_ctxt, 0) =3D smc_ret;
+	cpu_reg(host_ctxt, 1) =3D ret;
+}
+
 typedef void (*hcall_t)(struct kvm_cpu_context *);
=20
 #define HANDLE_FUNC(x)	[__KVM_HOST_SMCCC_FUNC_##x] =3D (hcall_t)handle_##x
@@ -474,6 +487,7 @@ static const hcall_t host_hcall[] =3D {
 	HANDLE_FUNC(__pkvm_reset_tracing),
 	HANDLE_FUNC(__pkvm_swap_reader_tracing),
 	HANDLE_FUNC(__pkvm_enable_event),
+	HANDLE_FUNC(__pkvm_selftest_event),
 };
=20
 static void handle_host_hcall(struct kvm_cpu_context *host_ctxt)
diff --git a/arch/arm64/kvm/hyp_trace.c b/arch/arm64/kvm/hyp_trace.c
index fef082559a19..ac36f0b56fc2 100644
--- a/arch/arm64/kvm/hyp_trace.c
+++ b/arch/arm64/kvm/hyp_trace.c
@@ -895,6 +895,35 @@ static int hyp_trace_clock_show(struct seq_file *m, vo=
id *v)
 }
 DEFINE_SHOW_ATTRIBUTE(hyp_trace_clock);
=20
+#ifdef CONFIG_PKVM_SELFTESTS
+static int selftest_event_open(struct inode *inode, struct file *file)
+{
+	if (file->f_mode & FMODE_WRITE)
+		return kvm_call_hyp_nvhe(__pkvm_selftest_event);
+
+	return 0;
+}
+
+static ssize_t selftest_event_write(struct file *f, const char __user *buf,
+				    size_t cnt, loff_t *pos)
+{
+	return cnt;
+}
+
+static const struct file_operations selftest_event_fops =3D {
+	.open	=3D selftest_event_open,
+	.write	=3D selftest_event_write,
+};
+
+static void hyp_trace_init_testing_tracefs(struct dentry *root)
+{
+	tracefs_create_file("selftest_event", TRACEFS_MODE_WRITE, root, NULL,
+			    &selftest_event_fops);
+}
+#else
+static void hyp_trace_init_testing_tracefs(struct dentry *root) { }
+#endif
+
 int hyp_trace_init_tracefs(void)
 {
 	struct dentry *root, *per_cpu_root;
@@ -953,6 +982,7 @@ int hyp_trace_init_tracefs(void)
 	}
=20
 	hyp_trace_init_event_tracefs(root);
+	hyp_trace_init_testing_tracefs(root);
=20
 	return 0;
 }
diff --git a/tools/testing/selftests/hyp-trace/Makefile b/tools/testing/sel=
ftests/hyp-trace/Makefile
new file mode 100644
index 000000000000..2a5b2e29667e
--- /dev/null
+++ b/tools/testing/selftests/hyp-trace/Makefile
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0
+all:
+
+TEST_PROGS :=3D hyp-trace-test
+
+include ../lib.mk
diff --git a/tools/testing/selftests/hyp-trace/config b/tools/testing/selft=
ests/hyp-trace/config
new file mode 100644
index 000000000000..39cee8ec30fa
--- /dev/null
+++ b/tools/testing/selftests/hyp-trace/config
@@ -0,0 +1,4 @@
+CONFIG_FTRACE=3Dy
+CONFIG_ARM64=3Dy
+CONFIG_KVM=3Dy
+CONFIG_PROTECTED_NVHE_TESTING=3Dy
diff --git a/tools/testing/selftests/hyp-trace/hyp-trace-test b/tools/testi=
ng/selftests/hyp-trace/hyp-trace-test
new file mode 100755
index 000000000000..868eb81bfb77
--- /dev/null
+++ b/tools/testing/selftests/hyp-trace/hyp-trace-test
@@ -0,0 +1,254 @@
+#!/bin/sh -e
+# SPDX-License-Identifier: GPL-2.0-only
+
+# hyp-trace-test - Tracefs for pKVM hypervisor test
+#
+# Copyright (C) 2024 - Google LLC
+# Author: Vincent Donnefort <vdonnefort@google.com>
+#
+
+log_and_die()
+{
+    echo "$1"
+
+    exit 1
+}
+
+host_clock()
+{
+    # BOOTTIME clock
+    awk '/now/ { printf "%.6f\n", $3 / 1000000000 }' /proc/timer_list
+}
+
+page_size()
+{
+    echo "$(awk '/KernelPageSize/ {print $2; exit}' /proc/self/smaps) * 10=
24" | bc
+}
+
+goto_hyp_trace()
+{
+    if [ -d "/sys/kernel/debug/tracing/hypervisor" ]; then
+        cd /sys/kernel/debug/tracing/hypervisor
+        return
+    fi
+
+    if [ -d "/sys/kernel/tracing/hypervisor" ]; then
+        cd /sys/kernel/tracing/hypervisor
+        return
+    fi
+
+    echo "ERROR: hyp tracing folder not found!"
+
+    exit 1
+}
+
+reset_hyp_trace()
+{
+    echo 0 > tracing_on
+    echo 0 > trace
+    for event in events/hypervisor/*; do
+        echo 0 > $event/enable
+    done
+}
+
+setup_hyp_trace()
+{
+    reset_hyp_trace
+
+    echo 16 > buffer_size_kb
+    echo 1 > events/hypervisor/selftest/enable
+    echo 1 > tracing_on
+}
+
+stop_hyp_trace()
+{
+    echo 0 > tracing_on
+}
+
+hyp_trace_loaded()
+{
+    grep -q "(loaded)" buffer_size_kb
+}
+
+write_events()
+{
+    local num=3D"$1"
+    local func=3D"$2"
+
+    for i in $(seq 1 $num); do
+        echo 1 > selftest_event
+        [ -z "$func" -o $i -eq $num ] || eval $func
+    done
+}
+
+consuming_read()
+{
+    local output=3D$1
+
+    cat trace_pipe > $output &
+
+    echo $!
+}
+
+run_test_consuming()
+{
+    local nr_events=3D$1
+    local func=3D$2
+    local tmp=3D"$(mktemp)"
+    local start_ts=3D0
+    local end_ts=3D0
+    local pid=3D0
+
+    echo "Output trace file: $tmp"
+
+    setup_hyp_trace
+    pid=3D$(consuming_read $tmp)
+
+    start_ts=3D$(host_clock)
+    write_events $nr_events $func
+    stop_hyp_trace
+    end_ts=3D$(host_clock)
+
+    kill $pid
+    validate_test $tmp $nr_events $start_ts $end_ts
+
+    rm $tmp
+}
+
+validate_test()
+{
+    local output=3D$1
+    local expected_events=3D$2
+    local start_ts=3D$3
+    local end_ts=3D$4
+    local prev_ts=3D$3
+    local ts=3D0
+    local num_events=3D0
+
+    IFS=3D$'\n'
+    for line in $(cat $output); do
+        echo "$line" | grep -q -E "^# " && continue
+        ts=3D$(echo "$line" | awk '{print $2}' | cut -d ':' -f1)
+        if [ $(echo "$ts<$prev_ts" | bc) -eq 1 ]; then
+            log_and_die "Error event @$ts < $prev_ts"
+        fi
+        prev_ts=3D$ts
+        num_events=3D$((num_events + 1))
+    done
+
+    if [ $(echo "$ts>$end_ts" | bc) -eq 1 ]; then
+        log_and_die "Error event @$ts > $end_ts"
+    fi
+
+    if [ $num_events -ne $expected_events ]; then
+        log_and_die "Expected $expected_events events, got $num_events"
+    fi
+}
+
+test_ts()
+{
+    echo "Test Timestamps..."
+
+    run_test_consuming 1000
+
+    echo "done."
+}
+
+test_extended_ts()
+{
+    echo "Test Extended Timestamps..."
+
+    run_test_consuming 1000 "sleep 0.1"
+
+    echo "done."
+}
+
+assert_loaded()
+{
+    hyp_trace_loaded || log_and_die "Expected loaded buffer"
+}
+
+assert_unloaded()
+{
+    ! hyp_trace_loaded || log_and_die "Expected unloaded buffer"
+}
+
+test_unloading()
+{
+    local tmp=3D"$(mktemp)"
+
+    echo "Test unloading..."
+
+    setup_hyp_trace
+    assert_loaded
+
+    echo 0 > tracing_on
+    assert_unloaded
+
+    pid=3D$(consuming_read $tmp)
+    sleep 1
+    assert_loaded
+    kill $pid
+    assert_unloaded
+
+    echo 1 > tracing_on
+    write_events 1
+    echo 0 > trace
+    assert_loaded
+    echo 0 > tracing_on
+    assert_unloaded
+
+    echo "done."
+}
+
+test_reset()
+{
+    local tmp=3D"$(mktemp)"
+
+    echo "Test Reset..."
+
+    setup_hyp_trace
+    write_events 5
+    echo 0 > trace
+    write_events 5
+
+    pid=3D$(consuming_read $tmp)
+    sleep 1
+    stop_hyp_trace
+    kill $pid
+
+    validate_test $tmp 5 0 $(host_clock)
+
+    rm $tmp
+
+    echo "done."
+}
+
+test_big_bpacking()
+{
+    local hyp_buffer_page_size=3D48
+    local page_size=3D$(page_size)
+    local min_buf_size=3D$(echo "$page_size * $page_size / ($hyp_buffer_pa=
ge_size * $(nproc))" | bc)
+
+    min_buf_size=3D$(echo "$min_buf_size * 2 / 1024" | bc)
+
+    echo "Test loading $min_buf_size kB buffer..."
+
+    reset_hyp_trace
+    echo $min_buf_size > buffer_size_kb
+    echo 1 > tracing_on
+
+    stop_hyp_trace
+
+    echo "done."
+}
+
+goto_hyp_trace
+
+test_reset
+test_unloading
+test_big_bpacking
+test_ts
+test_extended_ts
+
+exit 0
--=20
2.47.1.613.gc27f4b7a9f-goog