acpi,pci,pc,virtio fixes and enhancements

This includes new unit-tests for acpi by Marcel,
 hotplug for pci bridges by myself (piix only so far)
 and cpu hotplug for q35.
 And a bunch of fixes all over the place as usual.
 
 I included the patch to fix memory alignment for q35
 as well - even though it limits 32 bit guests to 3G (they
 previously could address more memory with PAE).
 To remove the limit, this will have to be fixed in seabios.
 
 I also added self as virtio co-maintainer so I don't need
 to troll the list for patches to review.
 
 Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQEcBAABAgAGBQJS5O2JAAoJECgfDbjSjVRpBRsH/iueYMYqtlIOCFSBf4TSU5LN
 3369DwM0EncrEIZhv6jjtX+CSKUca9IXkKcvBBHJOTwhUodGNZ1wJ/FpO9+AhjpB
 5XlIcF4nOi2eYDGs/8JToecK+edggTrSjJ8Bg5n7/bEtJ/oNyjTQ0RMX+2jXq1y3
 jnpVjO1ntQXkZQZWNM3O+8biVT1o8wJlFhFbYpx4TKgFplPnRrg1gZR3MtnhsZ+M
 OoBenpJYrh5Gw52d6HPauMDAux51MjGymzrk2Wmd/w3hPMP9BcxX7NvoapKlGTg9
 DkVr7cvbIzZ2JnsRLFNeWsJIupOjl6MwqhGJo5WcB5VS7NZTbVHmtBRvqKa76EU=
 =y3wE
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'mst/tags/for_anthony' into staging

acpi,pci,pc,virtio fixes and enhancements

This includes new unit-tests for acpi by Marcel,
hotplug for pci bridges by myself (piix only so far)
and cpu hotplug for q35.
And a bunch of fixes all over the place as usual.

I included the patch to fix memory alignment for q35
as well - even though it limits 32 bit guests to 3G (they
previously could address more memory with PAE).
To remove the limit, this will have to be fixed in seabios.

I also added self as virtio co-maintainer so I don't need
to troll the list for patches to review.

Signed-off-by: Michael S. Tsirkin <mst@redhat.com>

# gpg: Signature made Sun 26 Jan 2014 11:12:09 GMT using RSA key ID D28D5469
# gpg: Good signature from "Michael S. Tsirkin <mst@kernel.org>"
# gpg:                 aka "Michael S. Tsirkin <mst@redhat.com>"
# gpg: WARNING: This key is not certified with a trusted signature!
# gpg:          There is no indication that the signature belongs to the owner.
# Primary key fingerprint: 0270 606B 6F3C DF3D 0B17  0970 C350 3912 AFBE 8E67
#      Subkey fingerprint: 5D09 FD08 71C8 F85B 94CA  8A0D 281F 0DB8 D28D 5469

* mst/tags/for_anthony: (35 commits)
  MAINTAINERS: add self as virtio co-maintainer
  q35: document gigabyte_align
  q35: gigabyte alignment for ram
  acpi: Fix PCI hole handling on build_srat()
  pc: Save size of RAM below 4GB
  hw/pci: fix error flow in pci multifunction init
  acpi-test: update expected AML since recent changes
  pc: ACPI: update acpi-dsdt.hex.generated q35-acpi-dsdt.hex.generated
  pc: ACPI: unify source of CPU hotplug IO base/len
  pc: ACPI: expose PRST IO range via _CRS
  pc: Q35 DSDT: exclude CPU hotplug IO range from PCI bus resources
  pc: PIIX DSDT: exclude CPU/PCI hotplug & GPE0 IO range from PCI bus resources
  pc: set PRST base in DSDT depending on chipset
  acpi: ich9: add CPU hotplug handling to Q35 machine
  acpi: factor out common cpu hotplug code for PIIX4/Q35
  acpi-build: enable hotplug for PCI bridges
  piix4: add acpi pci hotplug support
  pcihp: generalization of piix4 acpi
  pci: add pci_for_each_bus_depth_first
  pc: make: fix dependencies: rebuild when included file is changed
  ...

Message-id: 1390735289-15563-1-git-send-email-mst@redhat.com
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2014-01-31 00:23:27 +00:00
commit 0159a64397
49 changed files with 1964 additions and 339 deletions

View file

@ -610,6 +610,7 @@ F: hw/*/*vhost*
virtio
M: Anthony Liguori <aliguori@amazon.com>
M: Michael S. Tsirkin <mst@redhat.com>
S: Supported
F: hw/*/virtio*

4
configure vendored
View file

@ -4774,6 +4774,10 @@ for bios_file in \
do
FILES="$FILES pc-bios/`basename $bios_file`"
done
for test_file in `find $source_path/tests/acpi-test-data -type f`
do
FILES="$FILES tests/acpi-test-data`echo $test_file | sed -e 's/.*acpi-test-data//'`"
done
mkdir -p $DIRS
for f in $FILES ; do
if [ -e "$source_path/$f" ] && [ "$source_path" != `pwd` ]; then

View file

@ -10,7 +10,9 @@ ACPI GPE block (IO ports 0xafe0-0xafe3, byte access):
Generic ACPI GPE block. Bit 2 (GPE.2) used to notify CPU
hot-add/remove event to ACPI BIOS, via SCI interrupt.
CPU present bitmap (IO port 0xaf00-0xaf1f, 1-byte access):
CPU present bitmap for:
ICH9-LPC (IO port 0x0cd8-0xcf7, 1-byte access)
PIIX-PM (IO port 0xaf00-0xaf1f, 1-byte access)
---------------------------------------------------------------
One bit per CPU. Bit position reflects corresponding CPU APIC ID.
Read-only.

View file

@ -1,2 +1 @@
common-obj-$(CONFIG_ACPI) += core.o piix4.o ich9.o
common-obj-$(CONFIG_ACPI) += core.o piix4.o ich9.o pcihp.o cpu_hotplug.o

64
hw/acpi/cpu_hotplug.c Normal file
View file

@ -0,0 +1,64 @@
/*
* QEMU ACPI hotplug utilities
*
* Copyright (C) 2013 Red Hat Inc
*
* Authors:
* Igor Mammedov <imammedo@redhat.com>
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*/
#include "hw/hw.h"
#include "hw/acpi/cpu_hotplug.h"
static uint64_t cpu_status_read(void *opaque, hwaddr addr, unsigned int size)
{
AcpiCpuHotplug *cpus = opaque;
uint64_t val = cpus->sts[addr];
return val;
}
static void cpu_status_write(void *opaque, hwaddr addr, uint64_t data,
unsigned int size)
{
/* TODO: implement VCPU removal on guest signal that CPU can be removed */
}
static const MemoryRegionOps AcpiCpuHotplug_ops = {
.read = cpu_status_read,
.write = cpu_status_write,
.endianness = DEVICE_LITTLE_ENDIAN,
.valid = {
.min_access_size = 1,
.max_access_size = 1,
},
};
void AcpiCpuHotplug_add(ACPIGPE *gpe, AcpiCpuHotplug *g, CPUState *cpu)
{
CPUClass *k = CPU_GET_CLASS(cpu);
int64_t cpu_id;
*gpe->sts = *gpe->sts | ACPI_CPU_HOTPLUG_STATUS;
cpu_id = k->get_arch_id(CPU(cpu));
g->sts[cpu_id / 8] |= (1 << (cpu_id % 8));
}
void AcpiCpuHotplug_init(MemoryRegion *parent, Object *owner,
AcpiCpuHotplug *gpe_cpu, uint16_t base)
{
CPUState *cpu;
CPU_FOREACH(cpu) {
CPUClass *cc = CPU_GET_CLASS(cpu);
int64_t id = cc->get_arch_id(cpu);
g_assert((id / 8) < ACPI_GPE_PROC_LEN);
gpe_cpu->sts[id / 8] |= (1 << (id % 8));
}
memory_region_init_io(&gpe_cpu->io, owner, &AcpiCpuHotplug_ops,
gpe_cpu, "acpi-cpu-hotplug", ACPI_GPE_PROC_LEN);
memory_region_add_subregion(parent, base, &gpe_cpu->io);
}

View file

