[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [RFC v1 2/3] intel_iommu: add 256 bits qi_desc support
From: |
Yi Sun |
Subject: |
[Qemu-devel] [RFC v1 2/3] intel_iommu: add 256 bits qi_desc support |
Date: |
Wed, 30 Jan 2019 13:09:12 +0800 |
From: "Liu, Yi L" <address@hidden>
Per Intel(R) VT-d 3.0, the qi_desc is 256 bits in Scalable
Mode. This patch adds emulation of 256bits qi_desc.
[Yi Sun is co-developer to rebase and refine the patch.]
Signed-off-by: Yi Sun <address@hidden>
Signed-off-by: Liu, Yi L <address@hidden>
---
hw/i386/intel_iommu.c | 182 +++++++++++++++++++++++++----------------
hw/i386/intel_iommu_internal.h | 8 +-
include/hw/i386/intel_iommu.h | 1 +
3 files changed, 116 insertions(+), 75 deletions(-)
diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c
index 396ac8e..3664a00 100644
--- a/hw/i386/intel_iommu.c
+++ b/hw/i386/intel_iommu.c
@@ -38,6 +38,7 @@
#include "trace.h"
#define vtd_devfn_check(devfn) ((devfn & VTD_DEVFN_CHECK_MASK) ? true : false)
+#define vtd_ecap_smts(s) ((s)->ecap & VTD_ECAP_SMTS)
/* context entry operations */
#define vtd_get_ce_size(s, ce) \
@@ -65,6 +66,9 @@
#define vtd_pe_get_slpt_base(pe) ((pe)->val[0] & VTD_SM_PASID_ENTRY_SLPTPTR)
#define vtd_pe_get_domain_id(pe) VTD_SM_PASID_ENTRY_DID((pe)->val[1])
+/* invalidation desc */
+#define vtd_get_inv_desc_width(s) ((s)->iq_dw ? 32 : 16)
+
static void vtd_address_space_refresh_all(IntelIOMMUState *s);
static void vtd_address_space_unmap(VTDAddressSpace *as, IOMMUNotifier *n);
@@ -1759,6 +1763,11 @@ static void vtd_root_table_setup(IntelIOMMUState *s)
s->root_scalable = s->root & VTD_RTADDR_SMT;
s->root &= VTD_RTADDR_ADDR_MASK(s->aw_bits);
+ /* if Scalable mode is not enabled, enforce iq_dw to be 16 byte */
+ if (!s->root_scalable) {
+ s->iq_dw = 0;
+ }
+
trace_vtd_reg_dmar_root(s->root, s->root_extended);
}
@@ -2052,7 +2061,7 @@ static void vtd_handle_gcmd_qie(IntelIOMMUState *s, bool
en)
if (en) {
s->iq = iqa_val & VTD_IQA_IQA_MASK(s->aw_bits);
/* 2^(x+8) entries */
- s->iq_size = 1UL << ((iqa_val & VTD_IQA_QS) + 8);
+ s->iq_size = 1UL << ((iqa_val & VTD_IQA_QS) + 8 - (s->iq_dw ? 1 : 0));
s->qi_enabled = true;
trace_vtd_inv_qi_setup(s->iq, s->iq_size);
/* Ok - report back to driver */
@@ -2219,54 +2228,66 @@ static void vtd_handle_iotlb_write(IntelIOMMUState *s)
}
/* Fetch an Invalidation Descriptor from the Invalidation Queue */
-static bool vtd_get_inv_desc(dma_addr_t base_addr, uint32_t offset,
+static bool vtd_get_inv_desc(IntelIOMMUState *s,
VTDInvDesc *inv_desc)
{
- dma_addr_t addr = base_addr + offset * sizeof(*inv_desc);
- if (dma_memory_read(&address_space_memory, addr, inv_desc,
- sizeof(*inv_desc))) {
- error_report_once("Read INV DESC failed");
- inv_desc->lo = 0;
- inv_desc->hi = 0;
+ dma_addr_t base_addr = s->iq;
+ uint32_t offset = s->iq_head;
+ uint32_t dw = vtd_get_inv_desc_width(s);
+ dma_addr_t addr = base_addr + offset * dw;
+
+ /* init */
+ inv_desc->val[0] = 0;
+ inv_desc->val[1] = 0;
+ inv_desc->val[2] = 0;
+ inv_desc->val[3] = 0;
+
+ if (dma_memory_read(&address_space_memory, addr, inv_desc, dw)) {
+ error_report_once("Read INV DESC failed.");
return false;
}
- inv_desc->lo = le64_to_cpu(inv_desc->lo);
- inv_desc->hi = le64_to_cpu(inv_desc->hi);
+ inv_desc->val[0] = le64_to_cpu(inv_desc->val[0]);
+ inv_desc->val[1] = le64_to_cpu(inv_desc->val[1]);
+ inv_desc->val[2] = le64_to_cpu(inv_desc->val[2]);
+ inv_desc->val[3] = le64_to_cpu(inv_desc->val[3]);
return true;
}
static bool vtd_process_wait_desc(IntelIOMMUState *s, VTDInvDesc *inv_desc)
{
- if ((inv_desc->hi & VTD_INV_DESC_WAIT_RSVD_HI) ||
- (inv_desc->lo & VTD_INV_DESC_WAIT_RSVD_LO)) {
- error_report_once("%s: invalid wait desc: hi=%"PRIx64", lo=%"PRIx64
- " (reserved nonzero)", __func__, inv_desc->hi,
- inv_desc->lo);
+ if ((inv_desc->val[1] & VTD_INV_DESC_WAIT_RSVD_HI) ||
+ (inv_desc->val[0] & VTD_INV_DESC_WAIT_RSVD_LO)) {
+ error_report_once("%s: invalid wait desc: val[1]=%"PRIx64
+ ", val[0]=%"PRIx64
+ " (reserved nonzero)", __func__, inv_desc->val[1],
+ inv_desc->val[0]);
return false;
}
- if (inv_desc->lo & VTD_INV_DESC_WAIT_SW) {
+ if (inv_desc->val[0] & VTD_INV_DESC_WAIT_SW) {
/* Status Write */
- uint32_t status_data = (uint32_t)(inv_desc->lo >>
+ uint32_t status_data = (uint32_t)(inv_desc->val[0] >>
VTD_INV_DESC_WAIT_DATA_SHIFT);
- assert(!(inv_desc->lo & VTD_INV_DESC_WAIT_IF));
+ assert(!(inv_desc->val[0] & VTD_INV_DESC_WAIT_IF));
/* FIXME: need to be masked with HAW? */
- dma_addr_t status_addr = inv_desc->hi;
+ dma_addr_t status_addr = inv_desc->val[1];
trace_vtd_inv_desc_wait_sw(status_addr, status_data);
status_data = cpu_to_le32(status_data);
if (dma_memory_write(&address_space_memory, status_addr, &status_data,
sizeof(status_data))) {
- trace_vtd_inv_desc_wait_write_fail(inv_desc->hi, inv_desc->lo);
+ trace_vtd_inv_desc_wait_write_fail(inv_desc->val[1],
+ inv_desc->val[0]);
return false;
}
- } else if (inv_desc->lo & VTD_INV_DESC_WAIT_IF) {
+ } else if (inv_desc->val[0] & VTD_INV_DESC_WAIT_IF) {
/* Interrupt flag */
vtd_generate_completion_event(s);
} else {
- error_report_once("%s: invalid wait desc: hi=%"PRIx64", lo=%"PRIx64
- " (unknown type)", __func__, inv_desc->hi,
- inv_desc->lo);
+ error_report_once("%s: invalid wait desc: val[1]=%"PRIx64
+ ", val[0]=%"PRIx64
+ " (unknown type)", __func__, inv_desc->val[1],
+ inv_desc->val[0]);
return false;
}
return true;
@@ -2277,31 +2298,33 @@ static bool
vtd_process_context_cache_desc(IntelIOMMUState *s,
{
uint16_t sid, fmask;
- if ((inv_desc->lo & VTD_INV_DESC_CC_RSVD) || inv_desc->hi) {
- error_report_once("%s: invalid cc inv desc: hi=%"PRIx64", lo=%"PRIx64
- " (reserved nonzero)", __func__, inv_desc->hi,
- inv_desc->lo);
+ if ((inv_desc->val[0] & VTD_INV_DESC_CC_RSVD) || inv_desc->val[1]) {
+ error_report_once("%s: invalid cc inv desc: val[1]=%"PRIx64
+ ", val[0]=%"PRIx64
+ " (reserved nonzero)", __func__, inv_desc->val[1],
+ inv_desc->val[0]);
return false;
}
- switch (inv_desc->lo & VTD_INV_DESC_CC_G) {
+ switch (inv_desc->val[0] & VTD_INV_DESC_CC_G) {
case VTD_INV_DESC_CC_DOMAIN:
trace_vtd_inv_desc_cc_domain(
- (uint16_t)VTD_INV_DESC_CC_DID(inv_desc->lo));
+ (uint16_t)VTD_INV_DESC_CC_DID(inv_desc->val[0]));
/* Fall through */
case VTD_INV_DESC_CC_GLOBAL:
vtd_context_global_invalidate(s);
break;
case VTD_INV_DESC_CC_DEVICE:
- sid = VTD_INV_DESC_CC_SID(inv_desc->lo);
- fmask = VTD_INV_DESC_CC_FM(inv_desc->lo);
+ sid = VTD_INV_DESC_CC_SID(inv_desc->val[0]);
+ fmask = VTD_INV_DESC_CC_FM(inv_desc->val[0]);
vtd_context_device_invalidate(s, sid, fmask);
break;
default:
- error_report_once("%s: invalid cc inv desc: hi=%"PRIx64", lo=%"PRIx64
- " (invalid type)", __func__, inv_desc->hi,
- inv_desc->lo);
+ error_report_once("%s: invalid cc inv desc: val[1]=%"PRIx64
+ ", val[0]=%"PRIx64
+ " (invalid type)", __func__, inv_desc->val[1],
+ inv_desc->val[0]);
return false;
}
return true;
@@ -2313,32 +2336,32 @@ static bool vtd_process_iotlb_desc(IntelIOMMUState *s,
VTDInvDesc *inv_desc)
uint8_t am;
hwaddr addr;
- if ((inv_desc->lo & VTD_INV_DESC_IOTLB_RSVD_LO) ||
- (inv_desc->hi & VTD_INV_DESC_IOTLB_RSVD_HI)) {
- error_report_once("%s: invalid iotlb inv desc: hi=0x%"PRIx64
- ", lo=0x%"PRIx64" (reserved bits unzero)\n",
- __func__, inv_desc->hi, inv_desc->lo);
+ if ((inv_desc->val[0] & VTD_INV_DESC_IOTLB_RSVD_LO) ||
+ (inv_desc->val[1] & VTD_INV_DESC_IOTLB_RSVD_HI)) {
+ error_report_once("%s: invalid iotlb inv desc: val[1]=0x%"PRIx64
+ ", val[0]=0x%"PRIx64" (reserved bits unzero)\n",
+ __func__, inv_desc->val[1], inv_desc->val[0]);
return false;
}
- switch (inv_desc->lo & VTD_INV_DESC_IOTLB_G) {
+ switch (inv_desc->val[0] & VTD_INV_DESC_IOTLB_G) {
case VTD_INV_DESC_IOTLB_GLOBAL:
vtd_iotlb_global_invalidate(s);
break;
case VTD_INV_DESC_IOTLB_DOMAIN:
- domain_id = VTD_INV_DESC_IOTLB_DID(inv_desc->lo);
+ domain_id = VTD_INV_DESC_IOTLB_DID(inv_desc->val[0]);
vtd_iotlb_domain_invalidate(s, domain_id);
break;
case VTD_INV_DESC_IOTLB_PAGE:
- domain_id = VTD_INV_DESC_IOTLB_DID(inv_desc->lo);
- addr = VTD_INV_DESC_IOTLB_ADDR(inv_desc->hi);
- am = VTD_INV_DESC_IOTLB_AM(inv_desc->hi);
+ domain_id = VTD_INV_DESC_IOTLB_DID(inv_desc->val[0]);
+ addr = VTD_INV_DESC_IOTLB_ADDR(inv_desc->val[1]);
+ am = VTD_INV_DESC_IOTLB_AM(inv_desc->val[1]);
if (am > VTD_MAMV) {
- error_report_once("%s: invalid iotlb inv desc: hi=0x%"PRIx64
- ", lo=0x%"PRIx64" (am=%u > VTD_MAMV=%u)\n",
- __func__, inv_desc->hi, inv_desc->lo,
+ error_report_once("%s: invalid iotlb inv desc: val[1]=0x%"PRIx64
+ ", val[0]=0x%"PRIx64" (am=%u > VTD_MAMV=%u)\n",
+ __func__, inv_desc->val[1], inv_desc->val[0],
am, (unsigned)VTD_MAMV);
return false;
}
@@ -2346,10 +2369,10 @@ static bool vtd_process_iotlb_desc(IntelIOMMUState *s,
VTDInvDesc *inv_desc)
break;
default:
- error_report_once("%s: invalid iotlb inv desc: hi=0x%"PRIx64
- ", lo=0x%"PRIx64" (type mismatch: 0x%llx)\n",
- __func__, inv_desc->hi, inv_desc->lo,
- inv_desc->lo & VTD_INV_DESC_IOTLB_G);
+ error_report_once("%s: invalid iotlb inv desc: val[1]=0x%"PRIx64
+ ", val[0]=0x%"PRIx64" (type mismatch: 0x%llx)\n",
+ __func__, inv_desc->val[1], inv_desc->val[0],
+ inv_desc->val[0] & VTD_INV_DESC_IOTLB_G);
return false;
}
return true;
@@ -2381,17 +2404,17 @@ static bool
vtd_process_device_iotlb_desc(IntelIOMMUState *s,
bool size;
uint8_t bus_num;
- addr = VTD_INV_DESC_DEVICE_IOTLB_ADDR(inv_desc->hi);
- sid = VTD_INV_DESC_DEVICE_IOTLB_SID(inv_desc->lo);
+ addr = VTD_INV_DESC_DEVICE_IOTLB_ADDR(inv_desc->val[1]);
+ sid = VTD_INV_DESC_DEVICE_IOTLB_SID(inv_desc->val[0]);
devfn = sid & 0xff;
bus_num = sid >> 8;
- size = VTD_INV_DESC_DEVICE_IOTLB_SIZE(inv_desc->hi);
+ size = VTD_INV_DESC_DEVICE_IOTLB_SIZE(inv_desc->val[1]);
- if ((inv_desc->lo & VTD_INV_DESC_DEVICE_IOTLB_RSVD_LO) ||
- (inv_desc->hi & VTD_INV_DESC_DEVICE_IOTLB_RSVD_HI)) {
- error_report_once("%s: invalid dev-iotlb inv desc: hi=%"PRIx64
- ", lo=%"PRIx64" (reserved nonzero)", __func__,
- inv_desc->hi, inv_desc->lo);
+ if ((inv_desc->val[0] & VTD_INV_DESC_DEVICE_IOTLB_RSVD_LO) ||
+ (inv_desc->val[1] & VTD_INV_DESC_DEVICE_IOTLB_RSVD_HI)) {
+ error_report_once("%s: invalid dev-iotlb inv desc: val[1]=%"PRIx64
+ ", val[0]=%"PRIx64" (reserved nonzero)", __func__,
+ inv_desc->val[1], inv_desc->val[0]);
return false;
}
@@ -2437,54 +2460,64 @@ static bool vtd_process_inv_desc(IntelIOMMUState *s)
uint8_t desc_type;
trace_vtd_inv_qi_head(s->iq_head);
- if (!vtd_get_inv_desc(s->iq, s->iq_head, &inv_desc)) {
+ if (!vtd_get_inv_desc(s, &inv_desc)) {
s->iq_last_desc_type = VTD_INV_DESC_NONE;
return false;
}
- desc_type = inv_desc.lo & VTD_INV_DESC_TYPE;
+ if (inv_desc.val[3] || inv_desc.val[2]) {
+ error_report_once("%s: invalid inv desc: val[3]=%"PRIx64
+ ", val[2]=%"PRIx64
+ " (detect reserve non-zero)", __func__,
+ inv_desc.val[3],
+ inv_desc.val[2]);
+ return false;
+ }
+
+ desc_type = inv_desc.val[0] & VTD_INV_DESC_TYPE;
/* FIXME: should update at first or at last? */
s->iq_last_desc_type = desc_type;
switch (desc_type) {
case VTD_INV_DESC_CC:
- trace_vtd_inv_desc("context-cache", inv_desc.hi, inv_desc.lo);
+ trace_vtd_inv_desc("context-cache", inv_desc.val[1], inv_desc.val[0]);
if (!vtd_process_context_cache_desc(s, &inv_desc)) {
return false;
}
break;
case VTD_INV_DESC_IOTLB:
- trace_vtd_inv_desc("iotlb", inv_desc.hi, inv_desc.lo);
+ trace_vtd_inv_desc("iotlb", inv_desc.val[1], inv_desc.val[0]);
if (!vtd_process_iotlb_desc(s, &inv_desc)) {
return false;
}
break;
case VTD_INV_DESC_WAIT:
- trace_vtd_inv_desc("wait", inv_desc.hi, inv_desc.lo);
+ trace_vtd_inv_desc("wait", inv_desc.val[1], inv_desc.val[0]);
if (!vtd_process_wait_desc(s, &inv_desc)) {
return false;
}
break;
case VTD_INV_DESC_IEC:
- trace_vtd_inv_desc("iec", inv_desc.hi, inv_desc.lo);
+ trace_vtd_inv_desc("iec", inv_desc.val[1], inv_desc.val[0]);
if (!vtd_process_inv_iec_desc(s, &inv_desc)) {
return false;
}
break;
case VTD_INV_DESC_DEVICE:
- trace_vtd_inv_desc("device", inv_desc.hi, inv_desc.lo);
+ trace_vtd_inv_desc("device", inv_desc.val[1], inv_desc.val[0]);
if (!vtd_process_device_iotlb_desc(s, &inv_desc)) {
return false;
}
break;
default:
- error_report_once("%s: invalid inv desc: hi=%"PRIx64", lo=%"PRIx64
- " (unknown type)", __func__, inv_desc.hi,
- inv_desc.lo);
+ error_report_once("%s: invalid inv desc: val[1]=%"PRIx64
+ ", val[0]=%"PRIx64
+ " (unknown type)", __func__, inv_desc.val[1],
+ inv_desc.val[0]);
return false;
}
s->iq_head++;
@@ -2525,7 +2558,7 @@ static void vtd_handle_iqt_write(IntelIOMMUState *s)
{
uint64_t val = vtd_get_quad_raw(s, DMAR_IQT_REG);
- s->iq_tail = VTD_IQT_QT(val);
+ s->iq_tail = VTD_IQT_QT(s->iq_dw, val);
trace_vtd_inv_qi_tail(s->iq_tail);
if (s->qi_enabled && !(vtd_get_long_raw(s, DMAR_FSTS_REG) & VTD_FSTS_IQE))
{
@@ -2794,6 +2827,11 @@ static void vtd_mem_write(void *opaque, hwaddr addr,
} else {
vtd_set_quad(s, addr, val);
}
+ if (vtd_ecap_smts(s)) {
+ s->iq_dw = val & VTD_IQA_DW_MASK;
+ } else {
+ s->iq_dw = 0;
+ }
break;
case DMAR_IQA_REG_HI:
@@ -3577,7 +3615,7 @@ static void vtd_init(IntelIOMMUState *s)
vtd_define_quad(s, DMAR_IQH_REG, 0, 0, 0);
vtd_define_quad(s, DMAR_IQT_REG, 0, 0x7fff0ULL, 0);
- vtd_define_quad(s, DMAR_IQA_REG, 0, 0xfffffffffffff007ULL, 0);
+ vtd_define_quad(s, DMAR_IQA_REG, 0, 0xfffffffffffff807ULL, 0);
vtd_define_long(s, DMAR_ICS_REG, 0, 0, 0x1UL);
vtd_define_long(s, DMAR_IECTL_REG, 0x80000000UL, 0x80000000UL, 0);
vtd_define_long(s, DMAR_IEDATA_REG, 0, 0xffffffffUL, 0);
diff --git a/hw/i386/intel_iommu_internal.h b/hw/i386/intel_iommu_internal.h
index 02674f9..2a753c5 100644
--- a/hw/i386/intel_iommu_internal.h
+++ b/hw/i386/intel_iommu_internal.h
@@ -190,6 +190,7 @@
#define VTD_ECAP_EIM (1ULL << 4)
#define VTD_ECAP_PT (1ULL << 6)
#define VTD_ECAP_MHMV (15ULL << 20)
+#define VTD_ECAP_SMTS (1ULL << 43)
/* CAP_REG */
/* (offset >> 4) << 24 */
@@ -218,11 +219,13 @@
#define VTD_CAP_SAGAW_48bit (0x4ULL << VTD_CAP_SAGAW_SHIFT)
/* IQT_REG */
-#define VTD_IQT_QT(val) (((val) >> 4) & 0x7fffULL)
+#define VTD_IQT_QT(dw_bit, val) (dw_bit ? (((val) >> 5) & 0x3fffULL) : \
+ (((val) >> 4) & 0x7fffULL))
/* IQA_REG */
#define VTD_IQA_IQA_MASK(aw) (VTD_HAW_MASK(aw) ^ 0xfffULL)
#define VTD_IQA_QS 0x7ULL
+#define VTD_IQA_DW_MASK 0x800
/* IQH_REG */
#define VTD_IQH_QH_SHIFT 4
@@ -321,8 +324,7 @@ typedef struct VTDInvDescIEC VTDInvDescIEC;
/* Queued Invalidation Descriptor */
union VTDInvDesc {
struct {
- uint64_t lo;
- uint64_t hi;
+ uint64_t val[4];
};
union {
VTDInvDescIEC iec;
diff --git a/include/hw/i386/intel_iommu.h b/include/hw/i386/intel_iommu.h
index ff13ff27..a5da139 100644
--- a/include/hw/i386/intel_iommu.h
+++ b/include/hw/i386/intel_iommu.h
@@ -230,6 +230,7 @@ struct IntelIOMMUState {
uint16_t iq_tail; /* Current invalidation queue tail */
dma_addr_t iq; /* Current invalidation queue pointer */
uint16_t iq_size; /* IQ Size in number of entries */
+ uint16_t iq_dw; /* IQ descriptor width */
bool qi_enabled; /* Set if the QI is enabled */
uint8_t iq_last_desc_type; /* The type of last completed descriptor */
--
1.9.1