virtio,pc,pci: features, fixes, cleanups

CXL now can use Generic Port Affinity Structures.
 CXL now allows control of link speed and width
 vhost-user-blk now supports live resize, by means of
 a new device-sync-config command
 amd iommu now supports interrupt remapping
 pcie devices now report extended tag field support
 intel_iommu dropped support for Transient Mapping, to match VTD spec
 arch agnostic ACPI infrastructure for vCPU Hotplug
 
 Fixes, cleanups all over the place.
 
 Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
 -----BEGIN PGP SIGNATURE-----
 
 iQFDBAABCAAtFiEEXQn9CHHI+FuUyooNKB8NuNKNVGkFAmcpNqUPHG1zdEByZWRo
 YXQuY29tAAoJECgfDbjSjVRp/2oH/0qO33prhDa48J5mqT9NuJzzYwp5QHKF9Zjv
 fDAplMUEmfxZIEgJchcyDWPYTGX2geT4pCFhRWioZMIR/0JyzrFgSwsk1kL88cMh
 46gzhNVD6ybyPJ7O0Zq3GLy5jo7rlw/n+fFxKAuRCzcbK/fmH8gNC+RwW1IP64Na
 HDczYilHUhnO7yKZFQzQNQVbK4BckrG1bu0Fcx0EMUQBf4V6x7GLOrT+3hkKYcr6
 +DG5DmUmv20or/FXnu2Ye+MzR8Ebx6JVK3A3sXEE4Ns2CCzK9QLzeeyc2aU13jWN
 OpZ6WcKF8HqYprIwnSsMTxhPcq0/c7TvrGrazVwna5RUBMyjjvc=
 =zSX4
 -----END PGP SIGNATURE-----

Merge tag 'for_upstream' of https://git.kernel.org/pub/scm/virt/kvm/mst/qemu into staging

virtio,pc,pci: features, fixes, cleanups

CXL now can use Generic Port Affinity Structures.
CXL now allows control of link speed and width
vhost-user-blk now supports live resize, by means of
a new device-sync-config command
amd iommu now supports interrupt remapping
pcie devices now report extended tag field support
intel_iommu dropped support for Transient Mapping, to match VTD spec
arch agnostic ACPI infrastructure for vCPU Hotplug

Fixes, cleanups all over the place.

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

# -----BEGIN PGP SIGNATURE-----
#
# iQFDBAABCAAtFiEEXQn9CHHI+FuUyooNKB8NuNKNVGkFAmcpNqUPHG1zdEByZWRo
# YXQuY29tAAoJECgfDbjSjVRp/2oH/0qO33prhDa48J5mqT9NuJzzYwp5QHKF9Zjv
# fDAplMUEmfxZIEgJchcyDWPYTGX2geT4pCFhRWioZMIR/0JyzrFgSwsk1kL88cMh
# 46gzhNVD6ybyPJ7O0Zq3GLy5jo7rlw/n+fFxKAuRCzcbK/fmH8gNC+RwW1IP64Na
# HDczYilHUhnO7yKZFQzQNQVbK4BckrG1bu0Fcx0EMUQBf4V6x7GLOrT+3hkKYcr6
# +DG5DmUmv20or/FXnu2Ye+MzR8Ebx6JVK3A3sXEE4Ns2CCzK9QLzeeyc2aU13jWN
# OpZ6WcKF8HqYprIwnSsMTxhPcq0/c7TvrGrazVwna5RUBMyjjvc=
# =zSX4
# -----END PGP SIGNATURE-----
# gpg: Signature made Mon 04 Nov 2024 21:03:33 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

* tag 'for_upstream' of https://git.kernel.org/pub/scm/virt/kvm/mst/qemu: (65 commits)
  intel_iommu: Add missed reserved bit check for IEC descriptor
  intel_iommu: Add missed sanity check for 256-bit invalidation queue
  intel_iommu: Send IQE event when setting reserved bit in IQT_TAIL
  hw/acpi: Update GED with vCPU Hotplug VMSD for migration
  tests/qtest/bios-tables-test: Update DSDT golden masters for x86/{pc,q35}
  hw/acpi: Update ACPI `_STA` method with QOM vCPU ACPI Hotplug states
  qtest: allow ACPI DSDT Table changes
  hw/acpi: Make CPUs ACPI `presence` conditional during vCPU hot-unplug
  hw/pci: Add parenthesis to PCI_BUILD_BDF macro
  hw/cxl: Ensure there is enough data to read the input header in cmd_get_physical_port_state()
  hw/cxl: Ensure there is enough data for the header in cmd_ccls_set_lsa()
  hw/cxl: Check that writes do not go beyond end of target attributes
  hw/cxl: Ensuring enough data to read parameters in cmd_tunnel_management_cmd()
  hw/cxl: Avoid accesses beyond the end of cel_log.
  hw/cxl: Check the length of data requested fits in get_log()
  hw/cxl: Check enough data in cmd_firmware_update_transfer()
  hw/cxl: Check input length is large enough in cmd_events_clear_records()
  hw/cxl: Check input includes at least the header in cmd_features_set_feature()
  hw/cxl: Check size of input data to dynamic capacity mailbox commands
  hw/cxl/cxl-mailbox-util: Fix output buffer index update when retrieving DC extents
  ...

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2024-11-05 15:47:52 +00:00
commit 9eb9350c0e
90 changed files with 1121 additions and 430 deletions

View File

@ -167,6 +167,8 @@ A vring address description
Note that a ring address is an IOVA if ``VIRTIO_F_IOMMU_PLATFORM`` has
been negotiated. Otherwise it is a user address.
.. _memory_region_description:
Memory region description
^^^^^^^^^^^^^^^^^^^^^^^^^
@ -180,7 +182,7 @@ Memory region description
:user address: a 64-bit user address
:mmap offset: 64-bit offset where region starts in the mapped memory
:mmap offset: a 64-bit offset where region starts in the mapped memory
When the ``VHOST_USER_PROTOCOL_F_XEN_MMAP`` protocol feature has been
successfully negotiated, the memory region description contains two extra
@ -190,7 +192,7 @@ fields at the end.
| guest address | size | user address | mmap offset | xen mmap flags | domid |
+---------------+------+--------------+-------------+----------------+-------+
:xen mmap flags: 32-bit bit field
:xen mmap flags: a 32-bit bit field
- Bit 0 is set for Xen foreign memory mapping.
- Bit 1 is set for Xen grant memory mapping.
@ -211,7 +213,7 @@ Single memory region description
:padding: 64-bit
A region is represented by Memory region description.
:region: region is represented by :ref:`Memory region description <memory_region_description>`.
Multiple Memory regions description
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -224,7 +226,7 @@ Multiple Memory regions description
:padding: 32-bit
A region is represented by Memory region description.
:regions: regions field contains 8 regions of type :ref:`Memory region description <memory_region_description>`.
Log description
^^^^^^^^^^^^^^^
@ -233,9 +235,9 @@ Log description
| log size | log offset |
+----------+------------+
:log size: size of area used for logging
:log size: a 64-bit size of area used for logging
:log offset: offset from start of supplied file descriptor where
:log offset: a 64-bit offset from start of supplied file descriptor where
logging starts (i.e. where guest address 0 would be
logged)
@ -382,7 +384,7 @@ the kernel implementation.
The communication consists of the *front-end* sending message requests and
the *back-end* sending message replies. Most of the requests don't require
replies. Here is a list of the ones that do:
replies, except for the following requests:
* ``VHOST_USER_GET_FEATURES``
* ``VHOST_USER_GET_PROTOCOL_FEATURES``
@ -1239,11 +1241,11 @@ Front-end message types
(*a vring descriptor index for split virtqueues* vs. *vring descriptor
indices for packed virtqueues*).
When and as long as all of a devices vrings are stopped, it is
When and as long as all of a device's vrings are stopped, it is
*suspended*, see :ref:`Suspended device state
<suspended_device_state>`.
The request payloads *num* field is currently reserved and must be
The request payload's *num* field is currently reserved and must be
set to 0.
``VHOST_USER_SET_VRING_KICK``
@ -1662,7 +1664,7 @@ Front-end message types
:reply payload: ``u64``
Front-end and back-end negotiate a channel over which to transfer the
back-ends internal state during migration. Either side (front-end or
back-end's internal state during migration. Either side (front-end or
back-end) may create the channel. The nature of this channel is not
restricted or defined in this document, but whichever side creates it
must create a file descriptor that is provided to the respectively
@ -1714,7 +1716,7 @@ Front-end message types
:request payload: N/A
:reply payload: ``u64``
After transferring the back-ends internal state during migration (see
After transferring the back-end's internal state during migration (see
the :ref:`Migrating back-end state <migrating_backend_state>`
section), check whether the back-end was able to successfully fully
process the state.

View File

@ -1,148 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. All rights reserved
*/
#include "qemu/osdep.h"
#include "hw/acpi/acpi_generic_initiator.h"
#include "hw/acpi/aml-build.h"
#include "hw/boards.h"
#include "hw/pci/pci_device.h"
#include "qemu/error-report.h"
typedef struct AcpiGenericInitiatorClass {
ObjectClass parent_class;
} AcpiGenericInitiatorClass;
OBJECT_DEFINE_TYPE_WITH_INTERFACES(AcpiGenericInitiator, acpi_generic_initiator,
ACPI_GENERIC_INITIATOR, OBJECT,
{ TYPE_USER_CREATABLE },
{ NULL })
OBJECT_DECLARE_SIMPLE_TYPE(AcpiGenericInitiator, ACPI_GENERIC_INITIATOR)
static void acpi_generic_initiator_init(Object *obj)
{
AcpiGenericInitiator *gi = ACPI_GENERIC_INITIATOR(obj);
gi->node = MAX_NODES;
gi->pci_dev = NULL;
}
static void acpi_generic_initiator_finalize(Object *obj)
{
AcpiGenericInitiator *gi = ACPI_GENERIC_INITIATOR(obj);
g_free(gi->pci_dev);
}
static void acpi_generic_initiator_set_pci_device(Object *obj, const char *val,
Error **errp)
{
AcpiGenericInitiator *gi = ACPI_GENERIC_INITIATOR(obj);
gi->pci_dev = g_strdup(val);
}
static void acpi_generic_initiator_set_node(Object *obj, Visitor *v,
const char *name, void *opaque,
Error **errp)
{
AcpiGenericInitiator *gi = ACPI_GENERIC_INITIATOR(obj);
MachineState *ms = MACHINE(qdev_get_machine());
uint32_t value;
if (!visit_type_uint32(v, name, &value, errp)) {
return;
}
if (value >= MAX_NODES) {
error_printf("%s: Invalid NUMA node specified\n",
TYPE_ACPI_GENERIC_INITIATOR);
exit(1);
}
gi->node = value;
ms->numa_state->nodes[gi->node].has_gi = true;
}
static void acpi_generic_initiator_class_init(ObjectClass *oc, void *data)
{
object_class_property_add_str(oc, "pci-dev", NULL,
acpi_generic_initiator_set_pci_device);
object_class_property_add(oc, "node", "int", NULL,
acpi_generic_initiator_set_node, NULL, NULL);
}
/*
* ACPI 6.3:
* Table 5-78 Generic Initiator Affinity Structure
*/
static void
build_srat_generic_pci_initiator_affinity(GArray *table_data, int node,
PCIDeviceHandle *handle)
{
uint8_t index;
build_append_int_noprefix(table_data, 5, 1); /* Type */
build_append_int_noprefix(table_data, 32, 1); /* Length */
build_append_int_noprefix(table_data, 0, 1); /* Reserved */
build_append_int_noprefix(table_data, 1, 1); /* Device Handle Type: PCI */
build_append_int_noprefix(table_data, node, 4); /* Proximity Domain */
/* Device Handle - PCI */
build_append_int_noprefix(table_data, handle->segment, 2);
build_append_int_noprefix(table_data, handle->bdf, 2);
for (index = 0; index < 12; index++) {
build_append_int_noprefix(table_data, 0, 1);
}
build_append_int_noprefix(table_data, GEN_AFFINITY_ENABLED, 4); /* Flags */
build_append_int_noprefix(table_data, 0, 4); /* Reserved */
}
static int build_all_acpi_generic_initiators(Object *obj, void *opaque)
{
MachineState *ms = MACHINE(qdev_get_machine());
AcpiGenericInitiator *gi;
GArray *table_data = opaque;
PCIDeviceHandle dev_handle;
PCIDevice *pci_dev;
Object *o;
if (!object_dynamic_cast(obj, TYPE_ACPI_GENERIC_INITIATOR)) {
return 0;
}
gi = ACPI_GENERIC_INITIATOR(obj);
if (gi->node >= ms->numa_state->num_nodes) {
error_printf("%s: Specified node %d is invalid.\n",
TYPE_ACPI_GENERIC_INITIATOR, gi->node);
exit(1);
}
o = object_resolve_path_type(gi->pci_dev, TYPE_PCI_DEVICE, NULL);
if (!o) {
error_printf("%s: Specified device must be a PCI device.\n",
TYPE_ACPI_GENERIC_INITIATOR);
exit(1);
}
pci_dev = PCI_DEVICE(o);
dev_handle.segment = 0;
dev_handle.bdf = PCI_BUILD_BDF(pci_bus_num(pci_get_bus(pci_dev)),
pci_dev->devfn);
build_srat_generic_pci_initiator_affinity(table_data,
gi->node, &dev_handle);
return 0;
}
void build_srat_generic_pci_initiator(GArray *table_data)
{
object_child_foreach_recursive(object_get_root(),
build_all_acpi_generic_initiators,
table_data);
}

View File