@ -185,6 +185,15 @@ static void pm_powerdown_req(Notifier *n, void *opaque)
acpi_pm1_evt_power_down(&pm->acpi_regs);
}
static void ich9_cpu_added_req(Notifier *n, void *opaque)
{
ICH9LPCPMRegs *pm = container_of(n, ICH9LPCPMRegs, cpu_added_notifier);
assert(pm != NULL);
AcpiCpuHotplug_add(&pm->acpi_regs.gpe, &pm->gpe_cpu, CPU(opaque));
acpi_update_sci(&pm->acpi_regs, pm->irq);
}
void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm,
qemu_irq sci_irq)
{
@ -210,6 +219,11 @@ void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm,
qemu_register_reset(pm_reset, pm);
pm->powerdown_notifier.notify = pm_powerdown_req;
qemu_register_powerdown_notifier(&pm->powerdown_notifier);
AcpiCpuHotplug_init(pci_address_space_io(lpc_pci), OBJECT(lpc_pci),
&pm->gpe_cpu, ICH9_CPU_HOTPLUG_IO_BASE);
pm->cpu_added_notifier.notify = ich9_cpu_added_req;
qemu_register_cpu_added_notifier(&pm->cpu_added_notifier);
}
static void ich9_pm_get_gpe0_blk(Object *obj, Visitor *v,

316
hw/acpi/pcihp.c Normal file
View file

@ -0,0 +1,316 @@
/*
* QEMU<->ACPI BIOS PCI hotplug interface
*
* QEMU supports PCI hotplug via ACPI. This module
* implements the interface between QEMU and the ACPI BIOS.
* Interface specification - see docs/specs/acpi_pci_hotplug.txt
*
* Copyright (c) 2013, Red Hat Inc, Michael S. Tsirkin (mst@redhat.com)
* Copyright (c) 2006 Fabrice Bellard
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2 as published by the Free Software Foundation.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>
*
* Contributions after 2012-01-13 are licensed under the terms of the
* GNU GPL, version 2 or (at your option) any later version.
*/
#include "hw/acpi/pcihp.h"
#include "hw/hw.h"
#include "hw/i386/pc.h"
#include "hw/pci/pci.h"
#include "hw/acpi/acpi.h"
#include "sysemu/sysemu.h"
#include "qemu/range.h"
#include "exec/ioport.h"
#include "exec/address-spaces.h"
#include "hw/pci/pci_bus.h"
#include "qom/qom-qobject.h"
#include "qapi/qmp/qint.h"
//#define DEBUG
#ifdef DEBUG
# define ACPI_PCIHP_DPRINTF(format, ...) printf(format, ## __VA_ARGS__)
#else
# define ACPI_PCIHP_DPRINTF(format, ...) do { } while (0)
#endif
#define PCI_HOTPLUG_ADDR 0xae00
#define PCI_HOTPLUG_SIZE 0x0014
#define PCI_UP_BASE 0xae00
#define PCI_DOWN_BASE 0xae04
#define PCI_EJ_BASE 0xae08
#define PCI_RMV_BASE 0xae0c
#define PCI_SEL_BASE 0xae10
typedef struct AcpiPciHpFind {
int bsel;
PCIBus *bus;
} AcpiPciHpFind;
static int acpi_pcihp_get_bsel(PCIBus *bus)
{
QObject *o = object_property_get_qobject(OBJECT(bus),
ACPI_PCIHP_PROP_BSEL, NULL);
int64_t bsel = -1;
if (o) {
bsel = qint_get_int(qobject_to_qint(o));
}
if (bsel < 0) {
return -1;
}
return bsel;
}
static void acpi_pcihp_test_hotplug_bus(PCIBus *bus, void *opaque)
{
AcpiPciHpFind *find = opaque;
if (find->bsel == acpi_pcihp_get_bsel(bus)) {
find->bus = bus;
}
}
static PCIBus *acpi_pcihp_find_hotplug_bus(AcpiPciHpState *s, int bsel)
{
AcpiPciHpFind find = { .bsel = bsel, .bus = NULL };
if (bsel < 0) {
return NULL;
}
pci_for_each_bus(s->root, acpi_pcihp_test_hotplug_bus, &find);
/* Make bsel 0 eject root bus if bsel property is not set,
* for compatibility with non acpi setups.
* TODO: really needed?
*/
if (!bsel && !find.bus) {
find.bus = s->root;
}
return find.bus;
}
static bool acpi_pcihp_pc_no_hotplug(AcpiPciHpState *s, PCIDevice *dev)
{
PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(dev);
/*
* ACPI doesn't allow hotplug of bridge devices. Don't allow
* hot-unplug of bridge devices unless they were added by hotplug
* (and so, not described by acpi).
*/
return (pc->is_bridge && !dev->qdev.hotplugged) || pc->no_hotplug;
}
static void acpi_pcihp_eject_slot(AcpiPciHpState *s, unsigned bsel, unsigned slots)
{
BusChild *kid, *next;
int slot = ffs(slots) - 1;
bool slot_free = true;
PCIBus *bus = acpi_pcihp_find_hotplug_bus(s, bsel);
if (!bus) {
return;
}
/* Mark request as complete */
s->acpi_pcihp_pci_status[bsel].down &= ~(1U << slot);
QTAILQ_FOREACH_SAFE(kid, &bus->qbus.children, sibling, next) {
DeviceState *qdev = kid->child;
PCIDevice *dev = PCI_DEVICE(qdev);
if (PCI_SLOT(dev->devfn) == slot) {
if (acpi_pcihp_pc_no_hotplug(s, dev)) {
slot_free = false;
} else {
object_unparent(OBJECT(qdev));
}
}
}
if (slot_free) {
s->acpi_pcihp_pci_status[bsel].device_present &= ~(1U << slot);
}
}
static void acpi_pcihp_update_hotplug_bus(AcpiPciHpState *s, int bsel)
{
BusChild *kid, *next;
PCIBus *bus = acpi_pcihp_find_hotplug_bus(s, bsel);
/* Execute any pending removes during reset */
while (s->acpi_pcihp_pci_status[bsel].down) {
acpi_pcihp_eject_slot(s, bsel, s->acpi_pcihp_pci_status[bsel].down);
}
s->acpi_pcihp_pci_status[bsel].hotplug_enable = ~0;
s->acpi_pcihp_pci_status[bsel].device_present = 0;
if (!bus) {
return;
}
QTAILQ_FOREACH_SAFE(kid, &bus->qbus.children, sibling, next) {
DeviceState *qdev = kid->child;
PCIDevice *pdev = PCI_DEVICE(qdev);
int slot = PCI_SLOT(pdev->devfn);
if (acpi_pcihp_pc_no_hotplug(s, pdev)) {
s->acpi_pcihp_pci_status[bsel].hotplug_enable &= ~(1U << slot);
}
s->acpi_pcihp_pci_status[bsel].device_present |= (1U << slot);
}
}
static void acpi_pcihp_update(AcpiPciHpState *s)
{
int i;
for (i = 0; i < ACPI_PCIHP_MAX_HOTPLUG_BUS; ++i) {
acpi_pcihp_update_hotplug_bus(s, i);
}
}
void acpi_pcihp_reset(AcpiPciHpState *s)
{
acpi_pcihp_update(s);
}
static void enable_device(AcpiPciHpState *s, unsigned bsel, int slot)
{
s->acpi_pcihp_pci_status[bsel].device_present |= (1U << slot);
}
static void disable_device(AcpiPciHpState *s, unsigned bsel, int slot)
{
s->acpi_pcihp_pci_status[bsel].down |= (1U << slot);
}
int acpi_pcihp_device_hotplug(AcpiPciHpState *s, PCIDevice *dev,
PCIHotplugState state)
{
int slot = PCI_SLOT(dev->devfn);
int bsel = acpi_pcihp_get_bsel(dev->bus);
if (bsel < 0) {
return -1;
}
/* Don't send event when device is enabled during qemu machine creation:
* it is present on boot, no hotplug event is necessary. We do send an
* event when the device is disabled later. */
if (state == PCI_COLDPLUG_ENABLED) {
s->acpi_pcihp_pci_status[bsel].device_present |= (1U << slot);
return 0;
}
if (state == PCI_HOTPLUG_ENABLED) {
enable_device(s, bsel, slot);
} else {
disable_device(s, bsel, slot);
}
return 0;
}
static uint64_t pci_read(void *opaque, hwaddr addr, unsigned int size)
{
AcpiPciHpState *s = opaque;
uint32_t val = 0;
int bsel = s->hotplug_select;
if (bsel < 0 || bsel > ACPI_PCIHP_MAX_HOTPLUG_BUS) {
return 0;
}
switch (addr) {
case PCI_UP_BASE - PCI_HOTPLUG_ADDR:
/* Manufacture an "up" value to cause a device check on any hotplug
* slot with a device. Extra device checks are harmless. */
val = s->acpi_pcihp_pci_status[bsel].device_present &
s->acpi_pcihp_pci_status[bsel].hotplug_enable;
ACPI_PCIHP_DPRINTF("pci_up_read %" PRIu32 "\n", val);
break;
case PCI_DOWN_BASE - PCI_HOTPLUG_ADDR:
val = s->acpi_pcihp_pci_status[bsel].down;
ACPI_PCIHP_DPRINTF("pci_down_read %" PRIu32 "\n", val);
break;
case PCI_EJ_BASE - PCI_HOTPLUG_ADDR:
/* No feature defined yet */
ACPI_PCIHP_DPRINTF("pci_features_read %" PRIu32 "\n", val);
break;
case PCI_RMV_BASE - PCI_HOTPLUG_ADDR:
val = s->acpi_pcihp_pci_status[bsel].hotplug_enable;
ACPI_PCIHP_DPRINTF("pci_rmv_read %" PRIu32 "\n", val);
break;
case PCI_SEL_BASE - PCI_HOTPLUG_ADDR:
val = s->hotplug_select;
ACPI_PCIHP_DPRINTF("pci_sel_read %" PRIu32 "\n", val);
default:
break;
}
return val;
}
static void pci_write(void *opaque, hwaddr addr, uint64_t data,
unsigned int size)
{
AcpiPciHpState *s = opaque;
switch (addr) {
case PCI_EJ_BASE - PCI_HOTPLUG_ADDR:
if (s->hotplug_select >= ACPI_PCIHP_MAX_HOTPLUG_BUS) {
break;
}
acpi_pcihp_eject_slot(s, s->hotplug_select, data);
ACPI_PCIHP_DPRINTF("pciej write %" HWADDR_PRIx " <== %" PRIu64 "\n",
addr, data);
break;
case PCI_SEL_BASE - PCI_HOTPLUG_ADDR:
s->hotplug_select = data;
ACPI_PCIHP_DPRINTF("pcisel write %" HWADDR_PRIx " <== %" PRIu64 "\n",
addr, data);
default:
break;
}
}
static const MemoryRegionOps acpi_pcihp_io_ops = {
.read = pci_read,
.write = pci_write,
.endianness = DEVICE_LITTLE_ENDIAN,
.valid = {
.min_access_size = 4,
.max_access_size = 4,
},
};
void acpi_pcihp_init(AcpiPciHpState *s, PCIBus *root_bus,
MemoryRegion *address_space_io)
{
s->root= root_bus;
memory_region_init_io(&s->io, NULL, &acpi_pcihp_io_ops, s,
"acpi-pci-hotplug",
PCI_HOTPLUG_SIZE);
memory_region_add_subregion(address_space_io, PCI_HOTPLUG_ADDR, &s->io);
}
const VMStateDescription vmstate_acpi_pcihp_pci_status = {
.name = "acpi_pcihp_pci_status",
.version_id = 1,
.minimum_version_id = 1,
.minimum_version_id_old = 1,
.fields = (VMStateField []) {
VMSTATE_UINT32(up, AcpiPciHpPciStatus),
VMSTATE_UINT32(down, AcpiPciHpPciStatus),
VMSTATE_END_OF_LIST()
}
};

View file

@ -30,6 +30,8 @@
#include "hw/nvram/fw_cfg.h"
#include "exec/address-spaces.h"
#include "hw/acpi/piix4.h"
#include "hw/acpi/pcihp.h"
#include "hw/acpi/cpu_hotplug.h"
//#define DEBUG
@ -49,21 +51,13 @@
#define PCI_EJ_BASE 0xae08
#define PCI_RMV_BASE 0xae0c
#define PIIX4_PROC_BASE 0xaf00
#define PIIX4_PROC_LEN 32
#define PIIX4_PCI_HOTPLUG_STATUS 2
#define PIIX4_CPU_HOTPLUG_STATUS 4
struct pci_status {
uint32_t up; /* deprecated, maintained for migration compatibility */
uint32_t down;
};
typedef struct CPUStatus {
uint8_t sts[PIIX4_PROC_LEN];
} CPUStatus;
typedef struct PIIX4PMState {
/*< private >*/
PCIDevice parent_obj;
@ -73,8 +67,6 @@ typedef struct PIIX4PMState {
uint32_t io_base;
MemoryRegion io_gpe;
MemoryRegion io_pci;
MemoryRegion io_cpu;
ACPIREGS ar;
APMState apm;
@ -88,16 +80,21 @@ typedef struct PIIX4PMState {
Notifier machine_ready;
Notifier powerdown_notifier;
/* for pci hotplug */
/* for legacy pci hotplug (compatible with qemu 1.6 and older) */
MemoryRegion io_pci;
struct pci_status pci0_status;
uint32_t pci0_hotplug_enable;
uint32_t pci0_slot_device_present;
/* for new pci hotplug (with PCI2PCI bridge support) */
AcpiPciHpState acpi_pci_hotplug;
bool use_acpi_pci_hotplug;
uint8_t disable_s3;
uint8_t disable_s4;
uint8_t s4_val;
CPUStatus gpe_cpu;
AcpiCpuHotplug gpe_cpu;
Notifier cpu_added_notifier;
} PIIX4PMState;
@ -263,6 +260,18 @@ static int acpi_load_old(QEMUFile *f, void *opaque, int version_id)
return ret;
}
static bool vmstate_test_use_acpi_pci_hotplug(void *opaque, int version_id)
{
PIIX4PMState *s = opaque;
return s->use_acpi_pci_hotplug;
}
static bool vmstate_test_no_use_acpi_pci_hotplug(void *opaque, int version_id)
{
PIIX4PMState *s = opaque;
return !s->use_acpi_pci_hotplug;
}
/* qemu-kvm 1.2 uses version 3 but advertised as 2
* To support incoming qemu-kvm 1.2 migration, change version_id
* and minimum_version_id to 2 below (which breaks migration from
@ -285,8 +294,12 @@ static const VMStateDescription vmstate_acpi = {
VMSTATE_TIMER(ar.tmr.timer, PIIX4PMState),
VMSTATE_INT64(ar.tmr.overflow_time, PIIX4PMState),
VMSTATE_STRUCT(ar.gpe, PIIX4PMState, 2, vmstate_gpe, ACPIGPE),
VMSTATE_STRUCT(pci0_status, PIIX4PMState, 2, vmstate_pci_status,
struct pci_status),
VMSTATE_STRUCT_TEST(pci0_status, PIIX4PMState,
vmstate_test_no_use_acpi_pci_hotplug,
2, vmstate_pci_status,
struct pci_status),
VMSTATE_PCI_HOTPLUG(acpi_pci_hotplug, PIIX4PMState,
vmstate_test_use_acpi_pci_hotplug),
VMSTATE_END_OF_LIST()
}
};
@ -364,7 +377,11 @@ static void piix4_reset(void *opaque)
pci_conf[0x5B] = 0x02;
}
pm_io_space_update(s);
piix4_update_hotplug(s);
if (s->use_acpi_pci_hotplug) {
acpi_pcihp_reset(&s->acpi_pci_hotplug);
} else {
piix4_update_hotplug(s);
}
}
static void piix4_pm_powerdown_req(Notifier *n, void *opaque)
@ -375,6 +392,26 @@ static void piix4_pm_powerdown_req(Notifier *n, void *opaque)
acpi_pm1_evt_power_down(&s->ar);
}
static int piix4_acpi_pci_hotplug(DeviceState *qdev, PCIDevice *dev,
PCIHotplugState state)
{
PIIX4PMState *s = PIIX4_PM(qdev);
int ret = acpi_pcihp_device_hotplug(&s->acpi_pci_hotplug, dev, state);
if (ret < 0) {
return ret;
}
s->ar.gpe.sts[0] |= PIIX4_PCI_HOTPLUG_STATUS;
acpi_update_sci(&s->ar, s->irq);
return 0;
}
static void piix4_update_bus_hotplug(PCIBus *bus, void *opaque)
{
PIIX4PMState *s = opaque;
pci_bus_hotplug(bus, piix4_acpi_pci_hotplug, DEVICE(s));
}
static void piix4_pm_machine_ready(Notifier *n, void *opaque)
{
PIIX4PMState *s = container_of(n, PIIX4PMState, machine_ready);
@ -388,6 +425,10 @@ static void piix4_pm_machine_ready(Notifier *n, void *opaque)
pci_conf[0x63] = 0x60;
pci_conf[0x67] = (memory_region_present(io_as, 0x3f8) ? 0x08 : 0) |
(memory_region_present(io_as, 0x2f8) ? 0x90 : 0);
if (s->use_acpi_pci_hotplug) {
pci_for_each_bus(d->bus, piix4_update_bus_hotplug, s);
}
}
static void piix4_pm_add_propeties(PIIX4PMState *s)
@ -509,6 +550,8 @@ static Property piix4_pm_properties[] = {
DEFINE_PROP_UINT8(ACPI_PM_PROP_S3_DISABLED, PIIX4PMState, disable_s3, 0),
DEFINE_PROP_UINT8(ACPI_PM_PROP_S4_DISABLED, PIIX4PMState, disable_s4, 0),
DEFINE_PROP_UINT8(ACPI_PM_PROP_S4_VAL, PIIX4PMState, s4_val, 2),
DEFINE_PROP_BOOL("acpi-pci-hotplug-with-bridge-support", PIIX4PMState,
use_acpi_pci_hotplug, true),
DEFINE_PROP_END_OF_LIST(),
};
@ -632,61 +675,13 @@ static const MemoryRegionOps piix4_pci_ops = {
},
};
static uint64_t cpu_status_read(void *opaque, hwaddr addr, unsigned int size)
{
PIIX4PMState *s = opaque;
CPUStatus *cpus = &s->gpe_cpu;
uint64_t val = cpus->sts[addr];
return val;
}
static void cpu_status_write(void *opaque, hwaddr addr, uint64_t data,
unsigned int size)
{
/* TODO: implement VCPU removal on guest signal that CPU can be removed */
}
static const MemoryRegionOps cpu_hotplug_ops = {
.read = cpu_status_read,
.write = cpu_status_write,
.endianness = DEVICE_LITTLE_ENDIAN,
.valid = {
.min_access_size = 1,
.max_access_size = 1,
},
};
typedef enum {
PLUG,
UNPLUG,
} HotplugEventType;
static void piix4_cpu_hotplug_req(PIIX4PMState *s, CPUState *cpu,
HotplugEventType action)
{
CPUStatus *g = &s->gpe_cpu;
ACPIGPE *gpe = &s->ar.gpe;
CPUClass *k = CPU_GET_CLASS(cpu);
int64_t cpu_id;
assert(s != NULL);
*gpe->sts = *gpe->sts | PIIX4_CPU_HOTPLUG_STATUS;
cpu_id = k->get_arch_id(CPU(cpu));
if (action == PLUG) {
g->sts[cpu_id / 8] |= (1 << (cpu_id % 8));
} else {
g->sts[cpu_id / 8] &= ~(1 << (cpu_id % 8));
}
acpi_update_sci(&s->ar, s->irq);
}
static void piix4_cpu_added_req(Notifier *n, void *opaque)
{
PIIX4PMState *s = container_of(n, PIIX4PMState, cpu_added_notifier);
piix4_cpu_hotplug_req(s, CPU(opaque), PLUG);
assert(s != NULL);
AcpiCpuHotplug_add(&s->ar.gpe, &s->gpe_cpu, CPU(opaque));
acpi_update_sci(&s->ar, s->irq);
}
static int piix4_device_hotplug(DeviceState *qdev, PCIDevice *dev,
@ -695,28 +690,22 @@ static int piix4_device_hotplug(DeviceState *qdev, PCIDevice *dev,
static void piix4_acpi_system_hot_add_init(MemoryRegion *parent,
PCIBus *bus, PIIX4PMState *s)
{
CPUState *cpu;
memory_region_init_io(&s->io_gpe, OBJECT(s), &piix4_gpe_ops, s,
"acpi-gpe0", GPE_LEN);
memory_region_add_subregion(parent, GPE_BASE, &s->io_gpe);
memory_region_init_io(&s->io_pci, OBJECT(s), &piix4_pci_ops, s,
"acpi-pci-hotplug", PCI_HOTPLUG_SIZE);
memory_region_add_subregion(parent, PCI_HOTPLUG_ADDR,
&s->io_pci);
pci_bus_hotplug(bus, piix4_device_hotplug, DEVICE(s));
CPU_FOREACH(cpu) {
CPUClass *cc = CPU_GET_CLASS(cpu);
int64_t id = cc->get_arch_id(cpu);
g_assert((id / 8) < PIIX4_PROC_LEN);
s->gpe_cpu.sts[id / 8] |= (1 << (id % 8));
if (s->use_acpi_pci_hotplug) {
acpi_pcihp_init(&s->acpi_pci_hotplug, bus, parent);
} else {
memory_region_init_io(&s->io_pci, OBJECT(s), &piix4_pci_ops, s,
"acpi-pci-hotplug", PCI_HOTPLUG_SIZE);
memory_region_add_subregion(parent, PCI_HOTPLUG_ADDR,
&s->io_pci);
pci_bus_hotplug(bus, piix4_device_hotplug, DEVICE(s));
}
memory_region_init_io(&s->io_cpu, OBJECT(s), &cpu_hotplug_ops, s,
"acpi-cpu-hotplug", PIIX4_PROC_LEN);
memory_region_add_subregion(parent, PIIX4_PROC_BASE, &s->io_cpu);
AcpiCpuHotplug_init(parent, OBJECT(s), &s->gpe_cpu,
PIIX4_CPU_HOTPLUG_IO_BASE);
s->cpu_added_notifier.notify = piix4_cpu_added_req;
qemu_register_cpu_added_notifier(&s->cpu_added_notifier);
}

View file

@ -17,7 +17,7 @@ iasl-option=$(shell if test -z "`$(1) $(2) 2>&1 > /dev/null`" \
ifdef IASL
#IASL Present. Generate hex files from .dsl
hw/i386/%.hex: $(SRC_PATH)/hw/i386/%.dsl $(SRC_PATH)/scripts/acpi_extract_preprocess.py $(SRC_PATH)/scripts/acpi_extract.py
$(call quiet-command, cpp -P $< -o $*.dsl.i.orig, " CPP $(TARGET_DIR)$*.dsl.i.orig")
$(call quiet-command, cpp -P $(QEMU_DGFLAGS) $(QEMU_INCLUDES) $< -o $*.dsl.i.orig, " CPP $(TARGET_DIR)$*.dsl.i.orig")
$(call quiet-command, $(PYTHON) $(SRC_PATH)/scripts/acpi_extract_preprocess.py $*.dsl.i.orig > $*.dsl.i, " ACPI_PREPROCESS $(TARGET_DIR)$*.dsl.i")
$(call quiet-command, $(IASL) $(call iasl-option,$(IASL),-Pn,) -vs -l -tc -p $* $*.dsl.i $(if $(V), , > /dev/null) 2>&1 ," IASL $(TARGET_DIR)$*.dsl.i")
$(call quiet-command, $(PYTHON) $(SRC_PATH)/scripts/acpi_extract.py $*.lst > $*.off, " ACPI_EXTRACT $(TARGET_DIR)$*.off")

View file

@ -36,9 +36,11 @@
#include "hw/nvram/fw_cfg.h"
#include "bios-linker-loader.h"
#include "hw/loader.h"
#include "hw/isa/isa.h"
/* Supported chipsets: */
#include "hw/acpi/piix4.h"
#include "hw/acpi/pcihp.h"
#include "hw/i386/ich9.h"
#include "hw/pci/pci_bus.h"
#include "hw/pci-host/q35.h"
@ -78,8 +80,15 @@ typedef struct AcpiMiscInfo {
uint16_t pvpanic_port;
} AcpiMiscInfo;
typedef struct AcpiBuildPciBusHotplugState {
GArray *device_table;
GArray *notify_table;
struct AcpiBuildPciBusHotplugState *parent;
} AcpiBuildPciBusHotplugState;
static void acpi_get_dsdt(AcpiMiscInfo *info)
{
uint16_t *applesmc_sta;
Object *piix = piix4_pm_find();
Object *lpc = ich9_lpc_find();
assert(!!piix != !!lpc);
@ -87,11 +96,17 @@ static void acpi_get_dsdt(AcpiMiscInfo *info)
if (piix) {
info->dsdt_code = AcpiDsdtAmlCode;
info->dsdt_size = sizeof AcpiDsdtAmlCode;
applesmc_sta = piix_dsdt_applesmc_sta;
}
if (lpc) {
info->dsdt_code = Q35AcpiDsdtAmlCode;
info->dsdt_size = sizeof Q35AcpiDsdtAmlCode;
applesmc_sta = q35_dsdt_applesmc_sta;
}
/* Patch in appropriate value for AppleSMC _STA */
*(uint8_t *)(info->dsdt_code + *applesmc_sta) =
applesmc_find() ? 0x0b : 0x00;
}
static
@ -171,38 +186,6 @@ static void acpi_get_pm_info(AcpiPmInfo *pm)
NULL);
}
static void acpi_get_hotplug_info(AcpiMiscInfo *misc)
{
int i;
PCIBus *bus = find_i440fx();
if (!bus) {
/* Only PIIX supports ACPI hotplug */
memset(misc->slot_hotplug_enable, 0, sizeof misc->slot_hotplug_enable);
return;
}
memset(misc->slot_hotplug_enable, 0xff,
DIV_ROUND_UP(PCI_SLOT_MAX, BITS_PER_BYTE));
for (i = 0; i < ARRAY_SIZE(bus->devices); ++i) {
PCIDeviceClass *pc;
PCIDevice *pdev = bus->devices[i];
if (!pdev) {
continue;
}
pc = PCI_DEVICE_GET_CLASS(pdev);
if (pc->no_hotplug) {
int slot = PCI_SLOT(i);
clear_bit(slot, misc->slot_hotplug_enable);
}
}
}
static void acpi_get_misc_info(AcpiMiscInfo *info)
{
info->has_hpet = hpet_find();
@ -368,6 +351,12 @@ static void build_package(GArray *package, uint8_t op, unsigned min_bytes)
build_prepend_byte(package, op);
}
static void build_extop_package(GArray *package, uint8_t op)
{
build_package(package, op, 1);
build_prepend_byte(package, 0x5B); /* ExtOpPrefix */
}
static void build_append_value(GArray *table, uint32_t value, int size)
{
uint8_t prefix;
@ -394,8 +383,44 @@ static void build_append_value(GArray *table, uint32_t value, int size)
}
}
static void build_append_notify_target(GArray *method, GArray *target_name,
uint32_t value, int size)
static void build_append_int(GArray *table, uint32_t value)
{
if (value == 0x00) {
build_append_byte(table, 0x00); /* ZeroOp */
} else if (value == 0x01) {
build_append_byte(table, 0x01); /* OneOp */
} else if (value <= 0xFF) {
build_append_value(table, value, 1);
} else if (value <= 0xFFFFF) {
build_append_value(table, value, 2);
} else {
build_append_value(table, value, 4);
}
}
static GArray *build_alloc_method(const char *name, uint8_t arg_count)
{
GArray *method = build_alloc_array();
build_append_nameseg(method, "%s", name);
build_append_byte(method, arg_count); /* MethodFlags: ArgCount */
return method;
}
static void build_append_and_cleanup_method(GArray *device, GArray *method)
{
uint8_t op = 0x14; /* MethodOp */
build_package(method, op, 0);
build_append_array(device, method);
build_free_array(method);
}
static void build_append_notify_target_ifequal(GArray *method,
GArray *target_name,
uint32_t value, int size)
{
GArray *notify = build_alloc_array();
uint8_t op = 0xA0; /* IfOp */
@ -415,6 +440,7 @@ static void build_append_notify_target(GArray *method, GArray *target_name,
build_free_array(notify);
}
/* End here */
#define ACPI_PORT_SMI_CMD 0x00b2 /* TODO: this is APM_CNT_IOPORT */
static inline void *acpi_data_push(GArray *table_data, unsigned size)
@ -624,44 +650,236 @@ static inline char acpi_get_hex(uint32_t val)
#include "hw/i386/ssdt-pcihp.hex"
static void
build_append_notify(GArray *device, const char *name,
const char *format, int skip, int count)
build_append_notify_method(GArray *device, const char *name,
const char *format, int count)
{
int i;
GArray *method = build_alloc_array();
uint8_t op = 0x14; /* MethodOp */
GArray *method = build_alloc_method(name, 2);
build_append_nameseg(method, "%s", name);
build_append_byte(method, 0x02); /* MethodFlags: ArgCount */
for (i = skip; i < count; i++) {
for (i = 0; i < count; i++) {
GArray *target = build_alloc_array();
build_append_nameseg(target, format, i);
assert(i < 256); /* Fits in 1 byte */
build_append_notify_target(method, target, i, 1);
build_append_notify_target_ifequal(method, target, i, 1);
build_free_array(target);
}
build_package(method, op, 2);
build_append_array(device, method);
build_free_array(method);
build_append_and_cleanup_method(device, method);
}
static void patch_pcihp(int slot, uint8_t *ssdt_ptr, uint32_t eject)
static void patch_pcihp(int slot, uint8_t *ssdt_ptr)
{
ssdt_ptr[ACPI_PCIHP_OFFSET_HEX] = acpi_get_hex(slot >> 4);
ssdt_ptr[ACPI_PCIHP_OFFSET_HEX + 1] = acpi_get_hex(slot);
unsigned devfn = PCI_DEVFN(slot, 0);
ssdt_ptr[ACPI_PCIHP_OFFSET_HEX] = acpi_get_hex(devfn >> 4);
ssdt_ptr[ACPI_PCIHP_OFFSET_HEX + 1] = acpi_get_hex(devfn);
ssdt_ptr[ACPI_PCIHP_OFFSET_ID] = slot;
ssdt_ptr[ACPI_PCIHP_OFFSET_ADR + 2] = slot;
}
/* Runtime patching of ACPI_EJ0: to disable hotplug for a slot,
* replace the method name: _EJ0 by ACPI_EJ0_.
*/
/* Sanity check */
assert(!memcmp(ssdt_ptr + ACPI_PCIHP_OFFSET_EJ0, "_EJ0", 4));
/* Assign BSEL property to all buses. In the future, this can be changed
* to only assign to buses that support hotplug.
*/
static void *acpi_set_bsel(PCIBus *bus, void *opaque)
{
unsigned *bsel_alloc = opaque;
unsigned *bus_bsel;
if (!eject) {
memcpy(ssdt_ptr + ACPI_PCIHP_OFFSET_EJ0, "EJ0_", 4);
if (bus->qbus.allow_hotplug) {
bus_bsel = g_malloc(sizeof *bus_bsel);
*bus_bsel = (*bsel_alloc)++;
object_property_add_uint32_ptr(OBJECT(bus), ACPI_PCIHP_PROP_BSEL,
bus_bsel, NULL);
}
return bsel_alloc;
}
static void acpi_set_pci_info(void)
{
PCIBus *bus = find_i440fx(); /* TODO: Q35 support */
unsigned bsel_alloc = 0;
if (bus) {
/* Scan all PCI buses. Set property to enable acpi based hotplug. */
pci_for_each_bus_depth_first(bus, acpi_set_bsel, NULL, &bsel_alloc);
}
}
static void build_pci_bus_state_init(AcpiBuildPciBusHotplugState *state,
AcpiBuildPciBusHotplugState *parent)
{
state->parent = parent;
state->device_table = build_alloc_array();
state->notify_table = build_alloc_array();
}
static void build_pci_bus_state_cleanup(AcpiBuildPciBusHotplugState *state)
{
build_free_array(state->device_table);
build_free_array(state->notify_table);
}
static void *build_pci_bus_begin(PCIBus *bus, void *parent_state)
{
AcpiBuildPciBusHotplugState *parent = parent_state;
AcpiBuildPciBusHotplugState *child = g_malloc(sizeof *child);
build_pci_bus_state_init(child, parent);
return child;
}
static void build_pci_bus_end(PCIBus *bus, void *bus_state)
{
AcpiBuildPciBusHotplugState *child = bus_state;
AcpiBuildPciBusHotplugState *parent = child->parent;
GArray *bus_table = build_alloc_array();
DECLARE_BITMAP(slot_hotplug_enable, PCI_SLOT_MAX);
uint8_t op;
int i;
QObject *bsel;
GArray *method;
bool bus_hotplug_support = false;
if (bus->parent_dev) {
op = 0x82; /* DeviceOp */
build_append_nameseg(bus_table, "S%.02X_",
bus->parent_dev->devfn);
build_append_byte(bus_table, 0x08); /* NameOp */
build_append_nameseg(bus_table, "_SUN");
build_append_value(bus_table, PCI_SLOT(bus->parent_dev->devfn), 1);
build_append_byte(bus_table, 0x08); /* NameOp */
build_append_nameseg(bus_table, "_ADR");
build_append_value(bus_table, (PCI_SLOT(bus->parent_dev->devfn) << 16) |
PCI_FUNC(bus->parent_dev->devfn), 4);
} else {
op = 0x10; /* ScopeOp */;
build_append_nameseg(bus_table, "PCI0");
}
bsel = object_property_get_qobject(OBJECT(bus), ACPI_PCIHP_PROP_BSEL, NULL);
if (bsel) {
build_append_byte(bus_table, 0x08); /* NameOp */
build_append_nameseg(bus_table, "BSEL");
build_append_int(bus_table, qint_get_int(qobject_to_qint(bsel)));
memset(slot_hotplug_enable, 0xff, sizeof slot_hotplug_enable);
for (i = 0; i < ARRAY_SIZE(bus->devices); ++i) {
PCIDeviceClass *pc;
PCIDevice *pdev = bus->devices[i];
if (!pdev) {
continue;
}
pc = PCI_DEVICE_GET_CLASS(pdev);
if (pc->no_hotplug || pc->is_bridge) {
int slot = PCI_SLOT(i);
clear_bit(slot, slot_hotplug_enable);
}
}
/* Append Device object for each slot which supports eject */
for (i = 0; i < PCI_SLOT_MAX; i++) {
bool can_eject = test_bit(i, slot_hotplug_enable);
if (can_eject) {
void *pcihp = acpi_data_push(bus_table,
ACPI_PCIHP_SIZEOF);
memcpy(pcihp, ACPI_PCIHP_AML, ACPI_PCIHP_SIZEOF);
patch_pcihp(i, pcihp);
bus_hotplug_support = true;
}
}
method = build_alloc_method("DVNT", 2);
for (i = 0; i < PCI_SLOT_MAX; i++) {
GArray *notify;
uint8_t op;
if (!test_bit(i, slot_hotplug_enable)) {
continue;
}
notify = build_alloc_array();
op = 0xA0; /* IfOp */
build_append_byte(notify, 0x7B); /* AndOp */
build_append_byte(notify, 0x68); /* Arg0Op */
build_append_int(notify, 0x1 << i);
build_append_byte(notify, 0x00); /* NullName */
build_append_byte(notify, 0x86); /* NotifyOp */
build_append_nameseg(notify, "S%.02X_", PCI_DEVFN(i, 0));
build_append_byte(notify, 0x69); /* Arg1Op */
/* Pack it up */
build_package(notify, op, 0);
build_append_array(method, notify);
build_free_array(notify);
}
build_append_and_cleanup_method(bus_table, method);
}
/* Append PCNT method to notify about events on local and child buses.
* Add unconditionally for root since DSDT expects it.
*/
if (bus_hotplug_support || child->notify_table->len || !bus->parent_dev) {
method = build_alloc_method("PCNT", 0);
/* If bus supports hotplug select it and notify about local events */
if (bsel) {
build_append_byte(method, 0x70); /* StoreOp */
build_append_int(method, qint_get_int(qobject_to_qint(bsel)));
build_append_nameseg(method, "BNUM");
build_append_nameseg(method, "DVNT");
build_append_nameseg(method, "PCIU");
build_append_int(method, 1); /* Device Check */
build_append_nameseg(method, "DVNT");
build_append_nameseg(method, "PCID");
build_append_int(method, 3); /* Eject Request */
}
/* Notify about child bus events in any case */
build_append_array(method, child->notify_table);
build_append_and_cleanup_method(bus_table, method);
/* Append description of child buses */
build_append_array(bus_table, child->device_table);
/* Pack it up */
if (bus->parent_dev) {
build_extop_package(bus_table, op);
} else {
build_package(bus_table, op, 0);
}
/* Append our bus description to parent table */
build_append_array(parent->device_table, bus_table);
/* Also tell parent how to notify us, invoking PCNT method.
* At the moment this is not needed for root as we have a single root.
*/
if (bus->parent_dev) {
build_append_byte(parent->notify_table, '^'); /* ParentPrefixChar */
build_append_byte(parent->notify_table, 0x2E); /* DualNamePrefix */
build_append_nameseg(parent->notify_table, "S%.02X_",
bus->parent_dev->devfn);
build_append_nameseg(parent->notify_table, "PCNT");
}
}
build_free_array(bus_table);
build_pci_bus_state_cleanup(child);
g_free(child);
}
static void patch_pci_windows(PcPciInfo *pci, uint8_t *start, unsigned size)
@ -733,7 +951,7 @@ build_ssdt(GArray *table_data, GArray *linker,
* Method(NTFY, 2) {If (LEqual(Arg0, 0x00)) {Notify(CP00, Arg1)} ...}
*/
/* Arg0 = Processor ID = APIC ID */
build_append_notify(sb_scope, "NTFY", "CP%0.02X", 0, acpi_cpus);
build_append_notify_method(sb_scope, "NTFY", "CP%0.02X", acpi_cpus);
/* build "Name(CPON, Package() { One, One, ..., Zero, Zero, ... })" */
build_append_byte(sb_scope, 0x08); /* NameOp */
@ -755,24 +973,19 @@ build_ssdt(GArray *table_data, GArray *linker,
}
{
GArray *pci0 = build_alloc_array();
uint8_t op = 0x10; /* ScopeOp */;
AcpiBuildPciBusHotplugState hotplug_state;
PCIBus *bus = find_i440fx(); /* TODO: Q35 support */
build_append_nameseg(pci0, "PCI0");
build_pci_bus_state_init(&hotplug_state, NULL);
/* build Device object for each slot */
for (i = 1; i < PCI_SLOT_MAX; i++) {
bool eject = test_bit(i, misc->slot_hotplug_enable);
void *pcihp = acpi_data_push(pci0, ACPI_PCIHP_SIZEOF);
memcpy(pcihp, ACPI_PCIHP_AML, ACPI_PCIHP_SIZEOF);
patch_pcihp(i, pcihp, eject);
if (bus) {
/* Scan all PCI buses. Generate tables to support hotplug. */
pci_for_each_bus_depth_first(bus, build_pci_bus_begin,
build_pci_bus_end, &hotplug_state);
}
build_append_notify(pci0, "PCNT", "S%0.02X_", 1, PCI_SLOT_MAX);
build_package(pci0, op, 3);
build_append_array(sb_scope, pci0);
build_free_array(pci0);
build_append_array(sb_scope, hotplug_state.device_table);
build_pci_bus_state_cleanup(&hotplug_state);
}
build_package(sb_scope, op, 3);
@ -867,16 +1080,16 @@ build_srat(GArray *table_data, GArray *linker,
next_base = mem_base + mem_len;
/* Cut out the ACPI_PCI hole */
if (mem_base <= guest_info->ram_size &&
next_base > guest_info->ram_size) {
mem_len -= next_base - guest_info->ram_size;
if (mem_base <= guest_info->ram_size_below_4g &&
next_base > guest_info->ram_size_below_4g) {
mem_len -= next_base - guest_info->ram_size_below_4g;
if (mem_len > 0) {
numamem = acpi_data_push(table_data, sizeof *numamem);
acpi_build_srat_memory(numamem, mem_base, mem_len, i-1, 1);
}
mem_base = 1ULL << 32;
mem_len = next_base - guest_info->ram_size;
next_base += (1ULL << 32) - guest_info->ram_size;
mem_len = next_base - guest_info->ram_size_below_4g;
next_base += (1ULL << 32) - guest_info->ram_size_below_4g;
}
numamem = acpi_data_push(table_data, sizeof *numamem);
acpi_build_srat_memory(numamem, mem_base, mem_len, i - 1, 1);
@ -1055,7 +1268,6 @@ void acpi_build(PcGuestInfo *guest_info, AcpiBuildTables *tables)
acpi_get_cpu_info(&cpu);
acpi_get_pm_info(&pm);
acpi_get_dsdt(&misc);
acpi_get_hotplug_info(&misc);
acpi_get_misc_info(&misc);
acpi_get_pci_info(&pci);
@ -1200,6 +1412,8 @@ void acpi_setup(PcGuestInfo *guest_info)
build_state->guest_info = guest_info;
acpi_set_pci_info();
acpi_build_tables_init(&tables);
acpi_build(build_state->guest_info, &tables);

View file

@ -16,6 +16,7 @@
/****************************************************************
* CPU hotplug
****************************************************************/
#define CPU_HOTPLUG_RESOURCE_DEVICE PRES
Scope(\_SB) {
/* Objects filled in by run-time generated SSDT */
@ -52,7 +53,8 @@ Scope(\_SB) {
Sleep(200)
}
OperationRegion(PRST, SystemIO, 0xaf00, 32)
#define CPU_STATUS_LEN ACPI_GPE_PROC_LEN
OperationRegion(PRST, SystemIO, CPU_STATUS_BASE, CPU_STATUS_LEN)
Field(PRST, ByteAcc, NoLock, Preserve) {
PRS, 256
}
@ -89,4 +91,14 @@ Scope(\_SB) {
Increment(Local0)
}
}
Device(CPU_HOTPLUG_RESOURCE_DEVICE) {
Name(_HID, "ACPI0004")
Name(_CRS, ResourceTemplate() {
IO(Decode16, CPU_STATUS_BASE, CPU_STATUS_BASE, 0, CPU_STATUS_LEN)
})
Name(_STA, 0xB) /* present, functioning, decoding, not shown in UI */
}
}

View file

@ -16,6 +16,17 @@
/* Common legacy ISA style devices. */
Scope(\_SB.PCI0.ISA) {
Device (SMC) {
Name(_HID, EisaId("APP0001"))
/* _STA will be patched to 0x0B if AppleSMC is present */
ACPI_EXTRACT_NAME_BYTE_CONST DSDT_APPLESMC_STA
Name(_STA, 0xF0)
Name(_CRS, ResourceTemplate () {
IO (Decode16, 0x0300, 0x0300, 0x01, 0x20)
IRQNoFlags() { 6 }
})
}
Device(RTC) {
Name(_HID, EisaId("PNP0B00"))
Name(_CRS, ResourceTemplate() {

View file

@ -30,20 +30,7 @@ Scope(\_SB.PCI0) {
0x01, // Address Alignment
0x08, // Address Length
)
WordIO(ResourceProducer, MinFixed, MaxFixed, PosDecode, EntireRange,
0x0000, // Address Space Granularity
0x0000, // Address Range Minimum
0x0CF7, // Address Range Maximum
0x0000, // Address Translation Offset
0x0CF8, // Address Length
,, , TypeStatic)
WordIO(ResourceProducer, MinFixed, MaxFixed, PosDecode, EntireRange,
0x0000, // Address Space Granularity
0x0D00, // Address Range Minimum
0xFFFF, // Address Range Maximum
0x0000, // Address Translation Offset
0xF300, // Address Length
,, , TypeStatic)
BOARD_SPECIFIC_PCI_RESOURSES
DWordMemory(ResourceProducer, PosDecode, MinFixed, MaxFixed, Cacheable, ReadWrite,
0x00000000, // Address Space Granularity
0x000A0000, // Address Range Minimum

View file

@ -35,6 +35,45 @@ DefinitionBlock (
/****************************************************************
* PCI Bus definition
****************************************************************/
#define BOARD_SPECIFIC_PCI_RESOURSES \
WordIO(ResourceProducer, MinFixed, MaxFixed, PosDecode, EntireRange, \
0x0000, \
0x0000, \
0x0CF7, \
0x0000, \
0x0CF8, \
,, , TypeStatic) \
WordIO(ResourceProducer, MinFixed, MaxFixed, PosDecode, EntireRange, \
0x0000, \
0x0D00, \
0xADFF, \
0x0000, \
0xA100, \
,, , TypeStatic) \
/* 0xae00-0xae0e hole for PCI hotplug, hw/acpi/piix4.c:PCI_HOTPLUG_ADDR */ \
WordIO(ResourceProducer, MinFixed, MaxFixed, PosDecode, EntireRange, \
0x0000, \
0xAE0F, \
0xAEFF, \
0x0000, \
0x00F1, \
,, , TypeStatic) \
/* 0xaf00-0xaf1f hole for CPU hotplug, hw/acpi/piix4.c:PIIX4_PROC_BASE */ \
WordIO(ResourceProducer, MinFixed, MaxFixed, PosDecode, EntireRange, \
0x0000, \
0xAF20, \
0xAFDF, \
0x0000, \
0x00C0, \
,, , TypeStatic) \
/* 0xafe0-0xafe3 hole for ACPI.GPE0, hw/acpi/piix4.c:GPE_BASE */ \
WordIO(ResourceProducer, MinFixed, MaxFixed, PosDecode, EntireRange, \
0x0000, \
0xAFE4, \
0xFFFF, \
0x0000, \
0x501C, \
,, , TypeStatic)
Scope(\_SB) {
Device(PCI0) {
@ -114,6 +153,7 @@ DefinitionBlock (
}
}
#define DSDT_APPLESMC_STA piix_dsdt_applesmc_sta
#include "acpi-dsdt-isa.dsl"
@ -133,32 +173,28 @@ DefinitionBlock (
B0EJ, 32,
}
OperationRegion(BNMR, SystemIO, 0xae10, 0x04)
Field(BNMR, DWordAcc, NoLock, WriteAsZeros) {
BNUM, 32,
}
/* Lock to protect access to fields above. */
Mutex(BLCK, 0)
/* Methods called by bulk generated PCI devices below */
/* Methods called by hotplug devices */
Method(PCEJ, 1, NotSerialized) {
Method(PCEJ, 2, NotSerialized) {
// _EJ0 method - eject callback
Store(ShiftLeft(1, Arg0), B0EJ)
Acquire(BLCK, 0xFFFF)
Store(Arg0, BNUM)
Store(ShiftLeft(1, Arg1), B0EJ)
Release(BLCK)
Return (0x0)
}
/* Hotplug notification method supplied by SSDT */
External(\_SB.PCI0.PCNT, MethodObj)
/* PCI hotplug notify method */
Method(PCNF, 0) {
// Local0 = iterator
Store(Zero, Local0)
While (LLess(Local0, 31)) {
Increment(Local0)
If (And(PCIU, ShiftLeft(1, Local0))) {
PCNT(Local0, 1)
}
If (And(PCID, ShiftLeft(1, Local0))) {
PCNT(Local0, 3)
}
}
}
}
@ -293,6 +329,8 @@ DefinitionBlock (
}
}
#include "hw/acpi/cpu_hotplug_defs.h"
#define CPU_STATUS_BASE PIIX4_CPU_HOTPLUG_IO_BASE
#include "acpi-dsdt-cpu-hotplug.dsl"
@ -307,7 +345,9 @@ DefinitionBlock (
}
Method(_E01) {
// PCI hotplug event
\_SB.PCI0.PCNF()
Acquire(\_SB.PCI0.BLCK, 0xFFFF)
\_SB.PCI0.PCNT()
Release(\_SB.PCI0.BLCK)
}
Method(_E02) {
// CPU hotplug event

View file

@ -3,12 +3,12 @@ static unsigned char AcpiDsdtAmlCode[] = {
0x53,
0x44,
0x54,
0x37,
0x87,
0x11,
0x0,
0x0,
0x1,
0xd8,
0xb8,
0x42,
0x58,
0x50,
@ -860,8 +860,8 @@ static unsigned char AcpiDsdtAmlCode[] = {
0x4e,
0x1,
0x10,
0x4c,
0x1b,
0x4b,
0x1e,
0x2f,
0x3,
0x5f,
@ -879,6 +879,53 @@ static unsigned char AcpiDsdtAmlCode[] = {
0x5b,
0x82,
0x2d,
0x53,
0x4d,
0x43,
0x5f,
0x8,
0x5f,
0x48,
0x49,
0x44,
0xc,
0x6,
0x10,
0x0,
0x1,
0x8,
0x5f,
0x53,
0x54,
0x41,
0xb,
0x0,
0xff,
0x8,
0x5f,
0x43,
0x52,
0x53,
0x11,
0x10,
0xa,
0xd,
0x47,
0x1,
0x0,
0x3,
0x0,
0x3,
0x1,
0x20,
0x22,
0x40,
0x0,
0x79,
0x0,
0x5b,
0x82,
0x2d,
0x52,
0x54,
0x43,
@ -1305,7 +1352,7 @@ static unsigned char AcpiDsdtAmlCode[] = {
0x79,
0x0,
0x10,
0x4b,
0x48,
0x8,
0x2e,
0x5f,
@ -1371,79 +1418,76 @@ static unsigned char AcpiDsdtAmlCode[] = {
0x45,
0x4a,
0x20,
0x5b,
0x80,
0x42,
0x4e,
0x4d,
0x52,
0x1,
0xb,
0x10,
0xae,
0xa,
0x4,
0x5b,
0x81,
0xb,
0x42,
0x4e,
0x4d,
0x52,
0x43,
0x42,
0x4e,
0x55,
0x4d,
0x20,
0x5b,
0x1,
0x42,
0x4c,
0x43,
0x4b,
0x0,
0x14,
0x11,
0x25,
0x50,
0x43,
0x45,
0x4a,
0x1,
0x2,
0x5b,
0x23,
0x42,
0x4c,
0x43,
0x4b,
0xff,
0xff,
0x70,
0x68,
0x42,
0x4e,
0x55,
0x4d,
0x70,
0x79,
0x1,
0x68,
0x69,
0x0,
0x42,
0x30,
0x45,
0x4a,
0x5b,
0x27,
0x42,
0x4c,
0x43,
0x4b,
0xa4,
0x0,
0x14,
0x36,
0x50,
0x43,
0x4e,
0x46,
0x0,
0x70,
0x0,
0x60,
0xa2,
0x2c,
0x95,
0x60,
0xa,
0x1f,
0x75,
0x60,
0xa0,
0x11,
0x7b,
0x50,
0x43,
0x49,
0x55,
0x79,
0x1,
0x60,
0x0,
0x0,
0x50,
0x43,
0x4e,
0x54,
0x60,
0x1,
0xa0,
0x12,
0x7b,
0x50,
0x43,
0x49,
0x44,
0x79,
0x1,
0x60,
0x0,
0x0,
0x50,
0x43,
0x4e,
0x54,
0x60,
0xa,
0x3,
0x10,
0x4a,
0xa0,
@ -4248,8 +4292,8 @@ static unsigned char AcpiDsdtAmlCode[] = {
0x75,
0x60,
0x10,
0x4e,
0x9,
0x42,
0xc,
0x5f,
0x47,
0x50,
@ -4277,12 +4321,31 @@ static unsigned char AcpiDsdtAmlCode[] = {
0x30,
0x0,
0x14,
0x15,
0x39,
0x5f,
0x45,
0x30,
0x31,
0x0,
0x5b,
0x23,
0x5c,
0x2f,
0x3,
0x5f,
0x53,
0x42,
0x5f,
0x50,
0x43,
0x49,
0x30,
0x42,
0x4c,
0x43,
0x4b,
0xff,
0xff,
0x5c,
0x2f,
0x3,
@ -4297,7 +4360,24 @@ static unsigned char AcpiDsdtAmlCode[] = {
0x50,
0x43,
0x4e,
0x46,
0x54,
0x5b,
0x27,
0x5c,
0x2f,
0x3,
0x5f,
0x53,
0x42,
0x5f,
0x50,
0x43,
0x49,
0x30,
0x42,
0x4c,
0x43,
0x4b,
0x14,
0x10,
0x5f,
@ -4407,3 +4487,6 @@ static unsigned char AcpiDsdtAmlCode[] = {
0x46,
0x0
};
static unsigned short piix_dsdt_applesmc_sta[] = {
0x384
};

View file

@ -1072,6 +1072,7 @@ PcGuestInfo *pc_guest_info_init(ram_addr_t below_4g_mem_size,
PcGuestInfo *guest_info = &guest_info_state->info;
int i, j;
guest_info->ram_size_below_4g = below_4g_mem_size;
guest_info->ram_size = below_4g_mem_size + above_4g_mem_size;
guest_info->apic_id_limit = pc_apic_id_limit(max_cpus);
guest_info->apic_xrupt_override = kvm_allows_irq0_override();

View file

@ -51,6 +51,11 @@
static bool has_pci_info;
static bool has_acpi_build = true;
static bool smbios_type1_defaults = true;
/* Make sure that guest addresses aligned at 1Gbyte boundaries get mapped to
* host addresses aligned at 1Gbyte boundaries. This way we can use 1GByte
* pages in the host.
*/
static bool gigabyte_align = true;
/* PC hardware initialisation */
static void pc_q35_init(QEMUMachineInitArgs *args)
@ -92,9 +97,19 @@ static void pc_q35_init(QEMUMachineInitArgs *args)
kvmclock_create();
/* Check whether RAM fits below 4G (leaving 1/2 GByte for IO memory
* and 256 Mbytes for PCI Express Enhanced Configuration Access Mapping
* also known as MMCFG).
* If it doesn't, we need to split it in chunks below and above 4G.
* In any case, try to make sure that guest addresses aligned at
* 1G boundaries get mapped to host addresses aligned at 1G boundaries.
* For old machine types, use whatever split we used historically to avoid
* breaking migration.
*/
if (args->ram_size >= 0xb0000000) {
above_4g_mem_size = args->ram_size - 0xb0000000;
below_4g_mem_size = 0xb0000000;
ram_addr_t lowmem = gigabyte_align ? 0x80000000 : 0xb0000000;
above_4g_mem_size = args->ram_size - lowmem;
below_4g_mem_size = lowmem;
} else {
above_4g_mem_size = 0;
below_4g_mem_size = args->ram_size;
@ -228,6 +243,7 @@ static void pc_q35_init(QEMUMachineInitArgs *args)
static void pc_compat_1_7(QEMUMachineInitArgs *args)
{
smbios_type1_defaults = false;
gigabyte_align = false;
}
static void pc_compat_1_6(QEMUMachineInitArgs *args)

View file

@ -48,6 +48,22 @@ DefinitionBlock (
/****************************************************************
* PCI Bus definition
****************************************************************/
#define BOARD_SPECIFIC_PCI_RESOURSES \
WordIO(ResourceProducer, MinFixed, MaxFixed, PosDecode, EntireRange, \
0x0000, \
0x0000, \
0x0CD7, \
0x0000, \
0x0CD8, \
,, , TypeStatic) \
/* 0xcd8-0xcf7 hole for CPU hotplug, hw/acpi/ich9.c:ICH9_PROC_BASE */ \
WordIO(ResourceProducer, MinFixed, MaxFixed, PosDecode, EntireRange, \
0x0000, \
0x0D00, \
0xFFFF, \
0x0000, \
0xF300, \
,, , TypeStatic)
Scope(\_SB) {
Device(PCI0) {
@ -171,6 +187,7 @@ DefinitionBlock (
}
}
#define DSDT_APPLESMC_STA q35_dsdt_applesmc_sta
#include "acpi-dsdt-isa.dsl"
@ -404,6 +421,8 @@ DefinitionBlock (
define_gsi_link(GSIH, 0, 0x17)
}
#include "hw/acpi/cpu_hotplug_defs.h"
#define CPU_STATUS_BASE ICH9_CPU_HOTPLUG_IO_BASE
#include "acpi-dsdt-cpu-hotplug.dsl"

View file

@ -3,12 +3,12 @@ static unsigned char Q35AcpiDsdtAmlCode[] = {
0x53,
0x44,
0x54,
0xb0,
0xdf,
0x1c,
0x0,
0x0,
0x1,
0xfe,
0xff,
0x42,
0x58,
0x50,
@ -1033,8 +1033,8 @@ static unsigned char Q35AcpiDsdtAmlCode[] = {
0x4e,
0x1,
0x10,
0x4c,
0x1b,
0x4b,
0x1e,
0x2f,
0x3,
0x5f,
@ -1052,6 +1052,53 @@ static unsigned char Q35AcpiDsdtAmlCode[] = {
0x5b,
0x82,
0x2d,
0x53,
0x4d,
0x43,
0x5f,
0x8,
0x5f,
0x48,
0x49,
0x44,
0xc,
0x6,
0x10,
0x0,
0x1,
0x8,
0x5f,
0x53,
0x54,
0x41,
0xb,
0x0,
0xff,
0x8,
0x5f,
0x43,
0x52,
0x53,
0x11,
0x10,
0xa,
0xd,
0x47,
0x1,
0x0,
0x3,
0x0,
0x3,
0x1,
0x20,
0x22,
0x40,
0x0,
0x79,
0x0,
0x5b,
0x82,
0x2d,
0x52,
0x54,
0x43,
@ -7229,12 +7276,19 @@ static unsigned char Q35AcpiDsdtAmlCode[] = {
0x30,
0x0,
0x14,
0x10,
0x6,
0x5f,
0x4c,
0x30,
0x31,
0x0,
0x14,
0x10,
0x5f,
0x45,
0x30,
0x32,
0x0,
0x5c,
0x2e,
0x5f,
@ -7250,13 +7304,6 @@ static unsigned char Q35AcpiDsdtAmlCode[] = {
0x5f,
0x4c,
0x30,
0x32,
0x0,
0x14,
0x6,
0x5f,
0x4c,
0x30,
0x33,
0x0,
0x14,
@ -7344,3 +7391,6 @@ static unsigned char Q35AcpiDsdtAmlCode[] = {
0x46,
0x0
};
static unsigned short q35_dsdt_applesmc_sta[] = {
0x431
};

View file

@ -25,6 +25,7 @@ DefinitionBlock ("ssdt-pcihp.aml", "SSDT", 0x01, "BXPC", "BXSSDTPCIHP", 0x1)
/* Objects supplied by DSDT */
External(\_SB.PCI0, DeviceObj)
External(\_SB.PCI0.PCEJ, MethodObj)
External(BSEL, IntObj)
Scope(\_SB.PCI0) {
@ -33,19 +34,17 @@ DefinitionBlock ("ssdt-pcihp.aml", "SSDT", 0x01, "BXPC", "BXSSDTPCIHP", 0x1)
ACPI_EXTRACT_DEVICE_END ssdt_pcihp_end
ACPI_EXTRACT_DEVICE_STRING ssdt_pcihp_name
// Method _EJ0 can be patched by BIOS to EJ0_
// at runtime, if the slot is detected to not support hotplug.
// Extract the offset of the address dword and the
// _EJ0 name to allow this patching.
// Extract the offsets of the device name, address dword and the slot
// name byte - we fill them in for each device.
Device(SAA) {
ACPI_EXTRACT_NAME_BYTE_CONST ssdt_pcihp_id
Name(_SUN, 0xAA)
ACPI_EXTRACT_NAME_DWORD_CONST ssdt_pcihp_adr
Name(_ADR, 0xAA0000)
ACPI_EXTRACT_METHOD_STRING ssdt_pcihp_ej0
Method(_EJ0, 1) {
Return (PCEJ(_SUN))
PCEJ(BSEL, _SUN)
}
}
}
}

View file

@ -5,19 +5,19 @@ static unsigned char ssdt_pcihp_adr[] = {
0x44
};
static unsigned char ssdt_pcihp_end[] = {
0x58
0x5b
};
static unsigned char ssdp_pcihp_aml[] = {
0x53,
0x53,
0x44,
0x54,
0x58,
0x5b,
0x0,
0x0,
0x0,
0x1,
0x76,
0xe8,
0x42,
0x58,
0x50,
@ -45,7 +45,7 @@ static unsigned char ssdp_pcihp_aml[] = {
0x13,
0x20,
0x10,
0x33,
0x36,
0x5c,
0x2e,
0x5f,
@ -58,7 +58,7 @@ static unsigned char ssdp_pcihp_aml[] = {
0x30,
0x5b,
0x82,
0x26,
0x29,
0x53,
0x41,
0x41,
@ -81,17 +81,20 @@ static unsigned char ssdp_pcihp_aml[] = {
0xaa,
0x0,
0x14,
0xf,
0x12,
0x5f,
0x45,
0x4a,
0x30,
0x1,
0xa4,
0x50,
0x43,
0x45,
0x4a,
0x42,
0x53,
0x45,
0x4c,
0x5f,
0x53,
0x55,
@ -103,6 +106,3 @@ static unsigned char ssdt_pcihp_start[] = {
static unsigned char ssdt_pcihp_id[] = {
0x3d
};
static unsigned char ssdt_pcihp_ej0[] = {
0x4a
};

View file

@ -11,7 +11,7 @@ static unsigned char ssdp_proc_aml[] = {
0x0,
0x0,
0x1,
0xb8,
0x78,
0x42,
0x58,
0x50,
@ -47,8 +47,8 @@ static unsigned char ssdp_proc_aml[] = {
0x41,
0x41,
0xaa,
0x10,
0xb0,
0x0,
0x0,
0x0,
0x0,
0x0,

View file

@ -66,7 +66,6 @@ struct AppleSMCData {
QLIST_ENTRY(AppleSMCData) node;
};
#define TYPE_APPLE_SMC "isa-applesmc"
#define APPLE_SMC(obj) OBJECT_CHECK(AppleSMCState, (obj), TYPE_APPLE_SMC)
typedef struct AppleSMCState AppleSMCState;

View file

@ -321,7 +321,7 @@ void vhost_net_ack_features(struct vhost_net *net, unsigned features)
bool vhost_net_virtqueue_pending(VHostNetState *net, int idx)
{
return -ENOSYS;
return false;
}
void vhost_net_virtqueue_mask(VHostNetState *net, VirtIODevice *dev,

View file

@ -793,6 +793,15 @@ static void pci_config_free(PCIDevice *pci_dev)
g_free(pci_dev->used);
}
static void do_pci_unregister_device(PCIDevice *pci_dev)
{
pci_dev->bus->devices[pci_dev->devfn] = NULL;
pci_config_free(pci_dev);
address_space_destroy(&pci_dev->bus_master_as);
memory_region_destroy(&pci_dev->bus_master_enable_region);
}
/* -1 for devfn means auto assign */
static PCIDevice *do_pci_register_device(PCIDevice *pci_dev, PCIBus *bus,
const char *name, int devfn)
@ -858,7 +867,7 @@ static PCIDevice *do_pci_register_device(PCIDevice *pci_dev, PCIBus *bus,
pci_init_mask_bridge(pci_dev);
}
if (pci_init_multifunction(bus, pci_dev)) {
pci_config_free(pci_dev);
do_pci_unregister_device(pci_dev);
return NULL;
}
@ -873,15 +882,6 @@ static PCIDevice *do_pci_register_device(PCIDevice *pci_dev, PCIBus *bus,
return pci_dev;
}
static void do_pci_unregister_device(PCIDevice *pci_dev)
{
pci_dev->bus->devices[pci_dev->devfn] = NULL;
pci_config_free(pci_dev);
address_space_destroy(&pci_dev->bus_master_as);
memory_region_destroy(&pci_dev->bus_master_enable_region);
}
static void pci_unregister_io_regions(PCIDevice *pci_dev)
{
PCIIORegion *r;
@ -1704,6 +1704,34 @@ static PCIBus *pci_find_bus_nr(PCIBus *bus, int bus_num)
return NULL;
}
void pci_for_each_bus_depth_first(PCIBus *bus,
void *(*begin)(PCIBus *bus, void *parent_state),
void (*end)(PCIBus *bus, void *state),
void *parent_state)
{
PCIBus *sec;
void *state;
if (!bus) {
return;
}
if (begin) {
state = begin(bus, parent_state);
} else {
state = parent_state;
}
QLIST_FOREACH(sec, &bus->child, sibling) {
pci_for_each_bus_depth_first(sec, begin, end, state);
}
if (end) {
end(bus, state);
}
}
PCIDevice *pci_find_device(PCIBus *bus, int bus_num, uint8_t devfn)
{
bus = pci_find_bus_nr(bus, bus_num);

View file

@ -0,0 +1,27 @@
/*
* QEMU ACPI hotplug utilities
*
* Copyright (C) 2013 Red Hat Inc
*
* Authors:
* Igor Mammedov <imammedo@redhat.com>
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*/
#ifndef ACPI_HOTPLUG_H
#define ACPI_HOTPLUG_H
#include "hw/acpi/acpi.h"
#include "hw/acpi/cpu_hotplug_defs.h"
typedef struct AcpiCpuHotplug {
MemoryRegion io;
uint8_t sts[ACPI_GPE_PROC_LEN];
} AcpiCpuHotplug;
void AcpiCpuHotplug_add(ACPIGPE *gpe, AcpiCpuHotplug *g, CPUState *cpu);
void AcpiCpuHotplug_init(MemoryRegion *parent, Object *owner,
AcpiCpuHotplug *gpe_cpu, uint16_t base);
#endif

View file

@ -0,0 +1,24 @@
/*
* QEMU ACPI hotplug utilities shared defines
*
* Copyright (C) 2013 Red Hat Inc
*
* Authors:
* Igor Mammedov <imammedo@redhat.com>
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*/
#ifndef ACPI_HOTPLUG_DEFS_H
#define ACPI_HOTPLUG_DEFS_H
/*
* ONLY DEFINEs are permited in this file since it's shared
* between C and ASL code.
*/
#define ACPI_CPU_HOTPLUG_STATUS 4
#define ACPI_GPE_PROC_LEN 32
#define ICH9_CPU_HOTPLUG_IO_BASE 0x0CD8
#define PIIX4_CPU_HOTPLUG_IO_BASE 0xaf00
#endif

View file

@ -22,6 +22,7 @@
#define HW_ACPI_ICH9_H
#include "hw/acpi/acpi.h"
#include "hw/acpi/cpu_hotplug.h"
typedef struct ICH9LPCPMRegs {
/*
@ -42,6 +43,9 @@ typedef struct ICH9LPCPMRegs {
uint32_t pm_io_base;
Notifier powerdown_notifier;
AcpiCpuHotplug gpe_cpu;
Notifier cpu_added_notifier;
} ICH9LPCPMRegs;
void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm,

72
include/hw/acpi/pcihp.h Normal file
View file

@ -0,0 +1,72 @@
/*
* QEMU<->ACPI BIOS PCI hotplug interface
*
* QEMU supports PCI hotplug via ACPI. This module
* implements the interface between QEMU and the ACPI BIOS.
* Interface specification - see docs/specs/acpi_pci_hotplug.txt
*
* Copyright (c) 2013, Red Hat Inc, Michael S. Tsirkin (mst@redhat.com)
* Copyright (c) 2006 Fabrice Bellard
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2 as published by the Free Software Foundation.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>
*
* Contributions after 2012-01-13 are licensed under the terms of the
* GNU GPL, version 2 or (at your option) any later version.
*/
#ifndef HW_ACPI_PCIHP_H
#define HW_ACPI_PCIHP_H
#include <inttypes.h>
#include <qemu/typedefs.h>
#include "hw/pci/pci.h" /* for PCIHotplugState */
typedef struct AcpiPciHpPciStatus {
uint32_t up; /* deprecated, maintained for migration compatibility */
uint32_t down;
uint32_t hotplug_enable;
uint32_t device_present;
} AcpiPciHpPciStatus;
#define ACPI_PCIHP_PROP_BSEL "acpi-pcihp-bsel"
#define ACPI_PCIHP_MAX_HOTPLUG_BUS 256
typedef struct AcpiPciHpState {
AcpiPciHpPciStatus acpi_pcihp_pci_status[ACPI_PCIHP_MAX_HOTPLUG_BUS];
uint32_t hotplug_select;
PCIBus *root;
MemoryRegion io;
} AcpiPciHpState;
void acpi_pcihp_init(AcpiPciHpState *, PCIBus *root,
MemoryRegion *address_space_io);
/* Invoke on device hotplug */
int acpi_pcihp_device_hotplug(AcpiPciHpState *, PCIDevice *,
PCIHotplugState state);
/* Called on reset */
void acpi_pcihp_reset(AcpiPciHpState *s);
extern const VMStateDescription vmstate_acpi_pcihp_pci_status;
#define VMSTATE_PCI_HOTPLUG(pcihp, state, test_pcihp) \
VMSTATE_UINT32_TEST(pcihp.hotplug_select, state, \
test_pcihp), \
VMSTATE_STRUCT_ARRAY_TEST(pcihp.acpi_pcihp_pci_status, state, \
ACPI_PCIHP_MAX_HOTPLUG_BUS, \
test_pcihp, 1, \
vmstate_acpi_pcihp_pci_status, \
AcpiPciHpPciStatus)
#endif

View file

@ -35,7 +35,7 @@ typedef struct PcPciInfo {
struct PcGuestInfo {
bool has_pci_info;
bool isapc_ram_fw;
hwaddr ram_size;
hwaddr ram_size, ram_size_below_4g;
unsigned apic_id_limit;
bool apic_xrupt_override;
uint64_t numa_nodes;
@ -265,6 +265,11 @@ int e820_add_entry(uint64_t, uint64_t, uint32_t);
.driver = TYPE_USB_DEVICE,\
.property = "msos-desc",\
.value = "no",\
},\
{\
.driver = "PIIX4_PM",\
.property = "acpi-pci-hotplug-with-bridge-support",\
.value = "off",\
}
#define PC_COMPAT_1_6 \

View file

@ -20,6 +20,13 @@
#define TYPE_ISA_BUS "ISA"
#define ISA_BUS(obj) OBJECT_CHECK(ISABus, (obj), TYPE_ISA_BUS)
#define TYPE_APPLE_SMC "isa-applesmc"
static inline bool applesmc_find(void)
{
return object_resolve_path_type("", TYPE_APPLE_SMC, NULL);
}
typedef struct ISADeviceClass {
DeviceClass parent_class;
} ISADeviceClass;

View file

@ -387,6 +387,20 @@ int pci_bus_num(PCIBus *s);
void pci_for_each_device(PCIBus *bus, int bus_num,
void (*fn)(PCIBus *bus, PCIDevice *d, void *opaque),
void *opaque);
void pci_for_each_bus_depth_first(PCIBus *bus,
void *(*begin)(PCIBus *bus, void *parent_state),
void (*end)(PCIBus *bus, void *state),
void *parent_state);
/* Use this wrapper when specific scan order is not required. */
static inline
void pci_for_each_bus(PCIBus *bus,
void (*fn)(PCIBus *bus, void *opaque),
void *opaque)
{
pci_for_each_bus_depth_first(bus, NULL, fn, opaque);
}
PCIBus *pci_find_primary_bus(void);
PCIBus *pci_device_root_bus(const PCIDevice *d);
const char *pci_root_bus_path(PCIDevice *dev);

View file

@ -26,6 +26,10 @@ case $line in
# save for the next definitions
prefix=${line#*=}
;;
IASL=*) # iasl executable
value=${line#*=}
echo "#define CONFIG_IASL $value"
;;
CONFIG_AUDIO_DRIVERS=*)
drivers=${line#*=}
echo "#define CONFIG_AUDIO_DRIVERS \\"

View file

@ -0,0 +1,339 @@
# This python script adds a new gdb command, "dump-guest-memory". It
# should be loaded with "source dump-guest-memory.py" at the (gdb)
# prompt.
#
# Copyright (C) 2013, Red Hat, Inc.
#
# Authors:
# Laszlo Ersek <lersek@redhat.com>
#
# This work is licensed under the terms of the GNU GPL, version 2 or later. See
# the COPYING file in the top-level directory.
#
# The leading docstring doesn't have idiomatic Python formatting. It is
# printed by gdb's "help" command (the first line is printed in the
# "help data" summary), and it should match how other help texts look in
# gdb.
import struct
class DumpGuestMemory(gdb.Command):
"""Extract guest vmcore from qemu process coredump.
The sole argument is FILE, identifying the target file to write the
guest vmcore to.
This GDB command reimplements the dump-guest-memory QMP command in
python, using the representation of guest memory as captured in the qemu
coredump. The qemu process that has been dumped must have had the
command line option "-machine dump-guest-core=on".
For simplicity, the "paging", "begin" and "end" parameters of the QMP
command are not supported -- no attempt is made to get the guest's
internal paging structures (ie. paging=false is hard-wired), and guest
memory is always fully dumped.
Only x86_64 guests are supported.
The CORE/NT_PRSTATUS and QEMU notes (that is, the VCPUs' statuses) are
not written to the vmcore. Preparing these would require context that is
only present in the KVM host kernel module when the guest is alive. A
fake ELF note is written instead, only to keep the ELF parser of "crash"
happy.
Dependent on how busted the qemu process was at the time of the
coredump, this command might produce unpredictable results. If qemu
deliberately called abort(), or it was dumped in response to a signal at
a halfway fortunate point, then its coredump should be in reasonable
shape and this command should mostly work."""
TARGET_PAGE_SIZE = 0x1000
TARGET_PAGE_MASK = 0xFFFFFFFFFFFFF000
# Various ELF constants
EM_X86_64 = 62 # AMD x86-64 target machine
ELFDATA2LSB = 1 # little endian
ELFCLASS64 = 2
ELFMAG = "\x7FELF"
EV_CURRENT = 1
ET_CORE = 4
PT_LOAD = 1
PT_NOTE = 4
# Special value for e_phnum. This indicates that the real number of
# program headers is too large to fit into e_phnum. Instead the real
# value is in the field sh_info of section 0.
PN_XNUM = 0xFFFF
# Format strings for packing and header size calculation.
ELF64_EHDR = ("4s" # e_ident/magic
"B" # e_ident/class
"B" # e_ident/data
"B" # e_ident/version
"B" # e_ident/osabi
"8s" # e_ident/pad
"H" # e_type
"H" # e_machine
"I" # e_version
"Q" # e_entry
"Q" # e_phoff
"Q" # e_shoff
"I" # e_flags
"H" # e_ehsize
"H" # e_phentsize
"H" # e_phnum
"H" # e_shentsize
"H" # e_shnum
"H" # e_shstrndx
)
ELF64_PHDR = ("I" # p_type
"I" # p_flags
"Q" # p_offset
"Q" # p_vaddr
"Q" # p_paddr
"Q" # p_filesz
"Q" # p_memsz
"Q" # p_align
)
def __init__(self):
super(DumpGuestMemory, self).__init__("dump-guest-memory",
gdb.COMMAND_DATA,
gdb.COMPLETE_FILENAME)
self.uintptr_t = gdb.lookup_type("uintptr_t")
self.elf64_ehdr_le = struct.Struct("<%s" % self.ELF64_EHDR)
self.elf64_phdr_le = struct.Struct("<%s" % self.ELF64_PHDR)
def int128_get64(self, val):
assert (val["hi"] == 0)
return val["lo"]
def qtailq_foreach(self, head, field_str):
var_p = head["tqh_first"]
while (var_p != 0):
var = var_p.dereference()
yield var
var_p = var[field_str]["tqe_next"]
def qemu_get_ram_block(self, ram_addr):
ram_blocks = gdb.parse_and_eval("ram_list.blocks")
for block in self.qtailq_foreach(ram_blocks, "next"):
if (ram_addr - block["offset"] < block["length"]):
return block
raise gdb.GdbError("Bad ram offset %x" % ram_addr)
def qemu_get_ram_ptr(self, ram_addr):
block = self.qemu_get_ram_block(ram_addr)
return block["host"] + (ram_addr - block["offset"])
def memory_region_get_ram_ptr(self, mr):
if (mr["alias"] != 0):
return (self.memory_region_get_ram_ptr(mr["alias"].dereference()) +
mr["alias_offset"])
return self.qemu_get_ram_ptr(mr["ram_addr"] & self.TARGET_PAGE_MASK)
def guest_phys_blocks_init(self):
self.guest_phys_blocks = []
def guest_phys_blocks_append(self):
print "guest RAM blocks:"
print ("target_start target_end host_addr message "
"count")
print ("---------------- ---------------- ---------------- ------- "
"-----")
current_map_p = gdb.parse_and_eval("address_space_memory.current_map")
current_map = current_map_p.dereference()
for cur in range(current_map["nr"]):
flat_range = (current_map["ranges"] + cur).dereference()
mr = flat_range["mr"].dereference()
# we only care about RAM
if (not mr["ram"]):
continue
section_size = self.int128_get64(flat_range["addr"]["size"])
target_start = self.int128_get64(flat_range["addr"]["start"])
target_end = target_start + section_size
host_addr = (self.memory_region_get_ram_ptr(mr) +
flat_range["offset_in_region"])
predecessor = None
# find continuity in guest physical address space
if (len(self.guest_phys_blocks) > 0):
predecessor = self.guest_phys_blocks[-1]
predecessor_size = (predecessor["target_end"] -
predecessor["target_start"])
# the memory API guarantees monotonically increasing
# traversal
assert (predecessor["target_end"] <= target_start)
# we want continuity in both guest-physical and
# host-virtual memory
if (predecessor["target_end"] < target_start or
predecessor["host_addr"] + predecessor_size != host_addr):
predecessor = None
if (predecessor is None):
# isolated mapping, add it to the list
self.guest_phys_blocks.append({"target_start": target_start,
"target_end" : target_end,
"host_addr" : host_addr})
message = "added"
else:
# expand predecessor until @target_end; predecessor's
# start doesn't change
predecessor["target_end"] = target_end
message = "joined"
print ("%016x %016x %016x %-7s %5u" %
(target_start, target_end, host_addr.cast(self.uintptr_t),
message, len(self.guest_phys_blocks)))
def cpu_get_dump_info(self):
# We can't synchronize the registers with KVM post-mortem, and
# the bits in (first_x86_cpu->env.hflags) seem to be stale; they
# may not reflect long mode for example. Hence just assume the
# most common values. This also means that instruction pointer
# etc. will be bogus in the dump, but at least the RAM contents
# should be valid.
self.dump_info = {"d_machine": self.EM_X86_64,
"d_endian" : self.ELFDATA2LSB,
"d_class" : self.ELFCLASS64}
def encode_elf64_ehdr_le(self):
return self.elf64_ehdr_le.pack(
self.ELFMAG, # e_ident/magic
self.dump_info["d_class"], # e_ident/class
self.dump_info["d_endian"], # e_ident/data
self.EV_CURRENT, # e_ident/version
0, # e_ident/osabi
"", # e_ident/pad
self.ET_CORE, # e_type
self.dump_info["d_machine"], # e_machine
self.EV_CURRENT, # e_version
0, # e_entry
self.elf64_ehdr_le.size, # e_phoff
0, # e_shoff
0, # e_flags
self.elf64_ehdr_le.size, # e_ehsize
self.elf64_phdr_le.size, # e_phentsize
self.phdr_num, # e_phnum
0, # e_shentsize
0, # e_shnum
0 # e_shstrndx
)
def encode_elf64_note_le(self):
return self.elf64_phdr_le.pack(self.PT_NOTE, # p_type
0, # p_flags
(self.memory_offset -
len(self.note)), # p_offset
0, # p_vaddr
0, # p_paddr
len(self.note), # p_filesz
len(self.note), # p_memsz
0 # p_align
)
def encode_elf64_load_le(self, offset, start_hwaddr, range_size):
return self.elf64_phdr_le.pack(self.PT_LOAD, # p_type
0, # p_flags
offset, # p_offset
0, # p_vaddr
start_hwaddr, # p_paddr
range_size, # p_filesz
range_size, # p_memsz
0 # p_align
)
def note_init(self, name, desc, type):
# name must include a trailing NUL
namesz = (len(name) + 1 + 3) / 4 * 4
descsz = (len(desc) + 3) / 4 * 4
fmt = ("<" # little endian
"I" # n_namesz
"I" # n_descsz
"I" # n_type
"%us" # name
"%us" # desc
% (namesz, descsz))
self.note = struct.pack(fmt,
len(name) + 1, len(desc), type, name, desc)
def dump_init(self):
self.guest_phys_blocks_init()
self.guest_phys_blocks_append()
self.cpu_get_dump_info()
# we have no way to retrieve the VCPU status from KVM
# post-mortem
self.note_init("NONE", "EMPTY", 0)
# Account for PT_NOTE.
self.phdr_num = 1
# We should never reach PN_XNUM for paging=false dumps: there's
# just a handful of discontiguous ranges after merging.
self.phdr_num += len(self.guest_phys_blocks)
assert (self.phdr_num < self.PN_XNUM)
# Calculate the ELF file offset where the memory dump commences:
#
# ELF header
# PT_NOTE
# PT_LOAD: 1
# PT_LOAD: 2
# ...
# PT_LOAD: len(self.guest_phys_blocks)
# ELF note
# memory dump
self.memory_offset = (self.elf64_ehdr_le.size +
self.elf64_phdr_le.size * self.phdr_num +
len(self.note))
def dump_begin(self, vmcore):
vmcore.write(self.encode_elf64_ehdr_le())
vmcore.write(self.encode_elf64_note_le())
running = self.memory_offset
for block in self.guest_phys_blocks:
range_size = block["target_end"] - block["target_start"]
vmcore.write(self.encode_elf64_load_le(running,
block["target_start"],
range_size))
running += range_size
vmcore.write(self.note)
def dump_iterate(self, vmcore):
qemu_core = gdb.inferiors()[0]
for block in self.guest_phys_blocks:
cur = block["host_addr"]
left = block["target_end"] - block["target_start"]
print ("dumping range at %016x for length %016x" %
(cur.cast(self.uintptr_t), left))
while (left > 0):
chunk_size = min(self.TARGET_PAGE_SIZE, left)
chunk = qemu_core.read_memory(cur, chunk_size)
vmcore.write(chunk)
cur += chunk_size
left -= chunk_size
def create_vmcore(self, filename):
vmcore = open(filename, "wb")
self.dump_begin(vmcore)
self.dump_iterate(vmcore)
vmcore.close()
def invoke(self, args, from_tty):
# Unwittingly pressing the Enter key after the command should
# not dump the same multi-gig coredump to the same file.
self.dont_repeat()
argv = gdb.string_to_argv(args)
if (len(argv) != 1):
raise gdb.GdbError("usage: dump-guest-memory FILE")
self.dump_init()
self.create_vmcore(argv[0])
DumpGuestMemory()

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,36 @@
#! /bin/bash
#
# Rebuild expected AML files for acpi unit-test
#
# Copyright (c) 2013 Red Hat Inc.
#
# Authors:
# Marcel Apfelbaum <marcel.a@redhat.com>
#
# This work is licensed under the terms of the GNU GPLv2.
# See the COPYING.LIB file in the top-level directory.
qemu=
if [ -e x86_64-softmmu/qemu-system-x86_64 ]; then
qemu="x86_64-softmmu/qemu-system-x86_64"
elif [ -e i386-softmmu/qemu-system-i386 ]; then
qemu="i386-softmmu/qemu-system-i386"
else
echo "Run 'make' to build the qemu exectutable!"
echo "Run this script from the build directory."
exit 1;
fi
if [ ! -e "tests/acpi-test" ]; then
echo "Test: acpi-test is required! Run make check before this script."
echo "Run this script from the build directory."
exit 1;
fi
TEST_ACPI_REBUILD_AML=y QTEST_QEMU_BINARY=$qemu tests/acpi-test
echo "The files were rebuilt and can be added to git."
echo "However, if new files were created, please copy them manually" \
"to tests/acpi-test-data/pc/ or tests/acpi-test-data/q35/ ."

View file

@ -13,19 +13,32 @@
#include <string.h>
#include <stdio.h>
#include <glib.h>
#include <glib/gstdio.h>
#include "qemu-common.h"
#include "libqtest.h"
#include "qemu/compiler.h"
#include "hw/i386/acpi-defs.h"
#define MACHINE_PC "pc"
#define MACHINE_Q35 "q35"
#define ACPI_REBUILD_EXPECTED_AML "TEST_ACPI_REBUILD_AML"
#define ACPI_SSDT_SIGNATURE 0x54445353 /* SSDT */
/* DSDT and SSDTs format */
typedef struct {
AcpiTableHeader header;
uint8_t *aml;
int aml_len;
} AcpiSdtTable;
gchar *aml; /* aml bytecode from guest */
gsize aml_len;
gchar *aml_file;
gchar *asl; /* asl code generated from aml */
gsize asl_len;
gchar *asl_file;
bool asl_file_retain; /* do not delete the temp asl */
} QEMU_PACKED AcpiSdtTable;
typedef struct {
const char *machine;
uint32_t rsdp_addr;
AcpiRsdpDescriptor rsdp_table;
AcpiRsdtDescriptorRev1 rsdt_table;
@ -33,8 +46,7 @@ typedef struct {
AcpiFacsDescriptorRev1 facs_table;
uint32_t *rsdt_tables_addr;
int rsdt_tables_nr;
AcpiSdtTable dsdt_table;
GArray *ssdt_tables;
GArray *tables;
} test_data;
#define LOW(x) ((x) & 0xff)
@ -51,13 +63,13 @@ typedef struct {
field = readb(addr); \
break; \
case 2: \
field = le16_to_cpu(readw(addr)); \
field = readw(addr); \
break; \
case 4: \
field = le32_to_cpu(readl(addr)); \
field = readl(addr); \
break; \
case 8: \
field = le64_to_cpu(readq(addr)); \
field = readq(addr); \
break; \
default: \
g_assert(false); \
@ -91,8 +103,10 @@ typedef struct {
/* Boot sector code: write SIGNATURE into memory,
* then halt.
* Q35 machine requires a minimum 0x7e000 bytes disk.
* (bug or feature?)
*/
static uint8_t boot_sector[0x200] = {
static uint8_t boot_sector[0x7e000] = {
/* 7c00: mov $0xdead,%ax */
[0x00] = 0xb8,
[0x01] = LOW(SIGNATURE),
@ -117,17 +131,45 @@ static uint8_t boot_sector[0x200] = {
};
static const char *disk = "tests/acpi-test-disk.raw";
static const char *data_dir = "tests/acpi-test-data";
#ifdef CONFIG_IASL
static const char *iasl = stringify(CONFIG_IASL);
#else
static const char *iasl;
#endif
static void free_test_data(test_data *data)
{
AcpiSdtTable *temp;
int i;
g_free(data->rsdt_tables_addr);
for (i = 0; i < data->ssdt_tables->len; ++i) {
g_free(g_array_index(data->ssdt_tables, AcpiSdtTable, i).aml);
if (data->rsdt_tables_addr) {
g_free(data->rsdt_tables_addr);
}
g_array_free(data->ssdt_tables, false);
g_free(data->dsdt_table.aml);
for (i = 0; i < data->tables->len; ++i) {
temp = &g_array_index(data->tables, AcpiSdtTable, i);
if (temp->aml) {
g_free(temp->aml);
}
if (temp->aml_file) {
if (g_strstr_len(temp->aml_file, -1, "aml-")) {
unlink(temp->aml_file);
}
g_free(temp->aml_file);
}
if (temp->asl) {
g_free(temp->asl);
}
if (temp->asl_file) {
if (!temp->asl_file_retain) {
unlink(temp->asl_file);
}
g_free(temp->asl_file);
}
}
g_array_free(data->tables, false);
}
static uint8_t acpi_checksum(const uint8_t *data, int len)
@ -292,34 +334,219 @@ static void test_dst_table(AcpiSdtTable *sdt_table, uint32_t addr)
ACPI_READ_ARRAY_PTR(sdt_table->aml, sdt_table->aml_len, addr);
checksum = acpi_checksum((uint8_t *)sdt_table, sizeof(AcpiTableHeader)) +
acpi_checksum(sdt_table->aml, sdt_table->aml_len);
acpi_checksum((uint8_t *)sdt_table->aml, sdt_table->aml_len);
g_assert(!checksum);
}
static void test_acpi_dsdt_table(test_data *data)
{
AcpiSdtTable *dsdt_table = &data->dsdt_table;
AcpiSdtTable dsdt_table;
uint32_t addr = data->fadt_table.dsdt;
test_dst_table(dsdt_table, addr);
g_assert_cmphex(dsdt_table->header.signature, ==, ACPI_DSDT_SIGNATURE);
memset(&dsdt_table, 0, sizeof(dsdt_table));
data->tables = g_array_new(false, true, sizeof(AcpiSdtTable));
test_dst_table(&dsdt_table, addr);
g_assert_cmphex(dsdt_table.header.signature, ==, ACPI_DSDT_SIGNATURE);
/* Place DSDT first */
g_array_append_val(data->tables, dsdt_table);
}
static void test_acpi_ssdt_tables(test_data *data)
static void test_acpi_tables(test_data *data)
{
GArray *ssdt_tables;
int ssdt_tables_nr = data->rsdt_tables_nr - 1; /* fadt is first */
int tables_nr = data->rsdt_tables_nr - 1; /* fadt is first */
int i;
ssdt_tables = g_array_sized_new(false, true, sizeof(AcpiSdtTable),
ssdt_tables_nr);
for (i = 0; i < ssdt_tables_nr; i++) {
for (i = 0; i < tables_nr; i++) {
AcpiSdtTable ssdt_table;
memset(&ssdt_table, 0 , sizeof(ssdt_table));
uint32_t addr = data->rsdt_tables_addr[i + 1]; /* fadt is first */
test_dst_table(&ssdt_table, addr);
g_array_append_val(ssdt_tables, ssdt_table);
g_array_append_val(data->tables, ssdt_table);
}
data->ssdt_tables = ssdt_tables;
}
static void dump_aml_files(test_data *data, bool rebuild)
{
AcpiSdtTable *sdt;
GError *error = NULL;
gchar *aml_file = NULL;
gint fd;
ssize_t ret;
int i;
for (i = 0; i < data->tables->len; ++i) {
sdt = &g_array_index(data->tables, AcpiSdtTable, i);
g_assert(sdt->aml);
if (rebuild) {
aml_file = g_strdup_printf("%s/%s/%.4s", data_dir, data->machine,
(gchar *)&sdt->header.signature);
fd = g_open(aml_file, O_WRONLY|O_TRUNC|O_CREAT,
S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH);
} else {
fd = g_file_open_tmp("aml-XXXXXX", &sdt->aml_file, &error);
g_assert_no_error(error);
}
g_assert(fd >= 0);
ret = qemu_write_full(fd, sdt, sizeof(AcpiTableHeader));
g_assert(ret == sizeof(AcpiTableHeader));
ret = qemu_write_full(fd, sdt->aml, sdt->aml_len);
g_assert(ret == sdt->aml_len);
close(fd);
if (aml_file) {
g_free(aml_file);
}
}
}
static bool compare_signature(AcpiSdtTable *sdt, uint32_t signature)
{
return sdt->header.signature == signature;
}
static void load_asl(GArray *sdts, AcpiSdtTable *sdt)
{
AcpiSdtTable *temp;
GError *error = NULL;
GString *command_line = g_string_new(iasl);
gint fd;
gchar *out, *out_err;
gboolean ret;
int i;
fd = g_file_open_tmp("asl-XXXXXX.dsl", &sdt->asl_file, &error);
g_assert_no_error(error);
close(fd);
/* build command line */
g_string_append_printf(command_line, " -p %s ", sdt->asl_file);
if (compare_signature(sdt, ACPI_DSDT_SIGNATURE) ||
compare_signature(sdt, ACPI_SSDT_SIGNATURE)) {
for (i = 0; i < sdts->len; ++i) {
temp = &g_array_index(sdts, AcpiSdtTable, i);
if (compare_signature(temp, ACPI_DSDT_SIGNATURE) ||
compare_signature(temp, ACPI_SSDT_SIGNATURE)) {
g_string_append_printf(command_line, "-e %s ", temp->aml_file);
}
}
}
g_string_append_printf(command_line, "-d %s", sdt->aml_file);
/* pass 'out' and 'out_err' in order to be redirected */
g_spawn_command_line_sync(command_line->str, &out, &out_err, NULL, &error);
g_assert_no_error(error);
ret = g_file_get_contents(sdt->asl_file, (gchar **)&sdt->asl,
&sdt->asl_len, &error);
g_assert(ret);
g_assert_no_error(error);
g_assert(sdt->asl_len);
g_free(out);
g_free(out_err);
g_string_free(command_line, true);
}
#define COMMENT_END "*/"
#define DEF_BLOCK "DefinitionBlock ("
#define BLOCK_NAME_END ".aml"
static GString *normalize_asl(gchar *asl_code)
{
GString *asl = g_string_new(asl_code);
gchar *comment, *block_name;
/* strip comments (different generation days) */
comment = g_strstr_len(asl->str, asl->len, COMMENT_END);
if (comment) {
asl = g_string_erase(asl, 0, comment + sizeof(COMMENT_END) - asl->str);
}
/* strip def block name (it has file path in it) */
if (g_str_has_prefix(asl->str, DEF_BLOCK)) {
block_name = g_strstr_len(asl->str, asl->len, BLOCK_NAME_END);
g_assert(block_name);
asl = g_string_erase(asl, 0,
block_name + sizeof(BLOCK_NAME_END) - asl->str);
}
return asl;
}
static GArray *load_expected_aml(test_data *data)
{
int i;
AcpiSdtTable *sdt;
gchar *aml_file;
GError *error = NULL;
gboolean ret;
GArray *exp_tables = g_array_new(false, true, sizeof(AcpiSdtTable));
for (i = 0; i < data->tables->len; ++i) {
AcpiSdtTable exp_sdt;
sdt = &g_array_index(data->tables, AcpiSdtTable, i);
memset(&exp_sdt, 0, sizeof(exp_sdt));
exp_sdt.header.signature = sdt->header.signature;
aml_file = g_strdup_printf("%s/%s/%.4s", data_dir, data->machine,
(gchar *)&exp_sdt.header.signature);
exp_sdt.aml_file = aml_file;
g_assert(g_file_test(aml_file, G_FILE_TEST_EXISTS));
ret = g_file_get_contents(aml_file, &exp_sdt.aml,
&exp_sdt.aml_len, &error);
g_assert(ret);
g_assert_no_error(error);
g_assert(exp_sdt.aml);
g_assert(exp_sdt.aml_len);
g_array_append_val(exp_tables, exp_sdt);
}
return exp_tables;
}
static void test_acpi_asl(test_data *data)
{
int i;
AcpiSdtTable *sdt, *exp_sdt;
test_data exp_data;
memset(&exp_data, 0, sizeof(exp_data));
exp_data.tables = load_expected_aml(data);
dump_aml_files(data, false);
for (i = 0; i < data->tables->len; ++i) {
GString *asl, *exp_asl;
sdt = &g_array_index(data->tables, AcpiSdtTable, i);
exp_sdt = &g_array_index(exp_data.tables, AcpiSdtTable, i);
load_asl(data->tables, sdt);
asl = normalize_asl(sdt->asl);
load_asl(exp_data.tables, exp_sdt);
exp_asl = normalize_asl(exp_sdt->asl);
if (g_strcmp0(asl->str, exp_asl->str)) {
sdt->asl_file_retain = true;
exp_sdt->asl_file_retain = true;
fprintf(stderr,
"acpi-test: Warning! %.4s mismatch. "
"Orig asl: %s, expected asl %s.\n",
(gchar *)&exp_sdt->header.signature,
sdt->asl_file, exp_sdt->asl_file);
}
g_string_free(asl, true);
g_string_free(exp_asl, true);
}
free_test_data(&exp_data);
}
static void test_acpi_one(const char *params, test_data *data)
@ -329,10 +556,14 @@ static void test_acpi_one(const char *params, test_data *data)
uint8_t signature_high;
uint16_t signature;
int i;
const char *device = "";
memset(data, 0, sizeof(*data));
args = g_strdup_printf("-net none -display none %s %s",
params ? params : "", disk);
if (!g_strcmp0(data->machine, MACHINE_Q35)) {
device = ",id=hd -device ide-hd,drive=hd";
}
args = g_strdup_printf("-net none -display none %s -drive file=%s%s,",
params ? params : "", disk, device);
qtest_start(args);
/* Wait at most 1 minute */
@ -360,7 +591,15 @@ static void test_acpi_one(const char *params, test_data *data)
test_acpi_fadt_table(data);
test_acpi_facs_table(data);
test_acpi_dsdt_table(data);
test_acpi_ssdt_tables(data);
test_acpi_tables(data);
if (iasl) {
if (getenv(ACPI_REBUILD_EXPECTED_AML)) {
dump_aml_files(data, true);
} else {
test_acpi_asl(data);
}
}
qtest_quit(global_qtest);
g_free(args);
@ -373,8 +612,14 @@ static void test_acpi_tcg(void)
/* Supplying -machine accel argument overrides the default (qtest).
* This is to make guest actually run.
*/
memset(&data, 0, sizeof(data));
data.machine = MACHINE_PC;
test_acpi_one("-machine accel=tcg", &data);
free_test_data(&data);
memset(&data, 0, sizeof(data));
data.machine = MACHINE_Q35;
test_acpi_one("-machine q35,accel=tcg", &data);
free_test_data(&data);
}