diff --git a/hw/i386/acpi-common.c b/hw/i386/acpi-common.c index 8a76965406..a6a30e8363 100644 --- a/hw/i386/acpi-common.c +++ b/hw/i386/acpi-common.c @@ -103,6 +103,16 @@ void acpi_build_madt(GArray *table_data, BIOSLinker *linker, io_apic->address = cpu_to_le32(IO_APIC_DEFAULT_ADDRESS); io_apic->interrupt = cpu_to_le32(0); + if (x86ms->ioapic2) { + AcpiMadtIoApic *io_apic2; + io_apic2 = acpi_data_push(table_data, sizeof *io_apic); + io_apic2->type = ACPI_APIC_IO; + io_apic2->length = sizeof(*io_apic); + io_apic2->io_apic_id = ACPI_BUILD_IOAPIC_ID + 1; + io_apic2->address = cpu_to_le32(IO_APIC_SECONDARY_ADDRESS); + io_apic2->interrupt = cpu_to_le32(IO_APIC_SECONDARY_IRQBASE); + } + if (x86ms->apic_xrupt_override) { intsrcovr = acpi_data_push(table_data, sizeof *intsrcovr); intsrcovr->type = ACPI_APIC_XRUPT_OVERRIDE; diff --git a/hw/i386/microvm.c b/hw/i386/microvm.c index 5428448b70..5688608613 100644 --- a/hw/i386/microvm.c +++ b/hw/i386/microvm.c @@ -96,13 +96,6 @@ static void microvm_set_rtc(MicrovmMachineState *mms, ISADevice *s) rtc_set_memory(s, 0x5d, val >> 16); } -static void microvm_gsi_handler(void *opaque, int n, int level) -{ - GSIState *s = opaque; - - qemu_set_irq(s->ioapic_irq[n], level); -} - static void create_gpex(MicrovmMachineState *mms) { X86MachineState *x86ms = X86_MACHINE(mms); @@ -152,34 +145,56 @@ static void create_gpex(MicrovmMachineState *mms) } } +static int microvm_ioapics(MicrovmMachineState *mms) +{ + if (!x86_machine_is_acpi_enabled(X86_MACHINE(mms))) { + return 1; + } + if (mms->ioapic2 == ON_OFF_AUTO_OFF) { + return 1; + } + return 2; +} + static void microvm_devices_init(MicrovmMachineState *mms) { X86MachineState *x86ms = X86_MACHINE(mms); ISABus *isa_bus; ISADevice *rtc_state; GSIState *gsi_state; + int ioapics; int i; /* Core components */ - + ioapics = microvm_ioapics(mms); gsi_state = g_malloc0(sizeof(*gsi_state)); - if (mms->pic == ON_OFF_AUTO_ON || mms->pic == ON_OFF_AUTO_AUTO) { - x86ms->gsi = qemu_allocate_irqs(gsi_handler, gsi_state, GSI_NUM_PINS); - } else { - x86ms->gsi = qemu_allocate_irqs(microvm_gsi_handler, - gsi_state, GSI_NUM_PINS); - } + x86ms->gsi = qemu_allocate_irqs(gsi_handler, gsi_state, + IOAPIC_NUM_PINS * ioapics); isa_bus = isa_bus_new(NULL, get_system_memory(), get_system_io(), &error_abort); isa_bus_irqs(isa_bus, x86ms->gsi); ioapic_init_gsi(gsi_state, "machine"); + if (ioapics > 1) { + x86ms->ioapic2 = ioapic_init_secondary(gsi_state); + } kvmclock_create(true); - mms->virtio_irq_base = x86_machine_is_acpi_enabled(x86ms) ? 16 : 5; - for (i = 0; i < VIRTIO_NUM_TRANSPORTS; i++) { + mms->virtio_irq_base = 5; + mms->virtio_num_transports = 8; + if (x86ms->ioapic2) { + mms->pcie_irq_base = 16; /* 16 -> 19 */ + /* use second ioapic (24 -> 47) for virtio-mmio irq lines */ + mms->virtio_irq_base = IO_APIC_SECONDARY_IRQBASE; + mms->virtio_num_transports = IOAPIC_NUM_PINS; + } else if (x86_machine_is_acpi_enabled(x86ms)) { + mms->pcie_irq_base = 12; /* 12 -> 15 */ + mms->virtio_irq_base = 16; /* 16 -> 23 */ + } + + for (i = 0; i < mms->virtio_num_transports; i++) { sysbus_create_simple("virtio-mmio", VIRTIO_MMIO_BASE + i * 512, x86ms->gsi[mms->virtio_irq_base + i]); @@ -221,12 +236,12 @@ static void microvm_devices_init(MicrovmMachineState *mms) mms->gpex.mmio32.size = PCIE_MMIO_SIZE; mms->gpex.ecam.base = PCIE_ECAM_BASE; mms->gpex.ecam.size = PCIE_ECAM_SIZE; - mms->gpex.irq = PCIE_IRQ_BASE; + mms->gpex.irq = mms->pcie_irq_base; create_gpex(mms); - x86ms->pci_irq_mask = ((1 << (PCIE_IRQ_BASE + 0)) | - (1 << (PCIE_IRQ_BASE + 1)) | - (1 << (PCIE_IRQ_BASE + 2)) | - (1 << (PCIE_IRQ_BASE + 3))); + x86ms->pci_irq_mask = ((1 << (mms->pcie_irq_base + 0)) | + (1 << (mms->pcie_irq_base + 1)) | + (1 << (mms->pcie_irq_base + 2)) | + (1 << (mms->pcie_irq_base + 3))); } else { x86ms->pci_irq_mask = 0; } @@ -550,6 +565,23 @@ static void microvm_machine_set_pcie(Object *obj, Visitor *v, const char *name, visit_type_OnOffAuto(v, name, &mms->pcie, errp); } +static void microvm_machine_get_ioapic2(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + MicrovmMachineState *mms = MICROVM_MACHINE(obj); + OnOffAuto ioapic2 = mms->ioapic2; + + visit_type_OnOffAuto(v, name, &ioapic2, errp); +} + +static void microvm_machine_set_ioapic2(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + MicrovmMachineState *mms = MICROVM_MACHINE(obj); + + visit_type_OnOffAuto(v, name, &mms->ioapic2, errp); +} + static bool microvm_machine_get_isa_serial(Object *obj, Error **errp) { MicrovmMachineState *mms = MICROVM_MACHINE(obj); @@ -626,6 +658,7 @@ static void microvm_machine_initfn(Object *obj) mms->pit = ON_OFF_AUTO_AUTO; mms->rtc = ON_OFF_AUTO_AUTO; mms->pcie = ON_OFF_AUTO_AUTO; + mms->ioapic2 = ON_OFF_AUTO_AUTO; mms->isa_serial = true; mms->option_roms = true; mms->auto_kernel_cmdline = true; @@ -699,6 +732,13 @@ static void microvm_class_init(ObjectClass *oc, void *data) object_class_property_set_description(oc, MICROVM_MACHINE_PCIE, "Enable PCIe"); + object_class_property_add(oc, MICROVM_MACHINE_IOAPIC2, "OnOffAuto", + microvm_machine_get_ioapic2, + microvm_machine_set_ioapic2, + NULL, NULL); + object_class_property_set_description(oc, MICROVM_MACHINE_IOAPIC2, + "Enable second IO-APIC"); + object_class_property_add_bool(oc, MICROVM_MACHINE_ISA_SERIAL, microvm_machine_get_isa_serial, microvm_machine_set_isa_serial); diff --git a/hw/i386/x86.c b/hw/i386/x86.c index 5944fc44ed..d68a9eaefc 100644 --- a/hw/i386/x86.c +++ b/hw/i386/x86.c @@ -588,11 +588,21 @@ void gsi_handler(void *opaque, int n, int level) GSIState *s = opaque; trace_x86_gsi_interrupt(n, level); - if (n < ISA_NUM_IRQS) { - /* Under KVM, Kernel will forward to both PIC and IOAPIC */ - qemu_set_irq(s->i8259_irq[n], level); + switch (n) { + case 0 ... ISA_NUM_IRQS - 1: + if (s->i8259_irq[n]) { + /* Under KVM, Kernel will forward to both PIC and IOAPIC */ + qemu_set_irq(s->i8259_irq[n], level); + } + /* fall through */ + case ISA_NUM_IRQS ... IOAPIC_NUM_PINS - 1: + qemu_set_irq(s->ioapic_irq[n], level); + break; + case IO_APIC_SECONDARY_IRQBASE + ... IO_APIC_SECONDARY_IRQBASE + IOAPIC_NUM_PINS - 1: + qemu_set_irq(s->ioapic2_irq[n - IO_APIC_SECONDARY_IRQBASE], level); + break; } - qemu_set_irq(s->ioapic_irq[n], level); } void ioapic_init_gsi(GSIState *gsi_state, const char *parent_name) @@ -618,6 +628,23 @@ void ioapic_init_gsi(GSIState *gsi_state, const char *parent_name) } } +DeviceState *ioapic_init_secondary(GSIState *gsi_state) +{ + DeviceState *dev; + SysBusDevice *d; + unsigned int i; + + dev = qdev_new(TYPE_IOAPIC); + d = SYS_BUS_DEVICE(dev); + sysbus_realize_and_unref(d, &error_fatal); + sysbus_mmio_map(d, 0, IO_APIC_SECONDARY_ADDRESS); + + for (i = 0; i < IOAPIC_NUM_PINS; i++) { + gsi_state->ioapic2_irq[i] = qdev_get_gpio_in(dev, i); + } + return dev; +} + struct setup_data { uint64_t next; uint32_t type; diff --git a/include/hw/i386/ioapic.h b/include/hw/i386/ioapic.h index 06bfaaeac6..ef37b8a9fd 100644 --- a/include/hw/i386/ioapic.h +++ b/include/hw/i386/ioapic.h @@ -22,6 +22,8 @@ #define IOAPIC_NUM_PINS 24 #define IO_APIC_DEFAULT_ADDRESS 0xfec00000 +#define IO_APIC_SECONDARY_ADDRESS (IO_APIC_DEFAULT_ADDRESS + 0x10000) +#define IO_APIC_SECONDARY_IRQBASE 24 /* primary 0 -> 23, secondary 24 -> 47 */ #define TYPE_KVM_IOAPIC "kvm-ioapic" #define TYPE_IOAPIC "ioapic" diff --git a/include/hw/i386/ioapic_internal.h b/include/hw/i386/ioapic_internal.h index 0f9002a2c2..021e715f11 100644 --- a/include/hw/i386/ioapic_internal.h +++ b/include/hw/i386/ioapic_internal.h @@ -27,7 +27,7 @@ #include "qemu/notify.h" #include "qom/object.h" -#define MAX_IOAPICS 1 +#define MAX_IOAPICS 2 #define IOAPIC_LVT_DEST_SHIFT 56 #define IOAPIC_LVT_DEST_IDX_SHIFT 48 diff --git a/include/hw/i386/microvm.h b/include/hw/i386/microvm.h index 0fc2160077..f25f837441 100644 --- a/include/hw/i386/microvm.h +++ b/include/hw/i386/microvm.h @@ -52,7 +52,6 @@ /* Platform virtio definitions */ #define VIRTIO_MMIO_BASE 0xfeb00000 -#define VIRTIO_NUM_TRANSPORTS 8 #define VIRTIO_CMDLINE_MAXLEN 64 #define GED_MMIO_BASE 0xfea00000 @@ -67,13 +66,13 @@ #define PCIE_MMIO_SIZE 0x20000000 #define PCIE_ECAM_BASE 0xe0000000 #define PCIE_ECAM_SIZE 0x10000000 -#define PCIE_IRQ_BASE 12 /* Machine type options */ #define MICROVM_MACHINE_PIT "pit" #define MICROVM_MACHINE_PIC "pic" #define MICROVM_MACHINE_RTC "rtc" #define MICROVM_MACHINE_PCIE "pcie" +#define MICROVM_MACHINE_IOAPIC2 "ioapic2" #define MICROVM_MACHINE_ISA_SERIAL "isa-serial" #define MICROVM_MACHINE_OPTION_ROMS "x-option-roms" #define MICROVM_MACHINE_AUTO_KERNEL_CMDLINE "auto-kernel-cmdline" @@ -92,12 +91,15 @@ struct MicrovmMachineState { OnOffAuto pit; OnOffAuto rtc; OnOffAuto pcie; + OnOffAuto ioapic2; bool isa_serial; bool option_roms; bool auto_kernel_cmdline; /* Machine state */ + uint32_t pcie_irq_base; uint32_t virtio_irq_base; + uint32_t virtio_num_transports; bool kernel_cmdline_fixed; Notifier machine_done; Notifier powerdown_req; diff --git a/include/hw/i386/x86.h b/include/hw/i386/x86.h index 739fac5087..3f9b052cfc 100644 --- a/include/hw/i386/x86.h +++ b/include/hw/i386/x86.h @@ -50,6 +50,7 @@ struct X86MachineState { ISADevice *rtc; FWCfgState *fw_cfg; qemu_irq *gsi; + DeviceState *ioapic2; GMappedFile *initrd_mapped_file; HotplugHandler *acpi_dev; @@ -120,10 +121,12 @@ bool x86_machine_is_acpi_enabled(const X86MachineState *x86ms); typedef struct GSIState { qemu_irq i8259_irq[ISA_NUM_IRQS]; qemu_irq ioapic_irq[IOAPIC_NUM_PINS]; + qemu_irq ioapic2_irq[IOAPIC_NUM_PINS]; } GSIState; qemu_irq x86_allocate_cpu_irq(void); void gsi_handler(void *opaque, int n, int level); void ioapic_init_gsi(GSIState *gsi_state, const char *parent_name); +DeviceState *ioapic_init_secondary(GSIState *gsi_state); #endif diff --git a/tests/data/acpi/microvm/APIC.ioapic2 b/tests/data/acpi/microvm/APIC.ioapic2 new file mode 100644 index 0000000000..a305f89d99 Binary files /dev/null and b/tests/data/acpi/microvm/APIC.ioapic2 differ diff --git a/tests/data/acpi/microvm/DSDT.ioapic2 b/tests/data/acpi/microvm/DSDT.ioapic2 new file mode 100644 index 0000000000..aee44dd3de Binary files /dev/null and b/tests/data/acpi/microvm/DSDT.ioapic2 differ diff --git a/tests/qtest/bios-tables-test.c b/tests/qtest/bios-tables-test.c index 64a9a772ee..f2f79dd6a4 100644 --- a/tests/qtest/bios-tables-test.c +++ b/tests/qtest/bios-tables-test.c @@ -1124,7 +1124,7 @@ static void test_acpi_microvm_tcg(void) test_data data; test_acpi_microvm_prepare(&data); - test_acpi_one(" -machine microvm,acpi=on,rtc=off", + test_acpi_one(" -machine microvm,acpi=on,ioapic2=off,rtc=off", &data); free_test_data(&data); } @@ -1135,7 +1135,7 @@ static void test_acpi_microvm_usb_tcg(void) test_acpi_microvm_prepare(&data); data.variant = ".usb"; - test_acpi_one(" -machine microvm,acpi=on,usb=on,rtc=off", + test_acpi_one(" -machine microvm,acpi=on,ioapic2=off,usb=on,rtc=off", &data); free_test_data(&data); } @@ -1146,7 +1146,7 @@ static void test_acpi_microvm_rtc_tcg(void) test_acpi_microvm_prepare(&data); data.variant = ".rtc"; - test_acpi_one(" -machine microvm,acpi=on,rtc=on", + test_acpi_one(" -machine microvm,acpi=on,ioapic2=off,rtc=on", &data); free_test_data(&data); } @@ -1158,7 +1158,18 @@ static void test_acpi_microvm_pcie_tcg(void) test_acpi_microvm_prepare(&data); data.variant = ".pcie"; data.tcg_only = true; /* need constant host-phys-bits */ - test_acpi_one(" -machine microvm,acpi=on,rtc=off,pcie=on", + test_acpi_one(" -machine microvm,acpi=on,ioapic2=off,rtc=off,pcie=on", + &data); + free_test_data(&data); +} + +static void test_acpi_microvm_ioapic2_tcg(void) +{ + test_data data; + + test_acpi_microvm_prepare(&data); + data.variant = ".ioapic2"; + test_acpi_one(" -machine microvm,acpi=on,ioapic2=on,rtc=off", &data); free_test_data(&data); } @@ -1323,6 +1334,7 @@ int main(int argc, char *argv[]) qtest_add_func("acpi/microvm", test_acpi_microvm_tcg); qtest_add_func("acpi/microvm/usb", test_acpi_microvm_usb_tcg); qtest_add_func("acpi/microvm/rtc", test_acpi_microvm_rtc_tcg); + qtest_add_func("acpi/microvm/ioapic2", test_acpi_microvm_ioapic2_tcg); if (strcmp(arch, "x86_64") == 0) { qtest_add_func("acpi/microvm/pcie", test_acpi_microvm_pcie_tcg); }