pc,pci,virtio: fixes, cleanups

Lots of fixes, cleanups.
 CPU hot-unplug improvements.
 A new AER property for virtio devices, adding a dummy AER capability.
 
 Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
 -----BEGIN PGP SIGNATURE-----
 
 iQFDBAABCAAtFiEEXQn9CHHI+FuUyooNKB8NuNKNVGkFAl/REawPHG1zdEByZWRo
 YXQuY29tAAoJECgfDbjSjVRp2MQIALsT4+JQRvo3YR0L3hpVFzRU4cMwVYryqe2/
 RRWggBI4OB3kiQT2YD4lElXmZnTTG5p5B69P+DE5Wj5faTQoIQdWZjDS9pO+tI+p
 gVNKgaN7ByKz5PUQNhhGKUfQ4F6x5yDePWlWGJZAmExEysAuC/nmCe5Lo1PrDzEV
 t0H/TmCNxAb4HsT9LG1pogTM/lpXDsjlVG/Mryh2XNe7z6c0ZyGo1WMI59RFVGId
 9EpOV1OXWVHgkSWD6Rn7mqNq7zASS2Du/osHcQ6C51L/UE+pZPJW7gDqDRudItBR
 XXteNAeLXo5NdGJsTdVuHfy+Z0fJdHny9jLHMO2FkhV1wzYgJSM=
 =LaYX
 -----END PGP SIGNATURE-----

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

pc,pci,virtio: fixes, cleanups

Lots of fixes, cleanups.
CPU hot-unplug improvements.
A new AER property for virtio devices, adding a dummy AER capability.

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

# gpg: Signature made Wed 09 Dec 2020 18:04:28 GMT
# gpg:                using RSA key 5D09FD0871C8F85B94CA8A0D281F0DB8D28D5469
# gpg:                issuer "mst@redhat.com"
# 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: (65 commits)
  hw/virtio-pci Added AER capability.
  hw/virtio-pci Added counter for pcie capabilities offsets.
  pcie_aer: Fix help message of pcie_aer_inject_error command
  x86: ich9: let firmware negotiate 'CPU hot-unplug with SMI' feature
  x86: ich9: factor out "guest_cpu_hotplug_features"
  tests/acpi: update expected files
  x86: acpi: let the firmware handle pending "CPU remove" events in SMM
  tests/acpi: allow expected files change
  x86: acpi: introduce AcpiPmInfo::smi_on_cpu_unplug
  acpi: cpuhp: introduce 'firmware performs eject' status/control bits
  hw/i386/pc: add max combined fw size as machine configuration option
  block/export: avoid g_return_val_if() input validation
  contrib/vhost-user-input: avoid g_return_val_if() input validation
  contrib/vhost-user-gpu: avoid g_return_val_if() input validation
  contrib/vhost-user-blk: avoid g_return_val_if() input validation
  .gitlab-ci: add build-libvhost-user
  libvhost-user: add a simple link test without glib
  libvhost-user: make it a meson subproject
  libvhost-user: drop qemu/osdep.h dependency
  libvhost-user: remove qemu/compiler.h usage
  ...

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2020-12-09 20:08:54 +00:00
commit 5e7b204dbf
90 changed files with 1296 additions and 858 deletions

View File

@ -535,6 +535,17 @@ check-dco:
variables:
GIT_DEPTH: 1000
build-libvhost-user:
stage: build
image: $CI_REGISTRY_IMAGE/qemu/fedora:latest
before_script:
- dnf install -y meson ninja-build
script:
- mkdir subprojects/libvhost-user/build
- cd subprojects/libvhost-user/build
- meson
- ninja
pages:
image: $CI_REGISTRY_IMAGE/qemu/ubuntu2004:latest
stage: test

View File

@ -11,7 +11,7 @@
*/
#include "qemu/osdep.h"
#include "block/block.h"
#include "contrib/libvhost-user/libvhost-user.h"
#include "subprojects/libvhost-user/libvhost-user.h" /* only for the type definitions */
#include "standard-headers/linux/virtio_blk.h"
#include "qemu/vhost-user-server.h"
#include "vhost-user-blk-server.h"
@ -267,7 +267,9 @@ vu_blk_get_config(VuDev *vu_dev, uint8_t *config, uint32_t len)
VuServer *server = container_of(vu_dev, VuServer, vu_dev);
VuBlkExport *vexp = container_of(server, VuBlkExport, vu_server);
g_return_val_if_fail(len <= sizeof(struct virtio_blk_config), -1);
if (len > sizeof(struct virtio_blk_config)) {
return -1;
}
memcpy(config, &vexp->blkcfg, len);
return 0;

View File

@ -1,4 +0,0 @@
libvhost_user = static_library('vhost-user',
files('libvhost-user.c', 'libvhost-user-glib.c'),
build_by_default: false)
vhost_user = declare_dependency(link_with: libvhost_user)

View File

@ -1,6 +1,5 @@
# FIXME: broken on 32-bit architectures
executable('vhost-user-blk', files('vhost-user-blk.c'),
link_with: libvhost_user,
dependencies: qemuutil,
dependencies: [qemuutil, vhost_user],
build_by_default: false,
install: false)

View File

@ -17,8 +17,7 @@
#include "qemu/osdep.h"
#include "standard-headers/linux/virtio_blk.h"
#include "contrib/libvhost-user/libvhost-user-glib.h"
#include "contrib/libvhost-user/libvhost-user.h"
#include "libvhost-user-glib.h"
#if defined(__linux__)
#include <linux/fs.h>
@ -404,7 +403,9 @@ vub_get_config(VuDev *vu_dev, uint8_t *config, uint32_t len)
VugDev *gdev;
VubDev *vdev_blk;
g_return_val_if_fail(len <= sizeof(struct virtio_blk_config), -1);
if (len > sizeof(struct virtio_blk_config)) {
return -1;
}
gdev = container_of(vu_dev, VugDev, parent);
vdev_blk = container_of(gdev, VubDev, parent);

View File

@ -2,8 +2,7 @@ if 'CONFIG_TOOLS' in config_host and 'CONFIG_VIRGL' in config_host \
and 'CONFIG_GBM' in config_host and 'CONFIG_LINUX' in config_host \
and pixman.found()
executable('vhost-user-gpu', files('vhost-user-gpu.c', 'virgl.c', 'vugbm.c'),
link_with: libvhost_user,
dependencies: [qemuutil, pixman, gbm, virgl],
dependencies: [qemuutil, pixman, gbm, virgl, vhost_user],
install: true,
install_dir: get_option('libexecdir'))

View File

