arm64 libc: hide .cerror, .curbrk, .minbrk for WITHOUT_SYMVER
[freebsd-src.git] / usr.sbin / bhyve / pci_irq.c
blobf22b15cefaaf1a7111bd2748554b788f3b6251af
1 /*-
2 * Copyright (c) 2014 Hudson River Trading LLC
3 * Written by: John H. Baldwin <jhb@FreeBSD.org>
4 * All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
32 #include <sys/param.h>
33 #include <machine/vmm.h>
35 #include <assert.h>
36 #include <pthread.h>
37 #include <stdbool.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <vmmapi.h>
42 #include "acpi.h"
43 #include "inout.h"
44 #include "pci_emul.h"
45 #include "pci_irq.h"
46 #include "pci_lpc.h"
49 * Implement an 8 pin PCI interrupt router compatible with the router
50 * present on Intel's ICH10 chip.
53 /* Fields in each PIRQ register. */
54 #define PIRQ_DIS 0x80
55 #define PIRQ_IRQ 0x0f
57 /* Only IRQs 3-7, 9-12, and 14-15 are permitted. */
58 #define PERMITTED_IRQS 0xdef8
59 #define IRQ_PERMITTED(irq) (((1U << (irq)) & PERMITTED_IRQS) != 0)
61 /* IRQ count to disable an IRQ. */
62 #define IRQ_DISABLED 0xff
64 static struct pirq {
65 uint8_t reg;
66 int use_count;
67 int active_count;
68 pthread_mutex_t lock;
69 } pirqs[8];
71 static u_char irq_counts[16];
72 static int pirq_cold = 1;
75 * Returns true if this pin is enabled with a valid IRQ. Setting the
76 * register to a reserved IRQ causes interrupts to not be asserted as
77 * if the pin was disabled.
79 static bool
80 pirq_valid_irq(int reg)
83 if (reg & PIRQ_DIS)
84 return (false);
85 return (IRQ_PERMITTED(reg & PIRQ_IRQ));
88 uint8_t
89 pirq_read(int pin)
92 assert(pin > 0 && pin <= nitems(pirqs));
93 return (pirqs[pin - 1].reg);
96 void
97 pirq_write(struct vmctx *ctx, int pin, uint8_t val)
99 struct pirq *pirq;
101 assert(pin > 0 && pin <= nitems(pirqs));
102 pirq = &pirqs[pin - 1];
103 pthread_mutex_lock(&pirq->lock);
104 if (pirq->reg != (val & (PIRQ_DIS | PIRQ_IRQ))) {
105 if (pirq->active_count != 0 && pirq_valid_irq(pirq->reg))
106 vm_isa_deassert_irq(ctx, pirq->reg & PIRQ_IRQ, -1);
107 pirq->reg = val & (PIRQ_DIS | PIRQ_IRQ);
108 if (pirq->active_count != 0 && pirq_valid_irq(pirq->reg))
109 vm_isa_assert_irq(ctx, pirq->reg & PIRQ_IRQ, -1);
111 pthread_mutex_unlock(&pirq->lock);
114 void
115 pci_irq_reserve(int irq)
118 assert(irq >= 0 && irq < nitems(irq_counts));
119 assert(pirq_cold);
120 assert(irq_counts[irq] == 0 || irq_counts[irq] == IRQ_DISABLED);
121 irq_counts[irq] = IRQ_DISABLED;
124 void
125 pci_irq_use(int irq)
128 assert(irq >= 0 && irq < nitems(irq_counts));
129 assert(pirq_cold);
130 assert(irq_counts[irq] != IRQ_DISABLED);
131 irq_counts[irq]++;
134 void
135 pci_irq_init(struct vmctx *ctx)
137 int i;
139 for (i = 0; i < nitems(pirqs); i++) {
140 pirqs[i].reg = PIRQ_DIS;
141 pirqs[i].use_count = 0;
142 pirqs[i].active_count = 0;
143 pthread_mutex_init(&pirqs[i].lock, NULL);
145 for (i = 0; i < nitems(irq_counts); i++) {
146 if (IRQ_PERMITTED(i))
147 irq_counts[i] = 0;
148 else
149 irq_counts[i] = IRQ_DISABLED;
153 void
154 pci_irq_assert(struct pci_devinst *pi)
156 struct pirq *pirq;
158 if (pi->pi_lintr.pirq_pin > 0) {
159 assert(pi->pi_lintr.pirq_pin <= nitems(pirqs));
160 pirq = &pirqs[pi->pi_lintr.pirq_pin - 1];
161 pthread_mutex_lock(&pirq->lock);
162 pirq->active_count++;
163 if (pirq->active_count == 1 && pirq_valid_irq(pirq->reg)) {
164 vm_isa_assert_irq(pi->pi_vmctx, pirq->reg & PIRQ_IRQ,
165 pi->pi_lintr.ioapic_irq);
166 pthread_mutex_unlock(&pirq->lock);
167 return;
169 pthread_mutex_unlock(&pirq->lock);
171 vm_ioapic_assert_irq(pi->pi_vmctx, pi->pi_lintr.ioapic_irq);
174 void
175 pci_irq_deassert(struct pci_devinst *pi)
177 struct pirq *pirq;
179 if (pi->pi_lintr.pirq_pin > 0) {
180 assert(pi->pi_lintr.pirq_pin <= nitems(pirqs));
181 pirq = &pirqs[pi->pi_lintr.pirq_pin - 1];
182 pthread_mutex_lock(&pirq->lock);
183 pirq->active_count--;
184 if (pirq->active_count == 0 && pirq_valid_irq(pirq->reg)) {
185 vm_isa_deassert_irq(pi->pi_vmctx, pirq->reg & PIRQ_IRQ,
186 pi->pi_lintr.ioapic_irq);
187 pthread_mutex_unlock(&pirq->lock);
188 return;
190 pthread_mutex_unlock(&pirq->lock);
192 vm_ioapic_deassert_irq(pi->pi_vmctx, pi->pi_lintr.ioapic_irq);
196 pirq_alloc_pin(struct vmctx *ctx)
198 int best_count, best_irq, best_pin, irq, pin;
200 pirq_cold = 0;
202 /* First, find the least-used PIRQ pin. */
203 best_pin = 0;
204 best_count = pirqs[0].use_count;
205 for (pin = 1; pin < nitems(pirqs); pin++) {
206 if (pirqs[pin].use_count < best_count) {
207 best_pin = pin;
208 best_count = pirqs[pin].use_count;
211 pirqs[best_pin].use_count++;
213 /* Second, route this pin to an IRQ. */
214 if (pirqs[best_pin].reg == PIRQ_DIS) {
215 best_irq = -1;
216 best_count = 0;
217 for (irq = 0; irq < nitems(irq_counts); irq++) {
218 if (irq_counts[irq] == IRQ_DISABLED)
219 continue;
220 if (best_irq == -1 || irq_counts[irq] < best_count) {
221 best_irq = irq;
222 best_count = irq_counts[irq];
225 assert(best_irq >= 0);
226 irq_counts[best_irq]++;
227 pirqs[best_pin].reg = best_irq;
228 vm_isa_set_irq_trigger(ctx, best_irq, LEVEL_TRIGGER);
231 return (best_pin + 1);
235 pirq_irq(int pin)
237 assert(pin > 0 && pin <= nitems(pirqs));
238 return (pirqs[pin - 1].reg & PIRQ_IRQ);
241 /* XXX: Generate $PIR table. */
243 static void
244 pirq_dsdt(void)
246 char *irq_prs, *old;
247 int irq, pin;
249 irq_prs = NULL;
250 for (irq = 0; irq < nitems(irq_counts); irq++) {
251 if (!IRQ_PERMITTED(irq))
252 continue;
253 if (irq_prs == NULL)
254 asprintf(&irq_prs, "%d", irq);
255 else {
256 old = irq_prs;
257 asprintf(&irq_prs, "%s,%d", old, irq);
258 free(old);
263 * A helper method to validate a link register's value. This
264 * duplicates pirq_valid_irq().
266 dsdt_line("");
267 dsdt_line("Method (PIRV, 1, NotSerialized)");
268 dsdt_line("{");
269 dsdt_line(" If (And (Arg0, 0x%02X))", PIRQ_DIS);
270 dsdt_line(" {");
271 dsdt_line(" Return (0x00)");
272 dsdt_line(" }");
273 dsdt_line(" And (Arg0, 0x%02X, Local0)", PIRQ_IRQ);
274 dsdt_line(" If (LLess (Local0, 0x03))");
275 dsdt_line(" {");
276 dsdt_line(" Return (0x00)");
277 dsdt_line(" }");
278 dsdt_line(" If (LEqual (Local0, 0x08))");
279 dsdt_line(" {");
280 dsdt_line(" Return (0x00)");
281 dsdt_line(" }");
282 dsdt_line(" If (LEqual (Local0, 0x0D))");
283 dsdt_line(" {");
284 dsdt_line(" Return (0x00)");
285 dsdt_line(" }");
286 dsdt_line(" Return (0x01)");
287 dsdt_line("}");
289 for (pin = 0; pin < nitems(pirqs); pin++) {
290 dsdt_line("");
291 dsdt_line("Device (LNK%c)", 'A' + pin);
292 dsdt_line("{");
293 dsdt_line(" Name (_HID, EisaId (\"PNP0C0F\"))");
294 dsdt_line(" Name (_UID, 0x%02X)", pin + 1);
295 dsdt_line(" Method (_STA, 0, NotSerialized)");
296 dsdt_line(" {");
297 dsdt_line(" If (PIRV (PIR%c))", 'A' + pin);
298 dsdt_line(" {");
299 dsdt_line(" Return (0x0B)");
300 dsdt_line(" }");
301 dsdt_line(" Else");
302 dsdt_line(" {");
303 dsdt_line(" Return (0x09)");
304 dsdt_line(" }");
305 dsdt_line(" }");
306 dsdt_line(" Name (_PRS, ResourceTemplate ()");
307 dsdt_line(" {");
308 dsdt_line(" IRQ (Level, ActiveLow, Shared, )");
309 dsdt_line(" {%s}", irq_prs);
310 dsdt_line(" })");
311 dsdt_line(" Name (CB%02X, ResourceTemplate ()", pin + 1);
312 dsdt_line(" {");
313 dsdt_line(" IRQ (Level, ActiveLow, Shared, )");
314 dsdt_line(" {}");
315 dsdt_line(" })");
316 dsdt_line(" CreateWordField (CB%02X, 0x01, CIR%c)",
317 pin + 1, 'A' + pin);
318 dsdt_line(" Method (_CRS, 0, NotSerialized)");
319 dsdt_line(" {");
320 dsdt_line(" And (PIR%c, 0x%02X, Local0)", 'A' + pin,
321 PIRQ_DIS | PIRQ_IRQ);
322 dsdt_line(" If (PIRV (Local0))");
323 dsdt_line(" {");
324 dsdt_line(" ShiftLeft (0x01, Local0, CIR%c)", 'A' + pin);
325 dsdt_line(" }");
326 dsdt_line(" Else");
327 dsdt_line(" {");
328 dsdt_line(" Store (0x00, CIR%c)", 'A' + pin);
329 dsdt_line(" }");
330 dsdt_line(" Return (CB%02X)", pin + 1);
331 dsdt_line(" }");
332 dsdt_line(" Method (_DIS, 0, NotSerialized)");
333 dsdt_line(" {");
334 dsdt_line(" Store (0x80, PIR%c)", 'A' + pin);
335 dsdt_line(" }");
336 dsdt_line(" Method (_SRS, 1, NotSerialized)");
337 dsdt_line(" {");
338 dsdt_line(" CreateWordField (Arg0, 0x01, SIR%c)", 'A' + pin);
339 dsdt_line(" FindSetRightBit (SIR%c, Local0)", 'A' + pin);
340 dsdt_line(" Store (Decrement (Local0), PIR%c)", 'A' + pin);
341 dsdt_line(" }");
342 dsdt_line("}");
344 free(irq_prs);
346 LPC_DSDT(pirq_dsdt);