microvm: add acpi support

-----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2.0.22 (GNU/Linux)
 
 iQIcBAABCgAGBQJfY2pCAAoJEEy22O7T6HE4LegQANGeb7oAWgdD6rI9aFQTD6zK
 tU3jjfIbt/xsxnbnWVlNeouoxwKTJUiBxmVNgPPZdgXYd7GgKn8qZe5ccnnF6TX2
 n9+GCV1Jvc7clBMVvj3EceSaKQrd859i2mXc85YEeC6T/Hcq9zSwDN9UTCeQYOCZ
 27r6wzWpL2sU6/vHXzb24VSZzKl91uQAekFI7WM7z+/fv3kz5KGff2zzq2Rp+77s
 do5MU++fcnAmz31c9vBoT9v5tvNWe6xXu79Fn2ay9orDj/uThkF+PqGGVQiyxeOi
 6E2X321AhMxSUqVgev1j0O+ZcxECxXoK8/7RQu72JLbTUPBAyC8jyuFATuMbQd7Z
 xS5WNBEe0Qo7zA1ZBvpnQyPrW21Wi95mON9GPrk0ixH0ECl6Bb+vMRunBK6tiUtw
 635qi73VfpNbu4fi7CBQwd/9+LDfgVb3+uEoO0EEL5WWbK9XjoklK+7chGbu4RFg
 H0or5yvX2CbR7z52W+HXNMuseUFvIQrJ8taOla8AoS/I9cKxj+j99AEYv8MNMVwe
 rRvJF0iL/hl1pdgeOm+noRDG9ledefYxscvUdVAs+5rdiOEMKDbNbm+Ff6BKfM5N
 8rTS7d6s8PXFql/YTc1CLTQmMh2q6RpiSO929mKFQc+MiQOBzwWP5tCpFOy1ywMQ
 0sxmqx24FzwVakd22k7/
 =IWLS
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/kraxel/tags/microvm-20200917-pull-request' into staging

microvm: add acpi support

# gpg: Signature made Thu 17 Sep 2020 14:53:06 BST
# gpg:                using RSA key 4CB6D8EED3E87138
# gpg: Good signature from "Gerd Hoffmann (work) <kraxel@redhat.com>" [full]
# gpg:                 aka "Gerd Hoffmann <gerd@kraxel.org>" [full]
# gpg:                 aka "Gerd Hoffmann (private) <kraxel@gmail.com>" [full]
# Primary key fingerprint: A032 8CFF B93A 17A7 9901  FE7D 4CB6 D8EE D3E8 7138

* remotes/kraxel/tags/microvm-20200917-pull-request: (21 commits)
  microvm: enable ramfb
  tests/acpi: update expected data files for microvm
  tests/acpi: add microvm test
  tests/acpi: allow override blkdev
  tests/acpi: allow microvm test data updates.
  microvm: wire up hotplug
  x86: move cpu hotplug from pc to x86
  x86: move acpi_dev from pc/microvm
  x86: constify x86_machine_is_*_enabled
  microvm/acpi: disable virtio-mmio cmdline hack
  microvm/acpi: use seabios with acpi=on
  microvm/acpi: use GSI 16-23 for virtio
  microvm/acpi: add acpi_dsdt_add_virtio() for x86
  microvm/acpi: add minimal acpi support
  microvm: make virtio irq base runtime configurable
  acpi: move acpi_dsdt_add_power_button() to ged
  acpi: ged: add x86 device variant.
  acpi: ged: add control regs
  seabios: add bios-microvm.bin binary
  seabios: add microvm config, update build rules
  ...

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2020-09-17 20:40:59 +01:00
commit a6a0c8394c
26 changed files with 823 additions and 313 deletions

View File

@ -20,6 +20,7 @@
#include "hw/qdev-properties.h"
#include "migration/vmstate.h"
#include "qemu/error-report.h"
#include "sysemu/runstate.h"
static const uint32_t ged_supported_events[] = {
ACPI_GED_MEM_HOTPLUG_EVT,
@ -141,6 +142,14 @@ void build_ged_aml(Aml *table, const char *name, HotplugHandler *hotplug_dev,
aml_append(table, dev);
}
void acpi_dsdt_add_power_button(Aml *scope)
{
Aml *dev = aml_device(ACPI_POWER_BUTTON_DEVICE);
aml_append(dev, aml_name_decl("_HID", aml_string("PNP0C0C")));
aml_append(dev, aml_name_decl("_UID", aml_int(0)));
aml_append(scope, dev);
}
/* Memory read by the GED _EVT AML dynamic method */
static uint64_t ged_evt_read(void *opaque, hwaddr addr, unsigned size)
{
@ -176,6 +185,45 @@ static const MemoryRegionOps ged_evt_ops = {
},
};
static uint64_t ged_regs_read(void *opaque, hwaddr addr, unsigned size)
{
return 0;
}
static void ged_regs_write(void *opaque, hwaddr addr, uint64_t data,
unsigned int size)
{
bool slp_en;
int slp_typ;
switch (addr) {
case ACPI_GED_REG_SLEEP_CTL:
slp_typ = (data >> 2) & 0x07;
slp_en = (data >> 5) & 0x01;
if (slp_en && slp_typ == 5) {
qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN);
}
return;
case ACPI_GED_REG_SLEEP_STS:
return;
case ACPI_GED_REG_RESET:
if (data == ACPI_GED_RESET_VALUE) {
qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN);
}
return;
}
}
static const MemoryRegionOps ged_regs_ops = {
.read = ged_regs_read,
.write = ged_regs_write,
.endianness = DEVICE_LITTLE_ENDIAN,
.valid = {
.min_access_size = 1,
.max_access_size = 1,
},
};
static void acpi_ged_device_plug_cb(HotplugHandler *hotplug_dev,
DeviceState *dev, Error **errp)
{
@ -332,6 +380,10 @@ static void acpi_ged_initfn(Object *obj)
sysbus_init_mmio(sbd, &s->container_memhp);
acpi_memory_hotplug_init(&s->container_memhp, OBJECT(dev),
&s->memhp_state, 0);
memory_region_init_io(&ged_st->regs, obj, &ged_regs_ops, ged_st,
TYPE_ACPI_GED "-regs", ACPI_GED_REG_COUNT);
sysbus_init_mmio(sbd, &ged_st->regs);
}
static void acpi_ged_class_init(ObjectClass *class, void *data)

View File

