Path: blob/master/tools/testing/selftests/kvm/lib/arm64/vgic.c
49657 views
// SPDX-License-Identifier: GPL-2.01/*2* ARM Generic Interrupt Controller (GIC) v3 host support3*/45#include <linux/kernel.h>6#include <linux/kvm.h>7#include <linux/sizes.h>8#include <asm/cputype.h>9#include <asm/kvm_para.h>10#include <asm/kvm.h>1112#include "kvm_util.h"13#include "vgic.h"14#include "gic.h"15#include "gic_v3.h"1617bool kvm_supports_vgic_v3(void)18{19struct kvm_vm *vm = vm_create_barebones();20int r;2122r = __kvm_test_create_device(vm, KVM_DEV_TYPE_ARM_VGIC_V3);23kvm_vm_free(vm);2425return !r;26}2728/*29* vGIC-v3 default host setup30*31* Input args:32* vm - KVM VM33* nr_vcpus - Number of vCPUs supported by this VM34*35* Output args: None36*37* Return: GIC file-descriptor or negative error code upon failure38*39* The function creates a vGIC-v3 device and maps the distributor and40* redistributor regions of the guest. Since it depends on the number of41* vCPUs for the VM, it must be called after all the vCPUs have been created.42*/43int __vgic_v3_setup(struct kvm_vm *vm, unsigned int nr_vcpus, uint32_t nr_irqs)44{45int gic_fd;46uint64_t attr;47unsigned int nr_gic_pages;4849/* Distributor setup */50gic_fd = __kvm_create_device(vm, KVM_DEV_TYPE_ARM_VGIC_V3);51if (gic_fd < 0)52return gic_fd;5354kvm_device_attr_set(gic_fd, KVM_DEV_ARM_VGIC_GRP_NR_IRQS, 0, &nr_irqs);5556attr = GICD_BASE_GPA;57kvm_device_attr_set(gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR,58KVM_VGIC_V3_ADDR_TYPE_DIST, &attr);59nr_gic_pages = vm_calc_num_guest_pages(vm->mode, KVM_VGIC_V3_DIST_SIZE);60virt_map(vm, GICD_BASE_GPA, GICD_BASE_GPA, nr_gic_pages);6162/* Redistributor setup */63attr = REDIST_REGION_ATTR_ADDR(nr_vcpus, GICR_BASE_GPA, 0, 0);64kvm_device_attr_set(gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR,65KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION, &attr);66nr_gic_pages = vm_calc_num_guest_pages(vm->mode,67KVM_VGIC_V3_REDIST_SIZE * nr_vcpus);68virt_map(vm, GICR_BASE_GPA, GICR_BASE_GPA, nr_gic_pages);6970return gic_fd;71}7273void __vgic_v3_init(int fd)74{75kvm_device_attr_set(fd, KVM_DEV_ARM_VGIC_GRP_CTRL,76KVM_DEV_ARM_VGIC_CTRL_INIT, NULL);77}7879int vgic_v3_setup(struct kvm_vm *vm, unsigned int nr_vcpus, uint32_t nr_irqs)80{81unsigned int nr_vcpus_created = 0;82struct list_head *iter;83int fd;8485TEST_ASSERT(nr_vcpus, "Number of vCPUs cannot be empty");8687/*88* Make sure that the caller is infact calling this89* function after all the vCPUs are added.90*/91list_for_each(iter, &vm->vcpus)92nr_vcpus_created++;93TEST_ASSERT(nr_vcpus == nr_vcpus_created,94"Number of vCPUs requested (%u) doesn't match with the ones created for the VM (%u)",95nr_vcpus, nr_vcpus_created);9697fd = __vgic_v3_setup(vm, nr_vcpus, nr_irqs);98if (fd < 0)99return fd;100101__vgic_v3_init(fd);102return fd;103}104105/* should only work for level sensitive interrupts */106int _kvm_irq_set_level_info(int gic_fd, uint32_t intid, int level)107{108uint64_t attr = 32 * (intid / 32);109uint64_t index = intid % 32;110uint64_t val;111int ret;112113ret = __kvm_device_attr_get(gic_fd, KVM_DEV_ARM_VGIC_GRP_LEVEL_INFO,114attr, &val);115if (ret != 0)116return ret;117118val |= 1U << index;119ret = __kvm_device_attr_set(gic_fd, KVM_DEV_ARM_VGIC_GRP_LEVEL_INFO,120attr, &val);121return ret;122}123124void kvm_irq_set_level_info(int gic_fd, uint32_t intid, int level)125{126int ret = _kvm_irq_set_level_info(gic_fd, intid, level);127128TEST_ASSERT(!ret, KVM_IOCTL_ERROR(KVM_DEV_ARM_VGIC_GRP_LEVEL_INFO, ret));129}130131int _kvm_arm_irq_line(struct kvm_vm *vm, uint32_t intid, int level)132{133uint32_t irq = intid & KVM_ARM_IRQ_NUM_MASK;134135TEST_ASSERT(!INTID_IS_SGI(intid), "KVM_IRQ_LINE's interface itself "136"doesn't allow injecting SGIs. There's no mask for it.");137138if (INTID_IS_PPI(intid))139irq |= KVM_ARM_IRQ_TYPE_PPI << KVM_ARM_IRQ_TYPE_SHIFT;140else141irq |= KVM_ARM_IRQ_TYPE_SPI << KVM_ARM_IRQ_TYPE_SHIFT;142143return _kvm_irq_line(vm, irq, level);144}145146void kvm_arm_irq_line(struct kvm_vm *vm, uint32_t intid, int level)147{148int ret = _kvm_arm_irq_line(vm, intid, level);149150TEST_ASSERT(!ret, KVM_IOCTL_ERROR(KVM_IRQ_LINE, ret));151}152153static void vgic_poke_irq(int gic_fd, uint32_t intid, struct kvm_vcpu *vcpu,154uint64_t reg_off)155{156uint64_t reg = intid / 32;157uint64_t index = intid % 32;158uint64_t attr = reg_off + reg * 4;159uint64_t val;160bool intid_is_private = INTID_IS_SGI(intid) || INTID_IS_PPI(intid);161162uint32_t group = intid_is_private ? KVM_DEV_ARM_VGIC_GRP_REDIST_REGS163: KVM_DEV_ARM_VGIC_GRP_DIST_REGS;164165if (intid_is_private) {166/* TODO: only vcpu 0 implemented for now. */167assert(vcpu->id == 0);168attr += SZ_64K;169}170171/* Check that the addr part of the attr is within 32 bits. */172assert((attr & ~KVM_DEV_ARM_VGIC_OFFSET_MASK) == 0);173174/*175* All calls will succeed, even with invalid intid's, as long as the176* addr part of the attr is within 32 bits (checked above). An invalid177* intid will just make the read/writes point to above the intended178* register space (i.e., ICPENDR after ISPENDR).179*/180kvm_device_attr_get(gic_fd, group, attr, &val);181val |= 1ULL << index;182kvm_device_attr_set(gic_fd, group, attr, &val);183}184185void kvm_irq_write_ispendr(int gic_fd, uint32_t intid, struct kvm_vcpu *vcpu)186{187vgic_poke_irq(gic_fd, intid, vcpu, GICD_ISPENDR);188}189190void kvm_irq_write_isactiver(int gic_fd, uint32_t intid, struct kvm_vcpu *vcpu)191{192vgic_poke_irq(gic_fd, intid, vcpu, GICD_ISACTIVER);193}194195int vgic_its_setup(struct kvm_vm *vm)196{197int its_fd = kvm_create_device(vm, KVM_DEV_TYPE_ARM_VGIC_ITS);198u64 attr;199200attr = GITS_BASE_GPA;201kvm_device_attr_set(its_fd, KVM_DEV_ARM_VGIC_GRP_ADDR,202KVM_VGIC_ITS_ADDR_TYPE, &attr);203204kvm_device_attr_set(its_fd, KVM_DEV_ARM_VGIC_GRP_CTRL,205KVM_DEV_ARM_VGIC_CTRL_INIT, NULL);206207virt_map(vm, GITS_BASE_GPA, GITS_BASE_GPA,208vm_calc_num_guest_pages(vm->mode, KVM_VGIC_V3_ITS_SIZE));209210return its_fd;211}212213214