@ -1937,6 +1937,89 @@ void build_srat_memory(GArray *table_data, uint64_t base,
build_append_int_noprefix(table_data, 0, 8); /* Reserved */
}
/*
* ACPI Spec Revision 6.3
* Table 5-80 Device Handle - PCI
*/
static void build_append_srat_pci_device_handle(GArray *table_data,
uint16_t segment,
uint8_t bus, uint8_t devfn)
{
/* PCI segment number */
build_append_int_noprefix(table_data, segment, 2);
/* PCI Bus Device Function */
build_append_int_noprefix(table_data, bus, 1);
build_append_int_noprefix(table_data, devfn, 1);
/* Reserved */
build_append_int_noprefix(table_data, 0, 12);
}
static void build_append_srat_acpi_device_handle(GArray *table_data,
const char *hid,
uint32_t uid)
{
assert(strlen(hid) == 8);
/* Device Handle - ACPI */
for (int i = 0; i < sizeof(hid); i++) {
build_append_int_noprefix(table_data, hid[i], 1);
}
build_append_int_noprefix(table_data, uid, 4);
build_append_int_noprefix(table_data, 0, 4);
}
/*
* ACPI spec, Revision 6.3
* 5.2.16.6 Generic Initiator Affinity Structure
* With PCI Device Handle.
*/
void build_srat_pci_generic_initiator(GArray *table_data, uint32_t node,
uint16_t segment, uint8_t bus,
uint8_t devfn)
{
/* Type */
build_append_int_noprefix(table_data, 5, 1);
/* Length */
build_append_int_noprefix(table_data, 32, 1);
/* Reserved */
build_append_int_noprefix(table_data, 0, 1);
/* Device Handle Type: PCI */
build_append_int_noprefix(table_data, 1, 1);
/* Proximity Domain */
build_append_int_noprefix(table_data, node, 4);
/* Device Handle */
build_append_srat_pci_device_handle(table_data, segment, bus, devfn);
/* Flags - GI Enabled */
build_append_int_noprefix(table_data, 1, 4);
/* Reserved */
build_append_int_noprefix(table_data, 0, 4);
}
/*
* ACPI spec, Revision 6.5
* 5.2.16.7 Generic Port Affinity Structure
* With ACPI Device Handle.
*/
void build_srat_acpi_generic_port(GArray *table_data, uint32_t node,
const char *hid, uint32_t uid)
{
/* Type */
build_append_int_noprefix(table_data, 6, 1);
/* Length */
build_append_int_noprefix(table_data, 32, 1);
/* Reserved */
build_append_int_noprefix(table_data, 0, 1);
/* Device Handle Type: ACPI */
build_append_int_noprefix(table_data, 0, 1);
/* Proximity Domain */
build_append_int_noprefix(table_data, node, 4);
/* Device Handle */
build_append_srat_acpi_device_handle(table_data, hid, uid);
/* Flags - GP Enabled */
build_append_int_noprefix(table_data, 1, 4);
/* Reserved */
build_append_int_noprefix(table_data, 0, 4);
}
/*
* ACPI spec 5.2.17 System Locality Distance Information Table
* (Revision 2.0 or later)

View File

@ -50,6 +50,18 @@ void acpi_cpu_ospm_status(CPUHotplugState *cpu_st, ACPIOSTInfoList ***list)
}
}
static bool check_cpu_enabled_status(DeviceState *dev)
{
CPUClass *k = dev ? CPU_GET_CLASS(dev) : NULL;
CPUState *cpu = CPU(dev);
if (cpu && (!k->cpu_enabled_status || k->cpu_enabled_status(cpu))) {
return true;
}
return false;
}
static uint64_t cpu_hotplug_rd(void *opaque, hwaddr addr, unsigned size)
{
uint64_t val = 0;
@ -63,10 +75,11 @@ static uint64_t cpu_hotplug_rd(void *opaque, hwaddr addr, unsigned size)
cdev = &cpu_st->devs[cpu_st->selector];
switch (addr) {
case ACPI_CPU_FLAGS_OFFSET_RW: /* pack and return is_* fields */
val |= cdev->cpu ? 1 : 0;
val |= check_cpu_enabled_status(DEVICE(cdev->cpu)) ? 1 : 0;
val |= cdev->is_inserting ? 2 : 0;
val |= cdev->is_removing ? 4 : 0;
val |= cdev->fw_remove ? 16 : 0;
val |= cdev->cpu ? 32 : 0;
trace_cpuhp_acpi_read_flags(cpu_st->selector, val);
break;
case ACPI_CPU_CMD_DATA_OFFSET_RW:
@ -233,6 +246,17 @@ void cpu_hotplug_hw_init(MemoryRegion *as, Object *owner,
memory_region_add_subregion(as, base_addr, &state->ctrl_reg);
}
static bool should_remain_acpi_present(DeviceState *dev)
{
CPUClass *k = CPU_GET_CLASS(dev);
/*
* A system may contain CPUs that are always present on one die, NUMA node,
* or socket, yet may be non-present on another simultaneously. Check from
* architecture specific code.
*/
return k->cpu_persistent_status && k->cpu_persistent_status(CPU(dev));
}
static AcpiCpuStatus *get_cpu_status(CPUHotplugState *cpu_st, DeviceState *dev)
{
CPUClass *k = CPU_GET_CLASS(dev);
@ -289,7 +313,9 @@ void acpi_cpu_unplug_cb(CPUHotplugState *cpu_st,
return;
}
cdev->cpu = NULL;
if (!should_remain_acpi_present(dev)) {
cdev->cpu = NULL;
}
}
static const VMStateDescription vmstate_cpuhp_sts = {
@ -336,6 +362,7 @@ const VMStateDescription vmstate_cpu_hotplug = {
#define CPU_REMOVE_EVENT "CRMV"
#define CPU_EJECT_EVENT "CEJ0"
#define CPU_FW_EJECT_EVENT "CEJF"
#define CPU_PRESENT "CPRS"
void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts,
build_madt_cpu_fn build_madt_cpu, hwaddr base_addr,
@ -396,7 +423,9 @@ void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts,
aml_append(field, aml_named_field(CPU_EJECT_EVENT, 1));
/* 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));
/* 1 if present, read only */
aml_append(field, aml_named_field(CPU_PRESENT, 1));
aml_append(field, aml_reserved_field(2));
aml_append(field, aml_named_field(CPU_COMMAND, 8));
aml_append(cpu_ctrl_dev, field);
@ -426,6 +455,7 @@ void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts,
Aml *ctrl_lock = aml_name("%s.%s", cphp_res_path, CPU_LOCK);
Aml *cpu_selector = aml_name("%s.%s", cphp_res_path, CPU_SELECTOR);
Aml *is_enabled = aml_name("%s.%s", cphp_res_path, CPU_ENABLED);
Aml *is_present = aml_name("%s.%s", cphp_res_path, CPU_PRESENT);
Aml *cpu_cmd = aml_name("%s.%s", cphp_res_path, CPU_COMMAND);
Aml *cpu_data = aml_name("%s.%s", cphp_res_path, CPU_DATA);
Aml *ins_evt = aml_name("%s.%s", cphp_res_path, CPU_INSERT_EVENT);
@ -454,13 +484,26 @@ void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts,
{
Aml *idx = aml_arg(0);
Aml *sta = aml_local(0);
Aml *ifctx2;
Aml *else_ctx;
aml_append(method, aml_acquire(ctrl_lock, 0xFFFF));
aml_append(method, aml_store(idx, cpu_selector));
aml_append(method, aml_store(zero, sta));
ifctx = aml_if(aml_equal(is_enabled, one));
ifctx = aml_if(aml_equal(is_present, one));
{
aml_append(ifctx, aml_store(aml_int(0xF), sta));
ifctx2 = aml_if(aml_equal(is_enabled, one));
{
/* cpu is present and enabled */
aml_append(ifctx2, aml_store(aml_int(0xF), sta));
}
aml_append(ifctx, ifctx2);
else_ctx = aml_else();
{
/* cpu is present but disabled */
aml_append(else_ctx, aml_store(aml_int(0xD), sta));
}
aml_append(ifctx, else_ctx);
}
aml_append(method, ifctx);
aml_append(method, aml_release(ctrl_lock));

View File

@ -331,6 +331,24 @@ static const VMStateDescription vmstate_memhp_state = {
}
};
static bool cpuhp_needed(void *opaque)
{
MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine());
return mc->has_hotpluggable_cpus;
}
static const VMStateDescription vmstate_cpuhp_state = {
.name = "acpi-ged/cpuhp",
.version_id = 1,
.minimum_version_id = 1,
.needed = cpuhp_needed,
.fields = (VMStateField[]) {
VMSTATE_CPU_HOTPLUG(cpuhp_state, AcpiGedState),
VMSTATE_END_OF_LIST()
}
};
static const VMStateDescription vmstate_ged_state = {
.name = "acpi-ged-state",
.version_id = 1,
@ -379,6 +397,7 @@ static const VMStateDescription vmstate_acpi_ged = {
},
.subsections = (const VMStateDescription * const []) {
&vmstate_memhp_state,
&vmstate_cpuhp_state,
&vmstate_ghes_state,
NULL
}

View File

@ -1,6 +1,5 @@
acpi_ss = ss.source_set()
acpi_ss.add(files(
'acpi_generic_initiator.c',
'acpi_interface.c',
'aml-build.c',
'bios-linker-loader.c',

View File

@ -24,8 +24,14 @@
*/
#include "qemu/osdep.h"
#include "qemu/error-report.h"
#include "qom/object_interfaces.h"
#include "qapi/error.h"
#include "hw/boards.h"
#include "hw/acpi/aml-build.h"
#include "hw/acpi/pci.h"
#include "hw/pci/pci_bridge.h"
#include "hw/pci/pci_device.h"
#include "hw/pci/pcie_host.h"
/*
@ -59,3 +65,239 @@ void build_mcfg(GArray *table_data, BIOSLinker *linker, AcpiMcfgInfo *info,
acpi_table_end(linker, &table);
}
typedef struct AcpiGenericInitiator {
/* private */
Object parent;
/* public */
char *pci_dev;
uint32_t node;
} AcpiGenericInitiator;
typedef struct AcpiGenericInitiatorClass {
ObjectClass parent_class;
} AcpiGenericInitiatorClass;
#define TYPE_ACPI_GENERIC_INITIATOR "acpi-generic-initiator"
OBJECT_DEFINE_TYPE_WITH_INTERFACES(AcpiGenericInitiator, acpi_generic_initiator,
ACPI_GENERIC_INITIATOR, OBJECT,
{ TYPE_USER_CREATABLE },
{ NULL })
OBJECT_DECLARE_SIMPLE_TYPE(AcpiGenericInitiator, ACPI_GENERIC_INITIATOR)
static void acpi_generic_initiator_init(Object *obj)
{
AcpiGenericInitiator *gi = ACPI_GENERIC_INITIATOR(obj);
gi->node = MAX_NODES;
gi->pci_dev = NULL;
}
static void acpi_generic_initiator_finalize(Object *obj)
{
AcpiGenericInitiator *gi = ACPI_GENERIC_INITIATOR(obj);
g_free(gi->pci_dev);
}
static void acpi_generic_initiator_set_pci_device(Object *obj, const char *val,
Error **errp)
{
AcpiGenericInitiator *gi = ACPI_GENERIC_INITIATOR(obj);
gi->pci_dev = g_strdup(val);
}
static void acpi_generic_initiator_set_node(Object *obj, Visitor *v,
const char *name, void *opaque,
Error **errp)
{
AcpiGenericInitiator *gi = ACPI_GENERIC_INITIATOR(obj);
MachineState *ms = MACHINE(qdev_get_machine());
uint32_t value;
if (!visit_type_uint32(v, name, &value, errp)) {
return;
}
if (value >= MAX_NODES) {
error_printf("%s: Invalid NUMA node specified\n",
TYPE_ACPI_GENERIC_INITIATOR);
exit(1);
}
gi->node = value;
ms->numa_state->nodes[gi->node].has_gi = true;
}
static void acpi_generic_initiator_class_init(ObjectClass *oc, void *data)
{
object_class_property_add_str(oc, "pci-dev", NULL,
acpi_generic_initiator_set_pci_device);
object_class_property_set_description(oc, "pci-dev",
"PCI device to associate with the node");
object_class_property_add(oc, "node", "int", NULL,
acpi_generic_initiator_set_node, NULL, NULL);
object_class_property_set_description(oc, "node",
"NUMA node associated with the PCI device");
}
static int build_acpi_generic_initiator(Object *obj, void *opaque)
{
MachineState *ms = MACHINE(qdev_get_machine());
AcpiGenericInitiator *gi;
GArray *table_data = opaque;
int32_t devfn;
uint8_t bus;
Object *o;
if (!object_dynamic_cast(obj, TYPE_ACPI_GENERIC_INITIATOR)) {
return 0;
}
gi = ACPI_GENERIC_INITIATOR(obj);
if (gi->node >= ms->numa_state->num_nodes) {
error_printf("%s: Specified node %d is invalid.\n",
TYPE_ACPI_GENERIC_INITIATOR, gi->node);
exit(1);
}
o = object_resolve_path_type(gi->pci_dev, TYPE_PCI_DEVICE, NULL);
if (!o) {
error_printf("%s: Specified device must be a PCI device.\n",
TYPE_ACPI_GENERIC_INITIATOR);
exit(1);
}
bus = object_property_get_uint(o, "busnr", &error_fatal);
devfn = object_property_get_uint(o, "addr", &error_fatal);
/* devfn is constrained in PCI to be 8 bit but storage is an int32_t */
assert(devfn >= 0 && devfn < PCI_DEVFN_MAX);
build_srat_pci_generic_initiator(table_data, gi->node, 0, bus, devfn);
return 0;
}
typedef struct AcpiGenericPort {
/* private */
Object parent;
/* public */
char *pci_bus;
uint32_t node;
} AcpiGenericPort;
typedef struct AcpiGenericPortClass {
ObjectClass parent_class;
} AcpiGenericPortClass;
#define TYPE_ACPI_GENERIC_PORT "acpi-generic-port"
OBJECT_DEFINE_TYPE_WITH_INTERFACES(AcpiGenericPort, acpi_generic_port,
ACPI_GENERIC_PORT, OBJECT,
{ TYPE_USER_CREATABLE },
{ NULL })
OBJECT_DECLARE_SIMPLE_TYPE(AcpiGenericPort, ACPI_GENERIC_PORT)
static void acpi_generic_port_init(Object *obj)
{
AcpiGenericPort *gp = ACPI_GENERIC_PORT(obj);
gp->node = MAX_NODES;
gp->pci_bus = NULL;
}
static void acpi_generic_port_finalize(Object *obj)
{
AcpiGenericPort *gp = ACPI_GENERIC_PORT(obj);
g_free(gp->pci_bus);
}
static void acpi_generic_port_set_pci_bus(Object *obj, const char *val,
Error **errp)
{
AcpiGenericPort *gp = ACPI_GENERIC_PORT(obj);
gp->pci_bus = g_strdup(val);
}
static void acpi_generic_port_set_node(Object *obj, Visitor *v,
const char *name, void *opaque,
Error **errp)
{
AcpiGenericPort *gp = ACPI_GENERIC_PORT(obj);
uint32_t value;
if (!visit_type_uint32(v, name, &value, errp)) {
return;
}
if (value >= MAX_NODES) {
error_printf("%s: Invalid NUMA node specified\n",
TYPE_ACPI_GENERIC_INITIATOR);
exit(1);
}
gp->node = value;
}
static void acpi_generic_port_class_init(ObjectClass *oc, void *data)
{
object_class_property_add_str(oc, "pci-bus", NULL,
acpi_generic_port_set_pci_bus);
object_class_property_set_description(oc, "pci-bus",
"PCI Bus of the host bridge associated with this GP affinity structure");
object_class_property_add(oc, "node", "int", NULL,
acpi_generic_port_set_node, NULL, NULL);
object_class_property_set_description(oc, "node",
"The NUMA node like ID to index HMAT/SLIT NUMA properties involving GP");
}
static int build_acpi_generic_port(Object *obj, void *opaque)
{
MachineState *ms = MACHINE(qdev_get_machine());
const char *hid = "ACPI0016";
GArray *table_data = opaque;
AcpiGenericPort *gp;
uint32_t uid;
Object *o;
if (!object_dynamic_cast(obj, TYPE_ACPI_GENERIC_PORT)) {
return 0;
}
gp = ACPI_GENERIC_PORT(obj);
if (gp->node >= ms->numa_state->num_nodes) {
error_printf("%s: node %d is invalid.\n",
TYPE_ACPI_GENERIC_PORT, gp->node);
exit(1);
}
o = object_resolve_path_type(gp->pci_bus, TYPE_PXB_CXL_BUS, NULL);
if (!o) {
error_printf("%s: device must be a CXL host bridge.\n",
TYPE_ACPI_GENERIC_PORT);
exit(1);
}
uid = object_property_get_uint(o, "acpi_uid", &error_fatal);
build_srat_acpi_generic_port(table_data, gp->node, hid, uid);
return 0;
}
void build_srat_generic_affinity_structures(GArray *table_data)
{
object_child_foreach_recursive(object_get_root(),
build_acpi_generic_initiator,
table_data);
object_child_foreach_recursive(object_get_root(), build_acpi_generic_port,
table_data);
}

View File

@ -57,7 +57,6 @@
#include "migration/vmstate.h"
#include "hw/acpi/ghes.h"
#include "hw/acpi/viot.h"
#include "hw/acpi/acpi_generic_initiator.h"
#include "hw/virtio/virtio-acpi.h"
#include "target/arm/multiprocessing.h"
@ -511,7 +510,7 @@ build_srat(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
}
}
build_srat_generic_pci_initiator(table_data);
build_srat_generic_affinity_structures(table_data);
if (ms->nvdimms_state->is_enabled) {
nvdimm_build_srat(table_data);

View File

@ -90,27 +90,39 @@ static void vhost_user_blk_set_config(VirtIODevice *vdev, const uint8_t *config)
s->blkcfg.wce = blkcfg->wce;
}
static int vhost_user_blk_sync_config(DeviceState *dev, Error **errp)
{
int ret;
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
VHostUserBlk *s = VHOST_USER_BLK(vdev);
ret = vhost_dev_get_config(&s->dev, (uint8_t *)&s->blkcfg,
vdev->config_len, errp);
if (ret < 0) {
return ret;
}
memcpy(vdev->config, &s->blkcfg, vdev->config_len);
virtio_notify_config(vdev);
return 0;
}
static int vhost_user_blk_handle_config_change(struct vhost_dev *dev)
{
int ret;
VirtIODevice *vdev = dev->vdev;
VHostUserBlk *s = VHOST_USER_BLK(dev->vdev);
Error *local_err = NULL;
if (!dev->started) {
return 0;
}
ret = vhost_dev_get_config(dev, (uint8_t *)&s->blkcfg,
vdev->config_len, &local_err);
ret = vhost_user_blk_sync_config(DEVICE(dev->vdev), &local_err);
if (ret < 0) {
error_report_err(local_err);
return ret;
}
memcpy(dev->vdev->config, &s->blkcfg, vdev->config_len);
virtio_notify_config(dev->vdev);
return 0;
}
@ -579,6 +591,7 @@ static void vhost_user_blk_class_init(ObjectClass *klass, void *data)
device_class_set_props(dc, vhost_user_blk_properties);
dc->vmsd = &vmstate_vhost_user_blk;
dc->sync_config = vhost_user_blk_sync_config;
set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
vdc->realize = vhost_user_blk_device_realize;
vdc->unrealize = vhost_user_blk_device_unrealize;

View File

@ -34,7 +34,9 @@
#include "hw/virtio/virtio-iommu.h"
#include "audio/audio.h"
GlobalProperty hw_compat_9_1[] = {};
GlobalProperty hw_compat_9_1[] = {
{ TYPE_PCI_DEVICE, "x-pcie-ext-tag", "false" },
};
const size_t hw_compat_9_1_len = G_N_ELEMENTS(hw_compat_9_1);
GlobalProperty hw_compat_9_0[] = {

View File

@ -151,6 +151,9 @@ static CXLRetCode cmd_tunnel_management_cmd(const struct cxl_cmd *cmd,
in = (void *)payload_in;
out = (void *)payload_out;
if (len_in < sizeof(*in)) {
return CXL_MBOX_INVALID_PAYLOAD_LENGTH;
}
/* Enough room for minimum sized message - no payload */
if (in->size < sizeof(in->ccimessage)) {
return CXL_MBOX_INVALID_PAYLOAD_LENGTH;
@ -266,6 +269,12 @@ static CXLRetCode cmd_events_clear_records(const struct cxl_cmd *cmd,
CXLClearEventPayload *pl;
pl = (CXLClearEventPayload *)payload_in;
if (len_in < sizeof(*pl) ||
len_in < sizeof(*pl) + sizeof(*pl->handle) * pl->nr_recs) {
return CXL_MBOX_INVALID_PAYLOAD_LENGTH;
}
*len_out = 0;
return cxl_event_clear_records(cxlds, pl);
}
@ -374,7 +383,7 @@ static CXLRetCode cmd_infostat_identify(const struct cxl_cmd *cmd,
uint16_t pcie_subsys_vid;
uint16_t pcie_subsys_id;
uint64_t sn;
uint8_t max_message_size;
uint8_t max_message_size;
uint8_t component_type;
} QEMU_PACKED *is_identify;
QEMU_BUILD_BUG_ON(sizeof(*is_identify) != 18);
@ -521,6 +530,9 @@ static CXLRetCode cmd_get_physical_port_state(const struct cxl_cmd *cmd,
in = (struct cxl_fmapi_get_phys_port_state_req_pl *)payload_in;
out = (struct cxl_fmapi_get_phys_port_state_resp_pl *)payload_out;
if (len_in < sizeof(*in)) {
return CXL_MBOX_INVALID_PAYLOAD_LENGTH;
}
/* Check if what was requested can fit */
if (sizeof(*out) + sizeof(*out->ports) * in->num_ports > cci->payload_max) {
return CXL_MBOX_INVALID_INPUT;
@ -649,9 +661,9 @@ static CXLRetCode cmd_firmware_update_get_info(const struct cxl_cmd *cmd,
} QEMU_PACKED *fw_info;
QEMU_BUILD_BUG_ON(sizeof(*fw_info) != 0x50);
if ((cxl_dstate->vmem_size < CXL_CAPACITY_MULTIPLIER) ||
(cxl_dstate->pmem_size < CXL_CAPACITY_MULTIPLIER) ||
(ct3d->dc.total_capacity < CXL_CAPACITY_MULTIPLIER)) {
if (!QEMU_IS_ALIGNED(cxl_dstate->vmem_size, CXL_CAPACITY_MULTIPLIER) ||
!QEMU_IS_ALIGNED(cxl_dstate->pmem_size, CXL_CAPACITY_MULTIPLIER) ||
!QEMU_IS_ALIGNED(ct3d->dc.total_capacity, CXL_CAPACITY_MULTIPLIER)) {
return CXL_MBOX_INTERNAL_ERROR;
}
@ -699,6 +711,10 @@ static CXLRetCode cmd_firmware_update_transfer(const struct cxl_cmd *cmd,
} QEMU_PACKED *fw_transfer = (void *)payload_in;
size_t offset, length;
if (len < sizeof(*fw_transfer)) {
return CXL_MBOX_INVALID_PAYLOAD_LENGTH;
}
if (fw_transfer->action == CXL_FW_XFER_ACTION_ABORT) {
/*
* At this point there aren't any on-going transfers
@ -927,17 +943,7 @@ static CXLRetCode cmd_logs_get_log(const struct cxl_cmd *cmd,
get_log = (void *)payload_in;
/*
* CXL r3.1 Section 8.2.9.5.2: Get Log (Opcode 0401h)
* The device shall return Invalid Input if the Offset or Length
* fields attempt to access beyond the size of the log as reported by Get
* Supported Logs.
*
* The CEL buffer is large enough to fit all commands in the emulation, so
* the only possible failure would be if the mailbox itself isn't big
* enough.
*/
if (get_log->offset + get_log->length > cci->payload_max) {
if (get_log->length > cci->payload_max) {
return CXL_MBOX_INVALID_INPUT;
}
@ -945,6 +951,20 @@ static CXLRetCode cmd_logs_get_log(const struct cxl_cmd *cmd,
return CXL_MBOX_INVALID_LOG;
}
/*
* CXL r3.1 Section 8.2.9.5.2: Get Log (Opcode 0401h)
* The device shall return Invalid Input if the Offset or Length
* fields attempt to access beyond the size of the log as reported by Get
* Supported Log.
*
* Only valid for there to be one entry per opcode, but the length + offset
* may still be greater than that if the inputs are not valid and so access
* beyond the end of cci->cel_log.
*/
if ((uint64_t)get_log->offset + get_log->length >= sizeof(cci->cel_log)) {
return CXL_MBOX_INVALID_INPUT;
}
/* Store off everything to local variables so we can wipe out the payload */
*len_out = get_log->length;
@ -1133,10 +1153,8 @@ static CXLRetCode cmd_features_get_supported(const struct cxl_cmd *cmd,
(struct CXLSupportedFeatureEntry) {
.uuid = ecs_uuid,
.feat_index = index,
.get_feat_size = CXL_ECS_NUM_MEDIA_FRUS *
sizeof(CXLMemECSReadAttrs),
.set_feat_size = CXL_ECS_NUM_MEDIA_FRUS *
sizeof(CXLMemECSWriteAttrs),
.get_feat_size = sizeof(CXLMemECSReadAttrs),
.set_feat_size = sizeof(CXLMemECSWriteAttrs),
.attr_flags = CXL_FEAT_ENTRY_ATTR_FLAG_CHANGABLE,
.get_feat_version = CXL_ECS_GET_FEATURE_VERSION,
.set_feat_version = CXL_ECS_SET_FEATURE_VERSION,
@ -1204,13 +1222,10 @@ static CXLRetCode cmd_features_get_feature(const struct cxl_cmd *cmd,
(uint8_t *)&ct3d->patrol_scrub_attrs + get_feature->offset,
bytes_to_copy);
} else if (qemu_uuid_is_equal(&get_feature->uuid, &ecs_uuid)) {
if (get_feature->offset >= CXL_ECS_NUM_MEDIA_FRUS *
sizeof(CXLMemECSReadAttrs)) {
if (get_feature->offset >= sizeof(CXLMemECSReadAttrs)) {
return CXL_MBOX_INVALID_INPUT;
}
bytes_to_copy = CXL_ECS_NUM_MEDIA_FRUS *
sizeof(CXLMemECSReadAttrs) -
get_feature->offset;
bytes_to_copy = sizeof(CXLMemECSReadAttrs) - get_feature->offset;
bytes_to_copy = MIN(bytes_to_copy, get_feature->count);
memcpy(payload_out,
(uint8_t *)&ct3d->ecs_attrs + get_feature->offset,
@ -1243,6 +1258,9 @@ static CXLRetCode cmd_features_set_feature(const struct cxl_cmd *cmd,
CXLType3Dev *ct3d;
uint16_t count;
if (len_in < sizeof(*hdr)) {
return CXL_MBOX_INVALID_PAYLOAD_LENGTH;
}
if (!object_dynamic_cast(OBJECT(cci->d), TYPE_CXL_TYPE3)) {
return CXL_MBOX_UNSUPPORTED;
@ -1277,6 +1295,11 @@ static CXLRetCode cmd_features_set_feature(const struct cxl_cmd *cmd,
ps_set_feature = (void *)payload_in;
ps_write_attrs = &ps_set_feature->feat_data;
if ((uint32_t)hdr->offset + bytes_to_copy >
sizeof(ct3d->patrol_scrub_wr_attrs)) {
return CXL_MBOX_INVALID_PAYLOAD_LENGTH;
}
memcpy((uint8_t *)&ct3d->patrol_scrub_wr_attrs + hdr->offset,
ps_write_attrs,
bytes_to_copy);
@ -1299,18 +1322,22 @@ static CXLRetCode cmd_features_set_feature(const struct cxl_cmd *cmd,
ecs_set_feature = (void *)payload_in;
ecs_write_attrs = ecs_set_feature->feat_data;
memcpy((uint8_t *)ct3d->ecs_wr_attrs + hdr->offset,
if ((uint32_t)hdr->offset + bytes_to_copy >
sizeof(ct3d->ecs_wr_attrs)) {
return CXL_MBOX_INVALID_PAYLOAD_LENGTH;
}
memcpy((uint8_t *)&ct3d->ecs_wr_attrs + hdr->offset,
ecs_write_attrs,
bytes_to_copy);
set_feat_info->data_size += bytes_to_copy;
if (data_transfer_flag == CXL_SET_FEATURE_FLAG_FULL_DATA_TRANSFER ||
data_transfer_flag == CXL_SET_FEATURE_FLAG_FINISH_DATA_TRANSFER) {
ct3d->ecs_attrs.ecs_log_cap = ct3d->ecs_wr_attrs.ecs_log_cap;
for (count = 0; count < CXL_ECS_NUM_MEDIA_FRUS; count++) {
ct3d->ecs_attrs[count].ecs_log_cap =
ct3d->ecs_wr_attrs[count].ecs_log_cap;
ct3d->ecs_attrs[count].ecs_config =
ct3d->ecs_wr_attrs[count].ecs_config & 0x1F;
ct3d->ecs_attrs.fru_attrs[count].ecs_config =
ct3d->ecs_wr_attrs.fru_attrs[count].ecs_config & 0x1F;
}
}
} else {
@ -1324,7 +1351,7 @@ static CXLRetCode cmd_features_set_feature(const struct cxl_cmd *cmd,
if (qemu_uuid_is_equal(&hdr->uuid, &patrol_scrub_uuid)) {
memset(&ct3d->patrol_scrub_wr_attrs, 0, set_feat_info->data_size);
} else if (qemu_uuid_is_equal(&hdr->uuid, &ecs_uuid)) {
memset(ct3d->ecs_wr_attrs, 0, set_feat_info->data_size);
memset(&ct3d->ecs_wr_attrs, 0, set_feat_info->data_size);
}
set_feat_info->data_transfer_flag = 0;
set_feat_info->data_saved_across_reset = false;
@ -1445,7 +1472,7 @@ static CXLRetCode cmd_ccls_get_lsa(const struct cxl_cmd *cmd,
} QEMU_PACKED *get_lsa;
CXLType3Dev *ct3d = CXL_TYPE3(cci->d);
CXLType3Class *cvc = CXL_TYPE3_GET_CLASS(ct3d);
uint32_t offset, length;
uint64_t offset, length;
get_lsa = (void *)payload_in;
offset = get_lsa->offset;
@ -1479,8 +1506,8 @@ static CXLRetCode cmd_ccls_set_lsa(const struct cxl_cmd *cmd,
const size_t hdr_len = offsetof(struct set_lsa_pl, data);
*len_out = 0;
if (!len_in) {
return CXL_MBOX_SUCCESS;
if (len_in < hdr_len) {
return CXL_MBOX_INVALID_PAYLOAD_LENGTH;
}
if (set_lsa_payload->offset + len_in > cvc->get_lsa_size(ct3d) + hdr_len) {
@ -2233,6 +2260,7 @@ static CXLRetCode cmd_dcd_get_dyn_cap_ext_list(const struct cxl_cmd *cmd,
stw_le_p(&out_rec->shared_seq, ent->shared_seq);
record_done++;
out_rec++;
if (record_done == record_count) {
break;
}
@ -2470,11 +2498,20 @@ static CXLRetCode cmd_dcd_add_dyn_cap_rsp(const struct cxl_cmd *cmd,
uint64_t dpa, len;
CXLRetCode ret;
if (len_in < sizeof(*in)) {
return CXL_MBOX_INVALID_PAYLOAD_LENGTH;
}
if (in->num_entries_updated == 0) {
cxl_extent_group_list_delete_front(&ct3d->dc.extents_pending);
return CXL_MBOX_SUCCESS;
}
if (len_in <
sizeof(*in) + sizeof(*in->updated_entries) * in->num_entries_updated) {
return CXL_MBOX_INVALID_PAYLOAD_LENGTH;
}
/* Adding extents causes exceeding device's extent tracking ability. */
if (in->num_entries_updated + ct3d->dc.total_extent_count >
CXL_NUM_EXTENTS_SUPPORTED) {
@ -2629,10 +2666,19 @@ static CXLRetCode cmd_dcd_release_dyn_cap(const struct cxl_cmd *cmd,
uint32_t updated_list_size;
CXLRetCode ret;
if (len_in < sizeof(*in)) {
return CXL_MBOX_INVALID_PAYLOAD_LENGTH;
}
if (in->num_entries_updated == 0) {
return CXL_MBOX_INVALID_INPUT;
}
if (len_in <
sizeof(*in) + sizeof(*in->updated_entries) * in->num_entries_updated) {
return CXL_MBOX_INVALID_PAYLOAD_LENGTH;
}
ret = cxl_detect_malformed_extent_list(ct3d, in);
if (ret != CXL_MBOX_SUCCESS) {
return ret;
@ -2879,7 +2925,8 @@ static void bg_timercb(void *opaque)
}
} else {
/* estimate only */
cci->bg.complete_pct = 100 * now / total_time;
cci->bg.complete_pct =
100 * (now - cci->bg.starttime) / cci->bg.runtime;
timer_mod(cci->bg.timer, now + CXL_MBOX_BG_UPDATE_FREQ);
}

View File

@ -68,7 +68,6 @@
#include "hw/acpi/utils.h"
#include "hw/acpi/pci.h"
#include "hw/acpi/cxl.h"
#include "hw/acpi/acpi_generic_initiator.h"
#include "qom/qom-qobject.h"
#include "hw/i386/amd_iommu.h"
@ -741,7 +740,8 @@ static Aml *build_prt(bool is_pci0_prt)
int pin;
method = aml_method("_PRT", 0, AML_NOTSERIALIZED);
rt_pkg = aml_varpackage(nroutes);
assert(nroutes < 256);
rt_pkg = aml_package(nroutes);
for (pin = 0; pin < nroutes; pin++) {
Aml *pkg = aml_package(4);
@ -1476,6 +1476,7 @@ build_dsdt(GArray *table_data, BIOSLinker *linker,
QLIST_FOREACH(bus, &bus->child, sibling) {
uint8_t bus_num = pci_bus_num(bus);
uint8_t numa_node = pci_bus_numa_node(bus);
uint32_t uid;
/* look only for expander root buses */
if (!pci_bus_is_root(bus)) {
@ -1486,6 +1487,8 @@ build_dsdt(GArray *table_data, BIOSLinker *linker,
root_bus_limit = bus_num - 1;
}
uid = object_property_get_uint(OBJECT(bus), "acpi_uid",
&error_fatal);
scope = aml_scope("\\_SB");
if (pci_bus_is_cxl(bus)) {
@ -1493,7 +1496,7 @@ build_dsdt(GArray *table_data, BIOSLinker *linker,
} else {
dev = aml_device("PC%.02X", bus_num);
}
aml_append(dev, aml_name_decl("_UID", aml_int(bus_num)));
aml_append(dev, aml_name_decl("_UID", aml_int(uid)));
aml_append(dev, aml_name_decl("_BBN", aml_int(bus_num)));
if (pci_bus_is_cxl(bus)) {
struct Aml *aml_pkg = aml_package(2);
@ -1971,7 +1974,7 @@ build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine)
build_srat_memory(table_data, 0, 0, 0, MEM_AFFINITY_NOFLAGS);
}
build_srat_generic_pci_initiator(table_data);
build_srat_generic_affinity_structures(table_data);
/*
* Entry is required for Windows to enable memory hotplug in OS
@ -2321,7 +2324,7 @@ build_amd_iommu(GArray *table_data, BIOSLinker *linker, const char *oem_id,
/* Capability offset */
build_append_int_noprefix(table_data, s->pci.capab_offset, 2);
/* IOMMU base address */
build_append_int_noprefix(table_data, s->mmio.addr, 8);
build_append_int_noprefix(table_data, s->mr_mmio.addr, 8);
/* PCI Segment Group */
build_append_int_noprefix(table_data, 0, 2);
/* IOMMU info */
@ -2356,7 +2359,7 @@ build_amd_iommu(GArray *table_data, BIOSLinker *linker, const char *oem_id,
/* Capability offset */
build_append_int_noprefix(table_data, s->pci.capab_offset, 2);
/* IOMMU base address */
build_append_int_noprefix(table_data, s->mmio.addr, 8);
build_append_int_noprefix(table_data, s->mr_mmio.addr, 8);
/* PCI Segment Group */
build_append_int_noprefix(table_data, 0, 2);
/* IOMMU info */

View File

@ -32,6 +32,7 @@
#include "trace.h"
#include "hw/i386/apic-msidef.h"
#include "hw/qdev-properties.h"
#include "kvm/kvm_i386.h"
/* used AMD-Vi MMIO registers */
const char *amdvi_mmio_low[] = {
@ -60,8 +61,9 @@ struct AMDVIAddressSpace {
uint8_t bus_num; /* bus number */
uint8_t devfn; /* device function */
AMDVIState *iommu_state; /* AMDVI - one per machine */
MemoryRegion root; /* AMDVI Root memory map region */
MemoryRegion root; /* AMDVI Root memory map region */
IOMMUMemoryRegion iommu; /* Device's address translation region */
MemoryRegion iommu_nodma; /* Alias of shared nodma memory region */
MemoryRegion iommu_ir; /* Device's interrupt remapping region */
AddressSpace as; /* device's corresponding address space */
};
@ -430,6 +432,12 @@ static void amdvi_complete_ppr(AMDVIState *s, uint64_t *cmd)
trace_amdvi_ppr_exec();
}
static void amdvi_intremap_inval_notify_all(AMDVIState *s, bool global,
uint32_t index, uint32_t mask)
{
x86_iommu_iec_notify_all(X86_IOMMU_DEVICE(s), global, index, mask);
}
static void amdvi_inval_all(AMDVIState *s, uint64_t *cmd)
{
if (extract64(cmd[0], 0, 60) || cmd[1]) {
@ -437,6 +445,9 @@ static void amdvi_inval_all(AMDVIState *s, uint64_t *cmd)
s->cmdbuf + s->cmdbuf_head);
}
/* Notify global invalidation */
amdvi_intremap_inval_notify_all(s, true, 0, 0);
amdvi_iotlb_reset(s);
trace_amdvi_all_inval();
}
@ -485,6 +496,9 @@ static void amdvi_inval_inttable(AMDVIState *s, uint64_t *cmd)
return;
}
/* Notify global invalidation */
amdvi_intremap_inval_notify_all(s, true, 0, 0);
trace_amdvi_intr_inval();
}
@ -1412,6 +1426,7 @@ static AddressSpace *amdvi_host_dma_iommu(PCIBus *bus, void *opaque, int devfn)
AMDVIState *s = opaque;
AMDVIAddressSpace **iommu_as, *amdvi_dev_as;
int bus_num = pci_bus_num(bus);
X86IOMMUState *x86_iommu = X86_IOMMU_DEVICE(s);
iommu_as = s->address_spaces[bus_num];
@ -1436,13 +1451,13 @@ static AddressSpace *amdvi_host_dma_iommu(PCIBus *bus, void *opaque, int devfn)
* Memory region relationships looks like (Address range shows
* only lower 32 bits to make it short in length...):
*
* |-----------------+-------------------+----------|
* | Name | Address range | Priority |
* |-----------------+-------------------+----------+
* | amdvi_root | 00000000-ffffffff | 0 |
* | amdvi_iommu | 00000000-ffffffff | 1 |
* | amdvi_iommu_ir | fee00000-feefffff | 64 |
* |-----------------+-------------------+----------|
* |--------------------+-------------------+----------|
* | Name | Address range | Priority |
* |--------------------+-------------------+----------+
* | amdvi-root | 00000000-ffffffff | 0 |
* | amdvi-iommu_nodma | 00000000-ffffffff | 0 |
* | amdvi-iommu_ir | fee00000-feefffff | 1 |
* |--------------------+-------------------+----------|
*/
memory_region_init_iommu(&amdvi_dev_as->iommu,
sizeof(amdvi_dev_as->iommu),
@ -1452,16 +1467,34 @@ static AddressSpace *amdvi_host_dma_iommu(PCIBus *bus, void *opaque, int devfn)
memory_region_init(&amdvi_dev_as->root, OBJECT(s),
"amdvi_root", UINT64_MAX);
address_space_init(&amdvi_dev_as->as, &amdvi_dev_as->root, name);
memory_region_init_io(&amdvi_dev_as->iommu_ir, OBJECT(s),
&amdvi_ir_ops, s, "amd_iommu_ir",
AMDVI_INT_ADDR_SIZE);
memory_region_add_subregion_overlap(&amdvi_dev_as->root,
AMDVI_INT_ADDR_FIRST,
&amdvi_dev_as->iommu_ir,
64);
memory_region_add_subregion_overlap(&amdvi_dev_as->root, 0,
MEMORY_REGION(&amdvi_dev_as->iommu),
1);
0);
/* Build the DMA Disabled alias to shared memory */
memory_region_init_alias(&amdvi_dev_as->iommu_nodma, OBJECT(s),
"amdvi-sys", &s->mr_sys, 0,
memory_region_size(&s->mr_sys));
memory_region_add_subregion_overlap(&amdvi_dev_as->root, 0,
&amdvi_dev_as->iommu_nodma,
0);
/* Build the Interrupt Remapping alias to shared memory */
memory_region_init_alias(&amdvi_dev_as->iommu_ir, OBJECT(s),
"amdvi-ir", &s->mr_ir, 0,
memory_region_size(&s->mr_ir));
memory_region_add_subregion_overlap(MEMORY_REGION(&amdvi_dev_as->iommu),
AMDVI_INT_ADDR_FIRST,
&amdvi_dev_as->iommu_ir, 1);
if (!x86_iommu->pt_supported) {
memory_region_set_enabled(&amdvi_dev_as->iommu_nodma, false);
memory_region_set_enabled(MEMORY_REGION(&amdvi_dev_as->iommu),
true);
} else {
memory_region_set_enabled(MEMORY_REGION(&amdvi_dev_as->iommu),
false);
memory_region_set_enabled(&amdvi_dev_as->iommu_nodma, true);
}
}
return &iommu_as[devfn]->as;
}
@ -1598,10 +1631,37 @@ static void amdvi_sysbus_realize(DeviceState *dev, Error **errp)
x86ms->ioapic_as = amdvi_host_dma_iommu(bus, s, AMDVI_IOAPIC_SB_DEVID);
/* set up MMIO */
memory_region_init_io(&s->mmio, OBJECT(s), &mmio_mem_ops, s, "amdvi-mmio",
AMDVI_MMIO_SIZE);
memory_region_init_io(&s->mr_mmio, OBJECT(s), &mmio_mem_ops, s,
"amdvi-mmio", AMDVI_MMIO_SIZE);
memory_region_add_subregion(get_system_memory(), AMDVI_BASE_ADDR,
&s->mmio);
&s->mr_mmio);
/* Create the share memory regions by all devices */
memory_region_init(&s->mr_sys, OBJECT(s), "amdvi-sys", UINT64_MAX);
/* set up the DMA disabled memory region */
memory_region_init_alias(&s->mr_nodma, OBJECT(s),
"amdvi-nodma", get_system_memory(), 0,
memory_region_size(get_system_memory()));
memory_region_add_subregion_overlap(&s->mr_sys, 0,
&s->mr_nodma, 0);
/* set up the Interrupt Remapping memory region */
memory_region_init_io(&s->mr_ir, OBJECT(s), &amdvi_ir_ops,
s, "amdvi-ir", AMDVI_INT_ADDR_SIZE);
memory_region_add_subregion_overlap(&s->mr_sys, AMDVI_INT_ADDR_FIRST,
&s->mr_ir, 1);
/* AMD IOMMU with x2APIC mode requires xtsup=on */
if (x86ms->apic_id_limit > 255 && !s->xtsup) {
error_report("AMD IOMMU with x2APIC confguration requires xtsup=on");
exit(EXIT_FAILURE);
}
if (s->xtsup && kvm_irqchip_is_split() && !kvm_enable_x2apic()) {
error_report("AMD IOMMU xtsup=on requires support on the KVM side");
exit(EXIT_FAILURE);
}
pci_setup_iommu(bus, &amdvi_iommu_ops, s);
amdvi_init(s);
}

View File

@ -353,7 +353,10 @@ struct AMDVIState {
uint32_t pprlog_head; /* ppr log head */
uint32_t pprlog_tail; /* ppr log tail */
MemoryRegion mmio; /* MMIO region */
MemoryRegion mr_mmio; /* MMIO region */
MemoryRegion mr_sys;
MemoryRegion mr_nodma;
MemoryRegion mr_ir;
uint8_t mmior[AMDVI_MMIO_SIZE]; /* read/write MMIO */
uint8_t w1cmask[AMDVI_MMIO_SIZE]; /* read/write 1 clear mask */
uint8_t romask[AMDVI_MMIO_SIZE]; /* MMIO read/only mask */

View File

@ -2532,15 +2532,51 @@ static bool vtd_get_inv_desc(IntelIOMMUState *s,
return true;
}
static bool vtd_inv_desc_reserved_check(IntelIOMMUState *s,
VTDInvDesc *inv_desc,
uint64_t mask[4], bool dw,
const char *func_name,
const char *desc_type)
{
if (s->iq_dw) {
if (inv_desc->val[0] & mask[0] || inv_desc->val[1] & mask[1] ||
inv_desc->val[2] & mask[2] || inv_desc->val[3] & mask[3]) {
error_report("%s: invalid %s desc val[3]: 0x%"PRIx64
" val[2]: 0x%"PRIx64" val[1]=0x%"PRIx64
" val[0]=0x%"PRIx64" (reserved nonzero)",
func_name, desc_type, inv_desc->val[3],
inv_desc->val[2], inv_desc->val[1],
inv_desc->val[0]);
return false;
}
} else {
if (dw) {
error_report("%s: 256-bit %s desc in 128-bit invalidation queue",
func_name, desc_type);
return false;
}
if (inv_desc->lo & mask[0] || inv_desc->hi & mask[1]) {
error_report("%s: invalid %s desc: hi=%"PRIx64", lo=%"PRIx64
" (reserved nonzero)", func_name, desc_type,
inv_desc->hi, inv_desc->lo);
return false;
}
}
return true;
}
static bool vtd_process_wait_desc(IntelIOMMUState *s, VTDInvDesc *inv_desc)
{
if ((inv_desc->hi & VTD_INV_DESC_WAIT_RSVD_HI) ||
(inv_desc->lo & VTD_INV_DESC_WAIT_RSVD_LO)) {
error_report_once("%s: invalid wait desc: hi=%"PRIx64", lo=%"PRIx64
" (reserved nonzero)", __func__, inv_desc->hi,
inv_desc->lo);
uint64_t mask[4] = {VTD_INV_DESC_WAIT_RSVD_LO, VTD_INV_DESC_WAIT_RSVD_HI,
VTD_INV_DESC_ALL_ONE, VTD_INV_DESC_ALL_ONE};
if (!vtd_inv_desc_reserved_check(s, inv_desc, mask, false,
__func__, "wait")) {
return false;
}
if (inv_desc->lo & VTD_INV_DESC_WAIT_SW) {
/* Status Write */
uint32_t status_data = (uint32_t)(inv_desc->lo >>
@ -2574,13 +2610,14 @@ static bool vtd_process_context_cache_desc(IntelIOMMUState *s,
VTDInvDesc *inv_desc)
{
uint16_t sid, fmask;
uint64_t mask[4] = {VTD_INV_DESC_CC_RSVD, VTD_INV_DESC_ALL_ONE,
VTD_INV_DESC_ALL_ONE, VTD_INV_DESC_ALL_ONE};
if ((inv_desc->lo & VTD_INV_DESC_CC_RSVD) || inv_desc->hi) {
error_report_once("%s: invalid cc inv desc: hi=%"PRIx64", lo=%"PRIx64
" (reserved nonzero)", __func__, inv_desc->hi,
inv_desc->lo);
if (!vtd_inv_desc_reserved_check(s, inv_desc, mask, false,
__func__, "cc inv")) {
return false;
}
switch (inv_desc->lo & VTD_INV_DESC_CC_G) {
case VTD_INV_DESC_CC_DOMAIN:
trace_vtd_inv_desc_cc_domain(
@ -2610,12 +2647,11 @@ static bool vtd_process_iotlb_desc(IntelIOMMUState *s, VTDInvDesc *inv_desc)
uint16_t domain_id;
uint8_t am;
hwaddr addr;
uint64_t mask[4] = {VTD_INV_DESC_IOTLB_RSVD_LO, VTD_INV_DESC_IOTLB_RSVD_HI,
VTD_INV_DESC_ALL_ONE, VTD_INV_DESC_ALL_ONE};
if ((inv_desc->lo & VTD_INV_DESC_IOTLB_RSVD_LO) ||
(inv_desc->hi & VTD_INV_DESC_IOTLB_RSVD_HI)) {
error_report_once("%s: invalid iotlb inv desc: hi=0x%"PRIx64
", lo=0x%"PRIx64" (reserved bits unzero)",
__func__, inv_desc->hi, inv_desc->lo);
if (!vtd_inv_desc_reserved_check(s, inv_desc, mask, false,
__func__, "iotlb inv")) {
return false;
}
@ -2656,6 +2692,14 @@ static bool vtd_process_iotlb_desc(IntelIOMMUState *s, VTDInvDesc *inv_desc)
static bool vtd_process_inv_iec_desc(IntelIOMMUState *s,
VTDInvDesc *inv_desc)
{
uint64_t mask[4] = {VTD_INV_DESC_IEC_RSVD, VTD_INV_DESC_ALL_ONE,
VTD_INV_DESC_ALL_ONE, VTD_INV_DESC_ALL_ONE};
if (!vtd_inv_desc_reserved_check(s, inv_desc, mask, false,
__func__, "iec inv")) {
return false;
}
trace_vtd_inv_desc_iec(inv_desc->iec.granularity,
inv_desc->iec.index,
inv_desc->iec.index_mask);
@ -2705,19 +2749,19 @@ static bool vtd_process_device_iotlb_desc(IntelIOMMUState *s,
hwaddr addr;
uint16_t sid;
bool size;
uint64_t mask[4] = {VTD_INV_DESC_DEVICE_IOTLB_RSVD_LO,
VTD_INV_DESC_DEVICE_IOTLB_RSVD_HI,
VTD_INV_DESC_ALL_ONE, VTD_INV_DESC_ALL_ONE};
if (!vtd_inv_desc_reserved_check(s, inv_desc, mask, false,
__func__, "dev-iotlb inv")) {
return false;
}
addr = VTD_INV_DESC_DEVICE_IOTLB_ADDR(inv_desc->hi);
sid = VTD_INV_DESC_DEVICE_IOTLB_SID(inv_desc->lo);
size = VTD_INV_DESC_DEVICE_IOTLB_SIZE(inv_desc->hi);
if ((inv_desc->lo & VTD_INV_DESC_DEVICE_IOTLB_RSVD_LO) ||
(inv_desc->hi & VTD_INV_DESC_DEVICE_IOTLB_RSVD_HI)) {
error_report_once("%s: invalid dev-iotlb inv desc: hi=%"PRIx64
", lo=%"PRIx64" (reserved nonzero)", __func__,
inv_desc->hi, inv_desc->lo);
return false;
}
/*
* Using sid is OK since the guest should have finished the
* initialization of both the bus and device.
@ -2847,6 +2891,7 @@ static void vtd_handle_iqt_write(IntelIOMMUState *s)
if (s->iq_dw && (val & VTD_IQT_QT_256_RSV_BIT)) {
error_report_once("%s: RSV bit is set: val=0x%"PRIx64,
__func__, val);
vtd_handle_inv_queue_error(s);
return;
}
s->iq_tail = VTD_IQT_QT(s->iq_dw, val);
@ -3372,6 +3417,7 @@ static Property vtd_properties[] = {
DEFINE_PROP_BOOL("x-pasid-mode", IntelIOMMUState, pasid, false),
DEFINE_PROP_BOOL("dma-drain", IntelIOMMUState, dma_drain, true),
DEFINE_PROP_BOOL("dma-translation", IntelIOMMUState, dma_translation, true),
DEFINE_PROP_BOOL("stale-tm", IntelIOMMUState, stale_tm, false),
DEFINE_PROP_END_OF_LIST(),
};
@ -4138,15 +4184,15 @@ static void vtd_init(IntelIOMMUState *s)
*/
vtd_spte_rsvd[0] = ~0ULL;
vtd_spte_rsvd[1] = VTD_SPTE_PAGE_L1_RSVD_MASK(s->aw_bits,
x86_iommu->dt_supported);
x86_iommu->dt_supported && s->stale_tm);
vtd_spte_rsvd[2] = VTD_SPTE_PAGE_L2_RSVD_MASK(s->aw_bits);
vtd_spte_rsvd[3] = VTD_SPTE_PAGE_L3_RSVD_MASK(s->aw_bits);
vtd_spte_rsvd[4] = VTD_SPTE_PAGE_L4_RSVD_MASK(s->aw_bits);
vtd_spte_rsvd_large[2] = VTD_SPTE_LPAGE_L2_RSVD_MASK(s->aw_bits,
x86_iommu->dt_supported);
x86_iommu->dt_supported && s->stale_tm);
vtd_spte_rsvd_large[3] = VTD_SPTE_LPAGE_L3_RSVD_MASK(s->aw_bits,
x86_iommu->dt_supported);
x86_iommu->dt_supported && s->stale_tm);
if (s->scalable_mode || s->snoop_control) {
vtd_spte_rsvd[1] &= ~VTD_SPTE_SNP;

View File

@ -356,6 +356,7 @@ union VTDInvDesc {
typedef union VTDInvDesc VTDInvDesc;
/* Masks for struct VTDInvDesc */
#define VTD_INV_DESC_ALL_ONE -1ULL
#define VTD_INV_DESC_TYPE(val) ((((val) >> 5) & 0x70ULL) | \
((val) & 0xfULL))
#define VTD_INV_DESC_CC 0x1 /* Context-cache Invalidate Desc */
@ -409,11 +410,14 @@ typedef union VTDInvDesc VTDInvDesc;
#define VTD_INV_DESC_DEVICE_IOTLB_RSVD_HI 0xffeULL
#define VTD_INV_DESC_DEVICE_IOTLB_RSVD_LO 0xffff0000ffe0f1f0
/* Masks for Interrupt Entry Invalidate Descriptor */
#define VTD_INV_DESC_IEC_RSVD 0xffff000007fff1e0ULL
/* Rsvd field masks for spte */
#define VTD_SPTE_SNP 0x800ULL
#define VTD_SPTE_PAGE_L1_RSVD_MASK(aw, dt_supported) \
dt_supported ? \
#define VTD_SPTE_PAGE_L1_RSVD_MASK(aw, stale_tm) \
stale_tm ? \
(0x800ULL | ~(VTD_HAW_MASK(aw) | VTD_SL_IGN_COM | VTD_SL_TM)) : \
(0x800ULL | ~(VTD_HAW_MASK(aw) | VTD_SL_IGN_COM))
#define VTD_SPTE_PAGE_L2_RSVD_MASK(aw) \
@ -423,12 +427,12 @@ typedef union VTDInvDesc VTDInvDesc;
#define VTD_SPTE_PAGE_L4_RSVD_MASK(aw) \
(0x880ULL | ~(VTD_HAW_MASK(aw) | VTD_SL_IGN_COM))
#define VTD_SPTE_LPAGE_L2_RSVD_MASK(aw, dt_supported) \
dt_supported ? \
#define VTD_SPTE_LPAGE_L2_RSVD_MASK(aw, stale_tm) \
stale_tm ? \
(0x1ff800ULL | ~(VTD_HAW_MASK(aw) | VTD_SL_IGN_COM | VTD_SL_TM)) : \
(0x1ff800ULL | ~(VTD_HAW_MASK(aw) | VTD_SL_IGN_COM))
#define VTD_SPTE_LPAGE_L3_RSVD_MASK(aw, dt_supported) \
dt_supported ? \
#define VTD_SPTE_LPAGE_L3_RSVD_MASK(aw, stale_tm) \
stale_tm ? \
(0x3ffff800ULL | ~(VTD_HAW_MASK(aw) | VTD_SL_IGN_COM | VTD_SL_TM)) : \
(0x3ffff800ULL | ~(VTD_HAW_MASK(aw) | VTD_SL_IGN_COM))

View File

@ -82,6 +82,7 @@
GlobalProperty pc_compat_9_1[] = {
{ "ICH9-LPC", "x-smi-swsmi-timer", "off" },
{ "ICH9-LPC", "x-smi-periodic-timer", "off" },
{ TYPE_INTEL_IOMMU_DEVICE, "stale-tm", "on" },
};
const size_t pc_compat_9_1_len = G_N_ELEMENTS(pc_compat_9_1);

View File

@ -17,6 +17,7 @@
#include "hw/mem/pc-dimm.h"
#include "hw/pci/pci.h"
#include "hw/qdev-properties.h"
#include "hw/qdev-properties-system.h"
#include "qapi/error.h"
#include "qemu/log.h"
#include "qemu/module.h"
@ -919,16 +920,15 @@ static void ct3_realize(PCIDevice *pci_dev, Error **errp)
ct3d->patrol_scrub_attrs.scrub_flags = CXL_MEMDEV_PS_ENABLE_DEFAULT;
/* Set default value for DDR5 ECS read attributes */
ct3d->ecs_attrs.ecs_log_cap = CXL_ECS_LOG_ENTRY_TYPE_DEFAULT;
for (count = 0; count < CXL_ECS_NUM_MEDIA_FRUS; count++) {
ct3d->ecs_attrs[count].ecs_log_cap =
CXL_ECS_LOG_ENTRY_TYPE_DEFAULT;
ct3d->ecs_attrs[count].ecs_cap =
ct3d->ecs_attrs.fru_attrs[count].ecs_cap =
CXL_ECS_REALTIME_REPORT_CAP_DEFAULT;
ct3d->ecs_attrs[count].ecs_config =
ct3d->ecs_attrs.fru_attrs[count].ecs_config =
CXL_ECS_THRESHOLD_COUNT_DEFAULT |
(CXL_ECS_MODE_DEFAULT << 3);
/* Reserved */
ct3d->ecs_attrs[count].ecs_flags = 0;
ct3d->ecs_attrs.fru_attrs[count].ecs_flags = 0;
}
return;
@ -1200,6 +1200,7 @@ static void ct3d_reset(DeviceState *dev)
uint32_t *reg_state = ct3d->cxl_cstate.crb.cache_mem_registers;
uint32_t *write_msk = ct3d->cxl_cstate.crb.cache_mem_regs_write_mask;
pcie_cap_fill_link_ep_usp(PCI_DEVICE(dev), ct3d->width, ct3d->speed);
cxl_component_register_init_common(reg_state, write_msk, CXL2_TYPE3_DEVICE);
cxl_device_register_init_t3(ct3d);
@ -1229,6 +1230,10 @@ static Property ct3_props[] = {
DEFINE_PROP_UINT8("num-dc-regions", CXLType3Dev, dc.num_regions, 0),
DEFINE_PROP_LINK("volatile-dc-memdev", CXLType3Dev, dc.host_dc,
TYPE_MEMORY_BACKEND, HostMemoryBackend *),
DEFINE_PROP_PCIE_LINK_SPEED("x-speed", CXLType3Dev,
speed, PCIE_LINK_SPEED_32),
DEFINE_PROP_PCIE_LINK_WIDTH("x-width", CXLType3Dev,
width, PCIE_LINK_WIDTH_16),
DEFINE_PROP_END_OF_LIST(),
};
@ -1375,9 +1380,7 @@ void qmp_cxl_inject_poison(const char *path, uint64_t start, uint64_t length,
ct3d = CXL_TYPE3(obj);
QLIST_FOREACH(p, &ct3d->poison_list, node) {
if (((start >= p->start) && (start < p->start + p->length)) ||
((start + length > p->start) &&
(start + length <= p->start + p->length))) {
if ((start < p->start + p->length) && (start + length > p->start)) {
error_setg(errp,
"Overlap with existing poisoned region not supported");
return;
@ -2060,11 +2063,11 @@ static void qmp_cxl_process_dynamic_capacity_prescriptive(const char *path,
stw_le_p(&dCap.host_id, hid);
/* only valid for DC_REGION_CONFIG_UPDATED event */
dCap.updated_region_id = 0;
dCap.flags = 0;
for (i = 0; i < num_extents; i++) {
memcpy(&dCap.dynamic_capacity_extent, &extents[i],
sizeof(CXLDCExtentRaw));
dCap.flags = 0;
if (i < num_extents - 1) {
/* Set "More" flag */
dCap.flags |= BIT(0);

View File

@ -13,6 +13,8 @@
#include "hw/pci/msi.h"
#include "hw/pci/pcie.h"
#include "hw/pci/pcie_port.h"
#include "hw/qdev-properties.h"
#include "hw/qdev-properties-system.h"
#include "hw/cxl/cxl.h"
#include "qapi/error.h"
@ -210,24 +212,20 @@ static void cxl_dsp_exitfn(PCIDevice *d)
pci_bridge_exitfn(d);
}
static void cxl_dsp_instance_post_init(Object *obj)
{
PCIESlot *s = PCIE_SLOT(obj);
if (!s->speed) {
s->speed = QEMU_PCI_EXP_LNK_2_5GT;
}
if (!s->width) {
s->width = QEMU_PCI_EXP_LNK_X1;
}
}
static Property cxl_dsp_props[] = {
DEFINE_PROP_PCIE_LINK_SPEED("x-speed", PCIESlot,
speed, PCIE_LINK_SPEED_64),
DEFINE_PROP_PCIE_LINK_WIDTH("x-width", PCIESlot,
width, PCIE_LINK_WIDTH_16),
DEFINE_PROP_END_OF_LIST()
};
static void cxl_dsp_class_init(ObjectClass *oc, void *data)
{
DeviceClass *dc = DEVICE_CLASS(oc);
PCIDeviceClass *k = PCI_DEVICE_CLASS(oc);
device_class_set_props(dc, cxl_dsp_props);
k->config_write = cxl_dsp_config_write;
k->realize = cxl_dsp_realize;
k->exit = cxl_dsp_exitfn;
@ -243,7 +241,6 @@ static const TypeInfo cxl_dsp_info = {
.name = TYPE_CXL_DSP,
.instance_size = sizeof(CXLDownstreamPort),
.parent = TYPE_PCIE_SLOT,
.instance_post_init = cxl_dsp_instance_post_init,
.class_init = cxl_dsp_class_init,
.interfaces = (InterfaceInfo[]) {
{ INTERFACE_PCIE_DEVICE },

View File

@ -24,6 +24,7 @@
#include "hw/pci/pcie_port.h"
#include "hw/pci/msi.h"
#include "hw/qdev-properties.h"
#include "hw/qdev-properties-system.h"
#include "hw/sysbus.h"
#include "qapi/error.h"
#include "hw/cxl/cxl.h"
@ -206,6 +207,10 @@ static Property gen_rp_props[] = {
-1),
DEFINE_PROP_SIZE("pref64-reserve", CXLRootPort, res_reserve.mem_pref_64,
-1),
DEFINE_PROP_PCIE_LINK_SPEED("x-speed", PCIESlot,
speed, PCIE_LINK_SPEED_64),
DEFINE_PROP_PCIE_LINK_WIDTH("x-width", PCIESlot,
width, PCIE_LINK_WIDTH_32),
DEFINE_PROP_END_OF_LIST()
};

View File

@ -11,6 +11,7 @@
#include "qemu/osdep.h"
#include "qemu/log.h"
#include "hw/qdev-properties.h"
#include "hw/qdev-properties-system.h"
#include "hw/pci/msi.h"
#include "hw/pci/pcie.h"
#include "hw/pci/pcie_port.h"
@ -100,6 +101,7 @@ static void cxl_usp_reset(DeviceState *qdev)
pci_bridge_reset(qdev);
pcie_cap_deverr_reset(d);
pcie_cap_fill_link_ep_usp(d, usp->width, usp->speed);
latch_registers(usp);
}
@ -363,6 +365,10 @@ static void cxl_usp_exitfn(PCIDevice *d)
static Property cxl_upstream_props[] = {
DEFINE_PROP_UINT64("sn", CXLUpstreamPort, sn, UI64_NULL),
DEFINE_PROP_STRING("cdat", CXLUpstreamPort, cxl_cstate.cdat.filename),
DEFINE_PROP_PCIE_LINK_SPEED("x-speed", CXLUpstreamPort,
speed, PCIE_LINK_SPEED_32),
DEFINE_PROP_PCIE_LINK_WIDTH("x-width", CXLUpstreamPort,
width, PCIE_LINK_WIDTH_16),
DEFINE_PROP_END_OF_LIST()
};

View File

@ -38,7 +38,6 @@ DECLARE_INSTANCE_CHECKER(PXBBus, PXB_BUS,
DECLARE_INSTANCE_CHECKER(PXBBus, PXB_PCIE_BUS,
TYPE_PXB_PCIE_BUS)
#define TYPE_PXB_CXL_BUS "pxb-cxl-bus"
DECLARE_INSTANCE_CHECKER(PXBBus, PXB_CXL_BUS,
TYPE_PXB_CXL_BUS)
@ -85,12 +84,25 @@ static uint16_t pxb_bus_numa_node(PCIBus *bus)
return pxb->numa_node;
}
static void prop_pxb_uid_get(Object *obj, Visitor *v, const char *name,
void *opaque, Error **errp)
{
uint32_t uid = pci_bus_num(PCI_BUS(obj));
visit_type_uint32(v, name, &uid, errp);
}
static void pxb_bus_class_init(ObjectClass *class, void *data)
{
PCIBusClass *pbc = PCI_BUS_CLASS(class);
pbc->bus_num = pxb_bus_num;
pbc->numa_node = pxb_bus_numa_node;
object_class_property_add(class, "acpi_uid", "uint32",
prop_pxb_uid_get, NULL, NULL, NULL);
object_class_property_set_description(class, "acpi_uid",
"ACPI Unique ID used to distinguish this PCI Host Bridge / ACPI00016");
}
static const TypeInfo pxb_bus_info = {
@ -318,7 +330,7 @@ static gint pxb_compare(gconstpointer a, gconstpointer b)
0;
}
static void pxb_dev_realize_common(PCIDevice *dev, enum BusType type,
static bool pxb_dev_realize_common(PCIDevice *dev, enum BusType type,
Error **errp)
{
PXBDev *pxb = PXB_DEV(dev);
@ -330,13 +342,13 @@ static void pxb_dev_realize_common(PCIDevice *dev, enum BusType type,
if (ms->numa_state == NULL) {
error_setg(errp, "NUMA is not supported by this machine-type");
return;
return false;
}
if (pxb->numa_node != NUMA_NODE_UNASSIGNED &&
pxb->numa_node >= ms->numa_state->num_nodes) {
error_setg(errp, "Illegal numa node %d", pxb->numa_node);
return;
return false;
}
if (dev->qdev.id && *dev->qdev.id) {
@ -382,12 +394,13 @@ static void pxb_dev_realize_common(PCIDevice *dev, enum BusType type,
pci_config_set_class(dev->config, PCI_CLASS_BRIDGE_HOST);
pxb_dev_list = g_list_insert_sorted(pxb_dev_list, pxb, pxb_compare);
return;
return true;
err_register_bus:
object_unref(OBJECT(bds));
object_unparent(OBJECT(bus));
object_unref(OBJECT(ds));
return false;
}
static void pxb_dev_realize(PCIDevice *dev, Error **errp)
@ -488,7 +501,9 @@ static void pxb_cxl_dev_realize(PCIDevice *dev, Error **errp)
return;
}
pxb_dev_realize_common(dev, CXL, errp);
if (!pxb_dev_realize_common(dev, CXL, errp)) {
return;
}
pxb_cxl_dev_reset(DEVICE(dev));
}

View File

@ -141,6 +141,7 @@ void acpi_dsdt_add_gpex(Aml *scope, struct GPEXConfig *cfg)
QLIST_FOREACH(bus, &bus->child, sibling) {
uint8_t bus_num = pci_bus_num(bus);
uint8_t numa_node = pci_bus_numa_node(bus);
uint32_t uid;
bool is_cxl = pci_bus_is_cxl(bus);
if (!pci_bus_is_root(bus)) {
@ -156,6 +157,8 @@ void acpi_dsdt_add_gpex(Aml *scope, struct GPEXConfig *cfg)
nr_pcie_buses = bus_num;
}
uid = object_property_get_uint(OBJECT(bus), "acpi_uid",
&error_fatal);
dev = aml_device("PC%.02X", bus_num);
if (is_cxl) {
struct Aml *pkg = aml_package(2);
@ -168,7 +171,7 @@ void acpi_dsdt_add_gpex(Aml *scope, struct GPEXConfig *cfg)
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("_UID", aml_int(uid)));
aml_append(dev, aml_name_decl("_STR", aml_unicode("pxb Device")));
aml_append(dev, aml_name_decl("_CCA", aml_int(1)));
if (numa_node != NUMA_NODE_UNASSIGNED) {

View File

@ -67,6 +67,19 @@ static char *pcibus_get_fw_dev_path(DeviceState *dev);
static void pcibus_reset_hold(Object *obj, ResetType type);
static bool pcie_has_upstream_port(PCIDevice *dev);
static void prop_pci_busnr_get(Object *obj, Visitor *v, const char *name,
void *opaque, Error **errp)
{
uint8_t busnr = pci_dev_bus_num(PCI_DEVICE(obj));
visit_type_uint8(v, name, &busnr, errp);
}
static const PropertyInfo prop_pci_busnr = {
.name = "busnr",
.get = prop_pci_busnr_get,
};
static Property pci_props[] = {
DEFINE_PROP_PCI_DEVFN("addr", PCIDevice, devfn, -1),
DEFINE_PROP_STRING("romfile", PCIDevice, romfile),
@ -87,6 +100,9 @@ static Property pci_props[] = {
QEMU_PCIE_ARI_NEXTFN_1_BITNR, false),
DEFINE_PROP_SIZE32("x-max-bounce-buffer-size", PCIDevice,
max_bounce_buffer_size, DEFAULT_MAX_BOUNCE_BUFFER_SIZE),
DEFINE_PROP_BIT("x-pcie-ext-tag", PCIDevice, cap_present,
QEMU_PCIE_EXT_TAG_BITNR, true),
{ .name = "busnr", .info = &prop_pci_busnr },
DEFINE_PROP_END_OF_LIST()
};

View File

@ -380,9 +380,12 @@ void pci_bridge_initfn(PCIDevice *dev, const char *typename)
sec_bus->map_irq = br->map_irq ? br->map_irq : pci_swizzle_map_irq_fn;
sec_bus->address_space_mem = &br->address_space_mem;
memory_region_init(&br->address_space_mem, OBJECT(br), "pci_bridge_pci", UINT64_MAX);
address_space_init(&br->as_mem, &br->address_space_mem,
"pci_bridge_pci_mem");
sec_bus->address_space_io = &br->address_space_io;
memory_region_init(&br->address_space_io, OBJECT(br), "pci_bridge_io",
4 * GiB);
address_space_init(&br->as_io, &br->address_space_io, "pci_bridge_pci_io");
pci_bridge_region_init(br);
QLIST_INIT(&sec_bus->child);
QLIST_INSERT_HEAD(&parent->child, sec_bus, sibling);
@ -399,6 +402,8 @@ void pci_bridge_exitfn(PCIDevice *pci_dev)
PCIBridge *s = PCI_BRIDGE(pci_dev);
assert(QLIST_EMPTY(&s->sec_bus.child));
QLIST_REMOVE(&s->sec_bus, sibling);
address_space_destroy(&s->as_mem);
address_space_destroy(&s->as_io);
pci_bridge_region_del(s, &s->windows);
pci_bridge_region_cleanup(s, &s->windows);
/* object_unparent() is called automatically during device deletion */

View File

@ -86,7 +86,13 @@ pcie_cap_v1_fill(PCIDevice *dev, uint8_t port, uint8_t type, uint8_t version)
* Specification, Revision 1.1., or subsequent PCI Express Base
* Specification revisions.
*/
pci_set_long(exp_cap + PCI_EXP_DEVCAP, PCI_EXP_DEVCAP_RBER);
uint32_t devcap = PCI_EXP_DEVCAP_RBER;
if (dev->cap_present & QEMU_PCIE_EXT_TAG) {
devcap = PCI_EXP_DEVCAP_RBER | PCI_EXP_DEVCAP_EXT_TAG;
}
pci_set_long(exp_cap + PCI_EXP_DEVCAP, devcap);
pci_set_long(exp_cap + PCI_EXP_LNKCAP,
(port << PCI_EXP_LNKCAP_PN_SHIFT) |
@ -105,6 +111,73 @@ pcie_cap_v1_fill(PCIDevice *dev, uint8_t port, uint8_t type, uint8_t version)
pci_set_word(cmask + PCI_EXP_LNKSTA, 0);
}
/* Includes setting the target speed default */
static void pcie_cap_fill_lnk(uint8_t *exp_cap, PCIExpLinkWidth width,
PCIExpLinkSpeed speed)
{
/* Clear and fill LNKCAP from what was configured above */
pci_long_test_and_clear_mask(exp_cap + PCI_EXP_LNKCAP,
PCI_EXP_LNKCAP_MLW | PCI_EXP_LNKCAP_SLS);
pci_long_test_and_set_mask(exp_cap + PCI_EXP_LNKCAP,
QEMU_PCI_EXP_LNKCAP_MLW(width) |
QEMU_PCI_EXP_LNKCAP_MLS(speed));
if (speed > QEMU_PCI_EXP_LNK_2_5GT) {
/*
* Target Link Speed defaults to the highest link speed supported by
* the component. 2.5GT/s devices are permitted to hardwire to zero.
*/
pci_word_test_and_clear_mask(exp_cap + PCI_EXP_LNKCTL2,
PCI_EXP_LNKCTL2_TLS);
pci_word_test_and_set_mask(exp_cap + PCI_EXP_LNKCTL2,
QEMU_PCI_EXP_LNKCAP_MLS(speed) &
PCI_EXP_LNKCTL2_TLS);
}
/*
* 2.5 & 5.0GT/s can be fully described by LNKCAP, but 8.0GT/s is
* actually a reference to the highest bit supported in this register.
* We assume the device supports all link speeds.
*/
if (speed > QEMU_PCI_EXP_LNK_5GT) {
pci_long_test_and_clear_mask(exp_cap + PCI_EXP_LNKCAP2, ~0U);
pci_long_test_and_set_mask(exp_cap + PCI_EXP_LNKCAP2,
PCI_EXP_LNKCAP2_SLS_2_5GB |
PCI_EXP_LNKCAP2_SLS_5_0GB |
PCI_EXP_LNKCAP2_SLS_8_0GB);
if (speed > QEMU_PCI_EXP_LNK_8GT) {
pci_long_test_and_set_mask(exp_cap + PCI_EXP_LNKCAP2,
PCI_EXP_LNKCAP2_SLS_16_0GB);
}
if (speed > QEMU_PCI_EXP_LNK_16GT) {
pci_long_test_and_set_mask(exp_cap + PCI_EXP_LNKCAP2,
PCI_EXP_LNKCAP2_SLS_32_0GB);
}
if (speed > QEMU_PCI_EXP_LNK_32GT) {
pci_long_test_and_set_mask(exp_cap + PCI_EXP_LNKCAP2,
PCI_EXP_LNKCAP2_SLS_64_0GB);
}
}
}
void pcie_cap_fill_link_ep_usp(PCIDevice *dev, PCIExpLinkWidth width,
PCIExpLinkSpeed speed)
{
uint8_t *exp_cap = dev->config + dev->exp.exp_cap;
/*
* For an end point or USP need to set the current status as well
* as the capabilities.
*/
pci_long_test_and_clear_mask(exp_cap + PCI_EXP_LNKSTA,
PCI_EXP_LNKSTA_CLS | PCI_EXP_LNKSTA_NLW);
pci_long_test_and_set_mask(exp_cap + PCI_EXP_LNKSTA,
QEMU_PCI_EXP_LNKSTA_NLW(width) |
QEMU_PCI_EXP_LNKSTA_CLS(speed));
pcie_cap_fill_lnk(exp_cap, width, speed);
}
static void pcie_cap_fill_slot_lnk(PCIDevice *dev)
{
PCIESlot *s = (PCIESlot *)object_dynamic_cast(OBJECT(dev), TYPE_PCIE_SLOT);
@ -115,13 +188,6 @@ static void pcie_cap_fill_slot_lnk(PCIDevice *dev)
return;
}
/* Clear and fill LNKCAP from what was configured above */
pci_long_test_and_clear_mask(exp_cap + PCI_EXP_LNKCAP,
PCI_EXP_LNKCAP_MLW | PCI_EXP_LNKCAP_SLS);
pci_long_test_and_set_mask(exp_cap + PCI_EXP_LNKCAP,
QEMU_PCI_EXP_LNKCAP_MLW(s->width) |
QEMU_PCI_EXP_LNKCAP_MLS(s->speed));
/*
* Link bandwidth notification is required for all root ports and
* downstream ports supporting links wider than x1 or multiple link
@ -144,42 +210,9 @@ static void pcie_cap_fill_slot_lnk(PCIDevice *dev)
pci_long_test_and_set_mask(exp_cap + PCI_EXP_LNKCAP,
PCI_EXP_LNKCAP_DLLLARC);
/* the PCI_EXP_LNKSTA_DLLLA will be set in the hotplug function */
/*
* Target Link Speed defaults to the highest link speed supported by
* the component. 2.5GT/s devices are permitted to hardwire to zero.
*/
pci_word_test_and_clear_mask(exp_cap + PCI_EXP_LNKCTL2,
PCI_EXP_LNKCTL2_TLS);
pci_word_test_and_set_mask(exp_cap + PCI_EXP_LNKCTL2,
QEMU_PCI_EXP_LNKCAP_MLS(s->speed) &
PCI_EXP_LNKCTL2_TLS);
}
/*
* 2.5 & 5.0GT/s can be fully described by LNKCAP, but 8.0GT/s is
* actually a reference to the highest bit supported in this register.
* We assume the device supports all link speeds.
*/
if (s->speed > QEMU_PCI_EXP_LNK_5GT) {
pci_long_test_and_clear_mask(exp_cap + PCI_EXP_LNKCAP2, ~0U);
pci_long_test_and_set_mask(exp_cap + PCI_EXP_LNKCAP2,
PCI_EXP_LNKCAP2_SLS_2_5GB |
PCI_EXP_LNKCAP2_SLS_5_0GB |
PCI_EXP_LNKCAP2_SLS_8_0GB);
if (s->speed > QEMU_PCI_EXP_LNK_8GT) {
pci_long_test_and_set_mask(exp_cap + PCI_EXP_LNKCAP2,
PCI_EXP_LNKCAP2_SLS_16_0GB);
}
if (s->speed > QEMU_PCI_EXP_LNK_16GT) {
pci_long_test_and_set_mask(exp_cap + PCI_EXP_LNKCAP2,
PCI_EXP_LNKCAP2_SLS_32_0GB);
}
if (s->speed > QEMU_PCI_EXP_LNK_32GT) {
pci_long_test_and_set_mask(exp_cap + PCI_EXP_LNKCAP2,
PCI_EXP_LNKCAP2_SLS_64_0GB);
}
}
pcie_cap_fill_lnk(exp_cap, s->width, s->speed);
}
int pcie_cap_init(PCIDevice *dev, uint8_t offset,

View File

@ -1185,9 +1185,16 @@ static int vhost_user_set_vring_num(struct vhost_dev *dev,
static void vhost_user_host_notifier_free(VhostUserHostNotifier *n)
{
assert(n && n->unmap_addr);
munmap(n->unmap_addr, qemu_real_host_page_size());
n->unmap_addr = NULL;
if (n->unmap_addr) {
munmap(n->unmap_addr, qemu_real_host_page_size());
n->unmap_addr = NULL;
}
if (n->destroy) {
memory_region_transaction_begin();
object_unparent(OBJECT(&n->mr));
memory_region_transaction_commit();
g_free(n);
}
}
/*
@ -1195,17 +1202,28 @@ static void vhost_user_host_notifier_free(VhostUserHostNotifier *n)
* under rcu.
*/
static void vhost_user_host_notifier_remove(VhostUserHostNotifier *n,
VirtIODevice *vdev)
VirtIODevice *vdev, bool destroy)
{
/*
* if destroy == false and n->addr == NULL, we have nothing to do.
* so, just return.
*/
if (!n || (!destroy && !n->addr)) {
return;
}
if (n->addr) {
if (vdev) {
memory_region_transaction_begin();
virtio_queue_set_host_notifier_mr(vdev, n->idx, &n->mr, false);
memory_region_transaction_commit();
}
assert(!n->unmap_addr);
n->unmap_addr = n->addr;
n->addr = NULL;
call_rcu(n, vhost_user_host_notifier_free, rcu);
}
n->destroy = destroy;
call_rcu(n, vhost_user_host_notifier_free, rcu);
}
static int vhost_user_set_vring_base(struct vhost_dev *dev,
@ -1279,9 +1297,7 @@ static int vhost_user_get_vring_base(struct vhost_dev *dev,
struct vhost_user *u = dev->opaque;
VhostUserHostNotifier *n = fetch_notifier(u->user, ring->index);
if (n) {
vhost_user_host_notifier_remove(n, dev->vdev);
}
vhost_user_host_notifier_remove(n, dev->vdev, false);
ret = vhost_user_write(dev, &msg, NULL, 0);
if (ret < 0) {
@ -1562,7 +1578,7 @@ static int vhost_user_backend_handle_vring_host_notifier(struct vhost_dev *dev,
* new mapped address.
*/
n = fetch_or_create_notifier(user, queue_idx);
vhost_user_host_notifier_remove(n, vdev);
vhost_user_host_notifier_remove(n, vdev, false);
if (area->u64 & VHOST_USER_VRING_NOFD_MASK) {
return 0;
@ -1607,9 +1623,14 @@ vhost_user_backend_handle_shared_object_add(struct vhost_dev *dev,
QemuUUID uuid;
memcpy(uuid.data, object->uuid, sizeof(object->uuid));
return virtio_add_vhost_device(&uuid, dev);
return !virtio_add_vhost_device(&uuid, dev);
}
/*
* Handle VHOST_USER_BACKEND_SHARED_OBJECT_REMOVE backend requests.
*
* Return: 0 on success, 1 on error.
*/
static int
vhost_user_backend_handle_shared_object_remove(struct vhost_dev *dev,
VhostUserShared *object)
@ -1623,16 +1644,16 @@ vhost_user_backend_handle_shared_object_remove(struct vhost_dev *dev,
struct vhost_dev *owner = virtio_lookup_vhost_device(&uuid);
if (dev != owner) {
/* Not allowed to remove non-owned entries */
return 0;
return 1;
}
break;
}
default:
/* Not allowed to remove non-owned entries */
return 0;
return 1;
}
return virtio_remove_resource(&uuid);
return !virtio_remove_resource(&uuid);
}
static bool vhost_user_send_resp(QIOChannel *ioc, VhostUserHeader *hdr,
@ -2736,15 +2757,7 @@ static int vhost_user_set_inflight_fd(struct vhost_dev *dev,
static void vhost_user_state_destroy(gpointer data)
{
VhostUserHostNotifier *n = (VhostUserHostNotifier *) data;
if (n) {
vhost_user_host_notifier_remove(n, NULL);
object_unparent(OBJECT(&n->mr));
/*
* We can't free until vhost_user_host_notifier_remove has
* done it's thing so schedule the free with RCU.
*/
g_free_rcu(n, rcu);
}
vhost_user_host_notifier_remove(n, NULL, true);
}
bool vhost_user_init(VhostUserState *user, CharBackend *chr, Error **errp)
@ -2765,9 +2778,7 @@ void vhost_user_cleanup(VhostUserState *user)
if (!user->chr) {
return;
}
memory_region_transaction_begin();
user->notifiers = (GPtrArray *) g_ptr_array_free(user->notifiers, true);
memory_region_transaction_commit();
user->chr = NULL;
}

View File

@ -2057,6 +2057,8 @@ static void virtio_pci_device_plugged(DeviceState *d, Error **errp)
if (modern_pio) {
memory_region_init(&proxy->io_bar, OBJECT(proxy),
"virtio-pci-io", 0x4);
address_space_init(&proxy->modern_cfg_io_as, &proxy->io_bar,
"virtio-pci-cfg-io-as");
pci_register_bar(&proxy->pci_dev, proxy->modern_io_bar_idx,
PCI_BASE_ADDRESS_SPACE_IO, &proxy->io_bar);
@ -2180,6 +2182,9 @@ static void virtio_pci_realize(PCIDevice *pci_dev, Error **errp)
/* PCI BAR regions must be powers of 2 */
pow2ceil(proxy->notify.offset + proxy->notify.size));
address_space_init(&proxy->modern_cfg_mem_as, &proxy->modern_bar,
"virtio-pci-cfg-mem-as");
if (proxy->disable_legacy == ON_OFF_AUTO_AUTO) {
proxy->disable_legacy = pcie_port ? ON_OFF_AUTO_ON : ON_OFF_AUTO_OFF;
}
@ -2269,12 +2274,17 @@ 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));
bool modern_pio = proxy->flags & VIRTIO_PCI_FLAG_MODERN_PIO_NOTIFY;
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);
}
address_space_destroy(&proxy->modern_cfg_mem_as);
if (modern_pio) {
address_space_destroy(&proxy->modern_cfg_io_as);
}
}
static void virtio_pci_reset(DeviceState *qdev)
@ -2385,6 +2395,14 @@ static void virtio_pci_dc_realize(DeviceState *qdev, Error **errp)
vpciklass->parent_dc_realize(qdev, errp);
}
static int virtio_pci_sync_config(DeviceState *dev, Error **errp)
{
VirtIOPCIProxy *proxy = VIRTIO_PCI(dev);
VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
return qdev_sync_config(DEVICE(vdev), errp);
}
static void virtio_pci_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
@ -2401,6 +2419,7 @@ static void virtio_pci_class_init(ObjectClass *klass, void *data)
device_class_set_parent_realize(dc, virtio_pci_dc_realize,
&vpciklass->parent_dc_realize);
rc->phases.hold = virtio_pci_bus_reset_hold;
dc->sync_config = virtio_pci_sync_config;
}
static const TypeInfo virtio_pci_info = {

View File

@ -1104,7 +1104,14 @@ struct AddressSpace {
QTAILQ_HEAD(, MemoryListener) listeners;
QTAILQ_ENTRY(AddressSpace) address_spaces_link;
/* Maximum DMA bounce buffer size used for indirect memory map requests */
/*
* Maximum DMA bounce buffer size used for indirect memory map requests.
* This limits the total size of bounce buffer allocations made for
* DMA requests to indirect memory regions within this AddressSpace. DMA
* requests that exceed the limit (e.g. due to overly large requested size
* or concurrent DMA requests having claimed too much buffer space) will be
* rejected and left to the caller to handle.
*/
size_t max_bounce_buffer_size;
/* Total size of bounce buffers currently allocated, atomically accessed */
size_t bounce_buffer_size;

View File

@ -1,47 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. All rights reserved
*/
#ifndef ACPI_GENERIC_INITIATOR_H
#define ACPI_GENERIC_INITIATOR_H
#include "qom/object_interfaces.h"
#define TYPE_ACPI_GENERIC_INITIATOR "acpi-generic-initiator"
typedef struct AcpiGenericInitiator {
/* private */
Object parent;
/* public */
char *pci_dev;
uint16_t node;
} AcpiGenericInitiator;
/*
* ACPI 6.3:
* Table 5-81 Flags Generic Initiator Affinity Structure
*/
typedef enum {
/*
* If clear, the OSPM ignores the contents of the Generic
* Initiator/Port Affinity Structure. This allows system firmware
* to populate the SRAT with a static number of structures, but only
* enable them as necessary.
*/
GEN_AFFINITY_ENABLED = (1 << 0),
} GenericAffinityFlags;
/*
* ACPI 6.3:
* Table 5-80 Device Handle - PCI
*/
typedef struct PCIDeviceHandle {
uint16_t segment;
uint16_t bdf;
} PCIDeviceHandle;
void build_srat_generic_pci_initiator(GArray *table_data);
#endif

View File

@ -486,6 +486,13 @@ Aml *build_crs(PCIHostState *host, CrsRangeSet *range_set, uint32_t io_offset,
void build_srat_memory(GArray *table_data, uint64_t base,
uint64_t len, int node, MemoryAffinityFlags flags);
void build_srat_pci_generic_initiator(GArray *table_data, uint32_t node,
uint16_t segment, uint8_t bus,
uint8_t devfn);
void build_srat_acpi_generic_port(GArray *table_data, uint32_t node,
const char *hid, uint32_t uid);
void build_slit(GArray *table_data, BIOSLinker *linker, MachineState *ms,
const char *oem_id, const char *oem_table_id);

View File

@ -40,4 +40,7 @@ Aml *aml_pci_device_dsm(void);
void build_append_pci_bus_devices(Aml *parent_scope, PCIBus *bus);
void build_pci_bridge_aml(AcpiDevAmlIf *adev, Aml *scope);
void build_srat_generic_affinity_structures(GArray *table_data);
#endif

View File

@ -158,6 +158,8 @@ struct CPUClass {
void (*dump_state)(CPUState *cpu, FILE *, int flags);
void (*query_cpu_fast)(CPUState *cpu, CpuInfoFast *value);
int64_t (*get_arch_id)(CPUState *cpu);
bool (*cpu_persistent_status)(CPUState *cpu);
bool (*cpu_enabled_status)(CPUState *cpu);
void (*set_pc)(CPUState *cpu, vaddr value);
vaddr (*get_pc)(CPUState *cpu);
int (*gdb_read_register)(CPUState *cpu, GByteArray *buf, int reg);

View File

@ -463,18 +463,6 @@ typedef struct CXLMemPatrolScrubWriteAttrs {
#define CXL_MEMDEV_PS_ENABLE_DEFAULT 0
/* CXL memory device DDR5 ECS control attributes */
typedef struct CXLMemECSReadAttrs {
uint8_t ecs_log_cap;
uint8_t ecs_cap;
uint16_t ecs_config;
uint8_t ecs_flags;
} QEMU_PACKED CXLMemECSReadAttrs;
typedef struct CXLMemECSWriteAttrs {
uint8_t ecs_log_cap;
uint16_t ecs_config;
} QEMU_PACKED CXLMemECSWriteAttrs;
#define CXL_ECS_GET_FEATURE_VERSION 0x01
#define CXL_ECS_SET_FEATURE_VERSION 0x01
#define CXL_ECS_LOG_ENTRY_TYPE_DEFAULT 0x01
@ -483,6 +471,26 @@ typedef struct CXLMemECSWriteAttrs {
#define CXL_ECS_MODE_DEFAULT 0
#define CXL_ECS_NUM_MEDIA_FRUS 3 /* Default */
typedef struct CXLMemECSFRUReadAttrs {
uint8_t ecs_cap;
uint16_t ecs_config;
uint8_t ecs_flags;
} QEMU_PACKED CXLMemECSFRUReadAttrs;
typedef struct CXLMemECSReadAttrs {
uint8_t ecs_log_cap;
CXLMemECSFRUReadAttrs fru_attrs[CXL_ECS_NUM_MEDIA_FRUS];
} QEMU_PACKED CXLMemECSReadAttrs;
typedef struct CXLMemECSFRUWriteAttrs {
uint16_t ecs_config;
} QEMU_PACKED CXLMemECSFRUWriteAttrs;
typedef struct CXLMemECSWriteAttrs {
uint8_t ecs_log_cap;
CXLMemECSFRUWriteAttrs fru_attrs[CXL_ECS_NUM_MEDIA_FRUS];
} QEMU_PACKED CXLMemECSWriteAttrs;
#define DCD_MAX_NUM_REGION 8
typedef struct CXLDCExtentRaw {
@ -549,6 +557,10 @@ struct CXLType3Dev {
CXLCCI vdm_fm_owned_ld_mctp_cci;
CXLCCI ld0_cci;
/* PCIe link characteristics */
PCIExpLinkSpeed speed;
PCIExpLinkWidth width;
/* DOE */
DOECap doe_cdat;
@ -571,8 +583,8 @@ struct CXLType3Dev {
CXLMemPatrolScrubReadAttrs patrol_scrub_attrs;
CXLMemPatrolScrubWriteAttrs patrol_scrub_wr_attrs;
/* ECS control attributes */
CXLMemECSReadAttrs ecs_attrs[CXL_ECS_NUM_MEDIA_FRUS];
CXLMemECSWriteAttrs ecs_wr_attrs[CXL_ECS_NUM_MEDIA_FRUS];
CXLMemECSReadAttrs ecs_attrs;
CXLMemECSWriteAttrs ecs_wr_attrs;
struct dynamic_capacity {
HostMemoryBackend *host_dc;

View File

@ -306,6 +306,9 @@ struct IntelIOMMUState {
bool dma_translation; /* Whether DMA translation supported */
bool pasid; /* Whether to support PASID */
/* Transient Mapping, Reserved(0) since VTD spec revision 3.2 */
bool stale_tm;
/*
* Protects IOMMU states in general. Currently it protects the
* per-IOMMU IOTLB cache, and context entry cache in VTDAddressSpace.

View File

@ -12,6 +12,10 @@ typedef struct CXLUpstreamPort {
/*< public >*/
CXLComponentState cxl_cstate;
CXLCCI swcci;
PCIExpLinkSpeed speed;
PCIExpLinkWidth width;
DOECap doe_cdat;
uint64_t sn;
} CXLUpstreamPort;

View File

@ -16,7 +16,7 @@ extern bool pci_available;
#define PCI_BUS_NUM(x) (((x) >> 8) & 0xff)
#define PCI_SLOT(devfn) (((devfn) >> 3) & 0x1f)
#define PCI_FUNC(devfn) ((devfn) & 0x07)
#define PCI_BUILD_BDF(bus, devfn) ((bus << 8) | (devfn))
#define PCI_BUILD_BDF(bus, devfn) (((bus) << 8) | (devfn))
#define PCI_BDF_TO_DEVFN(x) ((x) & 0xff)
#define PCI_BUS_MAX 256
#define PCI_DEVFN_MAX 256
@ -214,6 +214,8 @@ enum {
QEMU_PCIE_ERR_UNC_MASK = (1 << QEMU_PCIE_ERR_UNC_MASK_BITNR),
#define QEMU_PCIE_ARI_NEXTFN_1_BITNR 12
QEMU_PCIE_ARI_NEXTFN_1 = (1 << QEMU_PCIE_ARI_NEXTFN_1_BITNR),
#define QEMU_PCIE_EXT_TAG_BITNR 13
QEMU_PCIE_EXT_TAG = (1 << QEMU_PCIE_EXT_TAG_BITNR),
};
typedef struct PCIINTxRoute {

View File

@ -72,6 +72,8 @@ struct PCIBridge {
*/
MemoryRegion address_space_mem;
MemoryRegion address_space_io;
AddressSpace as_mem;
AddressSpace as_io;
PCIBridgeWindows windows;
@ -102,6 +104,7 @@ typedef struct PXBPCIEDev {
PXBDev parent_obj;
} PXBPCIEDev;
#define TYPE_PXB_CXL_BUS "pxb-cxl-bus"
#define TYPE_PXB_DEV "pxb"
OBJECT_DECLARE_SIMPLE_TYPE(PXBDev, PXB_DEV)

View File

@ -168,7 +168,11 @@ struct PCIDevice {
char *failover_pair_id;
uint32_t acpi_index;
/* Maximum DMA bounce buffer size used for indirect memory map requests */
/*
* Indirect DMA region bounce buffer size as configured for the device. This
* is a configuration parameter that is reflected into bus_master_as when
* realizing the device.
*/
uint32_t max_bounce_buffer_size;
};

View File

@ -141,6 +141,8 @@ void pcie_acs_reset(PCIDevice *dev);
void pcie_ari_init(PCIDevice *dev, uint16_t offset);
void pcie_dev_ser_num_init(PCIDevice *dev, uint16_t offset, uint64_t ser_num);
void pcie_ats_init(PCIDevice *dev, uint16_t offset, bool aligned);
void pcie_cap_fill_link_ep_usp(PCIDevice *dev, PCIExpLinkWidth width,
PCIExpLinkSpeed speed);
void pcie_cap_slot_pre_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev,
Error **errp);

View File

@ -95,6 +95,7 @@ typedef void (*DeviceUnrealize)(DeviceState *dev);
typedef void (*DeviceReset)(DeviceState *dev);
typedef void (*BusRealize)(BusState *bus, Error **errp);
typedef void (*BusUnrealize)(BusState *bus);
typedef int (*DeviceSyncConfig)(DeviceState *dev, Error **errp);
/**
* struct DeviceClass - The base class for all devices.
@ -103,6 +104,9 @@ typedef void (*BusUnrealize)(BusState *bus);
* property is changed to %true.
* @unrealize: Callback function invoked when the #DeviceState:realized
* property is changed to %false.
* @sync_config: Callback function invoked when QMP command device-sync-config
* is called. Should synchronize device configuration from host to guest part
* and notify the guest about the change.
* @hotpluggable: indicates if #DeviceClass is hotpluggable, available
* as readonly "hotpluggable" property of #DeviceState instance
*
@ -162,6 +166,7 @@ struct DeviceClass {
DeviceReset legacy_reset;
DeviceRealize realize;
DeviceUnrealize unrealize;
DeviceSyncConfig sync_config;
/**
* @vmsd: device state serialisation description for
@ -547,6 +552,7 @@ bool qdev_hotplug_allowed(DeviceState *dev, Error **errp);
*/
HotplugHandler *qdev_get_hotplug_handler(DeviceState *dev);
void qdev_unplug(DeviceState *dev, Error **errp);
int qdev_sync_config(DeviceState *dev, Error **errp);
void qdev_simple_device_unplug_cb(HotplugHandler *hotplug_dev,
DeviceState *dev, Error **errp);
void qdev_machine_creation_done(void);

View File

@ -54,6 +54,7 @@ typedef struct VhostUserHostNotifier {
void *addr;
void *unmap_addr;
int idx;
bool destroy;
} VhostUserHostNotifier;
/**

View File

@ -147,6 +147,9 @@ struct VirtIOPCIProxy {
};
MemoryRegion modern_bar;
MemoryRegion io_bar;
/* address space for VirtIOPCIRegions */
AddressSpace modern_cfg_mem_as;
AddressSpace modern_cfg_io_as;
uint32_t legacy_io_bar_idx;
uint32_t msix_bar_idx;
uint32_t modern_io_bar_idx;

View File

@ -163,3 +163,27 @@
##
{ 'event': 'DEVICE_UNPLUG_GUEST_ERROR',
'data': { '*device': 'str', 'path': 'str' } }
##
# @device-sync-config:
#
# Synchronize device configuration from host to guest part. First,
# copy the configuration from the host part (backend) to the guest
# part (frontend). Then notify guest software that device
# configuration changed.
#
# The command may be used to notify the guest about block device
# capcity change. Currently only vhost-user-blk device supports
# this.
#
# @id: the device's ID or QOM path
#
# Features:
#
# @unstable: The command is experimental.
#
# Since: 9.1
##
{ 'command': 'device-sync-config',
'features': [ 'unstable' ],
'data': {'id': 'str'} }

View File

@ -844,6 +844,45 @@
'data': { 'pci-dev': 'str',
'node': 'uint32' } }
##
# @AcpiGenericPortProperties:
#
# Properties for acpi-generic-port objects.
#
# @pci-bus: QOM path of the PCI bus of the hostbridge associated with
# this SRAT Generic Port Affinity Structure. This is the same as
# the bus parameter for the root ports attached to this host
# bridge. The resulting SRAT Generic Port Affinity Structure will
# refer to the ACPI object in DSDT that represents the host bridge
# (e.g. ACPI0016 for CXL host bridges). See ACPI 6.5 Section
# 5.2.16.7 for more information.
#
# @node: Similar to a NUMA node ID, but instead of providing a
# reference point used for defining NUMA distances and access
# characteristics to memory or from an initiator (e.g. CPU), this
# node defines the boundary point between non-discoverable system
# buses which must be described by firmware, and a discoverable
# bus. NUMA distances and access characteristics are defined to
# and from that point. For system software to establish full
# initiator to target characteristics this information must be
# combined with information retrieved from the discoverable part
# of the path. An example would use CDAT (see UEFI.org)
# information read from devices and switches in conjunction with
# link characteristics read from PCIe Configuration space.
# To get the full path latency from CPU to CXL attached DRAM
# CXL device: Add the latency from CPU to Generic Port (from
# HMAT indexed via the the node ID in this SRAT structure) to
# that for CXL bus links, the latency across intermediate switches
# and from the EP port to the actual memory. Bandwidth is more
# complex as there may be interleaving across multiple devices
# and shared links in the path.
#
# Since: 9.1
##
{ 'struct': 'AcpiGenericPortProperties',
'data': { 'pci-bus': 'str',
'node': 'uint32' } }
##
# @RngProperties:
#
@ -1043,6 +1082,7 @@
{ 'enum': 'ObjectType',
'data': [
'acpi-generic-initiator',
'acpi-generic-port',
'authz-list',
'authz-listfile',
'authz-pam',
@ -1118,6 +1158,7 @@
'discriminator': 'qom-type',
'data': {
'acpi-generic-initiator': 'AcpiGenericInitiatorProperties',
'acpi-generic-port': 'AcpiGenericPortProperties',
'authz-list': 'AuthZListProperties',
'authz-listfile': 'AuthZListFileProperties',
'authz-pam': 'AuthZPAMProperties',

View File

@ -23,6 +23,7 @@
#include "monitor/monitor.h"
#include "monitor/qdev.h"
#include "sysemu/arch_init.h"
#include "sysemu/runstate.h"
#include "qapi/error.h"
#include "qapi/qapi-commands-qdev.h"
#include "qapi/qmp/dispatch.h"
@ -885,13 +886,20 @@ void qmp_device_add(QDict *qdict, QObject **ret_data, Error **errp)
object_unref(OBJECT(dev));
}
static DeviceState *find_device_state(const char *id, Error **errp)
/*
* Note that creating new APIs using error classes other than GenericError is
* not recommended. Set use_generic_error=true for new interfaces.
*/
static DeviceState *find_device_state(const char *id, bool use_generic_error,
Error **errp)
{
Object *obj = object_resolve_path_at(qdev_get_peripheral(), id);
DeviceState *dev;
if (!obj) {
error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
error_set(errp,
(use_generic_error ?
ERROR_CLASS_GENERIC_ERROR : ERROR_CLASS_DEVICE_NOT_FOUND),
"Device '%s' not found", id);
return NULL;
}
@ -956,7 +964,7 @@ void qdev_unplug(DeviceState *dev, Error **errp)
void qmp_device_del(const char *id, Error **errp)
{
DeviceState *dev = find_device_state(id, errp);
DeviceState *dev = find_device_state(id, false, errp);
if (dev != NULL) {
if (dev->pending_deleted_event &&
(dev->pending_deleted_expires_ms == 0 ||
@ -970,6 +978,43 @@ void qmp_device_del(const char *id, Error **errp)
}
}
int qdev_sync_config(DeviceState *dev, Error **errp)
{
DeviceClass *dc = DEVICE_GET_CLASS(dev);
if (!dc->sync_config) {
error_setg(errp, "device-sync-config is not supported for '%s'",
object_get_typename(OBJECT(dev)));
return -ENOTSUP;
}
return dc->sync_config(dev, errp);
}
void qmp_device_sync_config(const char *id, Error **errp)
{
DeviceState *dev;
/*
* During migration there is a race between syncing`configuration
* and migrating it (if migrate first, that target would get
* outdated version), so let's just not allow it.
*/
if (migration_is_running()) {
error_setg(errp, "Config synchronization is not allowed "
"during migration");
return;
}
dev = find_device_state(id, true, errp);
if (!dev) {
return;
}
qdev_sync_config(dev, errp);
}
void hmp_device_add(Monitor *mon, const QDict *qdict)
{
Error *err = NULL;
@ -1076,7 +1121,7 @@ BlockBackend *blk_by_qdev_id(const char *id, Error **errp)
GLOBAL_STATE_CODE();
dev = find_device_state(id, errp);
dev = find_device_state(id, false, errp);
if (dev == NULL) {
return NULL;
}

View File

@ -14,7 +14,7 @@ while getopts "o:" arg; do
esac
done
for machine in tests/data/acpi/*
for machine in tests/data/acpi/*/*
do
if [[ ! -d "$machine" ]];
then

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.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,37 @@
/*
* QTest fuzzer-generated testcase for virtio balloon device
*
* Copyright (c) 2024 Gao Shiyuan <gaoshiyuan@baidu.com>
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "qemu/osdep.h"
#include "libqtest.h"
/*
* https://gitlab.com/qemu-project/qemu/-/issues/2576
* Used to trigger:
* virtio_address_space_lookup: Assertion `mrs.mr' failed.
*/
static void oss_fuzz_71649(void)
{
QTestState *s = qtest_init("-device virtio-balloon -machine q35"
" -nodefaults");
qtest_outl(s, 0xcf8, 0x80000890);
qtest_outl(s, 0xcfc, 0x2);
qtest_outl(s, 0xcf8, 0x80000891);
qtest_inl(s, 0xcfc);
qtest_quit(s);
}
int main(int argc, char **argv)
{
g_test_init(&argc, &argv, NULL);
qtest_add_func("fuzz/virtio/oss_fuzz_71649", oss_fuzz_71649);
return g_test_run();
}

View File

@ -88,6 +88,7 @@ qtests_i386 = \
(config_all_devices.has_key('CONFIG_MEGASAS_SCSI_PCI') ? ['fuzz-megasas-test'] : []) + \
(config_all_devices.has_key('CONFIG_LSI_SCSI_PCI') ? ['fuzz-lsi53c895a-test'] : []) + \
(config_all_devices.has_key('CONFIG_VIRTIO_SCSI') ? ['fuzz-virtio-scsi-test'] : []) + \
(config_all_devices.has_key('CONFIG_VIRTIO_BALLOON') ? ['fuzz-virtio-balloon-test'] : []) + \
(config_all_devices.has_key('CONFIG_Q35') ? ['q35-test'] : []) + \
(config_all_devices.has_key('CONFIG_SB16') ? ['fuzz-sb16-test'] : []) + \
(config_all_devices.has_key('CONFIG_SDHCI_PCI') ? ['fuzz-sdcard-test'] : []) + \