pci, pc, virtio: fixes, features

AMD IOMMU VAPIC support + fixes all over the place.
 
 Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
 -----BEGIN PGP SIGNATURE-----
 
 iQEcBAABAgAGBQJb4IrKAAoJECgfDbjSjVRp9xEIAIT25r0SeThU32cl8955dBu3
 L2q2e+4du4KcwrC1a65mhBeATFtRthL/cWFHf1rvmwsp1t6ib+uVBH/3ezH1b48o
 rhrPjysYGbX+M/gxHv8uBM01JnMnmsaZVJv2iAifkO1fjJ5VCWXqJt89y7VryeUz
 LRzN1Zzq84umDXUuqptBKI8MF8ySwqnRHCE6YrbpTAppaJRY8zIyWkQzMd+Ls9m/
 Rwuo6QiySD4z5WrnL2hpvUCQw2qDTct9xDNrlGpxL1JVvOgo5Y5VFkF2X9IP7qap
 TIC7Y9cfUjGNf8ferYsydgzpyTjFrBMUqqcu65HjUlpACXwwwrLHPScfpT37VJI=
 =WPCi
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/mst/tags/for_upstream' into staging

pci, pc, virtio: fixes, features

AMD IOMMU VAPIC support + fixes all over the place.

Signed-off-by: Michael S. Tsirkin <mst@redhat.com>

# gpg: Signature made Mon 05 Nov 2018 18:24:10 GMT
# gpg:                using RSA key 281F0DB8D28D5469
# 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: (33 commits)
  vhost-scsi: prevent using uninitialized vqs
  piix_pci: fix i440fx data sheet link
  piix: use TYPE_FOO constants than string constats
  i440fx: use ARRAY_SIZE for pam_regions
  pci_bridge: fix typo in comment
  hw/pci: Add missing include
  hw/pci-bridge/ioh3420: Remove unuseful header
  hw/pci-bridge/xio3130: Remove unused functions
  tests/bios-tables-test: add 64-bit PCI MMIO aperture round-up test on Q35
  bios-tables-test: prepare expected files for mmio64
  hw/pci-host/x86: extend the 64-bit PCI hole relative to the fw-assigned base
  hw/pci-host/x86: extract get_pci_hole64_start_value() helpers
  pci-testdev: add optional memory bar
  MAINTAINERS: list "tests/acpi-test-data" files in ACPI/SMBIOS section
  x86_iommu/amd: Enable Guest virtual APIC support
  x86_iommu/amd: Add interrupt remap support when VAPIC is enabled
  i386: acpi: add IVHD device entry for IOAPIC
  x86_iommu/amd: Add interrupt remap support when VAPIC is not enabled
  x86_iommu/amd: Prepare for interrupt remap support
  x86_iommu/amd: make the address space naming consistent with intel-iommu
  ...

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2018-11-06 11:43:18 +00:00
commit b66db50f67
74 changed files with 838 additions and 290 deletions

View File

@ -1120,6 +1120,8 @@ F: hw/i386/acpi-build.[hc]
F: hw/arm/virt-acpi-build.c F: hw/arm/virt-acpi-build.c
F: tests/bios-tables-test.c F: tests/bios-tables-test.c
F: tests/acpi-utils.[hc] F: tests/acpi-utils.[hc]
F: tests/acpi-test-data/*
F: tests/acpi-test-data/*/*
ppc4xx ppc4xx
M: Alexander Graf <agraf@suse.de> M: Alexander Graf <agraf@suse.de>
@ -1376,7 +1378,7 @@ Intel Hexadecimal Object File Loader
M: Su Hang <suhang16@mails.ucas.ac.cn> M: Su Hang <suhang16@mails.ucas.ac.cn>
S: Maintained S: Maintained
F: tests/hexloader-test.c F: tests/hexloader-test.c
F: tests/hex-loader-check-data/test.hex F: tests/data/hex-loader/test.hex
CHRP NVRAM CHRP NVRAM
M: Thomas Huth <thuth@redhat.com> M: Thomas Huth <thuth@redhat.com>

57
configure vendored
View File

@ -7392,22 +7392,33 @@ if test "$ccache_cpp2" = "yes"; then
echo "export CCACHE_CPP2=y" >> $config_host_mak echo "export CCACHE_CPP2=y" >> $config_host_mak
fi fi
# build tree in object directory in case the source is not in the current directory # If we're using a separate build tree, set it up now.
# DIRS are directories which we simply mkdir in the build tree;
# LINKS are things to symlink back into the source tree
# (these can be both files and directories).
# Caution: do not add files or directories here using wildcards. This
# will result in problems later if a new file matching the wildcard is
# added to the source tree -- nothing will cause configure to be rerun
# so the build tree will be missing the link back to the new file, and
# tests might fail. Prefer to keep the relevant files in their own
# directory and symlink the directory instead.
DIRS="tests tests/tcg tests/tcg/cris tests/tcg/lm32 tests/libqos tests/qapi-schema tests/tcg/xtensa tests/qemu-iotests tests/vm" DIRS="tests tests/tcg tests/tcg/cris tests/tcg/lm32 tests/libqos tests/qapi-schema tests/tcg/xtensa tests/qemu-iotests tests/vm"
DIRS="$DIRS tests/fp" DIRS="$DIRS tests/fp"
DIRS="$DIRS docs docs/interop fsdev scsi" DIRS="$DIRS docs docs/interop fsdev scsi"
DIRS="$DIRS pc-bios/optionrom pc-bios/spapr-rtas pc-bios/s390-ccw" DIRS="$DIRS pc-bios/optionrom pc-bios/spapr-rtas pc-bios/s390-ccw"
DIRS="$DIRS roms/seabios roms/vgabios" DIRS="$DIRS roms/seabios roms/vgabios"
FILES="Makefile tests/tcg/Makefile qdict-test-data.txt" LINKS="Makefile tests/tcg/Makefile qdict-test-data.txt"
FILES="$FILES tests/tcg/cris/Makefile tests/tcg/cris/.gdbinit" LINKS="$LINKS tests/tcg/cris/Makefile tests/tcg/cris/.gdbinit"
FILES="$FILES tests/tcg/lm32/Makefile tests/tcg/xtensa/Makefile po/Makefile" LINKS="$LINKS tests/tcg/lm32/Makefile tests/tcg/xtensa/Makefile po/Makefile"
FILES="$FILES tests/fp/Makefile" LINKS="$LINKS tests/fp/Makefile"
FILES="$FILES pc-bios/optionrom/Makefile pc-bios/keymaps" LINKS="$LINKS pc-bios/optionrom/Makefile pc-bios/keymaps"
FILES="$FILES pc-bios/spapr-rtas/Makefile" LINKS="$LINKS pc-bios/spapr-rtas/Makefile"
FILES="$FILES pc-bios/s390-ccw/Makefile" LINKS="$LINKS pc-bios/s390-ccw/Makefile"
FILES="$FILES roms/seabios/Makefile roms/vgabios/Makefile" LINKS="$LINKS roms/seabios/Makefile roms/vgabios/Makefile"
FILES="$FILES pc-bios/qemu-icon.bmp" LINKS="$LINKS pc-bios/qemu-icon.bmp"
FILES="$FILES .gdbinit scripts" # scripts needed by relative path in .gdbinit LINKS="$LINKS .gdbinit scripts" # scripts needed by relative path in .gdbinit
LINKS="$LINKS tests/acceptance tests/data"
LINKS="$LINKS tests/qemu-iotests/check"
for bios_file in \ for bios_file in \
$source_path/pc-bios/*.bin \ $source_path/pc-bios/*.bin \
$source_path/pc-bios/*.lid \ $source_path/pc-bios/*.lid \
@ -7419,18 +7430,10 @@ for bios_file in \
$source_path/pc-bios/u-boot.* \ $source_path/pc-bios/u-boot.* \
$source_path/pc-bios/palcode-* $source_path/pc-bios/palcode-*
do do
FILES="$FILES pc-bios/$(basename $bios_file)" LINKS="$LINKS pc-bios/$(basename $bios_file)"
done
for test_file in $(find $source_path/tests/acpi-test-data -type f)
do
FILES="$FILES tests/acpi-test-data$(echo $test_file | sed -e 's/.*acpi-test-data//')"
done
for test_file in $(find $source_path/tests/hex-loader-check-data -type f)
do
FILES="$FILES tests/hex-loader-check-data$(echo $test_file | sed -e 's/.*hex-loader-check-data//')"
done done
mkdir -p $DIRS mkdir -p $DIRS
for f in $FILES ; do for f in $LINKS ; do
if [ -e "$source_path/$f" ] && [ "$pwd_is_source_path" != "y" ]; then if [ -e "$source_path/$f" ] && [ "$pwd_is_source_path" != "y" ]; then
symlink "$source_path/$f" "$f" symlink "$source_path/$f" "$f"
fi fi
@ -7452,25 +7455,13 @@ for rom in seabios vgabios ; do
echo "RANLIB=$ranlib" >> $config_mak echo "RANLIB=$ranlib" >> $config_mak
done done
# set up tests data directory
for tests_subdir in acceptance data; do
if [ ! -e tests/$tests_subdir ]; then
symlink "$source_path/tests/$tests_subdir" tests/$tests_subdir
fi
done
# set up qemu-iotests in this build directory # set up qemu-iotests in this build directory
iotests_common_env="tests/qemu-iotests/common.env" iotests_common_env="tests/qemu-iotests/common.env"
iotests_check="tests/qemu-iotests/check"
echo "# Automatically generated by configure - do not modify" > "$iotests_common_env" echo "# Automatically generated by configure - do not modify" > "$iotests_common_env"
echo >> "$iotests_common_env" echo >> "$iotests_common_env"
echo "export PYTHON='$python'" >> "$iotests_common_env" echo "export PYTHON='$python'" >> "$iotests_common_env"
if [ ! -e "$iotests_check" ]; then
symlink "$source_path/$iotests_check" "$iotests_check"
fi
# Save the configure command line for later reuse. # Save the configure command line for later reuse.
cat <<EOD >config.status cat <<EOD >config.status
#!/bin/sh #!/bin/sh

View File

@ -1,11 +1,11 @@
pci-test is a device used for testing low level IO pci-test is a device used for testing low level IO
device implements up to two BARs: BAR0 and BAR1. device implements up to three BARs: BAR0, BAR1 and BAR2.
Each BAR can be memory or IO. Guests must detect Each of BAR 0+1 can be memory or IO. Guests must detect
BAR type and act accordingly. BAR types and act accordingly.
Each BAR size is up to 4K bytes. BAR 0+1 size is up to 4K bytes each.
Each BAR starts with the following header: BAR 0+1 starts with the following header:
typedef struct PCITestDevHdr { typedef struct PCITestDevHdr {
uint8_t test; <- write-only, starts a given test number uint8_t test; <- write-only, starts a given test number
@ -24,3 +24,8 @@ All registers are little endian.
device is expected to always implement tests 0 to N on each BAR, and to add new device is expected to always implement tests 0 to N on each BAR, and to add new
tests with higher numbers. In this way a guest can scan test numbers until it tests with higher numbers. In this way a guest can scan test numbers until it
detects an access type that it does not support on this BAR, then stop. detects an access type that it does not support on this BAR, then stop.
BAR2 is a 64bit memory bar, without backing storage. It is disabled
by default and can be enabled using the membar=<size> property. This
can be used to test whether guests handle pci bars of a specific
(possibly quite large) size correctly.

View File

@ -217,7 +217,32 @@ static uint64_t vhost_user_blk_get_features(VirtIODevice *vdev,
static void vhost_user_blk_handle_output(VirtIODevice *vdev, VirtQueue *vq) static void vhost_user_blk_handle_output(VirtIODevice *vdev, VirtQueue *vq)
{ {
VHostUserBlk *s = VHOST_USER_BLK(vdev);
int i;
if (!(virtio_host_has_feature(vdev, VIRTIO_F_VERSION_1) &&
!virtio_vdev_has_feature(vdev, VIRTIO_F_VERSION_1))) {
return;
}
if (s->dev.started) {
return;
}
/* Some guests kick before setting VIRTIO_CONFIG_S_DRIVER_OK so start
* vhost here instead of waiting for .set_status().
*/
vhost_user_blk_start(vdev);
/* Kick right away to begin processing requests already in vring */
for (i = 0; i < s->dev.nvqs; i++) {
VirtQueue *kick_vq = virtio_get_queue(vdev, i);
if (!virtio_queue_get_desc_addr(vdev, i)) {
continue;
}
event_notifier_set(virtio_queue_get_host_notifier(kick_vq));
}
} }
static void vhost_user_blk_device_realize(DeviceState *dev, Error **errp) static void vhost_user_blk_device_realize(DeviceState *dev, Error **errp)

