mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-08-01 06:43:53 -06:00
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:
commit
f6b615b52d
53 changed files with 4477 additions and 324 deletions
|
@ -50,3 +50,8 @@ config CS4231
|
|||
|
||||
config ASC
|
||||
bool
|
||||
|
||||
config VIRTIO_SND
|
||||
bool
|
||||
default y
|
||||
depends on VIRTIO
|
||||
|
|
|
@ -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'))
|
||||
|
|
|
@ -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
93
hw/audio/virtio-snd-pci.c
Normal 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
1409
hw/audio/virtio-snd.c
Normal file
File diff suppressed because it is too large
Load diff
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
@ -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
111
hw/cxl/switch-mailbox-cci.c
Normal 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);
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 },
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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[] = {
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue