Commit e5410ee2 authored by Ricardo Koller's avatar Ricardo Koller Committed by Marc Zyngier

KVM: selftests: aarch64: Cmdline arg to set number of IRQs in vgic_irq test

Add the ability to specify the number of vIRQs exposed by KVM (arg
defaults to 64). Then extend the KVM_IRQ_LINE test by injecting all
available SPIs at once (specified by the nr-irqs arg). As a bonus,
inject all SGIs at once as well.
Signed-off-by: default avatarRicardo Koller <ricarkol@google.com>
Acked-by: default avatarAndrew Jones <drjones@redhat.com>
Signed-off-by: default avatarMarc Zyngier <maz@kernel.org>
Link: https://lore.kernel.org/r/20211109023906.1091208-9-ricarkol@google.com
parent e1cb399e
...@@ -382,7 +382,7 @@ static struct kvm_vm *test_vm_create(void) ...@@ -382,7 +382,7 @@ static struct kvm_vm *test_vm_create(void)
ucall_init(vm, NULL); ucall_init(vm, NULL);
test_init_timer_irq(vm); test_init_timer_irq(vm);
vgic_v3_setup(vm, nr_vcpus, GICD_BASE_GPA, GICR_BASE_GPA); vgic_v3_setup(vm, nr_vcpus, 64, GICD_BASE_GPA, GICR_BASE_GPA);
/* Make all the test's cmdline args visible to the guest */ /* Make all the test's cmdline args visible to the guest */
sync_global_to_guest(vm, test_args); sync_global_to_guest(vm, test_args);
......
...@@ -23,6 +23,14 @@ ...@@ -23,6 +23,14 @@
#define GICR_BASE_GPA 0x080A0000ULL #define GICR_BASE_GPA 0x080A0000ULL
#define VCPU_ID 0 #define VCPU_ID 0
/*
* Stores the user specified args; it's passed to the guest and to every test
* function.
*/
struct test_args {
uint32_t nr_irqs; /* number of KVM supported IRQs. */
};
/* /*
* KVM implements 32 priority levels: * KVM implements 32 priority levels:
* 0x00 (highest priority) - 0xF8 (lowest priority), in steps of 8 * 0x00 (highest priority) - 0xF8 (lowest priority), in steps of 8
...@@ -51,14 +59,18 @@ typedef enum { ...@@ -51,14 +59,18 @@ typedef enum {
struct kvm_inject_args { struct kvm_inject_args {
kvm_inject_cmd cmd; kvm_inject_cmd cmd;
uint32_t intid; uint32_t first_intid;
uint32_t num;
}; };
/* Used on the guest side to perform the hypercall. */ /* Used on the guest side to perform the hypercall. */
static void kvm_inject_call(kvm_inject_cmd cmd, uint32_t intid); static void kvm_inject_call(kvm_inject_cmd cmd, uint32_t first_intid, uint32_t num);
#define KVM_INJECT(cmd, intid) \ #define KVM_INJECT(cmd, intid) \
kvm_inject_call(cmd, intid) kvm_inject_call(cmd, intid, 1)
#define KVM_INJECT_MULTI(cmd, intid, num) \
kvm_inject_call(cmd, intid, num)
/* Used on the host side to get the hypercall info. */ /* Used on the host side to get the hypercall info. */
static void kvm_inject_get_call(struct kvm_vm *vm, struct ucall *uc, static void kvm_inject_get_call(struct kvm_vm *vm, struct ucall *uc,
...@@ -122,11 +134,12 @@ static void guest_irq_handler(struct ex_regs *regs) ...@@ -122,11 +134,12 @@ static void guest_irq_handler(struct ex_regs *regs)
GUEST_ASSERT(!gic_irq_get_pending(intid)); GUEST_ASSERT(!gic_irq_get_pending(intid));
} }
static void kvm_inject_call(kvm_inject_cmd cmd, uint32_t intid) static void kvm_inject_call(kvm_inject_cmd cmd, uint32_t first_intid, uint32_t num)
{ {
struct kvm_inject_args args = { struct kvm_inject_args args = {
.cmd = cmd, .cmd = cmd,
.intid = intid, .first_intid = first_intid,
.num = num,
}; };
GUEST_SYNC(&args); GUEST_SYNC(&args);
} }
...@@ -138,14 +151,30 @@ do { \ ...@@ -138,14 +151,30 @@ do { \
GUEST_ASSERT(_intid == 0 || _intid == IAR_SPURIOUS); \ GUEST_ASSERT(_intid == 0 || _intid == IAR_SPURIOUS); \
} while (0) } while (0)
static void guest_inject(uint32_t intid, kvm_inject_cmd cmd) static void reset_priorities(struct test_args *args)
{
int i;
for (i = 0; i < args->nr_irqs; i++)
gic_set_priority(i, IRQ_DEFAULT_PRIO_REG);
}
static void guest_inject(struct test_args *args,
uint32_t first_intid, uint32_t num,
kvm_inject_cmd cmd)
{ {
uint32_t i;
reset_stats(); reset_stats();
/* Cycle over all priorities to make things more interesting. */
for (i = first_intid; i < num + first_intid; i++)
gic_set_priority(i, (i % (KVM_NUM_PRIOS - 1)) << 3);
asm volatile("msr daifset, #2" : : : "memory"); asm volatile("msr daifset, #2" : : : "memory");
KVM_INJECT(cmd, intid); KVM_INJECT_MULTI(cmd, first_intid, num);
while (irq_handled < 1) { while (irq_handled < num) {
asm volatile("wfi\n" asm volatile("wfi\n"
"msr daifclr, #2\n" "msr daifclr, #2\n"
/* handle IRQ */ /* handle IRQ */
...@@ -154,57 +183,72 @@ static void guest_inject(uint32_t intid, kvm_inject_cmd cmd) ...@@ -154,57 +183,72 @@ static void guest_inject(uint32_t intid, kvm_inject_cmd cmd)
} }
asm volatile("msr daifclr, #2" : : : "memory"); asm volatile("msr daifclr, #2" : : : "memory");
GUEST_ASSERT_EQ(irq_handled, 1); GUEST_ASSERT_EQ(irq_handled, num);
GUEST_ASSERT_EQ(irqnr_received[intid], 1); for (i = first_intid; i < num + first_intid; i++)
GUEST_ASSERT_EQ(irqnr_received[i], 1);
GUEST_ASSERT_IAR_EMPTY(); GUEST_ASSERT_IAR_EMPTY();
reset_priorities(args);
} }
static void test_injection(struct kvm_inject_desc *f) static void test_injection(struct test_args *args, struct kvm_inject_desc *f)
{ {
if (f->sgi) uint32_t nr_irqs = args->nr_irqs;
guest_inject(MIN_SGI, f->cmd);
if (f->sgi) {
guest_inject(args, MIN_SGI, 1, f->cmd);
guest_inject(args, 0, 16, f->cmd);
}
if (f->ppi) if (f->ppi)
guest_inject(MIN_PPI, f->cmd); guest_inject(args, MIN_PPI, 1, f->cmd);
if (f->spi) if (f->spi) {
guest_inject(MIN_SPI, f->cmd); guest_inject(args, MIN_SPI, 1, f->cmd);
guest_inject(args, nr_irqs - 1, 1, f->cmd);
guest_inject(args, MIN_SPI, nr_irqs - MIN_SPI, f->cmd);
}
} }
static void guest_code(void) static void guest_code(struct test_args args)
{ {
uint32_t i; uint32_t i, nr_irqs = args.nr_irqs;
uint32_t nr_irqs = 64; /* absolute minimum number of IRQs supported. */
struct kvm_inject_desc *f; struct kvm_inject_desc *f;
gic_init(GIC_V3, 1, dist, redist); gic_init(GIC_V3, 1, dist, redist);
for (i = 0; i < nr_irqs; i++) { for (i = 0; i < nr_irqs; i++)
gic_irq_enable(i); gic_irq_enable(i);
gic_set_priority(i, IRQ_DEFAULT_PRIO_REG);
}
reset_priorities(&args);
gic_set_priority_mask(CPU_PRIO_MASK); gic_set_priority_mask(CPU_PRIO_MASK);
local_irq_enable(); local_irq_enable();
/* Start the tests. */ /* Start the tests. */
for_each_inject_fn(inject_edge_fns, f) for_each_inject_fn(inject_edge_fns, f)
test_injection(f); test_injection(&args, f);
GUEST_DONE(); GUEST_DONE();
} }
static void run_guest_cmd(struct kvm_vm *vm, int gic_fd, static void run_guest_cmd(struct kvm_vm *vm, int gic_fd,
struct kvm_inject_args *inject_args) struct kvm_inject_args *inject_args,
struct test_args *test_args)
{ {
kvm_inject_cmd cmd = inject_args->cmd; kvm_inject_cmd cmd = inject_args->cmd;
uint32_t intid = inject_args->intid; uint32_t intid = inject_args->first_intid;
uint32_t num = inject_args->num;
uint32_t i;
assert(intid < UINT_MAX - num);
switch (cmd) { switch (cmd) {
case KVM_INJECT_EDGE_IRQ_LINE: case KVM_INJECT_EDGE_IRQ_LINE:
kvm_arm_irq_line(vm, intid, 1); for (i = intid; i < intid + num; i++)
kvm_arm_irq_line(vm, intid, 0); kvm_arm_irq_line(vm, i, 1);
for (i = intid; i < intid + num; i++)
kvm_arm_irq_line(vm, i, 0);
break; break;
default: default:
break; break;
...@@ -222,21 +266,35 @@ static void kvm_inject_get_call(struct kvm_vm *vm, struct ucall *uc, ...@@ -222,21 +266,35 @@ static void kvm_inject_get_call(struct kvm_vm *vm, struct ucall *uc,
memcpy(args, kvm_args_hva, sizeof(struct kvm_inject_args)); memcpy(args, kvm_args_hva, sizeof(struct kvm_inject_args));
} }
static void print_args(struct test_args *args)
{
printf("nr-irqs=%d\n", args->nr_irqs);
}
static void test_vgic(void) static void test_vgic(uint32_t nr_irqs)
{ {
struct ucall uc; struct ucall uc;
int gic_fd; int gic_fd;
struct kvm_vm *vm; struct kvm_vm *vm;
struct kvm_inject_args inject_args; struct kvm_inject_args inject_args;
struct test_args args = {
.nr_irqs = nr_irqs,
};
print_args(&args);
vm = vm_create_default(VCPU_ID, 0, guest_code); vm = vm_create_default(VCPU_ID, 0, guest_code);
ucall_init(vm, NULL); ucall_init(vm, NULL);
vm_init_descriptor_tables(vm); vm_init_descriptor_tables(vm);
vcpu_init_descriptor_tables(vm, VCPU_ID); vcpu_init_descriptor_tables(vm, VCPU_ID);
gic_fd = vgic_v3_setup(vm, 1, GICD_BASE_GPA, GICR_BASE_GPA); /* Setup the guest args page (so it gets the args). */
vcpu_args_set(vm, 0, 1, args);
gic_fd = vgic_v3_setup(vm, 1, nr_irqs,
GICD_BASE_GPA, GICR_BASE_GPA);
vm_install_exception_handler(vm, VECTOR_IRQ_CURRENT, vm_install_exception_handler(vm, VECTOR_IRQ_CURRENT,
guest_irq_handler); guest_irq_handler);
...@@ -247,7 +305,7 @@ static void test_vgic(void) ...@@ -247,7 +305,7 @@ static void test_vgic(void)
switch (get_ucall(vm, VCPU_ID, &uc)) { switch (get_ucall(vm, VCPU_ID, &uc)) {
case UCALL_SYNC: case UCALL_SYNC:
kvm_inject_get_call(vm, &uc, &inject_args); kvm_inject_get_call(vm, &uc, &inject_args);
run_guest_cmd(vm, gic_fd, &inject_args); run_guest_cmd(vm, gic_fd, &inject_args, &args);
break; break;
case UCALL_ABORT: case UCALL_ABORT:
TEST_FAIL("%s at %s:%ld\n\tvalues: %#lx, %#lx", TEST_FAIL("%s at %s:%ld\n\tvalues: %#lx, %#lx",
...@@ -266,12 +324,39 @@ static void test_vgic(void) ...@@ -266,12 +324,39 @@ static void test_vgic(void)
kvm_vm_free(vm); kvm_vm_free(vm);
} }
int main(int ac, char **av) static void help(const char *name)
{ {
printf(
"\n"
"usage: %s [-n num_irqs]\n", name);
printf(" -n: specify the number of IRQs to configure the vgic with.\n");
puts("");
exit(1);
}
int main(int argc, char **argv)
{
uint32_t nr_irqs = 64;
int opt;
/* Tell stdout not to buffer its content */ /* Tell stdout not to buffer its content */
setbuf(stdout, NULL); setbuf(stdout, NULL);
test_vgic(); while ((opt = getopt(argc, argv, "hg:n:")) != -1) {
switch (opt) {
case 'n':
nr_irqs = atoi(optarg);
if (nr_irqs > 1024 || nr_irqs % 32)
help(argv[0]);
break;
case 'h':
default:
help(argv[0]);
break;
}
}
test_vgic(nr_irqs);
return 0; return 0;
} }
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
((uint64_t)(flags) << 12) | \ ((uint64_t)(flags) << 12) | \
index) index)
int vgic_v3_setup(struct kvm_vm *vm, unsigned int nr_vcpus, int vgic_v3_setup(struct kvm_vm *vm, unsigned int nr_vcpus, uint32_t nr_irqs,
uint64_t gicd_base_gpa, uint64_t gicr_base_gpa); uint64_t gicd_base_gpa, uint64_t gicr_base_gpa);
#define VGIC_MAX_RESERVED 1023 #define VGIC_MAX_RESERVED 1023
......
...@@ -31,7 +31,7 @@ ...@@ -31,7 +31,7 @@
* redistributor regions of the guest. Since it depends on the number of * redistributor regions of the guest. Since it depends on the number of
* vCPUs for the VM, it must be called after all the vCPUs have been created. * vCPUs for the VM, it must be called after all the vCPUs have been created.
*/ */
int vgic_v3_setup(struct kvm_vm *vm, unsigned int nr_vcpus, int vgic_v3_setup(struct kvm_vm *vm, unsigned int nr_vcpus, uint32_t nr_irqs,
uint64_t gicd_base_gpa, uint64_t gicr_base_gpa) uint64_t gicd_base_gpa, uint64_t gicr_base_gpa)
{ {
int gic_fd; int gic_fd;
...@@ -53,6 +53,13 @@ int vgic_v3_setup(struct kvm_vm *vm, unsigned int nr_vcpus, ...@@ -53,6 +53,13 @@ int vgic_v3_setup(struct kvm_vm *vm, unsigned int nr_vcpus,
/* Distributor setup */ /* Distributor setup */
gic_fd = kvm_create_device(vm, KVM_DEV_TYPE_ARM_VGIC_V3, false); gic_fd = kvm_create_device(vm, KVM_DEV_TYPE_ARM_VGIC_V3, false);
kvm_device_access(gic_fd, KVM_DEV_ARM_VGIC_GRP_NR_IRQS,
0, &nr_irqs, true);
kvm_device_access(gic_fd, KVM_DEV_ARM_VGIC_GRP_CTRL,
KVM_DEV_ARM_VGIC_CTRL_INIT, NULL, true);
kvm_device_access(gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, kvm_device_access(gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR,
KVM_VGIC_V3_ADDR_TYPE_DIST, &gicd_base_gpa, true); KVM_VGIC_V3_ADDR_TYPE_DIST, &gicd_base_gpa, true);
nr_gic_pages = vm_calc_num_guest_pages(vm->mode, KVM_VGIC_V3_DIST_SIZE); nr_gic_pages = vm_calc_num_guest_pages(vm->mode, KVM_VGIC_V3_DIST_SIZE);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment