pci, pc, virtio: features, fixes

reconnect for vhost blk
 tests for UEFI
 misc other stuff
 
 Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
 -----BEGIN PGP SIGNATURE-----
 
 iQEcBAABAgAGBQJc5AAMAAoJECgfDbjSjVRp57wIAKUAF8mvTaFfl/ge9IKJ0ZSO
 +dZS+2Zdc52n3Qk2K511hMvMmWD6xO98+VSXzwax2KgfIVCcQyYSCSbkyqZ4XiFg
 JUobhLHs6W24zn+2T8vZoqe4XWU6Pm1Y6NM72EFuhag8mhFlwWyMjpTwsISbjhGM
 /FpHsPpB2/c7Uofe8CVmPl55PDqOoIm35YlbH5v6zUtBiPNRgZTDOMtYjl/tyuZC
 VExs41/G15psNDIFd4dL1Zq6UjQBdu3ALIjIXTmPfg0nFXZn7FrOBRHbXWyzR9Tb
 rqEDauxb3iGHphgNqeumfrSPXNrfB91Z8AD2aerTcvgUEdbc9QykxS/YM3erkGo=
 =kkcS
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/mst/tags/for_upstream' into staging

pci, pc, virtio: features, fixes

reconnect for vhost blk
tests for UEFI
misc other stuff

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

# gpg: Signature made Tue 21 May 2019 14:41:32 BST
# gpg:                using RSA key 281F0DB8D28D5469
# gpg: Good signature from "Michael S. Tsirkin <mst@kernel.org>" [full]
# gpg:                 aka "Michael S. Tsirkin <mst@redhat.com>" [full]
# 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

* remotes/mst/tags/for_upstream: (34 commits)
  tests: acpi: print error unable to dump ACPI table during rebuild
  tests: acpi: refactor rebuild-expected-aml.sh to dump ACPI tables for a specified list of targets
  tests: acpi: allow to override default accelerator
  tests: acpi: ignore SMBIOS tests when UEFI firmware is used
  tests: acpi: add a way to start tests with UEFI firmware
  tests: acpi: add acpi_find_rsdp_address_uefi() helper
  tests: acpi: move boot_sector_init() into x86 tests branch
  tests: acpi: skip FACS table if board uses hw reduced ACPI profile
  tests: acpi: fetch X_DSDT if pointer to DSDT is 0
  tests: acpi: make pointer to RSDP 64bit
  tests: acpi: make RSDT test routine handle XSDT
  tests: acpi: make acpi_fetch_table() take size of fetched table pointer
  tests: acpi: rename acpi_parse_rsdp_table() into acpi_fetch_rsdp_table()
  pci: Simplify pci_bus_is_root()
  pcie: Remove redundant test in pcie_mmcfg_data_{read,write}()
  libvhost-user: fix bad vu_log_write
  hw/arm/virt-acpi-build: pass AcpiMcfgInfo to build_mcfg()
  i386, acpi: remove mcfg_ prefix in AcpiMcfgInfo members
  hw/arm/virt-acpi-build: remove unnecessary variable mcfg_start
  do not call vhost_net_cleanup() on running net from char user event
  ...

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2019-05-21 14:56:57 +01:00
commit 247ba27c52
36 changed files with 1889 additions and 1452 deletions

View File

@ -1484,7 +1484,7 @@ M: Michael S. Tsirkin <mst@redhat.com>
S: Supported
F: hw/*/*vhost*
F: docs/interop/vhost-user.json
F: docs/interop/vhost-user.txt
F: docs/interop/vhost-user.rst
F: contrib/vhost-user-*/
F: backends/vhost-user.c
F: include/sysemu/vhost-user-backend.h

View File

@ -433,7 +433,7 @@ vu_log_write(VuDev *dev, uint64_t address, uint64_t length)
page = address / VHOST_LOG_PAGE;
while (page * VHOST_LOG_PAGE < address + length) {
vu_log_page(dev->log_table, page);
page += VHOST_LOG_PAGE;
page += 1;
}
vu_log_kick(dev);

View File

@ -398,7 +398,8 @@ vub_get_features(VuDev *dev)
static uint64_t
vub_get_protocol_features(VuDev *dev)
{
return 1ull << VHOST_USER_PROTOCOL_F_CONFIG;
return 1ull << VHOST_USER_PROTOCOL_F_CONFIG |
1ull << VHOST_USER_PROTOCOL_F_INFLIGHT_SHMFD;
}
static int

View File

@ -15,4 +15,4 @@ Contents:
bitmaps
live-block-operations
pr-helper
vhost-user