View File

@ -97,8 +97,8 @@ static void virtio_blk_rw_complete(void *opaque, int ret)
if (req->qiov.nalloc != -1) { if (req->qiov.nalloc != -1) {
/* If nalloc is != 1 req->qiov is a local copy of the original /* If nalloc is != 1 req->qiov is a local copy of the original
* external iovec. It was allocated in submit_merged_requests * external iovec. It was allocated in submit_requests to be
* to be able to merge requests. */ * able to merge requests. */
qemu_iovec_destroy(&req->qiov); qemu_iovec_destroy(&req->qiov);
} }

View File

@ -2467,9 +2467,12 @@ build_dmar_q35(GArray *table_data, BIOSLinker *linker)
* IVRS table as specified in AMD IOMMU Specification v2.62, Section 5.2 * IVRS table as specified in AMD IOMMU Specification v2.62, Section 5.2
* accessible here http://support.amd.com/TechDocs/48882_IOMMU.pdf * accessible here http://support.amd.com/TechDocs/48882_IOMMU.pdf
*/ */
#define IOAPIC_SB_DEVID (uint64_t)PCI_BUILD_BDF(0, PCI_DEVFN(0x14, 0))
static void static void
build_amd_iommu(GArray *table_data, BIOSLinker *linker) build_amd_iommu(GArray *table_data, BIOSLinker *linker)
{ {
int ivhd_table_len = 28;
int iommu_start = table_data->len; int iommu_start = table_data->len;
AMDVIState *s = AMD_IOMMU_DEVICE(x86_iommu_get_default()); AMDVIState *s = AMD_IOMMU_DEVICE(x86_iommu_get_default());
@ -2491,8 +2494,16 @@ build_amd_iommu(GArray *table_data, BIOSLinker *linker)
(1UL << 6) | /* PrefSup */ (1UL << 6) | /* PrefSup */
(1UL << 7), /* PPRSup */ (1UL << 7), /* PPRSup */
1); 1);
/*
* When interrupt remapping is supported, we add a special IVHD device
* for type IO-APIC.
*/
if (x86_iommu_get_default()->intr_supported) {
ivhd_table_len += 8;
}
/* IVHD length */ /* IVHD length */
build_append_int_noprefix(table_data, 28, 2); build_append_int_noprefix(table_data, ivhd_table_len, 2);
/* DeviceID */ /* DeviceID */
build_append_int_noprefix(table_data, s->devid, 2); build_append_int_noprefix(table_data, s->devid, 2);
/* Capability offset */ /* Capability offset */
@ -2507,7 +2518,8 @@ build_amd_iommu(GArray *table_data, BIOSLinker *linker)
build_append_int_noprefix(table_data, build_append_int_noprefix(table_data,
(48UL << 30) | /* HATS */ (48UL << 30) | /* HATS */
(48UL << 28) | /* GATS */ (48UL << 28) | /* GATS */
(1UL << 2), /* GTSup */ (1UL << 2) | /* GTSup */
(1UL << 6), /* GASup */
4); 4);
/* /*
* Type 1 device entry reporting all devices * Type 1 device entry reporting all devices
@ -2516,6 +2528,21 @@ build_amd_iommu(GArray *table_data, BIOSLinker *linker)
*/ */
build_append_int_noprefix(table_data, 0x0000001, 4); build_append_int_noprefix(table_data, 0x0000001, 4);
/*
* Add a special IVHD device type.
* Refer to spec - Table 95: IVHD device entry type codes
*
* Linux IOMMU driver checks for the special IVHD device (type IO-APIC).
* See Linux kernel commit 'c2ff5cf5294bcbd7fa50f7d860e90a66db7e5059'
*/
if (x86_iommu_get_default()->intr_supported) {
build_append_int_noprefix(table_data,
(0x1ull << 56) | /* type IOAPIC */
(IOAPIC_SB_DEVID << 40) | /* IOAPIC devid */
0x48, /* special device */
8);
}
build_header(linker, table_data, (void *)(table_data->data + iommu_start), build_header(linker, table_data, (void *)(table_data->data + iommu_start),
"IVRS", table_data->len - iommu_start, 1, NULL, NULL); "IVRS", table_data->len - iommu_start, 1, NULL, NULL);
} }

View File

