]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - usr.sbin/bhyve/pci_irq.c
- Copy stable/10@285827 to releng/10.2 in preparation for 10.2-RC1
[FreeBSD/releng/10.2.git] / usr.sbin / bhyve / pci_irq.c
1 /*-
2  * Copyright (c) 2014 Hudson River Trading LLC
3  * Written by: John H. Baldwin <jhb@FreeBSD.org>
4  * All rights reserved.
5  *
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.
14  *
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.
26  */
27
28
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31
32 #include <sys/param.h>
33 #include <machine/vmm.h>
34
35 #include <assert.h>
36 #include <pthread.h>
37 #include <stdbool.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <vmmapi.h>
41
42 #include "acpi.h"
43 #include "inout.h"
44 #include "pci_emul.h"
45 #include "pci_irq.h"
46 #include "pci_lpc.h"
47
48 /*
49  * Implement an 8 pin PCI interrupt router compatible with the router
50  * present on Intel's ICH10 chip.
51  */
52
53 /* Fields in each PIRQ register. */
54 #define PIRQ_DIS        0x80
55 #define PIRQ_IRQ        0x0f
56
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)
60
61 /* IRQ count to disable an IRQ. */
62 #define IRQ_DISABLED    0xff
63
64 static struct pirq {
65         uint8_t reg;
66         int     use_count;
67         int     active_count;
68         pthread_mutex_t lock;
69 } pirqs[8];
70
71 static u_char irq_counts[16];
72 static int pirq_cold = 1;
73
74 /*
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.
78  */
79 static bool
80 pirq_valid_irq(int reg)
81 {
82
83         if (reg & PIRQ_DIS)
84                 return (false);
85         return (IRQ_PERMITTED(reg & PIRQ_IRQ));
86 }
87
88 uint8_t
89 pirq_read(int pin)
90 {
91
92         assert(pin > 0 && pin <= nitems(pirqs));
93         return (pirqs[pin - 1].reg);
94 }
95
96 void
97 pirq_write(struct vmctx *ctx, int pin, uint8_t val)
98 {
99         struct pirq *pirq;
100
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);
110         }
111         pthread_mutex_unlock(&pirq->lock);
112 }
113
114 void
115 pci_irq_reserve(int irq)
116 {
117
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;
122 }
123
124 void
125 pci_irq_use(int irq)
126 {
127
128         assert(irq >= 0 && irq < nitems(irq_counts));
129         assert(pirq_cold);
130         assert(irq_counts[irq] != IRQ_DISABLED);
131         irq_counts[irq]++;
132 }
133
134 void
135 pci_irq_init(struct vmctx *ctx)
136 {
137         int i;
138
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);
144         }
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;
150         }
151 }
152
153 void
154 pci_irq_assert(struct pci_devinst *pi)
155 {
156         struct pirq *pirq;
157
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;
168                 }
169                 pthread_mutex_unlock(&pirq->lock);
170         }
171         vm_ioapic_assert_irq(pi->pi_vmctx, pi->pi_lintr.ioapic_irq);
172 }
173
174 void
175 pci_irq_deassert(struct pci_devinst *pi)
176 {
177         struct pirq *pirq;
178
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;
189                 }
190                 pthread_mutex_unlock(&pirq->lock);
191         }
192         vm_ioapic_deassert_irq(pi->pi_vmctx, pi->pi_lintr.ioapic_irq);
193 }
194
195 int
196 pirq_alloc_pin(struct vmctx *ctx)
197 {
198         int best_count, best_irq, best_pin, irq, pin;
199
200         pirq_cold = 0;
201
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;
209                 }
210         }
211         pirqs[best_pin].use_count++;
212
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];
223                         }
224                 }
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);
229         }
230
231         return (best_pin + 1);
232 }
233
234 int
235 pirq_irq(int pin)
236 {
237         assert(pin > 0 && pin <= nitems(pirqs));
238         return (pirqs[pin - 1].reg & PIRQ_IRQ);
239 }
240
241 /* XXX: Generate $PIR table. */
242
243 static void
244 pirq_dsdt(void)
245 {
246         char *irq_prs, *old;
247         int irq, pin;
248
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);
259                 }
260         }
261
262         /*
263          * A helper method to validate a link register's value.  This
264          * duplicates pirq_valid_irq().
265          */
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("}");
288
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("}");
343         }
344         free(irq_prs);
345 }
346 LPC_DSDT(pirq_dsdt);