mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-08-30 13:31:52 -06:00
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
81 changed files with 2486 additions and 288 deletions
|
@ -2009,6 +2009,7 @@ F: hw/pci-bridge/*
|
||||||
F: qapi/pci.json
|
F: qapi/pci.json
|
||||||
F: docs/pci*
|
F: docs/pci*
|
||||||
F: docs/specs/*pci*
|
F: docs/specs/*pci*
|
||||||
|
F: docs/system/sriov.rst
|
||||||
|
|
||||||
PCIE DOE
|
PCIE DOE
|
||||||
M: Huai-Cheng Kuo <hchkuo@avery-design.com.tw>
|
M: Huai-Cheng Kuo <hchkuo@avery-design.com.tw>
|
||||||
|
@ -2208,6 +2209,7 @@ F: docs/devel/vfio-iommufd.rst
|
||||||
|
|
||||||
vhost
|
vhost
|
||||||
M: Michael S. Tsirkin <mst@redhat.com>
|
M: Michael S. Tsirkin <mst@redhat.com>
|
||||||
|
R: Stefano Garzarella <sgarzare@redhat.com>
|
||||||
S: Supported
|
S: Supported
|
||||||
F: hw/*/*vhost*
|
F: hw/*/*vhost*
|
||||||
F: docs/interop/vhost-user.json
|
F: docs/interop/vhost-user.json
|
||||||
|
@ -3398,6 +3400,12 @@ F: tests/qtest/*tpm*
|
||||||
F: docs/specs/tpm.rst
|
F: docs/specs/tpm.rst
|
||||||
T: git https://github.com/stefanberger/qemu-tpm.git tpm-next
|
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
|
Checkpatch
|
||||||
S: Odd Fixes
|
S: Odd Fixes
|
||||||
F: scripts/checkpatch.pl
|
F: scripts/checkpatch.pl
|
||||||
|
@ -3657,6 +3665,7 @@ F: tests/uefi-test-tools/
|
||||||
VT-d Emulation
|
VT-d Emulation
|
||||||
M: Michael S. Tsirkin <mst@redhat.com>
|
M: Michael S. Tsirkin <mst@redhat.com>
|
||||||
R: Jason Wang <jasowang@redhat.com>
|
R: Jason Wang <jasowang@redhat.com>
|
||||||
|
R: Yi Liu <yi.l.liu@intel.com>
|
||||||
S: Supported
|
S: Supported
|
||||||
F: hw/i386/intel_iommu.c
|
F: hw/i386/intel_iommu.c
|
||||||
F: hw/i386/intel_iommu_internal.h
|
F: hw/i386/intel_iommu_internal.h
|
||||||
|
|
|
@ -340,14 +340,71 @@ err:
|
||||||
return ret;
|
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)
|
static int do_kvm_destroy_vcpu(CPUState *cpu)
|
||||||
{
|
{
|
||||||
KVMState *s = kvm_state;
|
KVMState *s = kvm_state;
|
||||||
long mmap_size;
|
long mmap_size;
|
||||||
struct KVMParkedVcpu *vcpu = NULL;
|
|
||||||
int ret = 0;
|
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);
|
ret = kvm_arch_destroy_vcpu(cpu);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
|
@ -373,10 +430,7 @@ static int do_kvm_destroy_vcpu(CPUState *cpu)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
vcpu = g_malloc0(sizeof(*vcpu));
|
kvm_park_vcpu(cpu);
|
||||||
vcpu->vcpu_id = kvm_arch_vcpu_id(cpu);
|
|
||||||
vcpu->kvm_fd = cpu->kvm_fd;
|
|
||||||
QLIST_INSERT_HEAD(&kvm_state->kvm_parked_vcpus, vcpu, node);
|
|
||||||
err:
|
err:
|
||||||
return ret;
|
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)
|
int kvm_init_vcpu(CPUState *cpu, Error **errp)
|
||||||
{
|
{
|
||||||
KVMState *s = kvm_state;
|
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));
|
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) {
|
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));
|
kvm_arch_vcpu_id(cpu));
|
||||||
goto err;
|
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);
|
mmap_size = kvm_ioctl(s, KVM_GET_VCPU_MMAP_SIZE, 0);
|
||||||
if (mmap_size < 0) {
|
if (mmap_size < 0) {
|
||||||
ret = mmap_size;
|
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_insert_breakpoint(CPUState *cpu, int type, vaddr addr, vaddr len);
|
||||||
int kvm_remove_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);
|
void kvm_remove_all_breakpoints(CPUState *cpu);
|
||||||
|
|
||||||
#endif /* KVM_CPUS_H */
|
#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_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_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_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_commit_routes(void) ""
|
||||||
kvm_irqchip_add_msi_route(char *name, int vector, int virq) "dev %s vector %d virq %d"
|
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"
|
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_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_reaper_kick(const char *reason) "%s"
|
||||||
kvm_dirty_ring_flush(int finished) "%d"
|
kvm_dirty_ring_flush(int finished) "%d"
|
||||||
kvm_destroy_vcpu(void) ""
|
|
||||||
kvm_failed_get_vcpu_mmap_size(void) ""
|
kvm_failed_get_vcpu_mmap_size(void) ""
|
||||||
kvm_cpu_exec(void) ""
|
kvm_cpu_exec(void) ""
|
||||||
kvm_interrupt_exit_request(void) ""
|
kvm_interrupt_exit_request(void) ""
|
||||||
|
|
|
@ -3,3 +3,7 @@ source tpm/Kconfig
|
||||||
config IOMMUFD
|
config IOMMUFD
|
||||||
bool
|
bool
|
||||||
depends on VFIO
|
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: gio, if_true: files('dbus-vmstate.c'))
|
||||||
system_ss.add(when: 'CONFIG_SGX', if_true: files('hostmem-epc.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')
|
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;
|
VubDev *vdev_blk = req->vdev_blk;
|
||||||
desc = buf;
|
desc = buf;
|
||||||
uint64_t range[2] = { le64_to_cpu(desc->sector) << 9,
|
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 (type == VIRTIO_BLK_T_DISCARD) {
|
||||||
if (ioctl(vdev_blk->blk_fd, BLKDISCARD, range) == 0) {
|
if (ioctl(vdev_blk->blk_fd, BLKDISCARD, range) == 0) {
|
||||||
g_free(buf);
|
g_free(buf);
|
||||||
|
|
|
@ -64,7 +64,8 @@ GED IO interface (4 byte access)
|
||||||
0: Memory hotplug event
|
0: Memory hotplug event
|
||||||
1: System power down event
|
1: System power down event
|
||||||
2: NVDIMM hotplug event
|
2: NVDIMM hotplug event
|
||||||
3-31: Reserved
|
3: CPU hotplug event
|
||||||
|
4-31: Reserved
|
||||||
|
|
||||||
**write_access:**
|
**write_access:**
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,7 @@ guest hardware that is specific to QEMU.
|
||||||
edu
|
edu
|
||||||
ivshmem-spec
|
ivshmem-spec
|
||||||
pvpanic
|
pvpanic
|
||||||
|
spdm
|
||||||
standard-vga
|
standard-vga
|
||||||
virt-ctlr
|
virt-ctlr
|
||||||
vmcoreinfo
|
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
|
multi-process
|
||||||
confidential-guest-support
|
confidential-guest-support
|
||||||
vm-templating
|
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)
|
static void gdb_process_breakpoint_remove_all(GDBProcess *p)
|
||||||
{
|
{
|
||||||
CPUState *cpu = gdb_get_first_cpu_in_process(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;
|
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)
|
void acpi_cpu_ospm_status(CPUHotplugState *cpu_st, ACPIOSTInfoList ***list)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
#include "trace.h"
|
#include "trace.h"
|
||||||
#include "sysemu/numa.h"
|
#include "sysemu/numa.h"
|
||||||
|
|
||||||
#define ACPI_CPU_HOTPLUG_REG_LEN 12
|
|
||||||
#define ACPI_CPU_SELECTOR_OFFSET_WR 0
|
#define ACPI_CPU_SELECTOR_OFFSET_WR 0
|
||||||
#define ACPI_CPU_FLAGS_OFFSET_RW 4
|
#define ACPI_CPU_FLAGS_OFFSET_RW 4
|
||||||
#define ACPI_CPU_CMD_OFFSET_WR 5
|
#define ACPI_CPU_CMD_OFFSET_WR 5
|
||||||
|
@ -339,9 +338,10 @@ const VMStateDescription vmstate_cpu_hotplug = {
|
||||||
#define CPU_FW_EJECT_EVENT "CEJF"
|
#define CPU_FW_EJECT_EVENT "CEJF"
|
||||||
|
|
||||||
void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts,
|
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 *res_root,
|
||||||
const char *event_handler_method)
|
const char *event_handler_method,
|
||||||
|
AmlRegionSpace rs)
|
||||||
{
|
{
|
||||||
Aml *ifctx;
|
Aml *ifctx;
|
||||||
Aml *field;
|
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_name_decl("_UID", aml_string("CPU Hotplug resources")));
|
||||||
aml_append(cpu_ctrl_dev, aml_mutex(CPU_LOCK, 0));
|
aml_append(cpu_ctrl_dev, aml_mutex(CPU_LOCK, 0));
|
||||||
|
|
||||||
|
assert((rs == AML_SYSTEM_IO) || (rs == AML_SYSTEM_MEMORY));
|
||||||
|
|
||||||
crs = aml_resource_template();
|
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));
|
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));
|
aml_append(cpu_ctrl_dev, aml_name_decl("_CRS", crs));
|
||||||
|
|
||||||
/* declare CPU hotplug MMIO region with related access fields */
|
/* declare CPU hotplug MMIO region with related access fields */
|
||||||
aml_append(cpu_ctrl_dev,
|
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));
|
ACPI_CPU_HOTPLUG_REG_LEN));
|
||||||
|
|
||||||
field = aml_field("PRST", AML_BYTE_ACC, AML_NOLOCK,
|
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_MEM_HOTPLUG_EVT,
|
||||||
ACPI_GED_PWR_DOWN_EVT,
|
ACPI_GED_PWR_DOWN_EVT,
|
||||||
ACPI_GED_NVDIMM_HOTPLUG_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 "."
|
aml_append(if_ctx, aml_call0(MEMORY_DEVICES_CONTAINER "."
|
||||||
MEMORY_SLOT_SCAN_METHOD));
|
MEMORY_SLOT_SCAN_METHOD));
|
||||||
break;
|
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:
|
case ACPI_GED_PWR_DOWN_EVT:
|
||||||
aml_append(if_ctx,
|
aml_append(if_ctx,
|
||||||
aml_notify(aml_name(ACPI_POWER_BUTTON_DEVICE),
|
aml_notify(aml_name(ACPI_POWER_BUTTON_DEVICE),
|
||||||
|
@ -234,6 +238,8 @@ static void acpi_ged_device_plug_cb(HotplugHandler *hotplug_dev,
|
||||||
} else {
|
} else {
|
||||||
acpi_memory_plug_cb(hotplug_dev, &s->memhp_state, dev, errp);
|
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 {
|
} else {
|
||||||
error_setg(errp, "virt: device plug request for unsupported device"
|
error_setg(errp, "virt: device plug request for unsupported device"
|
||||||
" type: %s", object_get_typename(OBJECT(dev)));
|
" 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) &&
|
if ((object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM) &&
|
||||||
!(object_dynamic_cast(OBJECT(dev), TYPE_NVDIMM)))) {
|
!(object_dynamic_cast(OBJECT(dev), TYPE_NVDIMM)))) {
|
||||||
acpi_memory_unplug_request_cb(hotplug_dev, &s->memhp_state, dev, errp);
|
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 {
|
} else {
|
||||||
error_setg(errp, "acpi: device unplug request for unsupported device"
|
error_setg(errp, "acpi: device unplug request for unsupported device"
|
||||||
" type: %s", object_get_typename(OBJECT(dev)));
|
" 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)) {
|
if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
|
||||||
acpi_memory_unplug_cb(&s->memhp_state, dev, errp);
|
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 {
|
} else {
|
||||||
error_setg(errp, "acpi: device unplug for unsupported device"
|
error_setg(errp, "acpi: device unplug for unsupported device"
|
||||||
" type: %s", object_get_typename(OBJECT(dev)));
|
" 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);
|
AcpiGedState *s = ACPI_GED(adev);
|
||||||
|
|
||||||
acpi_memory_ospm_status(&s->memhp_state, list);
|
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)
|
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;
|
sel = ACPI_GED_PWR_DOWN_EVT;
|
||||||
} else if (ev & ACPI_NVDIMM_HOTPLUG_STATUS) {
|
} else if (ev & ACPI_NVDIMM_HOTPLUG_STATUS) {
|
||||||
sel = ACPI_GED_NVDIMM_HOTPLUG_EVT;
|
sel = ACPI_GED_NVDIMM_HOTPLUG_EVT;
|
||||||
|
} else if (ev & ACPI_CPU_HOTPLUG_STATUS) {
|
||||||
|
sel = ACPI_GED_CPU_HOTPLUG_EVT;
|
||||||
} else {
|
} else {
|
||||||
/* Unknown event. Return without generating interrupt. */
|
/* Unknown event. Return without generating interrupt. */
|
||||||
warn_report("GED: Unsupported event %d. No irq injected", ev);
|
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)
|
static void acpi_ged_initfn(Object *obj)
|
||||||
{
|
{
|
||||||
DeviceState *dev = DEVICE(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";
|
dc->desc = "ACPI Generic Event Device";
|
||||||
device_class_set_props(dc, acpi_ged_properties);
|
device_class_set_props(dc, acpi_ged_properties);
|
||||||
dc->vmsd = &vmstate_acpi_ged;
|
dc->vmsd = &vmstate_acpi_ged;
|
||||||
|
dc->realize = acpi_ged_realize;
|
||||||
|
|
||||||
hc->plug = acpi_ged_device_plug_cb;
|
hc->plug = acpi_ged_device_plug_cb;
|
||||||
hc->unplug_request = acpi_ged_unplug_request_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)
|
static void virt_machine_9_0_options(MachineClass *mc)
|
||||||
{
|
{
|
||||||
virt_machine_9_1_options(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);
|
compat_props_add(mc->compat_props, hw_compat_9_0, hw_compat_9_0_len);
|
||||||
}
|
}
|
||||||
DEFINE_VIRT_MACHINE(9, 0)
|
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.");
|
error_report("Number of channels is not supported.");
|
||||||
return cpu_to_le32(VIRTIO_SND_S_NOT_SUPP);
|
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.");
|
error_report("Stream format is not supported.");
|
||||||
return cpu_to_le32(VIRTIO_SND_S_NOT_SUPP);
|
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.");
|
error_report("Stream rate is not supported.");
|
||||||
return cpu_to_le32(VIRTIO_SND_S_NOT_SUPP);
|
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;
|
VirtIOSoundPCMStream *stream = data;
|
||||||
VirtIOSoundPCMBuffer *buffer;
|
VirtIOSoundPCMBuffer *buffer;
|
||||||
size_t size;
|
size_t size, max_size;
|
||||||
|
|
||||||
WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) {
|
WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) {
|
||||||
while (!QSIMPLEQ_EMPTY(&stream->queue)) {
|
while (!QSIMPLEQ_EMPTY(&stream->queue)) {
|
||||||
|
@ -1275,7 +1277,12 @@ static void virtio_snd_pcm_in_cb(void *data, int available)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
max_size = iov_size(buffer->elem->in_sg, buffer->elem->in_num);
|
||||||
for (;;) {
|
for (;;) {
|
||||||
|
if (buffer->size >= max_size) {
|
||||||
|
return_rx_buffer(stream, buffer);
|
||||||
|
break;
|
||||||
|
}
|
||||||
size = AUD_read(stream->voice.in,
|
size = AUD_read(stream->voice.in,
|
||||||
buffer->data + buffer->size,
|
buffer->data + buffer->size,
|
||||||
MIN(available, (stream->params.period_bytes -
|
MIN(available, (stream->params.period_bytes -
|
||||||
|
|
|
@ -51,6 +51,7 @@ static const int user_feature_bits[] = {
|
||||||
VIRTIO_F_RING_PACKED,
|
VIRTIO_F_RING_PACKED,
|
||||||
VIRTIO_F_IOMMU_PLATFORM,
|
VIRTIO_F_IOMMU_PLATFORM,
|
||||||
VIRTIO_F_RING_RESET,
|
VIRTIO_F_RING_RESET,
|
||||||
|
VIRTIO_F_IN_ORDER,
|
||||||
VIRTIO_F_NOTIFICATION_DATA,
|
VIRTIO_F_NOTIFICATION_DATA,
|
||||||
VHOST_INVALID_FEATURE_BIT
|
VHOST_INVALID_FEATURE_BIT
|
||||||
};
|
};
|
||||||
|
|
|
@ -282,7 +282,10 @@ static void cpu_common_finalize(Object *obj)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
free_queued_cpu_work(cpu);
|
free_queued_cpu_work(cpu);
|
||||||
|
/* If cleanup didn't happen in context to gdb_unregister_coprocessor_all */
|
||||||
|
if (cpu->gdb_regs) {
|
||||||
g_array_free(cpu->gdb_regs, TRUE);
|
g_array_free(cpu->gdb_regs, TRUE);
|
||||||
|
}
|
||||||
qemu_lockcnt_destroy(&cpu->in_ioctl_lock);
|
qemu_lockcnt_destroy(&cpu->in_ioctl_lock);
|
||||||
qemu_mutex_destroy(&cpu->work_mutex);
|
qemu_mutex_destroy(&cpu->work_mutex);
|
||||||
qemu_cond_destroy(cpu->halt_cond);
|
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 */
|
/* Default 128 MB as guest ram size */
|
||||||
mc->default_ram_size = 128 * MiB;
|
mc->default_ram_size = 128 * MiB;
|
||||||
mc->rom_file_has_mr = true;
|
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.
|
/* numa node memory size aligned on 8MB by default.
|
||||||
* On Linux, each node's border has to be 8MB aligned
|
* 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;
|
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,
|
CXLRetCode cxl_event_get_records(CXLDeviceState *cxlds, CXLGetEventPayload *pl,
|
||||||
uint8_t log_type, int max_recs,
|
uint8_t log_type, int max_recs,
|
||||||
size_t *len)
|
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,
|
static void machine_get_cfmw(Object *obj, Visitor *v, const char *name,
|
||||||
void *opaque, Error **errp)
|
void *opaque, Error **errp)
|
||||||
{
|
{
|
||||||
CXLFixedMemoryWindowOptionsList **list = opaque;
|
CXLState *state = opaque;
|
||||||
|
CXLFixedMemoryWindowOptionsList **list = &state->cfmw_list;
|
||||||
|
|
||||||
visit_type_CXLFixedMemoryWindowOptionsList(v, name, list, errp);
|
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,
|
.fw_unplugs_cpu = pm->smi_on_cpu_unplug,
|
||||||
};
|
};
|
||||||
build_cpus_aml(dsdt, machine, opts, pc_madt_cpu_entry,
|
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) {
|
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;
|
struct vtd_iotlb_key key;
|
||||||
VTDIOTLBEntry *entry;
|
VTDIOTLBEntry *entry;
|
||||||
int level;
|
unsigned level;
|
||||||
|
|
||||||
for (level = VTD_SL_PT_LEVEL; level < VTD_SL_PML4_LEVEL; level++) {
|
for (level = VTD_SL_PT_LEVEL; level < VTD_SL_PML4_LEVEL; level++) {
|
||||||
key.gfn = vtd_get_iotlb_gfn(addr, level);
|
key.gfn = vtd_get_iotlb_gfn(addr, level);
|
||||||
|
|
|
@ -264,10 +264,10 @@
|
||||||
#define VTD_FRCD_FR(val) (((val) & 0xffULL) << 32)
|
#define VTD_FRCD_FR(val) (((val) & 0xffULL) << 32)
|
||||||
#define VTD_FRCD_SID_MASK 0xffffULL
|
#define VTD_FRCD_SID_MASK 0xffffULL
|
||||||
#define VTD_FRCD_SID(val) ((val) & VTD_FRCD_SID_MASK)
|
#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 */
|
/* For the low 64-bit of 128-bit */
|
||||||
#define VTD_FRCD_FI(val) ((val) & ~0xfffULL)
|
#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)
|
#define VTD_FRCD_IR_IDX(val) (((val) & 0xffffULL) << 48)
|
||||||
|
|
||||||
/* DMA Remapping Fault Conditions */
|
/* DMA Remapping Fault Conditions */
|
||||||
|
@ -436,7 +436,7 @@ struct VTDIOTLBPageInvInfo {
|
||||||
uint16_t domain_id;
|
uint16_t domain_id;
|
||||||
uint32_t pasid;
|
uint32_t pasid;
|
||||||
uint64_t addr;
|
uint64_t addr;
|
||||||
uint8_t mask;
|
uint64_t mask;
|
||||||
};
|
};
|
||||||
typedef struct VTDIOTLBPageInvInfo VTDIOTLBPageInvInfo;
|
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);
|
pc_i440fx_machine_9_1_options(m);
|
||||||
m->alias = NULL;
|
m->alias = NULL;
|
||||||
m->is_default = false;
|
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, hw_compat_9_0, hw_compat_9_0_len);
|
||||||
compat_props_add(m->compat_props, pc_compat_9_0, pc_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);
|
PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
|
||||||
pc_q35_machine_9_1_options(m);
|
pc_q35_machine_9_1_options(m);
|
||||||
m->alias = NULL;
|
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, hw_compat_9_0, hw_compat_9_0_len);
|
||||||
compat_props_add(m->compat_props, pc_compat_9_0, pc_compat_9_0_len);
|
compat_props_add(m->compat_props, pc_compat_9_0, pc_compat_9_0_len);
|
||||||
pcmc->isa_bios_alias = false;
|
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");
|
error_setg(errp, "volatile memdev must have backing device");
|
||||||
return false;
|
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_nonvolatile(vmr, false);
|
||||||
memory_region_set_enabled(vmr, true);
|
memory_region_set_enabled(vmr, true);
|
||||||
host_memory_backend_set_mapped(ct3d->hostvmem, 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");
|
error_setg(errp, "persistent memdev must have backing device");
|
||||||
return false;
|
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_nonvolatile(pmr, true);
|
||||||
memory_region_set_enabled(pmr, true);
|
memory_region_set_enabled(pmr, true);
|
||||||
host_memory_backend_set_mapped(ct3d->hostpmem, true);
|
host_memory_backend_set_mapped(ct3d->hostpmem, true);
|
||||||
|
@ -790,6 +800,11 @@ static bool cxl_setup_memory(CXLType3Dev *ct3d, Error **errp)
|
||||||
return false;
|
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
|
* Set DC regions as volatile for now, non-volatile support can
|
||||||
* be added in the future if needed.
|
* 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;
|
uint8_t *pci_conf = pci_dev->config;
|
||||||
unsigned short msix_num = 6;
|
unsigned short msix_num = 6;
|
||||||
int i, rc;
|
int i, rc;
|
||||||
|
uint16_t count;
|
||||||
|
|
||||||
QTAILQ_INIT(&ct3d->error_list);
|
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);
|
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;
|
return;
|
||||||
|
|
||||||
err_release_cdat:
|
err_release_cdat:
|
||||||
|
@ -1127,7 +1165,7 @@ MemTxResult cxl_type3_read(PCIDevice *d, hwaddr host_addr, uint64_t *data,
|
||||||
return MEMTX_ERROR;
|
return MEMTX_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sanitize_running(&ct3d->cci)) {
|
if (cxl_dev_media_disabled(&ct3d->cxl_dstate)) {
|
||||||
qemu_guest_getrandom_nofail(data, size);
|
qemu_guest_getrandom_nofail(data, size);
|
||||||
return MEMTX_OK;
|
return MEMTX_OK;
|
||||||
}
|
}
|
||||||
|
@ -1149,7 +1187,7 @@ MemTxResult cxl_type3_write(PCIDevice *d, hwaddr host_addr, uint64_t data,
|
||||||
return MEMTX_ERROR;
|
return MEMTX_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sanitize_running(&ct3d->cci)) {
|
if (cxl_dev_media_disabled(&ct3d->cxl_dstate)) {
|
||||||
return MEMTX_OK;
|
return MEMTX_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1304,6 +1342,12 @@ void cxl_set_poison_list_overflowed(CXLType3Dev *ct3d)
|
||||||
cxl_device_get_timestamp(&ct3d->cxl_dstate);
|
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,
|
void qmp_cxl_inject_poison(const char *path, uint64_t start, uint64_t length,
|
||||||
Error **errp)
|
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 = g_new0(CXLPoison, 1);
|
||||||
p->length = length;
|
p->length = length;
|
||||||
p->start = start;
|
p->start = start;
|
||||||
/* Different from injected via the mbox */
|
/* Different from injected via the mbox */
|
||||||
p->type = CXL_POISON_TYPE_INTERNAL;
|
p->type = CXL_POISON_TYPE_INTERNAL;
|
||||||
|
|
||||||
|
if (ct3d->poison_list_cnt < CXL_POISON_LIST_LIMIT) {
|
||||||
QLIST_INSERT_HEAD(&ct3d->poison_list, p, node);
|
QLIST_INSERT_HEAD(&ct3d->poison_list, p, node);
|
||||||
ct3d->poison_list_cnt++;
|
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 */
|
/* 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_IOMMU_PLATFORM,
|
||||||
VIRTIO_F_RING_PACKED,
|
VIRTIO_F_RING_PACKED,
|
||||||
VIRTIO_F_RING_RESET,
|
VIRTIO_F_RING_RESET,
|
||||||
|
VIRTIO_F_IN_ORDER,
|
||||||
VIRTIO_F_NOTIFICATION_DATA,
|
VIRTIO_F_NOTIFICATION_DATA,
|
||||||
VIRTIO_NET_F_HASH_REPORT,
|
VIRTIO_NET_F_HASH_REPORT,
|
||||||
VHOST_INVALID_FEATURE_BIT
|
VHOST_INVALID_FEATURE_BIT
|
||||||
|
@ -78,6 +79,7 @@ static const int user_feature_bits[] = {
|
||||||
VIRTIO_F_IOMMU_PLATFORM,
|
VIRTIO_F_IOMMU_PLATFORM,
|
||||||
VIRTIO_F_RING_PACKED,
|
VIRTIO_F_RING_PACKED,
|
||||||
VIRTIO_F_RING_RESET,
|
VIRTIO_F_RING_RESET,
|
||||||
|
VIRTIO_F_IN_ORDER,
|
||||||
VIRTIO_NET_F_RSS,
|
VIRTIO_NET_F_RSS,
|
||||||
VIRTIO_NET_F_HASH_REPORT,
|
VIRTIO_NET_F_HASH_REPORT,
|
||||||
VIRTIO_NET_F_GUEST_USO4,
|
VIRTIO_NET_F_GUEST_USO4,
|
||||||
|
|
|
@ -203,6 +203,7 @@
|
||||||
#include "sysemu/hostmem.h"
|
#include "sysemu/hostmem.h"
|
||||||
#include "hw/pci/msix.h"
|
#include "hw/pci/msix.h"
|
||||||
#include "hw/pci/pcie_sriov.h"
|
#include "hw/pci/pcie_sriov.h"
|
||||||
|
#include "sysemu/spdm-socket.h"
|
||||||
#include "migration/vmstate.h"
|
#include "migration/vmstate.h"
|
||||||
|
|
||||||
#include "nvme.h"
|
#include "nvme.h"
|
||||||
|
@ -8315,6 +8316,27 @@ static int nvme_add_pm_capability(PCIDevice *pci_dev, uint8_t offset)
|
||||||
return 0;
|
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)
|
static bool nvme_init_pci(NvmeCtrl *n, PCIDevice *pci_dev, Error **errp)
|
||||||
{
|
{
|
||||||
ERRP_GUARD();
|
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);
|
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) {
|
if (n->params.cmb_size_mb) {
|
||||||
nvme_init_cmb(n, pci_dev);
|
nvme_init_cmb(n, pci_dev);
|
||||||
}
|
}
|
||||||
|
@ -8650,6 +8691,11 @@ static void nvme_exit(PCIDevice *pci_dev)
|
||||||
g_free(n->cmb.buf);
|
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) {
|
if (n->pmr.dev) {
|
||||||
host_memory_backend_set_mapped(n->pmr.dev, false);
|
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,
|
DEFINE_PROP_BOOL("msix-exclusive-bar", NvmeCtrl, params.msix_exclusive_bar,
|
||||||
false),
|
false),
|
||||||
DEFINE_PROP_UINT16("mqes", NvmeCtrl, params.mqes, 0x7ff),
|
DEFINE_PROP_UINT16("mqes", NvmeCtrl, params.mqes, 0x7ff),
|
||||||
|
DEFINE_PROP_UINT16("spdm_port", PCIDevice, spdm_port, 0),
|
||||||
DEFINE_PROP_END_OF_LIST(),
|
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);
|
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);
|
pci_default_write_config(dev, address, val, len);
|
||||||
pcie_cap_flr_write_config(dev, address, val, len);
|
pcie_cap_flr_write_config(dev, address, val, len);
|
||||||
nvme_sriov_post_write_config(dev, old_num_vfs);
|
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 = {
|
static const VMStateDescription nvme_vmstate = {
|
||||||
.name = "nvme",
|
.name = "nvme",
|
||||||
.unmigratable = 1,
|
.unmigratable = 1,
|
||||||
|
@ -8783,6 +8844,7 @@ static void nvme_class_init(ObjectClass *oc, void *data)
|
||||||
|
|
||||||
pc->realize = nvme_realize;
|
pc->realize = nvme_realize;
|
||||||
pc->config_write = nvme_pci_write_config;
|
pc->config_write = nvme_pci_write_config;
|
||||||
|
pc->config_read = nvme_pci_read_config;
|
||||||
pc->exit = nvme_exit;
|
pc->exit = nvme_exit;
|
||||||
pc->class_id = PCI_CLASS_STORAGE_EXPRESS;
|
pc->class_id = PCI_CLASS_STORAGE_EXPRESS;
|
||||||
pc->revision = 2;
|
pc->revision = 2;
|
||||||
|
|
|
@ -7,7 +7,8 @@
|
||||||
#include "hw/pci/pcie_host.h"
|
#include "hw/pci/pcie_host.h"
|
||||||
#include "hw/acpi/cxl.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;
|
Aml *method, *crs;
|
||||||
int i, slot_no;
|
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 *pkg = aml_package(4);
|
||||||
aml_append(pkg, aml_int((slot_no << 16) | 0xFFFF));
|
aml_append(pkg, aml_int((slot_no << 16) | 0xFFFF));
|
||||||
aml_append(pkg, aml_int(i));
|
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(pkg, aml_int(0));
|
||||||
aml_append(rt_pkg, pkg);
|
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 */
|
/* Create GSI link device */
|
||||||
for (i = 0; i < PCI_NUM_PINS; i++) {
|
for (i = 0; i < PCI_NUM_PINS; i++) {
|
||||||
uint32_t irqs = irq + 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("_HID", aml_string("PNP0C0F")));
|
||||||
aml_append(dev_gsi, aml_name_decl("_UID", aml_int(i)));
|
aml_append(dev_gsi, aml_name_decl("_UID", aml_int(i)));
|
||||||
crs = aml_resource_template();
|
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));
|
aml_append(dev_gsi, aml_name_decl("_CRS", crs));
|
||||||
method = aml_method("_SRS", 1, AML_NOTSERIALIZED);
|
method = aml_method("_SRS", 1, AML_NOTSERIALIZED);
|
||||||
aml_append(dev_gsi, method);
|
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)));
|
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:
|
* 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("_STR", aml_unicode("PCIe 0 Device")));
|
||||||
aml_append(dev, aml_name_decl("_CCA", aml_int(1)));
|
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);
|
method = aml_method("_CBA", 0, AML_NOTSERIALIZED);
|
||||||
aml_append(method, aml_return(aml_int(cfg->ecam.base)));
|
aml_append(method, aml_return(aml_int(cfg->ecam.base)));
|
||||||
|
|
40
hw/pci/pci.c
40
hw/pci/pci.c
|
@ -85,6 +85,7 @@ static Property pci_props[] = {
|
||||||
QEMU_PCIE_ERR_UNC_MASK_BITNR, true),
|
QEMU_PCIE_ERR_UNC_MASK_BITNR, true),
|
||||||
DEFINE_PROP_BIT("x-pcie-ari-nextfn-1", PCIDevice, cap_present,
|
DEFINE_PROP_BIT("x-pcie-ari-nextfn-1", PCIDevice, cap_present,
|
||||||
QEMU_PCIE_ARI_NEXTFN_1_BITNR, false),
|
QEMU_PCIE_ARI_NEXTFN_1_BITNR, false),
|
||||||
|
DEFINE_PROP_STRING("sriov-pf", PCIDevice, sriov_pf),
|
||||||
DEFINE_PROP_END_OF_LIST()
|
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;
|
dev->config[PCI_HEADER_TYPE] |= PCI_HEADER_TYPE_MULTI_FUNCTION;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/* SR/IOV is not handled here. */
|
||||||
* With SR/IOV and ARI, a device at function 0 need not be a multifunction
|
if (pci_is_vf(dev)) {
|
||||||
* 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) {
|
|
||||||
return;
|
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 */
|
/* function 0 indicates single function, so function > 0 must be NULL */
|
||||||
for (func = 1; func < PCI_FUNC_MAX; ++func) {
|
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, "
|
error_setg(errp, "PCI: %x.0 indicates single function, "
|
||||||
"but %x.%x is already populated.",
|
"but %x.%x is already populated.",
|
||||||
slot, slot, func);
|
slot, slot, func);
|
||||||
|
@ -1283,6 +1280,7 @@ static void pci_qdev_unrealize(DeviceState *dev)
|
||||||
|
|
||||||
pci_unregister_io_regions(pci_dev);
|
pci_unregister_io_regions(pci_dev);
|
||||||
pci_del_option_rom(pci_dev);
|
pci_del_option_rom(pci_dev);
|
||||||
|
pcie_sriov_unregister_device(pci_dev);
|
||||||
|
|
||||||
if (pc->exit) {
|
if (pc->exit) {
|
||||||
pc->exit(pci_dev);
|
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);
|
pcibus_t size = memory_region_size(memory);
|
||||||
uint8_t hdr_type;
|
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 >= 0);
|
||||||
assert(region_num < PCI_NUM_REGIONS);
|
assert(region_num < PCI_NUM_REGIONS);
|
||||||
assert(is_power_of_2(size));
|
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);
|
assert(hdr_type != PCI_HEADER_TYPE_BRIDGE || region_num < 2);
|
||||||
|
|
||||||
r = &pci_dev->io_regions[region_num];
|
r = &pci_dev->io_regions[region_num];
|
||||||
r->addr = PCI_BAR_UNMAPPED;
|
|
||||||
r->size = size;
|
r->size = size;
|
||||||
r->type = type;
|
r->type = type;
|
||||||
r->memory = memory;
|
r->memory = memory;
|
||||||
|
@ -1333,6 +1329,18 @@ 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_io
|
||||||
: pci_get_bus(pci_dev)->address_space_mem;
|
: pci_get_bus(pci_dev)->address_space_mem;
|
||||||
|
|
||||||
|
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]);
|
||||||
|
|
||||||
|
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 {
|
||||||
|
r->addr = PCI_BAR_UNMAPPED;
|
||||||
|
|
||||||
wmask = ~(size - 1);
|
wmask = ~(size - 1);
|
||||||
if (region_num == PCI_ROM_SLOT) {
|
if (region_num == PCI_ROM_SLOT) {
|
||||||
/* ROM enable bit is writable */
|
/* ROM enable bit is writable */
|
||||||
|
@ -1350,6 +1358,7 @@ void pci_register_bar(PCIDevice *pci_dev, int region_num,
|
||||||
pci_set_long(pci_dev->wmask + addr, wmask & 0xffffffff);
|
pci_set_long(pci_dev->wmask + addr, wmask & 0xffffffff);
|
||||||
pci_set_long(pci_dev->cmask + addr, 0xffffffff);
|
pci_set_long(pci_dev->cmask + addr, 0xffffffff);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void pci_update_vga(PCIDevice *pci_dev)
|
static void pci_update_vga(PCIDevice *pci_dev)
|
||||||
|
@ -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);
|
pci_get_word(pf->config + sriov_cap + PCI_SRIOV_VF_OFFSET);
|
||||||
uint16_t vf_stride =
|
uint16_t vf_stride =
|
||||||
pci_get_word(pf->config + sriov_cap + PCI_SRIOV_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) {
|
if (type & PCI_BASE_ADDRESS_MEM_TYPE_64) {
|
||||||
new_addr = pci_get_quad(pf->config + bar);
|
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
|
* A PCIe Downstream Port that do not have ARI Forwarding enabled must
|
||||||
* associate only Device 0 with the device attached to the bus
|
* associate only Device 0 with the device attached to the bus
|
||||||
|
|
|
@ -20,6 +20,8 @@
|
||||||
#include "qapi/error.h"
|
#include "qapi/error.h"
|
||||||
#include "trace.h"
|
#include "trace.h"
|
||||||
|
|
||||||
|
static GHashTable *pfs;
|
||||||
|
|
||||||
static void unparent_vfs(PCIDevice *dev, uint16_t total_vfs)
|
static void unparent_vfs(PCIDevice *dev, uint16_t total_vfs)
|
||||||
{
|
{
|
||||||
for (uint16_t i = 0; i < total_vfs; i++) {
|
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;
|
dev->exp.sriov_pf.vf = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool pcie_sriov_pf_init(PCIDevice *dev, uint16_t offset,
|
static void clear_ctrl_vfe(PCIDevice *dev)
|
||||||
const char *vfname, uint16_t vf_dev_id,
|
{
|
||||||
uint16_t init_vfs, uint16_t total_vfs,
|
uint8_t *ctrl = dev->config + dev->exp.sriov_cap + PCI_SRIOV_CTRL;
|
||||||
uint16_t vf_offset, uint16_t vf_stride,
|
pci_set_word(ctrl, pci_get_word(ctrl) & ~PCI_SRIOV_CTRL_VFE);
|
||||||
Error **errp)
|
}
|
||||||
|
|
||||||
|
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 *cfg = dev->config + offset;
|
||||||
uint8_t *wmask;
|
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) {
|
if (total_vfs) {
|
||||||
uint16_t ari_cap = pcie_find_capability(dev, PCI_EXT_CAP_ID_ARI);
|
uint16_t ari_cap = pcie_find_capability(dev, PCI_EXT_CAP_ID_ARI);
|
||||||
uint16_t first_vf_devfn = dev->devfn + vf_offset;
|
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);
|
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);
|
dev->exp.sriov_pf.vf = g_new(PCIDevice *, total_vfs);
|
||||||
|
|
||||||
for (uint16_t i = 0; i < total_vfs; i++) {
|
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;
|
uint8_t *cfg = dev->config + dev->exp.sriov_cap;
|
||||||
|
|
||||||
|
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));
|
unparent_vfs(dev, pci_get_word(cfg + PCI_SRIOV_TOTAL_VF));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void pcie_sriov_pf_init_vf_bar(PCIDevice *dev, int region_num,
|
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,
|
void pcie_sriov_vf_register_bar(PCIDevice *dev, int region_num,
|
||||||
MemoryRegion *memory)
|
MemoryRegion *memory)
|
||||||
{
|
{
|
||||||
PCIIORegion *r;
|
|
||||||
PCIBus *bus = pci_get_bus(dev);
|
|
||||||
uint8_t type;
|
uint8_t type;
|
||||||
pcibus_t size = memory_region_size(memory);
|
|
||||||
|
|
||||||
assert(pci_is_vf(dev)); /* PFs must use pci_register_bar */
|
assert(dev->exp.sriov_vf.pf);
|
||||||
assert(region_num >= 0);
|
|
||||||
assert(region_num < PCI_NUM_REGIONS);
|
|
||||||
type = dev->exp.sriov_vf.pf->exp.sriov_pf.vf_bar_type[region_num];
|
type = dev->exp.sriov_vf.pf->exp.sriov_pf.vf_bar_type[region_num];
|
||||||
|
|
||||||
if (!is_power_of_2(size)) {
|
return pci_register_bar(dev, region_num, type, memory);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
return (*(PCIDevice **)a)->devfn - (*(PCIDevice **)b)->devfn;
|
||||||
pci_set_word(ctrl, pci_get_word(ctrl) & ~PCI_SRIOV_CTRL_VFE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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 i;
|
||||||
uint16_t sriov_cap = dev->exp.sriov_cap;
|
|
||||||
|
|
||||||
assert(sriov_cap > 0);
|
if (!pfs || !dev->qdev.id) {
|
||||||
num_vfs = pci_get_word(dev->config + sriov_cap + PCI_SRIOV_NUM_VF);
|
return 0;
|
||||||
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),
|
pf = g_hash_table_lookup(pfs, dev->qdev.id);
|
||||||
PCI_FUNC(dev->devfn), num_vfs);
|
if (!pf) {
|
||||||
for (i = 0; i < num_vfs; i++) {
|
return 0;
|
||||||
pci_set_enabled(dev->exp.sriov_pf.vf[i], true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
if (!dev->exp.sriov_pf.vf && dev->qdev.id &&
|
||||||
uint8_t *cfg = dev->config + dev->exp.sriov_cap;
|
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),
|
if (dev->sriov_pf) {
|
||||||
PCI_FUNC(dev->devfn));
|
PCIDevice *pci_pf;
|
||||||
for (i = 0; i < pci_get_word(cfg + PCI_SRIOV_TOTAL_VF); i++) {
|
GPtrArray *pf;
|
||||||
pci_set_enabled(dev->exp.sriov_pf.vf[i], false);
|
|
||||||
|
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)
|
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;
|
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
|
static void
|
||||||
acpi_dsdt_add_uart(Aml *scope, const MemMapEntry *uart_memmap,
|
acpi_dsdt_add_uart(Aml *scope, const MemMapEntry *uart_memmap,
|
||||||
uint32_t uart_irq)
|
uint32_t uart_irq)
|
||||||
{
|
{
|
||||||
Aml *dev = aml_device("COM0");
|
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_append(dev, aml_name_decl("_UID", aml_int(0)));
|
||||||
|
|
||||||
Aml *crs = aml_resource_template();
|
Aml *crs = aml_resource_template();
|
||||||
|
@ -411,6 +435,14 @@ static void build_dsdt(GArray *table_data,
|
||||||
|
|
||||||
socket_count = riscv_socket_count(ms);
|
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);
|
acpi_dsdt_add_uart(scope, &memmap[VIRT_UART0], UART0_IRQ);
|
||||||
|
|
||||||
if (socket_count == 1) {
|
if (socket_count == 1) {
|
||||||
|
|
|
@ -38,6 +38,7 @@ static const int kernel_feature_bits[] = {
|
||||||
VIRTIO_RING_F_EVENT_IDX,
|
VIRTIO_RING_F_EVENT_IDX,
|
||||||
VIRTIO_SCSI_F_HOTPLUG,
|
VIRTIO_SCSI_F_HOTPLUG,
|
||||||
VIRTIO_F_RING_RESET,
|
VIRTIO_F_RING_RESET,
|
||||||
|
VIRTIO_F_IN_ORDER,
|
||||||
VIRTIO_F_NOTIFICATION_DATA,
|
VIRTIO_F_NOTIFICATION_DATA,
|
||||||
VHOST_INVALID_FEATURE_BIT
|
VHOST_INVALID_FEATURE_BIT
|
||||||
};
|
};
|
||||||
|
|
|
@ -36,6 +36,7 @@ static const int user_feature_bits[] = {
|
||||||
VIRTIO_RING_F_EVENT_IDX,
|
VIRTIO_RING_F_EVENT_IDX,
|
||||||
VIRTIO_SCSI_F_HOTPLUG,
|
VIRTIO_SCSI_F_HOTPLUG,
|
||||||
VIRTIO_F_RING_RESET,
|
VIRTIO_F_RING_RESET,
|
||||||
|
VIRTIO_F_IN_ORDER,
|
||||||
VIRTIO_F_NOTIFICATION_DATA,
|
VIRTIO_F_NOTIFICATION_DATA,
|
||||||
VHOST_INVALID_FEATURE_BIT
|
VHOST_INVALID_FEATURE_BIT
|
||||||
};
|
};
|
||||||
|
|
|
@ -1093,6 +1093,7 @@ static bool smbios_get_tables_ep(MachineState *ms,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
unsigned i, dimm_cnt, offset;
|
unsigned i, dimm_cnt, offset;
|
||||||
|
MachineClass *mc = MACHINE_GET_CLASS(ms);
|
||||||
ERRP_GUARD();
|
ERRP_GUARD();
|
||||||
|
|
||||||
assert(ep_type == SMBIOS_ENTRY_POINT_TYPE_32 ||
|
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_9_table(errp);
|
||||||
smbios_build_type_11_table();
|
smbios_build_type_11_table();
|
||||||
|
|
||||||
#define MAX_DIMM_SZ (16 * GiB)
|
#define GET_DIMM_SZ ((i < dimm_cnt - 1) ? mc->smbios_memory_device_size \
|
||||||
#define GET_DIMM_SZ ((i < dimm_cnt - 1) ? MAX_DIMM_SZ \
|
: ((current_machine->ram_size - 1) % mc->smbios_memory_device_size) + 1)
|
||||||
: ((current_machine->ram_size - 1) % MAX_DIMM_SZ) + 1)
|
|
||||||
|
|
||||||
dimm_cnt = QEMU_ALIGN_UP(current_machine->ram_size, MAX_DIMM_SZ) /
|
dimm_cnt = QEMU_ALIGN_UP(current_machine->ram_size,
|
||||||
MAX_DIMM_SZ;
|
mc->smbios_memory_device_size) /
|
||||||
|
mc->smbios_memory_device_size;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The offset determines if we need to keep additional space between
|
* 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);
|
IOMMUMemoryRegion *iommu_mr = IOMMU_MEMORY_REGION(section->mr);
|
||||||
int iommu_idx;
|
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
|
* FIXME: For VFIO iommu types which have KVM acceleration to
|
||||||
* avoid bouncing all map/unmaps through qemu this way, this
|
* 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)) {
|
if (memory_region_is_iommu(section->mr)) {
|
||||||
VFIOGuestIOMMU *giommu;
|
VFIOGuestIOMMU *giommu;
|
||||||
|
|
||||||
|
trace_vfio_listener_region_del_iommu(section->mr->name);
|
||||||
QLIST_FOREACH(giommu, &bcontainer->giommu_list, giommu_next) {
|
QLIST_FOREACH(giommu, &bcontainer->giommu_list, giommu_next) {
|
||||||
if (MEMORY_REGION(giommu->iommu_mr) == section->mr &&
|
if (MEMORY_REGION(giommu->iommu_mr) == section->mr &&
|
||||||
giommu->n.start == section->offset_within_region) {
|
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_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_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_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_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_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"
|
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_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_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(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_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(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
|
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_RING_PACKED,
|
||||||
VIRTIO_F_IOMMU_PLATFORM,
|
VIRTIO_F_IOMMU_PLATFORM,
|
||||||
VIRTIO_F_RING_RESET,
|
VIRTIO_F_RING_RESET,
|
||||||
|
VIRTIO_F_IN_ORDER,
|
||||||
VIRTIO_F_NOTIFICATION_DATA,
|
VIRTIO_F_NOTIFICATION_DATA,
|
||||||
VHOST_INVALID_FEATURE_BIT
|
VHOST_INVALID_FEATURE_BIT
|
||||||
};
|
};
|
||||||
|
|
|
@ -21,6 +21,7 @@ static const int user_feature_bits[] = {
|
||||||
VIRTIO_RING_F_INDIRECT_DESC,
|
VIRTIO_RING_F_INDIRECT_DESC,
|
||||||
VIRTIO_RING_F_EVENT_IDX,
|
VIRTIO_RING_F_EVENT_IDX,
|
||||||
VIRTIO_F_NOTIFY_ON_EMPTY,
|
VIRTIO_F_NOTIFY_ON_EMPTY,
|
||||||
|
VIRTIO_F_IN_ORDER,
|
||||||
VIRTIO_F_NOTIFICATION_DATA,
|
VIRTIO_F_NOTIFICATION_DATA,
|
||||||
VHOST_INVALID_FEATURE_BIT
|
VHOST_INVALID_FEATURE_BIT
|
||||||
};
|
};
|
||||||
|
|
|
@ -205,6 +205,7 @@ virtio_crypto_create_asym_session(VirtIOCrypto *vcrypto,
|
||||||
int queue_index;
|
int queue_index;
|
||||||
uint32_t algo, keytype, keylen;
|
uint32_t algo, keytype, keylen;
|
||||||
|
|
||||||
|
sreq->info.op_code = opcode;
|
||||||
algo = ldl_le_p(&sess_req->para.algo);
|
algo = ldl_le_p(&sess_req->para.algo);
|
||||||
keytype = ldl_le_p(&sess_req->para.keytype);
|
keytype = ldl_le_p(&sess_req->para.keytype);
|
||||||
keylen = ldl_le_p(&sess_req->para.keylen);
|
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);
|
iov_discard_front(&iov, &out_num, keylen);
|
||||||
}
|
}
|
||||||
|
|
||||||
sreq->info.op_code = opcode;
|
|
||||||
asym_info = &sreq->info.u.asym_sess_info;
|
asym_info = &sreq->info.u.asym_sess_info;
|
||||||
asym_info->algo = algo;
|
asym_info->algo = algo;
|
||||||
asym_info->keytype = keytype;
|
asym_info->keytype = keytype;
|
||||||
|
|
|
@ -308,6 +308,7 @@ static void virtio_iommu_detach_endpoint_from_domain(VirtIOIOMMUEndpoint *ep)
|
||||||
if (!ep->domain) {
|
if (!ep->domain) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
trace_virtio_iommu_detach_endpoint_from_domain(domain->id, ep->id);
|
||||||
g_tree_foreach(domain->mappings, virtio_iommu_notify_unmap_cb,
|
g_tree_foreach(domain->mappings, virtio_iommu_notify_unmap_cb,
|
||||||
ep->iommu_mr);
|
ep->iommu_mr);
|
||||||
QLIST_REMOVE(ep, next);
|
QLIST_REMOVE(ep, next);
|
||||||
|
@ -467,26 +468,6 @@ static AddressSpace *virtio_iommu_find_add_as(PCIBus *bus, void *opaque,
|
||||||
return &sdev->as;
|
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)
|
static gboolean hiod_equal(gconstpointer v1, gconstpointer v2)
|
||||||
{
|
{
|
||||||
const struct hiod_key *key1 = v1;
|
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);
|
IOMMUPciBus *sbus = g_hash_table_lookup(s->as_by_busptr, bus);
|
||||||
IOMMUDevice *sdev;
|
IOMMUDevice *sdev;
|
||||||
GList *current_ranges;
|
|
||||||
GList *l, *tmp, *new_ranges = NULL;
|
|
||||||
int ret = -EINVAL;
|
int ret = -EINVAL;
|
||||||
|
|
||||||
if (!sbus) {
|
if (!sbus) {
|
||||||
|
@ -573,35 +552,10 @@ static int virtio_iommu_set_host_iova_ranges(VirtIOIOMMU *s, PCIBus *bus,
|
||||||
return ret;
|
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) {
|
if (sdev->host_resv_ranges) {
|
||||||
range_inverse_array(iova_ranges,
|
error_setg(errp, "%s virtio-iommu does not support aliased BDF",
|
||||||
&new_ranges,
|
__func__);
|
||||||
0, UINT64_MAX);
|
return ret;
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
range_inverse_array(iova_ranges,
|
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);
|
rebuild_resv_regions(sdev);
|
||||||
|
|
||||||
return 0;
|
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,
|
static bool check_page_size_mask(VirtIOIOMMU *viommu, uint64_t new_mask,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
|
@ -726,9 +697,10 @@ virtio_iommu_unset_iommu_device(PCIBus *bus, void *opaque, int devfn)
|
||||||
if (!hiod) {
|
if (!hiod) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
virtio_iommu_unset_host_iova_ranges(viommu, hiod->aliased_bus,
|
||||||
|
hiod->aliased_devfn);
|
||||||
|
|
||||||
g_hash_table_remove(viommu->host_iommu_devices, &key);
|
g_hash_table_remove(viommu->host_iommu_devices, &key);
|
||||||
virtio_iommu_device_clear(viommu, bus, devfn);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static const PCIIOMMUOps virtio_iommu_ops = {
|
static const PCIIOMMUOps virtio_iommu_ops = {
|
||||||
|
@ -815,6 +787,7 @@ static int virtio_iommu_detach(VirtIOIOMMU *s,
|
||||||
if (QLIST_EMPTY(&domain->endpoint_list)) {
|
if (QLIST_EMPTY(&domain->endpoint_list)) {
|
||||||
g_tree_remove(s->domains, GUINT_TO_POINTER(domain->id));
|
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;
|
return VIRTIO_IOMMU_S_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -977,7 +950,6 @@ static int virtio_iommu_probe(VirtIOIOMMU *s,
|
||||||
}
|
}
|
||||||
buf += count;
|
buf += count;
|
||||||
free -= count;
|
free -= count;
|
||||||
sdev->probe_done = true;
|
|
||||||
|
|
||||||
return VIRTIO_IOMMU_S_OK;
|
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->device_id = PCI_DEVICE_ID_VIRTIO_NET;
|
||||||
k->revision = VIRTIO_PCI_ABI_VERSION;
|
k->revision = VIRTIO_PCI_ABI_VERSION;
|
||||||
k->class_id = PCI_CLASS_NETWORK_ETHERNET;
|
k->class_id = PCI_CLASS_NETWORK_ETHERNET;
|
||||||
|
k->sriov_vf_user_creatable = true;
|
||||||
set_bit(DEVICE_CATEGORY_NETWORK, dc->categories);
|
set_bit(DEVICE_CATEGORY_NETWORK, dc->categories);
|
||||||
device_class_set_props(dc, virtio_net_properties);
|
device_class_set_props(dc, virtio_net_properties);
|
||||||
vpciklass->realize = virtio_net_pci_realize;
|
vpciklass->realize = virtio_net_pci_realize;
|
||||||
|
|
|
@ -1955,6 +1955,7 @@ static void virtio_pci_device_plugged(DeviceState *d, Error **errp)
|
||||||
uint8_t *config;
|
uint8_t *config;
|
||||||
uint32_t size;
|
uint32_t size;
|
||||||
VirtIODevice *vdev = virtio_bus_get_device(bus);
|
VirtIODevice *vdev = virtio_bus_get_device(bus);
|
||||||
|
int16_t res;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Virtio capabilities present without
|
* 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_register_bar(&proxy->pci_dev, proxy->legacy_io_bar_idx,
|
||||||
PCI_BASE_ADDRESS_SPACE_IO, &proxy->bar);
|
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)
|
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)) {
|
if (pcie_port && pci_is_express(pci_dev)) {
|
||||||
int pos;
|
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);
|
pos = pcie_endpoint_cap_init(pci_dev, 0);
|
||||||
assert(pos > 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);
|
pci_set_word(pci_dev->config + pos + PCI_PM_PMC, 0x3);
|
||||||
|
|
||||||
if (proxy->flags & VIRTIO_PCI_FLAG_AER) {
|
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);
|
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) {
|
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) {
|
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);
|
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) {
|
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)) &&
|
bool pcie_port = pci_bus_is_express(pci_get_bus(pci_dev)) &&
|
||||||
!pci_bus_is_root(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);
|
msix_uninit_exclusive_bar(pci_dev);
|
||||||
if (proxy->flags & VIRTIO_PCI_FLAG_AER && pcie_port &&
|
if (proxy->flags & VIRTIO_PCI_FLAG_AER && pcie_port &&
|
||||||
pci_is_express(pci_dev)) {
|
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;
|
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,
|
static void virtqueue_packed_fill_desc(VirtQueue *vq,
|
||||||
const VirtQueueElement *elem,
|
const VirtQueueElement *elem,
|
||||||
unsigned int idx,
|
unsigned int idx,
|
||||||
|
@ -922,7 +962,9 @@ void virtqueue_fill(VirtQueue *vq, const VirtQueueElement *elem,
|
||||||
return;
|
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);
|
virtqueue_packed_fill(vq, elem, len, idx);
|
||||||
} else {
|
} else {
|
||||||
virtqueue_split_fill(vq, elem, len, idx);
|
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)
|
void virtqueue_flush(VirtQueue *vq, unsigned int count)
|
||||||
{
|
{
|
||||||
if (virtio_device_disabled(vq->vdev)) {
|
if (virtio_device_disabled(vq->vdev)) {
|
||||||
|
@ -988,7 +1097,9 @@ void virtqueue_flush(VirtQueue *vq, unsigned int count)
|
||||||
return;
|
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);
|
virtqueue_packed_flush(vq, count);
|
||||||
} else {
|
} else {
|
||||||
virtqueue_split_flush(vq, count);
|
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)
|
static void *virtqueue_split_pop(VirtQueue *vq, size_t sz)
|
||||||
{
|
{
|
||||||
unsigned int i, head, max;
|
unsigned int i, head, max, idx;
|
||||||
VRingMemoryRegionCaches *caches;
|
VRingMemoryRegionCaches *caches;
|
||||||
MemoryRegionCache indirect_desc_cache;
|
MemoryRegionCache indirect_desc_cache;
|
||||||
MemoryRegionCache *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];
|
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++;
|
vq->inuse++;
|
||||||
|
|
||||||
trace_virtqueue_pop(vq, elem, elem->in_num, elem->out_num);
|
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->index = id;
|
||||||
elem->ndescs = (desc_cache == &indirect_desc_cache) ? 1 : elem_entries;
|
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->last_avail_idx += elem->ndescs;
|
||||||
vq->inuse += 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,
|
void cpu_address_space_init(CPUState *cpu, int asidx,
|
||||||
const char *prefix, MemoryRegion *mr);
|
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,
|
void cpu_physical_memory_rw(hwaddr addr, void *buf,
|
||||||
hwaddr len, bool is_write);
|
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,
|
gdb_get_reg_cb get_reg, gdb_set_reg_cb set_reg,
|
||||||
const GDBFeature *feature, int g_pos);
|
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
|
* gdbserver_start: start the gdb server
|
||||||
* @port_or_device: connection spec for gdb
|
* @port_or_device: connection spec for gdb
|
||||||
|
|
|
@ -19,6 +19,8 @@
|
||||||
#include "hw/boards.h"
|
#include "hw/boards.h"
|
||||||
#include "hw/hotplug.h"
|
#include "hw/hotplug.h"
|
||||||
|
|
||||||
|
#define ACPI_CPU_HOTPLUG_REG_LEN 12
|
||||||
|
|
||||||
typedef struct AcpiCpuStatus {
|
typedef struct AcpiCpuStatus {
|
||||||
CPUState *cpu;
|
CPUState *cpu;
|
||||||
uint64_t arch_id;
|
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);
|
GArray *entry, bool force_enabled);
|
||||||
|
|
||||||
void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts,
|
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 *res_root,
|
||||||
const char *event_handler_method);
|
const char *event_handler_method,
|
||||||
|
AmlRegionSpace rs);
|
||||||
|
|
||||||
void acpi_cpu_ospm_status(CPUHotplugState *cpu_st, ACPIOSTInfoList ***list);
|
void acpi_cpu_ospm_status(CPUHotplugState *cpu_st, ACPIOSTInfoList ***list);
|
||||||
|
|
||||||
|
|
|
@ -62,6 +62,7 @@
|
||||||
#include "hw/sysbus.h"
|
#include "hw/sysbus.h"
|
||||||
#include "hw/acpi/memory_hotplug.h"
|
#include "hw/acpi/memory_hotplug.h"
|
||||||
#include "hw/acpi/ghes.h"
|
#include "hw/acpi/ghes.h"
|
||||||
|
#include "hw/acpi/cpu.h"
|
||||||
#include "qom/object.h"
|
#include "qom/object.h"
|
||||||
|
|
||||||
#define ACPI_POWER_BUTTON_DEVICE "PWRB"
|
#define ACPI_POWER_BUTTON_DEVICE "PWRB"
|
||||||
|
@ -86,6 +87,7 @@ OBJECT_DECLARE_SIMPLE_TYPE(AcpiGedState, ACPI_GED)
|
||||||
#define GED_DEVICE "GED"
|
#define GED_DEVICE "GED"
|
||||||
#define AML_GED_EVT_REG "EREG"
|
#define AML_GED_EVT_REG "EREG"
|
||||||
#define AML_GED_EVT_SEL "ESEL"
|
#define AML_GED_EVT_SEL "ESEL"
|
||||||
|
#define AML_GED_EVT_CPU_SCAN_METHOD "\\_SB.GED.CSCN"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Platforms need to specify the GED event bitmap
|
* 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_MEM_HOTPLUG_EVT 0x1
|
||||||
#define ACPI_GED_PWR_DOWN_EVT 0x2
|
#define ACPI_GED_PWR_DOWN_EVT 0x2
|
||||||
#define ACPI_GED_NVDIMM_HOTPLUG_EVT 0x4
|
#define ACPI_GED_NVDIMM_HOTPLUG_EVT 0x4
|
||||||
|
#define ACPI_GED_CPU_HOTPLUG_EVT 0x8
|
||||||
|
|
||||||
typedef struct GEDState {
|
typedef struct GEDState {
|
||||||
MemoryRegion evt;
|
MemoryRegion evt;
|
||||||
|
@ -106,6 +109,8 @@ struct AcpiGedState {
|
||||||
SysBusDevice parent_obj;
|
SysBusDevice parent_obj;
|
||||||
MemHotplugState memhp_state;
|
MemHotplugState memhp_state;
|
||||||
MemoryRegion container_memhp;
|
MemoryRegion container_memhp;
|
||||||
|
CPUHotplugState cpuhp_state;
|
||||||
|
MemoryRegion container_cpuhp;
|
||||||
GEDState ged_state;
|
GEDState ged_state;
|
||||||
uint32_t ged_event_bitmap;
|
uint32_t ged_event_bitmap;
|
||||||
qemu_irq irq;
|
qemu_irq irq;
|
||||||
|
|
|
@ -237,6 +237,9 @@ typedef struct {
|
||||||
* purposes only.
|
* purposes only.
|
||||||
* Applies only to default memory backend, i.e., explicit memory backend
|
* Applies only to default memory backend, i.e., explicit memory backend
|
||||||
* wasn't used.
|
* wasn't used.
|
||||||
|
* @smbios_memory_device_size:
|
||||||
|
* Default size of memory device,
|
||||||
|
* SMBIOS 3.1.0 "7.18 Memory Device (Type 17)"
|
||||||
*/
|
*/
|
||||||
struct MachineClass {
|
struct MachineClass {
|
||||||
/*< private >*/
|
/*< private >*/
|
||||||
|
@ -304,6 +307,7 @@ struct MachineClass {
|
||||||
const CPUArchIdList *(*possible_cpu_arch_ids)(MachineState *machine);
|
const CPUArchIdList *(*possible_cpu_arch_ids)(MachineState *machine);
|
||||||
int64_t (*get_default_cpu_node_id)(const MachineState *ms, int idx);
|
int64_t (*get_default_cpu_node_id)(const MachineState *ms, int idx);
|
||||||
ram_addr_t (*fixup_ram_size)(ram_addr_t size);
|
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;
|
QSIMPLEQ_HEAD(, qemu_work_item) work_list;
|
||||||
|
|
||||||
struct CPUAddressSpace *cpu_ases;
|
struct CPUAddressSpace *cpu_ases;
|
||||||
|
int cpu_ases_count;
|
||||||
int num_ases;
|
int num_ases;
|
||||||
AddressSpace *as;
|
AddressSpace *as;
|
||||||
MemoryRegion *memory;
|
MemoryRegion *memory;
|
||||||
|
|
|
@ -181,6 +181,21 @@ typedef struct CXLCCI {
|
||||||
uint64_t runtime;
|
uint64_t runtime;
|
||||||
QEMUTimer *timer;
|
QEMUTimer *timer;
|
||||||
} bg;
|
} 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;
|
size_t payload_max;
|
||||||
/* Pointer to device hosting the CCI */
|
/* Pointer to device hosting the CCI */
|
||||||
DeviceState *d;
|
DeviceState *d;
|
||||||
|
@ -397,9 +412,14 @@ static inline void __toggle_media(CXLDeviceState *cxl_dstate, int val)
|
||||||
#define cxl_dev_enable_media(cxlds) \
|
#define cxl_dev_enable_media(cxlds) \
|
||||||
do { __toggle_media((cxlds), 0x1); } while (0)
|
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 {
|
typedef struct CXLError {
|
||||||
|
@ -422,6 +442,47 @@ typedef struct CXLPoison {
|
||||||
typedef QLIST_HEAD(, CXLPoison) CXLPoisonList;
|
typedef QLIST_HEAD(, CXLPoison) CXLPoisonList;
|
||||||
#define CXL_POISON_LIST_LIMIT 256
|
#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
|
#define DCD_MAX_NUM_REGION 8
|
||||||
|
|
||||||
typedef struct CXLDCExtentRaw {
|
typedef struct CXLDCExtentRaw {
|
||||||
|
@ -459,6 +520,14 @@ typedef struct CXLDCRegion {
|
||||||
unsigned long *blk_bitmap;
|
unsigned long *blk_bitmap;
|
||||||
} CXLDCRegion;
|
} 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 {
|
struct CXLType3Dev {
|
||||||
/* Private */
|
/* Private */
|
||||||
PCIDevice parent_obj;
|
PCIDevice parent_obj;
|
||||||
|
@ -491,6 +560,19 @@ struct CXLType3Dev {
|
||||||
unsigned int poison_list_cnt;
|
unsigned int poison_list_cnt;
|
||||||
bool poison_list_overflowed;
|
bool poison_list_overflowed;
|
||||||
uint64_t poison_list_overflow_ts;
|
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 {
|
struct dynamic_capacity {
|
||||||
HostMemoryBackend *host_dc;
|
HostMemoryBackend *host_dc;
|
||||||
|
@ -554,10 +636,12 @@ CXLRetCode cxl_event_get_records(CXLDeviceState *cxlds, CXLGetEventPayload *pl,
|
||||||
size_t *len);
|
size_t *len);
|
||||||
CXLRetCode cxl_event_clear_records(CXLDeviceState *cxlds,
|
CXLRetCode cxl_event_clear_records(CXLDeviceState *cxlds,
|
||||||
CXLClearEventPayload *pl);
|
CXLClearEventPayload *pl);
|
||||||
|
void cxl_discard_all_event_records(CXLDeviceState *cxlds);
|
||||||
|
|
||||||
void cxl_event_irq_assert(CXLType3Dev *ct3d);
|
void cxl_event_irq_assert(CXLType3Dev *ct3d);
|
||||||
|
|
||||||
void cxl_set_poison_list_overflowed(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);
|
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/pci.h"
|
||||||
#include "hw/pci/pcie.h"
|
#include "hw/pci/pcie.h"
|
||||||
|
#include "hw/pci/pcie_doe.h"
|
||||||
|
|
||||||
#define TYPE_PCI_DEVICE "pci-device"
|
#define TYPE_PCI_DEVICE "pci-device"
|
||||||
typedef struct PCIDeviceClass PCIDeviceClass;
|
typedef struct PCIDeviceClass PCIDeviceClass;
|
||||||
|
@ -37,6 +38,8 @@ struct PCIDeviceClass {
|
||||||
uint16_t subsystem_id; /* only for header type = 0 */
|
uint16_t subsystem_id; /* only for header type = 0 */
|
||||||
|
|
||||||
const char *romfile; /* rom bar */
|
const char *romfile; /* rom bar */
|
||||||
|
|
||||||
|
bool sriov_vf_user_creatable;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum PCIReqIDType {
|
enum PCIReqIDType {
|
||||||
|
@ -157,9 +160,17 @@ struct PCIDevice {
|
||||||
MSIVectorReleaseNotifier msix_vector_release_notifier;
|
MSIVectorReleaseNotifier msix_vector_release_notifier;
|
||||||
MSIVectorPollNotifier msix_vector_poll_notifier;
|
MSIVectorPollNotifier msix_vector_poll_notifier;
|
||||||
|
|
||||||
|
/* SPDM */
|
||||||
|
uint16_t spdm_port;
|
||||||
|
|
||||||
|
/* DOE */
|
||||||
|
DOECap doe_spdm;
|
||||||
|
|
||||||
/* ID of standby device in net_failover pair */
|
/* ID of standby device in net_failover pair */
|
||||||
char *failover_pair_id;
|
char *failover_pair_id;
|
||||||
uint32_t acpi_index;
|
uint32_t acpi_index;
|
||||||
|
|
||||||
|
char *sriov_pf;
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline int pci_intx(PCIDevice *pci_dev)
|
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)
|
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)
|
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 */
|
/* PCI-SIG defined Data Object Types - r6.0 Table 6-32 */
|
||||||
#define PCI_SIG_DOE_DISCOVERY 0x00
|
#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_DW_SIZE_MAX (1 << 18)
|
||||||
#define PCI_DOE_PROTOCOL_NUM_MAX 256
|
#define PCI_DOE_PROTOCOL_NUM_MAX 256
|
||||||
|
@ -106,6 +108,9 @@ struct DOECap {
|
||||||
/* Protocols and its callback response */
|
/* Protocols and its callback response */
|
||||||
DOEProtocol *protocols;
|
DOEProtocol *protocols;
|
||||||
uint16_t protocol_num;
|
uint16_t protocol_num;
|
||||||
|
|
||||||
|
/* Used for spdm-socket */
|
||||||
|
int spdm_socket;
|
||||||
};
|
};
|
||||||
|
|
||||||
void pcie_doe_init(PCIDevice *pdev, DOECap *doe_cap, uint16_t offset,
|
void pcie_doe_init(PCIDevice *pdev, DOECap *doe_cap, uint16_t offset,
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
typedef struct PCIESriovPF {
|
typedef struct PCIESriovPF {
|
||||||
uint8_t vf_bar_type[PCI_NUM_REGIONS]; /* Store type for each VF bar */
|
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 */
|
PCIDevice **vf; /* Pointer to an array of num_vfs VF devices */
|
||||||
|
bool vf_user_created; /* If VFs are created by user */
|
||||||
} PCIESriovPF;
|
} PCIESriovPF;
|
||||||
|
|
||||||
typedef struct PCIESriovVF {
|
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,
|
void pcie_sriov_vf_register_bar(PCIDevice *dev, int region_num,
|
||||||
MemoryRegion *memory);
|
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
|
* Default (minimal) page size support values
|
||||||
* as required by the SR/IOV standard:
|
* as required by the SR/IOV standard:
|
||||||
|
|
|
@ -43,7 +43,6 @@ typedef struct IOMMUDevice {
|
||||||
MemoryRegion bypass_mr; /* The alias of shared memory MR */
|
MemoryRegion bypass_mr; /* The alias of shared memory MR */
|
||||||
GList *resv_regions;
|
GList *resv_regions;
|
||||||
GList *host_resv_ranges;
|
GList *host_resv_ranges;
|
||||||
bool probe_done;
|
|
||||||
} IOMMUDevice;
|
} IOMMUDevice;
|
||||||
|
|
||||||
typedef struct IOMMUPciBus {
|
typedef struct IOMMUPciBus {
|
||||||
|
|
|
@ -152,6 +152,7 @@ struct VirtIOPCIProxy {
|
||||||
uint32_t modern_io_bar_idx;
|
uint32_t modern_io_bar_idx;
|
||||||
uint32_t modern_mem_bar_idx;
|
uint32_t modern_mem_bar_idx;
|
||||||
int config_cap;
|
int config_cap;
|
||||||
|
uint16_t last_pcie_cap_offset;
|
||||||
uint32_t flags;
|
uint32_t flags;
|
||||||
bool disable_modern;
|
bool disable_modern;
|
||||||
bool ignore_backend_features;
|
bool ignore_backend_features;
|
||||||
|
|
|
@ -69,6 +69,8 @@ typedef struct VirtQueueElement
|
||||||
unsigned int ndescs;
|
unsigned int ndescs;
|
||||||
unsigned int out_num;
|
unsigned int out_num;
|
||||||
unsigned int in_num;
|
unsigned int in_num;
|
||||||
|
/* Element has been processed (VIRTIO_F_IN_ORDER) */
|
||||||
|
bool in_order_filled;
|
||||||
hwaddr *in_addr;
|
hwaddr *in_addr;
|
||||||
hwaddr *out_addr;
|
hwaddr *out_addr;
|
||||||
struct iovec *in_sg;
|
struct iovec *in_sg;
|
||||||
|
@ -371,7 +373,9 @@ typedef struct VirtIORNGConf VirtIORNGConf;
|
||||||
DEFINE_PROP_BIT64("packed", _state, _field, \
|
DEFINE_PROP_BIT64("packed", _state, _field, \
|
||||||
VIRTIO_F_RING_PACKED, false), \
|
VIRTIO_F_RING_PACKED, false), \
|
||||||
DEFINE_PROP_BIT64("queue_reset", _state, _field, \
|
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);
|
hwaddr virtio_queue_get_desc_addr(VirtIODevice *vdev, int n);
|
||||||
bool virtio_queue_enabled_legacy(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);
|
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 */
|
/* Arch specific hooks */
|
||||||
|
|
||||||
extern const KVMCapabilityInfo kvm_arch_required_capabilities[];
|
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_PACKED,
|
||||||
VIRTIO_F_RING_RESET,
|
VIRTIO_F_RING_RESET,
|
||||||
VIRTIO_F_VERSION_1,
|
VIRTIO_F_VERSION_1,
|
||||||
|
VIRTIO_F_IN_ORDER,
|
||||||
VIRTIO_F_NOTIFICATION_DATA,
|
VIRTIO_F_NOTIFICATION_DATA,
|
||||||
VIRTIO_NET_F_CSUM,
|
VIRTIO_NET_F_CSUM,
|
||||||
VIRTIO_NET_F_CTRL_GUEST_OFFLOADS,
|
VIRTIO_NET_F_CTRL_GUEST_OFFLOADS,
|
||||||
|
|
|
@ -763,6 +763,7 @@ void cpu_address_space_init(CPUState *cpu, int asidx,
|
||||||
|
|
||||||
if (!cpu->cpu_ases) {
|
if (!cpu->cpu_ases) {
|
||||||
cpu->cpu_ases = g_new0(CPUAddressSpace, cpu->num_ases);
|
cpu->cpu_ases = g_new0(CPUAddressSpace, cpu->num_ases);
|
||||||
|
cpu->cpu_ases_count = cpu->num_ases;
|
||||||
}
|
}
|
||||||
|
|
||||||
newas = &cpu->cpu_ases[asidx];
|
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)
|
AddressSpace *cpu_get_address_space(CPUState *cpu, int asidx)
|
||||||
{
|
{
|
||||||
/* Return the AddressSpace corresponding to the specified index */
|
/* 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,
|
data->arch, data->machine,
|
||||||
sdt->aml, ext);
|
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) &&
|
if (!g_file_test(aml_file, G_FILE_TEST_EXISTS) &&
|
||||||
sdt->aml_len == exp_sdt->aml_len &&
|
sdt->aml_len == exp_sdt->aml_len &&
|
||||||
!memcmp(sdt->aml, exp_sdt->aml, 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:
|
try_again:
|
||||||
aml_file = g_strdup_printf("%s/%s/%s/%.4s%s", data_dir, data->arch,
|
aml_file = g_strdup_printf("%s/%s/%s/%.4s%s", data_dir, data->arch,
|
||||||
data->machine, sdt->aml, ext);
|
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) {
|
if (verbosity_level >= 2) {
|
||||||
fprintf(stderr, "Looking for expected file '%s'\n", aml_file);
|
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 */
|
#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)
|
static void test_acpi_aarch64_virt_tcg(void)
|
||||||
{
|
{
|
||||||
test_data data = {
|
test_data data = {
|
||||||
|
@ -2455,6 +2463,10 @@ int main(int argc, char *argv[])
|
||||||
qtest_add_func("acpi/virt/viot", test_acpi_aarch64_virt_viot);
|
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();
|
ret = g_test_run();
|
||||||
boot_sector_cleanup(disk);
|
boot_sector_cleanup(disk);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue