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

@ -2310,6 +2310,15 @@ F: hw/virtio/virtio-mem-pci.h
F: hw/virtio/virtio-mem-pci.c
F: include/hw/virtio/virtio-mem.h
virtio-snd
M: Gerd Hoffmann <kraxel@redhat.com>
R: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
S: Supported
F: hw/audio/virtio-snd.c
F: hw/audio/virtio-snd-pci.c
F: include/hw/audio/virtio-snd.h
F: docs/system/devices/virtio-snd.rst
nvme
M: Keith Busch <kbusch@kernel.org>
M: Klaus Jensen <its@irrelevant.dk>

View file

@ -108,6 +108,43 @@ A vring state description
:num: a 32-bit number
A vring descriptor index for split virtqueues
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+-------------+---------------------+
| vring index | index in avail ring |
+-------------+---------------------+
:vring index: 32-bit index of the respective virtqueue
:index in avail ring: 32-bit value, of which currently only the lower 16
bits are used:
- Bits 015: Index of the next *Available Ring* descriptor that the
back-end will process. This is a free-running index that is not
wrapped by the ring size.
- Bits 1631: Reserved (set to zero)
Vring descriptor indices for packed virtqueues
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+-------------+--------------------+
| vring index | descriptor indices |
+-------------+--------------------+
:vring index: 32-bit index of the respective virtqueue
:descriptor indices: 32-bit value:
- Bits 014: Index of the next *Available Ring* descriptor that the
back-end will process. This is a free-running index that is not
wrapped by the ring size.
- Bit 15: Driver (Available) Ring Wrap Counter
- Bits 1630: Index of the entry in the *Used Ring* where the back-end
will place the next descriptor. This is a free-running index that
is not wrapped by the ring size.
- Bit 31: Device (Used) Ring Wrap Counter
A vring address description
^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -285,6 +322,32 @@ VhostUserShared
:UUID: 16 bytes UUID, whose first three components (a 32-bit value, then
two 16-bit values) are stored in big endian.
Device state transfer parameters
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+--------------------+-----------------+
| transfer direction | migration phase |
+--------------------+-----------------+
:transfer direction: a 32-bit enum, describing the direction in which
the state is transferred:
- 0: Save: Transfer the state from the back-end to the front-end,
which happens on the source side of migration
- 1: Load: Transfer the state from the front-end to the back-end,
which happens on the destination side of migration
:migration phase: a 32-bit enum, describing the state in which the VM
guest and devices are:
- 0: Stopped (in the period after the transfer of memory-mapped
regions before switch-over to the destination): The VM guest is
stopped, and the vhost-user device is suspended (see
:ref:`Suspended device state <suspended_device_state>`).
In the future, additional phases might be added e.g. to allow
iterative migration while the device is running.
C structure
-----------
@ -344,6 +407,7 @@ in the ancillary data:
* ``VHOST_USER_SET_VRING_ERR``
* ``VHOST_USER_SET_BACKEND_REQ_FD`` (previous name ``VHOST_USER_SET_SLAVE_REQ_FD``)
* ``VHOST_USER_SET_INFLIGHT_FD`` (if ``VHOST_USER_PROTOCOL_F_INFLIGHT_SHMFD``)
* ``VHOST_USER_SET_DEVICE_STATE_FD``
If *front-end* is unable to send the full message or receives a wrong
reply it will close the connection. An optional reconnection mechanism
@ -374,35 +438,50 @@ negotiation.
Ring states
-----------
Rings can be in one of three states:
Rings have two independent states: started/stopped, and enabled/disabled.
* stopped: the back-end must not process the ring at all.
* While a ring is stopped, the back-end must not process the ring at
all, regardless of whether it is enabled or disabled. The
enabled/disabled state should still be tracked, though, so it can come
into effect once the ring is started.
* started but disabled: the back-end must process the ring without
* started and disabled: The back-end must process the ring without
causing any side effects. For example, for a networking device,
in the disabled state the back-end must not supply any new RX packets,
but must process and discard any TX packets.
* started and enabled.
* started and enabled: The back-end must process the ring normally, i.e.
process all requests and execute them.
Each ring is initialized in a stopped state. The back-end must start
ring upon receiving a kick (that is, detecting that file descriptor is
readable) on the descriptor specified by ``VHOST_USER_SET_VRING_KICK``
or receiving the in-band message ``VHOST_USER_VRING_KICK`` if negotiated,
and stop ring upon receiving ``VHOST_USER_GET_VRING_BASE``.
Each ring is initialized in a stopped and disabled state. The back-end
must start a ring upon receiving a kick (that is, detecting that file
descriptor is readable) on the descriptor specified by
``VHOST_USER_SET_VRING_KICK`` or receiving the in-band message
``VHOST_USER_VRING_KICK`` if negotiated, and stop a ring upon receiving
``VHOST_USER_GET_VRING_BASE``.
Rings can be enabled or disabled by ``VHOST_USER_SET_VRING_ENABLE``.
If ``VHOST_USER_F_PROTOCOL_FEATURES`` has not been negotiated, the
ring starts directly in the enabled state.
If ``VHOST_USER_F_PROTOCOL_FEATURES`` has been negotiated, the ring is
initialized in a disabled state and is enabled by
``VHOST_USER_SET_VRING_ENABLE`` with parameter 1.
In addition, upon receiving a ``VHOST_USER_SET_FEATURES`` message from
the front-end without ``VHOST_USER_F_PROTOCOL_FEATURES`` set, the
back-end must enable all rings immediately.
While processing the rings (whether they are enabled or not), the back-end
must support changing some configuration aspects on the fly.
.. _suspended_device_state:
Suspended device state
^^^^^^^^^^^^^^^^^^^^^^
While all vrings are stopped, the device is *suspended*. In addition to
not processing any vring (because they are stopped), the device must:
* not write to any guest memory regions,
* not send any notifications to the guest,
* not send any messages to the front-end,
* still process and reply to messages from the front-end.
Multiple queue support
----------------------
@ -490,7 +569,8 @@ ancillary data, it may be used to inform the front-end that the log has
been modified.
Once the source has finished migration, rings will be stopped by the
source. No further update must be done before rings are restarted.
source (:ref:`Suspended device state <suspended_device_state>`). No
further update must be done before rings are restarted.
In postcopy migration the back-end is started before all the memory has
been received from the source host, and care must be taken to avoid
@ -502,6 +582,80 @@ it performs WAKE ioctl's on the userfaultfd to wake the stalled
back-end. The front-end indicates support for this via the
``VHOST_USER_PROTOCOL_F_PAGEFAULT`` feature.
.. _migrating_backend_state:
Migrating back-end state
^^^^^^^^^^^^^^^^^^^^^^^^
Migrating device state involves transferring the state from one
back-end, called the source, to another back-end, called the
destination. After migration, the destination transparently resumes
operation without requiring the driver to re-initialize the device at
the VIRTIO level. If the migration fails, then the source can
transparently resume operation until another migration attempt is made.
Generally, the front-end is connected to a virtual machine guest (which
contains the driver), which has its own state to transfer between source
and destination, and therefore will have an implementation-specific
mechanism to do so. The ``VHOST_USER_PROTOCOL_F_DEVICE_STATE`` feature
provides functionality to have the front-end include the back-end's
state in this transfer operation so the back-end does not need to
implement its own mechanism, and so the virtual machine may have its
complete state, including vhost-user devices' states, contained within a
single stream of data.
To do this, the back-end state is transferred from back-end to front-end
on the source side, and vice versa on the destination side. This
transfer happens over a channel that is negotiated using the
``VHOST_USER_SET_DEVICE_STATE_FD`` message. This message has two
parameters:
* Direction of transfer: On the source, the data is saved, transferring
it from the back-end to the front-end. On the destination, the data
is loaded, transferring it from the front-end to the back-end.
* Migration phase: Currently, the only supported phase is the period
after the transfer of memory-mapped regions before switch-over to the
destination, when both the source and destination devices are
suspended (:ref:`Suspended device state <suspended_device_state>`).
In the future, additional phases might be supported to allow iterative
migration while the device is running.
The nature of the channel is implementation-defined, but it must
generally behave like a pipe: The writing end will write all the data it
has into it, signalling the end of data by closing its end. The reading
end must read all of this data (until encountering the end of file) and
process it.
* When saving, the writing end is the source back-end, and the reading
end is the source front-end. After reading the state data from the
channel, the source front-end must transfer it to the destination
front-end through an implementation-defined mechanism.
* When loading, the writing end is the destination front-end, and the
reading end is the destination back-end. After reading the state data
from the channel, the destination back-end must deserialize its
internal state from that data and set itself up to allow the driver to
seamlessly resume operation on the VIRTIO level.
Seamlessly resuming operation means that the migration must be
transparent to the guest driver, which operates on the VIRTIO level.
This driver will not perform any re-initialization steps, but continue
to use the device as if no migration had occurred. The vhost-user
front-end, however, will re-initialize the vhost state on the
destination, following the usual protocol for establishing a connection
to a vhost-user back-end: This includes, for example, setting up memory
mappings and kick and call FDs as necessary, negotiating protocol
features, or setting the initial vring base indices (to the same value
as on the source side, so that operation can resume).
Both on the source and on the destination side, after the respective
front-end has seen all data transferred (when the transfer FD has been
closed), it sends the ``VHOST_USER_CHECK_DEVICE_STATE`` message to
verify that data transfer was successful in the back-end, too. The
back-end responds once it knows whether the transfer and processing was
successful or not.
Memory access
-------------
@ -896,6 +1050,7 @@ Protocol features
#define VHOST_USER_PROTOCOL_F_STATUS 16
#define VHOST_USER_PROTOCOL_F_XEN_MMAP 17
#define VHOST_USER_PROTOCOL_F_SHARED_OBJECT 18
#define VHOST_USER_PROTOCOL_F_DEVICE_STATE 19
Front-end message types
-----------------------
@ -1042,18 +1197,54 @@ Front-end message types
``VHOST_USER_SET_VRING_BASE``
:id: 10
:equivalent ioctl: ``VHOST_SET_VRING_BASE``
:request payload: vring state description
:request payload: vring descriptor index/indices
:reply payload: N/A
Sets the base offset in the available vring.
Sets the next index to use for descriptors in this vring:
* For a split virtqueue, sets only the next descriptor index to
process in the *Available Ring*. The device is supposed to read the
next index in the *Used Ring* from the respective vring structure in
guest memory.
* For a packed virtqueue, both indices are supplied, as they are not
explicitly available in memory.
Consequently, the payload type is specific to the type of virt queue
(*a vring descriptor index for split virtqueues* vs. *vring descriptor
indices for packed virtqueues*).
``VHOST_USER_GET_VRING_BASE``
:id: 11
:equivalent ioctl: ``VHOST_USER_GET_VRING_BASE``
:request payload: vring state description
:reply payload: vring state description
:reply payload: vring descriptor index/indices
Get the available vring base offset.
Stops the vring and returns the current descriptor index or indices:
* For a split virtqueue, returns only the 16-bit next descriptor
index to process in the *Available Ring*. Note that this may
differ from the available ring index in the vring structure in
memory, which points to where the driver will put new available
descriptors. For the *Used Ring*, the device only needs the next
descriptor index at which to put new descriptors, which is the
value in the vring structure in memory, so this value is not
covered by this message.
* For a packed virtqueue, neither index is explicitly available to
read from memory, so both indices (as maintained by the device) are
returned.
Consequently, the payload type is specific to the type of virt queue
(*a vring descriptor index for split virtqueues* vs. *vring descriptor
indices for packed virtqueues*).
When and as long as all of a devices vrings are stopped, it is
*suspended*, see :ref:`Suspended device state
<suspended_device_state>`.
The request payloads *num* field is currently reserved and must be
set to 0.
``VHOST_USER_SET_VRING_KICK``
:id: 12
@ -1464,6 +1655,76 @@ Front-end message types
the requested UUID. Back-end will reply passing the fd when the operation
is successful, or no fd otherwise.
``VHOST_USER_SET_DEVICE_STATE_FD``
:id: 42
:equivalent ioctl: N/A
:request payload: device state transfer parameters
:reply payload: ``u64``
Front-end and back-end negotiate a channel over which to transfer the
back-ends internal state during migration. Either side (front-end or
back-end) may create the channel. The nature of this channel is not
restricted or defined in this document, but whichever side creates it
must create a file descriptor that is provided to the respectively
other side, allowing access to the channel. This FD must behave as
follows:
* For the writing end, it must allow writing the whole back-end state
sequentially. Closing the file descriptor signals the end of
transfer.
* For the reading end, it must allow reading the whole back-end state
sequentially. The end of file signals the end of the transfer.
For example, the channel may be a pipe, in which case the two ends of
the pipe fulfill these requirements respectively.
Initially, the front-end creates a channel along with such an FD. It
passes the FD to the back-end as ancillary data of a
``VHOST_USER_SET_DEVICE_STATE_FD`` message. The back-end may create a
different transfer channel, passing the respective FD back to the
front-end as ancillary data of the reply. If so, the front-end must
then discard its channel and use the one provided by the back-end.
Whether the back-end should decide to use its own channel is decided
based on efficiency: If the channel is a pipe, both ends will most
likely need to copy data into and out of it. Any channel that allows
for more efficient processing on at least one end, e.g. through
zero-copy, is considered more efficient and thus preferred. If the
back-end can provide such a channel, it should decide to use it.
The request payload contains parameters for the subsequent data
transfer, as described in the :ref:`Migrating back-end state
<migrating_backend_state>` section.
The value returned is both an indication for success, and whether a
file descriptor for a back-end-provided channel is returned: Bits 07
are 0 on success, and non-zero on error. Bit 8 is the invalid FD
flag; this flag is set when there is no file descriptor returned.
When this flag is not set, the front-end must use the returned file
descriptor as its end of the transfer channel. The back-end must not
both indicate an error and return a file descriptor.
Using this function requires prior negotiation of the
``VHOST_USER_PROTOCOL_F_DEVICE_STATE`` feature.
``VHOST_USER_CHECK_DEVICE_STATE``
:id: 43
:equivalent ioctl: N/A
:request payload: N/A
:reply payload: ``u64``
After transferring the back-ends internal state during migration (see
the :ref:`Migrating back-end state <migrating_backend_state>`
section), check whether the back-end was able to successfully fully
process the state.
The value returned indicates success or error; 0 is success, any
non-zero value is an error.
Using this function requires prior negotiation of the
``VHOST_USER_PROTOCOL_F_DEVICE_STATE`` feature.
Back-end message types
----------------------