@ -26,7 +26,9 @@
#include "amd_iommu.h" #include "amd_iommu.h"
#include "qapi/error.h" #include "qapi/error.h"
#include "qemu/error-report.h" #include "qemu/error-report.h"
#include "hw/i386/apic_internal.h"
#include "trace.h" #include "trace.h"
#include "hw/i386/apic-msidef.h"
/* used AMD-Vi MMIO registers */ /* used AMD-Vi MMIO registers */
const char *amdvi_mmio_low[] = { const char *amdvi_mmio_low[] = {
@ -55,6 +57,7 @@ struct AMDVIAddressSpace {
uint8_t bus_num; /* bus number */ uint8_t bus_num; /* bus number */
uint8_t devfn; /* device function */ uint8_t devfn; /* device function */
AMDVIState *iommu_state; /* AMDVI - one per machine */ AMDVIState *iommu_state; /* AMDVI - one per machine */
MemoryRegion root; /* AMDVI Root memory map region */
IOMMUMemoryRegion iommu; /* Device's address translation region */ IOMMUMemoryRegion iommu; /* Device's address translation region */
MemoryRegion iommu_ir; /* Device's interrupt remapping region */ MemoryRegion iommu_ir; /* Device's interrupt remapping region */
AddressSpace as; /* device's corresponding address space */ AddressSpace as; /* device's corresponding address space */
@ -605,6 +608,7 @@ static void amdvi_handle_control_write(AMDVIState *s)
s->completion_wait_intr = !!(control & AMDVI_MMIO_CONTROL_COMWAITINTEN); s->completion_wait_intr = !!(control & AMDVI_MMIO_CONTROL_COMWAITINTEN);
s->cmdbuf_enabled = s->enabled && !!(control & s->cmdbuf_enabled = s->enabled && !!(control &
AMDVI_MMIO_CONTROL_CMDBUFLEN); AMDVI_MMIO_CONTROL_CMDBUFLEN);
s->ga_enabled = !!(control & AMDVI_MMIO_CONTROL_GAEN);
/* update the flags depending on the control register */ /* update the flags depending on the control register */
if (s->cmdbuf_enabled) { if (s->cmdbuf_enabled) {
@ -807,7 +811,7 @@ static inline uint64_t amdvi_get_perms(uint64_t entry)
AMDVI_DEV_PERM_SHIFT; AMDVI_DEV_PERM_SHIFT;
} }
/* a valid entry should have V = 1 and reserved bits honoured */ /* validate that reserved bits are honoured */
static bool amdvi_validate_dte(AMDVIState *s, uint16_t devid, static bool amdvi_validate_dte(AMDVIState *s, uint16_t devid,
uint64_t *dte) uint64_t *dte)
{ {
@ -820,7 +824,7 @@ static bool amdvi_validate_dte(AMDVIState *s, uint16_t devid,
return false; return false;
} }
return dte[0] & AMDVI_DEV_VALID; return true;
} }
/* get a device table entry given the devid */ /* get a device table entry given the devid */
@ -966,8 +970,12 @@ static void amdvi_do_translate(AMDVIAddressSpace *as, hwaddr addr,
return; return;
} }
/* devices with V = 0 are not translated */
if (!amdvi_get_dte(s, devid, entry)) { if (!amdvi_get_dte(s, devid, entry)) {
return;
}
/* devices with V = 0 are not translated */
if (!(entry[0] & AMDVI_DEV_VALID)) {
goto out; goto out;
} }
@ -1026,10 +1034,366 @@ static IOMMUTLBEntry amdvi_translate(IOMMUMemoryRegion *iommu, hwaddr addr,
return ret; return ret;
} }
static int amdvi_get_irte(AMDVIState *s, MSIMessage *origin, uint64_t *dte,
union irte *irte, uint16_t devid)
{
uint64_t irte_root, offset;
irte_root = dte[2] & AMDVI_IR_PHYS_ADDR_MASK;
offset = (origin->data & AMDVI_IRTE_OFFSET) << 2;
trace_amdvi_ir_irte(irte_root, offset);
if (dma_memory_read(&address_space_memory, irte_root + offset,
irte, sizeof(*irte))) {
trace_amdvi_ir_err("failed to get irte");
return -AMDVI_IR_GET_IRTE;
}
trace_amdvi_ir_irte_val(irte->val);
return 0;
}
static int amdvi_int_remap_legacy(AMDVIState *iommu,
MSIMessage *origin,
MSIMessage *translated,
uint64_t *dte,
X86IOMMUIrq *irq,
uint16_t sid)
{
int ret;
union irte irte;
/* get interrupt remapping table */
ret = amdvi_get_irte(iommu, origin, dte, &irte, sid);
if (ret < 0) {
return ret;
}
if (!irte.fields.valid) {
trace_amdvi_ir_target_abort("RemapEn is disabled");
return -AMDVI_IR_TARGET_ABORT;
}
if (irte.fields.guest_mode) {
error_report_once("guest mode is not zero");
return -AMDVI_IR_ERR;
}
if (irte.fields.int_type > AMDVI_IOAPIC_INT_TYPE_ARBITRATED) {
error_report_once("reserved int_type");
return -AMDVI_IR_ERR;
}
irq->delivery_mode = irte.fields.int_type;
irq->vector = irte.fields.vector;
irq->dest_mode = irte.fields.dm;
irq->redir_hint = irte.fields.rq_eoi;
irq->dest = irte.fields.destination;
return 0;
}
static int amdvi_get_irte_ga(AMDVIState *s, MSIMessage *origin, uint64_t *dte,
struct irte_ga *irte, uint16_t devid)
{
uint64_t irte_root, offset;
irte_root = dte[2] & AMDVI_IR_PHYS_ADDR_MASK;
offset = (origin->data & AMDVI_IRTE_OFFSET) << 4;
trace_amdvi_ir_irte(irte_root, offset);
if (dma_memory_read(&address_space_memory, irte_root + offset,
irte, sizeof(*irte))) {
trace_amdvi_ir_err("failed to get irte_ga");
return -AMDVI_IR_GET_IRTE;
}
trace_amdvi_ir_irte_ga_val(irte->hi.val, irte->lo.val);
return 0;
}
static int amdvi_int_remap_ga(AMDVIState *iommu,
MSIMessage *origin,
MSIMessage *translated,
uint64_t *dte,
X86IOMMUIrq *irq,
uint16_t sid)
{
int ret;
struct irte_ga irte;
/* get interrupt remapping table */
ret = amdvi_get_irte_ga(iommu, origin, dte, &irte, sid);
if (ret < 0) {
return ret;
}
if (!irte.lo.fields_remap.valid) {
trace_amdvi_ir_target_abort("RemapEn is disabled");
return -AMDVI_IR_TARGET_ABORT;
}
if (irte.lo.fields_remap.guest_mode) {
error_report_once("guest mode is not zero");
return -AMDVI_IR_ERR;
}
if (irte.lo.fields_remap.int_type > AMDVI_IOAPIC_INT_TYPE_ARBITRATED) {
error_report_once("reserved int_type is set");
return -AMDVI_IR_ERR;
}
irq->delivery_mode = irte.lo.fields_remap.int_type;
irq->vector = irte.hi.fields.vector;
irq->dest_mode = irte.lo.fields_remap.dm;
irq->redir_hint = irte.lo.fields_remap.rq_eoi;
irq->dest = irte.lo.fields_remap.destination;
return 0;
}
static int __amdvi_int_remap_msi(AMDVIState *iommu,
MSIMessage *origin,
MSIMessage *translated,
uint64_t *dte,
X86IOMMUIrq *irq,
uint16_t sid)
{
int ret;
uint8_t int_ctl;
int_ctl = (dte[2] >> AMDVI_IR_INTCTL_SHIFT) & 3;
trace_amdvi_ir_intctl(int_ctl);
switch (int_ctl) {
case AMDVI_IR_INTCTL_PASS:
memcpy(translated, origin, sizeof(*origin));
return 0;
case AMDVI_IR_INTCTL_REMAP:
break;
case AMDVI_IR_INTCTL_ABORT:
trace_amdvi_ir_target_abort("int_ctl abort");
return -AMDVI_IR_TARGET_ABORT;
default:
trace_amdvi_ir_err("int_ctl reserved");
return -AMDVI_IR_ERR;
}
if (iommu->ga_enabled) {
ret = amdvi_int_remap_ga(iommu, origin, translated, dte, irq, sid);
} else {
ret = amdvi_int_remap_legacy(iommu, origin, translated, dte, irq, sid);
}
return ret;
}
/* Interrupt remapping for MSI/MSI-X entry */
static int amdvi_int_remap_msi(AMDVIState *iommu,
MSIMessage *origin,
MSIMessage *translated,
uint16_t sid)
{
int ret = 0;
uint64_t pass = 0;
uint64_t dte[4] = { 0 };
X86IOMMUIrq irq = { 0 };
uint8_t dest_mode, delivery_mode;
assert(origin && translated);
/*
* When IOMMU is enabled, interrupt remap request will come either from
* IO-APIC or PCI device. If interrupt is from PCI device then it will
* have a valid requester id but if the interrupt is from IO-APIC
* then requester id will be invalid.
*/
if (sid == X86_IOMMU_SID_INVALID) {
sid = AMDVI_IOAPIC_SB_DEVID;
}
trace_amdvi_ir_remap_msi_req(origin->address, origin->data, sid);
/* check if device table entry is set before we go further. */
if (!iommu || !iommu->devtab_len) {
memcpy(translated, origin, sizeof(*origin));
goto out;
}
if (!amdvi_get_dte(iommu, sid, dte)) {
return -AMDVI_IR_ERR;
}
/* Check if IR is enabled in DTE */
if (!(dte[2] & AMDVI_IR_REMAP_ENABLE)) {
memcpy(translated, origin, sizeof(*origin));
goto out;
}
/* validate that we are configure with intremap=on */
if (!X86_IOMMU_DEVICE(iommu)->intr_supported) {
trace_amdvi_err("Interrupt remapping is enabled in the guest but "
"not in the host. Use intremap=on to enable interrupt "
"remapping in amd-iommu.");
return -AMDVI_IR_ERR;
}
if (origin->address & AMDVI_MSI_ADDR_HI_MASK) {
trace_amdvi_err("MSI address high 32 bits non-zero when "
"Interrupt Remapping enabled.");
return -AMDVI_IR_ERR;
}
if ((origin->address & AMDVI_MSI_ADDR_LO_MASK) != APIC_DEFAULT_ADDRESS) {
trace_amdvi_err("MSI is not from IOAPIC.");
return -AMDVI_IR_ERR;
}
/*
* The MSI data register [10:8] are used to get the upstream interrupt type.
*
* See MSI/MSI-X format:
* https://pdfs.semanticscholar.org/presentation/9420/c279e942eca568157711ef5c92b800c40a79.pdf
* (page 5)
*/
delivery_mode = (origin->data >> MSI_DATA_DELIVERY_MODE_SHIFT) & 7;
switch (delivery_mode) {
case AMDVI_IOAPIC_INT_TYPE_FIXED:
case AMDVI_IOAPIC_INT_TYPE_ARBITRATED:
trace_amdvi_ir_delivery_mode("fixed/arbitrated");
ret = __amdvi_int_remap_msi(iommu, origin, translated, dte, &irq, sid);
if (ret < 0) {
goto remap_fail;
} else {
/* Translate IRQ to MSI messages */
x86_iommu_irq_to_msi_message(&irq, translated);
goto out;
}
break;
case AMDVI_IOAPIC_INT_TYPE_SMI:
error_report("SMI is not supported!");
ret = -AMDVI_IR_ERR;
break;
case AMDVI_IOAPIC_INT_TYPE_NMI:
pass = dte[3] & AMDVI_DEV_NMI_PASS_MASK;
trace_amdvi_ir_delivery_mode("nmi");
break;
case AMDVI_IOAPIC_INT_TYPE_INIT:
pass = dte[3] & AMDVI_DEV_INT_PASS_MASK;
trace_amdvi_ir_delivery_mode("init");
break;
case AMDVI_IOAPIC_INT_TYPE_EINT:
pass = dte[3] & AMDVI_DEV_EINT_PASS_MASK;
trace_amdvi_ir_delivery_mode("eint");
break;
default:
trace_amdvi_ir_delivery_mode("unsupported delivery_mode");
ret = -AMDVI_IR_ERR;
break;
}
if (ret < 0) {
goto remap_fail;
}
/*
* The MSI address register bit[2] is used to get the destination
* mode. The dest_mode 1 is valid for fixed and arbitrated interrupts
* only.
*/
dest_mode = (origin->address >> MSI_ADDR_DEST_MODE_SHIFT) & 1;
if (dest_mode) {
trace_amdvi_ir_err("invalid dest_mode");
ret = -AMDVI_IR_ERR;
goto remap_fail;
}
if (pass) {
memcpy(translated, origin, sizeof(*origin));
} else {
trace_amdvi_ir_err("passthrough is not enabled");
ret = -AMDVI_IR_ERR;
goto remap_fail;
}
out:
trace_amdvi_ir_remap_msi(origin->address, origin->data,
translated->address, translated->data);
return 0;
remap_fail:
return ret;
}
static int amdvi_int_remap(X86IOMMUState *iommu,
MSIMessage *origin,
MSIMessage *translated,
uint16_t sid)
{
return amdvi_int_remap_msi(AMD_IOMMU_DEVICE(iommu), origin,
translated, sid);
}
static MemTxResult amdvi_mem_ir_write(void *opaque, hwaddr addr,
uint64_t value, unsigned size,
MemTxAttrs attrs)
{
int ret;
MSIMessage from = { 0, 0 }, to = { 0, 0 };
uint16_t sid = AMDVI_IOAPIC_SB_DEVID;
from.address = (uint64_t) addr + AMDVI_INT_ADDR_FIRST;
from.data = (uint32_t) value;
trace_amdvi_mem_ir_write_req(addr, value, size);
if (!attrs.unspecified) {
/* We have explicit Source ID */
sid = attrs.requester_id;
}
ret = amdvi_int_remap_msi(opaque, &from, &to, sid);
if (ret < 0) {
/* TODO: log the event using IOMMU log event interface */
error_report_once("failed to remap interrupt from devid 0x%x", sid);
return MEMTX_ERROR;
}
apic_get_class()->send_msi(&to);
trace_amdvi_mem_ir_write(to.address, to.data);
return MEMTX_OK;
}
static MemTxResult amdvi_mem_ir_read(void *opaque, hwaddr addr,
uint64_t *data, unsigned size,
MemTxAttrs attrs)
{
return MEMTX_OK;
}
static const MemoryRegionOps amdvi_ir_ops = {
.read_with_attrs = amdvi_mem_ir_read,
.write_with_attrs = amdvi_mem_ir_write,
.endianness = DEVICE_LITTLE_ENDIAN,
.impl = {
.min_access_size = 4,
.max_access_size = 4,
},
.valid = {
.min_access_size = 4,
.max_access_size = 4,
}
};
static AddressSpace *amdvi_host_dma_iommu(PCIBus *bus, void *opaque, int devfn) static AddressSpace *amdvi_host_dma_iommu(PCIBus *bus, void *opaque, int devfn)
{ {
char name[128];
AMDVIState *s = opaque; AMDVIState *s = opaque;
AMDVIAddressSpace **iommu_as; AMDVIAddressSpace **iommu_as, *amdvi_dev_as;
int bus_num = pci_bus_num(bus); int bus_num = pci_bus_num(bus);
iommu_as = s->address_spaces[bus_num]; iommu_as = s->address_spaces[bus_num];
@ -1042,19 +1406,45 @@ static AddressSpace *amdvi_host_dma_iommu(PCIBus *bus, void *opaque, int devfn)
/* set up AMD-Vi region */ /* set up AMD-Vi region */
if (!iommu_as[devfn]) { if (!iommu_as[devfn]) {
snprintf(name, sizeof(name), "amd_iommu_devfn_%d", devfn);
iommu_as[devfn] = g_malloc0(sizeof(AMDVIAddressSpace)); iommu_as[devfn] = g_malloc0(sizeof(AMDVIAddressSpace));
iommu_as[devfn]->bus_num = (uint8_t)bus_num; iommu_as[devfn]->bus_num = (uint8_t)bus_num;
iommu_as[devfn]->devfn = (uint8_t)devfn; iommu_as[devfn]->devfn = (uint8_t)devfn;
iommu_as[devfn]->iommu_state = s; iommu_as[devfn]->iommu_state = s;
memory_region_init_iommu(&iommu_as[devfn]->iommu, amdvi_dev_as = iommu_as[devfn];
sizeof(iommu_as[devfn]->iommu),
/*
* Memory region relationships looks like (Address range shows
* only lower 32 bits to make it short in length...):
*
* |-----------------+-------------------+----------|
* | Name | Address range | Priority |
* |-----------------+-------------------+----------+
* | amdvi_root | 00000000-ffffffff | 0 |
* | amdvi_iommu | 00000000-ffffffff | 1 |
* | amdvi_iommu_ir | fee00000-feefffff | 64 |
* |-----------------+-------------------+----------|
*/
memory_region_init_iommu(&amdvi_dev_as->iommu,
sizeof(amdvi_dev_as->iommu),
TYPE_AMD_IOMMU_MEMORY_REGION, TYPE_AMD_IOMMU_MEMORY_REGION,
OBJECT(s), OBJECT(s),
"amd-iommu", UINT64_MAX); "amd_iommu", UINT64_MAX);
address_space_init(&iommu_as[devfn]->as, memory_region_init(&amdvi_dev_as->root, OBJECT(s),
MEMORY_REGION(&iommu_as[devfn]->iommu), "amdvi_root", UINT64_MAX);
"amd-iommu"); address_space_init(&amdvi_dev_as->as, &amdvi_dev_as->root, name);
memory_region_init_io(&amdvi_dev_as->iommu_ir, OBJECT(s),
&amdvi_ir_ops, s, "amd_iommu_ir",
AMDVI_INT_ADDR_SIZE);
memory_region_add_subregion_overlap(&amdvi_dev_as->root,
AMDVI_INT_ADDR_FIRST,
&amdvi_dev_as->iommu_ir,
64);
memory_region_add_subregion_overlap(&amdvi_dev_as->root, 0,
MEMORY_REGION(&amdvi_dev_as->iommu),
1);
} }
return &iommu_as[devfn]->as; return &iommu_as[devfn]->as;
} }
@ -1172,6 +1562,9 @@ static void amdvi_realize(DeviceState *dev, Error **err)
return; return;
} }
/* Pseudo address space under root PCI bus. */
pcms->ioapic_as = amdvi_host_dma_iommu(bus, s, AMDVI_IOAPIC_SB_DEVID);
/* set up MMIO */ /* set up MMIO */
memory_region_init_io(&s->mmio, OBJECT(s), &mmio_mem_ops, s, "amdvi-mmio", memory_region_init_io(&s->mmio, OBJECT(s), &mmio_mem_ops, s, "amdvi-mmio",
AMDVI_MMIO_SIZE); AMDVI_MMIO_SIZE);
@ -1205,6 +1598,7 @@ static void amdvi_class_init(ObjectClass *klass, void* data)
dc->vmsd = &vmstate_amdvi; dc->vmsd = &vmstate_amdvi;
dc->hotpluggable = false; dc->hotpluggable = false;
dc_class->realize = amdvi_realize; dc_class->realize = amdvi_realize;
dc_class->int_remap = amdvi_int_remap;
/* Supported by the pc-q35-* machine types */ /* Supported by the pc-q35-* machine types */
dc->user_creatable = true; dc->user_creatable = true;
} }

