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