]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/bhyve/pci_irq.c
Merge llvm-project release/16.x llvmorg-16.0.4-0-gae42196bc493
[FreeBSD/FreeBSD.git] / usr.sbin / bhyve / pci_irq.c
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 __FBSDID("$FreeBSD$");
33
34 #include <sys/param.h>
35 #include <machine/vmm.h>
36
37 #include <assert.h>
38 #include <pthread.h>
39 #include <stdbool.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <vmmapi.h>
43
44 #include "acpi.h"
45 #include "inout.h"
46 #include "pci_emul.h"
47 #include "pci_irq.h"
48 #include "pci_lpc.h"
49
50 /*
51  * Implement an 8 pin PCI interrupt router compatible with the router
52  * present on Intel's ICH10 chip.
53  */
54
55 /* Fields in each PIRQ register. */
56 #define PIRQ_DIS        0x80
57 #define PIRQ_IRQ        0x0f
58
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)
62
63 /* IRQ count to disable an IRQ. */
64 #define IRQ_DISABLED    0xff
65
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];
73
74 #define NIRQ_COUNTS     16
75 static u_char irq_counts[NIRQ_COUNTS];
76 static int pirq_cold = 1;
77
78 /*
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.
82  */
83 static bool
84 pirq_valid_irq(int reg)
85 {
86
87         if (reg & PIRQ_DIS)
88                 return (false);
89         return (IRQ_PERMITTED(reg & PIRQ_IRQ));
90 }
91
92 uint8_t
93 pirq_read(int pin)
94 {
95
96         assert(pin > 0 && pin <= NPIRQS);
97         return (pirqs[pin - 1].reg);
98 }
99
100 void
101 pirq_write(struct vmctx *ctx, int pin, uint8_t val)
102 {
103         struct pirq *pirq;
104
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);
114         }
115         pthread_mutex_unlock(&pirq->lock);
116 }
117
118 void
119 pci_irq_reserve(int irq)
120 {
121
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;
126 }
127
128 void
129 pci_irq_use(int irq)
130 {
131
132         assert(irq >= 0 && irq < NIRQ_COUNTS);
133         assert(pirq_cold);
134         assert(irq_counts[irq] != IRQ_DISABLED);
135         irq_counts[irq]++;
136 }
137
138 void
139 pci_irq_init(struct vmctx *ctx __unused)
140 {
141         int i;
142
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);
148         }
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;
154         }
155 }
156
157 void
158 pci_irq_assert(struct pci_devinst *pi)
159 {
160         struct pirq *pirq;
161         int pin;
162
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;
174                 }
175                 pthread_mutex_unlock(&pirq->lock);
176         }
177         vm_ioapic_assert_irq(pi->pi_vmctx, pi->pi_lintr.ioapic_irq);
178 }
179
180 void
181 pci_irq_deassert(struct pci_devinst *pi)
182 {
183         struct pirq *pirq;
184         int pin;
185
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;
197                 }
198                 pthread_mutex_unlock(&pirq->lock);
199         }
200         vm_ioapic_deassert_irq(pi->pi_vmctx, pi->pi_lintr.ioapic_irq);
201 }
202
203 int
204 pirq_alloc_pin(struct pci_devinst *pi)
205 {
206         struct vmctx *ctx = pi->pi_vmctx;
207         int best_count, best_irq, best_pin, irq, pin;
208
209         pirq_cold = 0;
210
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;
222                         }
223                 }
224         }
225         pirqs[best_pin].use_count++;
226
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];
237                         }
238                 }
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);
243         }
244
245         return (best_pin + 1);
246 }
247
248 int
249 pirq_irq(int pin)
250 {
251         assert(pin > 0 && pin <= NPIRQS);
252         return (pirqs[pin - 1].reg & PIRQ_IRQ);
253 }
254
255 /* XXX: Generate $PIR table. */
256
257 static void
258 pirq_dsdt(void)
259 {
260         char *irq_prs, *old;
261         int irq, pin;
262
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);
273                 }
274         }
275
276         /*
277          * A helper method to validate a link register's value.  This
278          * duplicates pirq_valid_irq().
279          */
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("}");
302
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("}");
357         }
358         free(irq_prs);
359 }
360 LPC_DSDT(pirq_dsdt);