From: CLEMENT MATHIEU--DRIF <[email protected]>
Signed-off-by: Clement Mathieu--Drif <[email protected]>
Reviewed-by: Michael S. Tsirkin <[email protected]>
Message-ID: <[email protected]>
Signed-off-by: Michael S. Tsirkin <[email protected]>
---
hw/i386/intel_iommu.c | 55 +++++++++++++++++++++++++++++++++++++++++++
1 file changed, 55 insertions(+)
diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c
index 4e7ad3a290..d952ec1428 100644
--- a/hw/i386/intel_iommu.c
+++ b/hw/i386/intel_iommu.c
@@ -3386,6 +3386,27 @@ static void vtd_handle_iectl_write(IntelIOMMUState *s)
}
}
+static void vtd_handle_prs_write(IntelIOMMUState *s)
+{
+ uint32_t prs = vtd_get_long_raw(s, DMAR_PRS_REG);
+ if (!(prs & VTD_PR_STATUS_PPR) && !(prs & VTD_PR_STATUS_PRO)) {
+ vtd_set_clear_mask_long(s, DMAR_PECTL_REG, VTD_PR_PECTL_IP, 0);
+ }
+}
+
+static void vtd_handle_pectl_write(IntelIOMMUState *s)
+{
+ uint32_t pectl = vtd_get_long_raw(s, DMAR_PECTL_REG);
+ if ((pectl & VTD_PR_PECTL_IP) && !(pectl & VTD_PR_PECTL_IM)) {
+ /*
+ * If IP field was 1 when software clears the IM field,
+ * the interrupt is generated along with clearing the IP field.
+ */
+ vtd_set_clear_mask_long(s, DMAR_PECTL_REG, VTD_PR_PECTL_IP, 0);
+ vtd_generate_interrupt(s, DMAR_PEADDR_REG, DMAR_PEDATA_REG);
+ }
+}
+
static uint64_t vtd_mem_read(void *opaque, hwaddr addr, unsigned size)
{
IntelIOMMUState *s = opaque;
@@ -3428,6 +3449,11 @@ static uint64_t vtd_mem_read(void *opaque, hwaddr addr,
unsigned size)
val = s->iq >> 32;
break;
+ case DMAR_PEUADDR_REG:
+ assert(size == 4);
+ val = vtd_get_long_raw(s, DMAR_PEUADDR_REG);
+ break;
+
default:
if (size == 4) {
val = vtd_get_long(s, addr);
@@ -3491,6 +3517,11 @@ static void vtd_mem_write(void *opaque, hwaddr addr,
vtd_handle_iotlb_write(s);
break;
+ case DMAR_PEUADDR_REG:
+ assert(size == 4);
+ vtd_set_long(s, addr, val);
+ break;
+
/* Invalidate Address Register, 64-bit */
case DMAR_IVA_REG:
if (size == 4) {
@@ -3671,6 +3702,18 @@ static void vtd_mem_write(void *opaque, hwaddr addr,
vtd_set_long(s, addr, val);
break;
+ case DMAR_PRS_REG:
+ assert(size == 4);
+ vtd_set_long(s, addr, val);
+ vtd_handle_prs_write(s);
+ break;
+
+ case DMAR_PECTL_REG:
+ assert(size == 4);
+ vtd_set_long(s, addr, val);
+ vtd_handle_pectl_write(s);
+ break;
+
default:
if (size == 4) {
vtd_set_long(s, addr, val);
@@ -4722,6 +4765,18 @@ static void vtd_init(IntelIOMMUState *s)
* Interrupt remapping registers.
*/
vtd_define_quad(s, DMAR_IRTA_REG, 0, 0xfffffffffffff80fULL, 0);
+
+ /* Page request registers */
+ if (s->ecap & VTD_ECAP_PRS) {
+ vtd_define_quad(s, DMAR_PQH_REG, 0, 0x7ffe0ULL, 0);
+ vtd_define_quad(s, DMAR_PQT_REG, 0, 0x7ffe0ULL, 0);
+ vtd_define_quad(s, DMAR_PQA_REG, 0, 0xfffffffffffff007ULL, 0);
+ vtd_define_long(s, DMAR_PRS_REG, 0, 0, 0x3UL);
+ vtd_define_long(s, DMAR_PECTL_REG, 0, 0x80000000UL, 0);
+ vtd_define_long(s, DMAR_PEDATA_REG, 0, 0xffffUL, 0);
+ vtd_define_long(s, DMAR_PEADDR_REG, 0, 0xfffffffcUL, 0);
+ vtd_define_long(s, DMAR_PEUADDR_REG, 0, 0xffffffffUL, 0);
+ }
}
/* Should not reset address_spaces when reset because devices will still use
--
MST