mirror of
https://github.com/torvalds/linux.git
synced 2026-05-24 23:22:31 +02:00
Use u32 instead of uint32_t to make the KVM selftests code more concise and more similar to the kernel (since selftests are primarily developed by kernel developers). This commit was generated with the following command: git ls-files tools/testing/selftests/kvm | xargs sed -i 's/uint32_t/u32/g' Then by manually adjusting whitespace to make checkpatch.pl happy. No functional change intended. Signed-off-by: David Matlack <dmatlack@google.com> Link: https://patch.msgid.link/20260420212004.3938325-7-seanjc@google.com Signed-off-by: Sean Christopherson <seanjc@google.com>
213 lines
5.5 KiB
C
213 lines
5.5 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* ARM Generic Interrupt Controller (GIC) v3 host support
|
|
*/
|
|
|
|
#include <linux/kernel.h>
|
|
#include <linux/kvm.h>
|
|
#include <linux/sizes.h>
|
|
#include <asm/cputype.h>
|
|
#include <asm/kvm_para.h>
|
|
#include <asm/kvm.h>
|
|
|
|
#include "kvm_util.h"
|
|
#include "vgic.h"
|
|
#include "gic.h"
|
|
#include "gic_v3.h"
|
|
|
|
bool kvm_supports_vgic_v3(void)
|
|
{
|
|
struct kvm_vm *vm = vm_create_barebones();
|
|
int r;
|
|
|
|
r = __kvm_test_create_device(vm, KVM_DEV_TYPE_ARM_VGIC_V3);
|
|
kvm_vm_free(vm);
|
|
|
|
return !r;
|
|
}
|
|
|
|
/*
|
|
* vGIC-v3 default host setup
|
|
*
|
|
* Input args:
|
|
* vm - KVM VM
|
|
* nr_vcpus - Number of vCPUs supported by this VM
|
|
*
|
|
* Output args: None
|
|
*
|
|
* Return: GIC file-descriptor or negative error code upon failure
|
|
*
|
|
* The function creates a vGIC-v3 device and maps the distributor and
|
|
* 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.
|
|
*/
|
|
int __vgic_v3_setup(struct kvm_vm *vm, unsigned int nr_vcpus, u32 nr_irqs)
|
|
{
|
|
int gic_fd;
|
|
u64 attr;
|
|
unsigned int nr_gic_pages;
|
|
|
|
/* Distributor setup */
|
|
gic_fd = __kvm_create_device(vm, KVM_DEV_TYPE_ARM_VGIC_V3);
|
|
if (gic_fd < 0)
|
|
return gic_fd;
|
|
|
|
kvm_device_attr_set(gic_fd, KVM_DEV_ARM_VGIC_GRP_NR_IRQS, 0, &nr_irqs);
|
|
|
|
attr = GICD_BASE_GPA;
|
|
kvm_device_attr_set(gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR,
|
|
KVM_VGIC_V3_ADDR_TYPE_DIST, &attr);
|
|
nr_gic_pages = vm_calc_num_guest_pages(vm->mode, KVM_VGIC_V3_DIST_SIZE);
|
|
virt_map(vm, GICD_BASE_GPA, GICD_BASE_GPA, nr_gic_pages);
|
|
|
|
/* Redistributor setup */
|
|
attr = REDIST_REGION_ATTR_ADDR(nr_vcpus, GICR_BASE_GPA, 0, 0);
|
|
kvm_device_attr_set(gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR,
|
|
KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION, &attr);
|
|
nr_gic_pages = vm_calc_num_guest_pages(vm->mode,
|
|
KVM_VGIC_V3_REDIST_SIZE * nr_vcpus);
|
|
virt_map(vm, GICR_BASE_GPA, GICR_BASE_GPA, nr_gic_pages);
|
|
|
|
return gic_fd;
|
|
}
|
|
|
|
void __vgic_v3_init(int fd)
|
|
{
|
|
kvm_device_attr_set(fd, KVM_DEV_ARM_VGIC_GRP_CTRL,
|
|
KVM_DEV_ARM_VGIC_CTRL_INIT, NULL);
|
|
}
|
|
|
|
int vgic_v3_setup(struct kvm_vm *vm, unsigned int nr_vcpus, u32 nr_irqs)
|
|
{
|
|
unsigned int nr_vcpus_created = 0;
|
|
struct list_head *iter;
|
|
int fd;
|
|
|
|
TEST_ASSERT(nr_vcpus, "Number of vCPUs cannot be empty");
|
|
|
|
/*
|
|
* Make sure that the caller is infact calling this
|
|
* function after all the vCPUs are added.
|
|
*/
|
|
list_for_each(iter, &vm->vcpus)
|
|
nr_vcpus_created++;
|
|
TEST_ASSERT(nr_vcpus == nr_vcpus_created,
|
|
"Number of vCPUs requested (%u) doesn't match with the ones created for the VM (%u)",
|
|
nr_vcpus, nr_vcpus_created);
|
|
|
|
fd = __vgic_v3_setup(vm, nr_vcpus, nr_irqs);
|
|
if (fd < 0)
|
|
return fd;
|
|
|
|
__vgic_v3_init(fd);
|
|
return fd;
|
|
}
|
|
|
|
/* should only work for level sensitive interrupts */
|
|
int _kvm_irq_set_level_info(int gic_fd, u32 intid, int level)
|
|
{
|
|
u64 attr = 32 * (intid / 32);
|
|
u64 index = intid % 32;
|
|
u64 val;
|
|
int ret;
|
|
|
|
ret = __kvm_device_attr_get(gic_fd, KVM_DEV_ARM_VGIC_GRP_LEVEL_INFO,
|
|
attr, &val);
|
|
if (ret != 0)
|
|
return ret;
|
|
|
|
val |= 1U << index;
|
|
ret = __kvm_device_attr_set(gic_fd, KVM_DEV_ARM_VGIC_GRP_LEVEL_INFO,
|
|
attr, &val);
|
|
return ret;
|
|
}
|
|
|
|
void kvm_irq_set_level_info(int gic_fd, u32 intid, int level)
|
|
{
|
|
int ret = _kvm_irq_set_level_info(gic_fd, intid, level);
|
|
|
|
TEST_ASSERT(!ret, KVM_IOCTL_ERROR(KVM_DEV_ARM_VGIC_GRP_LEVEL_INFO, ret));
|
|
}
|
|
|
|
int _kvm_arm_irq_line(struct kvm_vm *vm, u32 intid, int level)
|
|
{
|
|
u32 irq = intid & KVM_ARM_IRQ_NUM_MASK;
|
|
|
|
TEST_ASSERT(!INTID_IS_SGI(intid), "KVM_IRQ_LINE's interface itself "
|
|
"doesn't allow injecting SGIs. There's no mask for it.");
|
|
|
|
if (INTID_IS_PPI(intid))
|
|
irq |= KVM_ARM_IRQ_TYPE_PPI << KVM_ARM_IRQ_TYPE_SHIFT;
|
|
else
|
|
irq |= KVM_ARM_IRQ_TYPE_SPI << KVM_ARM_IRQ_TYPE_SHIFT;
|
|
|
|
return _kvm_irq_line(vm, irq, level);
|
|
}
|
|
|
|
void kvm_arm_irq_line(struct kvm_vm *vm, u32 intid, int level)
|
|
{
|
|
int ret = _kvm_arm_irq_line(vm, intid, level);
|
|
|
|
TEST_ASSERT(!ret, KVM_IOCTL_ERROR(KVM_IRQ_LINE, ret));
|
|
}
|
|
|
|
static void vgic_poke_irq(int gic_fd, u32 intid, struct kvm_vcpu *vcpu,
|
|
u64 reg_off)
|
|
{
|
|
u64 reg = intid / 32;
|
|
u64 index = intid % 32;
|
|
u64 attr = reg_off + reg * 4;
|
|
u64 val;
|
|
bool intid_is_private = INTID_IS_SGI(intid) || INTID_IS_PPI(intid);
|
|
|
|
u32 group = intid_is_private ? KVM_DEV_ARM_VGIC_GRP_REDIST_REGS
|
|
: KVM_DEV_ARM_VGIC_GRP_DIST_REGS;
|
|
|
|
if (intid_is_private) {
|
|
/* TODO: only vcpu 0 implemented for now. */
|
|
assert(vcpu->id == 0);
|
|
attr += SZ_64K;
|
|
}
|
|
|
|
/* Check that the addr part of the attr is within 32 bits. */
|
|
assert((attr & ~KVM_DEV_ARM_VGIC_OFFSET_MASK) == 0);
|
|
|
|
/*
|
|
* All calls will succeed, even with invalid intid's, as long as the
|
|
* addr part of the attr is within 32 bits (checked above). An invalid
|
|
* intid will just make the read/writes point to above the intended
|
|
* register space (i.e., ICPENDR after ISPENDR).
|
|
*/
|
|
kvm_device_attr_get(gic_fd, group, attr, &val);
|
|
val |= 1ULL << index;
|
|
kvm_device_attr_set(gic_fd, group, attr, &val);
|
|
}
|
|
|
|
void kvm_irq_write_ispendr(int gic_fd, u32 intid, struct kvm_vcpu *vcpu)
|
|
{
|
|
vgic_poke_irq(gic_fd, intid, vcpu, GICD_ISPENDR);
|
|
}
|
|
|
|
void kvm_irq_write_isactiver(int gic_fd, u32 intid, struct kvm_vcpu *vcpu)
|
|
{
|
|
vgic_poke_irq(gic_fd, intid, vcpu, GICD_ISACTIVER);
|
|
}
|
|
|
|
int vgic_its_setup(struct kvm_vm *vm)
|
|
{
|
|
int its_fd = kvm_create_device(vm, KVM_DEV_TYPE_ARM_VGIC_ITS);
|
|
u64 attr;
|
|
|
|
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);
|
|
|
|
virt_map(vm, GITS_BASE_GPA, GITS_BASE_GPA,
|
|
vm_calc_num_guest_pages(vm->mode, KVM_VGIC_V3_ITS_SIZE));
|
|
|
|
return its_fd;
|
|
}
|