-
Notifications
You must be signed in to change notification settings - Fork 52.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
KVM: selftests: Add a minimal library for interacting with an ITS
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 the guest. Signed-off-by: Oliver Upton <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Marc Zyngier <[email protected]>
- Loading branch information
Showing
6 changed files
with
295 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
/* SPDX-License-Identifier: GPL-2.0 */ | ||
|
||
#ifndef __SELFTESTS_GIC_V3_ITS_H__ | ||
#define __SELFTESTS_GIC_V3_ITS_H__ | ||
|
||
#include <linux/sizes.h> | ||
|
||
void its_init(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 its_send_mapd_cmd(void *cmdq_base, u32 device_id, vm_paddr_t itt_base, | ||
size_t itt_size, bool valid); | ||
void its_send_mapc_cmd(void *cmdq_base, u32 vcpu_id, u32 collection_id, bool valid); | ||
void its_send_mapti_cmd(void *cmdq_base, u32 device_id, u32 event_id, | ||
u32 collection_id, u32 intid); | ||
void its_send_invall_cmd(void *cmdq_base, u32 collection_id); | ||
|
||
#endif // __SELFTESTS_GIC_V3_ITS_H__ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,248 @@ | ||
// SPDX-License-Identifier: GPL-2.0 | ||
/* | ||
* Guest ITS library, generously donated by drivers/irqchip/irq-gic-v3-its.c | ||
* over in the kernel tree. | ||
*/ | ||
|
||
#include <linux/kvm.h> | ||
#include <linux/sizes.h> | ||
#include <asm/kvm_para.h> | ||
#include <asm/kvm.h> | ||
|
||
#include "kvm_util.h" | ||
#include "vgic.h" | ||
#include "gic.h" | ||
#include "gic_v3.h" | ||
#include "processor.h" | ||
|
||
static u64 its_read_u64(unsigned long offset) | ||
{ | ||
return readq_relaxed(GITS_BASE_GVA + offset); | ||
} | ||
|
||
static void its_write_u64(unsigned long offset, u64 val) | ||
{ | ||
writeq_relaxed(val, GITS_BASE_GVA + offset); | ||
} | ||
|
||
static u32 its_read_u32(unsigned long offset) | ||
{ | ||
return readl_relaxed(GITS_BASE_GVA + offset); | ||
} | ||
|
||
static void its_write_u32(unsigned long offset, u32 val) | ||
{ | ||
writel_relaxed(val, GITS_BASE_GVA + offset); | ||
} | ||
|
||
static unsigned long its_find_baser(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 = its_read_u64(offset); | ||
if (GITS_BASER_TYPE(baser) == type) | ||
return offset; | ||
} | ||
|
||
GUEST_FAIL("Couldn't find an ITS BASER of type %u", type); | ||
return -1; | ||
} | ||
|
||
static void its_install_table(unsigned int type, vm_paddr_t base, size_t size) | ||
{ | ||
unsigned long offset = its_find_baser(type); | ||
u64 baser; | ||
|
||
baser = ((size / SZ_64K) - 1) | | ||
GITS_BASER_PAGE_SIZE_64K | | ||
GITS_BASER_InnerShareable | | ||
base | | ||
GITS_BASER_RaWaWb | | ||
GITS_BASER_VALID; | ||
|
||
its_write_u64(offset, baser); | ||
} | ||
|
||
static void its_install_cmdq(vm_paddr_t base, size_t size) | ||
{ | ||
u64 cbaser; | ||
|
||
cbaser = ((size / SZ_4K) - 1) | | ||
GITS_CBASER_InnerShareable | | ||
base | | ||
GITS_CBASER_RaWaWb | | ||
GITS_CBASER_VALID; | ||
|
||
its_write_u64(GITS_CBASER, cbaser); | ||
} | ||
|
||
void its_init(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) | ||
{ | ||
u32 ctlr; | ||
|
||
its_install_table(GITS_BASER_TYPE_COLLECTION, coll_tbl, coll_tbl_sz); | ||
its_install_table(GITS_BASER_TYPE_DEVICE, device_tbl, device_tbl_sz); | ||
its_install_cmdq(cmdq, cmdq_size); | ||
|
||
ctlr = its_read_u32(GITS_CTLR); | ||
ctlr |= GITS_CTLR_ENABLE; | ||
its_write_u32(GITS_CTLR, ctlr); | ||
} | ||
|
||
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); | ||
} | ||
|
||
#define GITS_CMDQ_POLL_ITERATIONS 0 | ||
|
||
static void its_send_cmd(void *cmdq_base, struct its_cmd_block *cmd) | ||
{ | ||
u64 cwriter = its_read_u64(GITS_CWRITER); | ||
struct its_cmd_block *dst = cmdq_base + cwriter; | ||
u64 cbaser = its_read_u64(GITS_CBASER); | ||
size_t cmdq_size; | ||
u64 next; | ||
int i; | ||
|
||
cmdq_size = ((cbaser & 0xFF) + 1) * SZ_4K; | ||
|
||
its_fixup_cmd(cmd); | ||
|
||
WRITE_ONCE(*dst, *cmd); | ||
dsb(ishst); | ||
next = (cwriter + sizeof(*cmd)) % cmdq_size; | ||
its_write_u64(GITS_CWRITER, next); | ||
|
||
/* | ||
* Polling isn't necessary considering KVM's ITS emulation at the time | ||
* of writing this, as the CMDQ is processed synchronously after a write | ||
* to CWRITER. | ||
*/ | ||
for (i = 0; its_read_u64(GITS_CREADR) != next; i++) { | ||
__GUEST_ASSERT(i < GITS_CMDQ_POLL_ITERATIONS, | ||
"ITS didn't process command at offset %lu after %d iterations\n", | ||
cwriter, i); | ||
|
||
cpu_relax(); | ||
} | ||
} | ||
|
||
void its_send_mapd_cmd(void *cmdq_base, 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); | ||
|
||
its_send_cmd(cmdq_base, &cmd); | ||
} | ||
|
||
void its_send_mapc_cmd(void *cmdq_base, u32 vcpu_id, 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); | ||
|
||
its_send_cmd(cmdq_base, &cmd); | ||
} | ||
|
||
void its_send_mapti_cmd(void *cmdq_base, 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); | ||
|
||
its_send_cmd(cmdq_base, &cmd); | ||
} | ||
|
||
void its_send_invall_cmd(void *cmdq_base, u32 collection_id) | ||
{ | ||
struct its_cmd_block cmd = {}; | ||
|
||
its_encode_cmd(&cmd, GITS_CMD_INVALL); | ||
its_encode_collection(&cmd, collection_id); | ||
|
||
its_send_cmd(cmdq_base, &cmd); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters