git: 06e6ca6dd350 - main - dmar: Disable protected memory regions after initialization

From: Wojciech Macek <wma_at_FreeBSD.org>
Date: Fri, 29 Oct 2021 08:31:40 UTC
The branch main has been updated by wma:

URL: https://cgit.FreeBSD.org/src/commit/?id=06e6ca6dd3508866b584942a6a40739e09453cc6

commit 06e6ca6dd3508866b584942a6a40739e09453cc6
Author:     Kornel Duleba <mindal@semihalf.com>
AuthorDate: 2021-10-21 14:02:26 +0000
Commit:     Wojciech Macek <wma@FreeBSD.org>
CommitDate: 2021-10-29 08:08:25 +0000

    dmar: Disable protected memory regions after initialization
    
    Some BIOSes protect memory region they reside in by using DMAR to
    prevent devices from doing any DMA transactions to that part of RAM.
    AMI refers to this as "DMA Control Guarantee".
    Disable the protection when address translation is enabled.
    I stumbled upon this while investigation a failing coredump on a device
    which has this feature enabled.
    
    Sponsored by:           Stormshield
    Obtained from:          Semihalf
    Reviewed by:            kib
    Differential revision:  https://reviews.freebsd.org/D32591
---
 sys/x86/iommu/intel_ctx.c   |  4 ++++
 sys/x86/iommu/intel_dmar.h  |  1 +
 sys/x86/iommu/intel_drv.c   |  4 ++++
 sys/x86/iommu/intel_utils.c | 27 +++++++++++++++++++++++++++
 4 files changed, 36 insertions(+)

diff --git a/sys/x86/iommu/intel_ctx.c b/sys/x86/iommu/intel_ctx.c
index 7aedbf159ac7..815dc6146b00 100644
--- a/sys/x86/iommu/intel_ctx.c
+++ b/sys/x86/iommu/intel_ctx.c
@@ -631,6 +631,10 @@ dmar_get_ctx_for_dev1(struct dmar_unit *dmar, device_t dev, uint16_t rid,
 	 * to avoid unneeded command.
 	 */
 	if (enable && !rmrr_init && (dmar->hw_gcmd & DMAR_GCMD_TE) == 0) {
+		error = dmar_disable_protected_regions(dmar);
+		if (error != 0)
+			printf("dmar%d: Failed to disable protected regions\n",
+			    dmar->iommu.unit);
 		error = dmar_enable_translation(dmar);
 		if (error == 0) {
 			if (bootverbose) {
diff --git a/sys/x86/iommu/intel_dmar.h b/sys/x86/iommu/intel_dmar.h
index 0ad94dbf4123..b34505a4e5d0 100644
--- a/sys/x86/iommu/intel_dmar.h
+++ b/sys/x86/iommu/intel_dmar.h
@@ -227,6 +227,7 @@ int dmar_flush_write_bufs(struct dmar_unit *unit);
 void dmar_flush_pte_to_ram(struct dmar_unit *unit, dmar_pte_t *dst);
 void dmar_flush_ctx_to_ram(struct dmar_unit *unit, dmar_ctx_entry_t *dst);
 void dmar_flush_root_to_ram(struct dmar_unit *unit, dmar_root_entry_t *dst);
+int dmar_disable_protected_regions(struct dmar_unit *unit);
 int dmar_enable_translation(struct dmar_unit *unit);
 int dmar_disable_translation(struct dmar_unit *unit);
 int dmar_load_irt_ptr(struct dmar_unit *unit);
diff --git a/sys/x86/iommu/intel_drv.c b/sys/x86/iommu/intel_drv.c
index 0b470d7bbf7a..66849dd43053 100644
--- a/sys/x86/iommu/intel_drv.c
+++ b/sys/x86/iommu/intel_drv.c
@@ -1065,6 +1065,10 @@ dmar_instantiate_rmrr_ctxs(struct iommu_unit *unit)
 		KASSERT((dmar->hw_gcmd & DMAR_GCMD_TE) == 0,
 	    ("dmar%d: RMRR not handled but translation is already enabled",
 		    dmar->iommu.unit));
+		error = dmar_disable_protected_regions(dmar);
+		if (error != 0)
+			printf("dmar%d: Failed to disable protected regions\n",
+			    dmar->iommu.unit);
 		error = dmar_enable_translation(dmar);
 		if (bootverbose) {
 			if (error == 0) {
diff --git a/sys/x86/iommu/intel_utils.c b/sys/x86/iommu/intel_utils.c
index 152c7cac3a7d..4511cceb6444 100644
--- a/sys/x86/iommu/intel_utils.c
+++ b/sys/x86/iommu/intel_utils.c
@@ -489,6 +489,33 @@ dmar_flush_write_bufs(struct dmar_unit *unit)
 	return (error);
 }
 
+/*
+ * Some BIOSes protect memory region they reside in by using DMAR to
+ * prevent devices from doing any DMA transactions to that part of RAM.
+ * AMI refers to this as "DMA Control Guarantee".
+ * We need to disable this when address translation is enabled.
+ */
+int
+dmar_disable_protected_regions(struct dmar_unit *unit)
+{
+	uint32_t reg;
+	int error;
+
+	DMAR_ASSERT_LOCKED(unit);
+
+	/* Check if we support the feature. */
+	if ((unit->hw_cap & (DMAR_CAP_PLMR | DMAR_CAP_PHMR)) == 0)
+		return (0);
+
+	reg = dmar_read4(unit, DMAR_PMEN_REG);
+	reg &= ~DMAR_PMEN_EPM;
+	dmar_write4(unit, DMAR_PMEN_REG, reg);
+	DMAR_WAIT_UNTIL(((dmar_read4(unit, DMAR_PMEN_REG) & DMAR_PMEN_PRS)
+	    != 0));
+
+	return (error);
+}
+
 int
 dmar_enable_translation(struct dmar_unit *unit)
 {