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:
Peter Maydell 2016-09-26 19:47:00 +01:00
commit 7cfdc02dae
16 changed files with 2009 additions and 72 deletions

View file

@ -226,7 +226,7 @@ static void build_extop_package(GArray *package, uint8_t op)
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;

View file

@ -3,6 +3,7 @@ obj-y += multiboot.o
obj-y += pc.o pc_piix.o pc_q35.o
obj-y += pc_sysfw.o
obj-y += x86-iommu.o intel_iommu.o
obj-y += amd_iommu.o
obj-$(CONFIG_XEN) += ../xenpv/ xen/
obj-y += kvmvapic.o

View file

@ -59,7 +59,8 @@
#include "qapi/qmp/qint.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"
@ -2562,6 +2563,62 @@ build_dmar_q35(GArray *table_data, BIOSLinker *linker)
build_header(linker, table_data, (void *)(table_data->data + dmar_start),
"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 *
build_rsdp(GArray *rsdp_table, BIOSLinker *linker, unsigned rsdt_tbl_offset)
@ -2622,11 +2679,6 @@ static bool acpi_get_mcfg(AcpiMcfgInfo *mcfg)
return true;
}
static bool acpi_has_iommu(void)
{
return !!x86_iommu_get_default();
}
static
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);
build_mcfg_q35(tables_blob, tables->linker, &mcfg);
}
if (acpi_has_iommu()) {
acpi_add_table(table_offsets, tables_blob);
build_dmar_q35(tables_blob, tables->linker);
if (x86_iommu_get_default()) {
IommuType IOMMUType = x86_iommu_get_type();
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) {
nvdimm_build_acpi(table_offsets, tables_blob, tables->linker,

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
View 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

View file

@ -2453,6 +2453,7 @@ static void vtd_realize(DeviceState *dev, Error **errp)
X86IOMMUState *x86_iommu = X86_IOMMU_DEVICE(dev);
VTD_DPRINTF(GENERAL, "");
x86_iommu->type = TYPE_INTEL;
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,
"intel_iommu", DMAR_REG_SIZE);

View file

@ -13,3 +13,32 @@ mhp_pc_dimm_assigned_address(uint64_t addr) "0x%"PRIx64
# 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
# 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

View file

@ -71,6 +71,11 @@ X86IOMMUState *x86_iommu_get_default(void)
return x86_iommu_default;
}
IommuType x86_iommu_get_type(void)
{
return x86_iommu_default->type;
}
static void x86_iommu_realize(DeviceState *dev, Error **errp)
{
X86IOMMUState *x86_iommu = X86_IOMMU_DEVICE(dev);
@ -79,6 +84,7 @@ static void x86_iommu_realize(DeviceState *dev, Error **errp)
if (x86_class->realize) {
x86_class->realize(dev, errp);
}
x86_iommu_set_default(X86_IOMMU_DEVICE(dev));
}

View file

@ -303,6 +303,10 @@ void virtqueue_fill(VirtQueue *vq, const VirtQueueElement *elem,
virtqueue_unmap_sg(vq, elem, len);
if (unlikely(vq->vdev->broken)) {
return;
}
idx = (idx + vq->used_idx) % vq->vring.num;
uelem.id = elem->index;
@ -313,6 +317,12 @@ void virtqueue_fill(VirtQueue *vq, const VirtQueueElement *elem,
void virtqueue_flush(VirtQueue *vq, unsigned int count)
{
uint16_t old, new;
if (unlikely(vq->vdev->broken)) {
vq->inuse -= count;
return;
}
/* Make sure buffer is written before we update index. */
smp_wmb();
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. */
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);
exit(1);
return -EINVAL;
}
/* On success, callers read a descriptor at vq->last_avail_idx.
* 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;
}
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
* 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 (head >= vq->vring.num) {
error_report("Guest says index %u is available", head);
exit(1);
if (*head >= vq->vring.num) {
virtio_error(vq->vdev, "Guest says index %u is available", *head);
return false;
}
return head;
return true;
}
static unsigned virtqueue_read_next_desc(VirtIODevice *vdev, VRingDesc *desc,
hwaddr desc_pa, unsigned int max)
{
unsigned int next;
enum {
VIRTQUEUE_READ_DESC_ERROR = -1,
VIRTQUEUE_READ_DESC_DONE = 0, /* end of chain */
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 (!(desc->flags & VRING_DESC_F_NEXT)) {
return max;
return VIRTQUEUE_READ_DESC_DONE;
}
/* 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! */
smp_wmb();
if (next >= max) {
error_report("Desc next is %u", next);
exit(1);
if (*next >= max) {
virtio_error(vdev, "Desc next is %u", *next);
return VIRTQUEUE_READ_DESC_ERROR;
}
vring_desc_read(vdev, desc, desc_pa, next);
return next;
vring_desc_read(vdev, desc, desc_pa, *next);
return VIRTQUEUE_READ_DESC_MORE;
}
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 total_bufs, in_total, out_total;
int rc;
idx = vq->last_avail_idx;
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;
unsigned int max, num_bufs, indirect = 0;
VRingDesc desc;
hwaddr desc_pa;
int i;
unsigned int i;
max = vq->vring.num;
num_bufs = total_bufs;
i = virtqueue_get_head(vq, idx++);
if (!virtqueue_get_head(vq, idx++, &i)) {
goto err;
}
desc_pa = vq->vring.desc;
vring_desc_read(vdev, &desc, desc_pa, i);
if (desc.flags & VRING_DESC_F_INDIRECT) {
if (desc.len % sizeof(VRingDesc)) {
error_report("Invalid size for indirect buffer table");
exit(1);
virtio_error(vdev, "Invalid size for indirect buffer table");
goto err;
}
/* If we've got too many, that implies a descriptor loop. */
if (num_bufs >= max) {
error_report("Looped descriptor");
exit(1);
virtio_error(vdev, "Looped descriptor");
goto err;
}
/* loop over the indirect descriptor table */
@ -437,8 +456,8 @@ void virtqueue_get_avail_bytes(VirtQueue *vq, unsigned int *in_bytes,
do {
/* If we've got too many, that implies a descriptor loop. */
if (++num_bufs > max) {
error_report("Looped descriptor");
exit(1);
virtio_error(vdev, "Looped descriptor");
goto err;
}
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) {
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)
total_bufs = num_bufs;
else
total_bufs++;
}
if (rc < 0) {
goto err;
}
done:
if (in_bytes) {
*in_bytes = in_total;
@ -463,6 +493,11 @@ done:
if (out_bytes) {
*out_bytes = out_total;
}
return;
err:
in_total = out_total = 0;
goto done;
}
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;
}
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,
hwaddr pa, size_t sz)
{
bool ok = false;
unsigned num_sg = *p_num_sg;
assert(num_sg <= max_num_sg);
if (!sz) {
error_report("virtio: zero sized buffers are not allowed");
exit(1);
virtio_error(vdev, "virtio: zero sized buffers are not allowed");
goto out;
}
while (sz) {
hwaddr len = sz;
if (num_sg == max_num_sg) {
error_report("virtio: too many write descriptors in indirect table");
exit(1);
virtio_error(vdev, "virtio: too many write descriptors in "
"indirect table");
goto out;
}
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;
addr[num_sg] = pa;
@ -502,7 +545,28 @@ static void virtqueue_map_desc(unsigned int *p_num_sg, hwaddr *addr, struct iove
pa += len;
num_sg++;
}
ok = true;
out:
*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,
@ -577,7 +641,11 @@ void *virtqueue_pop(VirtQueue *vq, size_t sz)
hwaddr addr[VIRTQUEUE_MAX_SIZE];
struct iovec iov[VIRTQUEUE_MAX_SIZE];
VRingDesc desc;
int rc;
if (unlikely(vdev->broken)) {
return NULL;
}
if (virtio_queue_empty(vq)) {
return NULL;
}
@ -591,20 +659,24 @@ void *virtqueue_pop(VirtQueue *vq, size_t sz)
max = vq->vring.num;
if (vq->inuse >= vq->vring.num) {
error_report("Virtqueue size exceeded");
exit(1);
virtio_error(vdev, "Virtqueue size exceeded");
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)) {
vring_set_avail_event(vq, vq->last_avail_idx);
}
i = head;
vring_desc_read(vdev, &desc, desc_pa, i);
if (desc.flags & VRING_DESC_F_INDIRECT) {
if (desc.len % sizeof(VRingDesc)) {
error_report("Invalid size for indirect buffer table");
exit(1);
virtio_error(vdev, "Invalid size for indirect buffer table");
return NULL;
}
/* loop over the indirect descriptor table */
@ -616,24 +688,38 @@ void *virtqueue_pop(VirtQueue *vq, size_t sz)
/* Collect all the descriptors */
do {
bool map_ok;
if (desc.flags & VRING_DESC_F_WRITE) {
virtqueue_map_desc(&in_num, addr + out_num, iov + out_num,
VIRTQUEUE_MAX_SIZE - out_num, true, desc.addr, desc.len);
map_ok = virtqueue_map_desc(vdev, &in_num, addr + out_num,
iov + out_num,
VIRTQUEUE_MAX_SIZE - out_num, true,
desc.addr, desc.len);
} else {
if (in_num) {
error_report("Incorrect order for descriptors");
exit(1);
virtio_error(vdev, "Incorrect order for descriptors");
goto err_undo_map;
}
virtqueue_map_desc(&out_num, addr, iov,
VIRTQUEUE_MAX_SIZE, false, desc.addr, desc.len);
map_ok = virtqueue_map_desc(vdev, &out_num, addr, iov,
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 ((in_num + out_num) > max) {
error_report("Looped descriptor");
exit(1);
virtio_error(vdev, "Looped descriptor");
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 */
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);
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
@ -742,6 +832,10 @@ static void virtio_notify_vector(VirtIODevice *vdev, uint16_t vector)
BusState *qbus = qdev_get_parent_bus(DEVICE(vdev));
VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
if (unlikely(vdev->broken)) {
return;
}
if (k->notify) {
k->notify(qbus->parent, vector);
}
@ -825,6 +919,7 @@ void virtio_reset(void *opaque)
k->reset(vdev);
}
vdev->broken = false;
vdev->guest_features = 0;
vdev->queue_sel = 0;
vdev->status = 0;
@ -1132,6 +1227,10 @@ static void virtio_queue_notify_vq(VirtQueue *vq)
if (vq->vring.desc && vq->handle_output) {
VirtIODevice *vdev = vq->vdev;
if (unlikely(vdev->broken)) {
return;
}
trace_virtio_queue_notify(vdev, vq - vdev->vq, vq);
vq->handle_output(vdev, vq);
}
@ -1316,6 +1415,13 @@ static bool virtio_extra_state_needed(void *opaque)
k->has_extra_state(qbus->parent);
}
static bool virtio_broken_needed(void *opaque)
{
VirtIODevice *vdev = opaque;
return vdev->broken;
}
static const VMStateDescription vmstate_virtqueue = {
.name = "virtqueue_state",
.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 = {
.name = "virtio",
.version_id = 1,
@ -1443,6 +1560,7 @@ static const VMStateDescription vmstate_virtio = {
&vmstate_virtio_64bit_features,
&vmstate_virtio_virtqueues,
&vmstate_virtio_ringsize,
&vmstate_virtio_broken,
&vmstate_virtio_extra_state,
NULL
}
@ -1608,7 +1726,7 @@ int virtio_load(VirtIODevice *vdev, QEMUFile *f, int version_id)
"inconsistent with Host index 0x%x",
i, vdev->vq[i].last_avail_idx);
return -1;
}
}
if (k->load_queue) {
ret = k->load_queue(qbus->parent, i, f);
if (ret)
@ -1753,6 +1871,7 @@ void virtio_init(VirtIODevice *vdev, const char *name,
vdev->config_vector = VIRTIO_NO_VECTOR;
vdev->vq = g_malloc0(sizeof(VirtQueue) * VIRTIO_QUEUE_MAX);
vdev->vm_running = runstate_is_running();
vdev->broken = false;
for (i = 0; i < VIRTIO_QUEUE_MAX; i++) {
vdev->vq[i].vector = VIRTIO_NO_VECTOR;
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);
}
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)
{
VirtIODevice *vdev = VIRTIO_DEVICE(dev);

View file

@ -367,6 +367,7 @@ Aml *aml_sizeof(Aml *arg);
Aml *aml_concatenate(Aml *source1, Aml *source2, Aml *target);
Aml *aml_object_type(Aml *object);
void build_append_int_noprefix(GArray *table, uint64_t value, int size);
void
build_header(BIOSLinker *linker, GArray *table_data,
AcpiTableHeader *h, const char *sig, int len, uint8_t rev,

View file

@ -367,17 +367,15 @@ int e820_get_num_entries(void);
bool e820_get_entry(int, uint32_t, uint64_t *, uint64_t *);
#define PC_COMPAT_2_8 \
#define PC_COMPAT_2_7 \
HW_COMPAT_2_7 \
{\
.driver = TYPE_X86_CPU,\
.property = "l3-cache",\
.value = "off",\
},
#define PC_COMPAT_2_7 \
PC_COMPAT_2_8 \
HW_COMPAT_2_7
#define PC_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 \
PC_COMPAT_2_6 \
HW_COMPAT_2_5
/* Helper for setting model-id for CPU models that changed model-id

View file

@ -37,6 +37,12 @@
typedef struct X86IOMMUState X86IOMMUState;
typedef struct X86IOMMUClass X86IOMMUClass;
typedef enum IommuType {
TYPE_INTEL,
TYPE_AMD,
TYPE_NONE
} IommuType;
struct X86IOMMUClass {
SysBusDeviceClass parent;
/* Intel/AMD specific realize() hook */
@ -67,6 +73,7 @@ typedef struct IEC_Notifier IEC_Notifier;
struct X86IOMMUState {
SysBusDevice busdev;
bool intr_supported; /* Whether vIOMMU supports IR */
IommuType type; /* IOMMU type - AMD/Intel */
QLIST_HEAD(, IEC_Notifier) iec_notifiers; /* IEC notify list */
};
@ -76,6 +83,11 @@ struct X86IOMMUState {
*/
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
* Cache) notifiers

View file

@ -13,9 +13,12 @@
/* PCI bus */
#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_FUNC(devfn) ((devfn) & 0x07)
#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_FUNC_MAX 8

View file

@ -87,6 +87,7 @@ struct VirtIODevice
VirtQueue *vq;
uint16_t device_id;
bool vm_running;
bool broken; /* device in invalid state, needs reset */
VMChangeStateEntry *vmstate;
char *bus_name;
uint8_t device_endian;
@ -135,6 +136,8 @@ void virtio_init(VirtIODevice *vdev, const char *name,
uint16_t device_id, size_t config_size);
void virtio_cleanup(VirtIODevice *vdev);
void virtio_error(VirtIODevice *vdev, const char *fmt, ...) GCC_FMT_ATTR(2, 3);
/* Set the child bus name. */
void virtio_device_set_child_bus_name(VirtIODevice *vdev, char *bus_name);

View file

@ -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/pc-cpu-test$(EXESUF): tests/pc-cpu-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/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)

View file

@ -20,6 +20,11 @@
#include "libqos/pci-pc.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/virtio_ids.h>
#include <linux/virtio_net.h>
@ -50,6 +55,7 @@
#define VHOST_MEMORY_MAX_NREGIONS 8
#define VHOST_USER_F_PROTOCOL_FEATURES 30
#define VHOST_USER_PROTOCOL_F_MQ 0
#define VHOST_USER_PROTOCOL_F_LOG_SHMFD 1
#define VHOST_LOG_PAGE 0x1000
@ -72,6 +78,7 @@ typedef enum VhostUserRequest {
VHOST_USER_SET_VRING_ERR = 14,
VHOST_USER_GET_PROTOCOL_FEATURES = 15,
VHOST_USER_SET_PROTOCOL_FEATURES = 16,
VHOST_USER_GET_QUEUE_NUM = 17,
VHOST_USER_SET_VRING_ENABLE = 18,
VHOST_USER_MAX
} VhostUserRequest;
@ -123,6 +130,13 @@ static VhostUserMsg m __attribute__ ((unused));
#define VHOST_USER_VERSION (0x1)
/*****************************************************************************/
enum {
TEST_FLAGS_OK,
TEST_FLAGS_DISCONNECT,
TEST_FLAGS_BAD,
TEST_FLAGS_END,
};
typedef struct TestServer {
gchar *socket_path;
gchar *mig_path;
@ -135,6 +149,9 @@ typedef struct TestServer {
CompatGCond data_cond;
int log_fd;
uint64_t rings;
bool test_fail;
int test_flags;
int queues;
} TestServer;
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;
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) {
g_test_message("Wrong message size received %d\n", size);
return;
@ -274,6 +297,13 @@ static void chr_read(void *opaque, const uint8_t *buf, int size)
msg.size = sizeof(m.payload.u64);
msg.payload.u64 = 0x1ULL << VHOST_F_LOG_ALL |
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;
qemu_chr_fe_write_all(chr, p, VHOST_USER_HDR_SIZE + msg.size);
break;
@ -281,6 +311,10 @@ static void chr_read(void *opaque, const uint8_t *buf, int size)
case VHOST_USER_SET_FEATURES:
g_assert_cmpint(msg.payload.u64 & (0x1ULL << VHOST_USER_F_PROTOCOL_FEATURES),
!=, 0ULL);
if (s->test_flags == TEST_FLAGS_DISCONNECT) {
qemu_chr_disconnect(chr);
s->test_flags = TEST_FLAGS_BAD;
}
break;
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.size = sizeof(m.payload.u64);
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;
qemu_chr_fe_write_all(chr, p, VHOST_USER_HDR_SIZE + msg.size);
break;
@ -300,7 +337,7 @@ static void chr_read(void *opaque, const uint8_t *buf, int size)
p = (uint8_t *) &msg;
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);
break;
@ -340,10 +377,18 @@ static void chr_read(void *opaque, const uint8_t *buf, int size)
break;
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;
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:
break;
}
@ -390,10 +435,21 @@ static TestServer *test_server_new(const gchar *name)
g_cond_init(&server->data_cond);
server->log_fd = -1;
server->queues = 1;
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)
{
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);
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)
@ -641,7 +698,6 @@ static void test_migrate(void)
global_qtest = global;
}
#ifdef CONFIG_HAS_GLIB_SUBPROCESS_TESTS
static void wait_for_rings_started(TestServer *s, size_t count)
{
gint64 end_time;
@ -659,6 +715,7 @@ static void wait_for_rings_started(TestServer *s, size_t count)
g_mutex_unlock(&s->data_mutex);
}
#ifdef CONFIG_HAS_GLIB_SUBPROCESS_TESTS
static gboolean
reconnect_cb(gpointer user_data)
{
@ -715,8 +772,144 @@ static void test_reconnect(void)
g_test_trap_assert_passed();
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
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)
{
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_func("/vhost-user/migrate", test_migrate);
qtest_add_func("/vhost-user/multiqueue", test_multiqueue);
#ifdef CONFIG_HAS_GLIB_SUBPROCESS_TESTS
qtest_add_func("/vhost-user/reconnect/subprocess",
test_reconnect_subprocess);
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
ret = g_test_run();