mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
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:
@@ -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>
|
||||
|
||||
+281
-20
@@ -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 0–15: 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 16–31: 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 0–14: 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 16–30: 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 device’s vrings are stopped, it is
|
||||
*suspended*, see :ref:`Suspended device state
|
||||
<suspended_device_state>`.
|
||||
|
||||
The request payload’s *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-end’s internal state during migration. Either side (front-end or
|
||||
back-end) may create the channel. The nature of this channel is not
|
||||
restricted or defined in this document, but whichever side creates it
|
||||
must create a file descriptor that is provided to the respectively
|
||||
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 0–7
|
||||
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-end’s internal state during migration (see
|
||||
the :ref:`Migrating back-end state <migrating_backend_state>`
|
||||
section), check whether the back-end was able to successfully fully
|
||||
process the state.
|
||||
|
||||
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
|
||||
----------------------
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -50,3 +50,8 @@ config CS4231
|
||||
|
||||
config ASC
|
||||
bool
|
||||
|
||||
config VIRTIO_SND
|
||||
bool
|
||||
default y
|
||||
depends on VIRTIO
|
||||
|
||||
@@ -13,3 +13,5 @@ system_ss.add(when: 'CONFIG_PL041', if_true: files('pl041.c', 'lm4549.c'))
|
||||
system_ss.add(when: 'CONFIG_SB16', if_true: files('sb16.c'))
|
||||
system_ss.add(when: 'CONFIG_VT82C686', if_true: files('via-ac97.c'))
|
||||
system_ss.add(when: 'CONFIG_WM8750', if_true: files('wm8750.c'))
|
||||
system_ss.add(when: ['CONFIG_VIRTIO_SND', 'CONFIG_VIRTIO'], if_true: files('virtio-snd.c'))
|
||||
system_ss.add(when: ['CONFIG_VIRTIO_SND', 'CONFIG_VIRTIO', 'CONFIG_VIRTIO_PCI'], if_true: files('virtio-snd-pci.c'))
|
||||
|
||||
@@ -38,3 +38,23 @@ asc_write_fifo(const char fifo, int reg, unsigned size, int wrptr, int cnt, uint
|
||||
asc_write_reg(int reg, unsigned size, uint64_t value) "reg=0x%03x size=%u value=0x%"PRIx64
|
||||
asc_write_extreg(const char fifo, int reg, unsigned size, uint64_t value) "fifo %c reg=0x%03x size=%u value=0x%"PRIx64
|
||||
asc_update_irq(int irq, int a, int b) "set IRQ to %d (A: 0x%x B: 0x%x)"
|
||||
|
||||
#virtio-snd.c
|
||||
virtio_snd_get_config(void *vdev, uint32_t jacks, uint32_t streams, uint32_t chmaps) "snd %p: get_config jacks=%"PRIu32" streams=%"PRIu32" chmaps=%"PRIu32""
|
||||
virtio_snd_set_config(void *vdev, uint32_t jacks, uint32_t new_jacks, uint32_t streams, uint32_t new_streams, uint32_t chmaps, uint32_t new_chmaps) "snd %p: set_config jacks from %"PRIu32"->%"PRIu32", streams from %"PRIu32"->%"PRIu32", chmaps from %"PRIu32"->%"PRIu32
|
||||
virtio_snd_get_features(void *vdev, uint64_t features) "snd %p: get_features 0x%"PRIx64
|
||||
virtio_snd_vm_state_running(void) "vm state running"
|
||||
virtio_snd_vm_state_stopped(void) "vm state stopped"
|
||||
virtio_snd_realize(void *snd) "snd %p: realize"
|
||||
virtio_snd_unrealize(void *snd) "snd %p: unrealize"
|
||||
virtio_snd_handle_pcm_set_params(uint32_t stream) "VIRTIO_SND_PCM_SET_PARAMS called for stream %"PRIu32
|
||||
virtio_snd_handle_ctrl(void *vdev, void *vq) "snd %p: handle ctrl event for queue %p"
|
||||
virtio_snd_handle_pcm_info(uint32_t stream) "VIRTIO_SND_R_PCM_INFO called for stream %"PRIu32
|
||||
virtio_snd_handle_pcm_start_stop(const char *code, uint32_t stream) "%s called for stream %"PRIu32
|
||||
virtio_snd_handle_pcm_release(uint32_t stream) "VIRTIO_SND_PCM_RELEASE called for stream %"PRIu32
|
||||
virtio_snd_handle_code(uint32_t val, const char *code) "ctrl code msg val = %"PRIu32" == %s"
|
||||
virtio_snd_handle_chmap_info(void) "VIRTIO_SND_CHMAP_INFO called"
|
||||
virtio_snd_handle_event(void) "event queue callback called"
|
||||
virtio_snd_pcm_stream_flush(uint32_t stream) "flushing stream %"PRIu32
|
||||
virtio_snd_handle_tx_xfer(void) "tx queue callback called"
|
||||
virtio_snd_handle_rx_xfer(void) "rx queue callback called"
|
||||
|
||||
@@ -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);
|
||||
File diff suppressed because it is too large
Load Diff
+2
-1
@@ -60,7 +60,8 @@ static void ct3_build_cdat(CDATObject *cdat, Error **errp)
|
||||
return;
|
||||
}
|
||||
|
||||
cdat->built_buf_len = cdat->build_cdat_table(&cdat->built_buf, cdat->private);
|
||||
cdat->built_buf_len = cdat->build_cdat_table(&cdat->built_buf,
|
||||
cdat->private);
|
||||
|
||||
if (!cdat->built_buf_len) {
|
||||
/* Build later as not all data available yet */
|
||||
|
||||
@@ -67,16 +67,24 @@ static uint64_t cxl_cache_mem_read_reg(void *opaque, hwaddr offset,
|
||||
CXLComponentState *cxl_cstate = opaque;
|
||||
ComponentRegisters *cregs = &cxl_cstate->crb;
|
||||
|
||||
if (size == 8) {
|
||||
switch (size) {
|
||||
case 4:
|
||||
if (cregs->special_ops && cregs->special_ops->read) {
|
||||
return cregs->special_ops->read(cxl_cstate, offset, 4);
|
||||
} else {
|
||||
QEMU_BUILD_BUG_ON(sizeof(*cregs->cache_mem_registers) != 4);
|
||||
return cregs->cache_mem_registers[offset / 4];
|
||||
}
|
||||
case 8:
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"CXL 8 byte cache mem registers not implemented\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (cregs->special_ops && cregs->special_ops->read) {
|
||||
return cregs->special_ops->read(cxl_cstate, offset, size);
|
||||
} else {
|
||||
return cregs->cache_mem_registers[offset / sizeof(*cregs->cache_mem_registers)];
|
||||
default:
|
||||
/*
|
||||
* In line with specifiction limitaions on access sizes, this
|
||||
* routine is not called with other sizes.
|
||||
*/
|
||||
g_assert_not_reached();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,25 +125,37 @@ static void cxl_cache_mem_write_reg(void *opaque, hwaddr offset, uint64_t value,
|
||||
ComponentRegisters *cregs = &cxl_cstate->crb;
|
||||
uint32_t mask;
|
||||
|
||||
if (size == 8) {
|
||||
switch (size) {
|
||||
case 4: {
|
||||
QEMU_BUILD_BUG_ON(sizeof(*cregs->cache_mem_regs_write_mask) != 4);
|
||||
QEMU_BUILD_BUG_ON(sizeof(*cregs->cache_mem_registers) != 4);
|
||||
mask = cregs->cache_mem_regs_write_mask[offset / 4];
|
||||
value &= mask;
|
||||
/* RO bits should remain constant. Done by reading existing value */
|
||||
value |= ~mask & cregs->cache_mem_registers[offset / 4];
|
||||
if (cregs->special_ops && cregs->special_ops->write) {
|
||||
cregs->special_ops->write(cxl_cstate, offset, value, size);
|
||||
return;
|
||||
}
|
||||
|
||||
if (offset >= A_CXL_HDM_DECODER_CAPABILITY &&
|
||||
offset <= A_CXL_HDM_DECODER3_TARGET_LIST_HI) {
|
||||
dumb_hdm_handler(cxl_cstate, offset, value);
|
||||
} else {
|
||||
cregs->cache_mem_registers[offset / 4] = value;
|
||||
}
|
||||
return;
|
||||
}
|
||||
case 8:
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"CXL 8 byte cache mem registers not implemented\n");
|
||||
return;
|
||||
}
|
||||
mask = cregs->cache_mem_regs_write_mask[offset / sizeof(*cregs->cache_mem_regs_write_mask)];
|
||||
value &= mask;
|
||||
/* RO bits should remain constant. Done by reading existing value */
|
||||
value |= ~mask & cregs->cache_mem_registers[offset / sizeof(*cregs->cache_mem_registers)];
|
||||
if (cregs->special_ops && cregs->special_ops->write) {
|
||||
cregs->special_ops->write(cxl_cstate, offset, value, size);
|
||||
return;
|
||||
}
|
||||
|
||||
if (offset >= A_CXL_HDM_DECODER_CAPABILITY &&
|
||||
offset <= A_CXL_HDM_DECODER3_TARGET_LIST_HI) {
|
||||
dumb_hdm_handler(cxl_cstate, offset, value);
|
||||
} else {
|
||||
cregs->cache_mem_registers[offset / sizeof(*cregs->cache_mem_registers)] = value;
|
||||
default:
|
||||
/*
|
||||
* In line with specifiction limitaions on access sizes, this
|
||||
* routine is not called with other sizes.
|
||||
*/
|
||||
g_assert_not_reached();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -221,7 +241,8 @@ static void hdm_init_common(uint32_t *reg_state, uint32_t *write_msk,
|
||||
ARRAY_FIELD_DP32(reg_state, CXL_HDM_DECODER_CAPABILITY, TARGET_COUNT, 1);
|
||||
ARRAY_FIELD_DP32(reg_state, CXL_HDM_DECODER_CAPABILITY, INTERLEAVE_256B, 1);
|
||||
ARRAY_FIELD_DP32(reg_state, CXL_HDM_DECODER_CAPABILITY, INTERLEAVE_4K, 1);
|
||||
ARRAY_FIELD_DP32(reg_state, CXL_HDM_DECODER_CAPABILITY, POISON_ON_ERR_CAP, 0);
|
||||
ARRAY_FIELD_DP32(reg_state, CXL_HDM_DECODER_CAPABILITY,
|
||||
POISON_ON_ERR_CAP, 0);
|
||||
ARRAY_FIELD_DP32(reg_state, CXL_HDM_DECODER_GLOBAL_CONTROL,
|
||||
HDM_DECODER_ENABLE, 0);
|
||||
write_msk[R_CXL_HDM_DECODER_GLOBAL_CONTROL] = 0x3;
|
||||
@@ -244,15 +265,16 @@ static void hdm_init_common(uint32_t *reg_state, uint32_t *write_msk,
|
||||
}
|
||||
}
|
||||
|
||||
void cxl_component_register_init_common(uint32_t *reg_state, uint32_t *write_msk,
|
||||
void cxl_component_register_init_common(uint32_t *reg_state,
|
||||
uint32_t *write_msk,
|
||||
enum reg_type type)
|
||||
{
|
||||
int caps = 0;
|
||||
|
||||
/*
|
||||
* In CXL 2.0 the capabilities required for each CXL component are such that,
|
||||
* with the ordering chosen here, a single number can be used to define
|
||||
* which capabilities should be provided.
|
||||
* In CXL 2.0 the capabilities required for each CXL component are such
|
||||
* that, with the ordering chosen here, a single number can be used to
|
||||
* define which capabilities should be provided.
|
||||
*/
|
||||
switch (type) {
|
||||
case CXL2_DOWNSTREAM_PORT:
|
||||
@@ -283,7 +305,6 @@ void cxl_component_register_init_common(uint32_t *reg_state, uint32_t *write_msk
|
||||
ARRAY_FIELD_DP32(reg_state, CXL_CAPABILITY_HEADER, ARRAY_SIZE, caps);
|
||||
|
||||
#define init_cap_reg(reg, id, version) \
|
||||
QEMU_BUILD_BUG_ON(CXL_##reg##_REGISTERS_OFFSET == 0); \
|
||||
do { \
|
||||
int which = R_CXL_##reg##_CAPABILITY_HEADER; \
|
||||
reg_state[which] = FIELD_DP32(reg_state[which], \
|
||||
@@ -373,26 +394,35 @@ void cxl_component_create_dvsec(CXLComponentState *cxl,
|
||||
case NON_CXL_FUNCTION_MAP_DVSEC:
|
||||
break; /* Not yet implemented */
|
||||
case EXTENSIONS_PORT_DVSEC:
|
||||
wmask[offset + offsetof(CXLDVSECPortExtensions, control)] = 0x0F;
|
||||
wmask[offset + offsetof(CXLDVSECPortExtensions, control) + 1] = 0x40;
|
||||
wmask[offset + offsetof(CXLDVSECPortExtensions, alt_bus_base)] = 0xFF;
|
||||
wmask[offset + offsetof(CXLDVSECPortExtensions, alt_bus_limit)] = 0xFF;
|
||||
wmask[offset + offsetof(CXLDVSECPortExtensions, alt_memory_base)] = 0xF0;
|
||||
wmask[offset + offsetof(CXLDVSECPortExtensions, alt_memory_base) + 1] = 0xFF;
|
||||
wmask[offset + offsetof(CXLDVSECPortExtensions, alt_memory_limit)] = 0xF0;
|
||||
wmask[offset + offsetof(CXLDVSECPortExtensions, alt_memory_limit) + 1] = 0xFF;
|
||||
wmask[offset + offsetof(CXLDVSECPortExtensions, alt_prefetch_base)] = 0xF0;
|
||||
wmask[offset + offsetof(CXLDVSECPortExtensions, alt_prefetch_base) + 1] = 0xFF;
|
||||
wmask[offset + offsetof(CXLDVSECPortExtensions, alt_prefetch_limit)] = 0xF0;
|
||||
wmask[offset + offsetof(CXLDVSECPortExtensions, alt_prefetch_limit) + 1] = 0xFF;
|
||||
wmask[offset + offsetof(CXLDVSECPortExtensions, alt_prefetch_base_high)] = 0xFF;
|
||||
wmask[offset + offsetof(CXLDVSECPortExtensions, alt_prefetch_base_high) + 1] = 0xFF;
|
||||
wmask[offset + offsetof(CXLDVSECPortExtensions, alt_prefetch_base_high) + 2] = 0xFF;
|
||||
wmask[offset + offsetof(CXLDVSECPortExtensions, alt_prefetch_base_high) + 3] = 0xFF;
|
||||
wmask[offset + offsetof(CXLDVSECPortExtensions, alt_prefetch_limit_high)] = 0xFF;
|
||||
wmask[offset + offsetof(CXLDVSECPortExtensions, alt_prefetch_limit_high) + 1] = 0xFF;
|
||||
wmask[offset + offsetof(CXLDVSECPortExtensions, alt_prefetch_limit_high) + 2] = 0xFF;
|
||||
wmask[offset + offsetof(CXLDVSECPortExtensions, alt_prefetch_limit_high) + 3] = 0xFF;
|
||||
wmask[offset + offsetof(CXLDVSECPortExt, control)] = 0x0F;
|
||||
wmask[offset + offsetof(CXLDVSECPortExt, control) + 1] = 0x40;
|
||||
wmask[offset + offsetof(CXLDVSECPortExt, alt_bus_base)] = 0xFF;
|
||||
wmask[offset + offsetof(CXLDVSECPortExt, alt_bus_limit)] = 0xFF;
|
||||
wmask[offset + offsetof(CXLDVSECPortExt, alt_memory_base)] = 0xF0;
|
||||
wmask[offset + offsetof(CXLDVSECPortExt, alt_memory_base) + 1] = 0xFF;
|
||||
wmask[offset + offsetof(CXLDVSECPortExt, alt_memory_limit)] = 0xF0;
|
||||
wmask[offset + offsetof(CXLDVSECPortExt, alt_memory_limit) + 1] = 0xFF;
|
||||
wmask[offset + offsetof(CXLDVSECPortExt, alt_prefetch_base)] = 0xF0;
|
||||
wmask[offset + offsetof(CXLDVSECPortExt, alt_prefetch_base) + 1] = 0xFF;
|
||||
wmask[offset + offsetof(CXLDVSECPortExt, alt_prefetch_limit)] = 0xF0;
|
||||
wmask[offset + offsetof(CXLDVSECPortExt, alt_prefetch_limit) + 1] =
|
||||
0xFF;
|
||||
wmask[offset + offsetof(CXLDVSECPortExt, alt_prefetch_base_high)] =
|
||||
0xFF;
|
||||
wmask[offset + offsetof(CXLDVSECPortExt, alt_prefetch_base_high) + 1] =
|
||||
0xFF;
|
||||
wmask[offset + offsetof(CXLDVSECPortExt, alt_prefetch_base_high) + 2] =
|
||||
0xFF;
|
||||
wmask[offset + offsetof(CXLDVSECPortExt, alt_prefetch_base_high) + 3] =
|
||||
0xFF;
|
||||
wmask[offset + offsetof(CXLDVSECPortExt, alt_prefetch_limit_high)] =
|
||||
0xFF;
|
||||
wmask[offset + offsetof(CXLDVSECPortExt, alt_prefetch_limit_high) + 1] =
|
||||
0xFF;
|
||||
wmask[offset + offsetof(CXLDVSECPortExt, alt_prefetch_limit_high) + 2] =
|
||||
0xFF;
|
||||
wmask[offset + offsetof(CXLDVSECPortExt, alt_prefetch_limit_high) + 3] =
|
||||
0xFF;
|
||||
break;
|
||||
case GPF_PORT_DVSEC:
|
||||
wmask[offset + offsetof(CXLDVSECPortGPF, phase1_ctrl)] = 0x0F;
|
||||
@@ -420,7 +450,7 @@ void cxl_component_create_dvsec(CXLComponentState *cxl,
|
||||
default: /* Registers are RO for other component types */
|
||||
break;
|
||||
}
|
||||
/* There are rw1cs bits in the status register but never set currently */
|
||||
/* There are rw1cs bits in the status register but never set */
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
+129
-14
@@ -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)
|
||||
|
||||
+7
-4
@@ -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;
|
||||
|
||||
+905
-155
File diff suppressed because it is too large
Load Diff
@@ -6,6 +6,7 @@ system_ss.add(when: 'CONFIG_CXL',
|
||||
'cxl-host.c',
|
||||
'cxl-cdat.c',
|
||||
'cxl-events.c',
|
||||
'switch-mailbox-cci.c',
|
||||
),
|
||||
if_false: files(
|
||||
'cxl-host-stubs.c',
|
||||
|
||||
@@ -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);
|
||||
+47
-16
@@ -23,6 +23,7 @@
|
||||
#include "qemu/pmem.h"
|
||||
#include "qemu/range.h"
|
||||
#include "qemu/rcu.h"
|
||||
#include "qemu/guest-random.h"
|
||||
#include "sysemu/hostmem.h"
|
||||
#include "sysemu/numa.h"
|
||||
#include "hw/cxl/cxl.h"
|
||||
@@ -208,10 +209,9 @@ static int ct3_build_cdat_table(CDATSubHeader ***cdat_table, void *priv)
|
||||
}
|
||||
|
||||
if (nonvolatile_mr) {
|
||||
uint64_t base = volatile_mr ? memory_region_size(volatile_mr) : 0;
|
||||
rc = ct3_build_cdat_entries_for_mr(&(table[cur_ent]), dsmad_handle++,
|
||||
nonvolatile_mr, true,
|
||||
(volatile_mr ?
|
||||
memory_region_size(volatile_mr) : 0));
|
||||
nonvolatile_mr, true, base);
|
||||
if (rc < 0) {
|
||||
goto error_cleanup;
|
||||
}
|
||||
@@ -514,7 +514,8 @@ static void ct3d_reg_write(void *opaque, hwaddr offset, uint64_t value,
|
||||
case A_CXL_RAS_UNC_ERR_STATUS:
|
||||
{
|
||||
uint32_t capctrl = ldl_le_p(cache_mem + R_CXL_RAS_ERR_CAP_CTRL);
|
||||
uint32_t fe = FIELD_EX32(capctrl, CXL_RAS_ERR_CAP_CTRL, FIRST_ERROR_POINTER);
|
||||
uint32_t fe = FIELD_EX32(capctrl, CXL_RAS_ERR_CAP_CTRL,
|
||||
FIRST_ERROR_POINTER);
|
||||
CXLError *cxl_err;
|
||||
uint32_t unc_err;
|
||||
|
||||
@@ -533,7 +534,8 @@ static void ct3d_reg_write(void *opaque, hwaddr offset, uint64_t value,
|
||||
* closest to behavior of hardware not capable of multiple
|
||||
* header recording.
|
||||
*/
|
||||
QTAILQ_FOREACH_SAFE(cxl_err, &ct3d->error_list, node, cxl_next) {
|
||||
QTAILQ_FOREACH_SAFE(cxl_err, &ct3d->error_list, node,
|
||||
cxl_next) {
|
||||
if ((1 << cxl_err->type) & value) {
|
||||
QTAILQ_REMOVE(&ct3d->error_list, cxl_err, node);
|
||||
g_free(cxl_err);
|
||||
@@ -715,7 +717,8 @@ static void ct3_realize(PCIDevice *pci_dev, Error **errp)
|
||||
pci_dev, CXL_COMPONENT_REG_BAR_IDX,
|
||||
PCI_BASE_ADDRESS_SPACE_MEMORY | PCI_BASE_ADDRESS_MEM_TYPE_64, mr);
|
||||
|
||||
cxl_device_register_block_init(OBJECT(pci_dev), &ct3d->cxl_dstate);
|
||||
cxl_device_register_block_init(OBJECT(pci_dev), &ct3d->cxl_dstate,
|
||||
&ct3d->cci);
|
||||
pci_register_bar(pci_dev, CXL_DEVICE_REG_BAR_IDX,
|
||||
PCI_BASE_ADDRESS_SPACE_MEMORY |
|
||||
PCI_BASE_ADDRESS_MEM_TYPE_64,
|
||||
@@ -885,32 +888,43 @@ static int cxl_type3_hpa_to_as_and_dpa(CXLType3Dev *ct3d,
|
||||
MemTxResult cxl_type3_read(PCIDevice *d, hwaddr host_addr, uint64_t *data,
|
||||
unsigned size, MemTxAttrs attrs)
|
||||
{
|
||||
CXLType3Dev *ct3d = CXL_TYPE3(d);
|
||||
uint64_t dpa_offset = 0;
|
||||
AddressSpace *as = NULL;
|
||||
int res;
|
||||
|
||||
res = cxl_type3_hpa_to_as_and_dpa(CXL_TYPE3(d), host_addr, size,
|
||||
res = cxl_type3_hpa_to_as_and_dpa(ct3d, host_addr, size,
|
||||
&as, &dpa_offset);
|
||||
if (res) {
|
||||
return MEMTX_ERROR;
|
||||
}
|
||||
|
||||
if (sanitize_running(&ct3d->cci)) {
|
||||
qemu_guest_getrandom_nofail(data, size);
|
||||
return MEMTX_OK;
|
||||
}
|
||||
|
||||
return address_space_read(as, dpa_offset, attrs, data, size);
|
||||
}
|
||||
|
||||
MemTxResult cxl_type3_write(PCIDevice *d, hwaddr host_addr, uint64_t data,
|
||||
unsigned size, MemTxAttrs attrs)
|
||||
{
|
||||
CXLType3Dev *ct3d = CXL_TYPE3(d);
|
||||
uint64_t dpa_offset = 0;
|
||||
AddressSpace *as = NULL;
|
||||
int res;
|
||||
|
||||
res = cxl_type3_hpa_to_as_and_dpa(CXL_TYPE3(d), host_addr, size,
|
||||
res = cxl_type3_hpa_to_as_and_dpa(ct3d, host_addr, size,
|
||||
&as, &dpa_offset);
|
||||
if (res) {
|
||||
return MEMTX_ERROR;
|
||||
}
|
||||
|
||||
if (sanitize_running(&ct3d->cci)) {
|
||||
return MEMTX_OK;
|
||||
}
|
||||
|
||||
return address_space_write(as, dpa_offset, attrs, &data, size);
|
||||
}
|
||||
|
||||
@@ -921,7 +935,18 @@ static void ct3d_reset(DeviceState *dev)
|
||||
uint32_t *write_msk = ct3d->cxl_cstate.crb.cache_mem_regs_write_mask;
|
||||
|
||||
cxl_component_register_init_common(reg_state, write_msk, CXL2_TYPE3_DEVICE);
|
||||
cxl_device_register_init_common(&ct3d->cxl_dstate);
|
||||
cxl_device_register_init_t3(ct3d);
|
||||
|
||||
/*
|
||||
* Bring up an endpoint to target with MCTP over VDM.
|
||||
* This device is emulating an MLD with single LD for now.
|
||||
*/
|
||||
cxl_initialize_t3_fm_owned_ld_mctpcci(&ct3d->vdm_fm_owned_ld_mctp_cci,
|
||||
DEVICE(ct3d), DEVICE(ct3d),
|
||||
512); /* Max payload made up */
|
||||
cxl_initialize_t3_ld_cci(&ct3d->ld0_cci, DEVICE(ct3d), DEVICE(ct3d),
|
||||
512); /* Max payload made up */
|
||||
|
||||
}
|
||||
|
||||
static Property ct3_props[] = {
|
||||
@@ -1072,7 +1097,8 @@ void qmp_cxl_inject_poison(const char *path, uint64_t start, uint64_t length,
|
||||
if (((start >= p->start) && (start < p->start + p->length)) ||
|
||||
((start + length > p->start) &&
|
||||
(start + length <= p->start + p->length))) {
|
||||
error_setg(errp, "Overlap with existing poisoned region not supported");
|
||||
error_setg(errp,
|
||||
"Overlap with existing poisoned region not supported");
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -1085,7 +1111,8 @@ void qmp_cxl_inject_poison(const char *path, uint64_t start, uint64_t length,
|
||||
p = g_new0(CXLPoison, 1);
|
||||
p->length = length;
|
||||
p->start = start;
|
||||
p->type = CXL_POISON_TYPE_INTERNAL; /* Different from injected via the mbox */
|
||||
/* Different from injected via the mbox */
|
||||
p->type = CXL_POISON_TYPE_INTERNAL;
|
||||
|
||||
QLIST_INSERT_HEAD(&ct3d->poison_list, p, node);
|
||||
ct3d->poison_list_cnt++;
|
||||
@@ -1222,7 +1249,8 @@ void qmp_cxl_inject_correctable_error(const char *path, CxlCorErrorType type,
|
||||
return;
|
||||
}
|
||||
/* If the error is masked, nothting to do here */
|
||||
if (!((1 << cxl_err_type) & ~ldl_le_p(reg_state + R_CXL_RAS_COR_ERR_MASK))) {
|
||||
if (!((1 << cxl_err_type) &
|
||||
~ldl_le_p(reg_state + R_CXL_RAS_COR_ERR_MASK))) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1372,7 +1400,8 @@ void qmp_cxl_inject_dram_event(const char *path, CxlEventLog log, uint8_t flags,
|
||||
bool has_bank, uint8_t bank,
|
||||
bool has_row, uint32_t row,
|
||||
bool has_column, uint16_t column,
|
||||
bool has_correction_mask, uint64List *correction_mask,
|
||||
bool has_correction_mask,
|
||||
uint64List *correction_mask,
|
||||
Error **errp)
|
||||
{
|
||||
Object *obj = object_resolve_path(path, NULL);
|
||||
@@ -1473,7 +1502,7 @@ void qmp_cxl_inject_memory_module_event(const char *path, CxlEventLog log,
|
||||
int16_t temperature,
|
||||
uint32_t dirty_shutdown_count,
|
||||
uint32_t corrected_volatile_error_count,
|
||||
uint32_t corrected_persistent_error_count,
|
||||
uint32_t corrected_persist_error_count,
|
||||
Error **errp)
|
||||
{
|
||||
Object *obj = object_resolve_path(path, NULL);
|
||||
@@ -1513,8 +1542,10 @@ void qmp_cxl_inject_memory_module_event(const char *path, CxlEventLog log,
|
||||
module.life_used = life_used;
|
||||
stw_le_p(&module.temperature, temperature);
|
||||
stl_le_p(&module.dirty_shutdown_count, dirty_shutdown_count);
|
||||
stl_le_p(&module.corrected_volatile_error_count, corrected_volatile_error_count);
|
||||
stl_le_p(&module.corrected_persistent_error_count, corrected_persistent_error_count);
|
||||
stl_le_p(&module.corrected_volatile_error_count,
|
||||
corrected_volatile_error_count);
|
||||
stl_le_p(&module.corrected_persistent_error_count,
|
||||
corrected_persist_error_count);
|
||||
|
||||
if (cxl_event_insert(cxlds, enc_log, (CXLEventRecordRaw *)&module)) {
|
||||
cxl_event_irq_assert(ct3d);
|
||||
|
||||
@@ -33,7 +33,8 @@ void qmp_cxl_inject_dram_event(const char *path, CxlEventLog log, uint8_t flags,
|
||||
bool has_bank, uint8_t bank,
|
||||
bool has_row, uint32_t row,
|
||||
bool has_column, uint16_t column,
|
||||
bool has_correction_mask, uint64List *correction_mask,
|
||||
bool has_correction_mask,
|
||||
uint64List *correction_mask,
|
||||
Error **errp) {}
|
||||
|
||||
void qmp_cxl_inject_memory_module_event(const char *path, CxlEventLog log,
|
||||
@@ -45,7 +46,7 @@ void qmp_cxl_inject_memory_module_event(const char *path, CxlEventLog log,
|
||||
int16_t temperature,
|
||||
uint32_t dirty_shutdown_count,
|
||||
uint32_t corrected_volatile_error_count,
|
||||
uint32_t corrected_persistent_error_count,
|
||||
uint32_t corrected_persist_error_count,
|
||||
Error **errp) {}
|
||||
|
||||
void qmp_cxl_inject_poison(const char *path, uint64_t start, uint64_t length,
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "hw/pci/msi.h"
|
||||
#include "hw/pci/pcie.h"
|
||||
#include "hw/pci/pcie_port.h"
|
||||
#include "hw/cxl/cxl.h"
|
||||
#include "qapi/error.h"
|
||||
|
||||
typedef struct CXLDownstreamPort {
|
||||
@@ -23,9 +24,6 @@ typedef struct CXLDownstreamPort {
|
||||
CXLComponentState cxl_cstate;
|
||||
} CXLDownstreamPort;
|
||||
|
||||
#define TYPE_CXL_DSP "cxl-downstream"
|
||||
DECLARE_INSTANCE_CHECKER(CXLDownstreamPort, CXL_DSP, TYPE_CXL_DSP)
|
||||
|
||||
#define CXL_DOWNSTREAM_PORT_MSI_OFFSET 0x70
|
||||
#define CXL_DOWNSTREAM_PORT_MSI_NR_VECTOR 1
|
||||
#define CXL_DOWNSTREAM_PORT_EXP_OFFSET 0x90
|
||||
@@ -98,7 +96,7 @@ static void build_dvsecs(CXLComponentState *cxl)
|
||||
{
|
||||
uint8_t *dvsec;
|
||||
|
||||
dvsec = (uint8_t *)&(CXLDVSECPortExtensions){ 0 };
|
||||
dvsec = (uint8_t *)&(CXLDVSECPortExt){ 0 };
|
||||
cxl_component_create_dvsec(cxl, CXL2_DOWNSTREAM_PORT,
|
||||
EXTENSIONS_PORT_DVSEC_LENGTH,
|
||||
EXTENSIONS_PORT_DVSEC,
|
||||
@@ -212,6 +210,19 @@ static void cxl_dsp_exitfn(PCIDevice *d)
|
||||
pci_bridge_exitfn(d);
|
||||
}
|
||||
|
||||
static void cxl_dsp_instance_post_init(Object *obj)
|
||||
{
|
||||
PCIESlot *s = PCIE_SLOT(obj);
|
||||
|
||||
if (!s->speed) {
|
||||
s->speed = QEMU_PCI_EXP_LNK_2_5GT;
|
||||
}
|
||||
|
||||
if (!s->width) {
|
||||
s->width = QEMU_PCI_EXP_LNK_X1;
|
||||
}
|
||||
}
|
||||
|
||||
static void cxl_dsp_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||
@@ -232,6 +243,7 @@ static const TypeInfo cxl_dsp_info = {
|
||||
.name = TYPE_CXL_DSP,
|
||||
.instance_size = sizeof(CXLDownstreamPort),
|
||||
.parent = TYPE_PCIE_SLOT,
|
||||
.instance_post_init = cxl_dsp_instance_post_init,
|
||||
.class_init = cxl_dsp_class_init,
|
||||
.interfaces = (InterfaceInfo[]) {
|
||||
{ INTERFACE_PCIE_DEVICE },
|
||||
|
||||
@@ -107,7 +107,7 @@ static void build_dvsecs(CXLComponentState *cxl)
|
||||
{
|
||||
uint8_t *dvsec;
|
||||
|
||||
dvsec = (uint8_t *)&(CXLDVSECPortExtensions){ 0 };
|
||||
dvsec = (uint8_t *)&(CXLDVSECPortExt){ 0 };
|
||||
cxl_component_create_dvsec(cxl, CXL2_ROOT_PORT,
|
||||
EXTENSIONS_PORT_DVSEC_LENGTH,
|
||||
EXTENSIONS_PORT_DVSEC,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user