diff options
author | Paolo Bonzini <pbonzini@redhat.com> | 2019-10-26 15:36:22 +0200 |
---|---|---|
committer | Paolo Bonzini <pbonzini@redhat.com> | 2019-10-26 15:38:02 +0200 |
commit | 673652a785efb5f7da342b49c80a1abf033c82a7 (patch) | |
tree | 2f277cce43b50e85cca36d7354ba2dd7ffd14d41 /hw/i386 | |
parent | 856bd2c28e108ad0eb909bbbf3774f6f8bd7c2d4 (diff) | |
parent | df84f17d1beabbb2aca39be18b21afc277cd6754 (diff) | |
download | qemu-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/Kconfig | 10 | ||||
-rw-r--r-- | hw/i386/Makefile.objs | 2 | ||||
-rw-r--r-- | hw/i386/acpi-build.c | 29 | ||||
-rw-r--r-- | hw/i386/amd_iommu.c | 3 | ||||
-rw-r--r-- | hw/i386/intel_iommu.c | 3 | ||||
-rw-r--r-- | hw/i386/microvm.c | 572 | ||||
-rw-r--r-- | hw/i386/pc.c | 815 | ||||
-rw-r--r-- | hw/i386/pc_piix.c | 65 | ||||
-rw-r--r-- | hw/i386/pc_q35.c | 62 | ||||
-rw-r--r-- | hw/i386/pc_sysfw.c | 60 | ||||
-rw-r--r-- | hw/i386/x86.c | 795 | ||||
-rw-r--r-- | hw/i386/xen/xen-hvm.c | 28 |
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(µvm_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; } |