/*- * Copyright (c) 2014 Tycho Nightingale * Copyright (c) 2017 Intel Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #define pr_prefix "vpic: " #include #include #include #include #include #define ACRN_DBG_PIC 6U static void vpic_set_pinstate(struct acrn_vpic *vpic, uint32_t pin, uint8_t level); struct acrn_vpic *vm_pic(const struct acrn_vm *vm) { return (struct acrn_vpic *)&(vm->arch_vm.vpic); } static inline bool master_pic(const struct acrn_vpic *vpic, const struct i8259_reg_state *i8259) { bool ret; if (i8259 == &vpic->i8259[0]) { ret = true; } else { ret = false; } return ret; } static inline uint32_t vpic_get_highest_isrpin(const struct i8259_reg_state *i8259) { uint32_t bit, pin, i; uint32_t found_pin = INVALID_INTERRUPT_PIN; pin = (i8259->lowprio + 1U) & 0x7U; for (i = 0U; i < NR_VPIC_PINS_PER_CHIP; i++) { bit = (1U << pin); if ((i8259->service & bit) != 0U) { /* * An IS bit that is masked by an IMR bit will not be * cleared by a non-specific EOI in Special Mask Mode. */ if ((i8259->smm != 0U) && ((i8259->mask & bit) != 0U)) { pin = (pin + 1U) & 0x7U; continue; } else { found_pin = pin; break; } } pin = (pin + 1U) & 0x7U; } return found_pin; } static inline uint32_t vpic_get_highest_irrpin(const struct i8259_reg_state *i8259) { uint8_t serviced; uint32_t bit, pin, tmp; uint32_t found_pin = INVALID_INTERRUPT_PIN; /* * In 'Special Fully-Nested Mode' when an interrupt request from * a slave is in service, the slave is not locked out from the * master's priority logic. */ serviced = i8259->service; if (i8259->sfn) { serviced &= ~(uint8_t)(1U << 2U); } /* * In 'Special Mask Mode', when a mask bit is set in OCW1 it inhibits * further interrupts at that level and enables interrupts from all * other levels that are not masked. In other words the ISR has no * bearing on the levels that can generate interrupts. */ if (i8259->smm != 0U) { serviced = 0U; } pin = (i8259->lowprio + 1U) & 0x7U; for (tmp = 0U; tmp < NR_VPIC_PINS_PER_CHIP; tmp++) { bit = (1U << pin); /* * If there is already an interrupt in service at the same * or higher priority then bail. */ if ((serviced & bit) != 0U) { break; } /* * If an interrupt is asserted and not masked then return * the corresponding 'pin' to the caller. */ if (((i8259->request & bit) != 0U) && ((i8259->mask & bit) == 0U)) { found_pin = pin; break; } pin = (pin + 1U) & 0x7U; } return found_pin; } static void vpic_notify_intr(struct acrn_vpic *vpic) { struct i8259_reg_state *i8259; uint32_t pin; /* * First check the slave. */ i8259 = &vpic->i8259[1]; pin = vpic_get_highest_irrpin(i8259); if (!i8259->intr_raised && (pin < NR_VPIC_PINS_PER_CHIP)) { dev_dbg(ACRN_DBG_PIC, "pic slave notify pin = %hhu (imr 0x%x irr 0x%x isr 0x%x)\n", pin, i8259->mask, i8259->request, i8259->service); /* * Cascade the request from the slave to the master. */ i8259->intr_raised = true; vpic_set_pinstate(vpic, 2U, 1U); vpic_set_pinstate(vpic, 2U, 0U); } else { dev_dbg(ACRN_DBG_PIC, "pic slave no eligible interrupt (imr 0x%x irr 0x%x isr 0x%x)", i8259->mask, i8259->request, i8259->service); } /* * Then check the master. */ i8259 = &vpic->i8259[0]; pin = vpic_get_highest_irrpin(i8259); if (!i8259->intr_raised && (pin < NR_VPIC_PINS_PER_CHIP)) { dev_dbg(ACRN_DBG_PIC, "pic master notify pin = %hhu (imr 0x%x irr 0x%x isr 0x%x)\n", pin, i8259->mask, i8259->request, i8259->service); /* * From Section 3.6.2, "Interrupt Modes", in the * MPtable Specification, Version 1.4 * * PIC interrupts are routed to both the Local APIC * and the I/O APIC to support operation in 1 of 3 * modes. * * 1. Legacy PIC Mode: the PIC effectively bypasses * all APIC components. In this mode the local APIC is * disabled and LINT0 is reconfigured as INTR to * deliver the PIC interrupt directly to the CPU. * * 2. Virtual Wire Mode: the APIC is treated as a * virtual wire which delivers interrupts from the PIC * to the CPU. In this mode LINT0 is programmed as * ExtINT to indicate that the PIC is the source of * the interrupt. * * 3. Virtual Wire Mode via I/O APIC: PIC interrupts are * fielded by the I/O APIC and delivered to the appropriate * CPU. In this mode the I/O APIC input 0 is programmed * as ExtINT to indicate that the PIC is the source of the * interrupt. */ i8259->intr_raised = true; if (vpic->vm->wire_mode == VPIC_WIRE_INTR) { struct acrn_vcpu *bsp = vcpu_from_vid(vpic->vm, BOOT_CPU_ID); vcpu_inject_extint(bsp); } else { /* * The input parameters here guarantee the return value of vlapic_set_local_intr is 0, means * success. */ (void)vlapic_set_local_intr(vpic->vm, BROADCAST_CPU_ID, APIC_LVT_LINT0); /* notify vioapic pin0 if existing * For vPIC + vIOAPIC mode, vpic master irq connected * to vioapic pin0 (irq2) * From MPSpec session 5.1 */ vioapic_set_irqline_lock(vpic->vm, 0U, GSI_RAISING_PULSE); } } else { dev_dbg(ACRN_DBG_PIC, "pic master no eligible interrupt (imr 0x%x irr 0x%x isr 0x%x)", i8259->mask, i8259->request, i8259->service); } } static int32_t vpic_icw1(const struct acrn_vpic *vpic, struct i8259_reg_state *i8259, uint8_t val) { int32_t ret; dev_dbg(ACRN_DBG_PIC, "vm 0x%x: i8259 icw1 0x%x\n", vpic->vm, val); i8259->ready = false; i8259->icw_num = 1U; i8259->request = 0U; i8259->mask = 0U; i8259->lowprio = 7U; i8259->rd_cmd_reg = 0U; i8259->poll = false; i8259->smm = 0U; if ((val & ICW1_SNGL) != 0U) { dev_dbg(ACRN_DBG_PIC, "vpic cascade mode required\n"); ret = -1; } else if ((val & ICW1_IC4) == 0U) { dev_dbg(ACRN_DBG_PIC, "vpic icw4 required\n"); ret = -1; } else { i8259->icw_num++; ret = 0; } return ret; } static int32_t vpic_icw2(const struct acrn_vpic *vpic, struct i8259_reg_state *i8259, uint8_t val) { dev_dbg(ACRN_DBG_PIC, "vm 0x%x: i8259 icw2 0x%x\n", vpic->vm, val); i8259->irq_base = val & 0xf8U; i8259->icw_num++; return 0; } static int32_t vpic_icw3(const struct acrn_vpic *vpic, struct i8259_reg_state *i8259, uint8_t val) { dev_dbg(ACRN_DBG_PIC, "vm 0x%x: i8259 icw3 0x%x\n", vpic->vm, val); i8259->icw_num++; return 0; } static int32_t vpic_icw4(const struct acrn_vpic *vpic, struct i8259_reg_state *i8259, uint8_t val) { int32_t ret; dev_dbg(ACRN_DBG_PIC, "vm 0x%x: i8259 icw4 0x%x\n", vpic->vm, val); if ((val & ICW4_8086) == 0U) { dev_dbg(ACRN_DBG_PIC, "vpic microprocessor mode required\n"); ret = -1; } else { if ((val & ICW4_AEOI) != 0U) { i8259->aeoi = true; } if ((val & ICW4_SFNM) != 0U) { if (master_pic(vpic, i8259)) { i8259->sfn = true; } else { dev_dbg(ACRN_DBG_PIC, "Ignoring special fully nested mode on slave pic: %#x", val); } } i8259->icw_num = 0U; i8259->ready = true; ret = 0; } return ret; } static int32_t vpic_ocw1(const struct acrn_vpic *vpic, struct i8259_reg_state *i8259, uint8_t val) { uint32_t pin, i, bit; uint8_t old = i8259->mask; dev_dbg(ACRN_DBG_PIC, "vm 0x%x: i8259 ocw1 0x%x\n", vpic->vm, val); i8259->mask = val & 0xffU; pin = (i8259->lowprio + 1U) & 0x7U; /* query and setup if pin/irq is for passthrough device */ for (i = 0U; i < NR_VPIC_PINS_PER_CHIP; i++) { bit = (1U << pin); /* remap for active: interrupt mask -> unmask * remap for deactive: when vIOAPIC take it over */ if (((i8259->mask & bit) == 0U) && ((old & bit) != 0U)) { uint32_t virt_pin; /* master i8259 pin2 connect with slave i8259, * not device, so not need pt remap */ if ((pin == 2U) && master_pic(vpic, i8259)) { pin = (pin + 1U) & 0x7U; continue; } virt_pin = (master_pic(vpic, i8259)) ? pin : (pin + 8U); (void)ptirq_intx_pin_remap(vpic->vm, virt_pin, PTDEV_VPIN_PIC); } pin = (pin + 1U) & 0x7U; } return 0; } static int32_t vpic_ocw2(const struct acrn_vpic *vpic, struct i8259_reg_state *i8259, uint8_t val) { dev_dbg(ACRN_DBG_PIC, "vm 0x%x: i8259 ocw2 0x%x\n", vpic->vm, val); i8259->rotate = ((val & OCW2_R) != 0U); if ((val & OCW2_EOI) != 0U) { uint32_t isr_bit; if ((val & OCW2_SL) != 0U) { /* specific EOI */ isr_bit = val & 0x7U; } else { /* non-specific EOI */ isr_bit = vpic_get_highest_isrpin(i8259); } if (isr_bit < NR_VPIC_PINS_PER_CHIP) { i8259->service &= ~(uint8_t)(1U << isr_bit); if (i8259->rotate) { i8259->lowprio = isr_bit; } } /* if level ack PTDEV */ if ((i8259->elc & (1U << (isr_bit & 0x7U))) != 0U) { ptirq_intx_ack(vpic->vm, (master_pic(vpic, i8259) ? isr_bit : isr_bit + 8U), PTDEV_VPIN_PIC); } } else if (((val & OCW2_SL) != 0U) && i8259->rotate) { /* specific priority */ i8259->lowprio = val & 0x7U; } else { /* TODO: Any action required in this case? */ } return 0; } static int32_t vpic_ocw3(const struct acrn_vpic *vpic, struct i8259_reg_state *i8259, uint8_t val) { dev_dbg(ACRN_DBG_PIC, "vm 0x%x: i8259 ocw3 0x%x\n", vpic->vm, val); if ((val & OCW3_ESMM) != 0U) { i8259->smm = ((val & OCW3_SMM) != 0U) ? 1U : 0U; dev_dbg(ACRN_DBG_PIC, "%s i8259 special mask mode %s\n", master_pic(vpic, i8259) ? "master" : "slave", (i8259->smm != 0U) ? "enabled" : "disabled"); } if ((val & OCW3_RR) != 0U) { /* read register command */ i8259->rd_cmd_reg = val & OCW3_RIS; /* Polling mode */ i8259->poll = ((val & OCW3_P) != 0U); } return 0; } /** * @pre pin < NR_VPIC_PINS_TOTAL */ static void vpic_set_pinstate(struct acrn_vpic *vpic, uint32_t pin, uint8_t level) { struct i8259_reg_state *i8259; uint8_t old_lvl; bool lvl_trigger; if (pin < NR_VPIC_PINS_TOTAL) { i8259 = &vpic->i8259[pin >> 3U]; old_lvl = i8259->pin_state[pin & 0x7U]; if (level != 0U) { i8259->pin_state[pin & 0x7U] = 1U; } else { i8259->pin_state[pin & 0x7U] = 0U; } lvl_trigger = ((vpic->i8259[pin >> 3U].elc & (1U << (pin & 0x7U))) != 0U); if (((old_lvl == 0U) && (level == 1U)) || ((level == 1U) && lvl_trigger)) { /* raising edge or level */ dev_dbg(ACRN_DBG_PIC, "pic pin%hhu: asserted\n", pin); i8259->request |= (uint8_t)(1U << (pin & 0x7U)); } else if ((old_lvl == 1U) && (level == 0U)) { /* falling edge */ dev_dbg(ACRN_DBG_PIC, "pic pin%hhu: deasserted\n", pin); if (lvl_trigger) { i8259->request &= ~(uint8_t)(1U << (pin & 0x7U)); } } else { dev_dbg(ACRN_DBG_PIC, "pic pin%hhu: %s, ignored\n", pin, (level != 0U) ? "asserted" : "deasserted"); } } } /** * @brief Set vPIC IRQ line status. * * @param[in] vpic Pointer to virtual pic structure * @param[in] irqline Target IRQ number * @param[in] operation action options:GSI_SET_HIGH/GSI_SET_LOW/ * GSI_RAISING_PULSE/GSI_FALLING_PULSE * * @return None */ void vpic_set_irqline(struct acrn_vpic *vpic, uint32_t irqline, uint32_t operation) { struct i8259_reg_state *i8259; uint32_t pin; uint64_t rflags; if (irqline < NR_VPIC_PINS_TOTAL) { i8259 = &vpic->i8259[irqline >> 3U]; pin = irqline; if (i8259->ready) { spinlock_irqsave_obtain(&(vpic->lock), &rflags); switch (operation) { case GSI_SET_HIGH: vpic_set_pinstate(vpic, pin, 1U); break; case GSI_SET_LOW: vpic_set_pinstate(vpic, pin, 0U); break; case GSI_RAISING_PULSE: vpic_set_pinstate(vpic, pin, 1U); vpic_set_pinstate(vpic, pin, 0U); break; case GSI_FALLING_PULSE: vpic_set_pinstate(vpic, pin, 0U); vpic_set_pinstate(vpic, pin, 1U); break; default: /* * The function caller could guarantee the pre condition. */ break; } vpic_notify_intr(vpic); spinlock_irqrestore_release(&(vpic->lock), rflags); } } } uint32_t vpic_pincount(void) { return NR_VPIC_PINS_TOTAL; } /** * @pre vm->vpic != NULL * @pre irqline < NR_VPIC_PINS_TOTAL * @pre this function should be called after vpic_init() */ void vpic_get_irqline_trigger_mode(const struct acrn_vpic *vpic, uint32_t irqline, enum vpic_trigger *trigger) { if ((vpic->i8259[irqline >> 3U].elc & (1U << (irqline & 0x7U))) != 0U) { *trigger = LEVEL_TRIGGER; } else { *trigger = EDGE_TRIGGER; } } /** * @brief Get pending virtual interrupts for vPIC. * * @param[in] vpic Pointer to target VM's vpic table * @param[inout] vecptr Pointer to vector buffer and will be filled * with eligible vector if any. * * @pre this function should be called after vpic_init() * @return None */ void vpic_pending_intr(struct acrn_vpic *vpic, uint32_t *vecptr) { struct i8259_reg_state *i8259; uint32_t pin; i8259 = &vpic->i8259[0]; spinlock_obtain(&(vpic->lock)); pin = vpic_get_highest_irrpin(i8259); if (pin == 2U) { i8259 = &vpic->i8259[1]; pin = vpic_get_highest_irrpin(i8259); } /* * If there are no pins active at this moment then return the spurious * interrupt vector instead. */ if (pin >= NR_VPIC_PINS_PER_CHIP) { *vecptr = VECTOR_INVALID; } else { *vecptr = i8259->irq_base + pin; dev_dbg(ACRN_DBG_PIC, "Got pending vector 0x%x\n", *vecptr); } spinlock_release(&(vpic->lock)); } static void vpic_pin_accepted(struct i8259_reg_state *i8259, uint32_t pin) { i8259->intr_raised = false; if ((i8259->elc & (1U << pin)) == 0U) { /*only used edge trigger mode*/ i8259->request &= ~(uint8_t)(1U << pin); } if (i8259->aeoi) { if (i8259->rotate) { i8259->lowprio = pin; } } else { i8259->service |= (uint8_t)(1U << pin); } } /** * @brief Accept virtual interrupt for vPIC. * * @param[in] vm Pointer to target VM * @param[in] vector Target virtual interrupt vector * * @return None * * @pre vm != NULL * @pre this function should be called after vpic_init() */ void vpic_intr_accepted(struct acrn_vpic *vpic, uint32_t vector) { uint32_t pin; spinlock_obtain(&(vpic->lock)); pin = (vector & 0x7U); if ((vector & ~0x7U) == vpic->i8259[1].irq_base) { vpic_pin_accepted(&vpic->i8259[1], pin); /* * If this vector originated from the slave, * accept the cascaded interrupt too. */ vpic_pin_accepted(&vpic->i8259[0], 2U); } else { vpic_pin_accepted(&vpic->i8259[0], pin); } vpic_notify_intr(vpic); spinlock_release(&(vpic->lock)); } static int32_t vpic_read(struct acrn_vpic *vpic, struct i8259_reg_state *i8259, uint16_t port, uint32_t *eax) { uint32_t pin; spinlock_obtain(&(vpic->lock)); if (i8259->poll) { i8259->poll = false; pin = vpic_get_highest_irrpin(i8259); if (pin < NR_VPIC_PINS_PER_CHIP) { vpic_pin_accepted(i8259, pin); *eax = 0x80U | pin; } else { *eax = 0U; } } else { if ((port & ICU_IMR_OFFSET) != 0U) { /* read interrupt mask register */ *eax = i8259->mask; } else { if (i8259->rd_cmd_reg == OCW3_RIS) { /* read interrupt service register */ *eax = i8259->service; } else { /* read interrupt request register */ *eax = i8259->request; } } } spinlock_release(&(vpic->lock)); return 0; } static int32_t vpic_write(struct acrn_vpic *vpic, struct i8259_reg_state *i8259, uint16_t port, uint32_t *eax) { int32_t error; uint8_t val; error = 0; val = (uint8_t)*eax; spinlock_obtain(&(vpic->lock)); if ((port & ICU_IMR_OFFSET) != 0U) { switch (i8259->icw_num) { case 2U: error = vpic_icw2(vpic, i8259, val); break; case 3U: error = vpic_icw3(vpic, i8259, val); break; case 4U: error = vpic_icw4(vpic, i8259, val); break; default: error = vpic_ocw1(vpic, i8259, val); break; } } else { if ((val & (1U << 4U)) != 0U) { error = vpic_icw1(vpic, i8259, val); } if (i8259->ready) { if ((val & (1U << 3U)) != 0U) { error = vpic_ocw3(vpic, i8259, val); } else { error = vpic_ocw2(vpic, i8259, val); } } } if (i8259->ready) { vpic_notify_intr(vpic); } spinlock_release(&(vpic->lock)); return error; } static int32_t vpic_master_handler(struct acrn_vpic *vpic, bool in, uint16_t port, size_t bytes, uint32_t *eax) { struct i8259_reg_state *i8259; int32_t ret; i8259 = &vpic->i8259[0]; if (bytes != 1U) { ret = -1; } else if (in) { ret = vpic_read(vpic, i8259, port, eax); } else { ret = vpic_write(vpic, i8259, port, eax); } return ret; } /** * @pre vcpu != NULL * @pre vcpu->vm != NULL */ static bool vpic_master_io_read(struct acrn_vcpu *vcpu, uint16_t addr, size_t width) { struct pio_request *pio_req = &vcpu->req.reqs.pio; if (vpic_master_handler(vm_pic(vcpu->vm), true, addr, width, &pio_req->value) < 0) { pr_err("pic master read port 0x%x width=%d failed\n", addr, width); } return true; } /** * @pre vcpu != NULL * @pre vcpu->vm != NULL */ static bool vpic_master_io_write(struct acrn_vcpu *vcpu, uint16_t addr, size_t width, uint32_t v) { uint32_t val = v; if (vpic_master_handler(vm_pic(vcpu->vm), false, addr, width, &val) < 0) { pr_err("%s: write port 0x%x width=%d value 0x%x failed\n", __func__, addr, width, val); } return true; } static int32_t vpic_slave_handler(struct acrn_vpic *vpic, bool in, uint16_t port, size_t bytes, uint32_t *eax) { struct i8259_reg_state *i8259; int32_t ret; i8259 = &vpic->i8259[1]; if (bytes != 1U) { ret = -1; } else if (in) { ret = vpic_read(vpic, i8259, port, eax); } else { ret = vpic_write(vpic, i8259, port, eax); } return ret; } /** * @pre vcpu != NULL * @pre vcpu->vm != NULL */ static bool vpic_slave_io_read(struct acrn_vcpu *vcpu, uint16_t addr, size_t width) { struct pio_request *pio_req = &vcpu->req.reqs.pio; if (vpic_slave_handler(vm_pic(vcpu->vm), true, addr, width, &pio_req->value) < 0) { pr_err("pic slave read port 0x%x width=%d failed\n", addr, width); } return true; } /** * @pre vcpu != NULL * @pre vcpu->vm != NULL */ static bool vpic_slave_io_write(struct acrn_vcpu *vcpu, uint16_t addr, size_t width, uint32_t v) { uint32_t val = v; if (vpic_slave_handler(vm_pic(vcpu->vm), false, addr, width, &val) < 0) { pr_err("%s: write port 0x%x width=%d value 0x%x failed\n", __func__, addr, width, val); } return true; } static int32_t vpic_elc_handler(struct acrn_vpic *vpic, bool in, uint16_t port, size_t bytes, uint32_t *eax) { bool is_master; int32_t ret; is_master = (port == IO_ELCR1); if (bytes == 1U) { spinlock_obtain(&(vpic->lock)); if (in) { if (is_master) { *eax = vpic->i8259[0].elc; } else { *eax = vpic->i8259[1].elc; } } else { /* * For the master PIC the cascade channel (IRQ2), the * heart beat timer (IRQ0), and the keyboard * controller (IRQ1) cannot be programmed for level * mode. * * For the slave PIC the real time clock (IRQ8) and * the floating point error interrupt (IRQ13) cannot * be programmed for level mode. */ if (is_master) { vpic->i8259[0].elc = (uint8_t)(*eax & 0xf8U); } else { vpic->i8259[1].elc = (uint8_t)(*eax & 0xdeU); } } spinlock_release(&(vpic->lock)); ret = 0; } else { ret = -1; } return ret; } /** * @pre vcpu != NULL * @pre vcpu->vm != NULL */ static bool vpic_elc_io_read(struct acrn_vcpu *vcpu, uint16_t addr, size_t width) { struct pio_request *pio_req = &vcpu->req.reqs.pio; if (vpic_elc_handler(vm_pic(vcpu->vm), true, addr, width, &pio_req->value) < 0) { pr_err("pic elc read port 0x%x width=%d failed", addr, width); } return true; } /** * @pre vcpu != NULL * @pre vcpu->vm != NULL */ static bool vpic_elc_io_write(struct acrn_vcpu *vcpu, uint16_t addr, size_t width, uint32_t v) { uint32_t val = v; if (vpic_elc_handler(vm_pic(vcpu->vm), false, addr, width, &val) < 0) { pr_err("%s: write port 0x%x width=%d value 0x%x failed\n", __func__, addr, width, val); } return true; } static void vpic_register_io_handler(struct acrn_vm *vm) { struct vm_io_range master_range = { .base = 0x20U, .len = 2U }; struct vm_io_range slave_range = { .base = 0xa0U, .len = 2U }; struct vm_io_range elcr_range = { .base = 0x4d0U, .len = 2U }; register_pio_emulation_handler(vm, PIC_MASTER_PIO_IDX, &master_range, vpic_master_io_read, vpic_master_io_write); register_pio_emulation_handler(vm, PIC_SLAVE_PIO_IDX, &slave_range, vpic_slave_io_read, vpic_slave_io_write); register_pio_emulation_handler(vm, PIC_ELC_PIO_IDX, &elcr_range, vpic_elc_io_read, vpic_elc_io_write); } void vpic_init(struct acrn_vm *vm) { struct acrn_vpic *vpic = vm_pic(vm); vpic_register_io_handler(vm); vpic->vm = vm; vpic->i8259[0].mask = 0xffU; vpic->i8259[1].mask = 0xffU; spinlock_init(&(vpic->lock)); }