@ -1044,7 +1044,9 @@ vg_get_config(VuDev *dev, uint8_t *config, uint32_t len)
{
VuGpu *g = container_of(dev, VuGpu, dev.parent);
g_return_val_if_fail(len <= sizeof(struct virtio_gpu_config), -1);
if (len > sizeof(struct virtio_gpu_config)) {
return -1;
}
if (opt_virgl) {
g->virtio_config.num_capsets = vg_virgl_get_num_capsets();

View File

@ -17,7 +17,7 @@
#include "qemu/osdep.h"
#include "contrib/libvhost-user/libvhost-user-glib.h"
#include "libvhost-user-glib.h"
#include "standard-headers/linux/virtio_gpu.h"
#include "qemu/queue.h"

View File

@ -12,8 +12,7 @@
#include "qemu/iov.h"
#include "qemu/bswap.h"
#include "qemu/sockets.h"
#include "contrib/libvhost-user/libvhost-user.h"
#include "contrib/libvhost-user/libvhost-user-glib.h"
#include "libvhost-user-glib.h"
#include "standard-headers/linux/virtio_input.h"
#include "qapi/error.h"
@ -212,7 +211,9 @@ static int vi_get_config(VuDev *dev, uint8_t *config, uint32_t len)
{
VuInput *vi = container_of(dev, VuInput, dev.parent);
g_return_val_if_fail(len <= sizeof(*vi->sel_config), -1);
if (len > sizeof(*vi->sel_config)) {
return -1;
}
if (vi->sel_config) {
memcpy(config, vi->sel_config, len);

View File

@ -1,5 +1,4 @@
executable('vhost-user-input', files('main.c'),
link_with: libvhost_user,
dependencies: qemuutil,
dependencies: [qemuutil, vhost_user],
build_by_default: targetos == 'linux',
install: false)

View File

@ -1,7 +1,6 @@
if 'CONFIG_LIBISCSI' in config_host
executable('vhost-user-scsi', files('vhost-user-scsi.c'),
link_with: libvhost_user,
dependencies: [qemuutil, libiscsi],
dependencies: [qemuutil, libiscsi, vhost_user],
build_by_default: targetos == 'linux',
install: false)
endif

View File

@ -15,7 +15,7 @@
#define inline __attribute__((gnu_inline)) /* required for libiscsi v1.9.0 */
#include <iscsi/scsi-lowlevel.h>
#undef inline
#include "contrib/libvhost-user/libvhost-user-glib.h"
#include "libvhost-user-glib.h"
#include "standard-headers/linux/virtio_scsi.h"
@ -232,6 +232,7 @@ static void vus_proc_req(VuDev *vu_dev, int idx)
VugDev *gdev;
VusDev *vdev_scsi;
VuVirtq *vq;
VuVirtqElement *elem = NULL;
assert(vu_dev);
@ -248,7 +249,6 @@ static void vus_proc_req(VuDev *vu_dev, int idx)
g_debug("Got kicked on vq[%d]@%p", idx, vq);
while (1) {
VuVirtqElement *elem;
VirtIOSCSICmdReq *req;
VirtIOSCSICmdResp *rsp;
@ -288,6 +288,7 @@ static void vus_proc_req(VuDev *vu_dev, int idx)
free(elem);
}
free(elem);
}
static void vus_queue_set_started(VuDev *vu_dev, int idx, bool started)

View File

@ -56,8 +56,11 @@ read access:
no device check event to OSPM was issued.
It's valid only when bit 0 is set.
2: Device remove event, used to distinguish device for which
no device eject request to OSPM was issued.
3-7: reserved and should be ignored by OSPM
no device eject request to OSPM was issued. Firmware must
ignore this bit.
3: reserved and should be ignored by OSPM
4: if set to 1, OSPM requests firmware to perform device eject.
5-7: reserved and should be ignored by OSPM
[0x5-0x7] reserved
[0x8] Command data: (DWORD access)
contains 0 unless value last stored in 'Command field' is one of:
@ -79,10 +82,16 @@ write access:
selected CPU device
2: if set to 1 clears device remove event, set by OSPM
after it has emitted device eject request for the
selected CPU device
selected CPU device.
3: if set to 1 initiates device eject, set by OSPM when it
triggers CPU device removal and calls _EJ0 method
4-7: reserved, OSPM must clear them before writing to register
triggers CPU device removal and calls _EJ0 method or by firmware
when bit #4 is set. In case bit #4 were set, it's cleared as
part of device eject.
4: if set to 1, OSPM hands over device eject to firmware.
Firmware shall issue device eject request as described above
(bit #3) and OSPM should not touch device eject bit (#3) in case
it's asked firmware to perform CPU device eject.
5-7: reserved, OSPM must clear them before writing to register
[0x5] Command field: (1 byte access)
value:
0: selects a CPU device with inserting/removing events and

View File

@ -1302,8 +1302,8 @@ ERST
" -c for correctable error\n\t\t\t"
"<id> = qdev device id\n\t\t\t"
"<error_status> = error string or 32bit\n\t\t\t"
"<tlb header> = 32bit x 4\n\t\t\t"
"<tlb header prefix> = 32bit x 4",
"<tlp header> = 32bit x 4\n\t\t\t"
"<tlp header prefix> = 32bit x 4",
.cmd = hmp_pcie_aer_inject_error,
},

View File

@ -27,6 +27,9 @@
#include "sysemu/numa.h"
#include "hw/boards.h"
#include "hw/acpi/tpm.h"
#include "hw/pci/pci_host.h"
#include "hw/pci/pci_bus.h"
#include "hw/pci/pci_bridge.h"
static GArray *build_alloc_array(void)
{
@ -55,6 +58,128 @@ static void build_append_array(GArray *array, GArray *val)
#define ACPI_NAMESEG_LEN 4
void crs_range_insert(GPtrArray *ranges, uint64_t base, uint64_t limit)
{
CrsRangeEntry *entry;
entry = g_malloc(sizeof(*entry));
entry->base = base;
entry->limit = limit;
g_ptr_array_add(ranges, entry);
}
static void crs_range_free(gpointer data)
{
CrsRangeEntry *entry = (CrsRangeEntry *)data;
g_free(entry);
}
void crs_range_set_init(CrsRangeSet *range_set)
{
range_set->io_ranges = g_ptr_array_new_with_free_func(crs_range_free);
range_set->mem_ranges = g_ptr_array_new_with_free_func(crs_range_free);
range_set->mem_64bit_ranges =
g_ptr_array_new_with_free_func(crs_range_free);
}
void crs_range_set_free(CrsRangeSet *range_set)
{
g_ptr_array_free(range_set->io_ranges, true);
g_ptr_array_free(range_set->mem_ranges, true);
g_ptr_array_free(range_set->mem_64bit_ranges, true);
}
static gint crs_range_compare(gconstpointer a, gconstpointer b)
{
CrsRangeEntry *entry_a = *(CrsRangeEntry **)a;
CrsRangeEntry *entry_b = *(CrsRangeEntry **)b;
if (entry_a->base < entry_b->base) {
return -1;
} else if (entry_a->base > entry_b->base) {
return 1;
} else {
return 0;
}
}
/*
* crs_replace_with_free_ranges - given the 'used' ranges within [start - end]
* interval, computes the 'free' ranges from the same interval.
* Example: If the input array is { [a1 - a2],[b1 - b2] }, the function
* will return { [base - a1], [a2 - b1], [b2 - limit] }.
*/
void crs_replace_with_free_ranges(GPtrArray *ranges,
uint64_t start, uint64_t end)
{
GPtrArray *free_ranges = g_ptr_array_new();
uint64_t free_base = start;
int i;
g_ptr_array_sort(ranges, crs_range_compare);
for (i = 0; i < ranges->len; i++) {
CrsRangeEntry *used = g_ptr_array_index(ranges, i);
if (free_base < used->base) {
crs_range_insert(free_ranges, free_base, used->base - 1);
}
free_base = used->limit + 1;
}
if (free_base < end) {
crs_range_insert(free_ranges, free_base, end);
}
g_ptr_array_set_size(ranges, 0);
for (i = 0; i < free_ranges->len; i++) {
g_ptr_array_add(ranges, g_ptr_array_index(free_ranges, i));
}
g_ptr_array_free(free_ranges, true);
}
/*
* crs_range_merge - merges adjacent ranges in the given array.
* Array elements are deleted and replaced with the merged ranges.
*/
static void crs_range_merge(GPtrArray *range)
{
GPtrArray *tmp = g_ptr_array_new_with_free_func(crs_range_free);
CrsRangeEntry *entry;
uint64_t range_base, range_limit;
int i;
if (!range->len) {
return;
}
g_ptr_array_sort(range, crs_range_compare);
entry = g_ptr_array_index(range, 0);
range_base = entry->base;
range_limit = entry->limit;
for (i = 1; i < range->len; i++) {
entry = g_ptr_array_index(range, i);
if (entry->base - 1 == range_limit) {
range_limit = entry->limit;
} else {
crs_range_insert(tmp, range_base, range_limit);
range_base = entry->base;
range_limit = entry->limit;
}
}
crs_range_insert(tmp, range_base, range_limit);
g_ptr_array_set_size(range, 0);
for (i = 0; i < tmp->len; i++) {
entry = g_ptr_array_index(tmp, i);
crs_range_insert(range, entry->base, entry->limit);
}
g_ptr_array_free(tmp, true);
}
static void
build_append_nameseg(GArray *array, const char *seg)
{
@ -1951,6 +2076,166 @@ void build_tpm2(GArray *table_data, BIOSLinker *linker, GArray *tcpalog)
tpm2_ptr, "TPM2", table_data->len - tpm2_start, 4, NULL, NULL);
}
Aml *build_crs(PCIHostState *host, CrsRangeSet *range_set)
{
Aml *crs = aml_resource_template();
CrsRangeSet temp_range_set;
CrsRangeEntry *entry;
uint8_t max_bus = pci_bus_num(host->bus);
uint8_t type;
int devfn;
int i;
crs_range_set_init(&temp_range_set);
for (devfn = 0; devfn < ARRAY_SIZE(host->bus->devices); devfn++) {
uint64_t range_base, range_limit;
PCIDevice *dev = host->bus->devices[devfn];
if (!dev) {
continue;
}
for (i = 0; i < PCI_NUM_REGIONS; i++) {
PCIIORegion *r = &dev->io_regions[i];
range_base = r->addr;
range_limit = r->addr + r->size - 1;
/*
* Work-around for old bioses
* that do not support multiple root buses
*/
if (!range_base || range_base > range_limit) {
continue;
}
if (r->type & PCI_BASE_ADDRESS_SPACE_IO) {
crs_range_insert(temp_range_set.io_ranges,
range_base, range_limit);
} else { /* "memory" */
uint64_t length = range_limit - range_base + 1;
if (range_limit <= UINT32_MAX && length <= UINT32_MAX) {
crs_range_insert(temp_range_set.mem_ranges, range_base,
range_limit);
} else {
crs_range_insert(temp_range_set.mem_64bit_ranges,
range_base, range_limit);
}
}
}
type = dev->config[PCI_HEADER_TYPE] & ~PCI_HEADER_TYPE_MULTI_FUNCTION;
if (type == PCI_HEADER_TYPE_BRIDGE) {
uint8_t subordinate = dev->config[PCI_SUBORDINATE_BUS];
if (subordinate > max_bus) {
max_bus = subordinate;
}
range_base = pci_bridge_get_base(dev, PCI_BASE_ADDRESS_SPACE_IO);
range_limit = pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_SPACE_IO);
/*
* Work-around for old bioses
* that do not support multiple root buses
*/
if (range_base && range_base <= range_limit) {
crs_range_insert(temp_range_set.io_ranges,
range_base, range_limit);
}
range_base =
pci_bridge_get_base(dev, PCI_BASE_ADDRESS_SPACE_MEMORY);
range_limit =
pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_SPACE_MEMORY);
/*
* Work-around for old bioses
* that do not support multiple root buses
*/
if (range_base && range_base <= range_limit) {
uint64_t length = range_limit - range_base + 1;
if (range_limit <= UINT32_MAX && length <= UINT32_MAX) {
crs_range_insert(temp_range_set.mem_ranges,
range_base, range_limit);
} else {
crs_range_insert(temp_range_set.mem_64bit_ranges,
range_base, range_limit);
}
}
range_base =
pci_bridge_get_base(dev, PCI_BASE_ADDRESS_MEM_PREFETCH);
range_limit =
pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_MEM_PREFETCH);
/*
* Work-around for old bioses
* that do not support multiple root buses
*/
if (range_base && range_base <= range_limit) {
uint64_t length = range_limit - range_base + 1;
if (range_limit <= UINT32_MAX && length <= UINT32_MAX) {
crs_range_insert(temp_range_set.mem_ranges,
range_base, range_limit);
} else {
crs_range_insert(temp_range_set.mem_64bit_ranges,
range_base, range_limit);
}
}
}
}
crs_range_merge(temp_range_set.io_ranges);
for (i = 0; i < temp_range_set.io_ranges->len; i++) {
entry = g_ptr_array_index(temp_range_set.io_ranges, i);
aml_append(crs,
aml_word_io(AML_MIN_FIXED, AML_MAX_FIXED,
AML_POS_DECODE, AML_ENTIRE_RANGE,
0, entry->base, entry->limit, 0,
entry->limit - entry->base + 1));
crs_range_insert(range_set->io_ranges, entry->base, entry->limit);
}
crs_range_merge(temp_range_set.mem_ranges);
for (i = 0; i < temp_range_set.mem_ranges->len; i++) {
entry = g_ptr_array_index(temp_range_set.mem_ranges, i);
assert(entry->limit <= UINT32_MAX &&
(entry->limit - entry->base + 1) <= UINT32_MAX);
aml_append(crs,
aml_dword_memory(AML_POS_DECODE, AML_MIN_FIXED,
AML_MAX_FIXED, AML_NON_CACHEABLE,
AML_READ_WRITE,
0, entry->base, entry->limit, 0,
entry->limit - entry->base + 1));
crs_range_insert(range_set->mem_ranges, entry->base, entry->limit);
}
crs_range_merge(temp_range_set.mem_64bit_ranges);
for (i = 0; i < temp_range_set.mem_64bit_ranges->len; i++) {
entry = g_ptr_array_index(temp_range_set.mem_64bit_ranges, i);
aml_append(crs,
aml_qword_memory(AML_POS_DECODE, AML_MIN_FIXED,
AML_MAX_FIXED, AML_NON_CACHEABLE,
AML_READ_WRITE,
0, entry->base, entry->limit, 0,
entry->limit - entry->base + 1));
crs_range_insert(range_set->mem_64bit_ranges,
entry->base, entry->limit);
}
crs_range_set_free(&temp_range_set);
aml_append(crs,
aml_word_bus_number(AML_MIN_FIXED, AML_MAX_FIXED, AML_POS_DECODE,
0,
pci_bus_num(host->bus),
max_bus,
0,
max_bus - pci_bus_num(host->bus) + 1));
return crs;
}
/* ACPI 5.0: 6.4.3.8.2 Serial Bus Connection Descriptors */
static Aml *aml_serial_bus_device(uint8_t serial_bus_type, uint8_t flags,
uint16_t type_flags,

View File

@ -71,6 +71,7 @@ static uint64_t cpu_hotplug_rd(void *opaque, hwaddr addr, unsigned size)
val |= cdev->cpu ? 1 : 0;
val |= cdev->is_inserting ? 2 : 0;
val |= cdev->is_removing ? 4 : 0;
val |= cdev->fw_remove ? 16 : 0;
trace_cpuhp_acpi_read_flags(cpu_st->selector, val);
break;
case ACPI_CPU_CMD_DATA_OFFSET_RW:
@ -148,6 +149,14 @@ static void cpu_hotplug_wr(void *opaque, hwaddr addr, uint64_t data,
hotplug_ctrl = qdev_get_hotplug_handler(dev);
hotplug_handler_unplug(hotplug_ctrl, dev, NULL);
object_unparent(OBJECT(dev));
cdev->fw_remove = false;
} else if (data & 16) {
if (!cdev->cpu || cdev->cpu == first_cpu) {
trace_cpuhp_acpi_fw_remove_invalid_cpu(cpu_st->selector);
break;
}
trace_cpuhp_acpi_fw_remove_cpu(cpu_st->selector);
cdev->fw_remove = true;
}
break;
case ACPI_CPU_CMD_OFFSET_WR:
@ -159,7 +168,8 @@ static void cpu_hotplug_wr(void *opaque, hwaddr addr, uint64_t data,
do {
cdev = &cpu_st->devs[iter];
if (cdev->is_inserting || cdev->is_removing) {
if (cdev->is_inserting || cdev->is_removing ||
cdev->fw_remove) {
cpu_st->selector = iter;
trace_cpuhp_acpi_cpu_has_events(cpu_st->selector,
cdev->is_inserting, cdev->is_removing);
@ -332,6 +342,7 @@ const VMStateDescription vmstate_cpu_hotplug = {
#define CPU_INSERT_EVENT "CINS"
#define CPU_REMOVE_EVENT "CRMV"
#define CPU_EJECT_EVENT "CEJ0"
#define CPU_FW_EJECT_EVENT "CEJF"
void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts,
hwaddr io_base,
@ -384,7 +395,9 @@ void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts,
aml_append(field, aml_named_field(CPU_REMOVE_EVENT, 1));
/* initiates device eject, write only */
aml_append(field, aml_named_field(CPU_EJECT_EVENT, 1));
aml_append(field, aml_reserved_field(4));
/* tell firmware to do device eject, write only */
aml_append(field, aml_named_field(CPU_FW_EJECT_EVENT, 1));
aml_append(field, aml_reserved_field(3));
aml_append(field, aml_named_field(CPU_COMMAND, 8));
aml_append(cpu_ctrl_dev, field);
@ -419,6 +432,7 @@ void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts,
Aml *ins_evt = aml_name("%s.%s", cphp_res_path, CPU_INSERT_EVENT);
Aml *rm_evt = aml_name("%s.%s", cphp_res_path, CPU_REMOVE_EVENT);
Aml *ej_evt = aml_name("%s.%s", cphp_res_path, CPU_EJECT_EVENT);
Aml *fw_ej_evt = aml_name("%s.%s", cphp_res_path, CPU_FW_EJECT_EVENT);
aml_append(cpus_dev, aml_name_decl("_HID", aml_string("ACPI0010")));
aml_append(cpus_dev, aml_name_decl("_CID", aml_eisaid("PNP0A05")));
@ -461,7 +475,13 @@ void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts,
aml_append(method, aml_acquire(ctrl_lock, 0xFFFF));
aml_append(method, aml_store(idx, cpu_selector));
aml_append(method, aml_store(one, ej_evt));
if (opts.fw_unplugs_cpu) {
aml_append(method, aml_store(one, fw_ej_evt));
aml_append(method, aml_store(aml_int(OVMF_CPUHP_SMI_CMD),
aml_name("%s", opts.smi_path)));
} else {
aml_append(method, aml_store(one, ej_evt));
}
aml_append(method, aml_release(ctrl_lock));
}
aml_append(cpus_dev, method);

View File

@ -29,6 +29,8 @@ cpuhp_acpi_clear_inserting_evt(uint32_t idx) "idx[0x%"PRIx32"]"
cpuhp_acpi_clear_remove_evt(uint32_t idx) "idx[0x%"PRIx32"]"
cpuhp_acpi_ejecting_invalid_cpu(uint32_t idx) "0x%"PRIx32
cpuhp_acpi_ejecting_cpu(uint32_t idx) "0x%"PRIx32
cpuhp_acpi_fw_remove_invalid_cpu(uint32_t idx) "0x%"PRIx32
cpuhp_acpi_fw_remove_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

View File

@ -465,14 +465,15 @@ IOMMUMemoryRegion *smmu_iommu_mr(SMMUState *s, uint32_t sid)
/* Unmap the whole notifier's range */
static void smmu_unmap_notifier_range(IOMMUNotifier *n)
{
IOMMUTLBEntry entry;
IOMMUTLBEvent event;
entry.target_as = &address_space_memory;
entry.iova = n->start;
entry.perm = IOMMU_NONE;
entry.addr_mask = n->end - n->start;
event.type = IOMMU_NOTIFIER_UNMAP;
event.entry.target_as = &address_space_memory;
event.entry.iova = n->start;
event.entry.perm = IOMMU_NONE;
event.entry.addr_mask = n->end - n->start;
memory_region_notify_one(n, &entry);
memory_region_notify_iommu_one(n, &event);
}
/* Unmap all notifiers attached to @mr */

View File

@ -800,7 +800,7 @@ static void smmuv3_notify_iova(IOMMUMemoryRegion *mr,
uint8_t tg, uint64_t num_pages)
{
SMMUDevice *sdev = container_of(mr, SMMUDevice, iommu);
IOMMUTLBEntry entry;
IOMMUTLBEvent event;
uint8_t granule = tg;
if (!tg) {
@ -823,12 +823,13 @@ static void smmuv3_notify_iova(IOMMUMemoryRegion *mr,
granule = tt->granule_sz;
}
entry.target_as = &address_space_memory;
entry.iova = iova;
entry.addr_mask = num_pages * (1 << granule) - 1;
entry.perm = IOMMU_NONE;
event.type = IOMMU_NOTIFIER_UNMAP;
event.entry.target_as = &address_space_memory;
event.entry.iova = iova;
event.entry.addr_mask = num_pages * (1 << granule) - 1;
event.entry.perm = IOMMU_NONE;
memory_region_notify_one(n, &entry);
memory_region_notify_iommu_one(n, &event);
}
/* invalidate an asid/iova range tuple in all mr's */

View File

@ -57,6 +57,8 @@
#define ARM_SPI_BASE 32
#define ACPI_BUILD_TABLE_SIZE 0x20000
static void acpi_dsdt_add_cpus(Aml *scope, int smp_cpus)
{
uint16_t i;
@ -153,7 +155,8 @@ static void acpi_dsdt_add_virtio(Aml *scope,
}
static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap,
uint32_t irq, bool use_highmem, bool highmem_ecam)
uint32_t irq, bool use_highmem, bool highmem_ecam,
VirtMachineState *vms)
{
int ecam_id = VIRT_ECAM_ID(highmem_ecam);
struct GPEXConfig cfg = {
@ -161,6 +164,7 @@ static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap,
.pio = memmap[VIRT_PCIE_PIO],
.ecam = memmap[ecam_id],
.irq = irq,
.bus = vms->bus,
};
if (use_highmem) {
@ -609,7 +613,7 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
acpi_dsdt_add_virtio(scope, &memmap[VIRT_MMIO],
(irqmap[VIRT_MMIO] + ARM_SPI_BASE), NUM_VIRTIO_TRANSPORTS);
acpi_dsdt_add_pci(scope, memmap, (irqmap[VIRT_PCIE] + ARM_SPI_BASE),
vms->highmem, vms->highmem_ecam);
vms->highmem, vms->highmem_ecam, vms);
if (vms->acpi_dev) {
build_ged_aml(scope, "\\_SB."GED_DEVICE,
HOTPLUG_HANDLER(vms->acpi_dev),
@ -654,6 +658,15 @@ struct AcpiBuildState {
bool patched;
} AcpiBuildState;
static void acpi_align_size(GArray *blob, unsigned align)
{
/*
* Align size to multiple of given size. This reduces the chance
* we need to change size in the future (breaking cross version migration).
*/
g_array_set_size(blob, ROUND_UP(acpi_data_len(blob), align));
}
static
void virt_acpi_build(VirtMachineState *vms, AcpiBuildTables *tables)
{
@ -741,6 +754,20 @@ void virt_acpi_build(VirtMachineState *vms, AcpiBuildTables *tables)
build_rsdp(tables->rsdp, tables->linker, &rsdp_data);
}
/*
* The align size is 128, warn if 64k is not enough therefore
* the align size could be resized.
*/
if (tables_blob->len > ACPI_BUILD_TABLE_SIZE / 2) {
warn_report("ACPI table size %u exceeds %d bytes,"
" migration may not work",
tables_blob->len, ACPI_BUILD_TABLE_SIZE / 2);
error_printf("Try removing CPUs, NUMA nodes, memory slots"
" or PCI bridges.");
}
acpi_align_size(tables_blob, ACPI_BUILD_TABLE_SIZE);
/* Cleanup memory that's no longer used. */
g_array_free(table_offsets, true);
}

View File

@ -1289,7 +1289,8 @@ static void create_pcie(VirtMachineState *vms)
}
pci = PCI_HOST_BRIDGE(dev);
if (pci->bus) {
vms->bus = pci->bus;
if (vms->bus) {
for (i = 0; i < nb_nics; i++) {
NICInfo *nd = &nd_table[i];
@ -1346,7 +1347,7 @@ static void create_pcie(VirtMachineState *vms)
switch (vms->iommu) {
case VIRT_IOMMU_SMMUV3:
create_smmu(vms, pci->bus);
create_smmu(vms, vms->bus);
qemu_fdt_setprop_cells(vms->fdt, nodename, "iommu-map",
0x0, vms->iommu_phandle, 0x0, 0x10000);
break;
@ -1481,6 +1482,8 @@ void virt_machine_done(Notifier *notifier, void *data)
exit(1);
}
fw_cfg_add_extra_pci_roots(vms->bus, vms->fw_cfg);
virt_acpi_setup(vms);
virt_build_smbios(vms);
}
@ -2587,10 +2590,17 @@ static void machvirt_machine_init(void)
}
type_init(machvirt_machine_init);
static void virt_machine_5_2_options(MachineClass *mc)
static void virt_machine_6_0_options(MachineClass *mc)
{
}
DEFINE_VIRT_MACHINE_AS_LATEST(5, 2)
DEFINE_VIRT_MACHINE_AS_LATEST(6, 0)
static void virt_machine_5_2_options(MachineClass *mc)
{
virt_machine_6_0_options(mc);
compat_props_add(mc->compat_props, hw_compat_5_2, hw_compat_5_2_len);
}
DEFINE_VIRT_MACHINE(5, 2)
static void virt_machine_5_1_options(MachineClass *mc)
{

View File

@ -28,6 +28,9 @@
#include "hw/mem/nvdimm.h"
#include "migration/vmstate.h"
GlobalProperty hw_compat_5_2[] = {};
const size_t hw_compat_5_2_len = G_N_ELEMENTS(hw_compat_5_2);
GlobalProperty hw_compat_5_1[] = {
{ "vhost-scsi", "num_queues", "1"},
{ "vhost-user-blk", "num-queues", "1"},

View File

@ -214,26 +214,17 @@ void device_listener_unregister(DeviceListener *listener)
bool qdev_should_hide_device(QemuOpts *opts)
{
int rc = -1;
DeviceListener *listener;
QTAILQ_FOREACH(listener, &device_listeners, link) {
if (listener->should_be_hidden) {
/*
* should_be_hidden_will return
* 1 if device matches opts and it should be hidden
* 0 if device matches opts and should not be hidden
* -1 if device doesn't match ops
*/
rc = listener->should_be_hidden(listener, opts);
}
if (rc > 0) {
break;
if (listener->hide_device) {
if (listener->hide_device(listener, opts)) {
return true;
}
}
}
return rc > 0;
return false;
}
void qdev_set_legacy_instance_id(DeviceState *dev, int alias_id,

View File

@ -96,6 +96,7 @@ typedef struct AcpiPmInfo {
bool s4_disabled;
bool pcihp_bridge_en;
bool smi_on_cpuhp;
bool smi_on_cpu_unplug;
bool pcihp_root_en;
uint8_t s4_val;
AcpiFadtData fadt;
@ -197,6 +198,7 @@ static void acpi_get_pm_info(MachineState *machine, AcpiPmInfo *pm)
pm->pcihp_io_base = 0;
pm->pcihp_io_len = 0;
pm->smi_on_cpuhp = false;
pm->smi_on_cpu_unplug = false;
assert(obj);
init_common_fadt_data(machine, obj, &pm->fadt);
@ -220,6 +222,8 @@ static void acpi_get_pm_info(MachineState *machine, AcpiPmInfo *pm)
pm->cpu_hp_io_base = ICH9_CPU_HOTPLUG_IO_BASE;
pm->smi_on_cpuhp =
!!(smi_features & BIT_ULL(ICH9_LPC_SMI_F_CPU_HOTPLUG_BIT));
pm->smi_on_cpu_unplug =
!!(smi_features & BIT_ULL(ICH9_LPC_SMI_F_CPU_HOT_UNPLUG_BIT));
}
/* The above need not be conditional on machine type because the reset port
@ -613,299 +617,6 @@ static Aml *build_prt(bool is_pci0_prt)
return method;
}
typedef struct CrsRangeEntry {
uint64_t base;
uint64_t limit;
} CrsRangeEntry;
static void crs_range_insert(GPtrArray *ranges, uint64_t base, uint64_t limit)
{
CrsRangeEntry *entry;
entry = g_malloc(sizeof(*entry));
entry->base = base;
entry->limit = limit;
g_ptr_array_add(ranges, entry);
}
static void crs_range_free(gpointer data)
{
CrsRangeEntry *entry = (CrsRangeEntry *)data;
g_free(entry);
}
typedef struct CrsRangeSet {
GPtrArray *io_ranges;
GPtrArray *mem_ranges;
GPtrArray *mem_64bit_ranges;
} CrsRangeSet;
static void crs_range_set_init(CrsRangeSet *range_set)
{
range_set->io_ranges = g_ptr_array_new_with_free_func(crs_range_free);
range_set->mem_ranges = g_ptr_array_new_with_free_func(crs_range_free);
range_set->mem_64bit_ranges =
g_ptr_array_new_with_free_func(crs_range_free);
}
static void crs_range_set_free(CrsRangeSet *range_set)
{
g_ptr_array_free(range_set->io_ranges, true);
g_ptr_array_free(range_set->mem_ranges, true);
g_ptr_array_free(range_set->mem_64bit_ranges, true);
}
static gint crs_range_compare(gconstpointer a, gconstpointer b)
{
CrsRangeEntry *entry_a = *(CrsRangeEntry **)a;
CrsRangeEntry *entry_b = *(CrsRangeEntry **)b;
if (entry_a->base < entry_b->base) {
return -1;
} else if (entry_a->base > entry_b->base) {
return 1;
} else {
return 0;
}
}
/*
* crs_replace_with_free_ranges - given the 'used' ranges within [start - end]
* interval, computes the 'free' ranges from the same interval.
* Example: If the input array is { [a1 - a2],[b1 - b2] }, the function
* will return { [base - a1], [a2 - b1], [b2 - limit] }.
*/
static void crs_replace_with_free_ranges(GPtrArray *ranges,
uint64_t start, uint64_t end)
{
GPtrArray *free_ranges = g_ptr_array_new();
uint64_t free_base = start;
int i;
g_ptr_array_sort(ranges, crs_range_compare);
for (i = 0; i < ranges->len; i++) {
CrsRangeEntry *used = g_ptr_array_index(ranges, i);
if (free_base < used->base) {
crs_range_insert(free_ranges, free_base, used->base - 1);
}
free_base = used->limit + 1;
}
if (free_base < end) {
crs_range_insert(free_ranges, free_base, end);
}
g_ptr_array_set_size(ranges, 0);
for (i = 0; i < free_ranges->len; i++) {
g_ptr_array_add(ranges, g_ptr_array_index(free_ranges, i));
}
g_ptr_array_free(free_ranges, true);
}
/*
* crs_range_merge - merges adjacent ranges in the given array.
* Array elements are deleted and replaced with the merged ranges.
*/
static void crs_range_merge(GPtrArray *range)
{
GPtrArray *tmp = g_ptr_array_new_with_free_func(crs_range_free);
CrsRangeEntry *entry;
uint64_t range_base, range_limit;
int i;
if (!range->len) {
return;
}
g_ptr_array_sort(range, crs_range_compare);
entry = g_ptr_array_index(range, 0);
range_base = entry->base;
range_limit = entry->limit;
for (i = 1; i < range->len; i++) {
entry = g_ptr_array_index(range, i);
if (entry->base - 1 == range_limit) {
range_limit = entry->limit;
} else {
crs_range_insert(tmp, range_base, range_limit);
range_base = entry->base;
range_limit = entry->limit;
}
}
crs_range_insert(tmp, range_base, range_limit);
g_ptr_array_set_size(range, 0);
for (i = 0; i < tmp->len; i++) {
entry = g_ptr_array_index(tmp, i);
crs_range_insert(range, entry->base, entry->limit);
}
g_ptr_array_free(tmp, true);
}
static Aml *build_crs(PCIHostState *host, CrsRangeSet *range_set)
{
Aml *crs = aml_resource_template();
CrsRangeSet temp_range_set;
CrsRangeEntry *entry;
uint8_t max_bus = pci_bus_num(host->bus);
uint8_t type;
int devfn;
int i;
crs_range_set_init(&temp_range_set);
for (devfn = 0; devfn < ARRAY_SIZE(host->bus->devices); devfn++) {
uint64_t range_base, range_limit;
PCIDevice *dev = host->bus->devices[devfn];
if (!dev) {
continue;
}
for (i = 0; i < PCI_NUM_REGIONS; i++) {
PCIIORegion *r = &dev->io_regions[i];
range_base = r->addr;
range_limit = r->addr + r->size - 1;
/*
* Work-around for old bioses
* that do not support multiple root buses
*/
if (!range_base || range_base > range_limit) {
continue;
}
if (r->type & PCI_BASE_ADDRESS_SPACE_IO) {
crs_range_insert(temp_range_set.io_ranges,
range_base, range_limit);
} else { /* "memory" */
uint64_t length = range_limit - range_base + 1;
if (range_limit <= UINT32_MAX && length <= UINT32_MAX) {
crs_range_insert(temp_range_set.mem_ranges, range_base,
range_limit);
} else {
crs_range_insert(temp_range_set.mem_64bit_ranges,
range_base, range_limit);
}
}
}
type = dev->config[PCI_HEADER_TYPE] & ~PCI_HEADER_TYPE_MULTI_FUNCTION;
if (type == PCI_HEADER_TYPE_BRIDGE) {
uint8_t subordinate = dev->config[PCI_SUBORDINATE_BUS];
if (subordinate > max_bus) {
max_bus = subordinate;
}
range_base = pci_bridge_get_base(dev, PCI_BASE_ADDRESS_SPACE_IO);
range_limit = pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_SPACE_IO);
/*
* Work-around for old bioses
* that do not support multiple root buses
*/
if (range_base && range_base <= range_limit) {
crs_range_insert(temp_range_set.io_ranges,
range_base, range_limit);
}
range_base =
pci_bridge_get_base(dev, PCI_BASE_ADDRESS_SPACE_MEMORY);
range_limit =
pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_SPACE_MEMORY);
/*
* Work-around for old bioses
* that do not support multiple root buses
*/
if (range_base && range_base <= range_limit) {
uint64_t length = range_limit - range_base + 1;
if (range_limit <= UINT32_MAX && length <= UINT32_MAX) {
crs_range_insert(temp_range_set.mem_ranges,
range_base, range_limit);
} else {
crs_range_insert(temp_range_set.mem_64bit_ranges,
range_base, range_limit);
}
}
range_base =
pci_bridge_get_base(dev, PCI_BASE_ADDRESS_MEM_PREFETCH);
range_limit =
pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_MEM_PREFETCH);
/*
* Work-around for old bioses
* that do not support multiple root buses
*/
if (range_base && range_base <= range_limit) {
uint64_t length = range_limit - range_base + 1;
if (range_limit <= UINT32_MAX && length <= UINT32_MAX) {
crs_range_insert(temp_range_set.mem_ranges,
range_base, range_limit);
} else {
crs_range_insert(temp_range_set.mem_64bit_ranges,
range_base, range_limit);
}
}
}
}
crs_range_merge(temp_range_set.io_ranges);
for (i = 0; i < temp_range_set.io_ranges->len; i++) {
entry = g_ptr_array_index(temp_range_set.io_ranges, i);
aml_append(crs,
aml_word_io(AML_MIN_FIXED, AML_MAX_FIXED,
AML_POS_DECODE, AML_ENTIRE_RANGE,
0, entry->base, entry->limit, 0,
entry->limit - entry->base + 1));
crs_range_insert(range_set->io_ranges, entry->base, entry->limit);
}
crs_range_merge(temp_range_set.mem_ranges);
for (i = 0; i < temp_range_set.mem_ranges->len; i++) {
entry = g_ptr_array_index(temp_range_set.mem_ranges, i);
assert(entry->limit <= UINT32_MAX &&
(entry->limit - entry->base + 1) <= UINT32_MAX);
aml_append(crs,
aml_dword_memory(AML_POS_DECODE, AML_MIN_FIXED,
AML_MAX_FIXED, AML_NON_CACHEABLE,
AML_READ_WRITE,
0, entry->base, entry->limit, 0,
entry->limit - entry->base + 1));
crs_range_insert(range_set->mem_ranges, entry->base, entry->limit);
}
crs_range_merge(temp_range_set.mem_64bit_ranges);
for (i = 0; i < temp_range_set.mem_64bit_ranges->len; i++) {
entry = g_ptr_array_index(temp_range_set.mem_64bit_ranges, i);
aml_append(crs,
aml_qword_memory(AML_POS_DECODE, AML_MIN_FIXED,
AML_MAX_FIXED, AML_NON_CACHEABLE,
AML_READ_WRITE,
0, entry->base, entry->limit, 0,
entry->limit - entry->base + 1));
crs_range_insert(range_set->mem_64bit_ranges,
entry->base, entry->limit);
}
crs_range_set_free(&temp_range_set);
aml_append(crs,
aml_word_bus_number(AML_MIN_FIXED, AML_MAX_FIXED, AML_POS_DECODE,
0,
pci_bus_num(host->bus),
max_bus,
0,
max_bus - pci_bus_num(host->bus) + 1));
return crs;
}
static void build_hpet_aml(Aml *table)
{
Aml *crs;
@ -1582,6 +1293,7 @@ build_dsdt(GArray *table_data, BIOSLinker *linker,
CPUHotplugFeatures opts = {
.acpi_1_compatible = true, .has_legacy_cphp = true,
.smi_path = pm->smi_on_cpuhp ? "\\_SB.PCI0.SMI0.SMIC" : NULL,
.fw_unplugs_cpu = pm->smi_on_cpu_unplug,
};
build_cpus_aml(dsdt, machine, opts, pm->cpu_hp_io_base,
"\\_SB.PCI0", "\\_GPE._E02");

View File

@ -1073,7 +1073,7 @@ static int vtd_iova_to_slpte(IntelIOMMUState *s, VTDContextEntry *ce,
}
}
typedef int (*vtd_page_walk_hook)(IOMMUTLBEntry *entry, void *private);
typedef int (*vtd_page_walk_hook)(IOMMUTLBEvent *event, void *private);
/**
* Constant information used during page walking
@ -1094,11 +1094,12 @@ typedef struct {
uint16_t domain_id;
} vtd_page_walk_info;
static int vtd_page_walk_one(IOMMUTLBEntry *entry, vtd_page_walk_info *info)
static int vtd_page_walk_one(IOMMUTLBEvent *event, vtd_page_walk_info *info)
{
VTDAddressSpace *as = info->as;
vtd_page_walk_hook hook_fn = info->hook_fn;
void *private = info->private;
IOMMUTLBEntry *entry = &event->entry;
DMAMap target = {
.iova = entry->iova,
.size = entry->addr_mask,
@ -1107,7 +1108,7 @@ static int vtd_page_walk_one(IOMMUTLBEntry *entry, vtd_page_walk_info *info)
};
DMAMap *mapped = iova_tree_find(as->iova_tree, &target);
if (entry->perm == IOMMU_NONE && !info->notify_unmap) {
if (event->type == IOMMU_NOTIFIER_UNMAP && !info->notify_unmap) {
trace_vtd_page_walk_one_skip_unmap(entry->iova, entry->addr_mask);
return 0;
}
@ -1115,7 +1116,7 @@ static int vtd_page_walk_one(IOMMUTLBEntry *entry, vtd_page_walk_info *info)
assert(hook_fn);
/* Update local IOVA mapped ranges */
if (entry->perm) {
if (event->type == IOMMU_NOTIFIER_MAP) {
if (mapped) {
/* If it's exactly the same translation, skip */
if (!memcmp(mapped, &target, sizeof(target))) {
@ -1141,19 +1142,21 @@ static int vtd_page_walk_one(IOMMUTLBEntry *entry, vtd_page_walk_info *info)
int ret;
/* Emulate an UNMAP */
event->type = IOMMU_NOTIFIER_UNMAP;
entry->perm = IOMMU_NONE;
trace_vtd_page_walk_one(info->domain_id,
entry->iova,
entry->translated_addr,
entry->addr_mask,
entry->perm);
ret = hook_fn(entry, private);
ret = hook_fn(event, private);
if (ret) {
return ret;
}
/* Drop any existing mapping */
iova_tree_remove(as->iova_tree, &target);
/* Recover the correct permission */
/* Recover the correct type */
event->type = IOMMU_NOTIFIER_MAP;
entry->perm = cache_perm;
}
}
@ -1170,7 +1173,7 @@ static int vtd_page_walk_one(IOMMUTLBEntry *entry, vtd_page_walk_info *info)
trace_vtd_page_walk_one(info->domain_id, entry->iova,
entry->translated_addr, entry->addr_mask,
entry->perm);
return hook_fn(entry, private);
return hook_fn(event, private);
}
/**
@ -1191,7 +1194,7 @@ static int vtd_page_walk_level(dma_addr_t addr, uint64_t start,
uint32_t offset;
uint64_t slpte;
uint64_t subpage_size, subpage_mask;
IOMMUTLBEntry entry;
IOMMUTLBEvent event;
uint64_t iova = start;
uint64_t iova_next;
int ret = 0;
@ -1245,13 +1248,15 @@ static int vtd_page_walk_level(dma_addr_t addr, uint64_t start,
*
* In either case, we send an IOTLB notification down.
*/
entry.target_as = &address_space_memory;
entry.iova = iova & subpage_mask;
entry.perm = IOMMU_ACCESS_FLAG(read_cur, write_cur);
entry.addr_mask = ~subpage_mask;
event.entry.target_as = &address_space_memory;
event.entry.iova = iova & subpage_mask;
event.entry.perm = IOMMU_ACCESS_FLAG(read_cur, write_cur);
event.entry.addr_mask = ~subpage_mask;
/* NOTE: this is only meaningful if entry_valid == true */
entry.translated_addr = vtd_get_slpte_addr(slpte, info->aw);
ret = vtd_page_walk_one(&entry, info);
event.entry.translated_addr = vtd_get_slpte_addr(slpte, info->aw);
event.type = event.entry.perm ? IOMMU_NOTIFIER_MAP :
IOMMU_NOTIFIER_UNMAP;
ret = vtd_page_walk_one(&event, info);
}
if (ret < 0) {
@ -1430,10 +1435,10 @@ static int vtd_dev_to_context_entry(IntelIOMMUState *s, uint8_t bus_num,
return 0;
}
static int vtd_sync_shadow_page_hook(IOMMUTLBEntry *entry,
static int vtd_sync_shadow_page_hook(IOMMUTLBEvent *event,
void *private)
{
memory_region_notify_iommu((IOMMUMemoryRegion *)private, 0, *entry);
memory_region_notify_iommu(private, 0, *event);
return 0;
}
@ -1473,6 +1478,10 @@ static int vtd_sync_shadow_page_table(VTDAddressSpace *vtd_as)
VTDContextEntry ce;
IOMMUNotifier *n;
if (!(vtd_as->iommu.iommu_notify_flags & IOMMU_NOTIFIER_IOTLB_EVENTS)) {
return 0;
}
ret = vtd_dev_to_context_entry(vtd_as->iommu_state,
pci_bus_num(vtd_as->bus),
vtd_as->devfn, &ce);
@ -1993,14 +2002,17 @@ static void vtd_iotlb_page_invalidate_notify(IntelIOMMUState *s,
* page tables. We just deliver the PSI down to
* invalidate caches.
*/
IOMMUTLBEntry entry = {
.target_as = &address_space_memory,
.iova = addr,
.translated_addr = 0,
.addr_mask = size - 1,
.perm = IOMMU_NONE,
IOMMUTLBEvent event = {
.type = IOMMU_NOTIFIER_UNMAP,
.entry = {
.target_as = &address_space_memory,
.iova = addr,
.translated_addr = 0,
.addr_mask = size - 1,
.perm = IOMMU_NONE,
},
};
memory_region_notify_iommu(&vtd_as->iommu, 0, entry);
memory_region_notify_iommu(&vtd_as->iommu, 0, event);
}
}
}
@ -2412,7 +2424,7 @@ static bool vtd_process_device_iotlb_desc(IntelIOMMUState *s,
VTDInvDesc *inv_desc)
{
VTDAddressSpace *vtd_dev_as;
IOMMUTLBEntry entry;
IOMMUTLBEvent event;
struct VTDBus *vtd_bus;
hwaddr addr;
uint64_t sz;
@ -2460,12 +2472,13 @@ static bool vtd_process_device_iotlb_desc(IntelIOMMUState *s,
sz = VTD_PAGE_SIZE;
}
entry.target_as = &vtd_dev_as->as;
entry.addr_mask = sz - 1;
entry.iova = addr;
entry.perm = IOMMU_NONE;
entry.translated_addr = 0;
memory_region_notify_iommu(&vtd_dev_as->iommu, 0, entry);
event.type = IOMMU_NOTIFIER_DEVIOTLB_UNMAP;
event.entry.target_as = &vtd_dev_as->as;
event.entry.addr_mask = sz - 1;
event.entry.iova = addr;
event.entry.perm = IOMMU_NONE;
event.entry.translated_addr = 0;
memory_region_notify_iommu(&vtd_dev_as->iommu, 0, event);
done:
return true;
@ -3485,19 +3498,20 @@ static void vtd_address_space_unmap(VTDAddressSpace *as, IOMMUNotifier *n)
size = remain = end - start + 1;
while (remain >= VTD_PAGE_SIZE) {
IOMMUTLBEntry entry;
IOMMUTLBEvent event;
uint64_t mask = get_naturally_aligned_size(start, remain, s->aw_bits);
assert(mask);
entry.iova = start;
entry.addr_mask = mask - 1;
entry.target_as = &address_space_memory;
entry.perm = IOMMU_NONE;
event.type = IOMMU_NOTIFIER_UNMAP;
event.entry.iova = start;
event.entry.addr_mask = mask - 1;
event.entry.target_as = &address_space_memory;
event.entry.perm = IOMMU_NONE;
/* This field is meaningless for unmap */
entry.translated_addr = 0;
event.entry.translated_addr = 0;
memory_region_notify_one(n, &entry);
memory_region_notify_iommu_one(n, &event);
start += mask;
remain -= mask;
@ -3533,9 +3547,9 @@ static void vtd_address_space_refresh_all(IntelIOMMUState *s)
vtd_switch_address_space_all(s);
}
static int vtd_replay_hook(IOMMUTLBEntry *entry, void *private)
static int vtd_replay_hook(IOMMUTLBEvent *event, void *private)
{
memory_region_notify_one((IOMMUNotifier *)private, entry);
memory_region_notify_iommu_one(private, event);
return 0;
}

View File

@ -97,6 +97,11 @@
#include "trace.h"
#include CONFIG_DEVICES
GlobalProperty pc_compat_5_2[] = {
{ "ICH9-LPC", "x-smi-cpu-hotunplug", "off" },
};
const size_t pc_compat_5_2_len = G_N_ELEMENTS(pc_compat_5_2);
GlobalProperty pc_compat_5_1[] = {
{ "ICH9-LPC", "x-smi-cpu-hotplug", "off" },
};
@ -777,27 +782,11 @@ void pc_machine_done(Notifier *notifier, void *data)
PCMachineState *pcms = container_of(notifier,
PCMachineState, machine_done);
X86MachineState *x86ms = X86_MACHINE(pcms);
PCIBus *bus = pcms->bus;
/* set the number of CPUs */
x86_rtc_set_cpus_count(x86ms->rtc, x86ms->boot_cpus);
if (bus) {
int extra_hosts = 0;
QLIST_FOREACH(bus, &bus->child, sibling) {
/* look for expander root buses */
if (pci_bus_is_root(bus)) {
extra_hosts++;
}
}
if (extra_hosts && x86ms->fw_cfg) {
uint64_t *val = g_malloc(sizeof(*val));
*val = cpu_to_le64(extra_hosts);
fw_cfg_add_file(x86ms->fw_cfg,
"etc/extra-pci-roots", val, sizeof(*val));
}
}
fw_cfg_add_extra_pci_roots(pcms->bus, x86ms->fw_cfg);
acpi_setup();
if (x86ms->fw_cfg) {
@ -1582,6 +1571,50 @@ static void pc_machine_set_max_ram_below_4g(Object *obj, Visitor *v,
pcms->max_ram_below_4g = value;
}
static void pc_machine_get_max_fw_size(Object *obj, Visitor *v,
const char *name, void *opaque,
Error **errp)
{
PCMachineState *pcms = PC_MACHINE(obj);
uint64_t value = pcms->max_fw_size;
visit_type_size(v, name, &value, errp);
}
static void pc_machine_set_max_fw_size(Object *obj, Visitor *v,
const char *name, void *opaque,
Error **errp)
{
PCMachineState *pcms = PC_MACHINE(obj);
Error *error = NULL;
uint64_t value;
visit_type_size(v, name, &value, &error);
if (error) {
error_propagate(errp, error);
return;
}
/*
* We don't have a theoretically justifiable exact lower bound on the base
* address of any flash mapping. In practice, the IO-APIC MMIO range is
* [0xFEE00000..0xFEE01000] -- see IO_APIC_DEFAULT_ADDRESS --, leaving free
* only 18MB-4KB below 4G. For now, restrict the cumulative mapping to 8MB in
* size.
*/
if (value > 16 * MiB) {
error_setg(errp,
"User specified max allowed firmware size %" PRIu64 " is "
"greater than 16MiB. If combined firwmare size exceeds "
"16MiB the system may not boot, or experience intermittent"
"stability issues.",
value);
return;
}
pcms->max_fw_size = value;
}
static void pc_machine_initfn(Object *obj)
{
PCMachineState *pcms = PC_MACHINE(obj);
@ -1597,6 +1630,7 @@ static void pc_machine_initfn(Object *obj)
pcms->smbus_enabled = true;
pcms->sata_enabled = true;
pcms->pit_enabled = true;
pcms->max_fw_size = 8 * MiB;
#ifdef CONFIG_HPET
pcms->hpet_enabled = true;
#endif
@ -1723,6 +1757,12 @@ static void pc_machine_class_init(ObjectClass *oc, void *data)
object_class_property_add_bool(oc, "hpet",
pc_machine_get_hpet, pc_machine_set_hpet);
object_class_property_add(oc, PC_MACHINE_MAX_FW_SIZE, "size",
pc_machine_get_max_fw_size, pc_machine_set_max_fw_size,
NULL, NULL);
object_class_property_set_description(oc, PC_MACHINE_MAX_FW_SIZE,
"Maximum combined firmware size");
}
static const TypeInfo pc_machine_info = {

View File

@ -426,7 +426,7 @@ static void pc_i440fx_machine_options(MachineClass *m)
machine_class_allow_dynamic_sysbus_dev(m, TYPE_VMBUS_BRIDGE);
}
static void pc_i440fx_5_2_machine_options(MachineClass *m)
static void pc_i440fx_6_0_machine_options(MachineClass *m)
{
PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
pc_i440fx_machine_options(m);
@ -435,6 +435,18 @@ static void pc_i440fx_5_2_machine_options(MachineClass *m)
pcmc->default_cpu_version = 1;
}
DEFINE_I440FX_MACHINE(v6_0, "pc-i440fx-6.0", NULL,
pc_i440fx_6_0_machine_options);
static void pc_i440fx_5_2_machine_options(MachineClass *m)
{
pc_i440fx_6_0_machine_options(m);
m->alias = NULL;
m->is_default = false;
compat_props_add(m->compat_props, hw_compat_5_2, hw_compat_5_2_len);
compat_props_add(m->compat_props, pc_compat_5_2, pc_compat_5_2_len);
}
DEFINE_I440FX_MACHINE(v5_2, "pc-i440fx-5.2", NULL,
pc_i440fx_5_2_machine_options);

View File

@ -344,7 +344,7 @@ static void pc_q35_machine_options(MachineClass *m)
m->max_cpus = 288;
}
static void pc_q35_5_2_machine_options(MachineClass *m)
static void pc_q35_6_0_machine_options(MachineClass *m)
{
PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
pc_q35_machine_options(m);
@ -352,6 +352,17 @@ static void pc_q35_5_2_machine_options(MachineClass *m)
pcmc->default_cpu_version = 1;
}
DEFINE_Q35_MACHINE(v6_0, "pc-q35-6.0", NULL,
pc_q35_6_0_machine_options);
static void pc_q35_5_2_machine_options(MachineClass *m)
{
pc_q35_6_0_machine_options(m);
m->alias = NULL;
compat_props_add(m->compat_props, hw_compat_5_2, hw_compat_5_2_len);
compat_props_add(m->compat_props, pc_compat_5_2, pc_compat_5_2_len);
}
DEFINE_Q35_MACHINE(v5_2, "pc-q35-5.2", NULL,
pc_q35_5_2_machine_options);

View File

@ -39,15 +39,6 @@
#include "hw/block/flash.h"
#include "sysemu/kvm.h"
/*
* We don't have a theoretically justifiable exact lower bound on the base
* address of any flash mapping. In practice, the IO-APIC MMIO range is
* [0xFEE00000..0xFEE01000] -- see IO_APIC_DEFAULT_ADDRESS --, leaving free
* only 18MB-4KB below 4G. For now, restrict the cumulative mapping to 8MB in
* size.
*/
#define FLASH_SIZE_LIMIT (8 * MiB)
#define FLASH_SECTOR_SIZE 4096
static void pc_isa_bios_init(MemoryRegion *rom_memory,
@ -140,7 +131,7 @@ void pc_system_flash_cleanup_unused(PCMachineState *pcms)
* Stop at the first pcms->flash[0] lacking a block backend.
* Set each flash's size from its block backend. Fatal error if the
* size isn't a non-zero multiple of 4KiB, or the total size exceeds
* FLASH_SIZE_LIMIT.
* pcms->max_fw_size.
*
* If pcms->flash[0] has a block backend, its memory is passed to
* pc_isa_bios_init(). Merging several flash devices for isa-bios is
@ -182,10 +173,10 @@ static void pc_system_flash_map(PCMachineState *pcms,
}
if ((hwaddr)size != size
|| total_size > HWADDR_MAX - size
|| total_size + size > FLASH_SIZE_LIMIT) {
|| total_size + size > pcms->max_fw_size) {
error_report("combined size of system firmware exceeds "
"%" PRIu64 " bytes",
FLASH_SIZE_LIMIT);
pcms->max_fw_size);
exit(1);
}

View File

@ -366,6 +366,7 @@ static void smi_features_ok_callback(void *opaque)
{
ICH9LPCState *lpc = opaque;
uint64_t guest_features;
uint64_t guest_cpu_hotplug_features;
if (lpc->smi_features_ok) {
/* negotiation already complete, features locked */
@ -378,9 +379,12 @@ static void smi_features_ok_callback(void *opaque)
/* guest requests invalid features, leave @features_ok at zero */
return;
}
guest_cpu_hotplug_features = guest_features &
(BIT_ULL(ICH9_LPC_SMI_F_CPU_HOTPLUG_BIT) |
BIT_ULL(ICH9_LPC_SMI_F_CPU_HOT_UNPLUG_BIT));
if (!(guest_features & BIT_ULL(ICH9_LPC_SMI_F_BROADCAST_BIT)) &&
guest_features & (BIT_ULL(ICH9_LPC_SMI_F_CPU_HOTPLUG_BIT) |
BIT_ULL(ICH9_LPC_SMI_F_CPU_HOT_UNPLUG_BIT))) {
guest_cpu_hotplug_features) {
/*
* cpu hot-[un]plug with SMI requires SMI broadcast,
* leave @features_ok at zero
@ -388,6 +392,12 @@ static void smi_features_ok_callback(void *opaque)
return;
}
if (guest_cpu_hotplug_features ==
BIT_ULL(ICH9_LPC_SMI_F_CPU_HOT_UNPLUG_BIT)) {
/* cpu hot-unplug is unsupported without cpu-hotplug */
return;
}
/* valid feature subset requested, lock it down, report success */
lpc->smi_negotiated_features = guest_features;
lpc->smi_features_ok = 1;
@ -770,7 +780,7 @@ static Property ich9_lpc_properties[] = {
DEFINE_PROP_BIT64("x-smi-cpu-hotplug", ICH9LPCState, smi_host_features,
ICH9_LPC_SMI_F_CPU_HOTPLUG_BIT, true),
DEFINE_PROP_BIT64("x-smi-cpu-hotunplug", ICH9LPCState, smi_host_features,
ICH9_LPC_SMI_F_CPU_HOT_UNPLUG_BIT, false),
ICH9_LPC_SMI_F_CPU_HOT_UNPLUG_BIT, true),
DEFINE_PROP_END_OF_LIST(),
};

View File

@ -82,8 +82,10 @@ static void tz_mpc_iommu_notify(TZMPC *s, uint32_t lutidx,
/* Called when the LUT word at lutidx has changed from oldlut to newlut;
* must call the IOMMU notifiers for the changed blocks.
*/
IOMMUTLBEntry entry = {
.addr_mask = s->blocksize - 1,
IOMMUTLBEvent event = {
.entry = {
.addr_mask = s->blocksize - 1,
}
};
hwaddr addr = lutidx * s->blocksize * 32;
int i;
@ -100,26 +102,28 @@ static void tz_mpc_iommu_notify(TZMPC *s, uint32_t lutidx,
block_is_ns = newlut & (1 << i);
trace_tz_mpc_iommu_notify(addr);
entry.iova = addr;
entry.translated_addr = addr;
event.entry.iova = addr;
event.entry.translated_addr = addr;
entry.perm = IOMMU_NONE;
memory_region_notify_iommu(&s->upstream, IOMMU_IDX_S, entry);
memory_region_notify_iommu(&s->upstream, IOMMU_IDX_NS, entry);
event.type = IOMMU_NOTIFIER_UNMAP;
event.entry.perm = IOMMU_NONE;
memory_region_notify_iommu(&s->upstream, IOMMU_IDX_S, event);
memory_region_notify_iommu(&s->upstream, IOMMU_IDX_NS, event);
entry.perm = IOMMU_RW;
event.type = IOMMU_NOTIFIER_MAP;
event.entry.perm = IOMMU_RW;
if (block_is_ns) {
entry.target_as = &s->blocked_io_as;
event.entry.target_as = &s->blocked_io_as;
} else {
entry.target_as = &s->downstream_as;
event.entry.target_as = &s->downstream_as;
}
memory_region_notify_iommu(&s->upstream, IOMMU_IDX_S, entry);
memory_region_notify_iommu(&s->upstream, IOMMU_IDX_S, event);
if (block_is_ns) {
entry.target_as = &s->downstream_as;
event.entry.target_as = &s->downstream_as;
} else {
entry.target_as = &s->blocked_io_as;
event.entry.target_as = &s->blocked_io_as;
}
memory_region_notify_iommu(&s->upstream, IOMMU_IDX_NS, entry);
memory_region_notify_iommu(&s->upstream, IOMMU_IDX_NS, event);
}
}

View File

@ -788,95 +788,97 @@ static inline uint64_t virtio_net_supported_guest_offloads(VirtIONet *n)
return virtio_net_guest_offloads_by_features(vdev->guest_features);
}
typedef struct {
VirtIONet *n;
char *id;
} FailoverId;
/**
* Set the id of the failover primary device
*
* @opaque: FailoverId to setup
* @opts: opts for device we are handling
* @errp: returns an error if this function fails
*/
static int failover_set_primary(void *opaque, QemuOpts *opts, Error **errp)
{
FailoverId *fid = opaque;
const char *standby_id = qemu_opt_get(opts, "failover_pair_id");
if (g_strcmp0(standby_id, fid->n->netclient_name) == 0) {
fid->id = g_strdup(opts->id);
return 1;
}
return 0;
}
/**
* Find the primary device id for this failover virtio-net
*
* @n: VirtIONet device
* @errp: returns an error if this function fails
*/
static char *failover_find_primary_device_id(VirtIONet *n)
{
Error *err = NULL;
FailoverId fid;
fid.n = n;
if (!qemu_opts_foreach(qemu_find_opts("device"),
failover_set_primary, &fid, &err)) {
return NULL;
}
return fid.id;
}
/**
* Find the primary device for this failover virtio-net
*
* @n: VirtIONet device
* @errp: returns an error if this function fails
*/
static DeviceState *failover_find_primary_device(VirtIONet *n)
{
char *id = failover_find_primary_device_id(n);
if (!id) {
return NULL;
}
return qdev_find_recursive(sysbus_get_default(), id);
}
static void failover_add_primary(VirtIONet *n, Error **errp)
{
Error *err = NULL;
QemuOpts *opts;
char *id;
DeviceState *dev = failover_find_primary_device(n);
if (n->primary_dev) {
if (dev) {
return;
}
n->primary_device_opts = qemu_opts_find(qemu_find_opts("device"),
n->primary_device_id);
if (n->primary_device_opts) {
n->primary_dev = qdev_device_add(n->primary_device_opts, &err);
id = failover_find_primary_device_id(n);
if (!id) {
return;
}
opts = qemu_opts_find(qemu_find_opts("device"), id);
if (opts) {
dev = qdev_device_add(opts, &err);
if (err) {
qemu_opts_del(n->primary_device_opts);
}
if (n->primary_dev) {
n->primary_bus = n->primary_dev->parent_bus;
if (err) {
qdev_unplug(n->primary_dev, &err);
qdev_set_id(n->primary_dev, "");
}
qemu_opts_del(opts);
}
} else {
error_setg(errp, "Primary device not found");
error_append_hint(errp, "Virtio-net failover will not work. Make "
"sure primary device has parameter"
" failover_pair_id=<virtio-net-id>\n");
}
"sure primary device has parameter"
" failover_pair_id=<virtio-net-id>\n");
}
error_propagate(errp, err);
}
static int is_my_primary(void *opaque, QemuOpts *opts, Error **errp)
{
VirtIONet *n = opaque;
int ret = 0;
const char *standby_id = qemu_opt_get(opts, "failover_pair_id");
if (standby_id != NULL && (g_strcmp0(standby_id, n->netclient_name) == 0)) {
n->primary_device_id = g_strdup(opts->id);
ret = 1;
}
return ret;
}
static DeviceState *virtio_net_find_primary(VirtIONet *n, Error **errp)
{
DeviceState *dev = NULL;
Error *err = NULL;
if (qemu_opts_foreach(qemu_find_opts("device"),
is_my_primary, n, &err)) {
if (err) {
error_propagate(errp, err);
return NULL;
}
if (n->primary_device_id) {
dev = qdev_find_recursive(sysbus_get_default(),
n->primary_device_id);
} else {
error_setg(errp, "Primary device id not found");
return NULL;
}
}
return dev;
}
static DeviceState *virtio_connect_failover_devices(VirtIONet *n,
DeviceState *dev,
Error **errp)
{
DeviceState *prim_dev = NULL;
Error *err = NULL;
prim_dev = virtio_net_find_primary(n, &err);
if (prim_dev) {
n->primary_device_id = g_strdup(prim_dev->id);
n->primary_device_opts = prim_dev->opts;
} else {
error_propagate(errp, err);
}
return prim_dev;
}
static void virtio_net_set_features(VirtIODevice *vdev, uint64_t features)
{
VirtIONet *n = VIRTIO_NET(vdev);
@ -929,25 +931,12 @@ static void virtio_net_set_features(VirtIODevice *vdev, uint64_t features)
if (virtio_has_feature(features, VIRTIO_NET_F_STANDBY)) {
qapi_event_send_failover_negotiated(n->netclient_name);
qatomic_set(&n->primary_should_be_hidden, false);
qatomic_set(&n->failover_primary_hidden, false);
failover_add_primary(n, &err);
if (err) {
n->primary_dev = virtio_connect_failover_devices(n, n->qdev, &err);
if (err) {
goto out_err;
}
failover_add_primary(n, &err);
if (err) {
goto out_err;
}
warn_report_err(err);
}
}
return;
out_err:
if (err) {
warn_report_err(err);
}
}
static int virtio_net_handle_rx_mode(VirtIONet *n, uint8_t cmd,
@ -3095,17 +3084,17 @@ void virtio_net_set_netclient_name(VirtIONet *n, const char *name,
n->netclient_type = g_strdup(type);
}
static bool failover_unplug_primary(VirtIONet *n)
static bool failover_unplug_primary(VirtIONet *n, DeviceState *dev)
{
HotplugHandler *hotplug_ctrl;
PCIDevice *pci_dev;
Error *err = NULL;
hotplug_ctrl = qdev_get_hotplug_handler(n->primary_dev);
hotplug_ctrl = qdev_get_hotplug_handler(dev);
if (hotplug_ctrl) {
pci_dev = PCI_DEVICE(n->primary_dev);
pci_dev = PCI_DEVICE(dev);
pci_dev->partially_hotplugged = true;
hotplug_handler_unplug_request(hotplug_ctrl, n->primary_dev, &err);
hotplug_handler_unplug_request(hotplug_ctrl, dev, &err);
if (err) {
error_report_err(err);
return false;
@ -3116,41 +3105,31 @@ static bool failover_unplug_primary(VirtIONet *n)
return true;
}
static bool failover_replug_primary(VirtIONet *n, Error **errp)
static bool failover_replug_primary(VirtIONet *n, DeviceState *dev,
Error **errp)
{
Error *err = NULL;
HotplugHandler *hotplug_ctrl;
PCIDevice *pdev = PCI_DEVICE(n->primary_dev);
PCIDevice *pdev = PCI_DEVICE(dev);
BusState *primary_bus;
if (!pdev->partially_hotplugged) {
return true;
}
if (!n->primary_device_opts) {
n->primary_device_opts = qemu_opts_from_qdict(
qemu_find_opts("device"),
n->primary_device_dict, errp);
if (!n->primary_device_opts) {
return false;
}
}
n->primary_bus = n->primary_dev->parent_bus;
if (!n->primary_bus) {
primary_bus = dev->parent_bus;
if (!primary_bus) {
error_setg(errp, "virtio_net: couldn't find primary bus");
return false;
}
qdev_set_parent_bus(n->primary_dev, n->primary_bus, &error_abort);
n->primary_should_be_hidden = false;
if (!qemu_opt_set_bool(n->primary_device_opts,
"partially_hotplugged", true, errp)) {
return false;
}
hotplug_ctrl = qdev_get_hotplug_handler(n->primary_dev);
qdev_set_parent_bus(dev, primary_bus, &error_abort);
qatomic_set(&n->failover_primary_hidden, false);
hotplug_ctrl = qdev_get_hotplug_handler(dev);
if (hotplug_ctrl) {
hotplug_handler_pre_plug(hotplug_ctrl, n->primary_dev, &err);
hotplug_handler_pre_plug(hotplug_ctrl, dev, &err);
if (err) {
goto out;
}
hotplug_handler_plug(hotplug_ctrl, n->primary_dev, &err);
hotplug_handler_plug(hotplug_ctrl, dev, &err);
}
out:
@ -3158,34 +3137,29 @@ out:
return !err;
}
static void virtio_net_handle_migration_primary(VirtIONet *n,
MigrationState *s)
static void virtio_net_handle_migration_primary(VirtIONet *n, MigrationState *s)
{
bool should_be_hidden;
Error *err = NULL;
DeviceState *dev = failover_find_primary_device(n);
should_be_hidden = qatomic_read(&n->primary_should_be_hidden);
if (!n->primary_dev) {
n->primary_dev = virtio_connect_failover_devices(n, n->qdev, &err);
if (!n->primary_dev) {
return;
}
if (!dev) {
return;
}
should_be_hidden = qatomic_read(&n->failover_primary_hidden);
if (migration_in_setup(s) && !should_be_hidden) {
if (failover_unplug_primary(n)) {
vmstate_unregister(VMSTATE_IF(n->primary_dev),
qdev_get_vmsd(n->primary_dev),
n->primary_dev);
qapi_event_send_unplug_primary(n->primary_device_id);
qatomic_set(&n->primary_should_be_hidden, true);
if (failover_unplug_primary(n, dev)) {
vmstate_unregister(VMSTATE_IF(dev), qdev_get_vmsd(dev), dev);
qapi_event_send_unplug_primary(dev->id);
qatomic_set(&n->failover_primary_hidden, true);
} else {
warn_report("couldn't unplug primary device");
}
} else if (migration_has_failed(s)) {
/* We already unplugged the device let's plug it back */
if (!failover_replug_primary(n, &err)) {
if (!failover_replug_primary(n, dev, &err)) {
if (err) {
error_report_err(err);
}
@ -3200,55 +3174,22 @@ static void virtio_net_migration_state_notifier(Notifier *notifier, void *data)
virtio_net_handle_migration_primary(n, s);
}
static int virtio_net_primary_should_be_hidden(DeviceListener *listener,
QemuOpts *device_opts)
static bool failover_hide_primary_device(DeviceListener *listener,
QemuOpts *device_opts)
{
VirtIONet *n = container_of(listener, VirtIONet, primary_listener);
bool match_found = false;
bool hide = false;
const char *standby_id;
if (!device_opts) {
return -1;
return false;
}
n->primary_device_dict = qemu_opts_to_qdict(device_opts,
n->primary_device_dict);
if (n->primary_device_dict) {
g_free(n->standby_id);
n->standby_id = g_strdup(qdict_get_try_str(n->primary_device_dict,
"failover_pair_id"));
}
if (g_strcmp0(n->standby_id, n->netclient_name) == 0) {
match_found = true;
} else {
match_found = false;
hide = false;
g_free(n->standby_id);
n->primary_device_dict = NULL;
goto out;
standby_id = qemu_opt_get(device_opts, "failover_pair_id");
if (g_strcmp0(standby_id, n->netclient_name) != 0) {
return false;
}
n->primary_device_opts = device_opts;
/* primary_should_be_hidden is set during feature negotiation */
hide = qatomic_read(&n->primary_should_be_hidden);
if (n->primary_device_dict) {
g_free(n->primary_device_id);
n->primary_device_id = g_strdup(qdict_get_try_str(
n->primary_device_dict, "id"));
if (!n->primary_device_id) {
warn_report("primary_device_id not set");
}
}
out:
if (match_found && hide) {
return 1;
} else if (match_found && !hide) {
return 0;
} else {
return -1;
}
/* failover_primary_hidden is set during feature negotiation */
return qatomic_read(&n->failover_primary_hidden);
}
static void virtio_net_device_realize(DeviceState *dev, Error **errp)
@ -3285,9 +3226,8 @@ static void virtio_net_device_realize(DeviceState *dev, Error **errp)
}
if (n->failover) {
n->primary_listener.should_be_hidden =
virtio_net_primary_should_be_hidden;
qatomic_set(&n->primary_should_be_hidden, true);
n->primary_listener.hide_device = failover_hide_primary_device;
qatomic_set(&n->failover_primary_hidden, true);
device_listener_register(&n->primary_listener);
n->migration_state.notify = virtio_net_migration_state_notifier;
add_migration_state_change_notifier(&n->migration_state);
@ -3426,10 +3366,6 @@ static void virtio_net_device_unrealize(DeviceState *dev)
if (n->failover) {
device_listener_unregister(&n->primary_listener);
g_free(n->primary_device_id);
g_free(n->standby_id);
qobject_unref(n->primary_device_dict);
n->primary_device_dict = NULL;
}
max_queues = n->multiqueue ? n->max_queues : 1;
@ -3475,13 +3411,15 @@ static int virtio_net_pre_save(void *opaque)
static bool primary_unplug_pending(void *opaque)
{
DeviceState *dev = opaque;
DeviceState *primary;
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
VirtIONet *n = VIRTIO_NET(vdev);
if (!virtio_vdev_has_feature(vdev, VIRTIO_NET_F_STANDBY)) {
return false;
}
return n->primary_dev ? n->primary_dev->pending_deleted_event : false;
primary = failover_find_primary_device(n);
return primary ? primary->pending_deleted_event : false;
}
static bool dev_unplug_pending(void *opaque)

View File

@ -40,6 +40,7 @@
#include "qemu/cutils.h"
#include "qapi/error.h"
#include "hw/acpi/aml-build.h"
#include "hw/pci/pci_bus.h"
#define FW_CFG_FILE_SLOTS_DFLT 0x20
@ -1061,6 +1062,28 @@ bool fw_cfg_add_from_generator(FWCfgState *s, const char *filename,
return true;
}
void fw_cfg_add_extra_pci_roots(PCIBus *bus, FWCfgState *s)
{
int extra_hosts = 0;
if (!bus) {
return;
}
QLIST_FOREACH(bus, &bus->child, sibling) {
/* look for expander root buses */
if (pci_bus_is_root(bus)) {
extra_hosts++;
}
}
if (extra_hosts && s) {
uint64_t *val = g_malloc(sizeof(*val));
*val = cpu_to_le64(extra_hosts);
fw_cfg_add_file(s, "etc/extra-pci-roots", val, sizeof(*val));
}
}
static void fw_cfg_machine_reset(void *opaque)
{
MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine());

View File

@ -1,22 +1,16 @@
#include "qemu/osdep.h"
#include "hw/acpi/aml-build.h"
#include "hw/pci-host/gpex.h"
#include "hw/arm/virt.h"
#include "hw/pci/pci_bus.h"
#include "hw/pci/pci_bridge.h"
#include "hw/pci/pcie_host.h"
void acpi_dsdt_add_gpex(Aml *scope, struct GPEXConfig *cfg)
static void acpi_dsdt_add_pci_route_table(Aml *dev, uint32_t irq)
{
int nr_pcie_buses = cfg->ecam.size / PCIE_MMCFG_SIZE_MIN;
Aml *method, *crs, *ifctx, *UUID, *ifctx1, *elsectx, *buf;
Aml *method, *crs;
int i, slot_no;
Aml *dev = aml_device("%s", "PCI0");
aml_append(dev, aml_name_decl("_HID", aml_string("PNP0A08")));
aml_append(dev, aml_name_decl("_CID", aml_string("PNP0A03")));
aml_append(dev, aml_name_decl("_SEG", aml_int(0)));
aml_append(dev, aml_name_decl("_BBN", aml_int(0)));
aml_append(dev, aml_name_decl("_UID", aml_int(0)));
aml_append(dev, aml_name_decl("_STR", aml_unicode("PCIe 0 Device")));
aml_append(dev, aml_name_decl("_CCA", aml_int(1)));
/* Declare the PCI Routing Table. */
Aml *rt_pkg = aml_varpackage(PCI_SLOT_MAX * PCI_NUM_PINS);
for (slot_no = 0; slot_no < PCI_SLOT_MAX; slot_no++) {
@ -34,7 +28,7 @@ void acpi_dsdt_add_gpex(Aml *scope, struct GPEXConfig *cfg)
/* Create GSI link device */
for (i = 0; i < PCI_NUM_PINS; i++) {
uint32_t irqs = cfg->irq + i;
uint32_t irqs = irq + i;
Aml *dev_gsi = aml_device("GSI%d", i);
aml_append(dev_gsi, aml_name_decl("_HID", aml_string("PNP0C0F")));
aml_append(dev_gsi, aml_name_decl("_UID", aml_int(i)));
@ -52,43 +46,11 @@ void acpi_dsdt_add_gpex(Aml *scope, struct GPEXConfig *cfg)
aml_append(dev_gsi, method);
aml_append(dev, dev_gsi);
}
}
method = aml_method("_CBA", 0, AML_NOTSERIALIZED);
aml_append(method, aml_return(aml_int(cfg->ecam.base)));
aml_append(dev, method);
Aml *rbuf = aml_resource_template();
aml_append(rbuf,
aml_word_bus_number(AML_MIN_FIXED, AML_MAX_FIXED, AML_POS_DECODE,
0x0000, 0x0000, nr_pcie_buses - 1, 0x0000,
nr_pcie_buses));
if (cfg->mmio32.size) {
aml_append(rbuf,
aml_dword_memory(AML_POS_DECODE, AML_MIN_FIXED, AML_MAX_FIXED,
AML_NON_CACHEABLE, AML_READ_WRITE, 0x0000,
cfg->mmio32.base,
cfg->mmio32.base + cfg->mmio32.size - 1,
0x0000,
cfg->mmio32.size));
}
if (cfg->pio.size) {
aml_append(rbuf,
aml_dword_io(AML_MIN_FIXED, AML_MAX_FIXED, AML_POS_DECODE,
AML_ENTIRE_RANGE, 0x0000, 0x0000,
cfg->pio.size - 1,
cfg->pio.base,
cfg->pio.size));
}
if (cfg->mmio64.size) {
aml_append(rbuf,
aml_qword_memory(AML_POS_DECODE, AML_MIN_FIXED, AML_MAX_FIXED,
AML_NON_CACHEABLE, AML_READ_WRITE, 0x0000,
cfg->mmio64.base,
cfg->mmio64.base + cfg->mmio64.size - 1,
0x0000,
cfg->mmio64.size));
}
aml_append(dev, aml_name_decl("_CRS", rbuf));
static void acpi_dsdt_add_pci_osc(Aml *dev)
{
Aml *method, *UUID, *ifctx, *ifctx1, *elsectx, *buf;
/* Declare an _OSC (OS Control Handoff) method */
aml_append(dev, aml_name_decl("SUPP", aml_int(0)));
@ -160,6 +122,112 @@ void acpi_dsdt_add_gpex(Aml *scope, struct GPEXConfig *cfg)
buf = aml_buffer(1, byte_list);
aml_append(method, aml_return(buf));
aml_append(dev, method);
}
void acpi_dsdt_add_gpex(Aml *scope, struct GPEXConfig *cfg)
{
int nr_pcie_buses = cfg->ecam.size / PCIE_MMCFG_SIZE_MIN;
Aml *method, *crs, *dev, *rbuf;
PCIBus *bus = cfg->bus;
CrsRangeSet crs_range_set;
/* start to construct the tables for pxb */
crs_range_set_init(&crs_range_set);
if (bus) {
QLIST_FOREACH(bus, &bus->child, sibling) {
uint8_t bus_num = pci_bus_num(bus);
uint8_t numa_node = pci_bus_numa_node(bus);
if (!pci_bus_is_root(bus)) {
continue;
}
/*
* 0 - (nr_pcie_buses - 1) is the bus range for the main
* host-bridge and it equals the MIN of the
* busNr defined for pxb-pcie.
*/
if (bus_num < nr_pcie_buses) {
nr_pcie_buses = bus_num;
}
dev = aml_device("PC%.02X", bus_num);
aml_append(dev, aml_name_decl("_HID", aml_string("PNP0A08")));
aml_append(dev, aml_name_decl("_CID", aml_string("PNP0A03")));
aml_append(dev, aml_name_decl("_BBN", aml_int(bus_num)));
aml_append(dev, aml_name_decl("_UID", aml_int(bus_num)));
aml_append(dev, aml_name_decl("_STR", aml_unicode("pxb Device")));
if (numa_node != NUMA_NODE_UNASSIGNED) {
aml_append(dev, aml_name_decl("_PXM", aml_int(numa_node)));
}
acpi_dsdt_add_pci_route_table(dev, cfg->irq);
/*
* Resources defined for PXBs are composed by the folling parts:
* 1. The resources the pci-brige/pcie-root-port need.
* 2. The resources the devices behind pxb need.
*/
crs = build_crs(PCI_HOST_BRIDGE(BUS(bus)->parent), &crs_range_set);
aml_append(dev, aml_name_decl("_CRS", crs));
acpi_dsdt_add_pci_osc(dev);
aml_append(scope, dev);
}
}
crs_range_set_free(&crs_range_set);
/* tables for the main */
dev = aml_device("%s", "PCI0");
aml_append(dev, aml_name_decl("_HID", aml_string("PNP0A08")));
aml_append(dev, aml_name_decl("_CID", aml_string("PNP0A03")));
aml_append(dev, aml_name_decl("_SEG", aml_int(0)));
aml_append(dev, aml_name_decl("_BBN", aml_int(0)));
aml_append(dev, aml_name_decl("_UID", aml_int(0)));
aml_append(dev, aml_name_decl("_STR", aml_unicode("PCIe 0 Device")));
aml_append(dev, aml_name_decl("_CCA", aml_int(1)));
acpi_dsdt_add_pci_route_table(dev, cfg->irq);
method = aml_method("_CBA", 0, AML_NOTSERIALIZED);
aml_append(method, aml_return(aml_int(cfg->ecam.base)));
aml_append(dev, method);
rbuf = aml_resource_template();
aml_append(rbuf,
aml_word_bus_number(AML_MIN_FIXED, AML_MAX_FIXED, AML_POS_DECODE,
0x0000, 0x0000, nr_pcie_buses - 1, 0x0000,
nr_pcie_buses));
if (cfg->mmio32.size) {
aml_append(rbuf,
aml_dword_memory(AML_POS_DECODE, AML_MIN_FIXED, AML_MAX_FIXED,
AML_NON_CACHEABLE, AML_READ_WRITE, 0x0000,
cfg->mmio32.base,
cfg->mmio32.base + cfg->mmio32.size - 1,
0x0000,
cfg->mmio32.size));
}
if (cfg->pio.size) {
aml_append(rbuf,
aml_dword_io(AML_MIN_FIXED, AML_MAX_FIXED, AML_POS_DECODE,
AML_ENTIRE_RANGE, 0x0000, 0x0000,
cfg->pio.size - 1,
cfg->pio.base,
cfg->pio.size));
}
if (cfg->mmio64.size) {
aml_append(rbuf,
aml_qword_memory(AML_POS_DECODE, AML_MIN_FIXED, AML_MAX_FIXED,
AML_NON_CACHEABLE, AML_READ_WRITE, 0x0000,
cfg->mmio64.base,
cfg->mmio64.base + cfg->mmio64.size - 1,
0x0000,
cfg->mmio64.size));
}
aml_append(dev, aml_name_decl("_CRS", rbuf));
acpi_dsdt_add_pci_osc(dev);
Aml *dev_res0 = aml_device("%s", "RES0");
aml_append(dev_res0, aml_name_decl("_HID", aml_string("PNP0C02")));

View File

@ -4510,14 +4510,25 @@ static void spapr_machine_latest_class_options(MachineClass *mc)
type_init(spapr_machine_register_##suffix)
/*
* pseries-5.2
* pseries-6.0
*/
static void spapr_machine_5_2_class_options(MachineClass *mc)
static void spapr_machine_6_0_class_options(MachineClass *mc)
{
/* Defaults for the latest behaviour inherited from the base class */
}
DEFINE_SPAPR_MACHINE(5_2, "5.2", true);
DEFINE_SPAPR_MACHINE(6_0, "6.0", true);
/*
* pseries-5.2
*/
static void spapr_machine_5_2_class_options(MachineClass *mc)
{
spapr_machine_6_0_class_options(mc);
compat_props_add(mc->compat_props, hw_compat_5_2, hw_compat_5_2_len);
}
DEFINE_SPAPR_MACHINE(5_2, "5.2", false);
/*
* pseries-5.1

View File

@ -445,7 +445,7 @@ static void spapr_tce_reset(DeviceState *dev)
static target_ulong put_tce_emu(SpaprTceTable *tcet, target_ulong ioba,
target_ulong tce)
{
IOMMUTLBEntry entry;
IOMMUTLBEvent event;
hwaddr page_mask = IOMMU_PAGE_MASK(tcet->page_shift);
unsigned long index = (ioba - tcet->bus_offset) >> tcet->page_shift;
@ -457,12 +457,13 @@ static target_ulong put_tce_emu(SpaprTceTable *tcet, target_ulong ioba,
tcet->table[index] = tce;
entry.target_as = &address_space_memory,
entry.iova = (ioba - tcet->bus_offset) & page_mask;
entry.translated_addr = tce & page_mask;
entry.addr_mask = ~page_mask;
entry.perm = spapr_tce_iommu_access_flags(tce);
memory_region_notify_iommu(&tcet->iommu, 0, entry);
event.entry.target_as = &address_space_memory,
event.entry.iova = (ioba - tcet->bus_offset) & page_mask;
event.entry.translated_addr = tce & page_mask;
event.entry.addr_mask = ~page_mask;
event.entry.perm = spapr_tce_iommu_access_flags(tce);
event.type = event.entry.perm ? IOMMU_NOTIFIER_MAP : IOMMU_NOTIFIER_UNMAP;
memory_region_notify_iommu(&tcet->iommu, 0, event);
return H_SUCCESS;
}

View File

@ -602,15 +602,18 @@ static uint32_t s390_pci_update_iotlb(S390PCIIOMMU *iommu,
S390IOTLBEntry *entry)
{
S390IOTLBEntry *cache = g_hash_table_lookup(iommu->iotlb, &entry->iova);
IOMMUTLBEntry notify = {
.target_as = &address_space_memory,
.iova = entry->iova,
.translated_addr = entry->translated_addr,
.perm = entry->perm,
.addr_mask = ~PAGE_MASK,
IOMMUTLBEvent event = {
.type = entry->perm ? IOMMU_NOTIFIER_MAP : IOMMU_NOTIFIER_UNMAP,
.entry = {
.target_as = &address_space_memory,
.iova = entry->iova,
.translated_addr = entry->translated_addr,
.perm = entry->perm,
.addr_mask = ~PAGE_MASK,
},
};
if (entry->perm == IOMMU_NONE) {
if (event.type == IOMMU_NOTIFIER_UNMAP) {
if (!cache) {
goto out;
}
@ -623,9 +626,11 @@ static uint32_t s390_pci_update_iotlb(S390PCIIOMMU *iommu,
goto out;
}
notify.perm = IOMMU_NONE;
memory_region_notify_iommu(&iommu->iommu_mr, 0, notify);
notify.perm = entry->perm;
event.type = IOMMU_NOTIFIER_UNMAP;
event.entry.perm = IOMMU_NONE;
memory_region_notify_iommu(&iommu->iommu_mr, 0, event);
event.type = IOMMU_NOTIFIER_MAP;
event.entry.perm = entry->perm;
}
cache = g_new(S390IOTLBEntry, 1);
@ -637,7 +642,7 @@ static uint32_t s390_pci_update_iotlb(S390PCIIOMMU *iommu,
dec_dma_avail(iommu);
}
memory_region_notify_iommu(&iommu->iommu_mr, 0, notify);
memory_region_notify_iommu(&iommu->iommu_mr, 0, event);
out:
return iommu->dma_limit ? iommu->dma_limit->avail : 1;

View File

@ -789,14 +789,26 @@ bool css_migration_enabled(void)
} \
type_init(ccw_machine_register_##suffix)
static void ccw_machine_6_0_instance_options(MachineState *machine)
{
}
static void ccw_machine_6_0_class_options(MachineClass *mc)
{
}
DEFINE_CCW_MACHINE(6_0, "6.0", true);
static void ccw_machine_5_2_instance_options(MachineState *machine)
{
ccw_machine_6_0_instance_options(machine);
}
static void ccw_machine_5_2_class_options(MachineClass *mc)
{
ccw_machine_6_0_class_options(mc);
compat_props_add(mc->compat_props, hw_compat_5_2, hw_compat_5_2_len);
}
DEFINE_CCW_MACHINE(5_2, "5.2", true);
DEFINE_CCW_MACHINE(5_2, "5.2", false);
static void ccw_machine_5_1_instance_options(MachineState *machine)
{

View File

@ -718,7 +718,7 @@ static void vhost_iommu_region_add(MemoryListener *listener,
iommu_idx = memory_region_iommu_attrs_to_index(iommu_mr,
MEMTXATTRS_UNSPECIFIED);
iommu_notifier_init(&iommu->n, vhost_iommu_unmap_notify,
IOMMU_NOTIFIER_UNMAP,
IOMMU_NOTIFIER_DEVIOTLB_UNMAP,
section->offset_within_region,
int128_get64(end),
iommu_idx);

View File

@ -129,7 +129,7 @@ static void virtio_iommu_notify_map(IOMMUMemoryRegion *mr, hwaddr virt_start,
hwaddr virt_end, hwaddr paddr,
uint32_t flags)
{
IOMMUTLBEntry entry;
IOMMUTLBEvent event;
IOMMUAccessFlags perm = IOMMU_ACCESS_FLAG(flags & VIRTIO_IOMMU_MAP_F_READ,
flags & VIRTIO_IOMMU_MAP_F_WRITE);
@ -141,19 +141,20 @@ static void virtio_iommu_notify_map(IOMMUMemoryRegion *mr, hwaddr virt_start,
trace_virtio_iommu_notify_map(mr->parent_obj.name, virt_start, virt_end,
paddr, perm);
entry.target_as = &address_space_memory;
entry.addr_mask = virt_end - virt_start;
entry.iova = virt_start;
entry.perm = perm;
entry.translated_addr = paddr;
event.type = IOMMU_NOTIFIER_MAP;
event.entry.target_as = &address_space_memory;
event.entry.addr_mask = virt_end - virt_start;
event.entry.iova = virt_start;
event.entry.perm = perm;
event.entry.translated_addr = paddr;
memory_region_notify_iommu(mr, 0, entry);
memory_region_notify_iommu(mr, 0, event);
}
static void virtio_iommu_notify_unmap(IOMMUMemoryRegion *mr, hwaddr virt_start,
hwaddr virt_end)
{
IOMMUTLBEntry entry;
IOMMUTLBEvent event;
if (!(mr->iommu_notify_flags & IOMMU_NOTIFIER_UNMAP)) {
return;
@ -161,13 +162,14 @@ static void virtio_iommu_notify_unmap(IOMMUMemoryRegion *mr, hwaddr virt_start,
trace_virtio_iommu_notify_unmap(mr->parent_obj.name, virt_start, virt_end);
entry.target_as = &address_space_memory;
entry.addr_mask = virt_end - virt_start;
entry.iova = virt_start;
entry.perm = IOMMU_NONE;
entry.translated_addr = 0;
event.type = IOMMU_NOTIFIER_UNMAP;
event.entry.target_as = &address_space_memory;
event.entry.addr_mask = virt_end - virt_start;
event.entry.iova = virt_start;
event.entry.perm = IOMMU_NONE;
event.entry.translated_addr = 0;
memory_region_notify_iommu(mr, 0, entry);
memory_region_notify_iommu(mr, 0, event);
}
static gboolean virtio_iommu_notify_unmap_cb(gpointer key, gpointer value,

View File

@ -1798,6 +1798,7 @@ static void virtio_pci_realize(PCIDevice *pci_dev, Error **errp)
if (pcie_port && pci_is_express(pci_dev)) {
int pos;
uint16_t last_pcie_cap_offset = PCI_CONFIG_SPACE_SIZE;
pos = pcie_endpoint_cap_init(pci_dev, 0);
assert(pos > 0);
@ -1816,6 +1817,12 @@ static void virtio_pci_realize(PCIDevice *pci_dev, Error **errp)
*/
pci_set_word(pci_dev->config + pos + PCI_PM_PMC, 0x3);
if (proxy->flags & VIRTIO_PCI_FLAG_AER) {
pcie_aer_init(pci_dev, PCI_ERR_VER, last_pcie_cap_offset,
PCI_ERR_SIZEOF, NULL);
last_pcie_cap_offset += PCI_ERR_SIZEOF;
}
if (proxy->flags & VIRTIO_PCI_FLAG_INIT_DEVERR) {
/* Init error enabling flags */
pcie_cap_deverr_init(pci_dev);
@ -1833,7 +1840,8 @@ static void virtio_pci_realize(PCIDevice *pci_dev, Error **errp)
}
if (proxy->flags & VIRTIO_PCI_FLAG_ATS) {
pcie_ats_init(pci_dev, 256);
pcie_ats_init(pci_dev, last_pcie_cap_offset);
last_pcie_cap_offset += PCI_EXT_CAP_ATS_SIZEOF;
}
if (proxy->flags & VIRTIO_PCI_FLAG_INIT_FLR) {
@ -1856,7 +1864,15 @@ static void virtio_pci_realize(PCIDevice *pci_dev, Error **errp)
static void virtio_pci_exit(PCIDevice *pci_dev)
{
VirtIOPCIProxy *proxy = VIRTIO_PCI(pci_dev);
bool pcie_port = pci_bus_is_express(pci_get_bus(pci_dev)) &&
!pci_bus_is_root(pci_get_bus(pci_dev));
msix_uninit_exclusive_bar(pci_dev);
if (proxy->flags & VIRTIO_PCI_FLAG_AER && pcie_port &&
pci_is_express(pci_dev)) {
pcie_aer_exit(pci_dev);
}
}
static void virtio_pci_reset(DeviceState *qdev)
@ -1909,6 +1925,8 @@ static Property virtio_pci_properties[] = {
VIRTIO_PCI_FLAG_INIT_PM_BIT, true),
DEFINE_PROP_BIT("x-pcie-flr-init", VirtIOPCIProxy, flags,
VIRTIO_PCI_FLAG_INIT_FLR_BIT, true),
DEFINE_PROP_BIT("aer", VirtIOPCIProxy, flags,
VIRTIO_PCI_FLAG_AER_BIT, false),
DEFINE_PROP_END_OF_LIST(),
};

View File

@ -41,6 +41,7 @@ enum {
VIRTIO_PCI_FLAG_INIT_LNKCTL_BIT,
VIRTIO_PCI_FLAG_INIT_PM_BIT,
VIRTIO_PCI_FLAG_INIT_FLR_BIT,
VIRTIO_PCI_FLAG_AER_BIT,
};
/* Need to activate work-arounds for buggy guests at vmstate load. */
@ -80,6 +81,9 @@ enum {
/* Init Function Level Reset capability */
#define VIRTIO_PCI_FLAG_INIT_FLR (1 << VIRTIO_PCI_FLAG_INIT_FLR_BIT)
/* Advanced Error Reporting capability */
#define VIRTIO_PCI_FLAG_AER (1 << VIRTIO_PCI_FLAG_AER_BIT)
typedef struct {
MSIMessage msg;
int virq;

View File

@ -3161,12 +3161,15 @@ int virtio_load(VirtIODevice *vdev, QEMUFile *f, int version_id)
nheads = vring_avail_idx(&vdev->vq[i]) - vdev->vq[i].last_avail_idx;
/* Check it isn't doing strange things with descriptor numbers. */
if (nheads > vdev->vq[i].vring.num) {
qemu_log_mask(LOG_GUEST_ERROR,
"VQ %d size 0x%x Guest index 0x%x "
"inconsistent with Host index 0x%x: delta 0x%x",
i, vdev->vq[i].vring.num,
vring_avail_idx(&vdev->vq[i]),
vdev->vq[i].last_avail_idx, nheads);
virtio_error(vdev, "VQ %d size 0x%x Guest index 0x%x "
"inconsistent with Host index 0x%x: delta 0x%x",
i, vdev->vq[i].vring.num,
vring_avail_idx(&vdev->vq[i]),
vdev->vq[i].last_avail_idx, nheads);
vdev->vq[i].used_idx = 0;
vdev->vq[i].shadow_avail_idx = 0;
vdev->vq[i].inuse = 0;
continue;
}
vdev->vq[i].used_idx = vring_used_idx(&vdev->vq[i]);
vdev->vq[i].shadow_avail_idx = vring_avail_idx(&vdev->vq[i]);

View File

@ -97,9 +97,14 @@ typedef enum {
IOMMU_NOTIFIER_UNMAP = 0x1,
/* Notify entry changes (newly created entries) */
IOMMU_NOTIFIER_MAP = 0x2,
/* Notify changes on device IOTLB entries */
IOMMU_NOTIFIER_DEVIOTLB_UNMAP = 0x04,
} IOMMUNotifierFlag;
#define IOMMU_NOTIFIER_ALL (IOMMU_NOTIFIER_MAP | IOMMU_NOTIFIER_UNMAP)
#define IOMMU_NOTIFIER_IOTLB_EVENTS (IOMMU_NOTIFIER_MAP | IOMMU_NOTIFIER_UNMAP)
#define IOMMU_NOTIFIER_DEVIOTLB_EVENTS IOMMU_NOTIFIER_DEVIOTLB_UNMAP
#define IOMMU_NOTIFIER_ALL (IOMMU_NOTIFIER_IOTLB_EVENTS | \
IOMMU_NOTIFIER_DEVIOTLB_EVENTS)
struct IOMMUNotifier;
typedef void (*IOMMUNotify)(struct IOMMUNotifier *notifier,
@ -116,6 +121,11 @@ struct IOMMUNotifier {
};
typedef struct IOMMUNotifier IOMMUNotifier;
typedef struct IOMMUTLBEvent {
IOMMUNotifierFlag type;
IOMMUTLBEntry entry;
} IOMMUTLBEvent;
/* RAM is pre-allocated and passed into qemu_ram_alloc_from_ptr */
#define RAM_PREALLOC (1 << 0)
@ -236,7 +246,7 @@ enum IOMMUMemoryRegionAttr {
* The IOMMU implementation must use the IOMMU notifier infrastructure
* to report whenever mappings are changed, by calling
* memory_region_notify_iommu() (or, if necessary, by calling
* memory_region_notify_one() for each registered notifier).
* memory_region_notify_iommu_one() for each registered notifier).
*
* Conceptually an IOMMU provides a mapping from input address
* to an output TLB entry. If the IOMMU is aware of memory transaction
@ -1326,39 +1336,33 @@ uint64_t memory_region_iommu_get_min_page_size(IOMMUMemoryRegion *iommu_mr);
/**
* memory_region_notify_iommu: notify a change in an IOMMU translation entry.
*
* The notification type will be decided by entry.perm bits:
*
* - For UNMAP (cache invalidation) notifies: set entry.perm to IOMMU_NONE.
* - For MAP (newly added entry) notifies: set entry.perm to the
* permission of the page (which is definitely !IOMMU_NONE).
*
* Note: for any IOMMU implementation, an in-place mapping change
* should be notified with an UNMAP followed by a MAP.
*
* @iommu_mr: the memory region that was changed
* @iommu_idx: the IOMMU index for the translation table which has changed
* @entry: the new entry in the IOMMU translation table. The entry
* replaces all old entries for the same virtual I/O address range.
* Deleted entries have .@perm == 0.
* @event: TLB event with the new entry in the IOMMU translation table.
* The entry replaces all old entries for the same virtual I/O address
* range.
*/
void memory_region_notify_iommu(IOMMUMemoryRegion *iommu_mr,
int iommu_idx,
IOMMUTLBEntry entry);
IOMMUTLBEvent event);
/**
* memory_region_notify_one: notify a change in an IOMMU translation
* memory_region_notify_iommu_one: notify a change in an IOMMU translation
* entry to a single notifier
*
* This works just like memory_region_notify_iommu(), but it only
* notifies a specific notifier, not all of them.
*
* @notifier: the notifier to be notified
* @entry: the new entry in the IOMMU translation table. The entry
* replaces all old entries for the same virtual I/O address range.
* Deleted entries have .@perm == 0.
* @event: TLB event with the new entry in the IOMMU translation table.
* The entry replaces all old entries for the same virtual I/O address
* range.
*/
void memory_region_notify_one(IOMMUNotifier *notifier,
IOMMUTLBEntry *entry);
void memory_region_notify_iommu_one(IOMMUNotifier *notifier,
IOMMUTLBEvent *event);
/**
* memory_region_register_iommu_notifier: register a notifier for changes to

View File

@ -224,6 +224,20 @@ struct AcpiBuildTables {
BIOSLinker *linker;
} AcpiBuildTables;
typedef
struct CrsRangeEntry {
uint64_t base;
uint64_t limit;
} CrsRangeEntry;
typedef
struct CrsRangeSet {
GPtrArray *io_ranges;
GPtrArray *mem_ranges;
GPtrArray *mem_64bit_ranges;
} CrsRangeSet;
/*
* ACPI 5.0: 6.4.3.8.2 Serial Bus Connection Descriptors
* Serial Bus Type
@ -432,6 +446,14 @@ build_append_gas_from_struct(GArray *table, const struct AcpiGenericAddress *s)
s->access_width, s->address);
}
void crs_range_insert(GPtrArray *ranges, uint64_t base, uint64_t limit);
void crs_replace_with_free_ranges(GPtrArray *ranges,
uint64_t start, uint64_t end);
void crs_range_set_init(CrsRangeSet *range_set);
void crs_range_set_free(CrsRangeSet *range_set);
Aml *build_crs(PCIHostState *host, CrsRangeSet *range_set);
void build_srat_memory(AcpiSratMemoryAffinity *numamem, uint64_t base,
uint64_t len, int node, MemoryAffinityFlags flags);

View File

@ -22,6 +22,7 @@ typedef struct AcpiCpuStatus {
uint64_t arch_id;
bool is_inserting;
bool is_removing;
bool fw_remove;
uint32_t ost_event;
uint32_t ost_status;
} AcpiCpuStatus;
@ -50,6 +51,7 @@ void cpu_hotplug_hw_init(MemoryRegion *as, Object *owner,
typedef struct CPUHotplugFeatures {
bool acpi_1_compatible;
bool has_legacy_cphp;
bool fw_unplugs_cpu;
const char *smi_path;
} CPUHotplugFeatures;

View File

@ -163,6 +163,7 @@ struct VirtMachineState {
DeviceState *gic;
DeviceState *acpi_dev;
Notifier powerdown_notifier;
PCIBus *bus;
};
#define VIRT_ECAM_ID(high) (high ? VIRT_HIGH_PCIE_ECAM : VIRT_PCIE_ECAM)

View File

@ -310,6 +310,9 @@ struct MachineState {
} \
type_init(machine_initfn##_register_types)
extern GlobalProperty hw_compat_5_2[];
extern const size_t hw_compat_5_2_len;
extern GlobalProperty hw_compat_5_1[];
extern const size_t hw_compat_5_1_len;

View File

@ -44,6 +44,7 @@ typedef struct PCMachineState {
bool sata_enabled;
bool pit_enabled;
bool hpet_enabled;
uint64_t max_fw_size;
/* NUMA information: */
uint64_t numa_nodes;
@ -60,6 +61,7 @@ typedef struct PCMachineState {
#define PC_MACHINE_SMBUS "smbus"
#define PC_MACHINE_SATA "sata"
#define PC_MACHINE_PIT "pit"
#define PC_MACHINE_MAX_FW_SIZE "max-fw-size"
/**
* PCMachineClass:
@ -191,6 +193,9 @@ void pc_system_firmware_init(PCMachineState *pcms, MemoryRegion *rom_memory);
void pc_madt_cpu_entry(AcpiDeviceIf *adev, int uid,
const CPUArchIdList *apic_ids, GArray *entry);
extern GlobalProperty pc_compat_5_2[];
extern const size_t pc_compat_5_2_len;
extern GlobalProperty pc_compat_5_1[];
extern const size_t pc_compat_5_1_len;

View File

@ -308,6 +308,15 @@ void *fw_cfg_modify_file(FWCfgState *s, const char *filename, void *data,
bool fw_cfg_add_from_generator(FWCfgState *s, const char *filename,
const char *gen_id, Error **errp);
/**
* fw_cfg_add_extra_pci_roots:
* @bus: main pci root bus to be scanned from
* @s: fw_cfg device being modified
*
* Add a new fw_cfg item...
*/
void fw_cfg_add_extra_pci_roots(PCIBus *bus, FWCfgState *s);
FWCfgState *fw_cfg_init_io_dma(uint32_t iobase, uint32_t dma_iobase,
AddressSpace *dma_as);
FWCfgState *fw_cfg_init_io(uint32_t iobase);

View File

@ -59,6 +59,7 @@ struct GPEXConfig {
MemMapEntry mmio64;
MemMapEntry pio;
int irq;
PCIBus *bus;
};
int gpex_set_irq_num(GPEXHost *s, int index, int gsi);

View File

@ -81,16 +81,17 @@ typedef void (*BusUnrealize)(BusState *bus);
* </note>
*
* # Hiding a device #
* To hide a device, a DeviceListener function should_be_hidden() needs to
* To hide a device, a DeviceListener function hide_device() needs to
* be registered.
* It can be used to defer adding a device and therefore hide it from the
* guest. The handler registering to this DeviceListener can save the QOpts
* passed to it for re-using it later and must return that it wants the device
* to be/remain hidden or not. When the handler function decides the device
* shall not be hidden it will be added in qdev_device_add() and
* realized as any other device. Otherwise qdev_device_add() will return early
* without adding the device. The guest will not see a "hidden" device
* until it was marked don't hide and qdev_device_add called again.
* It can be used to defer adding a device and therefore hide it from
* the guest. The handler registering to this DeviceListener can save
* the QOpts passed to it for re-using it later. It must return if it
* wants the device to be hidden or visible. When the handler function
* decides the device shall be visible it will be added with
* qdev_device_add() and realized as any other device. Otherwise
* qdev_device_add() will return early without adding the device. The
* guest will not see a "hidden" device until it was marked visible
* and qdev_device_add called again.
*
*/
struct DeviceClass {
@ -196,11 +197,12 @@ struct DeviceListener {
void (*realize)(DeviceListener *listener, DeviceState *dev);
void (*unrealize)(DeviceListener *listener, DeviceState *dev);
/*
* This callback is called upon init of the DeviceState and allows to
* inform qdev that a device should be hidden, depending on the device
* opts, for example, to hide a standby device.
* This callback is called upon init of the DeviceState and
* informs qdev if a device should be visible or hidden. We can
* hide a failover device depending for example on the device
* opts.
*/
int (*should_be_hidden)(DeviceListener *listener, QemuOpts *device_opts);
bool (*hide_device)(DeviceListener *listener, QemuOpts *device_opts);
QTAILQ_ENTRY(DeviceListener) link;
};

View File

@ -202,13 +202,8 @@ struct VirtIONet {
AnnounceTimer announce_timer;
bool needs_vnet_hdr_swap;
bool mtu_bypass_backend;
QemuOpts *primary_device_opts;
QDict *primary_device_dict;
DeviceState *primary_dev;
BusState *primary_bus;
char *primary_device_id;
char *standby_id;
bool primary_should_be_hidden;
/* primary failover device is hidden*/
bool failover_primary_hidden;
bool failover;
DeviceListener primary_listener;
Notifier migration_state;

View File

@ -11,7 +11,7 @@
#ifndef VHOST_USER_SERVER_H
#define VHOST_USER_SERVER_H
#include "contrib/libvhost-user/libvhost-user.h"
#include "subprojects/libvhost-user/libvhost-user.h" /* only for the type definitions */
#include "io/channel-socket.h"
#include "io/channel-file.h"
#include "io/net-listener.h"

View File

@ -1475,7 +1475,12 @@ trace_events_subdirs += [
'util',
]
subdir('contrib/libvhost-user')
vhost_user = not_found
if 'CONFIG_VHOST_USER' in config_host
libvhost_user = subproject('libvhost-user')
vhost_user = libvhost_user.get_variable('vhost_user_dep')
endif
subdir('qapi')
subdir('qobject')
subdir('stubs')

View File

@ -1942,11 +1942,16 @@ void memory_region_unregister_iommu_notifier(MemoryRegion *mr,
memory_region_update_iommu_notify_flags(iommu_mr, NULL);
}
void memory_region_notify_one(IOMMUNotifier *notifier,
IOMMUTLBEntry *entry)
void memory_region_notify_iommu_one(IOMMUNotifier *notifier,
IOMMUTLBEvent *event)
{
IOMMUNotifierFlag request_flags;
IOMMUTLBEntry *entry = &event->entry;
hwaddr entry_end = entry->iova + entry->addr_mask;
IOMMUTLBEntry tmp = *entry;
if (event->type == IOMMU_NOTIFIER_UNMAP) {
assert(entry->perm == IOMMU_NONE);
}
/*
* Skip the notification if the notification does not overlap
@ -1956,22 +1961,22 @@ void memory_region_notify_one(IOMMUNotifier *notifier,
return;
}
assert(entry->iova >= notifier->start && entry_end <= notifier->end);
if (entry->perm & IOMMU_RW) {
request_flags = IOMMU_NOTIFIER_MAP;
if (notifier->notifier_flags & IOMMU_NOTIFIER_DEVIOTLB_UNMAP) {
/* Crop (iova, addr_mask) to range */
tmp.iova = MAX(tmp.iova, notifier->start);
tmp.addr_mask = MIN(entry_end, notifier->end) - tmp.iova;
} else {
request_flags = IOMMU_NOTIFIER_UNMAP;
assert(entry->iova >= notifier->start && entry_end <= notifier->end);
}
if (notifier->notifier_flags & request_flags) {
notifier->notify(notifier, entry);
if (event->type & notifier->notifier_flags) {
notifier->notify(notifier, &tmp);
}
}
void memory_region_notify_iommu(IOMMUMemoryRegion *iommu_mr,
int iommu_idx,
IOMMUTLBEntry entry)
IOMMUTLBEvent event)
{
IOMMUNotifier *iommu_notifier;
@ -1979,7 +1984,7 @@ void memory_region_notify_iommu(IOMMUMemoryRegion *iommu_mr,
IOMMU_NOTIFIER_FOREACH(iommu_notifier, iommu_mr) {
if (iommu_notifier->iommu_idx == iommu_idx) {
memory_region_notify_one(iommu_notifier, &entry);
memory_region_notify_iommu_one(iommu_notifier, &event);
}
}
}

View File

@ -572,35 +572,12 @@ void qdev_set_id(DeviceState *dev, const char *id)
}
}
static int is_failover_device(void *opaque, const char *name, const char *value,
Error **errp)
{
if (strcmp(name, "failover_pair_id") == 0) {
QemuOpts *opts = (QemuOpts *)opaque;
if (qdev_should_hide_device(opts)) {
return 1;
}
}
return 0;
}
static bool should_hide_device(QemuOpts *opts)
{
if (qemu_opt_foreach(opts, is_failover_device, opts, NULL) == 0) {
return false;
}
return true;
}
DeviceState *qdev_device_add(QemuOpts *opts, Error **errp)
{
DeviceClass *dc;
const char *driver, *path;
DeviceState *dev = NULL;
BusState *bus = NULL;
bool hide;
driver = qemu_opt_get(opts, "driver");
if (!driver) {
@ -634,14 +611,22 @@ DeviceState *qdev_device_add(QemuOpts *opts, Error **errp)
return NULL;
}
}
hide = should_hide_device(opts);
if ((hide || qdev_hotplug) && bus && !qbus_is_hotpluggable(bus)) {
error_setg(errp, QERR_BUS_NO_HOTPLUG, bus->name);
return NULL;
if (qemu_opt_get(opts, "failover_pair_id")) {
if (!opts->id) {
error_setg(errp, "Device with failover_pair_id don't have id");
return NULL;
}
if (qdev_should_hide_device(opts)) {
if (bus && !qbus_is_hotpluggable(bus)) {
error_setg(errp, QERR_BUS_NO_HOTPLUG, bus->name);
}
return NULL;
}
}
if (hide) {
if (qdev_hotplug && bus && !qbus_is_hotpluggable(bus)) {
error_setg(errp, QERR_BUS_NO_HOTPLUG, bus->name);
return NULL;
}

View File

@ -12,10 +12,16 @@
* later. See the COPYING file in the top-level directory.
*/
#include "qemu/osdep.h"
#include "libvhost-user-glib.h"
#ifndef container_of
#define container_of(ptr, type, member) \
__extension__({ \
void *__mptr = (void *)(ptr); \
((type *)(__mptr - offsetof(type, member))); \
})
#endif
/* glib event loop integration for libvhost-user and misc callbacks */
G_STATIC_ASSERT((int)G_IO_IN == (int)VU_WATCH_IN);

View File

@ -26,7 +26,7 @@
#include <sys/socket.h>
#include <sys/eventfd.h>
#include <sys/mman.h>
#include "qemu/compiler.h"
#include <endian.h>
#if defined(__linux__)
#include <sys/syscall.h>
@ -41,9 +41,6 @@
#endif
#include "qemu/atomic.h"
#include "qemu/osdep.h"
#include "qemu/bswap.h"
#include "qemu/memfd.h"
#include "libvhost-user.h"
@ -62,6 +59,10 @@
/* Round number up to multiple */
#define ALIGN_UP(n, m) ALIGN_DOWN((n) + (m) - 1, (m))
#ifndef unlikely
#define unlikely(x) __builtin_expect(!!(x), 0)
#endif
/* Align each region to cache line size in inflight buffer */
#define INFLIGHT_ALIGNMENT 64
@ -1081,7 +1082,7 @@ vu_set_vring_addr_exec(VuDev *dev, VhostUserMsg *vmsg)
return false;
}
vq->used_idx = lduw_le_p(&vq->vring.used->idx);
vq->used_idx = le16toh(vq->vring.used->idx);
if (vq->last_avail_idx != vq->used_idx) {
bool resume = dev->iface->queue_is_processed_in_order &&
@ -1198,7 +1199,7 @@ vu_check_queue_inflights(VuDev *dev, VuVirtq *vq)
return 0;
}
vq->used_idx = lduw_le_p(&vq->vring.used->idx);
vq->used_idx = le16toh(vq->vring.used->idx);
vq->resubmit_num = 0;
vq->resubmit_list = NULL;
vq->counter = 0;
@ -1615,11 +1616,45 @@ vu_inflight_queue_size(uint16_t queue_size)
sizeof(uint16_t), INFLIGHT_ALIGNMENT);
}
#ifdef MFD_ALLOW_SEALING
static void *
memfd_alloc(const char *name, size_t size, unsigned int flags, int *fd)
{
void *ptr;
int ret;
*fd = memfd_create(name, MFD_ALLOW_SEALING);
if (*fd < 0) {
return NULL;
}
ret = ftruncate(*fd, size);
if (ret < 0) {
close(*fd);
return NULL;
}
ret = fcntl(*fd, F_ADD_SEALS, flags);
if (ret < 0) {
close(*fd);
return NULL;
}
ptr = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, *fd, 0);
if (ptr == MAP_FAILED) {
close(*fd);
return NULL;
}
return ptr;
}
#endif
static bool
vu_get_inflight_fd(VuDev *dev, VhostUserMsg *vmsg)
{
int fd;
void *addr;
int fd = -1;
void *addr = NULL;
uint64_t mmap_size;
uint16_t num_queues, queue_size;
@ -1637,9 +1672,13 @@ vu_get_inflight_fd(VuDev *dev, VhostUserMsg *vmsg)
mmap_size = vu_inflight_queue_size(queue_size) * num_queues;
addr = qemu_memfd_alloc("vhost-inflight", mmap_size,
F_SEAL_GROW | F_SEAL_SHRINK | F_SEAL_SEAL,
&fd, NULL);
#ifdef MFD_ALLOW_SEALING
addr = memfd_alloc("vhost-inflight", mmap_size,
F_SEAL_GROW | F_SEAL_SHRINK | F_SEAL_SEAL,
&fd);
#else
vu_panic(dev, "Not implemented: memfd support is missing");
#endif
if (!addr) {
vu_panic(dev, "Failed to alloc vhost inflight area");
@ -2031,13 +2070,13 @@ vu_queue_started(const VuDev *dev, const VuVirtq *vq)
static inline uint16_t
vring_avail_flags(VuVirtq *vq)
{
return lduw_le_p(&vq->vring.avail->flags);
return le16toh(vq->vring.avail->flags);
}
static inline uint16_t
vring_avail_idx(VuVirtq *vq)
{
vq->shadow_avail_idx = lduw_le_p(&vq->vring.avail->idx);
vq->shadow_avail_idx = le16toh(vq->vring.avail->idx);
return vq->shadow_avail_idx;
}
@ -2045,7 +2084,7 @@ vring_avail_idx(VuVirtq *vq)
static inline uint16_t
vring_avail_ring(VuVirtq *vq, int i)
{
return lduw_le_p(&vq->vring.avail->ring[i]);
return le16toh(vq->vring.avail->ring[i]);
}
static inline uint16_t
@ -2133,12 +2172,12 @@ virtqueue_read_next_desc(VuDev *dev, struct vring_desc *desc,
int i, unsigned int max, unsigned int *next)
{
/* If this descriptor says it doesn't chain, we're done. */
if (!(lduw_le_p(&desc[i].flags) & VRING_DESC_F_NEXT)) {
if (!(le16toh(desc[i].flags) & VRING_DESC_F_NEXT)) {
return VIRTQUEUE_READ_DESC_DONE;
}
/* Check they're not leading us off end of descriptors. */
*next = lduw_le_p(&desc[i].next);
*next = le16toh(desc[i].next);
/* Make sure compiler knows to grab that: we don't want it changing! */
smp_wmb();
@ -2181,8 +2220,8 @@ vu_queue_get_avail_bytes(VuDev *dev, VuVirtq *vq, unsigned int *in_bytes,
}
desc = vq->vring.desc;
if (lduw_le_p(&desc[i].flags) & VRING_DESC_F_INDIRECT) {
if (ldl_le_p(&desc[i].len) % sizeof(struct vring_desc)) {
if (le16toh(desc[i].flags) & VRING_DESC_F_INDIRECT) {
if (le32toh(desc[i].len) % sizeof(struct vring_desc)) {
vu_panic(dev, "Invalid size for indirect buffer table");
goto err;
}
@ -2195,8 +2234,8 @@ vu_queue_get_avail_bytes(VuDev *dev, VuVirtq *vq, unsigned int *in_bytes,
/* loop over the indirect descriptor table */
indirect = 1;
desc_addr = ldq_le_p(&desc[i].addr);
desc_len = ldl_le_p(&desc[i].len);
desc_addr = le64toh(desc[i].addr);
desc_len = le32toh(desc[i].len);
max = desc_len / sizeof(struct vring_desc);
read_len = desc_len;
desc = vu_gpa_to_va(dev, &read_len, desc_addr);
@ -2223,10 +2262,10 @@ vu_queue_get_avail_bytes(VuDev *dev, VuVirtq *vq, unsigned int *in_bytes,
goto err;
}
if (lduw_le_p(&desc[i].flags) & VRING_DESC_F_WRITE) {
in_total += ldl_le_p(&desc[i].len);
if (le16toh(desc[i].flags) & VRING_DESC_F_WRITE) {
in_total += le32toh(desc[i].len);
} else {
out_total += ldl_le_p(&desc[i].len);
out_total += le32toh(desc[i].len);
}
if (in_total >= max_in_bytes && out_total >= max_out_bytes) {
goto done;
@ -2377,7 +2416,7 @@ vring_used_flags_set_bit(VuVirtq *vq, int mask)
flags = (uint16_t *)((char*)vq->vring.used +
offsetof(struct vring_used, flags));
stw_le_p(flags, lduw_le_p(flags) | mask);
*flags = htole16(le16toh(*flags) | mask);
}
static inline void
@ -2387,17 +2426,20 @@ vring_used_flags_unset_bit(VuVirtq *vq, int mask)
flags = (uint16_t *)((char*)vq->vring.used +
offsetof(struct vring_used, flags));
stw_le_p(flags, lduw_le_p(flags) & ~mask);
*flags = htole16(le16toh(*flags) & ~mask);
}
static inline void
vring_set_avail_event(VuVirtq *vq, uint16_t val)
{
uint16_t *avail;
if (!vq->notification) {
return;
}
stw_le_p(&vq->vring.used->ring[vq->vring.num], val);
avail = (uint16_t *)&vq->vring.used->ring[vq->vring.num];
*avail = htole16(val);
}
void
@ -2487,15 +2529,15 @@ vu_queue_map_desc(VuDev *dev, VuVirtq *vq, unsigned int idx, size_t sz)
struct vring_desc desc_buf[VIRTQUEUE_MAX_SIZE];
int rc;
if (lduw_le_p(&desc[i].flags) & VRING_DESC_F_INDIRECT) {
if (ldl_le_p(&desc[i].len) % sizeof(struct vring_desc)) {
if (le16toh(desc[i].flags) & VRING_DESC_F_INDIRECT) {
if (le32toh(desc[i].len) % sizeof(struct vring_desc)) {
vu_panic(dev, "Invalid size for indirect buffer table");
return NULL;
}
/* loop over the indirect descriptor table */
desc_addr = ldq_le_p(&desc[i].addr);
desc_len = ldl_le_p(&desc[i].len);
desc_addr = le64toh(desc[i].addr);
desc_len = le32toh(desc[i].len);
max = desc_len / sizeof(struct vring_desc);
read_len = desc_len;
desc = vu_gpa_to_va(dev, &read_len, desc_addr);
@ -2517,11 +2559,11 @@ vu_queue_map_desc(VuDev *dev, VuVirtq *vq, unsigned int idx, size_t sz)
/* Collect all the descriptors */
do {
if (lduw_le_p(&desc[i].flags) & VRING_DESC_F_WRITE) {
if (le16toh(desc[i].flags) & VRING_DESC_F_WRITE) {
if (!virtqueue_map_desc(dev, &in_num, iov + out_num,
VIRTQUEUE_MAX_SIZE - out_num, true,
ldq_le_p(&desc[i].addr),
ldl_le_p(&desc[i].len))) {
le64toh(desc[i].addr),
le32toh(desc[i].len))) {
return NULL;
}
} else {
@ -2531,8 +2573,8 @@ vu_queue_map_desc(VuDev *dev, VuVirtq *vq, unsigned int idx, size_t sz)
}
if (!virtqueue_map_desc(dev, &out_num, iov,
VIRTQUEUE_MAX_SIZE, false,
ldq_le_p(&desc[i].addr),
ldl_le_p(&desc[i].len))) {
le64toh(desc[i].addr),
le32toh(desc[i].len))) {
return NULL;
}
}
@ -2731,15 +2773,15 @@ vu_log_queue_fill(VuDev *dev, VuVirtq *vq,
max = vq->vring.num;
i = elem->index;
if (lduw_le_p(&desc[i].flags) & VRING_DESC_F_INDIRECT) {
if (ldl_le_p(&desc[i].len) % sizeof(struct vring_desc)) {
if (le16toh(desc[i].flags) & VRING_DESC_F_INDIRECT) {
if (le32toh(desc[i].len) % sizeof(struct vring_desc)) {
vu_panic(dev, "Invalid size for indirect buffer table");
return;
}
/* loop over the indirect descriptor table */
desc_addr = ldq_le_p(&desc[i].addr);
desc_len = ldl_le_p(&desc[i].len);
desc_addr = le64toh(desc[i].addr);
desc_len = le32toh(desc[i].len);
max = desc_len / sizeof(struct vring_desc);
read_len = desc_len;
desc = vu_gpa_to_va(dev, &read_len, desc_addr);
@ -2765,9 +2807,9 @@ vu_log_queue_fill(VuDev *dev, VuVirtq *vq,
return;
}
if (lduw_le_p(&desc[i].flags) & VRING_DESC_F_WRITE) {
min = MIN(ldl_le_p(&desc[i].len), len);
vu_log_write(dev, ldq_le_p(&desc[i].addr), min);
if (le16toh(desc[i].flags) & VRING_DESC_F_WRITE) {
min = MIN(le32toh(desc[i].len), len);
vu_log_write(dev, le64toh(desc[i].addr), min);
len -= min;
}
@ -2792,15 +2834,15 @@ vu_queue_fill(VuDev *dev, VuVirtq *vq,
idx = (idx + vq->used_idx) % vq->vring.num;
stl_le_p(&uelem.id, elem->index);
stl_le_p(&uelem.len, len);
uelem.id = htole32(elem->index);
uelem.len = htole32(len);
vring_used_write(dev, vq, &uelem, idx);
}
static inline
void vring_used_idx_set(VuDev *dev, VuVirtq *vq, uint16_t val)
{
stw_le_p(&vq->vring.used->idx, val);
vq->vring.used->idx = htole16(val);
vu_log_write(dev,
vq->vring.log_guest_addr + offsetof(struct vring_used, idx),
sizeof(vq->vring.used->idx));

View File

@ -0,0 +1,45 @@
/*
* A trivial unit test to check linking without glib. A real test suite should
* probably based off libvhost-user-glib instead.
*/
#include <assert.h>
#include <stdlib.h>
#include "libvhost-user.h"
static void
panic(VuDev *dev, const char *err)
{
abort();
}
static void
set_watch(VuDev *dev, int fd, int condition,
vu_watch_cb cb, void *data)
{
abort();
}
static void
remove_watch(VuDev *dev, int fd)
{
abort();
}
static const VuDevIface iface = {
0,
};
int
main(int argc, const char *argv[])
{
bool rc;
uint16_t max_queues = 2;
int socket = 0;
VuDev dev = { 0, };
rc = vu_init(&dev, max_queues, socket, panic, NULL, set_watch, remove_watch, &iface);
assert(rc == true);
vu_deinit(&dev);
return 0;
}

View File

@ -0,0 +1,24 @@
project('libvhost-user', 'c',
license: 'GPL-2.0-or-later',
default_options: ['c_std=gnu99'])
glib = dependency('glib-2.0')
inc = include_directories('../../include', '../../linux-headers')
vhost_user = static_library('vhost-user',
files('libvhost-user.c'),
include_directories: inc,
c_args: '-D_GNU_SOURCE')
executable('link-test', files('link-test.c'),
link_whole: vhost_user,
include_directories: inc)
vhost_user_glib = static_library('vhost-user-glib',
files('libvhost-user-glib.c'),
include_directories: inc,
link_with: vhost_user,
dependencies: glib)
vhost_user_dep = declare_dependency(link_with: vhost_user_glib,
include_directories: include_directories('.'))

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.

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

@ -264,8 +264,7 @@ endforeach
if have_tools and 'CONFIG_VHOST_USER' in config_host and 'CONFIG_LINUX' in config_host
executable('vhost-user-bridge',
sources: files('vhost-user-bridge.c'),
link_with: [libvhost_user],
dependencies: [qemuutil])
dependencies: [qemuutil, vhost_user])
endif
if have_system and 'CONFIG_POSIX' in config_host

View File

@ -671,12 +671,21 @@ static void test_acpi_one(const char *params, test_data *data)
* TODO: convert '-drive if=pflash' to new syntax (see e33763be7cd3)
* when arm/virt boad starts to support it.
*/
args = g_strdup_printf("-machine %s %s -accel tcg -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->tcg_only ? "" : "-accel kvm",
data->uefi_fl1, data->uefi_fl2, data->cd, params ? params : "");
if (data->cd) {
args = g_strdup_printf("-machine %s %s -accel tcg "
"-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->tcg_only ? "" : "-accel kvm",
data->uefi_fl1, data->uefi_fl2, data->cd, params ? params : "");
} else {
args = g_strdup_printf("-machine %s %s -accel tcg "
"-nodefaults -nographic "
"-drive if=pflash,format=raw,file=%s,readonly "
"-drive if=pflash,format=raw,file=%s,snapshot=on %s",
data->machine, data->tcg_only ? "" : "-accel kvm",
data->uefi_fl1, data->uefi_fl2, params ? params : "");
}
} else {
args = g_strdup_printf("-machine %s %s -accel tcg "
"-net none -display none %s "
@ -1176,6 +1185,40 @@ static void test_acpi_virt_tcg_numamem(void)
}
#ifdef CONFIG_PXB
static void test_acpi_virt_tcg_pxb(void)
{
test_data data = {
.machine = "virt",
.tcg_only = true,
.uefi_fl1 = "pc-bios/edk2-aarch64-code.fd",
.uefi_fl2 = "pc-bios/edk2-arm-vars.fd",
.ram_start = 0x40000000ULL,
.scan_len = 128ULL * 1024 * 1024,
};
/*
* While using -cdrom, the cdrom would auto plugged into pxb-pcie,
* the reason is the bus of pxb-pcie is also root bus, it would lead
* to the error only PCI/PCIE bridge could plug onto pxb.
* Therefore,thr cdrom is defined and plugged onto the scsi controller
* to solve the conflicts.
*/
data.variant = ".pxb";
test_acpi_one(" -device pcie-root-port,chassis=1,id=pci.1"
" -device virtio-scsi-pci,id=scsi0,bus=pci.1"
" -drive file="
"tests/data/uefi-boot-images/bios-tables-test.aarch64.iso.qcow2,"
"if=none,media=cdrom,id=drive-scsi0-0-0-1,readonly=on"
" -device scsi-cd,bus=scsi0.0,scsi-id=0,"
"drive=drive-scsi0-0-0-1,id=scsi0-0-0-1,bootindex=1"
" -cpu cortex-a57"
" -device pxb-pcie,bus_nr=128",
&data);
free_test_data(&data);
}
#endif
static void test_acpi_tcg_acpi_hmat(const char *machine)
{
test_data data;
@ -1287,6 +1330,9 @@ int main(int argc, char *argv[])
qtest_add_func("acpi/virt", test_acpi_virt_tcg);
qtest_add_func("acpi/virt/numamem", test_acpi_virt_tcg_numamem);
qtest_add_func("acpi/virt/memhp", test_acpi_virt_tcg_memhp);
#ifdef CONFIG_PXB
qtest_add_func("acpi/virt/pxb", test_acpi_virt_tcg_pxb);
#endif
}
ret = g_test_run();
boot_sector_cleanup(disk);

View File

@ -34,7 +34,7 @@
#include "qemu/ctype.h"
#include "qemu/iov.h"
#include "standard-headers/linux/virtio_net.h"
#include "contrib/libvhost-user/libvhost-user.h"
#include "libvhost-user.h"
#define VHOST_USER_BRIDGE_DEBUG 1

View File

@ -35,7 +35,7 @@
#include <grp.h>
#include <unistd.h>
#include "contrib/libvhost-user/libvhost-user.h"
#include "libvhost-user.h"
struct fv_VuDev;
struct fv_QueueInfo {

View File

@ -8,8 +8,7 @@ executable('virtiofsd', files(
'helper.c',
'passthrough_ll.c',
'passthrough_seccomp.c'),
link_with: libvhost_user,
dependencies: [seccomp, qemuutil, libcap_ng],
dependencies: [seccomp, qemuutil, libcap_ng, vhost_user],
install: true,
install_dir: get_option('libexecdir'))