pci_irq.c revision 268972
1/*- 2 * Copyright (c) 2014 Advanced Computing Technologies 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: stable/10/usr.sbin/bhyve/pci_irq.c 268972 2014-07-22 03:14:37Z jhb $"); 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 64static struct pirq { 65 uint8_t reg; 66 int use_count; 67 int active_count; 68 pthread_mutex_t lock; 69} pirqs[8]; 70 71static u_char irq_counts[16]; 72static 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 */ 79static bool 80pirq_valid_irq(int reg) 81{ 82 83 if (reg & PIRQ_DIS) 84 return (false); 85 return (IRQ_PERMITTED(reg & PIRQ_IRQ)); 86} 87 88uint8_t 89pirq_read(int pin) 90{ 91 92 assert(pin > 0 && pin <= nitems(pirqs)); 93 return (pirqs[pin - 1].reg); 94} 95 96void 97pirq_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 114void 115pci_irq_reserve(int irq) 116{ 117 118 assert(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 124void 125pci_irq_use(int irq) 126{ 127 128 assert(irq < nitems(irq_counts)); 129 assert(pirq_cold); 130 if (irq_counts[irq] != IRQ_DISABLED) 131 irq_counts[irq]++; 132} 133 134void 135pci_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 153void 154pci_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 174void 175pci_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 195int 196pirq_alloc_pin(struct vmctx *ctx) 197{ 198 int best_count, best_irq, best_pin, irq, pin; 199 200 pirq_cold = 1; 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 234int 235pirq_irq(int pin) 236{ 237 238 if (pin == -1) 239 return (255); 240 assert(pin > 0 && pin <= nitems(pirqs)); 241 return (pirqs[pin - 1].reg & PIRQ_IRQ); 242} 243 244/* XXX: Generate $PIR table. */ 245 246static void 247pirq_dsdt(void) 248{ 249 char *irq_prs, *old; 250 int irq, pin; 251 252 irq_prs = NULL; 253 for (irq = 0; irq < nitems(irq_counts); irq++) { 254 if (!IRQ_PERMITTED(irq)) 255 continue; 256 if (irq_prs == NULL) 257 asprintf(&irq_prs, "%d", irq); 258 else { 259 old = irq_prs; 260 asprintf(&irq_prs, "%s,%d", old, irq); 261 free(old); 262 } 263 } 264 265 /* 266 * A helper method to validate a link register's value. This 267 * duplicates pirq_valid_irq(). 268 */ 269 dsdt_line(""); 270 dsdt_line("Method (PIRV, 1, NotSerialized)"); 271 dsdt_line("{"); 272 dsdt_line(" If (And (Arg0, 0x%02X))", PIRQ_DIS); 273 dsdt_line(" {"); 274 dsdt_line(" Return (0x00)"); 275 dsdt_line(" }"); 276 dsdt_line(" And (Arg0, 0x%02X, Local0)", PIRQ_IRQ); 277 dsdt_line(" If (LLess (Local0, 0x03))"); 278 dsdt_line(" {"); 279 dsdt_line(" Return (0x00)"); 280 dsdt_line(" }"); 281 dsdt_line(" If (LEqual (Local0, 0x08))"); 282 dsdt_line(" {"); 283 dsdt_line(" Return (0x00)"); 284 dsdt_line(" }"); 285 dsdt_line(" If (LEqual (Local0, 0x0D))"); 286 dsdt_line(" {"); 287 dsdt_line(" Return (0x00)"); 288 dsdt_line(" }"); 289 dsdt_line(" Return (0x01)"); 290 dsdt_line("}"); 291 292 for (pin = 0; pin < nitems(pirqs); pin++) { 293 dsdt_line(""); 294 dsdt_line("Device (LNK%c)", 'A' + pin); 295 dsdt_line("{"); 296 dsdt_line(" Name (_HID, EisaId (\"PNP0C0F\"))"); 297 dsdt_line(" Name (_UID, 0x%02X)", pin + 1); 298 dsdt_line(" Method (_STA, 0, NotSerialized)"); 299 dsdt_line(" {"); 300 dsdt_line(" If (PIRV (PIR%c))", 'A' + pin); 301 dsdt_line(" {"); 302 dsdt_line(" Return (0x0B)"); 303 dsdt_line(" }"); 304 dsdt_line(" Else"); 305 dsdt_line(" {"); 306 dsdt_line(" Return (0x09)"); 307 dsdt_line(" }"); 308 dsdt_line(" }"); 309 dsdt_line(" Name (_PRS, ResourceTemplate ()"); 310 dsdt_line(" {"); 311 dsdt_line(" IRQ (Level, ActiveLow, Shared, )"); 312 dsdt_line(" {%s}", irq_prs); 313 dsdt_line(" })"); 314 dsdt_line(" Name (CB%02X, ResourceTemplate ()", pin + 1); 315 dsdt_line(" {"); 316 dsdt_line(" IRQ (Level, ActiveLow, Shared, )"); 317 dsdt_line(" {}"); 318 dsdt_line(" })"); 319 dsdt_line(" CreateWordField (CB%02X, 0x01, CIR%c)", 320 pin + 1, 'A' + pin); 321 dsdt_line(" Method (_CRS, 0, NotSerialized)"); 322 dsdt_line(" {"); 323 dsdt_line(" And (PIR%c, 0x%02X, Local0)", 'A' + pin, 324 PIRQ_DIS | PIRQ_IRQ); 325 dsdt_line(" If (PIRV (Local0))"); 326 dsdt_line(" {"); 327 dsdt_line(" ShiftLeft (0x01, Local0, CIR%c)", 'A' + pin); 328 dsdt_line(" }"); 329 dsdt_line(" Else"); 330 dsdt_line(" {"); 331 dsdt_line(" Store (0x00, CIR%c)", 'A' + pin); 332 dsdt_line(" }"); 333 dsdt_line(" Return (CB%02X)", pin + 1); 334 dsdt_line(" }"); 335 dsdt_line(" Method (_DIS, 0, NotSerialized)"); 336 dsdt_line(" {"); 337 dsdt_line(" Store (0x80, PIR%c)", 'A' + pin); 338 dsdt_line(" }"); 339 dsdt_line(" Method (_SRS, 1, NotSerialized)"); 340 dsdt_line(" {"); 341 dsdt_line(" CreateWordField (Arg0, 0x01, SIR%c)", 'A' + pin); 342 dsdt_line(" FindSetRightBit (SIR%c, Local0)", 'A' + pin); 343 dsdt_line(" Store (Decrement (Local0), PIR%c)", 'A' + pin); 344 dsdt_line(" }"); 345 dsdt_line("}"); 346 } 347 free(irq_prs); 348} 349LPC_DSDT(pirq_dsdt); 350