]> rtime.felk.cvut.cz Git - jailhouse.git/blob - hypervisor/arch/arm/irqchip.c
arm: GIC: reset the CPU interface before running a new guest
[jailhouse.git] / hypervisor / arch / arm / irqchip.c
1 /*
2  * Jailhouse, a Linux-based partitioning hypervisor
3  *
4  * Copyright (c) ARM Limited, 2014
5  *
6  * Authors:
7  *  Jean-Philippe Brucker <jean-philippe.brucker@arm.com>
8  *
9  * This work is licensed under the terms of the GNU GPL, version 2.  See
10  * the COPYING file in the top-level directory.
11  */
12
13 #include <jailhouse/entry.h>
14 #include <jailhouse/mmio.h>
15 #include <jailhouse/paging.h>
16 #include <jailhouse/printk.h>
17 #include <jailhouse/string.h>
18 #include <asm/gic_common.h>
19 #include <asm/irqchip.h>
20 #include <asm/platform.h>
21 #include <asm/setup.h>
22 #include <asm/sysregs.h>
23
24 /* AMBA's biosfood */
25 #define AMBA_DEVICE     0xb105f00d
26
27 void *gicd_base;
28 unsigned long gicd_size;
29
30 /*
31  * The init function must be called after the MMU setup, and whilst in the
32  * per-cpu setup, which means that a bool must be set by the master CPU
33  */
34 static bool irqchip_is_init;
35 static struct irqchip_ops irqchip;
36
37 static int irqchip_init_pending(struct per_cpu *cpu_data)
38 {
39         struct pending_irq *pend_array;
40
41         if (cpu_data->pending_irqs == NULL) {
42                 cpu_data->pending_irqs = pend_array = page_alloc(&mem_pool, 1);
43                 if (pend_array == NULL)
44                         return -ENOMEM;
45         } else {
46                 pend_array = cpu_data->pending_irqs;
47         }
48
49         memset(pend_array, 0, PAGE_SIZE);
50
51         cpu_data->pending_irqs = pend_array;
52         cpu_data->first_pending = NULL;
53
54         return 0;
55 }
56
57 /*
58  * Find the first available pending struct for insertion. The `prev' pointer is
59  * set to the previous pending interrupt, if any, to help inserting the new one
60  * into the list.
61  * Returns NULL when no slot is available
62  */
63 static struct pending_irq* get_pending_slot(struct per_cpu *cpu_data,
64                                             struct pending_irq **prev)
65 {
66         u32 i, pending_idx;
67         struct pending_irq *pending = cpu_data->first_pending;
68
69         *prev = NULL;
70
71         for (i = 0; i < MAX_PENDING_IRQS; i++) {
72                 pending_idx = pending - cpu_data->pending_irqs;
73                 if (pending == NULL || i < pending_idx)
74                         return cpu_data->pending_irqs + i;
75
76                 *prev = pending;
77                 pending = pending->next;
78         }
79
80         return NULL;
81 }
82
83 int irqchip_insert_pending(struct per_cpu *cpu_data, struct pending_irq *irq)
84 {
85         struct pending_irq *prev = NULL;
86         struct pending_irq *slot;
87
88         spin_lock(&cpu_data->gic_lock);
89
90         slot = get_pending_slot(cpu_data, &prev);
91         if (slot == NULL) {
92                 spin_unlock(&cpu_data->gic_lock);
93                 return -ENOMEM;
94         }
95
96         /*
97          * Don't override the pointers yet, they may be read by the injection
98          * loop. Odds are astronomically low, but hey.
99          */
100         memcpy(slot, irq, sizeof(struct pending_irq) - 2 * sizeof(void *));
101         slot->prev = prev;
102         if (prev) {
103                 slot->next = prev->next;
104                 prev->next = slot;
105         } else {
106                 slot->next = cpu_data->first_pending;
107                 cpu_data->first_pending = slot;
108         }
109         if (slot->next)
110                 slot->next->prev = slot;
111
112         spin_unlock(&cpu_data->gic_lock);
113
114         return 0;
115 }
116
117 int irqchip_set_pending(struct per_cpu *cpu_data, u32 irq_id, bool try_inject)
118 {
119         struct pending_irq pending;
120
121         pending.virt_id = irq_id;
122         /* Priority must be less than ICC_PMR */
123         pending.priority = 0;
124
125         if (is_sgi(irq_id)) {
126                 pending.hw = 0;
127                 pending.type.sgi.maintenance = 0;
128                 pending.type.sgi.cpuid = 0;
129         } else {
130                 pending.hw = 1;
131                 pending.type.irq = irq_id;
132         }
133
134         if (try_inject && irqchip.inject_irq(cpu_data, &pending) == 0)
135                 return 0;
136
137         return irqchip_insert_pending(cpu_data, &pending);
138 }
139
140 /*
141  * Only executed by `irqchip_inject_pending' on a CPU to inject its own stuff.
142  */
143 int irqchip_remove_pending(struct per_cpu *cpu_data, struct pending_irq *irq)
144 {
145         spin_lock(&cpu_data->gic_lock);
146
147         if (cpu_data->first_pending == irq)
148                 cpu_data->first_pending = irq->next;
149         if (irq->prev)
150                 irq->prev->next = irq->next;
151         if (irq->next)
152                 irq->next->prev = irq->prev;
153
154         spin_unlock(&cpu_data->gic_lock);
155
156         return 0;
157 }
158
159 int irqchip_inject_pending(struct per_cpu *cpu_data)
160 {
161         int err;
162         struct pending_irq *pending = cpu_data->first_pending;
163
164         while (pending != NULL) {
165                 err = irqchip.inject_irq(cpu_data, pending);
166                 if (err == -EBUSY)
167                         /* The list registers are full. */
168                         break;
169                 else
170                         /*
171                          * Removal only changes the pointers, but does not
172                          * deallocate anything.
173                          * Concurrent accesses are avoided with the spinlock,
174                          * but the `next' pointer of the current pending object
175                          * may be rewritten by an external insert before or
176                          * after this removal, which isn't an issue.
177                          */
178                         irqchip_remove_pending(cpu_data, pending);
179
180                 pending = pending->next;
181         }
182
183         return 0;
184 }
185
186 void irqchip_handle_irq(struct per_cpu *cpu_data)
187 {
188         irqchip.handle_irq(cpu_data);
189 }
190
191 void irqchip_eoi_irq(u32 irqn, bool deactivate)
192 {
193         irqchip.eoi_irq(irqn, deactivate);
194 }
195
196 int irqchip_send_sgi(struct sgi *sgi)
197 {
198         return irqchip.send_sgi(sgi);
199 }
200
201 int irqchip_cpu_init(struct per_cpu *cpu_data)
202 {
203         int err;
204
205         err = irqchip_init_pending(cpu_data);
206         if (err)
207                 return err;
208
209         if (irqchip.cpu_init)
210                 return irqchip.cpu_init(cpu_data);
211
212         return 0;
213 }
214
215 int irqchip_cpu_reset(struct per_cpu *cpu_data)
216 {
217         int err;
218
219         err = irqchip_init_pending(cpu_data);
220         if (err)
221                 return err;
222
223         if (irqchip.cpu_reset)
224                 return irqchip.cpu_reset(cpu_data);
225
226         return 0;
227 }
228
229 /* Only the GIC is implemented */
230 extern struct irqchip_ops gic_irqchip;
231
232 int irqchip_init(void)
233 {
234         int i, err;
235         u32 pidr2, cidr;
236         u32 dev_id = 0;
237
238         /* Only executed on master CPU */
239         if (irqchip_is_init)
240                 return 0;
241
242         /* FIXME: parse device tree */
243         gicd_base = GICD_BASE;
244         gicd_size = GICD_SIZE;
245
246         if ((err = arch_map_device(gicd_base, gicd_base, gicd_size)) != 0)
247                 return err;
248
249         for (i = 3; i >= 0; i--) {
250                 cidr = mmio_read32(gicd_base + GICD_CIDR0 + i * 4);
251                 dev_id |= cidr << i * 8;
252         }
253         if (dev_id != AMBA_DEVICE)
254                 goto err_no_distributor;
255
256         /* Probe the GIC version */
257         pidr2 = mmio_read32(gicd_base + GICD_PIDR2);
258         switch (GICD_PIDR2_ARCH(pidr2)) {
259         case 0x2:
260                 break;
261         case 0x3:
262         case 0x4:
263                 memcpy(&irqchip, &gic_irqchip, sizeof(struct irqchip_ops));
264                 break;
265         }
266
267         if (irqchip.init) {
268                 err = irqchip.init();
269                 irqchip_is_init = true;
270
271                 return err;
272         }
273
274 err_no_distributor:
275         printk("GIC: no distributor found\n");
276         arch_unmap_device(gicd_base, gicd_size);
277
278         return -ENODEV;
279 }