@ -357,14 +357,6 @@ static void acpi_dsdt_add_gpio(Aml *scope, const MemMapEntry *gpio_memmap,
aml_append(scope, dev);
}
static void acpi_dsdt_add_power_button(Aml *scope)
{
Aml *dev = aml_device(ACPI_POWER_BUTTON_DEVICE);
aml_append(dev, aml_name_decl("_HID", aml_string("PNP0C0C")));
aml_append(dev, aml_name_decl("_UID", aml_int(0)));
aml_append(scope, dev);
}
static void acpi_dsdt_add_tpm(Aml *scope, VirtMachineState *vms)
{
PlatformBusDevice *pbus = PLATFORM_BUS_DEVICE(vms->platform_bus_dev);

View File

@ -103,6 +103,7 @@ config MICROVM
select I8259
select MC146818RTC
select VIRTIO_MMIO
select ACPI_HW_REDUCED
config X86_IOMMU
bool

View File

@ -2431,7 +2431,7 @@ void acpi_build(AcpiBuildTables *tables, MachineState *machine)
acpi_add_table(table_offsets, tables_blob);
acpi_build_madt(tables_blob, tables->linker, x86ms,
ACPI_DEVICE_IF(pcms->acpi_dev), true);
ACPI_DEVICE_IF(x86ms->acpi_dev), true);
vmgenid_dev = find_vmgenid_dev();
if (vmgenid_dev) {

240
hw/i386/acpi-microvm.c Normal file
View File

@ -0,0 +1,240 @@
/* Support for generating ACPI tables and passing them to Guests
*
* Copyright (C) 2008-2010 Kevin O'Connor <kevin@koconnor.net>
* Copyright (C) 2006 Fabrice Bellard
* Copyright (C) 2013 Red Hat Inc
*
* Author: Michael S. Tsirkin <mst@redhat.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License along
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include "qemu/osdep.h"
#include "qemu/cutils.h"
#include "qapi/error.h"
#include "exec/memory.h"
#include "hw/acpi/acpi.h"
#include "hw/acpi/aml-build.h"
#include "hw/acpi/bios-linker-loader.h"
#include "hw/acpi/generic_event_device.h"
#include "hw/acpi/utils.h"
#include "hw/boards.h"
#include "hw/i386/fw_cfg.h"
#include "hw/i386/microvm.h"
#include "hw/virtio/virtio-mmio.h"
#include "acpi-common.h"
#include "acpi-microvm.h"
static void acpi_dsdt_add_virtio(Aml *scope,
MicrovmMachineState *mms)
{
gchar *separator;
long int index;
BusState *bus;
BusChild *kid;
bus = sysbus_get_default();
QTAILQ_FOREACH(kid, &bus->children, sibling) {
DeviceState *dev = kid->child;
Object *obj = object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MMIO);
if (obj) {
VirtIOMMIOProxy *mmio = VIRTIO_MMIO(obj);
VirtioBusState *mmio_virtio_bus = &mmio->bus;
BusState *mmio_bus = &mmio_virtio_bus->parent_obj;
if (QTAILQ_EMPTY(&mmio_bus->children)) {
continue;
}
separator = g_strrstr(mmio_bus->name, ".");
if (!separator) {
continue;
}
if (qemu_strtol(separator + 1, NULL, 10, &index) != 0) {
continue;
}
uint32_t irq = mms->virtio_irq_base + index;
hwaddr base = VIRTIO_MMIO_BASE + index * 512;
hwaddr size = 512;
Aml *dev = aml_device("VR%02u", (unsigned)index);
aml_append(dev, aml_name_decl("_HID", aml_string("LNRO0005")));
aml_append(dev, aml_name_decl("_UID", aml_int(index)));
aml_append(dev, aml_name_decl("_CCA", aml_int(1)));
Aml *crs = aml_resource_template();
aml_append(crs, aml_memory32_fixed(base, size, AML_READ_WRITE));
aml_append(crs,
aml_interrupt(AML_CONSUMER, AML_LEVEL, AML_ACTIVE_HIGH,
AML_EXCLUSIVE, &irq, 1));
aml_append(dev, aml_name_decl("_CRS", crs));
aml_append(scope, dev);
}
}
}
static void
build_dsdt_microvm(GArray *table_data, BIOSLinker *linker,
MicrovmMachineState *mms)
{
X86MachineState *x86ms = X86_MACHINE(mms);
Aml *dsdt, *sb_scope, *scope, *pkg;
bool ambiguous;
Object *isabus;
isabus = object_resolve_path_type("", TYPE_ISA_BUS, &ambiguous);
assert(isabus);
assert(!ambiguous);
dsdt = init_aml_allocator();
/* Reserve space for header */
acpi_data_push(dsdt->buf, sizeof(AcpiTableHeader));
sb_scope = aml_scope("_SB");
fw_cfg_add_acpi_dsdt(sb_scope, x86ms->fw_cfg);
isa_build_aml(ISA_BUS(isabus), sb_scope);
build_ged_aml(sb_scope, GED_DEVICE, x86ms->acpi_dev,
GED_MMIO_IRQ, AML_SYSTEM_MEMORY, GED_MMIO_BASE);
acpi_dsdt_add_power_button(sb_scope);
acpi_dsdt_add_virtio(sb_scope, mms);
aml_append(dsdt, sb_scope);
/* ACPI 5.0: Table 7-209 System State Package */
scope = aml_scope("\\");
pkg = aml_package(4);
aml_append(pkg, aml_int(ACPI_GED_SLP_TYP_S5));
aml_append(pkg, aml_int(0)); /* ignored */
aml_append(pkg, aml_int(0)); /* reserved */
aml_append(pkg, aml_int(0)); /* reserved */
aml_append(scope, aml_name_decl("_S5", pkg));
aml_append(dsdt, scope);
/* copy AML table into ACPI tables blob and patch header there */
g_array_append_vals(table_data, dsdt->buf->data, dsdt->buf->len);
build_header(linker, table_data,
(void *)(table_data->data + table_data->len - dsdt->buf->len),
"DSDT", dsdt->buf->len, 2, NULL, NULL);
free_aml_allocator();
}
static void acpi_build_microvm(AcpiBuildTables *tables,
MicrovmMachineState *mms)
{
MachineState *machine = MACHINE(mms);
X86MachineState *x86ms = X86_MACHINE(mms);
GArray *table_offsets;
GArray *tables_blob = tables->table_data;
unsigned dsdt, xsdt;
AcpiFadtData pmfadt = {
/* ACPI 5.0: 4.1 Hardware-Reduced ACPI */
.rev = 5,
.flags = ((1 << ACPI_FADT_F_HW_REDUCED_ACPI) |
(1 << ACPI_FADT_F_RESET_REG_SUP)),
/* ACPI 5.0: 4.8.3.7 Sleep Control and Status Registers */
.sleep_ctl = {
.space_id = AML_AS_SYSTEM_MEMORY,
.bit_width = 8,
.address = GED_MMIO_BASE_REGS + ACPI_GED_REG_SLEEP_CTL,
},
.sleep_sts = {
.space_id = AML_AS_SYSTEM_MEMORY,
.bit_width = 8,
.address = GED_MMIO_BASE_REGS + ACPI_GED_REG_SLEEP_STS,
},
/* ACPI 5.0: 4.8.3.6 Reset Register */
.reset_reg = {
.space_id = AML_AS_SYSTEM_MEMORY,
.bit_width = 8,
.address = GED_MMIO_BASE_REGS + ACPI_GED_REG_RESET,
},
.reset_val = ACPI_GED_RESET_VALUE,
};
table_offsets = g_array_new(false, true /* clear */,
sizeof(uint32_t));
bios_linker_loader_alloc(tables->linker,
ACPI_BUILD_TABLE_FILE, tables_blob,
64 /* Ensure FACS is aligned */,
false /* high memory */);
dsdt = tables_blob->len;
build_dsdt_microvm(tables_blob, tables->linker, mms);
pmfadt.dsdt_tbl_offset = &dsdt;
pmfadt.xdsdt_tbl_offset = &dsdt;
acpi_add_table(table_offsets, tables_blob);
build_fadt(tables_blob, tables->linker, &pmfadt, NULL, NULL);
acpi_add_table(table_offsets, tables_blob);
acpi_build_madt(tables_blob, tables->linker, X86_MACHINE(machine),
ACPI_DEVICE_IF(x86ms->acpi_dev), false);
xsdt = tables_blob->len;
build_xsdt(tables_blob, tables->linker, table_offsets, NULL, NULL);
/* RSDP is in FSEG memory, so allocate it separately */
{
AcpiRsdpData rsdp_data = {
/* ACPI 2.0: 5.2.4.3 RSDP Structure */
.revision = 2, /* xsdt needs v2 */
.oem_id = ACPI_BUILD_APPNAME6,
.xsdt_tbl_offset = &xsdt,
.rsdt_tbl_offset = NULL,
};
build_rsdp(tables->rsdp, tables->linker, &rsdp_data);
}
/* Cleanup memory that's no longer used. */
g_array_free(table_offsets, true);
}
static void acpi_build_no_update(void *build_opaque)
{
/* nothing, microvm tables don't change at runtime */
}
void acpi_setup_microvm(MicrovmMachineState *mms)
{
X86MachineState *x86ms = X86_MACHINE(mms);
AcpiBuildTables tables;
assert(x86ms->fw_cfg);
if (!x86_machine_is_acpi_enabled(x86ms)) {
return;
}
acpi_build_tables_init(&tables);
acpi_build_microvm(&tables, mms);
/* Now expose it all to Guest */
acpi_add_rom_blob(acpi_build_no_update, NULL,
tables.table_data,
ACPI_BUILD_TABLE_FILE,
ACPI_BUILD_TABLE_MAX_SIZE);
acpi_add_rom_blob(acpi_build_no_update, NULL,
tables.linker->cmd_blob,
"etc/table-loader", 0);
acpi_add_rom_blob(acpi_build_no_update, NULL,
tables.rsdp,
ACPI_BUILD_RSDP_FILE, 0);
acpi_build_tables_cleanup(&tables, false);
}