View File

@ -103,6 +103,7 @@
#define AMDVI_MMIO_CONTROL_EVENTINTEN (1ULL << 3) #define AMDVI_MMIO_CONTROL_EVENTINTEN (1ULL << 3)
#define AMDVI_MMIO_CONTROL_COMWAITINTEN (1ULL << 4) #define AMDVI_MMIO_CONTROL_COMWAITINTEN (1ULL << 4)
#define AMDVI_MMIO_CONTROL_CMDBUFLEN (1ULL << 12) #define AMDVI_MMIO_CONTROL_CMDBUFLEN (1ULL << 12)
#define AMDVI_MMIO_CONTROL_GAEN (1ULL << 17)
/* MMIO status register bits */ /* MMIO status register bits */
#define AMDVI_MMIO_STATUS_CMDBUF_RUN (1 << 4) #define AMDVI_MMIO_STATUS_CMDBUF_RUN (1 << 4)
@ -175,7 +176,7 @@
/* extended feature support */ /* extended feature support */
#define AMDVI_EXT_FEATURES (AMDVI_FEATURE_PREFETCH | AMDVI_FEATURE_PPR | \ #define AMDVI_EXT_FEATURES (AMDVI_FEATURE_PREFETCH | AMDVI_FEATURE_PPR | \
AMDVI_FEATURE_IA | AMDVI_FEATURE_GT | AMDVI_FEATURE_HE | \ AMDVI_FEATURE_IA | AMDVI_FEATURE_GT | AMDVI_FEATURE_HE | \
AMDVI_GATS_MODE | AMDVI_HATS_MODE) AMDVI_GATS_MODE | AMDVI_HATS_MODE | AMDVI_FEATURE_GA)
/* capabilities header */ /* capabilities header */
#define AMDVI_CAPAB_FEATURES (AMDVI_CAPAB_FLAT_EXT | \ #define AMDVI_CAPAB_FEATURES (AMDVI_CAPAB_FLAT_EXT | \
@ -206,8 +207,94 @@
#define AMDVI_COMMAND_SIZE 16 #define AMDVI_COMMAND_SIZE 16
#define AMDVI_INT_ADDR_FIRST 0xfee00000 #define AMDVI_INT_ADDR_FIRST 0xfee00000
#define AMDVI_INT_ADDR_LAST 0xfeefffff #define AMDVI_INT_ADDR_LAST 0xfeefffff
#define AMDVI_INT_ADDR_SIZE (AMDVI_INT_ADDR_LAST - AMDVI_INT_ADDR_FIRST + 1)
#define AMDVI_MSI_ADDR_HI_MASK (0xffffffff00000000ULL)
#define AMDVI_MSI_ADDR_LO_MASK (0x00000000ffffffffULL)
/* SB IOAPIC is always on this device in AMD systems */
#define AMDVI_IOAPIC_SB_DEVID PCI_BUILD_BDF(0, PCI_DEVFN(0x14, 0))
/* Interrupt remapping errors */
#define AMDVI_IR_ERR 0x1
#define AMDVI_IR_GET_IRTE 0x2
#define AMDVI_IR_TARGET_ABORT 0x3
/* Interrupt remapping */
#define AMDVI_IR_REMAP_ENABLE 1ULL
#define AMDVI_IR_INTCTL_SHIFT 60
#define AMDVI_IR_INTCTL_ABORT 0
#define AMDVI_IR_INTCTL_PASS 1
#define AMDVI_IR_INTCTL_REMAP 2
#define AMDVI_IR_PHYS_ADDR_MASK (((1ULL << 45) - 1) << 6)
/* MSI data 10:0 bits (section 2.2.5.1 Fig 14) */
#define AMDVI_IRTE_OFFSET 0x7ff
/* Delivery mode of MSI data (same as IOAPIC deilver mode encoding) */
#define AMDVI_IOAPIC_INT_TYPE_FIXED 0x0
#define AMDVI_IOAPIC_INT_TYPE_ARBITRATED 0x1
#define AMDVI_IOAPIC_INT_TYPE_SMI 0x2
#define AMDVI_IOAPIC_INT_TYPE_NMI 0x4
#define AMDVI_IOAPIC_INT_TYPE_INIT 0x5
#define AMDVI_IOAPIC_INT_TYPE_EINT 0x7
/* Pass through interrupt */
#define AMDVI_DEV_INT_PASS_MASK (1ULL << 56)
#define AMDVI_DEV_EINT_PASS_MASK (1ULL << 57)
#define AMDVI_DEV_NMI_PASS_MASK (1ULL << 58)
#define AMDVI_DEV_LINT0_PASS_MASK (1ULL << 62)
#define AMDVI_DEV_LINT1_PASS_MASK (1ULL << 63)
/* Interrupt remapping table fields (Guest VAPIC not enabled) */
union irte {
uint32_t val;
struct {
uint32_t valid:1,
no_fault:1,
int_type:3,
rq_eoi:1,
dm:1,
guest_mode:1,
destination:8,
vector:8,
rsvd:8;
} fields;
};
/* Interrupt remapping table fields (Guest VAPIC is enabled) */
union irte_ga_lo {
uint64_t val;
/* For int remapping */
struct {
uint64_t valid:1,
no_fault:1,
/* ------ */
int_type:3,
rq_eoi:1,
dm:1,
/* ------ */
guest_mode:1,
destination:8,
rsvd_1:48;
} fields_remap;
};
union irte_ga_hi {
uint64_t val;
struct {
uint64_t vector:8,
rsvd_2:56;
} fields;
};
struct irte_ga {
union irte_ga_lo lo;
union irte_ga_hi hi;
};
#define TYPE_AMD_IOMMU_DEVICE "amd-iommu" #define TYPE_AMD_IOMMU_DEVICE "amd-iommu"
#define AMD_IOMMU_DEVICE(obj)\ #define AMD_IOMMU_DEVICE(obj)\
@ -278,6 +365,9 @@ typedef struct AMDVIState {
/* IOTLB */ /* IOTLB */
GHashTable *iotlb; GHashTable *iotlb;
/* Interrupt remapping */
bool ga_enabled;
} AMDVIState; } AMDVIState;
#endif #endif

View File

