15325 bhyve upstream sync 2023 January
[illumos-gate.git] / usr / src / cmd / bhyve / pci_irq.c
blob2ce962cdec44c1fc048caa77986e8dd296939e93
1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4 * Copyright (c) 2014 Hudson River Trading LLC
5 * Written by: John H. Baldwin <jhb@FreeBSD.org>
6 * All rights reserved.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
31 #include <sys/cdefs.h>
32 __FBSDID("$FreeBSD$");
34 #include <sys/param.h>
35 #include <machine/vmm.h>
37 #include <assert.h>
38 #include <pthread.h>
39 #include <stdbool.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <vmmapi.h>
44 #include "acpi.h"
45 #include "inout.h"
46 #include "pci_emul.h"
47 #include "pci_irq.h"
48 #include "pci_lpc.h"
51 * Implement an 8 pin PCI interrupt router compatible with the router
52 * present on Intel's ICH10 chip.
55 /* Fields in each PIRQ register. */
56 #define PIRQ_DIS 0x80
57 #define PIRQ_IRQ 0x0f
59 /* Only IRQs 3-7, 9-12, and 14-15 are permitted. */
60 #define PERMITTED_IRQS 0xdef8
61 #define IRQ_PERMITTED(irq) (((1U << (irq)) & PERMITTED_IRQS) != 0)
63 /* IRQ count to disable an IRQ. */
64 #define IRQ_DISABLED 0xff
66 #define NPIRQS 8
67 static struct pirq {
68 uint8_t reg;
69 int use_count;
70 int active_count;
71 pthread_mutex_t lock;
72 } pirqs[NPIRQS];
74 #define NIRQ_COUNTS 16
75 static u_char irq_counts[NIRQ_COUNTS];
76 static int pirq_cold = 1;
79 * Returns true if this pin is enabled with a valid IRQ. Setting the
80 * register to a reserved IRQ causes interrupts to not be asserted as
81 * if the pin was disabled.
83 static bool
84 pirq_valid_irq(int reg)
87 if (reg & PIRQ_DIS)
88 return (false);
89 return (IRQ_PERMITTED(reg & PIRQ_IRQ));
92 uint8_t
93 pirq_read(int pin)
96 assert(pin > 0 && pin <= NPIRQS);
97 return (pirqs[pin - 1].reg);
100 void
101 pirq_write(struct vmctx *ctx, int pin, uint8_t val)
103 struct pirq *pirq;
105 assert(pin > 0 && pin <= NPIRQS);
106 pirq = &pirqs[pin - 1];
107 pthread_mutex_lock(&pirq->lock);
108 if (pirq->reg != (val & (PIRQ_DIS | PIRQ_IRQ))) {
109 if (pirq->active_count != 0 && pirq_valid_irq(pirq->reg))
110 vm_isa_deassert_irq(ctx, pirq->reg & PIRQ_IRQ, -1);
111 pirq->reg = val & (PIRQ_DIS | PIRQ_IRQ);
112 if (pirq->active_count != 0 && pirq_valid_irq(pirq->reg))
113 vm_isa_assert_irq(ctx, pirq->reg & PIRQ_IRQ, -1);
115 pthread_mutex_unlock(&pirq->lock);
118 void
119 pci_irq_reserve(int irq)
122 assert(irq >= 0 && irq < NIRQ_COUNTS);
123 assert(pirq_cold);
124 assert(irq_counts[irq] == 0 || irq_counts[irq] == IRQ_DISABLED);
125 irq_counts[irq] = IRQ_DISABLED;
128 void
129 pci_irq_use(int irq)
132 assert(irq >= 0 && irq < NIRQ_COUNTS);
133 assert(pirq_cold);
134 assert(irq_counts[irq] != IRQ_DISABLED);
135 irq_counts[irq]++;
138 void
139 pci_irq_init(struct vmctx *ctx __unused)
141 int i;
143 for (i = 0; i < NPIRQS; i++) {
144 pirqs[i].reg = PIRQ_DIS;
145 pirqs[i].use_count = 0;
146 pirqs[i].active_count = 0;
147 pthread_mutex_init(&pirqs[i].lock, NULL);
149 for (i = 0; i < NIRQ_COUNTS; i++) {
150 if (IRQ_PERMITTED(i))
151 irq_counts[i] = 0;
152 else
153 irq_counts[i] = IRQ_DISABLED;
157 void
158 pci_irq_assert(struct pci_devinst *pi)
160 struct pirq *pirq;
161 int pin;
163 pin = pi->pi_lintr.pirq_pin;
164 if (pin > 0) {
165 assert(pin <= NPIRQS);
166 pirq = &pirqs[pin - 1];
167 pthread_mutex_lock(&pirq->lock);
168 pirq->active_count++;
169 if (pirq->active_count == 1 && pirq_valid_irq(pirq->reg)) {
170 vm_isa_assert_irq(pi->pi_vmctx, pirq->reg & PIRQ_IRQ,
171 pi->pi_lintr.ioapic_irq);
172 pthread_mutex_unlock(&pirq->lock);
173 return;
175 pthread_mutex_unlock(&pirq->lock);
177 vm_ioapic_assert_irq(pi->pi_vmctx, pi->pi_lintr.ioapic_irq);
180 void
181 pci_irq_deassert(struct pci_devinst *pi)
183 struct pirq *pirq;
184 int pin;
186 pin = pi->pi_lintr.pirq_pin;
187 if (pin > 0) {
188 assert(pin <= NPIRQS);
189 pirq = &pirqs[pin - 1];
190 pthread_mutex_lock(&pirq->lock);
191 pirq->active_count--;
192 if (pirq->active_count == 0 && pirq_valid_irq(pirq->reg)) {
193 vm_isa_deassert_irq(pi->pi_vmctx, pirq->reg & PIRQ_IRQ,
194 pi->pi_lintr.ioapic_irq);
195 pthread_mutex_unlock(&pirq->lock);
196 return;
198 pthread_mutex_unlock(&pirq->lock);
200 vm_ioapic_deassert_irq(pi->pi_vmctx, pi->pi_lintr.ioapic_irq);
204 pirq_alloc_pin(struct pci_devinst *pi)
206 struct vmctx *ctx = pi->pi_vmctx;
207 int best_count, best_irq, best_pin, irq, pin;
209 pirq_cold = 0;
211 if (lpc_bootrom()) {
212 /* For external bootrom use fixed mapping. */
213 best_pin = (4 + pi->pi_slot + pi->pi_lintr.pin) % 8;
214 } else {
215 /* Find the least-used PIRQ pin. */
216 best_pin = 0;
217 best_count = pirqs[0].use_count;
218 for (pin = 1; pin < NPIRQS; pin++) {
219 if (pirqs[pin].use_count < best_count) {
220 best_pin = pin;
221 best_count = pirqs[pin].use_count;
225 pirqs[best_pin].use_count++;
227 /* Second, route this pin to an IRQ. */
228 if (pirqs[best_pin].reg == PIRQ_DIS) {
229 best_irq = -1;
230 best_count = 0;
231 for (irq = 0; irq < NIRQ_COUNTS; irq++) {
232 if (irq_counts[irq] == IRQ_DISABLED)
233 continue;
234 if (best_irq == -1 || irq_counts[irq] < best_count) {
235 best_irq = irq;
236 best_count = irq_counts[irq];
239 assert(best_irq >= 0);
240 irq_counts[best_irq]++;
241 pirqs[best_pin].reg = best_irq;
242 vm_isa_set_irq_trigger(ctx, best_irq, LEVEL_TRIGGER);
245 return (best_pin + 1);
249 pirq_irq(int pin)
251 assert(pin > 0 && pin <= NPIRQS);
252 return (pirqs[pin - 1].reg & PIRQ_IRQ);
255 /* XXX: Generate $PIR table. */
257 static void
258 pirq_dsdt(void)
260 char *irq_prs, *old;
261 int irq, pin;
263 irq_prs = NULL;
264 for (irq = 0; irq < NIRQ_COUNTS; irq++) {
265 if (!IRQ_PERMITTED(irq))
266 continue;
267 if (irq_prs == NULL)
268 asprintf(&irq_prs, "%d", irq);
269 else {
270 old = irq_prs;
271 asprintf(&irq_prs, "%s,%d", old, irq);
272 free(old);
277 * A helper method to validate a link register's value. This
278 * duplicates pirq_valid_irq().
280 dsdt_line("");
281 dsdt_line("Method (PIRV, 1, NotSerialized)");
282 dsdt_line("{");
283 dsdt_line(" If (And (Arg0, 0x%02X))", PIRQ_DIS);
284 dsdt_line(" {");
285 dsdt_line(" Return (0x00)");
286 dsdt_line(" }");
287 dsdt_line(" And (Arg0, 0x%02X, Local0)", PIRQ_IRQ);
288 dsdt_line(" If (LLess (Local0, 0x03))");
289 dsdt_line(" {");
290 dsdt_line(" Return (0x00)");
291 dsdt_line(" }");
292 dsdt_line(" If (LEqual (Local0, 0x08))");
293 dsdt_line(" {");
294 dsdt_line(" Return (0x00)");
295 dsdt_line(" }");
296 dsdt_line(" If (LEqual (Local0, 0x0D))");
297 dsdt_line(" {");
298 dsdt_line(" Return (0x00)");
299 dsdt_line(" }");
300 dsdt_line(" Return (0x01)");
301 dsdt_line("}");
303 for (pin = 0; pin < NPIRQS; pin++) {
304 dsdt_line("");
305 dsdt_line("Device (LNK%c)", 'A' + pin);
306 dsdt_line("{");
307 dsdt_line(" Name (_HID, EisaId (\"PNP0C0F\"))");
308 dsdt_line(" Name (_UID, 0x%02X)", pin + 1);
309 dsdt_line(" Method (_STA, 0, NotSerialized)");
310 dsdt_line(" {");
311 dsdt_line(" If (PIRV (PIR%c))", 'A' + pin);
312 dsdt_line(" {");
313 dsdt_line(" Return (0x0B)");
314 dsdt_line(" }");
315 dsdt_line(" Else");
316 dsdt_line(" {");
317 dsdt_line(" Return (0x09)");
318 dsdt_line(" }");
319 dsdt_line(" }");
320 dsdt_line(" Name (_PRS, ResourceTemplate ()");
321 dsdt_line(" {");
322 dsdt_line(" IRQ (Level, ActiveLow, Shared, )");
323 dsdt_line(" {%s}", irq_prs);
324 dsdt_line(" })");
325 dsdt_line(" Name (CB%02X, ResourceTemplate ()", pin + 1);
326 dsdt_line(" {");
327 dsdt_line(" IRQ (Level, ActiveLow, Shared, )");
328 dsdt_line(" {}");
329 dsdt_line(" })");
330 dsdt_line(" CreateWordField (CB%02X, 0x01, CIR%c)",
331 pin + 1, 'A' + pin);
332 dsdt_line(" Method (_CRS, 0, NotSerialized)");
333 dsdt_line(" {");
334 dsdt_line(" And (PIR%c, 0x%02X, Local0)", 'A' + pin,
335 PIRQ_DIS | PIRQ_IRQ);
336 dsdt_line(" If (PIRV (Local0))");
337 dsdt_line(" {");
338 dsdt_line(" ShiftLeft (0x01, Local0, CIR%c)", 'A' + pin);
339 dsdt_line(" }");
340 dsdt_line(" Else");
341 dsdt_line(" {");
342 dsdt_line(" Store (0x00, CIR%c)", 'A' + pin);
343 dsdt_line(" }");
344 dsdt_line(" Return (CB%02X)", pin + 1);
345 dsdt_line(" }");
346 dsdt_line(" Method (_DIS, 0, NotSerialized)");
347 dsdt_line(" {");
348 dsdt_line(" Store (0x80, PIR%c)", 'A' + pin);
349 dsdt_line(" }");
350 dsdt_line(" Method (_SRS, 1, NotSerialized)");
351 dsdt_line(" {");
352 dsdt_line(" CreateWordField (Arg0, 0x01, SIR%c)", 'A' + pin);
353 dsdt_line(" FindSetRightBit (SIR%c, Local0)", 'A' + pin);
354 dsdt_line(" Store (Decrement (Local0), PIR%c)", 'A' + pin);
355 dsdt_line(" }");
356 dsdt_line("}");
358 free(irq_prs);
360 LPC_DSDT(pirq_dsdt);