View file

@ -93,6 +93,7 @@ Emulated Devices
devices/vhost-user.rst
devices/virtio-gpu.rst
devices/virtio-pmem.rst
devices/virtio-snd.rst
devices/vhost-user-rng.rst
devices/canokey.rst
devices/usb-u2f.rst

View file

@ -0,0 +1,49 @@
virtio sound
============
This document explains the setup and usage of the Virtio sound device.
The Virtio sound device is a paravirtualized sound card device.
Linux kernel support
--------------------
Virtio sound requires a guest Linux kernel built with the
``CONFIG_SND_VIRTIO`` option.
Description
-----------
Virtio sound implements capture and playback from inside a guest using the
configured audio backend of the host machine.
Device properties
-----------------
The Virtio sound device can be configured with the following properties:
* ``jacks`` number of physical jacks (Unimplemented).
* ``streams`` number of PCM streams. At the moment, no stream configuration is supported: the first one will always be a playback stream, an optional second will always be a capture stream. Adding more will cycle stream directions from playback to capture.
* ``chmaps`` number of channel maps (Unimplemented).
All streams are stereo and have the default channel positions ``Front left, right``.
Examples
--------
Add an audio device and an audio backend at once with ``-audio`` and ``model=virtio``:
* pulseaudio: ``-audio driver=pa,model=virtio``
or ``-audio driver=pa,model=virtio,server=/run/user/1000/pulse/native``
* sdl: ``-audio driver=sdl,model=virtio``
* coreaudio: ``-audio driver=coreaudio,model=virtio``
etc.
To specifically add virtualized sound devices, you have to specify a PCI device
and an audio backend listed with ``-audio driver=help`` that works on your host
machine, e.g.:
::
-device virtio-sound-pci,audiodev=my_audiodev \
-audiodev alsa,id=my_audiodev

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;
}

