1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 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); 361