8
hw/i386/acpi-microvm.h Normal file
View File

@ -0,0 +1,8 @@
#ifndef HW_I386_ACPI_MICROVM_H
#define HW_I386_ACPI_MICROVM_H
#include "hw/i386/microvm.h"
void acpi_setup_microvm(MicrovmMachineState *mms);
#endif

View File

@ -0,0 +1,36 @@
/*
* x86 variant of the generic event device for hw reduced acpi
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2 or later, as published by the Free Software Foundation.
*/
#include "qemu/osdep.h"
#include "hw/acpi/generic_event_device.h"
#include "hw/i386/pc.h"
static void acpi_ged_x86_class_init(ObjectClass *class, void *data)
{
AcpiDeviceIfClass *adevc = ACPI_DEVICE_IF_CLASS(class);
adevc->madt_cpu = pc_madt_cpu_entry;
}
static const TypeInfo acpi_ged_x86_info = {
.name = TYPE_ACPI_GED_X86,
.parent = TYPE_ACPI_GED,
.class_init = acpi_ged_x86_class_init,
.interfaces = (InterfaceInfo[]) {
{ TYPE_HOTPLUG_HANDLER },
{ TYPE_ACPI_DEVICE_IF },
{ }
}
};
static void acpi_ged_x86_register_types(void)
{
type_register_static(&acpi_ged_x86_info);
}
type_init(acpi_ged_x86_register_types)

View File

