virtio,pci,pc: features,fixes
pci: Initial support for SPDM Responders cxl: Add support for scan media, feature commands, device patrol scrub control, DDR5 ECS control, firmware updates virtio: in-order support virtio-net: support for SR-IOV emulation (note: known issues on s390, might get reverted if not fixed) smbios: memory device size is now configurable per Machine cpu: architecture agnostic code to support vCPU Hotplug Fixes, cleanups all over the place. Signed-off-by: Michael S. Tsirkin <mst@redhat.com> -----BEGIN PGP SIGNATURE----- iQFDBAABCAAtFiEEXQn9CHHI+FuUyooNKB8NuNKNVGkFAmae9l8PHG1zdEByZWRo YXQuY29tAAoJECgfDbjSjVRp8fYH/impBH9nViO/WK48io4mLSkl0EUL8Y/xrMvH zKFCKaXq8D96VTt1Z4EGKYgwG0voBKZaCEKYU/0ARGnSlSwxINQ8ROCnBWMfn2sx yQt08EXVMznNLtXjc6U5zCoCi6SaV85GH40No3MUFXBQt29ZSlFqO/fuHGZHYBwS wuVKvTjjNF4EsGt3rS4Qsv6BwZWMM+dE6yXpKWk68kR8IGp+6QGxkMbWt9uEX2Md VuemKVnFYw0XGCGy5K+ZkvoA2DGpEw0QxVSOMs8CI55Oc9SkTKz5fUSzXXGo1if+ M1CTjOPJu6pMym6gy6XpFa8/QioDA/jE2vBQvfJ64TwhJDV159s= =k8e9 -----END PGP SIGNATURE----- Merge tag 'for_upstream' of https://git.kernel.org/pub/scm/virt/kvm/mst/qemu into staging virtio,pci,pc: features,fixes pci: Initial support for SPDM Responders cxl: Add support for scan media, feature commands, device patrol scrub control, DDR5 ECS control, firmware updates virtio: in-order support virtio-net: support for SR-IOV emulation (note: known issues on s390, might get reverted if not fixed) smbios: memory device size is now configurable per Machine cpu: architecture agnostic code to support vCPU Hotplug Fixes, cleanups all over the place. Signed-off-by: Michael S. Tsirkin <mst@redhat.com> # -----BEGIN PGP SIGNATURE----- # # iQFDBAABCAAtFiEEXQn9CHHI+FuUyooNKB8NuNKNVGkFAmae9l8PHG1zdEByZWRo # YXQuY29tAAoJECgfDbjSjVRp8fYH/impBH9nViO/WK48io4mLSkl0EUL8Y/xrMvH # zKFCKaXq8D96VTt1Z4EGKYgwG0voBKZaCEKYU/0ARGnSlSwxINQ8ROCnBWMfn2sx # yQt08EXVMznNLtXjc6U5zCoCi6SaV85GH40No3MUFXBQt29ZSlFqO/fuHGZHYBwS # wuVKvTjjNF4EsGt3rS4Qsv6BwZWMM+dE6yXpKWk68kR8IGp+6QGxkMbWt9uEX2Md # VuemKVnFYw0XGCGy5K+ZkvoA2DGpEw0QxVSOMs8CI55Oc9SkTKz5fUSzXXGo1if+ # M1CTjOPJu6pMym6gy6XpFa8/QioDA/jE2vBQvfJ64TwhJDV159s= # =k8e9 # -----END PGP SIGNATURE----- # gpg: Signature made Tue 23 Jul 2024 10:16:31 AM AEST # gpg: using RSA key 5D09FD0871C8F85B94CA8A0D281F0DB8D28D5469 # gpg: issuer "mst@redhat.com" # gpg: Good signature from "Michael S. Tsirkin <mst@kernel.org>" [undefined] # gpg: aka "Michael S. Tsirkin <mst@redhat.com>" [undefined] # gpg: WARNING: The key's User ID is not certified with a trusted signature! # gpg: There is no indication that the signature belongs to the owner. # 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: (61 commits) hw/nvme: Add SPDM over DOE support backends: Initial support for SPDM socket support hw/pci: Add all Data Object Types defined in PCIe r6.0 tests/acpi: Add expected ACPI AML files for RISC-V tests/qtest/bios-tables-test.c: Enable basic testing for RISC-V tests/acpi: Add empty ACPI data files for RISC-V tests/qtest/bios-tables-test.c: Remove the fall back path tests/acpi: update expected DSDT blob for aarch64 and microvm acpi/gpex: Create PCI link devices outside PCI root bridge tests/acpi: Allow DSDT acpi table changes for aarch64 hw/riscv/virt-acpi-build.c: Update the HID of RISC-V UART hw/riscv/virt-acpi-build.c: Add namespace devices for PLIC and APLIC virtio-iommu: Add trace point on virtio_iommu_detach_endpoint_from_domain hw/vfio/common: Add vfio_listener_region_del_iommu trace event virtio-iommu: Remove the end point on detach virtio-iommu: Free [host_]resv_ranges on unset_iommu_devices virtio-iommu: Remove probe_done Revert "virtio-iommu: Clear IOMMUDevice when VFIO device is unplugged" gdbstub: Add helper function to unregister GDB register space physmem: Add helper function to destroy CPU AddressSpace ... Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
This commit is contained in:
commit
5885bcef3d
@ -2009,6 +2009,7 @@ F: hw/pci-bridge/*
|
||||
F: qapi/pci.json
|
||||
F: docs/pci*
|
||||
F: docs/specs/*pci*
|
||||
F: docs/system/sriov.rst
|
||||
|
||||
PCIE DOE
|
||||
M: Huai-Cheng Kuo <hchkuo@avery-design.com.tw>
|
||||
@ -2208,6 +2209,7 @@ F: docs/devel/vfio-iommufd.rst
|
||||
|
||||
vhost
|
||||
M: Michael S. Tsirkin <mst@redhat.com>
|
||||
R: Stefano Garzarella <sgarzare@redhat.com>
|
||||
S: Supported
|
||||
F: hw/*/*vhost*
|
||||
F: docs/interop/vhost-user.json
|
||||
@ -3398,6 +3400,12 @@ F: tests/qtest/*tpm*
|
||||
F: docs/specs/tpm.rst
|
||||
T: git https://github.com/stefanberger/qemu-tpm.git tpm-next
|
||||
|
||||
SPDM
|
||||
M: Alistair Francis <alistair.francis@wdc.com>
|
||||
S: Maintained
|
||||
F: backends/spdm-socket.c
|
||||
F: include/sysemu/spdm-socket.h
|
||||
|
||||
Checkpatch
|
||||
S: Odd Fixes
|
||||
F: scripts/checkpatch.pl
|
||||
@ -3657,6 +3665,7 @@ F: tests/uefi-test-tools/
|
||||
VT-d Emulation
|
||||
M: Michael S. Tsirkin <mst@redhat.com>
|
||||
R: Jason Wang <jasowang@redhat.com>
|
||||
R: Yi Liu <yi.l.liu@intel.com>
|
||||
S: Supported
|
||||
F: hw/i386/intel_iommu.c
|
||||
F: hw/i386/intel_iommu_internal.h
|
||||
|
@ -340,14 +340,71 @@ err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
void kvm_park_vcpu(CPUState *cpu)
|
||||
{
|
||||
struct KVMParkedVcpu *vcpu;
|
||||
|
||||
trace_kvm_park_vcpu(cpu->cpu_index, kvm_arch_vcpu_id(cpu));
|
||||
|
||||
vcpu = g_malloc0(sizeof(*vcpu));
|
||||
vcpu->vcpu_id = kvm_arch_vcpu_id(cpu);
|
||||
vcpu->kvm_fd = cpu->kvm_fd;
|
||||
QLIST_INSERT_HEAD(&kvm_state->kvm_parked_vcpus, vcpu, node);
|
||||
}
|
||||
|
||||
int kvm_unpark_vcpu(KVMState *s, unsigned long vcpu_id)
|
||||
{
|
||||
struct KVMParkedVcpu *cpu;
|
||||
int kvm_fd = -ENOENT;
|
||||
|
||||
QLIST_FOREACH(cpu, &s->kvm_parked_vcpus, node) {
|
||||
if (cpu->vcpu_id == vcpu_id) {
|
||||
QLIST_REMOVE(cpu, node);
|
||||
kvm_fd = cpu->kvm_fd;
|
||||
g_free(cpu);
|
||||
}
|
||||
}
|
||||
|
||||
trace_kvm_unpark_vcpu(vcpu_id, kvm_fd > 0 ? "unparked" : "!found parked");
|
||||
|
||||
return kvm_fd;
|
||||
}
|
||||
|
||||
int kvm_create_vcpu(CPUState *cpu)
|
||||
{
|
||||
unsigned long vcpu_id = kvm_arch_vcpu_id(cpu);
|
||||
KVMState *s = kvm_state;
|
||||
int kvm_fd;
|
||||
|
||||
/* check if the KVM vCPU already exist but is parked */
|
||||
kvm_fd = kvm_unpark_vcpu(s, vcpu_id);
|
||||
if (kvm_fd < 0) {
|
||||
/* vCPU not parked: create a new KVM vCPU */
|
||||
kvm_fd = kvm_vm_ioctl(s, KVM_CREATE_VCPU, vcpu_id);
|
||||
if (kvm_fd < 0) {
|
||||
error_report("KVM_CREATE_VCPU IOCTL failed for vCPU %lu", vcpu_id);
|
||||
return kvm_fd;
|
||||
}
|
||||
}
|
||||
|
||||
cpu->kvm_fd = kvm_fd;
|
||||
cpu->kvm_state = s;
|
||||
cpu->vcpu_dirty = true;
|
||||
cpu->dirty_pages = 0;
|
||||
cpu->throttle_us_per_full = 0;
|
||||
|
||||
trace_kvm_create_vcpu(cpu->cpu_index, vcpu_id, kvm_fd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_kvm_destroy_vcpu(CPUState *cpu)
|
||||
{
|
||||
KVMState *s = kvm_state;
|
||||
long mmap_size;
|
||||
struct KVMParkedVcpu *vcpu = NULL;
|
||||
int ret = 0;
|
||||
|
||||
trace_kvm_destroy_vcpu();
|
||||
trace_kvm_destroy_vcpu(cpu->cpu_index, kvm_arch_vcpu_id(cpu));
|
||||
|
||||
ret = kvm_arch_destroy_vcpu(cpu);
|
||||
if (ret < 0) {
|
||||
@ -373,10 +430,7 @@ static int do_kvm_destroy_vcpu(CPUState *cpu)
|
||||
}
|
||||
}
|
||||
|
||||
vcpu = g_malloc0(sizeof(*vcpu));
|
||||
vcpu->vcpu_id = kvm_arch_vcpu_id(cpu);
|
||||
vcpu->kvm_fd = cpu->kvm_fd;
|
||||
QLIST_INSERT_HEAD(&kvm_state->kvm_parked_vcpus, vcpu, node);
|
||||
kvm_park_vcpu(cpu);
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
@ -389,24 +443,6 @@ void kvm_destroy_vcpu(CPUState *cpu)
|
||||
}
|
||||
}
|
||||
|
||||
static int kvm_get_vcpu(KVMState *s, unsigned long vcpu_id)
|
||||
{
|
||||
struct KVMParkedVcpu *cpu;
|
||||
|
||||
QLIST_FOREACH(cpu, &s->kvm_parked_vcpus, node) {
|
||||
if (cpu->vcpu_id == vcpu_id) {
|
||||
int kvm_fd;
|
||||
|
||||
QLIST_REMOVE(cpu, node);
|
||||
kvm_fd = cpu->kvm_fd;
|
||||
g_free(cpu);
|
||||
return kvm_fd;
|
||||
}
|
||||
}
|
||||
|
||||
return kvm_vm_ioctl(s, KVM_CREATE_VCPU, (void *)vcpu_id);
|
||||
}
|
||||
|
||||
int kvm_init_vcpu(CPUState *cpu, Error **errp)
|
||||
{
|
||||
KVMState *s = kvm_state;
|
||||
@ -415,19 +451,14 @@ int kvm_init_vcpu(CPUState *cpu, Error **errp)
|
||||
|
||||
trace_kvm_init_vcpu(cpu->cpu_index, kvm_arch_vcpu_id(cpu));
|
||||
|
||||
ret = kvm_get_vcpu(s, kvm_arch_vcpu_id(cpu));
|
||||
ret = kvm_create_vcpu(cpu);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "kvm_init_vcpu: kvm_get_vcpu failed (%lu)",
|
||||
error_setg_errno(errp, -ret,
|
||||
"kvm_init_vcpu: kvm_create_vcpu failed (%lu)",
|
||||
kvm_arch_vcpu_id(cpu));
|
||||
goto err;
|
||||
}
|
||||
|
||||
cpu->kvm_fd = ret;
|
||||
cpu->kvm_state = s;
|
||||
cpu->vcpu_dirty = true;
|
||||
cpu->dirty_pages = 0;
|
||||
cpu->throttle_us_per_full = 0;
|
||||
|
||||
mmap_size = kvm_ioctl(s, KVM_GET_VCPU_MMAP_SIZE, 0);
|
||||
if (mmap_size < 0) {
|
||||
ret = mmap_size;
|
||||
|
@ -22,5 +22,4 @@ bool kvm_supports_guest_debug(void);
|
||||
int kvm_insert_breakpoint(CPUState *cpu, int type, vaddr addr, vaddr len);
|
||||
int kvm_remove_breakpoint(CPUState *cpu, int type, vaddr addr, vaddr len);
|
||||
void kvm_remove_all_breakpoints(CPUState *cpu);
|
||||
|
||||
#endif /* KVM_CPUS_H */
|
||||
|
@ -9,6 +9,10 @@ kvm_device_ioctl(int fd, int type, void *arg) "dev fd %d, type 0x%x, arg %p"
|
||||
kvm_failed_reg_get(uint64_t id, const char *msg) "Warning: Unable to retrieve ONEREG %" PRIu64 " from KVM: %s"
|
||||
kvm_failed_reg_set(uint64_t id, const char *msg) "Warning: Unable to set ONEREG %" PRIu64 " to KVM: %s"
|
||||
kvm_init_vcpu(int cpu_index, unsigned long arch_cpu_id) "index: %d id: %lu"
|
||||
kvm_create_vcpu(int cpu_index, unsigned long arch_cpu_id, int kvm_fd) "index: %d, id: %lu, kvm fd: %d"
|
||||
kvm_destroy_vcpu(int cpu_index, unsigned long arch_cpu_id) "index: %d id: %lu"
|
||||
kvm_park_vcpu(int cpu_index, unsigned long arch_cpu_id) "index: %d id: %lu"
|
||||
kvm_unpark_vcpu(unsigned long arch_cpu_id, const char *msg) "id: %lu %s"
|
||||
kvm_irqchip_commit_routes(void) ""
|
||||
kvm_irqchip_add_msi_route(char *name, int vector, int virq) "dev %s vector %d virq %d"
|
||||
kvm_irqchip_update_msi_route(int virq) "Updating MSI route virq=%d"
|
||||
@ -25,7 +29,6 @@ kvm_dirty_ring_reaper(const char *s) "%s"
|
||||
kvm_dirty_ring_reap(uint64_t count, int64_t t) "reaped %"PRIu64" pages (took %"PRIi64" us)"
|
||||
kvm_dirty_ring_reaper_kick(const char *reason) "%s"
|
||||
kvm_dirty_ring_flush(int finished) "%d"
|
||||
kvm_destroy_vcpu(void) ""
|
||||
kvm_failed_get_vcpu_mmap_size(void) ""
|
||||
kvm_cpu_exec(void) ""
|
||||
kvm_interrupt_exit_request(void) ""
|
||||
|
@ -3,3 +3,7 @@ source tpm/Kconfig
|
||||
config IOMMUFD
|
||||
bool
|
||||
depends on VFIO
|
||||
|
||||
config SPDM_SOCKET
|
||||
bool
|
||||
default y
|
||||
|
@ -33,4 +33,6 @@ endif
|
||||
system_ss.add(when: gio, if_true: files('dbus-vmstate.c'))
|
||||
system_ss.add(when: 'CONFIG_SGX', if_true: files('hostmem-epc.c'))
|
||||
|
||||
system_ss.add(when: 'CONFIG_SPDM_SOCKET', if_true: files('spdm-socket.c'))
|
||||
|
||||
subdir('tpm')
|
||||
|
216
backends/spdm-socket.c
Normal file
216
backends/spdm-socket.c
Normal file
@ -0,0 +1,216 @@
|
||||
/* SPDX-License-Identifier: BSD-3-Clause */
|
||||
/*
|
||||
* QEMU SPDM socket support
|
||||
*
|
||||
* This is based on:
|
||||
* https://github.com/DMTF/spdm-emu/blob/07c0a838bcc1c6207c656ac75885c0603e344b6f/spdm_emu/spdm_emu_common/command.c
|
||||
* but has been re-written to match QEMU style
|
||||
*
|
||||
* Copyright (c) 2021, DMTF. All rights reserved.
|
||||
* Copyright (c) 2023. Western Digital Corporation or its affiliates.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "sysemu/spdm-socket.h"
|
||||
#include "qapi/error.h"
|
||||
|
||||
static bool read_bytes(const int socket, uint8_t *buffer,
|
||||
size_t number_of_bytes)
|
||||
{
|
||||
ssize_t number_received = 0;
|
||||
ssize_t result;
|
||||
|
||||
while (number_received < number_of_bytes) {
|
||||
result = recv(socket, buffer + number_received,
|
||||
number_of_bytes - number_received, 0);
|
||||
if (result <= 0) {
|
||||
return false;
|
||||
}
|
||||
number_received += result;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool read_data32(const int socket, uint32_t *data)
|
||||
{
|
||||
bool result;
|
||||
|
||||
result = read_bytes(socket, (uint8_t *)data, sizeof(uint32_t));
|
||||
if (!result) {
|
||||
return result;
|
||||
}
|
||||
*data = ntohl(*data);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool read_multiple_bytes(const int socket, uint8_t *buffer,
|
||||
uint32_t *bytes_received,
|
||||
uint32_t max_buffer_length)
|
||||
{
|
||||
uint32_t length;
|
||||
bool result;
|
||||
|
||||
result = read_data32(socket, &length);
|
||||
if (!result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if (length > max_buffer_length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (bytes_received) {
|
||||
*bytes_received = length;
|
||||
}
|
||||
|
||||
if (length == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return read_bytes(socket, buffer, length);
|
||||
}
|
||||
|
||||
static bool receive_platform_data(const int socket,
|
||||
uint32_t transport_type,
|
||||
uint32_t *command,
|
||||
uint8_t *receive_buffer,
|
||||
uint32_t *bytes_to_receive)
|
||||
{
|
||||
bool result;
|
||||
uint32_t response;
|
||||
uint32_t bytes_received;
|
||||
|
||||
result = read_data32(socket, &response);
|
||||
if (!result) {
|
||||
return result;
|
||||
}
|
||||
*command = response;
|
||||
|
||||
result = read_data32(socket, &transport_type);
|
||||
if (!result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
bytes_received = 0;
|
||||
result = read_multiple_bytes(socket, receive_buffer, &bytes_received,
|
||||
*bytes_to_receive);
|
||||
if (!result) {
|
||||
return result;
|
||||
}
|
||||
*bytes_to_receive = bytes_received;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool write_bytes(const int socket, const uint8_t *buffer,
|
||||
uint32_t number_of_bytes)
|
||||
{
|
||||
ssize_t number_sent = 0;
|
||||
ssize_t result;
|
||||
|
||||
while (number_sent < number_of_bytes) {
|
||||
result = send(socket, buffer + number_sent,
|
||||
number_of_bytes - number_sent, 0);
|
||||
if (result == -1) {
|
||||
return false;
|
||||
}
|
||||
number_sent += result;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool write_data32(const int socket, uint32_t data)
|
||||
{
|
||||
data = htonl(data);
|
||||
return write_bytes(socket, (uint8_t *)&data, sizeof(uint32_t));
|
||||
}
|
||||
|
||||
static bool write_multiple_bytes(const int socket, const uint8_t *buffer,
|
||||
uint32_t bytes_to_send)
|
||||
{
|
||||
bool result;
|
||||
|
||||
result = write_data32(socket, bytes_to_send);
|
||||
if (!result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
return write_bytes(socket, buffer, bytes_to_send);
|
||||
}
|
||||
|
||||
static bool send_platform_data(const int socket,
|
||||
uint32_t transport_type, uint32_t command,
|
||||
const uint8_t *send_buffer, size_t bytes_to_send)
|
||||
{
|
||||
bool result;
|
||||
|
||||
result = write_data32(socket, command);
|
||||
if (!result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
result = write_data32(socket, transport_type);
|
||||
if (!result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
return write_multiple_bytes(socket, send_buffer, bytes_to_send);
|
||||
}
|
||||
|
||||
int spdm_socket_connect(uint16_t port, Error **errp)
|
||||
{
|
||||
int client_socket;
|
||||
struct sockaddr_in server_addr;
|
||||
|
||||
client_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
if (client_socket < 0) {
|
||||
error_setg(errp, "cannot create socket: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset((char *)&server_addr, 0, sizeof(server_addr));
|
||||
server_addr.sin_family = AF_INET;
|
||||
server_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
||||
server_addr.sin_port = htons(port);
|
||||
|
||||
|
||||
if (connect(client_socket, (struct sockaddr *)&server_addr,
|
||||
sizeof(server_addr)) < 0) {
|
||||
error_setg(errp, "cannot connect: %s", strerror(errno));
|
||||
close(client_socket);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return client_socket;
|
||||
}
|
||||
|
||||
uint32_t spdm_socket_rsp(const int socket, uint32_t transport_type,
|
||||
void *req, uint32_t req_len,
|
||||
void *rsp, uint32_t rsp_len)
|
||||
{
|
||||
uint32_t command;
|
||||
bool result;
|
||||
|
||||
result = send_platform_data(socket, transport_type,
|
||||
SPDM_SOCKET_COMMAND_NORMAL,
|
||||
req, req_len);
|
||||
if (!result) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
result = receive_platform_data(socket, transport_type, &command,
|
||||
(uint8_t *)rsp, &rsp_len);
|
||||
if (!result) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
assert(command != 0);
|
||||
|
||||
return rsp_len;
|
||||
}
|
||||
|
||||
void spdm_socket_close(const int socket, uint32_t transport_type)
|
||||
{
|
||||
send_platform_data(socket, transport_type,
|
||||
SPDM_SOCKET_COMMAND_SHUTDOWN, NULL, 0);
|
||||
}
|
@ -196,7 +196,7 @@ vub_discard_write_zeroes(VubReq *req, struct iovec *iov, uint32_t iovcnt,
|
||||
VubDev *vdev_blk = req->vdev_blk;
|
||||
desc = buf;
|
||||
uint64_t range[2] = { le64_to_cpu(desc->sector) << 9,
|
||||
le32_to_cpu(desc->num_sectors) << 9 };
|
||||
(uint64_t)le32_to_cpu(desc->num_sectors) << 9 };
|
||||
if (type == VIRTIO_BLK_T_DISCARD) {
|
||||
if (ioctl(vdev_blk->blk_fd, BLKDISCARD, range) == 0) {
|
||||
g_free(buf);
|
||||
|
@ -64,7 +64,8 @@ GED IO interface (4 byte access)
|
||||
0: Memory hotplug event
|
||||
1: System power down event
|
||||
2: NVDIMM hotplug event
|
||||
3-31: Reserved
|
||||
3: CPU hotplug event
|
||||
4-31: Reserved
|
||||
|
||||
**write_access:**
|
||||
|
||||
|
@ -29,6 +29,7 @@ guest hardware that is specific to QEMU.
|
||||
edu
|
||||
ivshmem-spec
|
||||
pvpanic
|
||||
spdm
|
||||
standard-vga
|
||||
virt-ctlr
|
||||
vmcoreinfo
|
||||
|
134
docs/specs/spdm.rst
Normal file
134
docs/specs/spdm.rst
Normal file
@ -0,0 +1,134 @@
|
||||
======================================================
|
||||
QEMU Security Protocols and Data Models (SPDM) Support
|
||||
======================================================
|
||||
|
||||
SPDM enables authentication, attestation and key exchange to assist in
|
||||
providing infrastructure security enablement. It's a standard published
|
||||
by the `DMTF`_.
|
||||
|
||||
QEMU supports connecting to a SPDM responder implementation. This allows an
|
||||
external application to emulate the SPDM responder logic for an SPDM device.
|
||||
|
||||
Setting up a SPDM server
|
||||
========================
|
||||
|
||||
When using QEMU with SPDM devices QEMU will connect to a server which
|
||||
implements the SPDM functionality.
|
||||
|
||||
SPDM-Utils
|
||||
----------
|
||||
|
||||
You can use `SPDM Utils`_ to emulate a responder. This is the simplest method.
|
||||
|
||||
SPDM-Utils is a Linux applications to manage, test and develop devices
|
||||
supporting DMTF Security Protocol and Data Model (SPDM). It is written in Rust
|
||||
and utilises libspdm.
|
||||
|
||||
To use SPDM-Utils you will need to do the following steps. Details are included
|
||||
in the SPDM-Utils README.
|
||||
|
||||
1. `Build libspdm`_
|
||||
2. `Build SPDM Utils`_
|
||||
3. `Run it as a server`_
|
||||
|
||||
spdm-emu
|
||||
--------
|
||||
|
||||
You can use `spdm emu`_ to model the
|
||||
SPDM responder.
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
$ cd spdm-emu
|
||||
$ git submodule init; git submodule update --recursive
|
||||
$ mkdir build; cd build
|
||||
$ cmake -DARCH=x64 -DTOOLCHAIN=GCC -DTARGET=Debug -DCRYPTO=openssl ..
|
||||
$ make -j32
|
||||
$ make copy_sample_key # Build certificates, required for SPDM authentication.
|
||||
|
||||
It is worth noting that the certificates should be in compliance with
|
||||
PCIe r6.1 sec 6.31.3. This means you will need to add the following to
|
||||
openssl.cnf
|
||||
|
||||
.. code-block::
|
||||
|
||||
subjectAltName = otherName:2.23.147;UTF8:Vendor=1b36:Device=0010:CC=010802:REV=02:SSVID=1af4:SSID=1100
|
||||
2.23.147 = ASN1:OID:2.23.147
|
||||
|
||||
and then manually regenerate some certificates with:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
$ openssl req -nodes -newkey ec:param.pem -keyout end_responder.key \
|
||||
-out end_responder.req -sha384 -batch \
|
||||
-subj "/CN=DMTF libspdm ECP384 responder cert"
|
||||
|
||||
$ openssl x509 -req -in end_responder.req -out end_responder.cert \
|
||||
-CA inter.cert -CAkey inter.key -sha384 -days 3650 -set_serial 3 \
|
||||
-extensions v3_end -extfile ../openssl.cnf
|
||||
|
||||
$ openssl asn1parse -in end_responder.cert -out end_responder.cert.der
|
||||
|
||||
$ cat ca.cert.der inter.cert.der end_responder.cert.der > bundle_responder.certchain.der
|
||||
|
||||
You can use SPDM-Utils instead as it will generate the correct certificates
|
||||
automatically.
|
||||
|
||||
The responder can then be launched with
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
$ cd bin
|
||||
$ ./spdm_responder_emu --trans PCI_DOE
|
||||
|
||||
Connecting an SPDM NVMe device
|
||||
==============================
|
||||
|
||||
Once a SPDM server is running we can start QEMU and connect to the server.
|
||||
|
||||
For an NVMe device first let's setup a block we can use
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
$ cd qemu-spdm/linux/image
|
||||
$ dd if=/dev/zero of=blknvme bs=1M count=2096 # 2GB NNMe Drive
|
||||
|
||||
Then you can add this to your QEMU command line:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
-drive file=blknvme,if=none,id=mynvme,format=raw \
|
||||
-device nvme,drive=mynvme,serial=deadbeef,spdm_port=2323
|
||||
|
||||
At which point QEMU will try to connect to the SPDM server.
|
||||
|
||||
Note that if using x64-64 you will want to use the q35 machine instead
|
||||
of the default. So the entire QEMU command might look like this
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
qemu-system-x86_64 -M q35 \
|
||||
--kernel bzImage \
|
||||
-drive file=rootfs.ext2,if=virtio,format=raw \
|
||||
-append "root=/dev/vda console=ttyS0" \
|
||||
-net none -nographic \
|
||||
-drive file=blknvme,if=none,id=mynvme,format=raw \
|
||||
-device nvme,drive=mynvme,serial=deadbeef,spdm_port=2323
|
||||
|
||||
.. _DMTF:
|
||||
https://www.dmtf.org/standards/SPDM
|
||||
|
||||
.. _SPDM Utils:
|
||||
https://github.com/westerndigitalcorporation/spdm-utils
|
||||
|
||||
.. _spdm emu:
|
||||
https://github.com/dmtf/spdm-emu
|
||||
|
||||
.. _Build libspdm:
|
||||
https://github.com/westerndigitalcorporation/spdm-utils?tab=readme-ov-file#build-libspdm
|
||||
|
||||
.. _Build SPDM Utils:
|
||||
https://github.com/westerndigitalcorporation/spdm-utils?tab=readme-ov-file#build-the-binary
|
||||
|
||||
.. _Run it as a server:
|
||||
https://github.com/westerndigitalcorporation/spdm-utils#qemu-spdm-device-emulation
|
@ -39,3 +39,4 @@ or Hypervisor.Framework.
|
||||
multi-process
|
||||
confidential-guest-support
|
||||
vm-templating
|
||||
sriov
|
||||
|
36
docs/system/sriov.rst
Normal file
36
docs/system/sriov.rst
Normal file
@ -0,0 +1,36 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
Compsable SR-IOV device
|
||||
=======================
|
||||
|
||||
SR-IOV (Single Root I/O Virtualization) is an optional extended capability of a
|
||||
PCI Express device. It allows a single physical function (PF) to appear as
|
||||
multiple virtual functions (VFs) for the main purpose of eliminating software
|
||||
overhead in I/O from virtual machines.
|
||||
|
||||
There are devices with predefined SR-IOV configurations, but it is also possible
|
||||
to compose an SR-IOV device yourself. Composing an SR-IOV device is currently
|
||||
only supported by virtio-net-pci.
|
||||
|
||||
Users can configure an SR-IOV-capable virtio-net device by adding
|
||||
virtio-net-pci functions to a bus. Below is a command line example:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
-netdev user,id=n -netdev user,id=o
|
||||
-netdev user,id=p -netdev user,id=q
|
||||
-device pcie-root-port,id=b
|
||||
-device virtio-net-pci,bus=b,addr=0x0.0x3,netdev=q,sriov-pf=f
|
||||
-device virtio-net-pci,bus=b,addr=0x0.0x2,netdev=p,sriov-pf=f
|
||||
-device virtio-net-pci,bus=b,addr=0x0.0x1,netdev=o,sriov-pf=f
|
||||
-device virtio-net-pci,bus=b,addr=0x0.0x0,netdev=n,id=f
|
||||
|
||||
The VFs specify the paired PF with ``sriov-pf`` property. The PF must be
|
||||
added after all VFs. It is the user's responsibility to ensure that VFs have
|
||||
function numbers larger than one of the PF, and that the function numbers
|
||||
have a consistent stride.
|
||||
|
||||
You may also need to perform additional steps to activate the SR-IOV feature on
|
||||
your guest. For Linux, refer to [1]_.
|
||||
|
||||
.. [1] https://docs.kernel.org/PCI/pci-iov-howto.html
|
@ -618,6 +618,19 @@ void gdb_register_coprocessor(CPUState *cpu,
|
||||
}
|
||||
}
|
||||
|
||||
void gdb_unregister_coprocessor_all(CPUState *cpu)
|
||||
{
|
||||
/*
|
||||
* Safe to nuke everything. GDBRegisterState::xml is static const char so
|
||||
* it won't be freed
|
||||
*/
|
||||
g_array_free(cpu->gdb_regs, true);
|
||||
|
||||
cpu->gdb_regs = NULL;
|
||||
cpu->gdb_num_regs = 0;
|
||||
cpu->gdb_num_g_regs = 0;
|
||||
}
|
||||
|
||||
static void gdb_process_breakpoint_remove_all(GDBProcess *p)
|
||||
{
|
||||
CPUState *cpu = gdb_get_first_cpu_in_process(p);
|
||||
|
@ -19,6 +19,12 @@ void legacy_acpi_cpu_hotplug_init(MemoryRegion *parent, Object *owner,
|
||||
return;
|
||||
}
|
||||
|
||||
void cpu_hotplug_hw_init(MemoryRegion *as, Object *owner,
|
||||
CPUHotplugState *state, hwaddr base_addr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
void acpi_cpu_ospm_status(CPUHotplugState *cpu_st, ACPIOSTInfoList ***list)
|
||||
{
|
||||
return;
|
||||
|
@ -7,7 +7,6 @@
|
||||
#include "trace.h"
|
||||
#include "sysemu/numa.h"
|
||||
|
||||
#define ACPI_CPU_HOTPLUG_REG_LEN 12
|
||||
#define ACPI_CPU_SELECTOR_OFFSET_WR 0
|
||||
#define ACPI_CPU_FLAGS_OFFSET_RW 4
|
||||
#define ACPI_CPU_CMD_OFFSET_WR 5
|
||||
@ -339,9 +338,10 @@ const VMStateDescription vmstate_cpu_hotplug = {
|
||||
#define CPU_FW_EJECT_EVENT "CEJF"
|
||||
|
||||
void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts,
|
||||
build_madt_cpu_fn build_madt_cpu, hwaddr io_base,
|
||||
build_madt_cpu_fn build_madt_cpu, hwaddr base_addr,
|
||||
const char *res_root,
|
||||
const char *event_handler_method)
|
||||
const char *event_handler_method,
|
||||
AmlRegionSpace rs)
|
||||
{
|
||||
Aml *ifctx;
|
||||
Aml *field;
|
||||
@ -365,14 +365,22 @@ void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts,
|
||||
aml_name_decl("_UID", aml_string("CPU Hotplug resources")));
|
||||
aml_append(cpu_ctrl_dev, aml_mutex(CPU_LOCK, 0));
|
||||
|
||||
assert((rs == AML_SYSTEM_IO) || (rs == AML_SYSTEM_MEMORY));
|
||||
|
||||
crs = aml_resource_template();
|
||||
aml_append(crs, aml_io(AML_DECODE16, io_base, io_base, 1,
|
||||
if (rs == AML_SYSTEM_IO) {
|
||||
aml_append(crs, aml_io(AML_DECODE16, base_addr, base_addr, 1,
|
||||
ACPI_CPU_HOTPLUG_REG_LEN));
|
||||
} else if (rs == AML_SYSTEM_MEMORY) {
|
||||
aml_append(crs, aml_memory32_fixed(base_addr,
|
||||
ACPI_CPU_HOTPLUG_REG_LEN, AML_READ_WRITE));
|
||||
}
|
||||
|
||||
aml_append(cpu_ctrl_dev, aml_name_decl("_CRS", crs));
|
||||
|
||||
/* declare CPU hotplug MMIO region with related access fields */
|
||||
aml_append(cpu_ctrl_dev,
|
||||
aml_operation_region("PRST", AML_SYSTEM_IO, aml_int(io_base),
|
||||
aml_operation_region("PRST", rs, aml_int(base_addr),
|
||||
ACPI_CPU_HOTPLUG_REG_LEN));
|
||||
|
||||
field = aml_field("PRST", AML_BYTE_ACC, AML_NOLOCK,
|
||||
|
@ -25,6 +25,7 @@ static const uint32_t ged_supported_events[] = {
|
||||
ACPI_GED_MEM_HOTPLUG_EVT,
|
||||
ACPI_GED_PWR_DOWN_EVT,
|
||||
ACPI_GED_NVDIMM_HOTPLUG_EVT,
|
||||
ACPI_GED_CPU_HOTPLUG_EVT,
|
||||
};
|
||||
|
||||
/*
|
||||
@ -107,6 +108,9 @@ void build_ged_aml(Aml *table, const char *name, HotplugHandler *hotplug_dev,
|
||||
aml_append(if_ctx, aml_call0(MEMORY_DEVICES_CONTAINER "."
|
||||
MEMORY_SLOT_SCAN_METHOD));
|
||||
break;
|
||||
case ACPI_GED_CPU_HOTPLUG_EVT:
|
||||
aml_append(if_ctx, aml_call0(AML_GED_EVT_CPU_SCAN_METHOD));
|
||||
break;
|
||||
case ACPI_GED_PWR_DOWN_EVT:
|
||||
aml_append(if_ctx,
|
||||
aml_notify(aml_name(ACPI_POWER_BUTTON_DEVICE),
|
||||
@ -234,6 +238,8 @@ static void acpi_ged_device_plug_cb(HotplugHandler *hotplug_dev,
|
||||
} else {
|
||||
acpi_memory_plug_cb(hotplug_dev, &s->memhp_state, dev, errp);
|
||||
}
|
||||
} else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) {
|
||||
acpi_cpu_plug_cb(hotplug_dev, &s->cpuhp_state, dev, errp);
|
||||
} else {
|
||||
error_setg(errp, "virt: device plug request for unsupported device"
|
||||
" type: %s", object_get_typename(OBJECT(dev)));
|
||||
@ -248,6 +254,8 @@ static void acpi_ged_unplug_request_cb(HotplugHandler *hotplug_dev,
|
||||
if ((object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM) &&
|
||||
!(object_dynamic_cast(OBJECT(dev), TYPE_NVDIMM)))) {
|
||||
acpi_memory_unplug_request_cb(hotplug_dev, &s->memhp_state, dev, errp);
|
||||
} else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) {
|
||||
acpi_cpu_unplug_request_cb(hotplug_dev, &s->cpuhp_state, dev, errp);
|
||||
} else {
|
||||
error_setg(errp, "acpi: device unplug request for unsupported device"
|
||||
" type: %s", object_get_typename(OBJECT(dev)));
|
||||
@ -261,6 +269,8 @@ static void acpi_ged_unplug_cb(HotplugHandler *hotplug_dev,
|
||||
|
||||
if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
|
||||
acpi_memory_unplug_cb(&s->memhp_state, dev, errp);
|
||||
} else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) {
|
||||
acpi_cpu_unplug_cb(&s->cpuhp_state, dev, errp);
|
||||
} else {
|
||||
error_setg(errp, "acpi: device unplug for unsupported device"
|
||||
" type: %s", object_get_typename(OBJECT(dev)));
|
||||
@ -272,6 +282,7 @@ static void acpi_ged_ospm_status(AcpiDeviceIf *adev, ACPIOSTInfoList ***list)
|
||||
AcpiGedState *s = ACPI_GED(adev);
|
||||
|
||||
acpi_memory_ospm_status(&s->memhp_state, list);
|
||||
acpi_cpu_ospm_status(&s->cpuhp_state, list);
|
||||
}
|
||||
|
||||
static void acpi_ged_send_event(AcpiDeviceIf *adev, AcpiEventStatusBits ev)
|
||||
@ -286,6 +297,8 @@ static void acpi_ged_send_event(AcpiDeviceIf *adev, AcpiEventStatusBits ev)
|
||||
sel = ACPI_GED_PWR_DOWN_EVT;
|
||||
} else if (ev & ACPI_NVDIMM_HOTPLUG_STATUS) {
|
||||
sel = ACPI_GED_NVDIMM_HOTPLUG_EVT;
|
||||
} else if (ev & ACPI_CPU_HOTPLUG_STATUS) {
|
||||
sel = ACPI_GED_CPU_HOTPLUG_EVT;
|
||||
} else {
|
||||
/* Unknown event. Return without generating interrupt. */
|
||||
warn_report("GED: Unsupported event %d. No irq injected", ev);
|
||||
@ -371,6 +384,42 @@ static const VMStateDescription vmstate_acpi_ged = {
|
||||
}
|
||||
};
|
||||
|
||||
static void acpi_ged_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
|
||||
AcpiGedState *s = ACPI_GED(dev);
|
||||
uint32_t ged_events;
|
||||
int i;
|
||||
|
||||
ged_events = ctpop32(s->ged_event_bitmap);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ged_supported_events) && ged_events; i++) {
|
||||
uint32_t event = s->ged_event_bitmap & ged_supported_events[i];
|
||||
|
||||
if (!event) {
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (event) {
|
||||
case ACPI_GED_CPU_HOTPLUG_EVT:
|
||||
/* initialize CPU Hotplug related regions */
|
||||
memory_region_init(&s->container_cpuhp, OBJECT(dev),
|
||||
"cpuhp container",
|
||||
ACPI_CPU_HOTPLUG_REG_LEN);
|
||||
sysbus_init_mmio(sbd, &s->container_cpuhp);
|
||||
cpu_hotplug_hw_init(&s->container_cpuhp, OBJECT(dev),
|
||||
&s->cpuhp_state, 0);
|
||||
break;
|
||||
}
|
||||
ged_events--;
|
||||
}
|
||||
|
||||
if (ged_events) {
|
||||
error_report("Unsupported events specified");
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
static void acpi_ged_initfn(Object *obj)
|
||||
{
|
||||
DeviceState *dev = DEVICE(obj);
|
||||
@ -411,6 +460,7 @@ static void acpi_ged_class_init(ObjectClass *class, void *data)
|
||||
dc->desc = "ACPI Generic Event Device";
|
||||
device_class_set_props(dc, acpi_ged_properties);
|
||||
dc->vmsd = &vmstate_acpi_ged;
|
||||
dc->realize = acpi_ged_realize;
|
||||
|
||||
hc->plug = acpi_ged_device_plug_cb;
|
||||
hc->unplug_request = acpi_ged_unplug_request_cb;
|
||||
|
@ -3308,6 +3308,7 @@ DEFINE_VIRT_MACHINE_AS_LATEST(9, 1)
|
||||
static void virt_machine_9_0_options(MachineClass *mc)
|
||||
{
|
||||
virt_machine_9_1_options(mc);
|
||||
mc->smbios_memory_device_size = 16 * GiB;
|
||||
compat_props_add(mc->compat_props, hw_compat_9_0, hw_compat_9_0_len);
|
||||
}
|
||||
DEFINE_VIRT_MACHINE(9, 0)
|
||||
|
@ -282,11 +282,13 @@ uint32_t virtio_snd_set_pcm_params(VirtIOSound *s,
|
||||
error_report("Number of channels is not supported.");
|
||||
return cpu_to_le32(VIRTIO_SND_S_NOT_SUPP);
|
||||
}
|
||||
if (!(supported_formats & BIT(params->format))) {
|
||||
if (BIT(params->format) > sizeof(supported_formats) ||
|
||||
!(supported_formats & BIT(params->format))) {
|
||||
error_report("Stream format is not supported.");
|
||||
return cpu_to_le32(VIRTIO_SND_S_NOT_SUPP);
|
||||
}
|
||||
if (!(supported_rates & BIT(params->rate))) {
|
||||
if (BIT(params->rate) > sizeof(supported_rates) ||
|
||||
!(supported_rates & BIT(params->rate))) {
|
||||
error_report("Stream rate is not supported.");
|
||||
return cpu_to_le32(VIRTIO_SND_S_NOT_SUPP);
|
||||
}
|
||||
@ -1261,7 +1263,7 @@ static void virtio_snd_pcm_in_cb(void *data, int available)
|
||||
{
|
||||
VirtIOSoundPCMStream *stream = data;
|
||||
VirtIOSoundPCMBuffer *buffer;
|
||||
size_t size;
|
||||
size_t size, max_size;
|
||||
|
||||
WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) {
|
||||
while (!QSIMPLEQ_EMPTY(&stream->queue)) {
|
||||
@ -1275,7 +1277,12 @@ static void virtio_snd_pcm_in_cb(void *data, int available)
|
||||
continue;
|
||||
}
|
||||
|
||||
max_size = iov_size(buffer->elem->in_sg, buffer->elem->in_num);
|
||||
for (;;) {
|
||||
if (buffer->size >= max_size) {
|
||||
return_rx_buffer(stream, buffer);
|
||||
break;
|
||||
}
|
||||
size = AUD_read(stream->voice.in,
|
||||
buffer->data + buffer->size,
|
||||
MIN(available, (stream->params.period_bytes -
|
||||
|
@ -51,6 +51,7 @@ static const int user_feature_bits[] = {
|
||||
VIRTIO_F_RING_PACKED,
|
||||
VIRTIO_F_IOMMU_PLATFORM,
|
||||
VIRTIO_F_RING_RESET,
|
||||
VIRTIO_F_IN_ORDER,
|
||||
VIRTIO_F_NOTIFICATION_DATA,
|
||||
VHOST_INVALID_FEATURE_BIT
|
||||
};
|
||||
|
@ -282,7 +282,10 @@ static void cpu_common_finalize(Object *obj)
|
||||
}
|
||||
#endif
|
||||
free_queued_cpu_work(cpu);
|
||||
g_array_free(cpu->gdb_regs, TRUE);
|
||||
/* If cleanup didn't happen in context to gdb_unregister_coprocessor_all */
|
||||
if (cpu->gdb_regs) {
|
||||
g_array_free(cpu->gdb_regs, TRUE);
|
||||
}
|
||||
qemu_lockcnt_destroy(&cpu->in_ioctl_lock);
|
||||
qemu_mutex_destroy(&cpu->work_mutex);
|
||||
qemu_cond_destroy(cpu->halt_cond);
|
||||
|
@ -1005,6 +1005,12 @@ static void machine_class_init(ObjectClass *oc, void *data)
|
||||
/* Default 128 MB as guest ram size */
|
||||
mc->default_ram_size = 128 * MiB;
|
||||
mc->rom_file_has_mr = true;
|
||||
/*
|
||||
* SMBIOS 3.1.0 7.18.5 Memory Device — Extended Size
|
||||
* use max possible value that could be encoded into
|
||||
* 'Extended Size' field (2047Tb).
|
||||
*/
|
||||
mc->smbios_memory_device_size = 2047 * TiB;
|
||||
|
||||
/* numa node memory size aligned on 8MB by default.
|
||||
* On Linux, each node's border has to be 8MB aligned
|
||||
|
@ -139,6 +139,19 @@ bool cxl_event_insert(CXLDeviceState *cxlds, CXLEventLogType log_type,
|
||||
return cxl_event_count(log) == 1;
|
||||
}
|
||||
|
||||
void cxl_discard_all_event_records(CXLDeviceState *cxlds)
|
||||
{
|
||||
CXLEventLogType log_type;
|
||||
CXLEventLog *log;
|
||||
|
||||
for (log_type = 0; log_type < CXL_EVENT_TYPE_MAX; log_type++) {
|
||||
log = &cxlds->event_logs[log_type];
|
||||
while (!cxl_event_empty(log)) {
|
||||
cxl_event_delete_head(cxlds, log_type, log);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CXLRetCode cxl_event_get_records(CXLDeviceState *cxlds, CXLGetEventPayload *pl,
|
||||
uint8_t log_type, int max_recs,
|
||||
size_t *len)
|
||||
|
@ -315,7 +315,8 @@ static void machine_set_cxl(Object *obj, Visitor *v, const char *name,
|
||||
static void machine_get_cfmw(Object *obj, Visitor *v, const char *name,
|
||||
void *opaque, Error **errp)
|
||||
{
|
||||
CXLFixedMemoryWindowOptionsList **list = opaque;
|
||||
CXLState *state = opaque;
|
||||
CXLFixedMemoryWindowOptionsList **list = &state->cfmw_list;
|
||||
|
||||
visit_type_CXLFixedMemoryWindowOptionsList(v, name, list, errp);
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1536,7 +1536,8 @@ build_dsdt(GArray *table_data, BIOSLinker *linker,
|
||||
.fw_unplugs_cpu = pm->smi_on_cpu_unplug,
|
||||
};
|
||||
build_cpus_aml(dsdt, machine, opts, pc_madt_cpu_entry,
|
||||
pm->cpu_hp_io_base, "\\_SB.PCI0", "\\_GPE._E02");
|
||||
pm->cpu_hp_io_base, "\\_SB.PCI0", "\\_GPE._E02",
|
||||
AML_SYSTEM_IO);
|
||||
}
|
||||
|
||||
if (pcms->memhp_io_base && nr_mem) {
|
||||
|
@ -358,7 +358,7 @@ static VTDIOTLBEntry *vtd_lookup_iotlb(IntelIOMMUState *s, uint16_t source_id,
|
||||
{
|
||||
struct vtd_iotlb_key key;
|
||||
VTDIOTLBEntry *entry;
|
||||
int level;
|
||||
unsigned level;
|
||||
|
||||
for (level = VTD_SL_PT_LEVEL; level < VTD_SL_PML4_LEVEL; level++) {
|
||||
key.gfn = vtd_get_iotlb_gfn(addr, level);
|
||||
|
@ -264,10 +264,10 @@
|
||||
#define VTD_FRCD_FR(val) (((val) & 0xffULL) << 32)
|
||||
#define VTD_FRCD_SID_MASK 0xffffULL
|
||||
#define VTD_FRCD_SID(val) ((val) & VTD_FRCD_SID_MASK)
|
||||
#define VTD_FRCD_PV(val) (((val) & 0xffffULL) << 40)
|
||||
#define VTD_FRCD_PP(val) (((val) & 0x1ULL) << 31)
|
||||
/* For the low 64-bit of 128-bit */
|
||||
#define VTD_FRCD_FI(val) ((val) & ~0xfffULL)
|
||||
#define VTD_FRCD_PV(val) (((val) & 0xffffULL) << 40)
|
||||
#define VTD_FRCD_PP(val) (((val) & 0x1) << 31)
|
||||
#define VTD_FRCD_IR_IDX(val) (((val) & 0xffffULL) << 48)
|
||||
|
||||
/* DMA Remapping Fault Conditions */
|
||||
@ -436,7 +436,7 @@ struct VTDIOTLBPageInvInfo {
|
||||
uint16_t domain_id;
|
||||
uint32_t pasid;
|
||||
uint64_t addr;
|
||||
uint8_t mask;
|
||||
uint64_t mask;
|
||||
};
|
||||
typedef struct VTDIOTLBPageInvInfo VTDIOTLBPageInvInfo;
|
||||
|
||||
|
@ -495,6 +495,7 @@ static void pc_i440fx_machine_9_0_options(MachineClass *m)
|
||||
pc_i440fx_machine_9_1_options(m);
|
||||
m->alias = NULL;
|
||||
m->is_default = false;
|
||||
m->smbios_memory_device_size = 16 * GiB;
|
||||
|
||||
compat_props_add(m->compat_props, hw_compat_9_0, hw_compat_9_0_len);
|
||||
compat_props_add(m->compat_props, pc_compat_9_0, pc_compat_9_0_len);
|
||||
|
@ -374,6 +374,7 @@ static void pc_q35_machine_9_0_options(MachineClass *m)
|
||||
PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
|
||||
pc_q35_machine_9_1_options(m);
|
||||
m->alias = NULL;
|
||||
m->smbios_memory_device_size = 16 * GiB;
|
||||
compat_props_add(m->compat_props, hw_compat_9_0, hw_compat_9_0_len);
|
||||
compat_props_add(m->compat_props, pc_compat_9_0, pc_compat_9_0_len);
|
||||
pcmc->isa_bios_alias = false;
|
||||
|
@ -737,6 +737,11 @@ static bool cxl_setup_memory(CXLType3Dev *ct3d, Error **errp)
|
||||
error_setg(errp, "volatile memdev must have backing device");
|
||||
return false;
|
||||
}
|
||||
if (host_memory_backend_is_mapped(ct3d->hostvmem)) {
|
||||
error_setg(errp, "memory backend %s can't be used multiple times.",
|
||||
object_get_canonical_path_component(OBJECT(ct3d->hostvmem)));
|
||||
return false;
|
||||
}
|
||||
memory_region_set_nonvolatile(vmr, false);
|
||||
memory_region_set_enabled(vmr, true);
|
||||
host_memory_backend_set_mapped(ct3d->hostvmem, true);
|
||||
@ -760,6 +765,11 @@ static bool cxl_setup_memory(CXLType3Dev *ct3d, Error **errp)
|
||||
error_setg(errp, "persistent memdev must have backing device");
|
||||
return false;
|
||||
}
|
||||
if (host_memory_backend_is_mapped(ct3d->hostpmem)) {
|
||||
error_setg(errp, "memory backend %s can't be used multiple times.",
|
||||
object_get_canonical_path_component(OBJECT(ct3d->hostpmem)));
|
||||
return false;
|
||||
}
|
||||
memory_region_set_nonvolatile(pmr, true);
|
||||
memory_region_set_enabled(pmr, true);
|
||||
host_memory_backend_set_mapped(ct3d->hostpmem, true);
|
||||
@ -790,6 +800,11 @@ static bool cxl_setup_memory(CXLType3Dev *ct3d, Error **errp)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (host_memory_backend_is_mapped(ct3d->dc.host_dc)) {
|
||||
error_setg(errp, "memory backend %s can't be used multiple times.",
|
||||
object_get_canonical_path_component(OBJECT(ct3d->dc.host_dc)));
|
||||
return false;
|
||||
}
|
||||
/*
|
||||
* Set DC regions as volatile for now, non-volatile support can
|
||||
* be added in the future if needed.
|
||||
@ -829,6 +844,7 @@ static void ct3_realize(PCIDevice *pci_dev, Error **errp)
|
||||
uint8_t *pci_conf = pci_dev->config;
|
||||
unsigned short msix_num = 6;
|
||||
int i, rc;
|
||||
uint16_t count;
|
||||
|
||||
QTAILQ_INIT(&ct3d->error_list);
|
||||
|
||||
@ -893,6 +909,28 @@ static void ct3_realize(PCIDevice *pci_dev, Error **errp)
|
||||
}
|
||||
cxl_event_init(&ct3d->cxl_dstate, 2);
|
||||
|
||||
/* Set default value for patrol scrub attributes */
|
||||
ct3d->patrol_scrub_attrs.scrub_cycle_cap =
|
||||
CXL_MEMDEV_PS_SCRUB_CYCLE_CHANGE_CAP_DEFAULT |
|
||||
CXL_MEMDEV_PS_SCRUB_REALTIME_REPORT_CAP_DEFAULT;
|
||||
ct3d->patrol_scrub_attrs.scrub_cycle =
|
||||
CXL_MEMDEV_PS_CUR_SCRUB_CYCLE_DEFAULT |
|
||||
(CXL_MEMDEV_PS_MIN_SCRUB_CYCLE_DEFAULT << 8);
|
||||
ct3d->patrol_scrub_attrs.scrub_flags = CXL_MEMDEV_PS_ENABLE_DEFAULT;
|
||||
|
||||
/* Set default value for DDR5 ECS read attributes */
|
||||
for (count = 0; count < CXL_ECS_NUM_MEDIA_FRUS; count++) {
|
||||
ct3d->ecs_attrs[count].ecs_log_cap =
|
||||
CXL_ECS_LOG_ENTRY_TYPE_DEFAULT;
|
||||
ct3d->ecs_attrs[count].ecs_cap =
|
||||
CXL_ECS_REALTIME_REPORT_CAP_DEFAULT;
|
||||
ct3d->ecs_attrs[count].ecs_config =
|
||||
CXL_ECS_THRESHOLD_COUNT_DEFAULT |
|
||||
(CXL_ECS_MODE_DEFAULT << 3);
|
||||
/* Reserved */
|
||||
ct3d->ecs_attrs[count].ecs_flags = 0;
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
err_release_cdat:
|
||||
@ -1127,7 +1165,7 @@ MemTxResult cxl_type3_read(PCIDevice *d, hwaddr host_addr, uint64_t *data,
|
||||
return MEMTX_ERROR;
|
||||
}
|
||||
|
||||
if (sanitize_running(&ct3d->cci)) {
|
||||
if (cxl_dev_media_disabled(&ct3d->cxl_dstate)) {
|
||||
qemu_guest_getrandom_nofail(data, size);
|
||||
return MEMTX_OK;
|
||||
}
|
||||
@ -1149,7 +1187,7 @@ MemTxResult cxl_type3_write(PCIDevice *d, hwaddr host_addr, uint64_t data,
|
||||
return MEMTX_ERROR;
|
||||
}
|
||||
|
||||
if (sanitize_running(&ct3d->cci)) {
|
||||
if (cxl_dev_media_disabled(&ct3d->cxl_dstate)) {
|
||||
return MEMTX_OK;
|
||||
}
|
||||
|
||||
@ -1304,6 +1342,12 @@ void cxl_set_poison_list_overflowed(CXLType3Dev *ct3d)
|
||||
cxl_device_get_timestamp(&ct3d->cxl_dstate);
|
||||
}
|
||||
|
||||
void cxl_clear_poison_list_overflowed(CXLType3Dev *ct3d)
|
||||
{
|
||||
ct3d->poison_list_overflowed = false;
|
||||
ct3d->poison_list_overflow_ts = 0;
|
||||
}
|
||||
|
||||
void qmp_cxl_inject_poison(const char *path, uint64_t start, uint64_t length,
|
||||
Error **errp)
|
||||
{
|
||||
@ -1340,19 +1384,21 @@ void qmp_cxl_inject_poison(const char *path, uint64_t start, uint64_t length,
|
||||
}
|
||||
}
|
||||
|
||||
if (ct3d->poison_list_cnt == CXL_POISON_LIST_LIMIT) {
|
||||
cxl_set_poison_list_overflowed(ct3d);
|
||||
return;
|
||||
}
|
||||
|
||||
p = g_new0(CXLPoison, 1);
|
||||
p->length = length;
|
||||
p->start = start;
|
||||
/* Different from injected via the mbox */
|
||||
p->type = CXL_POISON_TYPE_INTERNAL;
|
||||
|
||||
QLIST_INSERT_HEAD(&ct3d->poison_list, p, node);
|
||||
ct3d->poison_list_cnt++;
|
||||
if (ct3d->poison_list_cnt < CXL_POISON_LIST_LIMIT) {
|
||||
QLIST_INSERT_HEAD(&ct3d->poison_list, p, node);
|
||||
ct3d->poison_list_cnt++;
|
||||
} else {
|
||||
if (!ct3d->poison_list_overflowed) {
|
||||
cxl_set_poison_list_overflowed(ct3d);
|
||||
}
|
||||
QLIST_INSERT_HEAD(&ct3d->poison_list_bkp, p, node);
|
||||
}
|
||||
}
|
||||
|
||||
/* For uncorrectable errors include support for multiple header recording */
|
||||
|
@ -48,6 +48,7 @@ static const int kernel_feature_bits[] = {
|
||||
VIRTIO_F_IOMMU_PLATFORM,
|
||||
VIRTIO_F_RING_PACKED,
|
||||
VIRTIO_F_RING_RESET,
|
||||
VIRTIO_F_IN_ORDER,
|
||||
VIRTIO_F_NOTIFICATION_DATA,
|
||||
VIRTIO_NET_F_HASH_REPORT,
|
||||
VHOST_INVALID_FEATURE_BIT
|
||||
@ -78,6 +79,7 @@ static const int user_feature_bits[] = {
|
||||
VIRTIO_F_IOMMU_PLATFORM,
|
||||
VIRTIO_F_RING_PACKED,
|
||||
VIRTIO_F_RING_RESET,
|
||||
VIRTIO_F_IN_ORDER,
|
||||
VIRTIO_NET_F_RSS,
|
||||
VIRTIO_NET_F_HASH_REPORT,
|
||||
VIRTIO_NET_F_GUEST_USO4,
|
||||
|
@ -203,6 +203,7 @@
|
||||
#include "sysemu/hostmem.h"
|
||||
#include "hw/pci/msix.h"
|
||||
#include "hw/pci/pcie_sriov.h"
|
||||
#include "sysemu/spdm-socket.h"
|
||||
#include "migration/vmstate.h"
|
||||
|
||||
#include "nvme.h"
|
||||
@ -8315,6 +8316,27 @@ static int nvme_add_pm_capability(PCIDevice *pci_dev, uint8_t offset)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool pcie_doe_spdm_rsp(DOECap *doe_cap)
|
||||
{
|
||||
void *req = pcie_doe_get_write_mbox_ptr(doe_cap);
|
||||
uint32_t req_len = pcie_doe_get_obj_len(req) * 4;
|
||||
void *rsp = doe_cap->read_mbox;
|
||||
uint32_t rsp_len = SPDM_SOCKET_MAX_MESSAGE_BUFFER_SIZE;
|
||||
|
||||
uint32_t recvd = spdm_socket_rsp(doe_cap->spdm_socket,
|
||||
SPDM_SOCKET_TRANSPORT_TYPE_PCI_DOE,
|
||||
req, req_len, rsp, rsp_len);
|
||||
doe_cap->read_mbox_len += DIV_ROUND_UP(recvd, 4);
|
||||
|
||||
return recvd != 0;
|
||||
}
|
||||
|
||||
static DOEProtocol doe_spdm_prot[] = {
|
||||
{ PCI_VENDOR_ID_PCI_SIG, PCI_SIG_DOE_CMA, pcie_doe_spdm_rsp },
|
||||
{ PCI_VENDOR_ID_PCI_SIG, PCI_SIG_DOE_SECURED_CMA, pcie_doe_spdm_rsp },
|
||||
{ }
|
||||
};
|
||||
|
||||
static bool nvme_init_pci(NvmeCtrl *n, PCIDevice *pci_dev, Error **errp)
|
||||
{
|
||||
ERRP_GUARD();
|
||||
@ -8402,6 +8424,25 @@ static bool nvme_init_pci(NvmeCtrl *n, PCIDevice *pci_dev, Error **errp)
|
||||
|
||||
nvme_update_msixcap_ts(pci_dev, n->conf_msix_qsize);
|
||||
|
||||
pcie_cap_deverr_init(pci_dev);
|
||||
|
||||
/* DOE Initialisation */
|
||||
if (pci_dev->spdm_port) {
|
||||
uint16_t doe_offset = n->params.sriov_max_vfs ?
|
||||
PCI_CONFIG_SPACE_SIZE + PCI_ARI_SIZEOF
|
||||
: PCI_CONFIG_SPACE_SIZE;
|
||||
|
||||
pcie_doe_init(pci_dev, &pci_dev->doe_spdm, doe_offset,
|
||||
doe_spdm_prot, true, 0);
|
||||
|
||||
pci_dev->doe_spdm.spdm_socket = spdm_socket_connect(pci_dev->spdm_port,
|
||||
errp);
|
||||
|
||||
if (pci_dev->doe_spdm.spdm_socket < 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (n->params.cmb_size_mb) {
|
||||
nvme_init_cmb(n, pci_dev);
|
||||
}
|
||||
@ -8650,6 +8691,11 @@ static void nvme_exit(PCIDevice *pci_dev)
|
||||
g_free(n->cmb.buf);
|
||||
}
|
||||
|
||||
if (pci_dev->doe_spdm.spdm_socket > 0) {
|
||||
spdm_socket_close(pci_dev->doe_spdm.spdm_socket,
|
||||
SPDM_SOCKET_TRANSPORT_TYPE_PCI_DOE);
|
||||
}
|
||||
|
||||
if (n->pmr.dev) {
|
||||
host_memory_backend_set_mapped(n->pmr.dev, false);
|
||||
}
|
||||
@ -8695,6 +8741,7 @@ static Property nvme_props[] = {
|
||||
DEFINE_PROP_BOOL("msix-exclusive-bar", NvmeCtrl, params.msix_exclusive_bar,
|
||||
false),
|
||||
DEFINE_PROP_UINT16("mqes", NvmeCtrl, params.mqes, 0x7ff),
|
||||
DEFINE_PROP_UINT16("spdm_port", PCIDevice, spdm_port, 0),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
@ -8766,11 +8813,25 @@ static void nvme_pci_write_config(PCIDevice *dev, uint32_t address,
|
||||
{
|
||||
uint16_t old_num_vfs = pcie_sriov_num_vfs(dev);
|
||||
|
||||
if (pcie_find_capability(dev, PCI_EXT_CAP_ID_DOE)) {
|
||||
pcie_doe_write_config(&dev->doe_spdm, address, val, len);
|
||||
}
|
||||
pci_default_write_config(dev, address, val, len);
|
||||
pcie_cap_flr_write_config(dev, address, val, len);
|
||||
nvme_sriov_post_write_config(dev, old_num_vfs);
|
||||
}
|
||||
|
||||
static uint32_t nvme_pci_read_config(PCIDevice *dev, uint32_t address, int len)
|
||||
{
|
||||
uint32_t val;
|
||||
if (dev->spdm_port && pcie_find_capability(dev, PCI_EXT_CAP_ID_DOE)) {
|
||||
if (pcie_doe_read_config(&dev->doe_spdm, address, len, &val)) {
|
||||
return val;
|
||||
}
|
||||
}
|
||||
return pci_default_read_config(dev, address, len);
|
||||
}
|
||||
|
||||
static const VMStateDescription nvme_vmstate = {
|
||||
.name = "nvme",
|
||||
.unmigratable = 1,
|
||||
@ -8783,6 +8844,7 @@ static void nvme_class_init(ObjectClass *oc, void *data)
|
||||
|
||||
pc->realize = nvme_realize;
|
||||
pc->config_write = nvme_pci_write_config;
|
||||
pc->config_read = nvme_pci_read_config;
|
||||
pc->exit = nvme_exit;
|
||||
pc->class_id = PCI_CLASS_STORAGE_EXPRESS;
|
||||
pc->revision = 2;
|
||||
|
@ -7,7 +7,8 @@
|
||||
#include "hw/pci/pcie_host.h"
|
||||
#include "hw/acpi/cxl.h"
|
||||
|
||||
static void acpi_dsdt_add_pci_route_table(Aml *dev, uint32_t irq)
|
||||
static void acpi_dsdt_add_pci_route_table(Aml *dev, uint32_t irq,
|
||||
Aml *scope, uint8_t bus_num)
|
||||
{
|
||||
Aml *method, *crs;
|
||||
int i, slot_no;
|
||||
@ -20,7 +21,7 @@ static void acpi_dsdt_add_pci_route_table(Aml *dev, uint32_t irq)
|
||||
Aml *pkg = aml_package(4);
|
||||
aml_append(pkg, aml_int((slot_no << 16) | 0xFFFF));
|
||||
aml_append(pkg, aml_int(i));
|
||||
aml_append(pkg, aml_name("GSI%d", gsi));
|
||||
aml_append(pkg, aml_name("L%.02X%X", bus_num, gsi));
|
||||
aml_append(pkg, aml_int(0));
|
||||
aml_append(rt_pkg, pkg);
|
||||
}
|
||||
@ -30,7 +31,7 @@ static void acpi_dsdt_add_pci_route_table(Aml *dev, uint32_t irq)
|
||||
/* Create GSI link device */
|
||||
for (i = 0; i < PCI_NUM_PINS; i++) {
|
||||
uint32_t irqs = irq + i;
|
||||
Aml *dev_gsi = aml_device("GSI%d", i);
|
||||
Aml *dev_gsi = aml_device("L%.02X%X", bus_num, i);
|
||||
aml_append(dev_gsi, aml_name_decl("_HID", aml_string("PNP0C0F")));
|
||||
aml_append(dev_gsi, aml_name_decl("_UID", aml_int(i)));
|
||||
crs = aml_resource_template();
|
||||
@ -45,7 +46,7 @@ static void acpi_dsdt_add_pci_route_table(Aml *dev, uint32_t irq)
|
||||
aml_append(dev_gsi, aml_name_decl("_CRS", crs));
|
||||
method = aml_method("_SRS", 1, AML_NOTSERIALIZED);
|
||||
aml_append(dev_gsi, method);
|
||||
aml_append(dev, dev_gsi);
|
||||
aml_append(scope, dev_gsi);
|
||||
}
|
||||
}
|
||||
|
||||
@ -174,7 +175,7 @@ void acpi_dsdt_add_gpex(Aml *scope, struct GPEXConfig *cfg)
|
||||
aml_append(dev, aml_name_decl("_PXM", aml_int(numa_node)));
|
||||
}
|
||||
|
||||
acpi_dsdt_add_pci_route_table(dev, cfg->irq);
|
||||
acpi_dsdt_add_pci_route_table(dev, cfg->irq, scope, bus_num);
|
||||
|
||||
/*
|
||||
* Resources defined for PXBs are composed of the following parts:
|
||||
@ -205,7 +206,7 @@ void acpi_dsdt_add_gpex(Aml *scope, struct GPEXConfig *cfg)
|
||||
aml_append(dev, aml_name_decl("_STR", aml_unicode("PCIe 0 Device")));
|
||||
aml_append(dev, aml_name_decl("_CCA", aml_int(1)));
|
||||
|
||||
acpi_dsdt_add_pci_route_table(dev, cfg->irq);
|
||||
acpi_dsdt_add_pci_route_table(dev, cfg->irq, scope, 0);
|
||||
|
||||
method = aml_method("_CBA", 0, AML_NOTSERIALIZED);
|
||||
aml_append(method, aml_return(aml_int(cfg->ecam.base)));
|
||||
|
68
hw/pci/pci.c
68
hw/pci/pci.c
@ -85,6 +85,7 @@ static Property pci_props[] = {
|
||||
QEMU_PCIE_ERR_UNC_MASK_BITNR, true),
|
||||
DEFINE_PROP_BIT("x-pcie-ari-nextfn-1", PCIDevice, cap_present,
|
||||
QEMU_PCIE_ARI_NEXTFN_1_BITNR, false),
|
||||
DEFINE_PROP_STRING("sriov-pf", PCIDevice, sriov_pf),
|
||||
DEFINE_PROP_END_OF_LIST()
|
||||
};
|
||||
|
||||
@ -959,13 +960,8 @@ static void pci_init_multifunction(PCIBus *bus, PCIDevice *dev, Error **errp)
|
||||
dev->config[PCI_HEADER_TYPE] |= PCI_HEADER_TYPE_MULTI_FUNCTION;
|
||||
}
|
||||
|
||||
/*
|
||||
* With SR/IOV and ARI, a device at function 0 need not be a multifunction
|
||||
* device, as it may just be a VF that ended up with function 0 in
|
||||
* the legacy PCI interpretation. Avoid failing in such cases:
|
||||
*/
|
||||
if (pci_is_vf(dev) &&
|
||||
dev->exp.sriov_vf.pf->cap_present & QEMU_PCI_CAP_MULTIFUNCTION) {
|
||||
/* SR/IOV is not handled here. */
|
||||
if (pci_is_vf(dev)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -998,7 +994,8 @@ static void pci_init_multifunction(PCIBus *bus, PCIDevice *dev, Error **errp)
|
||||
}
|
||||
/* function 0 indicates single function, so function > 0 must be NULL */
|
||||
for (func = 1; func < PCI_FUNC_MAX; ++func) {
|
||||
if (bus->devices[PCI_DEVFN(slot, func)]) {
|
||||
PCIDevice *device = bus->devices[PCI_DEVFN(slot, func)];
|
||||
if (device && !pci_is_vf(device)) {
|
||||
error_setg(errp, "PCI: %x.0 indicates single function, "
|
||||
"but %x.%x is already populated.",
|
||||
slot, slot, func);
|
||||
@ -1283,6 +1280,7 @@ static void pci_qdev_unrealize(DeviceState *dev)
|
||||
|
||||
pci_unregister_io_regions(pci_dev);
|
||||
pci_del_option_rom(pci_dev);
|
||||
pcie_sriov_unregister_device(pci_dev);
|
||||
|
||||
if (pc->exit) {
|
||||
pc->exit(pci_dev);
|
||||
@ -1314,7 +1312,6 @@ void pci_register_bar(PCIDevice *pci_dev, int region_num,
|
||||
pcibus_t size = memory_region_size(memory);
|
||||
uint8_t hdr_type;
|
||||
|
||||
assert(!pci_is_vf(pci_dev)); /* VFs must use pcie_sriov_vf_register_bar */
|
||||
assert(region_num >= 0);
|
||||
assert(region_num < PCI_NUM_REGIONS);
|
||||
assert(is_power_of_2(size));
|
||||
@ -1325,7 +1322,6 @@ void pci_register_bar(PCIDevice *pci_dev, int region_num,
|
||||
assert(hdr_type != PCI_HEADER_TYPE_BRIDGE || region_num < 2);
|
||||
|
||||
r = &pci_dev->io_regions[region_num];
|
||||
r->addr = PCI_BAR_UNMAPPED;
|
||||
r->size = size;
|
||||
r->type = type;
|
||||
r->memory = memory;
|
||||
@ -1333,22 +1329,35 @@ void pci_register_bar(PCIDevice *pci_dev, int region_num,
|
||||
? pci_get_bus(pci_dev)->address_space_io
|
||||
: pci_get_bus(pci_dev)->address_space_mem;
|
||||
|
||||
wmask = ~(size - 1);
|
||||
if (region_num == PCI_ROM_SLOT) {
|
||||
/* ROM enable bit is writable */
|
||||
wmask |= PCI_ROM_ADDRESS_ENABLE;
|
||||
}
|
||||
if (pci_is_vf(pci_dev)) {
|
||||
PCIDevice *pf = pci_dev->exp.sriov_vf.pf;
|
||||
assert(!pf || type == pf->exp.sriov_pf.vf_bar_type[region_num]);
|
||||
|
||||
addr = pci_bar(pci_dev, region_num);
|
||||
pci_set_long(pci_dev->config + addr, type);
|
||||
|
||||
if (!(r->type & PCI_BASE_ADDRESS_SPACE_IO) &&
|
||||
r->type & PCI_BASE_ADDRESS_MEM_TYPE_64) {
|
||||
pci_set_quad(pci_dev->wmask + addr, wmask);
|
||||
pci_set_quad(pci_dev->cmask + addr, ~0ULL);
|
||||
r->addr = pci_bar_address(pci_dev, region_num, r->type, r->size);
|
||||
if (r->addr != PCI_BAR_UNMAPPED) {
|
||||
memory_region_add_subregion_overlap(r->address_space,
|
||||
r->addr, r->memory, 1);
|
||||
}
|
||||
} else {
|
||||
pci_set_long(pci_dev->wmask + addr, wmask & 0xffffffff);
|
||||
pci_set_long(pci_dev->cmask + addr, 0xffffffff);
|
||||
r->addr = PCI_BAR_UNMAPPED;
|
||||
|
||||
wmask = ~(size - 1);
|
||||
if (region_num == PCI_ROM_SLOT) {
|
||||
/* ROM enable bit is writable */
|
||||
wmask |= PCI_ROM_ADDRESS_ENABLE;
|
||||
}
|
||||
|
||||
addr = pci_bar(pci_dev, region_num);
|
||||
pci_set_long(pci_dev->config + addr, type);
|
||||
|
||||
if (!(r->type & PCI_BASE_ADDRESS_SPACE_IO) &&
|
||||
r->type & PCI_BASE_ADDRESS_MEM_TYPE_64) {
|
||||
pci_set_quad(pci_dev->wmask + addr, wmask);
|
||||
pci_set_quad(pci_dev->cmask + addr, ~0ULL);
|
||||
} else {
|
||||
pci_set_long(pci_dev->wmask + addr, wmask & 0xffffffff);
|
||||
pci_set_long(pci_dev->cmask + addr, 0xffffffff);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1437,7 +1446,11 @@ static pcibus_t pci_config_get_bar_addr(PCIDevice *d, int reg,
|
||||
pci_get_word(pf->config + sriov_cap + PCI_SRIOV_VF_OFFSET);
|
||||
uint16_t vf_stride =
|
||||
pci_get_word(pf->config + sriov_cap + PCI_SRIOV_VF_STRIDE);
|
||||
uint32_t vf_num = (d->devfn - (pf->devfn + vf_offset)) / vf_stride;
|
||||
uint32_t vf_num = d->devfn - (pf->devfn + vf_offset);
|
||||
|
||||
if (vf_num) {
|
||||
vf_num /= vf_stride;
|
||||
}
|
||||
|
||||
if (type & PCI_BASE_ADDRESS_MEM_TYPE_64) {
|
||||
new_addr = pci_get_quad(pf->config + bar);
|
||||
@ -2105,6 +2118,11 @@ static void pci_qdev_realize(DeviceState *qdev, Error **errp)
|
||||
}
|
||||
}
|
||||
|
||||
if (!pcie_sriov_register_device(pci_dev, errp)) {
|
||||
pci_qdev_unrealize(DEVICE(pci_dev));
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* A PCIe Downstream Port that do not have ARI Forwarding enabled must
|
||||
* associate only Device 0 with the device attached to the bus
|
||||
|
@ -20,6 +20,8 @@
|
||||
#include "qapi/error.h"
|
||||
#include "trace.h"
|
||||
|
||||
static GHashTable *pfs;
|
||||
|
||||
static void unparent_vfs(PCIDevice *dev, uint16_t total_vfs)
|
||||
{
|
||||
for (uint16_t i = 0; i < total_vfs; i++) {
|
||||
@ -31,17 +33,62 @@ static void unparent_vfs(PCIDevice *dev, uint16_t total_vfs)
|
||||
dev->exp.sriov_pf.vf = NULL;
|
||||
}
|
||||
|
||||
bool pcie_sriov_pf_init(PCIDevice *dev, uint16_t offset,
|
||||
const char *vfname, uint16_t vf_dev_id,
|
||||
uint16_t init_vfs, uint16_t total_vfs,
|
||||
uint16_t vf_offset, uint16_t vf_stride,
|
||||
Error **errp)
|
||||
static void clear_ctrl_vfe(PCIDevice *dev)
|
||||
{
|
||||
uint8_t *ctrl = dev->config + dev->exp.sriov_cap + PCI_SRIOV_CTRL;
|
||||
pci_set_word(ctrl, pci_get_word(ctrl) & ~PCI_SRIOV_CTRL_VFE);
|
||||
}
|
||||
|
||||
static void register_vfs(PCIDevice *dev)
|
||||
{
|
||||
uint16_t num_vfs;
|
||||
uint16_t i;
|
||||
uint16_t sriov_cap = dev->exp.sriov_cap;
|
||||
|
||||
assert(sriov_cap > 0);
|
||||
num_vfs = pci_get_word(dev->config + sriov_cap + PCI_SRIOV_NUM_VF);
|
||||
if (num_vfs > pci_get_word(dev->config + sriov_cap + PCI_SRIOV_TOTAL_VF)) {
|
||||
clear_ctrl_vfe(dev);
|
||||
return;
|
||||
}
|
||||
|
||||
trace_sriov_register_vfs(dev->name, PCI_SLOT(dev->devfn),
|
||||
PCI_FUNC(dev->devfn), num_vfs);
|
||||
for (i = 0; i < num_vfs; i++) {
|
||||
pci_set_enabled(dev->exp.sriov_pf.vf[i], true);
|
||||
}
|
||||
}
|
||||
|
||||
static void unregister_vfs(PCIDevice *dev)
|
||||
{
|
||||
uint16_t i;
|
||||
uint8_t *cfg = dev->config + dev->exp.sriov_cap;
|
||||
|
||||
trace_sriov_unregister_vfs(dev->name, PCI_SLOT(dev->devfn),
|
||||
PCI_FUNC(dev->devfn));
|
||||
for (i = 0; i < pci_get_word(cfg + PCI_SRIOV_TOTAL_VF); i++) {
|
||||
pci_set_enabled(dev->exp.sriov_pf.vf[i], false);
|
||||
}
|
||||
}
|
||||
|
||||
static bool pcie_sriov_pf_init_common(PCIDevice *dev, uint16_t offset,
|
||||
uint16_t vf_dev_id, uint16_t init_vfs,
|
||||
uint16_t total_vfs, uint16_t vf_offset,
|
||||
uint16_t vf_stride, Error **errp)
|
||||
{
|
||||
BusState *bus = qdev_get_parent_bus(&dev->qdev);
|
||||
int32_t devfn = dev->devfn + vf_offset;
|
||||
uint8_t *cfg = dev->config + offset;
|
||||
uint8_t *wmask;
|
||||
|
||||
if (!pci_is_express(dev)) {
|
||||
error_setg(errp, "PCI Express is required for SR-IOV PF");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pci_is_vf(dev)) {
|
||||
error_setg(errp, "a device cannot be both an SR-IOV PF and a VF");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (total_vfs) {
|
||||
uint16_t ari_cap = pcie_find_capability(dev, PCI_EXT_CAP_ID_ARI);
|
||||
uint16_t first_vf_devfn = dev->devfn + vf_offset;
|
||||
@ -90,6 +137,28 @@ bool pcie_sriov_pf_init(PCIDevice *dev, uint16_t offset,
|
||||
|
||||
qdev_prop_set_bit(&dev->qdev, "multifunction", true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool pcie_sriov_pf_init(PCIDevice *dev, uint16_t offset,
|
||||
const char *vfname, uint16_t vf_dev_id,
|
||||
uint16_t init_vfs, uint16_t total_vfs,
|
||||
uint16_t vf_offset, uint16_t vf_stride,
|
||||
Error **errp)
|
||||
{
|
||||
BusState *bus = qdev_get_parent_bus(&dev->qdev);
|
||||
int32_t devfn = dev->devfn + vf_offset;
|
||||
|
||||
if (pfs && g_hash_table_contains(pfs, dev->qdev.id)) {
|
||||
error_setg(errp, "attaching user-created SR-IOV VF unsupported");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!pcie_sriov_pf_init_common(dev, offset, vf_dev_id, init_vfs,
|
||||
total_vfs, vf_offset, vf_stride, errp)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
dev->exp.sriov_pf.vf = g_new(PCIDevice *, total_vfs);
|
||||
|
||||
for (uint16_t i = 0; i < total_vfs; i++) {
|
||||
@ -119,7 +188,24 @@ void pcie_sriov_pf_exit(PCIDevice *dev)
|
||||
{
|
||||
uint8_t *cfg = dev->config + dev->exp.sriov_cap;
|
||||
|
||||
unparent_vfs(dev, pci_get_word(cfg + PCI_SRIOV_TOTAL_VF));
|
||||
if (dev->exp.sriov_pf.vf_user_created) {
|
||||
uint16_t ven_id = pci_get_word(dev->config + PCI_VENDOR_ID);
|
||||
uint16_t total_vfs = pci_get_word(dev->config + PCI_SRIOV_TOTAL_VF);
|
||||
uint16_t vf_dev_id = pci_get_word(dev->config + PCI_SRIOV_VF_DID);
|
||||
|
||||
unregister_vfs(dev);
|
||||
|
||||
for (uint16_t i = 0; i < total_vfs; i++) {
|
||||
PCIDevice *vf = dev->exp.sriov_pf.vf[i];
|
||||
|
||||
vf->exp.sriov_vf.pf = NULL;
|
||||
|
||||
pci_config_set_vendor_id(vf->config, ven_id);
|
||||
pci_config_set_device_id(vf->config, vf_dev_id);
|
||||
}
|
||||
} else {
|
||||
unparent_vfs(dev, pci_get_word(cfg + PCI_SRIOV_TOTAL_VF));
|
||||
}
|
||||
}
|
||||
|
||||
void pcie_sriov_pf_init_vf_bar(PCIDevice *dev, int region_num,
|
||||
@ -152,74 +238,172 @@ void pcie_sriov_pf_init_vf_bar(PCIDevice *dev, int region_num,
|
||||
void pcie_sriov_vf_register_bar(PCIDevice *dev, int region_num,
|
||||
MemoryRegion *memory)
|
||||
{
|
||||
PCIIORegion *r;
|
||||
PCIBus *bus = pci_get_bus(dev);
|
||||
uint8_t type;
|
||||
pcibus_t size = memory_region_size(memory);
|
||||
|
||||
assert(pci_is_vf(dev)); /* PFs must use pci_register_bar */
|
||||
assert(region_num >= 0);
|
||||
assert(region_num < PCI_NUM_REGIONS);
|
||||
assert(dev->exp.sriov_vf.pf);
|
||||
type = dev->exp.sriov_vf.pf->exp.sriov_pf.vf_bar_type[region_num];
|
||||
|
||||
if (!is_power_of_2(size)) {
|
||||
error_report("%s: PCI region size must be a power"
|
||||
" of two - type=0x%x, size=0x%"FMT_PCIBUS,
|
||||
__func__, type, size);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
r = &dev->io_regions[region_num];
|
||||
r->memory = memory;
|
||||
r->address_space =
|
||||
type & PCI_BASE_ADDRESS_SPACE_IO
|
||||
? bus->address_space_io
|
||||
: bus->address_space_mem;
|
||||
r->size = size;
|
||||
r->type = type;
|
||||
|
||||
r->addr = pci_bar_address(dev, region_num, r->type, r->size);
|
||||
if (r->addr != PCI_BAR_UNMAPPED) {
|
||||
memory_region_add_subregion_overlap(r->address_space,
|
||||
r->addr, r->memory, 1);
|
||||
}
|
||||
return pci_register_bar(dev, region_num, type, memory);
|
||||
}
|
||||
|
||||
static void clear_ctrl_vfe(PCIDevice *dev)
|
||||
static gint compare_vf_devfns(gconstpointer a, gconstpointer b)
|
||||
{
|
||||
uint8_t *ctrl = dev->config + dev->exp.sriov_cap + PCI_SRIOV_CTRL;
|
||||
pci_set_word(ctrl, pci_get_word(ctrl) & ~PCI_SRIOV_CTRL_VFE);
|
||||
return (*(PCIDevice **)a)->devfn - (*(PCIDevice **)b)->devfn;
|
||||
}
|
||||
|
||||
static void register_vfs(PCIDevice *dev)
|
||||
int16_t pcie_sriov_pf_init_from_user_created_vfs(PCIDevice *dev,
|
||||
uint16_t offset,
|
||||
Error **errp)
|
||||
{
|
||||
uint16_t num_vfs;
|
||||
GPtrArray *pf;
|
||||
PCIDevice **vfs;
|
||||
BusState *bus = qdev_get_parent_bus(DEVICE(dev));
|
||||
uint16_t ven_id = pci_get_word(dev->config + PCI_VENDOR_ID);
|
||||
uint16_t vf_dev_id;
|
||||
uint16_t vf_offset;
|
||||
uint16_t vf_stride;
|
||||
uint16_t i;
|
||||
uint16_t sriov_cap = dev->exp.sriov_cap;
|
||||
|
||||
assert(sriov_cap > 0);
|
||||
num_vfs = pci_get_word(dev->config + sriov_cap + PCI_SRIOV_NUM_VF);
|
||||
if (num_vfs > pci_get_word(dev->config + sriov_cap + PCI_SRIOV_TOTAL_VF)) {
|
||||
clear_ctrl_vfe(dev);
|
||||
return;
|
||||
if (!pfs || !dev->qdev.id) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
trace_sriov_register_vfs(dev->name, PCI_SLOT(dev->devfn),
|
||||
PCI_FUNC(dev->devfn), num_vfs);
|
||||
for (i = 0; i < num_vfs; i++) {
|
||||
pci_set_enabled(dev->exp.sriov_pf.vf[i], true);
|
||||
pf = g_hash_table_lookup(pfs, dev->qdev.id);
|
||||
if (!pf) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (pf->len > UINT16_MAX) {
|
||||
error_setg(errp, "too many VFs");
|
||||
return -1;
|
||||
}
|
||||
|
||||
g_ptr_array_sort(pf, compare_vf_devfns);
|
||||
vfs = (void *)pf->pdata;
|
||||
|
||||
if (vfs[0]->devfn <= dev->devfn) {
|
||||
error_setg(errp, "a VF function number is less than the PF function number");
|
||||
return -1;
|
||||
}
|
||||
|
||||
vf_dev_id = pci_get_word(vfs[0]->config + PCI_DEVICE_ID);
|
||||
vf_offset = vfs[0]->devfn - dev->devfn;
|
||||
vf_stride = pf->len < 2 ? 0 : vfs[1]->devfn - vfs[0]->devfn;
|
||||
|
||||
for (i = 0; i < pf->len; i++) {
|
||||
if (bus != qdev_get_parent_bus(&vfs[i]->qdev)) {
|
||||
error_setg(errp, "SR-IOV VF parent bus mismatches with PF");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ven_id != pci_get_word(vfs[i]->config + PCI_VENDOR_ID)) {
|
||||
error_setg(errp, "SR-IOV VF vendor ID mismatches with PF");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (vf_dev_id != pci_get_word(vfs[i]->config + PCI_DEVICE_ID)) {
|
||||
error_setg(errp, "inconsistent SR-IOV VF device IDs");
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (size_t j = 0; j < PCI_NUM_REGIONS; j++) {
|
||||
if (vfs[i]->io_regions[j].size != vfs[0]->io_regions[j].size ||
|
||||
vfs[i]->io_regions[j].type != vfs[0]->io_regions[j].type) {
|
||||
error_setg(errp, "inconsistent SR-IOV BARs");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (vfs[i]->devfn - vfs[0]->devfn != vf_stride * i) {
|
||||
error_setg(errp, "inconsistent SR-IOV stride");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!pcie_sriov_pf_init_common(dev, offset, vf_dev_id, pf->len,
|
||||
pf->len, vf_offset, vf_stride, errp)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (i = 0; i < pf->len; i++) {
|
||||
vfs[i]->exp.sriov_vf.pf = dev;
|
||||
vfs[i]->exp.sriov_vf.vf_number = i;
|
||||
|
||||
/* set vid/did according to sr/iov spec - they are not used */
|
||||
pci_config_set_vendor_id(vfs[i]->config, 0xffff);
|
||||
pci_config_set_device_id(vfs[i]->config, 0xffff);
|
||||
}
|
||||
|
||||
dev->exp.sriov_pf.vf = vfs;
|
||||
dev->exp.sriov_pf.vf_user_created = true;
|
||||
|
||||
for (i = 0; i < PCI_NUM_REGIONS; i++) {
|
||||
PCIIORegion *region = &vfs[0]->io_regions[i];
|
||||
|
||||
if (region->size) {
|
||||
pcie_sriov_pf_init_vf_bar(dev, i, region->type, region->size);
|
||||
}
|
||||
}
|
||||
|
||||
return PCI_EXT_CAP_SRIOV_SIZEOF;
|
||||
}
|
||||
|
||||
static void unregister_vfs(PCIDevice *dev)
|
||||
bool pcie_sriov_register_device(PCIDevice *dev, Error **errp)
|
||||
{
|
||||
uint16_t i;
|
||||
uint8_t *cfg = dev->config + dev->exp.sriov_cap;
|
||||
if (!dev->exp.sriov_pf.vf && dev->qdev.id &&
|
||||
pfs && g_hash_table_contains(pfs, dev->qdev.id)) {
|
||||
error_setg(errp, "attaching user-created SR-IOV VF unsupported");
|
||||
return false;
|
||||
}
|
||||
|
||||
trace_sriov_unregister_vfs(dev->name, PCI_SLOT(dev->devfn),
|
||||
PCI_FUNC(dev->devfn));
|
||||
for (i = 0; i < pci_get_word(cfg + PCI_SRIOV_TOTAL_VF); i++) {
|
||||
pci_set_enabled(dev->exp.sriov_pf.vf[i], false);
|
||||
if (dev->sriov_pf) {
|
||||
PCIDevice *pci_pf;
|
||||
GPtrArray *pf;
|
||||
|
||||
if (!PCI_DEVICE_GET_CLASS(dev)->sriov_vf_user_creatable) {
|
||||
error_setg(errp, "user cannot create SR-IOV VF with this device type");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!pci_is_express(dev)) {
|
||||
error_setg(errp, "PCI Express is required for SR-IOV VF");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!pci_qdev_find_device(dev->sriov_pf, &pci_pf)) {
|
||||
error_setg(errp, "PCI device specified as SR-IOV PF already exists");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!pfs) {
|
||||
pfs = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
|
||||
}
|
||||
|
||||
pf = g_hash_table_lookup(pfs, dev->sriov_pf);
|
||||
if (!pf) {
|
||||
pf = g_ptr_array_new();
|
||||
g_hash_table_insert(pfs, g_strdup(dev->sriov_pf), pf);
|
||||
}
|
||||
|
||||
g_ptr_array_add(pf, dev);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void pcie_sriov_unregister_device(PCIDevice *dev)
|
||||
{
|
||||
if (dev->sriov_pf && pfs) {
|
||||
GPtrArray *pf = g_hash_table_lookup(pfs, dev->sriov_pf);
|
||||
|
||||
if (pf) {
|
||||
g_ptr_array_remove_fast(pf, dev);
|
||||
|
||||
if (!pf->len) {
|
||||
g_hash_table_remove(pfs, dev->sriov_pf);
|
||||
g_ptr_array_free(pf, FALSE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -306,7 +490,7 @@ void pcie_sriov_pf_add_sup_pgsize(PCIDevice *dev, uint16_t opt_sup_pgsize)
|
||||
|
||||
uint16_t pcie_sriov_vf_number(PCIDevice *dev)
|
||||
{
|
||||
assert(pci_is_vf(dev));
|
||||
assert(dev->exp.sriov_vf.pf);
|
||||
return dev->exp.sriov_vf.vf_number;
|
||||
}
|
||||
|
||||
|
@ -141,12 +141,36 @@ static void acpi_dsdt_add_cpus(Aml *scope, RISCVVirtState *s)
|
||||
}
|
||||
}
|
||||
|
||||
static void acpi_dsdt_add_plic_aplic(Aml *scope, uint8_t socket_count,
|
||||
uint64_t mmio_base, uint64_t mmio_size,
|
||||
const char *hid)
|
||||
{
|
||||
uint64_t plic_aplic_addr;
|
||||
uint32_t gsi_base;
|
||||
uint8_t socket;
|
||||
|
||||
for (socket = 0; socket < socket_count; socket++) {
|
||||
plic_aplic_addr = mmio_base + mmio_size * socket;
|
||||
gsi_base = VIRT_IRQCHIP_NUM_SOURCES * socket;
|
||||
Aml *dev = aml_device("IC%.02X", socket);
|
||||
aml_append(dev, aml_name_decl("_HID", aml_string("%s", hid)));
|
||||
aml_append(dev, aml_name_decl("_UID", aml_int(socket)));
|
||||
aml_append(dev, aml_name_decl("_GSB", aml_int(gsi_base)));
|
||||
|
||||
Aml *crs = aml_resource_template();
|
||||
aml_append(crs, aml_memory32_fixed(plic_aplic_addr, mmio_size,
|
||||
AML_READ_WRITE));
|
||||
aml_append(dev, aml_name_decl("_CRS", crs));
|
||||
aml_append(scope, dev);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
acpi_dsdt_add_uart(Aml *scope, const MemMapEntry *uart_memmap,
|
||||
uint32_t uart_irq)
|
||||
{
|
||||
Aml *dev = aml_device("COM0");
|
||||
aml_append(dev, aml_name_decl("_HID", aml_string("PNP0501")));
|
||||
aml_append(dev, aml_name_decl("_HID", aml_string("RSCV0003")));
|
||||
aml_append(dev, aml_name_decl("_UID", aml_int(0)));
|
||||
|
||||
Aml *crs = aml_resource_template();
|
||||
@ -411,6 +435,14 @@ static void build_dsdt(GArray *table_data,
|
||||
|
||||
socket_count = riscv_socket_count(ms);
|
||||
|
||||
if (s->aia_type == VIRT_AIA_TYPE_NONE) {
|
||||
acpi_dsdt_add_plic_aplic(scope, socket_count, memmap[VIRT_PLIC].base,
|
||||
memmap[VIRT_PLIC].size, "RSCV0001");
|
||||
} else {
|
||||
acpi_dsdt_add_plic_aplic(scope, socket_count, memmap[VIRT_APLIC_S].base,
|
||||
memmap[VIRT_APLIC_S].size, "RSCV0002");
|
||||
}
|
||||
|
||||
acpi_dsdt_add_uart(scope, &memmap[VIRT_UART0], UART0_IRQ);
|
||||
|
||||
if (socket_count == 1) {
|
||||
|
@ -38,6 +38,7 @@ static const int kernel_feature_bits[] = {
|
||||
VIRTIO_RING_F_EVENT_IDX,
|
||||
VIRTIO_SCSI_F_HOTPLUG,
|
||||
VIRTIO_F_RING_RESET,
|
||||
VIRTIO_F_IN_ORDER,
|
||||
VIRTIO_F_NOTIFICATION_DATA,
|
||||
VHOST_INVALID_FEATURE_BIT
|
||||
};
|
||||
|
@ -36,6 +36,7 @@ static const int user_feature_bits[] = {
|
||||
VIRTIO_RING_F_EVENT_IDX,
|
||||
VIRTIO_SCSI_F_HOTPLUG,
|
||||
VIRTIO_F_RING_RESET,
|
||||
VIRTIO_F_IN_ORDER,
|
||||
VIRTIO_F_NOTIFICATION_DATA,
|
||||
VHOST_INVALID_FEATURE_BIT
|
||||
};
|
||||
|
@ -1093,6 +1093,7 @@ static bool smbios_get_tables_ep(MachineState *ms,
|
||||
Error **errp)
|
||||
{
|
||||
unsigned i, dimm_cnt, offset;
|
||||
MachineClass *mc = MACHINE_GET_CLASS(ms);
|
||||
ERRP_GUARD();
|
||||
|
||||
assert(ep_type == SMBIOS_ENTRY_POINT_TYPE_32 ||
|
||||
@ -1123,12 +1124,12 @@ static bool smbios_get_tables_ep(MachineState *ms,
|
||||
smbios_build_type_9_table(errp);
|
||||
smbios_build_type_11_table();
|
||||
|
||||
#define MAX_DIMM_SZ (16 * GiB)
|
||||
#define GET_DIMM_SZ ((i < dimm_cnt - 1) ? MAX_DIMM_SZ \
|
||||
: ((current_machine->ram_size - 1) % MAX_DIMM_SZ) + 1)
|
||||
#define GET_DIMM_SZ ((i < dimm_cnt - 1) ? mc->smbios_memory_device_size \
|
||||
: ((current_machine->ram_size - 1) % mc->smbios_memory_device_size) + 1)
|
||||
|
||||
dimm_cnt = QEMU_ALIGN_UP(current_machine->ram_size, MAX_DIMM_SZ) /
|
||||
MAX_DIMM_SZ;
|
||||
dimm_cnt = QEMU_ALIGN_UP(current_machine->ram_size,
|
||||
mc->smbios_memory_device_size) /
|
||||
mc->smbios_memory_device_size;
|
||||
|
||||
/*
|
||||
* The offset determines if we need to keep additional space between
|
||||
|
@ -599,7 +599,7 @@ static void vfio_listener_region_add(MemoryListener *listener,
|
||||
IOMMUMemoryRegion *iommu_mr = IOMMU_MEMORY_REGION(section->mr);
|
||||
int iommu_idx;
|
||||
|
||||
trace_vfio_listener_region_add_iommu(iova, end);
|
||||
trace_vfio_listener_region_add_iommu(section->mr->name, iova, end);
|
||||
/*
|
||||
* FIXME: For VFIO iommu types which have KVM acceleration to
|
||||
* avoid bouncing all map/unmaps through qemu this way, this
|
||||
@ -725,6 +725,7 @@ static void vfio_listener_region_del(MemoryListener *listener,
|
||||
if (memory_region_is_iommu(section->mr)) {
|
||||
VFIOGuestIOMMU *giommu;
|
||||
|
||||
trace_vfio_listener_region_del_iommu(section->mr->name);
|
||||
QLIST_FOREACH(giommu, &bcontainer->giommu_list, giommu_next) {
|
||||
if (MEMORY_REGION(giommu->iommu_mr) == section->mr &&
|
||||
giommu->n.start == section->offset_within_region) {
|
||||
|
@ -95,7 +95,8 @@ vfio_region_read(char *name, int index, uint64_t addr, unsigned size, uint64_t d
|
||||
vfio_iommu_map_notify(const char *op, uint64_t iova_start, uint64_t iova_end) "iommu %s @ 0x%"PRIx64" - 0x%"PRIx64
|
||||
vfio_listener_region_skip(const char *name, uint64_t start, uint64_t end) "SKIPPING %s 0x%"PRIx64" - 0x%"PRIx64
|
||||
vfio_spapr_group_attach(int groupfd, int tablefd) "Attached groupfd %d to liobn fd %d"
|
||||
vfio_listener_region_add_iommu(uint64_t start, uint64_t end) "region_add [iommu] 0x%"PRIx64" - 0x%"PRIx64
|
||||
vfio_listener_region_add_iommu(const char* name, uint64_t start, uint64_t end) "region_add [iommu] %s 0x%"PRIx64" - 0x%"PRIx64
|
||||
vfio_listener_region_del_iommu(const char *name) "region_del [iommu] %s"
|
||||
vfio_listener_region_add_ram(uint64_t iova_start, uint64_t iova_end, void *vaddr) "region_add [ram] 0x%"PRIx64" - 0x%"PRIx64" [%p]"
|
||||
vfio_known_safe_misalignment(const char *name, uint64_t iova, uint64_t offset_within_region, uintptr_t page_size) "Region \"%s\" iova=0x%"PRIx64" offset_within_region=0x%"PRIx64" qemu_real_host_page_size=0x%"PRIxPTR
|
||||
vfio_listener_region_add_no_dma_map(const char *name, uint64_t iova, uint64_t size, uint64_t page_size) "Region \"%s\" 0x%"PRIx64" size=0x%"PRIx64" is not aligned to 0x%"PRIx64" and cannot be mapped for DMA"
|
||||
|
@ -116,6 +116,7 @@ virtio_iommu_get_config(uint64_t page_size_mask, uint64_t start, uint64_t end, u
|
||||
virtio_iommu_set_config(uint8_t bypass) "bypass=0x%x"
|
||||
virtio_iommu_attach(uint32_t domain_id, uint32_t ep_id) "domain=%d endpoint=%d"
|
||||
virtio_iommu_detach(uint32_t domain_id, uint32_t ep_id) "domain=%d endpoint=%d"
|
||||
virtio_iommu_detach_endpoint_from_domain(uint32_t domain_id, uint32_t ep_id) "domain=%d endpoint=%d"
|
||||
virtio_iommu_map(uint32_t domain_id, uint64_t virt_start, uint64_t virt_end, uint64_t phys_start, uint32_t flags) "domain=%d virt_start=0x%"PRIx64" virt_end=0x%"PRIx64 " phys_start=0x%"PRIx64" flags=%d"
|
||||
virtio_iommu_unmap(uint32_t domain_id, uint64_t virt_start, uint64_t virt_end) "domain=%d virt_start=0x%"PRIx64" virt_end=0x%"PRIx64
|
||||
virtio_iommu_unmap_done(uint32_t domain_id, uint64_t virt_start, uint64_t virt_end) "domain=%d virt_start=0x%"PRIx64" virt_end=0x%"PRIx64
|
||||
|
@ -33,6 +33,7 @@ static const int user_feature_bits[] = {
|
||||
VIRTIO_F_RING_PACKED,
|
||||
VIRTIO_F_IOMMU_PLATFORM,
|
||||
VIRTIO_F_RING_RESET,
|
||||
VIRTIO_F_IN_ORDER,
|
||||
VIRTIO_F_NOTIFICATION_DATA,
|
||||
VHOST_INVALID_FEATURE_BIT
|
||||
};
|
||||
|
@ -21,6 +21,7 @@ static const int user_feature_bits[] = {
|
||||
VIRTIO_RING_F_INDIRECT_DESC,
|
||||
VIRTIO_RING_F_EVENT_IDX,
|
||||
VIRTIO_F_NOTIFY_ON_EMPTY,
|
||||
VIRTIO_F_IN_ORDER,
|
||||
VIRTIO_F_NOTIFICATION_DATA,
|
||||
VHOST_INVALID_FEATURE_BIT
|
||||
};
|
||||
|
@ -205,6 +205,7 @@ virtio_crypto_create_asym_session(VirtIOCrypto *vcrypto,
|
||||
int queue_index;
|
||||
uint32_t algo, keytype, keylen;
|
||||
|
||||
sreq->info.op_code = opcode;
|
||||
algo = ldl_le_p(&sess_req->para.algo);
|
||||
keytype = ldl_le_p(&sess_req->para.keytype);
|
||||
keylen = ldl_le_p(&sess_req->para.keylen);
|
||||
@ -224,7 +225,6 @@ virtio_crypto_create_asym_session(VirtIOCrypto *vcrypto,
|
||||
iov_discard_front(&iov, &out_num, keylen);
|
||||
}
|
||||
|
||||
sreq->info.op_code = opcode;
|
||||
asym_info = &sreq->info.u.asym_sess_info;
|
||||
asym_info->algo = algo;
|
||||
asym_info->keytype = keytype;
|
||||
|
@ -308,6 +308,7 @@ static void virtio_iommu_detach_endpoint_from_domain(VirtIOIOMMUEndpoint *ep)
|
||||
if (!ep->domain) {
|
||||
return;
|
||||
}
|
||||
trace_virtio_iommu_detach_endpoint_from_domain(domain->id, ep->id);
|
||||
g_tree_foreach(domain->mappings, virtio_iommu_notify_unmap_cb,
|
||||
ep->iommu_mr);
|
||||
QLIST_REMOVE(ep, next);
|
||||
@ -467,26 +468,6 @@ static AddressSpace *virtio_iommu_find_add_as(PCIBus *bus, void *opaque,
|
||||
return &sdev->as;
|
||||
}
|
||||
|
||||
static void virtio_iommu_device_clear(VirtIOIOMMU *s, PCIBus *bus, int devfn)
|
||||
{
|
||||
IOMMUPciBus *sbus = g_hash_table_lookup(s->as_by_busptr, bus);
|
||||
IOMMUDevice *sdev;
|
||||
|
||||
if (!sbus) {
|
||||
return;
|
||||
}
|
||||
|
||||
sdev = sbus->pbdev[devfn];
|
||||
if (!sdev) {
|
||||
return;
|
||||
}
|
||||
|
||||
g_list_free_full(sdev->resv_regions, g_free);
|
||||
sdev->resv_regions = NULL;
|
||||
g_free(sdev);
|
||||
sbus->pbdev[devfn] = NULL;
|
||||
}
|
||||
|
||||
static gboolean hiod_equal(gconstpointer v1, gconstpointer v2)
|
||||
{
|
||||
const struct hiod_key *key1 = v1;
|
||||
@ -558,8 +539,6 @@ static int virtio_iommu_set_host_iova_ranges(VirtIOIOMMU *s, PCIBus *bus,
|
||||
{
|
||||
IOMMUPciBus *sbus = g_hash_table_lookup(s->as_by_busptr, bus);
|
||||
IOMMUDevice *sdev;
|
||||
GList *current_ranges;
|
||||
GList *l, *tmp, *new_ranges = NULL;
|
||||
int ret = -EINVAL;
|
||||
|
||||
if (!sbus) {
|
||||
@ -573,35 +552,10 @@ static int virtio_iommu_set_host_iova_ranges(VirtIOIOMMU *s, PCIBus *bus,
|
||||
return ret;
|
||||
}
|
||||
|
||||
current_ranges = sdev->host_resv_ranges;
|
||||
|
||||
g_assert(!sdev->probe_done);
|
||||
|
||||
/* check that each new resv region is included in an existing one */
|
||||
if (sdev->host_resv_ranges) {
|
||||
range_inverse_array(iova_ranges,
|
||||
&new_ranges,
|
||||
0, UINT64_MAX);
|
||||
|
||||
for (tmp = new_ranges; tmp; tmp = tmp->next) {
|
||||
Range *newr = (Range *)tmp->data;
|
||||
bool included = false;
|
||||
|
||||
for (l = current_ranges; l; l = l->next) {
|
||||
Range * r = (Range *)l->data;
|
||||
|
||||
if (range_contains_range(r, newr)) {
|
||||
included = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!included) {
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
/* all new reserved ranges are included in existing ones */
|
||||
ret = 0;
|
||||
goto out;
|
||||
error_setg(errp, "%s virtio-iommu does not support aliased BDF",
|
||||
__func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
range_inverse_array(iova_ranges,
|
||||
@ -610,14 +564,31 @@ static int virtio_iommu_set_host_iova_ranges(VirtIOIOMMU *s, PCIBus *bus,
|
||||
rebuild_resv_regions(sdev);
|
||||
|
||||
return 0;
|
||||
error:
|
||||
error_setg(errp, "%s Conflicting host reserved ranges set!",
|
||||
__func__);
|
||||
out:
|
||||
g_list_free_full(new_ranges, g_free);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void virtio_iommu_unset_host_iova_ranges(VirtIOIOMMU *s, PCIBus *bus,
|
||||
int devfn)
|
||||
{
|
||||
IOMMUPciBus *sbus = g_hash_table_lookup(s->as_by_busptr, bus);
|
||||
IOMMUDevice *sdev;
|
||||
|
||||
if (!sbus) {
|
||||
return;
|
||||
}
|
||||
|
||||
sdev = sbus->pbdev[devfn];
|
||||
if (!sdev) {
|
||||
return;
|
||||
}
|
||||
|
||||
g_list_free_full(g_steal_pointer(&sdev->host_resv_ranges), g_free);
|
||||
g_list_free_full(sdev->resv_regions, g_free);
|
||||
sdev->host_resv_ranges = NULL;
|
||||
sdev->resv_regions = NULL;
|
||||
add_prop_resv_regions(sdev);
|
||||
}
|
||||
|
||||
|
||||
static bool check_page_size_mask(VirtIOIOMMU *viommu, uint64_t new_mask,
|
||||
Error **errp)
|
||||
{
|
||||
@ -726,9 +697,10 @@ virtio_iommu_unset_iommu_device(PCIBus *bus, void *opaque, int devfn)
|
||||
if (!hiod) {
|
||||
return;
|
||||
}
|
||||
virtio_iommu_unset_host_iova_ranges(viommu, hiod->aliased_bus,
|
||||
hiod->aliased_devfn);
|
||||
|
||||
g_hash_table_remove(viommu->host_iommu_devices, &key);
|
||||
virtio_iommu_device_clear(viommu, bus, devfn);
|
||||
}
|
||||
|
||||
static const PCIIOMMUOps virtio_iommu_ops = {
|
||||
@ -815,6 +787,7 @@ static int virtio_iommu_detach(VirtIOIOMMU *s,
|
||||
if (QLIST_EMPTY(&domain->endpoint_list)) {
|
||||
g_tree_remove(s->domains, GUINT_TO_POINTER(domain->id));
|
||||
}
|
||||
g_tree_remove(s->endpoints, GUINT_TO_POINTER(ep_id));
|
||||
return VIRTIO_IOMMU_S_OK;
|
||||
}
|
||||
|
||||
@ -977,7 +950,6 @@ static int virtio_iommu_probe(VirtIOIOMMU *s,
|
||||
}
|
||||
buf += count;
|
||||
free -= count;
|
||||
sdev->probe_done = true;
|
||||
|
||||
return VIRTIO_IOMMU_S_OK;
|
||||
}
|
||||
|
@ -75,6 +75,7 @@ static void virtio_net_pci_class_init(ObjectClass *klass, void *data)
|
||||
k->device_id = PCI_DEVICE_ID_VIRTIO_NET;
|
||||
k->revision = VIRTIO_PCI_ABI_VERSION;
|
||||
k->class_id = PCI_CLASS_NETWORK_ETHERNET;
|
||||
k->sriov_vf_user_creatable = true;
|
||||
set_bit(DEVICE_CATEGORY_NETWORK, dc->categories);
|
||||
device_class_set_props(dc, virtio_net_properties);
|
||||
vpciklass->realize = virtio_net_pci_realize;
|
||||
|
@ -1955,6 +1955,7 @@ static void virtio_pci_device_plugged(DeviceState *d, Error **errp)
|
||||
uint8_t *config;
|
||||
uint32_t size;
|
||||
VirtIODevice *vdev = virtio_bus_get_device(bus);
|
||||
int16_t res;
|
||||
|
||||
/*
|
||||
* Virtio capabilities present without
|
||||
@ -2100,6 +2101,14 @@ static void virtio_pci_device_plugged(DeviceState *d, Error **errp)
|
||||
pci_register_bar(&proxy->pci_dev, proxy->legacy_io_bar_idx,
|
||||
PCI_BASE_ADDRESS_SPACE_IO, &proxy->bar);
|
||||
}
|
||||
|
||||
res = pcie_sriov_pf_init_from_user_created_vfs(&proxy->pci_dev,
|
||||
proxy->last_pcie_cap_offset,
|
||||
errp);
|
||||
if (res > 0) {
|
||||
proxy->last_pcie_cap_offset += res;
|
||||
virtio_add_feature(&vdev->host_features, VIRTIO_F_SR_IOV);
|
||||
}
|
||||
}
|
||||
|
||||
static void virtio_pci_device_unplugged(DeviceState *d)
|
||||
@ -2187,7 +2196,7 @@ static void virtio_pci_realize(PCIDevice *pci_dev, Error **errp)
|
||||
|
||||
if (pcie_port && pci_is_express(pci_dev)) {
|
||||
int pos;
|
||||
uint16_t last_pcie_cap_offset = PCI_CONFIG_SPACE_SIZE;
|
||||
proxy->last_pcie_cap_offset = PCI_CONFIG_SPACE_SIZE;
|
||||
|
||||
pos = pcie_endpoint_cap_init(pci_dev, 0);
|
||||
assert(pos > 0);
|
||||
@ -2207,9 +2216,9 @@ static void virtio_pci_realize(PCIDevice *pci_dev, Error **errp)
|
||||
pci_set_word(pci_dev->config + pos + PCI_PM_PMC, 0x3);
|
||||
|
||||
if (proxy->flags & VIRTIO_PCI_FLAG_AER) {
|
||||
pcie_aer_init(pci_dev, PCI_ERR_VER, last_pcie_cap_offset,
|
||||
pcie_aer_init(pci_dev, PCI_ERR_VER, proxy->last_pcie_cap_offset,
|
||||
PCI_ERR_SIZEOF, NULL);
|
||||
last_pcie_cap_offset += PCI_ERR_SIZEOF;
|
||||
proxy->last_pcie_cap_offset += PCI_ERR_SIZEOF;
|
||||
}
|
||||
|
||||
if (proxy->flags & VIRTIO_PCI_FLAG_INIT_DEVERR) {
|
||||
@ -2234,9 +2243,9 @@ static void virtio_pci_realize(PCIDevice *pci_dev, Error **errp)
|
||||
}
|
||||
|
||||
if (proxy->flags & VIRTIO_PCI_FLAG_ATS) {
|
||||
pcie_ats_init(pci_dev, last_pcie_cap_offset,
|
||||
pcie_ats_init(pci_dev, proxy->last_pcie_cap_offset,
|
||||
proxy->flags & VIRTIO_PCI_FLAG_ATS_PAGE_ALIGNED);
|
||||
last_pcie_cap_offset += PCI_EXT_CAP_ATS_SIZEOF;
|
||||
proxy->last_pcie_cap_offset += PCI_EXT_CAP_ATS_SIZEOF;
|
||||
}
|
||||
|
||||
if (proxy->flags & VIRTIO_PCI_FLAG_INIT_FLR) {
|
||||
@ -2263,6 +2272,7 @@ static void virtio_pci_exit(PCIDevice *pci_dev)
|
||||
bool pcie_port = pci_bus_is_express(pci_get_bus(pci_dev)) &&
|
||||
!pci_bus_is_root(pci_get_bus(pci_dev));
|
||||
|
||||
pcie_sriov_pf_exit(&proxy->pci_dev);
|
||||
msix_uninit_exclusive_bar(pci_dev);
|
||||
if (proxy->flags & VIRTIO_PCI_FLAG_AER && pcie_port &&
|
||||
pci_is_express(pci_dev)) {
|
||||
|
@ -872,6 +872,46 @@ static void virtqueue_packed_fill(VirtQueue *vq, const VirtQueueElement *elem,
|
||||
vq->used_elems[idx].ndescs = elem->ndescs;
|
||||
}
|
||||
|
||||
static void virtqueue_ordered_fill(VirtQueue *vq, const VirtQueueElement *elem,
|
||||
unsigned int len)
|
||||
{
|
||||
unsigned int i, steps, max_steps;
|
||||
|
||||
i = vq->used_idx % vq->vring.num;
|
||||
steps = 0;
|
||||
/*
|
||||
* We shouldn't need to increase 'i' by more than the distance
|
||||
* between used_idx and last_avail_idx.
|
||||
*/
|
||||
max_steps = (vq->last_avail_idx - vq->used_idx) % vq->vring.num;
|
||||
|
||||
/* Search for element in vq->used_elems */
|
||||
while (steps <= max_steps) {
|
||||
/* Found element, set length and mark as filled */
|
||||
if (vq->used_elems[i].index == elem->index) {
|
||||
vq->used_elems[i].len = len;
|
||||
vq->used_elems[i].in_order_filled = true;
|
||||
break;
|
||||
}
|
||||
|
||||
i += vq->used_elems[i].ndescs;
|
||||
steps += vq->used_elems[i].ndescs;
|
||||
|
||||
if (i >= vq->vring.num) {
|
||||
i -= vq->vring.num;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* We should be able to find a matching VirtQueueElement in
|
||||
* used_elems. If we don't, this is an error.
|
||||
*/
|
||||
if (steps >= max_steps) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "%s: %s cannot fill buffer id %u\n",
|
||||
__func__, vq->vdev->name, elem->index);
|
||||
}
|
||||
}
|
||||
|
||||
static void virtqueue_packed_fill_desc(VirtQueue *vq,
|
||||
const VirtQueueElement *elem,
|
||||
unsigned int idx,
|
||||
@ -922,7 +962,9 @@ void virtqueue_fill(VirtQueue *vq, const VirtQueueElement *elem,
|
||||
return;
|
||||
}
|
||||
|
||||
if (virtio_vdev_has_feature(vq->vdev, VIRTIO_F_RING_PACKED)) {
|
||||
if (virtio_vdev_has_feature(vq->vdev, VIRTIO_F_IN_ORDER)) {
|
||||
virtqueue_ordered_fill(vq, elem, len);
|
||||
} else if (virtio_vdev_has_feature(vq->vdev, VIRTIO_F_RING_PACKED)) {
|
||||
virtqueue_packed_fill(vq, elem, len, idx);
|
||||
} else {
|
||||
virtqueue_split_fill(vq, elem, len, idx);
|
||||
@ -981,6 +1023,73 @@ static void virtqueue_packed_flush(VirtQueue *vq, unsigned int count)
|
||||
}
|
||||
}
|
||||
|
||||
static void virtqueue_ordered_flush(VirtQueue *vq)
|
||||
{
|
||||
unsigned int i = vq->used_idx % vq->vring.num;
|
||||
unsigned int ndescs = 0;
|
||||
uint16_t old = vq->used_idx;
|
||||
uint16_t new;
|
||||
bool packed;
|
||||
VRingUsedElem uelem;
|
||||
|
||||
packed = virtio_vdev_has_feature(vq->vdev, VIRTIO_F_RING_PACKED);
|
||||
|
||||
if (packed) {
|
||||
if (unlikely(!vq->vring.desc)) {
|
||||
return;
|
||||
}
|
||||
} else if (unlikely(!vq->vring.used)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* First expected in-order element isn't ready, nothing to do */
|
||||
if (!vq->used_elems[i].in_order_filled) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Search for filled elements in-order */
|
||||
while (vq->used_elems[i].in_order_filled) {
|
||||
/*
|
||||
* First entry for packed VQs is written last so the guest
|
||||
* doesn't see invalid descriptors.
|
||||
*/
|
||||
if (packed && i != vq->used_idx) {
|
||||
virtqueue_packed_fill_desc(vq, &vq->used_elems[i], ndescs, false);
|
||||
} else if (!packed) {
|
||||
uelem.id = vq->used_elems[i].index;
|
||||
uelem.len = vq->used_elems[i].len;
|
||||
vring_used_write(vq, &uelem, i);
|
||||
}
|
||||
|
||||
vq->used_elems[i].in_order_filled = false;
|
||||
ndescs += vq->used_elems[i].ndescs;
|
||||
i += vq->used_elems[i].ndescs;
|
||||
if (i >= vq->vring.num) {
|
||||
i -= vq->vring.num;
|
||||
}
|
||||
}
|
||||
|
||||
if (packed) {
|
||||
virtqueue_packed_fill_desc(vq, &vq->used_elems[vq->used_idx], 0, true);
|
||||
vq->used_idx += ndescs;
|
||||
if (vq->used_idx >= vq->vring.num) {
|
||||
vq->used_idx -= vq->vring.num;
|
||||
vq->used_wrap_counter ^= 1;
|
||||
vq->signalled_used_valid = false;
|
||||
}
|
||||
} else {
|
||||
/* Make sure buffer is written before we update index. */
|
||||
smp_wmb();
|
||||
new = old + ndescs;
|
||||
vring_used_idx_set(vq, new);
|
||||
if (unlikely((int16_t)(new - vq->signalled_used) <
|
||||
(uint16_t)(new - old))) {
|
||||
vq->signalled_used_valid = false;
|
||||
}
|
||||
}
|
||||
vq->inuse -= ndescs;
|
||||
}
|
||||
|
||||
void virtqueue_flush(VirtQueue *vq, unsigned int count)
|
||||
{
|
||||
if (virtio_device_disabled(vq->vdev)) {
|
||||
@ -988,7 +1097,9 @@ void virtqueue_flush(VirtQueue *vq, unsigned int count)
|
||||
return;
|
||||
}
|
||||
|
||||
if (virtio_vdev_has_feature(vq->vdev, VIRTIO_F_RING_PACKED)) {
|
||||
if (virtio_vdev_has_feature(vq->vdev, VIRTIO_F_IN_ORDER)) {
|
||||
virtqueue_ordered_flush(vq);
|
||||
} else if (virtio_vdev_has_feature(vq->vdev, VIRTIO_F_RING_PACKED)) {
|
||||
virtqueue_packed_flush(vq, count);
|
||||
} else {
|
||||
virtqueue_split_flush(vq, count);
|
||||
@ -1505,7 +1616,7 @@ static void *virtqueue_alloc_element(size_t sz, unsigned out_num, unsigned in_nu
|
||||
|
||||
static void *virtqueue_split_pop(VirtQueue *vq, size_t sz)
|
||||
{
|
||||
unsigned int i, head, max;
|
||||
unsigned int i, head, max, idx;
|
||||
VRingMemoryRegionCaches *caches;
|
||||
MemoryRegionCache indirect_desc_cache;
|
||||
MemoryRegionCache *desc_cache;
|
||||
@ -1629,6 +1740,13 @@ static void *virtqueue_split_pop(VirtQueue *vq, size_t sz)
|
||||
elem->in_sg[i] = iov[out_num + i];
|
||||
}
|
||||
|
||||
if (virtio_vdev_has_feature(vdev, VIRTIO_F_IN_ORDER)) {
|
||||
idx = (vq->last_avail_idx - 1) % vq->vring.num;
|
||||
vq->used_elems[idx].index = elem->index;
|
||||
vq->used_elems[idx].len = elem->len;
|
||||
vq->used_elems[idx].ndescs = elem->ndescs;
|
||||
}
|
||||
|
||||
vq->inuse++;
|
||||
|
||||
trace_virtqueue_pop(vq, elem, elem->in_num, elem->out_num);
|
||||
@ -1762,6 +1880,13 @@ static void *virtqueue_packed_pop(VirtQueue *vq, size_t sz)
|
||||
|
||||
elem->index = id;
|
||||
elem->ndescs = (desc_cache == &indirect_desc_cache) ? 1 : elem_entries;
|
||||
|
||||
if (virtio_vdev_has_feature(vdev, VIRTIO_F_IN_ORDER)) {
|
||||
vq->used_elems[vq->last_avail_idx].index = elem->index;
|
||||
vq->used_elems[vq->last_avail_idx].len = elem->len;
|
||||
vq->used_elems[vq->last_avail_idx].ndescs = elem->ndescs;
|
||||
}
|
||||
|
||||
vq->last_avail_idx += elem->ndescs;
|
||||
vq->inuse += elem->ndescs;
|
||||
|
||||
|
@ -129,6 +129,14 @@ size_t qemu_ram_pagesize_largest(void);
|
||||
*/
|
||||
void cpu_address_space_init(CPUState *cpu, int asidx,
|
||||
const char *prefix, MemoryRegion *mr);
|
||||
/**
|
||||
* cpu_address_space_destroy:
|
||||
* @cpu: CPU for which address space needs to be destroyed
|
||||
* @asidx: integer index of this address space
|
||||
*
|
||||
* Note that with KVM only one address space is supported.
|
||||
*/
|
||||
void cpu_address_space_destroy(CPUState *cpu, int asidx);
|
||||
|
||||
void cpu_physical_memory_rw(hwaddr addr, void *buf,
|
||||
hwaddr len, bool is_write);
|
||||
|
@ -40,6 +40,12 @@ void gdb_register_coprocessor(CPUState *cpu,
|
||||
gdb_get_reg_cb get_reg, gdb_set_reg_cb set_reg,
|
||||
const GDBFeature *feature, int g_pos);
|
||||
|
||||
/**
|
||||
* gdb_unregister_coprocessor_all() - unregisters supplemental set of registers
|
||||
* @cpu - the CPU associated with registers
|
||||
*/
|
||||
void gdb_unregister_coprocessor_all(CPUState *cpu);
|
||||
|
||||
/**
|
||||
* gdbserver_start: start the gdb server
|
||||
* @port_or_device: connection spec for gdb
|
||||
|
@ -19,6 +19,8 @@
|
||||
#include "hw/boards.h"
|
||||
#include "hw/hotplug.h"
|
||||
|
||||
#define ACPI_CPU_HOTPLUG_REG_LEN 12
|
||||
|
||||
typedef struct AcpiCpuStatus {
|
||||
CPUState *cpu;
|
||||
uint64_t arch_id;
|
||||
@ -61,9 +63,10 @@ typedef void (*build_madt_cpu_fn)(int uid, const CPUArchIdList *apic_ids,
|
||||
GArray *entry, bool force_enabled);
|
||||
|
||||
void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts,
|
||||
build_madt_cpu_fn build_madt_cpu, hwaddr io_base,
|
||||
build_madt_cpu_fn build_madt_cpu, hwaddr base_addr,
|
||||
const char *res_root,
|
||||
const char *event_handler_method);
|
||||
const char *event_handler_method,
|
||||
AmlRegionSpace rs);
|
||||
|
||||
void acpi_cpu_ospm_status(CPUHotplugState *cpu_st, ACPIOSTInfoList ***list);
|
||||
|
||||
|
@ -62,6 +62,7 @@
|
||||
#include "hw/sysbus.h"
|
||||
#include "hw/acpi/memory_hotplug.h"
|
||||
#include "hw/acpi/ghes.h"
|
||||
#include "hw/acpi/cpu.h"
|
||||
#include "qom/object.h"
|
||||
|
||||
#define ACPI_POWER_BUTTON_DEVICE "PWRB"
|
||||
@ -86,6 +87,7 @@ OBJECT_DECLARE_SIMPLE_TYPE(AcpiGedState, ACPI_GED)
|
||||
#define GED_DEVICE "GED"
|
||||
#define AML_GED_EVT_REG "EREG"
|
||||
#define AML_GED_EVT_SEL "ESEL"
|
||||
#define AML_GED_EVT_CPU_SCAN_METHOD "\\_SB.GED.CSCN"
|
||||
|
||||
/*
|
||||
* Platforms need to specify the GED event bitmap
|
||||
@ -95,6 +97,7 @@ OBJECT_DECLARE_SIMPLE_TYPE(AcpiGedState, ACPI_GED)
|
||||
#define ACPI_GED_MEM_HOTPLUG_EVT 0x1
|
||||
#define ACPI_GED_PWR_DOWN_EVT 0x2
|
||||
#define ACPI_GED_NVDIMM_HOTPLUG_EVT 0x4
|
||||
#define ACPI_GED_CPU_HOTPLUG_EVT 0x8
|
||||
|
||||
typedef struct GEDState {
|
||||
MemoryRegion evt;
|
||||
@ -106,6 +109,8 @@ struct AcpiGedState {
|
||||
SysBusDevice parent_obj;
|
||||
MemHotplugState memhp_state;
|
||||
MemoryRegion container_memhp;
|
||||
CPUHotplugState cpuhp_state;
|
||||
MemoryRegion container_cpuhp;
|
||||
GEDState ged_state;
|
||||
uint32_t ged_event_bitmap;
|
||||
qemu_irq irq;
|
||||
|
@ -237,6 +237,9 @@ typedef struct {
|
||||
* purposes only.
|
||||
* Applies only to default memory backend, i.e., explicit memory backend
|
||||
* wasn't used.
|
||||
* @smbios_memory_device_size:
|
||||
* Default size of memory device,
|
||||
* SMBIOS 3.1.0 "7.18 Memory Device (Type 17)"
|
||||
*/
|
||||
struct MachineClass {
|
||||
/*< private >*/
|
||||
@ -304,6 +307,7 @@ struct MachineClass {
|
||||
const CPUArchIdList *(*possible_cpu_arch_ids)(MachineState *machine);
|
||||
int64_t (*get_default_cpu_node_id)(const MachineState *ms, int idx);
|
||||
ram_addr_t (*fixup_ram_size)(ram_addr_t size);
|
||||
uint64_t smbios_memory_device_size;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -496,6 +496,7 @@ struct CPUState {
|
||||
QSIMPLEQ_HEAD(, qemu_work_item) work_list;
|
||||
|
||||
struct CPUAddressSpace *cpu_ases;
|
||||
int cpu_ases_count;
|
||||
int num_ases;
|
||||
AddressSpace *as;
|
||||
MemoryRegion *memory;
|
||||
|
@ -181,6 +181,21 @@ typedef struct CXLCCI {
|
||||
uint64_t runtime;
|
||||
QEMUTimer *timer;
|
||||
} bg;
|
||||
|
||||
/* firmware update */
|
||||
struct {
|
||||
uint8_t active_slot;
|
||||
uint8_t staged_slot;
|
||||
bool slot[4];
|
||||
uint8_t curr_action;
|
||||
uint8_t curr_slot;
|
||||
/* handle partial transfers */
|
||||
bool transferring;
|
||||
size_t prev_offset;
|
||||
size_t prev_len;
|
||||
time_t last_partxfer;
|
||||
} fw;
|
||||
|
||||
size_t payload_max;
|
||||
/* Pointer to device hosting the CCI */
|
||||
DeviceState *d;
|
||||
@ -397,9 +412,14 @@ static inline void __toggle_media(CXLDeviceState *cxl_dstate, int val)
|
||||
#define cxl_dev_enable_media(cxlds) \
|
||||
do { __toggle_media((cxlds), 0x1); } while (0)
|
||||
|
||||
static inline bool sanitize_running(CXLCCI *cci)
|
||||
static inline bool cxl_dev_media_disabled(CXLDeviceState *cxl_dstate)
|
||||
{
|
||||
return !!cci->bg.runtime && cci->bg.opcode == 0x4400;
|
||||
uint64_t dev_status_reg = cxl_dstate->mbox_reg_state64[R_CXL_MEM_DEV_STS];
|
||||
return FIELD_EX64(dev_status_reg, CXL_MEM_DEV_STS, MEDIA_STATUS) == 0x3;
|
||||
}
|
||||
static inline bool scan_media_running(CXLCCI *cci)
|
||||
{
|
||||
return !!cci->bg.runtime && cci->bg.opcode == 0x4304;
|
||||
}
|
||||
|
||||
typedef struct CXLError {
|
||||
@ -422,6 +442,47 @@ typedef struct CXLPoison {
|
||||
typedef QLIST_HEAD(, CXLPoison) CXLPoisonList;
|
||||
#define CXL_POISON_LIST_LIMIT 256
|
||||
|
||||
/* CXL memory device patrol scrub control attributes */
|
||||
typedef struct CXLMemPatrolScrubReadAttrs {
|
||||
uint8_t scrub_cycle_cap;
|
||||
uint16_t scrub_cycle;
|
||||
uint8_t scrub_flags;
|
||||
} QEMU_PACKED CXLMemPatrolScrubReadAttrs;
|
||||
|
||||
typedef struct CXLMemPatrolScrubWriteAttrs {
|
||||
uint8_t scrub_cycle_hr;
|
||||
uint8_t scrub_flags;
|
||||
} QEMU_PACKED CXLMemPatrolScrubWriteAttrs;
|
||||
|
||||
#define CXL_MEMDEV_PS_GET_FEATURE_VERSION 0x01
|
||||
#define CXL_MEMDEV_PS_SET_FEATURE_VERSION 0x01
|
||||
#define CXL_MEMDEV_PS_SCRUB_CYCLE_CHANGE_CAP_DEFAULT BIT(0)
|
||||
#define CXL_MEMDEV_PS_SCRUB_REALTIME_REPORT_CAP_DEFAULT BIT(1)
|
||||
#define CXL_MEMDEV_PS_CUR_SCRUB_CYCLE_DEFAULT 12
|
||||
#define CXL_MEMDEV_PS_MIN_SCRUB_CYCLE_DEFAULT 1
|
||||
#define CXL_MEMDEV_PS_ENABLE_DEFAULT 0
|
||||
|
||||
/* CXL memory device DDR5 ECS control attributes */
|
||||
typedef struct CXLMemECSReadAttrs {
|
||||
uint8_t ecs_log_cap;
|
||||
uint8_t ecs_cap;
|
||||
uint16_t ecs_config;
|
||||
uint8_t ecs_flags;
|
||||
} QEMU_PACKED CXLMemECSReadAttrs;
|
||||
|
||||
typedef struct CXLMemECSWriteAttrs {
|
||||
uint8_t ecs_log_cap;
|
||||
uint16_t ecs_config;
|
||||
} QEMU_PACKED CXLMemECSWriteAttrs;
|
||||
|
||||
#define CXL_ECS_GET_FEATURE_VERSION 0x01
|
||||
#define CXL_ECS_SET_FEATURE_VERSION 0x01
|
||||
#define CXL_ECS_LOG_ENTRY_TYPE_DEFAULT 0x01
|
||||
#define CXL_ECS_REALTIME_REPORT_CAP_DEFAULT 1
|
||||
#define CXL_ECS_THRESHOLD_COUNT_DEFAULT 3 /* 3: 256, 4: 1024, 5: 4096 */
|
||||
#define CXL_ECS_MODE_DEFAULT 0
|
||||
#define CXL_ECS_NUM_MEDIA_FRUS 3 /* Default */
|
||||
|
||||
#define DCD_MAX_NUM_REGION 8
|
||||
|
||||
typedef struct CXLDCExtentRaw {
|
||||
@ -459,6 +520,14 @@ typedef struct CXLDCRegion {
|
||||
unsigned long *blk_bitmap;
|
||||
} CXLDCRegion;
|
||||
|
||||
typedef struct CXLSetFeatureInfo {
|
||||
QemuUUID uuid;
|
||||
uint8_t data_transfer_flag;
|
||||
bool data_saved_across_reset;
|
||||
uint16_t data_offset;
|
||||
size_t data_size;
|
||||
} CXLSetFeatureInfo;
|
||||
|
||||
struct CXLType3Dev {
|
||||
/* Private */
|
||||
PCIDevice parent_obj;
|
||||
@ -491,6 +560,19 @@ struct CXLType3Dev {
|
||||
unsigned int poison_list_cnt;
|
||||
bool poison_list_overflowed;
|
||||
uint64_t poison_list_overflow_ts;
|
||||
/* Poison Injection - backup */
|
||||
CXLPoisonList poison_list_bkp;
|
||||
CXLPoisonList scan_media_results;
|
||||
bool scan_media_hasrun;
|
||||
|
||||
CXLSetFeatureInfo set_feat_info;
|
||||
|
||||
/* Patrol scrub control attributes */
|
||||
CXLMemPatrolScrubReadAttrs patrol_scrub_attrs;
|
||||
CXLMemPatrolScrubWriteAttrs patrol_scrub_wr_attrs;
|
||||
/* ECS control attributes */
|
||||
CXLMemECSReadAttrs ecs_attrs[CXL_ECS_NUM_MEDIA_FRUS];
|
||||
CXLMemECSWriteAttrs ecs_wr_attrs[CXL_ECS_NUM_MEDIA_FRUS];
|
||||
|
||||
struct dynamic_capacity {
|
||||
HostMemoryBackend *host_dc;
|
||||
@ -554,10 +636,12 @@ CXLRetCode cxl_event_get_records(CXLDeviceState *cxlds, CXLGetEventPayload *pl,
|
||||
size_t *len);
|
||||
CXLRetCode cxl_event_clear_records(CXLDeviceState *cxlds,
|
||||
CXLClearEventPayload *pl);
|
||||
void cxl_discard_all_event_records(CXLDeviceState *cxlds);
|
||||
|
||||
void cxl_event_irq_assert(CXLType3Dev *ct3d);
|
||||
|
||||
void cxl_set_poison_list_overflowed(CXLType3Dev *ct3d);
|
||||
void cxl_clear_poison_list_overflowed(CXLType3Dev *ct3d);
|
||||
|
||||
CXLDCRegion *cxl_find_dc_region(CXLType3Dev *ct3d, uint64_t dpa, uint64_t len);
|
||||
|
||||
|
18
include/hw/cxl/cxl_mailbox.h
Normal file
18
include/hw/cxl/cxl_mailbox.h
Normal file
@ -0,0 +1,18 @@
|
||||
/*
|
||||
* QEMU CXL Mailbox
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2. See the
|
||||
* COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#ifndef CXL_MAILBOX_H
|
||||
#define CXL_MAILBOX_H
|
||||
|
||||
#define CXL_MBOX_IMMEDIATE_CONFIG_CHANGE (1 << 1)
|
||||
#define CXL_MBOX_IMMEDIATE_DATA_CHANGE (1 << 2)
|
||||
#define CXL_MBOX_IMMEDIATE_POLICY_CHANGE (1 << 3)
|
||||
#define CXL_MBOX_IMMEDIATE_LOG_CHANGE (1 << 4)
|
||||
#define CXL_MBOX_SECURITY_STATE_CHANGE (1 << 5)
|
||||
#define CXL_MBOX_BACKGROUND_OPERATION (1 << 6)
|
||||
|
||||
#endif
|
@ -3,6 +3,7 @@
|
||||
|
||||
#include "hw/pci/pci.h"
|
||||
#include "hw/pci/pcie.h"
|
||||
#include "hw/pci/pcie_doe.h"
|
||||
|
||||
#define TYPE_PCI_DEVICE "pci-device"
|
||||
typedef struct PCIDeviceClass PCIDeviceClass;
|
||||
@ -37,6 +38,8 @@ struct PCIDeviceClass {
|
||||
uint16_t subsystem_id; /* only for header type = 0 */
|
||||
|
||||
const char *romfile; /* rom bar */
|
||||
|
||||
bool sriov_vf_user_creatable;
|
||||
};
|
||||
|
||||
enum PCIReqIDType {
|
||||
@ -157,9 +160,17 @@ struct PCIDevice {
|
||||
MSIVectorReleaseNotifier msix_vector_release_notifier;
|
||||
MSIVectorPollNotifier msix_vector_poll_notifier;
|
||||
|
||||
/* SPDM */
|
||||
uint16_t spdm_port;
|
||||
|
||||
/* DOE */
|
||||
DOECap doe_spdm;
|
||||
|
||||
/* ID of standby device in net_failover pair */
|
||||
char *failover_pair_id;
|
||||
uint32_t acpi_index;
|
||||
|
||||
char *sriov_pf;
|
||||
};
|
||||
|
||||
static inline int pci_intx(PCIDevice *pci_dev)
|
||||
@ -192,7 +203,7 @@ static inline int pci_is_express_downstream_port(const PCIDevice *d)
|
||||
|
||||
static inline int pci_is_vf(const PCIDevice *d)
|
||||
{
|
||||
return d->exp.sriov_vf.pf != NULL;
|
||||
return d->sriov_pf || d->exp.sriov_vf.pf != NULL;
|
||||
}
|
||||
|
||||
static inline uint32_t pci_config_size(const PCIDevice *d)
|
||||
|
@ -46,6 +46,8 @@ REG32(PCI_DOE_CAP_STATUS, 0)
|
||||
|
||||
/* PCI-SIG defined Data Object Types - r6.0 Table 6-32 */
|
||||
#define PCI_SIG_DOE_DISCOVERY 0x00
|
||||
#define PCI_SIG_DOE_CMA 0x01
|
||||
#define PCI_SIG_DOE_SECURED_CMA 0x02
|
||||
|
||||
#define PCI_DOE_DW_SIZE_MAX (1 << 18)
|
||||
#define PCI_DOE_PROTOCOL_NUM_MAX 256
|
||||
@ -106,6 +108,9 @@ struct DOECap {
|
||||
/* Protocols and its callback response */
|
||||
DOEProtocol *protocols;
|
||||
uint16_t protocol_num;
|
||||
|
||||
/* Used for spdm-socket */
|
||||
int spdm_socket;
|
||||
};
|
||||
|
||||
void pcie_doe_init(PCIDevice *pdev, DOECap *doe_cap, uint16_t offset,
|
||||
|
@ -18,6 +18,7 @@
|
||||
typedef struct PCIESriovPF {
|
||||
uint8_t vf_bar_type[PCI_NUM_REGIONS]; /* Store type for each VF bar */
|
||||
PCIDevice **vf; /* Pointer to an array of num_vfs VF devices */
|
||||
bool vf_user_created; /* If VFs are created by user */
|
||||
} PCIESriovPF;
|
||||
|
||||
typedef struct PCIESriovVF {
|
||||
@ -40,6 +41,23 @@ void pcie_sriov_pf_init_vf_bar(PCIDevice *dev, int region_num,
|
||||
void pcie_sriov_vf_register_bar(PCIDevice *dev, int region_num,
|
||||
MemoryRegion *memory);
|
||||
|
||||
/**
|
||||
* pcie_sriov_pf_init_from_user_created_vfs() - Initialize PF with user-created
|
||||
* VFs.
|
||||
* @dev: A PCIe device being realized.
|
||||
* @offset: The offset of the SR-IOV capability.
|
||||
* @errp: pointer to Error*, to store an error if it happens.
|
||||
*
|
||||
* Return: The size of added capability. 0 if the user did not create VFs.
|
||||
* -1 if failed.
|
||||
*/
|
||||
int16_t pcie_sriov_pf_init_from_user_created_vfs(PCIDevice *dev,
|
||||
uint16_t offset,
|
||||
Error **errp);
|
||||
|
||||
bool pcie_sriov_register_device(PCIDevice *dev, Error **errp);
|
||||
void pcie_sriov_unregister_device(PCIDevice *dev);
|
||||
|
||||
/*
|
||||
* Default (minimal) page size support values
|
||||
* as required by the SR/IOV standard:
|
||||
|
@ -43,7 +43,6 @@ typedef struct IOMMUDevice {
|
||||
MemoryRegion bypass_mr; /* The alias of shared memory MR */
|
||||
GList *resv_regions;
|
||||
GList *host_resv_ranges;
|
||||
bool probe_done;
|
||||
} IOMMUDevice;
|
||||
|
||||
typedef struct IOMMUPciBus {
|
||||
|
@ -152,6 +152,7 @@ struct VirtIOPCIProxy {
|
||||
uint32_t modern_io_bar_idx;
|
||||
uint32_t modern_mem_bar_idx;
|
||||
int config_cap;
|
||||
uint16_t last_pcie_cap_offset;
|
||||
uint32_t flags;
|
||||
bool disable_modern;
|
||||
bool ignore_backend_features;
|
||||
|
@ -69,6 +69,8 @@ typedef struct VirtQueueElement
|
||||
unsigned int ndescs;
|
||||
unsigned int out_num;
|
||||
unsigned int in_num;
|
||||
/* Element has been processed (VIRTIO_F_IN_ORDER) */
|
||||
bool in_order_filled;
|
||||
hwaddr *in_addr;
|
||||
hwaddr *out_addr;
|
||||
struct iovec *in_sg;
|
||||
@ -371,7 +373,9 @@ typedef struct VirtIORNGConf VirtIORNGConf;
|
||||
DEFINE_PROP_BIT64("packed", _state, _field, \
|
||||
VIRTIO_F_RING_PACKED, false), \
|
||||
DEFINE_PROP_BIT64("queue_reset", _state, _field, \
|
||||
VIRTIO_F_RING_RESET, true)
|
||||
VIRTIO_F_RING_RESET, true), \
|
||||
DEFINE_PROP_BIT64("in_order", _state, _field, \
|
||||
VIRTIO_F_IN_ORDER, false)
|
||||
|
||||
hwaddr virtio_queue_get_desc_addr(VirtIODevice *vdev, int n);
|
||||
bool virtio_queue_enabled_legacy(VirtIODevice *vdev, int n);
|
||||
|
@ -313,6 +313,31 @@ int kvm_create_device(KVMState *s, uint64_t type, bool test);
|
||||
*/
|
||||
bool kvm_device_supported(int vmfd, uint64_t type);
|
||||
|
||||
/**
|
||||
* kvm_create_vcpu - Gets a parked KVM vCPU or creates a KVM vCPU
|
||||
* @cpu: QOM CPUState object for which KVM vCPU has to be fetched/created.
|
||||
*
|
||||
* @returns: 0 when success, errno (<0) when failed.
|
||||
*/
|
||||
int kvm_create_vcpu(CPUState *cpu);
|
||||
|
||||
/**
|
||||
* kvm_park_vcpu - Park QEMU KVM vCPU context
|
||||
* @cpu: QOM CPUState object for which QEMU KVM vCPU context has to be parked.
|
||||
*
|
||||
* @returns: none
|
||||
*/
|
||||
void kvm_park_vcpu(CPUState *cpu);
|
||||
|
||||
/**
|
||||
* kvm_unpark_vcpu - unpark QEMU KVM vCPU context
|
||||
* @s: KVM State
|
||||
* @vcpu_id: Architecture vCPU ID of the parked vCPU
|
||||
*
|
||||
* @returns: KVM fd
|
||||
*/
|
||||
int kvm_unpark_vcpu(KVMState *s, unsigned long vcpu_id);
|
||||
|
||||
/* Arch specific hooks */
|
||||
|
||||
extern const KVMCapabilityInfo kvm_arch_required_capabilities[];
|
||||
|
74
include/sysemu/spdm-socket.h
Normal file
74
include/sysemu/spdm-socket.h
Normal file
@ -0,0 +1,74 @@
|
||||
/*
|
||||
* QEMU SPDM socket support
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef SPDM_REQUESTER_H
|
||||
#define SPDM_REQUESTER_H
|
||||
|
||||
/**
|
||||
* spdm_socket_connect: connect to an external SPDM socket
|
||||
* @port: port to connect to
|
||||
* @errp: error object handle
|
||||
*
|
||||
* This will connect to an external SPDM socket server. On error
|
||||
* it will return -1 and errp will be set. On success this function
|
||||
* will return the socket number.
|
||||
*/
|
||||
int spdm_socket_connect(uint16_t port, Error **errp);
|
||||
|
||||
/**
|
||||
* spdm_socket_rsp: send and receive a message to a SPDM server
|
||||
* @socket: socket returned from spdm_socket_connect()
|
||||
* @transport_type: SPDM_SOCKET_TRANSPORT_TYPE_* macro
|
||||
* @req: request buffer
|
||||
* @req_len: request buffer length
|
||||
* @rsp: response buffer
|
||||
* @rsp_len: response buffer length
|
||||
*
|
||||
* Send platform data to a SPDM server on socket and then receive
|
||||
* a response.
|
||||
*/
|
||||
uint32_t spdm_socket_rsp(const int socket, uint32_t transport_type,
|
||||
void *req, uint32_t req_len,
|
||||
void *rsp, uint32_t rsp_len);
|
||||
|
||||
/**
|
||||
* spdm_socket_close: send a shutdown command to the server
|
||||
* @socket: socket returned from spdm_socket_connect()
|
||||
* @transport_type: SPDM_SOCKET_TRANSPORT_TYPE_* macro
|
||||
*
|
||||
* This will issue a shutdown command to the server.
|
||||
*/
|
||||
void spdm_socket_close(const int socket, uint32_t transport_type);
|
||||
|
||||
#define SPDM_SOCKET_COMMAND_NORMAL 0x0001
|
||||
#define SPDM_SOCKET_COMMAND_OOB_ENCAP_KEY_UPDATE 0x8001
|
||||
#define SPDM_SOCKET_COMMAND_CONTINUE 0xFFFD
|
||||
#define SPDM_SOCKET_COMMAND_SHUTDOWN 0xFFFE
|
||||
#define SPDM_SOCKET_COMMAND_UNKOWN 0xFFFF
|
||||
#define SPDM_SOCKET_COMMAND_TEST 0xDEAD
|
||||
|
||||
#define SPDM_SOCKET_TRANSPORT_TYPE_MCTP 0x01
|
||||
#define SPDM_SOCKET_TRANSPORT_TYPE_PCI_DOE 0x02
|
||||
|
||||
#define SPDM_SOCKET_MAX_MESSAGE_BUFFER_SIZE 0x1200
|
||||
|
||||
#endif
|
@ -62,6 +62,7 @@ const int vdpa_feature_bits[] = {
|
||||
VIRTIO_F_RING_PACKED,
|
||||
VIRTIO_F_RING_RESET,
|
||||
VIRTIO_F_VERSION_1,
|
||||
VIRTIO_F_IN_ORDER,
|
||||
VIRTIO_F_NOTIFICATION_DATA,
|
||||
VIRTIO_NET_F_CSUM,
|
||||
VIRTIO_NET_F_CTRL_GUEST_OFFLOADS,
|
||||
|
@ -763,6 +763,7 @@ void cpu_address_space_init(CPUState *cpu, int asidx,
|
||||
|
||||
if (!cpu->cpu_ases) {
|
||||
cpu->cpu_ases = g_new0(CPUAddressSpace, cpu->num_ases);
|
||||
cpu->cpu_ases_count = cpu->num_ases;
|
||||
}
|
||||
|
||||
newas = &cpu->cpu_ases[asidx];
|
||||
@ -776,6 +777,34 @@ void cpu_address_space_init(CPUState *cpu, int asidx,
|
||||
}
|
||||
}
|
||||
|
||||
void cpu_address_space_destroy(CPUState *cpu, int asidx)
|
||||
{
|
||||
CPUAddressSpace *cpuas;
|
||||
|
||||
assert(cpu->cpu_ases);
|
||||
assert(asidx >= 0 && asidx < cpu->num_ases);
|
||||
/* KVM cannot currently support multiple address spaces. */
|
||||
assert(asidx == 0 || !kvm_enabled());
|
||||
|
||||
cpuas = &cpu->cpu_ases[asidx];
|
||||
if (tcg_enabled()) {
|
||||
memory_listener_unregister(&cpuas->tcg_as_listener);
|
||||
}
|
||||
|
||||
address_space_destroy(cpuas->as);
|
||||
g_free_rcu(cpuas->as, rcu);
|
||||
|
||||
if (asidx == 0) {
|
||||
/* reset the convenience alias for address space 0 */
|
||||
cpu->as = NULL;
|
||||
}
|
||||
|
||||
if (--cpu->cpu_ases_count == 0) {
|
||||
g_free(cpu->cpu_ases);
|
||||
cpu->cpu_ases = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
AddressSpace *cpu_get_address_space(CPUState *cpu, int asidx)
|
||||
{
|
||||
/* Return the AddressSpace corresponding to the specified index */
|
||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
tests/data/acpi/riscv64/virt/APIC
Normal file
BIN
tests/data/acpi/riscv64/virt/APIC
Normal file
Binary file not shown.
BIN
tests/data/acpi/riscv64/virt/DSDT
Normal file
BIN
tests/data/acpi/riscv64/virt/DSDT
Normal file
Binary file not shown.
BIN
tests/data/acpi/riscv64/virt/FACP
Normal file
BIN
tests/data/acpi/riscv64/virt/FACP
Normal file
Binary file not shown.
BIN
tests/data/acpi/riscv64/virt/MCFG
Normal file
BIN
tests/data/acpi/riscv64/virt/MCFG
Normal file
Binary file not shown.
BIN
tests/data/acpi/riscv64/virt/RHCT
Normal file
BIN
tests/data/acpi/riscv64/virt/RHCT
Normal file
Binary file not shown.
BIN
tests/data/acpi/riscv64/virt/SPCR
Normal file
BIN
tests/data/acpi/riscv64/virt/SPCR
Normal file
Binary file not shown.
Binary file not shown.
@ -267,15 +267,6 @@ static void dump_aml_files(test_data *data, bool rebuild)
|
||||
data->arch, data->machine,
|
||||
sdt->aml, ext);
|
||||
|
||||
/*
|
||||
* To keep test cases not failing before the DATA files are moved to
|
||||
* ${arch}/${machine} folder, add this check as well.
|
||||
*/
|
||||
if (!g_file_test(aml_file, G_FILE_TEST_EXISTS)) {
|
||||
aml_file = g_strdup_printf("%s/%s/%.4s%s", data_dir,
|
||||
data->machine, sdt->aml, ext);
|
||||
}
|
||||
|
||||
if (!g_file_test(aml_file, G_FILE_TEST_EXISTS) &&
|
||||
sdt->aml_len == exp_sdt->aml_len &&
|
||||
!memcmp(sdt->aml, exp_sdt->aml, sdt->aml_len)) {
|
||||
@ -412,11 +403,6 @@ static GArray *load_expected_aml(test_data *data)
|
||||
try_again:
|
||||
aml_file = g_strdup_printf("%s/%s/%s/%.4s%s", data_dir, data->arch,
|
||||
data->machine, sdt->aml, ext);
|
||||
if (!g_file_test(aml_file, G_FILE_TEST_EXISTS)) {
|
||||
aml_file = g_strdup_printf("%s/%s/%.4s%s", data_dir, data->machine,
|
||||
sdt->aml, ext);
|
||||
}
|
||||
|
||||
if (verbosity_level >= 2) {
|
||||
fprintf(stderr, "Looking for expected file '%s'\n", aml_file);
|
||||
}
|
||||
@ -1977,6 +1963,28 @@ static void test_acpi_microvm_acpi_erst(void)
|
||||
}
|
||||
#endif /* CONFIG_POSIX */
|
||||
|
||||
static void test_acpi_riscv64_virt_tcg(void)
|
||||
{
|
||||
test_data data = {
|
||||
.machine = "virt",
|
||||
.arch = "riscv64",
|
||||
.tcg_only = true,
|
||||
.uefi_fl1 = "pc-bios/edk2-riscv-code.fd",
|
||||
.uefi_fl2 = "pc-bios/edk2-riscv-vars.fd",
|
||||
.cd = "tests/data/uefi-boot-images/bios-tables-test.riscv64.iso.qcow2",
|
||||
.ram_start = 0x80000000ULL,
|
||||
.scan_len = 128ULL * 1024 * 1024,
|
||||
};
|
||||
|
||||
/*
|
||||
* RHCT will have ISA string encoded. To reduce the effort
|
||||
* of updating expected AML file for any new default ISA extension,
|
||||
* use the profile rva22s64.
|
||||
*/
|
||||
test_acpi_one("-cpu rva22s64 ", &data);
|
||||
free_test_data(&data);
|
||||
}
|
||||
|
||||
static void test_acpi_aarch64_virt_tcg(void)
|
||||
{
|
||||
test_data data = {
|
||||
@ -2455,6 +2463,10 @@ int main(int argc, char *argv[])
|
||||
qtest_add_func("acpi/virt/viot", test_acpi_aarch64_virt_viot);
|
||||
}
|
||||
}
|
||||
} else if (strcmp(arch, "riscv64") == 0) {
|
||||
if (has_tcg && qtest_has_device("virtio-blk-pci")) {
|
||||
qtest_add_func("acpi/virt", test_acpi_riscv64_virt_tcg);
|
||||
}
|
||||
}
|
||||
ret = g_test_run();
|
||||
boot_sector_cleanup(disk);
|
||||
|
Loading…
Reference in New Issue
Block a user