All of lore.kernel.org
 help / color / mirror / Atom feed
From: Binbin Wu <binbin.wu@linux.intel.com>
To: kvm@vger.kernel.org
Cc: seanjc@google.com, pbonzini@redhat.com, chao.gao@intel.com,
	robert.hu@linux.intel.com, robert.hoo.linux@gmail.com,
	binbin.wu@linux.intel.com
Subject: [kvm-unit-tests PATCH v7 3/5] x86: Add test case for LAM_SUP
Date: Mon,  1 Jul 2024 15:30:08 +0800	[thread overview]
Message-ID: <20240701073010.91417-4-binbin.wu@linux.intel.com> (raw)
In-Reply-To: <20240701073010.91417-1-binbin.wu@linux.intel.com>

From: Robert Hoo <robert.hu@linux.intel.com>

This unit test covers:
1. CR4.LAM_SUP toggles.
2. Memory & MMIO access with supervisor mode address with LAM metadata.
3. INVLPG memory operand doesn't contain LAM meta data, if the address
   is non-canonical form then the INVLPG is the same as a NOP (no #GP).
4. INVPCID memory operand (descriptor pointer) could contain LAM meta data,
   however, the address in the descriptor should be canonical.

In x86/unittests.cfg, add 2 test cases/guest conf, with and without LAM.

LAM feature spec: https://cdrdv2.intel.com/v1/dl/getContent/671368,
Chapter LINEAR ADDRESS MASKING (LAM)

Signed-off-by: Robert Hoo <robert.hu@linux.intel.com>
Co-developed-by: Binbin Wu <binbin.wu@linux.intel.com>
Signed-off-by: Binbin Wu <binbin.wu@linux.intel.com>
Reviewed-by: Chao Gao <chao.gao@intel.com>
---
v7:
- Rename is_la57()/lam_sup_active() to is_la57_enabled()/is_lam_sup_enabled(),
  and move them to processor.h (Sean)
- Drop cr4_set_lam_sup()/cr4_clear_lam_sup() and use write_cr4_safe() instead. (Sean)
- Add get_lam_mask() to get lam status based on the address and vCPU state. (Sean)
- Drop the wrappers for INVLPG since INVLPG never faults. (Sean)
- Drop the wrapper for INVPCID and use invpcid_safe() instead. (Sean)
- Drop the check for X86_FEATURE_PCID. (Sean)
---
 lib/x86/processor.h |  20 +++++
 x86/Makefile.x86_64 |   1 +
 x86/lam.c           | 214 ++++++++++++++++++++++++++++++++++++++++++++
 x86/unittests.cfg   |  10 +++
 4 files changed, 245 insertions(+)
 create mode 100644 x86/lam.c

diff --git a/lib/x86/processor.h b/lib/x86/processor.h
index f7f2df50..a38f87ed 100644
--- a/lib/x86/processor.h
+++ b/lib/x86/processor.h
@@ -8,6 +8,14 @@
 #include <stdint.h>
 
 #define NONCANONICAL	0xaaaaaaaaaaaaaaaaull
+#define LAM57_MASK	GENMASK_ULL(62, 57)
+#define LAM48_MASK	GENMASK_ULL(62, 48)
+
+/* Set metadata with non-canonical pattern in mask bits of a linear address */
+static inline u64 set_la_non_canonical(u64 src, u64 mask)
+{
+	return (src & ~mask) | (NONCANONICAL & mask);
+}
 
 #ifdef __x86_64__
 #  define R "r"
@@ -120,6 +128,8 @@
 #define X86_CR4_CET		BIT(X86_CR4_CET_BIT)
 #define X86_CR4_PKS_BIT		(24)
 #define X86_CR4_PKS		BIT(X86_CR4_PKS_BIT)
+#define X86_CR4_LAM_SUP_BIT	(28)
+#define X86_CR4_LAM_SUP		BIT(X86_CR4_LAM_SUP_BIT)
 
 #define X86_EFLAGS_CF_BIT	(0)
 #define X86_EFLAGS_CF		BIT(X86_EFLAGS_CF_BIT)
@@ -968,4 +978,14 @@ struct invpcid_desc {
 	u64 addr : 64;
 } __attribute__((packed));
 
+static inline bool is_la57_enabled(void)
+{
+	return !!(read_cr4() & X86_CR4_LA57);
+}
+
+static inline bool is_lam_sup_enabled(void)
+{
+	return !!(read_cr4() & X86_CR4_LAM_SUP);
+}
+
 #endif
diff --git a/x86/Makefile.x86_64 b/x86/Makefile.x86_64
index 2771a6fa..e5db2365 100644
--- a/x86/Makefile.x86_64
+++ b/x86/Makefile.x86_64
@@ -38,6 +38,7 @@ tests += $(TEST_DIR)/rdpru.$(exe)
 tests += $(TEST_DIR)/pks.$(exe)
 tests += $(TEST_DIR)/pmu_lbr.$(exe)
 tests += $(TEST_DIR)/pmu_pebs.$(exe)
+tests += $(TEST_DIR)/lam.$(exe)
 
 ifeq ($(CONFIG_EFI),y)
 tests += $(TEST_DIR)/amd_sev.$(exe)
diff --git a/x86/lam.c b/x86/lam.c
new file mode 100644
index 00000000..2f95b6c9
--- /dev/null
+++ b/x86/lam.c
@@ -0,0 +1,214 @@
+/*
+ * Intel LAM unit test
+ *
+ * Copyright (C) 2023 Intel
+ *
+ * Author: Robert Hoo <robert.hu@linux.intel.com>
+ *         Binbin Wu <binbin.wu@linux.intel.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2 or
+ * later.
+ */
+
+#include "libcflat.h"
+#include "processor.h"
+#include "desc.h"
+#include "vmalloc.h"
+#include "alloc_page.h"
+#include "vm.h"
+#include "asm/io.h"
+#include "ioram.h"
+
+static void test_cr4_lam_set_clear(void)
+{
+	int vector;
+	bool has_lam = this_cpu_has(X86_FEATURE_LAM);
+
+	vector = write_cr4_safe(read_cr4() | X86_CR4_LAM_SUP);
+	report(has_lam ? !vector : vector == GP_VECTOR,
+	       "Expected CR4.LAM_SUP=1 to %s", has_lam ? "succeed" : "#GP");
+
+	vector = write_cr4_safe(read_cr4() & ~X86_CR4_LAM_SUP);
+	report(!vector, "Expected CR4.LAM_SUP=0 to succeed");
+}
+
+/* Refer to emulator.c */
+static void do_mov(void *mem)
+{
+	unsigned long t1, t2;
+
+	t1 = 0x123456789abcdefull & -1ul;
+	asm volatile("mov %[t1], (%[mem])\n\t"
+		     "mov (%[mem]), %[t2]"
+		     : [t2]"=r"(t2)
+		     : [t1]"r"(t1), [mem]"r"(mem)
+		     : "memory");
+	report(t1 == t2, "Mov result check");
+}
+
+static bool get_lam_mask(u64 address, u64* lam_mask)
+{
+	/*
+	 * Use LAM57_MASK as mask to construct non-canonical address if LAM is
+	 * not supported or enabled.
+	 */
+	*lam_mask = LAM57_MASK;
+
+	/*
+	 * Bit 63 determines if the address should be treated as a user address
+	 * or a supervisor address.
+	 */
+	if (address & BIT_ULL(63)) {
+		if (!(is_lam_sup_enabled()))
+			return false;
+
+		if (!is_la57_enabled())
+			*lam_mask = LAM48_MASK;
+		return true;
+	}
+
+	/* TODO: Get LAM mask for userspace address. */
+	return false;
+}
+
+
+static void test_ptr(u64* ptr, bool is_mmio)
+{
+	u64 lam_mask;
+	bool lam_active, fault;
+
+	lam_active = get_lam_mask((u64)ptr, &lam_mask);
+
+	fault = test_for_exception(GP_VECTOR, do_mov, ptr);
+	report(!fault, "Expected access to untagged address for %s to succeed",
+	       is_mmio ? "MMIO" : "memory");
+
+	ptr = (u64 *)set_la_non_canonical((u64)ptr, lam_mask);
+	fault = test_for_exception(GP_VECTOR, do_mov, ptr);
+	report(fault != lam_active, "Expected access to tagged address for %s %s LAM to %s",
+	       is_mmio ? "MMIO" : "memory", lam_active ? "with" : "without",
+	       lam_active ? "succeed" : "#GP");
+}
+
+/* invlpg with tagged address is same as NOP, no #GP expected. */
+static void test_invlpg(void *va, bool fep)
+{
+	u64 lam_mask;
+	u64 *ptr;
+
+	/*
+	 * The return value is not checked, invlpg should never faults no matter
+	 * LAM is supported or not.
+	 */
+	get_lam_mask((u64)va, &lam_mask);
+	ptr = (u64 *)set_la_non_canonical((u64)va, lam_mask);
+	if (fep)
+		asm volatile(KVM_FEP "invlpg (%0)" ::"r" (ptr) : "memory");
+	else
+		invlpg(ptr);
+
+	report(true, "Expected %sINVLPG with tagged addr to succeed", fep ? "fep: " : "");
+}
+
+/* LAM doesn't apply to the linear address in the descriptor of invpcid */
+static void test_invpcid(void *data)
+{
+	/*
+	 * Reuse the memory address for the descriptor since stack memory
+	 * address in KUT doesn't follow the kernel address space partitions.
+	 */
+	struct invpcid_desc *desc_ptr = (struct invpcid_desc *)data;
+	int vector;
+	u64 lam_mask;
+	bool lam_active;
+
+	if (!this_cpu_has(X86_FEATURE_INVPCID)) {
+		report_skip("INVPCID not supported");
+		return;
+	}
+
+	lam_active = get_lam_mask((u64)data, &lam_mask);
+
+	memset(desc_ptr, 0, sizeof(struct invpcid_desc));
+	desc_ptr->addr = (u64)data;
+
+	vector = invpcid_safe(0, desc_ptr);
+	report(!vector,
+	       "Expected INVPCID with untagged pointer + untagged addr to succeed");
+
+	desc_ptr->addr = set_la_non_canonical(desc_ptr->addr, lam_mask);
+	vector = invpcid_safe(0, desc_ptr);
+	report(vector==GP_VECTOR,
+	       "Expected INVPCID with untagged pointer + tagged addr to #GP");
+
+	desc_ptr = (struct invpcid_desc *)set_la_non_canonical((u64)desc_ptr,
+							       lam_mask);
+	vector = invpcid_safe(0, desc_ptr);
+	report(vector==GP_VECTOR,
+	       "Expected INVPCID with tagged pointer + tagged addr to #GP");
+
+	desc_ptr = (struct invpcid_desc *)data;
+	desc_ptr->addr = (u64)data;
+	desc_ptr = (struct invpcid_desc *)set_la_non_canonical((u64)desc_ptr,
+							       lam_mask);
+	vector = invpcid_safe(0, desc_ptr);
+	report(lam_active ? !vector : vector==GP_VECTOR,
+	       "Expected INVPCID with tagged pointer + untagged addr to %s",
+	       lam_active? "succeed" : "#GP");
+}
+
+static void test_lam_sup(void)
+{
+	void *vaddr, *vaddr_mmio;
+	phys_addr_t paddr;
+	unsigned long cr4 = read_cr4();
+	int vector;
+
+	/*
+	 * KUT initializes vfree_top to 0 for X86_64, and each virtual address
+	 * allocation decreases the size from vfree_top. It's guaranteed that
+	 * the return value of alloc_vpage() is considered as kernel mode
+	 * address and canonical since only a small mount virtual address range
+	 * is allocated in this test.
+	 */
+	vaddr = alloc_vpage();
+	vaddr_mmio = alloc_vpage();
+	paddr = virt_to_phys(alloc_page());
+	install_page(current_page_table(), paddr, vaddr);
+	install_page(current_page_table(), IORAM_BASE_PHYS, vaddr_mmio);
+
+	test_cr4_lam_set_clear();
+
+	/* Set for the following LAM_SUP tests. */
+	if (this_cpu_has(X86_FEATURE_LAM)) {
+		vector = write_cr4_safe(cr4 | X86_CR4_LAM_SUP);
+		report(!vector && is_lam_sup_enabled(),
+		       "Expected CR4.LAM_SUP=1 to succeed");
+	}
+
+	/* Test for normal memory. */
+	test_ptr(vaddr, false);
+	/* Test for MMIO to trigger instruction emulation. */
+	test_ptr(vaddr_mmio, true);
+	test_invpcid(vaddr);
+	test_invlpg(vaddr, false);
+	if (is_fep_available())
+		test_invlpg(vaddr, true);
+	else
+		report_skip("Skipping tests the forced emulation, "
+			    "use kvm.force_emulation_prefix=1 to enable\n");
+}
+
+int main(int ac, char **av)
+{
+	setup_vm();
+
+	if (!this_cpu_has(X86_FEATURE_LAM))
+		report_info("This CPU doesn't support LAM feature\n");
+	else
+		report_info("This CPU supports LAM feature\n");
+
+	test_lam_sup();
+
+	return report_summary();
+}
diff --git a/x86/unittests.cfg b/x86/unittests.cfg
index 7c1691a9..f1178edd 100644
--- a/x86/unittests.cfg
+++ b/x86/unittests.cfg
@@ -490,3 +490,13 @@ file = cet.flat
 arch = x86_64
 smp = 2
 extra_params = -enable-kvm -m 2048 -cpu host
+
+[intel-lam]
+file = lam.flat
+arch = x86_64
+extra_params = -enable-kvm -cpu host
+
+[intel-no-lam]
+file = lam.flat
+arch = x86_64
+extra_params = -enable-kvm -cpu host,-lam
-- 
2.43.2


  parent reply	other threads:[~2024-07-01  7:28 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-07-01  7:30 [kvm-unit-tests PATCH v7 0/5] x86: Add test cases for LAM Binbin Wu
2024-07-01  7:30 ` [kvm-unit-tests PATCH v7 1/5] x86: Move struct invpcid_desc to processor.h Binbin Wu
2024-07-01  7:30 ` [kvm-unit-tests PATCH v7 2/5] x86: Allow setting of CR3 LAM bits if LAM supported Binbin Wu
2024-07-01  7:30 ` Binbin Wu [this message]
2024-07-01  7:30 ` [kvm-unit-tests PATCH v7 4/5] x86: Add test cases for LAM_{U48,U57} Binbin Wu
2024-07-01  7:30 ` [kvm-unit-tests PATCH v7 5/5] x86: Add test case for INVVPID with LAM Binbin Wu
2024-09-25  1:22 ` [kvm-unit-tests PATCH v7 0/5] x86: Add test cases for LAM Binbin Wu
2025-02-24 17:23 ` Sean Christopherson

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20240701073010.91417-4-binbin.wu@linux.intel.com \
    --to=binbin.wu@linux.intel.com \
    --cc=chao.gao@intel.com \
    --cc=kvm@vger.kernel.org \
    --cc=pbonzini@redhat.com \
    --cc=robert.hoo.linux@gmail.com \
    --cc=robert.hu@linux.intel.com \
    --cc=seanjc@google.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.