@ -11,13 +11,14 @@ i386_ss.add(when: 'CONFIG_X86_IOMMU', if_true: files('x86-iommu.c'),
if_false: files('x86-iommu-stub.c'))
i386_ss.add(when: 'CONFIG_AMD_IOMMU', if_true: files('amd_iommu.c'))
i386_ss.add(when: 'CONFIG_I440FX', if_true: files('pc_piix.c'))
i386_ss.add(when: 'CONFIG_MICROVM', if_true: files('microvm.c'))
i386_ss.add(when: 'CONFIG_MICROVM', if_true: files('microvm.c', 'acpi-microvm.c'))
i386_ss.add(when: 'CONFIG_Q35', if_true: files('pc_q35.c'))
i386_ss.add(when: 'CONFIG_VMMOUSE', if_true: files('vmmouse.c'))
i386_ss.add(when: 'CONFIG_VMPORT', if_true: files('vmport.c'))
i386_ss.add(when: 'CONFIG_VTD', if_true: files('intel_iommu.c'))
i386_ss.add(when: 'CONFIG_ACPI', if_true: files('acpi-common.c'))
i386_ss.add(when: 'CONFIG_ACPI_HW_REDUCED', if_true: files('generic_event_device_x86.c'))
i386_ss.add(when: 'CONFIG_PC', if_true: files(
'pc.c',
'pc_sysfw.c',

View File

@ -26,6 +26,8 @@
#include "sysemu/cpus.h"
#include "sysemu/numa.h"
#include "sysemu/reset.h"
#include "sysemu/runstate.h"
#include "acpi-microvm.h"
#include "hw/loader.h"
#include "hw/irq.h"
@ -37,17 +39,21 @@
#include "hw/timer/i8254.h"
#include "hw/rtc/mc146818rtc.h"
#include "hw/char/serial.h"
#include "hw/display/ramfb.h"
#include "hw/i386/topology.h"
#include "hw/i386/e820_memory_layout.h"
#include "hw/i386/fw_cfg.h"
#include "hw/virtio/virtio-mmio.h"
#include "hw/acpi/acpi.h"
#include "hw/acpi/generic_event_device.h"
#include "cpu.h"
#include "elf.h"
#include "kvm_i386.h"
#include "hw/xen/start_info.h"
#define MICROVM_BIOS_FILENAME "bios-microvm.bin"
#define MICROVM_QBOOT_FILENAME "qboot.rom"
#define MICROVM_BIOS_FILENAME "bios-microvm.bin"
static void microvm_set_rtc(MicrovmMachineState *mms, ISADevice *s)
{
@ -121,13 +127,25 @@ static void microvm_devices_init(MicrovmMachineState *mms)
kvmclock_create();
mms->virtio_irq_base = x86_machine_is_acpi_enabled(x86ms) ? 16 : 5;
for (i = 0; i < VIRTIO_NUM_TRANSPORTS; i++) {
sysbus_create_simple("virtio-mmio",
VIRTIO_MMIO_BASE + i * 512,
x86ms->gsi[VIRTIO_IRQ_BASE + i]);
x86ms->gsi[mms->virtio_irq_base + i]);
}
/* Optional and legacy devices */
if (x86_machine_is_acpi_enabled(x86ms)) {
DeviceState *dev = qdev_new(TYPE_ACPI_GED_X86);
qdev_prop_set_uint32(dev, "ged-event", ACPI_GED_PWR_DOWN_EVT);
sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, GED_MMIO_BASE);
/* sysbus_mmio_map(SYS_BUS_DEVICE(dev), 1, GED_MMIO_BASE_MEMHP); */
sysbus_mmio_map(SYS_BUS_DEVICE(dev), 2, GED_MMIO_BASE_REGS);
sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0,
x86ms->gsi[GED_MMIO_IRQ]);
sysbus_realize(SYS_BUS_DEVICE(dev), &error_fatal);
x86ms->acpi_dev = HOTPLUG_HANDLER(dev);
}
if (mms->pic == ON_OFF_AUTO_ON || mms->pic == ON_OFF_AUTO_AUTO) {
qemu_irq *i8259;
@ -158,7 +176,9 @@ static void microvm_devices_init(MicrovmMachineState *mms)
}
if (bios_name == NULL) {
bios_name = MICROVM_BIOS_FILENAME;
bios_name = x86_machine_is_acpi_enabled(x86ms)
? MICROVM_BIOS_FILENAME
: MICROVM_QBOOT_FILENAME;
}
x86_bios_rom_init(get_system_memory(), true);
}
@ -227,7 +247,7 @@ static void microvm_memory_init(MicrovmMachineState *mms)
x86ms->ioapic_as = &address_space_memory;
}
static gchar *microvm_get_mmio_cmdline(gchar *name)
static gchar *microvm_get_mmio_cmdline(gchar *name, uint32_t virtio_irq_base)
{
gchar *cmdline;
gchar *separator;
@ -247,7 +267,7 @@ static gchar *microvm_get_mmio_cmdline(gchar *name)
ret = g_snprintf(cmdline, VIRTIO_CMDLINE_MAXLEN,
" virtio_mmio.device=512@0x%lx:%ld",
VIRTIO_MMIO_BASE + index * 512,
VIRTIO_IRQ_BASE + index);
virtio_irq_base + index);
if (ret < 0 || ret >= VIRTIO_CMDLINE_MAXLEN) {
g_free(cmdline);
return NULL;
@ -259,6 +279,7 @@ static gchar *microvm_get_mmio_cmdline(gchar *name)
static void microvm_fix_kernel_cmdline(MachineState *machine)
{
X86MachineState *x86ms = X86_MACHINE(machine);
MicrovmMachineState *mms = MICROVM_MACHINE(machine);
BusState *bus;
BusChild *kid;
char *cmdline;
@ -282,7 +303,8 @@ static void microvm_fix_kernel_cmdline(MachineState *machine)
BusState *mmio_bus = &mmio_virtio_bus->parent_obj;
if (!QTAILQ_EMPTY(&mmio_bus->children)) {
gchar *mmio_cmdline = microvm_get_mmio_cmdline(mmio_bus->name);
gchar *mmio_cmdline = microvm_get_mmio_cmdline
(mmio_bus->name, mms->virtio_irq_base);
if (mmio_cmdline) {
char *newcmd = g_strjoin(NULL, cmdline, mmio_cmdline, NULL);
g_free(mmio_cmdline);
@ -299,6 +321,39 @@ static void microvm_fix_kernel_cmdline(MachineState *machine)
g_free(cmdline);
}
static void microvm_device_pre_plug_cb(HotplugHandler *hotplug_dev,
DeviceState *dev, Error **errp)
{
x86_cpu_pre_plug(hotplug_dev, dev, errp);
}
static void microvm_device_plug_cb(HotplugHandler *hotplug_dev,
DeviceState *dev, Error **errp)
{
x86_cpu_plug(hotplug_dev, dev, errp);
}
static void microvm_device_unplug_request_cb(HotplugHandler *hotplug_dev,
DeviceState *dev, Error **errp)
{
error_setg(errp, "unplug not supported by microvm");
}
static void microvm_device_unplug_cb(HotplugHandler *hotplug_dev,
DeviceState *dev, Error **errp)
{
error_setg(errp, "unplug not supported by microvm");
}
static HotplugHandler *microvm_get_hotplug_handler(MachineState *machine,
DeviceState *dev)
{
if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) {
return HOTPLUG_HANDLER(machine);
}
return NULL;
}
static void microvm_machine_state_init(MachineState *machine)
{
MicrovmMachineState *mms = MICROVM_MACHINE(machine);
@ -322,7 +377,8 @@ static void microvm_machine_reset(MachineState *machine)
CPUState *cs;
X86CPU *cpu;
if (machine->kernel_filename != NULL &&
if (!x86_machine_is_acpi_enabled(X86_MACHINE(machine)) &&
machine->kernel_filename != NULL &&
mms->auto_kernel_cmdline && !mms->kernel_cmdline_fixed) {
microvm_fix_kernel_cmdline(machine);
mms->kernel_cmdline_fixed = true;
@ -435,6 +491,28 @@ static void microvm_machine_set_auto_kernel_cmdline(Object *obj, bool value,
mms->auto_kernel_cmdline = value;
}
static void microvm_machine_done(Notifier *notifier, void *data)
{
MicrovmMachineState *mms = container_of(notifier, MicrovmMachineState,
machine_done);
acpi_setup_microvm(mms);
}
static void microvm_powerdown_req(Notifier *notifier, void *data)
{
MicrovmMachineState *mms = container_of(notifier, MicrovmMachineState,
powerdown_req);
X86MachineState *x86ms = X86_MACHINE(mms);
if (x86ms->acpi_dev) {
Object *obj = OBJECT(x86ms->acpi_dev);
AcpiDeviceIfClass *adevc = ACPI_DEVICE_IF_GET_CLASS(obj);
adevc->send_event(ACPI_DEVICE_IF(x86ms->acpi_dev),
ACPI_POWER_DOWN_STATUS);
}
}
static void microvm_machine_initfn(Object *obj)
{
MicrovmMachineState *mms = MICROVM_MACHINE(obj);
@ -449,11 +527,17 @@ static void microvm_machine_initfn(Object *obj)
/* State */
mms->kernel_cmdline_fixed = false;
mms->machine_done.notify = microvm_machine_done;
qemu_add_machine_init_done_notifier(&mms->machine_done);
mms->powerdown_req.notify = microvm_powerdown_req;
qemu_register_powerdown_notifier(&mms->powerdown_req);
}
static void microvm_class_init(ObjectClass *oc, void *data)
{
MachineClass *mc = MACHINE_CLASS(oc);
HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc);
mc->init = microvm_machine_state_init;
@ -475,6 +559,13 @@ static void microvm_class_init(ObjectClass *oc, void *data)
/* Machine class handlers */
mc->reset = microvm_machine_reset;
/* hotplug (for cpu coldplug) */
mc->get_hotplug_handler = microvm_get_hotplug_handler;
hc->pre_plug = microvm_device_pre_plug_cb;
hc->plug = microvm_device_plug_cb;
hc->unplug_request = microvm_device_unplug_request_cb;
hc->unplug = microvm_device_unplug_cb;
object_class_property_add(oc, MICROVM_MACHINE_PIC, "OnOffAuto",
microvm_machine_get_pic,
microvm_machine_set_pic,
@ -514,6 +605,8 @@ static void microvm_class_init(ObjectClass *oc, void *data)
object_class_property_set_description(oc,
MICROVM_MACHINE_AUTO_KERNEL_CMDLINE,
"Set off to disable adding virtio-mmio devices to the kernel cmdline");
machine_class_allow_dynamic_sysbus_dev(mc, TYPE_RAMFB_DEVICE);
}
static const TypeInfo microvm_machine_info = {
@ -524,6 +617,7 @@ static const TypeInfo microvm_machine_info = {
.class_size = sizeof(MicrovmMachineClass),
.class_init = microvm_class_init,
.interfaces = (InterfaceInfo[]) {
{ TYPE_HOTPLUG_HANDLER },
{ }
},
};

View File

@ -803,19 +803,6 @@ void pc_hot_add_cpu(MachineState *ms, const int64_t id, Error **errp)
}
}
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);
}
}
static
void pc_machine_done(Notifier *notifier, void *data)
{
@ -825,7 +812,7 @@ void pc_machine_done(Notifier *notifier, void *data)
PCIBus *bus = pcms->bus;
/* set the number of CPUs */
rtc_set_cpus_count(x86ms->rtc, x86ms->boot_cpus);
x86_rtc_set_cpus_count(x86ms->rtc, x86ms->boot_cpus);
if (bus) {
int extra_hosts = 0;
@ -1274,6 +1261,7 @@ static void pc_memory_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
Error **errp)
{
const PCMachineState *pcms = PC_MACHINE(hotplug_dev);
const X86MachineState *x86ms = X86_MACHINE(hotplug_dev);
const PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms);
const MachineState *ms = MACHINE(hotplug_dev);
const bool is_nvdimm = object_dynamic_cast(OBJECT(dev), TYPE_NVDIMM);
@ -1285,7 +1273,7 @@ static void pc_memory_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
* but pcms->acpi_dev is still created. Check !acpi_enabled in
* addition to cover this case.
*/
if (!pcms->acpi_dev || !x86_machine_is_acpi_enabled(X86_MACHINE(pcms))) {
if (!x86ms->acpi_dev || !x86_machine_is_acpi_enabled(x86ms)) {
error_setg(errp,
"memory hotplug is not enabled: missing acpi device or acpi disabled");
return;
@ -1296,7 +1284,7 @@ static void pc_memory_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
return;
}
hotplug_handler_pre_plug(pcms->acpi_dev, dev, &local_err);
hotplug_handler_pre_plug(x86ms->acpi_dev, dev, &local_err);
if (local_err) {
error_propagate(errp, local_err);
return;
@ -1311,6 +1299,7 @@ static void pc_memory_plug(HotplugHandler *hotplug_dev,
{
Error *local_err = NULL;
PCMachineState *pcms = PC_MACHINE(hotplug_dev);
X86MachineState *x86ms = X86_MACHINE(hotplug_dev);
MachineState *ms = MACHINE(hotplug_dev);
bool is_nvdimm = object_dynamic_cast(OBJECT(dev), TYPE_NVDIMM);
@ -1323,7 +1312,7 @@ static void pc_memory_plug(HotplugHandler *hotplug_dev,
nvdimm_plug(ms->nvdimms_state);
}
hotplug_handler_plug(HOTPLUG_HANDLER(pcms->acpi_dev), dev, &error_abort);
hotplug_handler_plug(x86ms->acpi_dev, dev, &error_abort);
out:
error_propagate(errp, local_err);
}
@ -1331,14 +1320,14 @@ out:
static void pc_memory_unplug_request(HotplugHandler *hotplug_dev,
DeviceState *dev, Error **errp)
{
PCMachineState *pcms = PC_MACHINE(hotplug_dev);
X86MachineState *x86ms = X86_MACHINE(hotplug_dev);
/*
* 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 || !x86_machine_is_acpi_enabled(X86_MACHINE(pcms))) {
if (!x86ms->acpi_dev || !x86_machine_is_acpi_enabled(x86ms)) {
error_setg(errp,
"memory hotplug is not enabled: missing acpi device or acpi disabled");
return;
@ -1349,7 +1338,7 @@ static void pc_memory_unplug_request(HotplugHandler *hotplug_dev,
return;
}
hotplug_handler_unplug_request(HOTPLUG_HANDLER(pcms->acpi_dev), dev,
hotplug_handler_unplug_request(x86ms->acpi_dev, dev,
errp);
}
@ -1357,9 +1346,10 @@ static void pc_memory_unplug(HotplugHandler *hotplug_dev,
DeviceState *dev, Error **errp)
{
PCMachineState *pcms = PC_MACHINE(hotplug_dev);
X86MachineState *x86ms = X86_MACHINE(hotplug_dev);
Error *local_err = NULL;
hotplug_handler_unplug(HOTPLUG_HANDLER(pcms->acpi_dev), dev, &local_err);
hotplug_handler_unplug(x86ms->acpi_dev, dev, &local_err);
if (local_err) {
goto out;
}
@ -1370,263 +1360,6 @@ static void pc_memory_unplug(HotplugHandler *hotplug_dev,
error_propagate(errp, local_err);
}
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;
}
/* returns pointer to CPUArchId descriptor that matches CPU's apic_id
* in ms->possible_cpus->cpus, if ms->possible_cpus->cpus has no
* entry corresponding to CPU's apic_id returns NULL.
*/
static CPUArchId *pc_find_cpu_slot(MachineState *ms, uint32_t id, int *idx)
{
CPUArchId apic_id, *found_cpu;
apic_id.arch_id = id;
found_cpu = bsearch(&apic_id, ms->possible_cpus->cpus,
ms->possible_cpus->len, sizeof(*ms->possible_cpus->cpus),
pc_apic_cmp);
if (found_cpu && idx) {
*idx = found_cpu - ms->possible_cpus->cpus;
}
return found_cpu;
}
static void pc_cpu_plug(HotplugHandler *hotplug_dev,
DeviceState *dev, Error **errp)
{
CPUArchId *found_cpu;
Error *local_err = NULL;
X86CPU *cpu = X86_CPU(dev);
PCMachineState *pcms = PC_MACHINE(hotplug_dev);
X86MachineState *x86ms = X86_MACHINE(pcms);
if (pcms->acpi_dev) {
hotplug_handler_plug(HOTPLUG_HANDLER(pcms->acpi_dev), dev, &local_err);
if (local_err) {
goto out;
}
}
/* increment the number of CPUs */
x86ms->boot_cpus++;
if (x86ms->rtc) {
rtc_set_cpus_count(x86ms->rtc, x86ms->boot_cpus);
}
if (x86ms->fw_cfg) {
fw_cfg_modify_i16(x86ms->fw_cfg, FW_CFG_NB_CPUS, x86ms->boot_cpus);
}
found_cpu = pc_find_cpu_slot(MACHINE(pcms), cpu->apic_id, NULL);
found_cpu->cpu = OBJECT(dev);
out:
error_propagate(errp, local_err);
}
static void pc_cpu_unplug_request_cb(HotplugHandler *hotplug_dev,
DeviceState *dev, Error **errp)
{
int idx = -1;
X86CPU *cpu = X86_CPU(dev);
PCMachineState *pcms = PC_MACHINE(hotplug_dev);
if (!pcms->acpi_dev) {
error_setg(errp, "CPU hot unplug not supported without ACPI");
return;
}
pc_find_cpu_slot(MACHINE(pcms), cpu->apic_id, &idx);
assert(idx != -1);
if (idx == 0) {
error_setg(errp, "Boot CPU is unpluggable");
return;
}
hotplug_handler_unplug_request(HOTPLUG_HANDLER(pcms->acpi_dev), dev,
errp);
}
static void pc_cpu_unplug_cb(HotplugHandler *hotplug_dev,
DeviceState *dev, Error **errp)
{
CPUArchId *found_cpu;
Error *local_err = NULL;
X86CPU *cpu = X86_CPU(dev);
PCMachineState *pcms = PC_MACHINE(hotplug_dev);
X86MachineState *x86ms = X86_MACHINE(pcms);
hotplug_handler_unplug(HOTPLUG_HANDLER(pcms->acpi_dev), dev, &local_err);
if (local_err) {
goto out;
}
found_cpu = pc_find_cpu_slot(MACHINE(pcms), cpu->apic_id, NULL);
found_cpu->cpu = NULL;
qdev_unrealize(dev);
/* decrement the number of CPUs */
x86ms->boot_cpus--;
/* Update the number of CPUs in CMOS */
rtc_set_cpus_count(x86ms->rtc, x86ms->boot_cpus);
fw_cfg_modify_i16(x86ms->fw_cfg, FW_CFG_NB_CPUS, x86ms->boot_cpus);
out:
error_propagate(errp, local_err);
}
static void pc_cpu_pre_plug(HotplugHandler *hotplug_dev,
DeviceState *dev, Error **errp)
{
int idx;
CPUState *cs;
CPUArchId *cpu_slot;
X86CPUTopoIDs topo_ids;
X86CPU *cpu = X86_CPU(dev);
CPUX86State *env = &cpu->env;
MachineState *ms = MACHINE(hotplug_dev);
PCMachineState *pcms = PC_MACHINE(hotplug_dev);
X86MachineState *x86ms = X86_MACHINE(pcms);
unsigned int smp_cores = ms->smp.cores;
unsigned int smp_threads = ms->smp.threads;
X86CPUTopoInfo topo_info;
if(!object_dynamic_cast(OBJECT(cpu), ms->cpu_type)) {
error_setg(errp, "Invalid CPU type, expected cpu type: '%s'",
ms->cpu_type);
return;
}
init_topo_info(&topo_info, x86ms);
env->nr_dies = x86ms->smp_dies;
/*
* If APIC ID is not set,
* set it based on socket/die/core/thread properties.
*/
if (cpu->apic_id == UNASSIGNED_APIC_ID) {
int max_socket = (ms->smp.max_cpus - 1) /
smp_threads / smp_cores / x86ms->smp_dies;
/*
* die-id was optional in QEMU 4.0 and older, so keep it optional
* if there's only one die per socket.
*/
if (cpu->die_id < 0 && x86ms->smp_dies == 1) {
cpu->die_id = 0;
}
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;
}
if (cpu->die_id < 0) {
error_setg(errp, "CPU die-id is not set");
return;
} else if (cpu->die_id > x86ms->smp_dies - 1) {
error_setg(errp, "Invalid CPU die-id: %u must be in range 0:%u",
cpu->die_id, x86ms->smp_dies - 1);
return;
}
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_ids.pkg_id = cpu->socket_id;
topo_ids.die_id = cpu->die_id;
topo_ids.core_id = cpu->core_id;
topo_ids.smt_id = cpu->thread_id;
cpu->apic_id = x86_apicid_from_topo_ids(&topo_info, &topo_ids);
}
cpu_slot = pc_find_cpu_slot(MACHINE(pcms), cpu->apic_id, &idx);
if (!cpu_slot) {
MachineState *ms = MACHINE(pcms);
x86_topo_ids_from_apicid(cpu->apic_id, &topo_info, &topo_ids);
error_setg(errp,
"Invalid CPU [socket: %u, die: %u, core: %u, thread: %u] with"
" APIC ID %" PRIu32 ", valid index range 0:%d",
topo_ids.pkg_id, topo_ids.die_id, topo_ids.core_id, topo_ids.smt_id,
cpu->apic_id, ms->possible_cpus->len - 1);
return;
}
if (cpu_slot->cpu) {
error_setg(errp, "CPU[%d] with APIC ID %" PRIu32 " exists",
idx, cpu->apic_id);
return;
}
/* if 'address' properties socket-id/core-id/thread-id are not set, set them
* so that machine_query_hotpluggable_cpus would show correct values
*/
/* TODO: move socket_id/core_id/thread_id checks into x86_cpu_realizefn()
* once -smp refactoring is complete and there will be CPU private
* CPUState::nr_cores and CPUState::nr_threads fields instead of globals */
x86_topo_ids_from_apicid(cpu->apic_id, &topo_info, &topo_ids);
if (cpu->socket_id != -1 && cpu->socket_id != topo_ids.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_ids.pkg_id);
return;
}
cpu->socket_id = topo_ids.pkg_id;
if (cpu->die_id != -1 && cpu->die_id != topo_ids.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_ids.die_id);
return;
}
cpu->die_id = topo_ids.die_id;
if (cpu->core_id != -1 && cpu->core_id != topo_ids.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_ids.core_id);
return;
}
cpu->core_id = topo_ids.core_id;
if (cpu->thread_id != -1 && cpu->thread_id != topo_ids.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_ids.smt_id);
return;
}
cpu->thread_id = topo_ids.smt_id;
if (hyperv_feat_enabled(cpu, HYPERV_FEAT_VPINDEX) &&
!kvm_hv_vpindex_settable()) {
error_setg(errp, "kernel doesn't allow setting HyperV VP_INDEX");
return;
}
cs = CPU(cpu);
cs->cpu_index = idx;
numa_cpu_pre_plug(cpu_slot, dev, errp);
}
static void pc_virtio_md_pci_pre_plug(HotplugHandler *hotplug_dev,
DeviceState *dev, Error **errp)
{
@ -1695,7 +1428,7 @@ static void pc_machine_device_pre_plug_cb(HotplugHandler *hotplug_dev,
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)) {
pc_cpu_pre_plug(hotplug_dev, dev, errp);
x86_cpu_pre_plug(hotplug_dev, dev, errp);
} else if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_PMEM_PCI) ||
object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MEM_PCI)) {
pc_virtio_md_pci_pre_plug(hotplug_dev, dev, errp);
@ -1708,7 +1441,7 @@ static void pc_machine_device_plug_cb(HotplugHandler *hotplug_dev,
if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
pc_memory_plug(hotplug_dev, dev, errp);
} else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) {
pc_cpu_plug(hotplug_dev, dev, errp);
x86_cpu_plug(hotplug_dev, dev, errp);
} else if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_PMEM_PCI) ||
object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MEM_PCI)) {
pc_virtio_md_pci_plug(hotplug_dev, dev, errp);
@ -1721,7 +1454,7 @@ static void pc_machine_device_unplug_request_cb(HotplugHandler *hotplug_dev,
if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
pc_memory_unplug_request(hotplug_dev, dev, errp);
} else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) {
pc_cpu_unplug_request_cb(hotplug_dev, dev, errp);
x86_cpu_unplug_request_cb(hotplug_dev, dev, errp);
} else if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_PMEM_PCI) ||
object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MEM_PCI)) {
pc_virtio_md_pci_unplug_request(hotplug_dev, dev, errp);
@ -1737,7 +1470,7 @@ static void pc_machine_device_unplug_cb(HotplugHandler *hotplug_dev,
if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
pc_memory_unplug(hotplug_dev, dev, errp);
} else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) {
pc_cpu_unplug_cb(hotplug_dev, dev, errp);
x86_cpu_unplug_cb(hotplug_dev, dev, errp);
} else if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_PMEM_PCI) ||
object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MEM_PCI)) {
pc_virtio_md_pci_unplug(hotplug_dev, dev, errp);

View File

@ -293,7 +293,7 @@ static void pc_init1(MachineState *machine,
object_property_add_link(OBJECT(machine), PC_MACHINE_ACPI_DEVICE_PROP,
TYPE_HOTPLUG_HANDLER,
(Object **)&pcms->acpi_dev,
(Object **)&x86ms->acpi_dev,
object_property_allow_set_link,
OBJ_PROP_LINK_STRONG);
object_property_set_link(OBJECT(machine), PC_MACHINE_ACPI_DEVICE_PROP,

View File

@ -240,7 +240,7 @@ static void pc_q35_init(MachineState *machine)
object_property_add_link(OBJECT(machine), PC_MACHINE_ACPI_DEVICE_PROP,
TYPE_HOTPLUG_HANDLER,
(Object **)&pcms->acpi_dev,
(Object **)&x86ms->acpi_dev,
object_property_allow_set_link,
OBJ_PROP_LINK_STRONG);
object_property_set_link(OBJECT(machine), PC_MACHINE_ACPI_DEVICE_PROP,

View File

@ -41,6 +41,7 @@
#include "hw/i386/topology.h"
#include "hw/i386/fw_cfg.h"
#include "hw/intc/i8259.h"
#include "hw/rtc/mc146818rtc.h"
#include "hw/acpi/cpu_hotplug.h"
#include "hw/irq.h"
@ -137,6 +138,276 @@ void x86_cpus_init(X86MachineState *x86ms, int default_cpu_version)
}
}
void x86_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);
}
}
static int x86_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;
}
/*
* returns pointer to CPUArchId descriptor that matches CPU's apic_id
* in ms->possible_cpus->cpus, if ms->possible_cpus->cpus has no
* entry corresponding to CPU's apic_id returns NULL.
*/
CPUArchId *x86_find_cpu_slot(MachineState *ms, uint32_t id, int *idx)
{
CPUArchId apic_id, *found_cpu;
apic_id.arch_id = id;
found_cpu = bsearch(&apic_id, ms->possible_cpus->cpus,
ms->possible_cpus->len, sizeof(*ms->possible_cpus->cpus),
x86_apic_cmp);
if (found_cpu && idx) {
*idx = found_cpu - ms->possible_cpus->cpus;
}
return found_cpu;
}
void x86_cpu_plug(HotplugHandler *hotplug_dev,
DeviceState *dev, Error **errp)
{
CPUArchId *found_cpu;
Error *local_err = NULL;
X86CPU *cpu = X86_CPU(dev);
X86MachineState *x86ms = X86_MACHINE(hotplug_dev);
if (x86ms->acpi_dev) {
hotplug_handler_plug(x86ms->acpi_dev, dev, &local_err);
if (local_err) {
goto out;
}
}
/* increment the number of CPUs */
x86ms->boot_cpus++;
if (x86ms->rtc) {
x86_rtc_set_cpus_count(x86ms->rtc, x86ms->boot_cpus);
}
if (x86ms->fw_cfg) {
fw_cfg_modify_i16(x86ms->fw_cfg, FW_CFG_NB_CPUS, x86ms->boot_cpus);
}
found_cpu = x86_find_cpu_slot(MACHINE(x86ms), cpu->apic_id, NULL);
found_cpu->cpu = OBJECT(dev);
out:
error_propagate(errp, local_err);
}
void x86_cpu_unplug_request_cb(HotplugHandler *hotplug_dev,
DeviceState *dev, Error **errp)
{
int idx = -1;
X86CPU *cpu = X86_CPU(dev);
X86MachineState *x86ms = X86_MACHINE(hotplug_dev);
if (!x86ms->acpi_dev) {
error_setg(errp, "CPU hot unplug not supported without ACPI");
return;
}
x86_find_cpu_slot(MACHINE(x86ms), cpu->apic_id, &idx);
assert(idx != -1);
if (idx == 0) {
error_setg(errp, "Boot CPU is unpluggable");
return;
}
hotplug_handler_unplug_request(x86ms->acpi_dev, dev,
errp);
}
void x86_cpu_unplug_cb(HotplugHandler *hotplug_dev,
DeviceState *dev, Error **errp)
{
CPUArchId *found_cpu;
Error *local_err = NULL;
X86CPU *cpu = X86_CPU(dev);
X86MachineState *x86ms = X86_MACHINE(hotplug_dev);
hotplug_handler_unplug(x86ms->acpi_dev, dev, &local_err);
if (local_err) {
goto out;
}
found_cpu = x86_find_cpu_slot(MACHINE(x86ms), cpu->apic_id, NULL);
found_cpu->cpu = NULL;
qdev_unrealize(dev);
/* decrement the number of CPUs */
x86ms->boot_cpus--;
/* Update the number of CPUs in CMOS */
x86_rtc_set_cpus_count(x86ms->rtc, x86ms->boot_cpus);
fw_cfg_modify_i16(x86ms->fw_cfg, FW_CFG_NB_CPUS, x86ms->boot_cpus);
out:
error_propagate(errp, local_err);
}
void x86_cpu_pre_plug(HotplugHandler *hotplug_dev,
DeviceState *dev, Error **errp)
{
int idx;
CPUState *cs;
CPUArchId *cpu_slot;
X86CPUTopoIDs topo_ids;
X86CPU *cpu = X86_CPU(dev);
CPUX86State *env = &cpu->env;
MachineState *ms = MACHINE(hotplug_dev);
X86MachineState *x86ms = X86_MACHINE(hotplug_dev);
unsigned int smp_cores = ms->smp.cores;
unsigned int smp_threads = ms->smp.threads;
X86CPUTopoInfo topo_info;
if (!object_dynamic_cast(OBJECT(cpu), ms->cpu_type)) {
error_setg(errp, "Invalid CPU type, expected cpu type: '%s'",
ms->cpu_type);
return;
}
init_topo_info(&topo_info, x86ms);
env->nr_dies = x86ms->smp_dies;
/*
* If APIC ID is not set,
* set it based on socket/die/core/thread properties.
*/
if (cpu->apic_id == UNASSIGNED_APIC_ID) {
int max_socket = (ms->smp.max_cpus - 1) /
smp_threads / smp_cores / x86ms->smp_dies;
/*
* die-id was optional in QEMU 4.0 and older, so keep it optional
* if there's only one die per socket.
*/
if (cpu->die_id < 0 && x86ms->smp_dies == 1) {
cpu->die_id = 0;
}
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;
}
if (cpu->die_id < 0) {
error_setg(errp, "CPU die-id is not set");
return;
} else if (cpu->die_id > x86ms->smp_dies - 1) {
error_setg(errp, "Invalid CPU die-id: %u must be in range 0:%u",
cpu->die_id, x86ms->smp_dies - 1);
return;
}
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_ids.pkg_id = cpu->socket_id;
topo_ids.die_id = cpu->die_id;
topo_ids.core_id = cpu->core_id;
topo_ids.smt_id = cpu->thread_id;
cpu->apic_id = x86_apicid_from_topo_ids(&topo_info, &topo_ids);
}
cpu_slot = x86_find_cpu_slot(MACHINE(x86ms), cpu->apic_id, &idx);
if (!cpu_slot) {
MachineState *ms = MACHINE(x86ms);
x86_topo_ids_from_apicid(cpu->apic_id, &topo_info, &topo_ids);
error_setg(errp,
"Invalid CPU [socket: %u, die: %u, core: %u, thread: %u] with"
" APIC ID %" PRIu32 ", valid index range 0:%d",
topo_ids.pkg_id, topo_ids.die_id, topo_ids.core_id, topo_ids.smt_id,
cpu->apic_id, ms->possible_cpus->len - 1);
return;
}
if (cpu_slot->cpu) {
error_setg(errp, "CPU[%d] with APIC ID %" PRIu32 " exists",
idx, cpu->apic_id);
return;
}
/* if 'address' properties socket-id/core-id/thread-id are not set, set them
* so that machine_query_hotpluggable_cpus would show correct values
*/
/* TODO: move socket_id/core_id/thread_id checks into x86_cpu_realizefn()
* once -smp refactoring is complete and there will be CPU private
* CPUState::nr_cores and CPUState::nr_threads fields instead of globals */
x86_topo_ids_from_apicid(cpu->apic_id, &topo_info, &topo_ids);
if (cpu->socket_id != -1 && cpu->socket_id != topo_ids.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_ids.pkg_id);
return;
}
cpu->socket_id = topo_ids.pkg_id;
if (cpu->die_id != -1 && cpu->die_id != topo_ids.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_ids.die_id);
return;
}
cpu->die_id = topo_ids.die_id;
if (cpu->core_id != -1 && cpu->core_id != topo_ids.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_ids.core_id);
return;
}
cpu->core_id = topo_ids.core_id;
if (cpu->thread_id != -1 && cpu->thread_id != topo_ids.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_ids.smt_id);
return;
}
cpu->thread_id = topo_ids.smt_id;
if (hyperv_feat_enabled(cpu, HYPERV_FEAT_VPINDEX) &&
!kvm_hv_vpindex_settable()) {
error_setg(errp, "kernel doesn't allow setting HyperV VP_INDEX");
return;
}
cs = CPU(cpu);
cs->cpu_index = idx;
numa_cpu_pre_plug(cpu_slot, dev, errp);
}
CpuInstanceProperties
x86_cpu_index_to_props(MachineState *ms, unsigned cpu_index)
{
@ -821,7 +1092,7 @@ void x86_bios_rom_init(MemoryRegion *rom_memory, bool isapc_ram_fw)
bios);
}
bool x86_machine_is_smm_enabled(X86MachineState *x86ms)
bool x86_machine_is_smm_enabled(const X86MachineState *x86ms)
{
bool smm_available = false;
@ -863,7 +1134,7 @@ static void x86_machine_set_smm(Object *obj, Visitor *v, const char *name,
visit_type_OnOffAuto(v, name, &x86ms->smm, errp);
}
bool x86_machine_is_acpi_enabled(X86MachineState *x86ms)
bool x86_machine_is_acpi_enabled(const X86MachineState *x86ms)
{
if (x86ms->acpi == ON_OFF_AUTO_OFF) {
return false;

View File

@ -71,9 +71,24 @@ typedef struct AcpiGedState AcpiGedState;
DECLARE_INSTANCE_CHECKER(AcpiGedState, ACPI_GED,
TYPE_ACPI_GED)
#define TYPE_ACPI_GED_X86 "acpi-ged-x86"
#define ACPI_GED_X86(obj) \
OBJECT_CHECK(AcpiGedX86State, (obj), TYPE_ACPI_GED_X86)
#define ACPI_GED_EVT_SEL_OFFSET 0x0
#define ACPI_GED_EVT_SEL_LEN 0x4
#define ACPI_GED_REG_SLEEP_CTL 0x00
#define ACPI_GED_REG_SLEEP_STS 0x01
#define ACPI_GED_REG_RESET 0x02
#define ACPI_GED_REG_COUNT 0x03
/* ACPI_GED_REG_RESET value for reset*/
#define ACPI_GED_RESET_VALUE 0x42
/* ACPI_GED_REG_SLEEP_CTL.SLP_TYP value for S5 (aka poweroff) */
#define ACPI_GED_SLP_TYP_S5 0x05
#define GED_DEVICE "GED"
#define AML_GED_EVT_REG "EREG"
#define AML_GED_EVT_SEL "ESEL"
@ -89,6 +104,7 @@ DECLARE_INSTANCE_CHECKER(AcpiGedState, ACPI_GED,
typedef struct GEDState {
MemoryRegion evt;
MemoryRegion regs;
uint32_t sel;
} GEDState;
@ -104,5 +120,6 @@ struct AcpiGedState {
void build_ged_aml(Aml *table, const char* name, HotplugHandler *hotplug_dev,
uint32_t ged_irq, AmlRegionSpace rs, hwaddr ged_base);
void acpi_dsdt_add_power_button(Aml *scope);
#endif

View File

@ -24,14 +24,19 @@
#include "hw/boards.h"
#include "hw/i386/x86.h"
#include "hw/acpi/acpi_dev_interface.h"
#include "qom/object.h"
/* Platform virtio definitions */
#define VIRTIO_MMIO_BASE 0xfeb00000
#define VIRTIO_IRQ_BASE 5
#define VIRTIO_NUM_TRANSPORTS 8
#define VIRTIO_CMDLINE_MAXLEN 64
#define GED_MMIO_BASE 0xfea00000
#define GED_MMIO_BASE_MEMHP (GED_MMIO_BASE + 0x100)
#define GED_MMIO_BASE_REGS (GED_MMIO_BASE + 0x200)
#define GED_MMIO_IRQ 9
/* Machine type options */
#define MICROVM_MACHINE_PIT "pit"
#define MICROVM_MACHINE_PIC "pic"
@ -59,7 +64,10 @@ struct MicrovmMachineState {
bool auto_kernel_cmdline;
/* Machine state */
uint32_t virtio_irq_base;
bool kernel_cmdline_fixed;
Notifier machine_done;
Notifier powerdown_req;
};
typedef struct MicrovmMachineState MicrovmMachineState;

View File

@ -30,7 +30,6 @@ struct PCMachineState {
Notifier machine_done;
/* Pointers to devices and objects: */
HotplugHandler *acpi_dev;
PCIBus *bus;
I2CBus *smbus;
PFlashCFI01 *flash[2];

View File

@ -52,6 +52,7 @@ struct X86MachineState {
FWCfgState *fw_cfg;
qemu_irq *gsi;
GMappedFile *initrd_mapped_file;
HotplugHandler *acpi_dev;
/* RAM information (sizes, addresses, configuration): */
ram_addr_t below_4g_mem_size, above_4g_mem_size;
@ -91,6 +92,16 @@ CpuInstanceProperties x86_cpu_index_to_props(MachineState *ms,
unsigned cpu_index);
int64_t x86_get_default_cpu_node_id(const MachineState *ms, int idx);
const CPUArchIdList *x86_possible_cpu_arch_ids(MachineState *ms);
CPUArchId *x86_find_cpu_slot(MachineState *ms, uint32_t id, int *idx);
void x86_rtc_set_cpus_count(ISADevice *rtc, uint16_t cpus_count);
void x86_cpu_pre_plug(HotplugHandler *hotplug_dev,
DeviceState *dev, Error **errp);
void x86_cpu_plug(HotplugHandler *hotplug_dev,
DeviceState *dev, Error **errp);
void x86_cpu_unplug_request_cb(HotplugHandler *hotplug_dev,
DeviceState *dev, Error **errp);
void x86_cpu_unplug_cb(HotplugHandler *hotplug_dev,
DeviceState *dev, Error **errp);
void x86_bios_rom_init(MemoryRegion *rom_memory, bool isapc_ram_fw);
@ -100,8 +111,8 @@ void x86_load_linux(X86MachineState *x86ms,
bool pvh_enabled,
bool linuxboot_dma_enabled);
bool x86_machine_is_smm_enabled(X86MachineState *x86ms);
bool x86_machine_is_acpi_enabled(X86MachineState *x86ms);
bool x86_machine_is_smm_enabled(const X86MachineState *x86ms);
bool x86_machine_is_acpi_enabled(const X86MachineState *x86ms);
/* Global System Interrupts */

Binary file not shown.

View File

@ -28,6 +28,7 @@ blobs = files(
'bios.bin',
'bios-256k.bin',
'bios-microvm.bin',
'qboot.rom',
'sgabios.bin',
'vgabios.bin',
'vgabios-cirrus.bin',

BIN
pc-bios/qboot.rom Normal file

Binary file not shown.

View File

@ -68,13 +68,16 @@ default help:
@echo " efi -- update UEFI (edk2) platform firmware"
@echo " opensbi32-generic -- update OpenSBI for 32-bit generic machine"
@echo " opensbi64-generic -- update OpenSBI for 64-bit generic machine"
@echo " bios-microvm -- update bios-microvm.bin (qboot)"
@echo " qboot -- update qboot"
@echo " clean -- delete the files generated by the previous" \
"build targets"
bios: build-seabios-config-seabios-128k build-seabios-config-seabios-256k
bios: build-seabios-config-seabios-128k \
build-seabios-config-seabios-256k \
build-seabios-config-seabios-microvm
cp seabios/builds/seabios-128k/bios.bin ../pc-bios/bios.bin
cp seabios/builds/seabios-256k/bios.bin ../pc-bios/bios-256k.bin
cp seabios/builds/seabios-microvm/bios.bin ../pc-bios/bios-microvm.bin
vgabios seavgabios: $(patsubst %,seavgabios-%,$(vgabios_variants))
@ -183,9 +186,9 @@ opensbi64-generic:
cp opensbi/build/platform/generic/firmware/fw_dynamic.bin ../pc-bios/opensbi-riscv64-generic-fw_dynamic.bin
cp opensbi/build/platform/generic/firmware/fw_dynamic.elf ../pc-bios/opensbi-riscv64-generic-fw_dynamic.elf
bios-microvm:
qboot:
$(MAKE) -C qboot
cp qboot/bios.bin ../pc-bios/bios-microvm.bin
cp qboot/bios.bin ../pc-bios/qboot.rom
npcm7xx_bootrom:
$(MAKE) -C vbootrom CROSS_COMPILE=$(arm_cross_prefix)

View File

@ -0,0 +1,26 @@
CONFIG_QEMU=y
CONFIG_QEMU_HARDWARE=y
CONFIG_PERMIT_UNALIGNED_PCIROM=y
CONFIG_ROM_SIZE=128
CONFIG_XEN=n
CONFIG_BOOTSPLASH=n
CONFIG_ATA=n
CONFIG_AHCI=n
CONFIG_SDCARD=n
CONFIG_PVSCSI=n
CONFIG_ESP_SCSI=n
CONFIG_LSI_SCSI=n
CONFIG_MEGASAS=n
CONFIG_MPT_SCSI=n
CONFIG_FLOPPY=n
CONFIG_FLASH_FLOPPY=n
CONFIG_NVME=n
CONFIG_PS2PORT=n
CONFIG_USB=n
CONFIG_LPT=n
CONFIG_RTC_TIMER=n
CONFIG_USE_SMM=n
CONFIG_PMTIMER=n
CONFIG_TCGBIOS=n
CONFIG_HARDWARE_IRQ=n
CONFIG_ACPI_PARSE=y

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -72,6 +72,7 @@ typedef struct {
const char *variant;
const char *uefi_fl1;
const char *uefi_fl2;
const char *blkdev;
const char *cd;
const uint64_t ram_start;
const uint64_t scan_len;
@ -666,9 +667,10 @@ static void test_acpi_one(const char *params, test_data *data)
args = g_strdup_printf("-machine %s,kernel-irqchip=off %s -accel tcg "
"-net none -display none %s "
"-drive id=hd0,if=none,file=%s,format=raw "
"-device ide-hd,drive=hd0 ",
"-device %s,drive=hd0 ",
data->machine, data->tcg_only ? "" : "-accel kvm",
params ? params : "", disk);
params ? params : "", disk,
data->blkdev ?: "ide-hd");
}
data->qts = qtest_init(args);
@ -1042,6 +1044,20 @@ static void test_acpi_virt_tcg_memhp(void)
}
static void test_acpi_microvm_tcg(void)
{
test_data data;
memset(&data, 0, sizeof(data));
data.machine = "microvm";
data.required_struct_types = NULL; /* no smbios */
data.required_struct_types_len = 0;
data.blkdev = "virtio-blk-device";
test_acpi_one(" -machine microvm,acpi=on,rtc=off",
&data);
free_test_data(&data);
}
static void test_acpi_virt_tcg_numamem(void)
{
test_data data = {
@ -1159,6 +1175,7 @@ int main(int argc, char *argv[])
qtest_add_func("acpi/q35/dimmpxm", test_acpi_q35_tcg_dimm_pxm);
qtest_add_func("acpi/piix4/acpihmat", test_acpi_piix4_tcg_acpi_hmat);
qtest_add_func("acpi/q35/acpihmat", test_acpi_q35_tcg_acpi_hmat);
qtest_add_func("acpi/microvm", test_acpi_microvm_tcg);
} else if (strcmp(arch, "aarch64") == 0) {
qtest_add_func("acpi/virt", test_acpi_virt_tcg);
qtest_add_func("acpi/virt/numamem", test_acpi_virt_tcg_numamem);