1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * ARM Generic Interrupt Controller (GIC) support 4 */ 5 6 #include <errno.h> 7 #include <linux/bits.h> 8 #include <linux/sizes.h> 9 10 #include "kvm_util.h" 11 12 #include <gic.h> 13 #include "gic_private.h" 14 #include "processor.h" 15 #include "spinlock.h" 16 17 static const struct gic_common_ops *gic_common_ops; 18 static struct spinlock gic_lock; 19 20 static void gic_cpu_init(unsigned int cpu, void *redist_base) 21 { 22 gic_common_ops->gic_cpu_init(cpu, redist_base); 23 } 24 25 static void 26 gic_dist_init(enum gic_type type, unsigned int nr_cpus, void *dist_base) 27 { 28 const struct gic_common_ops *gic_ops = NULL; 29 30 spin_lock(&gic_lock); 31 32 /* Distributor initialization is needed only once per VM */ 33 if (gic_common_ops) { 34 spin_unlock(&gic_lock); 35 return; 36 } 37 38 if (type == GIC_V3) 39 gic_ops = &gicv3_ops; 40 41 GUEST_ASSERT(gic_ops); 42 43 gic_ops->gic_init(nr_cpus, dist_base); 44 gic_common_ops = gic_ops; 45 46 /* Make sure that the initialized data is visible to all the vCPUs */ 47 dsb(sy); 48 49 spin_unlock(&gic_lock); 50 } 51 52 void gic_init(enum gic_type type, unsigned int nr_cpus, 53 void *dist_base, void *redist_base) 54 { 55 uint32_t cpu = guest_get_vcpuid(); 56 57 GUEST_ASSERT(type < GIC_TYPE_MAX); 58 GUEST_ASSERT(dist_base); 59 GUEST_ASSERT(redist_base); 60 GUEST_ASSERT(nr_cpus); 61 62 gic_dist_init(type, nr_cpus, dist_base); 63 gic_cpu_init(cpu, redist_base); 64 } 65 66 void gic_irq_enable(unsigned int intid) 67 { 68 GUEST_ASSERT(gic_common_ops); 69 gic_common_ops->gic_irq_enable(intid); 70 } 71 72 void gic_irq_disable(unsigned int intid) 73 { 74 GUEST_ASSERT(gic_common_ops); 75 gic_common_ops->gic_irq_disable(intid); 76 } 77 78 unsigned int gic_get_and_ack_irq(void) 79 { 80 uint64_t irqstat; 81 unsigned int intid; 82 83 GUEST_ASSERT(gic_common_ops); 84 85 irqstat = gic_common_ops->gic_read_iar(); 86 intid = irqstat & GENMASK(23, 0); 87 88 return intid; 89 } 90 91 void gic_set_eoi(unsigned int intid) 92 { 93 GUEST_ASSERT(gic_common_ops); 94 gic_common_ops->gic_write_eoir(intid); 95 } 96 97 void gic_set_dir(unsigned int intid) 98 { 99 GUEST_ASSERT(gic_common_ops); 100 gic_common_ops->gic_write_dir(intid); 101 } 102 103 void gic_set_eoi_split(bool split) 104 { 105 GUEST_ASSERT(gic_common_ops); 106 gic_common_ops->gic_set_eoi_split(split); 107 } 108 109 void gic_set_priority_mask(uint64_t pmr) 110 { 111 GUEST_ASSERT(gic_common_ops); 112 gic_common_ops->gic_set_priority_mask(pmr); 113 } 114 115 void gic_set_priority(unsigned int intid, unsigned int prio) 116 { 117 GUEST_ASSERT(gic_common_ops); 118 gic_common_ops->gic_set_priority(intid, prio); 119 } 120 121 void gic_irq_set_active(unsigned int intid) 122 { 123 GUEST_ASSERT(gic_common_ops); 124 gic_common_ops->gic_irq_set_active(intid); 125 } 126 127 void gic_irq_clear_active(unsigned int intid) 128 { 129 GUEST_ASSERT(gic_common_ops); 130 gic_common_ops->gic_irq_clear_active(intid); 131 } 132 133 bool gic_irq_get_active(unsigned int intid) 134 { 135 GUEST_ASSERT(gic_common_ops); 136 return gic_common_ops->gic_irq_get_active(intid); 137 } 138 139 void gic_irq_set_pending(unsigned int intid) 140 { 141 GUEST_ASSERT(gic_common_ops); 142 gic_common_ops->gic_irq_set_pending(intid); 143 } 144 145 void gic_irq_clear_pending(unsigned int intid) 146 { 147 GUEST_ASSERT(gic_common_ops); 148 gic_common_ops->gic_irq_clear_pending(intid); 149 } 150 151 bool gic_irq_get_pending(unsigned int intid) 152 { 153 GUEST_ASSERT(gic_common_ops); 154 return gic_common_ops->gic_irq_get_pending(intid); 155 } 156 157 void gic_irq_set_config(unsigned int intid, bool is_edge) 158 { 159 GUEST_ASSERT(gic_common_ops); 160 gic_common_ops->gic_irq_set_config(intid, is_edge); 161 } 162