2004-03-14 15:20:30 +03:00
|
|
|
/*
|
|
|
|
* QEMU PC System Emulator
|
2007-09-17 01:08:06 +04:00
|
|
|
*
|
2004-03-14 15:20:30 +03:00
|
|
|
* Copyright (c) 2003-2004 Fabrice Bellard
|
2007-09-17 01:08:06 +04:00
|
|
|
*
|
2004-03-14 15:20:30 +03:00
|
|
|
* 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.
|
|
|
|
*/
|
2018-02-01 14:18:31 +03:00
|
|
|
|
2016-01-26 21:17:03 +03:00
|
|
|
#include "qemu/osdep.h"
|
2018-06-29 17:22:13 +03:00
|
|
|
#include "qemu/units.h"
|
2019-10-08 12:56:49 +03:00
|
|
|
#include "hw/i386/x86.h"
|
2013-02-05 20:06:20 +04:00
|
|
|
#include "hw/i386/pc.h"
|
|
|
|
#include "hw/char/serial.h"
|
2018-03-09 01:39:22 +03:00
|
|
|
#include "hw/char/parallel.h"
|
2013-02-05 20:06:20 +04:00
|
|
|
#include "hw/i386/apic.h"
|
2014-12-19 04:43:35 +03:00
|
|
|
#include "hw/i386/topology.h"
|
2019-04-22 19:58:21 +03:00
|
|
|
#include "hw/i386/fw_cfg.h"
|
2014-12-19 04:43:35 +03:00
|
|
|
#include "sysemu/cpus.h"
|
2013-02-05 20:06:20 +04:00
|
|
|
#include "hw/block/fdc.h"
|
2013-02-04 18:40:22 +04:00
|
|
|
#include "hw/ide.h"
|
|
|
|
#include "hw/pci/pci.h"
|
2015-06-02 14:23:07 +03:00
|
|
|
#include "hw/pci/pci_bus.h"
|
2013-02-05 20:06:20 +04:00
|
|
|
#include "hw/nvram/fw_cfg.h"
|
|
|
|
#include "hw/timer/hpet.h"
|
2018-12-11 19:34:06 +03:00
|
|
|
#include "hw/firmware/smbios.h"
|
2013-02-04 18:40:22 +04:00
|
|
|
#include "hw/loader.h"
|
2009-09-20 18:58:02 +04:00
|
|
|
#include "elf.h"
|
2019-08-12 08:23:45 +03:00
|
|
|
#include "migration/vmstate.h"
|
2013-03-18 20:36:02 +04:00
|
|
|
#include "multiboot.h"
|
2019-10-04 02:03:53 +03:00
|
|
|
#include "hw/rtc/mc146818rtc.h"
|
2019-12-12 19:15:43 +03:00
|
|
|
#include "hw/intc/i8259.h"
|
2018-03-09 01:39:23 +03:00
|
|
|
#include "hw/dma/i8257.h"
|
2013-02-05 20:06:20 +04:00
|
|
|
#include "hw/timer/i8254.h"
|
2018-03-09 01:39:24 +03:00
|
|
|
#include "hw/input/i8042.h"
|
2019-08-12 08:23:42 +03:00
|
|
|
#include "hw/irq.h"
|
2013-02-05 20:06:20 +04:00
|
|
|
#include "hw/audio/pcspk.h"
|
2013-02-04 18:40:22 +04:00
|
|
|
#include "hw/pci/msi.h"
|
|
|
|
#include "hw/sysbus.h"
|
2012-12-17 21:20:04 +04:00
|
|
|
#include "sysemu/sysemu.h"
|
2019-05-23 17:35:05 +03:00
|
|
|
#include "sysemu/tcg.h"
|
2015-02-08 21:51:16 +03:00
|
|
|
#include "sysemu/numa.h"
|
2012-12-17 21:20:04 +04:00
|
|
|
#include "sysemu/kvm.h"
|
2015-03-14 16:24:37 +03:00
|
|
|
#include "sysemu/qtest.h"
|
2019-08-12 08:23:38 +03:00
|
|
|
#include "sysemu/reset.h"
|
2019-08-12 08:23:59 +03:00
|
|
|
#include "sysemu/runstate.h"
|
2012-07-26 18:35:13 +04:00
|
|
|
#include "kvm_i386.h"
|
2013-02-05 20:06:20 +04:00
|
|
|
#include "hw/xen/xen.h"
|
2019-01-15 15:18:06 +03:00
|
|
|
#include "hw/xen/start_info.h"
|
2010-04-27 13:50:11 +04:00
|
|
|
#include "ui/qemu-spice.h"
|
2012-12-17 21:19:49 +04:00
|
|
|
#include "exec/memory.h"
|
|
|
|
#include "exec/address-spaces.h"
|
2012-12-17 21:20:04 +04:00
|
|
|
#include "sysemu/arch_init.h"
|
2012-12-17 21:20:00 +04:00
|
|
|
#include "qemu/bitmap.h"
|
2013-03-21 03:23:17 +04:00
|
|
|
#include "qemu/config-file.h"
|
2015-03-17 20:29:20 +03:00
|
|
|
#include "qemu/error-report.h"
|
2018-02-01 14:18:46 +03:00
|
|
|
#include "qemu/option.h"
|
2019-10-10 16:49:39 +03:00
|
|
|
#include "qemu/cutils.h"
|
2013-04-15 10:19:22 +04:00
|
|
|
#include "hw/acpi/acpi.h"
|
2014-03-14 23:33:51 +04:00
|
|
|
#include "hw/acpi/cpu_hotplug.h"
|
2013-04-30 20:00:53 +04:00
|
|
|
#include "hw/boards.h"
|
i386: ACPI table generation code from seabios
This adds C code for generating ACPI tables at runtime,
imported from seabios git tree
commit 51684b7ced75fb76776e8ee84833fcfb6ecf12dd
Although ACPI tables come from a system BIOS on real hw,
it makes sense that the ACPI tables are coupled with the
virtual machine, since they have to abstract the x86 machine to
the OS's.
This is widely desired as a way to avoid the churn
and proliferation of QEMU-specific interfaces
associated with ACPI tables in bios code.
Notes:
As BIOS can reprogram devices prior to loading
ACPI tables, we pre-format ACPI tables but defer loading
hardware configuration there until tables are loaded.
The code structure was intentionally kept as close
to the seabios original as possible, to simplify
comparison and making sure we didn't lose anything
in translation.
Minor code duplication results, to help ensure there are no functional
regressions, I think it's better to merge it like this and do more code
changes in follow-up patches.
Cross-version compatibility concerns have been addressed:
ACPI tables are exposed to guest as FW_CFG entries.
When running with -M 1.5 and older, this patch disables ACPI
table generation, and doesn't expose ACPI
tables to guest.
As table content is likely to change over time,
the following measures are taken to simplify
cross-version migration:
- All tables besides the RSDP are packed in a single FW CFG entry.
This entry size is currently 23K. We round it up to 64K
to avoid too much churn there.
- Tables are placed in special ROM blob (not mapped into guest memory)
which is automatically migrated together with the guest, same
as BIOS code.
- Offsets where hardware configuration is loaded in ACPI tables
are also migrated, this is in case future ACPI changes make us
rearrange the tables in memory.
This patch reuses some code from SeaBIOS, which was originally under
LGPLv2 and then relicensed to GPLv3 or LGPLv3, in QEMU under GPLv2+. This
relicensing has been acked by all contributors that had contributed to the
code since the v2->v3 relicense. ACKs approving the v2+ relicensing are
listed below. The list might include ACKs from people not holding
copyright on any parts of the reused code, but it's better to err on the
side of caution and include them.
Affected SeaBIOS files (GPLv2+ license headers added)
<http://thread.gmane.org/gmane.comp.bios.coreboot.seabios/5949>:
src/acpi-dsdt-cpu-hotplug.dsl
src/acpi-dsdt-dbug.dsl
src/acpi-dsdt-hpet.dsl
src/acpi-dsdt-isa.dsl
src/acpi-dsdt-pci-crs.dsl
src/acpi.c
src/acpi.h
src/ssdt-misc.dsl
src/ssdt-pcihp.dsl
src/ssdt-proc.dsl
tools/acpi_extract.py
tools/acpi_extract_preprocess.py
Each one of the listed people agreed to the following:
> If you allow the use of your contribution in QEMU under the
> terms of GPLv2 or later as proposed by this patch,
> please respond to this mail including the line:
>
> Acked-by: Name <email address>
Acked-by: Gerd Hoffmann <kraxel@redhat.com>
Acked-by: Jan Kiszka <jan.kiszka@siemens.com>
Acked-by: Jason Baron <jbaron@akamai.com>
Acked-by: David Woodhouse <David.Woodhouse@intel.com>
Acked-by: Gleb Natapov <gleb@redhat.com>
Acked-by: Marcelo Tosatti <mtosatti@redhat.com>
Acked-by: Dave Frodin <dave.frodin@se-eng.com>
Acked-by: Paolo Bonzini <pbonzini@redhat.com>
Acked-by: Kevin O'Connor <kevin@koconnor.net>
Acked-by: Laszlo Ersek <lersek@redhat.com>
Acked-by: Kenji Kaneshige <kaneshige.kenji@jp.fujitsu.com>
Acked-by: Isaku Yamahata <yamahata@valinux.co.jp>
Acked-by: Magnus Christensson <magnus.christensson@intel.com>
Acked-by: Hu Tao <hutao@cn.fujitsu.com>
Acked-by: Eduardo Habkost <ehabkost@redhat.com>
Reviewed-by: Gerd Hoffmann <kraxel@redhat.com>
Tested-by: Gerd Hoffmann <kraxel@redhat.com>
Reviewed-by: Igor Mammedov <imammedo@redhat.com>
Tested-by: Igor Mammedov <imammedo@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
2013-07-24 19:56:14 +04:00
|
|
|
#include "acpi-build.h"
|
2014-06-02 17:25:12 +04:00
|
|
|
#include "hw/mem/pc-dimm.h"
|
2018-02-01 14:18:31 +03:00
|
|
|
#include "qapi/error.h"
|
2018-02-11 12:36:01 +03:00
|
|
|
#include "qapi/qapi-visit-common.h"
|
2014-06-02 17:25:27 +04:00
|
|
|
#include "qapi/visitor.h"
|
2019-07-09 18:20:52 +03:00
|
|
|
#include "hw/core/cpu.h"
|
2018-12-12 20:57:53 +03:00
|
|
|
#include "hw/usb.h"
|
2016-10-19 15:05:42 +03:00
|
|
|
#include "hw/i386/intel_iommu.h"
|
2017-10-17 19:44:22 +03:00
|
|
|
#include "hw/net/ne2000-isa.h"
|
2019-01-17 15:49:03 +03:00
|
|
|
#include "standard-headers/asm-x86/bootparam.h"
|
2019-06-19 12:49:07 +03:00
|
|
|
#include "hw/virtio/virtio-pmem-pci.h"
|
|
|
|
#include "hw/mem/memory-device.h"
|
2019-06-20 08:45:24 +03:00
|
|
|
#include "sysemu/replay.h"
|
|
|
|
#include "qapi/qmp/qerror.h"
|
2019-07-12 19:02:57 +03:00
|
|
|
#include "config-devices.h"
|
2019-08-19 01:54:01 +03:00
|
|
|
#include "e820_memory_layout.h"
|
2019-09-16 13:49:28 +03:00
|
|
|
#include "fw_cfg.h"
|
2019-12-13 13:50:57 +03:00
|
|
|
#include "trace.h"
|
2010-05-30 00:23:49 +04:00
|
|
|
|
2019-11-12 13:48:11 +03:00
|
|
|
GlobalProperty pc_compat_4_2[] = {};
|
|
|
|
const size_t pc_compat_4_2_len = G_N_ELEMENTS(pc_compat_4_2);
|
|
|
|
|
2019-07-24 13:35:24 +03:00
|
|
|
GlobalProperty pc_compat_4_1[] = {};
|
|
|
|
const size_t pc_compat_4_1_len = G_N_ELEMENTS(pc_compat_4_1);
|
|
|
|
|
2019-04-11 13:20:25 +03:00
|
|
|
GlobalProperty pc_compat_4_0[] = {};
|
|
|
|
const size_t pc_compat_4_0_len = G_N_ELEMENTS(pc_compat_4_0);
|
|
|
|
|
2018-12-12 18:36:30 +03:00
|
|
|
GlobalProperty pc_compat_3_1[] = {
|
2019-01-07 22:30:20 +03:00
|
|
|
{ "intel-iommu", "dma-drain", "off" },
|
2018-12-20 15:07:32 +03:00
|
|
|
{ "Opteron_G3" "-" TYPE_X86_CPU, "rdtscp", "off" },
|
|
|
|
{ "Opteron_G4" "-" TYPE_X86_CPU, "rdtscp", "off" },
|
2019-01-21 18:50:51 +03:00
|
|
|
{ "Opteron_G4" "-" TYPE_X86_CPU, "npt", "off" },
|
|
|
|
{ "Opteron_G4" "-" TYPE_X86_CPU, "nrip-save", "off" },
|
2018-12-20 15:07:32 +03:00
|
|
|
{ "Opteron_G5" "-" TYPE_X86_CPU, "rdtscp", "off" },
|
2019-01-21 18:50:51 +03:00
|
|
|
{ "Opteron_G5" "-" TYPE_X86_CPU, "npt", "off" },
|
|
|
|
{ "Opteron_G5" "-" TYPE_X86_CPU, "nrip-save", "off" },
|
|
|
|
{ "EPYC" "-" TYPE_X86_CPU, "npt", "off" },
|
|
|
|
{ "EPYC" "-" TYPE_X86_CPU, "nrip-save", "off" },
|
|
|
|
{ "EPYC-IBPB" "-" TYPE_X86_CPU, "npt", "off" },
|
|
|
|
{ "EPYC-IBPB" "-" TYPE_X86_CPU, "nrip-save", "off" },
|
2018-12-20 15:11:00 +03:00
|
|
|
{ "Skylake-Client" "-" TYPE_X86_CPU, "mpx", "on" },
|
|
|
|
{ "Skylake-Client-IBRS" "-" TYPE_X86_CPU, "mpx", "on" },
|
|
|
|
{ "Skylake-Server" "-" TYPE_X86_CPU, "mpx", "on" },
|
|
|
|
{ "Skylake-Server-IBRS" "-" TYPE_X86_CPU, "mpx", "on" },
|
|
|
|
{ "Cascadelake-Server" "-" TYPE_X86_CPU, "mpx", "on" },
|
|
|
|
{ "Icelake-Client" "-" TYPE_X86_CPU, "mpx", "on" },
|
|
|
|
{ "Icelake-Server" "-" TYPE_X86_CPU, "mpx", "on" },
|
2018-12-27 05:43:03 +03:00
|
|
|
{ "Cascadelake-Server" "-" TYPE_X86_CPU, "stepping", "5" },
|
2019-01-30 02:52:59 +03:00
|
|
|
{ TYPE_X86_CPU, "x-intel-pt-auto-level", "off" },
|
2018-12-12 18:36:30 +03:00
|
|
|
};
|
|
|
|
const size_t pc_compat_3_1_len = G_N_ELEMENTS(pc_compat_3_1);
|
|
|
|
|
2018-12-12 18:36:30 +03:00
|
|
|
GlobalProperty pc_compat_3_0[] = {
|
2019-01-07 22:30:20 +03:00
|
|
|
{ TYPE_X86_CPU, "x-hv-synic-kvm-only", "on" },
|
|
|
|
{ "Skylake-Server" "-" TYPE_X86_CPU, "pku", "off" },
|
|
|
|
{ "Skylake-Server-IBRS" "-" TYPE_X86_CPU, "pku", "off" },
|
2018-12-12 18:36:30 +03:00
|
|
|
};
|
|
|
|
const size_t pc_compat_3_0_len = G_N_ELEMENTS(pc_compat_3_0);
|
|
|
|
|
2018-12-12 18:36:30 +03:00
|
|
|
GlobalProperty pc_compat_2_12[] = {
|
2019-01-07 22:30:20 +03:00
|
|
|
{ TYPE_X86_CPU, "legacy-cache", "on" },
|
|
|
|
{ TYPE_X86_CPU, "topoext", "off" },
|
|
|
|
{ "EPYC-" TYPE_X86_CPU, "xlevel", "0x8000000a" },
|
|
|
|
{ "EPYC-IBPB-" TYPE_X86_CPU, "xlevel", "0x8000000a" },
|
2018-12-12 18:36:30 +03:00
|
|
|
};
|
|
|
|
const size_t pc_compat_2_12_len = G_N_ELEMENTS(pc_compat_2_12);
|
|
|
|
|
2018-12-12 18:36:30 +03:00
|
|
|
GlobalProperty pc_compat_2_11[] = {
|
2019-01-07 22:30:20 +03:00
|
|
|
{ TYPE_X86_CPU, "x-migrate-smi-count", "off" },
|
|
|
|
{ "Skylake-Server" "-" TYPE_X86_CPU, "clflushopt", "off" },
|
2018-12-12 18:36:30 +03:00
|
|
|
};
|
|
|
|
const size_t pc_compat_2_11_len = G_N_ELEMENTS(pc_compat_2_11);
|
|
|
|
|
2018-12-12 18:36:30 +03:00
|
|
|
GlobalProperty pc_compat_2_10[] = {
|
2019-01-07 22:30:20 +03:00
|
|
|
{ TYPE_X86_CPU, "x-hv-max-vps", "0x40" },
|
|
|
|
{ "i440FX-pcihost", "x-pci-hole64-fix", "off" },
|
|
|
|
{ "q35-pcihost", "x-pci-hole64-fix", "off" },
|
2018-12-12 18:36:30 +03:00
|
|
|
};
|
|
|
|
const size_t pc_compat_2_10_len = G_N_ELEMENTS(pc_compat_2_10);
|
|
|
|
|
2018-12-12 18:36:30 +03:00
|
|
|
GlobalProperty pc_compat_2_9[] = {
|
2019-01-07 22:30:20 +03:00
|
|
|
{ "mch", "extended-tseg-mbytes", "0" },
|
2018-12-12 18:36:30 +03:00
|
|
|
};
|
|
|
|
const size_t pc_compat_2_9_len = G_N_ELEMENTS(pc_compat_2_9);
|
|
|
|
|
2018-12-12 18:36:30 +03:00
|
|
|
GlobalProperty pc_compat_2_8[] = {
|
2019-01-07 22:30:20 +03:00
|
|
|
{ TYPE_X86_CPU, "tcg-cpuid", "off" },
|
|
|
|
{ "kvmclock", "x-mach-use-reliable-get-clock", "off" },
|
|
|
|
{ "ICH9-LPC", "x-smi-broadcast", "off" },
|
|
|
|
{ TYPE_X86_CPU, "vmware-cpuid-freq", "off" },
|
|
|
|
{ "Haswell-" TYPE_X86_CPU, "stepping", "1" },
|
2018-12-12 18:36:30 +03:00
|
|
|
};
|
|
|
|
const size_t pc_compat_2_8_len = G_N_ELEMENTS(pc_compat_2_8);
|
|
|
|
|
2018-12-12 18:36:30 +03:00
|
|
|
GlobalProperty pc_compat_2_7[] = {
|
2019-01-07 22:30:20 +03:00
|
|
|
{ TYPE_X86_CPU, "l3-cache", "off" },
|
|
|
|
{ TYPE_X86_CPU, "full-cpuid-auto-level", "off" },
|
|
|
|
{ "Opteron_G3" "-" TYPE_X86_CPU, "family", "15" },
|
|
|
|
{ "Opteron_G3" "-" TYPE_X86_CPU, "model", "6" },
|
|
|
|
{ "Opteron_G3" "-" TYPE_X86_CPU, "stepping", "1" },
|
|
|
|
{ "isa-pcspk", "migrate", "off" },
|
2018-12-12 18:36:30 +03:00
|
|
|
};
|
|
|
|
const size_t pc_compat_2_7_len = G_N_ELEMENTS(pc_compat_2_7);
|
|
|
|
|
2018-12-12 18:36:30 +03:00
|
|
|
GlobalProperty pc_compat_2_6[] = {
|
2019-01-07 22:30:20 +03:00
|
|
|
{ TYPE_X86_CPU, "cpuid-0xb", "off" },
|
|
|
|
{ "vmxnet3", "romfile", "" },
|
|
|
|
{ TYPE_X86_CPU, "fill-mtrr-mask", "off" },
|
|
|
|
{ "apic-common", "legacy-instance-id", "on", }
|
2018-12-12 18:36:30 +03:00
|
|
|
};
|
|
|
|
const size_t pc_compat_2_6_len = G_N_ELEMENTS(pc_compat_2_6);
|
|
|
|
|
2018-12-12 18:36:30 +03:00
|
|
|
GlobalProperty pc_compat_2_5[] = {};
|
|
|
|
const size_t pc_compat_2_5_len = G_N_ELEMENTS(pc_compat_2_5);
|
|
|
|
|
2018-12-12 18:36:30 +03:00
|
|
|
GlobalProperty pc_compat_2_4[] = {
|
|
|
|
PC_CPU_MODEL_IDS("2.4.0")
|
2019-01-07 22:30:20 +03:00
|
|
|
{ "Haswell-" TYPE_X86_CPU, "abm", "off" },
|
|
|
|
{ "Haswell-noTSX-" TYPE_X86_CPU, "abm", "off" },
|
|
|
|
{ "Broadwell-" TYPE_X86_CPU, "abm", "off" },
|
|
|
|
{ "Broadwell-noTSX-" TYPE_X86_CPU, "abm", "off" },
|
|
|
|
{ "host" "-" TYPE_X86_CPU, "host-cache-info", "on" },
|
|
|
|
{ TYPE_X86_CPU, "check", "off" },
|
|
|
|
{ "qemu64" "-" TYPE_X86_CPU, "sse4a", "on" },
|
|
|
|
{ "qemu64" "-" TYPE_X86_CPU, "abm", "on" },
|
|
|
|
{ "qemu64" "-" TYPE_X86_CPU, "popcnt", "on" },
|
|
|
|
{ "qemu32" "-" TYPE_X86_CPU, "popcnt", "on" },
|
|
|
|
{ "Opteron_G2" "-" TYPE_X86_CPU, "rdtscp", "on" },
|
|
|
|
{ "Opteron_G3" "-" TYPE_X86_CPU, "rdtscp", "on" },
|
|
|
|
{ "Opteron_G4" "-" TYPE_X86_CPU, "rdtscp", "on" },
|
|
|
|
{ "Opteron_G5" "-" TYPE_X86_CPU, "rdtscp", "on", }
|
2018-12-12 18:36:30 +03:00
|
|
|
};
|
|
|
|
const size_t pc_compat_2_4_len = G_N_ELEMENTS(pc_compat_2_4);
|
|
|
|
|
2018-12-12 18:36:30 +03:00
|
|
|
GlobalProperty pc_compat_2_3[] = {
|
|
|
|
PC_CPU_MODEL_IDS("2.3.0")
|
2019-01-07 22:30:20 +03:00
|
|
|
{ TYPE_X86_CPU, "arat", "off" },
|
|
|
|
{ "qemu64" "-" TYPE_X86_CPU, "min-level", "4" },
|
|
|
|
{ "kvm64" "-" TYPE_X86_CPU, "min-level", "5" },
|
|
|
|
{ "pentium3" "-" TYPE_X86_CPU, "min-level", "2" },
|
|
|
|
{ "n270" "-" TYPE_X86_CPU, "min-level", "5" },
|
|
|
|
{ "Conroe" "-" TYPE_X86_CPU, "min-level", "4" },
|
|
|
|
{ "Penryn" "-" TYPE_X86_CPU, "min-level", "4" },
|
|
|
|
{ "Nehalem" "-" TYPE_X86_CPU, "min-level", "4" },
|
|
|
|
{ "n270" "-" TYPE_X86_CPU, "min-xlevel", "0x8000000a" },
|
|
|
|
{ "Penryn" "-" TYPE_X86_CPU, "min-xlevel", "0x8000000a" },
|
|
|
|
{ "Conroe" "-" TYPE_X86_CPU, "min-xlevel", "0x8000000a" },
|
|
|
|
{ "Nehalem" "-" TYPE_X86_CPU, "min-xlevel", "0x8000000a" },
|
|
|
|
{ "Westmere" "-" TYPE_X86_CPU, "min-xlevel", "0x8000000a" },
|
|
|
|
{ "SandyBridge" "-" TYPE_X86_CPU, "min-xlevel", "0x8000000a" },
|
|
|
|
{ "IvyBridge" "-" TYPE_X86_CPU, "min-xlevel", "0x8000000a" },
|
|
|
|
{ "Haswell" "-" TYPE_X86_CPU, "min-xlevel", "0x8000000a" },
|
|
|
|
{ "Haswell-noTSX" "-" TYPE_X86_CPU, "min-xlevel", "0x8000000a" },
|
|
|
|
{ "Broadwell" "-" TYPE_X86_CPU, "min-xlevel", "0x8000000a" },
|
|
|
|
{ "Broadwell-noTSX" "-" TYPE_X86_CPU, "min-xlevel", "0x8000000a" },
|
|
|
|
{ TYPE_X86_CPU, "kvm-no-smi-migration", "on" },
|
2018-12-12 18:36:30 +03:00
|
|
|
};
|
|
|
|
const size_t pc_compat_2_3_len = G_N_ELEMENTS(pc_compat_2_3);
|
|
|
|
|
2018-12-12 18:36:30 +03:00
|
|
|
GlobalProperty pc_compat_2_2[] = {
|
|
|
|
PC_CPU_MODEL_IDS("2.2.0")
|
2019-01-07 22:30:20 +03:00
|
|
|
{ "kvm64" "-" TYPE_X86_CPU, "vme", "off" },
|
|
|
|
{ "kvm32" "-" TYPE_X86_CPU, "vme", "off" },
|
|
|
|
{ "Conroe" "-" TYPE_X86_CPU, "vme", "off" },
|
|
|
|
{ "Penryn" "-" TYPE_X86_CPU, "vme", "off" },
|
|
|
|
{ "Nehalem" "-" TYPE_X86_CPU, "vme", "off" },
|
|
|
|
{ "Westmere" "-" TYPE_X86_CPU, "vme", "off" },
|
|
|
|
{ "SandyBridge" "-" TYPE_X86_CPU, "vme", "off" },
|
|
|
|
{ "Haswell" "-" TYPE_X86_CPU, "vme", "off" },
|
|
|
|
{ "Broadwell" "-" TYPE_X86_CPU, "vme", "off" },
|
|
|
|
{ "Opteron_G1" "-" TYPE_X86_CPU, "vme", "off" },
|
|
|
|
{ "Opteron_G2" "-" TYPE_X86_CPU, "vme", "off" },
|
|
|
|
{ "Opteron_G3" "-" TYPE_X86_CPU, "vme", "off" },
|
|
|
|
{ "Opteron_G4" "-" TYPE_X86_CPU, "vme", "off" },
|
|
|
|
{ "Opteron_G5" "-" TYPE_X86_CPU, "vme", "off" },
|
|
|
|
{ "Haswell" "-" TYPE_X86_CPU, "f16c", "off" },
|
|
|
|
{ "Haswell" "-" TYPE_X86_CPU, "rdrand", "off" },
|
|
|
|
{ "Broadwell" "-" TYPE_X86_CPU, "f16c", "off" },
|
|
|
|
{ "Broadwell" "-" TYPE_X86_CPU, "rdrand", "off" },
|
2018-12-12 18:36:30 +03:00
|
|
|
};
|
|
|
|
const size_t pc_compat_2_2_len = G_N_ELEMENTS(pc_compat_2_2);
|
|
|
|
|
2018-12-12 18:36:30 +03:00
|
|
|
GlobalProperty pc_compat_2_1[] = {
|
|
|
|
PC_CPU_MODEL_IDS("2.1.0")
|
2019-01-07 22:30:20 +03:00
|
|
|
{ "coreduo" "-" TYPE_X86_CPU, "vmx", "on" },
|
|
|
|
{ "core2duo" "-" TYPE_X86_CPU, "vmx", "on" },
|
2018-12-12 18:36:30 +03:00
|
|
|
};
|
|
|
|
const size_t pc_compat_2_1_len = G_N_ELEMENTS(pc_compat_2_1);
|
|
|
|
|
2018-12-12 20:57:53 +03:00
|
|
|
GlobalProperty pc_compat_2_0[] = {
|
|
|
|
PC_CPU_MODEL_IDS("2.0.0")
|
2019-01-07 22:30:20 +03:00
|
|
|
{ "virtio-scsi-pci", "any_layout", "off" },
|
|
|
|
{ "PIIX4_PM", "memory-hotplug-support", "off" },
|
|
|
|
{ "apic", "version", "0x11" },
|
|
|
|
{ "nec-usb-xhci", "superspeed-ports-first", "off" },
|
|
|
|
{ "nec-usb-xhci", "force-pcie-endcap", "on" },
|
|
|
|
{ "pci-serial", "prog_if", "0" },
|
|
|
|
{ "pci-serial-2x", "prog_if", "0" },
|
|
|
|
{ "pci-serial-4x", "prog_if", "0" },
|
|
|
|
{ "virtio-net-pci", "guest_announce", "off" },
|
|
|
|
{ "ICH9-LPC", "memory-hotplug-support", "off" },
|
|
|
|
{ "xio3130-downstream", COMPAT_PROP_PCP, "off" },
|
|
|
|
{ "ioh3420", COMPAT_PROP_PCP, "off" },
|
2018-12-12 20:57:53 +03:00
|
|
|
};
|
|
|
|
const size_t pc_compat_2_0_len = G_N_ELEMENTS(pc_compat_2_0);
|
|
|
|
|
|
|
|
GlobalProperty pc_compat_1_7[] = {
|
|
|
|
PC_CPU_MODEL_IDS("1.7.0")
|
2019-01-07 22:30:20 +03:00
|
|
|
{ TYPE_USB_DEVICE, "msos-desc", "no" },
|
|
|
|
{ "PIIX4_PM", "acpi-pci-hotplug-with-bridge-support", "off" },
|
|
|
|
{ "hpet", HPET_INTCAP, "4" },
|
2018-12-12 20:57:53 +03:00
|
|
|
};
|
|
|
|
const size_t pc_compat_1_7_len = G_N_ELEMENTS(pc_compat_1_7);
|
|
|
|
|
|
|
|
GlobalProperty pc_compat_1_6[] = {
|
|
|
|
PC_CPU_MODEL_IDS("1.6.0")
|
2019-01-07 22:30:20 +03:00
|
|
|
{ "e1000", "mitigation", "off" },
|
|
|
|
{ "qemu64-" TYPE_X86_CPU, "model", "2" },
|
|
|
|
{ "qemu32-" TYPE_X86_CPU, "model", "3" },
|
|
|
|
{ "i440FX-pcihost", "short_root_bus", "1" },
|
|
|
|
{ "q35-pcihost", "short_root_bus", "1" },
|
2018-12-12 20:57:53 +03:00
|
|
|
};
|
|
|
|
const size_t pc_compat_1_6_len = G_N_ELEMENTS(pc_compat_1_6);
|
|
|
|
|
|
|
|
GlobalProperty pc_compat_1_5[] = {
|
|
|
|
PC_CPU_MODEL_IDS("1.5.0")
|
2019-01-07 22:30:20 +03:00
|
|
|
{ "Conroe-" TYPE_X86_CPU, "model", "2" },
|
|
|
|
{ "Conroe-" TYPE_X86_CPU, "min-level", "2" },
|
|
|
|
{ "Penryn-" TYPE_X86_CPU, "model", "2" },
|
|
|
|
{ "Penryn-" TYPE_X86_CPU, "min-level", "2" },
|
|
|
|
{ "Nehalem-" TYPE_X86_CPU, "model", "2" },
|
|
|
|
{ "Nehalem-" TYPE_X86_CPU, "min-level", "2" },
|
|
|
|
{ "virtio-net-pci", "any_layout", "off" },
|
|
|
|
{ TYPE_X86_CPU, "pmu", "on" },
|
|
|
|
{ "i440FX-pcihost", "short_root_bus", "0" },
|
|
|
|
{ "q35-pcihost", "short_root_bus", "0" },
|
2018-12-12 20:57:53 +03:00
|
|
|
};
|
|
|
|
const size_t pc_compat_1_5_len = G_N_ELEMENTS(pc_compat_1_5);
|
|
|
|
|
|
|
|
GlobalProperty pc_compat_1_4[] = {
|
|
|
|
PC_CPU_MODEL_IDS("1.4.0")
|
2019-01-07 22:30:20 +03:00
|
|
|
{ "scsi-hd", "discard_granularity", "0" },
|
|
|
|
{ "scsi-cd", "discard_granularity", "0" },
|
|
|
|
{ "scsi-disk", "discard_granularity", "0" },
|
|
|
|
{ "ide-hd", "discard_granularity", "0" },
|
|
|
|
{ "ide-cd", "discard_granularity", "0" },
|
|
|
|
{ "ide-drive", "discard_granularity", "0" },
|
|
|
|
{ "virtio-blk-pci", "discard_granularity", "0" },
|
|
|
|
/* DEV_NVECTORS_UNSPECIFIED as a uint32_t string: */
|
|
|
|
{ "virtio-serial-pci", "vectors", "0xFFFFFFFF" },
|
|
|
|
{ "virtio-net-pci", "ctrl_guest_offloads", "off" },
|
|
|
|
{ "e1000", "romfile", "pxe-e1000.rom" },
|
|
|
|
{ "ne2k_pci", "romfile", "pxe-ne2k_pci.rom" },
|
|
|
|
{ "pcnet", "romfile", "pxe-pcnet.rom" },
|
|
|
|
{ "rtl8139", "romfile", "pxe-rtl8139.rom" },
|
|
|
|
{ "virtio-net-pci", "romfile", "pxe-virtio.rom" },
|
|
|
|
{ "486-" TYPE_X86_CPU, "model", "0" },
|
|
|
|
{ "n270" "-" TYPE_X86_CPU, "movbe", "off" },
|
|
|
|
{ "Westmere" "-" TYPE_X86_CPU, "pclmulqdq", "off" },
|
2018-12-12 20:57:53 +03:00
|
|
|
};
|
|
|
|
const size_t pc_compat_1_4_len = G_N_ELEMENTS(pc_compat_1_4);
|
|
|
|
|
2019-10-18 16:59:06 +03:00
|
|
|
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);
|
|
|
|
}
|
2019-12-13 14:07:36 +03:00
|
|
|
*irqs = qemu_allocate_irqs(gsi_handler, s, GSI_NUM_PINS);
|
2019-10-18 16:59:06 +03:00
|
|
|
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
2012-09-19 15:50:08 +04:00
|
|
|
static void ioport80_write(void *opaque, hwaddr addr, uint64_t data,
|
|
|
|
unsigned size)
|
2004-03-14 15:20:30 +03:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2013-01-09 22:10:22 +04:00
|
|
|
static uint64_t ioport80_read(void *opaque, hwaddr addr, unsigned size)
|
|
|
|
{
|
2013-01-11 20:41:43 +04:00
|
|
|
return 0xffffffffffffffffULL;
|
2013-01-09 22:10:22 +04:00
|
|
|
}
|
|
|
|
|
2004-05-09 01:03:41 +04:00
|
|
|
/* MSDOS compatibility mode FPU exception support */
|
2012-09-19 15:50:08 +04:00
|
|
|
static void ioportF0_write(void *opaque, hwaddr addr, uint64_t data,
|
|
|
|
unsigned size)
|
2004-05-09 01:03:41 +04:00
|
|
|
{
|
2019-10-16 11:18:10 +03:00
|
|
|
if (tcg_enabled()) {
|
2019-10-16 11:34:39 +03:00
|
|
|
cpu_set_ignne();
|
2019-10-16 11:18:10 +03:00
|
|
|
}
|
2004-05-09 01:03:41 +04:00
|
|
|
}
|
|
|
|
|
2013-01-09 22:10:22 +04:00
|
|
|
static uint64_t ioportF0_read(void *opaque, hwaddr addr, unsigned size)
|
|
|
|
{
|
2013-01-11 20:41:43 +04:00
|
|
|
return 0xffffffffffffffffULL;
|
2013-01-09 22:10:22 +04:00
|
|
|
}
|
|
|
|
|
2004-03-31 22:58:38 +04:00
|
|
|
/* PC cmos mappings */
|
|
|
|
|
2004-03-14 15:20:30 +03:00
|
|
|
#define REG_EQUIPMENT_BYTE 0x14
|
|
|
|
|
2016-02-17 21:25:31 +03:00
|
|
|
int cmos_get_fd_drive_type(FloppyDriveType fd0)
|
2004-05-23 20:26:20 +04:00
|
|
|
{
|
|
|
|
int val;
|
|
|
|
|
|
|
|
switch (fd0) {
|
2016-01-22 23:50:56 +03:00
|
|
|
case FLOPPY_DRIVE_TYPE_144:
|
2004-05-23 20:26:20 +04:00
|
|
|
/* 1.44 Mb 3"5 drive */
|
|
|
|
val = 4;
|
|
|
|
break;
|
2016-01-22 23:50:56 +03:00
|
|
|
case FLOPPY_DRIVE_TYPE_288:
|
2004-05-23 20:26:20 +04:00
|
|
|
/* 2.88 Mb 3"5 drive */
|
|
|
|
val = 5;
|
|
|
|
break;
|
2016-01-22 23:50:56 +03:00
|
|
|
case FLOPPY_DRIVE_TYPE_120:
|
2004-05-23 20:26:20 +04:00
|
|
|
/* 1.2 Mb 5"5 drive */
|
|
|
|
val = 2;
|
|
|
|
break;
|
2016-01-22 23:50:56 +03:00
|
|
|
case FLOPPY_DRIVE_TYPE_NONE:
|
2004-05-23 20:26:20 +04:00
|
|
|
default:
|
|
|
|
val = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
2012-07-10 13:12:38 +04:00
|
|
|
static void cmos_init_hd(ISADevice *s, int type_ofs, int info_ofs,
|
|
|
|
int16_t cylinders, int8_t heads, int8_t sectors)
|
2004-10-09 20:47:59 +04:00
|
|
|
{
|
|
|
|
rtc_set_memory(s, type_ofs, 47);
|
|
|
|
rtc_set_memory(s, info_ofs, cylinders);
|
|
|
|
rtc_set_memory(s, info_ofs + 1, cylinders >> 8);
|
|
|
|
rtc_set_memory(s, info_ofs + 2, heads);
|
|
|
|
rtc_set_memory(s, info_ofs + 3, 0xff);
|
|
|
|
rtc_set_memory(s, info_ofs + 4, 0xff);
|
|
|
|
rtc_set_memory(s, info_ofs + 5, 0xc0 | ((heads > 8) << 3));
|
|
|
|
rtc_set_memory(s, info_ofs + 6, cylinders);
|
|
|
|
rtc_set_memory(s, info_ofs + 7, cylinders >> 8);
|
|
|
|
rtc_set_memory(s, info_ofs + 8, sectors);
|
|
|
|
}
|
|
|
|
|
2007-10-31 04:54:04 +03:00
|
|
|
/* convert boot_device letter to something recognizable by the bios */
|
|
|
|
static int boot_device2nibble(char boot_device)
|
|
|
|
{
|
|
|
|
switch(boot_device) {
|
|
|
|
case 'a':
|
|
|
|
case 'b':
|
|
|
|
return 0x01; /* floppy boot */
|
|
|
|
case 'c':
|
|
|
|
return 0x02; /* hard drive boot */
|
|
|
|
case 'd':
|
|
|
|
return 0x03; /* CD-ROM boot */
|
|
|
|
case 'n':
|
|
|
|
return 0x04; /* Network boot */
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-12-03 22:04:02 +03:00
|
|
|
static void set_boot_dev(ISADevice *s, const char *boot_device, Error **errp)
|
2008-05-05 00:11:34 +04:00
|
|
|
{
|
|
|
|
#define PC_MAX_BOOT_DEVICES 3
|
|
|
|
int nbds, bds[3] = { 0, };
|
|
|
|
int i;
|
|
|
|
|
|
|
|
nbds = strlen(boot_device);
|
|
|
|
if (nbds > PC_MAX_BOOT_DEVICES) {
|
2014-12-03 22:04:02 +03:00
|
|
|
error_setg(errp, "Too many boot devices for PC");
|
|
|
|
return;
|
2008-05-05 00:11:34 +04:00
|
|
|
}
|
|
|
|
for (i = 0; i < nbds; i++) {
|
|
|
|
bds[i] = boot_device2nibble(boot_device[i]);
|
|
|
|
if (bds[i] == 0) {
|
2014-12-03 22:04:02 +03:00
|
|
|
error_setg(errp, "Invalid boot device for PC: '%c'",
|
|
|
|
boot_device[i]);
|
|
|
|
return;
|
2008-05-05 00:11:34 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
rtc_set_memory(s, 0x3d, (bds[1] << 4) | bds[0]);
|
2010-02-17 20:07:48 +03:00
|
|
|
rtc_set_memory(s, 0x38, (bds[2] << 4) | (fd_bootchk ? 0x0 : 0x1));
|
2008-05-05 00:11:34 +04:00
|
|
|
}
|
|
|
|
|
2014-12-03 22:04:02 +03:00
|
|
|
static void pc_boot_set(void *opaque, const char *boot_device, Error **errp)
|
2010-02-17 20:07:48 +03:00
|
|
|
{
|
2014-12-03 22:04:02 +03:00
|
|
|
set_boot_dev(opaque, boot_device, errp);
|
2010-02-17 20:07:48 +03:00
|
|
|
}
|
|
|
|
|
2015-06-25 16:35:05 +03:00
|
|
|
static void pc_cmos_init_floppy(ISADevice *rtc_state, ISADevice *floppy)
|
|
|
|
{
|
|
|
|
int val, nb, i;
|
2016-01-22 23:50:56 +03:00
|
|
|
FloppyDriveType fd_type[2] = { FLOPPY_DRIVE_TYPE_NONE,
|
|
|
|
FLOPPY_DRIVE_TYPE_NONE };
|
2015-06-25 16:35:05 +03:00
|
|
|
|
|
|
|
/* floppy type */
|
|
|
|
if (floppy) {
|
|
|
|
for (i = 0; i < 2; i++) {
|
|
|
|
fd_type[i] = isa_fdc_get_drive_type(floppy, i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
val = (cmos_get_fd_drive_type(fd_type[0]) << 4) |
|
|
|
|
cmos_get_fd_drive_type(fd_type[1]);
|
|
|
|
rtc_set_memory(rtc_state, 0x10, val);
|
|
|
|
|
|
|
|
val = rtc_get_memory(rtc_state, REG_EQUIPMENT_BYTE);
|
|
|
|
nb = 0;
|
2016-01-22 23:50:56 +03:00
|
|
|
if (fd_type[0] != FLOPPY_DRIVE_TYPE_NONE) {
|
2015-06-25 16:35:05 +03:00
|
|
|
nb++;
|
|
|
|
}
|
2016-01-22 23:50:56 +03:00
|
|
|
if (fd_type[1] != FLOPPY_DRIVE_TYPE_NONE) {
|
2015-06-25 16:35:05 +03:00
|
|
|
nb++;
|
|
|
|
}
|
|
|
|
switch (nb) {
|
|
|
|
case 0:
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
val |= 0x01; /* 1 drive, ready for boot */
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
val |= 0x41; /* 2 drives, ready for boot */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
rtc_set_memory(rtc_state, REG_EQUIPMENT_BYTE, val);
|
|
|
|
}
|
|
|
|
|
2010-06-24 21:58:20 +04:00
|
|
|
typedef struct pc_cmos_init_late_arg {
|
|
|
|
ISADevice *rtc_state;
|
2012-07-10 13:12:38 +04:00
|
|
|
BusState *idebus[2];
|
2010-06-24 21:58:20 +04:00
|
|
|
} pc_cmos_init_late_arg;
|
|
|
|
|
hw/i386/pc: reflect any FDC @ ioport 0x3f0 in the CMOS
With the pc-q35-2.4 machine type, if the user creates an ISA FDC manually:
-device isa-fdc,driveA=drive-fdc0-0-0 \
-drive file=...,if=none,id=drive-fdc0-0-0,format=raw
then the board-default FDC will be skipped, and only the explicitly
requested FDC will exist. qtree-wise, this is correct; however such an FDC
is currently not registered in the CMOS, because that code is only reached
for the board-default FDC.
The pc_cmos_init_late() one-shot reset handler -- one-shot because the
CMOS is not reprogrammed during warm reset -- should search for any ISA
FDC devices, created implicitly (by board code) or explicitly, and set the
CMOS accordingly to the ISA FDC(s) with iobase=0x3f0:
- if there is no such FDC, report both drives absent,
- if there is exactly one such FDC, report its drives in the CMOS,
- if there are more than one such FDCs, then pick one (it is not specified
which one), and print a warning about the ambiguity.
Cc: Jan Tomko <jtomko@redhat.com>
Cc: John Snow <jsnow@redhat.com>
Cc: Markus Armbruster <armbru@redhat.com>
Cc: Paolo Bonzini <pbonzini@redhat.com>
Reported-by: Jan Tomko <jtomko@redhat.com>
Suggested-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Laszlo Ersek <lersek@redhat.com>
Reviewed-by: John Snow <jsnow@redhat.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
2015-06-25 16:35:06 +03:00
|
|
|
typedef struct check_fdc_state {
|
|
|
|
ISADevice *floppy;
|
|
|
|
bool multiple;
|
|
|
|
} CheckFdcState;
|
|
|
|
|
|
|
|
static int check_fdc(Object *obj, void *opaque)
|
|
|
|
{
|
|
|
|
CheckFdcState *state = opaque;
|
|
|
|
Object *fdc;
|
|
|
|
uint32_t iobase;
|
|
|
|
Error *local_err = NULL;
|
|
|
|
|
|
|
|
fdc = object_dynamic_cast(obj, TYPE_ISA_FDC);
|
|
|
|
if (!fdc) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-06-07 19:36:22 +03:00
|
|
|
iobase = object_property_get_uint(obj, "iobase", &local_err);
|
hw/i386/pc: reflect any FDC @ ioport 0x3f0 in the CMOS
With the pc-q35-2.4 machine type, if the user creates an ISA FDC manually:
-device isa-fdc,driveA=drive-fdc0-0-0 \
-drive file=...,if=none,id=drive-fdc0-0-0,format=raw
then the board-default FDC will be skipped, and only the explicitly
requested FDC will exist. qtree-wise, this is correct; however such an FDC
is currently not registered in the CMOS, because that code is only reached
for the board-default FDC.
The pc_cmos_init_late() one-shot reset handler -- one-shot because the
CMOS is not reprogrammed during warm reset -- should search for any ISA
FDC devices, created implicitly (by board code) or explicitly, and set the
CMOS accordingly to the ISA FDC(s) with iobase=0x3f0:
- if there is no such FDC, report both drives absent,
- if there is exactly one such FDC, report its drives in the CMOS,
- if there are more than one such FDCs, then pick one (it is not specified
which one), and print a warning about the ambiguity.
Cc: Jan Tomko <jtomko@redhat.com>
Cc: John Snow <jsnow@redhat.com>
Cc: Markus Armbruster <armbru@redhat.com>
Cc: Paolo Bonzini <pbonzini@redhat.com>
Reported-by: Jan Tomko <jtomko@redhat.com>
Suggested-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Laszlo Ersek <lersek@redhat.com>
Reviewed-by: John Snow <jsnow@redhat.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
2015-06-25 16:35:06 +03:00
|
|
|
if (local_err || iobase != 0x3f0) {
|
|
|
|
error_free(local_err);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (state->floppy) {
|
|
|
|
state->multiple = true;
|
|
|
|
} else {
|
|
|
|
state->floppy = ISA_DEVICE(obj);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const char * const fdc_container_path[] = {
|
|
|
|
"/unattached", "/peripheral", "/peripheral-anon"
|
|
|
|
};
|
|
|
|
|
2015-12-30 23:11:51 +03:00
|
|
|
/*
|
|
|
|
* Locate the FDC at IO address 0x3f0, in order to configure the CMOS registers
|
|
|
|
* and ACPI objects.
|
|
|
|
*/
|
|
|
|
ISADevice *pc_find_fdc0(void)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
Object *container;
|
|
|
|
CheckFdcState state = { 0 };
|
|
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(fdc_container_path); i++) {
|
|
|
|
container = container_get(qdev_get_machine(), fdc_container_path[i]);
|
|
|
|
object_child_foreach(container, check_fdc, &state);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (state.multiple) {
|
2017-07-12 16:57:41 +03:00
|
|
|
warn_report("multiple floppy disk controllers with "
|
|
|
|
"iobase=0x3f0 have been found");
|
2015-12-18 18:35:24 +03:00
|
|
|
error_printf("the one being picked for CMOS setup might not reflect "
|
2017-09-11 22:52:43 +03:00
|
|
|
"your intent");
|
2015-12-30 23:11:51 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return state.floppy;
|
|
|
|
}
|
|
|
|
|
2010-06-24 21:58:20 +04:00
|
|
|
static void pc_cmos_init_late(void *opaque)
|
|
|
|
{
|
|
|
|
pc_cmos_init_late_arg *arg = opaque;
|
|
|
|
ISADevice *s = arg->rtc_state;
|
2012-07-10 13:12:38 +04:00
|
|
|
int16_t cylinders;
|
|
|
|
int8_t heads, sectors;
|
2010-06-24 21:58:20 +04:00
|
|
|
int val;
|
2012-07-10 13:12:53 +04:00
|
|
|
int i, trans;
|
2010-06-24 21:58:20 +04:00
|
|
|
|
2012-07-10 13:12:38 +04:00
|
|
|
val = 0;
|
2016-11-05 10:19:49 +03:00
|
|
|
if (arg->idebus[0] && ide_get_geometry(arg->idebus[0], 0,
|
|
|
|
&cylinders, &heads, §ors) >= 0) {
|
2012-07-10 13:12:38 +04:00
|
|
|
cmos_init_hd(s, 0x19, 0x1b, cylinders, heads, sectors);
|
|
|
|
val |= 0xf0;
|
|
|
|
}
|
2016-11-05 10:19:49 +03:00
|
|
|
if (arg->idebus[0] && ide_get_geometry(arg->idebus[0], 1,
|
|
|
|
&cylinders, &heads, §ors) >= 0) {
|
2012-07-10 13:12:38 +04:00
|
|
|
cmos_init_hd(s, 0x1a, 0x24, cylinders, heads, sectors);
|
|
|
|
val |= 0x0f;
|
|
|
|
}
|
|
|
|
rtc_set_memory(s, 0x12, val);
|
2010-06-24 21:58:20 +04:00
|
|
|
|
|
|
|
val = 0;
|
|
|
|
for (i = 0; i < 4; i++) {
|
2012-07-10 13:12:38 +04:00
|
|
|
/* NOTE: ide_get_geometry() returns the physical
|
|
|
|
geometry. It is always such that: 1 <= sects <= 63, 1
|
|
|
|
<= heads <= 16, 1 <= cylinders <= 16383. The BIOS
|
|
|
|
geometry can be different if a translation is done. */
|
2016-11-05 10:19:49 +03:00
|
|
|
if (arg->idebus[i / 2] &&
|
|
|
|
ide_get_geometry(arg->idebus[i / 2], i % 2,
|
2012-07-10 13:12:38 +04:00
|
|
|
&cylinders, &heads, §ors) >= 0) {
|
2012-07-10 13:12:53 +04:00
|
|
|
trans = ide_get_bios_chs_trans(arg->idebus[i / 2], i % 2) - 1;
|
|
|
|
assert((trans & ~3) == 0);
|
|
|
|
val |= trans << (i * 2);
|
2010-06-24 21:58:20 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
rtc_set_memory(s, 0x39, val);
|
|
|
|
|
2015-12-30 23:11:51 +03:00
|
|
|
pc_cmos_init_floppy(s, pc_find_fdc0());
|
hw/i386/pc: reflect any FDC @ ioport 0x3f0 in the CMOS
With the pc-q35-2.4 machine type, if the user creates an ISA FDC manually:
-device isa-fdc,driveA=drive-fdc0-0-0 \
-drive file=...,if=none,id=drive-fdc0-0-0,format=raw
then the board-default FDC will be skipped, and only the explicitly
requested FDC will exist. qtree-wise, this is correct; however such an FDC
is currently not registered in the CMOS, because that code is only reached
for the board-default FDC.
The pc_cmos_init_late() one-shot reset handler -- one-shot because the
CMOS is not reprogrammed during warm reset -- should search for any ISA
FDC devices, created implicitly (by board code) or explicitly, and set the
CMOS accordingly to the ISA FDC(s) with iobase=0x3f0:
- if there is no such FDC, report both drives absent,
- if there is exactly one such FDC, report its drives in the CMOS,
- if there are more than one such FDCs, then pick one (it is not specified
which one), and print a warning about the ambiguity.
Cc: Jan Tomko <jtomko@redhat.com>
Cc: John Snow <jsnow@redhat.com>
Cc: Markus Armbruster <armbru@redhat.com>
Cc: Paolo Bonzini <pbonzini@redhat.com>
Reported-by: Jan Tomko <jtomko@redhat.com>
Suggested-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Laszlo Ersek <lersek@redhat.com>
Reviewed-by: John Snow <jsnow@redhat.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
2015-06-25 16:35:06 +03:00
|
|
|
|
2010-06-24 21:58:20 +04:00
|
|
|
qemu_unregister_reset(pc_cmos_init_late, opaque);
|
|
|
|
}
|
|
|
|
|
2015-08-07 22:55:49 +03:00
|
|
|
void pc_cmos_init(PCMachineState *pcms,
|
2015-06-25 16:35:07 +03:00
|
|
|
BusState *idebus0, BusState *idebus1,
|
2011-02-05 19:32:23 +03:00
|
|
|
ISADevice *s)
|
2004-03-14 15:20:30 +03:00
|
|
|
{
|
2015-06-25 16:35:05 +03:00
|
|
|
int val;
|
2010-06-24 21:58:20 +04:00
|
|
|
static pc_cmos_init_late_arg arg;
|
2019-10-22 10:39:50 +03:00
|
|
|
X86MachineState *x86ms = X86_MACHINE(pcms);
|
2004-03-31 22:58:38 +04:00
|
|
|
|
|
|
|
/* various important CMOS locations needed by PC/Bochs bios */
|
2004-03-14 15:20:30 +03:00
|
|
|
|
|
|
|
/* memory size */
|
2012-08-15 15:12:20 +04:00
|
|
|
/* base memory (first MiB) */
|
2019-10-22 10:39:50 +03:00
|
|
|
val = MIN(x86ms->below_4g_mem_size / KiB, 640);
|
2004-04-08 00:51:30 +04:00
|
|
|
rtc_set_memory(s, 0x15, val);
|
|
|
|
rtc_set_memory(s, 0x16, val >> 8);
|
2012-08-15 15:12:20 +04:00
|
|
|
/* extended memory (next 64MiB) */
|
2019-10-22 10:39:50 +03:00
|
|
|
if (x86ms->below_4g_mem_size > 1 * MiB) {
|
|
|
|
val = (x86ms->below_4g_mem_size - 1 * MiB) / KiB;
|
2012-08-15 15:12:20 +04:00
|
|
|
} else {
|
|
|
|
val = 0;
|
|
|
|
}
|
2004-03-14 15:20:30 +03:00
|
|
|
if (val > 65535)
|
|
|
|
val = 65535;
|
2004-03-31 22:58:38 +04:00
|
|
|
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);
|
2012-08-15 15:12:20 +04:00
|
|
|
/* memory between 16MiB and 4GiB */
|
2019-10-22 10:39:50 +03:00
|
|
|
if (x86ms->below_4g_mem_size > 16 * MiB) {
|
|
|
|
val = (x86ms->below_4g_mem_size - 16 * MiB) / (64 * KiB);
|
2012-08-15 15:12:20 +04:00
|
|
|
} else {
|
2004-06-26 19:53:17 +04:00
|
|
|
val = 0;
|
2012-08-15 15:12:20 +04:00
|
|
|
}
|
2004-03-14 15:20:30 +03:00
|
|
|
if (val > 65535)
|
|
|
|
val = 65535;
|
2004-03-31 22:58:38 +04:00
|
|
|
rtc_set_memory(s, 0x34, val);
|
|
|
|
rtc_set_memory(s, 0x35, val >> 8);
|
2012-08-15 15:12:20 +04:00
|
|
|
/* memory above 4GiB */
|
2019-10-22 10:39:50 +03:00
|
|
|
val = x86ms->above_4g_mem_size / 65536;
|
2012-08-15 15:12:20 +04:00
|
|
|
rtc_set_memory(s, 0x5b, val);
|
|
|
|
rtc_set_memory(s, 0x5c, val >> 8);
|
|
|
|
rtc_set_memory(s, 0x5d, val >> 16);
|
2007-09-17 12:09:54 +04:00
|
|
|
|
2015-08-07 22:55:49 +03:00
|
|
|
object_property_add_link(OBJECT(pcms), "rtc_state",
|
2014-10-22 07:24:29 +04:00
|
|
|
TYPE_ISA_DEVICE,
|
2019-10-22 10:39:50 +03:00
|
|
|
(Object **)&x86ms->rtc,
|
2014-10-22 07:24:29 +04:00
|
|
|
object_property_allow_set_link,
|
2018-05-31 22:51:17 +03:00
|
|
|
OBJ_PROP_LINK_STRONG, &error_abort);
|
2015-08-07 22:55:49 +03:00
|
|
|
object_property_set_link(OBJECT(pcms), OBJECT(s),
|
2014-10-22 07:24:29 +04:00
|
|
|
"rtc_state", &error_abort);
|
2008-03-29 01:28:08 +03:00
|
|
|
|
2015-09-11 16:04:45 +03:00
|
|
|
set_boot_dev(s, MACHINE(pcms)->boot_order, &error_fatal);
|
2004-03-14 15:20:30 +03:00
|
|
|
|
2004-03-31 22:58:38 +04:00
|
|
|
val = 0;
|
|
|
|
val |= 0x02; /* FPU is there */
|
|
|
|
val |= 0x04; /* PS/2 mouse installed */
|
|
|
|
rtc_set_memory(s, REG_EQUIPMENT_BYTE, val);
|
|
|
|
|
hw/i386/pc: reflect any FDC @ ioport 0x3f0 in the CMOS
With the pc-q35-2.4 machine type, if the user creates an ISA FDC manually:
-device isa-fdc,driveA=drive-fdc0-0-0 \
-drive file=...,if=none,id=drive-fdc0-0-0,format=raw
then the board-default FDC will be skipped, and only the explicitly
requested FDC will exist. qtree-wise, this is correct; however such an FDC
is currently not registered in the CMOS, because that code is only reached
for the board-default FDC.
The pc_cmos_init_late() one-shot reset handler -- one-shot because the
CMOS is not reprogrammed during warm reset -- should search for any ISA
FDC devices, created implicitly (by board code) or explicitly, and set the
CMOS accordingly to the ISA FDC(s) with iobase=0x3f0:
- if there is no such FDC, report both drives absent,
- if there is exactly one such FDC, report its drives in the CMOS,
- if there are more than one such FDCs, then pick one (it is not specified
which one), and print a warning about the ambiguity.
Cc: Jan Tomko <jtomko@redhat.com>
Cc: John Snow <jsnow@redhat.com>
Cc: Markus Armbruster <armbru@redhat.com>
Cc: Paolo Bonzini <pbonzini@redhat.com>
Reported-by: Jan Tomko <jtomko@redhat.com>
Suggested-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Laszlo Ersek <lersek@redhat.com>
Reviewed-by: John Snow <jsnow@redhat.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
2015-06-25 16:35:06 +03:00
|
|
|
/* hard drives and FDC */
|
2010-06-24 21:58:20 +04:00
|
|
|
arg.rtc_state = s;
|
2012-07-10 13:12:38 +04:00
|
|
|
arg.idebus[0] = idebus0;
|
|
|
|
arg.idebus[1] = idebus1;
|
2010-06-24 21:58:20 +04:00
|
|
|
qemu_register_reset(pc_cmos_init_late, &arg);
|
2004-03-14 15:20:30 +03:00
|
|
|
}
|
|
|
|
|
2010-05-22 11:59:01 +04:00
|
|
|
static void handle_a20_line_change(void *opaque, int irq, int level)
|
2005-11-22 02:34:32 +03:00
|
|
|
{
|
2013-01-18 18:19:06 +04:00
|
|
|
X86CPU *cpu = opaque;
|
2004-04-06 00:26:03 +04:00
|
|
|
|
2010-05-22 11:59:01 +04:00
|
|
|
/* XXX: send to all CPUs ? */
|
2011-01-06 21:24:35 +03:00
|
|
|
/* XXX: add logic to handle multiple A20 line sources */
|
2013-01-18 18:19:06 +04:00
|
|
|
x86_cpu_set_a20(cpu, level);
|
2004-04-06 00:26:03 +04:00
|
|
|
}
|
|
|
|
|
2004-03-15 00:46:48 +03:00
|
|
|
#define NE2000_NB_MAX 6
|
|
|
|
|
2009-09-13 12:32:37 +04:00
|
|
|
static const int ne2000_io[NE2000_NB_MAX] = { 0x300, 0x320, 0x340, 0x360,
|
|
|
|
0x280, 0x380 };
|
|
|
|
static const int ne2000_irq[NE2000_NB_MAX] = { 9, 10, 11, 3, 4, 5 };
|
2004-03-15 00:46:48 +03:00
|
|
|
|
2011-12-16 01:09:51 +04:00
|
|
|
void pc_init_ne2k_isa(ISABus *bus, NICInfo *nd)
|
2006-02-05 07:14:41 +03:00
|
|
|
{
|
|
|
|
static int nb_ne2k = 0;
|
|
|
|
|
|
|
|
if (nb_ne2k == NE2000_NB_MAX)
|
|
|
|
return;
|
2011-12-16 01:09:51 +04:00
|
|
|
isa_ne2000_init(bus, ne2000_io[nb_ne2k],
|
2009-09-10 13:43:33 +04:00
|
|
|
ne2000_irq[nb_ne2k], nd);
|
2006-02-05 07:14:41 +03:00
|
|
|
nb_ne2k++;
|
|
|
|
}
|
|
|
|
|
2010-05-14 11:29:15 +04:00
|
|
|
void pc_acpi_smi_interrupt(void *opaque, int irq, int level)
|
2010-03-29 23:23:52 +04:00
|
|
|
{
|
2013-01-18 18:03:43 +04:00
|
|
|
X86CPU *cpu = opaque;
|
2010-03-29 23:23:52 +04:00
|
|
|
|
|
|
|
if (level) {
|
2013-01-18 18:03:43 +04:00
|
|
|
cpu_interrupt(CPU(cpu), CPU_INTERRUPT_SMI);
|
2010-03-29 23:23:52 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-20 08:45:24 +03:00
|
|
|
/*
|
|
|
|
* 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)
|
|
|
|
{
|
2019-10-22 10:39:50 +03:00
|
|
|
X86MachineState *x86ms = X86_MACHINE(ms);
|
2019-06-20 08:45:25 +03:00
|
|
|
|
2019-06-20 08:45:24 +03:00
|
|
|
if (opts) {
|
|
|
|
unsigned cpus = qemu_opt_get_number(opts, "cpus", 0);
|
|
|
|
unsigned sockets = qemu_opt_get_number(opts, "sockets", 0);
|
2019-06-20 08:45:25 +03:00
|
|
|
unsigned dies = qemu_opt_get_number(opts, "dies", 1);
|
2019-06-20 08:45:24 +03:00
|
|
|
unsigned cores = qemu_opt_get_number(opts, "cores", 0);
|
|
|
|
unsigned threads = qemu_opt_get_number(opts, "threads", 0);
|
|
|
|
|
|
|
|
/* compute missing values, prefer sockets over cores over threads */
|
|
|
|
if (cpus == 0 || sockets == 0) {
|
|
|
|
cores = cores > 0 ? cores : 1;
|
|
|
|
threads = threads > 0 ? threads : 1;
|
|
|
|
if (cpus == 0) {
|
|
|
|
sockets = sockets > 0 ? sockets : 1;
|
2019-06-20 08:45:25 +03:00
|
|
|
cpus = cores * threads * dies * sockets;
|
2019-06-20 08:45:24 +03:00
|
|
|
} else {
|
|
|
|
ms->smp.max_cpus =
|
|
|
|
qemu_opt_get_number(opts, "maxcpus", cpus);
|
2019-06-20 08:45:25 +03:00
|
|
|
sockets = ms->smp.max_cpus / (cores * threads * dies);
|
2019-06-20 08:45:24 +03:00
|
|
|
}
|
|
|
|
} else if (cores == 0) {
|
|
|
|
threads = threads > 0 ? threads : 1;
|
2019-06-20 08:45:25 +03:00
|
|
|
cores = cpus / (sockets * dies * threads);
|
2019-06-20 08:45:24 +03:00
|
|
|
cores = cores > 0 ? cores : 1;
|
|
|
|
} else if (threads == 0) {
|
2019-06-20 08:45:25 +03:00
|
|
|
threads = cpus / (cores * dies * sockets);
|
2019-06-20 08:45:24 +03:00
|
|
|
threads = threads > 0 ? threads : 1;
|
2019-06-20 08:45:25 +03:00
|
|
|
} else if (sockets * dies * cores * threads < cpus) {
|
2019-06-20 08:45:24 +03:00
|
|
|
error_report("cpu topology: "
|
2019-06-20 08:45:25 +03:00
|
|
|
"sockets (%u) * dies (%u) * cores (%u) * threads (%u) < "
|
2019-06-20 08:45:24 +03:00
|
|
|
"smp_cpus (%u)",
|
2019-06-20 08:45:25 +03:00
|
|
|
sockets, dies, cores, threads, cpus);
|
2019-06-20 08:45:24 +03:00
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
ms->smp.max_cpus =
|
|
|
|
qemu_opt_get_number(opts, "maxcpus", cpus);
|
|
|
|
|
|
|
|
if (ms->smp.max_cpus < cpus) {
|
|
|
|
error_report("maxcpus must be equal to or greater than smp");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
2019-06-20 08:45:25 +03:00
|
|
|
if (sockets * dies * cores * threads > ms->smp.max_cpus) {
|
2019-06-20 08:45:24 +03:00
|
|
|
error_report("cpu topology: "
|
2019-06-20 08:45:25 +03:00
|
|
|
"sockets (%u) * dies (%u) * cores (%u) * threads (%u) > "
|
2019-06-20 08:45:24 +03:00
|
|
|
"maxcpus (%u)",
|
2019-06-20 08:45:25 +03:00
|
|
|
sockets, dies, cores, threads,
|
2019-06-20 08:45:24 +03:00
|
|
|
ms->smp.max_cpus);
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
2019-06-20 08:45:25 +03:00
|
|
|
if (sockets * dies * cores * threads != ms->smp.max_cpus) {
|
2019-06-20 08:45:24 +03:00
|
|
|
warn_report("Invalid CPU topology deprecated: "
|
2019-06-20 08:45:25 +03:00
|
|
|
"sockets (%u) * dies (%u) * cores (%u) * threads (%u) "
|
2019-06-20 08:45:24 +03:00
|
|
|
"!= maxcpus (%u)",
|
2019-06-20 08:45:25 +03:00
|
|
|
sockets, dies, cores, threads,
|
2019-06-20 08:45:24 +03:00
|
|
|
ms->smp.max_cpus);
|
|
|
|
}
|
|
|
|
|
|
|
|
ms->smp.cpus = cpus;
|
|
|
|
ms->smp.cores = cores;
|
|
|
|
ms->smp.threads = threads;
|
2019-10-22 10:39:50 +03:00
|
|
|
x86ms->smp_dies = dies;
|
2019-06-20 08:45:24 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if (ms->smp.cpus > 1) {
|
|
|
|
Error *blocker = NULL;
|
|
|
|
error_setg(&blocker, QERR_REPLAY_NOT_SUPPORTED, "smp");
|
|
|
|
replay_add_blocker(blocker);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-18 23:54:20 +03:00
|
|
|
void pc_hot_add_cpu(MachineState *ms, const int64_t id, Error **errp)
|
2013-04-30 20:00:53 +04:00
|
|
|
{
|
2019-09-30 18:26:29 +03:00
|
|
|
X86MachineState *x86ms = X86_MACHINE(ms);
|
|
|
|
int64_t apic_id = x86_cpu_apic_id_from_index(x86ms, id);
|
2015-03-17 19:46:36 +03:00
|
|
|
Error *local_err = NULL;
|
2013-04-30 20:00:53 +04:00
|
|
|
|
2013-05-30 19:09:34 +04:00
|
|
|
if (id < 0) {
|
|
|
|
error_setg(errp, "Invalid CPU id: %" PRIi64, id);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-03-14 23:33:51 +04:00
|
|
|
if (apic_id >= ACPI_CPU_HOTPLUG_ID_LIMIT) {
|
|
|
|
error_setg(errp, "Unable to add CPU: %" PRIi64
|
|
|
|
", resulting APIC ID (%" PRIi64 ") is too large",
|
|
|
|
id, apic_id);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-09-30 18:26:29 +03:00
|
|
|
|
|
|
|
x86_cpu_new(X86_MACHINE(ms), apic_id, &local_err);
|
2015-03-17 19:46:36 +03:00
|
|
|
if (local_err) {
|
|
|
|
error_propagate(errp, local_err);
|
|
|
|
return;
|
|
|
|
}
|
2013-04-30 20:00:53 +04:00
|
|
|
}
|
|
|
|
|
2016-11-16 16:04:41 +03:00
|
|
|
static void rtc_set_cpus_count(ISADevice *rtc, uint16_t cpus_count)
|
|
|
|
{
|
|
|
|
if (cpus_count > 0xff) {
|
|
|
|
/* If the number of CPUs can't be represented in 8 bits, the
|
|
|
|
* BIOS must use "FW_CFG_NB_CPUS". Set RTC field to 0 just
|
|
|
|
* to make old BIOSes fail more predictably.
|
|
|
|
*/
|
|
|
|
rtc_set_memory(rtc, 0x5f, 0);
|
|
|
|
} else {
|
|
|
|
rtc_set_memory(rtc, 0x5f, cpus_count - 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-05-30 13:57:26 +04:00
|
|
|
static
|
2015-12-11 21:42:23 +03:00
|
|
|
void pc_machine_done(Notifier *notifier, void *data)
|
2013-05-30 13:57:26 +04:00
|
|
|
{
|
2015-12-11 21:42:23 +03:00
|
|
|
PCMachineState *pcms = container_of(notifier,
|
|
|
|
PCMachineState, machine_done);
|
2019-10-22 10:39:50 +03:00
|
|
|
X86MachineState *x86ms = X86_MACHINE(pcms);
|
2015-12-11 21:42:23 +03:00
|
|
|
PCIBus *bus = pcms->bus;
|
2015-06-02 14:23:07 +03:00
|
|
|
|
2016-07-14 19:54:31 +03:00
|
|
|
/* set the number of CPUs */
|
2019-10-22 10:39:50 +03:00
|
|
|
rtc_set_cpus_count(x86ms->rtc, x86ms->boot_cpus);
|
2016-07-14 19:54:31 +03:00
|
|
|
|
2015-06-02 14:23:07 +03:00
|
|
|
if (bus) {
|
|
|
|
int extra_hosts = 0;
|
|
|
|
|
|
|
|
QLIST_FOREACH(bus, &bus->child, sibling) {
|
|
|
|
/* look for expander root buses */
|
|
|
|
if (pci_bus_is_root(bus)) {
|
|
|
|
extra_hosts++;
|
|
|
|
}
|
|
|
|
}
|
2019-10-22 10:39:50 +03:00
|
|
|
if (extra_hosts && x86ms->fw_cfg) {
|
2015-06-02 14:23:07 +03:00
|
|
|
uint64_t *val = g_malloc(sizeof(*val));
|
|
|
|
*val = cpu_to_le64(extra_hosts);
|
2019-10-22 10:39:50 +03:00
|
|
|
fw_cfg_add_file(x86ms->fw_cfg,
|
2015-06-02 14:23:07 +03:00
|
|
|
"etc/extra-pci-roots", val, sizeof(*val));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-12-11 21:42:28 +03:00
|
|
|
acpi_setup();
|
2019-10-22 10:39:50 +03:00
|
|
|
if (x86ms->fw_cfg) {
|
|
|
|
fw_cfg_build_smbios(MACHINE(pcms), x86ms->fw_cfg);
|
|
|
|
fw_cfg_build_feature_control(MACHINE(pcms), x86ms->fw_cfg);
|
2016-11-16 16:04:41 +03:00
|
|
|
/* update FW_CFG_NB_CPUS to account for -device added CPUs */
|
2019-10-22 10:39:50 +03:00
|
|
|
fw_cfg_modify_i16(x86ms->fw_cfg, FW_CFG_NB_CPUS, x86ms->boot_cpus);
|
2016-05-24 20:37:18 +03:00
|
|
|
}
|
2016-10-19 15:05:42 +03:00
|
|
|
|
2019-10-22 10:39:50 +03:00
|
|
|
if (x86ms->apic_id_limit > 255 && !xen_enabled()) {
|
2016-10-19 15:05:42 +03:00
|
|
|
IntelIOMMUState *iommu = INTEL_IOMMU_DEVICE(x86_iommu_get_default());
|
|
|
|
|
2018-12-20 08:40:36 +03:00
|
|
|
if (!iommu || !x86_iommu_ir_supported(X86_IOMMU_DEVICE(iommu)) ||
|
2016-10-19 15:05:42 +03:00
|
|
|
iommu->intr_eim != ON_OFF_AUTO_ON) {
|
|
|
|
error_report("current -smp configuration requires "
|
|
|
|
"Extended Interrupt Mode enabled. "
|
|
|
|
"You can add an IOMMU using: "
|
|
|
|
"-device intel-iommu,intremap=on,eim=on");
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
}
|
2013-05-30 13:57:26 +04:00
|
|
|
}
|
|
|
|
|
2015-12-11 21:42:33 +03:00
|
|
|
void pc_guest_info_init(PCMachineState *pcms)
|
2013-05-30 13:57:26 +04:00
|
|
|
{
|
2016-06-16 15:23:48 +03:00
|
|
|
int i;
|
2019-08-09 09:57:22 +03:00
|
|
|
MachineState *ms = MACHINE(pcms);
|
2019-10-22 10:39:50 +03:00
|
|
|
X86MachineState *x86ms = X86_MACHINE(pcms);
|
2013-07-24 19:56:09 +04:00
|
|
|
|
2019-10-22 10:39:50 +03:00
|
|
|
x86ms->apic_xrupt_override = kvm_allows_irq0_override();
|
2019-08-09 09:57:22 +03:00
|
|
|
pcms->numa_nodes = ms->numa_state->num_nodes;
|
2015-12-11 21:42:32 +03:00
|
|
|
pcms->node_mem = g_malloc0(pcms->numa_nodes *
|
|
|
|
sizeof *pcms->node_mem);
|
2019-08-09 09:57:22 +03:00
|
|
|
for (i = 0; i < ms->numa_state->num_nodes; i++) {
|
2019-08-09 09:57:24 +03:00
|
|
|
pcms->node_mem[i] = ms->numa_state->nodes[i].node_mem;
|
2014-05-14 13:43:07 +04:00
|
|
|
}
|
|
|
|
|
2015-12-11 21:42:23 +03:00
|
|
|
pcms->machine_done.notify = pc_machine_done;
|
|
|
|
qemu_add_machine_init_done_notifier(&pcms->machine_done);
|
2013-05-30 13:57:26 +04:00
|
|
|
}
|
|
|
|
|
2013-10-29 16:57:34 +04:00
|
|
|
/* setup pci memory address space mapping into system address space */
|
|
|
|
void pc_pci_as_mapping_init(Object *owner, MemoryRegion *system_memory,
|
|
|
|
MemoryRegion *pci_address_space)
|
2013-07-29 18:47:57 +04:00
|
|
|
{
|
2013-10-29 16:57:34 +04:00
|
|
|
/* Set to lower priority than RAM */
|
|
|
|
memory_region_add_subregion_overlap(system_memory, 0x0,
|
|
|
|
pci_address_space, -1);
|
2013-07-29 18:47:57 +04:00
|
|
|
}
|
|
|
|
|
2015-12-11 21:42:25 +03:00
|
|
|
void xen_load_linux(PCMachineState *pcms)
|
2014-07-07 10:34:35 +04:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
FWCfgState *fw_cfg;
|
2019-09-30 18:26:29 +03:00
|
|
|
PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms);
|
2019-10-22 10:39:50 +03:00
|
|
|
X86MachineState *x86ms = X86_MACHINE(pcms);
|
2014-07-07 10:34:35 +04:00
|
|
|
|
2015-08-07 22:55:53 +03:00
|
|
|
assert(MACHINE(pcms)->kernel_filename != NULL);
|
2014-07-07 10:34:35 +04:00
|
|
|
|
2016-02-19 21:20:26 +03:00
|
|
|
fw_cfg = fw_cfg_init_io(FW_CFG_IO_BASE);
|
2019-10-22 10:39:50 +03:00
|
|
|
fw_cfg_add_i16(fw_cfg, FW_CFG_NB_CPUS, x86ms->boot_cpus);
|
2014-07-07 10:34:35 +04:00
|
|
|
rom_set_fw(fw_cfg);
|
|
|
|
|
2019-09-30 18:26:29 +03:00
|
|
|
x86_load_linux(x86ms, fw_cfg, pcmc->acpi_data_size,
|
|
|
|
pcmc->pvh_enabled, pcmc->linuxboot_dma_enabled);
|
2014-07-07 10:34:35 +04:00
|
|
|
for (i = 0; i < nb_option_roms; i++) {
|
|
|
|
assert(!strcmp(option_rom[i].name, "linuxboot.bin") ||
|
2016-05-23 21:11:33 +03:00
|
|
|
!strcmp(option_rom[i].name, "linuxboot_dma.bin") ||
|
2019-01-18 15:01:42 +03:00
|
|
|
!strcmp(option_rom[i].name, "pvh.bin") ||
|
2014-07-07 10:34:35 +04:00
|
|
|
!strcmp(option_rom[i].name, "multiboot.bin"));
|
|
|
|
rom_add_option(option_rom[i].name, option_rom[i].bootindex);
|
|
|
|
}
|
2019-10-22 10:39:50 +03:00
|
|
|
x86ms->fw_cfg = fw_cfg;
|
2014-07-07 10:34:35 +04:00
|
|
|
}
|
|
|
|
|
2015-12-11 21:42:24 +03:00
|
|
|
void pc_memory_init(PCMachineState *pcms,
|
|
|
|
MemoryRegion *system_memory,
|
|
|
|
MemoryRegion *rom_memory,
|
|
|
|
MemoryRegion **ram_memory)
|
2004-03-14 15:20:30 +03:00
|
|
|
{
|
2012-02-22 11:18:51 +04:00
|
|
|
int linux_boot, i;
|
|
|
|
MemoryRegion *ram, *option_rom_mr;
|
2011-07-26 15:26:17 +04:00
|
|
|
MemoryRegion *ram_below_4g, *ram_above_4g;
|
2013-04-16 04:24:08 +04:00
|
|
|
FWCfgState *fw_cfg;
|
2015-08-07 22:55:50 +03:00
|
|
|
MachineState *machine = MACHINE(pcms);
|
2019-08-19 01:54:06 +03:00
|
|
|
MachineClass *mc = MACHINE_GET_CLASS(machine);
|
2015-12-02 01:58:06 +03:00
|
|
|
PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms);
|
2019-10-22 10:39:50 +03:00
|
|
|
X86MachineState *x86ms = X86_MACHINE(pcms);
|
2005-07-23 23:05:37 +04:00
|
|
|
|
2019-10-22 10:39:50 +03:00
|
|
|
assert(machine->ram_size == x86ms->below_4g_mem_size +
|
|
|
|
x86ms->above_4g_mem_size);
|
2014-06-10 15:15:17 +04:00
|
|
|
|
|
|
|
linux_boot = (machine->kernel_filename != NULL);
|
2004-03-14 15:20:30 +03:00
|
|
|
|
2011-07-26 15:26:17 +04:00
|
|
|
/* Allocate RAM. We allocate it as a single memory region and use
|
2011-11-29 12:52:39 +04:00
|
|
|
* aliases to address portions of it, mostly for backwards compatibility
|
2011-07-26 15:26:17 +04:00
|
|
|
* with older qemus that used qemu_ram_alloc().
|
|
|
|
*/
|
2011-08-21 07:09:37 +04:00
|
|
|
ram = g_malloc(sizeof(*ram));
|
2014-06-10 15:15:17 +04:00
|
|
|
memory_region_allocate_system_memory(ram, NULL, "pc.ram",
|
|
|
|
machine->ram_size);
|
2011-08-15 18:17:38 +04:00
|
|
|
*ram_memory = ram;
|
2011-08-21 07:09:37 +04:00
|
|
|
ram_below_4g = g_malloc(sizeof(*ram_below_4g));
|
2013-06-06 13:41:28 +04:00
|
|
|
memory_region_init_alias(ram_below_4g, NULL, "ram-below-4g", ram,
|
2019-10-22 10:39:50 +03:00
|
|
|
0, x86ms->below_4g_mem_size);
|
2011-07-26 15:26:17 +04:00
|
|
|
memory_region_add_subregion(system_memory, 0, ram_below_4g);
|
2019-10-22 10:39:50 +03:00
|
|
|
e820_add_entry(0, x86ms->below_4g_mem_size, E820_RAM);
|
|
|
|
if (x86ms->above_4g_mem_size > 0) {
|
2011-08-21 07:09:37 +04:00
|
|
|
ram_above_4g = g_malloc(sizeof(*ram_above_4g));
|
2013-06-06 13:41:28 +04:00
|
|
|
memory_region_init_alias(ram_above_4g, NULL, "ram-above-4g", ram,
|
2019-10-22 10:39:50 +03:00
|
|
|
x86ms->below_4g_mem_size,
|
|
|
|
x86ms->above_4g_mem_size);
|
2011-07-26 15:26:17 +04:00
|
|
|
memory_region_add_subregion(system_memory, 0x100000000ULL,
|
|
|
|
ram_above_4g);
|
2019-10-22 10:39:50 +03:00
|
|
|
e820_add_entry(0x100000000ULL, x86ms->above_4g_mem_size, E820_RAM);
|
2010-07-06 20:37:17 +04:00
|
|
|
}
|
2008-09-15 20:01:01 +04:00
|
|
|
|
2015-12-11 21:42:28 +03:00
|
|
|
if (!pcmc->has_reserved_memory &&
|
2014-06-02 17:25:11 +04:00
|
|
|
(machine->ram_slots ||
|
2014-06-10 15:15:17 +04:00
|
|
|
(machine->maxram_size > machine->ram_size))) {
|
2014-06-02 17:25:11 +04:00
|
|
|
|
|
|
|
error_report("\"-memory 'slots|maxmem'\" is not supported by: %s",
|
|
|
|
mc->name);
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
|
2018-04-23 19:51:17 +03:00
|
|
|
/* always allocate the device memory information */
|
|
|
|
machine->device_memory = g_malloc0(sizeof(*machine->device_memory));
|
|
|
|
|
2018-04-23 19:51:24 +03:00
|
|
|
/* initialize device memory address space */
|
2015-12-11 21:42:28 +03:00
|
|
|
if (pcmc->has_reserved_memory &&
|
2014-06-10 15:15:17 +04:00
|
|
|
(machine->ram_size < machine->maxram_size)) {
|
2018-04-23 19:51:24 +03:00
|
|
|
ram_addr_t device_mem_size = machine->maxram_size - machine->ram_size;
|
2014-06-02 17:25:08 +04:00
|
|
|
|
2014-06-02 17:25:09 +04:00
|
|
|
if (machine->ram_slots > ACPI_MAX_RAM_SLOTS) {
|
|
|
|
error_report("unsupported amount of memory slots: %"PRIu64,
|
|
|
|
machine->ram_slots);
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
|
2015-01-29 16:48:41 +03:00
|
|
|
if (QEMU_ALIGN_UP(machine->maxram_size,
|
|
|
|
TARGET_PAGE_SIZE) != machine->maxram_size) {
|
|
|
|
error_report("maximum memory size must by aligned to multiple of "
|
|
|
|
"%d bytes", TARGET_PAGE_SIZE);
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
|
2018-04-23 19:51:17 +03:00
|
|
|
machine->device_memory->base =
|
2019-10-22 10:39:50 +03:00
|
|
|
ROUND_UP(0x100000000ULL + x86ms->above_4g_mem_size, 1 * GiB);
|
2014-06-02 17:25:08 +04:00
|
|
|
|
2015-12-02 01:58:06 +03:00
|
|
|
if (pcmc->enforce_aligned_dimm) {
|
2018-04-23 19:51:24 +03:00
|
|
|
/* size device region assuming 1G page max alignment per slot */
|
2018-06-29 17:22:13 +03:00
|
|
|
device_mem_size += (1 * GiB) * machine->ram_slots;
|
pc: count in 1Gb hugepage alignment when sizing hotplug-memory container
if DIMMs with different size/alignment are interleaved
in creation order, it could lead to hotplug-memory
container fragmentation and following inability to use
all RAM upto maxmem.
For example:
-m 4G,slots=3,maxmem=7G
-object memory-backend-file,id=mem-1,size=256M,mem-path=/pagesize-2MB
-device pc-dimm,id=mem1,memdev=mem-1
-object memory-backend-file,id=mem-2,size=1G,mem-path=/pagesize-1GB
-device pc-dimm,id=mem2,memdev=mem-2
-object memory-backend-file,id=mem-3,size=256M,mem-path=/pagesize-2MB
-device pc-dimm,id=mem3,memdev=mem-3
fragments hotplug-memory container and doesn't allow
to use 1GB hugepage backend to consume remainig 1Gb.
To ease managment factor count in max 1Gb alignment for
each memory slot when sizing hotplug-memory region so
that regadless of fragmentaion it would be possible to
add max aligned DIMM.
Signed-off-by: Igor Mammedov <imammedo@redhat.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
2014-10-31 19:38:42 +03:00
|
|
|
}
|
|
|
|
|
2018-04-23 19:51:24 +03:00
|
|
|
if ((machine->device_memory->base + device_mem_size) <
|
|
|
|
device_mem_size) {
|
2014-06-02 17:25:08 +04:00
|
|
|
error_report("unsupported amount of maximum memory: " RAM_ADDR_FMT,
|
|
|
|
machine->maxram_size);
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
|
2018-04-23 19:51:17 +03:00
|
|
|
memory_region_init(&machine->device_memory->mr, OBJECT(pcms),
|
2018-04-23 19:51:24 +03:00
|
|
|
"device-memory", device_mem_size);
|
2018-04-23 19:51:17 +03:00
|
|
|
memory_region_add_subregion(system_memory, machine->device_memory->base,
|
|
|
|
&machine->device_memory->mr);
|
2014-06-02 17:25:08 +04:00
|
|
|
}
|
2012-02-22 11:18:51 +04:00
|
|
|
|
|
|
|
/* Initialize PC system firmware */
|
2019-03-08 16:14:43 +03:00
|
|
|
pc_system_firmware_init(pcms, rom_memory);
|
2011-07-26 15:26:17 +04:00
|
|
|
|
2011-08-21 07:09:37 +04:00
|
|
|
option_rom_mr = g_malloc(sizeof(*option_rom_mr));
|
2017-07-07 17:42:53 +03:00
|
|
|
memory_region_init_ram(option_rom_mr, NULL, "pc.rom", PC_ROM_SIZE,
|
Fix bad error handling after memory_region_init_ram()
Symptom:
$ qemu-system-x86_64 -m 10000000
Unexpected error in ram_block_add() at /work/armbru/qemu/exec.c:1456:
upstream-qemu: cannot set up guest memory 'pc.ram': Cannot allocate memory
Aborted (core dumped)
Root cause: commit ef701d7 screwed up handling of out-of-memory
conditions. Before the commit, we report the error and exit(1), in
one place, ram_block_add(). The commit lifts the error handling up
the call chain some, to three places. Fine. Except it uses
&error_abort in these places, changing the behavior from exit(1) to
abort(), and thus undoing the work of commit 3922825 "exec: Don't
abort when we can't allocate guest memory".
The three places are:
* memory_region_init_ram()
Commit 4994653 (right after commit ef701d7) lifted the error
handling further, through memory_region_init_ram(), multiplying the
incorrect use of &error_abort. Later on, imitation of existing
(bad) code may have created more.
* memory_region_init_ram_ptr()
The &error_abort is still there.
* memory_region_init_rom_device()
Doesn't need fixing, because commit 33e0eb5 (soon after commit
ef701d7) lifted the error handling further, and in the process
changed it from &error_abort to passing it up the call chain.
Correct, because the callers are realize() methods.
Fix the error handling after memory_region_init_ram() with a
Coccinelle semantic patch:
@r@
expression mr, owner, name, size, err;
position p;
@@
memory_region_init_ram(mr, owner, name, size,
(
- &error_abort
+ &error_fatal
|
err@p
)
);
@script:python@
p << r.p;
@@
print "%s:%s:%s" % (p[0].file, p[0].line, p[0].column)
When the last argument is &error_abort, it gets replaced by
&error_fatal. This is the fix.
If the last argument is anything else, its position is reported. This
lets us check the fix is complete. Four positions get reported:
* ram_backend_memory_alloc()
Error is passed up the call chain, ultimately through
user_creatable_complete(). As far as I can tell, it's callers all
handle the error sanely.
* fsl_imx25_realize(), fsl_imx31_realize(), dp8393x_realize()
DeviceClass.realize() methods, errors handled sanely further up the
call chain.
We're good. Test case again behaves:
$ qemu-system-x86_64 -m 10000000
qemu-system-x86_64: cannot set up guest memory 'pc.ram': Cannot allocate memory
[Exit 1 ]
The next commits will repair the rest of commit ef701d7's damage.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Message-Id: <1441983105-26376-3-git-send-email-armbru@redhat.com>
Reviewed-by: Peter Crosthwaite <crosthwaite.peter@gmail.com>
2015-09-11 17:51:43 +03:00
|
|
|
&error_fatal);
|
pc: make 'pc.rom' readonly when machine has PCI enabled
looking at bios ROM mapping in QEMU it seems that only isapc
(i.e. not PCI enabled machine) requires ROM being mapped as
RW in other cases BIOS is mapped as RO. Do the same for option
ROM 'pc.rom' when machine has PCI enabled.
As useful side-effect pc.rom MemoryRegion stops being
put in vhost memory map (filtered out by vhost_section()),
which reduces number of entries by 1.
Coincidentally it fixes migration failure reported in
"[PATCH V2] vhost: fix a migration failed because of vhost region merge"
where following destination CLI with /sys/module/vhost/parameters/max_mem_regions = 8
export DIMMSCOUNT=6
QEMU -enable-kvm \
-netdev type=tap,id=guest0,vhost=on,script=no,vhostforce \
-device virtio-net-pci,netdev=guest0 \
-m 256,slots=256,maxmem=2G \
`i=0; while [ $i -lt $DIMMSCOUNT ]; do echo \
"-object memory-backend-ram,id=m$i,size=128M \
-device pc-dimm,id=d$i,memdev=m$i"; i=$(($i + 1)); \
done`
will fail to startup with error:
"-device pc-dimm,id=d5,memdev=m5: a used vhost backend has no free memory slots left"
while it's possible to add the 6th DIMM during hotplug
on source.
Issue is caused by the fact that number of entries in vhost map
is bigger on 1 entry, when -device is processed, than
after guest boots up, and that offending entry belongs to
'pc.rom', it's not like vhost intends to do IO in ROM range
so making it RO hides region from vhost and makes number
of entries in vhost memory map at -device/machine_done time
match number of entries after guest boots.
Signed-off-by: Igor Mammedov <imammedo@redhat.com>
Reported-by: Peng Hao <peng.hao2@zte.com.cn>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
2017-07-28 12:09:05 +03:00
|
|
|
if (pcmc->pci_enabled) {
|
|
|
|
memory_region_set_readonly(option_rom_mr, true);
|
|
|
|
}
|
2011-09-21 22:49:29 +04:00
|
|
|
memory_region_add_subregion_overlap(rom_memory,
|
2011-07-26 15:26:17 +04:00
|
|
|
PC_ROM_MIN_VGA,
|
|
|
|
option_rom_mr,
|
|
|
|
1);
|
2009-04-10 00:59:05 +04:00
|
|
|
|
2019-09-16 13:42:42 +03:00
|
|
|
fw_cfg = fw_cfg_arch_create(machine,
|
2019-10-22 10:39:50 +03:00
|
|
|
x86ms->boot_cpus, x86ms->apic_id_limit);
|
2015-10-08 18:02:57 +03:00
|
|
|
|
2010-01-08 17:25:40 +03:00
|
|
|
rom_set_fw(fw_cfg);
|
2009-06-29 17:37:37 +04:00
|
|
|
|
2018-04-23 19:51:17 +03:00
|
|
|
if (pcmc->has_reserved_memory && machine->device_memory->base) {
|
2014-06-02 17:25:10 +04:00
|
|
|
uint64_t *val = g_malloc(sizeof(*val));
|
2015-09-07 14:55:32 +03:00
|
|
|
PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms);
|
2018-04-23 19:51:17 +03:00
|
|
|
uint64_t res_mem_end = machine->device_memory->base;
|
2015-09-07 14:55:32 +03:00
|
|
|
|
|
|
|
if (!pcmc->broken_reserved_end) {
|
2018-04-23 19:51:17 +03:00
|
|
|
res_mem_end += memory_region_size(&machine->device_memory->mr);
|
2015-09-07 14:55:32 +03:00
|
|
|
}
|
2018-06-29 17:22:13 +03:00
|
|
|
*val = cpu_to_le64(ROUND_UP(res_mem_end, 1 * GiB));
|
2014-06-02 17:25:10 +04:00
|
|
|
fw_cfg_add_file(fw_cfg, "etc/reserved-memory-end", val, sizeof(*val));
|
|
|
|
}
|
|
|
|
|
2009-04-10 00:59:05 +04:00
|
|
|
if (linux_boot) {
|
2019-09-30 18:26:29 +03:00
|
|
|
x86_load_linux(x86ms, fw_cfg, pcmc->acpi_data_size,
|
|
|
|
pcmc->pvh_enabled, pcmc->linuxboot_dma_enabled);
|
2009-04-10 00:59:05 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < nb_option_roms; i++) {
|
2010-12-08 14:35:07 +03:00
|
|
|
rom_add_option(option_rom[i].name, option_rom[i].bootindex);
|
2009-06-17 17:05:30 +04:00
|
|
|
}
|
2019-10-22 10:39:50 +03:00
|
|
|
x86ms->fw_cfg = fw_cfg;
|
2016-07-14 08:56:23 +03:00
|
|
|
|
|
|
|
/* Init default IOAPIC address space */
|
2019-10-22 10:39:50 +03:00
|
|
|
x86ms->ioapic_as = &address_space_memory;
|
2019-09-18 16:06:23 +03:00
|
|
|
|
|
|
|
/* Init ACPI memory hotplug IO base address */
|
|
|
|
pcms->memhp_io_base = ACPI_MEMORY_HOTPLUG_BASE;
|
2010-05-14 11:29:11 +04:00
|
|
|
}
|
|
|
|
|
2017-11-11 18:25:00 +03:00
|
|
|
/*
|
|
|
|
* The 64bit pci hole starts after "above 4G RAM" and
|
|
|
|
* potentially the space reserved for memory hotplug.
|
|
|
|
*/
|
|
|
|
uint64_t pc_pci_hole64_start(void)
|
|
|
|
{
|
|
|
|
PCMachineState *pcms = PC_MACHINE(qdev_get_machine());
|
|
|
|
PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms);
|
2018-04-23 19:51:17 +03:00
|
|
|
MachineState *ms = MACHINE(pcms);
|
2019-10-22 10:39:50 +03:00
|
|
|
X86MachineState *x86ms = X86_MACHINE(pcms);
|
2017-11-11 18:25:00 +03:00
|
|
|
uint64_t hole64_start = 0;
|
|
|
|
|
2018-04-23 19:51:17 +03:00
|
|
|
if (pcmc->has_reserved_memory && ms->device_memory->base) {
|
|
|
|
hole64_start = ms->device_memory->base;
|
2017-11-11 18:25:00 +03:00
|
|
|
if (!pcmc->broken_reserved_end) {
|
2018-04-23 19:51:17 +03:00
|
|
|
hole64_start += memory_region_size(&ms->device_memory->mr);
|
2017-11-11 18:25:00 +03:00
|
|
|
}
|
|
|
|
} else {
|
2019-10-22 10:39:50 +03:00
|
|
|
hole64_start = 0x100000000ULL + x86ms->above_4g_mem_size;
|
2017-11-11 18:25:00 +03:00
|
|
|
}
|
|
|
|
|
2018-06-29 17:22:13 +03:00
|
|
|
return ROUND_UP(hole64_start, 1 * GiB);
|
2017-11-11 18:25:00 +03:00
|
|
|
}
|
|
|
|
|
2011-12-16 01:09:51 +04:00
|
|
|
DeviceState *pc_vga_init(ISABus *isa_bus, PCIBus *pci_bus)
|
2010-05-14 11:29:12 +04:00
|
|
|
{
|
2011-12-13 00:29:41 +04:00
|
|
|
DeviceState *dev = NULL;
|
|
|
|
|
2016-04-07 17:12:58 +03:00
|
|
|
rom_set_order_override(FW_CFG_ORDER_OVERRIDE_VGA);
|
2012-09-08 14:47:45 +04:00
|
|
|
if (pci_bus) {
|
|
|
|
PCIDevice *pcidev = pci_vga_init(pci_bus);
|
|
|
|
dev = pcidev ? &pcidev->qdev : NULL;
|
|
|
|
} else if (isa_bus) {
|
|
|
|
ISADevice *isadev = isa_vga_init(isa_bus);
|
2013-06-07 15:49:13 +04:00
|
|
|
dev = isadev ? DEVICE(isadev) : NULL;
|
2010-05-14 11:29:12 +04:00
|
|
|
}
|
2016-04-07 17:12:58 +03:00
|
|
|
rom_reset_order_override();
|
2011-12-13 00:29:41 +04:00
|
|
|
return dev;
|
2010-05-14 11:29:12 +04:00
|
|
|
}
|
|
|
|
|
2012-09-19 15:50:08 +04:00
|
|
|
static const MemoryRegionOps ioport80_io_ops = {
|
|
|
|
.write = ioport80_write,
|
2013-01-09 22:10:22 +04:00
|
|
|
.read = ioport80_read,
|
2012-09-19 15:50:08 +04:00
|
|
|
.endianness = DEVICE_NATIVE_ENDIAN,
|
|
|
|
.impl = {
|
|
|
|
.min_access_size = 1,
|
|
|
|
.max_access_size = 1,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
static const MemoryRegionOps ioportF0_io_ops = {
|
|
|
|
.write = ioportF0_write,
|
2013-01-09 22:10:22 +04:00
|
|
|
.read = ioportF0_read,
|
2012-09-19 15:50:08 +04:00
|
|
|
.endianness = DEVICE_NATIVE_ENDIAN,
|
|
|
|
.impl = {
|
|
|
|
.min_access_size = 1,
|
|
|
|
.max_access_size = 1,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2018-03-09 01:39:46 +03:00
|
|
|
static void pc_superio_init(ISABus *isa_bus, bool create_fdctrl, bool no_vmport)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
DriveInfo *fd[MAX_FD];
|
|
|
|
qemu_irq *a20_line;
|
|
|
|
ISADevice *i8042, *port92, *vmmouse;
|
|
|
|
|
2018-04-20 17:52:46 +03:00
|
|
|
serial_hds_isa_init(isa_bus, 0, MAX_ISA_SERIAL_PORTS);
|
2018-03-09 01:39:46 +03:00
|
|
|
parallel_hds_isa_init(isa_bus, MAX_PARALLEL_PORTS);
|
|
|
|
|
|
|
|
for (i = 0; i < MAX_FD; i++) {
|
|
|
|
fd[i] = drive_get(IF_FLOPPY, 0, i);
|
|
|
|
create_fdctrl |= !!fd[i];
|
|
|
|
}
|
|
|
|
if (create_fdctrl) {
|
|
|
|
fdctrl_init_isa(isa_bus, fd);
|
|
|
|
}
|
|
|
|
|
|
|
|
i8042 = isa_create_simple(isa_bus, "i8042");
|
|
|
|
if (!no_vmport) {
|
|
|
|
vmport_init(isa_bus);
|
|
|
|
vmmouse = isa_try_create(isa_bus, "vmmouse");
|
|
|
|
} else {
|
|
|
|
vmmouse = NULL;
|
|
|
|
}
|
|
|
|
if (vmmouse) {
|
|
|
|
DeviceState *dev = DEVICE(vmmouse);
|
|
|
|
qdev_prop_set_ptr(dev, "ps2_mouse", i8042);
|
|
|
|
qdev_init_nofail(dev);
|
|
|
|
}
|
2019-12-13 13:50:58 +03:00
|
|
|
port92 = isa_create_simple(isa_bus, TYPE_PORT92);
|
2018-03-09 01:39:46 +03:00
|
|
|
|
|
|
|
a20_line = qemu_allocate_irqs(handle_a20_line_change, first_cpu, 2);
|
|
|
|
i8042_setup_a20_line(i8042, a20_line[0]);
|
2019-12-13 13:50:59 +03:00
|
|
|
qdev_connect_gpio_out_named(DEVICE(port92),
|
|
|
|
PORT92_A20_LINE, 0, a20_line[1]);
|
2018-03-09 01:39:46 +03:00
|
|
|
g_free(a20_line);
|
|
|
|
}
|
|
|
|
|
2011-12-16 01:09:51 +04:00
|
|
|
void pc_basic_device_init(ISABus *isa_bus, qemu_irq *gsi,
|
2011-05-03 20:06:54 +04:00
|
|
|
ISADevice **rtc_state,
|
2015-05-28 23:04:08 +03:00
|
|
|
bool create_fdctrl,
|
2013-12-08 13:38:17 +04:00
|
|
|
bool no_vmport,
|
2016-11-05 10:19:50 +03:00
|
|
|
bool has_pit,
|
2016-01-22 18:09:21 +03:00
|
|
|
uint32_t hpet_irqs)
|
2010-05-14 11:29:13 +04:00
|
|
|
{
|
|
|
|
int i;
|
2012-02-01 23:31:41 +04:00
|
|
|
DeviceState *hpet = NULL;
|
|
|
|
int pit_isa_irq = 0;
|
|
|
|
qemu_irq pit_alt_irq = NULL;
|
2010-06-13 16:15:40 +04:00
|
|
|
qemu_irq rtc_irq = NULL;
|
2018-03-09 01:39:46 +03:00
|
|
|
ISADevice *pit = NULL;
|
2012-09-19 15:50:08 +04:00
|
|
|
MemoryRegion *ioport80_io = g_new(MemoryRegion, 1);
|
|
|
|
MemoryRegion *ioportF0_io = g_new(MemoryRegion, 1);
|
2010-05-14 11:29:13 +04:00
|
|
|
|
2013-06-06 13:41:28 +04:00
|
|
|
memory_region_init_io(ioport80_io, NULL, &ioport80_io_ops, NULL, "ioport80", 1);
|
2012-09-19 15:50:08 +04:00
|
|
|
memory_region_add_subregion(isa_bus->address_space_io, 0x80, ioport80_io);
|
2010-05-14 11:29:13 +04:00
|
|
|
|
2013-06-06 13:41:28 +04:00
|
|
|
memory_region_init_io(ioportF0_io, NULL, &ioportF0_io_ops, NULL, "ioportF0", 1);
|
2012-09-19 15:50:08 +04:00
|
|
|
memory_region_add_subregion(isa_bus->address_space_io, 0xf0, ioportF0_io);
|
2010-05-14 11:29:13 +04:00
|
|
|
|
2012-03-02 23:28:49 +04:00
|
|
|
/*
|
|
|
|
* Check if an HPET shall be created.
|
|
|
|
*
|
|
|
|
* Without KVM_CAP_PIT_STATE2, we cannot switch off the in-kernel PIT
|
|
|
|
* when the HPET wants to take over. Thus we have to disable the latter.
|
|
|
|
*/
|
|
|
|
if (!no_hpet && (!kvm_irqchip_in_kernel() || kvm_has_pit_state2())) {
|
2013-12-08 13:38:17 +04:00
|
|
|
/* In order to set property, here not using sysbus_try_create_simple */
|
2013-12-11 04:48:49 +04:00
|
|
|
hpet = qdev_try_create(NULL, TYPE_HPET);
|
2011-02-05 17:35:00 +03:00
|
|
|
if (hpet) {
|
2013-12-08 13:38:17 +04:00
|
|
|
/* For pc-piix-*, hpet's intcap is always IRQ2. For pc-q35-1.7
|
|
|
|
* and earlier, use IRQ2 for compat. Otherwise, use IRQ16~23,
|
|
|
|
* IRQ8 and IRQ2.
|
|
|
|
*/
|
2017-06-07 19:36:24 +03:00
|
|
|
uint8_t compat = object_property_get_uint(OBJECT(hpet),
|
2013-12-08 13:38:17 +04:00
|
|
|
HPET_INTCAP, NULL);
|
|
|
|
if (!compat) {
|
|
|
|
qdev_prop_set_uint32(hpet, HPET_INTCAP, hpet_irqs);
|
|
|
|
}
|
|
|
|
qdev_init_nofail(hpet);
|
|
|
|
sysbus_mmio_map(SYS_BUS_DEVICE(hpet), 0, HPET_BASE);
|
|
|
|
|
2011-10-07 11:19:35 +04:00
|
|
|
for (i = 0; i < GSI_NUM_PINS; i++) {
|
2013-01-20 05:47:33 +04:00
|
|
|
sysbus_connect_irq(SYS_BUS_DEVICE(hpet), i, gsi[i]);
|
2011-02-05 17:35:00 +03:00
|
|
|
}
|
2012-02-01 23:31:41 +04:00
|
|
|
pit_isa_irq = -1;
|
|
|
|
pit_alt_irq = qdev_get_gpio_in(hpet, HPET_LEGACY_PIT_INT);
|
|
|
|
rtc_irq = qdev_get_gpio_in(hpet, HPET_LEGACY_RTC_INT);
|
2010-06-13 16:15:38 +04:00
|
|
|
}
|
2010-05-14 11:29:13 +04:00
|
|
|
}
|
2017-10-17 19:44:16 +03:00
|
|
|
*rtc_state = mc146818_rtc_init(isa_bus, 2000, rtc_irq);
|
2010-06-13 16:15:40 +04:00
|
|
|
|
|
|
|
qemu_register_boot_set(pc_boot_set, *rtc_state);
|
|
|
|
|
2016-11-05 10:19:50 +03:00
|
|
|
if (!xen_enabled() && has_pit) {
|
2015-12-17 19:16:08 +03:00
|
|
|
if (kvm_pit_in_kernel()) {
|
2011-11-14 19:07:01 +04:00
|
|
|
pit = kvm_pit_init(isa_bus, 0x40);
|
|
|
|
} else {
|
2017-10-17 19:44:15 +03:00
|
|
|
pit = i8254_pit_init(isa_bus, 0x40, pit_isa_irq, pit_alt_irq);
|
2011-11-14 19:07:01 +04:00
|
|
|
}
|
|
|
|
if (hpet) {
|
|
|
|
/* connect PIT to output control line of the HPET */
|
2013-06-07 15:49:13 +04:00
|
|
|
qdev_connect_gpio_out(hpet, 0, qdev_get_gpio_in(DEVICE(pit), 0));
|
2011-11-14 19:07:01 +04:00
|
|
|
}
|
|
|
|
pcspk_init(isa_bus, pit);
|
2012-02-01 23:31:41 +04:00
|
|
|
}
|
2010-05-14 11:29:13 +04:00
|
|
|
|
2018-03-09 01:39:23 +03:00
|
|
|
i8257_dma_init(isa_bus, 0);
|
2010-05-14 11:29:13 +04:00
|
|
|
|
2018-03-09 01:39:46 +03:00
|
|
|
/* Super I/O */
|
|
|
|
pc_superio_init(isa_bus, create_fdctrl, no_vmport);
|
2010-05-14 11:29:13 +04:00
|
|
|
}
|
|
|
|
|
2018-03-02 12:29:06 +03:00
|
|
|
void pc_nic_init(PCMachineClass *pcmc, ISABus *isa_bus, PCIBus *pci_bus)
|
2012-11-15 00:54:01 +04:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
2016-04-07 17:12:58 +03:00
|
|
|
rom_set_order_override(FW_CFG_ORDER_OVERRIDE_NIC);
|
2012-11-15 00:54:01 +04:00
|
|
|
for (i = 0; i < nb_nics; i++) {
|
|
|
|
NICInfo *nd = &nd_table[i];
|
2018-03-02 12:29:06 +03:00
|
|
|
const char *model = nd->model ? nd->model : pcmc->default_nic_model;
|
2012-11-15 00:54:01 +04:00
|
|
|
|
2018-03-02 12:29:06 +03:00
|
|
|
if (g_str_equal(model, "ne2k_isa")) {
|
2012-11-15 00:54:01 +04:00
|
|
|
pc_init_ne2k_isa(isa_bus, nd);
|
|
|
|
} else {
|
2018-03-02 12:29:06 +03:00
|
|
|
pci_nic_init_nofail(nd, pci_bus, model, NULL);
|
2012-11-15 00:54:01 +04:00
|
|
|
}
|
|
|
|
}
|
2016-04-07 17:12:58 +03:00
|
|
|
rom_reset_order_override();
|
2012-11-15 00:54:01 +04:00
|
|
|
}
|
|
|
|
|
2019-10-18 16:59:09 +03:00
|
|
|
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 {
|
2019-12-12 16:14:40 +03:00
|
|
|
i8259 = i8259_init(isa_bus, x86_allocate_cpu_irq());
|
2019-10-18 16:59:09 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
for (size_t i = 0; i < ISA_NUM_IRQS; i++) {
|
|
|
|
i8259_irqs[i] = i8259[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
g_free(i8259);
|
|
|
|
}
|
|
|
|
|
2018-06-19 16:41:34 +03:00
|
|
|
static void pc_memory_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
|
|
|
|
Error **errp)
|
|
|
|
{
|
|
|
|
const PCMachineState *pcms = PC_MACHINE(hotplug_dev);
|
2018-08-01 16:34:44 +03:00
|
|
|
const PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms);
|
2019-03-08 21:20:53 +03:00
|
|
|
const MachineState *ms = MACHINE(hotplug_dev);
|
2018-06-19 16:41:34 +03:00
|
|
|
const bool is_nvdimm = object_dynamic_cast(OBJECT(dev), TYPE_NVDIMM);
|
2018-08-01 16:34:44 +03:00
|
|
|
const uint64_t legacy_align = TARGET_PAGE_SIZE;
|
2019-04-07 12:23:14 +03:00
|
|
|
Error *local_err = NULL;
|
2018-06-19 16:41:34 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* When -no-acpi is used with Q35 machine type, no ACPI is built,
|
|
|
|
* but pcms->acpi_dev is still created. Check !acpi_enabled in
|
|
|
|
* addition to cover this case.
|
|
|
|
*/
|
|
|
|
if (!pcms->acpi_dev || !acpi_enabled) {
|
|
|
|
error_setg(errp,
|
|
|
|
"memory hotplug is not enabled: missing acpi device or acpi disabled");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-03-08 21:20:53 +03:00
|
|
|
if (is_nvdimm && !ms->nvdimms_state->is_enabled) {
|
2018-06-19 16:41:34 +03:00
|
|
|
error_setg(errp, "nvdimm is not enabled: missing 'nvdimm' in '-M'");
|
|
|
|
return;
|
|
|
|
}
|
2018-08-01 16:34:41 +03:00
|
|
|
|
2019-04-07 12:23:14 +03:00
|
|
|
hotplug_handler_pre_plug(pcms->acpi_dev, dev, &local_err);
|
|
|
|
if (local_err) {
|
|
|
|
error_propagate(errp, local_err);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-10-05 12:20:12 +03:00
|
|
|
pc_dimm_pre_plug(PC_DIMM(dev), MACHINE(hotplug_dev),
|
2018-08-01 16:34:44 +03:00
|
|
|
pcmc->enforce_aligned_dimm ? NULL : &legacy_align, errp);
|
2018-06-19 16:41:34 +03:00
|
|
|
}
|
|
|
|
|
2018-06-19 16:41:31 +03:00
|
|
|
static void pc_memory_plug(HotplugHandler *hotplug_dev,
|
|
|
|
DeviceState *dev, Error **errp)
|
2014-06-02 17:25:12 +04:00
|
|
|
{
|
|
|
|
Error *local_err = NULL;
|
|
|
|
PCMachineState *pcms = PC_MACHINE(hotplug_dev);
|
2019-03-08 21:20:53 +03:00
|
|
|
MachineState *ms = MACHINE(hotplug_dev);
|
hw/i386: fix nvdimm check error path
Commit e987c37aee1752177906847630d32477da57e705 ("hw/i386: check if
nvdimm is enabled before plugging") introduced a check to reject nvdimm
hotplug if -machine pc,nvdimm=on was not given.
This check executes after pc_dimm_memory_plug() has already completed
and does not reverse the effect of this function in the case of failure.
Perform the check before calling pc_dimm_memory_plug(). This fixes the
following abort:
$ qemu -M accel=kvm -m 1G,slots=4,maxmem=8G \
-object memory-backend-file,id=mem1,share=on,mem-path=nvdimm.dat,size=1G
(qemu) device_add nvdimm,memdev=mem1
nvdimm is not enabled: missing 'nvdimm' in '-M'
(qemu) device_add nvdimm,memdev=mem1
Core dumped
The backtrace is:
#0 0x00007fffdb5b191f in raise () at /lib64/libc.so.6
#1 0x00007fffdb5b351a in abort () at /lib64/libc.so.6
#2 0x00007fffdb5a9da7 in __assert_fail_base () at /lib64/libc.so.6
#3 0x00007fffdb5a9e52 in () at /lib64/libc.so.6
#4 0x000055555577a5fa in qemu_ram_set_idstr (new_block=0x555556747a00, name=<optimized out>, dev=dev@entry=0x555556705590) at qemu/exec.c:1709
#5 0x0000555555a0fe86 in vmstate_register_ram (mr=mr@entry=0x55555673a0e0, dev=dev@entry=0x555556705590) at migration/savevm.c:2293
#6 0x0000555555965088 in pc_dimm_memory_plug (dev=dev@entry=0x555556705590, hpms=hpms@entry=0x5555566bb0e0, mr=mr@entry=0x555556705630, align=<optimized out>, errp=errp@entry=0x7fffffffc660)
at hw/mem/pc-dimm.c:110
#7 0x000055555581d89b in pc_dimm_plug (errp=0x7fffffffc6c0, dev=0x555556705590, hotplug_dev=<optimized out>) at qemu/hw/i386/pc.c:1713
#8 0x000055555581d89b in pc_machine_device_plug_cb (hotplug_dev=<optimized out>, dev=0x555556705590, errp=0x7fffffffc6c0) at qemu/hw/i386/pc.c:2004
#9 0x0000555555914da6 in device_set_realized (obj=<optimized out>, value=<optimized out>, errp=0x7fffffffc7e8) at hw/core/qdev.c:926
Cc: Haozhong Zhang <haozhong.zhang@intel.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Reviewed-by: Eduardo Habkost <ehabkost@redhat.com>
Reviewed-by: Haozhong Zhang <haozhong.zhang@intel.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
2017-06-09 18:16:15 +03:00
|
|
|
bool is_nvdimm = object_dynamic_cast(OBJECT(dev), TYPE_NVDIMM);
|
2014-06-02 17:25:12 +04:00
|
|
|
|
2018-10-05 12:20:12 +03:00
|
|
|
pc_dimm_plug(PC_DIMM(dev), MACHINE(pcms), &local_err);
|
2015-06-29 11:20:23 +03:00
|
|
|
if (local_err) {
|
2014-10-31 19:38:32 +03:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
hw/i386: fix nvdimm check error path
Commit e987c37aee1752177906847630d32477da57e705 ("hw/i386: check if
nvdimm is enabled before plugging") introduced a check to reject nvdimm
hotplug if -machine pc,nvdimm=on was not given.
This check executes after pc_dimm_memory_plug() has already completed
and does not reverse the effect of this function in the case of failure.
Perform the check before calling pc_dimm_memory_plug(). This fixes the
following abort:
$ qemu -M accel=kvm -m 1G,slots=4,maxmem=8G \
-object memory-backend-file,id=mem1,share=on,mem-path=nvdimm.dat,size=1G
(qemu) device_add nvdimm,memdev=mem1
nvdimm is not enabled: missing 'nvdimm' in '-M'
(qemu) device_add nvdimm,memdev=mem1
Core dumped
The backtrace is:
#0 0x00007fffdb5b191f in raise () at /lib64/libc.so.6
#1 0x00007fffdb5b351a in abort () at /lib64/libc.so.6
#2 0x00007fffdb5a9da7 in __assert_fail_base () at /lib64/libc.so.6
#3 0x00007fffdb5a9e52 in () at /lib64/libc.so.6
#4 0x000055555577a5fa in qemu_ram_set_idstr (new_block=0x555556747a00, name=<optimized out>, dev=dev@entry=0x555556705590) at qemu/exec.c:1709
#5 0x0000555555a0fe86 in vmstate_register_ram (mr=mr@entry=0x55555673a0e0, dev=dev@entry=0x555556705590) at migration/savevm.c:2293
#6 0x0000555555965088 in pc_dimm_memory_plug (dev=dev@entry=0x555556705590, hpms=hpms@entry=0x5555566bb0e0, mr=mr@entry=0x555556705630, align=<optimized out>, errp=errp@entry=0x7fffffffc660)
at hw/mem/pc-dimm.c:110
#7 0x000055555581d89b in pc_dimm_plug (errp=0x7fffffffc6c0, dev=0x555556705590, hotplug_dev=<optimized out>) at qemu/hw/i386/pc.c:1713
#8 0x000055555581d89b in pc_machine_device_plug_cb (hotplug_dev=<optimized out>, dev=0x555556705590, errp=0x7fffffffc6c0) at qemu/hw/i386/pc.c:2004
#9 0x0000555555914da6 in device_set_realized (obj=<optimized out>, value=<optimized out>, errp=0x7fffffffc7e8) at hw/core/qdev.c:926
Cc: Haozhong Zhang <haozhong.zhang@intel.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Reviewed-by: Eduardo Habkost <ehabkost@redhat.com>
Reviewed-by: Haozhong Zhang <haozhong.zhang@intel.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
2017-06-09 18:16:15 +03:00
|
|
|
if (is_nvdimm) {
|
2019-03-08 21:20:53 +03:00
|
|
|
nvdimm_plug(ms->nvdimms_state);
|
2016-11-07 14:13:36 +03:00
|
|
|
}
|
|
|
|
|
2018-12-12 12:57:07 +03:00
|
|
|
hotplug_handler_plug(HOTPLUG_HANDLER(pcms->acpi_dev), dev, &error_abort);
|
2014-06-02 17:25:12 +04:00
|
|
|
out:
|
|
|
|
error_propagate(errp, local_err);
|
|
|
|
}
|
|
|
|
|
2018-06-19 16:41:31 +03:00
|
|
|
static void pc_memory_unplug_request(HotplugHandler *hotplug_dev,
|
|
|
|
DeviceState *dev, Error **errp)
|
2015-04-27 11:47:17 +03:00
|
|
|
{
|
|
|
|
Error *local_err = NULL;
|
|
|
|
PCMachineState *pcms = PC_MACHINE(hotplug_dev);
|
|
|
|
|
2017-12-22 04:51:20 +03:00
|
|
|
/*
|
|
|
|
* When -no-acpi is used with Q35 machine type, no ACPI is built,
|
|
|
|
* but pcms->acpi_dev is still created. Check !acpi_enabled in
|
|
|
|
* addition to cover this case.
|
|
|
|
*/
|
|
|
|
if (!pcms->acpi_dev || !acpi_enabled) {
|
2015-04-27 11:47:17 +03:00
|
|
|
error_setg(&local_err,
|
2017-12-22 04:51:20 +03:00
|
|
|
"memory hotplug is not enabled: missing acpi device or acpi disabled");
|
2015-04-27 11:47:17 +03:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2016-10-28 19:35:40 +03:00
|
|
|
if (object_dynamic_cast(OBJECT(dev), TYPE_NVDIMM)) {
|
|
|
|
error_setg(&local_err,
|
|
|
|
"nvdimm device hot unplug is not supported yet.");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2018-12-12 12:57:07 +03:00
|
|
|
hotplug_handler_unplug_request(HOTPLUG_HANDLER(pcms->acpi_dev), dev,
|
|
|
|
&local_err);
|
2015-04-27 11:47:17 +03:00
|
|
|
out:
|
|
|
|
error_propagate(errp, local_err);
|
|
|
|
}
|
|
|
|
|
2018-06-19 16:41:31 +03:00
|
|
|
static void pc_memory_unplug(HotplugHandler *hotplug_dev,
|
|
|
|
DeviceState *dev, Error **errp)
|
2015-04-27 11:47:18 +03:00
|
|
|
{
|
|
|
|
PCMachineState *pcms = PC_MACHINE(hotplug_dev);
|
|
|
|
Error *local_err = NULL;
|
|
|
|
|
2018-12-12 12:57:07 +03:00
|
|
|
hotplug_handler_unplug(HOTPLUG_HANDLER(pcms->acpi_dev), dev, &local_err);
|
2015-04-27 11:47:18 +03:00
|
|
|
if (local_err) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2018-10-05 12:20:12 +03:00
|
|
|
pc_dimm_unplug(PC_DIMM(dev), MACHINE(pcms));
|
qdev: Let the hotplug_handler_unplug() caller delete the device
When unplugging a device, at one point the device will be destroyed
via object_unparent(). This will, one the one hand, unrealize the
removed device hierarchy, and on the other hand, destroy/free the
device hierarchy.
When chaining hotplug handlers, we want to overwrite a bus hotplug
handler by the machine hotplug handler, to be able to perform
some part of the plug/unplug and to forward the calls to the bus hotplug
handler.
For now, the bus hotplug handler would trigger an object_unparent(), not
allowing us to perform some unplug action on a device after we forwarded
the call to the bus hotplug handler. The device would be gone at that
point.
machine_unplug_handler(dev)
/* eventually do unplug stuff */
bus_unplug_handler(dev)
/* dev is gone, we can't do more unplug stuff */
So move the object_unparent() to the original caller of the unplug. For
now, keep the unrealize() at the original places of the
object_unparent(). For implicitly chained hotplug handlers (e.g. pc
code calling acpi hotplug handlers), the object_unparent() has to be
done by the outermost caller. So when calling hotplug_handler_unplug()
from inside an unplug handler, nothing is to be done.
hotplug_handler_unplug(dev) -> calls machine_unplug_handler()
machine_unplug_handler(dev) {
/* eventually do unplug stuff */
bus_unplug_handler(dev) -> calls unrealize(dev)
/* we can do more unplug stuff but device already unrealized */
}
object_unparent(dev)
In the long run, every unplug action should be factored out of the
unrealize() function into the unplug handler (especially for PCI). Then
we can get rid of the additonal unrealize() calls and object_unparent()
will properly unrealize the device hierarchy after the device has been
unplugged.
hotplug_handler_unplug(dev) -> calls machine_unplug_handler()
machine_unplug_handler(dev) {
/* eventually do unplug stuff */
bus_unplug_handler(dev) -> only unplugs, does not unrealize
/* we can do more unplug stuff */
}
object_unparent(dev) -> will unrealize
The original approach was suggested by Igor Mammedov for the PCI
part, but I extended it to all hotplug handlers. I consider this one
step into the right direction.
To summarize:
- object_unparent() on synchronous unplugs is done by common code
-- "Caller of hotplug_handler_unplug"
- object_unparent() on asynchronous unplugs ("unplug requests") has to
be done manually
-- "Caller of hotplug_handler_unplug"
Reviewed-by: Igor Mammedov <imammedo@redhat.com>
Acked-by: Cornelia Huck <cohuck@redhat.com>
Signed-off-by: David Hildenbrand <david@redhat.com>
Message-Id: <20190228122849.4296-2-david@redhat.com>
Reviewed-by: Greg Kurz <groug@kaod.org>
Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
2019-02-28 15:28:47 +03:00
|
|
|
object_property_set_bool(OBJECT(dev), false, "realized", NULL);
|
2015-04-27 11:47:18 +03:00
|
|
|
out:
|
|
|
|
error_propagate(errp, local_err);
|
|
|
|
}
|
|
|
|
|
2016-03-03 17:28:56 +03:00
|
|
|
static int pc_apic_cmp(const void *a, const void *b)
|
|
|
|
{
|
|
|
|
CPUArchId *apic_a = (CPUArchId *)a;
|
|
|
|
CPUArchId *apic_b = (CPUArchId *)b;
|
|
|
|
|
|
|
|
return apic_a->arch_id - apic_b->arch_id;
|
|
|
|
}
|
|
|
|
|
2016-07-06 09:20:39 +03:00
|
|
|
/* returns pointer to CPUArchId descriptor that matches CPU's apic_id
|
2017-02-09 14:08:32 +03:00
|
|
|
* in ms->possible_cpus->cpus, if ms->possible_cpus->cpus has no
|
2016-11-19 22:29:26 +03:00
|
|
|
* entry corresponding to CPU's apic_id returns NULL.
|
2016-07-06 09:20:39 +03:00
|
|
|
*/
|
2017-02-09 14:08:35 +03:00
|
|
|
static CPUArchId *pc_find_cpu_slot(MachineState *ms, uint32_t id, int *idx)
|
2016-07-06 09:20:39 +03:00
|
|
|
{
|
|
|
|
CPUArchId apic_id, *found_cpu;
|
|
|
|
|
2017-02-09 14:08:35 +03:00
|
|
|
apic_id.arch_id = id;
|
2017-02-09 14:08:32 +03:00
|
|
|
found_cpu = bsearch(&apic_id, ms->possible_cpus->cpus,
|
|
|
|
ms->possible_cpus->len, sizeof(*ms->possible_cpus->cpus),
|
2016-07-06 09:20:39 +03:00
|
|
|
pc_apic_cmp);
|
|
|
|
if (found_cpu && idx) {
|
2017-02-09 14:08:32 +03:00
|
|
|
*idx = found_cpu - ms->possible_cpus->cpus;
|
2016-07-06 09:20:39 +03:00
|
|
|
}
|
|
|
|
return found_cpu;
|
|
|
|
}
|
|
|
|
|
2014-10-22 07:24:28 +04:00
|
|
|
static void pc_cpu_plug(HotplugHandler *hotplug_dev,
|
|
|
|
DeviceState *dev, Error **errp)
|
|
|
|
{
|
2016-07-06 09:20:39 +03:00
|
|
|
CPUArchId *found_cpu;
|
2014-10-22 07:24:28 +04:00
|
|
|
Error *local_err = NULL;
|
2017-02-09 14:08:35 +03:00
|
|
|
X86CPU *cpu = X86_CPU(dev);
|
2014-10-22 07:24:28 +04:00
|
|
|
PCMachineState *pcms = PC_MACHINE(hotplug_dev);
|
2019-10-22 10:39:50 +03:00
|
|
|
X86MachineState *x86ms = X86_MACHINE(pcms);
|
2014-10-22 07:24:28 +04:00
|
|
|
|
2016-07-14 19:54:32 +03:00
|
|
|
if (pcms->acpi_dev) {
|
2018-12-12 12:57:07 +03:00
|
|
|
hotplug_handler_plug(HOTPLUG_HANDLER(pcms->acpi_dev), dev, &local_err);
|
2016-07-14 19:54:32 +03:00
|
|
|
if (local_err) {
|
|
|
|
goto out;
|
|
|
|
}
|
2014-10-22 07:24:28 +04:00
|
|
|
}
|
|
|
|
|
2016-11-16 16:04:41 +03:00
|
|
|
/* increment the number of CPUs */
|
2019-10-22 10:39:50 +03:00
|
|
|
x86ms->boot_cpus++;
|
|
|
|
if (x86ms->rtc) {
|
|
|
|
rtc_set_cpus_count(x86ms->rtc, x86ms->boot_cpus);
|
2016-12-30 17:33:11 +03:00
|
|
|
}
|
2019-10-22 10:39:50 +03:00
|
|
|
if (x86ms->fw_cfg) {
|
|
|
|
fw_cfg_modify_i16(x86ms->fw_cfg, FW_CFG_NB_CPUS, x86ms->boot_cpus);
|
2014-10-22 07:24:29 +04:00
|
|
|
}
|
|
|
|
|
2017-02-09 14:08:35 +03:00
|
|
|
found_cpu = pc_find_cpu_slot(MACHINE(pcms), cpu->apic_id, NULL);
|
2017-02-09 14:08:36 +03:00
|
|
|
found_cpu->cpu = OBJECT(dev);
|
2014-10-22 07:24:28 +04:00
|
|
|
out:
|
|
|
|
error_propagate(errp, local_err);
|
|
|
|
}
|
2016-06-14 17:14:02 +03:00
|
|
|
static void pc_cpu_unplug_request_cb(HotplugHandler *hotplug_dev,
|
|
|
|
DeviceState *dev, Error **errp)
|
|
|
|
{
|
2016-07-18 11:31:22 +03:00
|
|
|
int idx = -1;
|
2016-06-14 17:14:02 +03:00
|
|
|
Error *local_err = NULL;
|
2017-02-09 14:08:35 +03:00
|
|
|
X86CPU *cpu = X86_CPU(dev);
|
2016-06-14 17:14:02 +03:00
|
|
|
PCMachineState *pcms = PC_MACHINE(hotplug_dev);
|
|
|
|
|
2017-11-20 20:19:23 +03:00
|
|
|
if (!pcms->acpi_dev) {
|
|
|
|
error_setg(&local_err, "CPU hot unplug not supported without ACPI");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2017-02-09 14:08:35 +03:00
|
|
|
pc_find_cpu_slot(MACHINE(pcms), cpu->apic_id, &idx);
|
2016-07-18 11:31:22 +03:00
|
|
|
assert(idx != -1);
|
|
|
|
if (idx == 0) {
|
|
|
|
error_setg(&local_err, "Boot CPU is unpluggable");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2018-12-12 12:57:07 +03:00
|
|
|
hotplug_handler_unplug_request(HOTPLUG_HANDLER(pcms->acpi_dev), dev,
|
|
|
|
&local_err);
|
2016-06-14 17:14:02 +03:00
|
|
|
if (local_err) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
|
|
|
error_propagate(errp, local_err);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
static void pc_cpu_unplug_cb(HotplugHandler *hotplug_dev,
|
|
|
|
DeviceState *dev, Error **errp)
|
|
|
|
{
|
2016-06-24 16:36:11 +03:00
|
|
|
CPUArchId *found_cpu;
|
2016-06-14 17:14:02 +03:00
|
|
|
Error *local_err = NULL;
|
2017-02-09 14:08:35 +03:00
|
|
|
X86CPU *cpu = X86_CPU(dev);
|
2016-06-14 17:14:02 +03:00
|
|
|
PCMachineState *pcms = PC_MACHINE(hotplug_dev);
|
2019-10-22 10:39:50 +03:00
|
|
|
X86MachineState *x86ms = X86_MACHINE(pcms);
|
2016-06-14 17:14:02 +03:00
|
|
|
|
2018-12-12 12:57:07 +03:00
|
|
|
hotplug_handler_unplug(HOTPLUG_HANDLER(pcms->acpi_dev), dev, &local_err);
|
2016-06-14 17:14:02 +03:00
|
|
|
if (local_err) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2017-02-09 14:08:35 +03:00
|
|
|
found_cpu = pc_find_cpu_slot(MACHINE(pcms), cpu->apic_id, NULL);
|
2016-06-24 16:36:11 +03:00
|
|
|
found_cpu->cpu = NULL;
|
qdev: Let the hotplug_handler_unplug() caller delete the device
When unplugging a device, at one point the device will be destroyed
via object_unparent(). This will, one the one hand, unrealize the
removed device hierarchy, and on the other hand, destroy/free the
device hierarchy.
When chaining hotplug handlers, we want to overwrite a bus hotplug
handler by the machine hotplug handler, to be able to perform
some part of the plug/unplug and to forward the calls to the bus hotplug
handler.
For now, the bus hotplug handler would trigger an object_unparent(), not
allowing us to perform some unplug action on a device after we forwarded
the call to the bus hotplug handler. The device would be gone at that
point.
machine_unplug_handler(dev)
/* eventually do unplug stuff */
bus_unplug_handler(dev)
/* dev is gone, we can't do more unplug stuff */
So move the object_unparent() to the original caller of the unplug. For
now, keep the unrealize() at the original places of the
object_unparent(). For implicitly chained hotplug handlers (e.g. pc
code calling acpi hotplug handlers), the object_unparent() has to be
done by the outermost caller. So when calling hotplug_handler_unplug()
from inside an unplug handler, nothing is to be done.
hotplug_handler_unplug(dev) -> calls machine_unplug_handler()
machine_unplug_handler(dev) {
/* eventually do unplug stuff */
bus_unplug_handler(dev) -> calls unrealize(dev)
/* we can do more unplug stuff but device already unrealized */
}
object_unparent(dev)
In the long run, every unplug action should be factored out of the
unrealize() function into the unplug handler (especially for PCI). Then
we can get rid of the additonal unrealize() calls and object_unparent()
will properly unrealize the device hierarchy after the device has been
unplugged.
hotplug_handler_unplug(dev) -> calls machine_unplug_handler()
machine_unplug_handler(dev) {
/* eventually do unplug stuff */
bus_unplug_handler(dev) -> only unplugs, does not unrealize
/* we can do more unplug stuff */
}
object_unparent(dev) -> will unrealize
The original approach was suggested by Igor Mammedov for the PCI
part, but I extended it to all hotplug handlers. I consider this one
step into the right direction.
To summarize:
- object_unparent() on synchronous unplugs is done by common code
-- "Caller of hotplug_handler_unplug"
- object_unparent() on asynchronous unplugs ("unplug requests") has to
be done manually
-- "Caller of hotplug_handler_unplug"
Reviewed-by: Igor Mammedov <imammedo@redhat.com>
Acked-by: Cornelia Huck <cohuck@redhat.com>
Signed-off-by: David Hildenbrand <david@redhat.com>
Message-Id: <20190228122849.4296-2-david@redhat.com>
Reviewed-by: Greg Kurz <groug@kaod.org>
Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
2019-02-28 15:28:47 +03:00
|
|
|
object_property_set_bool(OBJECT(dev), false, "realized", NULL);
|
2016-06-14 17:14:02 +03:00
|
|
|
|
2016-11-16 16:04:41 +03:00
|
|
|
/* decrement the number of CPUs */
|
2019-10-22 10:39:50 +03:00
|
|
|
x86ms->boot_cpus--;
|
2016-11-16 16:04:41 +03:00
|
|
|
/* Update the number of CPUs in CMOS */
|
2019-10-22 10:39:50 +03:00
|
|
|
rtc_set_cpus_count(x86ms->rtc, x86ms->boot_cpus);
|
|
|
|
fw_cfg_modify_i16(x86ms->fw_cfg, FW_CFG_NB_CPUS, x86ms->boot_cpus);
|
2016-06-14 17:14:02 +03:00
|
|
|
out:
|
|
|
|
error_propagate(errp, local_err);
|
|
|
|
}
|
2014-10-22 07:24:28 +04:00
|
|
|
|
2016-07-06 09:20:40 +03:00
|
|
|
static void pc_cpu_pre_plug(HotplugHandler *hotplug_dev,
|
|
|
|
DeviceState *dev, Error **errp)
|
|
|
|
{
|
|
|
|
int idx;
|
2016-07-25 12:59:23 +03:00
|
|
|
CPUState *cs;
|
2016-07-14 19:54:30 +03:00
|
|
|
CPUArchId *cpu_slot;
|
2016-07-06 09:20:42 +03:00
|
|
|
X86CPUTopoInfo topo;
|
2016-07-06 09:20:40 +03:00
|
|
|
X86CPU *cpu = X86_CPU(dev);
|
2019-06-12 11:40:57 +03:00
|
|
|
CPUX86State *env = &cpu->env;
|
2017-10-10 15:34:39 +03:00
|
|
|
MachineState *ms = MACHINE(hotplug_dev);
|
2016-07-06 09:20:40 +03:00
|
|
|
PCMachineState *pcms = PC_MACHINE(hotplug_dev);
|
2019-10-22 10:39:50 +03:00
|
|
|
X86MachineState *x86ms = X86_MACHINE(pcms);
|
2019-05-18 23:54:25 +03:00
|
|
|
unsigned int smp_cores = ms->smp.cores;
|
|
|
|
unsigned int smp_threads = ms->smp.threads;
|
2016-07-06 09:20:40 +03:00
|
|
|
|
2017-10-10 15:34:39 +03:00
|
|
|
if(!object_dynamic_cast(OBJECT(cpu), ms->cpu_type)) {
|
|
|
|
error_setg(errp, "Invalid CPU type, expected cpu type: '%s'",
|
|
|
|
ms->cpu_type);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-10-22 10:39:50 +03:00
|
|
|
env->nr_dies = x86ms->smp_dies;
|
2019-06-12 11:40:57 +03:00
|
|
|
|
2019-06-12 11:40:56 +03:00
|
|
|
/*
|
|
|
|
* If APIC ID is not set,
|
|
|
|
* set it based on socket/die/core/thread properties.
|
|
|
|
*/
|
2016-07-14 19:54:30 +03:00
|
|
|
if (cpu->apic_id == UNASSIGNED_APIC_ID) {
|
2019-06-12 11:40:56 +03:00
|
|
|
int max_socket = (ms->smp.max_cpus - 1) /
|
2019-10-22 10:39:50 +03:00
|
|
|
smp_threads / smp_cores / x86ms->smp_dies;
|
2016-07-14 19:54:30 +03:00
|
|
|
|
2019-08-16 20:07:50 +03:00
|
|
|
/*
|
|
|
|
* die-id was optional in QEMU 4.0 and older, so keep it optional
|
|
|
|
* if there's only one die per socket.
|
|
|
|
*/
|
2019-10-22 10:39:50 +03:00
|
|
|
if (cpu->die_id < 0 && x86ms->smp_dies == 1) {
|
2019-08-16 20:07:50 +03:00
|
|
|
cpu->die_id = 0;
|
|
|
|
}
|
|
|
|
|
2016-07-14 19:54:30 +03:00
|
|
|
if (cpu->socket_id < 0) {
|
|
|
|
error_setg(errp, "CPU socket-id is not set");
|
|
|
|
return;
|
|
|
|
} else if (cpu->socket_id > max_socket) {
|
|
|
|
error_setg(errp, "Invalid CPU socket-id: %u must be in range 0:%u",
|
|
|
|
cpu->socket_id, max_socket);
|
|
|
|
return;
|
pc: Improve error message when die-id is omitted
The error message when die-id is omitted doesn't make sense:
$ qemu-system-x86_64 -smp 1,sockets=6,maxcpus=6 \
-device qemu64-x86_64-cpu,socket-id=1,core-id=0,thread-id=0
qemu-system-x86_64: -device qemu64-x86_64-cpu,socket-id=1,core-id=0,thread-id=0: \
Invalid CPU die-id: 4294967295 must be in range 0:0
Fix it, so it will now read:
qemu-system-x86_64: -device qemu64-x86_64-cpu,socket-id=1,core-id=0,thread-id=0: \
CPU die-id is not set
Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
Message-Id: <20190815183803.13346-3-ehabkost@redhat.com>
Reviewed-by: Igor Mammedov <imammedo@redhat.com>
Reviewed-by: Vanderson M. do Rosario <vandersonmr2@gmail.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
2019-08-15 21:38:02 +03:00
|
|
|
}
|
|
|
|
if (cpu->die_id < 0) {
|
|
|
|
error_setg(errp, "CPU die-id is not set");
|
|
|
|
return;
|
2019-10-22 10:39:50 +03:00
|
|
|
} else if (cpu->die_id > x86ms->smp_dies - 1) {
|
2019-06-12 11:40:58 +03:00
|
|
|
error_setg(errp, "Invalid CPU die-id: %u must be in range 0:%u",
|
2019-10-22 10:39:50 +03:00
|
|
|
cpu->die_id, x86ms->smp_dies - 1);
|
2019-06-12 11:40:58 +03:00
|
|
|
return;
|
2016-07-14 19:54:30 +03:00
|
|
|
}
|
|
|
|
if (cpu->core_id < 0) {
|
|
|
|
error_setg(errp, "CPU core-id is not set");
|
|
|
|
return;
|
|
|
|
} else if (cpu->core_id > (smp_cores - 1)) {
|
|
|
|
error_setg(errp, "Invalid CPU core-id: %u must be in range 0:%u",
|
|
|
|
cpu->core_id, smp_cores - 1);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (cpu->thread_id < 0) {
|
|
|
|
error_setg(errp, "CPU thread-id is not set");
|
|
|
|
return;
|
|
|
|
} else if (cpu->thread_id > (smp_threads - 1)) {
|
|
|
|
error_setg(errp, "Invalid CPU thread-id: %u must be in range 0:%u",
|
|
|
|
cpu->thread_id, smp_threads - 1);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
topo.pkg_id = cpu->socket_id;
|
2019-06-12 11:40:58 +03:00
|
|
|
topo.die_id = cpu->die_id;
|
2016-07-14 19:54:30 +03:00
|
|
|
topo.core_id = cpu->core_id;
|
|
|
|
topo.smt_id = cpu->thread_id;
|
2019-10-22 10:39:50 +03:00
|
|
|
cpu->apic_id = apicid_from_topo_ids(x86ms->smp_dies, smp_cores,
|
2019-06-12 11:40:59 +03:00
|
|
|
smp_threads, &topo);
|
2016-07-14 19:54:30 +03:00
|
|
|
}
|
|
|
|
|
2017-02-09 14:08:35 +03:00
|
|
|
cpu_slot = pc_find_cpu_slot(MACHINE(pcms), cpu->apic_id, &idx);
|
2016-07-06 09:20:40 +03:00
|
|
|
if (!cpu_slot) {
|
2017-02-09 14:08:32 +03:00
|
|
|
MachineState *ms = MACHINE(pcms);
|
|
|
|
|
2019-10-22 10:39:50 +03:00
|
|
|
x86_topo_ids_from_apicid(cpu->apic_id, x86ms->smp_dies,
|
2019-06-12 11:40:59 +03:00
|
|
|
smp_cores, smp_threads, &topo);
|
|
|
|
error_setg(errp,
|
|
|
|
"Invalid CPU [socket: %u, die: %u, core: %u, thread: %u] with"
|
|
|
|
" APIC ID %" PRIu32 ", valid index range 0:%d",
|
|
|
|
topo.pkg_id, topo.die_id, topo.core_id, topo.smt_id,
|
|
|
|
cpu->apic_id, ms->possible_cpus->len - 1);
|
2016-07-06 09:20:40 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cpu_slot->cpu) {
|
|
|
|
error_setg(errp, "CPU[%d] with APIC ID %" PRIu32 " exists",
|
|
|
|
idx, cpu->apic_id);
|
|
|
|
return;
|
|
|
|
}
|
2016-07-06 09:20:42 +03:00
|
|
|
|
|
|
|
/* if 'address' properties socket-id/core-id/thread-id are not set, set them
|
2017-02-10 13:20:57 +03:00
|
|
|
* so that machine_query_hotpluggable_cpus would show correct values
|
2016-07-06 09:20:42 +03:00
|
|
|
*/
|
|
|
|
/* 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 */
|
2019-10-22 10:39:50 +03:00
|
|
|
x86_topo_ids_from_apicid(cpu->apic_id, x86ms->smp_dies,
|
2019-06-12 11:40:59 +03:00
|
|
|
smp_cores, smp_threads, &topo);
|
2016-07-06 09:20:42 +03:00
|
|
|
if (cpu->socket_id != -1 && cpu->socket_id != topo.pkg_id) {
|
|
|
|
error_setg(errp, "property socket-id: %u doesn't match set apic-id:"
|
|
|
|
" 0x%x (socket-id: %u)", cpu->socket_id, cpu->apic_id, topo.pkg_id);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
cpu->socket_id = topo.pkg_id;
|
|
|
|
|
2019-06-12 11:40:58 +03:00
|
|
|
if (cpu->die_id != -1 && cpu->die_id != topo.die_id) {
|
|
|
|
error_setg(errp, "property die-id: %u doesn't match set apic-id:"
|
|
|
|
" 0x%x (die-id: %u)", cpu->die_id, cpu->apic_id, topo.die_id);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
cpu->die_id = topo.die_id;
|
|
|
|
|
2016-07-06 09:20:42 +03:00
|
|
|
if (cpu->core_id != -1 && cpu->core_id != topo.core_id) {
|
|
|
|
error_setg(errp, "property core-id: %u doesn't match set apic-id:"
|
|
|
|
" 0x%x (core-id: %u)", cpu->core_id, cpu->apic_id, topo.core_id);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
cpu->core_id = topo.core_id;
|
|
|
|
|
|
|
|
if (cpu->thread_id != -1 && cpu->thread_id != topo.smt_id) {
|
|
|
|
error_setg(errp, "property thread-id: %u doesn't match set apic-id:"
|
|
|
|
" 0x%x (thread-id: %u)", cpu->thread_id, cpu->apic_id, topo.smt_id);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
cpu->thread_id = topo.smt_id;
|
2016-07-25 12:59:23 +03:00
|
|
|
|
2019-05-17 17:19:16 +03:00
|
|
|
if (hyperv_feat_enabled(cpu, HYPERV_FEAT_VPINDEX) &&
|
|
|
|
!kvm_hv_vpindex_settable()) {
|
2018-07-02 16:41:56 +03:00
|
|
|
error_setg(errp, "kernel doesn't allow setting HyperV VP_INDEX");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-07-25 12:59:23 +03:00
|
|
|
cs = CPU(cpu);
|
|
|
|
cs->cpu_index = idx;
|
2017-05-10 14:29:47 +03:00
|
|
|
|
2017-05-30 19:23:56 +03:00
|
|
|
numa_cpu_pre_plug(cpu_slot, dev, errp);
|
2016-07-06 09:20:40 +03:00
|
|
|
}
|
|
|
|
|
2019-06-19 12:49:07 +03:00
|
|
|
static void pc_virtio_pmem_pci_pre_plug(HotplugHandler *hotplug_dev,
|
|
|
|
DeviceState *dev, Error **errp)
|
|
|
|
{
|
|
|
|
HotplugHandler *hotplug_dev2 = qdev_get_bus_hotplug_handler(dev);
|
|
|
|
Error *local_err = NULL;
|
|
|
|
|
|
|
|
if (!hotplug_dev2) {
|
|
|
|
/*
|
|
|
|
* Without a bus hotplug handler, we cannot control the plug/unplug
|
|
|
|
* order. This should never be the case on x86, however better add
|
|
|
|
* a safety net.
|
|
|
|
*/
|
|
|
|
error_setg(errp, "virtio-pmem-pci not supported on this bus.");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* First, see if we can plug this memory device at all. If that
|
|
|
|
* succeeds, branch of to the actual hotplug handler.
|
|
|
|
*/
|
|
|
|
memory_device_pre_plug(MEMORY_DEVICE(dev), MACHINE(hotplug_dev), NULL,
|
|
|
|
&local_err);
|
|
|
|
if (!local_err) {
|
|
|
|
hotplug_handler_pre_plug(hotplug_dev2, dev, &local_err);
|
|
|
|
}
|
|
|
|
error_propagate(errp, local_err);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void pc_virtio_pmem_pci_plug(HotplugHandler *hotplug_dev,
|
|
|
|
DeviceState *dev, Error **errp)
|
|
|
|
{
|
|
|
|
HotplugHandler *hotplug_dev2 = qdev_get_bus_hotplug_handler(dev);
|
|
|
|
Error *local_err = NULL;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Plug the memory device first and then branch off to the actual
|
|
|
|
* hotplug handler. If that one fails, we can easily undo the memory
|
|
|
|
* device bits.
|
|
|
|
*/
|
|
|
|
memory_device_plug(MEMORY_DEVICE(dev), MACHINE(hotplug_dev));
|
|
|
|
hotplug_handler_plug(hotplug_dev2, dev, &local_err);
|
|
|
|
if (local_err) {
|
|
|
|
memory_device_unplug(MEMORY_DEVICE(dev), MACHINE(hotplug_dev));
|
|
|
|
}
|
|
|
|
error_propagate(errp, local_err);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void pc_virtio_pmem_pci_unplug_request(HotplugHandler *hotplug_dev,
|
|
|
|
DeviceState *dev, Error **errp)
|
|
|
|
{
|
|
|
|
/* We don't support virtio pmem hot unplug */
|
|
|
|
error_setg(errp, "virtio pmem device unplug not supported.");
|
|
|
|
}
|
|
|
|
|
|
|
|
static void pc_virtio_pmem_pci_unplug(HotplugHandler *hotplug_dev,
|
|
|
|
DeviceState *dev, Error **errp)
|
|
|
|
{
|
|
|
|
/* We don't support virtio pmem hot unplug */
|
|
|
|
}
|
|
|
|
|
2016-07-06 09:20:40 +03:00
|
|
|
static void pc_machine_device_pre_plug_cb(HotplugHandler *hotplug_dev,
|
|
|
|
DeviceState *dev, Error **errp)
|
|
|
|
{
|
2018-06-19 16:41:34 +03:00
|
|
|
if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
|
|
|
|
pc_memory_pre_plug(hotplug_dev, dev, errp);
|
|
|
|
} else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) {
|
2016-07-06 09:20:40 +03:00
|
|
|
pc_cpu_pre_plug(hotplug_dev, dev, errp);
|
2019-06-19 12:49:07 +03:00
|
|
|
} else if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_PMEM_PCI)) {
|
|
|
|
pc_virtio_pmem_pci_pre_plug(hotplug_dev, dev, errp);
|
2016-07-06 09:20:40 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-02 17:25:12 +04:00
|
|
|
static void pc_machine_device_plug_cb(HotplugHandler *hotplug_dev,
|
|
|
|
DeviceState *dev, Error **errp)
|
|
|
|
{
|
|
|
|
if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
|
2018-06-19 16:41:31 +03:00
|
|
|
pc_memory_plug(hotplug_dev, dev, errp);
|
2014-10-22 07:24:28 +04:00
|
|
|
} else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) {
|
|
|
|
pc_cpu_plug(hotplug_dev, dev, errp);
|
2019-06-19 12:49:07 +03:00
|
|
|
} else if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_PMEM_PCI)) {
|
|
|
|
pc_virtio_pmem_pci_plug(hotplug_dev, dev, errp);
|
2014-06-02 17:25:12 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-28 10:45:37 +03:00
|
|
|
static void pc_machine_device_unplug_request_cb(HotplugHandler *hotplug_dev,
|
|
|
|
DeviceState *dev, Error **errp)
|
|
|
|
{
|
2015-04-27 11:47:17 +03:00
|
|
|
if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
|
2018-06-19 16:41:31 +03:00
|
|
|
pc_memory_unplug_request(hotplug_dev, dev, errp);
|
2016-06-14 17:14:02 +03:00
|
|
|
} else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) {
|
|
|
|
pc_cpu_unplug_request_cb(hotplug_dev, dev, errp);
|
2019-06-19 12:49:07 +03:00
|
|
|
} else if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_PMEM_PCI)) {
|
|
|
|
pc_virtio_pmem_pci_unplug_request(hotplug_dev, dev, errp);
|
2015-04-27 11:47:17 +03:00
|
|
|
} else {
|
|
|
|
error_setg(errp, "acpi: device unplug request for not supported device"
|
|
|
|
" type: %s", object_get_typename(OBJECT(dev)));
|
|
|
|
}
|
2015-01-28 10:45:37 +03:00
|
|
|
}
|
|
|
|
|
2015-01-28 10:45:39 +03:00
|
|
|
static void pc_machine_device_unplug_cb(HotplugHandler *hotplug_dev,
|
|
|
|
DeviceState *dev, Error **errp)
|
|
|
|
{
|
2015-04-27 11:47:18 +03:00
|
|
|
if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
|
2018-06-19 16:41:31 +03:00
|
|
|
pc_memory_unplug(hotplug_dev, dev, errp);
|
2016-06-14 17:14:02 +03:00
|
|
|
} else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) {
|
|
|
|
pc_cpu_unplug_cb(hotplug_dev, dev, errp);
|
2019-06-19 12:49:07 +03:00
|
|
|
} else if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_PMEM_PCI)) {
|
|
|
|
pc_virtio_pmem_pci_unplug(hotplug_dev, dev, errp);
|
2015-04-27 11:47:18 +03:00
|
|
|
} else {
|
|
|
|
error_setg(errp, "acpi: device unplug for not supported device"
|
|
|
|
" type: %s", object_get_typename(OBJECT(dev)));
|
|
|
|
}
|
2015-01-28 10:45:39 +03:00
|
|
|
}
|
|
|
|
|
2019-01-24 10:36:26 +03:00
|
|
|
static HotplugHandler *pc_get_hotplug_handler(MachineState *machine,
|
2014-06-02 17:25:12 +04:00
|
|
|
DeviceState *dev)
|
|
|
|
{
|
2014-10-22 07:24:28 +04:00
|
|
|
if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM) ||
|
2019-06-19 12:49:07 +03:00
|
|
|
object_dynamic_cast(OBJECT(dev), TYPE_CPU) ||
|
|
|
|
object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_PMEM_PCI)) {
|
2014-06-02 17:25:12 +04:00
|
|
|
return HOTPLUG_HANDLER(machine);
|
|
|
|
}
|
|
|
|
|
2018-05-10 20:10:56 +03:00
|
|
|
return NULL;
|
2014-06-02 17:25:12 +04:00
|
|
|
}
|
|
|
|
|
2014-06-02 17:25:27 +04:00
|
|
|
static void
|
2018-04-23 19:51:24 +03:00
|
|
|
pc_machine_get_device_memory_region_size(Object *obj, Visitor *v,
|
|
|
|
const char *name, void *opaque,
|
|
|
|
Error **errp)
|
2014-06-02 17:25:27 +04:00
|
|
|
{
|
2018-04-23 19:51:17 +03:00
|
|
|
MachineState *ms = MACHINE(obj);
|
2019-06-24 12:02:00 +03:00
|
|
|
int64_t value = 0;
|
|
|
|
|
|
|
|
if (ms->device_memory) {
|
|
|
|
value = memory_region_size(&ms->device_memory->mr);
|
|
|
|
}
|
2014-06-02 17:25:27 +04:00
|
|
|
|
qapi: Swap visit_* arguments for consistent 'name' placement
JSON uses "name":value, but many of our visitor interfaces were
called with visit_type_FOO(v, &value, name, errp). This can be
a bit confusing to have to mentally swap the parameter order to
match JSON order. It's particularly bad for visit_start_struct(),
where the 'name' parameter is smack in the middle of the
otherwise-related group of 'obj, kind, size' parameters! It's
time to do a global swap of the parameter ordering, so that the
'name' parameter is always immediately after the Visitor argument.
Additional reason in favor of the swap: the existing include/qjson.h
prefers listing 'name' first in json_prop_*(), and I have plans to
unify that file with the qapi visitors; listing 'name' first in
qapi will minimize churn to the (admittedly few) qjson.h clients.
Later patches will then fix docs, object.h, visitor-impl.h, and
those clients to match.
Done by first patching scripts/qapi*.py by hand to make generated
files do what I want, then by running the following Coccinelle
script to affect the rest of the code base:
$ spatch --sp-file script `git grep -l '\bvisit_' -- '**/*.[ch]'`
I then had to apply some touchups (Coccinelle insisted on TAB
indentation in visitor.h, and botched the signature of
visit_type_enum() by rewriting 'const char *const strings[]' to
the syntactically invalid 'const char*const[] strings'). The
movement of parameters is sufficient to provoke compiler errors
if any callers were missed.
// Part 1: Swap declaration order
@@
type TV, TErr, TObj, T1, T2;
identifier OBJ, ARG1, ARG2;
@@
void visit_start_struct
-(TV v, TObj OBJ, T1 ARG1, const char *name, T2 ARG2, TErr errp)
+(TV v, const char *name, TObj OBJ, T1 ARG1, T2 ARG2, TErr errp)
{ ... }
@@
type bool, TV, T1;
identifier ARG1;
@@
bool visit_optional
-(TV v, T1 ARG1, const char *name)
+(TV v, const char *name, T1 ARG1)
{ ... }
@@
type TV, TErr, TObj, T1;
identifier OBJ, ARG1;
@@
void visit_get_next_type
-(TV v, TObj OBJ, T1 ARG1, const char *name, TErr errp)
+(TV v, const char *name, TObj OBJ, T1 ARG1, TErr errp)
{ ... }
@@
type TV, TErr, TObj, T1, T2;
identifier OBJ, ARG1, ARG2;
@@
void visit_type_enum
-(TV v, TObj OBJ, T1 ARG1, T2 ARG2, const char *name, TErr errp)
+(TV v, const char *name, TObj OBJ, T1 ARG1, T2 ARG2, TErr errp)
{ ... }
@@
type TV, TErr, TObj;
identifier OBJ;
identifier VISIT_TYPE =~ "^visit_type_";
@@
void VISIT_TYPE
-(TV v, TObj OBJ, const char *name, TErr errp)
+(TV v, const char *name, TObj OBJ, TErr errp)
{ ... }
// Part 2: swap caller order
@@
expression V, NAME, OBJ, ARG1, ARG2, ERR;
identifier VISIT_TYPE =~ "^visit_type_";
@@
(
-visit_start_struct(V, OBJ, ARG1, NAME, ARG2, ERR)
+visit_start_struct(V, NAME, OBJ, ARG1, ARG2, ERR)
|
-visit_optional(V, ARG1, NAME)
+visit_optional(V, NAME, ARG1)
|
-visit_get_next_type(V, OBJ, ARG1, NAME, ERR)
+visit_get_next_type(V, NAME, OBJ, ARG1, ERR)
|
-visit_type_enum(V, OBJ, ARG1, ARG2, NAME, ERR)
+visit_type_enum(V, NAME, OBJ, ARG1, ARG2, ERR)
|
-VISIT_TYPE(V, OBJ, NAME, ERR)
+VISIT_TYPE(V, NAME, OBJ, ERR)
)
Signed-off-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Message-Id: <1454075341-13658-19-git-send-email-eblake@redhat.com>
Signed-off-by: Markus Armbruster <armbru@redhat.com>
2016-01-29 16:48:54 +03:00
|
|
|
visit_type_int(v, name, &value, errp);
|
2014-06-02 17:25:27 +04:00
|
|
|
}
|
|
|
|
|
qom: Swap 'name' next to visitor in ObjectPropertyAccessor
Similar to the previous patch, it's nice to have all functions
in the tree that involve a visitor and a name for conversion to
or from QAPI to consistently stick the 'name' parameter next
to the Visitor parameter.
Done by manually changing include/qom/object.h and qom/object.c,
then running this Coccinelle script and touching up the fallout
(Coccinelle insisted on adding some trailing whitespace).
@ rule1 @
identifier fn;
typedef Object, Visitor, Error;
identifier obj, v, opaque, name, errp;
@@
void fn
- (Object *obj, Visitor *v, void *opaque, const char *name,
+ (Object *obj, Visitor *v, const char *name, void *opaque,
Error **errp) { ... }
@@
identifier rule1.fn;
expression obj, v, opaque, name, errp;
@@
fn(obj, v,
- opaque, name,
+ name, opaque,
errp)
Signed-off-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Message-Id: <1454075341-13658-20-git-send-email-eblake@redhat.com>
Signed-off-by: Markus Armbruster <armbru@redhat.com>
2016-01-29 16:48:55 +03:00
|
|
|
static void pc_machine_get_vmport(Object *obj, Visitor *v, const char *name,
|
|
|
|
void *opaque, Error **errp)
|
2014-10-04 01:33:37 +04:00
|
|
|
{
|
|
|
|
PCMachineState *pcms = PC_MACHINE(obj);
|
2014-11-21 19:18:52 +03:00
|
|
|
OnOffAuto vmport = pcms->vmport;
|
2014-10-04 01:33:37 +04:00
|
|
|
|
qapi: Swap visit_* arguments for consistent 'name' placement
JSON uses "name":value, but many of our visitor interfaces were
called with visit_type_FOO(v, &value, name, errp). This can be
a bit confusing to have to mentally swap the parameter order to
match JSON order. It's particularly bad for visit_start_struct(),
where the 'name' parameter is smack in the middle of the
otherwise-related group of 'obj, kind, size' parameters! It's
time to do a global swap of the parameter ordering, so that the
'name' parameter is always immediately after the Visitor argument.
Additional reason in favor of the swap: the existing include/qjson.h
prefers listing 'name' first in json_prop_*(), and I have plans to
unify that file with the qapi visitors; listing 'name' first in
qapi will minimize churn to the (admittedly few) qjson.h clients.
Later patches will then fix docs, object.h, visitor-impl.h, and
those clients to match.
Done by first patching scripts/qapi*.py by hand to make generated
files do what I want, then by running the following Coccinelle
script to affect the rest of the code base:
$ spatch --sp-file script `git grep -l '\bvisit_' -- '**/*.[ch]'`
I then had to apply some touchups (Coccinelle insisted on TAB
indentation in visitor.h, and botched the signature of
visit_type_enum() by rewriting 'const char *const strings[]' to
the syntactically invalid 'const char*const[] strings'). The
movement of parameters is sufficient to provoke compiler errors
if any callers were missed.
// Part 1: Swap declaration order
@@
type TV, TErr, TObj, T1, T2;
identifier OBJ, ARG1, ARG2;
@@
void visit_start_struct
-(TV v, TObj OBJ, T1 ARG1, const char *name, T2 ARG2, TErr errp)
+(TV v, const char *name, TObj OBJ, T1 ARG1, T2 ARG2, TErr errp)
{ ... }
@@
type bool, TV, T1;
identifier ARG1;
@@
bool visit_optional
-(TV v, T1 ARG1, const char *name)
+(TV v, const char *name, T1 ARG1)
{ ... }
@@
type TV, TErr, TObj, T1;
identifier OBJ, ARG1;
@@
void visit_get_next_type
-(TV v, TObj OBJ, T1 ARG1, const char *name, TErr errp)
+(TV v, const char *name, TObj OBJ, T1 ARG1, TErr errp)
{ ... }
@@
type TV, TErr, TObj, T1, T2;
identifier OBJ, ARG1, ARG2;
@@
void visit_type_enum
-(TV v, TObj OBJ, T1 ARG1, T2 ARG2, const char *name, TErr errp)
+(TV v, const char *name, TObj OBJ, T1 ARG1, T2 ARG2, TErr errp)
{ ... }
@@
type TV, TErr, TObj;
identifier OBJ;
identifier VISIT_TYPE =~ "^visit_type_";
@@
void VISIT_TYPE
-(TV v, TObj OBJ, const char *name, TErr errp)
+(TV v, const char *name, TObj OBJ, TErr errp)
{ ... }
// Part 2: swap caller order
@@
expression V, NAME, OBJ, ARG1, ARG2, ERR;
identifier VISIT_TYPE =~ "^visit_type_";
@@
(
-visit_start_struct(V, OBJ, ARG1, NAME, ARG2, ERR)
+visit_start_struct(V, NAME, OBJ, ARG1, ARG2, ERR)
|
-visit_optional(V, ARG1, NAME)
+visit_optional(V, NAME, ARG1)
|
-visit_get_next_type(V, OBJ, ARG1, NAME, ERR)
+visit_get_next_type(V, NAME, OBJ, ARG1, ERR)
|
-visit_type_enum(V, OBJ, ARG1, ARG2, NAME, ERR)
+visit_type_enum(V, NAME, OBJ, ARG1, ARG2, ERR)
|
-VISIT_TYPE(V, OBJ, NAME, ERR)
+VISIT_TYPE(V, NAME, OBJ, ERR)
)
Signed-off-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Message-Id: <1454075341-13658-19-git-send-email-eblake@redhat.com>
Signed-off-by: Markus Armbruster <armbru@redhat.com>
2016-01-29 16:48:54 +03:00
|
|
|
visit_type_OnOffAuto(v, name, &vmport, errp);
|
2014-10-04 01:33:37 +04:00
|
|
|
}
|
|
|
|
|
qom: Swap 'name' next to visitor in ObjectPropertyAccessor
Similar to the previous patch, it's nice to have all functions
in the tree that involve a visitor and a name for conversion to
or from QAPI to consistently stick the 'name' parameter next
to the Visitor parameter.
Done by manually changing include/qom/object.h and qom/object.c,
then running this Coccinelle script and touching up the fallout
(Coccinelle insisted on adding some trailing whitespace).
@ rule1 @
identifier fn;
typedef Object, Visitor, Error;
identifier obj, v, opaque, name, errp;
@@
void fn
- (Object *obj, Visitor *v, void *opaque, const char *name,
+ (Object *obj, Visitor *v, const char *name, void *opaque,
Error **errp) { ... }
@@
identifier rule1.fn;
expression obj, v, opaque, name, errp;
@@
fn(obj, v,
- opaque, name,
+ name, opaque,
errp)
Signed-off-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Message-Id: <1454075341-13658-20-git-send-email-eblake@redhat.com>
Signed-off-by: Markus Armbruster <armbru@redhat.com>
2016-01-29 16:48:55 +03:00
|
|
|
static void pc_machine_set_vmport(Object *obj, Visitor *v, const char *name,
|
|
|
|
void *opaque, Error **errp)
|
2014-10-04 01:33:37 +04:00
|
|
|
{
|
|
|
|
PCMachineState *pcms = PC_MACHINE(obj);
|
|
|
|
|
qapi: Swap visit_* arguments for consistent 'name' placement
JSON uses "name":value, but many of our visitor interfaces were
called with visit_type_FOO(v, &value, name, errp). This can be
a bit confusing to have to mentally swap the parameter order to
match JSON order. It's particularly bad for visit_start_struct(),
where the 'name' parameter is smack in the middle of the
otherwise-related group of 'obj, kind, size' parameters! It's
time to do a global swap of the parameter ordering, so that the
'name' parameter is always immediately after the Visitor argument.
Additional reason in favor of the swap: the existing include/qjson.h
prefers listing 'name' first in json_prop_*(), and I have plans to
unify that file with the qapi visitors; listing 'name' first in
qapi will minimize churn to the (admittedly few) qjson.h clients.
Later patches will then fix docs, object.h, visitor-impl.h, and
those clients to match.
Done by first patching scripts/qapi*.py by hand to make generated
files do what I want, then by running the following Coccinelle
script to affect the rest of the code base:
$ spatch --sp-file script `git grep -l '\bvisit_' -- '**/*.[ch]'`
I then had to apply some touchups (Coccinelle insisted on TAB
indentation in visitor.h, and botched the signature of
visit_type_enum() by rewriting 'const char *const strings[]' to
the syntactically invalid 'const char*const[] strings'). The
movement of parameters is sufficient to provoke compiler errors
if any callers were missed.
// Part 1: Swap declaration order
@@
type TV, TErr, TObj, T1, T2;
identifier OBJ, ARG1, ARG2;
@@
void visit_start_struct
-(TV v, TObj OBJ, T1 ARG1, const char *name, T2 ARG2, TErr errp)
+(TV v, const char *name, TObj OBJ, T1 ARG1, T2 ARG2, TErr errp)
{ ... }
@@
type bool, TV, T1;
identifier ARG1;
@@
bool visit_optional
-(TV v, T1 ARG1, const char *name)
+(TV v, const char *name, T1 ARG1)
{ ... }
@@
type TV, TErr, TObj, T1;
identifier OBJ, ARG1;
@@
void visit_get_next_type
-(TV v, TObj OBJ, T1 ARG1, const char *name, TErr errp)
+(TV v, const char *name, TObj OBJ, T1 ARG1, TErr errp)
{ ... }
@@
type TV, TErr, TObj, T1, T2;
identifier OBJ, ARG1, ARG2;
@@
void visit_type_enum
-(TV v, TObj OBJ, T1 ARG1, T2 ARG2, const char *name, TErr errp)
+(TV v, const char *name, TObj OBJ, T1 ARG1, T2 ARG2, TErr errp)
{ ... }
@@
type TV, TErr, TObj;
identifier OBJ;
identifier VISIT_TYPE =~ "^visit_type_";
@@
void VISIT_TYPE
-(TV v, TObj OBJ, const char *name, TErr errp)
+(TV v, const char *name, TObj OBJ, TErr errp)
{ ... }
// Part 2: swap caller order
@@
expression V, NAME, OBJ, ARG1, ARG2, ERR;
identifier VISIT_TYPE =~ "^visit_type_";
@@
(
-visit_start_struct(V, OBJ, ARG1, NAME, ARG2, ERR)
+visit_start_struct(V, NAME, OBJ, ARG1, ARG2, ERR)
|
-visit_optional(V, ARG1, NAME)
+visit_optional(V, NAME, ARG1)
|
-visit_get_next_type(V, OBJ, ARG1, NAME, ERR)
+visit_get_next_type(V, NAME, OBJ, ARG1, ERR)
|
-visit_type_enum(V, OBJ, ARG1, ARG2, NAME, ERR)
+visit_type_enum(V, NAME, OBJ, ARG1, ARG2, ERR)
|
-VISIT_TYPE(V, OBJ, NAME, ERR)
+VISIT_TYPE(V, NAME, OBJ, ERR)
)
Signed-off-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Message-Id: <1454075341-13658-19-git-send-email-eblake@redhat.com>
Signed-off-by: Markus Armbruster <armbru@redhat.com>
2016-01-29 16:48:54 +03:00
|
|
|
visit_type_OnOffAuto(v, name, &pcms->vmport, errp);
|
2014-10-04 01:33:37 +04:00
|
|
|
}
|
|
|
|
|
2016-11-05 10:19:48 +03:00
|
|
|
static bool pc_machine_get_smbus(Object *obj, Error **errp)
|
|
|
|
{
|
|
|
|
PCMachineState *pcms = PC_MACHINE(obj);
|
|
|
|
|
2018-11-07 18:24:34 +03:00
|
|
|
return pcms->smbus_enabled;
|
2016-11-05 10:19:48 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static void pc_machine_set_smbus(Object *obj, bool value, Error **errp)
|
|
|
|
{
|
|
|
|
PCMachineState *pcms = PC_MACHINE(obj);
|
|
|
|
|
2018-11-07 18:24:34 +03:00
|
|
|
pcms->smbus_enabled = value;
|
2016-11-05 10:19:48 +03:00
|
|
|
}
|
|
|
|
|
2016-11-05 10:19:49 +03:00
|
|
|
static bool pc_machine_get_sata(Object *obj, Error **errp)
|
|
|
|
{
|
|
|
|
PCMachineState *pcms = PC_MACHINE(obj);
|
|
|
|
|
2018-11-07 18:24:34 +03:00
|
|
|
return pcms->sata_enabled;
|
2016-11-05 10:19:49 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static void pc_machine_set_sata(Object *obj, bool value, Error **errp)
|
|
|
|
{
|
|
|
|
PCMachineState *pcms = PC_MACHINE(obj);
|
|
|
|
|
2018-11-07 18:24:34 +03:00
|
|
|
pcms->sata_enabled = value;
|
2016-11-05 10:19:49 +03:00
|
|
|
}
|
|
|
|
|
2016-11-05 10:19:50 +03:00
|
|
|
static bool pc_machine_get_pit(Object *obj, Error **errp)
|
|
|
|
{
|
|
|
|
PCMachineState *pcms = PC_MACHINE(obj);
|
|
|
|
|
2018-11-07 18:24:34 +03:00
|
|
|
return pcms->pit_enabled;
|
2016-11-05 10:19:50 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static void pc_machine_set_pit(Object *obj, bool value, Error **errp)
|
|
|
|
{
|
|
|
|
PCMachineState *pcms = PC_MACHINE(obj);
|
|
|
|
|
2018-11-07 18:24:34 +03:00
|
|
|
pcms->pit_enabled = value;
|
2016-11-05 10:19:50 +03:00
|
|
|
}
|
|
|
|
|
2014-06-02 17:25:27 +04:00
|
|
|
static void pc_machine_initfn(Object *obj)
|
|
|
|
{
|
2014-06-20 05:40:25 +04:00
|
|
|
PCMachineState *pcms = PC_MACHINE(obj);
|
|
|
|
|
2019-07-12 19:02:57 +03:00
|
|
|
#ifdef CONFIG_VMPORT
|
2014-11-21 19:18:52 +03:00
|
|
|
pcms->vmport = ON_OFF_AUTO_AUTO;
|
2019-07-12 19:02:57 +03:00
|
|
|
#else
|
|
|
|
pcms->vmport = ON_OFF_AUTO_OFF;
|
|
|
|
#endif /* CONFIG_VMPORT */
|
2016-11-01 20:44:16 +03:00
|
|
|
/* acpi build is enabled by default if machine supports it */
|
|
|
|
pcms->acpi_build_enabled = PC_MACHINE_GET_CLASS(pcms)->has_acpi_build;
|
2018-11-07 18:24:34 +03:00
|
|
|
pcms->smbus_enabled = true;
|
|
|
|
pcms->sata_enabled = true;
|
|
|
|
pcms->pit_enabled = true;
|
pc: Support firmware configuration with -blockdev
The PC machines put firmware in ROM by default. To get it put into
flash memory (required by OVMF), you have to use -drive
if=pflash,unit=0,... and optionally -drive if=pflash,unit=1,...
Why two -drive? This permits setting up one part of the flash memory
read-only, and the other part read/write. It also makes upgrading
firmware on the host easier. Below the hood, it creates two separate
flash devices, because we were too lazy to improve our flash device
models to support sector protection.
The problem at hand is to do the same with -blockdev somehow, as one
more step towards deprecating -drive.
Mapping -drive if=none,... to -blockdev is a solved problem. With
if=T other than if=none, -drive additionally configures a block device
frontend. For non-onboard devices, that part maps to -device. Also a
solved problem. For onboard devices such as PC flash memory, we have
an unsolved problem.
This is actually an instance of a wider problem: our general device
configuration interface doesn't cover onboard devices. Instead, we have
a zoo of ad hoc interfaces that are much more limited. One of them is
-drive, which we'd rather deprecate, but can't until we have suitable
replacements for all its uses.
Sadly, I can't attack the wider problem today. So back to the narrow
problem.
My first idea was to reduce it to its solved buddy by using pluggable
instead of onboard devices for the flash memory. Workable, but it
requires some extra smarts in firmware descriptors and libvirt. Paolo
had an idea that is simpler for libvirt: keep the devices onboard, and
add machine properties for their block backends.
The implementation is less than straightforward, I'm afraid.
First, block backend properties are *qdev* properties. Machines can't
have those, as they're not devices. I could duplicate these qdev
properties as QOM properties, but I hate that.
More seriously, the properties do not belong to the machine, they
belong to the onboard flash devices. Adding them to the machine would
then require bad magic to somehow transfer them to the flash devices.
Fortunately, QOM provides the means to handle exactly this case: add
alias properties to the machine that forward to the onboard devices'
properties.
Properties need to be created in .instance_init() methods. For PC
machines, that's pc_machine_initfn(). To make alias properties work,
we need to create the onboard flash devices there, too. Requires
several bug fixes, in the previous commits. We also have to realize
the devices. More on that below.
If the user sets pflash0, firmware resides in flash memory.
pc_system_firmware_init() maps and realizes the flash devices.
Else, firmware resides in ROM. The onboard flash devices aren't used
then. pc_system_firmware_init() destroys them unrealized, along with
the alias properties.
The existing code to pick up drives defined with -drive if=pflash is
replaced by code to desugar into the machine properties.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
Acked-by: Laszlo Ersek <lersek@redhat.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Message-Id: <87ftrtux81.fsf@dusky.pond.sub.org>
2019-03-11 20:39:26 +03:00
|
|
|
|
|
|
|
pc_system_flash_create(pcms);
|
2014-06-02 17:25:27 +04:00
|
|
|
}
|
|
|
|
|
2019-05-18 23:54:20 +03:00
|
|
|
static void pc_machine_reset(MachineState *machine)
|
2015-09-16 12:19:13 +03:00
|
|
|
{
|
|
|
|
CPUState *cs;
|
|
|
|
X86CPU *cpu;
|
|
|
|
|
|
|
|
qemu_devices_reset();
|
|
|
|
|
|
|
|
/* Reset APIC after devices have been reset to cancel
|
|
|
|
* any changes that qemu_devices_reset() might have done.
|
|
|
|
*/
|
|
|
|
CPU_FOREACH(cs) {
|
|
|
|
cpu = X86_CPU(cs);
|
|
|
|
|
|
|
|
if (cpu->apic_state) {
|
|
|
|
device_reset(cpu->apic_state);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-22 09:17:51 +03:00
|
|
|
static void pc_machine_wakeup(MachineState *machine)
|
|
|
|
{
|
|
|
|
cpu_synchronize_all_states();
|
|
|
|
pc_machine_reset(machine);
|
|
|
|
cpu_synchronize_all_post_reset();
|
|
|
|
}
|
|
|
|
|
2019-09-16 11:07:17 +03:00
|
|
|
static bool pc_hotplug_allowed(MachineState *ms, DeviceState *dev, Error **errp)
|
|
|
|
{
|
|
|
|
X86IOMMUState *iommu = x86_iommu_get_default();
|
|
|
|
IntelIOMMUState *intel_iommu;
|
|
|
|
|
|
|
|
if (iommu &&
|
|
|
|
object_dynamic_cast((Object *)iommu, TYPE_INTEL_IOMMU_DEVICE) &&
|
|
|
|
object_dynamic_cast((Object *)dev, "vfio-pci")) {
|
|
|
|
intel_iommu = INTEL_IOMMU_DEVICE(iommu);
|
|
|
|
if (!intel_iommu->caching_mode) {
|
|
|
|
error_setg(errp, "Device assignment is not allowed without "
|
|
|
|
"enabling caching-mode=on for Intel IOMMU.");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-06-02 17:25:12 +04:00
|
|
|
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);
|
|
|
|
|
2015-12-02 01:58:03 +03:00
|
|
|
pcmc->pci_enabled = true;
|
|
|
|
pcmc->has_acpi_build = true;
|
|
|
|
pcmc->rsdp_in_ram = true;
|
|
|
|
pcmc->smbios_defaults = true;
|
|
|
|
pcmc->smbios_uuid_encoded = true;
|
|
|
|
pcmc->gigabyte_align = true;
|
|
|
|
pcmc->has_reserved_memory = true;
|
|
|
|
pcmc->kvmclock_enabled = true;
|
2015-12-02 01:58:06 +03:00
|
|
|
pcmc->enforce_aligned_dimm = true;
|
2015-12-02 01:58:05 +03:00
|
|
|
/* BIOS ACPI tables: 128K. Other BIOS datastructures: less than 4K reported
|
|
|
|
* to be used at the moment, 32K should be enough for a while. */
|
|
|
|
pcmc->acpi_data_size = 0x20000 + 0x8000;
|
2017-04-25 18:37:50 +03:00
|
|
|
pcmc->linuxboot_dma_enabled = true;
|
2019-01-22 15:10:48 +03:00
|
|
|
pcmc->pvh_enabled = true;
|
2018-05-10 20:10:56 +03:00
|
|
|
assert(!mc->get_hotplug_handler);
|
2019-01-24 10:36:26 +03:00
|
|
|
mc->get_hotplug_handler = pc_get_hotplug_handler;
|
2019-09-16 11:07:17 +03:00
|
|
|
mc->hotplug_allowed = pc_hotplug_allowed;
|
2019-09-26 16:26:16 +03:00
|
|
|
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;
|
2017-11-14 05:34:01 +03:00
|
|
|
mc->auto_enable_numa_with_memhp = true;
|
2017-02-10 13:20:57 +03:00
|
|
|
mc->has_hotpluggable_cpus = true;
|
2015-08-07 22:55:47 +03:00
|
|
|
mc->default_boot_order = "cad";
|
2015-08-07 22:55:48 +03:00
|
|
|
mc->hot_add_cpu = pc_hot_add_cpu;
|
2019-06-20 08:45:24 +03:00
|
|
|
mc->smp_parse = pc_smp_parse;
|
2017-02-15 13:05:40 +03:00
|
|
|
mc->block_default_type = IF_IDE;
|
2015-08-07 22:55:48 +03:00
|
|
|
mc->max_cpus = 255;
|
2015-09-16 12:19:13 +03:00
|
|
|
mc->reset = pc_machine_reset;
|
2019-07-22 09:17:51 +03:00
|
|
|
mc->wakeup = pc_machine_wakeup;
|
2016-07-06 09:20:40 +03:00
|
|
|
hc->pre_plug = pc_machine_device_pre_plug_cb;
|
2014-06-02 17:25:12 +04:00
|
|
|
hc->plug = pc_machine_device_plug_cb;
|
2015-01-28 10:45:37 +03:00
|
|
|
hc->unplug_request = pc_machine_device_unplug_request_cb;
|
2015-01-28 10:45:39 +03:00
|
|
|
hc->unplug = pc_machine_device_unplug_cb;
|
2017-09-13 19:04:56 +03:00
|
|
|
mc->default_cpu_type = TARGET_DEFAULT_CPU_TYPE;
|
2019-03-08 21:20:53 +03:00
|
|
|
mc->nvdimm_supported = true;
|
2019-06-10 16:10:07 +03:00
|
|
|
mc->numa_mem_supported = true;
|
2016-10-13 23:48:36 +03:00
|
|
|
|
2018-04-23 19:51:24 +03:00
|
|
|
object_class_property_add(oc, PC_MACHINE_DEVMEM_REGION_SIZE, "int",
|
|
|
|
pc_machine_get_device_memory_region_size, NULL,
|
2016-10-13 23:48:36 +03:00
|
|
|
NULL, NULL, &error_abort);
|
|
|
|
|
|
|
|
object_class_property_add(oc, PC_MACHINE_VMPORT, "OnOffAuto",
|
|
|
|
pc_machine_get_vmport, pc_machine_set_vmport,
|
|
|
|
NULL, NULL, &error_abort);
|
|
|
|
object_class_property_set_description(oc, PC_MACHINE_VMPORT,
|
|
|
|
"Enable vmport (pc & q35)", &error_abort);
|
|
|
|
|
2016-11-05 10:19:48 +03:00
|
|
|
object_class_property_add_bool(oc, PC_MACHINE_SMBUS,
|
|
|
|
pc_machine_get_smbus, pc_machine_set_smbus, &error_abort);
|
2016-11-05 10:19:49 +03:00
|
|
|
|
|
|
|
object_class_property_add_bool(oc, PC_MACHINE_SATA,
|
|
|
|
pc_machine_get_sata, pc_machine_set_sata, &error_abort);
|
2016-11-05 10:19:50 +03:00
|
|
|
|
|
|
|
object_class_property_add_bool(oc, PC_MACHINE_PIT,
|
|
|
|
pc_machine_get_pit, pc_machine_set_pit, &error_abort);
|
2014-06-02 17:25:12 +04:00
|
|
|
}
|
|
|
|
|
2014-06-02 17:24:57 +04:00
|
|
|
static const TypeInfo pc_machine_info = {
|
|
|
|
.name = TYPE_PC_MACHINE,
|
2019-10-22 10:39:50 +03:00
|
|
|
.parent = TYPE_X86_MACHINE,
|
2014-06-02 17:24:57 +04:00
|
|
|
.abstract = true,
|
|
|
|
.instance_size = sizeof(PCMachineState),
|
2014-06-02 17:25:27 +04:00
|
|
|
.instance_init = pc_machine_initfn,
|
2014-06-02 17:24:57 +04:00
|
|
|
.class_size = sizeof(PCMachineClass),
|
2014-06-02 17:25:12 +04:00
|
|
|
.class_init = pc_machine_class_init,
|
|
|
|
.interfaces = (InterfaceInfo[]) {
|
|
|
|
{ TYPE_HOTPLUG_HANDLER },
|
|
|
|
{ }
|
|
|
|
},
|
2014-06-02 17:24:57 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
static void pc_machine_register_types(void)
|
|
|
|
{
|
|
|
|
type_register_static(&pc_machine_info);
|
|
|
|
}
|
|
|
|
|
|
|
|
type_init(pc_machine_register_types)
|