@ -37,6 +37,9 @@
#include "kvm_i386.h" #include "kvm_i386.h"
#include "trace.h" #include "trace.h"
static void vtd_address_space_refresh_all(IntelIOMMUState *s);
static void vtd_address_space_unmap(VTDAddressSpace *as, IOMMUNotifier *n);
static void vtd_define_quad(IntelIOMMUState *s, hwaddr addr, uint64_t val, static void vtd_define_quad(IntelIOMMUState *s, hwaddr addr, uint64_t val,
uint64_t wmask, uint64_t w1cmask) uint64_t wmask, uint64_t w1cmask)
{ {
@ -227,6 +230,14 @@ static void vtd_reset_iotlb(IntelIOMMUState *s)
vtd_iommu_unlock(s); vtd_iommu_unlock(s);
} }
static void vtd_reset_caches(IntelIOMMUState *s)
{
vtd_iommu_lock(s);
vtd_reset_iotlb_locked(s);
vtd_reset_context_cache_locked(s);
vtd_iommu_unlock(s);
}
static uint64_t vtd_get_iotlb_key(uint64_t gfn, uint16_t source_id, static uint64_t vtd_get_iotlb_key(uint64_t gfn, uint16_t source_id,
uint32_t level) uint32_t level)
{ {
@ -1035,7 +1046,6 @@ static int vtd_sync_shadow_page_hook(IOMMUTLBEntry *entry,
return 0; return 0;
} }
/* If context entry is NULL, we'll try to fetch it on our own. */
static int vtd_sync_shadow_page_table_range(VTDAddressSpace *vtd_as, static int vtd_sync_shadow_page_table_range(VTDAddressSpace *vtd_as,
VTDContextEntry *ce, VTDContextEntry *ce,
hwaddr addr, hwaddr size) hwaddr addr, hwaddr size)
@ -1047,39 +1057,41 @@ static int vtd_sync_shadow_page_table_range(VTDAddressSpace *vtd_as,
.notify_unmap = true, .notify_unmap = true,
.aw = s->aw_bits, .aw = s->aw_bits,
.as = vtd_as, .as = vtd_as,
.domain_id = VTD_CONTEXT_ENTRY_DID(ce->hi),
}; };
VTDContextEntry ce_cache;
int ret;
if (ce) { return vtd_page_walk(ce, addr, addr + size, &info);
/* If the caller provided context entry, use it */
ce_cache = *ce;
} else {
/* If the caller didn't provide ce, try to fetch */
ret = vtd_dev_to_context_entry(s, pci_bus_num(vtd_as->bus),
vtd_as->devfn, &ce_cache);
if (ret) {
/*
* This should not really happen, but in case it happens,
* we just skip the sync for this time. After all we even
* don't have the root table pointer!
*/
error_report_once("%s: invalid context entry for bus 0x%x"
" devfn 0x%x",
__func__, pci_bus_num(vtd_as->bus),
vtd_as->devfn);
return 0;
}
}
info.domain_id = VTD_CONTEXT_ENTRY_DID(ce_cache.hi);
return vtd_page_walk(&ce_cache, addr, addr + size, &info);
} }
static int vtd_sync_shadow_page_table(VTDAddressSpace *vtd_as) static int vtd_sync_shadow_page_table(VTDAddressSpace *vtd_as)
{ {
return vtd_sync_shadow_page_table_range(vtd_as, NULL, 0, UINT64_MAX); int ret;
VTDContextEntry ce;
IOMMUNotifier *n;
ret = vtd_dev_to_context_entry(vtd_as->iommu_state,
pci_bus_num(vtd_as->bus),
vtd_as->devfn, &ce);
if (ret) {
if (ret == -VTD_FR_CONTEXT_ENTRY_P) {
/*
* It's a valid scenario to have a context entry that is
* not present. For example, when a device is removed
* from an existing domain then the context entry will be
* zeroed by the guest before it was put into another
* domain. When this happens, instead of synchronizing
* the shadow pages we should invalidate all existing
* mappings and notify the backends.
*/
IOMMU_NOTIFIER_FOREACH(n, &vtd_as->iommu) {
vtd_address_space_unmap(vtd_as, n);
}
ret = 0;
}
return ret;
}
return vtd_sync_shadow_page_table_range(vtd_as, &ce, 0, UINT64_MAX);
} }
/* /*
@ -1428,7 +1440,7 @@ static void vtd_context_global_invalidate(IntelIOMMUState *s)
vtd_reset_context_cache_locked(s); vtd_reset_context_cache_locked(s);
} }
vtd_iommu_unlock(s); vtd_iommu_unlock(s);
vtd_switch_address_space_all(s); vtd_address_space_refresh_all(s);
/* /*
* From VT-d spec 6.5.2.1, a global context entry invalidation * From VT-d spec 6.5.2.1, a global context entry invalidation
* should be followed by a IOTLB global invalidation, so we should * should be followed by a IOTLB global invalidation, so we should
@ -1719,6 +1731,8 @@ static void vtd_handle_gcmd_srtp(IntelIOMMUState *s)
vtd_root_table_setup(s); vtd_root_table_setup(s);
/* Ok - report back to driver */ /* Ok - report back to driver */
vtd_set_clear_mask_long(s, DMAR_GSTS_REG, 0, VTD_GSTS_RTPS); vtd_set_clear_mask_long(s, DMAR_GSTS_REG, 0, VTD_GSTS_RTPS);
vtd_reset_caches(s);
vtd_address_space_refresh_all(s);
} }
/* Set Interrupt Remap Table Pointer */ /* Set Interrupt Remap Table Pointer */
@ -1751,7 +1765,8 @@ static void vtd_handle_gcmd_te(IntelIOMMUState *s, bool en)
vtd_set_clear_mask_long(s, DMAR_GSTS_REG, VTD_GSTS_TES, 0); vtd_set_clear_mask_long(s, DMAR_GSTS_REG, VTD_GSTS_TES, 0);
} }
vtd_switch_address_space_all(s); vtd_reset_caches(s);
vtd_address_space_refresh_all(s);
} }
/* Handle Interrupt Remap Enable/Disable */ /* Handle Interrupt Remap Enable/Disable */
@ -2701,7 +2716,7 @@ static int vtd_irte_get(IntelIOMMUState *iommu, uint16_t index,
/* Fetch IRQ information of specific IR index */ /* Fetch IRQ information of specific IR index */
static int vtd_remap_irq_get(IntelIOMMUState *iommu, uint16_t index, static int vtd_remap_irq_get(IntelIOMMUState *iommu, uint16_t index,
VTDIrq *irq, uint16_t sid) X86IOMMUIrq *irq, uint16_t sid)
{ {
VTD_IR_TableEntry irte = {}; VTD_IR_TableEntry irte = {};
int ret = 0; int ret = 0;
@ -2730,30 +2745,6 @@ static int vtd_remap_irq_get(IntelIOMMUState *iommu, uint16_t index,
return 0; return 0;
} }
/* Generate one MSI message from VTDIrq info */
static void vtd_generate_msi_message(VTDIrq *irq, MSIMessage *msg_out)
{
VTD_MSIMessage msg = {};
/* Generate address bits */
msg.dest_mode = irq->dest_mode;
msg.redir_hint = irq->redir_hint;
msg.dest = irq->dest;
msg.__addr_hi = irq->dest & 0xffffff00;
msg.__addr_head = cpu_to_le32(0xfee);
/* Keep this from original MSI address bits */
msg.__not_used = irq->msi_addr_last_bits;
/* Generate data bits */
msg.vector = irq->vector;
msg.delivery_mode = irq->delivery_mode;
msg.level = 1;
msg.trigger_mode = irq->trigger_mode;
msg_out->address = msg.msi_addr;
msg_out->data = msg.msi_data;
}
/* Interrupt remapping for MSI/MSI-X entry */ /* Interrupt remapping for MSI/MSI-X entry */
static int vtd_interrupt_remap_msi(IntelIOMMUState *iommu, static int vtd_interrupt_remap_msi(IntelIOMMUState *iommu,
MSIMessage *origin, MSIMessage *origin,
@ -2763,7 +2754,7 @@ static int vtd_interrupt_remap_msi(IntelIOMMUState *iommu,
int ret = 0; int ret = 0;
VTD_IR_MSIAddress addr; VTD_IR_MSIAddress addr;
uint16_t index; uint16_t index;
VTDIrq irq = {}; X86IOMMUIrq irq = {};
assert(origin && translated); assert(origin && translated);
@ -2842,8 +2833,8 @@ static int vtd_interrupt_remap_msi(IntelIOMMUState *iommu,
*/ */
irq.msi_addr_last_bits = addr.addr.__not_care; irq.msi_addr_last_bits = addr.addr.__not_care;
/* Translate VTDIrq to MSI message */ /* Translate X86IOMMUIrq to MSI message */
vtd_generate_msi_message(&irq, translated); x86_iommu_irq_to_msi_message(&irq, translated);
out: out:
trace_vtd_ir_remap_msi(origin->address, origin->data, trace_vtd_ir_remap_msi(origin->address, origin->data,
@ -3051,6 +3042,12 @@ static void vtd_address_space_unmap_all(IntelIOMMUState *s)
} }
} }
static void vtd_address_space_refresh_all(IntelIOMMUState *s)
{
vtd_address_space_unmap_all(s);
vtd_switch_address_space_all(s);
}
static int vtd_replay_hook(IOMMUTLBEntry *entry, void *private) static int vtd_replay_hook(IOMMUTLBEntry *entry, void *private)
{ {
memory_region_notify_one((IOMMUNotifier *)private, entry); memory_region_notify_one((IOMMUNotifier *)private, entry);
@ -3160,10 +3157,7 @@ static void vtd_init(IntelIOMMUState *s)
s->cap |= VTD_CAP_CM; s->cap |= VTD_CAP_CM;
} }
vtd_iommu_lock(s); vtd_reset_caches(s);
vtd_reset_context_cache_locked(s);
vtd_reset_iotlb_locked(s);
vtd_iommu_unlock(s);
/* Define registers with default values and bit semantics */ /* Define registers with default values and bit semantics */
vtd_define_long(s, DMAR_VER_REG, 0x10UL, 0, 0); vtd_define_long(s, DMAR_VER_REG, 0x10UL, 0, 0);
@ -3226,11 +3220,7 @@ static void vtd_reset(DeviceState *dev)
IntelIOMMUState *s = INTEL_IOMMU_DEVICE(dev); IntelIOMMUState *s = INTEL_IOMMU_DEVICE(dev);
vtd_init(s); vtd_init(s);
vtd_address_space_refresh_all(s);
/*
* When device reset, throw away all mappings and external caches
*/
vtd_address_space_unmap_all(s);
} }
static AddressSpace *vtd_host_dma_iommu(PCIBus *bus, void *opaque, int devfn) static AddressSpace *vtd_host_dma_iommu(PCIBus *bus, void *opaque, int devfn)
@ -3248,13 +3238,6 @@ static bool vtd_decide_config(IntelIOMMUState *s, Error **errp)
{ {
X86IOMMUState *x86_iommu = X86_IOMMU_DEVICE(s); X86IOMMUState *x86_iommu = X86_IOMMU_DEVICE(s);
/* Currently Intel IOMMU IR only support "kernel-irqchip={off|split}" */
if (x86_iommu->intr_supported && kvm_irqchip_in_kernel() &&
!kvm_irqchip_is_split()) {
error_setg(errp, "Intel Interrupt Remapping cannot work with "
"kernel-irqchip=on, please use 'split|off'.");
return false;
}
if (s->intr_eim == ON_OFF_AUTO_ON && !x86_iommu->intr_supported) { if (s->intr_eim == ON_OFF_AUTO_ON && !x86_iommu->intr_supported) {
error_setg(errp, "eim=on cannot be selected without intremap=on"); error_setg(errp, "eim=on cannot be selected without intremap=on");
return false; return false;

View File

@ -101,6 +101,20 @@ amdvi_mode_invalid(uint8_t level, uint64_t addr)"error: translation level 0x%"PR
amdvi_page_fault(uint64_t addr) "error: page fault accessing guest physical address 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_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 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
amdvi_mem_ir_write_req(uint64_t addr, uint64_t val, uint32_t size) "addr 0x%"PRIx64" data 0x%"PRIx64" size 0x%"PRIx32
amdvi_mem_ir_write(uint64_t addr, uint64_t val) "addr 0x%"PRIx64" data 0x%"PRIx64
amdvi_ir_remap_msi_req(uint64_t addr, uint64_t data, uint8_t devid) "addr 0x%"PRIx64" data 0x%"PRIx64" devid 0x%"PRIx8
amdvi_ir_remap_msi(uint64_t addr, uint64_t data, uint64_t addr2, uint64_t data2) "(addr 0x%"PRIx64", data 0x%"PRIx64") -> (addr 0x%"PRIx64", data 0x%"PRIx64")"
amdvi_err(const char *str) "%s"
amdvi_ir_irte(uint64_t addr, uint64_t data) "addr 0x%"PRIx64" offset 0x%"PRIx64
amdvi_ir_irte_val(uint32_t data) "data 0x%"PRIx32
amdvi_ir_err(const char *str) "%s"
amdvi_ir_intctl(uint8_t val) "int_ctl 0x%"PRIx8
amdvi_ir_target_abort(const char *str) "%s"
amdvi_ir_delivery_mode(const char *str) "%s"
amdvi_ir_generate_msi_message(uint8_t vector, uint8_t delivery_mode, uint8_t dest_mode, uint8_t dest, uint8_t rh) "vector %d delivery-mode %d dest-mode %d dest-id %d rh %d"
amdvi_ir_irte_ga(uint64_t addr, uint64_t data) "addr 0x%"PRIx64" offset 0x%"PRIx64
amdvi_ir_irte_ga_val(uint64_t hi, uint64_t lo) "hi 0x%"PRIx64" lo 0x%"PRIx64
# hw/i386/vmport.c # hw/i386/vmport.c
vmport_register(unsigned char command, void *func, void *opaque) "command: 0x%02x func: %p opaque: %p" vmport_register(unsigned char command, void *func, void *opaque) "command: 0x%02x func: %p opaque: %p"

View File

@ -25,6 +25,7 @@
#include "qapi/error.h" #include "qapi/error.h"
#include "qemu/error-report.h" #include "qemu/error-report.h"
#include "trace.h" #include "trace.h"
#include "sysemu/kvm.h"
void x86_iommu_iec_register_notifier(X86IOMMUState *iommu, void x86_iommu_iec_register_notifier(X86IOMMUState *iommu,
iec_notify_fn fn, void *data) iec_notify_fn fn, void *data)
@ -52,6 +53,30 @@ void x86_iommu_iec_notify_all(X86IOMMUState *iommu, bool global,
} }
} }
/* Generate one MSI message from VTDIrq info */
void x86_iommu_irq_to_msi_message(X86IOMMUIrq *irq, MSIMessage *msg_out)
{
X86IOMMU_MSIMessage msg = {};
/* Generate address bits */
msg.dest_mode = irq->dest_mode;
msg.redir_hint = irq->redir_hint;
msg.dest = irq->dest;
msg.__addr_hi = irq->dest & 0xffffff00;
msg.__addr_head = cpu_to_le32(0xfee);
/* Keep this from original MSI address bits */
msg.__not_used = irq->msi_addr_last_bits;
/* Generate data bits */
msg.vector = irq->vector;
msg.delivery_mode = irq->delivery_mode;
msg.level = 1;
msg.trigger_mode = irq->trigger_mode;
msg_out->address = msg.msi_addr;
msg_out->data = msg.msi_data;
}
/* Default X86 IOMMU device */ /* Default X86 IOMMU device */
static X86IOMMUState *x86_iommu_default = NULL; static X86IOMMUState *x86_iommu_default = NULL;
@ -94,6 +119,14 @@ static void x86_iommu_realize(DeviceState *dev, Error **errp)
return; return;
} }
/* Both Intel and AMD IOMMU IR only support "kernel-irqchip={off|split}" */
if (x86_iommu->intr_supported && kvm_irqchip_in_kernel() &&
!kvm_irqchip_is_split()) {
error_setg(errp, "Interrupt Remapping cannot work with "
"kernel-irqchip=on, please use 'split|off'.");
return;
}
if (x86_class->realize) { if (x86_class->realize) {
x86_class->realize(dev, errp); x86_class->realize(dev, errp);
} }

View File

@ -85,6 +85,9 @@ typedef struct PCITestDevState {
MemoryRegion portio; MemoryRegion portio;
IOTest *tests; IOTest *tests;
int current; int current;
uint64_t membar_size;
MemoryRegion membar;
} PCITestDevState; } PCITestDevState;
#define TYPE_PCI_TEST_DEV "pci-testdev" #define TYPE_PCI_TEST_DEV "pci-testdev"
@ -253,6 +256,16 @@ static void pci_testdev_realize(PCIDevice *pci_dev, Error **errp)
pci_register_bar(pci_dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &d->mmio); pci_register_bar(pci_dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &d->mmio);
pci_register_bar(pci_dev, 1, PCI_BASE_ADDRESS_SPACE_IO, &d->portio); pci_register_bar(pci_dev, 1, PCI_BASE_ADDRESS_SPACE_IO, &d->portio);
if (d->membar_size) {
memory_region_init(&d->membar, OBJECT(d), "pci-testdev-membar",
d->membar_size);
pci_register_bar(pci_dev, 2,
PCI_BASE_ADDRESS_SPACE_MEMORY |
PCI_BASE_ADDRESS_MEM_PREFETCH |
PCI_BASE_ADDRESS_MEM_TYPE_64,
&d->membar);
}
d->current = -1; d->current = -1;
d->tests = g_malloc0(IOTEST_MAX * sizeof *d->tests); d->tests = g_malloc0(IOTEST_MAX * sizeof *d->tests);
for (i = 0; i < IOTEST_MAX; ++i) { for (i = 0; i < IOTEST_MAX; ++i) {
@ -305,6 +318,11 @@ static void qdev_pci_testdev_reset(DeviceState *dev)
pci_testdev_reset(d); pci_testdev_reset(d);
} }
static Property pci_testdev_properties[] = {
DEFINE_PROP_SIZE("membar", PCITestDevState, membar_size, 0),
DEFINE_PROP_END_OF_LIST(),
};
static void pci_testdev_class_init(ObjectClass *klass, void *data) static void pci_testdev_class_init(ObjectClass *klass, void *data)
{ {
DeviceClass *dc = DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass);
@ -319,6 +337,7 @@ static void pci_testdev_class_init(ObjectClass *klass, void *data)
dc->desc = "PCI Test Device"; dc->desc = "PCI Test Device";
set_bit(DEVICE_CATEGORY_MISC, dc->categories); set_bit(DEVICE_CATEGORY_MISC, dc->categories);
dc->reset = qdev_pci_testdev_reset; dc->reset = qdev_pci_testdev_reset;
dc->props = pci_testdev_properties;
} }
static const TypeInfo pci_testdev_info = { static const TypeInfo pci_testdev_info = {

View File

@ -24,7 +24,7 @@
#include "hw/pci/pci_ids.h" #include "hw/pci/pci_ids.h"
#include "hw/pci/msi.h" #include "hw/pci/msi.h"
#include "hw/pci/pcie.h" #include "hw/pci/pcie.h"
#include "ioh3420.h" #include "hw/pci/pcie_port.h"
#define PCI_DEVICE_ID_IOH_EPORT 0x3420 /* D0:F0 express mode */ #define PCI_DEVICE_ID_IOH_EPORT 0x3420 /* D0:F0 express mode */
#define PCI_DEVICE_ID_IOH_REV 0x2 #define PCI_DEVICE_ID_IOH_REV 0x2

View File

@ -1,6 +0,0 @@
#ifndef QEMU_IOH3420_H
#define QEMU_IOH3420_H
#include "hw/pci/pcie_port.h"
#endif /* QEMU_IOH3420_H */

View File

@ -23,7 +23,7 @@
#include "hw/pci/pci_ids.h" #include "hw/pci/pci_ids.h"
#include "hw/pci/msi.h" #include "hw/pci/msi.h"
#include "hw/pci/pcie.h" #include "hw/pci/pcie.h"
#include "xio3130_downstream.h" #include "hw/pci/pcie_port.h"
#include "qapi/error.h" #include "qapi/error.h"
#define PCI_DEVICE_ID_TI_XIO3130D 0x8233 /* downstream port */ #define PCI_DEVICE_ID_TI_XIO3130D 0x8233 /* downstream port */
@ -127,32 +127,6 @@ static void xio3130_downstream_exitfn(PCIDevice *d)
pci_bridge_exitfn(d); pci_bridge_exitfn(d);
} }
PCIESlot *xio3130_downstream_init(PCIBus *bus, int devfn, bool multifunction,
const char *bus_name, pci_map_irq_fn map_irq,
uint8_t port, uint8_t chassis,
uint16_t slot)
{
PCIDevice *d;
PCIBridge *br;
DeviceState *qdev;
d = pci_create_multifunction(bus, devfn, multifunction,
"xio3130-downstream");
if (!d) {
return NULL;
}
br = PCI_BRIDGE(d);
qdev = DEVICE(d);
pci_bridge_map_irq(br, bus_name, map_irq);
qdev_prop_set_uint8(qdev, "port", port);
qdev_prop_set_uint8(qdev, "chassis", chassis);
qdev_prop_set_uint16(qdev, "slot", slot);
qdev_init_nofail(qdev);
return PCIE_SLOT(d);
}
static Property xio3130_downstream_props[] = { static Property xio3130_downstream_props[] = {
DEFINE_PROP_BIT(COMPAT_PROP_PCP, PCIDevice, cap_present, DEFINE_PROP_BIT(COMPAT_PROP_PCP, PCIDevice, cap_present,
QEMU_PCIE_SLTCAP_PCP_BITNR, true), QEMU_PCIE_SLTCAP_PCP_BITNR, true),

View File

@ -1,11 +0,0 @@
#ifndef QEMU_XIO3130_DOWNSTREAM_H
#define QEMU_XIO3130_DOWNSTREAM_H
#include "hw/pci/pcie_port.h"
PCIESlot *xio3130_downstream_init(PCIBus *bus, int devfn, bool multifunction,
const char *bus_name, pci_map_irq_fn map_irq,
uint8_t port, uint8_t chassis,
uint16_t slot);
#endif /* QEMU_XIO3130_DOWNSTREAM_H */

View File

@ -23,7 +23,7 @@
#include "hw/pci/pci_ids.h" #include "hw/pci/pci_ids.h"
#include "hw/pci/msi.h" #include "hw/pci/msi.h"
#include "hw/pci/pcie.h" #include "hw/pci/pcie.h"
#include "xio3130_upstream.h" #include "hw/pci/pcie_port.h"
#define PCI_DEVICE_ID_TI_XIO3130U 0x8232 /* upstream port */ #define PCI_DEVICE_ID_TI_XIO3130U 0x8232 /* upstream port */
#define XIO3130_REVISION 0x2 #define XIO3130_REVISION 0x2
@ -108,28 +108,6 @@ static void xio3130_upstream_exitfn(PCIDevice *d)
pci_bridge_exitfn(d); pci_bridge_exitfn(d);
} }
PCIEPort *xio3130_upstream_init(PCIBus *bus, int devfn, bool multifunction,
const char *bus_name, pci_map_irq_fn map_irq,
uint8_t port)
{
PCIDevice *d;
PCIBridge *br;
DeviceState *qdev;
d = pci_create_multifunction(bus, devfn, multifunction, "x3130-upstream");
if (!d) {
return NULL;
}
br = PCI_BRIDGE(d);
qdev = DEVICE(d);
pci_bridge_map_irq(br, bus_name, map_irq);
qdev_prop_set_uint8(qdev, "port", port);
qdev_init_nofail(qdev);
return PCIE_PORT(d);
}
static const VMStateDescription vmstate_xio3130_upstream = { static const VMStateDescription vmstate_xio3130_upstream = {
.name = "xio3130-express-upstream-port", .name = "xio3130-express-upstream-port",
.priority = MIG_PRI_PCI_BUS, .priority = MIG_PRI_PCI_BUS,

View File

@ -1,10 +0,0 @@
#ifndef QEMU_XIO3130_UPSTREAM_H
#define QEMU_XIO3130_UPSTREAM_H
#include "hw/pci/pcie_port.h"
PCIEPort *xio3130_upstream_init(PCIBus *bus, int devfn, bool multifunction,
const char *bus_name, pci_map_irq_fn map_irq,
uint8_t port);
#endif /* QEMU_XIO3130_UPSTREAM_H */

View File

@ -40,7 +40,7 @@
/* /*
* I440FX chipset data sheet. * I440FX chipset data sheet.
* http://download.intel.com/design/chipsets/datashts/29054901.pdf * https://wiki.qemu.org/File:29054901.pdf
*/ */
#define I440FX_PCI_HOST_BRIDGE(obj) \ #define I440FX_PCI_HOST_BRIDGE(obj) \
@ -95,6 +95,9 @@ typedef struct PIIX3State {
#define I440FX_PCI_DEVICE(obj) \ #define I440FX_PCI_DEVICE(obj) \
OBJECT_CHECK(PCII440FXState, (obj), TYPE_I440FX_PCI_DEVICE) OBJECT_CHECK(PCII440FXState, (obj), TYPE_I440FX_PCI_DEVICE)
#define TYPE_PIIX3_DEVICE "PIIX3"
#define TYPE_PIIX3_XEN_DEVICE "PIIX3-xen"
struct PCII440FXState { struct PCII440FXState {
/*< private >*/ /*< private >*/
PCIDevice parent_obj; PCIDevice parent_obj;
@ -142,7 +145,7 @@ static void i440fx_update_memory_mappings(PCII440FXState *d)
PCIDevice *pd = PCI_DEVICE(d); PCIDevice *pd = PCI_DEVICE(d);
memory_region_transaction_begin(); memory_region_transaction_begin();
for (i = 0; i < 13; i++) { for (i = 0; i < ARRAY_SIZE(d->pam_regions); i++) {
pam_update(&d->pam_regions[i], i, pam_update(&d->pam_regions[i], i,
pd->config[I440FX_PAM + DIV_ROUND_UP(i, 2)]); pd->config[I440FX_PAM + DIV_ROUND_UP(i, 2)]);
} }
@ -249,9 +252,7 @@ static void i440fx_pcihost_get_pci_hole_end(Object *obj, Visitor *v,
* the 64bit PCI hole will start after "over 4G RAM" and the * the 64bit PCI hole will start after "over 4G RAM" and the
* reserved space for memory hotplug if any. * reserved space for memory hotplug if any.
*/ */
static void i440fx_pcihost_get_pci_hole64_start(Object *obj, Visitor *v, static uint64_t i440fx_pcihost_get_pci_hole64_start_value(Object *obj)
const char *name,
void *opaque, Error **errp)
{ {
PCIHostState *h = PCI_HOST_BRIDGE(obj); PCIHostState *h = PCI_HOST_BRIDGE(obj);
I440FXState *s = I440FX_PCI_HOST_BRIDGE(obj); I440FXState *s = I440FX_PCI_HOST_BRIDGE(obj);
@ -263,7 +264,16 @@ static void i440fx_pcihost_get_pci_hole64_start(Object *obj, Visitor *v,
if (!value && s->pci_hole64_fix) { if (!value && s->pci_hole64_fix) {
value = pc_pci_hole64_start(); value = pc_pci_hole64_start();
} }
visit_type_uint64(v, name, &value, errp); return value;
}
static void i440fx_pcihost_get_pci_hole64_start(Object *obj, Visitor *v,
const char *name,
void *opaque, Error **errp)
{
uint64_t hole64_start = i440fx_pcihost_get_pci_hole64_start_value(obj);
visit_type_uint64(v, name, &hole64_start, errp);
} }
/* /*
@ -278,7 +288,7 @@ static void i440fx_pcihost_get_pci_hole64_end(Object *obj, Visitor *v,
{ {
PCIHostState *h = PCI_HOST_BRIDGE(obj); PCIHostState *h = PCI_HOST_BRIDGE(obj);
I440FXState *s = I440FX_PCI_HOST_BRIDGE(obj); I440FXState *s = I440FX_PCI_HOST_BRIDGE(obj);
uint64_t hole64_start = pc_pci_hole64_start(); uint64_t hole64_start = i440fx_pcihost_get_pci_hole64_start_value(obj);
Range w64; Range w64;
uint64_t value, hole64_end; uint64_t value, hole64_end;
@ -405,7 +415,7 @@ PCIBus *i440fx_init(const char *host_type, const char *pci_type,
init_pam(dev, f->ram_memory, f->system_memory, f->pci_address_space, init_pam(dev, f->ram_memory, f->system_memory, f->pci_address_space,
&f->pam_regions[0], PAM_BIOS_BASE, PAM_BIOS_SIZE); &f->pam_regions[0], PAM_BIOS_BASE, PAM_BIOS_SIZE);
for (i = 0; i < 12; ++i) { for (i = 0; i < ARRAY_SIZE(f->pam_regions) - 1; ++i) {
init_pam(dev, f->ram_memory, f->system_memory, f->pci_address_space, init_pam(dev, f->ram_memory, f->system_memory, f->pci_address_space,
&f->pam_regions[i+1], PAM_EXPAN_BASE + i * PAM_EXPAN_SIZE, &f->pam_regions[i+1], PAM_EXPAN_BASE + i * PAM_EXPAN_SIZE,
PAM_EXPAN_SIZE); PAM_EXPAN_SIZE);
@ -417,13 +427,13 @@ PCIBus *i440fx_init(const char *host_type, const char *pci_type,
* These additional routes can be discovered through ACPI. */ * These additional routes can be discovered through ACPI. */
if (xen_enabled()) { if (xen_enabled()) {
PCIDevice *pci_dev = pci_create_simple_multifunction(b, PCIDevice *pci_dev = pci_create_simple_multifunction(b,
-1, true, "PIIX3-xen"); -1, true, TYPE_PIIX3_XEN_DEVICE);
piix3 = PIIX3_PCI_DEVICE(pci_dev); piix3 = PIIX3_PCI_DEVICE(pci_dev);
pci_bus_irqs(b, xen_piix3_set_irq, xen_pci_slot_get_pirq, pci_bus_irqs(b, xen_piix3_set_irq, xen_pci_slot_get_pirq,
piix3, XEN_PIIX_NUM_PIRQS); piix3, XEN_PIIX_NUM_PIRQS);
} else { } else {
PCIDevice *pci_dev = pci_create_simple_multifunction(b, PCIDevice *pci_dev = pci_create_simple_multifunction(b,
-1, true, "PIIX3"); -1, true, TYPE_PIIX3_DEVICE);
piix3 = PIIX3_PCI_DEVICE(pci_dev); piix3 = PIIX3_PCI_DEVICE(pci_dev);
pci_bus_irqs(b, piix3_set_irq, pci_slot_get_pirq, piix3, pci_bus_irqs(b, piix3_set_irq, pci_slot_get_pirq, piix3,
PIIX_NUM_PIRQS); PIIX_NUM_PIRQS);
@ -741,7 +751,7 @@ static void piix3_class_init(ObjectClass *klass, void *data)
} }
static const TypeInfo piix3_info = { static const TypeInfo piix3_info = {
.name = "PIIX3", .name = TYPE_PIIX3_DEVICE,
.parent = TYPE_PIIX3_PCI_DEVICE, .parent = TYPE_PIIX3_PCI_DEVICE,
.class_init = piix3_class_init, .class_init = piix3_class_init,
}; };
@ -754,7 +764,7 @@ static void piix3_xen_class_init(ObjectClass *klass, void *data)
}; };
static const TypeInfo piix3_xen_info = { static const TypeInfo piix3_xen_info = {
.name = "PIIX3-xen", .name = TYPE_PIIX3_XEN_DEVICE,
.parent = TYPE_PIIX3_PCI_DEVICE, .parent = TYPE_PIIX3_PCI_DEVICE,
.class_init = piix3_xen_class_init, .class_init = piix3_xen_class_init,
}; };

View File

@ -113,9 +113,7 @@ static void q35_host_get_pci_hole_end(Object *obj, Visitor *v,
* the 64bit PCI hole will start after "over 4G RAM" and the * the 64bit PCI hole will start after "over 4G RAM" and the
* reserved space for memory hotplug if any. * reserved space for memory hotplug if any.
*/ */
static void q35_host_get_pci_hole64_start(Object *obj, Visitor *v, static uint64_t q35_host_get_pci_hole64_start_value(Object *obj)
const char *name, void *opaque,
Error **errp)
{ {
PCIHostState *h = PCI_HOST_BRIDGE(obj); PCIHostState *h = PCI_HOST_BRIDGE(obj);
Q35PCIHost *s = Q35_HOST_DEVICE(obj); Q35PCIHost *s = Q35_HOST_DEVICE(obj);
@ -127,7 +125,16 @@ static void q35_host_get_pci_hole64_start(Object *obj, Visitor *v,
if (!value && s->pci_hole64_fix) { if (!value && s->pci_hole64_fix) {
value = pc_pci_hole64_start(); value = pc_pci_hole64_start();
} }
visit_type_uint64(v, name, &value, errp); return value;
}
static void q35_host_get_pci_hole64_start(Object *obj, Visitor *v,
const char *name, void *opaque,
Error **errp)
{
uint64_t hole64_start = q35_host_get_pci_hole64_start_value(obj);
visit_type_uint64(v, name, &hole64_start, errp);
} }
/* /*
@ -142,7 +149,7 @@ static void q35_host_get_pci_hole64_end(Object *obj, Visitor *v,
{ {
PCIHostState *h = PCI_HOST_BRIDGE(obj); PCIHostState *h = PCI_HOST_BRIDGE(obj);
Q35PCIHost *s = Q35_HOST_DEVICE(obj); Q35PCIHost *s = Q35_HOST_DEVICE(obj);
uint64_t hole64_start = pc_pci_hole64_start(); uint64_t hole64_start = q35_host_get_pci_hole64_start_value(obj);
Range w64; Range w64;
uint64_t value, hole64_end; uint64_t value, hole64_end;

View File

@ -399,7 +399,7 @@ void pci_bridge_exitfn(PCIDevice *pci_dev)
/* /*
* before qdev initialization(qdev_init()), this function sets bus_name and * before qdev initialization(qdev_init()), this function sets bus_name and
* map_irq callback which are necessry for pci_bridge_initfn() to * map_irq callback which are necessary for pci_bridge_initfn() to
* initialize bus. * initialize bus.
*/ */
void pci_bridge_map_irq(PCIBridge *br, const char* bus_name, void pci_bridge_map_irq(PCIBridge *br, const char* bus_name,

View File

@ -183,7 +183,7 @@ static void vhost_scsi_realize(DeviceState *dev, Error **errp)
} }
vsc->dev.nvqs = VHOST_SCSI_VQ_NUM_FIXED + vs->conf.num_queues; vsc->dev.nvqs = VHOST_SCSI_VQ_NUM_FIXED + vs->conf.num_queues;
vsc->dev.vqs = g_new(struct vhost_virtqueue, vsc->dev.nvqs); vsc->dev.vqs = g_new0(struct vhost_virtqueue, vsc->dev.nvqs);
vsc->dev.vq_index = 0; vsc->dev.vq_index = 0;
vsc->dev.backend_features = 0; vsc->dev.backend_features = 0;

View File

@ -66,8 +66,6 @@ typedef struct VTDIOTLBEntry VTDIOTLBEntry;
typedef struct VTDBus VTDBus; typedef struct VTDBus VTDBus;
typedef union VTD_IR_TableEntry VTD_IR_TableEntry; typedef union VTD_IR_TableEntry VTD_IR_TableEntry;
typedef union VTD_IR_MSIAddress VTD_IR_MSIAddress; typedef union VTD_IR_MSIAddress VTD_IR_MSIAddress;
typedef struct VTDIrq VTDIrq;
typedef struct VTD_MSIMessage VTD_MSIMessage;
/* Context-Entry */ /* Context-Entry */
struct VTDContextEntry { struct VTDContextEntry {
@ -197,63 +195,6 @@ union VTD_IR_MSIAddress {
uint32_t data; uint32_t data;
}; };
/* Generic IRQ entry information */
struct VTDIrq {
/* Used by both IOAPIC/MSI interrupt remapping */
uint8_t trigger_mode;
uint8_t vector;
uint8_t delivery_mode;
uint32_t dest;
uint8_t dest_mode;
/* only used by MSI interrupt remapping */
uint8_t redir_hint;
uint8_t msi_addr_last_bits;
};
struct VTD_MSIMessage {
union {
struct {
#ifdef HOST_WORDS_BIGENDIAN
uint32_t __addr_head:12; /* 0xfee */
uint32_t dest:8;
uint32_t __reserved:8;
uint32_t redir_hint:1;
uint32_t dest_mode:1;
uint32_t __not_used:2;
#else
uint32_t __not_used:2;
uint32_t dest_mode:1;
uint32_t redir_hint:1;
uint32_t __reserved:8;
uint32_t dest:8;
uint32_t __addr_head:12; /* 0xfee */
#endif
uint32_t __addr_hi;
} QEMU_PACKED;
uint64_t msi_addr;
};
union {
struct {
#ifdef HOST_WORDS_BIGENDIAN
uint16_t trigger_mode:1;
uint16_t level:1;
uint16_t __resved:3;
uint16_t delivery_mode:3;
uint16_t vector:8;
#else
uint16_t vector:8;
uint16_t delivery_mode:3;
uint16_t __resved:3;
uint16_t level:1;
uint16_t trigger_mode:1;
#endif
uint16_t __resved1;
} QEMU_PACKED;
uint32_t msi_data;
};
};
/* When IR is enabled, all MSI/MSI-X data bits should be zero */ /* When IR is enabled, all MSI/MSI-X data bits should be zero */
#define VTD_IR_MSI_DATA (0) #define VTD_IR_MSI_DATA (0)

View File

@ -22,6 +22,7 @@
#include "hw/sysbus.h" #include "hw/sysbus.h"
#include "hw/pci/pci.h" #include "hw/pci/pci.h"
#include "hw/pci/msi.h"
#define TYPE_X86_IOMMU_DEVICE ("x86-iommu") #define TYPE_X86_IOMMU_DEVICE ("x86-iommu")
#define X86_IOMMU_DEVICE(obj) \ #define X86_IOMMU_DEVICE(obj) \
@ -35,6 +36,8 @@
typedef struct X86IOMMUState X86IOMMUState; typedef struct X86IOMMUState X86IOMMUState;
typedef struct X86IOMMUClass X86IOMMUClass; typedef struct X86IOMMUClass X86IOMMUClass;
typedef struct X86IOMMUIrq X86IOMMUIrq;
typedef struct X86IOMMU_MSIMessage X86IOMMU_MSIMessage;
typedef enum IommuType { typedef enum IommuType {
TYPE_INTEL, TYPE_INTEL,
@ -78,6 +81,63 @@ struct X86IOMMUState {
QLIST_HEAD(, IEC_Notifier) iec_notifiers; /* IEC notify list */ QLIST_HEAD(, IEC_Notifier) iec_notifiers; /* IEC notify list */
}; };
/* Generic IRQ entry information when interrupt remapping is enabled */
struct X86IOMMUIrq {
/* Used by both IOAPIC/MSI interrupt remapping */
uint8_t trigger_mode;
uint8_t vector;
uint8_t delivery_mode;
uint32_t dest;
uint8_t dest_mode;
/* only used by MSI interrupt remapping */
uint8_t redir_hint;
uint8_t msi_addr_last_bits;
};
struct X86IOMMU_MSIMessage {
union {
struct {
#ifdef HOST_WORDS_BIGENDIAN
uint32_t __addr_head:12; /* 0xfee */
uint32_t dest:8;
uint32_t __reserved:8;
uint32_t redir_hint:1;
uint32_t dest_mode:1;
uint32_t __not_used:2;
#else
uint32_t __not_used:2;
uint32_t dest_mode:1;
uint32_t redir_hint:1;
uint32_t __reserved:8;
uint32_t dest:8;
uint32_t __addr_head:12; /* 0xfee */
#endif
uint32_t __addr_hi;
} QEMU_PACKED;
uint64_t msi_addr;
};
union {
struct {
#ifdef HOST_WORDS_BIGENDIAN
uint16_t trigger_mode:1;
uint16_t level:1;
uint16_t __resved:3;
uint16_t delivery_mode:3;
uint16_t vector:8;
#else
uint16_t vector:8;
uint16_t delivery_mode:3;
uint16_t __resved:3;
uint16_t level:1;
uint16_t trigger_mode:1;
#endif
uint16_t __resved1;
} QEMU_PACKED;
uint32_t msi_data;
};
};
/** /**
* x86_iommu_get_default - get default IOMMU device * x86_iommu_get_default - get default IOMMU device
* @return: pointer to default IOMMU device * @return: pointer to default IOMMU device
@ -110,4 +170,10 @@ void x86_iommu_iec_register_notifier(X86IOMMUState *iommu,
void x86_iommu_iec_notify_all(X86IOMMUState *iommu, bool global, void x86_iommu_iec_notify_all(X86IOMMUState *iommu, bool global,
uint32_t index, uint32_t mask); uint32_t index, uint32_t mask);
/**
* x86_iommu_irq_to_msi_message - Populate one MSIMessage from X86IOMMUIrq
* @X86IOMMUIrq: The IRQ information
* @out: Output MSI message
*/
void x86_iommu_irq_to_msi_message(X86IOMMUIrq *irq, MSIMessage *out);
#endif #endif

View File

@ -1,6 +1,8 @@
#ifndef QEMU_PCI_BUS_H #ifndef QEMU_PCI_BUS_H
#define QEMU_PCI_BUS_H #define QEMU_PCI_BUS_H
#include "hw/pci/pci.h"
/* /*
* PCI Bus datastructures. * PCI Bus datastructures.
* *

View File

@ -42,7 +42,7 @@ typedef struct {
} test_data; } test_data;
static char disk[] = "tests/acpi-test-disk-XXXXXX"; static char disk[] = "tests/acpi-test-disk-XXXXXX";
static const char *data_dir = "tests/acpi-test-data"; static const char *data_dir = "tests/data/acpi";
#ifdef CONFIG_IASL #ifdef CONFIG_IASL
static const char *iasl = stringify(CONFIG_IASL); static const char *iasl = stringify(CONFIG_IASL);
#else #else
@ -708,6 +708,21 @@ static void test_acpi_q35_tcg_bridge(void)
free_test_data(&data); free_test_data(&data);
} }
static void test_acpi_q35_tcg_mmio64(void)
{
test_data data = {
.machine = MACHINE_Q35,
.variant = ".mmio64",
.required_struct_types = base_required_struct_types,
.required_struct_types_len = ARRAY_SIZE(base_required_struct_types)
};
test_acpi_one("-m 128M,slots=1,maxmem=2G "
"-device pci-testdev,membar=2G",
&data);
free_test_data(&data);
}
static void test_acpi_piix4_tcg_cphp(void) static void test_acpi_piix4_tcg_cphp(void)
{ {
test_data data; test_data data;
@ -875,6 +890,7 @@ int main(int argc, char *argv[])
qtest_add_func("acpi/piix4/bridge", test_acpi_piix4_tcg_bridge); qtest_add_func("acpi/piix4/bridge", test_acpi_piix4_tcg_bridge);
qtest_add_func("acpi/q35", test_acpi_q35_tcg); qtest_add_func("acpi/q35", test_acpi_q35_tcg);
qtest_add_func("acpi/q35/bridge", test_acpi_q35_tcg_bridge); qtest_add_func("acpi/q35/bridge", test_acpi_q35_tcg_bridge);
qtest_add_func("acpi/q35/mmio64", test_acpi_q35_tcg_mmio64);
qtest_add_func("acpi/piix4/ipmi", test_acpi_piix4_tcg_ipmi); qtest_add_func("acpi/piix4/ipmi", test_acpi_piix4_tcg_ipmi);
qtest_add_func("acpi/q35/ipmi", test_acpi_q35_tcg_ipmi); qtest_add_func("acpi/q35/ipmi", test_acpi_q35_tcg_ipmi);
qtest_add_func("acpi/piix4/cpuhp", test_acpi_piix4_tcg_cphp); qtest_add_func("acpi/piix4/cpuhp", test_acpi_piix4_tcg_cphp);

Binary file not shown.

Binary file not shown.

View File

@ -32,5 +32,3 @@ fi
TEST_ACPI_REBUILD_AML=y QTEST_QEMU_BINARY=$qemu tests/bios-tables-test TEST_ACPI_REBUILD_AML=y QTEST_QEMU_BINARY=$qemu tests/bios-tables-test
echo "The files were rebuilt and can be added to git." echo "The files were rebuilt and can be added to git."
echo "However, if new files were created, please copy them manually" \
"to tests/acpi-test-data/pc/ or tests/acpi-test-data/q35/ ."

View File

@ -23,7 +23,7 @@ static void hex_loader_test(void)
const unsigned int base_addr = 0x00010000; const unsigned int base_addr = 0x00010000;
QTestState *s = qtest_initf( QTestState *s = qtest_initf(
"-M vexpress-a9 -nographic -device loader,file=tests/hex-loader-check-data/test.hex"); "-M vexpress-a9 -nographic -device loader,file=tests/data/hex-loader/test.hex");
for (i = 0; i < 256; ++i) { for (i = 0; i < 256; ++i) {
uint8_t val = qtest_readb(s, base_addr + i); uint8_t val = qtest_readb(s, base_addr + i);