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