mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
Merge branch kvm-arm64/rd-assignment-fixes into kvmarm-master/next
* kvm-arm64/rd-assignment-fixes: : \ : Fixes for failed GICv3 RD assignment to vcpu rollback. : : Patches by Karl Mehltretter. : / KVM: arm64: selftests: Test VGICv3 redistributor region retry KVM: arm64: selftests: Pass guest code to vm_gic_create_with_vcpus() KVM: arm64: vgic-v3: Roll back assignments from the new region KVM: arm64: vgic-v3: Undo assignment on iodev registration failure Signed-off-by: Marc Zyngier <maz@kernel.org>
This commit is contained in:
commit
6ec57ccbc5
|
|
@ -818,16 +818,21 @@ int vgic_register_redist_iodev(struct kvm_vcpu *vcpu)
|
|||
rd_dev->nr_regions = ARRAY_SIZE(vgic_v3_rd_registers);
|
||||
rd_dev->redist_vcpu = vcpu;
|
||||
|
||||
/* Protected by slots_lock */
|
||||
rdreg->free_index++;
|
||||
|
||||
mutex_unlock(&kvm->arch.config_lock);
|
||||
|
||||
ret = kvm_io_bus_register_dev(kvm, KVM_MMIO_BUS, rd_base,
|
||||
2 * SZ_64K, &rd_dev->dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (ret) {
|
||||
guard(mutex)(&vcpu->kvm->arch.config_lock);
|
||||
vgic_cpu->rdreg->free_index--;
|
||||
vgic_cpu->rdreg = NULL;
|
||||
vgic_cpu->rd_iodev.base_addr = VGIC_ADDR_UNDEF;
|
||||
}
|
||||
|
||||
/* Protected by slots_lock */
|
||||
rdreg->free_index++;
|
||||
return 0;
|
||||
return ret;
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&kvm->arch.config_lock);
|
||||
|
|
@ -841,7 +846,8 @@ void vgic_unregister_redist_iodev(struct kvm_vcpu *vcpu)
|
|||
kvm_io_bus_unregister_dev(vcpu->kvm, KVM_MMIO_BUS, &rd_dev->dev);
|
||||
}
|
||||
|
||||
static int vgic_register_all_redist_iodevs(struct kvm *kvm)
|
||||
static int vgic_register_all_redist_iodevs(struct kvm *kvm,
|
||||
struct vgic_redist_region *rdreg)
|
||||
{
|
||||
struct kvm_vcpu *vcpu;
|
||||
unsigned long c;
|
||||
|
|
@ -861,7 +867,14 @@ static int vgic_register_all_redist_iodevs(struct kvm *kvm)
|
|||
|
||||
for (i = 0; i < c; i++) {
|
||||
vcpu = kvm_get_vcpu(kvm, i);
|
||||
if (vcpu->arch.vgic_cpu.rdreg != rdreg)
|
||||
continue;
|
||||
|
||||
vgic_unregister_redist_iodev(vcpu);
|
||||
|
||||
guard(mutex)(&vcpu->kvm->arch.config_lock);
|
||||
vcpu->arch.vgic_cpu.rdreg = NULL;
|
||||
vcpu->arch.vgic_cpu.rd_iodev.base_addr = VGIC_ADDR_UNDEF;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -970,10 +983,13 @@ void vgic_v3_free_redist_region(struct kvm *kvm, struct vgic_redist_region *rdre
|
|||
|
||||
int vgic_v3_set_redist_base(struct kvm *kvm, u32 index, u64 addr, u32 count)
|
||||
{
|
||||
struct vgic_redist_region *rdreg;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&kvm->arch.config_lock);
|
||||
ret = vgic_v3_alloc_redist_region(kvm, index, addr, count);
|
||||
if (!ret)
|
||||
rdreg = vgic_v3_rdist_region_from_index(kvm, index);
|
||||
mutex_unlock(&kvm->arch.config_lock);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
|
@ -982,12 +998,9 @@ int vgic_v3_set_redist_base(struct kvm *kvm, u32 index, u64 addr, u32 count)
|
|||
* Register iodevs for each existing VCPU. Adding more VCPUs
|
||||
* afterwards will register the iodevs when needed.
|
||||
*/
|
||||
ret = vgic_register_all_redist_iodevs(kvm);
|
||||
ret = vgic_register_all_redist_iodevs(kvm, rdreg);
|
||||
if (ret) {
|
||||
struct vgic_redist_region *rdreg;
|
||||
|
||||
mutex_lock(&kvm->arch.config_lock);
|
||||
rdreg = vgic_v3_rdist_region_from_index(kvm, index);
|
||||
vgic_v3_free_redist_region(kvm, rdreg);
|
||||
mutex_unlock(&kvm->arch.config_lock);
|
||||
return ret;
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
* Copyright (C) 2020, Red Hat, Inc.
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/sizes.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <asm/kvm.h>
|
||||
#include <asm/kvm_para.h>
|
||||
|
|
@ -13,12 +14,21 @@
|
|||
|
||||
#include "test_util.h"
|
||||
#include "kvm_util.h"
|
||||
#include "gic.h"
|
||||
#include "processor.h"
|
||||
#include "vgic.h"
|
||||
#include "gic_v3.h"
|
||||
|
||||
#define NR_VCPUS 4
|
||||
|
||||
#define REDIST_RETRY_REGION0_BASE GICR_BASE_GPA
|
||||
#define REDIST_RETRY_REGION1_BASE \
|
||||
(REDIST_RETRY_REGION0_BASE + 2 * KVM_VGIC_V3_REDIST_SIZE)
|
||||
#define REDIST_RETRY_DIST_BASE \
|
||||
(REDIST_RETRY_REGION1_BASE + KVM_VGIC_V3_REDIST_SIZE)
|
||||
#define REDIST_RETRY_REGION2_BASE \
|
||||
(REDIST_RETRY_DIST_BASE + KVM_VGIC_V3_DIST_SIZE)
|
||||
|
||||
#define REG_OFFSET(vcpu, offset) (((u64)vcpu << 32) | offset)
|
||||
|
||||
#define VGIC_DEV_IS_V2(_d) ((_d) == KVM_DEV_TYPE_ARM_VGIC_V2)
|
||||
|
|
@ -65,6 +75,23 @@ static void guest_code(void)
|
|||
GUEST_DONE();
|
||||
}
|
||||
|
||||
static void guest_check_redist_retry(void)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
/* The first three redistributors span adjacent regions 0 and 1. */
|
||||
for (i = 0; i < NR_VCPUS; i++) {
|
||||
u64 base = i < 3 ? REDIST_RETRY_REGION0_BASE +
|
||||
i * KVM_VGIC_V3_REDIST_SIZE :
|
||||
REDIST_RETRY_REGION2_BASE;
|
||||
u64 typer = readq((void *)(unsigned long)(base + GICR_TYPER));
|
||||
|
||||
GUEST_ASSERT_EQ(GICR_TYPER_CPU_NUMBER(typer), i);
|
||||
}
|
||||
|
||||
GUEST_DONE();
|
||||
}
|
||||
|
||||
/* we don't want to assert on run execution, hence that helper */
|
||||
static int run_vcpu(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
|
|
@ -73,6 +100,7 @@ static int run_vcpu(struct kvm_vcpu *vcpu)
|
|||
|
||||
static struct vm_gic vm_gic_create_with_vcpus(u32 gic_dev_type,
|
||||
u32 nr_vcpus,
|
||||
void *guest_code,
|
||||
struct kvm_vcpu *vcpus[])
|
||||
{
|
||||
struct vm_gic v;
|
||||
|
|
@ -338,7 +366,7 @@ static void test_vgic_then_vcpus(u32 gic_dev_type)
|
|||
struct vm_gic v;
|
||||
int ret, i;
|
||||
|
||||
v = vm_gic_create_with_vcpus(gic_dev_type, 1, vcpus);
|
||||
v = vm_gic_create_with_vcpus(gic_dev_type, 1, guest_code, vcpus);
|
||||
|
||||
subtest_dist_rdist(&v);
|
||||
|
||||
|
|
@ -359,7 +387,8 @@ static void test_vcpus_then_vgic(u32 gic_dev_type)
|
|||
struct vm_gic v;
|
||||
int ret;
|
||||
|
||||
v = vm_gic_create_with_vcpus(gic_dev_type, NR_VCPUS, vcpus);
|
||||
v = vm_gic_create_with_vcpus(gic_dev_type, NR_VCPUS, guest_code,
|
||||
vcpus);
|
||||
|
||||
subtest_dist_rdist(&v);
|
||||
|
||||
|
|
@ -411,7 +440,8 @@ static void test_v3_new_redist_regions(void)
|
|||
u64 addr;
|
||||
int ret;
|
||||
|
||||
v = vm_gic_create_with_vcpus(KVM_DEV_TYPE_ARM_VGIC_V3, NR_VCPUS, vcpus);
|
||||
v = vm_gic_create_with_vcpus(KVM_DEV_TYPE_ARM_VGIC_V3, NR_VCPUS,
|
||||
guest_code, vcpus);
|
||||
subtest_v3_redist_regions(&v);
|
||||
kvm_device_attr_set(v.gic_fd, KVM_DEV_ARM_VGIC_GRP_CTRL,
|
||||
KVM_DEV_ARM_VGIC_CTRL_INIT, NULL);
|
||||
|
|
@ -422,7 +452,8 @@ static void test_v3_new_redist_regions(void)
|
|||
|
||||
/* step2 */
|
||||
|
||||
v = vm_gic_create_with_vcpus(KVM_DEV_TYPE_ARM_VGIC_V3, NR_VCPUS, vcpus);
|
||||
v = vm_gic_create_with_vcpus(KVM_DEV_TYPE_ARM_VGIC_V3, NR_VCPUS,
|
||||
guest_code, vcpus);
|
||||
subtest_v3_redist_regions(&v);
|
||||
|
||||
addr = REDIST_REGION_ATTR_ADDR(1, 0x280000, 0, 2);
|
||||
|
|
@ -436,7 +467,8 @@ static void test_v3_new_redist_regions(void)
|
|||
|
||||
/* step 3 */
|
||||
|
||||
v = vm_gic_create_with_vcpus(KVM_DEV_TYPE_ARM_VGIC_V3, NR_VCPUS, vcpus);
|
||||
v = vm_gic_create_with_vcpus(KVM_DEV_TYPE_ARM_VGIC_V3, NR_VCPUS,
|
||||
guest_code, vcpus);
|
||||
subtest_v3_redist_regions(&v);
|
||||
|
||||
ret = __kvm_device_attr_set(v.gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR,
|
||||
|
|
@ -457,6 +489,70 @@ static void test_v3_new_redist_regions(void)
|
|||
vm_gic_destroy(&v);
|
||||
}
|
||||
|
||||
static void test_v3_redist_region_retry(void)
|
||||
{
|
||||
struct kvm_vcpu *vcpus[NR_VCPUS];
|
||||
struct vm_gic v;
|
||||
struct ucall uc;
|
||||
u64 addr;
|
||||
int ret;
|
||||
|
||||
v = vm_gic_create_with_vcpus(KVM_DEV_TYPE_ARM_VGIC_V3, NR_VCPUS,
|
||||
guest_check_redist_retry, vcpus);
|
||||
|
||||
addr = REDIST_REGION_ATTR_ADDR(2, REDIST_RETRY_REGION0_BASE, 0, 0);
|
||||
kvm_device_attr_set(v.gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR,
|
||||
KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION, &addr);
|
||||
|
||||
addr = REDIST_REGION_ATTR_ADDR(1, REDIST_RETRY_REGION1_BASE, 0, 1);
|
||||
kvm_device_attr_set(v.gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR,
|
||||
KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION, &addr);
|
||||
|
||||
addr = REDIST_RETRY_DIST_BASE;
|
||||
kvm_device_attr_set(v.gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR,
|
||||
KVM_VGIC_V3_ADDR_TYPE_DIST, &addr);
|
||||
|
||||
addr = REDIST_REGION_ATTR_ADDR(1, REDIST_RETRY_DIST_BASE, 0, 2);
|
||||
ret = __kvm_device_attr_set(v.gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR,
|
||||
KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION,
|
||||
&addr);
|
||||
TEST_ASSERT(ret && errno == EINVAL,
|
||||
"register redist region colliding with dist");
|
||||
|
||||
addr = REDIST_REGION_ATTR_ADDR(1, REDIST_RETRY_REGION2_BASE, 0, 2);
|
||||
kvm_device_attr_set(v.gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR,
|
||||
KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION, &addr);
|
||||
|
||||
virt_map(v.vm, REDIST_RETRY_REGION0_BASE, REDIST_RETRY_REGION0_BASE,
|
||||
vm_calc_num_guest_pages(v.vm->mode,
|
||||
3 * KVM_VGIC_V3_REDIST_SIZE));
|
||||
virt_map(v.vm, REDIST_RETRY_REGION2_BASE, REDIST_RETRY_REGION2_BASE,
|
||||
vm_calc_num_guest_pages(v.vm->mode,
|
||||
KVM_VGIC_V3_REDIST_SIZE));
|
||||
|
||||
kvm_device_attr_set(v.gic_fd, KVM_DEV_ARM_VGIC_GRP_CTRL,
|
||||
KVM_DEV_ARM_VGIC_CTRL_INIT, NULL);
|
||||
|
||||
vcpu_run(vcpus[0]);
|
||||
switch (get_ucall(vcpus[0], &uc)) {
|
||||
case UCALL_DONE:
|
||||
break;
|
||||
case UCALL_ABORT:
|
||||
REPORT_GUEST_ASSERT(uc);
|
||||
break;
|
||||
case UCALL_NONE:
|
||||
if (vcpus[0]->run->exit_reason == KVM_EXIT_MMIO)
|
||||
TEST_FAIL("Unexpected MMIO exit at 0x%llx",
|
||||
vcpus[0]->run->mmio.phys_addr);
|
||||
fallthrough;
|
||||
default:
|
||||
TEST_FAIL("Unexpected ucall %lu, exit_reason %u",
|
||||
uc.cmd, vcpus[0]->run->exit_reason);
|
||||
}
|
||||
|
||||
vm_gic_destroy(&v);
|
||||
}
|
||||
|
||||
static void test_v3_typer_accesses(void)
|
||||
{
|
||||
struct vm_gic v;
|
||||
|
|
@ -608,7 +704,8 @@ static void test_v3_redist_ipa_range_check_at_vcpu_run(void)
|
|||
int ret, i;
|
||||
u64 addr;
|
||||
|
||||
v = vm_gic_create_with_vcpus(KVM_DEV_TYPE_ARM_VGIC_V3, 1, vcpus);
|
||||
v = vm_gic_create_with_vcpus(KVM_DEV_TYPE_ARM_VGIC_V3, 1, guest_code,
|
||||
vcpus);
|
||||
|
||||
/* Set space for 3 redists, we have 1 vcpu, so this succeeds. */
|
||||
addr = max_phys_size - (3 * 2 * 0x10000);
|
||||
|
|
@ -641,7 +738,8 @@ static void test_v3_its_region(void)
|
|||
u64 addr;
|
||||
int its_fd, ret;
|
||||
|
||||
v = vm_gic_create_with_vcpus(KVM_DEV_TYPE_ARM_VGIC_V3, NR_VCPUS, vcpus);
|
||||
v = vm_gic_create_with_vcpus(KVM_DEV_TYPE_ARM_VGIC_V3, NR_VCPUS,
|
||||
guest_code, vcpus);
|
||||
its_fd = kvm_create_device(v.vm, KVM_DEV_TYPE_ARM_VGIC_ITS);
|
||||
|
||||
addr = 0x401000;
|
||||
|
|
@ -684,7 +782,8 @@ static void test_v3_nassgicap(void)
|
|||
u32 typer2;
|
||||
int ret;
|
||||
|
||||
vm = vm_gic_create_with_vcpus(KVM_DEV_TYPE_ARM_VGIC_V3, NR_VCPUS, vcpus);
|
||||
vm = vm_gic_create_with_vcpus(KVM_DEV_TYPE_ARM_VGIC_V3, NR_VCPUS,
|
||||
guest_code, vcpus);
|
||||
kvm_device_attr_get(vm.gic_fd, KVM_DEV_ARM_VGIC_GRP_DIST_REGS,
|
||||
GICD_TYPER2, &typer2);
|
||||
has_nassgicap = typer2 & GICD_TYPER2_nASSGIcap;
|
||||
|
|
@ -978,6 +1077,7 @@ void run_tests(u32 gic_dev_type)
|
|||
|
||||
if (VGIC_DEV_IS_V3(gic_dev_type)) {
|
||||
test_v3_new_redist_regions();
|
||||
test_v3_redist_region_retry();
|
||||
test_v3_typer_accesses();
|
||||
test_v3_last_bit_redist_regions();
|
||||
test_v3_last_bit_single_rdist();
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user