kvmarm.lists.cs.columbia.edu archive mirror
 help / color / mirror / Atom feed
From: Oliver Upton <oliver.upton@linux.dev>
To: kvmarm@lists.linux.dev
Cc: kvm@vger.kernel.org, Marc Zyngier <maz@kernel.org>,
	James Morse <james.morse@arm.com>,
	Suzuki K Poulose <suzuki.poulose@arm.com>,
	Zenghui Yu <yuzenghui@huawei.com>,
	linux-kernel@vger.kernel.org,
	Oliver Upton <oliver.upton@linux.dev>
Subject: [PATCH v2 19/23] KVM: selftests: Add a minimal library for interacting with an ITS
Date: Tue, 13 Feb 2024 09:41:14 +0000	[thread overview]
Message-ID: <20240213094114.3961683-1-oliver.upton@linux.dev> (raw)
In-Reply-To: <20240213093250.3960069-1-oliver.upton@linux.dev>

A prerequisite of testing LPI injection performance is of course
instantiating an ITS for the guest. Add a small library for creating an
ITS and interacting with it *from userspace*.

Yep, you read that right. KVM unintentionally allows userspace to send
commands to the virtual ITS via the command queue. Besides adding test
coverage for an elusive UAPI, interacting with the ITS in userspace
simplifies the handling of commands that need to allocate memory, like a
MAPD command with an ITT.

Signed-off-by: Oliver Upton <oliver.upton@linux.dev>
---
 .../selftests/kvm/include/aarch64/gic.h       |   7 +-
 .../selftests/kvm/include/aarch64/vgic.h      |  20 ++
 .../testing/selftests/kvm/lib/aarch64/vgic.c  | 241 ++++++++++++++++++
 3 files changed, 267 insertions(+), 1 deletion(-)

diff --git a/tools/testing/selftests/kvm/include/aarch64/gic.h b/tools/testing/selftests/kvm/include/aarch64/gic.h
index 16d944486e9c..abb41d67880c 100644
--- a/tools/testing/selftests/kvm/include/aarch64/gic.h
+++ b/tools/testing/selftests/kvm/include/aarch64/gic.h
@@ -11,7 +11,12 @@ enum gic_type {
 	GIC_TYPE_MAX,
 };
 
-#define GICD_BASE_GPA		0x8000000ULL
+/*
+ * Note that the redistributor frames are at the end, as the range scales
+ * with the number of vCPUs in the VM.
+ */
+#define GITS_BASE_GPA		0x8000000ULL
+#define GICD_BASE_GPA		(GITS_BASE_GPA + SZ_128K)
 #define GICR_BASE_GPA		(GICD_BASE_GPA + SZ_64K)
 
 /* The GIC is identity-mapped into the guest at the time of setup. */
diff --git a/tools/testing/selftests/kvm/include/aarch64/vgic.h b/tools/testing/selftests/kvm/include/aarch64/vgic.h
index ce19aa0a8360..d45b2902439d 100644
--- a/tools/testing/selftests/kvm/include/aarch64/vgic.h
+++ b/tools/testing/selftests/kvm/include/aarch64/vgic.h
@@ -32,4 +32,24 @@ void kvm_irq_write_isactiver(int gic_fd, uint32_t intid, struct kvm_vcpu *vcpu);
 
 #define KVM_IRQCHIP_NUM_PINS	(1020 - 32)
 
+struct vgic_its {
+	int	its_fd;
+	void 	*cmdq_hva;
+	size_t	cmdq_size;
+};
+
+struct vgic_its *vgic_its_setup(struct kvm_vm *vm,
+				vm_paddr_t coll_tbl, size_t coll_tbl_sz,
+				vm_paddr_t device_tbl, size_t device_tbl_sz,
+				vm_paddr_t cmdq, size_t cmdq_size);
+void vgic_its_destroy(struct vgic_its *its);
+
+void vgic_its_send_mapd_cmd(struct vgic_its *its, u32 device_id,
+		            vm_paddr_t itt_base, size_t itt_size, bool valid);
+void vgic_its_send_mapc_cmd(struct vgic_its *its, struct kvm_vcpu *vcpu,
+			    u32 collection_id, bool valid);
+void vgic_its_send_mapti_cmd(struct vgic_its *its, u32 device_id,
+			     u32 event_id, u32 collection_id, u32 intid);
+void vgic_its_send_invall_cmd(struct vgic_its *its, u32 collection_id);
+
 #endif // SELFTEST_KVM_VGIC_H
diff --git a/tools/testing/selftests/kvm/lib/aarch64/vgic.c b/tools/testing/selftests/kvm/lib/aarch64/vgic.c
index ac55b6c2e915..fc7b4fbe6453 100644
--- a/tools/testing/selftests/kvm/lib/aarch64/vgic.c
+++ b/tools/testing/selftests/kvm/lib/aarch64/vgic.c
@@ -12,6 +12,7 @@
 #include "vgic.h"
 #include "gic.h"
 #include "gic_v3.h"
+#include "processor.h"
 
 /*
  * vGIC-v3 default host setup
@@ -166,3 +167,243 @@ void kvm_irq_write_isactiver(int gic_fd, uint32_t intid, struct kvm_vcpu *vcpu)
 {
 	vgic_poke_irq(gic_fd, intid, vcpu, GICD_ISACTIVER);
 }
+
+static u64 vgic_its_read_reg(int its_fd, unsigned long offset)
+{
+	u64 attr;
+
+	kvm_device_attr_get(its_fd, KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
+			    offset, &attr);
+	return attr;
+}
+
+static void vgic_its_write_reg(int its_fd, unsigned long offset, u64 val)
+{
+	kvm_device_attr_set(its_fd, KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
+			    offset, &val);
+}
+
+static unsigned long vgic_its_find_baser(int its_fd, unsigned int type)
+{
+	int i;
+
+	for (i = 0; i < GITS_BASER_NR_REGS; i++) {
+		u64 baser;
+		unsigned long offset = GITS_BASER + (i * sizeof(baser));
+
+		baser = vgic_its_read_reg(its_fd, offset);
+		if (GITS_BASER_TYPE(baser) == type)
+			return offset;
+	}
+
+	TEST_FAIL("Couldn't find an ITS BASER of type %u", type);
+	return -1;
+}
+
+static void vgic_its_install_table(int its_fd, unsigned int type, vm_paddr_t base,
+				   size_t size)
+{
+	unsigned long offset = vgic_its_find_baser(its_fd, type);
+	u64 baser;
+
+	baser = ((size / SZ_64K) - 1) |
+		GITS_BASER_PAGE_SIZE_64K |
+		GITS_BASER_InnerShareable |
+		base |
+		GITS_BASER_RaWaWb |
+		GITS_BASER_VALID;
+
+	vgic_its_write_reg(its_fd, offset, baser);
+}
+
+static void vgic_its_install_cmdq(int its_fd, vm_paddr_t base, size_t size)
+{
+	u64 cbaser;
+
+	cbaser = ((size / SZ_4K) - 1) |
+		 GITS_CBASER_InnerShareable |
+		 base |
+		 GITS_CBASER_RaWaWb |
+		 GITS_CBASER_VALID;
+
+	vgic_its_write_reg(its_fd, GITS_CBASER, cbaser);
+}
+
+struct vgic_its *vgic_its_setup(struct kvm_vm *vm,
+				vm_paddr_t coll_tbl, size_t coll_tbl_sz,
+				vm_paddr_t device_tbl, size_t device_tbl_sz,
+				vm_paddr_t cmdq, size_t cmdq_size)
+{
+	int its_fd = kvm_create_device(vm, KVM_DEV_TYPE_ARM_VGIC_ITS);
+	struct vgic_its *its = malloc(sizeof(struct vgic_its));
+	u64 attr, ctlr;
+
+	attr = GITS_BASE_GPA;
+	kvm_device_attr_set(its_fd, KVM_DEV_ARM_VGIC_GRP_ADDR,
+			    KVM_VGIC_ITS_ADDR_TYPE, &attr);
+
+	kvm_device_attr_set(its_fd, KVM_DEV_ARM_VGIC_GRP_CTRL,
+			    KVM_DEV_ARM_VGIC_CTRL_INIT, NULL);
+
+	vgic_its_install_table(its_fd, GITS_BASER_TYPE_COLLECTION, coll_tbl,
+			       coll_tbl_sz);
+	vgic_its_install_table(its_fd, GITS_BASER_TYPE_DEVICE, device_tbl,
+			       device_tbl_sz);
+
+	vgic_its_install_cmdq(its_fd, cmdq, cmdq_size);
+
+	ctlr = vgic_its_read_reg(its_fd, GITS_CTLR);
+	ctlr |= GITS_CTLR_ENABLE;
+	vgic_its_write_reg(its_fd, GITS_CTLR, ctlr);
+
+	*its = (struct vgic_its) {
+		.its_fd		= its_fd,
+		.cmdq_hva	= addr_gpa2hva(vm, cmdq),
+		.cmdq_size	= cmdq_size,
+	};
+
+	return its;
+}
+
+void vgic_its_destroy(struct vgic_its *its)
+{
+	close(its->its_fd);
+	free(its);
+}
+
+struct its_cmd_block {
+	union {
+		u64	raw_cmd[4];
+		__le64	raw_cmd_le[4];
+	};
+};
+
+static inline void its_fixup_cmd(struct its_cmd_block *cmd)
+{
+	/* Let's fixup BE commands */
+	cmd->raw_cmd_le[0] = cpu_to_le64(cmd->raw_cmd[0]);
+	cmd->raw_cmd_le[1] = cpu_to_le64(cmd->raw_cmd[1]);
+	cmd->raw_cmd_le[2] = cpu_to_le64(cmd->raw_cmd[2]);
+	cmd->raw_cmd_le[3] = cpu_to_le64(cmd->raw_cmd[3]);
+}
+
+static void its_mask_encode(u64 *raw_cmd, u64 val, int h, int l)
+{
+	u64 mask = GENMASK_ULL(h, l);
+	*raw_cmd &= ~mask;
+	*raw_cmd |= (val << l) & mask;
+}
+
+static void its_encode_cmd(struct its_cmd_block *cmd, u8 cmd_nr)
+{
+	its_mask_encode(&cmd->raw_cmd[0], cmd_nr, 7, 0);
+}
+
+static void its_encode_devid(struct its_cmd_block *cmd, u32 devid)
+{
+	its_mask_encode(&cmd->raw_cmd[0], devid, 63, 32);
+}
+
+static void its_encode_event_id(struct its_cmd_block *cmd, u32 id)
+{
+	its_mask_encode(&cmd->raw_cmd[1], id, 31, 0);
+}
+
+static void its_encode_phys_id(struct its_cmd_block *cmd, u32 phys_id)
+{
+	its_mask_encode(&cmd->raw_cmd[1], phys_id, 63, 32);
+}
+
+static void its_encode_size(struct its_cmd_block *cmd, u8 size)
+{
+	its_mask_encode(&cmd->raw_cmd[1], size, 4, 0);
+}
+
+static void its_encode_itt(struct its_cmd_block *cmd, u64 itt_addr)
+{
+	its_mask_encode(&cmd->raw_cmd[2], itt_addr >> 8, 51, 8);
+}
+
+static void its_encode_valid(struct its_cmd_block *cmd, int valid)
+{
+	its_mask_encode(&cmd->raw_cmd[2], !!valid, 63, 63);
+}
+
+static void its_encode_target(struct its_cmd_block *cmd, u64 target_addr)
+{
+	its_mask_encode(&cmd->raw_cmd[2], target_addr >> 16, 51, 16);
+}
+
+static void its_encode_collection(struct its_cmd_block *cmd, u16 col)
+{
+	its_mask_encode(&cmd->raw_cmd[2], col, 15, 0);
+}
+
+static void vgic_its_send_cmd(struct vgic_its *its, struct its_cmd_block *cmd)
+{
+	u64 cwriter = vgic_its_read_reg(its->its_fd, GITS_CWRITER);
+	struct its_cmd_block *dst = its->cmdq_hva + cwriter;
+	u64 next;
+
+	its_fixup_cmd(cmd);
+
+	WRITE_ONCE(*dst, *cmd);
+	dsb(ishst);
+
+	next = (cwriter + sizeof(*cmd)) % its->cmdq_size;
+	vgic_its_write_reg(its->its_fd, GITS_CWRITER, next);
+
+	TEST_ASSERT(vgic_its_read_reg(its->its_fd, GITS_CREADR) == next,
+		    "ITS didn't process command at offset: %lu\n", cwriter);
+}
+
+void vgic_its_send_mapd_cmd(struct vgic_its *its, u32 device_id,
+		            vm_paddr_t itt_base, size_t itt_size, bool valid)
+{
+	struct its_cmd_block cmd = {};
+
+	its_encode_cmd(&cmd, GITS_CMD_MAPD);
+	its_encode_devid(&cmd, device_id);
+	its_encode_size(&cmd, ilog2(itt_size) - 1);
+	its_encode_itt(&cmd, itt_base);
+	its_encode_valid(&cmd, valid);
+
+	vgic_its_send_cmd(its, &cmd);
+}
+
+void vgic_its_send_mapc_cmd(struct vgic_its *its, struct kvm_vcpu *vcpu,
+			    u32 collection_id, bool valid)
+{
+	struct its_cmd_block cmd = {};
+
+	its_encode_cmd(&cmd, GITS_CMD_MAPC);
+	its_encode_collection(&cmd, collection_id);
+	its_encode_target(&cmd, vcpu->id);
+	its_encode_valid(&cmd, valid);
+
+	vgic_its_send_cmd(its, &cmd);
+}
+
+void vgic_its_send_mapti_cmd(struct vgic_its *its, u32 device_id,
+			     u32 event_id, u32 collection_id, u32 intid)
+{
+	struct its_cmd_block cmd = {};
+
+	its_encode_cmd(&cmd, GITS_CMD_MAPTI);
+	its_encode_devid(&cmd, device_id);
+	its_encode_event_id(&cmd, event_id);
+	its_encode_phys_id(&cmd, intid);
+	its_encode_collection(&cmd, collection_id);
+
+	vgic_its_send_cmd(its, &cmd);
+}
+
+void vgic_its_send_invall_cmd(struct vgic_its *its, u32 collection_id)
+{
+	struct its_cmd_block cmd = {};
+
+	its_encode_cmd(&cmd, GITS_CMD_INVALL);
+	its_encode_collection(&cmd, collection_id);
+
+	vgic_its_send_cmd(its, &cmd);
+}
-- 
2.43.0.687.g38aa6559b0-goog


  parent reply	other threads:[~2024-02-13  9:41 UTC|newest]