View file

@ -0,0 +1,235 @@
/*
* VIRTIO Sound Device conforming to
*
* "Virtual I/O Device (VIRTIO) Version 1.2
* Committee Specification Draft 01
* 09 May 2022"
*
* Copyright (c) 2023 Emmanouil Pitsidianakis <manos.pitsidianakis@linaro.org>
* Copyright (C) 2019 OpenSynergy GmbH
*
* 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.
*/
#ifndef QEMU_VIRTIO_SOUND_H
#define QEMU_VIRTIO_SOUND_H
#include "hw/virtio/virtio.h"
#include "audio/audio.h"
#include "standard-headers/linux/virtio_ids.h"
#include "standard-headers/linux/virtio_snd.h"
#define TYPE_VIRTIO_SND "virtio-sound-device"
#define VIRTIO_SND(obj) \
OBJECT_CHECK(VirtIOSound, (obj), TYPE_VIRTIO_SND)
/* CONFIGURATION SPACE */
typedef struct virtio_snd_config virtio_snd_config;
/* COMMON DEFINITIONS */
/* common header for request/response*/
typedef struct virtio_snd_hdr virtio_snd_hdr;
/* event notification */
typedef struct virtio_snd_event virtio_snd_event;
/* common control request to query an item information */
typedef struct virtio_snd_query_info virtio_snd_query_info;
/* JACK CONTROL MESSAGES */
typedef struct virtio_snd_jack_hdr virtio_snd_jack_hdr;
/* jack information structure */
typedef struct virtio_snd_jack_info virtio_snd_jack_info;
/* jack remapping control request */
typedef struct virtio_snd_jack_remap virtio_snd_jack_remap;
/*
* PCM CONTROL MESSAGES
*/
typedef struct virtio_snd_pcm_hdr virtio_snd_pcm_hdr;
/* PCM stream info structure */
typedef struct virtio_snd_pcm_info virtio_snd_pcm_info;
/* set PCM stream params */
typedef struct virtio_snd_pcm_set_params virtio_snd_pcm_set_params;
/* I/O request header */
typedef struct virtio_snd_pcm_xfer virtio_snd_pcm_xfer;
/* I/O request status */
typedef struct virtio_snd_pcm_status virtio_snd_pcm_status;
/* device structs */
typedef struct VirtIOSound VirtIOSound;
typedef struct VirtIOSoundPCMStream VirtIOSoundPCMStream;
typedef struct virtio_snd_ctrl_command virtio_snd_ctrl_command;
typedef struct VirtIOSoundPCM VirtIOSoundPCM;
typedef struct VirtIOSoundPCMBuffer VirtIOSoundPCMBuffer;
/*
* The VirtIO sound spec reuses layouts and values from the High Definition
* Audio spec (virtio/v1.2: 5.14 Sound Device). This struct handles each I/O
* message's buffer (virtio/v1.2: 5.14.6.8 PCM I/O Messages).
*
* In the case of TX (i.e. playback) buffers, we defer reading the raw PCM data
* from the virtqueue until QEMU's sound backsystem calls the output callback.
* This is tracked by the `bool populated;` field, which is set to true when
* data has been read into our own buffer for consumption.
*
* VirtIOSoundPCMBuffer has a dynamic size since it includes the raw PCM data
* in its allocation. It must be initialized and destroyed as follows:
*
* size_t size = [[derived from owned VQ element descriptor sizes]];
* buffer = g_malloc0(sizeof(VirtIOSoundPCMBuffer) + size);
* buffer->elem = [[owned VQ element]];
*
* [..]
*
* g_free(buffer->elem);
* g_free(buffer);
*/
struct VirtIOSoundPCMBuffer {
QSIMPLEQ_ENTRY(VirtIOSoundPCMBuffer) entry;
VirtQueueElement *elem;
VirtQueue *vq;
size_t size;
/*
* In TX / Plaback, `offset` represents the first unused position inside
* `data`. If `offset == size` then there are no unused data left.
*/
uint64_t offset;
/* Used for the TX queue for lazy I/O copy from `elem` */
bool populated;
/*
* VirtIOSoundPCMBuffer is an unsized type because it ends with an array of
* bytes. The size of `data` is determined from the I/O message's read-only
* or write-only size when allocating VirtIOSoundPCMBuffer.
*/
uint8_t data[];
};
struct VirtIOSoundPCM {
VirtIOSound *snd;
/*
* PCM parameters are a separate field instead of a VirtIOSoundPCMStream
* field, because the operation of PCM control requests is first
* VIRTIO_SND_R_PCM_SET_PARAMS and then VIRTIO_SND_R_PCM_PREPARE; this
* means that some times we get parameters without having an allocated
* stream yet.
*/
virtio_snd_pcm_set_params *pcm_params;
VirtIOSoundPCMStream **streams;
};
struct VirtIOSoundPCMStream {
VirtIOSoundPCM *pcm;
virtio_snd_pcm_info info;
virtio_snd_pcm_set_params params;
uint32_t id;
/* channel position values (VIRTIO_SND_CHMAP_XXX) */
uint8_t positions[VIRTIO_SND_CHMAP_MAX_SIZE];
VirtIOSound *s;
bool flushing;
audsettings as;
union {
SWVoiceIn *in;
SWVoiceOut *out;
} voice;
QemuMutex queue_mutex;
bool active;
QSIMPLEQ_HEAD(, VirtIOSoundPCMBuffer) queue;
QSIMPLEQ_HEAD(, VirtIOSoundPCMBuffer) invalid;
};
/*
* PCM stream state machine.
* -------------------------
*
* 5.14.6.6.1 PCM Command Lifecycle
* ================================
*
* A PCM stream has the following command lifecycle:
* - `SET PARAMETERS`
* The driver negotiates the stream parameters (format, transport, etc) with
* the device.
* Possible valid transitions: `SET PARAMETERS`, `PREPARE`.
* - `PREPARE`
* The device prepares the stream (allocates resources, etc).
* Possible valid transitions: `SET PARAMETERS`, `PREPARE`, `START`,
* `RELEASE`. Output only: the driver transfers data for pre-buffing.
* - `START`
* The device starts the stream (unmute, putting into running state, etc).
* Possible valid transitions: `STOP`.
* The driver transfers data to/from the stream.
* - `STOP`
* The device stops the stream (mute, putting into non-running state, etc).
* Possible valid transitions: `START`, `RELEASE`.
* - `RELEASE`
* The device releases the stream (frees resources, etc).
* Possible valid transitions: `SET PARAMETERS`, `PREPARE`.
*
* +---------------+ +---------+ +---------+ +-------+ +-------+
* | SetParameters | | Prepare | | Release | | Start | | Stop |
* +---------------+ +---------+ +---------+ +-------+ +-------+
* |- | | | |
* || | | | |
* |< | | | |
* |------------->| | | |
* |<-------------| | | |
* | |- | | |
* | || | | |
* | |< | | |
* | |--------------------->| |
* | |---------->| | |
* | | | |-------->|
* | | | |<--------|
* | | |<-------------------|
* |<-------------------------| | |
* | |<----------| | |
*
* CTRL in the VirtIOSound device
* ==============================
*
* The control messages that affect the state of a stream arrive in the
* `virtio_snd_handle_ctrl()` queue callback and are of type `struct
* virtio_snd_ctrl_command`. They are stored in a queue field in the device
* type, `VirtIOSound`. This allows deferring the CTRL request completion if
* it's not immediately possible due to locking/state reasons.
*
* The CTRL message is finally handled in `process_cmd()`.
*/
struct VirtIOSound {
VirtIODevice parent_obj;
VirtQueue *queues[VIRTIO_SND_VQ_MAX];
uint64_t features;
VirtIOSoundPCM *pcm;
QEMUSoundCard card;
VMChangeStateEntry *vmstate;
virtio_snd_config snd_conf;
QemuMutex cmdq_mutex;
QTAILQ_HEAD(, virtio_snd_ctrl_command) cmdq;
bool processing_cmdq;
};
struct virtio_snd_ctrl_command {
VirtQueueElement *elem;
VirtQueue *vq;
virtio_snd_hdr ctrl;
virtio_snd_hdr resp;
QTAILQ_ENTRY(virtio_snd_ctrl_command) next;
};
#endif

