aboutsummaryrefslogtreecommitdiff
path: root/hw/i386
diff options
context:
space:
mode:
authorPaolo Bonzini <pbonzini@redhat.com>2019-10-26 15:36:22 +0200
committerPaolo Bonzini <pbonzini@redhat.com>2019-10-26 15:38:02 +0200
commit673652a785efb5f7da342b49c80a1abf033c82a7 (patch)
tree2f277cce43b50e85cca36d7354ba2dd7ffd14d41 /hw/i386
parent856bd2c28e108ad0eb909bbbf3774f6f8bd7c2d4 (diff)
parentdf84f17d1beabbb2aca39be18b21afc277cd6754 (diff)
downloadqemu-673652a785efb5f7da342b49c80a1abf033c82a7.zip
qemu-673652a785efb5f7da342b49c80a1abf033c82a7.tar.gz
qemu-673652a785efb5f7da342b49c80a1abf033c82a7.tar.bz2
Merge commit 'df84f17' into HEAD
This merge fixes a semantic conflict with the trivial tree. Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Diffstat (limited to 'hw/i386')
-rw-r--r--hw/i386/Kconfig10
-rw-r--r--hw/i386/Makefile.objs2
-rw-r--r--hw/i386/acpi-build.c29
-rw-r--r--hw/i386/amd_iommu.c3
-rw-r--r--hw/i386/intel_iommu.c3
-rw-r--r--hw/i386/microvm.c572
-rw-r--r--hw/i386/pc.c815
-rw-r--r--hw/i386/pc_piix.c65
-rw-r--r--hw/i386/pc_q35.c62
-rw-r--r--hw/i386/pc_sysfw.c60
-rw-r--r--hw/i386/x86.c795
-rw-r--r--hw/i386/xen/xen-hvm.c28
12 files changed, 1571 insertions, 873 deletions
diff --git a/hw/i386/Kconfig b/hw/i386/Kconfig
index c5c9d49..b25bb6d 100644
--- a/hw/i386/Kconfig
+++ b/hw/i386/Kconfig
@@ -92,6 +92,16 @@ config Q35
select SMBIOS
select FW_CFG_DMA
+config MICROVM
+ bool
+ imply SERIAL_ISA
+ select ISA_BUS
+ select APIC
+ select IOAPIC
+ select I8259
+ select MC146818RTC
+ select VIRTIO_MMIO
+
config VTD
bool
diff --git a/hw/i386/Makefile.objs b/hw/i386/Makefile.objs
index d3374e0..0d195b5 100644
--- a/hw/i386/Makefile.objs
+++ b/hw/i386/Makefile.objs
@@ -1,8 +1,10 @@
obj-$(CONFIG_KVM) += kvm/
obj-y += e820_memory_layout.o multiboot.o
+obj-y += x86.o
obj-y += pc.o
obj-$(CONFIG_I440FX) += pc_piix.o
obj-$(CONFIG_Q35) += pc_q35.o
+obj-$(CONFIG_MICROVM) += microvm.o
obj-y += fw_cfg.o pc_sysfw.o
obj-y += x86-iommu.o
obj-$(CONFIG_VTD) += intel_iommu.o
diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c
index d9435ba..9dd3dbb 100644
--- a/hw/i386/acpi-build.c
+++ b/hw/i386/acpi-build.c
@@ -361,6 +361,7 @@ static void
build_madt(GArray *table_data, BIOSLinker *linker, PCMachineState *pcms)
{
MachineClass *mc = MACHINE_GET_CLASS(pcms);
+ X86MachineState *x86ms = X86_MACHINE(pcms);
const CPUArchIdList *apic_ids = mc->possible_cpu_arch_ids(MACHINE(pcms));
int madt_start = table_data->len;
AcpiDeviceIfClass *adevc = ACPI_DEVICE_IF_GET_CLASS(pcms->acpi_dev);
@@ -390,7 +391,7 @@ build_madt(GArray *table_data, BIOSLinker *linker, PCMachineState *pcms)
io_apic->address = cpu_to_le32(IO_APIC_DEFAULT_ADDRESS);
io_apic->interrupt = cpu_to_le32(0);
- if (pcms->apic_xrupt_override) {
+ if (x86ms->apic_xrupt_override) {
intsrcovr = acpi_data_push(table_data, sizeof *intsrcovr);
intsrcovr->type = ACPI_APIC_XRUPT_OVERRIDE;
intsrcovr->length = sizeof(*intsrcovr);
@@ -1831,6 +1832,7 @@ build_dsdt(GArray *table_data, BIOSLinker *linker,
CrsRangeSet crs_range_set;
PCMachineState *pcms = PC_MACHINE(machine);
PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(machine);
+ X86MachineState *x86ms = X86_MACHINE(machine);
AcpiMcfgInfo mcfg;
uint32_t nr_mem = machine->ram_slots;
int root_bus_limit = 0xFF;
@@ -2103,7 +2105,7 @@ build_dsdt(GArray *table_data, BIOSLinker *linker,
* with half of the 16-bit control register. Hence, the total size
* of the i/o region used is FW_CFG_CTL_SIZE; when using DMA, the
* DMA control register is located at FW_CFG_DMA_IO_BASE + 4 */
- uint8_t io_size = object_property_get_bool(OBJECT(pcms->fw_cfg),
+ uint8_t io_size = object_property_get_bool(OBJECT(x86ms->fw_cfg),
"dma_enabled", NULL) ?
ROUND_UP(FW_CFG_CTL_SIZE, 4) + sizeof(dma_addr_t) :
FW_CFG_CTL_SIZE;
@@ -2336,6 +2338,7 @@ build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine)
int srat_start, numa_start, slots;
uint64_t mem_len, mem_base, next_base;
MachineClass *mc = MACHINE_GET_CLASS(machine);
+ X86MachineState *x86ms = X86_MACHINE(machine);
const CPUArchIdList *apic_ids = mc->possible_cpu_arch_ids(machine);
PCMachineState *pcms = PC_MACHINE(machine);
ram_addr_t hotplugabble_address_space_size =
@@ -2406,16 +2409,16 @@ build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine)
}
/* Cut out the ACPI_PCI hole */
- if (mem_base <= pcms->below_4g_mem_size &&
- next_base > pcms->below_4g_mem_size) {
- mem_len -= next_base - pcms->below_4g_mem_size;
+ if (mem_base <= x86ms->below_4g_mem_size &&
+ next_base > x86ms->below_4g_mem_size) {
+ mem_len -= next_base - x86ms->below_4g_mem_size;
if (mem_len > 0) {
numamem = acpi_data_push(table_data, sizeof *numamem);
build_srat_memory(numamem, mem_base, mem_len, i - 1,
MEM_AFFINITY_ENABLED);
}
mem_base = 1ULL << 32;
- mem_len = next_base - pcms->below_4g_mem_size;
+ mem_len = next_base - x86ms->below_4g_mem_size;
next_base = mem_base + mem_len;
}
@@ -2634,6 +2637,7 @@ void acpi_build(AcpiBuildTables *tables, MachineState *machine)
{
PCMachineState *pcms = PC_MACHINE(machine);
PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms);
+ X86MachineState *x86ms = X86_MACHINE(machine);
GArray *table_offsets;
unsigned facs, dsdt, rsdt, fadt;
AcpiPmInfo pm;
@@ -2795,7 +2799,7 @@ void acpi_build(AcpiBuildTables *tables, MachineState *machine)
*/
int legacy_aml_len =
pcmc->legacy_acpi_table_size +
- ACPI_BUILD_LEGACY_CPU_AML_SIZE * pcms->apic_id_limit;
+ ACPI_BUILD_LEGACY_CPU_AML_SIZE * x86ms->apic_id_limit;
int legacy_table_size =
ROUND_UP(tables_blob->len - aml_len + legacy_aml_len,
ACPI_BUILD_ALIGN_SIZE);
@@ -2885,13 +2889,14 @@ void acpi_setup(void)
{
PCMachineState *pcms = PC_MACHINE(qdev_get_machine());
PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms);
+ X86MachineState *x86ms = X86_MACHINE(pcms);
AcpiBuildTables tables;
AcpiBuildState *build_state;
Object *vmgenid_dev;
TPMIf *tpm;
static FwCfgTPMConfig tpm_config;
- if (!pcms->fw_cfg) {
+ if (!x86ms->fw_cfg) {
ACPI_BUILD_DPRINTF("No fw cfg. Bailing out.\n");
return;
}
@@ -2922,7 +2927,7 @@ void acpi_setup(void)
acpi_add_rom_blob(acpi_build_update, build_state,
tables.linker->cmd_blob, "etc/table-loader", 0);
- fw_cfg_add_file(pcms->fw_cfg, ACPI_BUILD_TPMLOG_FILE,
+ fw_cfg_add_file(x86ms->fw_cfg, ACPI_BUILD_TPMLOG_FILE,
tables.tcpalog->data, acpi_data_len(tables.tcpalog));
tpm = tpm_find();
@@ -2932,13 +2937,13 @@ void acpi_setup(void)
.tpm_version = tpm_get_version(tpm),
.tpmppi_version = TPM_PPI_VERSION_1_30
};
- fw_cfg_add_file(pcms->fw_cfg, "etc/tpm/config",
+ fw_cfg_add_file(x86ms->fw_cfg, "etc/tpm/config",
&tpm_config, sizeof tpm_config);
}
vmgenid_dev = find_vmgenid_dev();
if (vmgenid_dev) {
- vmgenid_add_fw_cfg(VMGENID(vmgenid_dev), pcms->fw_cfg,
+ vmgenid_add_fw_cfg(VMGENID(vmgenid_dev), x86ms->fw_cfg,
tables.vmgenid);
}
@@ -2951,7 +2956,7 @@ void acpi_setup(void)
uint32_t rsdp_size = acpi_data_len(tables.rsdp);
build_state->rsdp = g_memdup(tables.rsdp->data, rsdp_size);
- fw_cfg_add_file_callback(pcms->fw_cfg, ACPI_BUILD_RSDP_FILE,
+ fw_cfg_add_file_callback(x86ms->fw_cfg, ACPI_BUILD_RSDP_FILE,
acpi_build_update, NULL, build_state,
build_state->rsdp, rsdp_size, true);
build_state->rsdp_mr = NULL;
diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c
index d372636..d55dbf0 100644
--- a/hw/i386/amd_iommu.c
+++ b/hw/i386/amd_iommu.c
@@ -1540,6 +1540,7 @@ static void amdvi_realize(DeviceState *dev, Error **err)
X86IOMMUState *x86_iommu = X86_IOMMU_DEVICE(dev);
MachineState *ms = MACHINE(qdev_get_machine());
PCMachineState *pcms = PC_MACHINE(ms);
+ X86MachineState *x86ms = X86_MACHINE(ms);
PCIBus *bus = pcms->bus;
s->iotlb = g_hash_table_new_full(amdvi_uint64_hash,
@@ -1568,7 +1569,7 @@ static void amdvi_realize(DeviceState *dev, Error **err)
}
/* Pseudo address space under root PCI bus. */
- pcms->ioapic_as = amdvi_host_dma_iommu(bus, s, AMDVI_IOAPIC_SB_DEVID);
+ x86ms->ioapic_as = amdvi_host_dma_iommu(bus, s, AMDVI_IOAPIC_SB_DEVID);
/* set up MMIO */
memory_region_init_io(&s->mmio, OBJECT(s), &mmio_mem_ops, s, "amdvi-mmio",
diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c
index 771bed2..14e4e6a 100644
--- a/hw/i386/intel_iommu.c
+++ b/hw/i386/intel_iommu.c
@@ -3733,6 +3733,7 @@ static void vtd_realize(DeviceState *dev, Error **errp)
{
MachineState *ms = MACHINE(qdev_get_machine());
PCMachineState *pcms = PC_MACHINE(ms);
+ X86MachineState *x86ms = X86_MACHINE(ms);
PCIBus *bus = pcms->bus;
IntelIOMMUState *s = INTEL_IOMMU_DEVICE(dev);
X86IOMMUState *x86_iommu = X86_IOMMU_DEVICE(dev);
@@ -3773,7 +3774,7 @@ static void vtd_realize(DeviceState *dev, Error **errp)
sysbus_mmio_map(SYS_BUS_DEVICE(s), 0, Q35_HOST_BRIDGE_IOMMU_ADDR);
pci_setup_iommu(bus, vtd_host_dma_iommu, dev);
/* Pseudo address space under root PCI bus. */
- pcms->ioapic_as = vtd_host_dma_iommu(bus, s, Q35_PSEUDO_DEVFN_IOAPIC);
+ x86ms->ioapic_as = vtd_host_dma_iommu(bus, s, Q35_PSEUDO_DEVFN_IOAPIC);
qemu_add_machine_init_done_notifier(&vtd_machine_done_notify);
}
diff --git a/hw/i386/microvm.c b/hw/i386/microvm.c
new file mode 100644
index 0000000..8aacd6c
--- /dev/null
+++ b/hw/i386/microvm.c
@@ -0,0 +1,572 @@
+/*
+ * Copyright (c) 2018 Intel Corporation
+ * Copyright (c) 2019 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/error-report.h"
+#include "qemu/cutils.h"
+#include "qemu/units.h"
+#include "qapi/error.h"
+#include "qapi/visitor.h"
+#include "qapi/qapi-visit-common.h"
+#include "sysemu/sysemu.h"
+#include "sysemu/cpus.h"
+#include "sysemu/numa.h"
+#include "sysemu/reset.h"
+
+#include "hw/loader.h"
+#include "hw/irq.h"
+#include "hw/kvm/clock.h"
+#include "hw/i386/microvm.h"
+#include "hw/i386/x86.h"
+#include "hw/i386/pc.h"
+#include "target/i386/cpu.h"
+#include "hw/timer/i8254.h"
+#include "hw/rtc/mc146818rtc.h"
+#include "hw/char/serial.h"
+#include "hw/i386/topology.h"
+#include "hw/i386/e820_memory_layout.h"
+#include "hw/i386/fw_cfg.h"
+#include "hw/virtio/virtio-mmio.h"
+
+#include "cpu.h"
+#include "elf.h"
+#include "kvm_i386.h"
+#include "hw/xen/start_info.h"
+
+#define MICROVM_BIOS_FILENAME "bios-microvm.bin"
+
+static void microvm_set_rtc(MicrovmMachineState *mms, ISADevice *s)
+{
+ X86MachineState *x86ms = X86_MACHINE(mms);
+ int val;
+
+ val = MIN(x86ms->below_4g_mem_size / KiB, 640);
+ rtc_set_memory(s, 0x15, val);
+ rtc_set_memory(s, 0x16, val >> 8);
+ /* extended memory (next 64MiB) */
+ if (x86ms->below_4g_mem_size > 1 * MiB) {
+ val = (x86ms->below_4g_mem_size - 1 * MiB) / KiB;
+ } else {
+ val = 0;
+ }
+ if (val > 65535) {
+ val = 65535;
+ }
+ rtc_set_memory(s, 0x17, val);
+ rtc_set_memory(s, 0x18, val >> 8);
+ rtc_set_memory(s, 0x30, val);
+ rtc_set_memory(s, 0x31, val >> 8);
+ /* memory between 16MiB and 4GiB */
+ if (x86ms->below_4g_mem_size > 16 * MiB) {
+ val = (x86ms->below_4g_mem_size - 16 * MiB) / (64 * KiB);
+ } else {
+ val = 0;
+ }
+ if (val > 65535) {
+ val = 65535;
+ }
+ rtc_set_memory(s, 0x34, val);
+ rtc_set_memory(s, 0x35, val >> 8);
+ /* memory above 4GiB */
+ val = x86ms->above_4g_mem_size / 65536;
+ rtc_set_memory(s, 0x5b, val);
+ rtc_set_memory(s, 0x5c, val >> 8);
+ 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 microvm_devices_init(MicrovmMachineState *mms)
+{
+ X86MachineState *x86ms = X86_MACHINE(mms);
+ ISABus *isa_bus;
+ ISADevice *rtc_state;
+ GSIState *gsi_state;
+ int i;
+
+ /* Core components */
+
+ 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);
+ }
+
+ 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");
+
+ kvmclock_create();
+
+ for (i = 0; i < VIRTIO_NUM_TRANSPORTS; i++) {
+ sysbus_create_simple("virtio-mmio",
+ VIRTIO_MMIO_BASE + i * 512,
+ x86ms->gsi[VIRTIO_IRQ_BASE + i]);
+ }
+
+ /* Optional and legacy devices */
+
+ if (mms->pic == ON_OFF_AUTO_ON || mms->pic == ON_OFF_AUTO_AUTO) {
+ qemu_irq *i8259;
+
+ i8259 = i8259_init(isa_bus, pc_allocate_cpu_irq());
+ for (i = 0; i < ISA_NUM_IRQS; i++) {
+ gsi_state->i8259_irq[i] = i8259[i];
+ }
+ g_free(i8259);
+ }
+
+ if (mms->pit == ON_OFF_AUTO_ON || mms->pit == ON_OFF_AUTO_AUTO) {
+ if (kvm_pit_in_kernel()) {
+ kvm_pit_init(isa_bus, 0x40);
+ } else {
+ i8254_pit_init(isa_bus, 0x40, 0, NULL);
+ }
+ }
+
+ if (mms->rtc == ON_OFF_AUTO_ON ||
+ (mms->rtc == ON_OFF_AUTO_AUTO && !kvm_enabled())) {
+ rtc_state = mc146818_rtc_init(isa_bus, 2000, NULL);
+ microvm_set_rtc(mms, rtc_state);
+ }
+
+ if (mms->isa_serial) {
+ serial_hds_isa_init(isa_bus, 0, 1);
+ }
+
+ if (bios_name == NULL) {
+ bios_name = MICROVM_BIOS_FILENAME;
+ }
+ x86_bios_rom_init(get_system_memory(), true);
+}
+
+static void microvm_memory_init(MicrovmMachineState *mms)
+{
+ MachineState *machine = MACHINE(mms);
+ X86MachineState *x86ms = X86_MACHINE(mms);
+ MemoryRegion *ram, *ram_below_4g, *ram_above_4g;
+ MemoryRegion *system_memory = get_system_memory();
+ FWCfgState *fw_cfg;
+ ram_addr_t lowmem;
+ int i;
+
+ /*
+ * Check whether RAM fits below 4G (leaving 1/2 GByte for IO memory
+ * and 256 Mbytes for PCI Express Enhanced Configuration Access Mapping
+ * also known as MMCFG).
+ * If it doesn't, we need to split it in chunks below and above 4G.
+ * In any case, try to make sure that guest addresses aligned at
+ * 1G boundaries get mapped to host addresses aligned at 1G boundaries.
+ */
+ if (machine->ram_size >= 0xb0000000) {
+ lowmem = 0x80000000;
+ } else {
+ lowmem = 0xb0000000;
+ }
+
+ /*
+ * Handle the machine opt max-ram-below-4g. It is basically doing
+ * min(qemu limit, user limit).
+ */
+ if (!x86ms->max_ram_below_4g) {
+ x86ms->max_ram_below_4g = 4 * GiB;
+ }
+ if (lowmem > x86ms->max_ram_below_4g) {
+ lowmem = x86ms->max_ram_below_4g;
+ if (machine->ram_size - lowmem > lowmem &&
+ lowmem & (1 * GiB - 1)) {
+ warn_report("There is possibly poor performance as the ram size "
+ " (0x%" PRIx64 ") is more then twice the size of"
+ " max-ram-below-4g (%"PRIu64") and"
+ " max-ram-below-4g is not a multiple of 1G.",
+ (uint64_t)machine->ram_size, x86ms->max_ram_below_4g);
+ }
+ }
+
+ if (machine->ram_size > lowmem) {
+ x86ms->above_4g_mem_size = machine->ram_size - lowmem;
+ x86ms->below_4g_mem_size = lowmem;
+ } else {
+ x86ms->above_4g_mem_size = 0;
+ x86ms->below_4g_mem_size = machine->ram_size;
+ }
+
+ ram = g_malloc(sizeof(*ram));
+ memory_region_allocate_system_memory(ram, NULL, "microvm.ram",
+ machine->ram_size);
+
+ ram_below_4g = g_malloc(sizeof(*ram_below_4g));
+ memory_region_init_alias(ram_below_4g, NULL, "ram-below-4g", ram,
+ 0, x86ms->below_4g_mem_size);
+ memory_region_add_subregion(system_memory, 0, ram_below_4g);
+
+ e820_add_entry(0, x86ms->below_4g_mem_size, E820_RAM);
+
+ if (x86ms->above_4g_mem_size > 0) {
+ ram_above_4g = g_malloc(sizeof(*ram_above_4g));
+ memory_region_init_alias(ram_above_4g, NULL, "ram-above-4g", ram,
+ x86ms->below_4g_mem_size,
+ x86ms->above_4g_mem_size);
+ memory_region_add_subregion(system_memory, 0x100000000ULL,
+ ram_above_4g);
+ e820_add_entry(0x100000000ULL, x86ms->above_4g_mem_size, E820_RAM);
+ }
+
+ fw_cfg = fw_cfg_init_io_dma(FW_CFG_IO_BASE, FW_CFG_IO_BASE + 4,
+ &address_space_memory);
+
+ fw_cfg_add_i16(fw_cfg, FW_CFG_NB_CPUS, machine->smp.cpus);
+ fw_cfg_add_i16(fw_cfg, FW_CFG_MAX_CPUS, machine->smp.max_cpus);
+ fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, (uint64_t)machine->ram_size);
+ fw_cfg_add_i32(fw_cfg, FW_CFG_IRQ0_OVERRIDE, kvm_allows_irq0_override());
+ fw_cfg_add_bytes(fw_cfg, FW_CFG_E820_TABLE,
+ &e820_reserve, sizeof(e820_reserve));
+ fw_cfg_add_file(fw_cfg, "etc/e820", e820_table,
+ sizeof(struct e820_entry) * e820_get_num_entries());
+
+ rom_set_fw(fw_cfg);
+
+ if (machine->kernel_filename != NULL) {
+ x86_load_linux(x86ms, fw_cfg, 0, true, true);
+ }
+
+ if (mms->option_roms) {
+ for (i = 0; i < nb_option_roms; i++) {
+ rom_add_option(option_rom[i].name, option_rom[i].bootindex);
+ }
+ }
+
+ x86ms->fw_cfg = fw_cfg;
+ x86ms->ioapic_as = &address_space_memory;
+}
+
+static gchar *microvm_get_mmio_cmdline(gchar *name)
+{
+ gchar *cmdline;
+ gchar *separator;
+ long int index;
+ int ret;
+
+ separator = g_strrstr(name, ".");
+ if (!separator) {
+ return NULL;
+ }
+
+ if (qemu_strtol(separator + 1, NULL, 10, &index) != 0) {
+ return NULL;
+ }
+
+ cmdline = g_malloc0(VIRTIO_CMDLINE_MAXLEN);
+ ret = g_snprintf(cmdline, VIRTIO_CMDLINE_MAXLEN,
+ " virtio_mmio.device=512@0x%lx:%ld",
+ VIRTIO_MMIO_BASE + index * 512,
+ VIRTIO_IRQ_BASE + index);
+ if (ret < 0 || ret >= VIRTIO_CMDLINE_MAXLEN) {
+ g_free(cmdline);
+ return NULL;
+ }
+
+ return cmdline;
+}
+
+static void microvm_fix_kernel_cmdline(MachineState *machine)
+{
+ X86MachineState *x86ms = X86_MACHINE(machine);
+ BusState *bus;
+ BusChild *kid;
+ char *cmdline;
+
+ /*
+ * Find MMIO transports with attached devices, and add them to the kernel
+ * command line.
+ *
+ * Yes, this is a hack, but one that heavily improves the UX without
+ * introducing any significant issues.
+ */
+ cmdline = g_strdup(machine->kernel_cmdline);
+ bus = sysbus_get_default();
+ QTAILQ_FOREACH(kid, &bus->children, sibling) {
+ DeviceState *dev = kid->child;
+ ObjectClass *class = object_get_class(OBJECT(dev));
+
+ if (class == object_class_by_name(TYPE_VIRTIO_MMIO)) {
+ VirtIOMMIOProxy *mmio = VIRTIO_MMIO(OBJECT(dev));
+ VirtioBusState *mmio_virtio_bus = &mmio->bus;
+ BusState *mmio_bus = &mmio_virtio_bus->parent_obj;
+
+ if (!QTAILQ_EMPTY(&mmio_bus->children)) {
+ gchar *mmio_cmdline = microvm_get_mmio_cmdline(mmio_bus->name);
+ if (mmio_cmdline) {
+ char *newcmd = g_strjoin(NULL, cmdline, mmio_cmdline, NULL);
+ g_free(mmio_cmdline);
+ g_free(cmdline);
+ cmdline = newcmd;
+ }
+ }
+ }
+ }
+
+ fw_cfg_modify_i32(x86ms->fw_cfg, FW_CFG_CMDLINE_SIZE, strlen(cmdline) + 1);
+ fw_cfg_modify_string(x86ms->fw_cfg, FW_CFG_CMDLINE_DATA, cmdline);
+}
+
+static void microvm_machine_state_init(MachineState *machine)
+{
+ MicrovmMachineState *mms = MICROVM_MACHINE(machine);
+ X86MachineState *x86ms = X86_MACHINE(machine);
+ Error *local_err = NULL;
+
+ microvm_memory_init(mms);
+
+ x86_cpus_init(x86ms, CPU_VERSION_LATEST);
+ if (local_err) {
+ error_report_err(local_err);
+ exit(1);
+ }
+
+ microvm_devices_init(mms);
+}
+
+static void microvm_machine_reset(MachineState *machine)
+{
+ MicrovmMachineState *mms = MICROVM_MACHINE(machine);
+ CPUState *cs;
+ X86CPU *cpu;
+
+ if (machine->kernel_filename != NULL &&
+ mms->auto_kernel_cmdline && !mms->kernel_cmdline_fixed) {
+ microvm_fix_kernel_cmdline(machine);
+ mms->kernel_cmdline_fixed = true;
+ }
+
+ qemu_devices_reset();
+
+ CPU_FOREACH(cs) {
+ cpu = X86_CPU(cs);
+
+ if (cpu->apic_state) {
+ device_reset(cpu->apic_state);
+ }
+ }
+}
+
+static void microvm_machine_get_pic(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ MicrovmMachineState *mms = MICROVM_MACHINE(obj);
+ OnOffAuto pic = mms->pic;
+
+ visit_type_OnOffAuto(v, name, &pic, errp);
+}
+
+static void microvm_machine_set_pic(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ MicrovmMachineState *mms = MICROVM_MACHINE(obj);
+
+ visit_type_OnOffAuto(v, name, &mms->pic, errp);
+}
+
+static void microvm_machine_get_pit(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ MicrovmMachineState *mms = MICROVM_MACHINE(obj);
+ OnOffAuto pit = mms->pit;
+
+ visit_type_OnOffAuto(v, name, &pit, errp);
+}
+
+static void microvm_machine_set_pit(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ MicrovmMachineState *mms = MICROVM_MACHINE(obj);
+
+ visit_type_OnOffAuto(v, name, &mms->pit, errp);
+}
+
+static void microvm_machine_get_rtc(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ MicrovmMachineState *mms = MICROVM_MACHINE(obj);
+ OnOffAuto rtc = mms->rtc;
+
+ visit_type_OnOffAuto(v, name, &rtc, errp);
+}
+
+static void microvm_machine_set_rtc(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ MicrovmMachineState *mms = MICROVM_MACHINE(obj);
+
+ visit_type_OnOffAuto(v, name, &mms->rtc, errp);
+}
+
+static bool microvm_machine_get_isa_serial(Object *obj, Error **errp)
+{
+ MicrovmMachineState *mms = MICROVM_MACHINE(obj);
+
+ return mms->isa_serial;
+}
+
+static void microvm_machine_set_isa_serial(Object *obj, bool value,
+ Error **errp)
+{
+ MicrovmMachineState *mms = MICROVM_MACHINE(obj);
+
+ mms->isa_serial = value;
+}
+
+static bool microvm_machine_get_option_roms(Object *obj, Error **errp)
+{
+ MicrovmMachineState *mms = MICROVM_MACHINE(obj);
+
+ return mms->option_roms;
+}
+
+static void microvm_machine_set_option_roms(Object *obj, bool value,
+ Error **errp)
+{
+ MicrovmMachineState *mms = MICROVM_MACHINE(obj);
+
+ mms->option_roms = value;
+}
+
+static bool microvm_machine_get_auto_kernel_cmdline(Object *obj, Error **errp)
+{
+ MicrovmMachineState *mms = MICROVM_MACHINE(obj);
+
+ return mms->auto_kernel_cmdline;
+}
+
+static void microvm_machine_set_auto_kernel_cmdline(Object *obj, bool value,
+ Error **errp)
+{
+ MicrovmMachineState *mms = MICROVM_MACHINE(obj);
+
+ mms->auto_kernel_cmdline = value;
+}
+
+static void microvm_machine_initfn(Object *obj)
+{
+ MicrovmMachineState *mms = MICROVM_MACHINE(obj);
+
+ /* Configuration */
+ mms->pic = ON_OFF_AUTO_AUTO;
+ mms->pit = ON_OFF_AUTO_AUTO;
+ mms->rtc = ON_OFF_AUTO_AUTO;
+ mms->isa_serial = true;
+ mms->option_roms = true;
+ mms->auto_kernel_cmdline = true;
+
+ /* State */
+ mms->kernel_cmdline_fixed = false;
+}
+
+static void microvm_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->init = microvm_machine_state_init;
+
+ mc->family = "microvm_i386";
+ mc->desc = "microvm (i386)";
+ mc->units_per_default_bus = 1;
+ mc->no_floppy = 1;
+ mc->max_cpus = 288;
+ mc->has_hotpluggable_cpus = false;
+ mc->auto_enable_numa_with_memhp = false;
+ mc->default_cpu_type = TARGET_DEFAULT_CPU_TYPE;
+ mc->nvdimm_supported = false;
+
+ /* Avoid relying too much on kernel components */
+ mc->default_kernel_irqchip_split = true;
+
+ /* Machine class handlers */
+ mc->reset = microvm_machine_reset;
+
+ object_class_property_add(oc, MICROVM_MACHINE_PIC, "OnOffAuto",
+ microvm_machine_get_pic,
+ microvm_machine_set_pic,
+ NULL, NULL, &error_abort);
+ object_class_property_set_description(oc, MICROVM_MACHINE_PIC,
+ "Enable i8259 PIC", &error_abort);
+
+ object_class_property_add(oc, MICROVM_MACHINE_PIT, "OnOffAuto",
+ microvm_machine_get_pit,
+ microvm_machine_set_pit,
+ NULL, NULL, &error_abort);
+ object_class_property_set_description(oc, MICROVM_MACHINE_PIT,
+ "Enable i8254 PIT", &error_abort);
+
+ object_class_property_add(oc, MICROVM_MACHINE_RTC, "OnOffAuto",
+ microvm_machine_get_rtc,
+ microvm_machine_set_rtc,
+ NULL, NULL, &error_abort);
+ object_class_property_set_description(oc, MICROVM_MACHINE_RTC,
+ "Enable MC146818 RTC", &error_abort);
+
+ object_class_property_add_bool(oc, MICROVM_MACHINE_ISA_SERIAL,
+ microvm_machine_get_isa_serial,
+ microvm_machine_set_isa_serial,
+ &error_abort);
+ object_class_property_set_description(oc, MICROVM_MACHINE_ISA_SERIAL,
+ "Set off to disable the instantiation an ISA serial port",
+ &error_abort);
+
+ object_class_property_add_bool(oc, MICROVM_MACHINE_OPTION_ROMS,
+ microvm_machine_get_option_roms,
+ microvm_machine_set_option_roms,
+ &error_abort);
+ object_class_property_set_description(oc, MICROVM_MACHINE_OPTION_ROMS,
+ "Set off to disable loading option ROMs", &error_abort);
+
+ object_class_property_add_bool(oc, MICROVM_MACHINE_AUTO_KERNEL_CMDLINE,
+ microvm_machine_get_auto_kernel_cmdline,
+ microvm_machine_set_auto_kernel_cmdline,
+ &error_abort);
+ object_class_property_set_description(oc,
+ MICROVM_MACHINE_AUTO_KERNEL_CMDLINE,
+ "Set off to disable adding virtio-mmio devices to the kernel cmdline",
+ &error_abort);
+}
+
+static const TypeInfo microvm_machine_info = {
+ .name = TYPE_MICROVM_MACHINE,
+ .parent = TYPE_X86_MACHINE,
+ .instance_size = sizeof(MicrovmMachineState),
+ .instance_init = microvm_machine_initfn,
+ .class_size = sizeof(MicrovmMachineClass),
+ .class_init = microvm_class_init,
+ .interfaces = (InterfaceInfo[]) {
+ { }
+ },
+};
+
+static void microvm_machine_init(void)
+{
+ type_register_static(&microvm_machine_info);
+}
+type_init(microvm_machine_init);
diff --git a/hw/i386/pc.c b/hw/i386/pc.c
index 51b7243..b8f02c6 100644
--- a/hw/i386/pc.c
+++ b/hw/i386/pc.c
@@ -24,6 +24,7 @@
#include "qemu/osdep.h"
#include "qemu/units.h"
+#include "hw/i386/x86.h"
#include "hw/i386/pc.h"
#include "hw/char/serial.h"
#include "hw/char/parallel.h"
@@ -68,6 +69,7 @@
#include "qemu/config-file.h"
#include "qemu/error-report.h"
#include "qemu/option.h"
+#include "qemu/cutils.h"
#include "hw/acpi/acpi.h"
#include "hw/acpi/cpu_hotplug.h"
#include "hw/boards.h"
@@ -77,7 +79,6 @@
#include "qapi/qapi-visit-common.h"
#include "qapi/visitor.h"
#include "hw/core/cpu.h"
-#include "hw/nmi.h"
#include "hw/usb.h"
#include "hw/i386/intel_iommu.h"
#include "hw/net/ne2000-isa.h"
@@ -102,9 +103,6 @@
struct hpet_fw_config hpet_cfg = {.count = UINT8_MAX};
-/* Physical Address of PVH entry point read from kernel ELF NOTE */
-static size_t pvh_start_addr;
-
GlobalProperty pc_compat_4_1[] = {};
const size_t pc_compat_4_1_len = G_N_ELEMENTS(pc_compat_4_1);
@@ -357,6 +355,21 @@ void gsi_handler(void *opaque, int n, int level)
qemu_set_irq(s->ioapic_irq[n], level);
}
+GSIState *pc_gsi_create(qemu_irq **irqs, bool pci_enabled)
+{
+ GSIState *s;
+
+ s = g_new0(GSIState, 1);
+ if (kvm_ioapic_in_kernel()) {
+ kvm_pc_setup_irq_routing(pci_enabled);
+ *irqs = qemu_allocate_irqs(kvm_pc_gsi_handler, s, GSI_NUM_PINS);
+ } else {
+ *irqs = qemu_allocate_irqs(gsi_handler, s, GSI_NUM_PINS);
+ }
+
+ return s;
+}
+
static void ioport80_write(void *opaque, hwaddr addr, uint64_t data,
unsigned size)
{
@@ -681,17 +694,18 @@ void pc_cmos_init(PCMachineState *pcms,
{
int val;
static pc_cmos_init_late_arg arg;
+ X86MachineState *x86ms = X86_MACHINE(pcms);
/* various important CMOS locations needed by PC/Bochs bios */
/* memory size */
/* base memory (first MiB) */
- val = MIN(pcms->below_4g_mem_size / KiB, 640);
+ val = MIN(x86ms->below_4g_mem_size / KiB, 640);
rtc_set_memory(s, 0x15, val);
rtc_set_memory(s, 0x16, val >> 8);
/* extended memory (next 64MiB) */
- if (pcms->below_4g_mem_size > 1 * MiB) {
- val = (pcms->below_4g_mem_size - 1 * MiB) / KiB;
+ if (x86ms->below_4g_mem_size > 1 * MiB) {
+ val = (x86ms->below_4g_mem_size - 1 * MiB) / KiB;
} else {
val = 0;
}
@@ -702,8 +716,8 @@ void pc_cmos_init(PCMachineState *pcms,
rtc_set_memory(s, 0x30, val);
rtc_set_memory(s, 0x31, val >> 8);
/* memory between 16MiB and 4GiB */
- if (pcms->below_4g_mem_size > 16 * MiB) {
- val = (pcms->below_4g_mem_size - 16 * MiB) / (64 * KiB);
+ if (x86ms->below_4g_mem_size > 16 * MiB) {
+ val = (x86ms->below_4g_mem_size - 16 * MiB) / (64 * KiB);
} else {
val = 0;
}
@@ -712,14 +726,14 @@ void pc_cmos_init(PCMachineState *pcms,
rtc_set_memory(s, 0x34, val);
rtc_set_memory(s, 0x35, val >> 8);
/* memory above 4GiB */
- val = pcms->above_4g_mem_size / 65536;
+ val = x86ms->above_4g_mem_size / 65536;
rtc_set_memory(s, 0x5b, val);
rtc_set_memory(s, 0x5c, val >> 8);
rtc_set_memory(s, 0x5d, val >> 16);
object_property_add_link(OBJECT(pcms), "rtc_state",
TYPE_ISA_DEVICE,
- (Object **)&pcms->rtc,
+ (Object **)&x86ms->rtc,
object_property_allow_set_link,
OBJ_PROP_LINK_STRONG, &error_abort);
object_property_set_link(OBJECT(pcms), OBJECT(s),
@@ -866,478 +880,6 @@ static void handle_a20_line_change(void *opaque, int irq, int level)
x86_cpu_set_a20(cpu, level);
}
-/* Calculates initial APIC ID for a specific CPU index
- *
- * Currently we need to be able to calculate the APIC ID from the CPU index
- * alone (without requiring a CPU object), as the QEMU<->Seabios interfaces have
- * no concept of "CPU index", and the NUMA tables on fw_cfg need the APIC ID of
- * all CPUs up to max_cpus.
- */
-static uint32_t x86_cpu_apic_id_from_index(PCMachineState *pcms,
- unsigned int cpu_index)
-{
- MachineState *ms = MACHINE(pcms);
- PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms);
- uint32_t correct_id;
- static bool warned;
-
- correct_id = x86_apicid_from_cpu_idx(pcms->smp_dies, ms->smp.cores,
- ms->smp.threads, cpu_index);
- if (pcmc->compat_apic_id_mode) {
- if (cpu_index != correct_id && !warned && !qtest_enabled()) {
- error_report("APIC IDs set in compatibility mode, "
- "CPU topology won't match the configuration");
- warned = true;
- }
- return cpu_index;
- } else {
- return correct_id;
- }
-}
-
-static long get_file_size(FILE *f)
-{
- long where, size;
-
- /* XXX: on Unix systems, using fstat() probably makes more sense */
-
- where = ftell(f);
- fseek(f, 0, SEEK_END);
- size = ftell(f);
- fseek(f, where, SEEK_SET);
-
- return size;
-}
-
-struct setup_data {
- uint64_t next;
- uint32_t type;
- uint32_t len;
- uint8_t data[0];
-} __attribute__((packed));
-
-
-/*
- * The entry point into the kernel for PVH boot is different from
- * the native entry point. The PVH entry is defined by the x86/HVM
- * direct boot ABI and is available in an ELFNOTE in the kernel binary.
- *
- * This function is passed to load_elf() when it is called from
- * load_elfboot() which then additionally checks for an ELF Note of
- * type XEN_ELFNOTE_PHYS32_ENTRY and passes it to this function to
- * parse the PVH entry address from the ELF Note.
- *
- * Due to trickery in elf_opts.h, load_elf() is actually available as
- * load_elf32() or load_elf64() and this routine needs to be able
- * to deal with being called as 32 or 64 bit.
- *
- * The address of the PVH entry point is saved to the 'pvh_start_addr'
- * global variable. (although the entry point is 32-bit, the kernel
- * binary can be either 32-bit or 64-bit).
- */
-static uint64_t read_pvh_start_addr(void *arg1, void *arg2, bool is64)
-{
- size_t *elf_note_data_addr;
-
- /* Check if ELF Note header passed in is valid */
- if (arg1 == NULL) {
- return 0;
- }
-
- if (is64) {
- struct elf64_note *nhdr64 = (struct elf64_note *)arg1;
- uint64_t nhdr_size64 = sizeof(struct elf64_note);
- uint64_t phdr_align = *(uint64_t *)arg2;
- uint64_t nhdr_namesz = nhdr64->n_namesz;
-
- elf_note_data_addr =
- ((void *)nhdr64) + nhdr_size64 +
- QEMU_ALIGN_UP(nhdr_namesz, phdr_align);
- } else {
- struct elf32_note *nhdr32 = (struct elf32_note *)arg1;
- uint32_t nhdr_size32 = sizeof(struct elf32_note);
- uint32_t phdr_align = *(uint32_t *)arg2;
- uint32_t nhdr_namesz = nhdr32->n_namesz;
-
- elf_note_data_addr =
- ((void *)nhdr32) + nhdr_size32 +
- QEMU_ALIGN_UP(nhdr_namesz, phdr_align);
- }
-
- pvh_start_addr = *elf_note_data_addr;
-
- return pvh_start_addr;
-}
-
-static bool load_elfboot(const char *kernel_filename,
- int kernel_file_size,
- uint8_t *header,
- size_t pvh_xen_start_addr,
- FWCfgState *fw_cfg)
-{
- uint32_t flags = 0;
- uint32_t mh_load_addr = 0;
- uint32_t elf_kernel_size = 0;
- uint64_t elf_entry;
- uint64_t elf_low, elf_high;
- int kernel_size;
-
- if (ldl_p(header) != 0x464c457f) {
- return false; /* no elfboot */
- }
-
- bool elf_is64 = header[EI_CLASS] == ELFCLASS64;
- flags = elf_is64 ?
- ((Elf64_Ehdr *)header)->e_flags : ((Elf32_Ehdr *)header)->e_flags;
-
- if (flags & 0x00010004) { /* LOAD_ELF_HEADER_HAS_ADDR */
- error_report("elfboot unsupported flags = %x", flags);
- exit(1);
- }
-
- uint64_t elf_note_type = XEN_ELFNOTE_PHYS32_ENTRY;
- kernel_size = load_elf(kernel_filename, read_pvh_start_addr,
- NULL, &elf_note_type, &elf_entry,
- &elf_low, &elf_high, 0, I386_ELF_MACHINE,
- 0, 0);
-
- if (kernel_size < 0) {
- error_report("Error while loading elf kernel");
- exit(1);
- }
- mh_load_addr = elf_low;
- elf_kernel_size = elf_high - elf_low;
-
- if (pvh_start_addr == 0) {
- error_report("Error loading uncompressed kernel without PVH ELF Note");
- exit(1);
- }
- fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ENTRY, pvh_start_addr);
- fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ADDR, mh_load_addr);
- fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_SIZE, elf_kernel_size);
-
- return true;
-}
-
-static void load_linux(PCMachineState *pcms,
- FWCfgState *fw_cfg)
-{
- uint16_t protocol;
- int setup_size, kernel_size, cmdline_size;
- int dtb_size, setup_data_offset;
- uint32_t initrd_max;
- uint8_t header[8192], *setup, *kernel;
- hwaddr real_addr, prot_addr, cmdline_addr, initrd_addr = 0;
- FILE *f;
- char *vmode;
- MachineState *machine = MACHINE(pcms);
- PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms);
- struct setup_data *setup_data;
- const char *kernel_filename = machine->kernel_filename;
- const char *initrd_filename = machine->initrd_filename;
- const char *dtb_filename = machine->dtb;
- const char *kernel_cmdline = machine->kernel_cmdline;
-
- /* Align to 16 bytes as a paranoia measure */
- cmdline_size = (strlen(kernel_cmdline)+16) & ~15;
-
- /* load the kernel header */
- f = fopen(kernel_filename, "rb");
- if (!f || !(kernel_size = get_file_size(f)) ||
- fread(header, 1, MIN(ARRAY_SIZE(header), kernel_size), f) !=
- MIN(ARRAY_SIZE(header), kernel_size)) {
- fprintf(stderr, "qemu: could not load kernel '%s': %s\n",
- kernel_filename, strerror(errno));
- exit(1);
- }
-
- /* kernel protocol version */
-#if 0
- fprintf(stderr, "header magic: %#x\n", ldl_p(header+0x202));
-#endif
- if (ldl_p(header+0x202) == 0x53726448) {
- protocol = lduw_p(header+0x206);
- } else {
- /*
- * This could be a multiboot kernel. If it is, let's stop treating it
- * like a Linux kernel.
- * Note: some multiboot images could be in the ELF format (the same of
- * PVH), so we try multiboot first since we check the multiboot magic
- * header before to load it.
- */
- if (load_multiboot(fw_cfg, f, kernel_filename, initrd_filename,
- kernel_cmdline, kernel_size, header)) {
- return;
- }
- /*
- * Check if the file is an uncompressed kernel file (ELF) and load it,
- * saving the PVH entry point used by the x86/HVM direct boot ABI.
- * If load_elfboot() is successful, populate the fw_cfg info.
- */
- if (pcmc->pvh_enabled &&
- load_elfboot(kernel_filename, kernel_size,
- header, pvh_start_addr, fw_cfg)) {
- fclose(f);
-
- fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE,
- strlen(kernel_cmdline) + 1);
- fw_cfg_add_string(fw_cfg, FW_CFG_CMDLINE_DATA, kernel_cmdline);
-
- fw_cfg_add_i32(fw_cfg, FW_CFG_SETUP_SIZE, sizeof(header));
- fw_cfg_add_bytes(fw_cfg, FW_CFG_SETUP_DATA,
- header, sizeof(header));
-
- /* load initrd */
- if (initrd_filename) {
- GMappedFile *mapped_file;
- gsize initrd_size;
- gchar *initrd_data;
- GError *gerr = NULL;
-
- mapped_file = g_mapped_file_new(initrd_filename, false, &gerr);
- if (!mapped_file) {
- fprintf(stderr, "qemu: error reading initrd %s: %s\n",
- initrd_filename, gerr->message);
- exit(1);
- }
- pcms->initrd_mapped_file = mapped_file;
-
- initrd_data = g_mapped_file_get_contents(mapped_file);
- initrd_size = g_mapped_file_get_length(mapped_file);
- initrd_max = pcms->below_4g_mem_size - pcmc->acpi_data_size - 1;
- if (initrd_size >= initrd_max) {
- fprintf(stderr, "qemu: initrd is too large, cannot support."
- "(max: %"PRIu32", need %"PRId64")\n",
- initrd_max, (uint64_t)initrd_size);
- exit(1);
- }
-
- initrd_addr = (initrd_max - initrd_size) & ~4095;
-
- fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_ADDR, initrd_addr);
- fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_SIZE, initrd_size);
- fw_cfg_add_bytes(fw_cfg, FW_CFG_INITRD_DATA, initrd_data,
- initrd_size);
- }
-
- option_rom[nb_option_roms].bootindex = 0;
- option_rom[nb_option_roms].name = "pvh.bin";
- nb_option_roms++;
-
- return;
- }
- protocol = 0;
- }
-
- if (protocol < 0x200 || !(header[0x211] & 0x01)) {
- /* Low kernel */
- real_addr = 0x90000;
- cmdline_addr = 0x9a000 - cmdline_size;
- prot_addr = 0x10000;
- } else if (protocol < 0x202) {
- /* High but ancient kernel */
- real_addr = 0x90000;
- cmdline_addr = 0x9a000 - cmdline_size;
- prot_addr = 0x100000;
- } else {
- /* High and recent kernel */
- real_addr = 0x10000;
- cmdline_addr = 0x20000;
- prot_addr = 0x100000;
- }
-
-#if 0
- fprintf(stderr,
- "qemu: real_addr = 0x" TARGET_FMT_plx "\n"
- "qemu: cmdline_addr = 0x" TARGET_FMT_plx "\n"
- "qemu: prot_addr = 0x" TARGET_FMT_plx "\n",
- real_addr,
- cmdline_addr,
- prot_addr);
-#endif
-
- /* highest address for loading the initrd */
- if (protocol >= 0x20c &&
- lduw_p(header+0x236) & XLF_CAN_BE_LOADED_ABOVE_4G) {
- /*
- * Linux has supported initrd up to 4 GB for a very long time (2007,
- * long before XLF_CAN_BE_LOADED_ABOVE_4G which was added in 2013),
- * though it only sets initrd_max to 2 GB to "work around bootloader
- * bugs". Luckily, QEMU firmware(which does something like bootloader)
- * has supported this.
- *
- * It's believed that if XLF_CAN_BE_LOADED_ABOVE_4G is set, initrd can
- * be loaded into any address.
- *
- * In addition, initrd_max is uint32_t simply because QEMU doesn't
- * support the 64-bit boot protocol (specifically the ext_ramdisk_image
- * field).
- *
- * Therefore here just limit initrd_max to UINT32_MAX simply as well.
- */
- initrd_max = UINT32_MAX;
- } else if (protocol >= 0x203) {
- initrd_max = ldl_p(header+0x22c);
- } else {
- initrd_max = 0x37ffffff;
- }
-
- if (initrd_max >= pcms->below_4g_mem_size - pcmc->acpi_data_size) {
- initrd_max = pcms->below_4g_mem_size - pcmc->acpi_data_size - 1;
- }
-
- fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_ADDR, cmdline_addr);
- fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE, strlen(kernel_cmdline)+1);
- fw_cfg_add_string(fw_cfg, FW_CFG_CMDLINE_DATA, kernel_cmdline);
-
- if (protocol >= 0x202) {
- stl_p(header+0x228, cmdline_addr);
- } else {
- stw_p(header+0x20, 0xA33F);
- stw_p(header+0x22, cmdline_addr-real_addr);
- }
-
- /* handle vga= parameter */
- vmode = strstr(kernel_cmdline, "vga=");
- if (vmode) {
- unsigned int video_mode;
- /* skip "vga=" */
- vmode += 4;
- if (!strncmp(vmode, "normal", 6)) {
- video_mode = 0xffff;
- } else if (!strncmp(vmode, "ext", 3)) {
- video_mode = 0xfffe;
- } else if (!strncmp(vmode, "ask", 3)) {
- video_mode = 0xfffd;
- } else {
- video_mode = strtol(vmode, NULL, 0);
- }
- stw_p(header+0x1fa, video_mode);
- }
-
- /* loader type */
- /* High nybble = B reserved for QEMU; low nybble is revision number.
- If this code is substantially changed, you may want to consider
- incrementing the revision. */
- if (protocol >= 0x200) {
- header[0x210] = 0xB0;
- }
- /* heap */
- if (protocol >= 0x201) {
- header[0x211] |= 0x80; /* CAN_USE_HEAP */
- stw_p(header+0x224, cmdline_addr-real_addr-0x200);
- }
-
- /* load initrd */
- if (initrd_filename) {
- GMappedFile *mapped_file;
- gsize initrd_size;
- gchar *initrd_data;
- GError *gerr = NULL;
-
- if (protocol < 0x200) {
- fprintf(stderr, "qemu: linux kernel too old to load a ram disk\n");
- exit(1);
- }
-
- mapped_file = g_mapped_file_new(initrd_filename, false, &gerr);
- if (!mapped_file) {
- fprintf(stderr, "qemu: error reading initrd %s: %s\n",
- initrd_filename, gerr->message);
- exit(1);
- }
- pcms->initrd_mapped_file = mapped_file;
-
- initrd_data = g_mapped_file_get_contents(mapped_file);
- initrd_size = g_mapped_file_get_length(mapped_file);
- if (initrd_size >= initrd_max) {
- fprintf(stderr, "qemu: initrd is too large, cannot support."
- "(max: %"PRIu32", need %"PRId64")\n",
- initrd_max, (uint64_t)initrd_size);
- exit(1);
- }
-
- initrd_addr = (initrd_max-initrd_size) & ~4095;
-
- fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_ADDR, initrd_addr);
- fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_SIZE, initrd_size);
- fw_cfg_add_bytes(fw_cfg, FW_CFG_INITRD_DATA, initrd_data, initrd_size);
-
- stl_p(header+0x218, initrd_addr);
- stl_p(header+0x21c, initrd_size);
- }
-
- /* load kernel and setup */
- setup_size = header[0x1f1];
- if (setup_size == 0) {
- setup_size = 4;
- }
- setup_size = (setup_size+1)*512;
- if (setup_size > kernel_size) {
- fprintf(stderr, "qemu: invalid kernel header\n");
- exit(1);
- }
- kernel_size -= setup_size;
-
- setup = g_malloc(setup_size);
- kernel = g_malloc(kernel_size);
- fseek(f, 0, SEEK_SET);
- if (fread(setup, 1, setup_size, f) != setup_size) {
- fprintf(stderr, "fread() failed\n");
- exit(1);
- }
- if (fread(kernel, 1, kernel_size, f) != kernel_size) {
- fprintf(stderr, "fread() failed\n");
- exit(1);
- }
- fclose(f);
-
- /* append dtb to kernel */
- if (dtb_filename) {
- if (protocol < 0x209) {
- fprintf(stderr, "qemu: Linux kernel too old to load a dtb\n");
- exit(1);
- }
-
- dtb_size = get_image_size(dtb_filename);
- if (dtb_size <= 0) {
- fprintf(stderr, "qemu: error reading dtb %s: %s\n",
- dtb_filename, strerror(errno));
- exit(1);
- }
-
- setup_data_offset = QEMU_ALIGN_UP(kernel_size, 16);
- kernel_size = setup_data_offset + sizeof(struct setup_data) + dtb_size;
- kernel = g_realloc(kernel, kernel_size);
-
- stq_p(header+0x250, prot_addr + setup_data_offset);
-
- setup_data = (struct setup_data *)(kernel + setup_data_offset);
- setup_data->next = 0;
- setup_data->type = cpu_to_le32(SETUP_DTB);
- setup_data->len = cpu_to_le32(dtb_size);
-
- load_image_size(dtb_filename, setup_data->data, dtb_size);
- }
-
- memcpy(setup, header, MIN(sizeof(header), setup_size));
-
- fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ADDR, prot_addr);
- fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_SIZE, kernel_size);
- fw_cfg_add_bytes(fw_cfg, FW_CFG_KERNEL_DATA, kernel, kernel_size);
-
- fw_cfg_add_i32(fw_cfg, FW_CFG_SETUP_ADDR, real_addr);
- fw_cfg_add_i32(fw_cfg, FW_CFG_SETUP_SIZE, setup_size);
- fw_cfg_add_bytes(fw_cfg, FW_CFG_SETUP_DATA, setup, setup_size);
-
- option_rom[nb_option_roms].bootindex = 0;
- option_rom[nb_option_roms].name = "linuxboot.bin";
- if (pcmc->linuxboot_dma_enabled && fw_cfg_dma_enabled(fw_cfg)) {
- option_rom[nb_option_roms].name = "linuxboot_dma.bin";
- }
- nb_option_roms++;
-}
-
#define NE2000_NB_MAX 6
static const int ne2000_io[NE2000_NB_MAX] = { 0x300, 0x320, 0x340, 0x360,
@@ -1374,31 +916,13 @@ void pc_acpi_smi_interrupt(void *opaque, int irq, int level)
}
}
-static void pc_new_cpu(PCMachineState *pcms, int64_t apic_id, Error **errp)
-{
- Object *cpu = NULL;
- Error *local_err = NULL;
- CPUX86State *env = NULL;
-
- cpu = object_new(MACHINE(pcms)->cpu_type);
-
- env = &X86_CPU(cpu)->env;
- env->nr_dies = pcms->smp_dies;
-
- object_property_set_uint(cpu, apic_id, "apic-id", &local_err);
- object_property_set_bool(cpu, true, "realized", &local_err);
-
- object_unref(cpu);
- error_propagate(errp, local_err);
-}
-
/*
* This function is very similar to smp_parse()
* in hw/core/machine.c but includes CPU die support.
*/
void pc_smp_parse(MachineState *ms, QemuOpts *opts)
{
- PCMachineState *pcms = PC_MACHINE(ms);
+ X86MachineState *x86ms = X86_MACHINE(ms);
if (opts) {
unsigned cpus = qemu_opt_get_number(opts, "cpus", 0);
@@ -1462,7 +986,7 @@ void pc_smp_parse(MachineState *ms, QemuOpts *opts)
ms->smp.cpus = cpus;
ms->smp.cores = cores;
ms->smp.threads = threads;
- pcms->smp_dies = dies;
+ x86ms->smp_dies = dies;
}
if (ms->smp.cpus > 1) {
@@ -1474,8 +998,8 @@ void pc_smp_parse(MachineState *ms, QemuOpts *opts)
void pc_hot_add_cpu(MachineState *ms, const int64_t id, Error **errp)
{
- PCMachineState *pcms = PC_MACHINE(ms);
- int64_t apic_id = x86_cpu_apic_id_from_index(pcms, id);
+ X86MachineState *x86ms = X86_MACHINE(ms);
+ int64_t apic_id = x86_cpu_apic_id_from_index(x86ms, id);
Error *local_err = NULL;
if (id < 0) {
@@ -1490,38 +1014,14 @@ void pc_hot_add_cpu(MachineState *ms, const int64_t id, Error **errp)
return;
}
- pc_new_cpu(PC_MACHINE(ms), apic_id, &local_err);
+
+ x86_cpu_new(X86_MACHINE(ms), apic_id, &local_err);
if (local_err) {
error_propagate(errp, local_err);
return;
}
}
-void pc_cpus_init(PCMachineState *pcms)
-{
- int i;
- const CPUArchIdList *possible_cpus;
- MachineState *ms = MACHINE(pcms);
- MachineClass *mc = MACHINE_GET_CLASS(pcms);
- PCMachineClass *pcmc = PC_MACHINE_CLASS(mc);
-
- x86_cpu_set_default_version(pcmc->default_cpu_version);
-
- /* Calculates the limit to CPU APIC ID values
- *
- * Limit for the APIC ID value, so that all
- * CPU APIC IDs are < pcms->apic_id_limit.
- *
- * This is used for FW_CFG_MAX_CPUS. See comments on fw_cfg_arch_create().
- */
- pcms->apic_id_limit = x86_cpu_apic_id_from_index(pcms,
- ms->smp.max_cpus - 1) + 1;
- possible_cpus = mc->possible_cpu_arch_ids(ms);
- for (i = 0; i < ms->smp.cpus; i++) {
- pc_new_cpu(pcms, possible_cpus->cpus[i].arch_id, &error_fatal);
- }
-}
-
static void rtc_set_cpus_count(ISADevice *rtc, uint16_t cpus_count)
{
if (cpus_count > 0xff) {
@@ -1540,10 +1040,11 @@ void pc_machine_done(Notifier *notifier, void *data)
{
PCMachineState *pcms = container_of(notifier,
PCMachineState, machine_done);
+ X86MachineState *x86ms = X86_MACHINE(pcms);
PCIBus *bus = pcms->bus;
/* set the number of CPUs */
- rtc_set_cpus_count(pcms->rtc, pcms->boot_cpus);
+ rtc_set_cpus_count(x86ms->rtc, x86ms->boot_cpus);
if (bus) {
int extra_hosts = 0;
@@ -1554,23 +1055,23 @@ void pc_machine_done(Notifier *notifier, void *data)
extra_hosts++;
}
}
- if (extra_hosts && pcms->fw_cfg) {
+ if (extra_hosts && x86ms->fw_cfg) {
uint64_t *val = g_malloc(sizeof(*val));
*val = cpu_to_le64(extra_hosts);
- fw_cfg_add_file(pcms->fw_cfg,
+ fw_cfg_add_file(x86ms->fw_cfg,
"etc/extra-pci-roots", val, sizeof(*val));
}
}
acpi_setup();
- if (pcms->fw_cfg) {
- fw_cfg_build_smbios(MACHINE(pcms), pcms->fw_cfg);
- fw_cfg_build_feature_control(MACHINE(pcms), pcms->fw_cfg);
+ if (x86ms->fw_cfg) {
+ fw_cfg_build_smbios(MACHINE(pcms), x86ms->fw_cfg);
+ fw_cfg_build_feature_control(MACHINE(pcms), x86ms->fw_cfg);
/* update FW_CFG_NB_CPUS to account for -device added CPUs */
- fw_cfg_modify_i16(pcms->fw_cfg, FW_CFG_NB_CPUS, pcms->boot_cpus);
+ fw_cfg_modify_i16(x86ms->fw_cfg, FW_CFG_NB_CPUS, x86ms->boot_cpus);
}
- if (pcms->apic_id_limit > 255 && !xen_enabled()) {
+ if (x86ms->apic_id_limit > 255 && !xen_enabled()) {
IntelIOMMUState *iommu = INTEL_IOMMU_DEVICE(x86_iommu_get_default());
if (!iommu || !x86_iommu_ir_supported(X86_IOMMU_DEVICE(iommu)) ||
@@ -1588,8 +1089,9 @@ void pc_guest_info_init(PCMachineState *pcms)
{
int i;
MachineState *ms = MACHINE(pcms);
+ X86MachineState *x86ms = X86_MACHINE(pcms);
- pcms->apic_xrupt_override = kvm_allows_irq0_override();
+ x86ms->apic_xrupt_override = kvm_allows_irq0_override();
pcms->numa_nodes = ms->numa_state->num_nodes;
pcms->node_mem = g_malloc0(pcms->numa_nodes *
sizeof *pcms->node_mem);
@@ -1614,14 +1116,17 @@ void xen_load_linux(PCMachineState *pcms)
{
int i;
FWCfgState *fw_cfg;
+ PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms);
+ X86MachineState *x86ms = X86_MACHINE(pcms);
assert(MACHINE(pcms)->kernel_filename != NULL);
fw_cfg = fw_cfg_init_io(FW_CFG_IO_BASE);
- fw_cfg_add_i16(fw_cfg, FW_CFG_NB_CPUS, pcms->boot_cpus);
+ fw_cfg_add_i16(fw_cfg, FW_CFG_NB_CPUS, x86ms->boot_cpus);
rom_set_fw(fw_cfg);
- load_linux(pcms, fw_cfg);
+ x86_load_linux(x86ms, fw_cfg, pcmc->acpi_data_size,
+ pcmc->pvh_enabled, pcmc->linuxboot_dma_enabled);
for (i = 0; i < nb_option_roms; i++) {
assert(!strcmp(option_rom[i].name, "linuxboot.bin") ||
!strcmp(option_rom[i].name, "linuxboot_dma.bin") ||
@@ -1629,7 +1134,7 @@ void xen_load_linux(PCMachineState *pcms)
!strcmp(option_rom[i].name, "multiboot.bin"));
rom_add_option(option_rom[i].name, option_rom[i].bootindex);
}
- pcms->fw_cfg = fw_cfg;
+ x86ms->fw_cfg = fw_cfg;
}
void pc_memory_init(PCMachineState *pcms,
@@ -1644,9 +1149,10 @@ void pc_memory_init(PCMachineState *pcms,
MachineState *machine = MACHINE(pcms);
MachineClass *mc = MACHINE_GET_CLASS(machine);
PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms);
+ X86MachineState *x86ms = X86_MACHINE(pcms);
- assert(machine->ram_size == pcms->below_4g_mem_size +
- pcms->above_4g_mem_size);
+ assert(machine->ram_size == x86ms->below_4g_mem_size +
+ x86ms->above_4g_mem_size);
linux_boot = (machine->kernel_filename != NULL);
@@ -1660,17 +1166,17 @@ void pc_memory_init(PCMachineState *pcms,
*ram_memory = ram;
ram_below_4g = g_malloc(sizeof(*ram_below_4g));
memory_region_init_alias(ram_below_4g, NULL, "ram-below-4g", ram,
- 0, pcms->below_4g_mem_size);
+ 0, x86ms->below_4g_mem_size);
memory_region_add_subregion(system_memory, 0, ram_below_4g);
- e820_add_entry(0, pcms->below_4g_mem_size, E820_RAM);
- if (pcms->above_4g_mem_size > 0) {
+ e820_add_entry(0, x86ms->below_4g_mem_size, E820_RAM);
+ if (x86ms->above_4g_mem_size > 0) {
ram_above_4g = g_malloc(sizeof(*ram_above_4g));
memory_region_init_alias(ram_above_4g, NULL, "ram-above-4g", ram,
- pcms->below_4g_mem_size,
- pcms->above_4g_mem_size);
+ x86ms->below_4g_mem_size,
+ x86ms->above_4g_mem_size);
memory_region_add_subregion(system_memory, 0x100000000ULL,
ram_above_4g);
- e820_add_entry(0x100000000ULL, pcms->above_4g_mem_size, E820_RAM);
+ e820_add_entry(0x100000000ULL, x86ms->above_4g_mem_size, E820_RAM);
}
if (!pcmc->has_reserved_memory &&
@@ -1704,7 +1210,7 @@ void pc_memory_init(PCMachineState *pcms,
}
machine->device_memory->base =
- ROUND_UP(0x100000000ULL + pcms->above_4g_mem_size, 1 * GiB);
+ ROUND_UP(0x100000000ULL + x86ms->above_4g_mem_size, 1 * GiB);
if (pcmc->enforce_aligned_dimm) {
/* size device region assuming 1G page max alignment per slot */
@@ -1739,7 +1245,7 @@ void pc_memory_init(PCMachineState *pcms,
1);
fw_cfg = fw_cfg_arch_create(machine,
- pcms->boot_cpus, pcms->apic_id_limit);
+ x86ms->boot_cpus, x86ms->apic_id_limit);
rom_set_fw(fw_cfg);
@@ -1756,16 +1262,17 @@ void pc_memory_init(PCMachineState *pcms,
}
if (linux_boot) {
- load_linux(pcms, fw_cfg);
+ x86_load_linux(x86ms, fw_cfg, pcmc->acpi_data_size,
+ pcmc->pvh_enabled, pcmc->linuxboot_dma_enabled);
}
for (i = 0; i < nb_option_roms; i++) {
rom_add_option(option_rom[i].name, option_rom[i].bootindex);
}
- pcms->fw_cfg = fw_cfg;
+ x86ms->fw_cfg = fw_cfg;
/* Init default IOAPIC address space */
- pcms->ioapic_as = &address_space_memory;
+ x86ms->ioapic_as = &address_space_memory;
/* Init ACPI memory hotplug IO base address */
pcms->memhp_io_base = ACPI_MEMORY_HOTPLUG_BASE;
@@ -1780,6 +1287,7 @@ uint64_t pc_pci_hole64_start(void)
PCMachineState *pcms = PC_MACHINE(qdev_get_machine());
PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms);
MachineState *ms = MACHINE(pcms);
+ X86MachineState *x86ms = X86_MACHINE(pcms);
uint64_t hole64_start = 0;
if (pcmc->has_reserved_memory && ms->device_memory->base) {
@@ -1788,7 +1296,7 @@ uint64_t pc_pci_hole64_start(void)
hole64_start += memory_region_size(&ms->device_memory->mr);
}
} else {
- hole64_start = 0x100000000ULL + pcms->above_4g_mem_size;
+ hole64_start = 0x100000000ULL + x86ms->above_4g_mem_size;
}
return ROUND_UP(hole64_start, 1 * GiB);
@@ -1966,6 +1474,25 @@ void pc_nic_init(PCMachineClass *pcmc, ISABus *isa_bus, PCIBus *pci_bus)
rom_reset_order_override();
}
+void pc_i8259_create(ISABus *isa_bus, qemu_irq *i8259_irqs)
+{
+ qemu_irq *i8259;
+
+ if (kvm_pic_in_kernel()) {
+ i8259 = kvm_i8259_init(isa_bus);
+ } else if (xen_enabled()) {
+ i8259 = xen_interrupt_controller_init();
+ } else {
+ i8259 = i8259_init(isa_bus, pc_allocate_cpu_irq());
+ }
+
+ for (size_t i = 0; i < ISA_NUM_IRQS; i++) {
+ i8259_irqs[i] = i8259[i];
+ }
+
+ g_free(i8259);
+}
+
void ioapic_init_gsi(GSIState *gsi_state, const char *parent_name)
{
DeviceState *dev;
@@ -2127,6 +1654,7 @@ static void pc_cpu_plug(HotplugHandler *hotplug_dev,
Error *local_err = NULL;
X86CPU *cpu = X86_CPU(dev);
PCMachineState *pcms = PC_MACHINE(hotplug_dev);
+ X86MachineState *x86ms = X86_MACHINE(pcms);
if (pcms->acpi_dev) {
hotplug_handler_plug(HOTPLUG_HANDLER(pcms->acpi_dev), dev, &local_err);
@@ -2136,12 +1664,12 @@ static void pc_cpu_plug(HotplugHandler *hotplug_dev,
}
/* increment the number of CPUs */
- pcms->boot_cpus++;
- if (pcms->rtc) {
- rtc_set_cpus_count(pcms->rtc, pcms->boot_cpus);
+ x86ms->boot_cpus++;
+ if (x86ms->rtc) {
+ rtc_set_cpus_count(x86ms->rtc, x86ms->boot_cpus);
}
- if (pcms->fw_cfg) {
- fw_cfg_modify_i16(pcms->fw_cfg, FW_CFG_NB_CPUS, pcms->boot_cpus);
+ if (x86ms->fw_cfg) {
+ fw_cfg_modify_i16(x86ms->fw_cfg, FW_CFG_NB_CPUS, x86ms->boot_cpus);
}
found_cpu = pc_find_cpu_slot(MACHINE(pcms), cpu->apic_id, NULL);
@@ -2187,6 +1715,7 @@ static void pc_cpu_unplug_cb(HotplugHandler *hotplug_dev,
Error *local_err = NULL;
X86CPU *cpu = X86_CPU(dev);
PCMachineState *pcms = PC_MACHINE(hotplug_dev);
+ X86MachineState *x86ms = X86_MACHINE(pcms);
hotplug_handler_unplug(HOTPLUG_HANDLER(pcms->acpi_dev), dev, &local_err);
if (local_err) {
@@ -2198,10 +1727,10 @@ static void pc_cpu_unplug_cb(HotplugHandler *hotplug_dev,
object_property_set_bool(OBJECT(dev), false, "realized", NULL);
/* decrement the number of CPUs */
- pcms->boot_cpus--;
+ x86ms->boot_cpus--;
/* Update the number of CPUs in CMOS */
- rtc_set_cpus_count(pcms->rtc, pcms->boot_cpus);
- fw_cfg_modify_i16(pcms->fw_cfg, FW_CFG_NB_CPUS, pcms->boot_cpus);
+ rtc_set_cpus_count(x86ms->rtc, x86ms->boot_cpus);
+ fw_cfg_modify_i16(x86ms->fw_cfg, FW_CFG_NB_CPUS, x86ms->boot_cpus);
out:
error_propagate(errp, local_err);
}
@@ -2217,6 +1746,7 @@ static void pc_cpu_pre_plug(HotplugHandler *hotplug_dev,
CPUX86State *env = &cpu->env;
MachineState *ms = MACHINE(hotplug_dev);
PCMachineState *pcms = PC_MACHINE(hotplug_dev);
+ X86MachineState *x86ms = X86_MACHINE(pcms);
unsigned int smp_cores = ms->smp.cores;
unsigned int smp_threads = ms->smp.threads;
@@ -2226,7 +1756,7 @@ static void pc_cpu_pre_plug(HotplugHandler *hotplug_dev,
return;
}
- env->nr_dies = pcms->smp_dies;
+ env->nr_dies = x86ms->smp_dies;
/*
* If APIC ID is not set,
@@ -2234,13 +1764,13 @@ static void pc_cpu_pre_plug(HotplugHandler *hotplug_dev,
*/
if (cpu->apic_id == UNASSIGNED_APIC_ID) {
int max_socket = (ms->smp.max_cpus - 1) /
- smp_threads / smp_cores / pcms->smp_dies;
+ smp_threads / smp_cores / x86ms->smp_dies;
/*
* die-id was optional in QEMU 4.0 and older, so keep it optional
* if there's only one die per socket.
*/
- if (cpu->die_id < 0 && pcms->smp_dies == 1) {
+ if (cpu->die_id < 0 && x86ms->smp_dies == 1) {
cpu->die_id = 0;
}
@@ -2255,9 +1785,9 @@ static void pc_cpu_pre_plug(HotplugHandler *hotplug_dev,
if (cpu->die_id < 0) {
error_setg(errp, "CPU die-id is not set");
return;
- } else if (cpu->die_id > pcms->smp_dies - 1) {
+ } else if (cpu->die_id > x86ms->smp_dies - 1) {
error_setg(errp, "Invalid CPU die-id: %u must be in range 0:%u",
- cpu->die_id, pcms->smp_dies - 1);
+ cpu->die_id, x86ms->smp_dies - 1);
return;
}
if (cpu->core_id < 0) {
@@ -2281,7 +1811,7 @@ static void pc_cpu_pre_plug(HotplugHandler *hotplug_dev,
topo.die_id = cpu->die_id;
topo.core_id = cpu->core_id;
topo.smt_id = cpu->thread_id;
- cpu->apic_id = apicid_from_topo_ids(pcms->smp_dies, smp_cores,
+ cpu->apic_id = apicid_from_topo_ids(x86ms->smp_dies, smp_cores,
smp_threads, &topo);
}
@@ -2289,7 +1819,7 @@ static void pc_cpu_pre_plug(HotplugHandler *hotplug_dev,
if (!cpu_slot) {
MachineState *ms = MACHINE(pcms);
- x86_topo_ids_from_apicid(cpu->apic_id, pcms->smp_dies,
+ x86_topo_ids_from_apicid(cpu->apic_id, x86ms->smp_dies,
smp_cores, smp_threads, &topo);
error_setg(errp,
"Invalid CPU [socket: %u, die: %u, core: %u, thread: %u] with"
@@ -2311,7 +1841,7 @@ static void pc_cpu_pre_plug(HotplugHandler *hotplug_dev,
/* TODO: move socket_id/core_id/thread_id checks into x86_cpu_realizefn()
* once -smp refactoring is complete and there will be CPU private
* CPUState::nr_cores and CPUState::nr_threads fields instead of globals */
- x86_topo_ids_from_apicid(cpu->apic_id, pcms->smp_dies,
+ x86_topo_ids_from_apicid(cpu->apic_id, x86ms->smp_dies,
smp_cores, smp_threads, &topo);
if (cpu->socket_id != -1 && cpu->socket_id != topo.pkg_id) {
error_setg(errp, "property socket-id: %u doesn't match set apic-id:"
@@ -2493,45 +2023,6 @@ pc_machine_get_device_memory_region_size(Object *obj, Visitor *v,
visit_type_int(v, name, &value, errp);
}
-static void pc_machine_get_max_ram_below_4g(Object *obj, Visitor *v,
- const char *name, void *opaque,
- Error **errp)
-{
- PCMachineState *pcms = PC_MACHINE(obj);
- uint64_t value = pcms->max_ram_below_4g;
-
- visit_type_size(v, name, &value, errp);
-}
-
-static void pc_machine_set_max_ram_below_4g(Object *obj, Visitor *v,
- const char *name, void *opaque,
- Error **errp)
-{
- PCMachineState *pcms = PC_MACHINE(obj);
- Error *error = NULL;
- uint64_t value;
-
- visit_type_size(v, name, &value, &error);
- if (error) {
- error_propagate(errp, error);
- return;
- }
- if (value > 4 * GiB) {
- error_setg(&error,
- "Machine option 'max-ram-below-4g=%"PRIu64
- "' expects size less than or equal to 4G", value);
- error_propagate(errp, error);
- return;
- }
-
- if (value < 1 * MiB) {
- warn_report("Only %" PRIu64 " bytes of RAM below the 4GiB boundary,"
- "BIOS may not work with less than 1MiB", value);
- }
-
- pcms->max_ram_below_4g = value;
-}
-
static void pc_machine_get_vmport(Object *obj, Visitor *v, const char *name,
void *opaque, Error **errp)
{
@@ -2637,7 +2128,6 @@ static void pc_machine_initfn(Object *obj)
{
PCMachineState *pcms = PC_MACHINE(obj);
- pcms->max_ram_below_4g = 0; /* use default */
pcms->smm = ON_OFF_AUTO_AUTO;
#ifdef CONFIG_VMPORT
pcms->vmport = ON_OFF_AUTO_AUTO;
@@ -2649,7 +2139,6 @@ static void pc_machine_initfn(Object *obj)
pcms->smbus_enabled = true;
pcms->sata_enabled = true;
pcms->pit_enabled = true;
- pcms->smp_dies = 1;
pc_system_flash_create(pcms);
}
@@ -2680,86 +2169,6 @@ static void pc_machine_wakeup(MachineState *machine)
cpu_synchronize_all_post_reset();
}
-static CpuInstanceProperties
-pc_cpu_index_to_props(MachineState *ms, unsigned cpu_index)
-{
- MachineClass *mc = MACHINE_GET_CLASS(ms);
- const CPUArchIdList *possible_cpus = mc->possible_cpu_arch_ids(ms);
-
- assert(cpu_index < possible_cpus->len);
- return possible_cpus->cpus[cpu_index].props;
-}
-
-static int64_t pc_get_default_cpu_node_id(const MachineState *ms, int idx)
-{
- X86CPUTopoInfo topo;
- PCMachineState *pcms = PC_MACHINE(ms);
-
- assert(idx < ms->possible_cpus->len);
- x86_topo_ids_from_apicid(ms->possible_cpus->cpus[idx].arch_id,
- pcms->smp_dies, ms->smp.cores,
- ms->smp.threads, &topo);
- return topo.pkg_id % ms->numa_state->num_nodes;
-}
-
-static const CPUArchIdList *pc_possible_cpu_arch_ids(MachineState *ms)
-{
- PCMachineState *pcms = PC_MACHINE(ms);
- int i;
- unsigned int max_cpus = ms->smp.max_cpus;
-
- if (ms->possible_cpus) {
- /*
- * make sure that max_cpus hasn't changed since the first use, i.e.
- * -smp hasn't been parsed after it
- */
- assert(ms->possible_cpus->len == max_cpus);
- return ms->possible_cpus;
- }
-
- ms->possible_cpus = g_malloc0(sizeof(CPUArchIdList) +
- sizeof(CPUArchId) * max_cpus);
- ms->possible_cpus->len = max_cpus;
- for (i = 0; i < ms->possible_cpus->len; i++) {
- X86CPUTopoInfo topo;
-
- ms->possible_cpus->cpus[i].type = ms->cpu_type;
- ms->possible_cpus->cpus[i].vcpus_count = 1;
- ms->possible_cpus->cpus[i].arch_id = x86_cpu_apic_id_from_index(pcms, i);
- x86_topo_ids_from_apicid(ms->possible_cpus->cpus[i].arch_id,
- pcms->smp_dies, ms->smp.cores,
- ms->smp.threads, &topo);
- ms->possible_cpus->cpus[i].props.has_socket_id = true;
- ms->possible_cpus->cpus[i].props.socket_id = topo.pkg_id;
- if (pcms->smp_dies > 1) {
- ms->possible_cpus->cpus[i].props.has_die_id = true;
- ms->possible_cpus->cpus[i].props.die_id = topo.die_id;
- }
- ms->possible_cpus->cpus[i].props.has_core_id = true;
- ms->possible_cpus->cpus[i].props.core_id = topo.core_id;
- ms->possible_cpus->cpus[i].props.has_thread_id = true;
- ms->possible_cpus->cpus[i].props.thread_id = topo.smt_id;
- }
- return ms->possible_cpus;
-}
-
-static void x86_nmi(NMIState *n, int cpu_index, Error **errp)
-{
- /* cpu index isn't used */
- CPUState *cs;
-
- CPU_FOREACH(cs) {
- X86CPU *cpu = X86_CPU(cs);
-
- if (!cpu->apic_state) {
- cpu_interrupt(cs, CPU_INTERRUPT_NMI);
- } else {
- apic_deliver_nmi(cpu->apic_state);
- }
- }
-}
-
-
static bool pc_hotplug_allowed(MachineState *ms, DeviceState *dev, Error **errp)
{
X86IOMMUState *iommu = x86_iommu_get_default();
@@ -2784,7 +2193,6 @@ static void pc_machine_class_init(ObjectClass *oc, void *data)
MachineClass *mc = MACHINE_CLASS(oc);
PCMachineClass *pcmc = PC_MACHINE_CLASS(oc);
HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc);
- NMIClass *nc = NMI_CLASS(oc);
pcmc->pci_enabled = true;
pcmc->has_acpi_build = true;
@@ -2804,9 +2212,9 @@ static void pc_machine_class_init(ObjectClass *oc, void *data)
assert(!mc->get_hotplug_handler);
mc->get_hotplug_handler = pc_get_hotplug_handler;
mc->hotplug_allowed = pc_hotplug_allowed;
- mc->cpu_index_to_instance_props = pc_cpu_index_to_props;
- mc->get_default_cpu_node_id = pc_get_default_cpu_node_id;
- mc->possible_cpu_arch_ids = pc_possible_cpu_arch_ids;
+ mc->cpu_index_to_instance_props = x86_cpu_index_to_props;
+ mc->get_default_cpu_node_id = x86_get_default_cpu_node_id;
+ mc->possible_cpu_arch_ids = x86_possible_cpu_arch_ids;
mc->auto_enable_numa_with_memhp = true;
mc->has_hotpluggable_cpus = true;
mc->default_boot_order = "cad";
@@ -2820,7 +2228,6 @@ static void pc_machine_class_init(ObjectClass *oc, void *data)
hc->plug = pc_machine_device_plug_cb;
hc->unplug_request = pc_machine_device_unplug_request_cb;
hc->unplug = pc_machine_device_unplug_cb;
- nc->nmi_monitor_handler = x86_nmi;
mc->default_cpu_type = TARGET_DEFAULT_CPU_TYPE;
mc->nvdimm_supported = true;
mc->numa_mem_supported = true;
@@ -2829,13 +2236,6 @@ static void pc_machine_class_init(ObjectClass *oc, void *data)
pc_machine_get_device_memory_region_size, NULL,
NULL, NULL, &error_abort);
- object_class_property_add(oc, PC_MACHINE_MAX_RAM_BELOW_4G, "size",
- pc_machine_get_max_ram_below_4g, pc_machine_set_max_ram_below_4g,
- NULL, NULL, &error_abort);
-
- object_class_property_set_description(oc, PC_MACHINE_MAX_RAM_BELOW_4G,
- "Maximum ram below the 4G boundary (32bit boundary)", &error_abort);
-
object_class_property_add(oc, PC_MACHINE_SMM, "OnOffAuto",
pc_machine_get_smm, pc_machine_set_smm,
NULL, NULL, &error_abort);
@@ -2860,7 +2260,7 @@ static void pc_machine_class_init(ObjectClass *oc, void *data)
static const TypeInfo pc_machine_info = {
.name = TYPE_PC_MACHINE,
- .parent = TYPE_MACHINE,
+ .parent = TYPE_X86_MACHINE,
.abstract = true,
.instance_size = sizeof(PCMachineState),
.instance_init = pc_machine_initfn,
@@ -2868,7 +2268,6 @@ static const TypeInfo pc_machine_info = {
.class_init = pc_machine_class_init,
.interfaces = (InterfaceInfo[]) {
{ TYPE_HOTPLUG_HANDLER },
- { TYPE_NMI },
{ }
},
};
diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c
index 6824b72..3a4a64a 100644
--- a/hw/i386/pc_piix.c
+++ b/hw/i386/pc_piix.c
@@ -27,6 +27,7 @@
#include "qemu/units.h"
#include "hw/loader.h"
+#include "hw/i386/x86.h"
#include "hw/i386/pc.h"
#include "hw/i386/apic.h"
#include "hw/display/ramfb.h"
@@ -56,7 +57,6 @@
#endif
#include "migration/global_state.h"
#include "migration/misc.h"
-#include "kvm_i386.h"
#include "sysemu/numa.h"
#define MAX_IDE_BUS 2
@@ -73,6 +73,7 @@ static void pc_init1(MachineState *machine,
{
PCMachineState *pcms = PC_MACHINE(machine);
PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms);
+ X86MachineState *x86ms = X86_MACHINE(machine);
MemoryRegion *system_memory = get_system_memory();
MemoryRegion *system_io = get_system_io();
int i;
@@ -80,7 +81,6 @@ static void pc_init1(MachineState *machine,
ISABus *isa_bus;
PCII440FXState *i440fx_state;
int piix3_devfn = -1;
- qemu_irq *i8259;
qemu_irq smi_irq;
GSIState *gsi_state;
DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS];
@@ -125,11 +125,11 @@ static void pc_init1(MachineState *machine,
if (xen_enabled()) {
xen_hvm_init(pcms, &ram_memory);
} else {
- if (!pcms->max_ram_below_4g) {
- pcms->max_ram_below_4g = 0xe0000000; /* default: 3.5G */
+ if (!x86ms->max_ram_below_4g) {
+ x86ms->max_ram_below_4g = 0xe0000000; /* default: 3.5G */
}
- lowmem = pcms->max_ram_below_4g;
- if (machine->ram_size >= pcms->max_ram_below_4g) {
+ lowmem = x86ms->max_ram_below_4g;
+ if (machine->ram_size >= x86ms->max_ram_below_4g) {
if (pcmc->gigabyte_align) {
if (lowmem > 0xc0000000) {
lowmem = 0xc0000000;
@@ -138,21 +138,21 @@ static void pc_init1(MachineState *machine,
warn_report("Large machine and max_ram_below_4g "
"(%" PRIu64 ") not a multiple of 1G; "
"possible bad performance.",
- pcms->max_ram_below_4g);
+ x86ms->max_ram_below_4g);
}
}
}
if (machine->ram_size >= lowmem) {
- pcms->above_4g_mem_size = machine->ram_size - lowmem;
- pcms->below_4g_mem_size = lowmem;
+ x86ms->above_4g_mem_size = machine->ram_size - lowmem;
+ x86ms->below_4g_mem_size = lowmem;
} else {
- pcms->above_4g_mem_size = 0;
- pcms->below_4g_mem_size = machine->ram_size;
+ x86ms->above_4g_mem_size = 0;
+ x86ms->below_4g_mem_size = machine->ram_size;
}
}
- pc_cpus_init(pcms);
+ x86_cpus_init(x86ms, pcmc->default_cpu_version);
if (kvm_enabled() && pcmc->kvmclock_enabled) {
kvmclock_create();
@@ -187,22 +187,15 @@ static void pc_init1(MachineState *machine,
xen_load_linux(pcms);
}
- gsi_state = g_malloc0(sizeof(*gsi_state));
- if (kvm_ioapic_in_kernel()) {
- kvm_pc_setup_irq_routing(pcmc->pci_enabled);
- pcms->gsi = qemu_allocate_irqs(kvm_pc_gsi_handler, gsi_state,
- GSI_NUM_PINS);
- } else {
- pcms->gsi = qemu_allocate_irqs(gsi_handler, gsi_state, GSI_NUM_PINS);
- }
+ gsi_state = pc_gsi_create(&x86ms->gsi, pcmc->pci_enabled);
if (pcmc->pci_enabled) {
pci_bus = i440fx_init(host_type,
pci_type,
- &i440fx_state, &piix3_devfn, &isa_bus, pcms->gsi,
+ &i440fx_state, &piix3_devfn, &isa_bus, x86ms->gsi,
system_memory, system_io, machine->ram_size,
- pcms->below_4g_mem_size,
- pcms->above_4g_mem_size,
+ x86ms->below_4g_mem_size,
+ x86ms->above_4g_mem_size,
pci_memory, ram_memory);
pcms->bus = pci_bus;
} else {
@@ -212,25 +205,15 @@ static void pc_init1(MachineState *machine,
&error_abort);
no_hpet = 1;
}
- isa_bus_irqs(isa_bus, pcms->gsi);
+ isa_bus_irqs(isa_bus, x86ms->gsi);
- if (kvm_pic_in_kernel()) {
- i8259 = kvm_i8259_init(isa_bus);
- } else if (xen_enabled()) {
- i8259 = xen_interrupt_controller_init();
- } else {
- i8259 = i8259_init(isa_bus, pc_allocate_cpu_irq());
- }
+ pc_i8259_create(isa_bus, gsi_state->i8259_irq);
- for (i = 0; i < ISA_NUM_IRQS; i++) {
- gsi_state->i8259_irq[i] = i8259[i];
- }
- g_free(i8259);
if (pcmc->pci_enabled) {
ioapic_init_gsi(gsi_state, "i440fx");
}
- pc_register_ferr_irq(pcms->gsi[13]);
+ pc_register_ferr_irq(x86ms->gsi[13]);
pc_vga_init(isa_bus, pcmc->pci_enabled ? pci_bus : NULL);
@@ -240,7 +223,7 @@ static void pc_init1(MachineState *machine,
}
/* init basic PC hardware */
- pc_basic_device_init(isa_bus, pcms->gsi, &rtc_state, true,
+ pc_basic_device_init(isa_bus, x86ms->gsi, &rtc_state, true,
(pcms->vmport != ON_OFF_AUTO_ON), pcms->pit_enabled,
0x4);
@@ -287,7 +270,7 @@ else {
smi_irq = qemu_allocate_irq(pc_acpi_smi_interrupt, first_cpu, 0);
/* TODO: Populate SPD eeprom data. */
pcms->smbus = piix4_pm_init(pci_bus, piix3_devfn + 3, 0xb100,
- pcms->gsi[9], smi_irq,
+ x86ms->gsi[9], smi_irq,
pc_machine_is_smm_enabled(pcms),
&piix4_pm);
smbus_eeprom_init(pcms->smbus, 8, NULL, 0);
@@ -303,7 +286,7 @@ else {
if (machine->nvdimms_state->is_enabled) {
nvdimm_init_acpi_state(machine->nvdimms_state, system_io,
- pcms->fw_cfg, OBJECT(pcms));
+ x86ms->fw_cfg, OBJECT(pcms));
}
}
@@ -728,7 +711,7 @@ DEFINE_I440FX_MACHINE(v1_4, "pc-i440fx-1.4", pc_compat_1_4_fn,
static void pc_i440fx_1_3_machine_options(MachineClass *m)
{
- PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
+ X86MachineClass *x86mc = X86_MACHINE_CLASS(m);
static GlobalProperty compat[] = {
PC_CPU_MODEL_IDS("1.3.0")
{ "usb-tablet", "usb_version", "1" },
@@ -739,7 +722,7 @@ static void pc_i440fx_1_3_machine_options(MachineClass *m)
pc_i440fx_1_4_machine_options(m);
m->hw_version = "1.3.0";
- pcmc->compat_apic_id_mode = true;
+ x86mc->compat_apic_id_mode = true;
compat_props_add(m->compat_props, compat, G_N_ELEMENTS(compat));
}
diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c
index 748fc2e..d8b4c48 100644
--- a/hw/i386/pc_q35.c
+++ b/hw/i386/pc_q35.c
@@ -36,11 +36,11 @@
#include "hw/rtc/mc146818rtc.h"
#include "hw/xen/xen.h"
#include "sysemu/kvm.h"
-#include "kvm_i386.h"
#include "hw/kvm/clock.h"
#include "hw/pci-host/q35.h"
#include "hw/qdev-properties.h"
#include "exec/address-spaces.h"
+#include "hw/i386/x86.h"
#include "hw/i386/pc.h"
#include "hw/i386/ich9.h"
#include "hw/i386/amd_iommu.h"
@@ -115,6 +115,7 @@ static void pc_q35_init(MachineState *machine)
{
PCMachineState *pcms = PC_MACHINE(machine);
PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms);
+ X86MachineState *x86ms = X86_MACHINE(machine);
Q35PCIHost *q35_host;
PCIHostState *phb;
PCIBus *host_bus;
@@ -128,7 +129,6 @@ static void pc_q35_init(MachineState *machine)
MemoryRegion *ram_memory;
GSIState *gsi_state;
ISABus *isa_bus;
- qemu_irq *i8259;
int i;
ICH9LPCState *ich9_lpc;
PCIDevice *ahci;
@@ -152,34 +152,34 @@ static void pc_q35_init(MachineState *machine)
/* Handle the machine opt max-ram-below-4g. It is basically doing
* min(qemu limit, user limit).
*/
- if (!pcms->max_ram_below_4g) {
- pcms->max_ram_below_4g = 1ULL << 32; /* default: 4G */;
+ if (!x86ms->max_ram_below_4g) {
+ x86ms->max_ram_below_4g = 4 * GiB;
}
- if (lowmem > pcms->max_ram_below_4g) {
- lowmem = pcms->max_ram_below_4g;
+ if (lowmem > x86ms->max_ram_below_4g) {
+ lowmem = x86ms->max_ram_below_4g;
if (machine->ram_size - lowmem > lowmem &&
lowmem & (1 * GiB - 1)) {
warn_report("There is possibly poor performance as the ram size "
" (0x%" PRIx64 ") is more then twice the size of"
" max-ram-below-4g (%"PRIu64") and"
" max-ram-below-4g is not a multiple of 1G.",
- (uint64_t)machine->ram_size, pcms->max_ram_below_4g);
+ (uint64_t)machine->ram_size, x86ms->max_ram_below_4g);
}
}
if (machine->ram_size >= lowmem) {
- pcms->above_4g_mem_size = machine->ram_size - lowmem;
- pcms->below_4g_mem_size = lowmem;
+ x86ms->above_4g_mem_size = machine->ram_size - lowmem;
+ x86ms->below_4g_mem_size = lowmem;
} else {
- pcms->above_4g_mem_size = 0;
- pcms->below_4g_mem_size = machine->ram_size;
+ x86ms->above_4g_mem_size = 0;
+ x86ms->below_4g_mem_size = machine->ram_size;
}
if (xen_enabled()) {
xen_hvm_init(pcms, &ram_memory);
}
- pc_cpus_init(pcms);
+ x86_cpus_init(x86ms, pcmc->default_cpu_version);
kvmclock_create();
@@ -209,16 +209,6 @@ static void pc_q35_init(MachineState *machine)
rom_memory, &ram_memory);
}
- /* irq lines */
- gsi_state = g_malloc0(sizeof(*gsi_state));
- if (kvm_ioapic_in_kernel()) {
- kvm_pc_setup_irq_routing(pcmc->pci_enabled);
- pcms->gsi = qemu_allocate_irqs(kvm_pc_gsi_handler, gsi_state,
- GSI_NUM_PINS);
- } else {
- pcms->gsi = qemu_allocate_irqs(gsi_handler, gsi_state, GSI_NUM_PINS);
- }
-
/* create pci host bus */
q35_host = Q35_HOST_DEVICE(qdev_create(NULL, TYPE_Q35_HOST_DEVICE));
@@ -231,9 +221,9 @@ static void pc_q35_init(MachineState *machine)
MCH_HOST_PROP_SYSTEM_MEM, NULL);
object_property_set_link(OBJECT(q35_host), OBJECT(system_io),
MCH_HOST_PROP_IO_MEM, NULL);
- object_property_set_int(OBJECT(q35_host), pcms->below_4g_mem_size,
+ object_property_set_int(OBJECT(q35_host), x86ms->below_4g_mem_size,
PCI_HOST_BELOW_4G_MEM_SIZE, NULL);
- object_property_set_int(OBJECT(q35_host), pcms->above_4g_mem_size,
+ object_property_set_int(OBJECT(q35_host), x86ms->above_4g_mem_size,
PCI_HOST_ABOVE_4G_MEM_SIZE, NULL);
/* pci */
qdev_init_nofail(DEVICE(q35_host));
@@ -252,34 +242,26 @@ static void pc_q35_init(MachineState *machine)
object_property_set_link(OBJECT(machine), OBJECT(lpc),
PC_MACHINE_ACPI_DEVICE_PROP, &error_abort);
+ /* irq lines */
+ gsi_state = pc_gsi_create(&x86ms->gsi, pcmc->pci_enabled);
+
ich9_lpc = ICH9_LPC_DEVICE(lpc);
lpc_dev = DEVICE(lpc);
for (i = 0; i < GSI_NUM_PINS; i++) {
- qdev_connect_gpio_out_named(lpc_dev, ICH9_GPIO_GSI, i, pcms->gsi[i]);
+ qdev_connect_gpio_out_named(lpc_dev, ICH9_GPIO_GSI, i, x86ms->gsi[i]);
}
pci_bus_irqs(host_bus, ich9_lpc_set_irq, ich9_lpc_map_irq, ich9_lpc,
ICH9_LPC_NB_PIRQS);
pci_bus_set_route_irq_fn(host_bus, ich9_route_intx_pin_to_irq);
isa_bus = ich9_lpc->isa_bus;
- if (kvm_pic_in_kernel()) {
- i8259 = kvm_i8259_init(isa_bus);
- } else if (xen_enabled()) {
- i8259 = xen_interrupt_controller_init();
- } else {
- i8259 = i8259_init(isa_bus, pc_allocate_cpu_irq());
- }
-
- for (i = 0; i < ISA_NUM_IRQS; i++) {
- gsi_state->i8259_irq[i] = i8259[i];
- }
- g_free(i8259);
+ pc_i8259_create(isa_bus, gsi_state->i8259_irq);
if (pcmc->pci_enabled) {
ioapic_init_gsi(gsi_state, "q35");
}
- pc_register_ferr_irq(pcms->gsi[13]);
+ pc_register_ferr_irq(x86ms->gsi[13]);
assert(pcms->vmport != ON_OFF_AUTO__MAX);
if (pcms->vmport == ON_OFF_AUTO_AUTO) {
@@ -287,7 +269,7 @@ static void pc_q35_init(MachineState *machine)
}
/* init basic PC hardware */
- pc_basic_device_init(isa_bus, pcms->gsi, &rtc_state, !mc->no_floppy,
+ pc_basic_device_init(isa_bus, x86ms->gsi, &rtc_state, !mc->no_floppy,
(pcms->vmport != ON_OFF_AUTO_ON), pcms->pit_enabled,
0xff0104);
@@ -330,7 +312,7 @@ static void pc_q35_init(MachineState *machine)
if (machine->nvdimms_state->is_enabled) {
nvdimm_init_acpi_state(machine->nvdimms_state, system_io,
- pcms->fw_cfg, OBJECT(pcms));
+ x86ms->fw_cfg, OBJECT(pcms));
}
}
diff --git a/hw/i386/pc_sysfw.c b/hw/i386/pc_sysfw.c
index a9983f0..f5f3f46 100644
--- a/hw/i386/pc_sysfw.c
+++ b/hw/i386/pc_sysfw.c
@@ -31,6 +31,7 @@
#include "qemu/option.h"
#include "qemu/units.h"
#include "hw/sysbus.h"
+#include "hw/i386/x86.h"
#include "hw/i386/pc.h"
#include "hw/loader.h"
#include "hw/qdev-properties.h"
@@ -38,8 +39,6 @@
#include "hw/block/flash.h"
#include "sysemu/kvm.h"
-#define BIOS_FILENAME "bios.bin"
-
/*
* We don't have a theoretically justifiable exact lower bound on the base
* address of any flash mapping. In practice, the IO-APIC MMIO range is
@@ -211,59 +210,6 @@ static void pc_system_flash_map(PCMachineState *pcms,
}
}
-static void old_pc_system_rom_init(MemoryRegion *rom_memory, bool isapc_ram_fw)
-{
- char *filename;
- MemoryRegion *bios, *isa_bios;
- int bios_size, isa_bios_size;
- int ret;
-
- /* BIOS load */
- if (bios_name == NULL) {
- bios_name = BIOS_FILENAME;
- }
- filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
- if (filename) {
- bios_size = get_image_size(filename);
- } else {
- bios_size = -1;
- }
- if (bios_size <= 0 ||
- (bios_size % 65536) != 0) {
- goto bios_error;
- }
- bios = g_malloc(sizeof(*bios));
- memory_region_init_ram(bios, NULL, "pc.bios", bios_size, &error_fatal);
- if (!isapc_ram_fw) {
- memory_region_set_readonly(bios, true);
- }
- ret = rom_add_file_fixed(bios_name, (uint32_t)(-bios_size), -1);
- if (ret != 0) {
- bios_error:
- fprintf(stderr, "qemu: could not load PC BIOS '%s'\n", bios_name);
- exit(1);
- }
- g_free(filename);
-
- /* map the last 128KB of the BIOS in ISA space */
- isa_bios_size = MIN(bios_size, 128 * KiB);
- isa_bios = g_malloc(sizeof(*isa_bios));
- memory_region_init_alias(isa_bios, NULL, "isa-bios", bios,
- bios_size - isa_bios_size, isa_bios_size);
- memory_region_add_subregion_overlap(rom_memory,
- 0x100000 - isa_bios_size,
- isa_bios,
- 1);
- if (!isapc_ram_fw) {
- memory_region_set_readonly(isa_bios, true);
- }
-
- /* map all the bios at the top of memory */
- memory_region_add_subregion(rom_memory,
- (uint32_t)(-bios_size),
- bios);
-}
-
void pc_system_firmware_init(PCMachineState *pcms,
MemoryRegion *rom_memory)
{
@@ -272,7 +218,7 @@ void pc_system_firmware_init(PCMachineState *pcms,
BlockBackend *pflash_blk[ARRAY_SIZE(pcms->flash)];
if (!pcmc->pci_enabled) {
- old_pc_system_rom_init(rom_memory, true);
+ x86_bios_rom_init(rom_memory, true);
return;
}
@@ -293,7 +239,7 @@ void pc_system_firmware_init(PCMachineState *pcms,
if (!pflash_blk[0]) {
/* Machine property pflash0 not set, use ROM mode */
- old_pc_system_rom_init(rom_memory, false);
+ x86_bios_rom_init(rom_memory, false);
} else {
if (kvm_enabled() && !kvm_readonly_mem_enabled()) {
/*
diff --git a/hw/i386/x86.c b/hw/i386/x86.c
new file mode 100644
index 0000000..fd84b23
--- /dev/null
+++ b/hw/i386/x86.c
@@ -0,0 +1,795 @@
+/*
+ * Copyright (c) 2003-2004 Fabrice Bellard
+ * Copyright (c) 2019 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * 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 "qemu/osdep.h"
+#include "qemu/error-report.h"
+#include "qemu/option.h"
+#include "qemu/cutils.h"
+#include "qemu/units.h"
+#include "qemu-common.h"
+#include "qapi/error.h"
+#include "qapi/qmp/qerror.h"
+#include "qapi/qapi-visit-common.h"
+#include "qapi/visitor.h"
+#include "sysemu/qtest.h"
+#include "sysemu/numa.h"
+#include "sysemu/replay.h"
+#include "sysemu/sysemu.h"
+
+#include "hw/i386/x86.h"
+#include "target/i386/cpu.h"
+#include "hw/i386/topology.h"
+#include "hw/i386/fw_cfg.h"
+
+#include "hw/acpi/cpu_hotplug.h"
+#include "hw/nmi.h"
+#include "hw/loader.h"
+#include "multiboot.h"
+#include "elf.h"
+#include "standard-headers/asm-x86/bootparam.h"
+
+#define BIOS_FILENAME "bios.bin"
+
+/* Physical Address of PVH entry point read from kernel ELF NOTE */
+static size_t pvh_start_addr;
+
+/*
+ * Calculates initial APIC ID for a specific CPU index
+ *
+ * Currently we need to be able to calculate the APIC ID from the CPU index
+ * alone (without requiring a CPU object), as the QEMU<->Seabios interfaces have
+ * no concept of "CPU index", and the NUMA tables on fw_cfg need the APIC ID of
+ * all CPUs up to max_cpus.
+ */
+uint32_t x86_cpu_apic_id_from_index(X86MachineState *x86ms,
+ unsigned int cpu_index)
+{
+ MachineState *ms = MACHINE(x86ms);
+ X86MachineClass *x86mc = X86_MACHINE_GET_CLASS(x86ms);
+ uint32_t correct_id;
+ static bool warned;
+
+ correct_id = x86_apicid_from_cpu_idx(x86ms->smp_dies, ms->smp.cores,
+ ms->smp.threads, cpu_index);
+ if (x86mc->compat_apic_id_mode) {
+ if (cpu_index != correct_id && !warned && !qtest_enabled()) {
+ error_report("APIC IDs set in compatibility mode, "
+ "CPU topology won't match the configuration");
+ warned = true;
+ }
+ return cpu_index;
+ } else {
+ return correct_id;
+ }
+}
+
+
+void x86_cpu_new(X86MachineState *x86ms, int64_t apic_id, Error **errp)
+{
+ Object *cpu = NULL;
+ Error *local_err = NULL;
+ CPUX86State *env = NULL;
+
+ cpu = object_new(MACHINE(x86ms)->cpu_type);
+
+ env = &X86_CPU(cpu)->env;
+ env->nr_dies = x86ms->smp_dies;
+
+ object_property_set_uint(cpu, apic_id, "apic-id", &local_err);
+ object_property_set_bool(cpu, true, "realized", &local_err);
+
+ object_unref(cpu);
+ error_propagate(errp, local_err);
+}
+
+void x86_cpus_init(X86MachineState *x86ms, int default_cpu_version)
+{
+ int i;
+ const CPUArchIdList *possible_cpus;
+ MachineState *ms = MACHINE(x86ms);
+ MachineClass *mc = MACHINE_GET_CLASS(x86ms);
+
+ x86_cpu_set_default_version(default_cpu_version);
+
+ /*
+ * Calculates the limit to CPU APIC ID values
+ *
+ * Limit for the APIC ID value, so that all
+ * CPU APIC IDs are < x86ms->apic_id_limit.
+ *
+ * This is used for FW_CFG_MAX_CPUS. See comments on fw_cfg_arch_create().
+ */
+ x86ms->apic_id_limit = x86_cpu_apic_id_from_index(x86ms,
+ ms->smp.max_cpus - 1) + 1;
+ possible_cpus = mc->possible_cpu_arch_ids(ms);
+ for (i = 0; i < ms->smp.cpus; i++) {
+ x86_cpu_new(x86ms, possible_cpus->cpus[i].arch_id, &error_fatal);
+ }
+}
+
+CpuInstanceProperties
+x86_cpu_index_to_props(MachineState *ms, unsigned cpu_index)
+{
+ MachineClass *mc = MACHINE_GET_CLASS(ms);
+ const CPUArchIdList *possible_cpus = mc->possible_cpu_arch_ids(ms);
+
+ assert(cpu_index < possible_cpus->len);
+ return possible_cpus->cpus[cpu_index].props;
+}
+
+int64_t x86_get_default_cpu_node_id(const MachineState *ms, int idx)
+{
+ X86CPUTopoInfo topo;
+ X86MachineState *x86ms = X86_MACHINE(ms);
+
+ assert(idx < ms->possible_cpus->len);
+ x86_topo_ids_from_apicid(ms->possible_cpus->cpus[idx].arch_id,
+ x86ms->smp_dies, ms->smp.cores,
+ ms->smp.threads, &topo);
+ return topo.pkg_id % ms->numa_state->num_nodes;
+}
+
+const CPUArchIdList *x86_possible_cpu_arch_ids(MachineState *ms)
+{
+ X86MachineState *x86ms = X86_MACHINE(ms);
+ int i;
+ unsigned int max_cpus = ms->smp.max_cpus;
+
+ if (ms->possible_cpus) {
+ /*
+ * make sure that max_cpus hasn't changed since the first use, i.e.
+ * -smp hasn't been parsed after it
+ */
+ assert(ms->possible_cpus->len == max_cpus);
+ return ms->possible_cpus;
+ }
+
+ ms->possible_cpus = g_malloc0(sizeof(CPUArchIdList) +
+ sizeof(CPUArchId) * max_cpus);
+ ms->possible_cpus->len = max_cpus;
+ for (i = 0; i < ms->possible_cpus->len; i++) {
+ X86CPUTopoInfo topo;
+
+ ms->possible_cpus->cpus[i].type = ms->cpu_type;
+ ms->possible_cpus->cpus[i].vcpus_count = 1;
+ ms->possible_cpus->cpus[i].arch_id =
+ x86_cpu_apic_id_from_index(x86ms, i);
+ x86_topo_ids_from_apicid(ms->possible_cpus->cpus[i].arch_id,
+ x86ms->smp_dies, ms->smp.cores,
+ ms->smp.threads, &topo);
+ ms->possible_cpus->cpus[i].props.has_socket_id = true;
+ ms->possible_cpus->cpus[i].props.socket_id = topo.pkg_id;
+ if (x86ms->smp_dies > 1) {
+ ms->possible_cpus->cpus[i].props.has_die_id = true;
+ ms->possible_cpus->cpus[i].props.die_id = topo.die_id;
+ }
+ ms->possible_cpus->cpus[i].props.has_core_id = true;
+ ms->possible_cpus->cpus[i].props.core_id = topo.core_id;
+ ms->possible_cpus->cpus[i].props.has_thread_id = true;
+ ms->possible_cpus->cpus[i].props.thread_id = topo.smt_id;
+ }
+ return ms->possible_cpus;
+}
+
+static void x86_nmi(NMIState *n, int cpu_index, Error **errp)
+{
+ /* cpu index isn't used */
+ CPUState *cs;
+
+ CPU_FOREACH(cs) {
+ X86CPU *cpu = X86_CPU(cs);
+
+ if (!cpu->apic_state) {
+ cpu_interrupt(cs, CPU_INTERRUPT_NMI);
+ } else {
+ apic_deliver_nmi(cpu->apic_state);
+ }
+ }
+}
+
+static long get_file_size(FILE *f)
+{
+ long where, size;
+
+ /* XXX: on Unix systems, using fstat() probably makes more sense */
+
+ where = ftell(f);
+ fseek(f, 0, SEEK_END);
+ size = ftell(f);
+ fseek(f, where, SEEK_SET);
+
+ return size;
+}
+
+struct setup_data {
+ uint64_t next;
+ uint32_t type;
+ uint32_t len;
+ uint8_t data[0];
+} __attribute__((packed));
+
+
+/*
+ * The entry point into the kernel for PVH boot is different from
+ * the native entry point. The PVH entry is defined by the x86/HVM
+ * direct boot ABI and is available in an ELFNOTE in the kernel binary.
+ *
+ * This function is passed to load_elf() when it is called from
+ * load_elfboot() which then additionally checks for an ELF Note of
+ * type XEN_ELFNOTE_PHYS32_ENTRY and passes it to this function to
+ * parse the PVH entry address from the ELF Note.
+ *
+ * Due to trickery in elf_opts.h, load_elf() is actually available as
+ * load_elf32() or load_elf64() and this routine needs to be able
+ * to deal with being called as 32 or 64 bit.
+ *
+ * The address of the PVH entry point is saved to the 'pvh_start_addr'
+ * global variable. (although the entry point is 32-bit, the kernel
+ * binary can be either 32-bit or 64-bit).
+ */
+static uint64_t read_pvh_start_addr(void *arg1, void *arg2, bool is64)
+{
+ size_t *elf_note_data_addr;
+
+ /* Check if ELF Note header passed in is valid */
+ if (arg1 == NULL) {
+ return 0;
+ }
+
+ if (is64) {
+ struct elf64_note *nhdr64 = (struct elf64_note *)arg1;
+ uint64_t nhdr_size64 = sizeof(struct elf64_note);
+ uint64_t phdr_align = *(uint64_t *)arg2;
+ uint64_t nhdr_namesz = nhdr64->n_namesz;
+
+ elf_note_data_addr =
+ ((void *)nhdr64) + nhdr_size64 +
+ QEMU_ALIGN_UP(nhdr_namesz, phdr_align);
+ } else {
+ struct elf32_note *nhdr32 = (struct elf32_note *)arg1;
+ uint32_t nhdr_size32 = sizeof(struct elf32_note);
+ uint32_t phdr_align = *(uint32_t *)arg2;
+ uint32_t nhdr_namesz = nhdr32->n_namesz;
+
+ elf_note_data_addr =
+ ((void *)nhdr32) + nhdr_size32 +
+ QEMU_ALIGN_UP(nhdr_namesz, phdr_align);
+ }
+
+ pvh_start_addr = *elf_note_data_addr;
+
+ return pvh_start_addr;
+}
+
+static bool load_elfboot(const char *kernel_filename,
+ int kernel_file_size,
+ uint8_t *header,
+ size_t pvh_xen_start_addr,
+ FWCfgState *fw_cfg)
+{
+ uint32_t flags = 0;
+ uint32_t mh_load_addr = 0;
+ uint32_t elf_kernel_size = 0;
+ uint64_t elf_entry;
+ uint64_t elf_low, elf_high;
+ int kernel_size;
+
+ if (ldl_p(header) != 0x464c457f) {
+ return false; /* no elfboot */
+ }
+
+ bool elf_is64 = header[EI_CLASS] == ELFCLASS64;
+ flags = elf_is64 ?
+ ((Elf64_Ehdr *)header)->e_flags : ((Elf32_Ehdr *)header)->e_flags;
+
+ if (flags & 0x00010004) { /* LOAD_ELF_HEADER_HAS_ADDR */
+ error_report("elfboot unsupported flags = %x", flags);
+ exit(1);
+ }
+
+ uint64_t elf_note_type = XEN_ELFNOTE_PHYS32_ENTRY;
+ kernel_size = load_elf(kernel_filename, read_pvh_start_addr,
+ NULL, &elf_note_type, &elf_entry,
+ &elf_low, &elf_high, 0, I386_ELF_MACHINE,
+ 0, 0);
+
+ if (kernel_size < 0) {
+ error_report("Error while loading elf kernel");
+ exit(1);
+ }
+ mh_load_addr = elf_low;
+ elf_kernel_size = elf_high - elf_low;
+
+ if (pvh_start_addr == 0) {
+ error_report("Error loading uncompressed kernel without PVH ELF Note");
+ exit(1);
+ }
+ fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ENTRY, pvh_start_addr);
+ fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ADDR, mh_load_addr);
+ fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_SIZE, elf_kernel_size);
+
+ return true;
+}
+
+void x86_load_linux(X86MachineState *x86ms,
+ FWCfgState *fw_cfg,
+ int acpi_data_size,
+ bool pvh_enabled,
+ bool linuxboot_dma_enabled)
+{
+ uint16_t protocol;
+ int setup_size, kernel_size, cmdline_size;
+ int dtb_size, setup_data_offset;
+ uint32_t initrd_max;
+ uint8_t header[8192], *setup, *kernel;
+ hwaddr real_addr, prot_addr, cmdline_addr, initrd_addr = 0;
+ FILE *f;
+ char *vmode;
+ MachineState *machine = MACHINE(x86ms);
+ struct setup_data *setup_data;
+ const char *kernel_filename = machine->kernel_filename;
+ const char *initrd_filename = machine->initrd_filename;
+ const char *dtb_filename = machine->dtb;
+ const char *kernel_cmdline = machine->kernel_cmdline;
+
+ /* Align to 16 bytes as a paranoia measure */
+ cmdline_size = (strlen(kernel_cmdline) + 16) & ~15;
+
+ /* load the kernel header */
+ f = fopen(kernel_filename, "rb");
+ if (!f) {
+ fprintf(stderr, "qemu: could not open kernel file '%s': %s\n",
+ kernel_filename, strerror(errno));
+ exit(1);
+ }
+
+ kernel_size = get_file_size(f);
+ if (!kernel_size ||
+ fread(header, 1, MIN(ARRAY_SIZE(header), kernel_size), f) !=
+ MIN(ARRAY_SIZE(header), kernel_size)) {
+ fprintf(stderr, "qemu: could not load kernel '%s': %s\n",
+ kernel_filename, strerror(errno));
+ exit(1);
+ }
+
+ /* kernel protocol version */
+ if (ldl_p(header + 0x202) == 0x53726448) {
+ protocol = lduw_p(header + 0x206);
+ } else {
+ /*
+ * This could be a multiboot kernel. If it is, let's stop treating it
+ * like a Linux kernel.
+ * Note: some multiboot images could be in the ELF format (the same of
+ * PVH), so we try multiboot first since we check the multiboot magic
+ * header before to load it.
+ */
+ if (load_multiboot(fw_cfg, f, kernel_filename, initrd_filename,
+ kernel_cmdline, kernel_size, header)) {
+ return;
+ }
+ /*
+ * Check if the file is an uncompressed kernel file (ELF) and load it,
+ * saving the PVH entry point used by the x86/HVM direct boot ABI.
+ * If load_elfboot() is successful, populate the fw_cfg info.
+ */
+ if (pvh_enabled &&
+ load_elfboot(kernel_filename, kernel_size,
+ header, pvh_start_addr, fw_cfg)) {
+ fclose(f);
+
+ fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE,
+ strlen(kernel_cmdline) + 1);
+ fw_cfg_add_string(fw_cfg, FW_CFG_CMDLINE_DATA, kernel_cmdline);
+
+ fw_cfg_add_i32(fw_cfg, FW_CFG_SETUP_SIZE, sizeof(header));
+ fw_cfg_add_bytes(fw_cfg, FW_CFG_SETUP_DATA,
+ header, sizeof(header));
+
+ /* load initrd */
+ if (initrd_filename) {
+ GMappedFile *mapped_file;
+ gsize initrd_size;
+ gchar *initrd_data;
+ GError *gerr = NULL;
+
+ mapped_file = g_mapped_file_new(initrd_filename, false, &gerr);
+ if (!mapped_file) {
+ fprintf(stderr, "qemu: error reading initrd %s: %s\n",
+ initrd_filename, gerr->message);
+ exit(1);
+ }
+ x86ms->initrd_mapped_file = mapped_file;
+
+ initrd_data = g_mapped_file_get_contents(mapped_file);
+ initrd_size = g_mapped_file_get_length(mapped_file);
+ initrd_max = x86ms->below_4g_mem_size - acpi_data_size - 1;
+ if (initrd_size >= initrd_max) {
+ fprintf(stderr, "qemu: initrd is too large, cannot support."
+ "(max: %"PRIu32", need %"PRId64")\n",
+ initrd_max, (uint64_t)initrd_size);
+ exit(1);
+ }
+
+ initrd_addr = (initrd_max - initrd_size) & ~4095;
+
+ fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_ADDR, initrd_addr);
+ fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_SIZE, initrd_size);
+ fw_cfg_add_bytes(fw_cfg, FW_CFG_INITRD_DATA, initrd_data,
+ initrd_size);
+ }
+
+ option_rom[nb_option_roms].bootindex = 0;
+ option_rom[nb_option_roms].name = "pvh.bin";
+ nb_option_roms++;
+
+ return;
+ }
+ protocol = 0;
+ }
+
+ if (protocol < 0x200 || !(header[0x211] & 0x01)) {
+ /* Low kernel */
+ real_addr = 0x90000;
+ cmdline_addr = 0x9a000 - cmdline_size;
+ prot_addr = 0x10000;
+ } else if (protocol < 0x202) {
+ /* High but ancient kernel */
+ real_addr = 0x90000;
+ cmdline_addr = 0x9a000 - cmdline_size;
+ prot_addr = 0x100000;
+ } else {
+ /* High and recent kernel */
+ real_addr = 0x10000;
+ cmdline_addr = 0x20000;
+ prot_addr = 0x100000;
+ }
+
+ /* highest address for loading the initrd */
+ if (protocol >= 0x20c &&
+ lduw_p(header + 0x236) & XLF_CAN_BE_LOADED_ABOVE_4G) {
+ /*
+ * Linux has supported initrd up to 4 GB for a very long time (2007,
+ * long before XLF_CAN_BE_LOADED_ABOVE_4G which was added in 2013),
+ * though it only sets initrd_max to 2 GB to "work around bootloader
+ * bugs". Luckily, QEMU firmware(which does something like bootloader)
+ * has supported this.
+ *
+ * It's believed that if XLF_CAN_BE_LOADED_ABOVE_4G is set, initrd can
+ * be loaded into any address.
+ *
+ * In addition, initrd_max is uint32_t simply because QEMU doesn't
+ * support the 64-bit boot protocol (specifically the ext_ramdisk_image
+ * field).
+ *
+ * Therefore here just limit initrd_max to UINT32_MAX simply as well.
+ */
+ initrd_max = UINT32_MAX;
+ } else if (protocol >= 0x203) {
+ initrd_max = ldl_p(header + 0x22c);
+ } else {
+ initrd_max = 0x37ffffff;
+ }
+
+ if (initrd_max >= x86ms->below_4g_mem_size - acpi_data_size) {
+ initrd_max = x86ms->below_4g_mem_size - acpi_data_size - 1;
+ }
+
+ fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_ADDR, cmdline_addr);
+ fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE, strlen(kernel_cmdline) + 1);
+ fw_cfg_add_string(fw_cfg, FW_CFG_CMDLINE_DATA, kernel_cmdline);
+
+ if (protocol >= 0x202) {
+ stl_p(header + 0x228, cmdline_addr);
+ } else {
+ stw_p(header + 0x20, 0xA33F);
+ stw_p(header + 0x22, cmdline_addr - real_addr);
+ }
+
+ /* handle vga= parameter */
+ vmode = strstr(kernel_cmdline, "vga=");
+ if (vmode) {
+ unsigned int video_mode;
+ int ret;
+ /* skip "vga=" */
+ vmode += 4;
+ if (!strncmp(vmode, "normal", 6)) {
+ video_mode = 0xffff;
+ } else if (!strncmp(vmode, "ext", 3)) {
+ video_mode = 0xfffe;
+ } else if (!strncmp(vmode, "ask", 3)) {
+ video_mode = 0xfffd;
+ } else {
+ ret = qemu_strtoui(vmode, NULL, 0, &video_mode);
+ if (ret != 0) {
+ fprintf(stderr, "qemu: can't parse 'vga' parameter: %s\n",
+ strerror(-ret));
+ exit(1);
+ }
+ }
+ stw_p(header + 0x1fa, video_mode);
+ }
+
+ /* loader type */
+ /*
+ * High nybble = B reserved for QEMU; low nybble is revision number.
+ * If this code is substantially changed, you may want to consider
+ * incrementing the revision.
+ */
+ if (protocol >= 0x200) {
+ header[0x210] = 0xB0;
+ }
+ /* heap */
+ if (protocol >= 0x201) {
+ header[0x211] |= 0x80; /* CAN_USE_HEAP */
+ stw_p(header + 0x224, cmdline_addr - real_addr - 0x200);
+ }
+
+ /* load initrd */
+ if (initrd_filename) {
+ GMappedFile *mapped_file;
+ gsize initrd_size;
+ gchar *initrd_data;
+ GError *gerr = NULL;
+
+ if (protocol < 0x200) {
+ fprintf(stderr, "qemu: linux kernel too old to load a ram disk\n");
+ exit(1);
+ }
+
+ mapped_file = g_mapped_file_new(initrd_filename, false, &gerr);
+ if (!mapped_file) {
+ fprintf(stderr, "qemu: error reading initrd %s: %s\n",
+ initrd_filename, gerr->message);
+ exit(1);
+ }
+ x86ms->initrd_mapped_file = mapped_file;
+
+ initrd_data = g_mapped_file_get_contents(mapped_file);
+ initrd_size = g_mapped_file_get_length(mapped_file);
+ if (initrd_size >= initrd_max) {
+ fprintf(stderr, "qemu: initrd is too large, cannot support."
+ "(max: %"PRIu32", need %"PRId64")\n",
+ initrd_max, (uint64_t)initrd_size);
+ exit(1);
+ }
+
+ initrd_addr = (initrd_max - initrd_size) & ~4095;
+
+ fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_ADDR, initrd_addr);
+ fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_SIZE, initrd_size);
+ fw_cfg_add_bytes(fw_cfg, FW_CFG_INITRD_DATA, initrd_data, initrd_size);
+
+ stl_p(header + 0x218, initrd_addr);
+ stl_p(header + 0x21c, initrd_size);
+ }
+
+ /* load kernel and setup */
+ setup_size = header[0x1f1];
+ if (setup_size == 0) {
+ setup_size = 4;
+ }
+ setup_size = (setup_size + 1) * 512;
+ if (setup_size > kernel_size) {
+ fprintf(stderr, "qemu: invalid kernel header\n");
+ exit(1);
+ }
+ kernel_size -= setup_size;
+
+ setup = g_malloc(setup_size);
+ kernel = g_malloc(kernel_size);
+ fseek(f, 0, SEEK_SET);
+ if (fread(setup, 1, setup_size, f) != setup_size) {
+ fprintf(stderr, "fread() failed\n");
+ exit(1);
+ }
+ if (fread(kernel, 1, kernel_size, f) != kernel_size) {
+ fprintf(stderr, "fread() failed\n");
+ exit(1);
+ }
+ fclose(f);
+
+ /* append dtb to kernel */
+ if (dtb_filename) {
+ if (protocol < 0x209) {
+ fprintf(stderr, "qemu: Linux kernel too old to load a dtb\n");
+ exit(1);
+ }
+
+ dtb_size = get_image_size(dtb_filename);
+ if (dtb_size <= 0) {
+ fprintf(stderr, "qemu: error reading dtb %s: %s\n",
+ dtb_filename, strerror(errno));
+ exit(1);
+ }
+
+ setup_data_offset = QEMU_ALIGN_UP(kernel_size, 16);
+ kernel_size = setup_data_offset + sizeof(struct setup_data) + dtb_size;
+ kernel = g_realloc(kernel, kernel_size);
+
+ stq_p(header + 0x250, prot_addr + setup_data_offset);
+
+ setup_data = (struct setup_data *)(kernel + setup_data_offset);
+ setup_data->next = 0;
+ setup_data->type = cpu_to_le32(SETUP_DTB);
+ setup_data->len = cpu_to_le32(dtb_size);
+
+ load_image_size(dtb_filename, setup_data->data, dtb_size);
+ }
+
+ memcpy(setup, header, MIN(sizeof(header), setup_size));
+
+ fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ADDR, prot_addr);
+ fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_SIZE, kernel_size);
+ fw_cfg_add_bytes(fw_cfg, FW_CFG_KERNEL_DATA, kernel, kernel_size);
+
+ fw_cfg_add_i32(fw_cfg, FW_CFG_SETUP_ADDR, real_addr);
+ fw_cfg_add_i32(fw_cfg, FW_CFG_SETUP_SIZE, setup_size);
+ fw_cfg_add_bytes(fw_cfg, FW_CFG_SETUP_DATA, setup, setup_size);
+
+ option_rom[nb_option_roms].bootindex = 0;
+ option_rom[nb_option_roms].name = "linuxboot.bin";
+ if (linuxboot_dma_enabled && fw_cfg_dma_enabled(fw_cfg)) {
+ option_rom[nb_option_roms].name = "linuxboot_dma.bin";
+ }
+ nb_option_roms++;
+}
+
+void x86_bios_rom_init(MemoryRegion *rom_memory, bool isapc_ram_fw)
+{
+ char *filename;
+ MemoryRegion *bios, *isa_bios;
+ int bios_size, isa_bios_size;
+ int ret;
+
+ /* BIOS load */
+ if (bios_name == NULL) {
+ bios_name = BIOS_FILENAME;
+ }
+ filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
+ if (filename) {
+ bios_size = get_image_size(filename);
+ } else {
+ bios_size = -1;
+ }
+ if (bios_size <= 0 ||
+ (bios_size % 65536) != 0) {
+ goto bios_error;
+ }
+ bios = g_malloc(sizeof(*bios));
+ memory_region_init_ram(bios, NULL, "pc.bios", bios_size, &error_fatal);
+ if (!isapc_ram_fw) {
+ memory_region_set_readonly(bios, true);
+ }
+ ret = rom_add_file_fixed(bios_name, (uint32_t)(-bios_size), -1);
+ if (ret != 0) {
+ bios_error:
+ fprintf(stderr, "qemu: could not load PC BIOS '%s'\n", bios_name);
+ exit(1);
+ }
+ g_free(filename);
+
+ /* map the last 128KB of the BIOS in ISA space */
+ isa_bios_size = MIN(bios_size, 128 * KiB);
+ isa_bios = g_malloc(sizeof(*isa_bios));
+ memory_region_init_alias(isa_bios, NULL, "isa-bios", bios,
+ bios_size - isa_bios_size, isa_bios_size);
+ memory_region_add_subregion_overlap(rom_memory,
+ 0x100000 - isa_bios_size,
+ isa_bios,
+ 1);
+ if (!isapc_ram_fw) {
+ memory_region_set_readonly(isa_bios, true);
+ }
+
+ /* map all the bios at the top of memory */
+ memory_region_add_subregion(rom_memory,
+ (uint32_t)(-bios_size),
+ bios);
+}
+
+static void x86_machine_get_max_ram_below_4g(Object *obj, Visitor *v,
+ const char *name, void *opaque,
+ Error **errp)
+{
+ X86MachineState *x86ms = X86_MACHINE(obj);
+ uint64_t value = x86ms->max_ram_below_4g;
+
+ visit_type_size(v, name, &value, errp);
+}
+
+static void x86_machine_set_max_ram_below_4g(Object *obj, Visitor *v,
+ const char *name, void *opaque,
+ Error **errp)
+{
+ X86MachineState *x86ms = X86_MACHINE(obj);
+ Error *error = NULL;
+ uint64_t value;
+
+ visit_type_size(v, name, &value, &error);
+ if (error) {
+ error_propagate(errp, error);
+ return;
+ }
+ if (value > 4 * GiB) {
+ error_setg(&error,
+ "Machine option 'max-ram-below-4g=%"PRIu64
+ "' expects size less than or equal to 4G", value);
+ error_propagate(errp, error);
+ return;
+ }
+
+ if (value < 1 * MiB) {
+ warn_report("Only %" PRIu64 " bytes of RAM below the 4GiB boundary,"
+ "BIOS may not work with less than 1MiB", value);
+ }
+
+ x86ms->max_ram_below_4g = value;
+}
+
+static void x86_machine_initfn(Object *obj)
+{
+ X86MachineState *x86ms = X86_MACHINE(obj);
+
+ x86ms->max_ram_below_4g = 0; /* use default */
+ x86ms->smp_dies = 1;
+}
+
+static void x86_machine_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+ X86MachineClass *x86mc = X86_MACHINE_CLASS(oc);
+ NMIClass *nc = NMI_CLASS(oc);
+
+ mc->cpu_index_to_instance_props = x86_cpu_index_to_props;
+ mc->get_default_cpu_node_id = x86_get_default_cpu_node_id;
+ mc->possible_cpu_arch_ids = x86_possible_cpu_arch_ids;
+ x86mc->compat_apic_id_mode = false;
+ nc->nmi_monitor_handler = x86_nmi;
+
+ object_class_property_add(oc, X86_MACHINE_MAX_RAM_BELOW_4G, "size",
+ x86_machine_get_max_ram_below_4g, x86_machine_set_max_ram_below_4g,
+ NULL, NULL, &error_abort);
+
+ object_class_property_set_description(oc, X86_MACHINE_MAX_RAM_BELOW_4G,
+ "Maximum ram below the 4G boundary (32bit boundary)", &error_abort);
+}
+
+static const TypeInfo x86_machine_info = {
+ .name = TYPE_X86_MACHINE,
+ .parent = TYPE_MACHINE,
+ .abstract = true,
+ .instance_size = sizeof(X86MachineState),
+ .instance_init = x86_machine_initfn,
+ .class_size = sizeof(X86MachineClass),
+ .class_init = x86_machine_class_init,
+ .interfaces = (InterfaceInfo[]) {
+ { TYPE_NMI },
+ { }
+ },
+};
+
+static void x86_machine_register_types(void)
+{
+ type_register_static(&x86_machine_info);
+}
+
+type_init(x86_machine_register_types)
diff --git a/hw/i386/xen/xen-hvm.c b/hw/i386/xen/xen-hvm.c
index 6b5e5bb..95f23a2 100644
--- a/hw/i386/xen/xen-hvm.c
+++ b/hw/i386/xen/xen-hvm.c
@@ -197,11 +197,13 @@ qemu_irq *xen_interrupt_controller_init(void)
static void xen_ram_init(PCMachineState *pcms,
ram_addr_t ram_size, MemoryRegion **ram_memory_p)
{
+ X86MachineState *x86ms = X86_MACHINE(pcms);
MemoryRegion *sysmem = get_system_memory();
ram_addr_t block_len;
- uint64_t user_lowmem = object_property_get_uint(qdev_get_machine(),
- PC_MACHINE_MAX_RAM_BELOW_4G,
- &error_abort);
+ uint64_t user_lowmem =
+ object_property_get_uint(qdev_get_machine(),
+ X86_MACHINE_MAX_RAM_BELOW_4G,
+ &error_abort);
/* Handle the machine opt max-ram-below-4g. It is basically doing
* min(xen limit, user limit).
@@ -214,20 +216,20 @@ static void xen_ram_init(PCMachineState *pcms,
}
if (ram_size >= user_lowmem) {
- pcms->above_4g_mem_size = ram_size - user_lowmem;
- pcms->below_4g_mem_size = user_lowmem;
+ x86ms->above_4g_mem_size = ram_size - user_lowmem;
+ x86ms->below_4g_mem_size = user_lowmem;
} else {
- pcms->above_4g_mem_size = 0;
- pcms->below_4g_mem_size = ram_size;
+ x86ms->above_4g_mem_size = 0;
+ x86ms->below_4g_mem_size = ram_size;
}
- if (!pcms->above_4g_mem_size) {
+ if (!x86ms->above_4g_mem_size) {
block_len = ram_size;
} else {
/*
* Xen does not allocate the memory continuously, it keeps a
* hole of the size computed above or passed in.
*/
- block_len = (1ULL << 32) + pcms->above_4g_mem_size;
+ block_len = (1ULL << 32) + x86ms->above_4g_mem_size;
}
memory_region_init_ram(&ram_memory, NULL, "xen.ram", block_len,
&error_fatal);
@@ -244,12 +246,12 @@ static void xen_ram_init(PCMachineState *pcms,
*/
memory_region_init_alias(&ram_lo, NULL, "xen.ram.lo",
&ram_memory, 0xc0000,
- pcms->below_4g_mem_size - 0xc0000);
+ x86ms->below_4g_mem_size - 0xc0000);
memory_region_add_subregion(sysmem, 0xc0000, &ram_lo);
- if (pcms->above_4g_mem_size > 0) {
+ if (x86ms->above_4g_mem_size > 0) {
memory_region_init_alias(&ram_hi, NULL, "xen.ram.hi",
&ram_memory, 0x100000000ULL,
- pcms->above_4g_mem_size);
+ x86ms->above_4g_mem_size);
memory_region_add_subregion(sysmem, 0x100000000ULL, &ram_hi);
}
}
@@ -265,7 +267,7 @@ void xen_ram_alloc(ram_addr_t ram_addr, ram_addr_t size, MemoryRegion *mr,
/* RAM already populated in Xen */
fprintf(stderr, "%s: do not alloc "RAM_ADDR_FMT
" bytes of ram at "RAM_ADDR_FMT" when runstate is INMIGRATE\n",
- __func__, size, ram_addr);
+ __func__, size, ram_addr);
return;
}