virtio, pc: fixes and features
beginning of guest error handling for virtio devices amd iommu pc compat fixes Signed-off-by: Michael S. Tsirkin <mst@redhat.com> -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQEcBAABAgAGBQJX5aZhAAoJECgfDbjSjVRpVEIH/jIrAnSkbQ/1OD1+y6fdAXxw 5bjPxHiKqgKakO4LbNP4Ippit8IZ5EuNTHwTOuz7FV8LzDsd9wB09GS/mNHvYVz4 eioJVEYDjzjahRXVnTEfC85Jvv3IICGYSoj1v0u5JcbscB2GEH1L7MkK0n3HoIJ2 raIOx6sPY2NfHKeQCMZQBGbKpUxZUyVs0CdgC6kEM4eYYcQwoS8OdsYuCS6BE5Nh tCM/mgnhoi5gKMQ5SORf3cry79ibSQnDpmbrJDSTXtxJyIIq2f0wfpWKHmQ1tjTq SJw169NtBQ6/wQ++WeMrvsT8IB0OaIIpZ4HwZipPOnrF1JYEIuzvQOhQHDKOE4k= =uMzc -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/mst/tags/for_upstream' into staging virtio, pc: fixes and features beginning of guest error handling for virtio devices amd iommu pc compat fixes Signed-off-by: Michael S. Tsirkin <mst@redhat.com> # gpg: Signature made Fri 23 Sep 2016 23:02:09 BST # gpg: using RSA key 0x281F0DB8D28D5469 # gpg: Good signature from "Michael S. Tsirkin <mst@kernel.org>" # gpg: aka "Michael S. Tsirkin <mst@redhat.com>" # 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 * remotes/mst/tags/for_upstream: hw/i386: AMD IOMMU IVRS table hw/i386: Introduce AMD IOMMU hw/i386/trace-events: Add AMD IOMMU trace events hw/pci: Prepare for AMD IOMMU virtio: handle virtqueue_get_head() errors virtio: handle virtqueue_num_heads() errors virtio: handle virtqueue_read_next_desc() errors virtio: use unsigned int for virtqueue_get_avail_bytes() index virtio: handle virtqueue_get_avail_bytes() errors virtio: handle virtqueue_map_desc() errors virtio: migrate vdev->broken flag virtio: stop virtqueue processing if device is broken virtio: fix stray tab character target-i386: turn off CPU.l3-cache only for 2.7 and older machine types pc: clean up COMPAT macro chaining virtio: add check for descriptor's mapped address tests: add /vhost-user/flags-mismatch test tests: add a simple /vhost-user/multiqueue test tests: add /vhost-user/connect-fail test Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
7cfdc02dae
@ -226,7 +226,7 @@ static void build_extop_package(GArray *package, uint8_t op)
|
|||||||
build_prepend_byte(package, 0x5B); /* ExtOpPrefix */
|
build_prepend_byte(package, 0x5B); /* ExtOpPrefix */
|
||||||
}
|
}
|
||||||
|
|
||||||
static void build_append_int_noprefix(GArray *table, uint64_t value, int size)
|
void build_append_int_noprefix(GArray *table, uint64_t value, int size)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ obj-y += multiboot.o
|
|||||||
obj-y += pc.o pc_piix.o pc_q35.o
|
obj-y += pc.o pc_piix.o pc_q35.o
|
||||||
obj-y += pc_sysfw.o
|
obj-y += pc_sysfw.o
|
||||||
obj-y += x86-iommu.o intel_iommu.o
|
obj-y += x86-iommu.o intel_iommu.o
|
||||||
|
obj-y += amd_iommu.o
|
||||||
obj-$(CONFIG_XEN) += ../xenpv/ xen/
|
obj-$(CONFIG_XEN) += ../xenpv/ xen/
|
||||||
|
|
||||||
obj-y += kvmvapic.o
|
obj-y += kvmvapic.o
|
||||||
|
@ -59,7 +59,8 @@
|
|||||||
|
|
||||||
#include "qapi/qmp/qint.h"
|
#include "qapi/qmp/qint.h"
|
||||||
#include "qom/qom-qobject.h"
|
#include "qom/qom-qobject.h"
|
||||||
#include "hw/i386/x86-iommu.h"
|
#include "hw/i386/amd_iommu.h"
|
||||||
|
#include "hw/i386/intel_iommu.h"
|
||||||
|
|
||||||
#include "hw/acpi/ipmi.h"
|
#include "hw/acpi/ipmi.h"
|
||||||
|
|
||||||
@ -2562,6 +2563,62 @@ build_dmar_q35(GArray *table_data, BIOSLinker *linker)
|
|||||||
build_header(linker, table_data, (void *)(table_data->data + dmar_start),
|
build_header(linker, table_data, (void *)(table_data->data + dmar_start),
|
||||||
"DMAR", table_data->len - dmar_start, 1, NULL, NULL);
|
"DMAR", table_data->len - dmar_start, 1, NULL, NULL);
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
|
* IVRS table as specified in AMD IOMMU Specification v2.62, Section 5.2
|
||||||
|
* accessible here http://support.amd.com/TechDocs/48882_IOMMU.pdf
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
build_amd_iommu(GArray *table_data, BIOSLinker *linker)
|
||||||
|
{
|
||||||
|
int iommu_start = table_data->len;
|
||||||
|
AMDVIState *s = AMD_IOMMU_DEVICE(x86_iommu_get_default());
|
||||||
|
|
||||||
|
/* IVRS header */
|
||||||
|
acpi_data_push(table_data, sizeof(AcpiTableHeader));
|
||||||
|
/* IVinfo - IO virtualization information common to all
|
||||||
|
* IOMMU units in a system
|
||||||
|
*/
|
||||||
|
build_append_int_noprefix(table_data, 40UL << 8/* PASize */, 4);
|
||||||
|
/* reserved */
|
||||||
|
build_append_int_noprefix(table_data, 0, 8);
|
||||||
|
|
||||||
|
/* IVHD definition - type 10h */
|
||||||
|
build_append_int_noprefix(table_data, 0x10, 1);
|
||||||
|
/* virtualization flags */
|
||||||
|
build_append_int_noprefix(table_data,
|
||||||
|
(1UL << 0) | /* HtTunEn */
|
||||||
|
(1UL << 4) | /* iotblSup */
|
||||||
|
(1UL << 6) | /* PrefSup */
|
||||||
|
(1UL << 7), /* PPRSup */
|
||||||
|
1);
|
||||||
|
/* IVHD length */
|
||||||
|
build_append_int_noprefix(table_data, 0x24, 2);
|
||||||
|
/* DeviceID */
|
||||||
|
build_append_int_noprefix(table_data, s->devid, 2);
|
||||||
|
/* Capability offset */
|
||||||
|
build_append_int_noprefix(table_data, s->capab_offset, 2);
|
||||||
|
/* IOMMU base address */
|
||||||
|
build_append_int_noprefix(table_data, s->mmio.addr, 8);
|
||||||
|
/* PCI Segment Group */
|
||||||
|
build_append_int_noprefix(table_data, 0, 2);
|
||||||
|
/* IOMMU info */
|
||||||
|
build_append_int_noprefix(table_data, 0, 2);
|
||||||
|
/* IOMMU Feature Reporting */
|
||||||
|
build_append_int_noprefix(table_data,
|
||||||
|
(48UL << 30) | /* HATS */
|
||||||
|
(48UL << 28) | /* GATS */
|
||||||
|
(1UL << 2), /* GTSup */
|
||||||
|
4);
|
||||||
|
/*
|
||||||
|
* Type 1 device entry reporting all devices
|
||||||
|
* These are 4-byte device entries currently reporting the range of
|
||||||
|
* Refer to Spec - Table 95:IVHD Device Entry Type Codes(4-byte)
|
||||||
|
*/
|
||||||
|
build_append_int_noprefix(table_data, 0x0000001, 4);
|
||||||
|
|
||||||
|
build_header(linker, table_data, (void *)(table_data->data + iommu_start),
|
||||||
|
"IVRS", table_data->len - iommu_start, 1, NULL, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
static GArray *
|
static GArray *
|
||||||
build_rsdp(GArray *rsdp_table, BIOSLinker *linker, unsigned rsdt_tbl_offset)
|
build_rsdp(GArray *rsdp_table, BIOSLinker *linker, unsigned rsdt_tbl_offset)
|
||||||
@ -2622,11 +2679,6 @@ static bool acpi_get_mcfg(AcpiMcfgInfo *mcfg)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool acpi_has_iommu(void)
|
|
||||||
{
|
|
||||||
return !!x86_iommu_get_default();
|
|
||||||
}
|
|
||||||
|
|
||||||
static
|
static
|
||||||
void acpi_build(AcpiBuildTables *tables, MachineState *machine)
|
void acpi_build(AcpiBuildTables *tables, MachineState *machine)
|
||||||
{
|
{
|
||||||
@ -2706,9 +2758,15 @@ void acpi_build(AcpiBuildTables *tables, MachineState *machine)
|
|||||||
acpi_add_table(table_offsets, tables_blob);
|
acpi_add_table(table_offsets, tables_blob);
|
||||||
build_mcfg_q35(tables_blob, tables->linker, &mcfg);
|
build_mcfg_q35(tables_blob, tables->linker, &mcfg);
|
||||||
}
|
}
|
||||||
if (acpi_has_iommu()) {
|
if (x86_iommu_get_default()) {
|
||||||
acpi_add_table(table_offsets, tables_blob);
|
IommuType IOMMUType = x86_iommu_get_type();
|
||||||
build_dmar_q35(tables_blob, tables->linker);
|
if (IOMMUType == TYPE_AMD) {
|
||||||
|
acpi_add_table(table_offsets, tables_blob);
|
||||||
|
build_amd_iommu(tables_blob, tables->linker);
|
||||||
|
} else if (IOMMUType == TYPE_INTEL) {
|
||||||
|
acpi_add_table(table_offsets, tables_blob);
|
||||||
|
build_dmar_q35(tables_blob, tables->linker);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (pcms->acpi_nvdimm_state.is_enabled) {
|
if (pcms->acpi_nvdimm_state.is_enabled) {
|
||||||
nvdimm_build_acpi(table_offsets, tables_blob, tables->linker,
|
nvdimm_build_acpi(table_offsets, tables_blob, tables->linker,
|
||||||
|
1202
hw/i386/amd_iommu.c
Normal file
1202
hw/i386/amd_iommu.c
Normal file
File diff suppressed because it is too large
Load Diff
289
hw/i386/amd_iommu.h
Normal file
289
hw/i386/amd_iommu.h
Normal file
@ -0,0 +1,289 @@
|
|||||||
|
/*
|
||||||
|
* QEMU emulation of an AMD IOMMU (AMD-Vi)
|
||||||
|
*
|
||||||
|
* Copyright (C) 2011 Eduard - Gabriel Munteanu
|
||||||
|
* Copyright (C) 2015 David Kiarie, <davidkiarie4@gmail.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef AMD_IOMMU_H_
|
||||||
|
#define AMD_IOMMU_H_
|
||||||
|
|
||||||
|
#include "hw/hw.h"
|
||||||
|
#include "hw/pci/pci.h"
|
||||||
|
#include "hw/pci/msi.h"
|
||||||
|
#include "hw/sysbus.h"
|
||||||
|
#include "sysemu/dma.h"
|
||||||
|
#include "hw/i386/pc.h"
|
||||||
|
#include "hw/pci/pci_bus.h"
|
||||||
|
#include "hw/i386/x86-iommu.h"
|
||||||
|
|
||||||
|
/* Capability registers */
|
||||||
|
#define AMDVI_CAPAB_BAR_LOW 0x04
|
||||||
|
#define AMDVI_CAPAB_BAR_HIGH 0x08
|
||||||
|
#define AMDVI_CAPAB_RANGE 0x0C
|
||||||
|
#define AMDVI_CAPAB_MISC 0x10
|
||||||
|
|
||||||
|
#define AMDVI_CAPAB_SIZE 0x18
|
||||||
|
#define AMDVI_CAPAB_REG_SIZE 0x04
|
||||||
|
|
||||||
|
/* Capability header data */
|
||||||
|
#define AMDVI_CAPAB_ID_SEC 0xf
|
||||||
|
#define AMDVI_CAPAB_FLAT_EXT (1 << 28)
|
||||||
|
#define AMDVI_CAPAB_EFR_SUP (1 << 27)
|
||||||
|
#define AMDVI_CAPAB_FLAG_NPCACHE (1 << 26)
|
||||||
|
#define AMDVI_CAPAB_FLAG_HTTUNNEL (1 << 25)
|
||||||
|
#define AMDVI_CAPAB_FLAG_IOTLBSUP (1 << 24)
|
||||||
|
#define AMDVI_CAPAB_INIT_TYPE (3 << 16)
|
||||||
|
|
||||||
|
/* No. of used MMIO registers */
|
||||||
|
#define AMDVI_MMIO_REGS_HIGH 8
|
||||||
|
#define AMDVI_MMIO_REGS_LOW 7
|
||||||
|
|
||||||
|
/* MMIO registers */
|
||||||
|
#define AMDVI_MMIO_DEVICE_TABLE 0x0000
|
||||||
|
#define AMDVI_MMIO_COMMAND_BASE 0x0008
|
||||||
|
#define AMDVI_MMIO_EVENT_BASE 0x0010
|
||||||
|
#define AMDVI_MMIO_CONTROL 0x0018
|
||||||
|
#define AMDVI_MMIO_EXCL_BASE 0x0020
|
||||||
|
#define AMDVI_MMIO_EXCL_LIMIT 0x0028
|
||||||
|
#define AMDVI_MMIO_EXT_FEATURES 0x0030
|
||||||
|
#define AMDVI_MMIO_COMMAND_HEAD 0x2000
|
||||||
|
#define AMDVI_MMIO_COMMAND_TAIL 0x2008
|
||||||
|
#define AMDVI_MMIO_EVENT_HEAD 0x2010
|
||||||
|
#define AMDVI_MMIO_EVENT_TAIL 0x2018
|
||||||
|
#define AMDVI_MMIO_STATUS 0x2020
|
||||||
|
#define AMDVI_MMIO_PPR_BASE 0x0038
|
||||||
|
#define AMDVI_MMIO_PPR_HEAD 0x2030
|
||||||
|
#define AMDVI_MMIO_PPR_TAIL 0x2038
|
||||||
|
|
||||||
|
#define AMDVI_MMIO_SIZE 0x4000
|
||||||
|
|
||||||
|
#define AMDVI_MMIO_DEVTAB_SIZE_MASK ((1ULL << 12) - 1)
|
||||||
|
#define AMDVI_MMIO_DEVTAB_BASE_MASK (((1ULL << 52) - 1) & ~ \
|
||||||
|
AMDVI_MMIO_DEVTAB_SIZE_MASK)
|
||||||
|
#define AMDVI_MMIO_DEVTAB_ENTRY_SIZE 32
|
||||||
|
#define AMDVI_MMIO_DEVTAB_SIZE_UNIT 4096
|
||||||
|
|
||||||
|
/* some of this are similar but just for readability */
|
||||||
|
#define AMDVI_MMIO_CMDBUF_SIZE_BYTE (AMDVI_MMIO_COMMAND_BASE + 7)
|
||||||
|
#define AMDVI_MMIO_CMDBUF_SIZE_MASK 0x0f
|
||||||
|
#define AMDVI_MMIO_CMDBUF_BASE_MASK AMDVI_MMIO_DEVTAB_BASE_MASK
|
||||||
|
#define AMDVI_MMIO_CMDBUF_HEAD_MASK (((1ULL << 19) - 1) & ~0x0f)
|
||||||
|
#define AMDVI_MMIO_CMDBUF_TAIL_MASK AMDVI_MMIO_EVTLOG_HEAD_MASK
|
||||||
|
|
||||||
|
#define AMDVI_MMIO_EVTLOG_SIZE_BYTE (AMDVI_MMIO_EVENT_BASE + 7)
|
||||||
|
#define AMDVI_MMIO_EVTLOG_SIZE_MASK AMDVI_MMIO_CMDBUF_SIZE_MASK
|
||||||
|
#define AMDVI_MMIO_EVTLOG_BASE_MASK AMDVI_MMIO_CMDBUF_BASE_MASK
|
||||||
|
#define AMDVI_MMIO_EVTLOG_HEAD_MASK (((1ULL << 19) - 1) & ~0x0f)
|
||||||
|
#define AMDVI_MMIO_EVTLOG_TAIL_MASK AMDVI_MMIO_EVTLOG_HEAD_MASK
|
||||||
|
|
||||||
|
#define AMDVI_MMIO_PPRLOG_SIZE_BYTE (AMDVI_MMIO_EVENT_BASE + 7)
|
||||||
|
#define AMDVI_MMIO_PPRLOG_HEAD_MASK AMDVI_MMIO_EVTLOG_HEAD_MASK
|
||||||
|
#define AMDVI_MMIO_PPRLOG_TAIL_MASK AMDVI_MMIO_EVTLOG_HEAD_MASK
|
||||||
|
#define AMDVI_MMIO_PPRLOG_BASE_MASK AMDVI_MMIO_EVTLOG_BASE_MASK
|
||||||
|
#define AMDVI_MMIO_PPRLOG_SIZE_MASK AMDVI_MMIO_EVTLOG_SIZE_MASK
|
||||||
|
|
||||||
|
#define AMDVI_MMIO_EXCL_ENABLED_MASK (1ULL << 0)
|
||||||
|
#define AMDVI_MMIO_EXCL_ALLOW_MASK (1ULL << 1)
|
||||||
|
#define AMDVI_MMIO_EXCL_LIMIT_MASK AMDVI_MMIO_DEVTAB_BASE_MASK
|
||||||
|
#define AMDVI_MMIO_EXCL_LIMIT_LOW 0xfff
|
||||||
|
|
||||||
|
/* mmio control register flags */
|
||||||
|
#define AMDVI_MMIO_CONTROL_AMDVIEN (1ULL << 0)
|
||||||
|
#define AMDVI_MMIO_CONTROL_HTTUNEN (1ULL << 1)
|
||||||
|
#define AMDVI_MMIO_CONTROL_EVENTLOGEN (1ULL << 2)
|
||||||
|
#define AMDVI_MMIO_CONTROL_EVENTINTEN (1ULL << 3)
|
||||||
|
#define AMDVI_MMIO_CONTROL_COMWAITINTEN (1ULL << 4)
|
||||||
|
#define AMDVI_MMIO_CONTROL_CMDBUFLEN (1ULL << 12)
|
||||||
|
|
||||||
|
/* MMIO status register bits */
|
||||||
|
#define AMDVI_MMIO_STATUS_CMDBUF_RUN (1 << 4)
|
||||||
|
#define AMDVI_MMIO_STATUS_EVT_RUN (1 << 3)
|
||||||
|
#define AMDVI_MMIO_STATUS_COMP_INT (1 << 2)
|
||||||
|
#define AMDVI_MMIO_STATUS_EVT_OVF (1 << 0)
|
||||||
|
|
||||||
|
#define AMDVI_CMDBUF_ID_BYTE 0x07
|
||||||
|
#define AMDVI_CMDBUF_ID_RSHIFT 4
|
||||||
|
|
||||||
|
#define AMDVI_CMD_COMPLETION_WAIT 0x01
|
||||||
|
#define AMDVI_CMD_INVAL_DEVTAB_ENTRY 0x02
|
||||||
|
#define AMDVI_CMD_INVAL_AMDVI_PAGES 0x03
|
||||||
|
#define AMDVI_CMD_INVAL_IOTLB_PAGES 0x04
|
||||||
|
#define AMDVI_CMD_INVAL_INTR_TABLE 0x05
|
||||||
|
#define AMDVI_CMD_PREFETCH_AMDVI_PAGES 0x06
|
||||||
|
#define AMDVI_CMD_COMPLETE_PPR_REQUEST 0x07
|
||||||
|
#define AMDVI_CMD_INVAL_AMDVI_ALL 0x08
|
||||||
|
|
||||||
|
#define AMDVI_DEVTAB_ENTRY_SIZE 32
|
||||||
|
|
||||||
|
/* Device table entry bits 0:63 */
|
||||||
|
#define AMDVI_DEV_VALID (1ULL << 0)
|
||||||
|
#define AMDVI_DEV_TRANSLATION_VALID (1ULL << 1)
|
||||||
|
#define AMDVI_DEV_MODE_MASK 0x7
|
||||||
|
#define AMDVI_DEV_MODE_RSHIFT 9
|
||||||
|
#define AMDVI_DEV_PT_ROOT_MASK 0xffffffffff000
|
||||||
|
#define AMDVI_DEV_PT_ROOT_RSHIFT 12
|
||||||
|
#define AMDVI_DEV_PERM_SHIFT 61
|
||||||
|
#define AMDVI_DEV_PERM_READ (1ULL << 61)
|
||||||
|
#define AMDVI_DEV_PERM_WRITE (1ULL << 62)
|
||||||
|
|
||||||
|
/* Device table entry bits 64:127 */
|
||||||
|
#define AMDVI_DEV_DOMID_ID_MASK ((1ULL << 16) - 1)
|
||||||
|
|
||||||
|
/* Event codes and flags, as stored in the info field */
|
||||||
|
#define AMDVI_EVENT_ILLEGAL_DEVTAB_ENTRY (0x1U << 12)
|
||||||
|
#define AMDVI_EVENT_IOPF (0x2U << 12)
|
||||||
|
#define AMDVI_EVENT_IOPF_I (1U << 3)
|
||||||
|
#define AMDVI_EVENT_DEV_TAB_HW_ERROR (0x3U << 12)
|
||||||
|
#define AMDVI_EVENT_PAGE_TAB_HW_ERROR (0x4U << 12)
|
||||||
|
#define AMDVI_EVENT_ILLEGAL_COMMAND_ERROR (0x5U << 12)
|
||||||
|
#define AMDVI_EVENT_COMMAND_HW_ERROR (0x6U << 12)
|
||||||
|
|
||||||
|
#define AMDVI_EVENT_LEN 16
|
||||||
|
#define AMDVI_PERM_READ (1 << 0)
|
||||||
|
#define AMDVI_PERM_WRITE (1 << 1)
|
||||||
|
|
||||||
|
#define AMDVI_FEATURE_PREFETCH (1ULL << 0) /* page prefetch */
|
||||||
|
#define AMDVI_FEATURE_PPR (1ULL << 1) /* PPR Support */
|
||||||
|
#define AMDVI_FEATURE_GT (1ULL << 4) /* Guest Translation */
|
||||||
|
#define AMDVI_FEATURE_IA (1ULL << 6) /* inval all support */
|
||||||
|
#define AMDVI_FEATURE_GA (1ULL << 7) /* guest VAPIC support */
|
||||||
|
#define AMDVI_FEATURE_HE (1ULL << 8) /* hardware error regs */
|
||||||
|
#define AMDVI_FEATURE_PC (1ULL << 9) /* Perf counters */
|
||||||
|
|
||||||
|
/* reserved DTE bits */
|
||||||
|
#define AMDVI_DTE_LOWER_QUAD_RESERVED 0x80300000000000fc
|
||||||
|
#define AMDVI_DTE_MIDDLE_QUAD_RESERVED 0x0000000000000100
|
||||||
|
#define AMDVI_DTE_UPPER_QUAD_RESERVED 0x08f0000000000000
|
||||||
|
|
||||||
|
/* AMDVI paging mode */
|
||||||
|
#define AMDVI_GATS_MODE (6ULL << 12)
|
||||||
|
#define AMDVI_HATS_MODE (6ULL << 10)
|
||||||
|
|
||||||
|
/* IOTLB */
|
||||||
|
#define AMDVI_IOTLB_MAX_SIZE 1024
|
||||||
|
#define AMDVI_DEVID_SHIFT 36
|
||||||
|
|
||||||
|
/* extended feature support */
|
||||||
|
#define AMDVI_EXT_FEATURES (AMDVI_FEATURE_PREFETCH | AMDVI_FEATURE_PPR | \
|
||||||
|
AMDVI_FEATURE_IA | AMDVI_FEATURE_GT | AMDVI_FEATURE_HE | \
|
||||||
|
AMDVI_GATS_MODE | AMDVI_HATS_MODE)
|
||||||
|
|
||||||
|
/* capabilities header */
|
||||||
|
#define AMDVI_CAPAB_FEATURES (AMDVI_CAPAB_FLAT_EXT | \
|
||||||
|
AMDVI_CAPAB_FLAG_NPCACHE | AMDVI_CAPAB_FLAG_IOTLBSUP \
|
||||||
|
| AMDVI_CAPAB_ID_SEC | AMDVI_CAPAB_INIT_TYPE | \
|
||||||
|
AMDVI_CAPAB_FLAG_HTTUNNEL | AMDVI_CAPAB_EFR_SUP)
|
||||||
|
|
||||||
|
/* AMDVI default address */
|
||||||
|
#define AMDVI_BASE_ADDR 0xfed80000
|
||||||
|
|
||||||
|
/* page management constants */
|
||||||
|
#define AMDVI_PAGE_SHIFT 12
|
||||||
|
#define AMDVI_PAGE_SIZE (1ULL << AMDVI_PAGE_SHIFT)
|
||||||
|
|
||||||
|
#define AMDVI_PAGE_SHIFT_4K 12
|
||||||
|
#define AMDVI_PAGE_MASK_4K (~((1ULL << AMDVI_PAGE_SHIFT_4K) - 1))
|
||||||
|
|
||||||
|
#define AMDVI_MAX_VA_ADDR (48UL << 5)
|
||||||
|
#define AMDVI_MAX_PH_ADDR (40UL << 8)
|
||||||
|
#define AMDVI_MAX_GVA_ADDR (48UL << 15)
|
||||||
|
|
||||||
|
/* Completion Wait data size */
|
||||||
|
#define AMDVI_COMPLETION_DATA_SIZE 8
|
||||||
|
|
||||||
|
#define AMDVI_COMMAND_SIZE 16
|
||||||
|
/* Completion Wait data size */
|
||||||
|
#define AMDVI_COMPLETION_DATA_SIZE 8
|
||||||
|
|
||||||
|
#define AMDVI_COMMAND_SIZE 16
|
||||||
|
|
||||||
|
#define AMDVI_INT_ADDR_FIRST 0xfee00000
|
||||||
|
#define AMDVI_INT_ADDR_LAST 0xfeefffff
|
||||||
|
|
||||||
|
#define TYPE_AMD_IOMMU_DEVICE "amd-iommu"
|
||||||
|
#define AMD_IOMMU_DEVICE(obj)\
|
||||||
|
OBJECT_CHECK(AMDVIState, (obj), TYPE_AMD_IOMMU_DEVICE)
|
||||||
|
|
||||||
|
#define TYPE_AMD_IOMMU_PCI "AMDVI-PCI"
|
||||||
|
|
||||||
|
typedef struct AMDVIAddressSpace AMDVIAddressSpace;
|
||||||
|
|
||||||
|
/* functions to steal PCI config space */
|
||||||
|
typedef struct AMDVIPCIState {
|
||||||
|
PCIDevice dev; /* The PCI device itself */
|
||||||
|
} AMDVIPCIState;
|
||||||
|
|
||||||
|
typedef struct AMDVIState {
|
||||||
|
X86IOMMUState iommu; /* IOMMU bus device */
|
||||||
|
AMDVIPCIState pci; /* IOMMU PCI device */
|
||||||
|
|
||||||
|
uint32_t version;
|
||||||
|
uint32_t capab_offset; /* capability offset pointer */
|
||||||
|
|
||||||
|
uint64_t mmio_addr;
|
||||||
|
|
||||||
|
uint32_t devid; /* auto-assigned devid */
|
||||||
|
|
||||||
|
bool enabled; /* IOMMU enabled */
|
||||||
|
bool ats_enabled; /* address translation enabled */
|
||||||
|
bool cmdbuf_enabled; /* command buffer enabled */
|
||||||
|
bool evtlog_enabled; /* event log enabled */
|
||||||
|
bool excl_enabled;
|
||||||
|
|
||||||
|
hwaddr devtab; /* base address device table */
|
||||||
|
size_t devtab_len; /* device table length */
|
||||||
|
|
||||||
|
hwaddr cmdbuf; /* command buffer base address */
|
||||||
|
uint64_t cmdbuf_len; /* command buffer length */
|
||||||
|
uint32_t cmdbuf_head; /* current IOMMU read position */
|
||||||
|
uint32_t cmdbuf_tail; /* next Software write position */
|
||||||
|
bool completion_wait_intr;
|
||||||
|
|
||||||
|
hwaddr evtlog; /* base address event log */
|
||||||
|
bool evtlog_intr;
|
||||||
|
uint32_t evtlog_len; /* event log length */
|
||||||
|
uint32_t evtlog_head; /* current IOMMU write position */
|
||||||
|
uint32_t evtlog_tail; /* current Software read position */
|
||||||
|
|
||||||
|
/* unused for now */
|
||||||
|
hwaddr excl_base; /* base DVA - IOMMU exclusion range */
|
||||||
|
hwaddr excl_limit; /* limit of IOMMU exclusion range */
|
||||||
|
bool excl_allow; /* translate accesses to the exclusion range */
|
||||||
|
bool excl_enable; /* exclusion range enabled */
|
||||||
|
|
||||||
|
hwaddr ppr_log; /* base address ppr log */
|
||||||
|
uint32_t pprlog_len; /* ppr log len */
|
||||||
|
uint32_t pprlog_head; /* ppr log head */
|
||||||
|
uint32_t pprlog_tail; /* ppr log tail */
|
||||||
|
|
||||||
|
MemoryRegion mmio; /* MMIO region */
|
||||||
|
uint8_t mmior[AMDVI_MMIO_SIZE]; /* read/write MMIO */
|
||||||
|
uint8_t w1cmask[AMDVI_MMIO_SIZE]; /* read/write 1 clear mask */
|
||||||
|
uint8_t romask[AMDVI_MMIO_SIZE]; /* MMIO read/only mask */
|
||||||
|
bool mmio_enabled;
|
||||||
|
|
||||||
|
/* IOMMU function */
|
||||||
|
MemoryRegionIOMMUOps iommu_ops;
|
||||||
|
|
||||||
|
/* for each served device */
|
||||||
|
AMDVIAddressSpace **address_spaces[PCI_BUS_MAX];
|
||||||
|
|
||||||
|
/* IOTLB */
|
||||||
|
GHashTable *iotlb;
|
||||||
|
} AMDVIState;
|
||||||
|
|
||||||
|
#endif
|
@ -2453,6 +2453,7 @@ static void vtd_realize(DeviceState *dev, Error **errp)
|
|||||||
X86IOMMUState *x86_iommu = X86_IOMMU_DEVICE(dev);
|
X86IOMMUState *x86_iommu = X86_IOMMU_DEVICE(dev);
|
||||||
|
|
||||||
VTD_DPRINTF(GENERAL, "");
|
VTD_DPRINTF(GENERAL, "");
|
||||||
|
x86_iommu->type = TYPE_INTEL;
|
||||||
memset(s->vtd_as_by_bus_num, 0, sizeof(s->vtd_as_by_bus_num));
|
memset(s->vtd_as_by_bus_num, 0, sizeof(s->vtd_as_by_bus_num));
|
||||||
memory_region_init_io(&s->csrmem, OBJECT(s), &vtd_mem_ops, s,
|
memory_region_init_io(&s->csrmem, OBJECT(s), &vtd_mem_ops, s,
|
||||||
"intel_iommu", DMAR_REG_SIZE);
|
"intel_iommu", DMAR_REG_SIZE);
|
||||||
|
@ -13,3 +13,32 @@ mhp_pc_dimm_assigned_address(uint64_t addr) "0x%"PRIx64
|
|||||||
|
|
||||||
# hw/i386/x86-iommu.c
|
# hw/i386/x86-iommu.c
|
||||||
x86_iommu_iec_notify(bool global, uint32_t index, uint32_t mask) "Notify IEC invalidation: global=%d index=%" PRIu32 " mask=%" PRIu32
|
x86_iommu_iec_notify(bool global, uint32_t index, uint32_t mask) "Notify IEC invalidation: global=%d index=%" PRIu32 " mask=%" PRIu32
|
||||||
|
|
||||||
|
# hw/i386/amd_iommu.c
|
||||||
|
amdvi_evntlog_fail(uint64_t addr, uint32_t head) "error: fail to write at addr 0x%"PRIx64" + offset 0x%"PRIx32
|
||||||
|
amdvi_cache_update(uint16_t domid, uint8_t bus, uint8_t slot, uint8_t func, uint64_t gpa, uint64_t txaddr) " update iotlb domid 0x%"PRIx16" devid: %02x:%02x.%x gpa 0x%"PRIx64" hpa 0x%"PRIx64
|
||||||
|
amdvi_completion_wait_fail(uint64_t addr) "error: fail to write at address 0x%"PRIx64
|
||||||
|
amdvi_mmio_write(const char *reg, uint64_t addr, unsigned size, uint64_t val, uint64_t offset) "%s write addr 0x%"PRIx64", size %u, val 0x%"PRIx64", offset 0x%"PRIx64
|
||||||
|
amdvi_mmio_read(const char *reg, uint64_t addr, unsigned size, uint64_t offset) "%s read addr 0x%"PRIx64", size %u offset 0x%"PRIx64
|
||||||
|
amdvi_command_error(uint64_t status) "error: Executing commands with command buffer disabled 0x%"PRIx64
|
||||||
|
amdvi_command_read_fail(uint64_t addr, uint32_t head) "error: fail to access memory at 0x%"PRIx64" + 0x%"PRIx32
|
||||||
|
amdvi_command_exec(uint32_t head, uint32_t tail, uint64_t buf) "command buffer head at 0x%"PRIx32" command buffer tail at 0x%"PRIx32" command buffer base at 0x%"PRIx64
|
||||||
|
amdvi_unhandled_command(uint8_t type) "unhandled command 0x%"PRIx8
|
||||||
|
amdvi_intr_inval(void) "Interrupt table invalidated"
|
||||||
|
amdvi_iotlb_inval(void) "IOTLB pages invalidated"
|
||||||
|
amdvi_prefetch_pages(void) "Pre-fetch of AMD-Vi pages requested"
|
||||||
|
amdvi_pages_inval(uint16_t domid) "AMD-Vi pages for domain 0x%"PRIx16 " invalidated"
|
||||||
|
amdvi_all_inval(void) "Invalidation of all AMD-Vi cache requested "
|
||||||
|
amdvi_ppr_exec(void) "Execution of PPR queue requested "
|
||||||
|
amdvi_devtab_inval(uint8_t bus, uint8_t slot, uint8_t func) "device table entry for devid: %02x:%02x.%x invalidated"
|
||||||
|
amdvi_completion_wait(uint64_t addr, uint64_t data) "completion wait requested with store address 0x%"PRIx64" and store data 0x%"PRIx64
|
||||||
|
amdvi_control_status(uint64_t val) "MMIO_STATUS state 0x%"PRIx64
|
||||||
|
amdvi_iotlb_reset(void) "IOTLB exceed size limit - reset "
|
||||||
|
amdvi_completion_wait_exec(uint64_t addr, uint64_t data) "completion wait requested with store address 0x%"PRIx64" and store data 0x%"PRIx64
|
||||||
|
amdvi_dte_get_fail(uint64_t addr, uint32_t offset) "error: failed to access Device Entry devtab 0x%"PRIx64" offset 0x%"PRIx32
|
||||||
|
amdvi_invalid_dte(uint64_t addr) "PTE entry at 0x%"PRIx64" is invalid "
|
||||||
|
amdvi_get_pte_hwerror(uint64_t addr) "hardware error eccessing PTE at addr 0x%"PRIx64
|
||||||
|
amdvi_mode_invalid(uint8_t level, uint64_t addr)"error: translation level 0x%"PRIx8" translating addr 0x%"PRIx64
|
||||||
|
amdvi_page_fault(uint64_t addr) "error: page fault accessing guest physical address 0x%"PRIx64
|
||||||
|
amdvi_iotlb_hit(uint8_t bus, uint8_t slot, uint8_t func, uint64_t addr, uint64_t txaddr) "hit iotlb devid %02x:%02x.%x gpa 0x%"PRIx64" hpa 0x%"PRIx64
|
||||||
|
amdvi_translation_result(uint8_t bus, uint8_t slot, uint8_t func, uint64_t addr, uint64_t txaddr) "devid: %02x:%02x.%x gpa 0x%"PRIx64" hpa 0x%"PRIx64
|
||||||
|
@ -71,6 +71,11 @@ X86IOMMUState *x86_iommu_get_default(void)
|
|||||||
return x86_iommu_default;
|
return x86_iommu_default;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IommuType x86_iommu_get_type(void)
|
||||||
|
{
|
||||||
|
return x86_iommu_default->type;
|
||||||
|
}
|
||||||
|
|
||||||
static void x86_iommu_realize(DeviceState *dev, Error **errp)
|
static void x86_iommu_realize(DeviceState *dev, Error **errp)
|
||||||
{
|
{
|
||||||
X86IOMMUState *x86_iommu = X86_IOMMU_DEVICE(dev);
|
X86IOMMUState *x86_iommu = X86_IOMMU_DEVICE(dev);
|
||||||
@ -79,6 +84,7 @@ static void x86_iommu_realize(DeviceState *dev, Error **errp)
|
|||||||
if (x86_class->realize) {
|
if (x86_class->realize) {
|
||||||
x86_class->realize(dev, errp);
|
x86_class->realize(dev, errp);
|
||||||
}
|
}
|
||||||
|
|
||||||
x86_iommu_set_default(X86_IOMMU_DEVICE(dev));
|
x86_iommu_set_default(X86_IOMMU_DEVICE(dev));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -303,6 +303,10 @@ void virtqueue_fill(VirtQueue *vq, const VirtQueueElement *elem,
|
|||||||
|
|
||||||
virtqueue_unmap_sg(vq, elem, len);
|
virtqueue_unmap_sg(vq, elem, len);
|
||||||
|
|
||||||
|
if (unlikely(vq->vdev->broken)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
idx = (idx + vq->used_idx) % vq->vring.num;
|
idx = (idx + vq->used_idx) % vq->vring.num;
|
||||||
|
|
||||||
uelem.id = elem->index;
|
uelem.id = elem->index;
|
||||||
@ -313,6 +317,12 @@ void virtqueue_fill(VirtQueue *vq, const VirtQueueElement *elem,
|
|||||||
void virtqueue_flush(VirtQueue *vq, unsigned int count)
|
void virtqueue_flush(VirtQueue *vq, unsigned int count)
|
||||||
{
|
{
|
||||||
uint16_t old, new;
|
uint16_t old, new;
|
||||||
|
|
||||||
|
if (unlikely(vq->vdev->broken)) {
|
||||||
|
vq->inuse -= count;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/* Make sure buffer is written before we update index. */
|
/* Make sure buffer is written before we update index. */
|
||||||
smp_wmb();
|
smp_wmb();
|
||||||
trace_virtqueue_flush(vq, count);
|
trace_virtqueue_flush(vq, count);
|
||||||
@ -337,9 +347,9 @@ static int virtqueue_num_heads(VirtQueue *vq, unsigned int idx)
|
|||||||
|
|
||||||
/* Check it isn't doing very strange things with descriptor numbers. */
|
/* Check it isn't doing very strange things with descriptor numbers. */
|
||||||
if (num_heads > vq->vring.num) {
|
if (num_heads > vq->vring.num) {
|
||||||
error_report("Guest moved used index from %u to %u",
|
virtio_error(vq->vdev, "Guest moved used index from %u to %u",
|
||||||
idx, vq->shadow_avail_idx);
|
idx, vq->shadow_avail_idx);
|
||||||
exit(1);
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
/* On success, callers read a descriptor at vq->last_avail_idx.
|
/* On success, callers read a descriptor at vq->last_avail_idx.
|
||||||
* Make sure descriptor read does not bypass avail index read. */
|
* Make sure descriptor read does not bypass avail index read. */
|
||||||
@ -350,45 +360,49 @@ static int virtqueue_num_heads(VirtQueue *vq, unsigned int idx)
|
|||||||
return num_heads;
|
return num_heads;
|
||||||
}
|
}
|
||||||
|
|
||||||
static unsigned int virtqueue_get_head(VirtQueue *vq, unsigned int idx)
|
static bool virtqueue_get_head(VirtQueue *vq, unsigned int idx,
|
||||||
|
unsigned int *head)
|
||||||
{
|
{
|
||||||
unsigned int head;
|
|
||||||
|
|
||||||
/* Grab the next descriptor number they're advertising, and increment
|
/* Grab the next descriptor number they're advertising, and increment
|
||||||
* the index we've seen. */
|
* the index we've seen. */
|
||||||
head = vring_avail_ring(vq, idx % vq->vring.num);
|
*head = vring_avail_ring(vq, idx % vq->vring.num);
|
||||||
|
|
||||||
/* If their number is silly, that's a fatal mistake. */
|
/* If their number is silly, that's a fatal mistake. */
|
||||||
if (head >= vq->vring.num) {
|
if (*head >= vq->vring.num) {
|
||||||
error_report("Guest says index %u is available", head);
|
virtio_error(vq->vdev, "Guest says index %u is available", *head);
|
||||||
exit(1);
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return head;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static unsigned virtqueue_read_next_desc(VirtIODevice *vdev, VRingDesc *desc,
|
enum {
|
||||||
hwaddr desc_pa, unsigned int max)
|
VIRTQUEUE_READ_DESC_ERROR = -1,
|
||||||
{
|
VIRTQUEUE_READ_DESC_DONE = 0, /* end of chain */
|
||||||
unsigned int next;
|
VIRTQUEUE_READ_DESC_MORE = 1, /* more buffers in chain */
|
||||||
|
};
|
||||||
|
|
||||||
|
static int virtqueue_read_next_desc(VirtIODevice *vdev, VRingDesc *desc,
|
||||||
|
hwaddr desc_pa, unsigned int max,
|
||||||
|
unsigned int *next)
|
||||||
|
{
|
||||||
/* If this descriptor says it doesn't chain, we're done. */
|
/* If this descriptor says it doesn't chain, we're done. */
|
||||||
if (!(desc->flags & VRING_DESC_F_NEXT)) {
|
if (!(desc->flags & VRING_DESC_F_NEXT)) {
|
||||||
return max;
|
return VIRTQUEUE_READ_DESC_DONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check they're not leading us off end of descriptors. */
|
/* Check they're not leading us off end of descriptors. */
|
||||||
next = desc->next;
|
*next = desc->next;
|
||||||
/* Make sure compiler knows to grab that: we don't want it changing! */
|
/* Make sure compiler knows to grab that: we don't want it changing! */
|
||||||
smp_wmb();
|
smp_wmb();
|
||||||
|
|
||||||
if (next >= max) {
|
if (*next >= max) {
|
||||||
error_report("Desc next is %u", next);
|
virtio_error(vdev, "Desc next is %u", *next);
|
||||||
exit(1);
|
return VIRTQUEUE_READ_DESC_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
vring_desc_read(vdev, desc, desc_pa, next);
|
vring_desc_read(vdev, desc, desc_pa, *next);
|
||||||
return next;
|
return VIRTQUEUE_READ_DESC_MORE;
|
||||||
}
|
}
|
||||||
|
|
||||||
void virtqueue_get_avail_bytes(VirtQueue *vq, unsigned int *in_bytes,
|
void virtqueue_get_avail_bytes(VirtQueue *vq, unsigned int *in_bytes,
|
||||||
@ -397,33 +411,38 @@ void virtqueue_get_avail_bytes(VirtQueue *vq, unsigned int *in_bytes,
|
|||||||
{
|
{
|
||||||
unsigned int idx;
|
unsigned int idx;
|
||||||
unsigned int total_bufs, in_total, out_total;
|
unsigned int total_bufs, in_total, out_total;
|
||||||
|
int rc;
|
||||||
|
|
||||||
idx = vq->last_avail_idx;
|
idx = vq->last_avail_idx;
|
||||||
|
|
||||||
total_bufs = in_total = out_total = 0;
|
total_bufs = in_total = out_total = 0;
|
||||||
while (virtqueue_num_heads(vq, idx)) {
|
while ((rc = virtqueue_num_heads(vq, idx)) > 0) {
|
||||||
VirtIODevice *vdev = vq->vdev;
|
VirtIODevice *vdev = vq->vdev;
|
||||||
unsigned int max, num_bufs, indirect = 0;
|
unsigned int max, num_bufs, indirect = 0;
|
||||||
VRingDesc desc;
|
VRingDesc desc;
|
||||||
hwaddr desc_pa;
|
hwaddr desc_pa;
|
||||||
int i;
|
unsigned int i;
|
||||||
|
|
||||||
max = vq->vring.num;
|
max = vq->vring.num;
|
||||||
num_bufs = total_bufs;
|
num_bufs = total_bufs;
|
||||||
i = virtqueue_get_head(vq, idx++);
|
|
||||||
|
if (!virtqueue_get_head(vq, idx++, &i)) {
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
desc_pa = vq->vring.desc;
|
desc_pa = vq->vring.desc;
|
||||||
vring_desc_read(vdev, &desc, desc_pa, i);
|
vring_desc_read(vdev, &desc, desc_pa, i);
|
||||||
|
|
||||||
if (desc.flags & VRING_DESC_F_INDIRECT) {
|
if (desc.flags & VRING_DESC_F_INDIRECT) {
|
||||||
if (desc.len % sizeof(VRingDesc)) {
|
if (desc.len % sizeof(VRingDesc)) {
|
||||||
error_report("Invalid size for indirect buffer table");
|
virtio_error(vdev, "Invalid size for indirect buffer table");
|
||||||
exit(1);
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If we've got too many, that implies a descriptor loop. */
|
/* If we've got too many, that implies a descriptor loop. */
|
||||||
if (num_bufs >= max) {
|
if (num_bufs >= max) {
|
||||||
error_report("Looped descriptor");
|
virtio_error(vdev, "Looped descriptor");
|
||||||
exit(1);
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* loop over the indirect descriptor table */
|
/* loop over the indirect descriptor table */
|
||||||
@ -437,8 +456,8 @@ void virtqueue_get_avail_bytes(VirtQueue *vq, unsigned int *in_bytes,
|
|||||||
do {
|
do {
|
||||||
/* If we've got too many, that implies a descriptor loop. */
|
/* If we've got too many, that implies a descriptor loop. */
|
||||||
if (++num_bufs > max) {
|
if (++num_bufs > max) {
|
||||||
error_report("Looped descriptor");
|
virtio_error(vdev, "Looped descriptor");
|
||||||
exit(1);
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (desc.flags & VRING_DESC_F_WRITE) {
|
if (desc.flags & VRING_DESC_F_WRITE) {
|
||||||
@ -449,13 +468,24 @@ void virtqueue_get_avail_bytes(VirtQueue *vq, unsigned int *in_bytes,
|
|||||||
if (in_total >= max_in_bytes && out_total >= max_out_bytes) {
|
if (in_total >= max_in_bytes && out_total >= max_out_bytes) {
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
} while ((i = virtqueue_read_next_desc(vdev, &desc, desc_pa, max)) != max);
|
|
||||||
|
rc = virtqueue_read_next_desc(vdev, &desc, desc_pa, max, &i);
|
||||||
|
} while (rc == VIRTQUEUE_READ_DESC_MORE);
|
||||||
|
|
||||||
|
if (rc == VIRTQUEUE_READ_DESC_ERROR) {
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
if (!indirect)
|
if (!indirect)
|
||||||
total_bufs = num_bufs;
|
total_bufs = num_bufs;
|
||||||
else
|
else
|
||||||
total_bufs++;
|
total_bufs++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (rc < 0) {
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
done:
|
done:
|
||||||
if (in_bytes) {
|
if (in_bytes) {
|
||||||
*in_bytes = in_total;
|
*in_bytes = in_total;
|
||||||
@ -463,6 +493,11 @@ done:
|
|||||||
if (out_bytes) {
|
if (out_bytes) {
|
||||||
*out_bytes = out_total;
|
*out_bytes = out_total;
|
||||||
}
|
}
|
||||||
|
return;
|
||||||
|
|
||||||
|
err:
|
||||||
|
in_total = out_total = 0;
|
||||||
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
int virtqueue_avail_bytes(VirtQueue *vq, unsigned int in_bytes,
|
int virtqueue_avail_bytes(VirtQueue *vq, unsigned int in_bytes,
|
||||||
@ -474,27 +509,35 @@ int virtqueue_avail_bytes(VirtQueue *vq, unsigned int in_bytes,
|
|||||||
return in_bytes <= in_total && out_bytes <= out_total;
|
return in_bytes <= in_total && out_bytes <= out_total;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void virtqueue_map_desc(unsigned int *p_num_sg, hwaddr *addr, struct iovec *iov,
|
static bool virtqueue_map_desc(VirtIODevice *vdev, unsigned int *p_num_sg,
|
||||||
|
hwaddr *addr, struct iovec *iov,
|
||||||
unsigned int max_num_sg, bool is_write,
|
unsigned int max_num_sg, bool is_write,
|
||||||
hwaddr pa, size_t sz)
|
hwaddr pa, size_t sz)
|
||||||
{
|
{
|
||||||
|
bool ok = false;
|
||||||
unsigned num_sg = *p_num_sg;
|
unsigned num_sg = *p_num_sg;
|
||||||
assert(num_sg <= max_num_sg);
|
assert(num_sg <= max_num_sg);
|
||||||
|
|
||||||
if (!sz) {
|
if (!sz) {
|
||||||
error_report("virtio: zero sized buffers are not allowed");
|
virtio_error(vdev, "virtio: zero sized buffers are not allowed");
|
||||||
exit(1);
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (sz) {
|
while (sz) {
|
||||||
hwaddr len = sz;
|
hwaddr len = sz;
|
||||||
|
|
||||||
if (num_sg == max_num_sg) {
|
if (num_sg == max_num_sg) {
|
||||||
error_report("virtio: too many write descriptors in indirect table");
|
virtio_error(vdev, "virtio: too many write descriptors in "
|
||||||
exit(1);
|
"indirect table");
|
||||||
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
iov[num_sg].iov_base = cpu_physical_memory_map(pa, &len, is_write);
|
iov[num_sg].iov_base = cpu_physical_memory_map(pa, &len, is_write);
|
||||||
|
if (!iov[num_sg].iov_base) {
|
||||||
|
virtio_error(vdev, "virtio: bogus descriptor or out of resources");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
iov[num_sg].iov_len = len;
|
iov[num_sg].iov_len = len;
|
||||||
addr[num_sg] = pa;
|
addr[num_sg] = pa;
|
||||||
|
|
||||||
@ -502,7 +545,28 @@ static void virtqueue_map_desc(unsigned int *p_num_sg, hwaddr *addr, struct iove
|
|||||||
pa += len;
|
pa += len;
|
||||||
num_sg++;
|
num_sg++;
|
||||||
}
|
}
|
||||||
|
ok = true;
|
||||||
|
|
||||||
|
out:
|
||||||
*p_num_sg = num_sg;
|
*p_num_sg = num_sg;
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Only used by error code paths before we have a VirtQueueElement (therefore
|
||||||
|
* virtqueue_unmap_sg() can't be used). Assumes buffers weren't written to
|
||||||
|
* yet.
|
||||||
|
*/
|
||||||
|
static void virtqueue_undo_map_desc(unsigned int out_num, unsigned int in_num,
|
||||||
|
struct iovec *iov)
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
for (i = 0; i < out_num + in_num; i++) {
|
||||||
|
int is_write = i >= out_num;
|
||||||
|
|
||||||
|
cpu_physical_memory_unmap(iov->iov_base, iov->iov_len, is_write, 0);
|
||||||
|
iov++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void virtqueue_map_iovec(struct iovec *sg, hwaddr *addr,
|
static void virtqueue_map_iovec(struct iovec *sg, hwaddr *addr,
|
||||||
@ -577,7 +641,11 @@ void *virtqueue_pop(VirtQueue *vq, size_t sz)
|
|||||||
hwaddr addr[VIRTQUEUE_MAX_SIZE];
|
hwaddr addr[VIRTQUEUE_MAX_SIZE];
|
||||||
struct iovec iov[VIRTQUEUE_MAX_SIZE];
|
struct iovec iov[VIRTQUEUE_MAX_SIZE];
|
||||||
VRingDesc desc;
|
VRingDesc desc;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
if (unlikely(vdev->broken)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
if (virtio_queue_empty(vq)) {
|
if (virtio_queue_empty(vq)) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@ -591,20 +659,24 @@ void *virtqueue_pop(VirtQueue *vq, size_t sz)
|
|||||||
max = vq->vring.num;
|
max = vq->vring.num;
|
||||||
|
|
||||||
if (vq->inuse >= vq->vring.num) {
|
if (vq->inuse >= vq->vring.num) {
|
||||||
error_report("Virtqueue size exceeded");
|
virtio_error(vdev, "Virtqueue size exceeded");
|
||||||
exit(1);
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!virtqueue_get_head(vq, vq->last_avail_idx++, &head)) {
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
i = head = virtqueue_get_head(vq, vq->last_avail_idx++);
|
|
||||||
if (virtio_vdev_has_feature(vdev, VIRTIO_RING_F_EVENT_IDX)) {
|
if (virtio_vdev_has_feature(vdev, VIRTIO_RING_F_EVENT_IDX)) {
|
||||||
vring_set_avail_event(vq, vq->last_avail_idx);
|
vring_set_avail_event(vq, vq->last_avail_idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
i = head;
|
||||||
vring_desc_read(vdev, &desc, desc_pa, i);
|
vring_desc_read(vdev, &desc, desc_pa, i);
|
||||||
if (desc.flags & VRING_DESC_F_INDIRECT) {
|
if (desc.flags & VRING_DESC_F_INDIRECT) {
|
||||||
if (desc.len % sizeof(VRingDesc)) {
|
if (desc.len % sizeof(VRingDesc)) {
|
||||||
error_report("Invalid size for indirect buffer table");
|
virtio_error(vdev, "Invalid size for indirect buffer table");
|
||||||
exit(1);
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* loop over the indirect descriptor table */
|
/* loop over the indirect descriptor table */
|
||||||
@ -616,24 +688,38 @@ void *virtqueue_pop(VirtQueue *vq, size_t sz)
|
|||||||
|
|
||||||
/* Collect all the descriptors */
|
/* Collect all the descriptors */
|
||||||
do {
|
do {
|
||||||
|
bool map_ok;
|
||||||
|
|
||||||
if (desc.flags & VRING_DESC_F_WRITE) {
|
if (desc.flags & VRING_DESC_F_WRITE) {
|
||||||
virtqueue_map_desc(&in_num, addr + out_num, iov + out_num,
|
map_ok = virtqueue_map_desc(vdev, &in_num, addr + out_num,
|
||||||
VIRTQUEUE_MAX_SIZE - out_num, true, desc.addr, desc.len);
|
iov + out_num,
|
||||||
|
VIRTQUEUE_MAX_SIZE - out_num, true,
|
||||||
|
desc.addr, desc.len);
|
||||||
} else {
|
} else {
|
||||||
if (in_num) {
|
if (in_num) {
|
||||||
error_report("Incorrect order for descriptors");
|
virtio_error(vdev, "Incorrect order for descriptors");
|
||||||
exit(1);
|
goto err_undo_map;
|
||||||
}
|
}
|
||||||
virtqueue_map_desc(&out_num, addr, iov,
|
map_ok = virtqueue_map_desc(vdev, &out_num, addr, iov,
|
||||||
VIRTQUEUE_MAX_SIZE, false, desc.addr, desc.len);
|
VIRTQUEUE_MAX_SIZE, false,
|
||||||
|
desc.addr, desc.len);
|
||||||
|
}
|
||||||
|
if (!map_ok) {
|
||||||
|
goto err_undo_map;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If we've got too many, that implies a descriptor loop. */
|
/* If we've got too many, that implies a descriptor loop. */
|
||||||
if ((in_num + out_num) > max) {
|
if ((in_num + out_num) > max) {
|
||||||
error_report("Looped descriptor");
|
virtio_error(vdev, "Looped descriptor");
|
||||||
exit(1);
|
goto err_undo_map;
|
||||||
}
|
}
|
||||||
} while ((i = virtqueue_read_next_desc(vdev, &desc, desc_pa, max)) != max);
|
|
||||||
|
rc = virtqueue_read_next_desc(vdev, &desc, desc_pa, max, &i);
|
||||||
|
} while (rc == VIRTQUEUE_READ_DESC_MORE);
|
||||||
|
|
||||||
|
if (rc == VIRTQUEUE_READ_DESC_ERROR) {
|
||||||
|
goto err_undo_map;
|
||||||
|
}
|
||||||
|
|
||||||
/* Now copy what we have collected and mapped */
|
/* Now copy what we have collected and mapped */
|
||||||
elem = virtqueue_alloc_element(sz, out_num, in_num);
|
elem = virtqueue_alloc_element(sz, out_num, in_num);
|
||||||
@ -651,6 +737,10 @@ void *virtqueue_pop(VirtQueue *vq, size_t sz)
|
|||||||
|
|
||||||
trace_virtqueue_pop(vq, elem, elem->in_num, elem->out_num);
|
trace_virtqueue_pop(vq, elem, elem->in_num, elem->out_num);
|
||||||
return elem;
|
return elem;
|
||||||
|
|
||||||
|
err_undo_map:
|
||||||
|
virtqueue_undo_map_desc(out_num, in_num, iov);
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Reading and writing a structure directly to QEMUFile is *awful*, but
|
/* Reading and writing a structure directly to QEMUFile is *awful*, but
|
||||||
@ -742,6 +832,10 @@ static void virtio_notify_vector(VirtIODevice *vdev, uint16_t vector)
|
|||||||
BusState *qbus = qdev_get_parent_bus(DEVICE(vdev));
|
BusState *qbus = qdev_get_parent_bus(DEVICE(vdev));
|
||||||
VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
|
VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
|
||||||
|
|
||||||
|
if (unlikely(vdev->broken)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (k->notify) {
|
if (k->notify) {
|
||||||
k->notify(qbus->parent, vector);
|
k->notify(qbus->parent, vector);
|
||||||
}
|
}
|
||||||
@ -825,6 +919,7 @@ void virtio_reset(void *opaque)
|
|||||||
k->reset(vdev);
|
k->reset(vdev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vdev->broken = false;
|
||||||
vdev->guest_features = 0;
|
vdev->guest_features = 0;
|
||||||
vdev->queue_sel = 0;
|
vdev->queue_sel = 0;
|
||||||
vdev->status = 0;
|
vdev->status = 0;
|
||||||
@ -1132,6 +1227,10 @@ static void virtio_queue_notify_vq(VirtQueue *vq)
|
|||||||
if (vq->vring.desc && vq->handle_output) {
|
if (vq->vring.desc && vq->handle_output) {
|
||||||
VirtIODevice *vdev = vq->vdev;
|
VirtIODevice *vdev = vq->vdev;
|
||||||
|
|
||||||
|
if (unlikely(vdev->broken)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
trace_virtio_queue_notify(vdev, vq - vdev->vq, vq);
|
trace_virtio_queue_notify(vdev, vq - vdev->vq, vq);
|
||||||
vq->handle_output(vdev, vq);
|
vq->handle_output(vdev, vq);
|
||||||
}
|
}
|
||||||
@ -1316,6 +1415,13 @@ static bool virtio_extra_state_needed(void *opaque)
|
|||||||
k->has_extra_state(qbus->parent);
|
k->has_extra_state(qbus->parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool virtio_broken_needed(void *opaque)
|
||||||
|
{
|
||||||
|
VirtIODevice *vdev = opaque;
|
||||||
|
|
||||||
|
return vdev->broken;
|
||||||
|
}
|
||||||
|
|
||||||
static const VMStateDescription vmstate_virtqueue = {
|
static const VMStateDescription vmstate_virtqueue = {
|
||||||
.name = "virtqueue_state",
|
.name = "virtqueue_state",
|
||||||
.version_id = 1,
|
.version_id = 1,
|
||||||
@ -1430,6 +1536,17 @@ static const VMStateDescription vmstate_virtio_64bit_features = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const VMStateDescription vmstate_virtio_broken = {
|
||||||
|
.name = "virtio/broken",
|
||||||
|
.version_id = 1,
|
||||||
|
.minimum_version_id = 1,
|
||||||
|
.needed = &virtio_broken_needed,
|
||||||
|
.fields = (VMStateField[]) {
|
||||||
|
VMSTATE_BOOL(broken, VirtIODevice),
|
||||||
|
VMSTATE_END_OF_LIST()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
static const VMStateDescription vmstate_virtio = {
|
static const VMStateDescription vmstate_virtio = {
|
||||||
.name = "virtio",
|
.name = "virtio",
|
||||||
.version_id = 1,
|
.version_id = 1,
|
||||||
@ -1443,6 +1560,7 @@ static const VMStateDescription vmstate_virtio = {
|
|||||||
&vmstate_virtio_64bit_features,
|
&vmstate_virtio_64bit_features,
|
||||||
&vmstate_virtio_virtqueues,
|
&vmstate_virtio_virtqueues,
|
||||||
&vmstate_virtio_ringsize,
|
&vmstate_virtio_ringsize,
|
||||||
|
&vmstate_virtio_broken,
|
||||||
&vmstate_virtio_extra_state,
|
&vmstate_virtio_extra_state,
|
||||||
NULL
|
NULL
|
||||||
}
|
}
|
||||||
@ -1608,7 +1726,7 @@ int virtio_load(VirtIODevice *vdev, QEMUFile *f, int version_id)
|
|||||||
"inconsistent with Host index 0x%x",
|
"inconsistent with Host index 0x%x",
|
||||||
i, vdev->vq[i].last_avail_idx);
|
i, vdev->vq[i].last_avail_idx);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if (k->load_queue) {
|
if (k->load_queue) {
|
||||||
ret = k->load_queue(qbus->parent, i, f);
|
ret = k->load_queue(qbus->parent, i, f);
|
||||||
if (ret)
|
if (ret)
|
||||||
@ -1753,6 +1871,7 @@ void virtio_init(VirtIODevice *vdev, const char *name,
|
|||||||
vdev->config_vector = VIRTIO_NO_VECTOR;
|
vdev->config_vector = VIRTIO_NO_VECTOR;
|
||||||
vdev->vq = g_malloc0(sizeof(VirtQueue) * VIRTIO_QUEUE_MAX);
|
vdev->vq = g_malloc0(sizeof(VirtQueue) * VIRTIO_QUEUE_MAX);
|
||||||
vdev->vm_running = runstate_is_running();
|
vdev->vm_running = runstate_is_running();
|
||||||
|
vdev->broken = false;
|
||||||
for (i = 0; i < VIRTIO_QUEUE_MAX; i++) {
|
for (i = 0; i < VIRTIO_QUEUE_MAX; i++) {
|
||||||
vdev->vq[i].vector = VIRTIO_NO_VECTOR;
|
vdev->vq[i].vector = VIRTIO_NO_VECTOR;
|
||||||
vdev->vq[i].vdev = vdev;
|
vdev->vq[i].vdev = vdev;
|
||||||
@ -1939,6 +2058,22 @@ void virtio_device_set_child_bus_name(VirtIODevice *vdev, char *bus_name)
|
|||||||
vdev->bus_name = g_strdup(bus_name);
|
vdev->bus_name = g_strdup(bus_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GCC_FMT_ATTR(2, 3) virtio_error(VirtIODevice *vdev, const char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
va_start(ap, fmt);
|
||||||
|
error_vreport(fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
vdev->broken = true;
|
||||||
|
|
||||||
|
if (virtio_vdev_has_feature(vdev, VIRTIO_F_VERSION_1)) {
|
||||||
|
virtio_set_status(vdev, vdev->status | VIRTIO_CONFIG_S_NEEDS_RESET);
|
||||||
|
virtio_notify_config(vdev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void virtio_device_realize(DeviceState *dev, Error **errp)
|
static void virtio_device_realize(DeviceState *dev, Error **errp)
|
||||||
{
|
{
|
||||||
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
|
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
|
||||||
|
@ -367,6 +367,7 @@ Aml *aml_sizeof(Aml *arg);
|
|||||||
Aml *aml_concatenate(Aml *source1, Aml *source2, Aml *target);
|
Aml *aml_concatenate(Aml *source1, Aml *source2, Aml *target);
|
||||||
Aml *aml_object_type(Aml *object);
|
Aml *aml_object_type(Aml *object);
|
||||||
|
|
||||||
|
void build_append_int_noprefix(GArray *table, uint64_t value, int size);
|
||||||
void
|
void
|
||||||
build_header(BIOSLinker *linker, GArray *table_data,
|
build_header(BIOSLinker *linker, GArray *table_data,
|
||||||
AcpiTableHeader *h, const char *sig, int len, uint8_t rev,
|
AcpiTableHeader *h, const char *sig, int len, uint8_t rev,
|
||||||
|
@ -367,17 +367,15 @@ int e820_get_num_entries(void);
|
|||||||
bool e820_get_entry(int, uint32_t, uint64_t *, uint64_t *);
|
bool e820_get_entry(int, uint32_t, uint64_t *, uint64_t *);
|
||||||
|
|
||||||
#define PC_COMPAT_2_8 \
|
#define PC_COMPAT_2_8 \
|
||||||
|
|
||||||
|
#define PC_COMPAT_2_7 \
|
||||||
|
HW_COMPAT_2_7 \
|
||||||
{\
|
{\
|
||||||
.driver = TYPE_X86_CPU,\
|
.driver = TYPE_X86_CPU,\
|
||||||
.property = "l3-cache",\
|
.property = "l3-cache",\
|
||||||
.value = "off",\
|
.value = "off",\
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
#define PC_COMPAT_2_7 \
|
|
||||||
PC_COMPAT_2_8 \
|
|
||||||
HW_COMPAT_2_7
|
|
||||||
|
|
||||||
#define PC_COMPAT_2_6 \
|
#define PC_COMPAT_2_6 \
|
||||||
HW_COMPAT_2_6 \
|
HW_COMPAT_2_6 \
|
||||||
{\
|
{\
|
||||||
@ -405,7 +403,6 @@ bool e820_get_entry(int, uint32_t, uint64_t *, uint64_t *);
|
|||||||
},
|
},
|
||||||
|
|
||||||
#define PC_COMPAT_2_5 \
|
#define PC_COMPAT_2_5 \
|
||||||
PC_COMPAT_2_6 \
|
|
||||||
HW_COMPAT_2_5
|
HW_COMPAT_2_5
|
||||||
|
|
||||||
/* Helper for setting model-id for CPU models that changed model-id
|
/* Helper for setting model-id for CPU models that changed model-id
|
||||||
|
@ -37,6 +37,12 @@
|
|||||||
typedef struct X86IOMMUState X86IOMMUState;
|
typedef struct X86IOMMUState X86IOMMUState;
|
||||||
typedef struct X86IOMMUClass X86IOMMUClass;
|
typedef struct X86IOMMUClass X86IOMMUClass;
|
||||||
|
|
||||||
|
typedef enum IommuType {
|
||||||
|
TYPE_INTEL,
|
||||||
|
TYPE_AMD,
|
||||||
|
TYPE_NONE
|
||||||
|
} IommuType;
|
||||||
|
|
||||||
struct X86IOMMUClass {
|
struct X86IOMMUClass {
|
||||||
SysBusDeviceClass parent;
|
SysBusDeviceClass parent;
|
||||||
/* Intel/AMD specific realize() hook */
|
/* Intel/AMD specific realize() hook */
|
||||||
@ -67,6 +73,7 @@ typedef struct IEC_Notifier IEC_Notifier;
|
|||||||
struct X86IOMMUState {
|
struct X86IOMMUState {
|
||||||
SysBusDevice busdev;
|
SysBusDevice busdev;
|
||||||
bool intr_supported; /* Whether vIOMMU supports IR */
|
bool intr_supported; /* Whether vIOMMU supports IR */
|
||||||
|
IommuType type; /* IOMMU type - AMD/Intel */
|
||||||
QLIST_HEAD(, IEC_Notifier) iec_notifiers; /* IEC notify list */
|
QLIST_HEAD(, IEC_Notifier) iec_notifiers; /* IEC notify list */
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -76,6 +83,11 @@ struct X86IOMMUState {
|
|||||||
*/
|
*/
|
||||||
X86IOMMUState *x86_iommu_get_default(void);
|
X86IOMMUState *x86_iommu_get_default(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* x86_iommu_get_type - get IOMMU type
|
||||||
|
*/
|
||||||
|
IommuType x86_iommu_get_type(void);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* x86_iommu_iec_register_notifier - register IEC (Interrupt Entry
|
* x86_iommu_iec_register_notifier - register IEC (Interrupt Entry
|
||||||
* Cache) notifiers
|
* Cache) notifiers
|
||||||
|
@ -13,9 +13,12 @@
|
|||||||
/* PCI bus */
|
/* PCI bus */
|
||||||
|
|
||||||
#define PCI_DEVFN(slot, func) ((((slot) & 0x1f) << 3) | ((func) & 0x07))
|
#define PCI_DEVFN(slot, func) ((((slot) & 0x1f) << 3) | ((func) & 0x07))
|
||||||
|
#define PCI_BUS_NUM(x) (((x) >> 8) & 0xff)
|
||||||
#define PCI_SLOT(devfn) (((devfn) >> 3) & 0x1f)
|
#define PCI_SLOT(devfn) (((devfn) >> 3) & 0x1f)
|
||||||
#define PCI_FUNC(devfn) ((devfn) & 0x07)
|
#define PCI_FUNC(devfn) ((devfn) & 0x07)
|
||||||
#define PCI_BUILD_BDF(bus, devfn) ((bus << 8) | (devfn))
|
#define PCI_BUILD_BDF(bus, devfn) ((bus << 8) | (devfn))
|
||||||
|
#define PCI_BUS_MAX 256
|
||||||
|
#define PCI_DEVFN_MAX 256
|
||||||
#define PCI_SLOT_MAX 32
|
#define PCI_SLOT_MAX 32
|
||||||
#define PCI_FUNC_MAX 8
|
#define PCI_FUNC_MAX 8
|
||||||
|
|
||||||
|
@ -87,6 +87,7 @@ struct VirtIODevice
|
|||||||
VirtQueue *vq;
|
VirtQueue *vq;
|
||||||
uint16_t device_id;
|
uint16_t device_id;
|
||||||
bool vm_running;
|
bool vm_running;
|
||||||
|
bool broken; /* device in invalid state, needs reset */
|
||||||
VMChangeStateEntry *vmstate;
|
VMChangeStateEntry *vmstate;
|
||||||
char *bus_name;
|
char *bus_name;
|
||||||
uint8_t device_endian;
|
uint8_t device_endian;
|
||||||
@ -135,6 +136,8 @@ void virtio_init(VirtIODevice *vdev, const char *name,
|
|||||||
uint16_t device_id, size_t config_size);
|
uint16_t device_id, size_t config_size);
|
||||||
void virtio_cleanup(VirtIODevice *vdev);
|
void virtio_cleanup(VirtIODevice *vdev);
|
||||||
|
|
||||||
|
void virtio_error(VirtIODevice *vdev, const char *fmt, ...) GCC_FMT_ATTR(2, 3);
|
||||||
|
|
||||||
/* Set the child bus name. */
|
/* Set the child bus name. */
|
||||||
void virtio_device_set_child_bus_name(VirtIODevice *vdev, char *bus_name);
|
void virtio_device_set_child_bus_name(VirtIODevice *vdev, char *bus_name);
|
||||||
|
|
||||||
|
@ -656,7 +656,7 @@ tests/usb-hcd-ehci-test$(EXESUF): tests/usb-hcd-ehci-test.o $(libqos-usb-obj-y)
|
|||||||
tests/usb-hcd-xhci-test$(EXESUF): tests/usb-hcd-xhci-test.o $(libqos-usb-obj-y)
|
tests/usb-hcd-xhci-test$(EXESUF): tests/usb-hcd-xhci-test.o $(libqos-usb-obj-y)
|
||||||
tests/pc-cpu-test$(EXESUF): tests/pc-cpu-test.o
|
tests/pc-cpu-test$(EXESUF): tests/pc-cpu-test.o
|
||||||
tests/postcopy-test$(EXESUF): tests/postcopy-test.o
|
tests/postcopy-test$(EXESUF): tests/postcopy-test.o
|
||||||
tests/vhost-user-test$(EXESUF): tests/vhost-user-test.o qemu-char.o qemu-timer.o $(qtest-obj-y) $(test-io-obj-y) $(libqos-virtio-obj-y)
|
tests/vhost-user-test$(EXESUF): tests/vhost-user-test.o qemu-char.o qemu-timer.o $(qtest-obj-y) $(test-io-obj-y) $(libqos-virtio-obj-y) $(libqos-pc-obj-y)
|
||||||
tests/qemu-iotests/socket_scm_helper$(EXESUF): tests/qemu-iotests/socket_scm_helper.o
|
tests/qemu-iotests/socket_scm_helper$(EXESUF): tests/qemu-iotests/socket_scm_helper.o
|
||||||
tests/test-qemu-opts$(EXESUF): tests/test-qemu-opts.o $(test-util-obj-y)
|
tests/test-qemu-opts$(EXESUF): tests/test-qemu-opts.o $(test-util-obj-y)
|
||||||
tests/test-write-threshold$(EXESUF): tests/test-write-threshold.o $(test-block-obj-y)
|
tests/test-write-threshold$(EXESUF): tests/test-write-threshold.o $(test-block-obj-y)
|
||||||
|
@ -20,6 +20,11 @@
|
|||||||
#include "libqos/pci-pc.h"
|
#include "libqos/pci-pc.h"
|
||||||
#include "libqos/virtio-pci.h"
|
#include "libqos/virtio-pci.h"
|
||||||
|
|
||||||
|
#include "libqos/pci-pc.h"
|
||||||
|
#include "libqos/virtio-pci.h"
|
||||||
|
#include "libqos/malloc-pc.h"
|
||||||
|
#include "hw/virtio/virtio-net.h"
|
||||||
|
|
||||||
#include <linux/vhost.h>
|
#include <linux/vhost.h>
|
||||||
#include <linux/virtio_ids.h>
|
#include <linux/virtio_ids.h>
|
||||||
#include <linux/virtio_net.h>
|
#include <linux/virtio_net.h>
|
||||||
@ -50,6 +55,7 @@
|
|||||||
#define VHOST_MEMORY_MAX_NREGIONS 8
|
#define VHOST_MEMORY_MAX_NREGIONS 8
|
||||||
|
|
||||||
#define VHOST_USER_F_PROTOCOL_FEATURES 30
|
#define VHOST_USER_F_PROTOCOL_FEATURES 30
|
||||||
|
#define VHOST_USER_PROTOCOL_F_MQ 0
|
||||||
#define VHOST_USER_PROTOCOL_F_LOG_SHMFD 1
|
#define VHOST_USER_PROTOCOL_F_LOG_SHMFD 1
|
||||||
|
|
||||||
#define VHOST_LOG_PAGE 0x1000
|
#define VHOST_LOG_PAGE 0x1000
|
||||||
@ -72,6 +78,7 @@ typedef enum VhostUserRequest {
|
|||||||
VHOST_USER_SET_VRING_ERR = 14,
|
VHOST_USER_SET_VRING_ERR = 14,
|
||||||
VHOST_USER_GET_PROTOCOL_FEATURES = 15,
|
VHOST_USER_GET_PROTOCOL_FEATURES = 15,
|
||||||
VHOST_USER_SET_PROTOCOL_FEATURES = 16,
|
VHOST_USER_SET_PROTOCOL_FEATURES = 16,
|
||||||
|
VHOST_USER_GET_QUEUE_NUM = 17,
|
||||||
VHOST_USER_SET_VRING_ENABLE = 18,
|
VHOST_USER_SET_VRING_ENABLE = 18,
|
||||||
VHOST_USER_MAX
|
VHOST_USER_MAX
|
||||||
} VhostUserRequest;
|
} VhostUserRequest;
|
||||||
@ -123,6 +130,13 @@ static VhostUserMsg m __attribute__ ((unused));
|
|||||||
#define VHOST_USER_VERSION (0x1)
|
#define VHOST_USER_VERSION (0x1)
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
enum {
|
||||||
|
TEST_FLAGS_OK,
|
||||||
|
TEST_FLAGS_DISCONNECT,
|
||||||
|
TEST_FLAGS_BAD,
|
||||||
|
TEST_FLAGS_END,
|
||||||
|
};
|
||||||
|
|
||||||
typedef struct TestServer {
|
typedef struct TestServer {
|
||||||
gchar *socket_path;
|
gchar *socket_path;
|
||||||
gchar *mig_path;
|
gchar *mig_path;
|
||||||
@ -135,6 +149,9 @@ typedef struct TestServer {
|
|||||||
CompatGCond data_cond;
|
CompatGCond data_cond;
|
||||||
int log_fd;
|
int log_fd;
|
||||||
uint64_t rings;
|
uint64_t rings;
|
||||||
|
bool test_fail;
|
||||||
|
int test_flags;
|
||||||
|
int queues;
|
||||||
} TestServer;
|
} TestServer;
|
||||||
|
|
||||||
static const char *tmpfs;
|
static const char *tmpfs;
|
||||||
@ -249,6 +266,12 @@ static void chr_read(void *opaque, const uint8_t *buf, int size)
|
|||||||
uint8_t *p = (uint8_t *) &msg;
|
uint8_t *p = (uint8_t *) &msg;
|
||||||
int fd;
|
int fd;
|
||||||
|
|
||||||
|
if (s->test_fail) {
|
||||||
|
qemu_chr_disconnect(chr);
|
||||||
|
/* now switch to non-failure */
|
||||||
|
s->test_fail = false;
|
||||||
|
}
|
||||||
|
|
||||||
if (size != VHOST_USER_HDR_SIZE) {
|
if (size != VHOST_USER_HDR_SIZE) {
|
||||||
g_test_message("Wrong message size received %d\n", size);
|
g_test_message("Wrong message size received %d\n", size);
|
||||||
return;
|
return;
|
||||||
@ -274,6 +297,13 @@ static void chr_read(void *opaque, const uint8_t *buf, int size)
|
|||||||
msg.size = sizeof(m.payload.u64);
|
msg.size = sizeof(m.payload.u64);
|
||||||
msg.payload.u64 = 0x1ULL << VHOST_F_LOG_ALL |
|
msg.payload.u64 = 0x1ULL << VHOST_F_LOG_ALL |
|
||||||
0x1ULL << VHOST_USER_F_PROTOCOL_FEATURES;
|
0x1ULL << VHOST_USER_F_PROTOCOL_FEATURES;
|
||||||
|
if (s->queues > 1) {
|
||||||
|
msg.payload.u64 |= 0x1ULL << VIRTIO_NET_F_MQ;
|
||||||
|
}
|
||||||
|
if (s->test_flags >= TEST_FLAGS_BAD) {
|
||||||
|
msg.payload.u64 = 0;
|
||||||
|
s->test_flags = TEST_FLAGS_END;
|
||||||
|
}
|
||||||
p = (uint8_t *) &msg;
|
p = (uint8_t *) &msg;
|
||||||
qemu_chr_fe_write_all(chr, p, VHOST_USER_HDR_SIZE + msg.size);
|
qemu_chr_fe_write_all(chr, p, VHOST_USER_HDR_SIZE + msg.size);
|
||||||
break;
|
break;
|
||||||
@ -281,6 +311,10 @@ static void chr_read(void *opaque, const uint8_t *buf, int size)
|
|||||||
case VHOST_USER_SET_FEATURES:
|
case VHOST_USER_SET_FEATURES:
|
||||||
g_assert_cmpint(msg.payload.u64 & (0x1ULL << VHOST_USER_F_PROTOCOL_FEATURES),
|
g_assert_cmpint(msg.payload.u64 & (0x1ULL << VHOST_USER_F_PROTOCOL_FEATURES),
|
||||||
!=, 0ULL);
|
!=, 0ULL);
|
||||||
|
if (s->test_flags == TEST_FLAGS_DISCONNECT) {
|
||||||
|
qemu_chr_disconnect(chr);
|
||||||
|
s->test_flags = TEST_FLAGS_BAD;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case VHOST_USER_GET_PROTOCOL_FEATURES:
|
case VHOST_USER_GET_PROTOCOL_FEATURES:
|
||||||
@ -288,6 +322,9 @@ static void chr_read(void *opaque, const uint8_t *buf, int size)
|
|||||||
msg.flags |= VHOST_USER_REPLY_MASK;
|
msg.flags |= VHOST_USER_REPLY_MASK;
|
||||||
msg.size = sizeof(m.payload.u64);
|
msg.size = sizeof(m.payload.u64);
|
||||||
msg.payload.u64 = 1 << VHOST_USER_PROTOCOL_F_LOG_SHMFD;
|
msg.payload.u64 = 1 << VHOST_USER_PROTOCOL_F_LOG_SHMFD;
|
||||||
|
if (s->queues > 1) {
|
||||||
|
msg.payload.u64 |= 1 << VHOST_USER_PROTOCOL_F_MQ;
|
||||||
|
}
|
||||||
p = (uint8_t *) &msg;
|
p = (uint8_t *) &msg;
|
||||||
qemu_chr_fe_write_all(chr, p, VHOST_USER_HDR_SIZE + msg.size);
|
qemu_chr_fe_write_all(chr, p, VHOST_USER_HDR_SIZE + msg.size);
|
||||||
break;
|
break;
|
||||||
@ -300,7 +337,7 @@ static void chr_read(void *opaque, const uint8_t *buf, int size)
|
|||||||
p = (uint8_t *) &msg;
|
p = (uint8_t *) &msg;
|
||||||
qemu_chr_fe_write_all(chr, p, VHOST_USER_HDR_SIZE + msg.size);
|
qemu_chr_fe_write_all(chr, p, VHOST_USER_HDR_SIZE + msg.size);
|
||||||
|
|
||||||
assert(msg.payload.state.index < 2);
|
assert(msg.payload.state.index < s->queues * 2);
|
||||||
s->rings &= ~(0x1ULL << msg.payload.state.index);
|
s->rings &= ~(0x1ULL << msg.payload.state.index);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -340,10 +377,18 @@ static void chr_read(void *opaque, const uint8_t *buf, int size)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case VHOST_USER_SET_VRING_BASE:
|
case VHOST_USER_SET_VRING_BASE:
|
||||||
assert(msg.payload.state.index < 2);
|
assert(msg.payload.state.index < s->queues * 2);
|
||||||
s->rings |= 0x1ULL << msg.payload.state.index;
|
s->rings |= 0x1ULL << msg.payload.state.index;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case VHOST_USER_GET_QUEUE_NUM:
|
||||||
|
msg.flags |= VHOST_USER_REPLY_MASK;
|
||||||
|
msg.size = sizeof(m.payload.u64);
|
||||||
|
msg.payload.u64 = s->queues;
|
||||||
|
p = (uint8_t *) &msg;
|
||||||
|
qemu_chr_fe_write_all(chr, p, VHOST_USER_HDR_SIZE + msg.size);
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -390,10 +435,21 @@ static TestServer *test_server_new(const gchar *name)
|
|||||||
g_cond_init(&server->data_cond);
|
g_cond_init(&server->data_cond);
|
||||||
|
|
||||||
server->log_fd = -1;
|
server->log_fd = -1;
|
||||||
|
server->queues = 1;
|
||||||
|
|
||||||
return server;
|
return server;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void chr_event(void *opaque, int event)
|
||||||
|
{
|
||||||
|
TestServer *s = opaque;
|
||||||
|
|
||||||
|
if (s->test_flags == TEST_FLAGS_END &&
|
||||||
|
event == CHR_EVENT_CLOSED) {
|
||||||
|
s->test_flags = TEST_FLAGS_OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void test_server_create_chr(TestServer *server, const gchar *opt)
|
static void test_server_create_chr(TestServer *server, const gchar *opt)
|
||||||
{
|
{
|
||||||
gchar *chr_path;
|
gchar *chr_path;
|
||||||
@ -402,7 +458,8 @@ static void test_server_create_chr(TestServer *server, const gchar *opt)
|
|||||||
server->chr = qemu_chr_new(server->chr_name, chr_path, NULL);
|
server->chr = qemu_chr_new(server->chr_name, chr_path, NULL);
|
||||||
g_free(chr_path);
|
g_free(chr_path);
|
||||||
|
|
||||||
qemu_chr_add_handlers(server->chr, chr_can_read, chr_read, NULL, server);
|
qemu_chr_add_handlers(server->chr, chr_can_read, chr_read,
|
||||||
|
chr_event, server);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void test_server_listen(TestServer *server)
|
static void test_server_listen(TestServer *server)
|
||||||
@ -641,7 +698,6 @@ static void test_migrate(void)
|
|||||||
global_qtest = global;
|
global_qtest = global;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_HAS_GLIB_SUBPROCESS_TESTS
|
|
||||||
static void wait_for_rings_started(TestServer *s, size_t count)
|
static void wait_for_rings_started(TestServer *s, size_t count)
|
||||||
{
|
{
|
||||||
gint64 end_time;
|
gint64 end_time;
|
||||||
@ -659,6 +715,7 @@ static void wait_for_rings_started(TestServer *s, size_t count)
|
|||||||
g_mutex_unlock(&s->data_mutex);
|
g_mutex_unlock(&s->data_mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_HAS_GLIB_SUBPROCESS_TESTS
|
||||||
static gboolean
|
static gboolean
|
||||||
reconnect_cb(gpointer user_data)
|
reconnect_cb(gpointer user_data)
|
||||||
{
|
{
|
||||||
@ -715,8 +772,144 @@ static void test_reconnect(void)
|
|||||||
g_test_trap_assert_passed();
|
g_test_trap_assert_passed();
|
||||||
g_free(path);
|
g_free(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void test_connect_fail_subprocess(void)
|
||||||
|
{
|
||||||
|
TestServer *s = test_server_new("connect-fail");
|
||||||
|
char *cmd;
|
||||||
|
|
||||||
|
s->test_fail = true;
|
||||||
|
g_thread_new("connect", connect_thread, s);
|
||||||
|
cmd = GET_QEMU_CMDE(s, 2, ",server", "");
|
||||||
|
qtest_start(cmd);
|
||||||
|
g_free(cmd);
|
||||||
|
|
||||||
|
init_virtio_dev(s);
|
||||||
|
wait_for_fds(s);
|
||||||
|
wait_for_rings_started(s, 2);
|
||||||
|
|
||||||
|
qtest_end();
|
||||||
|
test_server_free(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_connect_fail(void)
|
||||||
|
{
|
||||||
|
gchar *path = g_strdup_printf("/%s/vhost-user/connect-fail/subprocess",
|
||||||
|
qtest_get_arch());
|
||||||
|
g_test_trap_subprocess(path, 0, 0);
|
||||||
|
g_test_trap_assert_passed();
|
||||||
|
g_free(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_flags_mismatch_subprocess(void)
|
||||||
|
{
|
||||||
|
TestServer *s = test_server_new("flags-mismatch");
|
||||||
|
char *cmd;
|
||||||
|
|
||||||
|
s->test_flags = TEST_FLAGS_DISCONNECT;
|
||||||
|
g_thread_new("connect", connect_thread, s);
|
||||||
|
cmd = GET_QEMU_CMDE(s, 2, ",server", "");
|
||||||
|
qtest_start(cmd);
|
||||||
|
g_free(cmd);
|
||||||
|
|
||||||
|
init_virtio_dev(s);
|
||||||
|
wait_for_fds(s);
|
||||||
|
wait_for_rings_started(s, 2);
|
||||||
|
|
||||||
|
qtest_end();
|
||||||
|
test_server_free(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_flags_mismatch(void)
|
||||||
|
{
|
||||||
|
gchar *path = g_strdup_printf("/%s/vhost-user/flags-mismatch/subprocess",
|
||||||
|
qtest_get_arch());
|
||||||
|
g_test_trap_subprocess(path, 0, 0);
|
||||||
|
g_test_trap_assert_passed();
|
||||||
|
g_free(path);
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static QVirtioPCIDevice *virtio_net_pci_init(QPCIBus *bus, int slot)
|
||||||
|
{
|
||||||
|
QVirtioPCIDevice *dev;
|
||||||
|
|
||||||
|
dev = qvirtio_pci_device_find(bus, VIRTIO_ID_NET);
|
||||||
|
g_assert(dev != NULL);
|
||||||
|
g_assert_cmphex(dev->vdev.device_type, ==, VIRTIO_ID_NET);
|
||||||
|
|
||||||
|
qvirtio_pci_device_enable(dev);
|
||||||
|
qvirtio_reset(&qvirtio_pci, &dev->vdev);
|
||||||
|
qvirtio_set_acknowledge(&qvirtio_pci, &dev->vdev);
|
||||||
|
qvirtio_set_driver(&qvirtio_pci, &dev->vdev);
|
||||||
|
|
||||||
|
return dev;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void driver_init(const QVirtioBus *bus, QVirtioDevice *dev)
|
||||||
|
{
|
||||||
|
uint32_t features;
|
||||||
|
|
||||||
|
features = qvirtio_get_features(bus, dev);
|
||||||
|
features = features & ~(QVIRTIO_F_BAD_FEATURE |
|
||||||
|
(1u << VIRTIO_RING_F_INDIRECT_DESC) |
|
||||||
|
(1u << VIRTIO_RING_F_EVENT_IDX));
|
||||||
|
qvirtio_set_features(bus, dev, features);
|
||||||
|
|
||||||
|
qvirtio_set_driver_ok(bus, dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define PCI_SLOT 0x04
|
||||||
|
|
||||||
|
static void test_multiqueue(void)
|
||||||
|
{
|
||||||
|
const int queues = 2;
|
||||||
|
TestServer *s = test_server_new("mq");
|
||||||
|
QVirtioPCIDevice *dev;
|
||||||
|
QPCIBus *bus;
|
||||||
|
QVirtQueuePCI *vq[queues * 2];
|
||||||
|
QGuestAllocator *alloc;
|
||||||
|
char *cmd;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
s->queues = queues;
|
||||||
|
test_server_listen(s);
|
||||||
|
|
||||||
|
cmd = g_strdup_printf(QEMU_CMD_MEM QEMU_CMD_CHR QEMU_CMD_NETDEV ",queues=%d "
|
||||||
|
"-device virtio-net-pci,netdev=net0,mq=on,vectors=%d",
|
||||||
|
512, 512, root, s->chr_name,
|
||||||
|
s->socket_path, "", s->chr_name,
|
||||||
|
queues, queues * 2 + 2);
|
||||||
|
qtest_start(cmd);
|
||||||
|
g_free(cmd);
|
||||||
|
|
||||||
|
bus = qpci_init_pc();
|
||||||
|
dev = virtio_net_pci_init(bus, PCI_SLOT);
|
||||||
|
|
||||||
|
alloc = pc_alloc_init();
|
||||||
|
for (i = 0; i < queues * 2; i++) {
|
||||||
|
vq[i] = (QVirtQueuePCI *)qvirtqueue_setup(&qvirtio_pci, &dev->vdev,
|
||||||
|
alloc, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
driver_init(&qvirtio_pci, &dev->vdev);
|
||||||
|
wait_for_rings_started(s, queues * 2);
|
||||||
|
|
||||||
|
/* End test */
|
||||||
|
for (i = 0; i < queues * 2; i++) {
|
||||||
|
qvirtqueue_cleanup(&qvirtio_pci, &vq[i]->vq, alloc);
|
||||||
|
}
|
||||||
|
pc_alloc_uninit(alloc);
|
||||||
|
qvirtio_pci_device_disable(dev);
|
||||||
|
g_free(dev->pdev);
|
||||||
|
g_free(dev);
|
||||||
|
qpci_free_pc(bus);
|
||||||
|
qtest_end();
|
||||||
|
|
||||||
|
test_server_free(s);
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
QTestState *s = NULL;
|
QTestState *s = NULL;
|
||||||
@ -762,10 +955,17 @@ int main(int argc, char **argv)
|
|||||||
|
|
||||||
qtest_add_data_func("/vhost-user/read-guest-mem", server, read_guest_mem);
|
qtest_add_data_func("/vhost-user/read-guest-mem", server, read_guest_mem);
|
||||||
qtest_add_func("/vhost-user/migrate", test_migrate);
|
qtest_add_func("/vhost-user/migrate", test_migrate);
|
||||||
|
qtest_add_func("/vhost-user/multiqueue", test_multiqueue);
|
||||||
#ifdef CONFIG_HAS_GLIB_SUBPROCESS_TESTS
|
#ifdef CONFIG_HAS_GLIB_SUBPROCESS_TESTS
|
||||||
qtest_add_func("/vhost-user/reconnect/subprocess",
|
qtest_add_func("/vhost-user/reconnect/subprocess",
|
||||||
test_reconnect_subprocess);
|
test_reconnect_subprocess);
|
||||||
qtest_add_func("/vhost-user/reconnect", test_reconnect);
|
qtest_add_func("/vhost-user/reconnect", test_reconnect);
|
||||||
|
qtest_add_func("/vhost-user/connect-fail/subprocess",
|
||||||
|
test_connect_fail_subprocess);
|
||||||
|
qtest_add_func("/vhost-user/connect-fail", test_connect_fail);
|
||||||
|
qtest_add_func("/vhost-user/flags-mismatch/subprocess",
|
||||||
|
test_flags_mismatch_subprocess);
|
||||||
|
qtest_add_func("/vhost-user/flags-mismatch", test_flags_mismatch);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
ret = g_test_run();
|
ret = g_test_run();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user