1351
docs/interop/vhost-user.rst Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -37,14 +37,7 @@
#include "hw/pci/pci_bus.h"
#include "qapi/error.h"
#include "qom/qom-qobject.h"
//#define DEBUG
#ifdef DEBUG
# define ACPI_PCIHP_DPRINTF(format, ...) printf(format, ## __VA_ARGS__)
#else
# define ACPI_PCIHP_DPRINTF(format, ...) do { } while (0)
#endif
#include "trace.h"
#define ACPI_PCIHP_ADDR 0xae00
#define ACPI_PCIHP_SIZE 0x0014
@ -159,6 +152,8 @@ static void acpi_pcihp_eject_slot(AcpiPciHpState *s, unsigned bsel, unsigned slo
int slot = ctz32(slots);
PCIBus *bus = acpi_pcihp_find_hotplug_bus(s, bsel);
trace_acpi_pci_eject_slot(bsel, slot);
if (!bus) {
return;
}
@ -270,6 +265,8 @@ void acpi_pcihp_device_plug_cb(HotplugHandler *hotplug_dev, AcpiPciHpState *s,
void acpi_pcihp_device_unplug_cb(HotplugHandler *hotplug_dev, AcpiPciHpState *s,
DeviceState *dev, Error **errp)
{
trace_acpi_pci_unplug(PCI_SLOT(PCI_DEVICE(dev)->devfn),
acpi_pcihp_get_bsel(pci_get_bus(PCI_DEVICE(dev))));
object_property_set_bool(OBJECT(dev), false, "realized", NULL);
}
@ -280,6 +277,9 @@ void acpi_pcihp_device_unplug_request_cb(HotplugHandler *hotplug_dev,
PCIDevice *pdev = PCI_DEVICE(dev);
int slot = PCI_SLOT(pdev->devfn);
int bsel = acpi_pcihp_get_bsel(pci_get_bus(pdev));
trace_acpi_pci_unplug_request(bsel, slot);
if (bsel < 0) {
error_setg(errp, "Unsupported bus. Bus doesn't have property '"
ACPI_PCIHP_PROP_BSEL "' set");
@ -306,23 +306,23 @@ static uint64_t pci_read(void *opaque, hwaddr addr, unsigned int size)
if (!s->legacy_piix) {
s->acpi_pcihp_pci_status[bsel].up = 0;
}
ACPI_PCIHP_DPRINTF("pci_up_read %" PRIu32 "\n", val);
trace_acpi_pci_up_read(val);
break;
case PCI_DOWN_BASE:
val = s->acpi_pcihp_pci_status[bsel].down;
ACPI_PCIHP_DPRINTF("pci_down_read %" PRIu32 "\n", val);
trace_acpi_pci_down_read(val);
break;
case PCI_EJ_BASE:
/* No feature defined yet */
ACPI_PCIHP_DPRINTF("pci_features_read %" PRIu32 "\n", val);
trace_acpi_pci_features_read(val);
break;
case PCI_RMV_BASE:
val = s->acpi_pcihp_pci_status[bsel].hotplug_enable;
ACPI_PCIHP_DPRINTF("pci_rmv_read %" PRIu32 "\n", val);
trace_acpi_pci_rmv_read(val);
break;
case PCI_SEL_BASE:
val = s->hotplug_select;
ACPI_PCIHP_DPRINTF("pci_sel_read %" PRIu32 "\n", val);
trace_acpi_pci_sel_read(val);
default:
break;
}
@ -340,13 +340,11 @@ static void pci_write(void *opaque, hwaddr addr, uint64_t data,
break;
}
acpi_pcihp_eject_slot(s, s->hotplug_select, data);
ACPI_PCIHP_DPRINTF("pciej write %" HWADDR_PRIx " <== %" PRIu64 "\n",
addr, data);
trace_acpi_pci_ej_write(addr, data);
break;
case PCI_SEL_BASE:
s->hotplug_select = s->legacy_piix ? ACPI_PCIHP_BSEL_DEFAULT : data;
ACPI_PCIHP_DPRINTF("pcisel write %" HWADDR_PRIx " <== %" PRIu64 "\n",
addr, data);
trace_acpi_pci_sel_write(addr, data);
default:
break;
}

View File

@ -39,14 +39,7 @@
#include "hw/acpi/acpi_dev_interface.h"
#include "hw/xen/xen.h"
#include "qom/cpu.h"
//#define DEBUG
#ifdef DEBUG
# define PIIX4_DPRINTF(format, ...) printf(format, ## __VA_ARGS__)
#else
# define PIIX4_DPRINTF(format, ...) do { } while (0)
#endif
#include "trace.h"
#define GPE_BASE 0xafe0
#define GPE_LEN 4
@ -583,7 +576,7 @@ static uint64_t gpe_readb(void *opaque, hwaddr addr, unsigned width)
PIIX4PMState *s = opaque;
uint32_t val = acpi_gpe_ioport_readb(&s->ar, addr);
PIIX4_DPRINTF("gpe read %" HWADDR_PRIx " == %" PRIu32 "\n", addr, val);
trace_piix4_gpe_readb(addr, width, val);
return val;
}
@ -592,10 +585,9 @@ static void gpe_writeb(void *opaque, hwaddr addr, uint64_t val,
{
PIIX4PMState *s = opaque;
trace_piix4_gpe_writeb(addr, width, val);
acpi_gpe_ioport_writeb(&s->ar, addr, val);
acpi_update_sci(&s->ar, s->irq);
PIIX4_DPRINTF("gpe write %" HWADDR_PRIx " <== %" PRIu64 "\n", addr, val);
}
static const MemoryRegionOps piix4_gpe_ops = {

View File

@ -31,6 +31,22 @@ cpuhp_acpi_ejecting_cpu(uint32_t idx) "0x%"PRIx32
cpuhp_acpi_write_ost_ev(uint32_t slot, uint32_t ev) "idx[0x%"PRIx32"] OST EVENT: 0x%"PRIx32
cpuhp_acpi_write_ost_status(uint32_t slot, uint32_t st) "idx[0x%"PRIx32"] OST STATUS: 0x%"PRIx32
# pcihp.c
acpi_pci_eject_slot(unsigned bsel, unsigned slot) "bsel: %u slot: %u"
acpi_pci_unplug(int bsel, int slot) "bsel: %d slot: %d"
acpi_pci_unplug_request(int bsel, int slot) "bsel: %d slot: %d"
acpi_pci_up_read(uint32_t val) "%" PRIu32
acpi_pci_down_read(uint32_t val) "%" PRIu32
acpi_pci_features_read(uint32_t val) "%" PRIu32
acpi_pci_rmv_read(uint32_t val) "%" PRIu32
acpi_pci_sel_read(uint32_t val) "%" PRIu32
acpi_pci_ej_write(uint64_t addr, uint64_t data) "0x%" PRIx64 " <== %" PRIu64
acpi_pci_sel_write(uint64_t addr, uint64_t data) "0x%" PRIx64 " <== %" PRIu64
# piix4.c
piix4_gpe_readb(uint64_t addr, unsigned width, uint64_t val) "addr: 0x%" PRIx64 " width: %d ==> 0x%" PRIx64
piix4_gpe_writeb(uint64_t addr, unsigned width, uint64_t val) "addr: 0x%" PRIx64 " width: %d <== 0x%" PRIx64
# tco.c
tco_timer_reload(int ticks, int msec) "ticks=%d (%d ms)"
tco_timer_expired(int timeouts_no, bool strap, bool no_reboot) "timeouts_no=%d no_reboot=%d/%d"

View File

@ -40,6 +40,7 @@
#include "hw/loader.h"
#include "hw/hw.h"
#include "hw/acpi/aml-build.h"
#include "hw/acpi/pci.h"
#include "hw/pci/pcie_host.h"
#include "hw/pci/pci.h"
#include "hw/arm/virt.h"
@ -546,25 +547,20 @@ build_srat(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
}
static void
build_mcfg(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
build_mcfg(GArray *table_data, BIOSLinker *linker, AcpiMcfgInfo *info)
{
AcpiTableMcfg *mcfg;
const MemMapEntry *memmap = vms->memmap;
int ecam_id = VIRT_ECAM_ID(vms->highmem_ecam);
int len = sizeof(*mcfg) + sizeof(mcfg->allocation[0]);
int mcfg_start = table_data->len;
mcfg = acpi_data_push(table_data, len);
mcfg->allocation[0].address = cpu_to_le64(memmap[ecam_id].base);
mcfg->allocation[0].address = cpu_to_le64(info->base);
/* Only a single allocation so no need to play with segments */
mcfg->allocation[0].pci_segment = cpu_to_le16(0);
mcfg->allocation[0].start_bus_number = 0;
mcfg->allocation[0].end_bus_number =
PCIE_MMCFG_BUS(memmap[ecam_id].size - 1);
mcfg->allocation[0].end_bus_number = PCIE_MMCFG_BUS(info->size - 1);
build_header(linker, table_data, (void *)(table_data->data + mcfg_start),
"MCFG", table_data->len - mcfg_start, 1, NULL, NULL);
build_header(linker, table_data, (void *)mcfg, "MCFG", len, 1, NULL, NULL);
}
/* GTDT */
@ -803,7 +799,13 @@ void virt_acpi_build(VirtMachineState *vms, AcpiBuildTables *tables)
build_gtdt(tables_blob, tables->linker, vms);
acpi_add_table(table_offsets, tables_blob);
build_mcfg(tables_blob, tables->linker, vms);
{
AcpiMcfgInfo mcfg = {
.base = vms->memmap[VIRT_ECAM_ID(vms->highmem_ecam)].base,
.size = vms->memmap[VIRT_ECAM_ID(vms->highmem_ecam)].size,
};
build_mcfg(tables_blob, tables->linker, &mcfg);
}
acpi_add_table(table_offsets, tables_blob);
build_spcr(tables_blob, tables->linker, vms);

View File

@ -103,7 +103,7 @@ const VhostDevConfigOps blk_ops = {
.vhost_dev_config_notifier = vhost_user_blk_handle_config_change,
};
static void vhost_user_blk_start(VirtIODevice *vdev)
static int vhost_user_blk_start(VirtIODevice *vdev)
{
VHostUserBlk *s = VHOST_USER_BLK(vdev);
BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev)));
@ -112,13 +112,13 @@ static void vhost_user_blk_start(VirtIODevice *vdev)
if (!k->set_guest_notifiers) {
error_report("binding does not support guest notifiers");
return;
return -ENOSYS;
}
ret = vhost_dev_enable_notifiers(&s->dev, vdev);
if (ret < 0) {
error_report("Error enabling host notifiers: %d", -ret);
return;
return ret;
}
ret = k->set_guest_notifiers(qbus->parent, s->dev.nvqs, true);
@ -157,12 +157,13 @@ static void vhost_user_blk_start(VirtIODevice *vdev)
vhost_virtqueue_mask(&s->dev, vdev, i, false);
}
return;
return ret;
err_guest_notifiers:
k->set_guest_notifiers(qbus->parent, s->dev.nvqs, false);
err_host_notifiers:
vhost_dev_disable_notifiers(&s->dev, vdev);
return ret;
}
static void vhost_user_blk_stop(VirtIODevice *vdev)
@ -190,18 +191,28 @@ static void vhost_user_blk_stop(VirtIODevice *vdev)
static void vhost_user_blk_set_status(VirtIODevice *vdev, uint8_t status)
{
VHostUserBlk *s = VHOST_USER_BLK(vdev);
bool should_start = status & VIRTIO_CONFIG_S_DRIVER_OK;
bool should_start = vdev->started;
int ret;
if (!vdev->vm_running) {
should_start = false;
}
if (!s->connected) {
return;
}
if (s->dev.started == should_start) {
return;
}
if (should_start) {
vhost_user_blk_start(vdev);
ret = vhost_user_blk_start(vdev);
if (ret < 0) {
error_report("vhost-user-blk: vhost start failed: %s",
strerror(-ret));
qemu_chr_fe_disconnect(&s->chardev);
}
} else {
vhost_user_blk_stop(vdev);
}
@ -237,10 +248,13 @@ static uint64_t vhost_user_blk_get_features(VirtIODevice *vdev,
static void vhost_user_blk_handle_output(VirtIODevice *vdev, VirtQueue *vq)
{
VHostUserBlk *s = VHOST_USER_BLK(vdev);
int i;
int i, ret;
if (!(virtio_host_has_feature(vdev, VIRTIO_F_VERSION_1) &&
!virtio_vdev_has_feature(vdev, VIRTIO_F_VERSION_1))) {
if (!vdev->start_on_kick) {
return;
}
if (!s->connected) {
return;
}
@ -251,7 +265,13 @@ static void vhost_user_blk_handle_output(VirtIODevice *vdev, VirtQueue *vq)
/* Some guests kick before setting VIRTIO_CONFIG_S_DRIVER_OK so start
* vhost here instead of waiting for .set_status().
*/
vhost_user_blk_start(vdev);
ret = vhost_user_blk_start(vdev);
if (ret < 0) {
error_report("vhost-user-blk: vhost start failed: %s",
strerror(-ret));
qemu_chr_fe_disconnect(&s->chardev);
return;
}
/* Kick right away to begin processing requests already in vring */
for (i = 0; i < s->dev.nvqs; i++) {
@ -271,11 +291,103 @@ static void vhost_user_blk_reset(VirtIODevice *vdev)
vhost_dev_free_inflight(s->inflight);
}
static int vhost_user_blk_connect(DeviceState *dev)
{
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
VHostUserBlk *s = VHOST_USER_BLK(vdev);
int ret = 0;
if (s->connected) {
return 0;
}
s->connected = true;
s->dev.nvqs = s->num_queues;
s->dev.vqs = s->vqs;
s->dev.vq_index = 0;
s->dev.backend_features = 0;
vhost_dev_set_config_notifier(&s->dev, &blk_ops);
ret = vhost_dev_init(&s->dev, &s->vhost_user, VHOST_BACKEND_TYPE_USER, 0);
if (ret < 0) {
error_report("vhost-user-blk: vhost initialization failed: %s",
strerror(-ret));
return ret;
}
/* restore vhost state */
if (vdev->started) {
ret = vhost_user_blk_start(vdev);
if (ret < 0) {
error_report("vhost-user-blk: vhost start failed: %s",
strerror(-ret));
return ret;
}
}
return 0;
}
static void vhost_user_blk_disconnect(DeviceState *dev)
{
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
VHostUserBlk *s = VHOST_USER_BLK(vdev);
if (!s->connected) {
return;
}
s->connected = false;
if (s->dev.started) {
vhost_user_blk_stop(vdev);
}
vhost_dev_cleanup(&s->dev);
}
static gboolean vhost_user_blk_watch(GIOChannel *chan, GIOCondition cond,
void *opaque)
{
DeviceState *dev = opaque;
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
VHostUserBlk *s = VHOST_USER_BLK(vdev);
qemu_chr_fe_disconnect(&s->chardev);
return true;
}
static void vhost_user_blk_event(void *opaque, int event)
{
DeviceState *dev = opaque;
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
VHostUserBlk *s = VHOST_USER_BLK(vdev);
switch (event) {
case CHR_EVENT_OPENED:
if (vhost_user_blk_connect(dev) < 0) {
qemu_chr_fe_disconnect(&s->chardev);
return;
}
s->watch = qemu_chr_fe_add_watch(&s->chardev, G_IO_HUP,
vhost_user_blk_watch, dev);
break;
case CHR_EVENT_CLOSED:
vhost_user_blk_disconnect(dev);
if (s->watch) {
g_source_remove(s->watch);
s->watch = 0;
}
break;
}
}
static void vhost_user_blk_device_realize(DeviceState *dev, Error **errp)
{
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
VHostUserBlk *s = VHOST_USER_BLK(vdev);
struct vhost_virtqueue *vqs = NULL;
Error *err = NULL;
int i, ret;
if (!s->chardev.chr) {
@ -306,27 +418,29 @@ static void vhost_user_blk_device_realize(DeviceState *dev, Error **errp)
}
s->inflight = g_new0(struct vhost_inflight, 1);
s->vqs = g_new(struct vhost_virtqueue, s->num_queues);
s->watch = 0;
s->connected = false;
s->dev.nvqs = s->num_queues;
s->dev.vqs = g_new(struct vhost_virtqueue, s->dev.nvqs);
s->dev.vq_index = 0;
s->dev.backend_features = 0;
vqs = s->dev.vqs;
qemu_chr_fe_set_handlers(&s->chardev, NULL, NULL, vhost_user_blk_event,
NULL, (void *)dev, NULL, true);
vhost_dev_set_config_notifier(&s->dev, &blk_ops);
ret = vhost_dev_init(&s->dev, &s->vhost_user, VHOST_BACKEND_TYPE_USER, 0);
if (ret < 0) {
error_setg(errp, "vhost-user-blk: vhost initialization failed: %s",
strerror(-ret));
reconnect:
if (qemu_chr_fe_wait_connected(&s->chardev, &err) < 0) {
error_report_err(err);
goto virtio_err;
}
/* check whether vhost_user_blk_connect() failed or not */
if (!s->connected) {
goto reconnect;
}
ret = vhost_dev_get_config(&s->dev, (uint8_t *)&s->blkcfg,
sizeof(struct virtio_blk_config));
sizeof(struct virtio_blk_config));
if (ret < 0) {
error_setg(errp, "vhost-user-blk: get block config failed");
goto vhost_err;
error_report("vhost-user-blk: get block config failed");
goto reconnect;
}
if (s->blkcfg.num_queues != s->num_queues) {
@ -335,10 +449,8 @@ static void vhost_user_blk_device_realize(DeviceState *dev, Error **errp)
return;
vhost_err:
vhost_dev_cleanup(&s->dev);
virtio_err:
g_free(vqs);
g_free(s->vqs);
g_free(s->inflight);
virtio_cleanup(vdev);
vhost_user_cleanup(&s->vhost_user);
@ -348,12 +460,13 @@ static void vhost_user_blk_device_unrealize(DeviceState *dev, Error **errp)
{
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
VHostUserBlk *s = VHOST_USER_BLK(dev);
struct vhost_virtqueue *vqs = s->dev.vqs;
vhost_user_blk_set_status(vdev, 0);
virtio_set_status(vdev, 0);
qemu_chr_fe_set_handlers(&s->chardev, NULL, NULL, NULL,
NULL, NULL, NULL, false);
vhost_dev_cleanup(&s->dev);
vhost_dev_free_inflight(s->inflight);
g_free(vqs);
g_free(s->vqs);
g_free(s->inflight);
virtio_cleanup(vdev);
vhost_user_cleanup(&s->vhost_user);

View File

@ -102,9 +102,26 @@ const size_t hw_compat_2_7_len = G_N_ELEMENTS(hw_compat_2_7);
GlobalProperty hw_compat_2_6[] = {
{ "virtio-mmio", "format_transport_address", "off" },
/* Optional because not all virtio-pci devices support legacy mode */
{ "virtio-pci", "disable-modern", "on", .optional = true },
{ "virtio-pci", "disable-legacy", "off", .optional = true },
/*
* don't include devices which are modern-only
* ie keyboard, mouse, tablet, gpu, vga & crypto
*/
{ "virtio-9p-pci", "disable-modern", "on" },
{ "virtio-9p-pci", "disable-legacy", "off" },
{ "virtio-balloon-pci", "disable-modern", "on" },
{ "virtio-balloon-pci", "disable-legacy", "off" },
{ "virtio-blk-pci", "disable-modern", "on" },
{ "virtio-blk-pci", "disable-legacy", "off" },
{ "virtio-input-host-pci", "disable-modern", "on" },
{ "virtio-input-host-pci", "disable-legacy", "off" },
{ "virtio-net-pci", "disable-modern", "on" },
{ "virtio-net-pci", "disable-legacy", "off" },
{ "virtio-rng-pci", "disable-modern", "on" },
{ "virtio-rng-pci", "disable-legacy", "off" },
{ "virtio-scsi-pci", "disable-modern", "on" },
{ "virtio-scsi-pci", "disable-legacy", "off" },
{ "virtio-serial-pci", "disable-modern", "on" },
{ "virtio-serial-pci", "disable-legacy", "off" },
};
const size_t hw_compat_2_6_len = G_N_ELEMENTS(hw_compat_2_6);

View File

@ -47,7 +47,9 @@ static void virtio_gpu_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp)
Error *local_error = NULL;
qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus));
virtio_pci_force_virtio_1(vpci_dev);
if (!virtio_pci_force_virtio_1(vpci_dev, errp)) {
return;
}
object_property_set_bool(OBJECT(vdev), true, "realized", &local_error);
if (local_error) {

View File

@ -154,7 +154,9 @@ static void virtio_vga_realize(VirtIOPCIProxy *vpci_dev, Error **errp)
/* init virtio bits */
qdev_set_parent_bus(DEVICE(g), BUS(&vpci_dev->bus));
virtio_pci_force_virtio_1(vpci_dev);
if (!virtio_pci_force_virtio_1(vpci_dev, errp)) {
return;
}
object_property_set_bool(OBJECT(g), true, "realized", &err);
if (err) {
error_propagate(errp, err);

View File

@ -59,6 +59,7 @@
#include "hw/i386/x86-iommu.h"
#include "hw/acpi/aml-build.h"
#include "hw/acpi/pci.h"
#include "qom/qom-qobject.h"
#include "hw/i386/amd_iommu.h"
@ -87,11 +88,6 @@
/* Default IOAPIC ID */
#define ACPI_BUILD_IOAPIC_ID 0x0
typedef struct AcpiMcfgInfo {
uint64_t mcfg_base;
uint32_t mcfg_size;
} AcpiMcfgInfo;
typedef struct AcpiPmInfo {
bool s3_disabled;
bool s4_disabled;
@ -2413,29 +2409,16 @@ static void
build_mcfg_q35(GArray *table_data, BIOSLinker *linker, AcpiMcfgInfo *info)
{
AcpiTableMcfg *mcfg;
const char *sig;
int len = sizeof(*mcfg) + 1 * sizeof(mcfg->allocation[0]);
mcfg = acpi_data_push(table_data, len);
mcfg->allocation[0].address = cpu_to_le64(info->mcfg_base);
mcfg->allocation[0].address = cpu_to_le64(info->base);
/* Only a single allocation so no need to play with segments */
mcfg->allocation[0].pci_segment = cpu_to_le16(0);
mcfg->allocation[0].start_bus_number = 0;
mcfg->allocation[0].end_bus_number = PCIE_MMCFG_BUS(info->mcfg_size - 1);
mcfg->allocation[0].end_bus_number = PCIE_MMCFG_BUS(info->size - 1);
/* MCFG is used for ECAM which can be enabled or disabled by guest.
* To avoid table size changes (which create migration issues),
* always create the table even if there are no allocations,
* but set the signature to a reserved value in this case.
* ACPI spec requires OSPMs to ignore such tables.
*/
if (info->mcfg_base == PCIE_BASE_ADDR_UNMAPPED) {
/* Reserved signature: ignored by OSPM */
sig = "QEMU";
} else {
sig = "MCFG";
}
build_header(linker, table_data, (void *)mcfg, sig, len, 1, NULL, NULL);
build_header(linker, table_data, (void *)mcfg, "MCFG", len, 1, NULL, NULL);
}
/*
@ -2602,12 +2585,15 @@ static bool acpi_get_mcfg(AcpiMcfgInfo *mcfg)
if (!o) {
return false;
}
mcfg->mcfg_base = qnum_get_uint(qobject_to(QNum, o));
mcfg->base = qnum_get_uint(qobject_to(QNum, o));
qobject_unref(o);
if (mcfg->base == PCIE_BASE_ADDR_UNMAPPED) {
return false;
}
o = object_property_get_qobject(pci_host, PCIE_HOST_MCFG_SIZE, NULL);
assert(o);
mcfg->mcfg_size = qnum_get_uint(qobject_to(QNum, o));
mcfg->size = qnum_get_uint(qobject_to(QNum, o));
qobject_unref(o);
return true;
}

View File

@ -66,11 +66,6 @@ static int pxb_bus_num(PCIBus *bus)
return pxb->bus_nr;
}
static bool pxb_is_root(PCIBus *bus)
{
return true; /* by definition */
}
static uint16_t pxb_bus_numa_node(PCIBus *bus)
{
PXBDev *pxb = convert_to_pxb(bus->parent_dev);
@ -83,7 +78,6 @@ static void pxb_bus_class_init(ObjectClass *class, void *data)
PCIBusClass *pbc = PCI_BUS_CLASS(class);
pbc->bus_num = pxb_bus_num;
pbc->is_root = pxb_is_root;
pbc->numa_node = pxb_bus_numa_node;
}

View File

@ -129,14 +129,9 @@ static void pci_bus_unrealize(BusState *qbus, Error **errp)
vmstate_unregister(NULL, &vmstate_pcibus, bus);
}
static bool pcibus_is_root(PCIBus *bus)
{
return !bus->parent_dev;
}
static int pcibus_num(PCIBus *bus)
{
if (pcibus_is_root(bus)) {
if (pci_bus_is_root(bus)) {
return 0; /* pci host bridge */
}
return bus->parent_dev->config[PCI_SECONDARY_BUS];
@ -164,7 +159,6 @@ static void pci_bus_class_init(ObjectClass *klass, void *data)
k->unrealize = pci_bus_unrealize;
k->reset = pcibus_reset;
pbc->is_root = pcibus_is_root;
pbc->bus_num = pcibus_num;
pbc->numa_node = pcibus_numa_node;
pbc->allows_extended_config_space = pcibus_allows_extended_config_space;
@ -398,6 +392,7 @@ static void pci_root_bus_init(PCIBus *bus, DeviceState *parent,
bus->slot_reserved_mask = 0x0;
bus->address_space_mem = address_space_mem;
bus->address_space_io = address_space_io;
bus->flags |= PCI_BUS_IS_ROOT;
/* host bridge */
QLIST_INIT(&bus->child);
@ -415,11 +410,6 @@ bool pci_bus_is_express(PCIBus *bus)
return object_dynamic_cast(OBJECT(bus), TYPE_PCIE_BUS);
}
bool pci_bus_is_root(PCIBus *bus)
{
return PCI_BUS_GET_CLASS(bus)->is_root(bus);
}
bool pci_bus_allows_extended_config_space(PCIBus *bus)
{
return PCI_BUS_GET_CLASS(bus)->allows_extended_config_space(bus);

View File

@ -47,11 +47,6 @@ static void pcie_mmcfg_data_write(void *opaque, hwaddr mmcfg_addr,
}
addr = PCIE_MMCFG_CONFOFFSET(mmcfg_addr);
limit = pci_config_size(pci_dev);
if (limit <= addr) {
/* conventional pci device can be behind pcie-to-pci bridge.
256 <= addr < 4K has no effects. */
return;
}
pci_host_config_write_common(pci_dev, addr, limit, val, len);
}
@ -70,11 +65,6 @@ static uint64_t pcie_mmcfg_data_read(void *opaque,
}
addr = PCIE_MMCFG_CONFOFFSET(mmcfg_addr);
limit = pci_config_size(pci_dev);
if (limit <= addr) {
/* conventional pci device can be behind pcie-to-pci bridge.
256 <= addr < 4K has no effects. */
return ~0x0;
}
return pci_host_config_read_common(pci_dev, addr, limit, len);
}

View File

@ -51,7 +51,9 @@ static void virtio_crypto_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp)
}
qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus));
virtio_pci_force_virtio_1(vpci_dev);
if (!virtio_pci_force_virtio_1(vpci_dev, errp)) {
return;
}
object_property_set_bool(OBJECT(vdev), true, "realized", errp);
object_property_set_link(OBJECT(vcrypto),
OBJECT(vcrypto->vdev.conf.cryptodev), "cryptodev",

View File

@ -48,7 +48,9 @@ static void virtio_input_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp)
DeviceState *vdev = DEVICE(&vinput->vdev);
qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus));
virtio_pci_force_virtio_1(vpci_dev);
if (!virtio_pci_force_virtio_1(vpci_dev, errp)) {
return;
}
object_property_set_bool(OBJECT(vdev), true, "realized", errp);
}