Thread overview: 41+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-02-13  9:32 [PATCH v2 00/23] KVM: arm64: Improvements to LPI injection Oliver Upton
2024-02-13  9:32 ` [PATCH v2 01/23] KVM: arm64: Add tracepoints + stats for LPI cache effectiveness Oliver Upton
2024-02-14 15:55   ` Marc Zyngier
2024-02-13  9:32 ` [PATCH v2 02/23] KVM: arm64: vgic: Store LPIs in an xarray Oliver Upton
2024-02-13 21:52   ` Oliver Upton
2024-02-13  9:32 ` [PATCH v2 03/23] KVM: arm64: vgic: Use xarray to find LPI in vgic_get_lpi() Oliver Upton
2024-02-13  9:32 ` [PATCH v2 04/23] KVM: arm64: vgic-v3: Iterate the xarray to find pending LPIs Oliver Upton
2024-02-13  9:32 ` [PATCH v2 05/23] KVM: arm64: vgic-its: Walk the LPI xarray in vgic_copy_lpi_list() Oliver Upton
2024-02-13  9:32 ` [PATCH v2 06/23] KVM: arm64: vgic: Get rid of the LPI linked-list Oliver Upton
2024-02-13  9:32 ` [PATCH v2 07/23] KVM: arm64: vgic: Use atomics to count LPIs Oliver Upton
2024-02-14 16:47   ` Marc Zyngier
2024-02-14 18:32     ` Oliver Upton
2024-02-14 20:01       ` Marc Zyngier
2024-02-14 23:01         ` Oliver Upton
2024-02-15  9:44           ` Marc Zyngier
2024-02-13  9:32 ` [PATCH v2 08/23] KVM: arm64: vgic: Free LPI vgic_irq structs in an RCU-safe manner Oliver Upton
2024-02-13  9:32 ` [PATCH v2 09/23] KVM: arm64: vgic: Rely on RCU protection in vgic_get_lpi() Oliver Upton
2024-02-13  9:32 ` [PATCH v2 10/23] KVM: arm64: vgic: Ensure the irq refcount is nonzero when taking a ref Oliver Upton
2024-02-13  9:32 ` [PATCH v2 11/23] KVM: arm64: vgic: Don't acquire the lpi_list_lock in vgic_put_irq() Oliver Upton
2024-02-13  9:32 ` [PATCH v2 12/23] KVM: arm64: vgic-its: Lazily allocate LPI translation cache Oliver Upton
2024-02-13  9:32 ` [PATCH v2 13/23] KVM: arm64: vgic-its: Pick cache victim based on usage count Oliver Upton
2024-02-13  9:37 ` [PATCH v2 14/23] KVM: arm64: vgic-its: Protect cached vgic_irq pointers with RCU Oliver Upton
2024-02-13  9:39 ` [PATCH v2 15/23] KVM: arm64: vgic-its: Treat the LPI translation cache as an rculist Oliver Upton
2024-02-13  9:40 ` [PATCH v2 16/23] KVM: arm64: vgic-its: Rely on RCU to protect translation cache reads Oliver Upton
2024-02-13  9:40 ` [PATCH v2 17/23] KVM: selftests: Align with kernel's GIC definitions Oliver Upton
2024-02-13  9:40 ` [PATCH v2 18/23] KVM: selftests: Standardise layout of GIC frames Oliver Upton
2024-02-13  9:41 ` Oliver Upton [this message]
2024-02-14 17:32   ` [PATCH v2 19/23] KVM: selftests: Add a minimal library for interacting with an ITS Marc Zyngier
2024-02-14 19:00     ` Oliver Upton
2024-02-14 20:09       ` Marc Zyngier
2024-02-14 20:55         ` Oliver Upton
2024-02-14 21:06           ` Oliver Upton
2024-02-13  9:41 ` [PATCH v2 20/23] KVM: selftests: Add helper for enabling LPIs on a redistributor Oliver Upton
2024-02-13  9:42 ` [PATCH v2 21/23] KVM: selftests: Use MPIDR_HWID_BITMASK from cputype.h Oliver Upton
2024-02-13  9:43 ` [PATCH v2 22/23] KVM: selftests: Hack in support for aligned page allocations Oliver Upton
2024-02-13  9:43 ` [PATCH v2 23/23] KVM: selftests: Add stress test for LPI injection Oliver Upton
2024-02-13 20:12 ` [PATCH v2 00/23] KVM: arm64: Improvements to " Oliver Upton
2024-02-14 17:43 ` Marc Zyngier
2024-02-14 18:40   ` Oliver Upton
2024-02-15 15:37     ` Marc Zyngier
2024-02-15 20:15       ` Oliver Upton

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=20240213094114.3961683-1-oliver.upton@linux.dev \
    --to=oliver.upton@linux.dev \
    --cc=james.morse@arm.com \
    --cc=kvm@vger.kernel.org \
    --cc=kvmarm@lists.linux.dev \
    --cc=linux-kernel@vger.kernel.org \
    --cc=maz@kernel.org \
    --cc=suzuki.poulose@arm.com \
    --cc=yuzenghui@huawei.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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).