1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * ARM Generic Interrupt Controller (GIC) v3 host support 4 */ 5 6 #include <linux/kernel.h> 7 #include <linux/kvm.h> 8 #include <linux/sizes.h> 9 #include <asm/cputype.h> 10 #include <asm/kvm_para.h> 11 #include <asm/kvm.h> 12 13 #include "kvm_util.h" 14 #include "vgic.h" 15 #include "gic.h" 16 #include "gic_v3.h" 17 18 /* 19 * vGIC-v3 default host setup 20 * 21 * Input args: 22 * vm - KVM VM 23 * nr_vcpus - Number of vCPUs supported by this VM 24 * 25 * Output args: None 26 * 27 * Return: GIC file-descriptor or negative error code upon failure 28 * 29 * The function creates a vGIC-v3 device and maps the distributor and 30 * redistributor regions of the guest. Since it depends on the number of 31 * vCPUs for the VM, it must be called after all the vCPUs have been created. 32 */ 33 int vgic_v3_setup(struct kvm_vm *vm, unsigned int nr_vcpus, uint32_t nr_irqs) 34 { 35 int gic_fd; 36 uint64_t attr; 37 struct list_head *iter; 38 unsigned int nr_gic_pages, nr_vcpus_created = 0; 39 40 TEST_ASSERT(nr_vcpus, "Number of vCPUs cannot be empty"); 41 42 /* 43 * Make sure that the caller is infact calling this 44 * function after all the vCPUs are added. 45 */ 46 list_for_each(iter, &vm->vcpus) 47 nr_vcpus_created++; 48 TEST_ASSERT(nr_vcpus == nr_vcpus_created, 49 "Number of vCPUs requested (%u) doesn't match with the ones created for the VM (%u)", 50 nr_vcpus, nr_vcpus_created); 51 52 /* Distributor setup */ 53 gic_fd = __kvm_create_device(vm, KVM_DEV_TYPE_ARM_VGIC_V3); 54 if (gic_fd < 0) 55 return gic_fd; 56 57 kvm_device_attr_set(gic_fd, KVM_DEV_ARM_VGIC_GRP_NR_IRQS, 0, &nr_irqs); 58 59 kvm_device_attr_set(gic_fd, KVM_DEV_ARM_VGIC_GRP_CTRL, 60 KVM_DEV_ARM_VGIC_CTRL_INIT, NULL); 61 62 attr = GICD_BASE_GPA; 63 kvm_device_attr_set(gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, 64 KVM_VGIC_V3_ADDR_TYPE_DIST, &attr); 65 nr_gic_pages = vm_calc_num_guest_pages(vm->mode, KVM_VGIC_V3_DIST_SIZE); 66 virt_map(vm, GICD_BASE_GPA, GICD_BASE_GPA, nr_gic_pages); 67 68 /* Redistributor setup */ 69 attr = REDIST_REGION_ATTR_ADDR(nr_vcpus, GICR_BASE_GPA, 0, 0); 70 kvm_device_attr_set(gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, 71 KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION, &attr); 72 nr_gic_pages = vm_calc_num_guest_pages(vm->mode, 73 KVM_VGIC_V3_REDIST_SIZE * nr_vcpus); 74 virt_map(vm, GICR_BASE_GPA, GICR_BASE_GPA, nr_gic_pages); 75 76 kvm_device_attr_set(gic_fd, KVM_DEV_ARM_VGIC_GRP_CTRL, 77 KVM_DEV_ARM_VGIC_CTRL_INIT, NULL); 78 79 return gic_fd; 80 } 81 82 /* should only work for level sensitive interrupts */ 83 int _kvm_irq_set_level_info(int gic_fd, uint32_t intid, int level) 84 { 85 uint64_t attr = 32 * (intid / 32); 86 uint64_t index = intid % 32; 87 uint64_t val; 88 int ret; 89 90 ret = __kvm_device_attr_get(gic_fd, KVM_DEV_ARM_VGIC_GRP_LEVEL_INFO, 91 attr, &val); 92 if (ret != 0) 93 return ret; 94 95 val |= 1U << index; 96 ret = __kvm_device_attr_set(gic_fd, KVM_DEV_ARM_VGIC_GRP_LEVEL_INFO, 97 attr, &val); 98 return ret; 99 } 100 101 void kvm_irq_set_level_info(int gic_fd, uint32_t intid, int level) 102 { 103 int ret = _kvm_irq_set_level_info(gic_fd, intid, level); 104 105 TEST_ASSERT(!ret, KVM_IOCTL_ERROR(KVM_DEV_ARM_VGIC_GRP_LEVEL_INFO, ret)); 106 } 107 108 int _kvm_arm_irq_line(struct kvm_vm *vm, uint32_t intid, int level) 109 { 110 uint32_t irq = intid & KVM_ARM_IRQ_NUM_MASK; 111 112 TEST_ASSERT(!INTID_IS_SGI(intid), "KVM_IRQ_LINE's interface itself " 113 "doesn't allow injecting SGIs. There's no mask for it."); 114 115 if (INTID_IS_PPI(intid)) 116 irq |= KVM_ARM_IRQ_TYPE_PPI << KVM_ARM_IRQ_TYPE_SHIFT; 117 else 118 irq |= KVM_ARM_IRQ_TYPE_SPI << KVM_ARM_IRQ_TYPE_SHIFT; 119 120 return _kvm_irq_line(vm, irq, level); 121 } 122 123 void kvm_arm_irq_line(struct kvm_vm *vm, uint32_t intid, int level) 124 { 125 int ret = _kvm_arm_irq_line(vm, intid, level); 126 127 TEST_ASSERT(!ret, KVM_IOCTL_ERROR(KVM_IRQ_LINE, ret)); 128 } 129 130 static void vgic_poke_irq(int gic_fd, uint32_t intid, struct kvm_vcpu *vcpu, 131 uint64_t reg_off) 132 { 133 uint64_t reg = intid / 32; 134 uint64_t index = intid % 32; 135 uint64_t attr = reg_off + reg * 4; 136 uint64_t val; 137 bool intid_is_private = INTID_IS_SGI(intid) || INTID_IS_PPI(intid); 138 139 uint32_t group = intid_is_private ? KVM_DEV_ARM_VGIC_GRP_REDIST_REGS 140 : KVM_DEV_ARM_VGIC_GRP_DIST_REGS; 141 142 if (intid_is_private) { 143 /* TODO: only vcpu 0 implemented for now. */ 144 assert(vcpu->id == 0); 145 attr += SZ_64K; 146 } 147 148 /* Check that the addr part of the attr is within 32 bits. */ 149 assert((attr & ~KVM_DEV_ARM_VGIC_OFFSET_MASK) == 0); 150 151 /* 152 * All calls will succeed, even with invalid intid's, as long as the 153 * addr part of the attr is within 32 bits (checked above). An invalid 154 * intid will just make the read/writes point to above the intended 155 * register space (i.e., ICPENDR after ISPENDR). 156 */ 157 kvm_device_attr_get(gic_fd, group, attr, &val); 158 val |= 1ULL << index; 159 kvm_device_attr_set(gic_fd, group, attr, &val); 160 } 161 162 void kvm_irq_write_ispendr(int gic_fd, uint32_t intid, struct kvm_vcpu *vcpu) 163 { 164 vgic_poke_irq(gic_fd, intid, vcpu, GICD_ISPENDR); 165 } 166 167 void kvm_irq_write_isactiver(int gic_fd, uint32_t intid, struct kvm_vcpu *vcpu) 168 { 169 vgic_poke_irq(gic_fd, intid, vcpu, GICD_ISACTIVER); 170 } 171 172 int vgic_its_setup(struct kvm_vm *vm) 173 { 174 int its_fd = kvm_create_device(vm, KVM_DEV_TYPE_ARM_VGIC_ITS); 175 u64 attr; 176 177 attr = GITS_BASE_GPA; 178 kvm_device_attr_set(its_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, 179 KVM_VGIC_ITS_ADDR_TYPE, &attr); 180 181 kvm_device_attr_set(its_fd, KVM_DEV_ARM_VGIC_GRP_CTRL, 182 KVM_DEV_ARM_VGIC_CTRL_INIT, NULL); 183 184 virt_map(vm, GITS_BASE_GPA, GITS_BASE_GPA, 185 vm_calc_num_guest_pages(vm->mode, KVM_VGIC_V3_ITS_SIZE)); 186 187 return its_fd; 188 } 189
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.