new file mode 100644
@@ -0,0 +1,9 @@
+#ifndef QEMU_HW_I386_ACPI_H
+#define QEMU_HW_I386_ACPI_H
+
+#include <stddef.h>
+
+void acpi_build_madt(unsigned char **out_blob, size_t *out_blob_size,
+ unsigned num_lapic);
+
+#endif
new file mode 100644
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 2013 Red Hat Inc.
+ * Copyright (C) 2008-2010 Kevin O'Connor <kevin@koconnor.net>
+ * Copyright (C) 2006 Fabrice Bellard
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "hw/acpi/acpi.h"
+#include "hw/i386/acpi.h"
+#include "kvm_i386.h"
+#include "sysemu/sysemu.h"
+
+static void acpi_table_fill_hdr(AcpiTableStdHdr *std_hdr, size_t blob_size,
+ const char *sig)
+{
+ g_assert(blob_size >= sizeof *std_hdr);
+
+ *std_hdr = acpi_dfl_hdr;
+ strncpy(std_hdr->sig, sig, sizeof std_hdr->sig);
+ strncpy(std_hdr->oem_id, "QEMU ", sizeof std_hdr->oem_id);
+ strncpy(std_hdr->oem_table_id + 4, sig, sizeof std_hdr->oem_table_id - 4);
+ std_hdr->length = cpu_to_le32(blob_size);
+ std_hdr->checksum = acpi_checksum((uint8_t *)std_hdr, blob_size);
+}
+
+void acpi_build_madt(unsigned char **out_blob, size_t *out_blob_size,
+ unsigned num_lapic)
+{
+ typedef struct {
+ uint8_t type;
+ uint8_t length;
+ } QEMU_PACKED AcpiSubHdr;
+
+ AcpiTableStdHdr *std_hdr;
+ struct {
+ uint32_t lapic_addr; /* Local Interrupt Controller Address */
+ uint32_t flags; /* Multiple APIC flags */
+ } QEMU_PACKED *madt;
+ struct {
+ AcpiSubHdr hdr;
+ uint8_t processor_id; /* ACPI Processor ID */
+ uint8_t apic_id; /* APIC ID */
+ uint32_t flags; /* LOcal APIC flags */
+ } QEMU_PACKED *lapic;
+ struct {
+ AcpiSubHdr hdr;
+ uint8_t io_apic_id; /* The I/O APIC's ID */
+ uint8_t reserved; /* constant zero */
+ uint32_t io_apic_addr; /* 32-bit physical address to access */
+ uint32_t gsi_base; /* interrupt inputs start here */
+ } QEMU_PACKED *io_apic;
+ struct {
+ AcpiSubHdr hdr;
+ uint8_t bus; /* constant zero: ISA */
+ uint8_t source; /* this bus-relative interrupt source... */
+ uint32_t gsi; /* ... will signal this global system interrupt */
+ uint16_t flags; /* MPS INTI Flags: Polarity, Trigger Mode */
+ } QEMU_PACKED *int_src_ovr;
+ struct {
+ AcpiSubHdr hdr;
+ uint8_t processor_id; /* ACPI Processor ID */
+ uint16_t flags; /* MPS INTI Flags: Polarity, Trigger Mode */
+ uint8_t lint; /* LAPIC interrupt input for NMI */
+ } QEMU_PACKED *lapic_nmi;
+
+ static const uint8_t pci_isa_irq[] = { 5, 9, 10, 11 };
+
+ unsigned num_int_src_ovr, i;
+ size_t blob_size;
+ char unsigned *blob;
+
+ num_int_src_ovr = sizeof pci_isa_irq + kvm_allows_irq0_override();
+
+ blob_size = (sizeof *std_hdr) * 1 +
+ (sizeof *madt) * 1 +
+ (sizeof *lapic) * num_lapic +
+ (sizeof *io_apic) * 1 +
+ (sizeof *int_src_ovr) * num_int_src_ovr +
+ (sizeof *lapic_nmi) * 1;
+ blob = g_malloc(blob_size);
+
+ std_hdr = (void *)blob;
+ madt = (void *)(std_hdr + 1 );
+ lapic = (void *)(madt + 1 );
+ io_apic = (void *)(lapic + num_lapic );
+ int_src_ovr = (void *)(io_apic + 1 );
+ lapic_nmi = (void *)(int_src_ovr + num_int_src_ovr);
+
+ madt->lapic_addr = cpu_to_le32(APIC_DEFAULT_ADDRESS);
+ madt->flags = cpu_to_le32(1); /* PCAT_COMPAT */
+
+ /* create a Local APIC structure for each possible APIC ID */
+ for (i = 0; i < num_lapic; ++i) {
+ lapic[i].hdr.type = 0; /* Processor Local APIC */
+ lapic[i].hdr.length = sizeof *lapic;
+ lapic[i].processor_id = i;
+ lapic[i].apic_id = i;
+ lapic[i].flags = cpu_to_le32(0); /* disabled */
+ }
+ /* enable the CPUs with a CPU index in the [0..smp_cpus-1] range */
+ for (i = 0; i < smp_cpus; ++i) {
+ lapic[x86_cpu_apic_id_from_index(i)].flags = cpu_to_le32(1);
+ }
+
+ io_apic->hdr.type = 1; /* I/O APIC */
+ io_apic->hdr.length = sizeof *io_apic;
+ io_apic->io_apic_id = 0;
+ io_apic->reserved = 0;
+ io_apic->io_apic_addr = cpu_to_le32(IO_APIC_DEFAULT_ADDRESS);
+ io_apic->gsi_base = cpu_to_le32(0);
+
+ for (i = 0; i < sizeof pci_isa_irq; ++i) {
+ int_src_ovr[i].hdr.type = 2; /* Interrupt Source Override */
+ int_src_ovr[i].hdr.length = sizeof *int_src_ovr;
+ int_src_ovr[i].bus = 0;
+ int_src_ovr[i].source = pci_isa_irq[i];
+ int_src_ovr[i].gsi = cpu_to_le32(pci_isa_irq[i]);
+ int_src_ovr[i].flags = cpu_to_le16(0xd);
+ /* active high, level-triggered */
+ }
+ if (kvm_allows_irq0_override()) {
+ int_src_ovr[i].hdr.type = 2; /* Interrupt Source Override */
+ int_src_ovr[i].hdr.length = sizeof *int_src_ovr;
+ int_src_ovr[i].bus = 0;
+ int_src_ovr[i].source = 0;
+ int_src_ovr[i].gsi = cpu_to_le32(2);
+ int_src_ovr[i].flags = cpu_to_le16(0); /* conforms to bus spec */
+ }
+
+ lapic_nmi->hdr.type = 4; /* Local APIC NMI */
+ lapic_nmi->hdr.length = sizeof *lapic_nmi;
+ lapic_nmi->processor_id = 0xff; /* all processors */
+ lapic_nmi->flags = cpu_to_le16(0); /* conforms to bus spec */
+ lapic_nmi->lint = 1; /* NMI connected to LAPIC input LINT1 */
+
+ acpi_table_fill_hdr(std_hdr, blob_size, "APIC");
+ *out_blob = blob;
+ *out_blob_size = blob_size;
+}
@@ -53,6 +53,7 @@
#include "qemu/bitmap.h"
#include "qemu/config-file.h"
#include "hw/acpi/acpi.h"
+#include "hw/i386/acpi.h"
/* debug PC/ISA interrupts */
//#define DEBUG_IRQ
@@ -922,6 +923,19 @@ void pc_acpi_init(const char *default_dsdt)
}
}
+static void pc_acpi_madt(FWCfgState *fw_cfg)
+{
+ unsigned num_lapic;
+ char unsigned *blob;
+ size_t blob_size;
+
+ /* see note on FW_CFG_MAX_CPUS in bochs_bios_init() */
+ num_lapic = pc_apic_id_limit(max_cpus);
+
+ acpi_build_madt(&blob, &blob_size, num_lapic);
+ fw_cfg_add_file(fw_cfg, "etc/acpi/APIC", blob, blob_size);
+}
+
FWCfgState *pc_memory_init(MemoryRegion *system_memory,
const char *kernel_filename,
const char *kernel_cmdline,
@@ -974,6 +988,8 @@ FWCfgState *pc_memory_init(MemoryRegion *system_memory,
fw_cfg = bochs_bios_init();
rom_set_fw(fw_cfg);
+ pc_acpi_madt(fw_cfg);
+
if (linux_boot) {
load_linux(fw_cfg, kernel_filename, initrd_filename, kernel_cmdline, below_4g_mem_size);
}
@@ -1,6 +1,7 @@
obj-$(CONFIG_KVM) += kvm/
obj-y += multiboot.o smbios.o
obj-y += pc.o pc_piix.o pc_q35.o
+obj-y += acpi.o
obj-$(CONFIG_XEN) += xen_domainbuild.o xen_machine_pv.o
obj-y += kvmvapic.o