1263035Stychon/*- 2263035Stychon * Copyright (c) 2014 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com> 3263035Stychon * All rights reserved. 4263035Stychon * 5263035Stychon * Redistribution and use in source and binary forms, with or without 6263035Stychon * modification, are permitted provided that the following conditions 7263035Stychon * are met: 8263035Stychon * 1. Redistributions of source code must retain the above copyright 9263035Stychon * notice, this list of conditions and the following disclaimer. 10263035Stychon * 2. Redistributions in binary form must reproduce the above copyright 11263035Stychon * notice, this list of conditions and the following disclaimer in the 12263035Stychon * documentation and/or other materials provided with the distribution. 13263035Stychon * 14263035Stychon * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND 15263035Stychon * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16263035Stychon * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17263035Stychon * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18263035Stychon * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19263035Stychon * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20263035Stychon * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21263035Stychon * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22263035Stychon * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23263035Stychon * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24263035Stychon * SUCH DAMAGE. 25263035Stychon */ 26263035Stychon 27263035Stychon#include <sys/cdefs.h> 28263035Stychon__FBSDID("$FreeBSD$"); 29263035Stychon 30263035Stychon#include <sys/param.h> 31263035Stychon#include <sys/types.h> 32263035Stychon#include <sys/queue.h> 33263035Stychon#include <sys/kernel.h> 34263035Stychon#include <sys/lock.h> 35263035Stychon#include <sys/malloc.h> 36263035Stychon#include <sys/mutex.h> 37263035Stychon#include <sys/systm.h> 38263035Stychon 39263035Stychon#include <x86/apicreg.h> 40263035Stychon#include <dev/ic/i8259.h> 41263035Stychon 42263035Stychon#include <machine/vmm.h> 43263035Stychon 44263035Stychon#include "vmm_ktr.h" 45263035Stychon#include "vmm_lapic.h" 46263035Stychon#include "vioapic.h" 47263035Stychon#include "vatpic.h" 48263035Stychon 49263035Stychonstatic MALLOC_DEFINE(M_VATPIC, "atpic", "bhyve virtual atpic (8259)"); 50263035Stychon 51263035Stychon#define VATPIC_LOCK(vatpic) mtx_lock_spin(&((vatpic)->mtx)) 52263035Stychon#define VATPIC_UNLOCK(vatpic) mtx_unlock_spin(&((vatpic)->mtx)) 53263035Stychon#define VATPIC_LOCKED(vatpic) mtx_owned(&((vatpic)->mtx)) 54263035Stychon 55263035Stychonenum irqstate { 56263035Stychon IRQSTATE_ASSERT, 57263035Stychon IRQSTATE_DEASSERT, 58263035Stychon IRQSTATE_PULSE 59263035Stychon}; 60263035Stychon 61263035Stychonstruct atpic { 62263035Stychon bool ready; 63263035Stychon int icw_num; 64263035Stychon int rd_cmd_reg; 65263035Stychon 66263035Stychon bool aeoi; 67263035Stychon bool poll; 68263035Stychon bool rotate; 69268891Sjhb bool sfn; /* special fully-nested mode */ 70263035Stychon 71263035Stychon int irq_base; 72263035Stychon uint8_t request; /* Interrupt Request Register (IIR) */ 73263035Stychon uint8_t service; /* Interrupt Service (ISR) */ 74263035Stychon uint8_t mask; /* Interrupt Mask Register (IMR) */ 75276447Sneel uint8_t smm; /* special mask mode */ 76263035Stychon 77263035Stychon int acnt[8]; /* sum of pin asserts and deasserts */ 78276429Sneel int lowprio; /* lowest priority irq */ 79268891Sjhb 80268891Sjhb bool intr_raised; 81263035Stychon}; 82263035Stychon 83263035Stychonstruct vatpic { 84263035Stychon struct vm *vm; 85263035Stychon struct mtx mtx; 86263035Stychon struct atpic atpic[2]; 87263035Stychon uint8_t elc[2]; 88263035Stychon}; 89263035Stychon 90263035Stychon#define VATPIC_CTR0(vatpic, fmt) \ 91263035Stychon VM_CTR0((vatpic)->vm, fmt) 92263035Stychon 93263035Stychon#define VATPIC_CTR1(vatpic, fmt, a1) \ 94263035Stychon VM_CTR1((vatpic)->vm, fmt, a1) 95263035Stychon 96263035Stychon#define VATPIC_CTR2(vatpic, fmt, a1, a2) \ 97263035Stychon VM_CTR2((vatpic)->vm, fmt, a1, a2) 98263035Stychon 99263035Stychon#define VATPIC_CTR3(vatpic, fmt, a1, a2, a3) \ 100263035Stychon VM_CTR3((vatpic)->vm, fmt, a1, a2, a3) 101263035Stychon 102263035Stychon#define VATPIC_CTR4(vatpic, fmt, a1, a2, a3, a4) \ 103263035Stychon VM_CTR4((vatpic)->vm, fmt, a1, a2, a3, a4) 104263035Stychon 105276429Sneel/* 106276429Sneel * Loop over all the pins in priority order from highest to lowest. 107276429Sneel */ 108276429Sneel#define ATPIC_PIN_FOREACH(pinvar, atpic, tmpvar) \ 109276429Sneel for (tmpvar = 0, pinvar = (atpic->lowprio + 1) & 0x7; \ 110276429Sneel tmpvar < 8; \ 111276429Sneel tmpvar++, pinvar = (pinvar + 1) & 0x7) 112276429Sneel 113268891Sjhbstatic void vatpic_set_pinstate(struct vatpic *vatpic, int pin, bool newstate); 114263035Stychon 115276429Sneelstatic __inline bool 116276429Sneelmaster_atpic(struct vatpic *vatpic, struct atpic *atpic) 117276429Sneel{ 118276429Sneel 119276429Sneel if (atpic == &vatpic->atpic[0]) 120276429Sneel return (true); 121276429Sneel else 122276429Sneel return (false); 123276429Sneel} 124276429Sneel 125263035Stychonstatic __inline int 126263035Stychonvatpic_get_highest_isrpin(struct atpic *atpic) 127263035Stychon{ 128263035Stychon int bit, pin; 129263035Stychon int i; 130263035Stychon 131276429Sneel ATPIC_PIN_FOREACH(pin, atpic, i) { 132263035Stychon bit = (1 << pin); 133263035Stychon 134276447Sneel if (atpic->service & bit) { 135276447Sneel /* 136276447Sneel * An IS bit that is masked by an IMR bit will not be 137276447Sneel * cleared by a non-specific EOI in Special Mask Mode. 138276447Sneel */ 139276447Sneel if (atpic->smm && (atpic->mask & bit) != 0) 140276447Sneel continue; 141276447Sneel else 142276447Sneel return (pin); 143276447Sneel } 144263035Stychon } 145263035Stychon 146263035Stychon return (-1); 147263035Stychon} 148263035Stychon 149263035Stychonstatic __inline int 150263035Stychonvatpic_get_highest_irrpin(struct atpic *atpic) 151263035Stychon{ 152268891Sjhb int serviced; 153276429Sneel int bit, pin, tmp; 154263035Stychon 155268891Sjhb /* 156268891Sjhb * In 'Special Fully-Nested Mode' when an interrupt request from 157268891Sjhb * a slave is in service, the slave is not locked out from the 158268891Sjhb * master's priority logic. 159268891Sjhb */ 160268891Sjhb serviced = atpic->service; 161268891Sjhb if (atpic->sfn) 162268891Sjhb serviced &= ~(1 << 2); 163268891Sjhb 164276447Sneel /* 165276447Sneel * In 'Special Mask Mode', when a mask bit is set in OCW1 it inhibits 166276447Sneel * further interrupts at that level and enables interrupts from all 167276447Sneel * other levels that are not masked. In other words the ISR has no 168276447Sneel * bearing on the levels that can generate interrupts. 169276447Sneel */ 170276447Sneel if (atpic->smm) 171276447Sneel serviced = 0; 172276447Sneel 173276429Sneel ATPIC_PIN_FOREACH(pin, atpic, tmp) { 174276429Sneel bit = 1 << pin; 175276429Sneel 176276429Sneel /* 177276429Sneel * If there is already an interrupt in service at the same 178276429Sneel * or higher priority then bail. 179276429Sneel */ 180276429Sneel if ((serviced & bit) != 0) 181263035Stychon break; 182263035Stychon 183276429Sneel /* 184276429Sneel * If an interrupt is asserted and not masked then return 185276429Sneel * the corresponding 'pin' to the caller. 186276429Sneel */ 187276429Sneel if ((atpic->request & bit) != 0 && (atpic->mask & bit) == 0) 188263035Stychon return (pin); 189263035Stychon } 190263035Stychon 191263035Stychon return (-1); 192263035Stychon} 193263035Stychon 194263035Stychonstatic void 195263035Stychonvatpic_notify_intr(struct vatpic *vatpic) 196263035Stychon{ 197263035Stychon struct atpic *atpic; 198263035Stychon int pin; 199263035Stychon 200263035Stychon KASSERT(VATPIC_LOCKED(vatpic), ("vatpic_notify_intr not locked")); 201263035Stychon 202268891Sjhb /* 203268891Sjhb * First check the slave. 204268891Sjhb */ 205268891Sjhb atpic = &vatpic->atpic[1]; 206268891Sjhb if (!atpic->intr_raised && 207268891Sjhb (pin = vatpic_get_highest_irrpin(atpic)) != -1) { 208268891Sjhb VATPIC_CTR4(vatpic, "atpic slave notify pin = %d " 209268891Sjhb "(imr 0x%x irr 0x%x isr 0x%x)", pin, 210268891Sjhb atpic->mask, atpic->request, atpic->service); 211268891Sjhb 212268891Sjhb /* 213268891Sjhb * Cascade the request from the slave to the master. 214268891Sjhb */ 215268891Sjhb atpic->intr_raised = true; 216268891Sjhb vatpic_set_pinstate(vatpic, 2, true); 217268891Sjhb vatpic_set_pinstate(vatpic, 2, false); 218268891Sjhb } else { 219268891Sjhb VATPIC_CTR3(vatpic, "atpic slave no eligible interrupts " 220268891Sjhb "(imr 0x%x irr 0x%x isr 0x%x)", 221268891Sjhb atpic->mask, atpic->request, atpic->service); 222268891Sjhb } 223268891Sjhb 224268891Sjhb /* 225268891Sjhb * Then check the master. 226268891Sjhb */ 227263035Stychon atpic = &vatpic->atpic[0]; 228268891Sjhb if (!atpic->intr_raised && 229268891Sjhb (pin = vatpic_get_highest_irrpin(atpic)) != -1) { 230268891Sjhb VATPIC_CTR4(vatpic, "atpic master notify pin = %d " 231263035Stychon "(imr 0x%x irr 0x%x isr 0x%x)", pin, 232263035Stychon atpic->mask, atpic->request, atpic->service); 233268891Sjhb 234268891Sjhb /* 235270159Sgrehan * From Section 3.6.2, "Interrupt Modes", in the 236270159Sgrehan * MPtable Specification, Version 1.4 237270159Sgrehan * 238268891Sjhb * PIC interrupts are routed to both the Local APIC 239268891Sjhb * and the I/O APIC to support operation in 1 of 3 240268891Sjhb * modes. 241268891Sjhb * 242268891Sjhb * 1. Legacy PIC Mode: the PIC effectively bypasses 243270159Sgrehan * all APIC components. In this mode the local APIC is 244268891Sjhb * disabled and LINT0 is reconfigured as INTR to 245268891Sjhb * deliver the PIC interrupt directly to the CPU. 246268891Sjhb * 247268891Sjhb * 2. Virtual Wire Mode: the APIC is treated as a 248268891Sjhb * virtual wire which delivers interrupts from the PIC 249270159Sgrehan * to the CPU. In this mode LINT0 is programmed as 250268891Sjhb * ExtINT to indicate that the PIC is the source of 251268891Sjhb * the interrupt. 252268891Sjhb * 253270159Sgrehan * 3. Virtual Wire Mode via I/O APIC: PIC interrupts are 254270159Sgrehan * fielded by the I/O APIC and delivered to the appropriate 255270159Sgrehan * CPU. In this mode the I/O APIC input 0 is programmed 256270159Sgrehan * as ExtINT to indicate that the PIC is the source of the 257270159Sgrehan * interrupt. 258268891Sjhb */ 259268891Sjhb atpic->intr_raised = true; 260263035Stychon lapic_set_local_intr(vatpic->vm, -1, APIC_LVT_LINT0); 261263035Stychon vioapic_pulse_irq(vatpic->vm, 0); 262263035Stychon } else { 263268891Sjhb VATPIC_CTR3(vatpic, "atpic master no eligible interrupts " 264263035Stychon "(imr 0x%x irr 0x%x isr 0x%x)", 265263035Stychon atpic->mask, atpic->request, atpic->service); 266263035Stychon } 267263035Stychon} 268263035Stychon 269263035Stychonstatic int 270263035Stychonvatpic_icw1(struct vatpic *vatpic, struct atpic *atpic, uint8_t val) 271263035Stychon{ 272263035Stychon VATPIC_CTR1(vatpic, "atpic icw1 0x%x", val); 273263035Stychon 274263035Stychon atpic->ready = false; 275263035Stychon 276263035Stychon atpic->icw_num = 1; 277284899Sneel atpic->request = 0; 278263035Stychon atpic->mask = 0; 279276429Sneel atpic->lowprio = 7; 280263035Stychon atpic->rd_cmd_reg = 0; 281276429Sneel atpic->poll = 0; 282276447Sneel atpic->smm = 0; 283263035Stychon 284263035Stychon if ((val & ICW1_SNGL) != 0) { 285263035Stychon VATPIC_CTR0(vatpic, "vatpic cascade mode required"); 286263035Stychon return (-1); 287263035Stychon } 288263035Stychon 289263035Stychon if ((val & ICW1_IC4) == 0) { 290263035Stychon VATPIC_CTR0(vatpic, "vatpic icw4 required"); 291263035Stychon return (-1); 292263035Stychon } 293263035Stychon 294263035Stychon atpic->icw_num++; 295263035Stychon 296263035Stychon return (0); 297263035Stychon} 298263035Stychon 299263035Stychonstatic int 300263035Stychonvatpic_icw2(struct vatpic *vatpic, struct atpic *atpic, uint8_t val) 301263035Stychon{ 302263035Stychon VATPIC_CTR1(vatpic, "atpic icw2 0x%x", val); 303263035Stychon 304263035Stychon atpic->irq_base = val & 0xf8; 305263035Stychon 306263035Stychon atpic->icw_num++; 307263035Stychon 308263035Stychon return (0); 309263035Stychon} 310263035Stychon 311263035Stychonstatic int 312263035Stychonvatpic_icw3(struct vatpic *vatpic, struct atpic *atpic, uint8_t val) 313263035Stychon{ 314263035Stychon VATPIC_CTR1(vatpic, "atpic icw3 0x%x", val); 315263035Stychon 316263035Stychon atpic->icw_num++; 317263035Stychon 318263035Stychon return (0); 319263035Stychon} 320263035Stychon 321263035Stychonstatic int 322263035Stychonvatpic_icw4(struct vatpic *vatpic, struct atpic *atpic, uint8_t val) 323263035Stychon{ 324263035Stychon VATPIC_CTR1(vatpic, "atpic icw4 0x%x", val); 325263035Stychon 326263035Stychon if ((val & ICW4_8086) == 0) { 327263035Stychon VATPIC_CTR0(vatpic, "vatpic microprocessor mode required"); 328263035Stychon return (-1); 329263035Stychon } 330263035Stychon 331263035Stychon if ((val & ICW4_AEOI) != 0) 332263035Stychon atpic->aeoi = true; 333263035Stychon 334276429Sneel if ((val & ICW4_SFNM) != 0) { 335276429Sneel if (master_atpic(vatpic, atpic)) { 336276429Sneel atpic->sfn = true; 337276429Sneel } else { 338276429Sneel VATPIC_CTR1(vatpic, "Ignoring special fully nested " 339276429Sneel "mode on slave atpic: %#x", val); 340276429Sneel } 341276429Sneel } 342276429Sneel 343263035Stychon atpic->icw_num = 0; 344263035Stychon atpic->ready = true; 345263035Stychon 346263035Stychon return (0); 347263035Stychon} 348263035Stychon 349263035Stychonstatic int 350263035Stychonvatpic_ocw1(struct vatpic *vatpic, struct atpic *atpic, uint8_t val) 351263035Stychon{ 352263035Stychon VATPIC_CTR1(vatpic, "atpic ocw1 0x%x", val); 353263035Stychon 354263035Stychon atpic->mask = val & 0xff; 355263035Stychon 356263035Stychon return (0); 357263035Stychon} 358263035Stychon 359263035Stychonstatic int 360263035Stychonvatpic_ocw2(struct vatpic *vatpic, struct atpic *atpic, uint8_t val) 361263035Stychon{ 362263035Stychon VATPIC_CTR1(vatpic, "atpic ocw2 0x%x", val); 363263035Stychon 364263035Stychon atpic->rotate = ((val & OCW2_R) != 0); 365263035Stychon 366263035Stychon if ((val & OCW2_EOI) != 0) { 367263035Stychon int isr_bit; 368263035Stychon 369263035Stychon if ((val & OCW2_SL) != 0) { 370263035Stychon /* specific EOI */ 371263035Stychon isr_bit = val & 0x7; 372263035Stychon } else { 373263035Stychon /* non-specific EOI */ 374263035Stychon isr_bit = vatpic_get_highest_isrpin(atpic); 375263035Stychon } 376263035Stychon 377263035Stychon if (isr_bit != -1) { 378263035Stychon atpic->service &= ~(1 << isr_bit); 379263035Stychon 380263035Stychon if (atpic->rotate) 381276429Sneel atpic->lowprio = isr_bit; 382263035Stychon } 383263035Stychon } else if ((val & OCW2_SL) != 0 && atpic->rotate == true) { 384263035Stychon /* specific priority */ 385276429Sneel atpic->lowprio = val & 0x7; 386263035Stychon } 387263035Stychon 388263035Stychon return (0); 389263035Stychon} 390263035Stychon 391263035Stychonstatic int 392263035Stychonvatpic_ocw3(struct vatpic *vatpic, struct atpic *atpic, uint8_t val) 393263035Stychon{ 394263035Stychon VATPIC_CTR1(vatpic, "atpic ocw3 0x%x", val); 395263035Stychon 396276429Sneel if (val & OCW3_ESMM) { 397276447Sneel atpic->smm = val & OCW3_SMM ? 1 : 0; 398276447Sneel VATPIC_CTR2(vatpic, "%s atpic special mask mode %s", 399276447Sneel master_atpic(vatpic, atpic) ? "master" : "slave", 400276447Sneel atpic->smm ? "enabled" : "disabled"); 401276429Sneel } 402263035Stychon 403263035Stychon if (val & OCW3_RR) { 404263035Stychon /* read register command */ 405263035Stychon atpic->rd_cmd_reg = val & OCW3_RIS; 406276429Sneel 407276429Sneel /* Polling mode */ 408276429Sneel atpic->poll = ((val & OCW3_P) != 0); 409263035Stychon } 410263035Stychon 411263035Stychon return (0); 412263035Stychon} 413263035Stychon 414263035Stychonstatic void 415263035Stychonvatpic_set_pinstate(struct vatpic *vatpic, int pin, bool newstate) 416263035Stychon{ 417263035Stychon struct atpic *atpic; 418263035Stychon int oldcnt, newcnt; 419263035Stychon bool level; 420263035Stychon 421263035Stychon KASSERT(pin >= 0 && pin < 16, 422263035Stychon ("vatpic_set_pinstate: invalid pin number %d", pin)); 423263035Stychon KASSERT(VATPIC_LOCKED(vatpic), 424263035Stychon ("vatpic_set_pinstate: vatpic is not locked")); 425263035Stychon 426263035Stychon atpic = &vatpic->atpic[pin >> 3]; 427263035Stychon 428263035Stychon oldcnt = atpic->acnt[pin & 0x7]; 429263035Stychon if (newstate) 430263035Stychon atpic->acnt[pin & 0x7]++; 431263035Stychon else 432263035Stychon atpic->acnt[pin & 0x7]--; 433263035Stychon newcnt = atpic->acnt[pin & 0x7]; 434263035Stychon 435263035Stychon if (newcnt < 0) { 436263035Stychon VATPIC_CTR2(vatpic, "atpic pin%d: bad acnt %d", pin, newcnt); 437263035Stychon } 438263035Stychon 439263035Stychon level = ((vatpic->elc[pin >> 3] & (1 << (pin & 0x7))) != 0); 440263035Stychon 441263035Stychon if ((oldcnt == 0 && newcnt == 1) || (newcnt > 0 && level == true)) { 442263035Stychon /* rising edge or level */ 443263035Stychon VATPIC_CTR1(vatpic, "atpic pin%d: asserted", pin); 444263035Stychon atpic->request |= (1 << (pin & 0x7)); 445263035Stychon } else if (oldcnt == 1 && newcnt == 0) { 446263035Stychon /* falling edge */ 447263035Stychon VATPIC_CTR1(vatpic, "atpic pin%d: deasserted", pin); 448276429Sneel if (level) 449276429Sneel atpic->request &= ~(1 << (pin & 0x7)); 450263035Stychon } else { 451263035Stychon VATPIC_CTR3(vatpic, "atpic pin%d: %s, ignored, acnt %d", 452263035Stychon pin, newstate ? "asserted" : "deasserted", newcnt); 453263035Stychon } 454263035Stychon 455263035Stychon vatpic_notify_intr(vatpic); 456263035Stychon} 457263035Stychon 458263035Stychonstatic int 459263035Stychonvatpic_set_irqstate(struct vm *vm, int irq, enum irqstate irqstate) 460263035Stychon{ 461263035Stychon struct vatpic *vatpic; 462263035Stychon struct atpic *atpic; 463263035Stychon 464263035Stychon if (irq < 0 || irq > 15) 465263035Stychon return (EINVAL); 466263035Stychon 467263035Stychon vatpic = vm_atpic(vm); 468263035Stychon atpic = &vatpic->atpic[irq >> 3]; 469263035Stychon 470263035Stychon if (atpic->ready == false) 471263035Stychon return (0); 472263035Stychon 473263035Stychon VATPIC_LOCK(vatpic); 474263035Stychon switch (irqstate) { 475263035Stychon case IRQSTATE_ASSERT: 476263035Stychon vatpic_set_pinstate(vatpic, irq, true); 477263035Stychon break; 478263035Stychon case IRQSTATE_DEASSERT: 479263035Stychon vatpic_set_pinstate(vatpic, irq, false); 480263035Stychon break; 481263035Stychon case IRQSTATE_PULSE: 482263035Stychon vatpic_set_pinstate(vatpic, irq, true); 483263035Stychon vatpic_set_pinstate(vatpic, irq, false); 484263035Stychon break; 485263035Stychon default: 486263035Stychon panic("vatpic_set_irqstate: invalid irqstate %d", irqstate); 487263035Stychon } 488263035Stychon VATPIC_UNLOCK(vatpic); 489263035Stychon 490263035Stychon return (0); 491263035Stychon} 492263035Stychon 493263035Stychonint 494263035Stychonvatpic_assert_irq(struct vm *vm, int irq) 495263035Stychon{ 496263035Stychon return (vatpic_set_irqstate(vm, irq, IRQSTATE_ASSERT)); 497263035Stychon} 498263035Stychon 499263035Stychonint 500263035Stychonvatpic_deassert_irq(struct vm *vm, int irq) 501263035Stychon{ 502263035Stychon return (vatpic_set_irqstate(vm, irq, IRQSTATE_DEASSERT)); 503263035Stychon} 504263035Stychon 505263035Stychonint 506263035Stychonvatpic_pulse_irq(struct vm *vm, int irq) 507263035Stychon{ 508263035Stychon return (vatpic_set_irqstate(vm, irq, IRQSTATE_PULSE)); 509263035Stychon} 510263035Stychon 511268972Sjhbint 512268972Sjhbvatpic_set_irq_trigger(struct vm *vm, int irq, enum vm_intr_trigger trigger) 513268972Sjhb{ 514268972Sjhb struct vatpic *vatpic; 515268972Sjhb 516268972Sjhb if (irq < 0 || irq > 15) 517268972Sjhb return (EINVAL); 518268972Sjhb 519268972Sjhb /* 520268972Sjhb * See comment in vatpic_elc_handler. These IRQs must be 521268972Sjhb * edge triggered. 522268972Sjhb */ 523268972Sjhb if (trigger == LEVEL_TRIGGER) { 524268972Sjhb switch (irq) { 525268972Sjhb case 0: 526268972Sjhb case 1: 527268972Sjhb case 2: 528268972Sjhb case 8: 529268972Sjhb case 13: 530268972Sjhb return (EINVAL); 531268972Sjhb } 532268972Sjhb } 533268972Sjhb 534268972Sjhb vatpic = vm_atpic(vm); 535268972Sjhb 536268972Sjhb VATPIC_LOCK(vatpic); 537268972Sjhb 538268972Sjhb if (trigger == LEVEL_TRIGGER) 539268972Sjhb vatpic->elc[irq >> 3] |= 1 << (irq & 0x7); 540268972Sjhb else 541268972Sjhb vatpic->elc[irq >> 3] &= ~(1 << (irq & 0x7)); 542268972Sjhb 543268972Sjhb VATPIC_UNLOCK(vatpic); 544268972Sjhb 545268972Sjhb return (0); 546268972Sjhb} 547268972Sjhb 548268891Sjhbvoid 549263035Stychonvatpic_pending_intr(struct vm *vm, int *vecptr) 550263035Stychon{ 551263035Stychon struct vatpic *vatpic; 552263035Stychon struct atpic *atpic; 553263035Stychon int pin; 554263035Stychon 555263035Stychon vatpic = vm_atpic(vm); 556263035Stychon 557263035Stychon atpic = &vatpic->atpic[0]; 558263035Stychon 559263035Stychon VATPIC_LOCK(vatpic); 560263035Stychon 561263035Stychon pin = vatpic_get_highest_irrpin(atpic); 562268891Sjhb if (pin == 2) { 563268891Sjhb atpic = &vatpic->atpic[1]; 564268891Sjhb pin = vatpic_get_highest_irrpin(atpic); 565268891Sjhb } 566263035Stychon 567276349Sneel /* 568276349Sneel * If there are no pins active at this moment then return the spurious 569276349Sneel * interrupt vector instead. 570276349Sneel */ 571276349Sneel if (pin == -1) 572276349Sneel pin = 7; 573276349Sneel 574276349Sneel KASSERT(pin >= 0 && pin <= 7, ("%s: invalid pin %d", __func__, pin)); 575263035Stychon *vecptr = atpic->irq_base + pin; 576263035Stychon 577263035Stychon VATPIC_UNLOCK(vatpic); 578268891Sjhb} 579263035Stychon 580268891Sjhbstatic void 581268891Sjhbvatpic_pin_accepted(struct atpic *atpic, int pin) 582268891Sjhb{ 583268891Sjhb atpic->intr_raised = false; 584268891Sjhb 585268891Sjhb if (atpic->acnt[pin] == 0) 586268891Sjhb atpic->request &= ~(1 << pin); 587268891Sjhb 588268891Sjhb if (atpic->aeoi == true) { 589268891Sjhb if (atpic->rotate == true) 590276429Sneel atpic->lowprio = pin; 591268891Sjhb } else { 592268891Sjhb atpic->service |= (1 << pin); 593268891Sjhb } 594263035Stychon} 595263035Stychon 596263035Stychonvoid 597263035Stychonvatpic_intr_accepted(struct vm *vm, int vector) 598263035Stychon{ 599263035Stychon struct vatpic *vatpic; 600263035Stychon int pin; 601263035Stychon 602263035Stychon vatpic = vm_atpic(vm); 603263035Stychon 604268891Sjhb VATPIC_LOCK(vatpic); 605263035Stychon 606263035Stychon pin = vector & 0x7; 607263035Stychon 608268891Sjhb if ((vector & ~0x7) == vatpic->atpic[1].irq_base) { 609268891Sjhb vatpic_pin_accepted(&vatpic->atpic[1], pin); 610268891Sjhb /* 611268891Sjhb * If this vector originated from the slave, 612268891Sjhb * accept the cascaded interrupt too. 613268891Sjhb */ 614268891Sjhb vatpic_pin_accepted(&vatpic->atpic[0], 2); 615263035Stychon } else { 616268891Sjhb vatpic_pin_accepted(&vatpic->atpic[0], pin); 617263035Stychon } 618263035Stychon 619263035Stychon vatpic_notify_intr(vatpic); 620263035Stychon 621263035Stychon VATPIC_UNLOCK(vatpic); 622263035Stychon} 623263035Stychon 624268891Sjhbstatic int 625268891Sjhbvatpic_read(struct vatpic *vatpic, struct atpic *atpic, bool in, int port, 626268891Sjhb int bytes, uint32_t *eax) 627263035Stychon{ 628276429Sneel int pin; 629276429Sneel 630268891Sjhb VATPIC_LOCK(vatpic); 631263035Stychon 632268891Sjhb if (atpic->poll) { 633276429Sneel atpic->poll = 0; 634276429Sneel pin = vatpic_get_highest_irrpin(atpic); 635276429Sneel if (pin >= 0) { 636276429Sneel vatpic_pin_accepted(atpic, pin); 637276429Sneel *eax = 0x80 | pin; 638276429Sneel } else { 639276429Sneel *eax = 0; 640276429Sneel } 641268891Sjhb } else { 642268891Sjhb if (port & ICU_IMR_OFFSET) { 643268891Sjhb /* read interrrupt mask register */ 644268891Sjhb *eax = atpic->mask; 645263035Stychon } else { 646268891Sjhb if (atpic->rd_cmd_reg == OCW3_RIS) { 647268891Sjhb /* read interrupt service register */ 648268891Sjhb *eax = atpic->service; 649263035Stychon } else { 650268891Sjhb /* read interrupt request register */ 651268891Sjhb *eax = atpic->request; 652263035Stychon } 653263035Stychon } 654263035Stychon } 655263035Stychon 656268891Sjhb VATPIC_UNLOCK(vatpic); 657263035Stychon 658268891Sjhb return (0); 659268891Sjhb 660268891Sjhb} 661268891Sjhb 662268891Sjhbstatic int 663268891Sjhbvatpic_write(struct vatpic *vatpic, struct atpic *atpic, bool in, int port, 664268891Sjhb int bytes, uint32_t *eax) 665268891Sjhb{ 666268891Sjhb int error; 667268891Sjhb uint8_t val; 668268891Sjhb 669268891Sjhb error = 0; 670268891Sjhb val = *eax; 671268891Sjhb 672263035Stychon VATPIC_LOCK(vatpic); 673263035Stychon 674268891Sjhb if (port & ICU_IMR_OFFSET) { 675272388Sgrehan switch (atpic->icw_num) { 676272388Sgrehan case 2: 677272388Sgrehan error = vatpic_icw2(vatpic, atpic, val); 678272388Sgrehan break; 679272388Sgrehan case 3: 680272388Sgrehan error = vatpic_icw3(vatpic, atpic, val); 681272388Sgrehan break; 682272388Sgrehan case 4: 683272388Sgrehan error = vatpic_icw4(vatpic, atpic, val); 684272388Sgrehan break; 685272388Sgrehan default: 686263035Stychon error = vatpic_ocw1(vatpic, atpic, val); 687272388Sgrehan break; 688263035Stychon } 689263035Stychon } else { 690263035Stychon if (val & (1 << 4)) 691263035Stychon error = vatpic_icw1(vatpic, atpic, val); 692263035Stychon 693263035Stychon if (atpic->ready) { 694263035Stychon if (val & (1 << 3)) 695263035Stychon error = vatpic_ocw3(vatpic, atpic, val); 696263035Stychon else 697263035Stychon error = vatpic_ocw2(vatpic, atpic, val); 698263035Stychon } 699263035Stychon } 700263035Stychon 701263035Stychon if (atpic->ready) 702263035Stychon vatpic_notify_intr(vatpic); 703263035Stychon 704263035Stychon VATPIC_UNLOCK(vatpic); 705263035Stychon 706263035Stychon return (error); 707263035Stychon} 708263035Stychon 709263035Stychonint 710276429Sneelvatpic_master_handler(struct vm *vm, int vcpuid, bool in, int port, int bytes, 711268891Sjhb uint32_t *eax) 712263035Stychon{ 713268891Sjhb struct vatpic *vatpic; 714268891Sjhb struct atpic *atpic; 715268891Sjhb 716268891Sjhb vatpic = vm_atpic(vm); 717268891Sjhb atpic = &vatpic->atpic[0]; 718268891Sjhb 719268891Sjhb if (bytes != 1) 720263035Stychon return (-1); 721263035Stychon 722268891Sjhb if (in) { 723268891Sjhb return (vatpic_read(vatpic, atpic, in, port, bytes, eax)); 724263035Stychon } 725263035Stychon 726268891Sjhb return (vatpic_write(vatpic, atpic, in, port, bytes, eax)); 727263035Stychon} 728263035Stychon 729263035Stychonint 730276429Sneelvatpic_slave_handler(struct vm *vm, int vcpuid, bool in, int port, int bytes, 731268891Sjhb uint32_t *eax) 732263035Stychon{ 733263035Stychon struct vatpic *vatpic; 734268891Sjhb struct atpic *atpic; 735268891Sjhb 736268891Sjhb vatpic = vm_atpic(vm); 737268891Sjhb atpic = &vatpic->atpic[1]; 738268891Sjhb 739268891Sjhb if (bytes != 1) 740268891Sjhb return (-1); 741268891Sjhb 742268891Sjhb if (in) { 743268891Sjhb return (vatpic_read(vatpic, atpic, in, port, bytes, eax)); 744268891Sjhb } 745268891Sjhb 746268891Sjhb return (vatpic_write(vatpic, atpic, in, port, bytes, eax)); 747268891Sjhb} 748268891Sjhb 749268891Sjhbint 750276429Sneelvatpic_elc_handler(struct vm *vm, int vcpuid, bool in, int port, int bytes, 751268891Sjhb uint32_t *eax) 752268891Sjhb{ 753268891Sjhb struct vatpic *vatpic; 754263035Stychon bool is_master; 755263035Stychon 756263035Stychon vatpic = vm_atpic(vm); 757268891Sjhb is_master = (port == IO_ELCR1); 758263035Stychon 759268891Sjhb if (bytes != 1) 760263035Stychon return (-1); 761263035Stychon 762268891Sjhb VATPIC_LOCK(vatpic); 763268891Sjhb 764268891Sjhb if (in) { 765263035Stychon if (is_master) 766268891Sjhb *eax = vatpic->elc[0]; 767263035Stychon else 768268891Sjhb *eax = vatpic->elc[1]; 769263035Stychon } else { 770263035Stychon /* 771263035Stychon * For the master PIC the cascade channel (IRQ2), the 772263035Stychon * heart beat timer (IRQ0), and the keyboard 773263035Stychon * controller (IRQ1) cannot be programmed for level 774263035Stychon * mode. 775263035Stychon * 776263035Stychon * For the slave PIC the real time clock (IRQ8) and 777263035Stychon * the floating point error interrupt (IRQ13) cannot 778263035Stychon * be programmed for level mode. 779263035Stychon */ 780263035Stychon if (is_master) 781268891Sjhb vatpic->elc[0] = (*eax & 0xf8); 782263035Stychon else 783268891Sjhb vatpic->elc[1] = (*eax & 0xde); 784263035Stychon } 785263035Stychon 786268891Sjhb VATPIC_UNLOCK(vatpic); 787268891Sjhb 788263035Stychon return (0); 789263035Stychon} 790263035Stychon 791263035Stychonstruct vatpic * 792263035Stychonvatpic_init(struct vm *vm) 793263035Stychon{ 794263035Stychon struct vatpic *vatpic; 795263035Stychon 796263035Stychon vatpic = malloc(sizeof(struct vatpic), M_VATPIC, M_WAITOK | M_ZERO); 797263035Stychon vatpic->vm = vm; 798263035Stychon 799263035Stychon mtx_init(&vatpic->mtx, "vatpic lock", NULL, MTX_SPIN); 800263035Stychon 801263035Stychon return (vatpic); 802263035Stychon} 803263035Stychon 804263035Stychonvoid 805263035Stychonvatpic_cleanup(struct vatpic *vatpic) 806263035Stychon{ 807263035Stychon free(vatpic, M_VATPIC); 808263035Stychon} 809