Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at master 228 lines 4.9 kB view raw
1// SPDX-License-Identifier: GPL-2.0 2 3#include <linux/kernel.h> 4#include <sys/syscall.h> 5#include <asm/kvm.h> 6#include <asm/kvm_para.h> 7 8#include <arm64/gic_v5.h> 9 10#include "test_util.h" 11#include "kvm_util.h" 12#include "processor.h" 13#include "vgic.h" 14 15#define NR_VCPUS 1 16 17struct vm_gic { 18 struct kvm_vm *vm; 19 int gic_fd; 20 u32 gic_dev_type; 21}; 22 23static u64 max_phys_size; 24 25#define GUEST_CMD_IRQ_CDIA 10 26#define GUEST_CMD_IRQ_DIEOI 11 27#define GUEST_CMD_IS_AWAKE 12 28#define GUEST_CMD_IS_READY 13 29 30static void guest_irq_handler(struct ex_regs *regs) 31{ 32 bool valid; 33 u32 hwirq; 34 u64 ia; 35 static int count; 36 37 /* 38 * We have pending interrupts. Should never actually enter WFI 39 * here! 40 */ 41 wfi(); 42 GUEST_SYNC(GUEST_CMD_IS_AWAKE); 43 44 ia = gicr_insn(CDIA); 45 valid = GICV5_GICR_CDIA_VALID(ia); 46 47 GUEST_SYNC(GUEST_CMD_IRQ_CDIA); 48 49 if (!valid) 50 return; 51 52 gsb_ack(); 53 isb(); 54 55 hwirq = FIELD_GET(GICV5_GICR_CDIA_INTID, ia); 56 57 gic_insn(hwirq, CDDI); 58 gic_insn(0, CDEOI); 59 60 GUEST_SYNC(GUEST_CMD_IRQ_DIEOI); 61 62 if (++count >= 2) 63 GUEST_DONE(); 64 65 /* Ask for the next interrupt to be injected */ 66 GUEST_SYNC(GUEST_CMD_IS_READY); 67} 68 69static void guest_code(void) 70{ 71 local_irq_disable(); 72 73 gicv5_cpu_enable_interrupts(); 74 local_irq_enable(); 75 76 /* Enable the SW_PPI (3) */ 77 write_sysreg_s(BIT_ULL(3), SYS_ICC_PPI_ENABLER0_EL1); 78 79 /* Ask for the first interrupt to be injected */ 80 GUEST_SYNC(GUEST_CMD_IS_READY); 81 82 /* Loop forever waiting for interrupts */ 83 while (1); 84} 85 86 87/* we don't want to assert on run execution, hence that helper */ 88static int run_vcpu(struct kvm_vcpu *vcpu) 89{ 90 return __vcpu_run(vcpu) ? -errno : 0; 91} 92 93static void vm_gic_destroy(struct vm_gic *v) 94{ 95 close(v->gic_fd); 96 kvm_vm_free(v->vm); 97} 98 99static void test_vgic_v5_ppis(u32 gic_dev_type) 100{ 101 struct kvm_vcpu *vcpus[NR_VCPUS]; 102 struct ucall uc; 103 u64 user_ppis[2]; 104 struct vm_gic v; 105 int ret, i; 106 107 v.gic_dev_type = gic_dev_type; 108 v.vm = __vm_create(VM_SHAPE_DEFAULT, NR_VCPUS, 0); 109 110 v.gic_fd = kvm_create_device(v.vm, gic_dev_type); 111 112 for (i = 0; i < NR_VCPUS; i++) 113 vcpus[i] = vm_vcpu_add(v.vm, i, guest_code); 114 115 vm_init_descriptor_tables(v.vm); 116 vm_install_exception_handler(v.vm, VECTOR_IRQ_CURRENT, guest_irq_handler); 117 118 for (i = 0; i < NR_VCPUS; i++) 119 vcpu_init_descriptor_tables(vcpus[i]); 120 121 kvm_device_attr_set(v.gic_fd, KVM_DEV_ARM_VGIC_GRP_CTRL, 122 KVM_DEV_ARM_VGIC_CTRL_INIT, NULL); 123 124 /* Read out the PPIs that user space is allowed to drive. */ 125 kvm_device_attr_get(v.gic_fd, KVM_DEV_ARM_VGIC_GRP_CTRL, 126 KVM_DEV_ARM_VGIC_USERSPACE_PPIS, &user_ppis); 127 128 /* We should always be able to drive the SW_PPI. */ 129 TEST_ASSERT(user_ppis[0] & BIT(GICV5_ARCH_PPI_SW_PPI), 130 "SW_PPI is not drivable by userspace"); 131 132 while (1) { 133 ret = run_vcpu(vcpus[0]); 134 135 switch (get_ucall(vcpus[0], &uc)) { 136 case UCALL_SYNC: 137 /* 138 * The guest is ready for the next level change. Set 139 * high if ready, and lower if it has been consumed. 140 */ 141 if (uc.args[1] == GUEST_CMD_IS_READY || 142 uc.args[1] == GUEST_CMD_IRQ_DIEOI) { 143 u64 irq; 144 bool level = uc.args[1] == GUEST_CMD_IRQ_DIEOI ? 0 : 1; 145 146 irq = FIELD_PREP(KVM_ARM_IRQ_NUM_MASK, 3); 147 irq |= KVM_ARM_IRQ_TYPE_PPI << KVM_ARM_IRQ_TYPE_SHIFT; 148 149 _kvm_irq_line(v.vm, irq, level); 150 } else if (uc.args[1] == GUEST_CMD_IS_AWAKE) { 151 pr_info("Guest skipping WFI due to pending IRQ\n"); 152 } else if (uc.args[1] == GUEST_CMD_IRQ_CDIA) { 153 pr_info("Guest acknowledged IRQ\n"); 154 } 155 156 continue; 157 case UCALL_ABORT: 158 REPORT_GUEST_ASSERT(uc); 159 break; 160 case UCALL_DONE: 161 goto done; 162 default: 163 TEST_FAIL("Unknown ucall %lu", uc.cmd); 164 } 165 } 166 167done: 168 TEST_ASSERT(ret == 0, "Failed to test GICv5 PPIs"); 169 170 vm_gic_destroy(&v); 171} 172 173/* 174 * Returns 0 if it's possible to create GIC device of a given type (V5). 175 */ 176int test_kvm_device(u32 gic_dev_type) 177{ 178 struct kvm_vcpu *vcpus[NR_VCPUS]; 179 struct vm_gic v; 180 int ret; 181 182 v.vm = vm_create_with_vcpus(NR_VCPUS, guest_code, vcpus); 183 184 /* try to create a non existing KVM device */ 185 ret = __kvm_test_create_device(v.vm, 0); 186 TEST_ASSERT(ret && errno == ENODEV, "unsupported device"); 187 188 /* trial mode */ 189 ret = __kvm_test_create_device(v.vm, gic_dev_type); 190 if (ret) 191 return ret; 192 v.gic_fd = kvm_create_device(v.vm, gic_dev_type); 193 194 ret = __kvm_create_device(v.vm, gic_dev_type); 195 TEST_ASSERT(ret < 0 && errno == EEXIST, "create GIC device twice"); 196 197 vm_gic_destroy(&v); 198 199 return 0; 200} 201 202void run_tests(u32 gic_dev_type) 203{ 204 pr_info("Test VGICv5 PPIs\n"); 205 test_vgic_v5_ppis(gic_dev_type); 206} 207 208int main(int ac, char **av) 209{ 210 int ret; 211 int pa_bits; 212 213 test_disable_default_vgic(); 214 215 pa_bits = vm_guest_mode_params[VM_MODE_DEFAULT].pa_bits; 216 max_phys_size = 1ULL << pa_bits; 217 218 ret = test_kvm_device(KVM_DEV_TYPE_ARM_VGIC_V5); 219 if (ret) { 220 pr_info("No GICv5 support; Not running GIC_v5 tests.\n"); 221 exit(KSFT_SKIP); 222 } 223 224 pr_info("Running VGIC_V5 tests.\n"); 225 run_tests(KVM_DEV_TYPE_ARM_VGIC_V5); 226 227 return 0; 228}