static struct apic_irq_message pci_translate_msi_vector(struct pci_device *device, unsigned int vector, unsigned int legacy_vectors, union x86_msi_vector msi) { struct apic_irq_message irq_msg = { .valid = 0 }; unsigned int idx; if (iommu_cell_emulates_ir(device->cell)) { if (!msi.remap.remapped) return irq_msg; idx = msi.remap.int_index | (msi.remap.int_index15 << 15); if (msi.remap.shv) idx += msi.remap.subhandle; return iommu_get_remapped_root_int(device->info->iommu, device->info->bdf, vector, idx); } irq_msg.vector = msi.native.vector; if (legacy_vectors > 1) { irq_msg.vector &= ~(legacy_vectors - 1); irq_msg.vector |= vector; } irq_msg.delivery_mode = msi.native.delivery_mode; irq_msg.level_triggered = 0; irq_msg.dest_logical = msi.native.dest_logical; irq_msg.redir_hint = msi.native.redir_hint; irq_msg.valid = 1; irq_msg.destination = msi.native.destination; return irq_msg; } void arch_pci_suppress_msi(struct pci_device *device, const struct jailhouse_pci_capability *cap) { unsigned int n, vectors = pci_enabled_msi_vectors(device); const struct jailhouse_pci_device *info = device->info; struct apic_irq_message irq_msg; union x86_msi_vector msi = { .native.dest_logical = 1, .native.redir_hint = 1, .native.address = MSI_ADDRESS_VALUE, }; if (!(pci_read_config(info->bdf, PCI_CFG_COMMAND, 2) & PCI_CMD_MASTER)) return; /* * Disable delivery by setting no destination CPU bit in logical * addressing mode. */ if (info->msi_64bits) pci_write_config(info->bdf, cap->start + 8, 0, 4); pci_write_config(info->bdf, cap->start + 4, (u32)msi.raw.address, 4); /* * Inject MSI vectors to avoid losing events while suppressed. * Linux can handle rare spurious interrupts. */ msi = pci_get_x86_msi_vector(device); for (n = 0; n < vectors; n++) { irq_msg = pci_translate_msi_vector(device, n, vectors, msi); apic_send_irq(irq_msg); } } static u32 pci_get_x86_msi_remap_address(unsigned int index) { union x86_msi_vector msi = { .remap.int_index15 = index >> 15, .remap.shv = 1, .remap.remapped = 1, .remap.int_index = index, .remap.address = MSI_ADDRESS_VALUE, }; return (u32)msi.raw.address; } int arch_pci_update_msi(struct pci_device *device, const struct jailhouse_pci_capability *cap) { unsigned int n, vectors = pci_enabled_msi_vectors(device); union x86_msi_vector msi = pci_get_x86_msi_vector(device); const struct jailhouse_pci_device *info = device->info; struct apic_irq_message irq_msg; u16 bdf = info->bdf; int result = 0; if (vectors == 0) return 0; for (n = 0; n < vectors; n++) { irq_msg = pci_translate_msi_vector(device, n, vectors, msi); result = iommu_map_interrupt(device->cell, bdf, n, irq_msg); // HACK for QEMU if (result == -ENOSYS) { for (n = 1; n < (info->msi_64bits ? 4 : 3); n++) pci_write_config(bdf, cap->start + n * 4, device->msi_registers.raw[n], 4); return 0; } if (result < 0) return result; } /* set result to the base index again */ result -= vectors - 1; pci_write_config(bdf, cap->start + (info->msi_64bits ? 12 : 8), 0, 2); if (info->msi_64bits) pci_write_config(bdf, cap->start + 8, 0, 4); pci_write_config(bdf, cap->start + 4, pci_get_x86_msi_remap_address(result), 4); return 0; } int arch_pci_update_msix_vector(struct pci_device *device, unsigned int index) { union x86_msi_vector msi = { .raw.address = device->msix_vectors[index].field.address, .raw.data = device->msix_vectors[index].field.data, }; struct apic_irq_message irq_msg; int result; if (!device->msix_registers.field.enable) return 0; irq_msg = pci_translate_msi_vector(device, index, 0, msi); result = iommu_map_interrupt(device->cell, device->info->bdf, index, irq_msg); // HACK for QEMU if (result == -ENOSYS) { mmio_write64(&device->msix_table[index].field.address, device->msix_vectors[index].field.address); mmio_write32(&device->msix_table[index].field.data, device->msix_vectors[index].field.data); return 0; } if (result < 0) return result; mmio_write64(&device->msix_table[index].field.address, pci_get_x86_msi_remap_address(result)); mmio_write32(&device->msix_table[index].field.data, 0); return 0; }
static struct apic_irq_message ioapic_translate_redir_entry(struct cell_ioapic *ioapic, unsigned int pin, union ioapic_redir_entry entry) { struct apic_irq_message irq_msg = { .valid = 0 }; unsigned int idx, ioapic_id; if (iommu_cell_emulates_ir(ioapic->cell)) { if (!entry.remap.remapped) return irq_msg; idx = entry.remap.int_index | (entry.remap.int_index15 << 15); ioapic_id = ioapic->info->id; return iommu_get_remapped_root_int(ioapic_id >> 16, (u16)ioapic_id, pin, idx); } irq_msg.vector = entry.native.vector; irq_msg.delivery_mode = entry.native.delivery_mode; irq_msg.level_triggered = entry.native.level_triggered; irq_msg.dest_logical = entry.native.dest_logical; /* align redir_hint and dest_logical - required by iommu_map_interrupt */ irq_msg.redir_hint = irq_msg.dest_logical; irq_msg.valid = 1; irq_msg.destination = entry.native.destination; return irq_msg; } static int ioapic_virt_redir_write(struct cell_ioapic *ioapic, unsigned int reg, u32 value) { unsigned int pin = (reg - IOAPIC_REDIR_TBL_START) / 2; struct phys_ioapic *phys_ioapic = ioapic->phys_ioapic; struct apic_irq_message irq_msg; union ioapic_redir_entry entry; int result; entry = phys_ioapic->shadow_redir_table[pin]; entry.raw[reg & 1] = value; phys_ioapic->shadow_redir_table[pin] = entry; /* Do not map the interrupt while masked. */ if (entry.native.mask) { /* * The mask is part of the lower 32 bits. Apply it when that * register half is written. */ if ((reg & 1) == 0) ioapic_reg_write(phys_ioapic, reg, IOAPIC_REDIR_MASK); return 0; } irq_msg = ioapic_translate_redir_entry(ioapic, pin, entry); result = iommu_map_interrupt(ioapic->cell, (u16)ioapic->info->id, pin, irq_msg); // HACK for QEMU if (result == -ENOSYS) { /* * Upper 32 bits aren't written when the register is masked. * Write them unconditionally when unmasking to keep an entry * in the consistent state. */ ioapic_reg_write(phys_ioapic, reg | 1, entry.raw[1]); ioapic_reg_write(phys_ioapic, reg, entry.raw[reg & 1]); return 0; } if (result < 0) return result; entry.remap.zero = 0; entry.remap.int_index15 = result >> 15; entry.remap.remapped = 1; entry.remap.int_index = result; ioapic_reg_write(phys_ioapic, reg, entry.raw[reg & 1]); return 0; }