1 /* $NetBSD: xen_intr.c,v 1.31 2023/02/25 00:32:13 riastradh Exp $ */ 2 3 /*- 4 * Copyright (c) 1998, 2001 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Charles M. Hannum, and by Jason R. Thorpe. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 __KERNEL_RCSID(0, "$NetBSD: xen_intr.c,v 1.31 2023/02/25 00:32:13 riastradh Exp $"); 34 35 #include "opt_multiprocessor.h" 36 #include "opt_pci.h" 37 38 #include <sys/param.h> 39 #include <sys/kernel.h> 40 #include <sys/kmem.h> 41 #include <sys/cpu.h> 42 #include <sys/device.h> 43 44 #include <xen/intr.h> 45 #include <xen/evtchn.h> 46 #include <xen/xenfunc.h> 47 48 #include <uvm/uvm.h> 49 50 #include <machine/cpu.h> 51 #include <machine/intr.h> 52 53 #include "acpica.h" 54 #include "ioapic.h" 55 #include "lapic.h" 56 #include "pci.h" 57 58 #if NACPICA > 0 59 #include <dev/acpi/acpivar.h> 60 #endif 61 62 #if NIOAPIC > 0 || NACPICA > 0 63 #include <machine/i82093var.h> 64 #endif 65 66 #if NLAPIC > 0 67 #include <machine/i82489var.h> 68 #endif 69 70 #if NPCI > 0 71 #include <dev/pci/ppbreg.h> 72 #ifdef __HAVE_PCI_MSI_MSIX 73 #include <x86/pci/msipic.h> 74 #include <x86/pci/pci_msi_machdep.h> 75 #endif 76 #endif 77 78 #if defined(MULTIPROCESSOR) 79 static const char *xen_ipi_names[XEN_NIPIS] = XEN_IPI_NAMES; 80 #endif 81 82 #if !defined(XENPVHVM) 83 void 84 x86_disable_intr(void) 85 { 86 87 kpreempt_disable(); 88 curcpu()->ci_vcpu->evtchn_upcall_mask = 1; 89 kpreempt_enable(); 90 91 __insn_barrier(); 92 } 93 94 void 95 x86_enable_intr(void) 96 { 97 struct cpu_info *ci; 98 99 __insn_barrier(); 100 101 kpreempt_disable(); 102 ci = curcpu(); 103 ci->ci_vcpu->evtchn_upcall_mask = 0; 104 __insn_barrier(); 105 if (__predict_false(ci->ci_vcpu->evtchn_upcall_pending)) 106 hypervisor_force_callback(); 107 kpreempt_enable(); 108 } 109 110 #endif /* !XENPVHVM */ 111 112 u_long 113 xen_read_psl(void) 114 { 115 u_long psl; 116 117 kpreempt_disable(); 118 psl = curcpu()->ci_vcpu->evtchn_upcall_mask; 119 kpreempt_enable(); 120 121 return psl; 122 } 123 124 void 125 xen_write_psl(u_long psl) 126 { 127 struct cpu_info *ci; 128 129 kpreempt_disable(); 130 ci = curcpu(); 131 ci->ci_vcpu->evtchn_upcall_mask = psl; 132 __insn_barrier(); 133 if (__predict_false(ci->ci_vcpu->evtchn_upcall_pending) && psl == 0) 134 hypervisor_force_callback(); 135 kpreempt_enable(); 136 } 137 138 void * 139 xen_intr_establish(int legacy_irq, struct pic *pic, int pin, 140 int type, int level, int (*handler)(void *), void *arg, 141 bool known_mpsafe) 142 { 143 144 return xen_intr_establish_xname(legacy_irq, pic, pin, type, level, 145 handler, arg, known_mpsafe, "XEN"); 146 } 147 148 void * 149 xen_intr_establish_xname(int legacy_irq, struct pic *pic, int pin, 150 int type, int level, int (*handler)(void *), void *arg, 151 bool known_mpsafe, const char *xname) 152 { 153 const char *intrstr; 154 char intrstr_buf[INTRIDBUF]; 155 156 if (pic->pic_type == PIC_XEN) { 157 struct intrhand *rih; 158 159 intrstr = intr_create_intrid(legacy_irq, pic, pin, intrstr_buf, 160 sizeof(intrstr_buf)); 161 162 rih = event_set_handler(pin, handler, arg, level, 163 intrstr, xname, known_mpsafe, NULL); 164 165 if (rih == NULL) { 166 printf("%s: can't establish interrupt\n", __func__); 167 return NULL; 168 } 169 170 return rih; 171 } /* Else we assume pintr */ 172 173 #if (NPCI > 0 || NISA > 0) && defined(XENPV) /* XXX: support PVHVM pirq */ 174 struct pintrhand *pih; 175 int gsi; 176 int evtchn; 177 /* the hack below is from x86's intr_establish_xname() */ 178 bool mpsafe = (known_mpsafe || level != IPL_VM); 179 180 KASSERTMSG(legacy_irq == -1 || (0 <= legacy_irq && legacy_irq < NUM_XEN_IRQS), 181 "bad legacy IRQ value: %d", legacy_irq); 182 KASSERTMSG(!(legacy_irq == -1 && pic == &i8259_pic), 183 "non-legacy IRQon i8259 "); 184 185 gsi = xen_pic_to_gsi(pic, pin); 186 if (gsi < 0) 187 return NULL; 188 KASSERTMSG(gsi < NR_EVENT_CHANNELS, "gsi %d >= NR_EVENT_CHANNELS %u", 189 gsi, (int)NR_EVENT_CHANNELS); 190 191 intrstr = intr_create_intrid(gsi, pic, pin, intrstr_buf, 192 sizeof(intrstr_buf)); 193 194 if (irq2port[gsi] == 0) { 195 extern struct cpu_info phycpu_info_primary; /* XXX */ 196 struct cpu_info *ci = &phycpu_info_primary; 197 198 pic->pic_addroute(pic, ci, pin, gsi, type); 199 200 evtchn = bind_pirq_to_evtch(gsi); 201 KASSERT(evtchn > 0); 202 KASSERT(evtchn < NR_EVENT_CHANNELS); 203 irq2port[gsi] = evtchn + 1; 204 xen_atomic_set_bit(&ci->ci_evtmask[0], evtchn); 205 } else { 206 /* 207 * Shared interrupt - we can't rebind. 208 * The port is shared instead. 209 */ 210 evtchn = irq2port[gsi] - 1; 211 } 212 213 pih = pirq_establish(gsi, evtchn, handler, arg, level, 214 intrstr, xname, mpsafe); 215 pih->pic = pic; 216 if (msipic_is_msi_pic(pic)) 217 pic->pic_hwunmask(pic, pin); 218 return pih; 219 #endif /* NPCI > 0 || NISA > 0 */ 220 221 /* FALLTHROUGH */ 222 return NULL; 223 } 224 225 /* 226 * Mask an interrupt source. 227 */ 228 void 229 xen_intr_mask(struct intrhand *ih) 230 { 231 /* XXX */ 232 panic("xen_intr_mask: not yet implemented."); 233 } 234 235 /* 236 * Unmask an interrupt source. 237 */ 238 void 239 xen_intr_unmask(struct intrhand *ih) 240 { 241 /* XXX */ 242 panic("xen_intr_unmask: not yet implemented."); 243 } 244 245 /* 246 * Deregister an interrupt handler. 247 */ 248 void 249 xen_intr_disestablish(struct intrhand *ih) 250 { 251 252 if (ih->ih_pic->pic_type == PIC_XEN) { 253 event_remove_handler(ih->ih_pin, ih->ih_realfun, 254 ih->ih_realarg); 255 /* event_remove_handler frees ih */ 256 return; 257 } 258 #if defined(DOM0OPS) && defined(XENPV) 259 /* 260 * Cache state, to prevent a use after free situation with 261 * ih. 262 */ 263 264 struct pintrhand *pih = (struct pintrhand *)ih; 265 266 int pirq = pih->pirq; 267 int port = pih->evtch; 268 KASSERT(irq2port[pirq] != 0); 269 270 pirq_disestablish(pih); 271 272 if (evtsource[port] == NULL) { 273 /* 274 * Last handler was removed by 275 * event_remove_handler(). 276 * 277 * We can safely unbind the pirq now. 278 */ 279 280 port = unbind_pirq_from_evtch(pirq); 281 KASSERT(port == pih->evtch); 282 irq2port[pirq] = 0; 283 } 284 #endif 285 return; 286 } 287 288 /* MI interface for kern_cpu.c */ 289 void xen_cpu_intr_redistribute(void); 290 291 void 292 xen_cpu_intr_redistribute(void) 293 { 294 KASSERT(mutex_owned(&cpu_lock)); 295 KASSERT(mp_online); 296 297 return; 298 } 299 300 /* MD - called by x86/cpu.c */ 301 #if defined(INTRSTACKSIZE) 302 static inline bool 303 redzone_const_or_false(bool x) 304 { 305 #ifdef DIAGNOSTIC 306 return x; 307 #else 308 return false; 309 #endif /* !DIAGNOSTIC */ 310 } 311 312 static inline int 313 redzone_const_or_zero(int x) 314 { 315 return redzone_const_or_false(true) ? x : 0; 316 } 317 #endif 318 319 void xen_cpu_intr_init(struct cpu_info *); 320 void 321 xen_cpu_intr_init(struct cpu_info *ci) 322 { 323 #if defined(__HAVE_PREEMPTION) 324 x86_init_preempt(ci); 325 #endif 326 x86_intr_calculatemasks(ci); 327 328 #if defined(INTRSTACKSIZE) 329 vaddr_t istack; 330 331 /* 332 * If the red zone is activated, protect both the top and 333 * the bottom of the stack with an unmapped page. 334 */ 335 istack = uvm_km_alloc(kernel_map, 336 INTRSTACKSIZE + redzone_const_or_zero(2 * PAGE_SIZE), 0, 337 UVM_KMF_WIRED|UVM_KMF_ZERO); 338 if (redzone_const_or_false(true)) { 339 pmap_kremove(istack, PAGE_SIZE); 340 pmap_kremove(istack + INTRSTACKSIZE + PAGE_SIZE, PAGE_SIZE); 341 pmap_update(pmap_kernel()); 342 } 343 344 /* 345 * 33 used to be 1. Arbitrarily reserve 32 more register_t's 346 * of space for ddb(4) to examine some subroutine arguments 347 * and to hunt for the next stack frame. 348 */ 349 ci->ci_intrstack = (char *)istack + redzone_const_or_zero(PAGE_SIZE) + 350 INTRSTACKSIZE - 33 * sizeof(register_t); 351 #endif 352 353 #ifdef MULTIPROCESSOR 354 for (int i = 0; i < XEN_NIPIS; i++) 355 evcnt_attach_dynamic(&ci->ci_ipi_events[i], EVCNT_TYPE_MISC, 356 NULL, device_xname(ci->ci_dev), xen_ipi_names[i]); 357 #endif 358 359 ci->ci_idepth = -1; 360 } 361 362 /* 363 * Everything below from here is duplicated from x86/intr.c 364 * When intr.c and xen_intr.c are unified, these will need to be 365 * merged. 366 */ 367 368 u_int xen_cpu_intr_count(struct cpu_info *ci); 369 370 u_int 371 xen_cpu_intr_count(struct cpu_info *ci) 372 { 373 374 KASSERT(ci->ci_nintrhand >= 0); 375 376 return ci->ci_nintrhand; 377 } 378 379 static const char * 380 xen_intr_string(int port, char *buf, size_t len, struct pic *pic) 381 { 382 KASSERT(pic->pic_type == PIC_XEN); 383 384 KASSERT(port >= 0); 385 KASSERT(port < NR_EVENT_CHANNELS); 386 387 snprintf(buf, len, "%s chan %d", pic->pic_name, port); 388 389 return buf; 390 } 391 392 static const char * 393 legacy_intr_string(int ih, char *buf, size_t len, struct pic *pic) 394 { 395 int legacy_irq; 396 397 KASSERT(pic->pic_type == PIC_I8259); 398 #if NLAPIC > 0 399 KASSERT(APIC_IRQ_ISLEGACY(ih)); 400 401 legacy_irq = APIC_IRQ_LEGACY_IRQ(ih); 402 #else 403 legacy_irq = ih; 404 #endif 405 KASSERT(legacy_irq >= 0 && legacy_irq < 16); 406 407 snprintf(buf, len, "%s pin %d", pic->pic_name, legacy_irq); 408 409 return buf; 410 } 411 412 const char * xintr_string(intr_handle_t ih, char *buf, size_t len); 413 414 const char * 415 xintr_string(intr_handle_t ih, char *buf, size_t len) 416 { 417 #if NIOAPIC > 0 418 struct ioapic_softc *pic; 419 #endif 420 421 if (ih == 0) 422 panic("%s: bogus handle 0x%" PRIx64, __func__, ih); 423 424 #if NIOAPIC > 0 425 if (ih & APIC_INT_VIA_APIC) { 426 pic = ioapic_find(APIC_IRQ_APIC(ih)); 427 if (pic != NULL) { 428 snprintf(buf, len, "%s pin %d", 429 device_xname(pic->sc_dev), APIC_IRQ_PIN(ih)); 430 } else { 431 snprintf(buf, len, 432 "apic %d int %d (irq %d)", 433 APIC_IRQ_APIC(ih), 434 APIC_IRQ_PIN(ih), 435 APIC_IRQ_LEGACY_IRQ(ih)); 436 } 437 } else 438 snprintf(buf, len, "irq %d", APIC_IRQ_LEGACY_IRQ(ih)); 439 440 #elif NLAPIC > 0 441 snprintf(buf, len, "irq %d", APIC_IRQ_LEGACY_IRQ(ih)); 442 #else 443 snprintf(buf, len, "irq %d", (int) ih); 444 #endif 445 return buf; 446 447 } 448 449 /* 450 * Create an interrupt id such as "ioapic0 pin 9". This interrupt id is used 451 * by MI code and intrctl(8). 452 */ 453 const char * xen_intr_create_intrid(int legacy_irq, struct pic *pic, 454 int pin, char *buf, size_t len); 455 456 const char * 457 xen_intr_create_intrid(int legacy_irq, struct pic *pic, int pin, char *buf, size_t len) 458 { 459 int ih = 0; 460 461 #if NPCI > 0 && defined(XENPV) 462 #if defined(__HAVE_PCI_MSI_MSIX) 463 if ((pic->pic_type == PIC_MSI) || (pic->pic_type == PIC_MSIX)) { 464 uint64_t pih; 465 int dev, vec; 466 467 dev = msipic_get_devid(pic); 468 vec = pin; 469 pih = __SHIFTIN((uint64_t)dev, MSI_INT_DEV_MASK) 470 | __SHIFTIN((uint64_t)vec, MSI_INT_VEC_MASK) 471 | APIC_INT_VIA_MSI; 472 if (pic->pic_type == PIC_MSI) 473 MSI_INT_MAKE_MSI(pih); 474 else if (pic->pic_type == PIC_MSIX) 475 MSI_INT_MAKE_MSIX(pih); 476 477 return x86_pci_msi_string(NULL, pih, buf, len); 478 } 479 #endif /* __HAVE_PCI_MSI_MSIX */ 480 #endif 481 482 if (pic->pic_type == PIC_XEN) { 483 ih = pin; /* Port == pin */ 484 return xen_intr_string(pin, buf, len, pic); 485 } 486 487 /* 488 * If the device is pci, "legacy_irq" is always -1. Least 8 bit of "ih" 489 * is only used in intr_string() to show the irq number. 490 * If the device is "legacy"(such as floppy), it should not use 491 * intr_string(). 492 */ 493 if (pic->pic_type == PIC_I8259) { 494 ih = legacy_irq; 495 return legacy_intr_string(ih, buf, len, pic); 496 } 497 498 #if NIOAPIC > 0 || NACPICA > 0 499 ih = ((pic->pic_apicid << APIC_INT_APIC_SHIFT) & APIC_INT_APIC_MASK) 500 | ((pin << APIC_INT_PIN_SHIFT) & APIC_INT_PIN_MASK); 501 if (pic->pic_type == PIC_IOAPIC) { 502 ih |= APIC_INT_VIA_APIC; 503 } 504 ih |= pin; 505 return intr_string(ih, buf, len); 506 #endif 507 508 return NULL; /* No pic found! */ 509 } 510 511 static struct intrsource xen_dummy_intrsource; 512 513 struct intrsource * 514 xen_intr_allocate_io_intrsource(const char *intrid) 515 { 516 /* Nothing to do, required by MSI code */ 517 return &xen_dummy_intrsource; 518 } 519 520 void 521 xen_intr_free_io_intrsource(const char *intrid) 522 { 523 /* Nothing to do, required by MSI code */ 524 } 525 526 #if defined(XENPV) 527 __strong_alias(x86_read_psl, xen_read_psl); 528 __strong_alias(x86_write_psl, xen_write_psl); 529 530 __strong_alias(intr_string, xintr_string); 531 __strong_alias(intr_create_intrid, xen_intr_create_intrid); 532 __strong_alias(intr_establish, xen_intr_establish); 533 __strong_alias(intr_establish_xname, xen_intr_establish_xname); 534 __strong_alias(intr_mask, xen_intr_mask); 535 __strong_alias(intr_unmask, xen_intr_unmask); 536 __strong_alias(intr_disestablish, xen_intr_disestablish); 537 __strong_alias(cpu_intr_redistribute, xen_cpu_intr_redistribute); 538 __strong_alias(cpu_intr_count, xen_cpu_intr_count); 539 __strong_alias(cpu_intr_init, xen_cpu_intr_init); 540 __strong_alias(intr_allocate_io_intrsource, xen_intr_allocate_io_intrsource); 541 __strong_alias(intr_free_io_intrsource, xen_intr_free_io_intrsource); 542 #endif /* XENPV */ 543