xref: /illumos-gate/usr/src/cmd/bhyve/pci_irq.c (revision 32640292)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2014 Hudson River Trading LLC
5  * Written by: John H. Baldwin <jhb@FreeBSD.org>
6  * All rights reserved.
7  *
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.
16  *
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.
28  */
29 
30 
31 #include <sys/cdefs.h>
32 
33 #include <sys/param.h>
34 #include <machine/vmm.h>
35 
36 #include <assert.h>
37 #include <pthread.h>
38 #include <stdbool.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <vmmapi.h>
42 
43 #include "acpi.h"
44 #include "inout.h"
45 #include "pci_emul.h"
46 #include "pci_irq.h"
47 #include "pci_lpc.h"
48 
49 /*
50  * Implement an 8 pin PCI interrupt router compatible with the router
51  * present on Intel's ICH10 chip.
52  */
53 
54 /* Fields in each PIRQ register. */
55 #define	PIRQ_DIS	0x80
56 #define	PIRQ_IRQ	0x0f
57 
58 /* Only IRQs 3-7, 9-12, and 14-15 are permitted. */
59 #define	PERMITTED_IRQS	0xdef8
60 #define	IRQ_PERMITTED(irq)	(((1U << (irq)) & PERMITTED_IRQS) != 0)
61 
62 /* IRQ count to disable an IRQ. */
63 #define	IRQ_DISABLED	0xff
64 
65 #define	NPIRQS		8
66 static struct pirq {
67 	uint8_t	reg;
68 	int	use_count;
69 	int	active_count;
70 	pthread_mutex_t lock;
71 } pirqs[NPIRQS];
72 
73 #define	NIRQ_COUNTS	16
74 static u_char irq_counts[NIRQ_COUNTS];
75 static int pirq_cold = 1;
76 
77 /*
78  * Returns true if this pin is enabled with a valid IRQ.  Setting the
79  * register to a reserved IRQ causes interrupts to not be asserted as
80  * if the pin was disabled.
81  */
82 static bool
pirq_valid_irq(int reg)83 pirq_valid_irq(int reg)
84 {
85 
86 	if (reg & PIRQ_DIS)
87 		return (false);
88 	return (IRQ_PERMITTED(reg & PIRQ_IRQ));
89 }
90 
91 uint8_t
pirq_read(int pin)92 pirq_read(int pin)
93 {
94 
95 	assert(pin > 0 && pin <= NPIRQS);
96 	return (pirqs[pin - 1].reg);
97 }
98 
99 void
pirq_write(struct vmctx * ctx,int pin,uint8_t val)100 pirq_write(struct vmctx *ctx, int pin, uint8_t val)
101 {
102 	struct pirq *pirq;
103 
104 	assert(pin > 0 && pin <= NPIRQS);
105 	pirq = &pirqs[pin - 1];
106 	pthread_mutex_lock(&pirq->lock);
107 	if (pirq->reg != (val & (PIRQ_DIS | PIRQ_IRQ))) {
108 		if (pirq->active_count != 0 && pirq_valid_irq(pirq->reg))
109 			vm_isa_deassert_irq(ctx, pirq->reg & PIRQ_IRQ, -1);
110 		pirq->reg = val & (PIRQ_DIS | PIRQ_IRQ);
111 		if (pirq->active_count != 0 && pirq_valid_irq(pirq->reg))
112 			vm_isa_assert_irq(ctx, pirq->reg & PIRQ_IRQ, -1);
113 	}
114 	pthread_mutex_unlock(&pirq->lock);
115 }
116 
117 void
pci_irq_reserve(int irq)118 pci_irq_reserve(int irq)
119 {
120 
121 	assert(irq >= 0 && irq < NIRQ_COUNTS);
122 	assert(pirq_cold);
123 	assert(irq_counts[irq] == 0 || irq_counts[irq] == IRQ_DISABLED);
124 	irq_counts[irq] = IRQ_DISABLED;
125 }
126 
127 void
pci_irq_use(int irq)128 pci_irq_use(int irq)
129 {
130 
131 	assert(irq >= 0 && irq < NIRQ_COUNTS);
132 	assert(pirq_cold);
133 	assert(irq_counts[irq] != IRQ_DISABLED);
134 	irq_counts[irq]++;
135 }
136 
137 void
pci_irq_init(struct vmctx * ctx __unused)138 pci_irq_init(struct vmctx *ctx __unused)
139 {
140 	int i;
141 
142 	for (i = 0; i < NPIRQS; i++) {
143 		pirqs[i].reg = PIRQ_DIS;
144 		pirqs[i].use_count = 0;
145 		pirqs[i].active_count = 0;
146 		pthread_mutex_init(&pirqs[i].lock, NULL);
147 	}
148 	for (i = 0; i < NIRQ_COUNTS; i++) {
149 		if (IRQ_PERMITTED(i))
150 			irq_counts[i] = 0;
151 		else
152 			irq_counts[i] = IRQ_DISABLED;
153 	}
154 }
155 
156 void
pci_irq_assert(struct pci_devinst * pi)157 pci_irq_assert(struct pci_devinst *pi)
158 {
159 	struct pirq *pirq;
160 	int pin;
161 
162 	pin = pi->pi_lintr.pirq_pin;
163 	if (pin > 0) {
164 		assert(pin <= NPIRQS);
165 		pirq = &pirqs[pin - 1];
166 		pthread_mutex_lock(&pirq->lock);
167 		pirq->active_count++;
168 		if (pirq->active_count == 1 && pirq_valid_irq(pirq->reg)) {
169 			vm_isa_assert_irq(pi->pi_vmctx, pirq->reg & PIRQ_IRQ,
170 			    pi->pi_lintr.ioapic_irq);
171 			pthread_mutex_unlock(&pirq->lock);
172 			return;
173 		}
174 		pthread_mutex_unlock(&pirq->lock);
175 	}
176 	vm_ioapic_assert_irq(pi->pi_vmctx, pi->pi_lintr.ioapic_irq);
177 }
178 
179 void
pci_irq_deassert(struct pci_devinst * pi)180 pci_irq_deassert(struct pci_devinst *pi)
181 {
182 	struct pirq *pirq;
183 	int pin;
184 
185 	pin = pi->pi_lintr.pirq_pin;
186 	if (pin > 0) {
187 		assert(pin <= NPIRQS);
188 		pirq = &pirqs[pin - 1];
189 		pthread_mutex_lock(&pirq->lock);
190 		pirq->active_count--;
191 		if (pirq->active_count == 0 && pirq_valid_irq(pirq->reg)) {
192 			vm_isa_deassert_irq(pi->pi_vmctx, pirq->reg & PIRQ_IRQ,
193 			    pi->pi_lintr.ioapic_irq);
194 			pthread_mutex_unlock(&pirq->lock);
195 			return;
196 		}
197 		pthread_mutex_unlock(&pirq->lock);
198 	}
199 	vm_ioapic_deassert_irq(pi->pi_vmctx, pi->pi_lintr.ioapic_irq);
200 }
201 
202 int
pirq_alloc_pin(struct pci_devinst * pi)203 pirq_alloc_pin(struct pci_devinst *pi)
204 {
205 	struct vmctx *ctx = pi->pi_vmctx;
206 	int best_count, best_irq, best_pin, irq, pin;
207 
208 	pirq_cold = 0;
209 
210 	if (lpc_bootrom()) {
211 		/* For external bootrom use fixed mapping. */
212 		best_pin = (4 + pi->pi_slot + pi->pi_lintr.pin) % 8;
213 	} else {
214 		/* Find the least-used PIRQ pin. */
215 		best_pin = 0;
216 		best_count = pirqs[0].use_count;
217 		for (pin = 1; pin < NPIRQS; pin++) {
218 			if (pirqs[pin].use_count < best_count) {
219 				best_pin = pin;
220 				best_count = pirqs[pin].use_count;
221 			}
222 		}
223 	}
224 	pirqs[best_pin].use_count++;
225 
226 	/* Second, route this pin to an IRQ. */
227 	if (pirqs[best_pin].reg == PIRQ_DIS) {
228 		best_irq = -1;
229 		best_count = 0;
230 		for (irq = 0; irq < NIRQ_COUNTS; irq++) {
231 			if (irq_counts[irq] == IRQ_DISABLED)
232 				continue;
233 			if (best_irq == -1 || irq_counts[irq] < best_count) {
234 				best_irq = irq;
235 				best_count = irq_counts[irq];
236 			}
237 		}
238 		assert(best_irq >= 0);
239 		irq_counts[best_irq]++;
240 		pirqs[best_pin].reg = best_irq;
241 		vm_isa_set_irq_trigger(ctx, best_irq, LEVEL_TRIGGER);
242 	}
243 
244 	return (best_pin + 1);
245 }
246 
247 int
pirq_irq(int pin)248 pirq_irq(int pin)
249 {
250 	assert(pin > 0 && pin <= NPIRQS);
251 	return (pirqs[pin - 1].reg & PIRQ_IRQ);
252 }
253 
254 /* XXX: Generate $PIR table. */
255 
256 static void
pirq_dsdt(void)257 pirq_dsdt(void)
258 {
259 	char *irq_prs, *old;
260 	int irq, pin;
261 
262 	irq_prs = NULL;
263 	for (irq = 0; irq < NIRQ_COUNTS; irq++) {
264 		if (!IRQ_PERMITTED(irq))
265 			continue;
266 		if (irq_prs == NULL)
267 			asprintf(&irq_prs, "%d", irq);
268 		else {
269 			old = irq_prs;
270 			asprintf(&irq_prs, "%s,%d", old, irq);
271 			free(old);
272 		}
273 	}
274 
275 	/*
276 	 * A helper method to validate a link register's value.  This
277 	 * duplicates pirq_valid_irq().
278 	 */
279 	dsdt_line("");
280 	dsdt_line("Method (PIRV, 1, NotSerialized)");
281 	dsdt_line("{");
282 	dsdt_line("  If (And (Arg0, 0x%02X))", PIRQ_DIS);
283 	dsdt_line("  {");
284 	dsdt_line("    Return (0x00)");
285 	dsdt_line("  }");
286 	dsdt_line("  And (Arg0, 0x%02X, Local0)", PIRQ_IRQ);
287 	dsdt_line("  If (LLess (Local0, 0x03))");
288 	dsdt_line("  {");
289 	dsdt_line("    Return (0x00)");
290 	dsdt_line("  }");
291 	dsdt_line("  If (LEqual (Local0, 0x08))");
292 	dsdt_line("  {");
293 	dsdt_line("    Return (0x00)");
294 	dsdt_line("  }");
295 	dsdt_line("  If (LEqual (Local0, 0x0D))");
296 	dsdt_line("  {");
297 	dsdt_line("    Return (0x00)");
298 	dsdt_line("  }");
299 	dsdt_line("  Return (0x01)");
300 	dsdt_line("}");
301 
302 	for (pin = 0; pin < NPIRQS; pin++) {
303 		dsdt_line("");
304 		dsdt_line("Device (LNK%c)", 'A' + pin);
305 		dsdt_line("{");
306 		dsdt_line("  Name (_HID, EisaId (\"PNP0C0F\"))");
307 		dsdt_line("  Name (_UID, 0x%02X)", pin + 1);
308 		dsdt_line("  Method (_STA, 0, NotSerialized)");
309 		dsdt_line("  {");
310 		dsdt_line("    If (PIRV (PIR%c))", 'A' + pin);
311 		dsdt_line("    {");
312 		dsdt_line("       Return (0x0B)");
313 		dsdt_line("    }");
314 		dsdt_line("    Else");
315 		dsdt_line("    {");
316 		dsdt_line("       Return (0x09)");
317 		dsdt_line("    }");
318 		dsdt_line("  }");
319 		dsdt_line("  Name (_PRS, ResourceTemplate ()");
320 		dsdt_line("  {");
321 		dsdt_line("    IRQ (Level, ActiveLow, Shared, )");
322 		dsdt_line("      {%s}", irq_prs);
323 		dsdt_line("  })");
324 		dsdt_line("  Name (CB%02X, ResourceTemplate ()", pin + 1);
325 		dsdt_line("  {");
326 		dsdt_line("    IRQ (Level, ActiveLow, Shared, )");
327 		dsdt_line("      {}");
328 		dsdt_line("  })");
329 		dsdt_line("  CreateWordField (CB%02X, 0x01, CIR%c)",
330 		    pin + 1, 'A' + pin);
331 		dsdt_line("  Method (_CRS, 0, NotSerialized)");
332 		dsdt_line("  {");
333 		dsdt_line("    And (PIR%c, 0x%02X, Local0)", 'A' + pin,
334 		    PIRQ_DIS | PIRQ_IRQ);
335 		dsdt_line("    If (PIRV (Local0))");
336 		dsdt_line("    {");
337 		dsdt_line("      ShiftLeft (0x01, Local0, CIR%c)", 'A' + pin);
338 		dsdt_line("    }");
339 		dsdt_line("    Else");
340 		dsdt_line("    {");
341 		dsdt_line("      Store (0x00, CIR%c)", 'A' + pin);
342 		dsdt_line("    }");
343 		dsdt_line("    Return (CB%02X)", pin + 1);
344 		dsdt_line("  }");
345 		dsdt_line("  Method (_DIS, 0, NotSerialized)");
346 		dsdt_line("  {");
347 		dsdt_line("    Store (0x80, PIR%c)", 'A' + pin);
348 		dsdt_line("  }");
349 		dsdt_line("  Method (_SRS, 1, NotSerialized)");
350 		dsdt_line("  {");
351 		dsdt_line("    CreateWordField (Arg0, 0x01, SIR%c)", 'A' + pin);
352 		dsdt_line("    FindSetRightBit (SIR%c, Local0)", 'A' + pin);
353 		dsdt_line("    Store (Decrement (Local0), PIR%c)", 'A' + pin);
354 		dsdt_line("  }");
355 		dsdt_line("}");
356 	}
357 	free(irq_prs);
358 }
359 LPC_DSDT(pirq_dsdt);
360