mirror of https://github.com/xemu-project/xemu.git
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:
commit
b66db50f67
|
@ -1120,6 +1120,8 @@ F: hw/i386/acpi-build.[hc]
|
|||
F: hw/arm/virt-acpi-build.c
|
||||
F: tests/bios-tables-test.c
|
||||
F: tests/acpi-utils.[hc]
|
||||
F: tests/acpi-test-data/*
|
||||
F: tests/acpi-test-data/*/*
|
||||
|
||||
ppc4xx
|
||||
M: Alexander Graf <agraf@suse.de>
|
||||
|
@ -1376,7 +1378,7 @@ Intel Hexadecimal Object File Loader
|
|||
M: Su Hang <suhang16@mails.ucas.ac.cn>
|
||||
S: Maintained
|
||||
F: tests/hexloader-test.c
|
||||
F: tests/hex-loader-check-data/test.hex
|
||||
F: tests/data/hex-loader/test.hex
|
||||
|
||||
CHRP NVRAM
|
||||
M: Thomas Huth <thuth@redhat.com>
|
||||
|
|
|
@ -7392,22 +7392,33 @@ if test "$ccache_cpp2" = "yes"; then
|
|||
echo "export CCACHE_CPP2=y" >> $config_host_mak
|
||||
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="$DIRS tests/fp"
|
||||
DIRS="$DIRS docs docs/interop fsdev scsi"
|
||||
DIRS="$DIRS pc-bios/optionrom pc-bios/spapr-rtas pc-bios/s390-ccw"
|
||||
DIRS="$DIRS roms/seabios roms/vgabios"
|
||||
FILES="Makefile tests/tcg/Makefile qdict-test-data.txt"
|
||||
FILES="$FILES tests/tcg/cris/Makefile tests/tcg/cris/.gdbinit"
|
||||
FILES="$FILES tests/tcg/lm32/Makefile tests/tcg/xtensa/Makefile po/Makefile"
|
||||
FILES="$FILES tests/fp/Makefile"
|
||||
FILES="$FILES pc-bios/optionrom/Makefile pc-bios/keymaps"
|
||||
FILES="$FILES pc-bios/spapr-rtas/Makefile"
|
||||
FILES="$FILES pc-bios/s390-ccw/Makefile"
|
||||
FILES="$FILES roms/seabios/Makefile roms/vgabios/Makefile"
|
||||
FILES="$FILES pc-bios/qemu-icon.bmp"
|
||||
FILES="$FILES .gdbinit scripts" # scripts needed by relative path in .gdbinit
|
||||
LINKS="Makefile tests/tcg/Makefile qdict-test-data.txt"
|
||||
LINKS="$LINKS tests/tcg/cris/Makefile tests/tcg/cris/.gdbinit"
|
||||
LINKS="$LINKS tests/tcg/lm32/Makefile tests/tcg/xtensa/Makefile po/Makefile"
|
||||
LINKS="$LINKS tests/fp/Makefile"
|
||||
LINKS="$LINKS pc-bios/optionrom/Makefile pc-bios/keymaps"
|
||||
LINKS="$LINKS pc-bios/spapr-rtas/Makefile"
|
||||
LINKS="$LINKS pc-bios/s390-ccw/Makefile"
|
||||
LINKS="$LINKS roms/seabios/Makefile roms/vgabios/Makefile"
|
||||
LINKS="$LINKS pc-bios/qemu-icon.bmp"
|
||||
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 \
|
||||
$source_path/pc-bios/*.bin \
|
||||
$source_path/pc-bios/*.lid \
|
||||
|
@ -7419,18 +7430,10 @@ for bios_file in \
|
|||
$source_path/pc-bios/u-boot.* \
|
||||
$source_path/pc-bios/palcode-*
|
||||
do
|
||||
FILES="$FILES 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//')"
|
||||
LINKS="$LINKS pc-bios/$(basename $bios_file)"
|
||||
done
|
||||
mkdir -p $DIRS
|
||||
for f in $FILES ; do
|
||||
for f in $LINKS ; do
|
||||
if [ -e "$source_path/$f" ] && [ "$pwd_is_source_path" != "y" ]; then
|
||||
symlink "$source_path/$f" "$f"
|
||||
fi
|
||||
|
@ -7452,25 +7455,13 @@ for rom in seabios vgabios ; do
|
|||
echo "RANLIB=$ranlib" >> $config_mak
|
||||
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
|
||||
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 >> "$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.
|
||||
cat <<EOD >config.status
|
||||
#!/bin/sh
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
pci-test is a device used for testing low level IO
|
||||
|
||||
device implements up to two BARs: BAR0 and BAR1.
|
||||
Each BAR can be memory or IO. Guests must detect
|
||||
BAR type and act accordingly.
|
||||
device implements up to three BARs: BAR0, BAR1 and BAR2.
|
||||
Each of BAR 0+1 can be memory or IO. Guests must detect
|
||||
BAR types and act accordingly.
|
||||
|
||||
Each BAR size is up to 4K bytes.
|
||||
Each BAR starts with the following header:
|
||||
BAR 0+1 size is up to 4K bytes each.
|
||||
BAR 0+1 starts with the following header:
|
||||
|
||||
typedef struct PCITestDevHdr {
|
||||
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
|
||||
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.
|
||||
|
||||
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.
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
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)
|
||||
|
|
|
@ -97,8 +97,8 @@ static void virtio_blk_rw_complete(void *opaque, int ret)
|
|||
|
||||
if (req->qiov.nalloc != -1) {
|
||||
/* If nalloc is != 1 req->qiov is a local copy of the original
|
||||
* external iovec. It was allocated in submit_merged_requests
|
||||
* to be able to merge requests. */
|
||||
* external iovec. It was allocated in submit_requests to be
|
||||
* able to merge requests. */
|
||||
qemu_iovec_destroy(&req->qiov);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
* 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
|
||||
build_amd_iommu(GArray *table_data, BIOSLinker *linker)
|
||||
{
|
||||
int ivhd_table_len = 28;
|
||||
int iommu_start = table_data->len;
|
||||
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 << 7), /* PPRSup */
|
||||
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 */
|
||||
build_append_int_noprefix(table_data, 28, 2);
|
||||
build_append_int_noprefix(table_data, ivhd_table_len, 2);
|
||||
/* DeviceID */
|
||||
build_append_int_noprefix(table_data, s->devid, 2);
|
||||
/* Capability offset */
|
||||
|
@ -2507,7 +2518,8 @@ build_amd_iommu(GArray *table_data, BIOSLinker *linker)
|
|||
build_append_int_noprefix(table_data,
|
||||
(48UL << 30) | /* HATS */
|
||||
(48UL << 28) | /* GATS */
|
||||
(1UL << 2), /* GTSup */
|
||||
(1UL << 2) | /* GTSup */
|
||||
(1UL << 6), /* GASup */
|
||||
4);
|
||||
/*
|
||||
* 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);
|
||||
|
||||
/*
|
||||
* 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),
|
||||
"IVRS", table_data->len - iommu_start, 1, NULL, NULL);
|
||||
}
|
||||
|
|
|
@ -26,7 +26,9 @@
|
|||
#include "amd_iommu.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "hw/i386/apic_internal.h"
|
||||
#include "trace.h"
|
||||
#include "hw/i386/apic-msidef.h"
|
||||
|
||||
/* used AMD-Vi MMIO registers */
|
||||
const char *amdvi_mmio_low[] = {
|
||||
|
@ -55,6 +57,7 @@ struct AMDVIAddressSpace {
|
|||
uint8_t bus_num; /* bus number */
|
||||
uint8_t devfn; /* device function */
|
||||
AMDVIState *iommu_state; /* AMDVI - one per machine */
|
||||
MemoryRegion root; /* AMDVI Root memory map region */
|
||||
IOMMUMemoryRegion iommu; /* Device's address translation region */
|
||||
MemoryRegion iommu_ir; /* Device's interrupt remapping region */
|
||||
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->cmdbuf_enabled = s->enabled && !!(control &
|
||||
AMDVI_MMIO_CONTROL_CMDBUFLEN);
|
||||
s->ga_enabled = !!(control & AMDVI_MMIO_CONTROL_GAEN);
|
||||
|
||||
/* update the flags depending on the control register */
|
||||
if (s->cmdbuf_enabled) {
|
||||
|
@ -807,7 +811,7 @@ static inline uint64_t amdvi_get_perms(uint64_t entry)
|
|||
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,
|
||||
uint64_t *dte)
|
||||
{
|
||||
|
@ -820,7 +824,7 @@ static bool amdvi_validate_dte(AMDVIState *s, uint16_t devid,
|
|||
return false;
|
||||
}
|
||||
|
||||
return dte[0] & AMDVI_DEV_VALID;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* get a device table entry given the devid */
|
||||
|
@ -966,8 +970,12 @@ static void amdvi_do_translate(AMDVIAddressSpace *as, hwaddr addr,
|
|||
return;
|
||||
}
|
||||
|
||||
/* devices with V = 0 are not translated */
|
||||
if (!amdvi_get_dte(s, devid, entry)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* devices with V = 0 are not translated */
|
||||
if (!(entry[0] & AMDVI_DEV_VALID)) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
@ -1026,10 +1034,366 @@ static IOMMUTLBEntry amdvi_translate(IOMMUMemoryRegion *iommu, hwaddr addr,
|
|||
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)
|
||||
{
|
||||
char name[128];
|
||||
AMDVIState *s = opaque;
|
||||
AMDVIAddressSpace **iommu_as;
|
||||
AMDVIAddressSpace **iommu_as, *amdvi_dev_as;
|
||||
int bus_num = pci_bus_num(bus);
|
||||
|
||||
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 */
|
||||
if (!iommu_as[devfn]) {
|
||||
snprintf(name, sizeof(name), "amd_iommu_devfn_%d", devfn);
|
||||
|
||||
iommu_as[devfn] = g_malloc0(sizeof(AMDVIAddressSpace));
|
||||
iommu_as[devfn]->bus_num = (uint8_t)bus_num;
|
||||
iommu_as[devfn]->devfn = (uint8_t)devfn;
|
||||
iommu_as[devfn]->iommu_state = s;
|
||||
|
||||
memory_region_init_iommu(&iommu_as[devfn]->iommu,
|
||||
sizeof(iommu_as[devfn]->iommu),
|
||||
amdvi_dev_as = iommu_as[devfn];
|
||||
|
||||
/*
|
||||
* 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,
|
||||
OBJECT(s),
|
||||
"amd-iommu", UINT64_MAX);
|
||||
address_space_init(&iommu_as[devfn]->as,
|
||||
MEMORY_REGION(&iommu_as[devfn]->iommu),
|
||||
"amd-iommu");
|
||||
"amd_iommu", UINT64_MAX);
|
||||
memory_region_init(&amdvi_dev_as->root, OBJECT(s),
|
||||
"amdvi_root", UINT64_MAX);
|
||||
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;
|
||||
}
|
||||
|
@ -1172,6 +1562,9 @@ static void amdvi_realize(DeviceState *dev, Error **err)
|
|||
return;
|
||||
}
|
||||
|
||||
/* Pseudo address space under root PCI bus. */
|
||||
pcms->ioapic_as = amdvi_host_dma_iommu(bus, s, AMDVI_IOAPIC_SB_DEVID);
|
||||
|
||||
/* set up MMIO */
|
||||
memory_region_init_io(&s->mmio, OBJECT(s), &mmio_mem_ops, s, "amdvi-mmio",
|
||||
AMDVI_MMIO_SIZE);
|
||||
|
@ -1205,6 +1598,7 @@ static void amdvi_class_init(ObjectClass *klass, void* data)
|
|||
dc->vmsd = &vmstate_amdvi;
|
||||
dc->hotpluggable = false;
|
||||
dc_class->realize = amdvi_realize;
|
||||
dc_class->int_remap = amdvi_int_remap;
|
||||
/* Supported by the pc-q35-* machine types */
|
||||
dc->user_creatable = true;
|
||||
}
|
||||
|
|
|
@ -103,6 +103,7 @@
|
|||
#define AMDVI_MMIO_CONTROL_EVENTINTEN (1ULL << 3)
|
||||
#define AMDVI_MMIO_CONTROL_COMWAITINTEN (1ULL << 4)
|
||||
#define AMDVI_MMIO_CONTROL_CMDBUFLEN (1ULL << 12)
|
||||
#define AMDVI_MMIO_CONTROL_GAEN (1ULL << 17)
|
||||
|
||||
/* MMIO status register bits */
|
||||
#define AMDVI_MMIO_STATUS_CMDBUF_RUN (1 << 4)
|
||||
|
@ -175,7 +176,7 @@
|
|||
/* 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)
|
||||
AMDVI_GATS_MODE | AMDVI_HATS_MODE | AMDVI_FEATURE_GA)
|
||||
|
||||
/* capabilities header */
|
||||
#define AMDVI_CAPAB_FEATURES (AMDVI_CAPAB_FLAT_EXT | \
|
||||
|
@ -206,8 +207,94 @@
|
|||
|
||||
#define AMDVI_COMMAND_SIZE 16
|
||||
|
||||
#define AMDVI_INT_ADDR_FIRST 0xfee00000
|
||||
#define AMDVI_INT_ADDR_LAST 0xfeefffff
|
||||
#define AMDVI_INT_ADDR_FIRST 0xfee00000
|
||||
#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 AMD_IOMMU_DEVICE(obj)\
|
||||
|
@ -278,6 +365,9 @@ typedef struct AMDVIState {
|
|||
|
||||
/* IOTLB */
|
||||
GHashTable *iotlb;
|
||||
|
||||
/* Interrupt remapping */
|
||||
bool ga_enabled;
|
||||
} AMDVIState;
|
||||
|
||||
#endif
|
||||
|
|
|
@ -37,6 +37,9 @@
|
|||
#include "kvm_i386.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,
|
||||
uint64_t wmask, uint64_t w1cmask)
|
||||
{
|
||||
|
@ -227,6 +230,14 @@ static void vtd_reset_iotlb(IntelIOMMUState *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,
|
||||
uint32_t level)
|
||||
{
|
||||
|
@ -1035,7 +1046,6 @@ static int vtd_sync_shadow_page_hook(IOMMUTLBEntry *entry,
|
|||
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,
|
||||
VTDContextEntry *ce,
|
||||
hwaddr addr, hwaddr size)
|
||||
|
@ -1047,39 +1057,41 @@ static int vtd_sync_shadow_page_table_range(VTDAddressSpace *vtd_as,
|
|||
.notify_unmap = true,
|
||||
.aw = s->aw_bits,
|
||||
.as = vtd_as,
|
||||
.domain_id = VTD_CONTEXT_ENTRY_DID(ce->hi),
|
||||
};
|
||||
VTDContextEntry ce_cache;
|
||||
int ret;
|
||||
|
||||
if (ce) {
|
||||
/* 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);
|
||||
return vtd_page_walk(ce, addr, addr + size, &info);
|
||||
}
|
||||
|
||||
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_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
|
||||
* 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);
|
||||
/* Ok - report back to driver */
|
||||
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 */
|
||||
|
@ -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_switch_address_space_all(s);
|
||||
vtd_reset_caches(s);
|
||||
vtd_address_space_refresh_all(s);
|
||||
}
|
||||
|
||||
/* 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 */
|
||||
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 = {};
|
||||
int ret = 0;
|
||||
|
@ -2730,30 +2745,6 @@ static int vtd_remap_irq_get(IntelIOMMUState *iommu, uint16_t index,
|
|||
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 */
|
||||
static int vtd_interrupt_remap_msi(IntelIOMMUState *iommu,
|
||||
MSIMessage *origin,
|
||||
|
@ -2763,7 +2754,7 @@ static int vtd_interrupt_remap_msi(IntelIOMMUState *iommu,
|
|||
int ret = 0;
|
||||
VTD_IR_MSIAddress addr;
|
||||
uint16_t index;
|
||||
VTDIrq irq = {};
|
||||
X86IOMMUIrq irq = {};
|
||||
|
||||
assert(origin && translated);
|
||||
|
||||
|
@ -2842,8 +2833,8 @@ static int vtd_interrupt_remap_msi(IntelIOMMUState *iommu,
|
|||
*/
|
||||
irq.msi_addr_last_bits = addr.addr.__not_care;
|
||||
|
||||
/* Translate VTDIrq to MSI message */
|
||||
vtd_generate_msi_message(&irq, translated);
|
||||
/* Translate X86IOMMUIrq to MSI message */
|
||||
x86_iommu_irq_to_msi_message(&irq, translated);
|
||||
|
||||
out:
|
||||
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)
|
||||
{
|
||||
memory_region_notify_one((IOMMUNotifier *)private, entry);
|
||||
|
@ -3160,10 +3157,7 @@ static void vtd_init(IntelIOMMUState *s)
|
|||
s->cap |= VTD_CAP_CM;
|
||||
}
|
||||
|
||||
vtd_iommu_lock(s);
|
||||
vtd_reset_context_cache_locked(s);
|
||||
vtd_reset_iotlb_locked(s);
|
||||
vtd_iommu_unlock(s);
|
||||
vtd_reset_caches(s);
|
||||
|
||||
/* Define registers with default values and bit semantics */
|
||||
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);
|
||||
|
||||
vtd_init(s);
|
||||
|
||||
/*
|
||||
* When device reset, throw away all mappings and external caches
|
||||
*/
|
||||
vtd_address_space_unmap_all(s);
|
||||
vtd_address_space_refresh_all(s);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
/* 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) {
|
||||
error_setg(errp, "eim=on cannot be selected without intremap=on");
|
||||
return false;
|
||||
|
|
|
@ -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_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_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
|
||||
vmport_register(unsigned char command, void *func, void *opaque) "command: 0x%02x func: %p opaque: %p"
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include "qapi/error.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "trace.h"
|
||||
#include "sysemu/kvm.h"
|
||||
|
||||
void x86_iommu_iec_register_notifier(X86IOMMUState *iommu,
|
||||
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 */
|
||||
static X86IOMMUState *x86_iommu_default = NULL;
|
||||
|
||||
|
@ -94,6 +119,14 @@ static void x86_iommu_realize(DeviceState *dev, Error **errp)
|
|||
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) {
|
||||
x86_class->realize(dev, errp);
|
||||
}
|
||||
|
|
|
@ -85,6 +85,9 @@ typedef struct PCITestDevState {
|
|||
MemoryRegion portio;
|
||||
IOTest *tests;
|
||||
int current;
|
||||
|
||||
uint64_t membar_size;
|
||||
MemoryRegion membar;
|
||||
} PCITestDevState;
|
||||
|
||||
#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, 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->tests = g_malloc0(IOTEST_MAX * sizeof *d->tests);
|
||||
for (i = 0; i < IOTEST_MAX; ++i) {
|
||||
|
@ -305,6 +318,11 @@ static void qdev_pci_testdev_reset(DeviceState *dev)
|
|||
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)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
@ -319,6 +337,7 @@ static void pci_testdev_class_init(ObjectClass *klass, void *data)
|
|||
dc->desc = "PCI Test Device";
|
||||
set_bit(DEVICE_CATEGORY_MISC, dc->categories);
|
||||
dc->reset = qdev_pci_testdev_reset;
|
||||
dc->props = pci_testdev_properties;
|
||||
}
|
||||
|
||||
static const TypeInfo pci_testdev_info = {
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
#include "hw/pci/pci_ids.h"
|
||||
#include "hw/pci/msi.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_REV 0x2
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
#ifndef QEMU_IOH3420_H
|
||||
#define QEMU_IOH3420_H
|
||||
|
||||
#include "hw/pci/pcie_port.h"
|
||||
|
||||
#endif /* QEMU_IOH3420_H */
|
|
@ -23,7 +23,7 @@
|
|||
#include "hw/pci/pci_ids.h"
|
||||
#include "hw/pci/msi.h"
|
||||
#include "hw/pci/pcie.h"
|
||||
#include "xio3130_downstream.h"
|
||||
#include "hw/pci/pcie_port.h"
|
||||
#include "qapi/error.h"
|
||||
|
||||
#define PCI_DEVICE_ID_TI_XIO3130D 0x8233 /* downstream port */
|
||||
|
@ -127,32 +127,6 @@ static void xio3130_downstream_exitfn(PCIDevice *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[] = {
|
||||
DEFINE_PROP_BIT(COMPAT_PROP_PCP, PCIDevice, cap_present,
|
||||
QEMU_PCIE_SLTCAP_PCP_BITNR, true),
|
||||
|
|
|
@ -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 */
|
|
@ -23,7 +23,7 @@
|
|||
#include "hw/pci/pci_ids.h"
|
||||
#include "hw/pci/msi.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 XIO3130_REVISION 0x2
|
||||
|
@ -108,28 +108,6 @@ static void xio3130_upstream_exitfn(PCIDevice *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 = {
|
||||
.name = "xio3130-express-upstream-port",
|
||||
.priority = MIG_PRI_PCI_BUS,
|
||||
|
|
|
@ -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 */
|
|
@ -40,7 +40,7 @@
|
|||
|
||||
/*
|
||||
* 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) \
|
||||
|
@ -95,6 +95,9 @@ typedef struct PIIX3State {
|
|||
#define I440FX_PCI_DEVICE(obj) \
|
||||
OBJECT_CHECK(PCII440FXState, (obj), TYPE_I440FX_PCI_DEVICE)
|
||||
|
||||
#define TYPE_PIIX3_DEVICE "PIIX3"
|
||||
#define TYPE_PIIX3_XEN_DEVICE "PIIX3-xen"
|
||||
|
||||
struct PCII440FXState {
|
||||
/*< private >*/
|
||||
PCIDevice parent_obj;
|
||||
|
@ -142,7 +145,7 @@ static void i440fx_update_memory_mappings(PCII440FXState *d)
|
|||
PCIDevice *pd = PCI_DEVICE(d);
|
||||
|
||||
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,
|
||||
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
|
||||
* reserved space for memory hotplug if any.
|
||||
*/
|
||||
static void i440fx_pcihost_get_pci_hole64_start(Object *obj, Visitor *v,
|
||||
const char *name,
|
||||
void *opaque, Error **errp)
|
||||
static uint64_t i440fx_pcihost_get_pci_hole64_start_value(Object *obj)
|
||||
{
|
||||
PCIHostState *h = 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) {
|
||||
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);
|
||||
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;
|
||||
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,
|
||||
&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,
|
||||
&f->pam_regions[i+1], PAM_EXPAN_BASE + i * 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. */
|
||||
if (xen_enabled()) {
|
||||
PCIDevice *pci_dev = pci_create_simple_multifunction(b,
|
||||
-1, true, "PIIX3-xen");
|
||||
-1, true, TYPE_PIIX3_XEN_DEVICE);
|
||||
piix3 = PIIX3_PCI_DEVICE(pci_dev);
|
||||
pci_bus_irqs(b, xen_piix3_set_irq, xen_pci_slot_get_pirq,
|
||||
piix3, XEN_PIIX_NUM_PIRQS);
|
||||
} else {
|
||||
PCIDevice *pci_dev = pci_create_simple_multifunction(b,
|
||||
-1, true, "PIIX3");
|
||||
-1, true, TYPE_PIIX3_DEVICE);
|
||||
piix3 = PIIX3_PCI_DEVICE(pci_dev);
|
||||
pci_bus_irqs(b, piix3_set_irq, pci_slot_get_pirq, piix3,
|
||||
PIIX_NUM_PIRQS);
|
||||
|
@ -741,7 +751,7 @@ static void piix3_class_init(ObjectClass *klass, void *data)
|
|||
}
|
||||
|
||||
static const TypeInfo piix3_info = {
|
||||
.name = "PIIX3",
|
||||
.name = TYPE_PIIX3_DEVICE,
|
||||
.parent = TYPE_PIIX3_PCI_DEVICE,
|
||||
.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 = {
|
||||
.name = "PIIX3-xen",
|
||||
.name = TYPE_PIIX3_XEN_DEVICE,
|
||||
.parent = TYPE_PIIX3_PCI_DEVICE,
|
||||
.class_init = piix3_xen_class_init,
|
||||
};
|
||||
|
|
|
@ -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
|
||||
* reserved space for memory hotplug if any.
|
||||
*/
|
||||
static void q35_host_get_pci_hole64_start(Object *obj, Visitor *v,
|
||||
const char *name, void *opaque,
|
||||
Error **errp)
|
||||
static uint64_t q35_host_get_pci_hole64_start_value(Object *obj)
|
||||
{
|
||||
PCIHostState *h = PCI_HOST_BRIDGE(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) {
|
||||
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);
|
||||
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;
|
||||
uint64_t value, hole64_end;
|
||||
|
||||
|
|
|
@ -399,7 +399,7 @@ void pci_bridge_exitfn(PCIDevice *pci_dev)
|
|||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
void pci_bridge_map_irq(PCIBridge *br, const char* bus_name,
|
||||
|
|
|
@ -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.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.backend_features = 0;
|
||||
|
||||
|
|
|
@ -66,8 +66,6 @@ typedef struct VTDIOTLBEntry VTDIOTLBEntry;
|
|||
typedef struct VTDBus VTDBus;
|
||||
typedef union VTD_IR_TableEntry VTD_IR_TableEntry;
|
||||
typedef union VTD_IR_MSIAddress VTD_IR_MSIAddress;
|
||||
typedef struct VTDIrq VTDIrq;
|
||||
typedef struct VTD_MSIMessage VTD_MSIMessage;
|
||||
|
||||
/* Context-Entry */
|
||||
struct VTDContextEntry {
|
||||
|
@ -197,63 +195,6 @@ union VTD_IR_MSIAddress {
|
|||
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 */
|
||||
#define VTD_IR_MSI_DATA (0)
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
|
||||
#include "hw/sysbus.h"
|
||||
#include "hw/pci/pci.h"
|
||||
#include "hw/pci/msi.h"
|
||||
|
||||
#define TYPE_X86_IOMMU_DEVICE ("x86-iommu")
|
||||
#define X86_IOMMU_DEVICE(obj) \
|
||||
|
@ -35,6 +36,8 @@
|
|||
|
||||
typedef struct X86IOMMUState X86IOMMUState;
|
||||
typedef struct X86IOMMUClass X86IOMMUClass;
|
||||
typedef struct X86IOMMUIrq X86IOMMUIrq;
|
||||
typedef struct X86IOMMU_MSIMessage X86IOMMU_MSIMessage;
|
||||
|
||||
typedef enum IommuType {
|
||||
TYPE_INTEL,
|
||||
|
@ -78,6 +81,63 @@ struct X86IOMMUState {
|
|||
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
|
||||
* @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,
|
||||
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
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
#ifndef QEMU_PCI_BUS_H
|
||||
#define QEMU_PCI_BUS_H
|
||||
|
||||
#include "hw/pci/pci.h"
|
||||
|
||||
/*
|
||||
* PCI Bus datastructures.
|
||||
*
|
||||
|
|
|
@ -42,7 +42,7 @@ typedef struct {
|
|||
} test_data;
|
||||
|
||||
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
|
||||
static const char *iasl = stringify(CONFIG_IASL);
|
||||
#else
|
||||
|
@ -708,6 +708,21 @@ static void test_acpi_q35_tcg_bridge(void)
|
|||
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)
|
||||
{
|
||||
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/q35", test_acpi_q35_tcg);
|
||||
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/q35/ipmi", test_acpi_q35_tcg_ipmi);
|
||||
qtest_add_func("acpi/piix4/cpuhp", test_acpi_piix4_tcg_cphp);
|
||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -32,5 +32,3 @@ fi
|
|||
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 "However, if new files were created, please copy them manually" \
|
||||
"to tests/acpi-test-data/pc/ or tests/acpi-test-data/q35/ ."
|
|
@ -23,7 +23,7 @@ static void hex_loader_test(void)
|
|||
const unsigned int base_addr = 0x00010000;
|
||||
|
||||
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) {
|
||||
uint8_t val = qtest_readb(s, base_addr + i);
|
||||
|
|
Loading…
Reference in New Issue