View File

@ -20,6 +20,7 @@
#include "standard-headers/linux/virtio_pci.h"
#include "hw/virtio/virtio.h"
#include "hw/pci/pci.h"
#include "hw/pci/pci_bus.h"
#include "qapi/error.h"
#include "qemu/error-report.h"
#include "hw/pci/msi.h"
@ -1721,16 +1722,22 @@ static void virtio_pci_realize(PCIDevice *pci_dev, Error **errp)
/* PCI BAR regions must be powers of 2 */
pow2ceil(proxy->notify.offset + proxy->notify.size));
if (proxy->disable_legacy == ON_OFF_AUTO_AUTO) {
proxy->disable_legacy = pcie_port ? ON_OFF_AUTO_ON : ON_OFF_AUTO_OFF;
}
if (!virtio_pci_modern(proxy) && !virtio_pci_legacy(proxy)) {
error_setg(errp, "device cannot work as neither modern nor legacy mode"
" is enabled");
error_append_hint(errp, "Set either disable-modern or disable-legacy"
" to off\n");
return;
if ((proxy->disable_legacy == ON_OFF_AUTO_ON) ||
((proxy->disable_legacy == ON_OFF_AUTO_AUTO) && pcie_port)) {
if (proxy->disable_modern) {
error_setg(errp, "device cannot work as neither modern nor "
"legacy mode is enabled");
error_append_hint(errp, "Set either disable-modern or "
"disable-legacy to off\n");
return;
}
proxy->mode = VIRTIO_PCI_MODE_MODERN;
} else {
if (proxy->disable_modern) {
proxy->mode = VIRTIO_PCI_MODE_LEGACY;
} else {
proxy->mode = VIRTIO_PCI_MODE_TRANSITIONAL;
}
}
if (pcie_port && pci_is_express(pci_dev)) {

View File

@ -15,6 +15,7 @@
#ifndef QEMU_VIRTIO_PCI_H
#define QEMU_VIRTIO_PCI_H
#include "qapi/error.h"
#include "hw/pci/msi.h"
#include "hw/virtio/virtio-bus.h"
@ -118,6 +119,12 @@ typedef struct VirtIOPCIQueue {
uint32_t used[2];
} VirtIOPCIQueue;
typedef enum {
VIRTIO_PCI_MODE_LEGACY,
VIRTIO_PCI_MODE_TRANSITIONAL,
VIRTIO_PCI_MODE_MODERN,
} VirtIOPCIMode;
struct VirtIOPCIProxy {
PCIDevice pci_dev;
MemoryRegion bar;
@ -142,6 +149,7 @@ struct VirtIOPCIProxy {
bool disable_modern;
bool ignore_backend_features;
OnOffAuto disable_legacy;
VirtIOPCIMode mode;
uint32_t class_code;
uint32_t nvectors;
uint32_t dfselect;
@ -156,23 +164,34 @@ struct VirtIOPCIProxy {
static inline bool virtio_pci_modern(VirtIOPCIProxy *proxy)
{
return !proxy->disable_modern;
return proxy->mode != VIRTIO_PCI_MODE_LEGACY;
}
static inline bool virtio_pci_legacy(VirtIOPCIProxy *proxy)
{
return proxy->disable_legacy == ON_OFF_AUTO_OFF;
return proxy->mode != VIRTIO_PCI_MODE_MODERN;
}
static inline void virtio_pci_force_virtio_1(VirtIOPCIProxy *proxy)
static inline bool virtio_pci_force_virtio_1(VirtIOPCIProxy *proxy,
Error **errp)
{
proxy->disable_modern = false;
proxy->disable_legacy = ON_OFF_AUTO_ON;
if (proxy->disable_legacy == ON_OFF_AUTO_OFF) {
error_setg(errp, "Unable to set disable-legacy=off on a virtio-1.0 "
"only device");
return false;
}
if (proxy->disable_modern == true) {
error_setg(errp, "Unable to set disable-modern=on on a virtio-1.0 "
"only device");
return false;
}
proxy->mode = VIRTIO_PCI_MODE_MODERN;
return true;
}
static inline void virtio_pci_disable_modern(VirtIOPCIProxy *proxy)
{
proxy->disable_modern = true;
proxy->mode = VIRTIO_PCI_MODE_LEGACY;
}
/*

View File

@ -1162,10 +1162,16 @@ int virtio_set_status(VirtIODevice *vdev, uint8_t val)
}
}
}
vdev->started = val & VIRTIO_CONFIG_S_DRIVER_OK;
if (unlikely(vdev->start_on_kick && vdev->started)) {
vdev->start_on_kick = false;
}
if (k->set_status) {
k->set_status(vdev, val);
}
vdev->status = val;
return 0;
}
@ -1208,6 +1214,9 @@ void virtio_reset(void *opaque)
k->reset(vdev);
}
vdev->start_on_kick = (virtio_host_has_feature(vdev, VIRTIO_F_VERSION_1) &&
!virtio_vdev_has_feature(vdev, VIRTIO_F_VERSION_1));
vdev->started = false;
vdev->broken = false;
vdev->guest_features = 0;
vdev->queue_sel = 0;
@ -1518,14 +1527,21 @@ void virtio_queue_set_align(VirtIODevice *vdev, int n, int align)
static bool virtio_queue_notify_aio_vq(VirtQueue *vq)
{
bool ret = false;
if (vq->vring.desc && vq->handle_aio_output) {
VirtIODevice *vdev = vq->vdev;
trace_virtio_queue_notify(vdev, vq - vdev->vq, vq);
return vq->handle_aio_output(vdev, vq);
ret = vq->handle_aio_output(vdev, vq);
if (unlikely(vdev->start_on_kick)) {
vdev->started = true;
vdev->start_on_kick = false;
}
}
return false;
return ret;
}
static void virtio_queue_notify_vq(VirtQueue *vq)
@ -1539,6 +1555,11 @@ static void virtio_queue_notify_vq(VirtQueue *vq)
trace_virtio_queue_notify(vdev, vq - vdev->vq, vq);
vq->handle_output(vdev, vq);
if (unlikely(vdev->start_on_kick)) {
vdev->started = true;
vdev->start_on_kick = false;
}
}
}
@ -1556,6 +1577,11 @@ void virtio_queue_notify(VirtIODevice *vdev, int n)
} else if (vq->handle_output) {
vq->handle_output(vdev, vq);
}
if (unlikely(vdev->start_on_kick)) {
vdev->started = true;
vdev->start_on_kick = false;
}
}
uint16_t virtio_queue_vector(VirtIODevice *vdev, int n)
@ -1770,6 +1796,13 @@ static bool virtio_broken_needed(void *opaque)
return vdev->broken;
}
static bool virtio_started_needed(void *opaque)
{
VirtIODevice *vdev = opaque;
return vdev->started;
}
static const VMStateDescription vmstate_virtqueue = {
.name = "virtqueue_state",
.version_id = 1,
@ -1898,6 +1931,17 @@ static const VMStateDescription vmstate_virtio_broken = {
}
};
static const VMStateDescription vmstate_virtio_started = {
.name = "virtio/started",
.version_id = 1,
.minimum_version_id = 1,
.needed = &virtio_started_needed,
.fields = (VMStateField[]) {
VMSTATE_BOOL(started, VirtIODevice),
VMSTATE_END_OF_LIST()
}
};
static const VMStateDescription vmstate_virtio = {
.name = "virtio",
.version_id = 1,
@ -1913,6 +1957,7 @@ static const VMStateDescription vmstate_virtio = {
&vmstate_virtio_ringsize,
&vmstate_virtio_broken,
&vmstate_virtio_extra_state,
&vmstate_virtio_started,
NULL
}
};
@ -2246,7 +2291,7 @@ static void virtio_vmstate_change(void *opaque, int running, RunState state)
VirtIODevice *vdev = opaque;
BusState *qbus = qdev_get_parent_bus(DEVICE(vdev));
VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
bool backend_run = running && (vdev->status & VIRTIO_CONFIG_S_DRIVER_OK);
bool backend_run = running && vdev->started;
vdev->vm_running = running;
if (backend_run) {
@ -2286,6 +2331,9 @@ void virtio_init(VirtIODevice *vdev, const char *name,
g_malloc0(sizeof(*vdev->vector_queues) * nvectors);
}
vdev->start_on_kick = (virtio_host_has_feature(vdev, VIRTIO_F_VERSION_1) &&
!virtio_vdev_has_feature(vdev, VIRTIO_F_VERSION_1));
vdev->started = false;
vdev->device_id = device_id;
vdev->status = 0;
atomic_set(&vdev->isr, 0);

33
include/hw/acpi/pci.h Normal file
View File

@ -0,0 +1,33 @@
/*
* Support for generating PCI related ACPI tables and passing them to Guests
*
* Copyright (C) 2006 Fabrice Bellard
* Copyright (C) 2008-2010 Kevin O'Connor <kevin@koconnor.net>
* Copyright (C) 2013-2019 Red Hat Inc
* Copyright (C) 2019 Intel Corporation
*
* Author: Wei Yang <richardw.yang@linux.intel.com>
* 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/>.
*/
#ifndef HW_ACPI_PCI_H
#define HW_ACPI_PCI_H
typedef struct AcpiMcfgInfo {
uint64_t base;
uint32_t size;
} AcpiMcfgInfo;
#endif

View File

@ -395,7 +395,6 @@ typedef PCIINTxRoute (*pci_route_irq_fn)(void *opaque, int pin);
#define TYPE_PCIE_BUS "PCIE"
bool pci_bus_is_express(PCIBus *bus);
bool pci_bus_is_root(PCIBus *bus);
bool pci_bus_allows_extended_config_space(PCIBus *bus);
void pci_root_bus_new_inplace(PCIBus *bus, size_t bus_size, DeviceState *parent,

View File

@ -15,14 +15,19 @@ typedef struct PCIBusClass {
BusClass parent_class;
/*< public >*/
bool (*is_root)(PCIBus *bus);
int (*bus_num)(PCIBus *bus);
uint16_t (*numa_node)(PCIBus *bus);
bool (*allows_extended_config_space)(PCIBus *bus);
} PCIBusClass;
enum PCIBusFlags {
/* This bus is the root of a PCI domain */
PCI_BUS_IS_ROOT = 0x0001,
};
struct PCIBus {
BusState qbus;
enum PCIBusFlags flags;
PCIIOMMUFunc iommu_fn;
void *iommu_opaque;
uint8_t devfn_min;
@ -47,4 +52,9 @@ struct PCIBus {
Notifier machine_done;
};
static inline bool pci_bus_is_root(PCIBus *bus)
{
return !!(bus->flags & PCI_BUS_IS_ROOT);
}
#endif /* QEMU_PCI_BUS_H */

View File

@ -251,8 +251,6 @@ struct PropertyInfo {
/**
* GlobalProperty:
* @used: Set to true if property was used when initializing a device.
* @optional: If set to true, GlobalProperty will be skipped without errors
* if the property doesn't exist.
*
* An error is fatal for non-hotplugged devices, when the global is applied.
*/
@ -261,7 +259,6 @@ typedef struct GlobalProperty {
const char *property;
const char *value;
bool used;
bool optional;
} GlobalProperty;
static inline void

View File

@ -38,6 +38,9 @@ typedef struct VHostUserBlk {
struct vhost_dev dev;
struct vhost_inflight *inflight;
VhostUserState vhost_user;
struct vhost_virtqueue *vqs;
guint watch;
bool connected;
} VHostUserBlk;
#endif

View File

@ -105,6 +105,8 @@ struct VirtIODevice
uint16_t device_id;
bool vm_running;
bool broken; /* device in invalid state, needs reset */
bool started;
bool start_on_kick; /* virtio 1.0 transitional devices support that */
VMChangeStateEntry *vmstate;
char *bus_name;
uint8_t device_endian;

View File

@ -236,7 +236,6 @@ static void chr_closed_bh(void *opaque)
s = DO_UPCAST(NetVhostUserState, nc, ncs[0]);
qmp_set_link(name, false, &err);
vhost_user_stop(queues, ncs);
qemu_chr_fe_set_handlers(&s->chr, NULL, NULL, net_vhost_user_event,
NULL, opaque, NULL, true);

View File

@ -385,9 +385,6 @@ void object_apply_global_props(Object *obj, const GPtrArray *props, Error **errp
if (object_dynamic_cast(obj, p->driver) == NULL) {
continue;
}
if (p->optional && !object_property_find(obj, p->property, NULL)) {
continue;
}
p->used = true;
object_property_parse(obj, p->value, p->property, &err);
if (err != NULL) {

View File

@ -51,19 +51,7 @@ uint32_t acpi_find_rsdp_address(QTestState *qts)
return off;
}
uint64_t acpi_get_xsdt_address(uint8_t *rsdp_table)
{
uint64_t xsdt_physical_address;
uint8_t revision = rsdp_table[15 /* Revision offset */];
/* We must have revision 2 if we're looking for an XSDT pointer */
g_assert(revision == 2);
memcpy(&xsdt_physical_address, &rsdp_table[24 /* XsdtAddress offset */], 8);
return le64_to_cpu(xsdt_physical_address);
}
void acpi_parse_rsdp_table(QTestState *qts, uint32_t addr, uint8_t *rsdp_table)
void acpi_fetch_rsdp_table(QTestState *qts, uint64_t addr, uint8_t *rsdp_table)
{
uint8_t revision;
@ -91,13 +79,15 @@ void acpi_parse_rsdp_table(QTestState *qts, uint32_t addr, uint8_t *rsdp_table)
* actual one.
*/
void acpi_fetch_table(QTestState *qts, uint8_t **aml, uint32_t *aml_len,
const uint8_t *addr_ptr, const char *sig,
const uint8_t *addr_ptr, int addr_size, const char *sig,
bool verify_checksum)
{
uint32_t addr, len;
uint32_t len;
uint64_t addr = 0;
memcpy(&addr, addr_ptr , sizeof(addr));
addr = le32_to_cpu(addr);
g_assert(addr_size == 4 || addr_size == 8);
memcpy(&addr, addr_ptr , addr_size);
addr = le64_to_cpu(addr);
qtest_memread(qts, addr + 4, &len, 4); /* Length of ACPI table */
*aml_len = le32_to_cpu(len);
*aml = g_malloc0(*aml_len);
@ -111,3 +101,47 @@ void acpi_fetch_table(QTestState *qts, uint8_t **aml, uint32_t *aml_len,
g_assert(!acpi_calc_checksum(*aml, *aml_len));
}
}
#define GUID_SIZE 16
static const uint8_t AcpiTestSupportGuid[GUID_SIZE] = {
0xb1, 0xa6, 0x87, 0xab,
0x34, 0x20,
0xa0, 0xbd,
0x71, 0xbd, 0x37, 0x50, 0x07, 0x75, 0x77, 0x85 };
typedef struct {
uint8_t signature_guid[GUID_SIZE];
uint64_t rsdp10;
uint64_t rsdp20;
} __attribute__((packed)) UefiTestSupport;
/* Wait at most 600 seconds (test is slow with TCG and --enable-debug) */
#define TEST_DELAY (1 * G_USEC_PER_SEC / 10)
#define TEST_CYCLES MAX((600 * G_USEC_PER_SEC / TEST_DELAY), 1)
#define MB 0x100000ULL
uint64_t acpi_find_rsdp_address_uefi(QTestState *qts, uint64_t start,
uint64_t size)
{
int i, j;
uint8_t data[GUID_SIZE];
for (i = 0; i < TEST_CYCLES; ++i) {
for (j = 0; j < size / MB; j++) {
/* look for GUID at every 1Mb block */
uint64_t addr = start + j * MB;
qtest_memread(qts, addr, data, sizeof(data));
if (!memcmp(AcpiTestSupportGuid, data, sizeof(data))) {
UefiTestSupport ret;
qtest_memread(qts, addr, &ret, sizeof(ret));
ret.rsdp10 = le64_to_cpu(ret.rsdp10);
ret.rsdp20 = le64_to_cpu(ret.rsdp20);
return ret.rsdp20 ? ret.rsdp20 : ret.rsdp10;
}
}
g_usleep(TEST_DELAY);
}
g_assert_not_reached();
return 0;
}

View File

@ -46,10 +46,11 @@ typedef struct {
uint8_t acpi_calc_checksum(const uint8_t *data, int len);
uint32_t acpi_find_rsdp_address(QTestState *qts);
uint64_t acpi_get_xsdt_address(uint8_t *rsdp_table);
void acpi_parse_rsdp_table(QTestState *qts, uint32_t addr, uint8_t *rsdp_table);
uint64_t acpi_find_rsdp_address_uefi(QTestState *qts, uint64_t start,
uint64_t size);
void acpi_fetch_rsdp_table(QTestState *qts, uint64_t addr, uint8_t *rsdp_table);
void acpi_fetch_table(QTestState *qts, uint8_t **aml, uint32_t *aml_len,
const uint8_t *addr_ptr, const char *sig,
const uint8_t *addr_ptr, int addr_size, const char *sig,
bool verify_checksum);
#endif /* TEST_ACPI_UTILS_H */

View File

@ -24,9 +24,15 @@
#define ACPI_REBUILD_EXPECTED_AML "TEST_ACPI_REBUILD_AML"
typedef struct {
const char *accel;
const char *machine;
const char *variant;
uint32_t rsdp_addr;
const char *uefi_fl1;
const char *uefi_fl2;
const char *cd;
const uint64_t ram_start;
const uint64_t scan_len;
uint64_t rsdp_addr;
uint8_t rsdp_table[36 /* ACPI 2.0+ RSDP size */];
GArray *tables;
uint32_t smbios_ep_addr;
@ -77,22 +83,13 @@ static void free_test_data(test_data *data)
g_array_free(data->tables, true);
}
static void test_acpi_rsdp_address(test_data *data)
{
uint32_t off = acpi_find_rsdp_address(data->qts);
g_assert_cmphex(off, <, 0x100000);
data->rsdp_addr = off;
}
static void test_acpi_rsdp_table(test_data *data)
{
uint8_t *rsdp_table = data->rsdp_table, revision;
uint32_t addr = data->rsdp_addr;
uint8_t *rsdp_table = data->rsdp_table;
acpi_parse_rsdp_table(data->qts, addr, rsdp_table);
revision = rsdp_table[15 /* Revision offset */];
acpi_fetch_rsdp_table(data->qts, data->rsdp_addr, rsdp_table);
switch (revision) {
switch (rsdp_table[15 /* Revision offset */]) {
case 0: /* ACPI 1.0 RSDP */
/* With rev 1, checksum is only for the first 20 bytes */
g_assert(!acpi_calc_checksum(rsdp_table, 20));
@ -107,21 +104,29 @@ static void test_acpi_rsdp_table(test_data *data)
}
}
static void test_acpi_rsdt_table(test_data *data)
static void test_acpi_rxsdt_table(test_data *data)
{
const char *sig = "RSDT";
AcpiSdtTable rsdt = {};
int entry_size = 4;
int addr_off = 16 /* RsdtAddress */;
uint8_t *ent;
/* read RSDT table */
if (data->rsdp_table[15 /* Revision offset */] != 0) {
addr_off = 24 /* XsdtAddress */;
entry_size = 8;
sig = "XSDT";
}
/* read [RX]SDT table */
acpi_fetch_table(data->qts, &rsdt.aml, &rsdt.aml_len,
&data->rsdp_table[16 /* RsdtAddress */], "RSDT", true);
&data->rsdp_table[addr_off], entry_size, sig, true);
/* Load all tables and add to test list directly RSDT referenced tables */
ACPI_FOREACH_RSDT_ENTRY(rsdt.aml, rsdt.aml_len, ent, 4 /* Entry size */) {
ACPI_FOREACH_RSDT_ENTRY(rsdt.aml, rsdt.aml_len, ent, entry_size) {
AcpiSdtTable ssdt_table = {};
acpi_fetch_table(data->qts, &ssdt_table.aml, &ssdt_table.aml_len, ent,
NULL, true);
entry_size, NULL, true);
/* Add table to ASL test tables list */
g_array_append_val(data->tables, ssdt_table);
}
@ -134,16 +139,29 @@ static void test_acpi_fadt_table(test_data *data)
AcpiSdtTable table = g_array_index(data->tables, typeof(table), 0);
uint8_t *fadt_aml = table.aml;
uint32_t fadt_len = table.aml_len;
uint32_t val;
int dsdt_offset = 40 /* DSDT */;
int dsdt_entry_size = 4;
g_assert(compare_signature(&table, "FACP"));
/* Since DSDT/FACS isn't in RSDT, add them to ASL test list manually */
acpi_fetch_table(data->qts, &table.aml, &table.aml_len,
fadt_aml + 36 /* FIRMWARE_CTRL */, "FACS", false);
g_array_append_val(data->tables, table);
memcpy(&val, fadt_aml + 112 /* Flags */, 4);
val = le32_to_cpu(val);
if (!(val & 1UL << 20 /* HW_REDUCED_ACPI */)) {
acpi_fetch_table(data->qts, &table.aml, &table.aml_len,
fadt_aml + 36 /* FIRMWARE_CTRL */, 4, "FACS", false);
g_array_append_val(data->tables, table);
}
memcpy(&val, fadt_aml + dsdt_offset, 4);
val = le32_to_cpu(val);
if (!val) {
dsdt_offset = 140 /* X_DSDT */;
dsdt_entry_size = 8;
}
acpi_fetch_table(data->qts, &table.aml, &table.aml_len,
fadt_aml + 40 /* DSDT */, "DSDT", true);
fadt_aml + dsdt_offset, dsdt_entry_size, "DSDT", true);
g_array_append_val(data->tables, table);
memset(fadt_aml + 36, 0, 4); /* sanitize FIRMWARE_CTRL ptr */
@ -177,11 +195,14 @@ static void dump_aml_files(test_data *data, bool rebuild)
sdt->aml, ext);
fd = g_open(aml_file, O_WRONLY|O_TRUNC|O_CREAT,
S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH);
if (fd < 0) {
perror(aml_file);
}
g_assert(fd >= 0);
} 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->aml, sdt->aml_len);
g_assert(ret == sdt->aml_len);
@ -505,23 +526,44 @@ static void test_smbios_structs(test_data *data)
static void test_acpi_one(const char *params, test_data *data)
{
char *args;
bool use_uefi = data->uefi_fl1 && data->uefi_fl2;
/* Disable kernel irqchip to be able to override apic irq0. */
args = g_strdup_printf("-machine %s,accel=%s,kernel-irqchip=off "
"-net none -display none %s "
"-drive id=hd0,if=none,file=%s,format=raw "
"-device ide-hd,drive=hd0 ",
data->machine, "kvm:tcg",
params ? params : "", disk);
if (use_uefi) {
/*
* TODO: convert '-drive if=pflash' to new syntax (see e33763be7cd3)
* when arm/virt boad starts to support it.
*/
args = g_strdup_printf("-machine %s,accel=%s -nodefaults -nographic "
"-drive if=pflash,format=raw,file=%s,readonly "
"-drive if=pflash,format=raw,file=%s,snapshot=on -cdrom %s %s",
data->machine, data->accel ? data->accel : "kvm:tcg",
data->uefi_fl1, data->uefi_fl2, data->cd, params ? params : "");
} else {
/* Disable kernel irqchip to be able to override apic irq0. */
args = g_strdup_printf("-machine %s,accel=%s,kernel-irqchip=off "
"-net none -display none %s "
"-drive id=hd0,if=none,file=%s,format=raw "
"-device ide-hd,drive=hd0 ",
data->machine, data->accel ? data->accel : "kvm:tcg",
params ? params : "", disk);
}
data->qts = qtest_init(args);
boot_sector_test(data->qts);
if (use_uefi) {
g_assert(data->scan_len);
data->rsdp_addr = acpi_find_rsdp_address_uefi(data->qts,
data->ram_start, data->scan_len);
} else {
boot_sector_test(data->qts);
data->rsdp_addr = acpi_find_rsdp_address(data->qts);
g_assert_cmphex(data->rsdp_addr, <, 0x100000);
}
data->tables = g_array_new(false, true, sizeof(AcpiSdtTable));
test_acpi_rsdp_address(data);
test_acpi_rsdp_table(data);
test_acpi_rsdt_table(data);
test_acpi_rxsdt_table(data);
test_acpi_fadt_table(data);
if (iasl) {
@ -532,8 +574,15 @@ static void test_acpi_one(const char *params, test_data *data)
}
}
test_smbios_entry_point(data);
test_smbios_structs(data);
/*
* TODO: make SMBIOS tests work with UEFI firmware,
* Bug on uefi-test-tools to provide entry point:
* https://bugs.launchpad.net/qemu/+bug/1821884
*/
if (!use_uefi) {
test_smbios_entry_point(data);
test_smbios_structs(data);
}
assert(!global_qtest);
qtest_quit(data->qts);
@ -769,13 +818,14 @@ int main(int argc, char *argv[])
const char *arch = qtest_get_arch();
int ret;
ret = boot_sector_init(disk);
if(ret)
return ret;
g_test_init(&argc, &argv, NULL);
if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) {
ret = boot_sector_init(disk);
if (ret) {
return ret;
}
qtest_add_func("acpi/piix4", test_acpi_piix4_tcg);
qtest_add_func("acpi/piix4/bridge", test_acpi_piix4_tcg_bridge);
qtest_add_func("acpi/q35", test_acpi_q35_tcg);

View File

@ -7,21 +7,12 @@
#
# Authors:
# Marcel Apfelbaum <marcel.a@redhat.com>
# Igor Mammedov <imammedo@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
qemu_bins="x86_64-softmmu/qemu-system-x86_64"
if [ ! -e "tests/bios-tables-test" ]; then
echo "Test: bios-tables-test is required! Run make check before this script."
@ -29,6 +20,14 @@ if [ ! -e "tests/bios-tables-test" ]; then
exit 1;
fi
TEST_ACPI_REBUILD_AML=y QTEST_QEMU_BINARY=$qemu tests/bios-tables-test
for qemu in $qemu_bins; do
if [ ! -e $qemu ]; then
echo "Run 'make' to build the following QEMU executables: $qemu_bins"
echo "Also, run this script from the build directory."
exit 1;
fi
TEST_ACPI_REBUILD_AML=y QTEST_QEMU_BINARY=$qemu tests/bios-tables-test
done
echo "The files were rebuilt and can be added to git."

View File

@ -40,14 +40,14 @@ static uint32_t acpi_find_vgia(QTestState *qts)
g_assert_cmphex(rsdp_offset, <, RSDP_ADDR_INVALID);
acpi_parse_rsdp_table(qts, rsdp_offset, rsdp_table);
acpi_fetch_rsdp_table(qts, rsdp_offset, rsdp_table);
acpi_fetch_table(qts, &rsdt, &rsdt_len, &rsdp_table[16 /* RsdtAddress */],
"RSDT", true);
4, "RSDT", true);
ACPI_FOREACH_RSDT_ENTRY(rsdt, rsdt_len, ent, 4 /* Entry size */) {
uint8_t *table_aml;
acpi_fetch_table(qts, &table_aml, &table_length, ent, NULL, true);
acpi_fetch_table(qts, &table_aml, &table_length, ent, 4, NULL, true);
if (!memcmp(table_aml + 16 /* OEM Table ID */, "VMGENID", 7)) {
uint32_t vgia_val;
uint8_t *aml = &table_aml[36 /* AML byte-code start */];