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