virtio,pc,pci: features, fixes

virtio sound card support
 
 vhost-user: back-end state migration
 
 cxl:
      line length reduction
      enabling fabric management
 
 vhost-vdpa:
      shadow virtqueue hash calculation Support
      shadow virtqueue RSS Support
 
 tests:
     CPU topology related smbios test cases
 
 Fixes, cleanups all over the place
 
 Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
 -----BEGIN PGP SIGNATURE-----
 
 iQFDBAABCAAtFiEEXQn9CHHI+FuUyooNKB8NuNKNVGkFAmVKDDoPHG1zdEByZWRo
 YXQuY29tAAoJECgfDbjSjVRpF08H/0Zts8uvkHbgiOEJw4JMHU6/VaCipfIYsp01
 GSfwYOyEsXJ7GIxKWaCiMnWXEm7tebNCPKf3DoUtcAojQj3vuF9XbWBKw/bfRn83
 nGO/iiwbYViSKxkwqUI+Up5YiN9o0M8gBFrY0kScPezbnYmo5u2bcADdEEq6gH68
 D0Ea8i+WmszL891ypvgCDBL2ObDk3qX3vA5Q6J2I+HKX2ofJM59BwaKwS5ghw+IG
 BmbKXUZJNjUQfN9dQ7vJuiuqdknJ2xUzwW2Vn612ffarbOZB1DZ6ruWlrHty5TjX
 0w4IXEJPBgZYbX9oc6zvTQnbLDBJbDU89mnme0TcmNMKWmQKTtc=
 =vEv+
 -----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

virtio sound card support

vhost-user: back-end state migration

cxl:
     line length reduction
     enabling fabric management

vhost-vdpa:
     shadow virtqueue hash calculation Support
     shadow virtqueue RSS Support

tests:
    CPU topology related smbios test cases

Fixes, cleanups all over the place

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

# -----BEGIN PGP SIGNATURE-----
#
# iQFDBAABCAAtFiEEXQn9CHHI+FuUyooNKB8NuNKNVGkFAmVKDDoPHG1zdEByZWRo
# YXQuY29tAAoJECgfDbjSjVRpF08H/0Zts8uvkHbgiOEJw4JMHU6/VaCipfIYsp01
# GSfwYOyEsXJ7GIxKWaCiMnWXEm7tebNCPKf3DoUtcAojQj3vuF9XbWBKw/bfRn83
# nGO/iiwbYViSKxkwqUI+Up5YiN9o0M8gBFrY0kScPezbnYmo5u2bcADdEEq6gH68
# D0Ea8i+WmszL891ypvgCDBL2ObDk3qX3vA5Q6J2I+HKX2ofJM59BwaKwS5ghw+IG
# BmbKXUZJNjUQfN9dQ7vJuiuqdknJ2xUzwW2Vn612ffarbOZB1DZ6ruWlrHty5TjX
# 0w4IXEJPBgZYbX9oc6zvTQnbLDBJbDU89mnme0TcmNMKWmQKTtc=
# =vEv+
# -----END PGP SIGNATURE-----
# gpg: Signature made Tue 07 Nov 2023 18:06:50 HKT
# 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: (63 commits)
  acpi/tests/avocado/bits: enable console logging from bits VM
  acpi/tests/avocado/bits: enforce 32-bit SMBIOS entry point
  hw/cxl: Add tunneled command support to mailbox for switch cci.
  hw/cxl: Add dummy security state get
  hw/cxl/type3: Cleanup multiple CXL_TYPE3() calls in read/write functions
  hw/cxl/mbox: Add Get Background Operation Status Command
  hw/cxl: Add support for device sanitation
  hw/cxl/mbox: Wire up interrupts for background completion
  hw/cxl/mbox: Add support for background operations
  hw/cxl: Implement Physical Ports status retrieval
  hw/pci-bridge/cxl_downstream: Set default link width and link speed
  hw/cxl/mbox: Add Physical Switch Identify command.
  hw/cxl/mbox: Add Information and Status / Identify command
  hw/cxl: Add a switch mailbox CCI function
  hw/pci-bridge/cxl_upstream: Move defintion of device to header.
  hw/cxl/mbox: Generalize the CCI command processing
  hw/cxl/mbox: Pull the CCI definition out of the CXLDeviceState
  hw/cxl/mbox: Split mailbox command payload into separate input and output
  hw/cxl/mbox: Pull the payload out of struct cxl_cmd and make instances constant
  hw/cxl: Fix a QEMU_BUILD_BUG_ON() in switch statement scope issue.
  ...

Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
This commit is contained in:
Stefan Hajnoczi 2023-11-07 18:59:40 +08:00
commit f6b615b52d
53 changed files with 4477 additions and 324 deletions

View file

@ -50,3 +50,8 @@ config CS4231
config ASC
bool
config VIRTIO_SND
bool
default y
depends on VIRTIO

View file

@ -13,3 +13,5 @@ system_ss.add(when: 'CONFIG_PL041', if_true: files('pl041.c', 'lm4549.c'))
system_ss.add(when: 'CONFIG_SB16', if_true: files('sb16.c'))
system_ss.add(when: 'CONFIG_VT82C686', if_true: files('via-ac97.c'))
system_ss.add(when: 'CONFIG_WM8750', if_true: files('wm8750.c'))
system_ss.add(when: ['CONFIG_VIRTIO_SND', 'CONFIG_VIRTIO'], if_true: files('virtio-snd.c'))
system_ss.add(when: ['CONFIG_VIRTIO_SND', 'CONFIG_VIRTIO', 'CONFIG_VIRTIO_PCI'], if_true: files('virtio-snd-pci.c'))

View file

@ -38,3 +38,23 @@ asc_write_fifo(const char fifo, int reg, unsigned size, int wrptr, int cnt, uint
asc_write_reg(int reg, unsigned size, uint64_t value) "reg=0x%03x size=%u value=0x%"PRIx64
asc_write_extreg(const char fifo, int reg, unsigned size, uint64_t value) "fifo %c reg=0x%03x size=%u value=0x%"PRIx64
asc_update_irq(int irq, int a, int b) "set IRQ to %d (A: 0x%x B: 0x%x)"
#virtio-snd.c
virtio_snd_get_config(void *vdev, uint32_t jacks, uint32_t streams, uint32_t chmaps) "snd %p: get_config jacks=%"PRIu32" streams=%"PRIu32" chmaps=%"PRIu32""
virtio_snd_set_config(void *vdev, uint32_t jacks, uint32_t new_jacks, uint32_t streams, uint32_t new_streams, uint32_t chmaps, uint32_t new_chmaps) "snd %p: set_config jacks from %"PRIu32"->%"PRIu32", streams from %"PRIu32"->%"PRIu32", chmaps from %"PRIu32"->%"PRIu32
virtio_snd_get_features(void *vdev, uint64_t features) "snd %p: get_features 0x%"PRIx64
virtio_snd_vm_state_running(void) "vm state running"
virtio_snd_vm_state_stopped(void) "vm state stopped"
virtio_snd_realize(void *snd) "snd %p: realize"
virtio_snd_unrealize(void *snd) "snd %p: unrealize"
virtio_snd_handle_pcm_set_params(uint32_t stream) "VIRTIO_SND_PCM_SET_PARAMS called for stream %"PRIu32
virtio_snd_handle_ctrl(void *vdev, void *vq) "snd %p: handle ctrl event for queue %p"
virtio_snd_handle_pcm_info(uint32_t stream) "VIRTIO_SND_R_PCM_INFO called for stream %"PRIu32
virtio_snd_handle_pcm_start_stop(const char *code, uint32_t stream) "%s called for stream %"PRIu32
virtio_snd_handle_pcm_release(uint32_t stream) "VIRTIO_SND_PCM_RELEASE called for stream %"PRIu32
virtio_snd_handle_code(uint32_t val, const char *code) "ctrl code msg val = %"PRIu32" == %s"
virtio_snd_handle_chmap_info(void) "VIRTIO_SND_CHMAP_INFO called"
virtio_snd_handle_event(void) "event queue callback called"
virtio_snd_pcm_stream_flush(uint32_t stream) "flushing stream %"PRIu32
virtio_snd_handle_tx_xfer(void) "tx queue callback called"
virtio_snd_handle_rx_xfer(void) "rx queue callback called"

93
hw/audio/virtio-snd-pci.c Normal file
View file

@ -0,0 +1,93 @@
/*
* VIRTIO Sound Device PCI Bindings
*
* Copyright (c) 2023 Emmanouil Pitsidianakis <manos.pitsidianakis@linaro.org>
*
* This work is licensed under the terms of the GNU GPL, version 2 or
* (at your option) any later version. See the COPYING file in the
* top-level directory.
*/
#include "qemu/osdep.h"
#include "qom/object.h"
#include "qapi/error.h"
#include "hw/audio/soundhw.h"
#include "hw/virtio/virtio-pci.h"
#include "hw/audio/virtio-snd.h"
/*
* virtio-snd-pci: This extends VirtioPCIProxy.
*/
#define TYPE_VIRTIO_SND_PCI "virtio-sound-pci"
OBJECT_DECLARE_SIMPLE_TYPE(VirtIOSoundPCI, VIRTIO_SND_PCI)
struct VirtIOSoundPCI {
VirtIOPCIProxy parent_obj;
VirtIOSound vdev;
};
static Property virtio_snd_pci_properties[] = {
DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags,
VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true),
DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 2),
DEFINE_PROP_END_OF_LIST(),
};
static void virtio_snd_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp)
{
VirtIOSoundPCI *dev = VIRTIO_SND_PCI(vpci_dev);
DeviceState *vdev = DEVICE(&dev->vdev);
virtio_pci_force_virtio_1(vpci_dev);
qdev_realize(vdev, BUS(&vpci_dev->bus), errp);
}
static void virtio_snd_pci_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
VirtioPCIClass *vpciklass = VIRTIO_PCI_CLASS(klass);
device_class_set_props(dc, virtio_snd_pci_properties);
dc->desc = "Virtio Sound";
set_bit(DEVICE_CATEGORY_SOUND, dc->categories);
vpciklass->realize = virtio_snd_pci_realize;
}
static void virtio_snd_pci_instance_init(Object *obj)
{
VirtIOSoundPCI *dev = VIRTIO_SND_PCI(obj);
virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
TYPE_VIRTIO_SND);
}
static const VirtioPCIDeviceTypeInfo virtio_snd_pci_info = {
.generic_name = TYPE_VIRTIO_SND_PCI,
.instance_size = sizeof(VirtIOSoundPCI),
.instance_init = virtio_snd_pci_instance_init,
.class_init = virtio_snd_pci_class_init,
};
/* Create a Virtio Sound PCI device, so '-audio driver,model=virtio' works. */
static int virtio_snd_pci_init(PCIBus *bus, const char *audiodev)
{
DeviceState *vdev = NULL;
VirtIOSoundPCI *dev = NULL;
vdev = qdev_new(TYPE_VIRTIO_SND_PCI);
assert(vdev);
dev = VIRTIO_SND_PCI(vdev);
qdev_prop_set_string(DEVICE(&dev->vdev), "audiodev", audiodev);
qdev_realize_and_unref(vdev, BUS(bus), &error_fatal);
return 0;
}
static void virtio_snd_pci_register(void)
{
virtio_pci_types_register(&virtio_snd_pci_info);
pci_register_soundhw("virtio", "Virtio Sound", virtio_snd_pci_init);
}
type_init(virtio_snd_pci_register);

1409
hw/audio/virtio-snd.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -60,7 +60,8 @@ static void ct3_build_cdat(CDATObject *cdat, Error **errp)
return;
}
cdat->built_buf_len = cdat->build_cdat_table(&cdat->built_buf, cdat->private);
cdat->built_buf_len = cdat->build_cdat_table(&cdat->built_buf,
cdat->private);
if (!cdat->built_buf_len) {
/* Build later as not all data available yet */

View file

@ -67,16 +67,24 @@ static uint64_t cxl_cache_mem_read_reg(void *opaque, hwaddr offset,
CXLComponentState *cxl_cstate = opaque;
ComponentRegisters *cregs = &cxl_cstate->crb;
if (size == 8) {
switch (size) {
case 4:
if (cregs->special_ops && cregs->special_ops->read) {
return cregs->special_ops->read(cxl_cstate, offset, 4);
} else {
QEMU_BUILD_BUG_ON(sizeof(*cregs->cache_mem_registers) != 4);
return cregs->cache_mem_registers[offset / 4];
}
case 8:
qemu_log_mask(LOG_UNIMP,
"CXL 8 byte cache mem registers not implemented\n");
return 0;
}
if (cregs->special_ops && cregs->special_ops->read) {
return cregs->special_ops->read(cxl_cstate, offset, size);
} else {
return cregs->cache_mem_registers[offset / sizeof(*cregs->cache_mem_registers)];
default:
/*
* In line with specifiction limitaions on access sizes, this
* routine is not called with other sizes.
*/
g_assert_not_reached();
}
}
@ -117,25 +125,37 @@ static void cxl_cache_mem_write_reg(void *opaque, hwaddr offset, uint64_t value,
ComponentRegisters *cregs = &cxl_cstate->crb;
uint32_t mask;
if (size == 8) {
switch (size) {
case 4: {
QEMU_BUILD_BUG_ON(sizeof(*cregs->cache_mem_regs_write_mask) != 4);
QEMU_BUILD_BUG_ON(sizeof(*cregs->cache_mem_registers) != 4);
mask = cregs->cache_mem_regs_write_mask[offset / 4];
value &= mask;
/* RO bits should remain constant. Done by reading existing value */
value |= ~mask & cregs->cache_mem_registers[offset / 4];
if (cregs->special_ops && cregs->special_ops->write) {
cregs->special_ops->write(cxl_cstate, offset, value, size);
return;
}
if (offset >= A_CXL_HDM_DECODER_CAPABILITY &&
offset <= A_CXL_HDM_DECODER3_TARGET_LIST_HI) {
dumb_hdm_handler(cxl_cstate, offset, value);
} else {
cregs->cache_mem_registers[offset / 4] = value;
}
return;
}
case 8:
qemu_log_mask(LOG_UNIMP,
"CXL 8 byte cache mem registers not implemented\n");
return;
}
mask = cregs->cache_mem_regs_write_mask[offset / sizeof(*cregs->cache_mem_regs_write_mask)];
value &= mask;
/* RO bits should remain constant. Done by reading existing value */
value |= ~mask & cregs->cache_mem_registers[offset / sizeof(*cregs->cache_mem_registers)];
if (cregs->special_ops && cregs->special_ops->write) {
cregs->special_ops->write(cxl_cstate, offset, value, size);
return;
}
if (offset >= A_CXL_HDM_DECODER_CAPABILITY &&
offset <= A_CXL_HDM_DECODER3_TARGET_LIST_HI) {
dumb_hdm_handler(cxl_cstate, offset, value);
} else {
cregs->cache_mem_registers[offset / sizeof(*cregs->cache_mem_registers)] = value;
default:
/*
* In line with specifiction limitaions on access sizes, this
* routine is not called with other sizes.
*/
g_assert_not_reached();
}
}
@ -221,7 +241,8 @@ static void hdm_init_common(uint32_t *reg_state, uint32_t *write_msk,
ARRAY_FIELD_DP32(reg_state, CXL_HDM_DECODER_CAPABILITY, TARGET_COUNT, 1);
ARRAY_FIELD_DP32(reg_state, CXL_HDM_DECODER_CAPABILITY, INTERLEAVE_256B, 1);
ARRAY_FIELD_DP32(reg_state, CXL_HDM_DECODER_CAPABILITY, INTERLEAVE_4K, 1);
ARRAY_FIELD_DP32(reg_state, CXL_HDM_DECODER_CAPABILITY, POISON_ON_ERR_CAP, 0);
ARRAY_FIELD_DP32(reg_state, CXL_HDM_DECODER_CAPABILITY,
POISON_ON_ERR_CAP, 0);
ARRAY_FIELD_DP32(reg_state, CXL_HDM_DECODER_GLOBAL_CONTROL,
HDM_DECODER_ENABLE, 0);
write_msk[R_CXL_HDM_DECODER_GLOBAL_CONTROL] = 0x3;
@ -244,15 +265,16 @@ static void hdm_init_common(uint32_t *reg_state, uint32_t *write_msk,
}
}
void cxl_component_register_init_common(uint32_t *reg_state, uint32_t *write_msk,
void cxl_component_register_init_common(uint32_t *reg_state,
uint32_t *write_msk,
enum reg_type type)
{
int caps = 0;
/*
* In CXL 2.0 the capabilities required for each CXL component are such that,
* with the ordering chosen here, a single number can be used to define
* which capabilities should be provided.
* In CXL 2.0 the capabilities required for each CXL component are such
* that, with the ordering chosen here, a single number can be used to
* define which capabilities should be provided.
*/
switch (type) {
case CXL2_DOWNSTREAM_PORT:
@ -283,7 +305,6 @@ void cxl_component_register_init_common(uint32_t *reg_state, uint32_t *write_msk
ARRAY_FIELD_DP32(reg_state, CXL_CAPABILITY_HEADER, ARRAY_SIZE, caps);
#define init_cap_reg(reg, id, version) \
QEMU_BUILD_BUG_ON(CXL_##reg##_REGISTERS_OFFSET == 0); \
do { \
int which = R_CXL_##reg##_CAPABILITY_HEADER; \
reg_state[which] = FIELD_DP32(reg_state[which], \
@ -373,26 +394,35 @@ void cxl_component_create_dvsec(CXLComponentState *cxl,
case NON_CXL_FUNCTION_MAP_DVSEC:
break; /* Not yet implemented */
case EXTENSIONS_PORT_DVSEC:
wmask[offset + offsetof(CXLDVSECPortExtensions, control)] = 0x0F;
wmask[offset + offsetof(CXLDVSECPortExtensions, control) + 1] = 0x40;
wmask[offset + offsetof(CXLDVSECPortExtensions, alt_bus_base)] = 0xFF;
wmask[offset + offsetof(CXLDVSECPortExtensions, alt_bus_limit)] = 0xFF;
wmask[offset + offsetof(CXLDVSECPortExtensions, alt_memory_base)] = 0xF0;
wmask[offset + offsetof(CXLDVSECPortExtensions, alt_memory_base) + 1] = 0xFF;
wmask[offset + offsetof(CXLDVSECPortExtensions, alt_memory_limit)] = 0xF0;
wmask[offset + offsetof(CXLDVSECPortExtensions, alt_memory_limit) + 1] = 0xFF;
wmask[offset + offsetof(CXLDVSECPortExtensions, alt_prefetch_base)] = 0xF0;
wmask[offset + offsetof(CXLDVSECPortExtensions, alt_prefetch_base) + 1] = 0xFF;
wmask[offset + offsetof(CXLDVSECPortExtensions, alt_prefetch_limit)] = 0xF0;
wmask[offset + offsetof(CXLDVSECPortExtensions, alt_prefetch_limit) + 1] = 0xFF;
wmask[offset + offsetof(CXLDVSECPortExtensions, alt_prefetch_base_high)] = 0xFF;
wmask[offset + offsetof(CXLDVSECPortExtensions, alt_prefetch_base_high) + 1] = 0xFF;
wmask[offset + offsetof(CXLDVSECPortExtensions, alt_prefetch_base_high) + 2] = 0xFF;
wmask[offset + offsetof(CXLDVSECPortExtensions, alt_prefetch_base_high) + 3] = 0xFF;
wmask[offset + offsetof(CXLDVSECPortExtensions, alt_prefetch_limit_high)] = 0xFF;
wmask[offset + offsetof(CXLDVSECPortExtensions, alt_prefetch_limit_high) + 1] = 0xFF;
wmask[offset + offsetof(CXLDVSECPortExtensions, alt_prefetch_limit_high) + 2] = 0xFF;
wmask[offset + offsetof(CXLDVSECPortExtensions, alt_prefetch_limit_high) + 3] = 0xFF;
wmask[offset + offsetof(CXLDVSECPortExt, control)] = 0x0F;
wmask[offset + offsetof(CXLDVSECPortExt, control) + 1] = 0x40;
wmask[offset + offsetof(CXLDVSECPortExt, alt_bus_base)] = 0xFF;
wmask[offset + offsetof(CXLDVSECPortExt, alt_bus_limit)] = 0xFF;
wmask[offset + offsetof(CXLDVSECPortExt, alt_memory_base)] = 0xF0;
wmask[offset + offsetof(CXLDVSECPortExt, alt_memory_base) + 1] = 0xFF;
wmask[offset + offsetof(CXLDVSECPortExt, alt_memory_limit)] = 0xF0;
wmask[offset + offsetof(CXLDVSECPortExt, alt_memory_limit) + 1] = 0xFF;
wmask[offset + offsetof(CXLDVSECPortExt, alt_prefetch_base)] = 0xF0;
wmask[offset + offsetof(CXLDVSECPortExt, alt_prefetch_base) + 1] = 0xFF;
wmask[offset + offsetof(CXLDVSECPortExt, alt_prefetch_limit)] = 0xF0;
wmask[offset + offsetof(CXLDVSECPortExt, alt_prefetch_limit) + 1] =
0xFF;
wmask[offset + offsetof(CXLDVSECPortExt, alt_prefetch_base_high)] =
0xFF;
wmask[offset + offsetof(CXLDVSECPortExt, alt_prefetch_base_high) + 1] =
0xFF;
wmask[offset + offsetof(CXLDVSECPortExt, alt_prefetch_base_high) + 2] =
0xFF;
wmask[offset + offsetof(CXLDVSECPortExt, alt_prefetch_base_high) + 3] =
0xFF;
wmask[offset + offsetof(CXLDVSECPortExt, alt_prefetch_limit_high)] =
0xFF;
wmask[offset + offsetof(CXLDVSECPortExt, alt_prefetch_limit_high) + 1] =
0xFF;
wmask[offset + offsetof(CXLDVSECPortExt, alt_prefetch_limit_high) + 2] =
0xFF;
wmask[offset + offsetof(CXLDVSECPortExt, alt_prefetch_limit_high) + 3] =
0xFF;
break;
case GPF_PORT_DVSEC:
wmask[offset + offsetof(CXLDVSECPortGPF, phase1_ctrl)] = 0x0F;
@ -420,7 +450,7 @@ void cxl_component_create_dvsec(CXLComponentState *cxl,
default: /* Registers are RO for other component types */
break;
}
/* There are rw1cs bits in the status register but never set currently */
/* There are rw1cs bits in the status register but never set */
break;
}

View file

@ -32,10 +32,13 @@ static uint64_t caps_reg_read(void *opaque, hwaddr offset, unsigned size)
{
CXLDeviceState *cxl_dstate = opaque;
if (size == 4) {
return cxl_dstate->caps_reg_state32[offset / sizeof(*cxl_dstate->caps_reg_state32)];
} else {
return cxl_dstate->caps_reg_state64[offset / sizeof(*cxl_dstate->caps_reg_state64)];
switch (size) {
case 4:
return cxl_dstate->caps_reg_state32[offset / size];
case 8:
return cxl_dstate->caps_reg_state64[offset / size];
default:
g_assert_not_reached();
}
}
@ -59,7 +62,17 @@ static uint64_t dev_reg_read(void *opaque, hwaddr offset, unsigned size)
static uint64_t mailbox_reg_read(void *opaque, hwaddr offset, unsigned size)
{
CXLDeviceState *cxl_dstate = opaque;
CXLDeviceState *cxl_dstate;
CXLCCI *cci = opaque;
if (object_dynamic_cast(OBJECT(cci->intf), TYPE_CXL_TYPE3)) {
cxl_dstate = &CXL_TYPE3(cci->intf)->cxl_dstate;
} else if (object_dynamic_cast(OBJECT(cci->intf),
TYPE_CXL_SWITCH_MAILBOX_CCI)) {
cxl_dstate = &CXL_SWITCH_MAILBOX_CCI(cci->intf)->cxl_dstate;
} else {
return 0;
}
switch (size) {
case 1:
@ -69,6 +82,25 @@ static uint64_t mailbox_reg_read(void *opaque, hwaddr offset, unsigned size)
case 4:
return cxl_dstate->mbox_reg_state32[offset / size];
case 8:
if (offset == A_CXL_DEV_BG_CMD_STS) {
uint64_t bg_status_reg;
bg_status_reg = FIELD_DP64(0, CXL_DEV_BG_CMD_STS, OP,
cci->bg.opcode);
bg_status_reg = FIELD_DP64(bg_status_reg, CXL_DEV_BG_CMD_STS,
PERCENTAGE_COMP, cci->bg.complete_pct);
bg_status_reg = FIELD_DP64(bg_status_reg, CXL_DEV_BG_CMD_STS,
RET_CODE, cci->bg.ret_code);
/* endian? */
cxl_dstate->mbox_reg_state64[offset / size] = bg_status_reg;
}
if (offset == A_CXL_DEV_MAILBOX_STS) {
uint64_t status_reg = cxl_dstate->mbox_reg_state64[offset / size];
if (cci->bg.complete_pct) {
status_reg = FIELD_DP64(status_reg, CXL_DEV_MAILBOX_STS, BG_OP,
0);
cxl_dstate->mbox_reg_state64[offset / size] = status_reg;
}
}
return cxl_dstate->mbox_reg_state64[offset / size];
default:
g_assert_not_reached();
@ -101,8 +133,7 @@ static void mailbox_mem_writeq(uint64_t *reg_state, hwaddr offset,
case A_CXL_DEV_MAILBOX_CMD:
break;
case A_CXL_DEV_BG_CMD_STS:
/* BG not supported */
/* fallthrough */
break;
case A_CXL_DEV_MAILBOX_STS:
/* Read only register, will get updated by the state machine */
return;
@ -120,7 +151,17 @@ static void mailbox_mem_writeq(uint64_t *reg_state, hwaddr offset,
static void mailbox_reg_write(void *opaque, hwaddr offset, uint64_t value,
unsigned size)
{
CXLDeviceState *cxl_dstate = opaque;
CXLDeviceState *cxl_dstate;
CXLCCI *cci = opaque;
if (object_dynamic_cast(OBJECT(cci->intf), TYPE_CXL_TYPE3)) {
cxl_dstate = &CXL_TYPE3(cci->intf)->cxl_dstate;
} else if (object_dynamic_cast(OBJECT(cci->intf),
TYPE_CXL_SWITCH_MAILBOX_CCI)) {
cxl_dstate = &CXL_SWITCH_MAILBOX_CCI(cci->intf)->cxl_dstate;
} else {
return;
}
if (offset >= A_CXL_DEV_CMD_PAYLOAD) {
memcpy(cxl_dstate->mbox_reg_state + offset, &value, size);
@ -140,7 +181,49 @@ static void mailbox_reg_write(void *opaque, hwaddr offset, uint64_t value,
if (ARRAY_FIELD_EX32(cxl_dstate->mbox_reg_state32, CXL_DEV_MAILBOX_CTRL,
DOORBELL)) {
cxl_process_mailbox(cxl_dstate);
uint64_t command_reg =
cxl_dstate->mbox_reg_state64[R_CXL_DEV_MAILBOX_CMD];
uint8_t cmd_set = FIELD_EX64(command_reg, CXL_DEV_MAILBOX_CMD,
COMMAND_SET);
uint8_t cmd = FIELD_EX64(command_reg, CXL_DEV_MAILBOX_CMD, COMMAND);
size_t len_in = FIELD_EX64(command_reg, CXL_DEV_MAILBOX_CMD, LENGTH);
uint8_t *pl = cxl_dstate->mbox_reg_state + A_CXL_DEV_CMD_PAYLOAD;
/*
* Copy taken to avoid need for individual command handlers to care
* about aliasing.
*/
g_autofree uint8_t *pl_in_copy = NULL;
size_t len_out = 0;
uint64_t status_reg;
bool bg_started = false;
int rc;
pl_in_copy = g_memdup2(pl, len_in);
if (len_in == 0 || pl_in_copy) {
/* Avoid stale data - including from earlier cmds */
memset(pl, 0, CXL_MAILBOX_MAX_PAYLOAD_SIZE);
rc = cxl_process_cci_message(cci, cmd_set, cmd, len_in, pl_in_copy,
&len_out, pl, &bg_started);
} else {
rc = CXL_MBOX_INTERNAL_ERROR;
}
/* Set bg and the return code */
status_reg = FIELD_DP64(0, CXL_DEV_MAILBOX_STS, BG_OP,
bg_started ? 1 : 0);
status_reg = FIELD_DP64(status_reg, CXL_DEV_MAILBOX_STS, ERRNO, rc);
/* Set the return length */
command_reg = FIELD_DP64(0, CXL_DEV_MAILBOX_CMD, COMMAND_SET, cmd_set);
command_reg = FIELD_DP64(command_reg, CXL_DEV_MAILBOX_CMD,
COMMAND, cmd);
command_reg = FIELD_DP64(command_reg, CXL_DEV_MAILBOX_CMD,
LENGTH, len_out);
cxl_dstate->mbox_reg_state64[R_CXL_DEV_MAILBOX_CMD] = command_reg;
cxl_dstate->mbox_reg_state64[R_CXL_DEV_MAILBOX_STS] = status_reg;
/* Tell the host we're done */
ARRAY_FIELD_DP32(cxl_dstate->mbox_reg_state32, CXL_DEV_MAILBOX_CTRL,
DOORBELL, 0);
}
}
@ -220,7 +303,8 @@ static const MemoryRegionOps caps_ops = {
},
};
void cxl_device_register_block_init(Object *obj, CXLDeviceState *cxl_dstate)
void cxl_device_register_block_init(Object *obj, CXLDeviceState *cxl_dstate,
CXLCCI *cci)
{
/* This will be a BAR, so needs to be rounded up to pow2 for PCI spec */
memory_region_init(&cxl_dstate->device_registers, obj, "device-registers",
@ -230,7 +314,7 @@ void cxl_device_register_block_init(Object *obj, CXLDeviceState *cxl_dstate)
"cap-array", CXL_CAPS_SIZE);
memory_region_init_io(&cxl_dstate->device, obj, &dev_ops, cxl_dstate,
"device-status", CXL_DEVICE_STATUS_REGISTERS_LENGTH);
memory_region_init_io(&cxl_dstate->mailbox, obj, &mailbox_ops, cxl_dstate,
memory_region_init_io(&cxl_dstate->mailbox, obj, &mailbox_ops, cci,
"mailbox", CXL_MAILBOX_REGISTERS_LENGTH);
memory_region_init_io(&cxl_dstate->memory_device, obj, &mdev_ops,
cxl_dstate, "memory device caps",
@ -273,16 +357,25 @@ static void device_reg_init_common(CXLDeviceState *cxl_dstate)
static void mailbox_reg_init_common(CXLDeviceState *cxl_dstate)
{
/* 2048 payload size, with no interrupt or background support */
const uint8_t msi_n = 9;
/* 2048 payload size */
ARRAY_FIELD_DP32(cxl_dstate->mbox_reg_state32, CXL_DEV_MAILBOX_CAP,
PAYLOAD_SIZE, CXL_MAILBOX_PAYLOAD_SHIFT);
cxl_dstate->payload_size = CXL_MAILBOX_MAX_PAYLOAD_SIZE;
/* irq support */
ARRAY_FIELD_DP32(cxl_dstate->mbox_reg_state32, CXL_DEV_MAILBOX_CAP,
BG_INT_CAP, 1);
ARRAY_FIELD_DP32(cxl_dstate->mbox_reg_state32, CXL_DEV_MAILBOX_CAP,
MSI_N, msi_n);
cxl_dstate->mbox_msi_n = msi_n;
}
static void memdev_reg_init_common(CXLDeviceState *cxl_dstate) { }
void cxl_device_register_init_common(CXLDeviceState *cxl_dstate)
void cxl_device_register_init_t3(CXLType3Dev *ct3d)
{
CXLDeviceState *cxl_dstate = &ct3d->cxl_dstate;
uint64_t *cap_h = cxl_dstate->caps_reg_state64;
const int cap_count = 3;
@ -300,7 +393,29 @@ void cxl_device_register_init_common(CXLDeviceState *cxl_dstate)
cxl_device_cap_init(cxl_dstate, MEMORY_DEVICE, 0x4000, 1);
memdev_reg_init_common(cxl_dstate);
cxl_initialize_mailbox(cxl_dstate);
cxl_initialize_mailbox_t3(&ct3d->cci, DEVICE(ct3d),
CXL_MAILBOX_MAX_PAYLOAD_SIZE);
}
void cxl_device_register_init_swcci(CSWMBCCIDev *sw)
{
CXLDeviceState *cxl_dstate = &sw->cxl_dstate;
uint64_t *cap_h = cxl_dstate->caps_reg_state64;
const int cap_count = 3;
/* CXL Device Capabilities Array Register */
ARRAY_FIELD_DP64(cap_h, CXL_DEV_CAP_ARRAY, CAP_ID, 0);
ARRAY_FIELD_DP64(cap_h, CXL_DEV_CAP_ARRAY, CAP_VERSION, 1);
ARRAY_FIELD_DP64(cap_h, CXL_DEV_CAP_ARRAY, CAP_COUNT, cap_count);
cxl_device_cap_init(cxl_dstate, DEVICE_STATUS, 1, 2);
device_reg_init_common(cxl_dstate);
cxl_device_cap_init(cxl_dstate, MAILBOX, 2, 1);
mailbox_reg_init_common(cxl_dstate);
cxl_device_cap_init(cxl_dstate, MEMORY_DEVICE, 0x4000, 1);
memdev_reg_init_common(cxl_dstate);
}
uint64_t cxl_device_get_timestamp(CXLDeviceState *cxl_dstate)

View file

@ -143,7 +143,7 @@ bool cxl_event_insert(CXLDeviceState *cxlds, CXLEventLogType log_type,
CXLRetCode cxl_event_get_records(CXLDeviceState *cxlds, CXLGetEventPayload *pl,
uint8_t log_type, int max_recs,
uint16_t *len)
size_t *len)
{
CXLEventLog *log;
CXLEvent *entry;
@ -170,8 +170,10 @@ CXLRetCode cxl_event_get_records(CXLDeviceState *cxlds, CXLGetEventPayload *pl,
if (log->overflow_err_count) {
pl->flags |= CXL_GET_EVENT_FLAG_OVERFLOW;
pl->overflow_err_count = cpu_to_le16(log->overflow_err_count);
pl->first_overflow_timestamp = cpu_to_le64(log->first_overflow_timestamp);
pl->last_overflow_timestamp = cpu_to_le64(log->last_overflow_timestamp);
pl->first_overflow_timestamp =
cpu_to_le64(log->first_overflow_timestamp);
pl->last_overflow_timestamp =
cpu_to_le64(log->last_overflow_timestamp);
}
pl->record_count = cpu_to_le16(nr);
@ -180,7 +182,8 @@ CXLRetCode cxl_event_get_records(CXLDeviceState *cxlds, CXLGetEventPayload *pl,
return CXL_MBOX_SUCCESS;
}
CXLRetCode cxl_event_clear_records(CXLDeviceState *cxlds, CXLClearEventPayload *pl)
CXLRetCode cxl_event_clear_records(CXLDeviceState *cxlds,
CXLClearEventPayload *pl)
{
CXLEventLog *log;
uint8_t log_type;

File diff suppressed because it is too large Load diff

View file

@ -6,6 +6,7 @@ system_ss.add(when: 'CONFIG_CXL',
'cxl-host.c',
'cxl-cdat.c',
'cxl-events.c',
'switch-mailbox-cci.c',
),
if_false: files(
'cxl-host-stubs.c',

111
hw/cxl/switch-mailbox-cci.c Normal file
View file

@ -0,0 +1,111 @@
/*
* SPDX-License-Identifier: GPL-2.0-or-later
*
* Emulation of a CXL Switch Mailbox CCI PCIe function.
*
* Copyright (c) 2023 Huawei Technologies.
*
* From www.computeexpresslink.org
* Compute Express Link (CXL) Specification revision 3.0 Version 1.0
*/
#include "qemu/osdep.h"
#include "hw/pci/pci.h"
#include "hw/pci-bridge/cxl_upstream_port.h"
#include "qapi/error.h"
#include "qemu/log.h"
#include "qemu/module.h"
#include "hw/qdev-properties.h"
#include "hw/cxl/cxl.h"
static void cswmbcci_reset(DeviceState *dev)
{
CSWMBCCIDev *cswmb = CXL_SWITCH_MAILBOX_CCI(dev);
cxl_device_register_init_swcci(cswmb);
}
static void cswbcci_realize(PCIDevice *pci_dev, Error **errp)
{
CSWMBCCIDev *cswmb = CXL_SWITCH_MAILBOX_CCI(pci_dev);
CXLComponentState *cxl_cstate = &cswmb->cxl_cstate;
CXLDeviceState *cxl_dstate = &cswmb->cxl_dstate;
CXLDVSECRegisterLocator *regloc_dvsec;
CXLUpstreamPort *usp;
if (!cswmb->target) {
error_setg(errp, "Target not set");
return;
}
usp = CXL_USP(cswmb->target);
pcie_endpoint_cap_init(pci_dev, 0x80);
cxl_cstate->dvsec_offset = 0x100;
cxl_cstate->pdev = pci_dev;
cswmb->cci = &usp->swcci;
cxl_device_register_block_init(OBJECT(pci_dev), cxl_dstate, cswmb->cci);
pci_register_bar(pci_dev, 0,
PCI_BASE_ADDRESS_SPACE_MEMORY |
PCI_BASE_ADDRESS_MEM_TYPE_64,
&cxl_dstate->device_registers);
regloc_dvsec = &(CXLDVSECRegisterLocator) {
.rsvd = 0,
.reg0_base_lo = RBI_CXL_DEVICE_REG | 0,
.reg0_base_hi = 0,
};
cxl_component_create_dvsec(cxl_cstate, CXL3_SWITCH_MAILBOX_CCI,
REG_LOC_DVSEC_LENGTH, REG_LOC_DVSEC,
REG_LOC_DVSEC_REVID, (uint8_t *)regloc_dvsec);
cxl_initialize_mailbox_swcci(cswmb->cci, DEVICE(pci_dev),
DEVICE(cswmb->target),
CXL_MAILBOX_MAX_PAYLOAD_SIZE);
}
static void cswmbcci_exit(PCIDevice *pci_dev)
{
/* Nothing to do here yet */
}
static Property cxl_switch_cci_props[] = {
DEFINE_PROP_LINK("target", CSWMBCCIDev,
target, TYPE_CXL_USP, PCIDevice *),
DEFINE_PROP_END_OF_LIST(),
};
static void cswmbcci_class_init(ObjectClass *oc, void *data)
{
DeviceClass *dc = DEVICE_CLASS(oc);
PCIDeviceClass *pc = PCI_DEVICE_CLASS(oc);
pc->realize = cswbcci_realize;
pc->exit = cswmbcci_exit;
/* Serial bus, CXL Switch CCI */
pc->class_id = 0x0c0b;
/*
* Huawei Technologies
* CXL Switch Mailbox CCI - DID assigned for emulation only.
* No real hardware will ever use this ID.
*/
pc->vendor_id = 0x19e5;
pc->device_id = 0xa123;
pc->revision = 0;
dc->desc = "CXL Switch Mailbox CCI";
dc->reset = cswmbcci_reset;
device_class_set_props(dc, cxl_switch_cci_props);
}
static const TypeInfo cswmbcci_info = {
.name = TYPE_CXL_SWITCH_MAILBOX_CCI,
.parent = TYPE_PCI_DEVICE,
.class_init = cswmbcci_class_init,
.instance_size = sizeof(CSWMBCCIDev),
.interfaces = (InterfaceInfo[]) {
{ INTERFACE_PCIE_DEVICE },
{ }
},
};
static void cxl_switch_mailbox_cci_register(void)
{
type_register_static(&cswmbcci_info);
}
type_init(cxl_switch_mailbox_cci_register);

View file

@ -23,6 +23,7 @@
#include "qemu/pmem.h"
#include "qemu/range.h"
#include "qemu/rcu.h"
#include "qemu/guest-random.h"
#include "sysemu/hostmem.h"
#include "sysemu/numa.h"
#include "hw/cxl/cxl.h"
@ -208,10 +209,9 @@ static int ct3_build_cdat_table(CDATSubHeader ***cdat_table, void *priv)
}
if (nonvolatile_mr) {
uint64_t base = volatile_mr ? memory_region_size(volatile_mr) : 0;
rc = ct3_build_cdat_entries_for_mr(&(table[cur_ent]), dsmad_handle++,
nonvolatile_mr, true,
(volatile_mr ?
memory_region_size(volatile_mr) : 0));
nonvolatile_mr, true, base);
if (rc < 0) {
goto error_cleanup;
}
@ -514,7 +514,8 @@ static void ct3d_reg_write(void *opaque, hwaddr offset, uint64_t value,
case A_CXL_RAS_UNC_ERR_STATUS:
{
uint32_t capctrl = ldl_le_p(cache_mem + R_CXL_RAS_ERR_CAP_CTRL);
uint32_t fe = FIELD_EX32(capctrl, CXL_RAS_ERR_CAP_CTRL, FIRST_ERROR_POINTER);
uint32_t fe = FIELD_EX32(capctrl, CXL_RAS_ERR_CAP_CTRL,
FIRST_ERROR_POINTER);
CXLError *cxl_err;
uint32_t unc_err;
@ -533,7 +534,8 @@ static void ct3d_reg_write(void *opaque, hwaddr offset, uint64_t value,
* closest to behavior of hardware not capable of multiple
* header recording.
*/
QTAILQ_FOREACH_SAFE(cxl_err, &ct3d->error_list, node, cxl_next) {
QTAILQ_FOREACH_SAFE(cxl_err, &ct3d->error_list, node,
cxl_next) {
if ((1 << cxl_err->type) & value) {
QTAILQ_REMOVE(&ct3d->error_list, cxl_err, node);
g_free(cxl_err);
@ -715,7 +717,8 @@ static void ct3_realize(PCIDevice *pci_dev, Error **errp)
pci_dev, CXL_COMPONENT_REG_BAR_IDX,
PCI_BASE_ADDRESS_SPACE_MEMORY | PCI_BASE_ADDRESS_MEM_TYPE_64, mr);
cxl_device_register_block_init(OBJECT(pci_dev), &ct3d->cxl_dstate);
cxl_device_register_block_init(OBJECT(pci_dev), &ct3d->cxl_dstate,
&ct3d->cci);
pci_register_bar(pci_dev, CXL_DEVICE_REG_BAR_IDX,
PCI_BASE_ADDRESS_SPACE_MEMORY |
PCI_BASE_ADDRESS_MEM_TYPE_64,
@ -885,32 +888,43 @@ static int cxl_type3_hpa_to_as_and_dpa(CXLType3Dev *ct3d,
MemTxResult cxl_type3_read(PCIDevice *d, hwaddr host_addr, uint64_t *data,
unsigned size, MemTxAttrs attrs)
{
CXLType3Dev *ct3d = CXL_TYPE3(d);
uint64_t dpa_offset = 0;
AddressSpace *as = NULL;
int res;
res = cxl_type3_hpa_to_as_and_dpa(CXL_TYPE3(d), host_addr, size,
res = cxl_type3_hpa_to_as_and_dpa(ct3d, host_addr, size,
&as, &dpa_offset);
if (res) {
return MEMTX_ERROR;
}
if (sanitize_running(&ct3d->cci)) {
qemu_guest_getrandom_nofail(data, size);
return MEMTX_OK;
}
return address_space_read(as, dpa_offset, attrs, data, size);
}
MemTxResult cxl_type3_write(PCIDevice *d, hwaddr host_addr, uint64_t data,
unsigned size, MemTxAttrs attrs)
{
CXLType3Dev *ct3d = CXL_TYPE3(d);
uint64_t dpa_offset = 0;
AddressSpace *as = NULL;
int res;
res = cxl_type3_hpa_to_as_and_dpa(CXL_TYPE3(d), host_addr, size,
res = cxl_type3_hpa_to_as_and_dpa(ct3d, host_addr, size,
&as, &dpa_offset);
if (res) {
return MEMTX_ERROR;
}
if (sanitize_running(&ct3d->cci)) {
return MEMTX_OK;
}
return address_space_write(as, dpa_offset, attrs, &data, size);
}
@ -921,7 +935,18 @@ static void ct3d_reset(DeviceState *dev)
uint32_t *write_msk = ct3d->cxl_cstate.crb.cache_mem_regs_write_mask;
cxl_component_register_init_common(reg_state, write_msk, CXL2_TYPE3_DEVICE);
cxl_device_register_init_common(&ct3d->cxl_dstate);
cxl_device_register_init_t3(ct3d);
/*
* Bring up an endpoint to target with MCTP over VDM.
* This device is emulating an MLD with single LD for now.
*/
cxl_initialize_t3_fm_owned_ld_mctpcci(&ct3d->vdm_fm_owned_ld_mctp_cci,
DEVICE(ct3d), DEVICE(ct3d),
512); /* Max payload made up */
cxl_initialize_t3_ld_cci(&ct3d->ld0_cci, DEVICE(ct3d), DEVICE(ct3d),
512); /* Max payload made up */
}
static Property ct3_props[] = {
@ -1072,7 +1097,8 @@ void qmp_cxl_inject_poison(const char *path, uint64_t start, uint64_t length,
if (((start >= p->start) && (start < p->start + p->length)) ||
((start + length > p->start) &&
(start + length <= p->start + p->length))) {
error_setg(errp, "Overlap with existing poisoned region not supported");
error_setg(errp,
"Overlap with existing poisoned region not supported");
return;
}
}
@ -1085,7 +1111,8 @@ void qmp_cxl_inject_poison(const char *path, uint64_t start, uint64_t length,
p = g_new0(CXLPoison, 1);
p->length = length;
p->start = start;
p->type = CXL_POISON_TYPE_INTERNAL; /* Different from injected via the mbox */
/* Different from injected via the mbox */
p->type = CXL_POISON_TYPE_INTERNAL;
QLIST_INSERT_HEAD(&ct3d->poison_list, p, node);
ct3d->poison_list_cnt++;
@ -1222,7 +1249,8 @@ void qmp_cxl_inject_correctable_error(const char *path, CxlCorErrorType type,
return;
}
/* If the error is masked, nothting to do here */
if (!((1 << cxl_err_type) & ~ldl_le_p(reg_state + R_CXL_RAS_COR_ERR_MASK))) {
if (!((1 << cxl_err_type) &
~ldl_le_p(reg_state + R_CXL_RAS_COR_ERR_MASK))) {
return;
}
@ -1372,7 +1400,8 @@ void qmp_cxl_inject_dram_event(const char *path, CxlEventLog log, uint8_t flags,
bool has_bank, uint8_t bank,
bool has_row, uint32_t row,
bool has_column, uint16_t column,
bool has_correction_mask, uint64List *correction_mask,
bool has_correction_mask,
uint64List *correction_mask,
Error **errp)
{
Object *obj = object_resolve_path(path, NULL);
@ -1473,7 +1502,7 @@ void qmp_cxl_inject_memory_module_event(const char *path, CxlEventLog log,
int16_t temperature,
uint32_t dirty_shutdown_count,
uint32_t corrected_volatile_error_count,
uint32_t corrected_persistent_error_count,
uint32_t corrected_persist_error_count,
Error **errp)
{
Object *obj = object_resolve_path(path, NULL);
@ -1513,8 +1542,10 @@ void qmp_cxl_inject_memory_module_event(const char *path, CxlEventLog log,
module.life_used = life_used;
stw_le_p(&module.temperature, temperature);
stl_le_p(&module.dirty_shutdown_count, dirty_shutdown_count);
stl_le_p(&module.corrected_volatile_error_count, corrected_volatile_error_count);
stl_le_p(&module.corrected_persistent_error_count, corrected_persistent_error_count);
stl_le_p(&module.corrected_volatile_error_count,
corrected_volatile_error_count);
stl_le_p(&module.corrected_persistent_error_count,
corrected_persist_error_count);
if (cxl_event_insert(cxlds, enc_log, (CXLEventRecordRaw *)&module)) {
cxl_event_irq_assert(ct3d);

View file

@ -33,7 +33,8 @@ void qmp_cxl_inject_dram_event(const char *path, CxlEventLog log, uint8_t flags,
bool has_bank, uint8_t bank,
bool has_row, uint32_t row,
bool has_column, uint16_t column,
bool has_correction_mask, uint64List *correction_mask,
bool has_correction_mask,
uint64List *correction_mask,
Error **errp) {}
void qmp_cxl_inject_memory_module_event(const char *path, CxlEventLog log,
@ -45,7 +46,7 @@ void qmp_cxl_inject_memory_module_event(const char *path, CxlEventLog log,
int16_t temperature,
uint32_t dirty_shutdown_count,
uint32_t corrected_volatile_error_count,
uint32_t corrected_persistent_error_count,
uint32_t corrected_persist_error_count,
Error **errp) {}
void qmp_cxl_inject_poison(const char *path, uint64_t start, uint64_t length,

View file

@ -13,6 +13,7 @@
#include "hw/pci/msi.h"
#include "hw/pci/pcie.h"
#include "hw/pci/pcie_port.h"
#include "hw/cxl/cxl.h"
#include "qapi/error.h"
typedef struct CXLDownstreamPort {
@ -23,9 +24,6 @@ typedef struct CXLDownstreamPort {
CXLComponentState cxl_cstate;
} CXLDownstreamPort;
#define TYPE_CXL_DSP "cxl-downstream"
DECLARE_INSTANCE_CHECKER(CXLDownstreamPort, CXL_DSP, TYPE_CXL_DSP)
#define CXL_DOWNSTREAM_PORT_MSI_OFFSET 0x70
#define CXL_DOWNSTREAM_PORT_MSI_NR_VECTOR 1
#define CXL_DOWNSTREAM_PORT_EXP_OFFSET 0x90
@ -98,7 +96,7 @@ static void build_dvsecs(CXLComponentState *cxl)
{
uint8_t *dvsec;
dvsec = (uint8_t *)&(CXLDVSECPortExtensions){ 0 };
dvsec = (uint8_t *)&(CXLDVSECPortExt){ 0 };
cxl_component_create_dvsec(cxl, CXL2_DOWNSTREAM_PORT,
EXTENSIONS_PORT_DVSEC_LENGTH,
EXTENSIONS_PORT_DVSEC,
@ -212,6 +210,19 @@ 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 void cxl_dsp_class_init(ObjectClass *oc, void *data)
{
DeviceClass *dc = DEVICE_CLASS(oc);
@ -232,6 +243,7 @@ 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

@ -107,7 +107,7 @@ static void build_dvsecs(CXLComponentState *cxl)
{
uint8_t *dvsec;
dvsec = (uint8_t *)&(CXLDVSECPortExtensions){ 0 };
dvsec = (uint8_t *)&(CXLDVSECPortExt){ 0 };
cxl_component_create_dvsec(cxl, CXL2_ROOT_PORT,
EXTENSIONS_PORT_DVSEC_LENGTH,
EXTENSIONS_PORT_DVSEC,

View file

@ -14,6 +14,7 @@
#include "hw/pci/msi.h"
#include "hw/pci/pcie.h"
#include "hw/pci/pcie_port.h"
#include "hw/pci-bridge/cxl_upstream_port.h"
/*
* Null value of all Fs suggested by IEEE RA guidelines for use of
* EU, OUI and CID
@ -30,16 +31,6 @@
#define CXL_UPSTREAM_PORT_DVSEC_OFFSET \
(CXL_UPSTREAM_PORT_SN_OFFSET + PCI_EXT_CAP_DSN_SIZEOF)
typedef struct CXLUpstreamPort {
/*< private >*/
PCIEPort parent_obj;
/*< public >*/
CXLComponentState cxl_cstate;
DOECap doe_cdat;
uint64_t sn;
} CXLUpstreamPort;
CXLComponentState *cxl_usp_to_cstate(CXLUpstreamPort *usp)
{
return &usp->cxl_cstate;
@ -116,7 +107,7 @@ static void build_dvsecs(CXLComponentState *cxl)
{
uint8_t *dvsec;
dvsec = (uint8_t *)&(CXLDVSECPortExtensions){
dvsec = (uint8_t *)&(CXLDVSECPortExt){
.status = 0x1, /* Port Power Management Init Complete */
};
cxl_component_create_dvsec(cxl, CXL2_UPSTREAM_PORT,

View file

@ -298,9 +298,108 @@ static struct vhost_dev *vuf_get_vhost(VirtIODevice *vdev)
return &fs->vhost_dev;
}
/**
* Fetch the internal state from virtiofsd and save it to `f`.
*/
static int vuf_save_state(QEMUFile *f, void *pv, size_t size,
const VMStateField *field, JSONWriter *vmdesc)
{
VirtIODevice *vdev = pv;
VHostUserFS *fs = VHOST_USER_FS(vdev);
Error *local_error = NULL;
int ret;
ret = vhost_save_backend_state(&fs->vhost_dev, f, &local_error);
if (ret < 0) {
error_reportf_err(local_error,
"Error saving back-end state of %s device %s "
"(tag: \"%s\"): ",
vdev->name, vdev->parent_obj.canonical_path,
fs->conf.tag ?: "<none>");
return ret;
}
return 0;
}
/**
* Load virtiofsd's internal state from `f` and send it over to virtiofsd.
*/
static int vuf_load_state(QEMUFile *f, void *pv, size_t size,
const VMStateField *field)
{
VirtIODevice *vdev = pv;
VHostUserFS *fs = VHOST_USER_FS(vdev);
Error *local_error = NULL;
int ret;
ret = vhost_load_backend_state(&fs->vhost_dev, f, &local_error);
if (ret < 0) {
error_reportf_err(local_error,
"Error loading back-end state of %s device %s "
"(tag: \"%s\"): ",
vdev->name, vdev->parent_obj.canonical_path,
fs->conf.tag ?: "<none>");
return ret;
}
return 0;
}
static bool vuf_is_internal_migration(void *opaque)
{
/* TODO: Return false when an external migration is requested */
return true;
}
static int vuf_check_migration_support(void *opaque)
{
VirtIODevice *vdev = opaque;
VHostUserFS *fs = VHOST_USER_FS(vdev);
if (!vhost_supports_device_state(&fs->vhost_dev)) {
error_report("Back-end of %s device %s (tag: \"%s\") does not support "
"migration through qemu",
vdev->name, vdev->parent_obj.canonical_path,
fs->conf.tag ?: "<none>");
return -ENOTSUP;
}
return 0;
}
static const VMStateDescription vuf_backend_vmstate;
static const VMStateDescription vuf_vmstate = {
.name = "vhost-user-fs",
.unmigratable = 1,
.version_id = 0,
.fields = (VMStateField[]) {
VMSTATE_VIRTIO_DEVICE,
VMSTATE_END_OF_LIST()
},
.subsections = (const VMStateDescription * []) {
&vuf_backend_vmstate,
NULL,
}
};
static const VMStateDescription vuf_backend_vmstate = {
.name = "vhost-user-fs-backend",
.version_id = 0,
.needed = vuf_is_internal_migration,
.pre_load = vuf_check_migration_support,
.pre_save = vuf_check_migration_support,
.fields = (VMStateField[]) {
{
.name = "back-end",
.info = &(const VMStateInfo) {
.name = "virtio-fs back-end state",
.get = vuf_load_state,
.put = vuf_save_state,
},
},
VMSTATE_END_OF_LIST()
},
};
static Property vuf_properties[] = {

View file

@ -103,6 +103,8 @@ typedef enum VhostUserRequest {
VHOST_USER_SET_STATUS = 39,
VHOST_USER_GET_STATUS = 40,
VHOST_USER_GET_SHARED_OBJECT = 41,
VHOST_USER_SET_DEVICE_STATE_FD = 42,
VHOST_USER_CHECK_DEVICE_STATE = 43,
VHOST_USER_MAX
} VhostUserRequest;
@ -201,6 +203,12 @@ typedef struct {
uint32_t size; /* the following payload size */
} QEMU_PACKED VhostUserHeader;
/* Request payload of VHOST_USER_SET_DEVICE_STATE_FD */
typedef struct VhostUserTransferDeviceState {
uint32_t direction;
uint32_t phase;
} VhostUserTransferDeviceState;
typedef union {
#define VHOST_USER_VRING_IDX_MASK (0xff)
#define VHOST_USER_VRING_NOFD_MASK (0x1 << 8)
@ -216,6 +224,7 @@ typedef union {
VhostUserVringArea area;
VhostUserInflight inflight;
VhostUserShared object;
VhostUserTransferDeviceState transfer_state;
} VhostUserPayload;
typedef struct VhostUserMsg {
@ -2855,6 +2864,140 @@ static void vhost_user_reset_status(struct vhost_dev *dev)
}
}
static bool vhost_user_supports_device_state(struct vhost_dev *dev)
{
return virtio_has_feature(dev->protocol_features,
VHOST_USER_PROTOCOL_F_DEVICE_STATE);
}
static int vhost_user_set_device_state_fd(struct vhost_dev *dev,
VhostDeviceStateDirection direction,
VhostDeviceStatePhase phase,
int fd,
int *reply_fd,
Error **errp)
{
int ret;
struct vhost_user *vu = dev->opaque;
VhostUserMsg msg = {
.hdr = {
.request = VHOST_USER_SET_DEVICE_STATE_FD,
.flags = VHOST_USER_VERSION,
.size = sizeof(msg.payload.transfer_state),
},
.payload.transfer_state = {
.direction = direction,
.phase = phase,
},
};
*reply_fd = -1;
if (!vhost_user_supports_device_state(dev)) {
close(fd);
error_setg(errp, "Back-end does not support migration state transfer");
return -ENOTSUP;
}
ret = vhost_user_write(dev, &msg, &fd, 1);
close(fd);
if (ret < 0) {
error_setg_errno(errp, -ret,
"Failed to send SET_DEVICE_STATE_FD message");
return ret;
}
ret = vhost_user_read(dev, &msg);
if (ret < 0) {
error_setg_errno(errp, -ret,
"Failed to receive SET_DEVICE_STATE_FD reply");
return ret;
}
if (msg.hdr.request != VHOST_USER_SET_DEVICE_STATE_FD) {
error_setg(errp,
"Received unexpected message type, expected %d, received %d",
VHOST_USER_SET_DEVICE_STATE_FD, msg.hdr.request);
return -EPROTO;
}
if (msg.hdr.size != sizeof(msg.payload.u64)) {
error_setg(errp,
"Received bad message size, expected %zu, received %" PRIu32,
sizeof(msg.payload.u64), msg.hdr.size);
return -EPROTO;
}
if ((msg.payload.u64 & 0xff) != 0) {
error_setg(errp, "Back-end did not accept migration state transfer");
return -EIO;
}
if (!(msg.payload.u64 & VHOST_USER_VRING_NOFD_MASK)) {
*reply_fd = qemu_chr_fe_get_msgfd(vu->user->chr);
if (*reply_fd < 0) {
error_setg(errp,
"Failed to get back-end-provided transfer pipe FD");
*reply_fd = -1;
return -EIO;
}
}
return 0;
}
static int vhost_user_check_device_state(struct vhost_dev *dev, Error **errp)
{
int ret;
VhostUserMsg msg = {
.hdr = {
.request = VHOST_USER_CHECK_DEVICE_STATE,
.flags = VHOST_USER_VERSION,
.size = 0,
},
};
if (!vhost_user_supports_device_state(dev)) {
error_setg(errp, "Back-end does not support migration state transfer");
return -ENOTSUP;
}
ret = vhost_user_write(dev, &msg, NULL, 0);
if (ret < 0) {
error_setg_errno(errp, -ret,
"Failed to send CHECK_DEVICE_STATE message");
return ret;
}
ret = vhost_user_read(dev, &msg);
if (ret < 0) {
error_setg_errno(errp, -ret,
"Failed to receive CHECK_DEVICE_STATE reply");
return ret;
}
if (msg.hdr.request != VHOST_USER_CHECK_DEVICE_STATE) {
error_setg(errp,
"Received unexpected message type, expected %d, received %d",
VHOST_USER_CHECK_DEVICE_STATE, msg.hdr.request);
return -EPROTO;
}
if (msg.hdr.size != sizeof(msg.payload.u64)) {
error_setg(errp,
"Received bad message size, expected %zu, received %" PRIu32,
sizeof(msg.payload.u64), msg.hdr.size);
return -EPROTO;
}
if (msg.payload.u64 != 0) {
error_setg(errp, "Back-end failed to process its internal state");
return -EIO;
}
return 0;
}
const VhostOps user_ops = {
.backend_type = VHOST_BACKEND_TYPE_USER,
.vhost_backend_init = vhost_user_backend_init,
@ -2890,4 +3033,7 @@ const VhostOps user_ops = {
.vhost_set_inflight_fd = vhost_user_set_inflight_fd,
.vhost_dev_start = vhost_user_dev_start,
.vhost_reset_status = vhost_user_reset_status,
.vhost_supports_device_state = vhost_user_supports_device_state,
.vhost_set_device_state_fd = vhost_user_set_device_state_fd,
.vhost_check_device_state = vhost_user_check_device_state,
};

View file

@ -2159,3 +2159,244 @@ int vhost_reset_device(struct vhost_dev *hdev)
return -ENOSYS;
}
bool vhost_supports_device_state(struct vhost_dev *dev)
{
if (dev->vhost_ops->vhost_supports_device_state) {
return dev->vhost_ops->vhost_supports_device_state(dev);
}
return false;
}
int vhost_set_device_state_fd(struct vhost_dev *dev,
VhostDeviceStateDirection direction,
VhostDeviceStatePhase phase,
int fd,
int *reply_fd,
Error **errp)
{
if (dev->vhost_ops->vhost_set_device_state_fd) {
return dev->vhost_ops->vhost_set_device_state_fd(dev, direction, phase,
fd, reply_fd, errp);
}
error_setg(errp,
"vhost transport does not support migration state transfer");
return -ENOSYS;
}
int vhost_check_device_state(struct vhost_dev *dev, Error **errp)
{
if (dev->vhost_ops->vhost_check_device_state) {
return dev->vhost_ops->vhost_check_device_state(dev, errp);
}
error_setg(errp,
"vhost transport does not support migration state transfer");
return -ENOSYS;
}
int vhost_save_backend_state(struct vhost_dev *dev, QEMUFile *f, Error **errp)
{
/* Maximum chunk size in which to transfer the state */
const size_t chunk_size = 1 * 1024 * 1024;
g_autofree void *transfer_buf = NULL;
g_autoptr(GError) g_err = NULL;
int pipe_fds[2], read_fd = -1, write_fd = -1, reply_fd = -1;
int ret;
/* [0] for reading (our end), [1] for writing (back-end's end) */
if (!g_unix_open_pipe(pipe_fds, FD_CLOEXEC, &g_err)) {
error_setg(errp, "Failed to set up state transfer pipe: %s",
g_err->message);
ret = -EINVAL;
goto fail;
}
read_fd = pipe_fds[0];
write_fd = pipe_fds[1];
/*
* VHOST_TRANSFER_STATE_PHASE_STOPPED means the device must be stopped.
* Ideally, it is suspended, but SUSPEND/RESUME currently do not exist for
* vhost-user, so just check that it is stopped at all.
*/
assert(!dev->started);
/* Transfer ownership of write_fd to the back-end */
ret = vhost_set_device_state_fd(dev,
VHOST_TRANSFER_STATE_DIRECTION_SAVE,
VHOST_TRANSFER_STATE_PHASE_STOPPED,
write_fd,
&reply_fd,
errp);
if (ret < 0) {
error_prepend(errp, "Failed to initiate state transfer: ");
goto fail;
}
/* If the back-end wishes to use a different pipe, switch over */
if (reply_fd >= 0) {
close(read_fd);
read_fd = reply_fd;
}
transfer_buf = g_malloc(chunk_size);
while (true) {
ssize_t read_ret;
read_ret = RETRY_ON_EINTR(read(read_fd, transfer_buf, chunk_size));
if (read_ret < 0) {
ret = -errno;
error_setg_errno(errp, -ret, "Failed to receive state");
goto fail;
}
assert(read_ret <= chunk_size);
qemu_put_be32(f, read_ret);
if (read_ret == 0) {
/* EOF */
break;
}
qemu_put_buffer(f, transfer_buf, read_ret);
}
/*
* Back-end will not really care, but be clean and close our end of the pipe
* before inquiring the back-end about whether transfer was successful
*/
close(read_fd);
read_fd = -1;
/* Also, verify that the device is still stopped */
assert(!dev->started);
ret = vhost_check_device_state(dev, errp);
if (ret < 0) {
goto fail;
}
ret = 0;
fail:
if (read_fd >= 0) {
close(read_fd);
}
return ret;
}
int vhost_load_backend_state(struct vhost_dev *dev, QEMUFile *f, Error **errp)
{
size_t transfer_buf_size = 0;
g_autofree void *transfer_buf = NULL;
g_autoptr(GError) g_err = NULL;
int pipe_fds[2], read_fd = -1, write_fd = -1, reply_fd = -1;
int ret;
/* [0] for reading (back-end's end), [1] for writing (our end) */
if (!g_unix_open_pipe(pipe_fds, FD_CLOEXEC, &g_err)) {
error_setg(errp, "Failed to set up state transfer pipe: %s",
g_err->message);
ret = -EINVAL;
goto fail;
}
read_fd = pipe_fds[0];
write_fd = pipe_fds[1];
/*
* VHOST_TRANSFER_STATE_PHASE_STOPPED means the device must be stopped.
* Ideally, it is suspended, but SUSPEND/RESUME currently do not exist for
* vhost-user, so just check that it is stopped at all.
*/
assert(!dev->started);
/* Transfer ownership of read_fd to the back-end */
ret = vhost_set_device_state_fd(dev,
VHOST_TRANSFER_STATE_DIRECTION_LOAD,
VHOST_TRANSFER_STATE_PHASE_STOPPED,
read_fd,
&reply_fd,
errp);
if (ret < 0) {
error_prepend(errp, "Failed to initiate state transfer: ");
goto fail;
}
/* If the back-end wishes to use a different pipe, switch over */
if (reply_fd >= 0) {
close(write_fd);
write_fd = reply_fd;
}
while (true) {
size_t this_chunk_size = qemu_get_be32(f);
ssize_t write_ret;
const uint8_t *transfer_pointer;
if (this_chunk_size == 0) {
/* End of state */
break;
}
if (transfer_buf_size < this_chunk_size) {
transfer_buf = g_realloc(transfer_buf, this_chunk_size);
transfer_buf_size = this_chunk_size;
}
if (qemu_get_buffer(f, transfer_buf, this_chunk_size) <
this_chunk_size)
{
error_setg(errp, "Failed to read state");
ret = -EINVAL;
goto fail;
}
transfer_pointer = transfer_buf;
while (this_chunk_size > 0) {
write_ret = RETRY_ON_EINTR(
write(write_fd, transfer_pointer, this_chunk_size)
);
if (write_ret < 0) {
ret = -errno;
error_setg_errno(errp, -ret, "Failed to send state");
goto fail;
} else if (write_ret == 0) {
error_setg(errp, "Failed to send state: Connection is closed");
ret = -ECONNRESET;
goto fail;
}
assert(write_ret <= this_chunk_size);
this_chunk_size -= write_ret;
transfer_pointer += write_ret;
}
}
/*
* Close our end, thus ending transfer, before inquiring the back-end about
* whether transfer was successful
*/
close(write_fd);
write_fd = -1;
/* Also, verify that the device is still stopped */
assert(!dev->started);
ret = vhost_check_device_state(dev, errp);
if (ret < 0) {
goto fail;
}
ret = 0;
fail:
if (write_fd >= 0) {
close(write_fd);
}
return ret;
}