View file

@ -61,4 +61,10 @@ OBJECT_DECLARE_SIMPLE_TYPE(CXLHost, PXB_CXL_HOST)
typedef struct CXLUpstreamPort CXLUpstreamPort;
DECLARE_INSTANCE_CHECKER(CXLUpstreamPort, CXL_USP, TYPE_CXL_USP)
CXLComponentState *cxl_usp_to_cstate(CXLUpstreamPort *usp);
#define TYPE_CXL_DSP "cxl-downstream"
typedef struct CXLDownstreamPort CXLDownstreamPort;
DECLARE_INSTANCE_CHECKER(CXLDownstreamPort, CXL_DSP, TYPE_CXL_DSP)
#endif

View file

@ -26,7 +26,8 @@ enum reg_type {
CXL2_LOGICAL_DEVICE,
CXL2_ROOT_PORT,
CXL2_UPSTREAM_PORT,
CXL2_DOWNSTREAM_PORT
CXL2_DOWNSTREAM_PORT,
CXL3_SWITCH_MAILBOX_CCI,
};
/*
@ -175,7 +176,8 @@ HDM_DECODER_INIT(3);
(CXL_IDE_REGISTERS_OFFSET + CXL_IDE_REGISTERS_SIZE)
#define CXL_SNOOP_REGISTERS_SIZE 0x8
QEMU_BUILD_BUG_MSG((CXL_SNOOP_REGISTERS_OFFSET + CXL_SNOOP_REGISTERS_SIZE) >= 0x1000,
QEMU_BUILD_BUG_MSG((CXL_SNOOP_REGISTERS_OFFSET +
CXL_SNOOP_REGISTERS_SIZE) >= 0x1000,
"No space for registers");
typedef struct component_registers {

View file

@ -111,6 +111,20 @@ typedef enum {
CXL_MBOX_MAX = 0x17
} CXLRetCode;
typedef struct CXLCCI CXLCCI;
typedef struct cxl_device_state CXLDeviceState;
struct cxl_cmd;
typedef CXLRetCode (*opcode_handler)(const struct cxl_cmd *cmd,
uint8_t *payload_in, size_t len_in,
uint8_t *payload_out, size_t *len_out,
CXLCCI *cci);
struct cxl_cmd {
const char *name;
opcode_handler handler;
ssize_t in;
uint16_t effect; /* Reported in CEL */
};
typedef struct CXLEvent {
CXLEventRecordRaw data;
QSIMPLEQ_ENTRY(CXLEvent) node;
@ -127,6 +141,31 @@ typedef struct CXLEventLog {
QSIMPLEQ_HEAD(, CXLEvent) events;
} CXLEventLog;
typedef struct CXLCCI {
const struct cxl_cmd (*cxl_cmd_set)[256];
struct cel_log {
uint16_t opcode;
uint16_t effect;
} cel_log[1 << 16];
size_t cel_size;
/* background command handling (times in ms) */
struct {
uint16_t opcode;
uint16_t complete_pct;
uint16_t ret_code; /* Current value of retcode */
uint64_t starttime;
/* set by each bg cmd, cleared by the bg_timer when complete */
uint64_t runtime;
QEMUTimer *timer;
} bg;
size_t payload_max;
/* Pointer to device hosting the CCI */
DeviceState *d;
/* Pointer to the device hosting the protocol conversion */
DeviceState *intf;
} CXLCCI;
typedef struct cxl_device_state {
MemoryRegion device_registers;
@ -154,17 +193,13 @@ typedef struct cxl_device_state {
struct {
MemoryRegion mailbox;
uint16_t payload_size;
uint8_t mbox_msi_n;
union {
uint8_t mbox_reg_state[CXL_MAILBOX_REGISTERS_LENGTH];
uint16_t mbox_reg_state16[CXL_MAILBOX_REGISTERS_LENGTH / 2];
uint32_t mbox_reg_state32[CXL_MAILBOX_REGISTERS_LENGTH / 4];
uint64_t mbox_reg_state64[CXL_MAILBOX_REGISTERS_LENGTH / 8];
};
struct cel_log {
uint16_t opcode;
uint16_t effect;
} cel_log[1 << 16];
size_t cel_size;
};
struct {
@ -178,21 +213,26 @@ typedef struct cxl_device_state {
uint64_t pmem_size;
uint64_t vmem_size;
const struct cxl_cmd (*cxl_cmd_set)[256];
CXLEventLog event_logs[CXL_EVENT_TYPE_MAX];
} CXLDeviceState;
/* Initialize the register block for a device */
void cxl_device_register_block_init(Object *obj, CXLDeviceState *dev);
void cxl_device_register_block_init(Object *obj, CXLDeviceState *dev,
CXLCCI *cci);
typedef struct CXLType3Dev CXLType3Dev;
typedef struct CSWMBCCIDev CSWMBCCIDev;
/* Set up default values for the register block */
void cxl_device_register_init_common(CXLDeviceState *dev);
void cxl_device_register_init_t3(CXLType3Dev *ct3d);
void cxl_device_register_init_swcci(CSWMBCCIDev *sw);
/*
* CXL 2.0 - 8.2.8.1 including errata F4
* Documented as a 128 bit register, but 64 bit accesses and the second
* 64 bits are currently reserved.
*/
REG64(CXL_DEV_CAP_ARRAY, 0) /* Documented as 128 bit register but 64 byte accesses */
REG64(CXL_DEV_CAP_ARRAY, 0)
FIELD(CXL_DEV_CAP_ARRAY, CAP_ID, 0, 16)
FIELD(CXL_DEV_CAP_ARRAY, CAP_VERSION, 16, 8)
FIELD(CXL_DEV_CAP_ARRAY, CAP_COUNT, 32, 16)
@ -231,8 +271,20 @@ CXL_DEVICE_CAPABILITY_HEADER_REGISTER(MEMORY_DEVICE,
CXL_DEVICE_CAP_HDR1_OFFSET +
CXL_DEVICE_CAP_REG_SIZE * 2)
void cxl_initialize_mailbox(CXLDeviceState *cxl_dstate);
void cxl_process_mailbox(CXLDeviceState *cxl_dstate);
void cxl_initialize_mailbox_t3(CXLCCI *cci, DeviceState *d, size_t payload_max);
void cxl_initialize_mailbox_swcci(CXLCCI *cci, DeviceState *intf,
DeviceState *d, size_t payload_max);
void cxl_init_cci(CXLCCI *cci, size_t payload_max);
int cxl_process_cci_message(CXLCCI *cci, uint8_t set, uint8_t cmd,
size_t len_in, uint8_t *pl_in,
size_t *len_out, uint8_t *pl_out,
bool *bg_started);
void cxl_initialize_t3_fm_owned_ld_mctpcci(CXLCCI *cci, DeviceState *d,
DeviceState *intf,
size_t payload_max);
void cxl_initialize_t3_ld_cci(CXLCCI *cci, DeviceState *d,
DeviceState *intf, size_t payload_max);
#define cxl_device_cap_init(dstate, reg, cap_id, ver) \
do { \
@ -297,6 +349,23 @@ REG64(CXL_MEM_DEV_STS, 0)
FIELD(CXL_MEM_DEV_STS, MBOX_READY, 4, 1)
FIELD(CXL_MEM_DEV_STS, RESET_NEEDED, 5, 3)
static inline void __toggle_media(CXLDeviceState *cxl_dstate, int val)
{
uint64_t dev_status_reg;
dev_status_reg = FIELD_DP64(0, CXL_MEM_DEV_STS, MEDIA_STATUS, val);
cxl_dstate->mbox_reg_state64[R_CXL_MEM_DEV_STS] = dev_status_reg;
}
#define cxl_dev_disable_media(cxlds) \
do { __toggle_media((cxlds), 0x3); } while (0)
#define cxl_dev_enable_media(cxlds) \
do { __toggle_media((cxlds), 0x1); } while (0)
static inline bool sanitize_running(CXLCCI *cci)
{
return !!cci->bg.runtime && cci->bg.opcode == 0x4400;
}
typedef struct CXLError {
QTAILQ_ENTRY(CXLError) node;
int type; /* Error code as per FE definition */
@ -333,6 +402,10 @@ struct CXLType3Dev {
AddressSpace hostpmem_as;
CXLComponentState cxl_cstate;
CXLDeviceState cxl_dstate;
CXLCCI cci; /* Primary PCI mailbox CCI */
/* Always intialized as no way to know if a VDM might show up */
CXLCCI vdm_fm_owned_ld_mctp_cci;
CXLCCI ld0_cci;
/* DOE */
DOECap doe_cdat;
@ -361,9 +434,21 @@ struct CXLType3Class {
uint64_t offset);
void (*set_lsa)(CXLType3Dev *ct3d, const void *buf, uint64_t size,
uint64_t offset);
bool (*set_cacheline)(CXLType3Dev *ct3d, uint64_t dpa_offset, uint8_t *data);
bool (*set_cacheline)(CXLType3Dev *ct3d, uint64_t dpa_offset,
uint8_t *data);
};
struct CSWMBCCIDev {
PCIDevice parent_obj;
PCIDevice *target;
CXLComponentState cxl_cstate;
CXLDeviceState cxl_dstate;
CXLCCI *cci;
};
#define TYPE_CXL_SWITCH_MAILBOX_CCI "cxl-switch-mailbox-cci"
OBJECT_DECLARE_TYPE(CSWMBCCIDev, CSWMBCCIClass, CXL_SWITCH_MAILBOX_CCI)
MemTxResult cxl_type3_read(PCIDevice *d, hwaddr host_addr, uint64_t *data,
unsigned size, MemTxAttrs attrs);
MemTxResult cxl_type3_write(PCIDevice *d, hwaddr host_addr, uint64_t data,
@ -376,7 +461,7 @@ bool cxl_event_insert(CXLDeviceState *cxlds, CXLEventLogType log_type,
CXLEventRecordRaw *event);
CXLRetCode cxl_event_get_records(CXLDeviceState *cxlds, CXLGetEventPayload *pl,
uint8_t log_type, int max_recs,
uint16_t *len);
size_t *len);
CXLRetCode cxl_event_clear_records(CXLDeviceState *cxlds,
CXLClearEventPayload *pl);

View file

@ -92,7 +92,8 @@ typedef enum CXLEventIntMode {
CXL_INT_RES = 0x03,
} CXLEventIntMode;
#define CXL_EVENT_INT_MODE_MASK 0x3
#define CXL_EVENT_INT_SETTING(vector) ((((uint8_t)vector & 0xf) << 4) | CXL_INT_MSI_MSIX)
#define CXL_EVENT_INT_SETTING(vector) \
((((uint8_t)vector & 0xf) << 4) | CXL_INT_MSI_MSIX)
typedef struct CXLEventInterruptPolicy {
uint8_t info_settings;
uint8_t warn_settings;

View file

@ -86,7 +86,7 @@ typedef struct CXLDVSECDevice {
QEMU_BUILD_BUG_ON(sizeof(CXLDVSECDevice) != 0x38);
/* CXL 2.0 - 8.1.5 (ID 0003) */
typedef struct CXLDVSECPortExtensions {
typedef struct CXLDVSECPortExt {
DVSECHeader hdr;
uint16_t status;
uint16_t control;
@ -100,8 +100,8 @@ typedef struct CXLDVSECPortExtensions {
uint32_t alt_prefetch_limit_high;
uint32_t rcrb_base;
uint32_t rcrb_base_high;
} CXLDVSECPortExtensions;
QEMU_BUILD_BUG_ON(sizeof(CXLDVSECPortExtensions) != 0x28);
} CXLDVSECPortExt;
QEMU_BUILD_BUG_ON(sizeof(CXLDVSECPortExt) != 0x28);
#define PORT_CONTROL_OFFSET 0xc
#define PORT_CONTROL_UNMASK_SBR 1

View file

@ -0,0 +1,19 @@
#ifndef CXL_USP_H
#define CXL_USP_H
#include "hw/pci/pcie.h"
#include "hw/pci/pcie_port.h"
#include "hw/cxl/cxl.h"
typedef struct CXLUpstreamPort {
/*< private >*/
PCIEPort parent_obj;
/*< public >*/
CXLComponentState cxl_cstate;
CXLCCI swcci;
DOECap doe_cdat;
uint64_t sn;
} CXLUpstreamPort;
#endif /* CXL_SUP_H */

View file

@ -26,6 +26,18 @@ typedef enum VhostSetConfigType {
VHOST_SET_CONFIG_TYPE_MIGRATION = 1,
} VhostSetConfigType;
typedef enum VhostDeviceStateDirection {
/* Transfer state from back-end (device) to front-end */
VHOST_TRANSFER_STATE_DIRECTION_SAVE = 0,
/* Transfer state from front-end to back-end (device) */
VHOST_TRANSFER_STATE_DIRECTION_LOAD = 1,
} VhostDeviceStateDirection;
typedef enum VhostDeviceStatePhase {
/* The device (and all its vrings) is stopped */
VHOST_TRANSFER_STATE_PHASE_STOPPED = 0,
} VhostDeviceStatePhase;
struct vhost_inflight;
struct vhost_dev;
struct vhost_log;
@ -129,6 +141,15 @@ typedef int (*vhost_set_config_call_op)(struct vhost_dev *dev,
typedef void (*vhost_reset_status_op)(struct vhost_dev *dev);
typedef bool (*vhost_supports_device_state_op)(struct vhost_dev *dev);
typedef int (*vhost_set_device_state_fd_op)(struct vhost_dev *dev,
VhostDeviceStateDirection direction,
VhostDeviceStatePhase phase,
int fd,
int *reply_fd,
Error **errp);
typedef int (*vhost_check_device_state_op)(struct vhost_dev *dev, Error **errp);
typedef struct VhostOps {
VhostBackendType backend_type;
vhost_backend_init vhost_backend_init;
@ -176,6 +197,9 @@ typedef struct VhostOps {
vhost_force_iommu_op vhost_force_iommu;
vhost_set_config_call_op vhost_set_config_call;
vhost_reset_status_op vhost_reset_status;
vhost_supports_device_state_op vhost_supports_device_state;
vhost_set_device_state_fd_op vhost_set_device_state_fd;
vhost_check_device_state_op vhost_check_device_state;
} VhostOps;
int vhost_backend_update_device_iotlb(struct vhost_dev *dev,

View file

@ -31,6 +31,7 @@ enum VhostUserProtocolFeature {
VHOST_USER_PROTOCOL_F_STATUS = 16,
/* Feature 17 reserved for VHOST_USER_PROTOCOL_F_XEN_MMAP. */
VHOST_USER_PROTOCOL_F_SHARED_OBJECT = 18,
VHOST_USER_PROTOCOL_F_DEVICE_STATE = 19,
VHOST_USER_PROTOCOL_F_MAX
};

View file

@ -351,4 +351,117 @@ static inline int vhost_reset_device(struct vhost_dev *hdev)
}
#endif /* CONFIG_VHOST */
/**
* vhost_supports_device_state(): Checks whether the back-end supports
* transferring internal device state for the purpose of migration.
* Support for this feature is required for vhost_set_device_state_fd()
* and vhost_check_device_state().
*
* @dev: The vhost device
*
* Returns true if the device supports these commands, and false if it
* does not.
*/
bool vhost_supports_device_state(struct vhost_dev *dev);
/**
* vhost_set_device_state_fd(): Begin transfer of internal state from/to
* the back-end for the purpose of migration. Data is to be transferred
* over a pipe according to @direction and @phase. The sending end must
* only write to the pipe, and the receiving end must only read from it.
* Once the sending end is done, it closes its FD. The receiving end
* must take this as the end-of-transfer signal and close its FD, too.
*
* @fd is the back-end's end of the pipe: The write FD for SAVE, and the
* read FD for LOAD. This function transfers ownership of @fd to the
* back-end, i.e. closes it in the front-end.
*
* The back-end may optionally reply with an FD of its own, if this
* improves efficiency on its end. In this case, the returned FD is
* stored in *reply_fd. The back-end will discard the FD sent to it,
* and the front-end must use *reply_fd for transferring state to/from
* the back-end.
*
* @dev: The vhost device
* @direction: The direction in which the state is to be transferred.
* For outgoing migrations, this is SAVE, and data is read
* from the back-end and stored by the front-end in the
* migration stream.
* For incoming migrations, this is LOAD, and data is read
* by the front-end from the migration stream and sent to
* the back-end to restore the saved state.
* @phase: Which migration phase we are in. Currently, there is only
* STOPPED (device and all vrings are stopped), in the future,
* more phases such as PRE_COPY or POST_COPY may be added.
* @fd: Back-end's end of the pipe through which to transfer state; note
* that ownership is transferred to the back-end, so this function
* closes @fd in the front-end.
* @reply_fd: If the back-end wishes to use a different pipe for state
* transfer, this will contain an FD for the front-end to
* use. Otherwise, -1 is stored here.
* @errp: Potential error description
*
* Returns 0 on success, and -errno on failure.
*/
int vhost_set_device_state_fd(struct vhost_dev *dev,
VhostDeviceStateDirection direction,
VhostDeviceStatePhase phase,
int fd,
int *reply_fd,
Error **errp);
/**
* vhost_set_device_state_fd(): After transferring state from/to the
* back-end via vhost_set_device_state_fd(), i.e. once the sending end
* has closed the pipe, inquire the back-end to report any potential
* errors that have occurred on its side. This allows to sense errors
* like:
* - During outgoing migration, when the source side had already started
* to produce its state, something went wrong and it failed to finish
* - During incoming migration, when the received state is somehow
* invalid and cannot be processed by the back-end
*
* @dev: The vhost device
* @errp: Potential error description
*
* Returns 0 when the back-end reports successful state transfer and
* processing, and -errno when an error occurred somewhere.
*/
int vhost_check_device_state(struct vhost_dev *dev, Error **errp);
/**
* vhost_save_backend_state(): High-level function to receive a vhost
* back-end's state, and save it in @f. Uses
* `vhost_set_device_state_fd()` to get the data from the back-end, and
* stores it in consecutive chunks that are each prefixed by their
* respective length (be32). The end is marked by a 0-length chunk.
*
* Must only be called while the device and all its vrings are stopped
* (`VHOST_TRANSFER_STATE_PHASE_STOPPED`).
*
* @dev: The vhost device from which to save the state
* @f: Migration stream in which to save the state
* @errp: Potential error message
*
* Returns 0 on success, and -errno otherwise.
*/
int vhost_save_backend_state(struct vhost_dev *dev, QEMUFile *f, Error **errp);
/**
* vhost_load_backend_state(): High-level function to load a vhost
* back-end's state from @f, and send it over to the back-end. Reads
* the data from @f in the format used by `vhost_save_state()`, and uses
* `vhost_set_device_state_fd()` to transfer it to the back-end.
*
* Must only be called while the device and all its vrings are stopped
* (`VHOST_TRANSFER_STATE_PHASE_STOPPED`).
*
* @dev: The vhost device to which to send the sate
* @f: Migration stream from which to load the state
* @errp: Potential error message
*
* Returns 0 on success, and -errno otherwise.
*/
int vhost_load_backend_state(struct vhost_dev *dev, QEMUFile *f, Error **errp);
#endif

View file

@ -121,6 +121,8 @@ static const uint64_t vdpa_svq_device_features =
BIT_ULL(VIRTIO_NET_F_CTRL_MAC_ADDR) |
/* VHOST_F_LOG_ALL is exposed by SVQ */
BIT_ULL(VHOST_F_LOG_ALL) |
BIT_ULL(VIRTIO_NET_F_HASH_REPORT) |
BIT_ULL(VIRTIO_NET_F_RSS) |
BIT_ULL(VIRTIO_NET_F_RSC_EXT) |
BIT_ULL(VIRTIO_NET_F_STANDBY) |
BIT_ULL(VIRTIO_NET_F_SPEED_DUPLEX);
@ -240,6 +242,12 @@ static void vhost_vdpa_cleanup(NetClientState *nc)
}
}
/** Dummy SetSteeringEBPF to support RSS for vhost-vdpa backend */
static bool vhost_vdpa_set_steering_ebpf(NetClientState *nc, int prog_fd)
{
return true;
}
static bool vhost_vdpa_has_vnet_hdr(NetClientState *nc)
{
assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_VDPA);
@ -422,6 +430,7 @@ static NetClientInfo net_vhost_vdpa_info = {
.has_vnet_hdr = vhost_vdpa_has_vnet_hdr,
.has_ufo = vhost_vdpa_has_ufo,
.check_peer_type = vhost_vdpa_check_peer_type,
.set_steering_ebpf = vhost_vdpa_set_steering_ebpf,
};
static int64_t vhost_vdpa_get_vring_group(int device_fd, unsigned vq_index,
@ -818,6 +827,103 @@ static int vhost_vdpa_net_load_mac(VhostVDPAState *s, const VirtIONet *n,
return 0;
}
static int vhost_vdpa_net_load_rss(VhostVDPAState *s, const VirtIONet *n,
struct iovec *out_cursor,
struct iovec *in_cursor, bool do_rss)
{
struct virtio_net_rss_config cfg = {};
ssize_t r;
g_autofree uint16_t *table = NULL;
/*
* According to VirtIO standard, "Initially the device has all hash
* types disabled and reports only VIRTIO_NET_HASH_REPORT_NONE.".
*
* Therefore, there is no need to send this CVQ command if the
* driver disables the all hash types, which aligns with
* the device's defaults.
*
* Note that the device's defaults can mismatch the driver's
* configuration only at live migration.
*/
if (!n->rss_data.enabled ||
n->rss_data.hash_types == VIRTIO_NET_HASH_REPORT_NONE) {
return 0;
}
table = g_malloc_n(n->rss_data.indirections_len,
sizeof(n->rss_data.indirections_table[0]));
cfg.hash_types = cpu_to_le32(n->rss_data.hash_types);
if (do_rss) {
/*
* According to VirtIO standard, "Number of entries in indirection_table
* is (indirection_table_mask + 1)".
*/
cfg.indirection_table_mask = cpu_to_le16(n->rss_data.indirections_len -
1);
cfg.unclassified_queue = cpu_to_le16(n->rss_data.default_queue);
for (int i = 0; i < n->rss_data.indirections_len; ++i) {
table[i] = cpu_to_le16(n->rss_data.indirections_table[i]);
}
cfg.max_tx_vq = cpu_to_le16(n->curr_queue_pairs);
} else {
/*
* According to VirtIO standard, "Field reserved MUST contain zeroes.
* It is defined to make the structure to match the layout of
* virtio_net_rss_config structure, defined in 5.1.6.5.7.".
*
* Therefore, we need to zero the fields in
* struct virtio_net_rss_config, which corresponds to the
* `reserved` field in struct virtio_net_hash_config.
*
* Note that all other fields are zeroed at their definitions,
* except for the `indirection_table` field, where the actual data
* is stored in the `table` variable to ensure compatibility
* with RSS case. Therefore, we need to zero the `table` variable here.
*/
table[0] = 0;
}
/*
* Considering that virtio_net_handle_rss() currently does not restore
* the hash key length parsed from the CVQ command sent from the guest
* into n->rss_data and uses the maximum key length in other code, so
* we also employ the maximum key length here.
*/
cfg.hash_key_length = sizeof(n->rss_data.key);
const struct iovec data[] = {
{
.iov_base = &cfg,
.iov_len = offsetof(struct virtio_net_rss_config,
indirection_table),
}, {
.iov_base = table,
.iov_len = n->rss_data.indirections_len *
sizeof(n->rss_data.indirections_table[0]),
}, {
.iov_base = &cfg.max_tx_vq,
.iov_len = offsetof(struct virtio_net_rss_config, hash_key_data) -
offsetof(struct virtio_net_rss_config, max_tx_vq),
}, {
.iov_base = (void *)n->rss_data.key,
.iov_len = sizeof(n->rss_data.key),
}
};
r = vhost_vdpa_net_load_cmd(s, out_cursor, in_cursor,
VIRTIO_NET_CTRL_MQ,
do_rss ? VIRTIO_NET_CTRL_MQ_RSS_CONFIG :
VIRTIO_NET_CTRL_MQ_HASH_CONFIG,
data, ARRAY_SIZE(data));
if (unlikely(r < 0)) {
return r;
}
return 0;
}
static int vhost_vdpa_net_load_mq(VhostVDPAState *s,
const VirtIONet *n,
struct iovec *out_cursor,
@ -843,6 +949,21 @@ static int vhost_vdpa_net_load_mq(VhostVDPAState *s,
return r;
}
if (virtio_vdev_has_feature(&n->parent_obj, VIRTIO_NET_F_RSS)) {
/* load the receive-side scaling state */
r = vhost_vdpa_net_load_rss(s, n, out_cursor, in_cursor, true);
if (unlikely(r < 0)) {
return r;
}
} else if (virtio_vdev_has_feature(&n->parent_obj,
VIRTIO_NET_F_HASH_REPORT)) {
/* load the hash calculation state */
r = vhost_vdpa_net_load_rss(s, n, out_cursor, in_cursor, false);
if (unlikely(r < 0)) {
return r;
}
}
return 0;
}
@ -1166,6 +1287,7 @@ static NetClientInfo net_vhost_vdpa_cvq_info = {
.has_vnet_hdr = vhost_vdpa_has_vnet_hdr,
.has_ufo = vhost_vdpa_has_ufo,
.check_peer_type = vhost_vdpa_check_peer_type,
.set_steering_ebpf = vhost_vdpa_set_steering_ebpf,
};
/*

View file

@ -111,6 +111,8 @@ static const QDevAlias qdev_alias_table[] = {
{ "virtio-serial-device", "virtio-serial", QEMU_ARCH_VIRTIO_MMIO },
{ "virtio-serial-ccw", "virtio-serial", QEMU_ARCH_VIRTIO_CCW },
{ "virtio-serial-pci", "virtio-serial", QEMU_ARCH_VIRTIO_PCI},
{ "virtio-sound-device", "virtio-sound", QEMU_ARCH_VIRTIO_MMIO },
{ "virtio-sound-pci", "virtio-sound", QEMU_ARCH_VIRTIO_PCI },
{ "virtio-tablet-device", "virtio-tablet", QEMU_ARCH_VIRTIO_MMIO },
{ "virtio-tablet-ccw", "virtio-tablet", QEMU_ARCH_VIRTIO_CCW },
{ "virtio-tablet-pci", "virtio-tablet", QEMU_ARCH_VIRTIO_PCI },

View file

@ -18,7 +18,7 @@
#
#
# Author:
# Ani Sinha <ani@anisinha.ca>
# Ani Sinha <anisinha@redhat.com>
# pylint: disable=invalid-name
# pylint: disable=consider-using-f-string
@ -48,6 +48,7 @@
)
from qemu.machine import QEMUMachine
from avocado import skipIf
from avocado.utils import datadrainer as drainer
from avocado_qemu import QemuBaseTest
deps = ["xorriso", "mformat"] # dependent tools needed in the test setup/box.
@ -141,12 +142,12 @@ def __init__(self, *args, **kwargs):
self._baseDir = None
# following are some standard configuration constants
self._bitsInternalVer = 2020
self._bitsCommitHash = 'b48b88ff' # commit hash must match
self._bitsInternalVer = 2020 # gitlab CI does shallow clones of depth 20
self._bitsCommitHash = 'c7920d2b' # commit hash must match
# the artifact tag below
self._bitsTag = "qemu-bits-10182022" # this is the latest bits
self._bitsTag = "qemu-bits-10262023" # this is the latest bits
# release as of today.
self._bitsArtSHA1Hash = 'b04790ac9b99b5662d0416392c73b97580641fe5'
self._bitsArtSHA1Hash = 'b22cdfcfc7453875297d06d626f5474ee36a343f'
self._bitsArtURL = ("https://gitlab.com/qemu-project/"
"biosbits-bits/-/jobs/artifacts/%s/"
"download?job=qemu-bits-build" %self._bitsTag)
@ -380,16 +381,26 @@ def test_acpi_smbios_bits(self):
# consistent in terms of timing. smilatency tests have consistent
# timing requirements.
self._vm.add_args('-icount', 'auto')
# currently there is no support in bits for recognizing 64-bit SMBIOS
# entry points. QEMU defaults to 64-bit entry points since the
# upstream commit bf376f3020 ("hw/i386/pc: Default to use SMBIOS 3.0
# for newer machine models"). Therefore, enforce 32-bit entry point.
self._vm.add_args('-machine', 'smbios-entry-point-type=32')
args = " ".join(str(arg) for arg in self._vm.base_args()) + \
" " + " ".join(str(arg) for arg in self._vm.args)
self.logger.info("launching QEMU vm with the following arguments: %s",
args)
# enable console logging
self._vm.set_console()
self._vm.launch()
self.logger.debug("Console output from bits VM follows ...")
c_drainer = drainer.LineLogger(self._vm.console_socket.fileno(),
logger=self.logger.getChild("console"),
stop_check=(lambda :
not self._vm.is_running()))
c_drainer.start()
# biosbits has been configured to run all the specified test suites
# in batch mode and then automatically initiate a vm shutdown.
# Rely on avocado's unit test timeout.
self._vm.event_wait('SHUTDOWN')
self._vm.wait(timeout=None)
self.parse_log()

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -95,8 +95,11 @@ typedef struct {
uint16_t smbios_cpu_curr_speed;
uint8_t smbios_core_count;
uint16_t smbios_core_count2;
uint8_t smbios_thread_count;
uint16_t smbios_thread_count2;
uint8_t *required_struct_types;
int required_struct_types_len;
int type4_count;
QTestState *qts;
} test_data;
@ -639,8 +642,10 @@ static void smbios_cpu_test(test_data *data, uint32_t addr,
SmbiosEntryPointType ep_type)
{
uint8_t core_count, expected_core_count = data->smbios_core_count;
uint8_t thread_count, expected_thread_count = data->smbios_thread_count;
uint16_t speed, expected_speed[2];
uint16_t core_count2, expected_core_count2 = data->smbios_core_count2;
uint16_t thread_count2, expected_thread_count2 = data->smbios_thread_count2;
int offset[2];
int i;
@ -662,6 +667,13 @@ static void smbios_cpu_test(test_data *data, uint32_t addr,
g_assert_cmpuint(core_count, ==, expected_core_count);
}
thread_count = qtest_readb(data->qts,
addr + offsetof(struct smbios_type_4, thread_count));
if (expected_thread_count) {
g_assert_cmpuint(thread_count, ==, expected_thread_count);
}
if (ep_type == SMBIOS_ENTRY_POINT_TYPE_64) {
core_count2 = qtest_readw(data->qts,
addr + offsetof(struct smbios_type_4, core_count2));
@ -670,6 +682,24 @@ static void smbios_cpu_test(test_data *data, uint32_t addr,
if (expected_core_count == 0xFF && expected_core_count2) {
g_assert_cmpuint(core_count2, ==, expected_core_count2);
}
thread_count2 = qtest_readw(data->qts,
addr + offsetof(struct smbios_type_4,
thread_count2));
/* Thread Count has reached its limit, checking Thread Count 2 */
if (expected_thread_count == 0xFF && expected_thread_count2) {
g_assert_cmpuint(thread_count2, ==, expected_thread_count2);
}
}
}
static void smbios_type4_count_test(test_data *data, int type4_count)
{
int expected_type4_count = data->type4_count;
if (expected_type4_count) {
g_assert_cmpuint(type4_count, ==, expected_type4_count);
}
}
@ -678,7 +708,7 @@ static void test_smbios_structs(test_data *data, SmbiosEntryPointType ep_type)
DECLARE_BITMAP(struct_bitmap, SMBIOS_MAX_TYPE+1) = { 0 };
SmbiosEntryPoint *ep_table = &data->smbios_ep_table;
int i = 0, len, max_len = 0;
int i = 0, len, max_len = 0, type4_count = 0;
uint8_t type, prv, crt;
uint64_t addr;
@ -704,6 +734,7 @@ static void test_smbios_structs(test_data *data, SmbiosEntryPointType ep_type)
if (type == 4) {
smbios_cpu_test(data, addr, ep_type);
type4_count++;
}
/* seek to end of unformatted string area of this struct ("\0\0") */
@ -747,6 +778,8 @@ static void test_smbios_structs(test_data *data, SmbiosEntryPointType ep_type)
for (i = 0; i < data->required_struct_types_len; i++) {
g_assert(test_bit(data->required_struct_types[i], struct_bitmap));
}
smbios_type4_count_test(data, type4_count);
}
static void test_acpi_load_tables(test_data *data)
@ -970,6 +1003,39 @@ static void test_acpi_q35_tcg(void)
free_test_data(&data);
}
static void test_acpi_q35_tcg_type4_count(void)
{
test_data data = {
.machine = MACHINE_Q35,
.variant = ".type4-count",
.required_struct_types = base_required_struct_types,
.required_struct_types_len = ARRAY_SIZE(base_required_struct_types),
.type4_count = 5,
};
test_acpi_one("-machine smbios-entry-point-type=64 "
"-smp cpus=100,maxcpus=120,sockets=5,"
"dies=2,cores=4,threads=3", &data);
free_test_data(&data);
}
static void test_acpi_q35_tcg_core_count(void)
{
test_data data = {
.machine = MACHINE_Q35,
.variant = ".core-count",
.required_struct_types = base_required_struct_types,
.required_struct_types_len = ARRAY_SIZE(base_required_struct_types),
.smbios_core_count = 9,
.smbios_core_count2 = 9,
};
test_acpi_one("-machine smbios-entry-point-type=64 "
"-smp 54,sockets=2,dies=3,cores=3,threads=3",
&data);
free_test_data(&data);
}
static void test_acpi_q35_tcg_core_count2(void)
{
test_data data = {
@ -978,10 +1044,46 @@ static void test_acpi_q35_tcg_core_count2(void)
.required_struct_types = base_required_struct_types,
.required_struct_types_len = ARRAY_SIZE(base_required_struct_types),
.smbios_core_count = 0xFF,
.smbios_core_count2 = 275,
.smbios_core_count2 = 260,
};
test_acpi_one("-machine smbios-entry-point-type=64 -smp 275", &data);
test_acpi_one("-machine smbios-entry-point-type=64 "
"-smp 260,dies=2,cores=130,threads=1",
&data);
free_test_data(&data);
}
static void test_acpi_q35_tcg_thread_count(void)
{
test_data data = {
.machine = MACHINE_Q35,
.variant = ".thread-count",
.required_struct_types = base_required_struct_types,
.required_struct_types_len = ARRAY_SIZE(base_required_struct_types),
.smbios_thread_count = 27,
.smbios_thread_count2 = 27,
};
test_acpi_one("-machine smbios-entry-point-type=64 "
"-smp cpus=15,maxcpus=54,sockets=2,dies=3,cores=3,threads=3",
&data);
free_test_data(&data);
}
static void test_acpi_q35_tcg_thread_count2(void)
{
test_data data = {
.machine = MACHINE_Q35,
.variant = ".thread-count2",
.required_struct_types = base_required_struct_types,
.required_struct_types_len = ARRAY_SIZE(base_required_struct_types),
.smbios_thread_count = 0xFF,
.smbios_thread_count2 = 260,
};
test_acpi_one("-machine smbios-entry-point-type=64 "
"-smp cpus=210,maxcpus=260,dies=2,cores=65,threads=2",
&data);
free_test_data(&data);
}
@ -2147,8 +2249,16 @@ int main(int argc, char *argv[])
if (has_kvm) {
qtest_add_func("acpi/q35/kvm/xapic", test_acpi_q35_kvm_xapic);
qtest_add_func("acpi/q35/kvm/dmar", test_acpi_q35_kvm_dmar);
qtest_add_func("acpi/q35/type4-count",
test_acpi_q35_tcg_type4_count);
qtest_add_func("acpi/q35/core-count",
test_acpi_q35_tcg_core_count);
qtest_add_func("acpi/q35/core-count2",
test_acpi_q35_tcg_core_count2);
qtest_add_func("acpi/q35/thread-count",
test_acpi_q35_tcg_thread_count);
qtest_add_func("acpi/q35/thread-count2",
test_acpi_q35_tcg_thread_count2);
}
if (qtest_has_device("virtio-iommu-pci")) {
qtest_add_func("acpi/q35/viot", test_acpi_q35_viot);

View file

@ -394,20 +394,47 @@ static char *smp_config_to_string(const SMPConfiguration *config)
config->has_maxcpus ? "true" : "false", config->maxcpus);
}
static char *cpu_topology_to_string(const CpuTopology *topo)
/* Use the different calculation than machine_topo_get_threads_per_socket(). */
static unsigned int cpu_topology_get_threads_per_socket(const CpuTopology *topo)
{
/* Check the divisor to avoid invalid topology examples causing SIGFPE. */
if (!topo->sockets) {
return 0;
} else {
return topo->max_cpus / topo->sockets;
}
}
/* Use the different calculation than machine_topo_get_cores_per_socket(). */
static unsigned int cpu_topology_get_cores_per_socket(const CpuTopology *topo)
{
/* Check the divisor to avoid invalid topology examples causing SIGFPE. */
if (!topo->threads) {
return 0;
} else {
return cpu_topology_get_threads_per_socket(topo) / topo->threads;
}
}
static char *cpu_topology_to_string(const CpuTopology *topo,
unsigned int threads_per_socket,
unsigned int cores_per_socket)
{
return g_strdup_printf(
"(CpuTopology) {\n"
" .cpus = %u,\n"
" .sockets = %u,\n"
" .dies = %u,\n"
" .clusters = %u,\n"
" .cores = %u,\n"
" .threads = %u,\n"
" .max_cpus = %u,\n"
" .cpus = %u,\n"
" .sockets = %u,\n"
" .dies = %u,\n"
" .clusters = %u,\n"
" .cores = %u,\n"
" .threads = %u,\n"
" .max_cpus = %u,\n"
" .threads_per_socket = %u,\n"
" .cores_per_socket = %u,\n"
"}",
topo->cpus, topo->sockets, topo->dies, topo->clusters,
topo->cores, topo->threads, topo->max_cpus);
topo->cores, topo->threads, topo->max_cpus,
threads_per_socket, cores_per_socket);
}
static void check_parse(MachineState *ms, const SMPConfiguration *config,
@ -415,14 +442,26 @@ static void check_parse(MachineState *ms, const SMPConfiguration *config,
bool is_valid)
{
g_autofree char *config_str = smp_config_to_string(config);
g_autofree char *expect_topo_str = cpu_topology_to_string(expect_topo);
g_autofree char *output_topo_str = NULL;
g_autofree char *expect_topo_str = NULL, *output_topo_str = NULL;
unsigned int expect_threads_per_socket, expect_cores_per_socket;
unsigned int ms_threads_per_socket, ms_cores_per_socket;
Error *err = NULL;
expect_threads_per_socket =
cpu_topology_get_threads_per_socket(expect_topo);
expect_cores_per_socket =
cpu_topology_get_cores_per_socket(expect_topo);
expect_topo_str = cpu_topology_to_string(expect_topo,
expect_threads_per_socket,
expect_cores_per_socket);
/* call the generic parser */
machine_parse_smp_config(ms, config, &err);
output_topo_str = cpu_topology_to_string(&ms->smp);
ms_threads_per_socket = machine_topo_get_threads_per_socket(ms);
ms_cores_per_socket = machine_topo_get_cores_per_socket(ms);
output_topo_str = cpu_topology_to_string(&ms->smp, ms_threads_per_socket,
ms_cores_per_socket);
/* when the configuration is supposed to be valid */
if (is_valid) {
@ -433,7 +472,9 @@ static void check_parse(MachineState *ms, const SMPConfiguration *config,
(ms->smp.clusters == expect_topo->clusters) &&
(ms->smp.cores == expect_topo->cores) &&
(ms->smp.threads == expect_topo->threads) &&
(ms->smp.max_cpus == expect_topo->max_cpus)) {
(ms->smp.max_cpus == expect_topo->max_cpus) &&
(ms_threads_per_socket == expect_threads_per_socket) &&
(ms_cores_per_socket == expect_cores_per_socket)) {
return;
}