mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH v1 0/2] x86/tboot: Add Intel TXT Protection Regions (TPR) support
@ 2026-06-03 11:44 Michal Camacho Romero
  2026-06-03 11:44 ` [PATCH v1 1/2] x86/tboot: Add support for parsing DTPR table and disabling TPRs Michal Camacho Romero
  2026-06-03 11:45 ` [PATCH v1 2/2] iommu/vt-d: Disable PMRs and skip force-IOMMU when TXT TPRs are active Michal Camacho Romero
  0 siblings, 2 replies; 15+ messages in thread
From: Michal Camacho Romero @ 2026-06-03 11:44 UTC (permalink / raw)
  To: Lu Baolu, Ning Sun, Thomas Gleixner
  Cc: x86, iommu, tboot-devel, linux-kernel, Mateusz Mowka,
	Adam Pawlicki, Pawel Randzio, Michal Camacho Romero

Intel TXT Protection Regions (TPRs) are a new hardware mechanism for
DMA protection that replaces Protected Memory Regions
(PMRs). TPRs are configured by the SINIT ACM and managed through the
DTPR table in the TXT heap's extended data elements.

This series adds kernel support for detecting and disabling TPRs during
early boot, allowing the kernel to take over DMA protection management
via the IOMMU.

Patch No.1 adds the TXT heap parsing logic to locate the DTPR table and
disable each TPR instance by setting the enable bit (BIT14 in TPRn_BASE
register).

Patch No.2 integrates TPR detection into the IOMMU initialization path:
skipping force-IOMMU, when TPRs provide DMA protection and tearing down
redundant PMRs.

Tested on Intel platforms with SINIT ACM supporting ACPI DTPR tables.

Documentation:
- Intel TXT DMA Protection Ranges, rev 0.73
  https://uefi.org/sites/default/files/resources/633933_Intel_TXT_DMA_Protection_Ranges_rev_0p73.pdf
- Intel TXT MLE Developer's Guide, rev 017
  https://cdrdv2-public.intel.com/315168/315168_TXT_MLE_DG_rev_017_7.pdf

Michal Camacho Romero (2):
  x86/tboot: Add support for parsing DTPR table and disabling TPRs
  iommu/vt-d: Disable PMRs and skip force-IOMMU when TXT TPRs are active

 arch/x86/kernel/tboot.c     | 146 ++++++++++++++++++++++++++++++++----
 drivers/iommu/intel/dmar.c  |  12 +++
 drivers/iommu/intel/iommu.c |   8 +-
 include/linux/tboot.h       |  10 +++
 4 files changed, 160 insertions(+), 16 deletions(-)

-- 
2.53.0

---------------------------------------------------------------------
Intel Technology Poland sp. z o.o.
ul. Slowackiego 173 | 80-298 Gdansk | Sad Rejonowy Gdansk Polnoc | VII Wydzial Gospodarczy Krajowego Rejestru Sadowego - KRS 101882 | NIP 957-07-52-316 | Kapital zakladowy 200.000 PLN.
Spolka oswiadcza, ze posiada status duzego przedsiebiorcy w rozumieniu ustawy z dnia 8 marca 2013 r. o przeciwdzialaniu nadmiernym opoznieniom w transakcjach handlowych.

Ta wiadomosc wraz z zalacznikami jest przeznaczona dla okreslonego adresata i moze zawierac informacje poufne. W razie przypadkowego otrzymania tej wiadomosci, prosimy o powiadomienie nadawcy oraz trwale jej usuniecie; jakiekolwiek przegladanie lub rozpowszechnianie jest zabronione.
This e-mail and any attachments may contain confidential material for the sole use of the intended recipient(s). If you are not the intended recipient, please contact the sender and delete all copies; any review or distribution by others is strictly prohibited.


^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH v1 1/2] x86/tboot: Add support for parsing DTPR table and disabling TPRs
  2026-06-03 11:44 [PATCH v1 0/2] x86/tboot: Add Intel TXT Protection Regions (TPR) support Michal Camacho Romero
@ 2026-06-03 11:44 ` Michal Camacho Romero
  2026-09-14 13:19   ` [PATCH v2 " Michal Camacho Romero
  2026-06-03 11:45 ` [PATCH v1 2/2] iommu/vt-d: Disable PMRs and skip force-IOMMU when TXT TPRs are active Michal Camacho Romero
  1 sibling, 1 reply; 15+ messages in thread
From: Michal Camacho Romero @ 2026-06-03 11:44 UTC (permalink / raw)
  To: Lu Baolu, Ning Sun, Thomas Gleixner
  Cc: x86, iommu, tboot-devel, linux-kernel, Mateusz Mowka,
	Adam Pawlicki, Pawel Randzio, Michal Camacho Romero

Add functions to locate and parse the DMA TXT Protection Ranges (DTPR)
table from the TXT heap's SinitMleData extended data elements (requires
SINIT MLE version >= 9).

* tboot_get_dtpr_table() - function walks through the TXT heap to find
                           the DTPR extended data element
                           (type HEAP_EXTDATA_TYPE_DTPR) and returns
                           pointer to the DTPR table.

* tboot_parse_dtpr_table() - function iterates over TPR instances and
                             disables each TPR region by setting bit 4
                             in the TPRn_BASE register via MMIO.

Using these functions will allow the kernel to deactivate SINIT
ACM-established TPRs prior to the Linux OS launch.

Link: https://uefi.org/sites/default/files/resources/633933_Intel_TXT_DMA_Protection_Ranges_rev_0p73.pdf
Link: https://cdrdv2-public.intel.com/315168/315168_TXT_MLE_DG_rev_017_7.pdf
Signed-off-by: Michal Camacho Romero <michal.camacho.romero@intel.com>
---
 arch/x86/kernel/tboot.c | 146 +++++++++++++++++++++++++++++++++++-----
 include/linux/tboot.h   |  10 +++
 2 files changed, 141 insertions(+), 15 deletions(-)

diff --git a/arch/x86/kernel/tboot.c b/arch/x86/kernel/tboot.c
index 46b8f1f16676..8825e5ee916c 100644
--- a/arch/x86/kernel/tboot.c
+++ b/arch/x86/kernel/tboot.c
@@ -18,6 +18,7 @@
 #include <linux/mm.h>
 #include <linux/tboot.h>
 #include <linux/debugfs.h>
+#include <acpi/actbl1.h>
 
 #include <asm/realmode.h>
 #include <asm/processor.h>
@@ -453,22 +454,30 @@ struct sha1_hash {
 	u8 hash[SHA1_SIZE];
 };
 
+struct heap_ext_data_elt {
+	u32 type;
+	u32 size;
+	u8  data[];
+} __packed;
+
 struct sinit_mle_data {
-	u32               version;             /* currently 6 */
-	struct sha1_hash  bios_acm_id;
-	u32               edx_senter_flags;
-	u64               mseg_valid;
-	struct sha1_hash  sinit_hash;
-	struct sha1_hash  mle_hash;
-	struct sha1_hash  stm_hash;
-	struct sha1_hash  lcp_policy_hash;
-	u32               lcp_policy_control;
-	u32               rlp_wakeup_addr;
-	u32               reserved;
-	u32               num_mdrs;
-	u32               mdrs_off;
-	u32               num_vtd_dmars;
-	u32               vtd_dmars_off;
+	u32                      version;             /* currently 9 */
+	struct sha1_hash         bios_acm_id;
+	u32                      edx_senter_flags;
+	u64                      mseg_valid;
+	struct sha1_hash         sinit_hash;
+	struct sha1_hash         mle_hash;
+	struct sha1_hash         stm_hash;
+	struct sha1_hash         lcp_policy_hash;
+	u32                      lcp_policy_control;
+	u32                      rlp_wakeup_addr;
+	u32                      reserved;
+	u32                      num_mdrs;
+	u32                      mdrs_off;
+	u32                      num_vtd_dmars;
+	u32                      vtd_dmars_off;
+	u32                      proc_scrtm_status; /* version 8 or later only*/
+	struct heap_ext_data_elt ext_data_elts[];
 } __packed;
 
 struct acpi_table_header *tboot_get_dmar_table(struct acpi_table_header *dmar_tbl)
@@ -514,3 +523,110 @@ struct acpi_table_header *tboot_get_dmar_table(struct acpi_table_header *dmar_tb
 
 	return dmar_tbl;
 }
+
+struct acpi_table_dtpr *tboot_get_dtpr_table(void **heap_base)
+{
+	void *heap_ptr, *config;
+	struct sinit_mle_data *sinit_mle;
+	struct heap_ext_data_elt *elt;
+	u64 sinit_mle_size;
+
+	if (!heap_base)
+		return NULL;
+
+	if (!tboot_enabled())
+		return NULL;
+	/*
+	 * ACPI tables may not be DMA protected by tboot, so use DMAR copy
+	 * SINIT saved in SinitMleData in TXT heap (which is DMA protected)
+	 */
+
+	/* map config space in order to get heap addr */
+	config = ioremap(TXT_PUB_CONFIG_REGS_BASE, NR_TXT_CONFIG_PAGES *
+			 PAGE_SIZE);
+	if (!config)
+		return NULL;
+
+	/* now map TXT heap */
+	*heap_base = ioremap(*(u64 *)(config + TXTCR_HEAP_BASE),
+			    *(u64 *)(config + TXTCR_HEAP_SIZE));
+	iounmap(config);
+
+	if (!(*heap_base))
+		return NULL;
+
+	/* walk heap to SinitMleData */
+	/* skip BiosData */
+	heap_ptr = *heap_base + *(u64 *) (*heap_base);
+	/* skip OsMleData */
+	heap_ptr += *(u64 *)heap_ptr;
+	/* skip OsSinitData */
+	heap_ptr += *(u64 *)heap_ptr;
+	/* now points to SinitMleDataSize; set to SinitMleData */
+	sinit_mle_size = *(u64 *)heap_ptr;
+	heap_ptr += sizeof(u64);
+
+	sinit_mle = (struct sinit_mle_data *)heap_ptr;
+	if (sinit_mle->version < 9) {
+		iounmap(*heap_base);
+		return NULL;
+	}
+
+	elt = sinit_mle->ext_data_elts;
+	while (elt->type != HEAP_EXTDATA_TYPE_DTPR &&
+		   elt->type != HEAP_EXTDATA_TYPE_END) {
+		elt = (void *)elt + elt->size;
+		if ((u64)elt > (u64)sinit_mle + sinit_mle_size) {
+			iounmap(*heap_base);
+			return NULL;
+		}
+	}
+
+	return (struct acpi_table_dtpr *)elt->data;
+}
+
+static bool tboot_tpr_enabled = false;
+void tboot_parse_dtpr_table(struct acpi_table_dtpr *dtpr)
+{
+	struct acpi_tpr_instance *tpr_inst;
+	struct acpi_tpr_array    *tpr_arr;
+	u32 *instance_cnt;
+	u64 *base;
+	u32 i, j;
+
+	if (!tboot_enabled())
+		return;
+
+	tboot_tpr_enabled = true;
+	instance_cnt = (u32*)(&dtpr->ins_cnt);
+	tpr_inst = (struct acpi_tpr_instance *)(instance_cnt + 1);
+	for (i = 0; i < *instance_cnt; ++i) {
+		for (j = 0; j < tpr_inst->tpr_cnt; ++j) {
+			tpr_arr =  (struct acpi_tpr_array*)((u8*) tpr_inst +
+			            sizeof(struct acpi_tpr_instance) +
+			            j * sizeof(struct acpi_tpr_array));
+
+			base = ioremap(tpr_arr->base, 16);
+			if (!base) {
+				pr_warn("TPR Instance %d, TPR No.%d disabling failure.\n", i, j);
+				continue;
+			}
+
+			pr_info("TPR instance %d, TPR %d:base %llx limit %llx\n", i, j,
+			        readq(base), readq(base + 1));
+			writeq(readq(base) | BIT(4), base);
+			iounmap(base);
+		}
+
+		tpr_inst = (struct acpi_tpr_instance *)((u8*)tpr_inst +
+		            sizeof(*tpr_inst) + j * sizeof(struct acpi_tpr_array));
+	}
+
+	if (tboot_tpr_enabled)
+		pr_debug("TPR protection detected, PMR will be disabled\n");
+}
+
+bool tboot_is_tpr_enabled(void)
+{
+	return tboot_tpr_enabled;
+}
diff --git a/include/linux/tboot.h b/include/linux/tboot.h
index d2279160ef39..39fb2e3ba80b 100644
--- a/include/linux/tboot.h
+++ b/include/linux/tboot.h
@@ -24,6 +24,10 @@ enum {
 #include <linux/acpi.h>
 /* used to communicate between tboot and the launched kernel */
 
+/*TXT Extended Data Element Types*/
+#define HEAP_EXTDATA_TYPE_END   0
+#define HEAP_EXTDATA_TYPE_DTPR 14
+
 #define TB_KEY_SIZE             64   /* 512 bits */
 
 #define MAX_TB_MAC_REGIONS      32
@@ -126,6 +130,9 @@ extern void tboot_probe(void);
 extern void tboot_shutdown(u32 shutdown_type);
 extern struct acpi_table_header *tboot_get_dmar_table(
 				      struct acpi_table_header *dmar_tbl);
+extern struct acpi_table_dtpr *tboot_get_dtpr_table(void **);
+extern void tboot_parse_dtpr_table(struct acpi_table_dtpr *);
+extern bool tboot_is_tpr_enabled(void);
 
 #else
 
@@ -135,6 +142,9 @@ extern struct acpi_table_header *tboot_get_dmar_table(
 #define tboot_sleep(sleep_state, pm1a_control, pm1b_control)	\
 					do { } while (0)
 #define tboot_get_dmar_table(dmar_tbl)	(dmar_tbl)
+#define tboot_get_dtpr_table(txt_heap) NULL
+#define tboot_parse_dtpr_table(dtpr) do { } while (0)
+#define tboot_is_tpr_enabled() 0
 
 #endif /* !CONFIG_INTEL_TXT */
 
-- 
2.53.0

---------------------------------------------------------------------
Intel Technology Poland sp. z o.o.
ul. Slowackiego 173 | 80-298 Gdansk | Sad Rejonowy Gdansk Polnoc | VII Wydzial Gospodarczy Krajowego Rejestru Sadowego - KRS 101882 | NIP 957-07-52-316 | Kapital zakladowy 200.000 PLN.
Spolka oswiadcza, ze posiada status duzego przedsiebiorcy w rozumieniu ustawy z dnia 8 marca 2013 r. o przeciwdzialaniu nadmiernym opoznieniom w transakcjach handlowych.

Ta wiadomosc wraz z zalacznikami jest przeznaczona dla okreslonego adresata i moze zawierac informacje poufne. W razie przypadkowego otrzymania tej wiadomosci, prosimy o powiadomienie nadawcy oraz trwale jej usuniecie; jakiekolwiek przegladanie lub rozpowszechnianie jest zabronione.
This e-mail and any attachments may contain confidential material for the sole use of the intended recipient(s). If you are not the intended recipient, please contact the sender and delete all copies; any review or distribution by others is strictly prohibited.


^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH v1 2/2] iommu/vt-d: Disable PMRs and skip force-IOMMU when TXT TPRs are active
  2026-06-03 11:44 [PATCH v1 0/2] x86/tboot: Add Intel TXT Protection Regions (TPR) support Michal Camacho Romero
  2026-06-03 11:44 ` [PATCH v1 1/2] x86/tboot: Add support for parsing DTPR table and disabling TPRs Michal Camacho Romero
@ 2026-06-03 11:45 ` Michal Camacho Romero
  2026-06-11  8:49   ` Baolu Lu
                     ` (2 more replies)
  1 sibling, 3 replies; 15+ messages in thread
From: Michal Camacho Romero @ 2026-06-03 11:45 UTC (permalink / raw)
  To: Lu Baolu, Ning Sun, Thomas Gleixner
  Cc: x86, iommu, tboot-devel, linux-kernel, Mateusz Mowka,
	Adam Pawlicki, Pawel Randzio, Michal Camacho Romero

When Intel TXT Protection Regions (TPRs) are present in the DTPR table,
hardware-level DMA protection is already enforced by the SINIT ACM.
In this case:

- Skip forcing IOMMU enablement in tboot_force_iommu(), since TPRs
  already provide DMA protection.
- Tear down PMRs during intel_iommu_init() when TPRs are active,
  while PMRs are redundant with TPR-based protection.
- Call tboot_parse_dtpr_table() from parse_dmar_table() to disable
  TPR regions early, allowing the kernel to manage DMA protection
  prior to the OS boot.

Link: https://uefi.org/sites/default/files/resources/633933_Intel_TXT_DMA_Protection_Ranges_rev_0p73.pdf
Link: https://cdrdv2-public.intel.com/315168/315168_TXT_MLE_DG_rev_017_7.pdf
Signed-off-by: Michal Camacho Romero <michal.camacho.romero@intel.com>
---
 drivers/iommu/intel/dmar.c  | 12 ++++++++++++
 drivers/iommu/intel/iommu.c |  8 +++++++-
 2 files changed, 19 insertions(+), 1 deletion(-)

diff --git a/drivers/iommu/intel/dmar.c b/drivers/iommu/intel/dmar.c
index d33c119a935e..3ab09117c79e 100644
--- a/drivers/iommu/intel/dmar.c
+++ b/drivers/iommu/intel/dmar.c
@@ -635,6 +635,8 @@ static int __init
 parse_dmar_table(void)
 {
 	struct acpi_table_dmar *dmar;
+	struct acpi_table_dtpr *dtpr;
+	void                   *txt_heap;
 	int drhd_count = 0;
 	int ret;
 	struct dmar_res_callback cb = {
@@ -670,6 +672,16 @@ parse_dmar_table(void)
 		return -EINVAL;
 	}
 
+	dtpr = tboot_get_dtpr_table(&txt_heap);
+	if (dtpr) {
+		/* TPR is enabled
+		 * This will also tell not to establish IOMMU PMRs
+		 */
+		tboot_parse_dtpr_table(dtpr);
+		iounmap(txt_heap);
+	}
+
+	txt_heap = NULL;
 	pr_info("Host address width %d\n", dmar->width + 1);
 	ret = dmar_walk_dmar_table(dmar, &cb);
 	if (ret == 0 && drhd_count == 0)
diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c
index 4d0e65bc131d..486693a13dc6 100644
--- a/drivers/iommu/intel/iommu.c
+++ b/drivers/iommu/intel/iommu.c
@@ -2540,6 +2540,12 @@ static __init int tboot_force_iommu(void)
 	if (!tboot_enabled())
 		return 0;
 
+	/* If TPR is enabled we don't need to force IOMMU,
+	 * TPR set by SINIT ACM will take care of DMA protection
+	 */
+	if (tboot_is_tpr_enabled())
+		return 0;
+
 	if (no_iommu || dmar_disabled)
 		pr_warn("Forcing Intel-IOMMU to enabled\n");
 
@@ -2597,7 +2603,7 @@ int __init intel_iommu_init(void)
 		 * calling SENTER, but the kernel is expected to reset/tear
 		 * down the PMRs.
 		 */
-		if (intel_iommu_tboot_noforce) {
+		if (intel_iommu_tboot_noforce || tboot_is_tpr_enabled()) {
 			for_each_iommu(iommu, drhd)
 				iommu_disable_protect_mem_regions(iommu);
 		}
-- 
2.53.0

---------------------------------------------------------------------
Intel Technology Poland sp. z o.o.
ul. Slowackiego 173 | 80-298 Gdansk | Sad Rejonowy Gdansk Polnoc | VII Wydzial Gospodarczy Krajowego Rejestru Sadowego - KRS 101882 | NIP 957-07-52-316 | Kapital zakladowy 200.000 PLN.
Spolka oswiadcza, ze posiada status duzego przedsiebiorcy w rozumieniu ustawy z dnia 8 marca 2013 r. o przeciwdzialaniu nadmiernym opoznieniom w transakcjach handlowych.

Ta wiadomosc wraz z zalacznikami jest przeznaczona dla okreslonego adresata i moze zawierac informacje poufne. W razie przypadkowego otrzymania tej wiadomosci, prosimy o powiadomienie nadawcy oraz trwale jej usuniecie; jakiekolwiek przegladanie lub rozpowszechnianie jest zabronione.
This e-mail and any attachments may contain confidential material for the sole use of the intended recipient(s). If you are not the intended recipient, please contact the sender and delete all copies; any review or distribution by others is strictly prohibited.


^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH v1 2/2] iommu/vt-d: Disable PMRs and skip force-IOMMU when TXT TPRs are active
  2026-06-03 11:45 ` [PATCH v1 2/2] iommu/vt-d: Disable PMRs and skip force-IOMMU when TXT TPRs are active Michal Camacho Romero
@ 2026-06-11  8:49   ` Baolu Lu
  2026-08-07  9:16   ` [PATCH v2 " Michal Camacho Romero
  2026-08-07 10:14   ` Michal Camacho Romero
  2 siblings, 0 replies; 15+ messages in thread
From: Baolu Lu @ 2026-06-11  8:49 UTC (permalink / raw)
  To: Michal Camacho Romero, Ning Sun, Thomas Gleixner
  Cc: baolu.lu, x86, iommu, tboot-devel, linux-kernel, Mateusz Mowka,
	Adam Pawlicki, Pawel Randzio

On 6/3/2026 7:45 PM, Michal Camacho Romero wrote:
> When Intel TXT Protection Regions (TPRs) are present in the DTPR table,
> hardware-level DMA protection is already enforced by the SINIT ACM.
> In this case:
> 
> - Skip forcing IOMMU enablement in tboot_force_iommu(), since TPRs
>    already provide DMA protection.
> - Tear down PMRs during intel_iommu_init() when TPRs are active,
>    while PMRs are redundant with TPR-based protection.
> - Call tboot_parse_dtpr_table() from parse_dmar_table() to disable
>    TPR regions early, allowing the kernel to manage DMA protection
>    prior to the OS boot.
> 
> Link: https://uefi.org/sites/default/files/resources/633933_Intel_TXT_DMA_Protection_Ranges_rev_0p73.pdf
> Link: https://cdrdv2-public.intel.com/315168/315168_TXT_MLE_DG_rev_017_7.pdf
> Signed-off-by: Michal Camacho Romero <michal.camacho.romero@intel.com>
> ---
>   drivers/iommu/intel/dmar.c  | 12 ++++++++++++
>   drivers/iommu/intel/iommu.c |  8 +++++++-
>   2 files changed, 19 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/iommu/intel/dmar.c b/drivers/iommu/intel/dmar.c
> index d33c119a935e..3ab09117c79e 100644
> --- a/drivers/iommu/intel/dmar.c
> +++ b/drivers/iommu/intel/dmar.c
> @@ -635,6 +635,8 @@ static int __init
>   parse_dmar_table(void)
>   {
>   	struct acpi_table_dmar *dmar;
> +	struct acpi_table_dtpr *dtpr;
> +	void                   *txt_heap;

Please avoid using extra whitespace before the variable name. Just use:

	void *txt_heap;

>   	int drhd_count = 0;
>   	int ret;
>   	struct dmar_res_callback cb = {
> @@ -670,6 +672,16 @@ parse_dmar_table(void)
>   		return -EINVAL;
>   	}
>   
> +	dtpr = tboot_get_dtpr_table(&txt_heap);
> +	if (dtpr) {
> +		/* TPR is enabled
> +		 * This will also tell not to establish IOMMU PMRs
> +		 */

Please use the standard kernel multiple-line comment format:

	/*
	 * TPR is enabled. This will also tell not to establish IOMMU
	 * PMRs.
	 */

> +		tboot_parse_dtpr_table(dtpr);
> +		iounmap(txt_heap);
> +	}
> +
> +	txt_heap = NULL;
>   	pr_info("Host address width %d\n", dmar->width + 1);
>   	ret = dmar_walk_dmar_table(dmar, &cb);
>   	if (ret == 0 && drhd_count == 0)
> diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c
> index 4d0e65bc131d..486693a13dc6 100644
> --- a/drivers/iommu/intel/iommu.c
> +++ b/drivers/iommu/intel/iommu.c
> @@ -2540,6 +2540,12 @@ static __init int tboot_force_iommu(void)
>   	if (!tboot_enabled())
>   		return 0;
>   
> +	/* If TPR is enabled we don't need to force IOMMU,
> +	 * TPR set by SINIT ACM will take care of DMA protection
> +	 */

Ditto.

> +	if (tboot_is_tpr_enabled())
> +		return 0;
> +
>   	if (no_iommu || dmar_disabled)
>   		pr_warn("Forcing Intel-IOMMU to enabled\n");
>   
> @@ -2597,7 +2603,7 @@ int __init intel_iommu_init(void)
>   		 * calling SENTER, but the kernel is expected to reset/tear
>   		 * down the PMRs.
>   		 */
> -		if (intel_iommu_tboot_noforce) {
> +		if (intel_iommu_tboot_noforce || tboot_is_tpr_enabled()) {
>   			for_each_iommu(iommu, drhd)
>   				iommu_disable_protect_mem_regions(iommu);
>   		}

With these nits fixed:

Reviewed-by: Lu Baolu <baolu.lu@linux.intel.com>

^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH v2 2/2] iommu/vt-d: Disable PMRs and skip force-IOMMU when TXT TPRs are active
  2026-06-03 11:45 ` [PATCH v1 2/2] iommu/vt-d: Disable PMRs and skip force-IOMMU when TXT TPRs are active Michal Camacho Romero
  2026-06-11  8:49   ` Baolu Lu
@ 2026-08-07  9:16   ` Michal Camacho Romero
  2026-08-07 10:14   ` Michal Camacho Romero
  2 siblings, 0 replies; 15+ messages in thread
From: Michal Camacho Romero @ 2026-08-07  9:16 UTC (permalink / raw)
  To: Baolu Lu, Ning Sun, Thomas Gleixner
  Cc: Michal Camacho Romero, x86, iommu, tboot-devel, linux-kernel,
	Mateusz Mowka, Adam Pawlicki, Pawel Randzio

When Intel TXT Protection Regions (TPRs) are present in the DTPR table,
hardware-level DMA protection is already enforced by the SINIT ACM.
In this case:

- Skip forcing IOMMU enablement in tboot_force_iommu(), since TPRs
  already provide DMA protection.
- Tear down PMRs during intel_iommu_init() when TPRs are active,
  while PMRs are redundant with TPR-based protection.
- Call tboot_parse_dtpr_table() from parse_dmar_table() to disable
  TPR regions early, allowing the kernel to manage DMA protection
  prior to the OS boot.

Link: https://uefi.org/sites/default/files/resources/633933_Intel_TXT_DMA_Protection_Ranges_rev_0p73.pdf
Link: https://cdrdv2-public.intel.com/315168/315168_TXT_MLE_DG_rev_017_7.pdf
Reviewed-by: Lu Baolu <baolu.lu@linux.intel.com>
Signed-off-by: Michal Camacho Romero <michal.camacho.romero@intel.com>
---
 drivers/iommu/intel/dmar.c  | 13 +++++++++++++
 drivers/iommu/intel/iommu.c |  9 ++++++++-
 2 files changed, 21 insertions(+), 1 deletion(-)

diff --git a/drivers/iommu/intel/dmar.c b/drivers/iommu/intel/dmar.c
index 767ec092accd..17c26d222253 100644
--- a/drivers/iommu/intel/dmar.c
+++ b/drivers/iommu/intel/dmar.c
@@ -635,6 +635,8 @@ static int __init
 parse_dmar_table(void)
 {
 	struct acpi_table_dmar *dmar;
+	struct acpi_table_dtpr *dtpr;
+	void *txt_heap;
 	int drhd_count = 0;
 	int ret;
 	struct dmar_res_callback cb = {
@@ -670,6 +672,17 @@ parse_dmar_table(void)
 		return -EINVAL;
 	}
 
+	dtpr = tboot_get_dtpr_table(&txt_heap);
+	if (dtpr) {
+		/*
+		 * TPR is enabled. This will also tell not to establish IOMMU
+		 * PMRs.
+		 */
+		tboot_parse_dtpr_table(dtpr);
+		iounmap(txt_heap);
+	}
+
+	txt_heap = NULL;
 	pr_info("Host address width %d\n", dmar->width + 1);
 	ret = dmar_walk_dmar_table(dmar, &cb);
 	if (ret == 0 && drhd_count == 0)
diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c
index 849d06dfe1ae..5bc6f2dd25c4 100644
--- a/drivers/iommu/intel/iommu.c
+++ b/drivers/iommu/intel/iommu.c
@@ -2543,6 +2543,13 @@ static __init int tboot_force_iommu(void)
 	if (!tboot_enabled())
 		return 0;
 
+	/*
+	 * If TPR is enabled we don't need to force IOMMU, TPR set by SINIT
+	 * ACM will take care of DMA protection.
+	 */
+	if (tboot_is_tpr_enabled())
+		return 0;
+
 	if (no_iommu || dmar_disabled)
 		pr_warn("Forcing Intel-IOMMU to enabled\n");
 
@@ -2600,7 +2607,7 @@ int __init intel_iommu_init(void)
 		 * calling SENTER, but the kernel is expected to reset/tear
 		 * down the PMRs.
 		 */
-		if (intel_iommu_tboot_noforce) {
+		if (intel_iommu_tboot_noforce || tboot_is_tpr_enabled()) {
 			for_each_iommu(iommu, drhd)
 				iommu_disable_protect_mem_regions(iommu);
 		}
-- 
2.55.0

---------------------------------------------------------------------
Intel Technology Poland sp. z o.o.
ul. Slowackiego 173 | 80-298 Gdansk | Sad Rejonowy Gdansk Polnoc | VII Wydzial Gospodarczy Krajowego Rejestru Sadowego - KRS 101882 | NIP 957-07-52-316 | Kapital zakladowy 200.000 PLN.
Spolka oswiadcza, ze posiada status duzego przedsiebiorcy w rozumieniu ustawy z dnia 8 marca 2013 r. o przeciwdzialaniu nadmiernym opoznieniom w transakcjach handlowych.

Ta wiadomosc wraz z zalacznikami jest przeznaczona dla okreslonego adresata i moze zawierac informacje poufne. W razie przypadkowego otrzymania tej wiadomosci, prosimy o powiadomienie nadawcy oraz trwale jej usuniecie; jakiekolwiek przegladanie lub rozpowszechnianie jest zabronione.
This e-mail and any attachments may contain confidential material for the sole use of the intended recipient(s). If you are not the intended recipient, please contact the sender and delete all copies; any review or distribution by others is strictly prohibited.


^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH v2 2/2] iommu/vt-d: Disable PMRs and skip force-IOMMU when TXT TPRs are active
  2026-06-03 11:45 ` [PATCH v1 2/2] iommu/vt-d: Disable PMRs and skip force-IOMMU when TXT TPRs are active Michal Camacho Romero
  2026-06-11  8:49   ` Baolu Lu
  2026-08-07  9:16   ` [PATCH v2 " Michal Camacho Romero
@ 2026-08-07 10:14   ` Michal Camacho Romero
  2026-08-20  3:28     ` Baolu Lu
  2 siblings, 1 reply; 15+ messages in thread
From: Michal Camacho Romero @ 2026-08-07 10:14 UTC (permalink / raw)
  To: Baolu Lu, Ning Sun, Thomas Gleixner
  Cc: Michal Camacho Romero, x86, iommu, tboot-devel, linux-kernel,
	Mateusz Mowka, Adam Pawlicki, Pawel Randzio

From: Michal Camacho Romero <michal.camacho.romero@intel.com>

When Intel TXT Protection Regions (TPRs) are present in the DTPR table,
hardware-level DMA protection is already enforced by the SINIT ACM.
In this case:

- Skip forcing IOMMU enablement in tboot_force_iommu(), since TPRs
  already provide DMA protection.
- Tear down PMRs during intel_iommu_init() when TPRs are active,
  while PMRs are redundant with TPR-based protection.
- Call tboot_parse_dtpr_table() from parse_dmar_table() to disable
  TPR regions early, allowing the kernel to manage DMA protection
  prior to the OS boot.

Link: https://uefi.org/sites/default/files/resources/633933_Intel_TXT_DMA_Protection_Ranges_rev_0p73.pdf
Link: https://cdrdv2-public.intel.com/315168/315168_TXT_MLE_DG_rev_017_7.pdf
Reviewed-by: Lu Baolu <baolu.lu@linux.intel.com>
Signed-off-by: Michal Camacho Romero <michal.camacho.romero@intel.com>
---
 drivers/iommu/intel/dmar.c  | 13 +++++++++++++
 drivers/iommu/intel/iommu.c |  9 ++++++++-
 2 files changed, 21 insertions(+), 1 deletion(-)

diff --git a/drivers/iommu/intel/dmar.c b/drivers/iommu/intel/dmar.c
index 767ec092accd..17c26d222253 100644
--- a/drivers/iommu/intel/dmar.c
+++ b/drivers/iommu/intel/dmar.c
@@ -635,6 +635,8 @@ static int __init
 parse_dmar_table(void)
 {
 	struct acpi_table_dmar *dmar;
+	struct acpi_table_dtpr *dtpr;
+	void *txt_heap;
 	int drhd_count = 0;
 	int ret;
 	struct dmar_res_callback cb = {
@@ -670,6 +672,17 @@ parse_dmar_table(void)
 		return -EINVAL;
 	}
 
+	dtpr = tboot_get_dtpr_table(&txt_heap);
+	if (dtpr) {
+		/*
+		 * TPR is enabled. This will also tell not to establish IOMMU
+		 * PMRs.
+		 */
+		tboot_parse_dtpr_table(dtpr);
+		iounmap(txt_heap);
+	}
+
+	txt_heap = NULL;
 	pr_info("Host address width %d\n", dmar->width + 1);
 	ret = dmar_walk_dmar_table(dmar, &cb);
 	if (ret == 0 && drhd_count == 0)
diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c
index 849d06dfe1ae..5bc6f2dd25c4 100644
--- a/drivers/iommu/intel/iommu.c
+++ b/drivers/iommu/intel/iommu.c
@@ -2543,6 +2543,13 @@ static __init int tboot_force_iommu(void)
 	if (!tboot_enabled())
 		return 0;
 
+	/*
+	 * If TPR is enabled we don't need to force IOMMU, TPR set by SINIT
+	 * ACM will take care of DMA protection.
+	 */
+	if (tboot_is_tpr_enabled())
+		return 0;
+
 	if (no_iommu || dmar_disabled)
 		pr_warn("Forcing Intel-IOMMU to enabled\n");
 
@@ -2600,7 +2607,7 @@ int __init intel_iommu_init(void)
 		 * calling SENTER, but the kernel is expected to reset/tear
 		 * down the PMRs.
 		 */
-		if (intel_iommu_tboot_noforce) {
+		if (intel_iommu_tboot_noforce || tboot_is_tpr_enabled()) {
 			for_each_iommu(iommu, drhd)
 				iommu_disable_protect_mem_regions(iommu);
 		}
-- 
2.55.0


^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH v2 2/2] iommu/vt-d: Disable PMRs and skip force-IOMMU when TXT TPRs are active
  2026-08-07 10:14   ` Michal Camacho Romero
@ 2026-08-20  3:28     ` Baolu Lu
  2026-09-03  9:33       ` [PATCH v3 " Michal Camacho Romero
  0 siblings, 1 reply; 15+ messages in thread
From: Baolu Lu @ 2026-08-20  3:28 UTC (permalink / raw)
  To: Michal Camacho Romero, Ning Sun, Thomas Gleixner
  Cc: Michal Camacho Romero, x86, iommu, tboot-devel, linux-kernel,
	Mateusz Mowka, Adam Pawlicki, Pawel Randzio

On 8/7/26 18:14, Michal Camacho Romero wrote:
> From: Michal Camacho Romero <michal.camacho.romero@intel.com>
> 
> When Intel TXT Protection Regions (TPRs) are present in the DTPR table,
> hardware-level DMA protection is already enforced by the SINIT ACM.
> In this case:
> 
> - Skip forcing IOMMU enablement in tboot_force_iommu(), since TPRs
>    already provide DMA protection.
> - Tear down PMRs during intel_iommu_init() when TPRs are active,
>    while PMRs are redundant with TPR-based protection.
> - Call tboot_parse_dtpr_table() from parse_dmar_table() to disable
>    TPR regions early, allowing the kernel to manage DMA protection
>    prior to the OS boot.
> 
> Link: https://uefi.org/sites/default/files/resources/633933_Intel_TXT_DMA_Protection_Ranges_rev_0p73.pdf
> Link: https://cdrdv2-public.intel.com/315168/315168_TXT_MLE_DG_rev_017_7.pdf
> Reviewed-by: Lu Baolu <baolu.lu@linux.intel.com>
> Signed-off-by: Michal Camacho Romero <michal.camacho.romero@intel.com>
> ---
>   drivers/iommu/intel/dmar.c  | 13 +++++++++++++
>   drivers/iommu/intel/iommu.c |  9 ++++++++-
>   2 files changed, 21 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/iommu/intel/dmar.c b/drivers/iommu/intel/dmar.c
> index 767ec092accd..17c26d222253 100644
> --- a/drivers/iommu/intel/dmar.c
> +++ b/drivers/iommu/intel/dmar.c
> @@ -635,6 +635,8 @@ static int __init
>   parse_dmar_table(void)
>   {
>   	struct acpi_table_dmar *dmar;
> +	struct acpi_table_dtpr *dtpr;
> +	void *txt_heap;
>   	int drhd_count = 0;
>   	int ret;
>   	struct dmar_res_callback cb = {
> @@ -670,6 +672,17 @@ parse_dmar_table(void)
>   		return -EINVAL;
>   	}
>   
> +	dtpr = tboot_get_dtpr_table(&txt_heap);
> +	if (dtpr) {
> +		/*
> +		 * TPR is enabled. This will also tell not to establish IOMMU
> +		 * PMRs.
> +		 */
> +		tboot_parse_dtpr_table(dtpr);
> +		iounmap(txt_heap);
> +	}
> +
> +	txt_heap = NULL;
>   	pr_info("Host address width %d\n", dmar->width + 1);
>   	ret = dmar_walk_dmar_table(dmar, &cb);
>   	if (ret == 0 && drhd_count == 0)
> diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c
> index 849d06dfe1ae..5bc6f2dd25c4 100644
> --- a/drivers/iommu/intel/iommu.c
> +++ b/drivers/iommu/intel/iommu.c
> @@ -2543,6 +2543,13 @@ static __init int tboot_force_iommu(void)

This patch doesn't apply cleanly on top of the recent mainline tree —
tboot_force_iommu() has been refactored by a recent merge. Please rebase
and re-post it after 7.3-rc1.

>   	if (!tboot_enabled())
>   		return 0;
>   
> +	/*
> +	 * If TPR is enabled we don't need to force IOMMU, TPR set by SINIT
> +	 * ACM will take care of DMA protection.
> +	 */
> +	if (tboot_is_tpr_enabled())
> +		return 0;
> +
>   	if (no_iommu || dmar_disabled)
>   		pr_warn("Forcing Intel-IOMMU to enabled\n");
>   
> @@ -2600,7 +2607,7 @@ int __init intel_iommu_init(void)
>   		 * calling SENTER, but the kernel is expected to reset/tear
>   		 * down the PMRs.
>   		 */
> -		if (intel_iommu_tboot_noforce) {
> +		if (intel_iommu_tboot_noforce || tboot_is_tpr_enabled()) {
>   			for_each_iommu(iommu, drhd)
>   				iommu_disable_protect_mem_regions(iommu);
>   		}

Thanks,
baolu

^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH v3 2/2] iommu/vt-d: Disable PMRs and skip force-IOMMU when TXT TPRs are active
  2026-08-20  3:28     ` Baolu Lu
@ 2026-09-03  9:33       ` Michal Camacho Romero
  2026-09-04  2:19         ` Baolu Lu
  0 siblings, 1 reply; 15+ messages in thread
From: Michal Camacho Romero @ 2026-09-03  9:33 UTC (permalink / raw)
  To: Baolu Lu, Ning Sun, Thomas Gleixner
  Cc: Michal Camacho Romero, x86, iommu, tboot-devel, linux-kernel,
	Mateusz Mowka, Adam Pawlicki, Pawel Randzio,
	Michal Camacho Romero

When Intel TXT Protection Regions (TPRs) are present in the DTPR table,
hardware-level DMA protection is already enforced by the SINIT ACM.
In this case:

- Skip forcing IOMMU enablement in tboot_force_iommu(), since TPRs
  already provide DMA protection.
- Tear down PMRs during intel_iommu_init() when TPRs are active,
  while PMRs are redundant with TPR-based protection.
- Call tboot_parse_dtpr_table() from parse_dmar_table() to disable
  TPR regions early, allowing the kernel to manage DMA protection
  prior to the OS boot.

Link: https://uefi.org/sites/default/files/resources/633933_Intel_TXT_DMA_Protection_Ranges_rev_0p73.pdf
Link: https://cdrdv2-public.intel.com/315168/315168_TXT_MLE_DG_rev_017_7.pdf
Reviewed-by: Lu Baolu <baolu.lu@linux.intel.com>
Signed-off-by: Michal Camacho Romero <michal.camacho.romero@linux.intel.com>
---
 drivers/iommu/intel/dmar.c  | 13 +++++++++++++
 drivers/iommu/intel/iommu.c |  9 ++++++++-
 2 files changed, 21 insertions(+), 1 deletion(-)

diff --git a/drivers/iommu/intel/dmar.c b/drivers/iommu/intel/dmar.c
index ba675b08cd20..c98a44487706 100644
--- a/drivers/iommu/intel/dmar.c
+++ b/drivers/iommu/intel/dmar.c
@@ -635,6 +635,8 @@ static int __init
 parse_dmar_table(void)
 {
 	struct acpi_table_dmar *dmar;
+	struct acpi_table_dtpr *dtpr;
+	void *txt_heap;
 	int drhd_count = 0;
 	int ret;
 	struct dmar_res_callback cb = {
@@ -670,6 +672,17 @@ parse_dmar_table(void)
 		return -EINVAL;
 	}
 
+	dtpr = tboot_get_dtpr_table(&txt_heap);
+	if (dtpr) {
+		/*
+		 * TPR is enabled. This will also tell not to establish IOMMU
+		 * PMRs.
+		 */
+		tboot_parse_dtpr_table(dtpr);
+		iounmap(txt_heap);
+	}
+
+	txt_heap = NULL;
 	pr_info("Host address width %d\n", dmar->width + 1);
 	ret = dmar_walk_dmar_table(dmar, &cb);
 	if (ret == 0 && drhd_count == 0)
diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c
index 2e3b3ab216f8..ce40b1bf0296 100644
--- a/drivers/iommu/intel/iommu.c
+++ b/drivers/iommu/intel/iommu.c
@@ -2563,6 +2563,13 @@ static __init void tboot_force_iommu(void)
 	if (!tboot_enabled() || intel_iommu_tboot_noforce)
 		return;
 
+	/*
+	 * If TPR is enabled we don't need to force IOMMU, TPR set by SINIT
+	 * ACM will take care of DMA protection.
+	 */
+	if (tboot_is_tpr_enabled())
+		return;
+
 	if (!dmar_can_force_on(DMAR_FORCEON_TBOOT))
 		panic("tboot: Failed to force IOMMU on\n");
 
@@ -2623,7 +2630,7 @@ int __init intel_iommu_init(void)
 		 * calling SENTER, but the kernel is expected to reset/tear
 		 * down the PMRs.
 		 */
-		if (intel_iommu_tboot_noforce) {
+		if (intel_iommu_tboot_noforce || tboot_is_tpr_enabled()) {
 			for_each_iommu(iommu, drhd)
 				iommu_disable_protect_mem_regions(iommu);
 		}
-- 
2.55.0


^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH v3 2/2] iommu/vt-d: Disable PMRs and skip force-IOMMU when TXT TPRs are active
  2026-09-03  9:33       ` [PATCH v3 " Michal Camacho Romero
@ 2026-09-04  2:19         ` Baolu Lu
  0 siblings, 0 replies; 15+ messages in thread
From: Baolu Lu @ 2026-09-04  2:19 UTC (permalink / raw)
  To: Michal Camacho Romero, Ning Sun, Thomas Gleixner
  Cc: baolu.lu, Michal Camacho Romero, x86, iommu, tboot-devel,
	linux-kernel, Mateusz Mowka, Adam Pawlicki, Pawel Randzio

On 9/3/2026 5:33 PM, Michal Camacho Romero wrote:
> When Intel TXT Protection Regions (TPRs) are present in the DTPR table,
> hardware-level DMA protection is already enforced by the SINIT ACM.
> In this case:
> 
> - Skip forcing IOMMU enablement in tboot_force_iommu(), since TPRs
>    already provide DMA protection.
> - Tear down PMRs during intel_iommu_init() when TPRs are active,
>    while PMRs are redundant with TPR-based protection.
> - Call tboot_parse_dtpr_table() from parse_dmar_table() to disable
>    TPR regions early, allowing the kernel to manage DMA protection
>    prior to the OS boot.
> 
> Link: https://uefi.org/sites/default/files/resources/633933_Intel_TXT_DMA_Protection_Ranges_rev_0p73.pdf
> Link: https://cdrdv2-public.intel.com/315168/315168_TXT_MLE_DG_rev_017_7.pdf
> Reviewed-by: Lu Baolu <baolu.lu@linux.intel.com>
> Signed-off-by: Michal Camacho Romero <michal.camacho.romero@linux.intel.com>
> ---
>   drivers/iommu/intel/dmar.c  | 13 +++++++++++++
>   drivers/iommu/intel/iommu.c |  9 ++++++++-
>   2 files changed, 21 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/iommu/intel/dmar.c b/drivers/iommu/intel/dmar.c
> index ba675b08cd20..c98a44487706 100644
> --- a/drivers/iommu/intel/dmar.c
> +++ b/drivers/iommu/intel/dmar.c
> @@ -635,6 +635,8 @@ static int __init
>   parse_dmar_table(void)
>   {
>   	struct acpi_table_dmar *dmar;
> +	struct acpi_table_dtpr *dtpr;
> +	void *txt_heap;
>   	int drhd_count = 0;
>   	int ret;
>   	struct dmar_res_callback cb = {
> @@ -670,6 +672,17 @@ parse_dmar_table(void)
>   		return -EINVAL;
>   	}
>   
> +	dtpr = tboot_get_dtpr_table(&txt_heap);

What happens if parse_dmar_table() returns failure after this call?
That would mean TPR teardown happens even if DMAR parsing later fails
and returns error. Is that the intended behavior (i.e., parse/disable
TPR regardless of whether DMAR is ultimately usable)?

Also, does tboot_get_dtpr_table() become a no-op and return NULL when
tboot is not active? This is important, since it is called
unconditionally on every boot.

> +	if (dtpr) {
> +		/*
> +		 * TPR is enabled. This will also tell not to establish IOMMU
> +		 * PMRs.
> +		 */
> +		tboot_parse_dtpr_table(dtpr);

Can this fail? If yes, why is there no error handling?

> +		iounmap(txt_heap);

txt_heap is not used in the IOMMU driver. Why map it inside a hidden
helper call and then unmap it here?

> +	}
> +
> +	txt_heap = NULL;

txt_heap is never used after this line, so this assignment is dead code?

>   	pr_info("Host address width %d\n", dmar->width + 1);
>   	ret = dmar_walk_dmar_table(dmar, &cb);
>   	if (ret == 0 && drhd_count == 0)
> diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c
> index 2e3b3ab216f8..ce40b1bf0296 100644
> --- a/drivers/iommu/intel/iommu.c
> +++ b/drivers/iommu/intel/iommu.c
> @@ -2563,6 +2563,13 @@ static __init void tboot_force_iommu(void)
>   	if (!tboot_enabled() || intel_iommu_tboot_noforce)
>   		return;
>   
> +	/*
> +	 * If TPR is enabled we don't need to force IOMMU, TPR set by SINIT
> +	 * ACM will take care of DMA protection.
> +	 */
> +	if (tboot_is_tpr_enabled())
> +		return;
> +
>   	if (!dmar_can_force_on(DMAR_FORCEON_TBOOT))
>   		panic("tboot: Failed to force IOMMU on\n");
>   
> @@ -2623,7 +2630,7 @@ int __init intel_iommu_init(void)
>   		 * calling SENTER, but the kernel is expected to reset/tear
>   		 * down the PMRs.
>   		 */
> -		if (intel_iommu_tboot_noforce) {
> +		if (intel_iommu_tboot_noforce || tboot_is_tpr_enabled()) {
>   			for_each_iommu(iommu, drhd)
>   				iommu_disable_protect_mem_regions(iommu);
>   		}

Thanks,
baolu

^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH v2 1/2] x86/tboot: Add support for parsing DTPR table and disabling TPRs
  2026-06-03 11:44 ` [PATCH v1 1/2] x86/tboot: Add support for parsing DTPR table and disabling TPRs Michal Camacho Romero
@ 2026-09-14 13:19   ` Michal Camacho Romero
  2026-09-15 23:23     ` Sun, Ning
  0 siblings, 1 reply; 15+ messages in thread
From: Michal Camacho Romero @ 2026-09-14 13:19 UTC (permalink / raw)
  To: Ning Sun
  Cc: Baolu Lu, Thomas Gleixner, Michal Camacho Romero, x86, iommu,
	tboot-devel, linux-kernel, Mateusz Mowka, Adam Pawlicki,
	Pawel Randzio

From: Michal Camacho Romero <michal.camacho.romero@intel.com>

Add functions to locate and parse the DMA TXT Protection Ranges (DTPR)
table from the TXT heap's SinitMleData extended data elements (requires
SINIT MLE version >= 9).

* tboot_get_dtpr_table() - function walks through the TXT heap to find
                           the DTPR extended data element
                           (type HEAP_EXTDATA_TYPE_DTPR) and returns
                           pointer to the DTPR table.

* tboot_parse_dtpr_table() - function iterates over TPR instances and
                             disables each TPR region by setting bit 4
                             in the TPRn_BASE register via MMIO.

Using these functions will allow the kernel to deactivate SINIT
ACM-established TPRs prior to the Linux OS launch.

Link: https://uefi.org/sites/default/files/resources/633933_Intel_TXT_DMA_Protection_Ranges_rev_0p73.pdf
Link: https://cdrdv2-public.intel.com/315168/315168_TXT_MLE_DG_rev_017_7.pdf
Signed-off-by: Michal Camacho Romero <michal.camacho.romero@intel.com>
---
 arch/x86/kernel/tboot.c | 230 +++++++++++++++++++++++++++++++++++++---
 include/linux/tboot.h   |  10 ++
 2 files changed, 225 insertions(+), 15 deletions(-)

diff --git a/arch/x86/kernel/tboot.c b/arch/x86/kernel/tboot.c
index 46b8f1f16676..b745683c1ed9 100644
--- a/arch/x86/kernel/tboot.c
+++ b/arch/x86/kernel/tboot.c
@@ -18,6 +18,7 @@
 #include <linux/mm.h>
 #include <linux/tboot.h>
 #include <linux/debugfs.h>
+#include <acpi/actbl1.h>
 
 #include <asm/realmode.h>
 #include <asm/processor.h>
@@ -223,6 +224,30 @@ static int tboot_setup_sleep(void)
 
 #endif
 
+static bool tboot_check_txt_heap_section_bounds(const u64 heap_end,
+                                                void *heap_base,
+                                                void *heap_section,
+                                                const u64 heap_section_size,
+                                                const char *section_name)
+{
+	if (heap_section_size == 0)
+	{
+		pr_err("%s has zero size\n", section_name);
+		iounmap(heap_base);
+		heap_base = NULL;
+		return false;
+	}
+
+	if ((u64)heap_section + heap_section_size > heap_end) {
+		pr_err("%s exceeds heap boundary\n", section_name);
+		iounmap(heap_base);
+		heap_base = NULL;
+		return false;
+	}
+
+	return true;
+}
+
 void tboot_shutdown(u32 shutdown_type)
 {
 	void (*shutdown)(void);
@@ -453,22 +478,30 @@ struct sha1_hash {
 	u8 hash[SHA1_SIZE];
 };
 
+struct heap_ext_data_elt {
+	u32 type;
+	u32 size;
+	u8  data[];
+} __packed;
+
 struct sinit_mle_data {
-	u32               version;             /* currently 6 */
-	struct sha1_hash  bios_acm_id;
-	u32               edx_senter_flags;
-	u64               mseg_valid;
-	struct sha1_hash  sinit_hash;
-	struct sha1_hash  mle_hash;
-	struct sha1_hash  stm_hash;
-	struct sha1_hash  lcp_policy_hash;
-	u32               lcp_policy_control;
-	u32               rlp_wakeup_addr;
-	u32               reserved;
-	u32               num_mdrs;
-	u32               mdrs_off;
-	u32               num_vtd_dmars;
-	u32               vtd_dmars_off;
+	u32                      version;             /* currently 9 */
+	struct sha1_hash         bios_acm_id;
+	u32                      edx_senter_flags;
+	u64                      mseg_valid;
+	struct sha1_hash         sinit_hash;
+	struct sha1_hash         mle_hash;
+	struct sha1_hash         stm_hash;
+	struct sha1_hash         lcp_policy_hash;
+	u32                      lcp_policy_control;
+	u32                      rlp_wakeup_addr;
+	u32                      reserved;
+	u32                      num_mdrs;
+	u32                      mdrs_off;
+	u32                      num_vtd_dmars;
+	u32                      vtd_dmars_off;
+	u32                      proc_scrtm_status; /* version 8 or later only*/
+	struct heap_ext_data_elt ext_data_elts[];
 } __packed;
 
 struct acpi_table_header *tboot_get_dmar_table(struct acpi_table_header *dmar_tbl)
@@ -514,3 +547,170 @@ struct acpi_table_header *tboot_get_dmar_table(struct acpi_table_header *dmar_tb
 
 	return dmar_tbl;
 }
+
+struct acpi_table_dtpr *tboot_get_dtpr_table(void **heap_base)
+{
+	void *heap_ptr, *config;
+	struct sinit_mle_data *sinit_mle;
+	struct heap_ext_data_elt *elt;
+	u64 heap_end, heap_size, sinit_mle_size, heap_section_size;
+
+	if (!heap_base)
+		return NULL;
+
+	if (!tboot_enabled())
+		return NULL;
+	/*
+	 * ACPI tables may not be DMA protected by tboot, so use DMAR copy
+	 * SINIT saved in SinitMleData in TXT heap (which is DMA protected)
+	 */
+
+	/* map config space in order to get heap addr */
+	config = ioremap(TXT_PUB_CONFIG_REGS_BASE, NR_TXT_CONFIG_PAGES *
+			 PAGE_SIZE);
+	if (!config)
+		return NULL;
+
+	/* now map TXT heap */
+	*heap_base = ioremap(*(u64 *)(config + TXTCR_HEAP_BASE),
+			    *(u64 *)(config + TXTCR_HEAP_SIZE));
+	heap_size = *(u64 *)(config + TXTCR_HEAP_SIZE);
+	heap_end = (u64)*heap_base + heap_size;
+	iounmap(config);
+
+	if (!(*heap_base))
+		return NULL;
+
+	/* walk heap to SinitMleData */
+	/* skip BiosData */
+	/* get BiosData section size */
+	heap_section_size = *(u64 *) (*heap_base);
+	if (!tboot_check_txt_heap_section_bounds(heap_end, *heap_base,*heap_base,
+	                                         heap_section_size, "BiosData")) {
+		return NULL;
+	}
+
+	/* skip OsMleData */
+	heap_ptr = *heap_base + heap_section_size;
+	/* get OsMleData section size */
+	heap_section_size = *(u64 *)heap_ptr;
+	if (!tboot_check_txt_heap_section_bounds(heap_end, *heap_base, heap_ptr,
+	                                         heap_section_size, "OsMleData")) {
+		return NULL;
+	}
+
+	/* skip OsSinitData */
+	heap_ptr += heap_section_size;
+	/* get OsSinitData section size */
+	heap_section_size = *(u64 *)heap_ptr;
+	if (!tboot_check_txt_heap_section_bounds(heap_end, *heap_base, heap_ptr,
+	                                         heap_section_size, "OsSinitData")) {
+		return NULL;
+	}
+
+	/* jump to the SinitMleData */
+	heap_ptr += heap_section_size;
+	/* now points to SinitMleDataSize; set to SinitMleData */
+	sinit_mle_size = *(u64 *)heap_ptr;
+	if(!tboot_check_txt_heap_section_bounds(heap_end, *heap_base, heap_ptr,
+	                                        sinit_mle_size, "SinitMleData")) {
+		return NULL;
+	}
+
+	heap_ptr += sizeof(u64);
+
+	sinit_mle = (struct sinit_mle_data *)heap_ptr;
+	if (sinit_mle->version < 9) {
+		iounmap(*heap_base);
+		*heap_base = NULL;
+		return NULL;
+	}
+
+	elt = sinit_mle->ext_data_elts;
+	while (elt->type != HEAP_EXTDATA_TYPE_DTPR &&
+		   elt->type != HEAP_EXTDATA_TYPE_END) {
+		elt = (void *)elt + elt->size;
+		/*
+		 * Check if the element is beyond the SinitMleData boundary or has an
+		 * invalid size. It's size should be at least 8 bytes.
+		 */
+		if (((u64)elt > (u64)sinit_mle + sinit_mle_size) || (elt->size < 8)) {
+			iounmap(*heap_base);
+			*heap_base = NULL;
+			return NULL;
+		}
+	}
+
+	if (elt->type == HEAP_EXTDATA_TYPE_END) {
+		pr_info("DTPR element not found in SinitMleData\n");
+		iounmap(*heap_base);
+		*heap_base = NULL;
+		return NULL;
+	}
+
+	return (struct acpi_table_dtpr *)elt->data;
+}
+
+static bool tboot_tpr_enabled = false;
+void tboot_parse_dtpr_table(struct acpi_table_dtpr *dtpr)
+{
+	struct acpi_tpr_instance *tpr_inst;
+	struct acpi_tpr_array    *tpr_arr;
+	u32 *instance_cnt;
+	u64 *base;
+	u32 i, j;
+	u32 ref_tpr_cnt;
+
+	if (dtpr == NULL)
+		return;
+
+	if (!tboot_enabled())
+		return;
+
+	instance_cnt = (u32*)(&dtpr->ins_cnt);
+	tpr_inst = (struct acpi_tpr_instance *)(instance_cnt + 1);
+	ref_tpr_cnt = tpr_inst->tpr_cnt;
+	for (i = 0; i < *instance_cnt; ++i) {
+		if (tpr_inst->tpr_cnt < 2) {
+			pr_err("TPR Instance %d has less than 2 TPRs, further DTPR "
+			       "processing interrupted.\n", i);
+			return;
+		}
+		if (tpr_inst->tpr_cnt != ref_tpr_cnt) {
+			pr_err("TPR Instance %d has inconsistent TPR count: expected %d,"
+			       " found %d\n", i, ref_tpr_cnt, tpr_inst->tpr_cnt);
+			return;
+		}
+
+		for (j = 0; j < tpr_inst->tpr_cnt; ++j) {
+			tpr_arr =  (struct acpi_tpr_array*)((u8*) tpr_inst +
+			            sizeof(struct acpi_tpr_instance) +
+			            j * sizeof(struct acpi_tpr_array));
+
+			base = ioremap(tpr_arr->base, 16);
+			if (!base) {
+				pr_warn("TPR Instance %d, TPR No.%d disabling failure.\n",
+				        i, j);
+				continue;
+			}
+
+			pr_info("TPR instance %d, TPR %d:base %llx limit %llx\n", i, j,
+			        readq(base), readq(base + 1));
+			writeq(readq(base) | BIT(4), base);
+			if (tboot_tpr_enabled == false)
+				tboot_tpr_enabled = true;
+			iounmap(base);
+		}
+
+		tpr_inst = (struct acpi_tpr_instance *)((u8*)tpr_inst +
+		            sizeof(*tpr_inst) + j * sizeof(struct acpi_tpr_array));
+	}
+
+	if (tboot_tpr_enabled)
+		pr_debug("TPR protection detected, PMR will be disabled\n");
+}
+
+bool tboot_is_tpr_enabled(void)
+{
+	return tboot_tpr_enabled;
+}
diff --git a/include/linux/tboot.h b/include/linux/tboot.h
index d2279160ef39..39fb2e3ba80b 100644
--- a/include/linux/tboot.h
+++ b/include/linux/tboot.h
@@ -24,6 +24,10 @@ enum {
 #include <linux/acpi.h>
 /* used to communicate between tboot and the launched kernel */
 
+/*TXT Extended Data Element Types*/
+#define HEAP_EXTDATA_TYPE_END   0
+#define HEAP_EXTDATA_TYPE_DTPR 14
+
 #define TB_KEY_SIZE             64   /* 512 bits */
 
 #define MAX_TB_MAC_REGIONS      32
@@ -126,6 +130,9 @@ extern void tboot_probe(void);
 extern void tboot_shutdown(u32 shutdown_type);
 extern struct acpi_table_header *tboot_get_dmar_table(
 				      struct acpi_table_header *dmar_tbl);
+extern struct acpi_table_dtpr *tboot_get_dtpr_table(void **);
+extern void tboot_parse_dtpr_table(struct acpi_table_dtpr *);
+extern bool tboot_is_tpr_enabled(void);
 
 #else
 
@@ -135,6 +142,9 @@ extern struct acpi_table_header *tboot_get_dmar_table(
 #define tboot_sleep(sleep_state, pm1a_control, pm1b_control)	\
 					do { } while (0)
 #define tboot_get_dmar_table(dmar_tbl)	(dmar_tbl)
+#define tboot_get_dtpr_table(txt_heap) NULL
+#define tboot_parse_dtpr_table(dtpr) do { } while (0)
+#define tboot_is_tpr_enabled() 0
 
 #endif /* !CONFIG_INTEL_TXT */
 
-- 
2.55.0


^ permalink raw reply	[flat|nested] 15+ messages in thread

* RE: [PATCH v2 1/2] x86/tboot: Add support for parsing DTPR table and disabling TPRs
  2026-09-14 13:19   ` [PATCH v2 " Michal Camacho Romero
@ 2026-09-15 23:23     ` Sun, Ning
  2026-09-17  9:41       ` [PATCH 1/1] " Michal Camacho Romero
  2026-09-17  9:43       ` [PATCH v3 1/2] " Michal Camacho Romero
  0 siblings, 2 replies; 15+ messages in thread
From: Sun, Ning @ 2026-09-15 23:23 UTC (permalink / raw)
  To: Michal Camacho Romero
  Cc: Baolu Lu, Thomas Gleixner, Camacho Romero, Michal, x86, iommu,
	tboot-devel, linux-kernel, Mowka, Mateusz, Pawlicki, AdamX,
	Randzio, Pawel, Sun, Ning

Hi Michal,

The overall approach looks reasonable: reading DTPR from the TXT heap copy in SinitMleData, requiring v9/ext-data support, and disabling TPR through BIT(4) in TPRn_BASE is consistent with the referenced TXT/DTPR specifications. 

Please find inline comments below.

Thanks,
-Ning

> -----Original Message-----
> From: Michal Camacho Romero <michal.camacho.romero@linux.intel.com>
> Sent: Monday, September 14, 2026 6:20 AM
> To: Sun, Ning <ning.sun@intel.com>
> Cc: Baolu Lu <baolu.lu@linux.intel.com>; Thomas Gleixner <tglx@kernel.org>; Camacho Romero, Michal
> <michal.camacho.romero@intel.com>; x86@kernel.org; iommu@lists.linux.dev; tboot-devel@lists.sourceforge.net; linux-
> kernel@vger.kernel.org; Mowka, Mateusz <mateusz.mowka@intel.com>; Pawlicki, AdamX <adamx.pawlicki@intel.com>; Randzio,
> Pawel <pawel.randzio@intel.com>
> Subject: [PATCH v2 1/2] x86/tboot: Add support for parsing DTPR table and disabling TPRs
> 
> From: Michal Camacho Romero <michal.camacho.romero@intel.com>
> 
> Add functions to locate and parse the DMA TXT Protection Ranges (DTPR) table from the TXT heap's SinitMleData extended data
> elements (requires SINIT MLE version >= 9).
> 
> * tboot_get_dtpr_table() - function walks through the TXT heap to find
>                            the DTPR extended data element
>                            (type HEAP_EXTDATA_TYPE_DTPR) and returns
>                            pointer to the DTPR table.
> 
> * tboot_parse_dtpr_table() - function iterates over TPR instances and
>                              disables each TPR region by setting bit 4
>                              in the TPRn_BASE register via MMIO.
> 
> Using these functions will allow the kernel to deactivate SINIT ACM-established TPRs prior to the Linux OS launch.
> 
> Link: https://uefi.org/sites/default/files/resources/633933_Intel_TXT_DMA_Protection_Ranges_rev_0p73.pdf
> Link: https://cdrdv2-public.intel.com/315168/315168_TXT_MLE_DG_rev_017_7.pdf
> Signed-off-by: Michal Camacho Romero <michal.camacho.romero@intel.com>
> ---
>  arch/x86/kernel/tboot.c | 230 +++++++++++++++++++++++++++++++++++++---
>  include/linux/tboot.h   |  10 ++
>  2 files changed, 225 insertions(+), 15 deletions(-)
> 
> diff --git a/arch/x86/kernel/tboot.c b/arch/x86/kernel/tboot.c index 46b8f1f16676..b745683c1ed9 100644
> --- a/arch/x86/kernel/tboot.c
> +++ b/arch/x86/kernel/tboot.c
> @@ -18,6 +18,7 @@
>  #include <linux/mm.h>
>  #include <linux/tboot.h>
>  #include <linux/debugfs.h>
> +#include <acpi/actbl1.h>
> 
>  #include <asm/realmode.h>
>  #include <asm/processor.h>
> @@ -223,6 +224,30 @@ static int tboot_setup_sleep(void)
> 
>  #endif
> 
> +static bool tboot_check_txt_heap_section_bounds(const u64 heap_end,
> +                                                void *heap_base,
> +                                                void *heap_section,
> +                                                const u64 heap_section_size,
> +                                                const char
> +*section_name) {
> +	if (heap_section_size == 0)
> +	{
> +		pr_err("%s has zero size\n", section_name);
> +		iounmap(heap_base);
> +		heap_base = NULL;
> +		return false;
> +	}
> +
> +	if ((u64)heap_section + heap_section_size > heap_end) {
> +		pr_err("%s exceeds heap boundary\n", section_name);
> +		iounmap(heap_base);
> +		heap_base = NULL;
> +		return false;
> +	}
> +
> +	return true;
> +}

This helper is a step in the right direction, but heap_base = NULL here only updates the local parameter copy, so it has no effect on the caller. Please either drop that assignment or change the API to take a pointer-to-pointer if you really want to clear the caller's mapping.

> +
>  void tboot_shutdown(u32 shutdown_type)
>  {
>  	void (*shutdown)(void);
> @@ -453,22 +478,30 @@ struct sha1_hash {
>  	u8 hash[SHA1_SIZE];
>  };
> 
> +struct heap_ext_data_elt {
> +	u32 type;
> +	u32 size;
> +	u8  data[];
> +} __packed;
> +
>  struct sinit_mle_data {
> -	u32               version;             /* currently 6 */
> -	struct sha1_hash  bios_acm_id;
> -	u32               edx_senter_flags;
> -	u64               mseg_valid;
> -	struct sha1_hash  sinit_hash;
> -	struct sha1_hash  mle_hash;
> -	struct sha1_hash  stm_hash;
> -	struct sha1_hash  lcp_policy_hash;
> -	u32               lcp_policy_control;
> -	u32               rlp_wakeup_addr;
> -	u32               reserved;
> -	u32               num_mdrs;
> -	u32               mdrs_off;
> -	u32               num_vtd_dmars;
> -	u32               vtd_dmars_off;
> +	u32                      version;             /* currently 9 */
> +	struct sha1_hash         bios_acm_id;
> +	u32                      edx_senter_flags;
> +	u64                      mseg_valid;
> +	struct sha1_hash         sinit_hash;
> +	struct sha1_hash         mle_hash;
> +	struct sha1_hash         stm_hash;
> +	struct sha1_hash         lcp_policy_hash;
> +	u32                      lcp_policy_control;
> +	u32                      rlp_wakeup_addr;
> +	u32                      reserved;
> +	u32                      num_mdrs;
> +	u32                      mdrs_off;
> +	u32                      num_vtd_dmars;
> +	u32                      vtd_dmars_off;
> +	u32                      proc_scrtm_status; /* version 8 or later only*/
> +	struct heap_ext_data_elt ext_data_elts[];
>  } __packed;
> 
>  struct acpi_table_header *tboot_get_dmar_table(struct acpi_table_header *dmar_tbl) @@ -514,3 +547,170 @@ struct
> acpi_table_header *tboot_get_dmar_table(struct acpi_table_header *dmar_tb
> 
>  	return dmar_tbl;
>  }
> +
> +struct acpi_table_dtpr *tboot_get_dtpr_table(void **heap_base) {
> +	void *heap_ptr, *config;
> +	struct sinit_mle_data *sinit_mle;
> +	struct heap_ext_data_elt *elt;
> +	u64 heap_end, heap_size, sinit_mle_size, heap_section_size;
> +
> +	if (!heap_base)
> +		return NULL;
> +
> +	if (!tboot_enabled())
> +		return NULL;
> +	/*
> +	 * ACPI tables may not be DMA protected by tboot, so use DMAR copy
> +	 * SINIT saved in SinitMleData in TXT heap (which is DMA protected)
> +	 */

This comment is stale. This path is fetching DTPR, not DMAR

> +
> +	/* map config space in order to get heap addr */
> +	config = ioremap(TXT_PUB_CONFIG_REGS_BASE, NR_TXT_CONFIG_PAGES *
> +			 PAGE_SIZE);
> +	if (!config)
> +		return NULL;
> +
> +	/* now map TXT heap */
> +	*heap_base = ioremap(*(u64 *)(config + TXTCR_HEAP_BASE),
> +			    *(u64 *)(config + TXTCR_HEAP_SIZE));
> +	heap_size = *(u64 *)(config + TXTCR_HEAP_SIZE);
> +	heap_end = (u64)*heap_base + heap_size;
> +	iounmap(config);
> +
> +	if (!(*heap_base))
> +		return NULL;
> +
> +	/* walk heap to SinitMleData */
> +	/* skip BiosData */
> +	/* get BiosData section size */
> +	heap_section_size = *(u64 *) (*heap_base);
> +	if (!tboot_check_txt_heap_section_bounds(heap_end, *heap_base,*heap_base,
> +	                                         heap_section_size, "BiosData")) {
> +		return NULL;
> +	}
> +
> +	/* skip OsMleData */
> +	heap_ptr = *heap_base + heap_section_size;
> +	/* get OsMleData section size */
> +	heap_section_size = *(u64 *)heap_ptr;
> +	if (!tboot_check_txt_heap_section_bounds(heap_end, *heap_base, heap_ptr,
> +	                                         heap_section_size, "OsMleData")) {
> +		return NULL;
> +	}
> +
> +	/* skip OsSinitData */
> +	heap_ptr += heap_section_size;
> +	/* get OsSinitData section size */
> +	heap_section_size = *(u64 *)heap_ptr;
> +	if (!tboot_check_txt_heap_section_bounds(heap_end, *heap_base, heap_ptr,
> +	                                         heap_section_size, "OsSinitData")) {
> +		return NULL;
> +	}
> +
> +	/* jump to the SinitMleData */
> +	heap_ptr += heap_section_size;
> +	/* now points to SinitMleDataSize; set to SinitMleData */
> +	sinit_mle_size = *(u64 *)heap_ptr;
> +	if(!tboot_check_txt_heap_section_bounds(heap_end, *heap_base, heap_ptr,
> +	                                        sinit_mle_size, "SinitMleData")) {
> +		return NULL;
> +	}
> +
> +	heap_ptr += sizeof(u64);
> +
> +	sinit_mle = (struct sinit_mle_data *)heap_ptr;
> +	if (sinit_mle->version < 9) {
> +		iounmap(*heap_base);
> +		*heap_base = NULL;
> +		return NULL;
> +	}
> +
> +	elt = sinit_mle->ext_data_elts;
> +	while (elt->type != HEAP_EXTDATA_TYPE_DTPR &&
> +		   elt->type != HEAP_EXTDATA_TYPE_END) {
> +		elt = (void *)elt + elt->size;
> +		/*
> +		 * Check if the element is beyond the SinitMleData boundary or has an
> +		 * invalid size. It's size should be at least 8 bytes.
> +		 */
> +		if (((u64)elt > (u64)sinit_mle + sinit_mle_size) || (elt->size < 8)) {
> +			iounmap(*heap_base);
> +			*heap_base = NULL;
> +			return NULL;
> +		}
> +	}

This is still not bounds-safe.

You dereference elt->type / elt->size before proving that the current element header is fully within the SinitMleData bounds. After advancing elt, you immediately dereference the new pointer via elt->size before proving it is still valid.

Please restructure this to:
1.	validate current header fits,
2.	validate elt->size >= sizeof(*elt),
3.	validate (u8 *)elt + elt->size stays within the end of SinitMleData,
4.	then either consume the element or advance.

The ext-data list is self-describing and end-terminated, so the parser needs to be strict here. 

Also, please use sizeof(*elt) instead of the hardcoded 8.

> +
> +	if (elt->type == HEAP_EXTDATA_TYPE_END) {
> +		pr_info("DTPR element not found in SinitMleData\n");
> +		iounmap(*heap_base);
> +		*heap_base = NULL;
> +		return NULL;
> +	}
> +
> +	return (struct acpi_table_dtpr *)elt->data; }

Returning elt->data here still needs more validation.

Before returning a struct acpi_table_dtpr *, please verify that:
*	the element payload is large enough to hold a DTPR header, and
*	the DTPR table length fits within the ext-data element payload.

Otherwise the caller can walk malformed or truncated data.

> +
> +static bool tboot_tpr_enabled = false;
> +void tboot_parse_dtpr_table(struct acpi_table_dtpr *dtpr) {
> +	struct acpi_tpr_instance *tpr_inst;
> +	struct acpi_tpr_array    *tpr_arr;
> +	u32 *instance_cnt;
> +	u64 *base;
> +	u32 i, j;
> +	u32 ref_tpr_cnt;
> +
> +	if (dtpr == NULL)
> +		return;
> +
> +	if (!tboot_enabled())
> +		return;
> +
> +	instance_cnt = (u32*)(&dtpr->ins_cnt);
> +	tpr_inst = (struct acpi_tpr_instance *)(instance_cnt + 1);
> +	ref_tpr_cnt = tpr_inst->tpr_cnt;
> +	for (i = 0; i < *instance_cnt; ++i) {

This still needs table-length-based bounds checking.

As written, the function walks variable-sized DTPR contents with no end pointer derived from the DTPR table length. Corrupted ins_cnt / tpr_cnt values can make this walk off the table and then MMIO-map arbitrary addresses.

> +		if (tpr_inst->tpr_cnt < 2) {
> +			pr_err("TPR Instance %d has less than 2 TPRs, further DTPR "
> +			       "processing interrupted.\n", i);
> +			return;
> +		}
> +		if (tpr_inst->tpr_cnt != ref_tpr_cnt) {
> +			pr_err("TPR Instance %d has inconsistent TPR count: expected %d,"
> +			       " found %d\n", i, ref_tpr_cnt, tpr_inst->tpr_cnt);
> +			return;
> +		}
> +
> +		for (j = 0; j < tpr_inst->tpr_cnt; ++j) {
> +			tpr_arr =  (struct acpi_tpr_array*)((u8*) tpr_inst +
> +			            sizeof(struct acpi_tpr_instance) +
> +			            j * sizeof(struct acpi_tpr_array));
> +
> +			base = ioremap(tpr_arr->base, 16);
> +			if (!base) {
> +				pr_warn("TPR Instance %d, TPR No.%d disabling failure.\n",
> +				        i, j);
> +				continue;
> +			}
> +
> +			pr_info("TPR instance %d, TPR %d:base %llx limit %llx\n", i, j,
> +			        readq(base), readq(base + 1));
> +			writeq(readq(base) | BIT(4), base);
> +			if (tboot_tpr_enabled == false)
> +				tboot_tpr_enabled = true;
> +			iounmap(base);
> +		}
> +
> +		tpr_inst = (struct acpi_tpr_instance *)((u8*)tpr_inst +
> +		            sizeof(*tpr_inst) + j * sizeof(struct acpi_tpr_array));
> +	}
> +
> +	if (tboot_tpr_enabled)
> +		pr_debug("TPR protection detected, PMR will be disabled\n"); }
> +
> +bool tboot_is_tpr_enabled(void)
> +{
> +	return tboot_tpr_enabled;
> +}
> diff --git a/include/linux/tboot.h b/include/linux/tboot.h index d2279160ef39..39fb2e3ba80b 100644
> --- a/include/linux/tboot.h
> +++ b/include/linux/tboot.h
> @@ -24,6 +24,10 @@ enum {
>  #include <linux/acpi.h>
>  /* used to communicate between tboot and the launched kernel */
> 
> +/*TXT Extended Data Element Types*/
> +#define HEAP_EXTDATA_TYPE_END   0
> +#define HEAP_EXTDATA_TYPE_DTPR 14
> +
>  #define TB_KEY_SIZE             64   /* 512 bits */
> 
>  #define MAX_TB_MAC_REGIONS      32
> @@ -126,6 +130,9 @@ extern void tboot_probe(void);  extern void tboot_shutdown(u32 shutdown_type);  extern struct
> acpi_table_header *tboot_get_dmar_table(
>  				      struct acpi_table_header *dmar_tbl);
> +extern struct acpi_table_dtpr *tboot_get_dtpr_table(void **); extern
> +void tboot_parse_dtpr_table(struct acpi_table_dtpr *); extern bool
> +tboot_is_tpr_enabled(void);
> 
>  #else
> 
> @@ -135,6 +142,9 @@ extern struct acpi_table_header *tboot_get_dmar_table(
>  #define tboot_sleep(sleep_state, pm1a_control, pm1b_control)	\
>  					do { } while (0)
>  #define tboot_get_dmar_table(dmar_tbl)	(dmar_tbl)
> +#define tboot_get_dtpr_table(txt_heap) NULL #define
> +tboot_parse_dtpr_table(dtpr) do { } while (0) #define
> +tboot_is_tpr_enabled() 0
> 
>  #endif /* !CONFIG_INTEL_TXT */
> 
> --
> 2.55.0


^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH 1/1] x86/tboot: Add support for parsing DTPR table and disabling TPRs
  2026-09-15 23:23     ` Sun, Ning
@ 2026-09-17  9:41       ` Michal Camacho Romero
  2026-09-18  6:45         ` kernel test robot
  2026-09-18  7:35         ` kernel test robot
  2026-09-17  9:43       ` [PATCH v3 1/2] " Michal Camacho Romero
  1 sibling, 2 replies; 15+ messages in thread
From: Michal Camacho Romero @ 2026-09-17  9:41 UTC (permalink / raw)
  To: Ning Sun
  Cc: Baolu Lu, Thomas Gleixner, Michal Camacho Romero, x86, iommu,
	tboot-devel, linux-kernel, Mateusz Mowka, Adam Pawlicki,
	Pawel Randzio

From: Michal Camacho Romero <michal.camacho.romero@intel.com>

Add functions to locate and parse the DMA TXT Protection Ranges (DTPR)
table from the TXT heap's SinitMleData extended data elements (requires
SINIT MLE version >= 9).

* tboot_get_dtpr_table() - function walks through the TXT heap to find
                           the DTPR extended data element
                           (type HEAP_EXTDATA_TYPE_DTPR) and returns
                           pointer to the DTPR table.

* tboot_parse_dtpr_table() - function iterates over TPR instances and
                             disables each TPR region by setting bit 4
                             in the TPRn_BASE register via MMIO.

Using these functions will allow the kernel to deactivate SINIT
ACM-established TPRs prior to the Linux OS launch.

Link: https://uefi.org/sites/default/files/resources/633933_Intel_TXT_DMA_Protection_Ranges_rev_0p73.pdf
Link: https://cdrdv2-public.intel.com/315168/315168_TXT_MLE_DG_rev_017_7.pdf
Signed-off-by: Michal Camacho Romero <michal.camacho.romero@intel.com>
---
 arch/x86/kernel/tboot.c | 358 ++++++++++++++++++++++++++++++++++++++--
 include/linux/tboot.h   |  18 ++
 2 files changed, 361 insertions(+), 15 deletions(-)

diff --git a/arch/x86/kernel/tboot.c b/arch/x86/kernel/tboot.c
index 46b8f1f16676..f7ccd3e4e9d2 100644
--- a/arch/x86/kernel/tboot.c
+++ b/arch/x86/kernel/tboot.c
@@ -18,6 +18,7 @@
 #include <linux/mm.h>
 #include <linux/tboot.h>
 #include <linux/debugfs.h>
+#include <acpi/actbl1.h>
 
 #include <asm/realmode.h>
 #include <asm/processor.h>
@@ -223,6 +224,132 @@ static int tboot_setup_sleep(void)
 
 #endif
 
+static bool tboot_check_txt_heap_section_bounds(const u64 heap_end,
+                                                void **heap_base,
+                                                void *heap_section,
+                                                const u64 heap_section_size,
+                                                const char *section_name)
+{
+	if (heap_section_size < 8)
+	{
+		pr_err("%s size is too small\n", section_name);
+		iounmap(*heap_base);
+		*heap_base = NULL;
+		return false;
+	}
+
+	if ((u64)heap_section + heap_section_size > heap_end) {
+		pr_err("%s exceeds heap boundary\n", section_name);
+		iounmap(*heap_base);
+		*heap_base = NULL;
+		return false;
+	}
+
+	return true;
+}
+
+static bool tboot_check_dtpr_size(const struct acpi_table_dtpr *dtpr,
+                                  const u64 dtpr_payload_size)
+{
+	u64 dtpr_offset, dtpr_ref_size;
+	u32 i, j, ref_tpr_cnt;
+
+	struct acpi_tpr_instance *tpr_inst    = NULL;
+	struct acpi_tpr_aux_sr   *tpr_aux_srl = NULL;
+
+	if (!dtpr)
+		return false;
+
+	if (dtpr_payload_size < sizeof(struct acpi_table_dtpr)) {
+		pr_err("DTPR element payload too small for a DTPR header\n");
+		return false;
+	}
+
+	dtpr_ref_size = dtpr->header.length;
+	dtpr_offset   = 0;
+
+	if (dtpr_ref_size < sizeof(struct acpi_table_dtpr)) {
+		pr_err("DTPR table header exceeds expected size\n");
+		return false;
+	}
+
+	dtpr_offset += sizeof(struct acpi_table_dtpr);
+	tpr_inst = (struct acpi_tpr_instance *)((u8 *)dtpr + dtpr_offset);
+	if (dtpr_offset + sizeof(struct acpi_tpr_instance) > dtpr_ref_size) {
+		pr_err("TPR instance No.0 header exceeds DTPR table size\n");
+		return false;
+	}
+
+	ref_tpr_cnt = tpr_inst->tpr_cnt;
+	if (ref_tpr_cnt < 2) {
+		pr_err("Reference TPR count is less than 2, further DTPR processing "
+		       "interrupted.\n");
+		return false;
+	}
+
+	for (i = 0; i < dtpr->ins_cnt; i++) {
+		/* iterate over each TPR instance */
+		dtpr_offset += sizeof(struct acpi_tpr_instance);
+		if (dtpr_offset > dtpr_ref_size && i != 0) {
+			pr_err("TPR instance No.%d header exceeds DTPR table size\n", i);
+			return false;
+		}
+
+		/* verify TPR count for the given Instance. It should be 2 at least*/
+		if (tpr_inst->tpr_cnt < 2 && i != 0) {
+			pr_err("TPR Instance %d has less than 2 TPRs, further DTPR "
+			       "processing interrupted.\n", i);
+			return false;
+		}
+
+		/* compare TPR count for the given Instance with the expected one */
+		/* each TPR Instance should have the equal number of TPRs */
+		if (tpr_inst->tpr_cnt != ref_tpr_cnt && i != 0) {
+			pr_err("TPR Instance %d has inconsistent TPR count: expected %d,"
+			       " found %d\n", i, ref_tpr_cnt, tpr_inst->tpr_cnt);
+			return false;
+		}
+
+		/* verify TPR array size for this instance */
+		dtpr_offset += tpr_inst->tpr_cnt * sizeof(struct acpi_tpr_array);
+		if (dtpr_offset > dtpr_ref_size) {
+			pr_err("TPR instance No.%d TPR entries exceed DTPR table size\n",
+			       i);
+			return false;
+		}
+
+		/* move to the next TPR instance */
+		tpr_inst = (struct acpi_tpr_instance *)((u8 *)dtpr + dtpr_offset);
+	}
+
+	tpr_aux_srl = (struct acpi_tpr_aux_sr *)((u8 *)dtpr + dtpr_offset);
+	dtpr_offset += sizeof(u32);
+
+	if (dtpr_offset > dtpr_ref_size) {
+		pr_err("TPR SRL count field exceeds DTPR table size\n");
+		return false;
+	}
+
+	dtpr_offset += tpr_aux_srl->srl_cnt *
+	               sizeof(struct acpi_tpr_serialize_request);
+	if (dtpr_offset > dtpr_ref_size) {
+		pr_err("TPR SRL entries exceed DTPR table size\n");
+		return false;
+	}
+
+	if (dtpr_offset < dtpr_ref_size) {
+		pr_err("DTPR table is smaller than expected\n");
+		return false;
+	}
+
+	if (dtpr_offset != dtpr_payload_size) {
+		pr_err("DTPR table size mismatch\n");
+		return false;
+	}
+
+	return true;
+}
+
 void tboot_shutdown(u32 shutdown_type)
 {
 	void (*shutdown)(void);
@@ -453,22 +580,30 @@ struct sha1_hash {
 	u8 hash[SHA1_SIZE];
 };
 
+struct heap_ext_data_elt {
+	u32 type;
+	u32 size;
+	u8  data[];
+} __packed;
+
 struct sinit_mle_data {
-	u32               version;             /* currently 6 */
-	struct sha1_hash  bios_acm_id;
-	u32               edx_senter_flags;
-	u64               mseg_valid;
-	struct sha1_hash  sinit_hash;
-	struct sha1_hash  mle_hash;
-	struct sha1_hash  stm_hash;
-	struct sha1_hash  lcp_policy_hash;
-	u32               lcp_policy_control;
-	u32               rlp_wakeup_addr;
-	u32               reserved;
-	u32               num_mdrs;
-	u32               mdrs_off;
-	u32               num_vtd_dmars;
-	u32               vtd_dmars_off;
+	u32                      version;             /* currently 9 */
+	struct sha1_hash         bios_acm_id;
+	u32                      edx_senter_flags;
+	u64                      mseg_valid;
+	struct sha1_hash         sinit_hash;
+	struct sha1_hash         mle_hash;
+	struct sha1_hash         stm_hash;
+	struct sha1_hash         lcp_policy_hash;
+	u32                      lcp_policy_control;
+	u32                      rlp_wakeup_addr;
+	u32                      reserved;
+	u32                      num_mdrs;
+	u32                      mdrs_off;
+	u32                      num_vtd_dmars;
+	u32                      vtd_dmars_off;
+	u32                      proc_scrtm_status; /* version 8 or later only*/
+	struct heap_ext_data_elt ext_data_elts[];
 } __packed;
 
 struct acpi_table_header *tboot_get_dmar_table(struct acpi_table_header *dmar_tbl)
@@ -514,3 +649,196 @@ struct acpi_table_header *tboot_get_dmar_table(struct acpi_table_header *dmar_tb
 
 	return dmar_tbl;
 }
+
+struct acpi_table_dtpr *tboot_get_dtpr_table(void **heap_base)
+{
+	void *heap_ptr, *config, *sinit_mle_end;
+	struct sinit_mle_data *sinit_mle;
+	struct heap_ext_data_elt *elt;
+	u64 heap_end, heap_size, sinit_mle_size, heap_section_size;
+
+	if (!heap_base)
+		return NULL;
+
+	if (!tboot_enabled())
+		return NULL;
+	/*
+	 * ACPI tables may not be DMA protected by tboot, so use DMAR copy
+	 * SINIT saved in SinitMleData in TXT heap (which is DMA protected)
+	 */
+
+	/* map config space in order to get heap addr */
+	config = ioremap(TXT_PUB_CONFIG_REGS_BASE, NR_TXT_CONFIG_PAGES *
+			 PAGE_SIZE);
+	if (!config)
+		return NULL;
+
+	/* now map TXT heap */
+	*heap_base = ioremap(*(u64 *)(config + TXTCR_HEAP_BASE),
+			    *(u64 *)(config + TXTCR_HEAP_SIZE));
+	heap_size = *(u64 *)(config + TXTCR_HEAP_SIZE);
+	heap_end = (u64)*heap_base + heap_size;
+	iounmap(config);
+
+	if (!(*heap_base))
+		return NULL;
+
+	/* walk heap to SinitMleData */
+	/* skip BiosData */
+	/* get BiosData section size */
+	heap_section_size = *(u64 *) (*heap_base);
+	if (!tboot_check_txt_heap_section_bounds(heap_end, heap_base, *heap_base,
+	                                         heap_section_size, "BiosData")) {
+		return NULL;
+	}
+
+	/* skip OsMleData */
+	heap_ptr = *heap_base + heap_section_size;
+	/* get OsMleData section size */
+	heap_section_size = *(u64 *)heap_ptr;
+	if (!tboot_check_txt_heap_section_bounds(heap_end, heap_base, heap_ptr,
+	                                         heap_section_size, "OsMleData")) {
+		return NULL;
+	}
+
+	/* skip OsSinitData */
+	heap_ptr += heap_section_size;
+	/* get OsSinitData section size */
+	heap_section_size = *(u64 *)heap_ptr;
+	if (!tboot_check_txt_heap_section_bounds(heap_end, heap_base, heap_ptr,
+	                                         heap_section_size, "OsSinitData")) {
+		return NULL;
+	}
+
+	/* jump to the SinitMleData */
+	heap_ptr += heap_section_size;
+	/* now points to SinitMleDataSize; set to SinitMleData */
+	sinit_mle_size = *(u64 *)heap_ptr;
+	if(!tboot_check_txt_heap_section_bounds(heap_end, heap_base, heap_ptr,
+	                                        sinit_mle_size, "SinitMleData")) {
+		return NULL;
+	}
+
+	heap_ptr += sizeof(u64);
+	sinit_mle = (struct sinit_mle_data *)heap_ptr;
+	sinit_mle_end = (void *)sinit_mle + sinit_mle_size;
+	if (sizeof(struct sinit_mle_data) > sinit_mle_size) {
+		pr_err("SinitMleData size is smaller than expected.\n");
+		goto err;
+	}
+
+	if (sinit_mle->version < 9) {
+		pr_err("Unsupported SinitMleData version: %u\n", sinit_mle->version);
+		goto err;
+	}
+
+	heap_ptr += sizeof(struct sinit_mle_data);
+	if (heap_ptr > sinit_mle_end) {
+		pr_err("SinitMleData header out of bounds.\n");
+		goto err;
+	}
+
+	elt = sinit_mle->ext_data_elts;
+	do {
+		if ((u8 *)elt + sizeof(*elt) > (u8 *)sinit_mle_end) {
+			pr_err("SinitMleData element header out of bounds.\n");
+			goto err;
+		}
+
+		if (elt->size < sizeof(*elt)) {
+			pr_err("Invalid SinitMleData element size: %u\n", elt->size);
+			goto err;
+		}
+
+		if (elt->type == HEAP_EXTDATA_TYPE_END || elt->type == HEAP_EXTDATA_TYPE_DTPR) {
+			break;
+		}
+
+		elt = (void *)elt + elt->size;
+	} while (elt <= sinit_mle_end);
+
+	if (elt >= sinit_mle_end){
+		pr_err("Reached the end of SinitMleData without finding DTPR nor END"
+		       " element.\n");
+		goto err;
+	}
+
+	if (elt->type == HEAP_EXTDATA_TYPE_END) {
+		pr_err("DTPR element not found in SinitMleData\n");
+		iounmap(*heap_base);
+		*heap_base = NULL;
+		return NULL;
+	}
+
+	if ((u8 *)elt + elt->size > (u8 *)sinit_mle_end) {
+		pr_err("DTPR Table exceeds SinitMleData bounds.\n");
+		goto err;
+	}
+
+	if (!tboot_check_dtpr_size((struct acpi_table_dtpr *)elt->data,
+	                           elt->size - sizeof(*elt))) {
+		pr_err("Invalid DTPR Table size.\n");
+		goto err;
+	}
+
+	return (struct acpi_table_dtpr *)elt->data;
+
+err:
+	iounmap(*heap_base);
+	*heap_base = NULL;
+	return NULL;
+}
+
+static bool tboot_tpr_enabled = false;
+void tboot_parse_dtpr_table(struct acpi_table_dtpr *dtpr)
+{
+	struct acpi_tpr_instance *tpr_inst;
+	struct acpi_tpr_array    *tpr_arr;
+	u32 *instance_cnt;
+	u64 *base;
+	u32 i, j;
+	u32 ref_tpr_cnt;
+
+	if (dtpr == NULL)
+		return;
+
+	if (!tboot_enabled())
+		return;
+
+	instance_cnt = (u32*)(&dtpr->ins_cnt);
+	tpr_inst = (struct acpi_tpr_instance *)(instance_cnt + 1);
+	ref_tpr_cnt = tpr_inst->tpr_cnt;
+
+	for (i = 0; i < *instance_cnt; ++i) {
+		for (j = 0; j < tpr_inst->tpr_cnt; ++j) {
+			tpr_arr =  (struct acpi_tpr_array*)((u8*) tpr_inst +
+			            sizeof(struct acpi_tpr_instance) +
+			            j * sizeof(struct acpi_tpr_array));
+
+			base = ioremap(tpr_arr->base, 16);
+			if (!base) {
+				pr_warn("TPR Instance %d, TPR No.%d disabling failure.\n",
+				        i, j);
+				continue;
+			}
+
+			pr_info("TPR instance %d, TPR %d:base %llx limit %llx\n", i, j,
+			        readq(base), readq(base + 1));
+			writeq(readq(base) | BIT(4), base);
+			if (tboot_tpr_enabled == false)
+				tboot_tpr_enabled = true;
+			iounmap(base);
+		}
+
+		tpr_inst = (struct acpi_tpr_instance *)((u8*)tpr_inst +
+		            sizeof(*tpr_inst) + j * sizeof(struct acpi_tpr_array));
+	}
+
+	if (tboot_tpr_enabled)
+		pr_debug("TPR protection detected, PMR will be disabled\n");
+}
+
+bool tboot_is_tpr_enabled(void)
+{
+	return tboot_tpr_enabled;
+}
diff --git a/include/linux/tboot.h b/include/linux/tboot.h
index d2279160ef39..7523f8ecdf70 100644
--- a/include/linux/tboot.h
+++ b/include/linux/tboot.h
@@ -24,6 +24,10 @@ enum {
 #include <linux/acpi.h>
 /* used to communicate between tboot and the launched kernel */
 
+/*TXT Extended Data Element Types*/
+#define HEAP_EXTDATA_TYPE_END   0
+#define HEAP_EXTDATA_TYPE_DTPR 14
+
 #define TB_KEY_SIZE             64   /* 512 bits */
 
 #define MAX_TB_MAC_REGIONS      32
@@ -58,6 +62,14 @@ struct tboot_acpi_sleep_info {
 	u64 kernel_s3_resume_vector;
 } __packed;
 
+/*
+ * structure for tboot extended data elements
+ */
+struct heap_ext_data_elt {
+	u32 type;
+	u32 size;
+} __packed;
+
 /*
  * shared memory page used for communication between tboot and kernel
  */
@@ -126,6 +138,9 @@ extern void tboot_probe(void);
 extern void tboot_shutdown(u32 shutdown_type);
 extern struct acpi_table_header *tboot_get_dmar_table(
 				      struct acpi_table_header *dmar_tbl);
+extern struct acpi_table_dtpr *tboot_get_dtpr_table(void **);
+extern void tboot_parse_dtpr_table(struct acpi_table_dtpr *);
+extern bool tboot_is_tpr_enabled(void);
 
 #else
 
@@ -135,6 +150,9 @@ extern struct acpi_table_header *tboot_get_dmar_table(
 #define tboot_sleep(sleep_state, pm1a_control, pm1b_control)	\
 					do { } while (0)
 #define tboot_get_dmar_table(dmar_tbl)	(dmar_tbl)
+#define tboot_get_dtpr_table(txt_heap) NULL
+#define tboot_parse_dtpr_table(dtpr) do { } while (0)
+#define tboot_is_tpr_enabled() 0
 
 #endif /* !CONFIG_INTEL_TXT */
 
-- 
2.55.0


^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH v3 1/2] x86/tboot: Add support for parsing DTPR table and disabling TPRs
  2026-09-15 23:23     ` Sun, Ning
  2026-09-17  9:41       ` [PATCH 1/1] " Michal Camacho Romero
@ 2026-09-17  9:43       ` Michal Camacho Romero
  1 sibling, 0 replies; 15+ messages in thread
From: Michal Camacho Romero @ 2026-09-17  9:43 UTC (permalink / raw)
  To: Ning Sun
  Cc: Baolu Lu, Thomas Gleixner, Michal Camacho Romero, x86, iommu,
	tboot-devel, linux-kernel, Mateusz Mowka, Adam Pawlicki,
	Pawel Randzio

From: Michal Camacho Romero <michal.camacho.romero@intel.com>

Add functions to locate and parse the DMA TXT Protection Ranges (DTPR)
table from the TXT heap's SinitMleData extended data elements (requires
SINIT MLE version >= 9).

* tboot_get_dtpr_table() - function walks through the TXT heap to find
                           the DTPR extended data element
                           (type HEAP_EXTDATA_TYPE_DTPR) and returns
                           pointer to the DTPR table.

* tboot_parse_dtpr_table() - function iterates over TPR instances and
                             disables each TPR region by setting bit 4
                             in the TPRn_BASE register via MMIO.

Using these functions will allow the kernel to deactivate SINIT
ACM-established TPRs prior to the Linux OS launch.

Link: https://uefi.org/sites/default/files/resources/633933_Intel_TXT_DMA_Protection_Ranges_rev_0p73.pdf
Link: https://cdrdv2-public.intel.com/315168/315168_TXT_MLE_DG_rev_017_7.pdf
Signed-off-by: Michal Camacho Romero <michal.camacho.romero@intel.com>
---
 arch/x86/kernel/tboot.c | 358 ++++++++++++++++++++++++++++++++++++++--
 include/linux/tboot.h   |  18 ++
 2 files changed, 361 insertions(+), 15 deletions(-)

diff --git a/arch/x86/kernel/tboot.c b/arch/x86/kernel/tboot.c
index 46b8f1f16676..f7ccd3e4e9d2 100644
--- a/arch/x86/kernel/tboot.c
+++ b/arch/x86/kernel/tboot.c
@@ -18,6 +18,7 @@
 #include <linux/mm.h>
 #include <linux/tboot.h>
 #include <linux/debugfs.h>
+#include <acpi/actbl1.h>
 
 #include <asm/realmode.h>
 #include <asm/processor.h>
@@ -223,6 +224,132 @@ static int tboot_setup_sleep(void)
 
 #endif
 
+static bool tboot_check_txt_heap_section_bounds(const u64 heap_end,
+                                                void **heap_base,
+                                                void *heap_section,
+                                                const u64 heap_section_size,
+                                                const char *section_name)
+{
+	if (heap_section_size < 8)
+	{
+		pr_err("%s size is too small\n", section_name);
+		iounmap(*heap_base);
+		*heap_base = NULL;
+		return false;
+	}
+
+	if ((u64)heap_section + heap_section_size > heap_end) {
+		pr_err("%s exceeds heap boundary\n", section_name);
+		iounmap(*heap_base);
+		*heap_base = NULL;
+		return false;
+	}
+
+	return true;
+}
+
+static bool tboot_check_dtpr_size(const struct acpi_table_dtpr *dtpr,
+                                  const u64 dtpr_payload_size)
+{
+	u64 dtpr_offset, dtpr_ref_size;
+	u32 i, j, ref_tpr_cnt;
+
+	struct acpi_tpr_instance *tpr_inst    = NULL;
+	struct acpi_tpr_aux_sr   *tpr_aux_srl = NULL;
+
+	if (!dtpr)
+		return false;
+
+	if (dtpr_payload_size < sizeof(struct acpi_table_dtpr)) {
+		pr_err("DTPR element payload too small for a DTPR header\n");
+		return false;
+	}
+
+	dtpr_ref_size = dtpr->header.length;
+	dtpr_offset   = 0;
+
+	if (dtpr_ref_size < sizeof(struct acpi_table_dtpr)) {
+		pr_err("DTPR table header exceeds expected size\n");
+		return false;
+	}
+
+	dtpr_offset += sizeof(struct acpi_table_dtpr);
+	tpr_inst = (struct acpi_tpr_instance *)((u8 *)dtpr + dtpr_offset);
+	if (dtpr_offset + sizeof(struct acpi_tpr_instance) > dtpr_ref_size) {
+		pr_err("TPR instance No.0 header exceeds DTPR table size\n");
+		return false;
+	}
+
+	ref_tpr_cnt = tpr_inst->tpr_cnt;
+	if (ref_tpr_cnt < 2) {
+		pr_err("Reference TPR count is less than 2, further DTPR processing "
+		       "interrupted.\n");
+		return false;
+	}
+
+	for (i = 0; i < dtpr->ins_cnt; i++) {
+		/* iterate over each TPR instance */
+		dtpr_offset += sizeof(struct acpi_tpr_instance);
+		if (dtpr_offset > dtpr_ref_size && i != 0) {
+			pr_err("TPR instance No.%d header exceeds DTPR table size\n", i);
+			return false;
+		}
+
+		/* verify TPR count for the given Instance. It should be 2 at least*/
+		if (tpr_inst->tpr_cnt < 2 && i != 0) {
+			pr_err("TPR Instance %d has less than 2 TPRs, further DTPR "
+			       "processing interrupted.\n", i);
+			return false;
+		}
+
+		/* compare TPR count for the given Instance with the expected one */
+		/* each TPR Instance should have the equal number of TPRs */
+		if (tpr_inst->tpr_cnt != ref_tpr_cnt && i != 0) {
+			pr_err("TPR Instance %d has inconsistent TPR count: expected %d,"
+			       " found %d\n", i, ref_tpr_cnt, tpr_inst->tpr_cnt);
+			return false;
+		}
+
+		/* verify TPR array size for this instance */
+		dtpr_offset += tpr_inst->tpr_cnt * sizeof(struct acpi_tpr_array);
+		if (dtpr_offset > dtpr_ref_size) {
+			pr_err("TPR instance No.%d TPR entries exceed DTPR table size\n",
+			       i);
+			return false;
+		}
+
+		/* move to the next TPR instance */
+		tpr_inst = (struct acpi_tpr_instance *)((u8 *)dtpr + dtpr_offset);
+	}
+
+	tpr_aux_srl = (struct acpi_tpr_aux_sr *)((u8 *)dtpr + dtpr_offset);
+	dtpr_offset += sizeof(u32);
+
+	if (dtpr_offset > dtpr_ref_size) {
+		pr_err("TPR SRL count field exceeds DTPR table size\n");
+		return false;
+	}
+
+	dtpr_offset += tpr_aux_srl->srl_cnt *
+	               sizeof(struct acpi_tpr_serialize_request);
+	if (dtpr_offset > dtpr_ref_size) {
+		pr_err("TPR SRL entries exceed DTPR table size\n");
+		return false;
+	}
+
+	if (dtpr_offset < dtpr_ref_size) {
+		pr_err("DTPR table is smaller than expected\n");
+		return false;
+	}
+
+	if (dtpr_offset != dtpr_payload_size) {
+		pr_err("DTPR table size mismatch\n");
+		return false;
+	}
+
+	return true;
+}
+
 void tboot_shutdown(u32 shutdown_type)
 {
 	void (*shutdown)(void);
@@ -453,22 +580,30 @@ struct sha1_hash {
 	u8 hash[SHA1_SIZE];
 };
 
+struct heap_ext_data_elt {
+	u32 type;
+	u32 size;
+	u8  data[];
+} __packed;
+
 struct sinit_mle_data {
-	u32               version;             /* currently 6 */
-	struct sha1_hash  bios_acm_id;
-	u32               edx_senter_flags;
-	u64               mseg_valid;
-	struct sha1_hash  sinit_hash;
-	struct sha1_hash  mle_hash;
-	struct sha1_hash  stm_hash;
-	struct sha1_hash  lcp_policy_hash;
-	u32               lcp_policy_control;
-	u32               rlp_wakeup_addr;
-	u32               reserved;
-	u32               num_mdrs;
-	u32               mdrs_off;
-	u32               num_vtd_dmars;
-	u32               vtd_dmars_off;
+	u32                      version;             /* currently 9 */
+	struct sha1_hash         bios_acm_id;
+	u32                      edx_senter_flags;
+	u64                      mseg_valid;
+	struct sha1_hash         sinit_hash;
+	struct sha1_hash         mle_hash;
+	struct sha1_hash         stm_hash;
+	struct sha1_hash         lcp_policy_hash;
+	u32                      lcp_policy_control;
+	u32                      rlp_wakeup_addr;
+	u32                      reserved;
+	u32                      num_mdrs;
+	u32                      mdrs_off;
+	u32                      num_vtd_dmars;
+	u32                      vtd_dmars_off;
+	u32                      proc_scrtm_status; /* version 8 or later only*/
+	struct heap_ext_data_elt ext_data_elts[];
 } __packed;
 
 struct acpi_table_header *tboot_get_dmar_table(struct acpi_table_header *dmar_tbl)
@@ -514,3 +649,196 @@ struct acpi_table_header *tboot_get_dmar_table(struct acpi_table_header *dmar_tb
 
 	return dmar_tbl;
 }
+
+struct acpi_table_dtpr *tboot_get_dtpr_table(void **heap_base)
+{
+	void *heap_ptr, *config, *sinit_mle_end;
+	struct sinit_mle_data *sinit_mle;
+	struct heap_ext_data_elt *elt;
+	u64 heap_end, heap_size, sinit_mle_size, heap_section_size;
+
+	if (!heap_base)
+		return NULL;
+
+	if (!tboot_enabled())
+		return NULL;
+	/*
+	 * ACPI tables may not be DMA protected by tboot, so use DMAR copy
+	 * SINIT saved in SinitMleData in TXT heap (which is DMA protected)
+	 */
+
+	/* map config space in order to get heap addr */
+	config = ioremap(TXT_PUB_CONFIG_REGS_BASE, NR_TXT_CONFIG_PAGES *
+			 PAGE_SIZE);
+	if (!config)
+		return NULL;
+
+	/* now map TXT heap */
+	*heap_base = ioremap(*(u64 *)(config + TXTCR_HEAP_BASE),
+			    *(u64 *)(config + TXTCR_HEAP_SIZE));
+	heap_size = *(u64 *)(config + TXTCR_HEAP_SIZE);
+	heap_end = (u64)*heap_base + heap_size;
+	iounmap(config);
+
+	if (!(*heap_base))
+		return NULL;
+
+	/* walk heap to SinitMleData */
+	/* skip BiosData */
+	/* get BiosData section size */
+	heap_section_size = *(u64 *) (*heap_base);
+	if (!tboot_check_txt_heap_section_bounds(heap_end, heap_base, *heap_base,
+	                                         heap_section_size, "BiosData")) {
+		return NULL;
+	}
+
+	/* skip OsMleData */
+	heap_ptr = *heap_base + heap_section_size;
+	/* get OsMleData section size */
+	heap_section_size = *(u64 *)heap_ptr;
+	if (!tboot_check_txt_heap_section_bounds(heap_end, heap_base, heap_ptr,
+	                                         heap_section_size, "OsMleData")) {
+		return NULL;
+	}
+
+	/* skip OsSinitData */
+	heap_ptr += heap_section_size;
+	/* get OsSinitData section size */
+	heap_section_size = *(u64 *)heap_ptr;
+	if (!tboot_check_txt_heap_section_bounds(heap_end, heap_base, heap_ptr,
+	                                         heap_section_size, "OsSinitData")) {
+		return NULL;
+	}
+
+	/* jump to the SinitMleData */
+	heap_ptr += heap_section_size;
+	/* now points to SinitMleDataSize; set to SinitMleData */
+	sinit_mle_size = *(u64 *)heap_ptr;
+	if(!tboot_check_txt_heap_section_bounds(heap_end, heap_base, heap_ptr,
+	                                        sinit_mle_size, "SinitMleData")) {
+		return NULL;
+	}
+
+	heap_ptr += sizeof(u64);
+	sinit_mle = (struct sinit_mle_data *)heap_ptr;
+	sinit_mle_end = (void *)sinit_mle + sinit_mle_size;
+	if (sizeof(struct sinit_mle_data) > sinit_mle_size) {
+		pr_err("SinitMleData size is smaller than expected.\n");
+		goto err;
+	}
+
+	if (sinit_mle->version < 9) {
+		pr_err("Unsupported SinitMleData version: %u\n", sinit_mle->version);
+		goto err;
+	}
+
+	heap_ptr += sizeof(struct sinit_mle_data);
+	if (heap_ptr > sinit_mle_end) {
+		pr_err("SinitMleData header out of bounds.\n");
+		goto err;
+	}
+
+	elt = sinit_mle->ext_data_elts;
+	do {
+		if ((u8 *)elt + sizeof(*elt) > (u8 *)sinit_mle_end) {
+			pr_err("SinitMleData element header out of bounds.\n");
+			goto err;
+		}
+
+		if (elt->size < sizeof(*elt)) {
+			pr_err("Invalid SinitMleData element size: %u\n", elt->size);
+			goto err;
+		}
+
+		if (elt->type == HEAP_EXTDATA_TYPE_END || elt->type == HEAP_EXTDATA_TYPE_DTPR) {
+			break;
+		}
+
+		elt = (void *)elt + elt->size;
+	} while (elt <= sinit_mle_end);
+
+	if (elt >= sinit_mle_end){
+		pr_err("Reached the end of SinitMleData without finding DTPR nor END"
+		       " element.\n");
+		goto err;
+	}
+
+	if (elt->type == HEAP_EXTDATA_TYPE_END) {
+		pr_err("DTPR element not found in SinitMleData\n");
+		iounmap(*heap_base);
+		*heap_base = NULL;
+		return NULL;
+	}
+
+	if ((u8 *)elt + elt->size > (u8 *)sinit_mle_end) {
+		pr_err("DTPR Table exceeds SinitMleData bounds.\n");
+		goto err;
+	}
+
+	if (!tboot_check_dtpr_size((struct acpi_table_dtpr *)elt->data,
+	                           elt->size - sizeof(*elt))) {
+		pr_err("Invalid DTPR Table size.\n");
+		goto err;
+	}
+
+	return (struct acpi_table_dtpr *)elt->data;
+
+err:
+	iounmap(*heap_base);
+	*heap_base = NULL;
+	return NULL;
+}
+
+static bool tboot_tpr_enabled = false;
+void tboot_parse_dtpr_table(struct acpi_table_dtpr *dtpr)
+{
+	struct acpi_tpr_instance *tpr_inst;
+	struct acpi_tpr_array    *tpr_arr;
+	u32 *instance_cnt;
+	u64 *base;
+	u32 i, j;
+	u32 ref_tpr_cnt;
+
+	if (dtpr == NULL)
+		return;
+
+	if (!tboot_enabled())
+		return;
+
+	instance_cnt = (u32*)(&dtpr->ins_cnt);
+	tpr_inst = (struct acpi_tpr_instance *)(instance_cnt + 1);
+	ref_tpr_cnt = tpr_inst->tpr_cnt;
+
+	for (i = 0; i < *instance_cnt; ++i) {
+		for (j = 0; j < tpr_inst->tpr_cnt; ++j) {
+			tpr_arr =  (struct acpi_tpr_array*)((u8*) tpr_inst +
+			            sizeof(struct acpi_tpr_instance) +
+			            j * sizeof(struct acpi_tpr_array));
+
+			base = ioremap(tpr_arr->base, 16);
+			if (!base) {
+				pr_warn("TPR Instance %d, TPR No.%d disabling failure.\n",
+				        i, j);
+				continue;
+			}
+
+			pr_info("TPR instance %d, TPR %d:base %llx limit %llx\n", i, j,
+			        readq(base), readq(base + 1));
+			writeq(readq(base) | BIT(4), base);
+			if (tboot_tpr_enabled == false)
+				tboot_tpr_enabled = true;
+			iounmap(base);
+		}
+
+		tpr_inst = (struct acpi_tpr_instance *)((u8*)tpr_inst +
+		            sizeof(*tpr_inst) + j * sizeof(struct acpi_tpr_array));
+	}
+
+	if (tboot_tpr_enabled)
+		pr_debug("TPR protection detected, PMR will be disabled\n");
+}
+
+bool tboot_is_tpr_enabled(void)
+{
+	return tboot_tpr_enabled;
+}
diff --git a/include/linux/tboot.h b/include/linux/tboot.h
index d2279160ef39..7523f8ecdf70 100644
--- a/include/linux/tboot.h
+++ b/include/linux/tboot.h
@@ -24,6 +24,10 @@ enum {
 #include <linux/acpi.h>
 /* used to communicate between tboot and the launched kernel */
 
+/*TXT Extended Data Element Types*/
+#define HEAP_EXTDATA_TYPE_END   0
+#define HEAP_EXTDATA_TYPE_DTPR 14
+
 #define TB_KEY_SIZE             64   /* 512 bits */
 
 #define MAX_TB_MAC_REGIONS      32
@@ -58,6 +62,14 @@ struct tboot_acpi_sleep_info {
 	u64 kernel_s3_resume_vector;
 } __packed;
 
+/*
+ * structure for tboot extended data elements
+ */
+struct heap_ext_data_elt {
+	u32 type;
+	u32 size;
+} __packed;
+
 /*
  * shared memory page used for communication between tboot and kernel
  */
@@ -126,6 +138,9 @@ extern void tboot_probe(void);
 extern void tboot_shutdown(u32 shutdown_type);
 extern struct acpi_table_header *tboot_get_dmar_table(
 				      struct acpi_table_header *dmar_tbl);
+extern struct acpi_table_dtpr *tboot_get_dtpr_table(void **);
+extern void tboot_parse_dtpr_table(struct acpi_table_dtpr *);
+extern bool tboot_is_tpr_enabled(void);
 
 #else
 
@@ -135,6 +150,9 @@ extern struct acpi_table_header *tboot_get_dmar_table(
 #define tboot_sleep(sleep_state, pm1a_control, pm1b_control)	\
 					do { } while (0)
 #define tboot_get_dmar_table(dmar_tbl)	(dmar_tbl)
+#define tboot_get_dtpr_table(txt_heap) NULL
+#define tboot_parse_dtpr_table(dtpr) do { } while (0)
+#define tboot_is_tpr_enabled() 0
 
 #endif /* !CONFIG_INTEL_TXT */
 
-- 
2.55.0


^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 1/1] x86/tboot: Add support for parsing DTPR table and disabling TPRs
  2026-09-17  9:41       ` [PATCH 1/1] " Michal Camacho Romero
@ 2026-09-18  6:45         ` kernel test robot
  2026-09-18  7:35         ` kernel test robot
  1 sibling, 0 replies; 15+ messages in thread
From: kernel test robot @ 2026-09-18  6:45 UTC (permalink / raw)
  To: Michal Camacho Romero, Ning Sun
  Cc: llvm, oe-kbuild-all, Baolu Lu, Thomas Gleixner,
	Michal Camacho Romero, x86, iommu, tboot-devel, linux-kernel,
	Mateusz Mowka, Adam Pawlicki, Pawel Randzio

Hi Michal,

kernel test robot noticed the following build errors:

[auto build test ERROR on tip/x86/core]
[also build test ERROR on tip/master linus/master v7.3-rc3 next-20260916]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Michal-Camacho-Romero/x86-tboot-Add-support-for-parsing-DTPR-table-and-disabling-TPRs/20260917-114133
base:   tip/x86/core
patch link:    https://lore.kernel.org/r/20260917094133.765149-1-michal.camacho.romero%40linux.intel.com
patch subject: [PATCH 1/1] x86/tboot: Add support for parsing DTPR table and disabling TPRs
config: x86_64-rhel-9.4-rust (https://download.01.org/0day-ci/archive/20260918/202609181428.YizH4EcD-lkp@intel.com/config)
compiler: clang version 22.1.3 (https://github.com/llvm/llvm-project e9846648fd6183ee6d8cbdb4502213fcf902a211)
rustc: rustc 1.96.0 (ac68faa20 2026-05-25)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260918/202609181428.YizH4EcD-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202609181428.YizH4EcD-lkp@intel.com/

All error/warnings (new ones prefixed by >>):

   arch/x86/kernel/tboot.c:255:9: warning: unused variable 'j' [-Wunused-variable]
     255 |         u32 i, j, ref_tpr_cnt;
         |                ^
>> arch/x86/kernel/tboot.c:583:8: error: redefinition of 'heap_ext_data_elt'
     583 | struct heap_ext_data_elt {
         |        ^
   include/linux/tboot.h:68:8: note: previous definition is here
      68 | struct heap_ext_data_elt {
         |        ^
>> arch/x86/kernel/tboot.c:758:15: warning: comparison of distinct pointer types ('struct heap_ext_data_elt *' and 'void *') [-Wcompare-distinct-pointer-types]
     758 |         } while (elt <= sinit_mle_end);
         |                  ~~~ ^  ~~~~~~~~~~~~~
   arch/x86/kernel/tboot.c:760:10: warning: comparison of distinct pointer types ('struct heap_ext_data_elt *' and 'void *') [-Wcompare-distinct-pointer-types]
     760 |         if (elt >= sinit_mle_end){
         |             ~~~ ^  ~~~~~~~~~~~~~
>> arch/x86/kernel/tboot.c:778:60: error: no member named 'data' in 'struct heap_ext_data_elt'
     778 |         if (!tboot_check_dtpr_size((struct acpi_table_dtpr *)elt->data,
         |                                                              ~~~  ^
   arch/x86/kernel/tboot.c:784:40: error: no member named 'data' in 'struct heap_ext_data_elt'
     784 |         return (struct acpi_table_dtpr *)elt->data;
         |                                          ~~~  ^
   arch/x86/kernel/tboot.c:800:6: warning: variable 'ref_tpr_cnt' set but not used [-Wunused-but-set-variable]
     800 |         u32 ref_tpr_cnt;
         |             ^
   4 warnings and 3 errors generated.


vim +/heap_ext_data_elt +583 arch/x86/kernel/tboot.c

   582	
 > 583	struct heap_ext_data_elt {
   584		u32 type;
   585		u32 size;
   586		u8  data[];
   587	} __packed;
   588	
   589	struct sinit_mle_data {
   590		u32                      version;             /* currently 9 */
   591		struct sha1_hash         bios_acm_id;
   592		u32                      edx_senter_flags;
   593		u64                      mseg_valid;
   594		struct sha1_hash         sinit_hash;
   595		struct sha1_hash         mle_hash;
   596		struct sha1_hash         stm_hash;
   597		struct sha1_hash         lcp_policy_hash;
   598		u32                      lcp_policy_control;
   599		u32                      rlp_wakeup_addr;
   600		u32                      reserved;
   601		u32                      num_mdrs;
   602		u32                      mdrs_off;
   603		u32                      num_vtd_dmars;
   604		u32                      vtd_dmars_off;
   605		u32                      proc_scrtm_status; /* version 8 or later only*/
   606		struct heap_ext_data_elt ext_data_elts[];
   607	} __packed;
   608	
   609	struct acpi_table_header *tboot_get_dmar_table(struct acpi_table_header *dmar_tbl)
   610	{
   611		void *heap_base, *heap_ptr, *config;
   612	
   613		if (!tboot_enabled())
   614			return dmar_tbl;
   615	
   616		/*
   617		 * ACPI tables may not be DMA protected by tboot, so use DMAR copy
   618		 * SINIT saved in SinitMleData in TXT heap (which is DMA protected)
   619		 */
   620	
   621		/* map config space in order to get heap addr */
   622		config = ioremap(TXT_PUB_CONFIG_REGS_BASE, NR_TXT_CONFIG_PAGES *
   623				 PAGE_SIZE);
   624		if (!config)
   625			return NULL;
   626	
   627		/* now map TXT heap */
   628		heap_base = ioremap(*(u64 *)(config + TXTCR_HEAP_BASE),
   629				    *(u64 *)(config + TXTCR_HEAP_SIZE));
   630		iounmap(config);
   631		if (!heap_base)
   632			return NULL;
   633	
   634		/* walk heap to SinitMleData */
   635		/* skip BiosData */
   636		heap_ptr = heap_base + *(u64 *)heap_base;
   637		/* skip OsMleData */
   638		heap_ptr += *(u64 *)heap_ptr;
   639		/* skip OsSinitData */
   640		heap_ptr += *(u64 *)heap_ptr;
   641		/* now points to SinitMleDataSize; set to SinitMleData */
   642		heap_ptr += sizeof(u64);
   643		/* get addr of DMAR table */
   644		dmar_tbl = (struct acpi_table_header *)(heap_ptr +
   645			   ((struct sinit_mle_data *)heap_ptr)->vtd_dmars_off -
   646			   sizeof(u64));
   647	
   648		/* don't unmap heap because dmar.c needs access to this */
   649	
   650		return dmar_tbl;
   651	}
   652	
   653	struct acpi_table_dtpr *tboot_get_dtpr_table(void **heap_base)
   654	{
   655		void *heap_ptr, *config, *sinit_mle_end;
   656		struct sinit_mle_data *sinit_mle;
   657		struct heap_ext_data_elt *elt;
   658		u64 heap_end, heap_size, sinit_mle_size, heap_section_size;
   659	
   660		if (!heap_base)
   661			return NULL;
   662	
   663		if (!tboot_enabled())
   664			return NULL;
   665		/*
   666		 * ACPI tables may not be DMA protected by tboot, so use DMAR copy
   667		 * SINIT saved in SinitMleData in TXT heap (which is DMA protected)
   668		 */
   669	
   670		/* map config space in order to get heap addr */
   671		config = ioremap(TXT_PUB_CONFIG_REGS_BASE, NR_TXT_CONFIG_PAGES *
   672				 PAGE_SIZE);
   673		if (!config)
   674			return NULL;
   675	
   676		/* now map TXT heap */
   677		*heap_base = ioremap(*(u64 *)(config + TXTCR_HEAP_BASE),
   678				    *(u64 *)(config + TXTCR_HEAP_SIZE));
   679		heap_size = *(u64 *)(config + TXTCR_HEAP_SIZE);
   680		heap_end = (u64)*heap_base + heap_size;
   681		iounmap(config);
   682	
   683		if (!(*heap_base))
   684			return NULL;
   685	
   686		/* walk heap to SinitMleData */
   687		/* skip BiosData */
   688		/* get BiosData section size */
   689		heap_section_size = *(u64 *) (*heap_base);
   690		if (!tboot_check_txt_heap_section_bounds(heap_end, heap_base, *heap_base,
   691		                                         heap_section_size, "BiosData")) {
   692			return NULL;
   693		}
   694	
   695		/* skip OsMleData */
   696		heap_ptr = *heap_base + heap_section_size;
   697		/* get OsMleData section size */
   698		heap_section_size = *(u64 *)heap_ptr;
   699		if (!tboot_check_txt_heap_section_bounds(heap_end, heap_base, heap_ptr,
   700		                                         heap_section_size, "OsMleData")) {
   701			return NULL;
   702		}
   703	
   704		/* skip OsSinitData */
   705		heap_ptr += heap_section_size;
   706		/* get OsSinitData section size */
   707		heap_section_size = *(u64 *)heap_ptr;
   708		if (!tboot_check_txt_heap_section_bounds(heap_end, heap_base, heap_ptr,
   709		                                         heap_section_size, "OsSinitData")) {
   710			return NULL;
   711		}
   712	
   713		/* jump to the SinitMleData */
   714		heap_ptr += heap_section_size;
   715		/* now points to SinitMleDataSize; set to SinitMleData */
   716		sinit_mle_size = *(u64 *)heap_ptr;
   717		if(!tboot_check_txt_heap_section_bounds(heap_end, heap_base, heap_ptr,
   718		                                        sinit_mle_size, "SinitMleData")) {
   719			return NULL;
   720		}
   721	
   722		heap_ptr += sizeof(u64);
   723		sinit_mle = (struct sinit_mle_data *)heap_ptr;
   724		sinit_mle_end = (void *)sinit_mle + sinit_mle_size;
   725		if (sizeof(struct sinit_mle_data) > sinit_mle_size) {
   726			pr_err("SinitMleData size is smaller than expected.\n");
   727			goto err;
   728		}
   729	
   730		if (sinit_mle->version < 9) {
   731			pr_err("Unsupported SinitMleData version: %u\n", sinit_mle->version);
   732			goto err;
   733		}
   734	
   735		heap_ptr += sizeof(struct sinit_mle_data);
   736		if (heap_ptr > sinit_mle_end) {
   737			pr_err("SinitMleData header out of bounds.\n");
   738			goto err;
   739		}
   740	
   741		elt = sinit_mle->ext_data_elts;
   742		do {
   743			if ((u8 *)elt + sizeof(*elt) > (u8 *)sinit_mle_end) {
   744				pr_err("SinitMleData element header out of bounds.\n");
   745				goto err;
   746			}
   747	
   748			if (elt->size < sizeof(*elt)) {
   749				pr_err("Invalid SinitMleData element size: %u\n", elt->size);
   750				goto err;
   751			}
   752	
   753			if (elt->type == HEAP_EXTDATA_TYPE_END || elt->type == HEAP_EXTDATA_TYPE_DTPR) {
   754				break;
   755			}
   756	
   757			elt = (void *)elt + elt->size;
 > 758		} while (elt <= sinit_mle_end);
   759	
   760		if (elt >= sinit_mle_end){
   761			pr_err("Reached the end of SinitMleData without finding DTPR nor END"
   762			       " element.\n");
   763			goto err;
   764		}
   765	
   766		if (elt->type == HEAP_EXTDATA_TYPE_END) {
   767			pr_err("DTPR element not found in SinitMleData\n");
   768			iounmap(*heap_base);
   769			*heap_base = NULL;
   770			return NULL;
   771		}
   772	
   773		if ((u8 *)elt + elt->size > (u8 *)sinit_mle_end) {
   774			pr_err("DTPR Table exceeds SinitMleData bounds.\n");
   775			goto err;
   776		}
   777	
 > 778		if (!tboot_check_dtpr_size((struct acpi_table_dtpr *)elt->data,
   779		                           elt->size - sizeof(*elt))) {
   780			pr_err("Invalid DTPR Table size.\n");
   781			goto err;
   782		}
   783	
   784		return (struct acpi_table_dtpr *)elt->data;
   785	
   786	err:
   787		iounmap(*heap_base);
   788		*heap_base = NULL;
   789		return NULL;
   790	}
   791	

--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH 1/1] x86/tboot: Add support for parsing DTPR table and disabling TPRs
  2026-09-17  9:41       ` [PATCH 1/1] " Michal Camacho Romero
  2026-09-18  6:45         ` kernel test robot
@ 2026-09-18  7:35         ` kernel test robot
  1 sibling, 0 replies; 15+ messages in thread
From: kernel test robot @ 2026-09-18  7:35 UTC (permalink / raw)
  To: Michal Camacho Romero, Ning Sun
  Cc: oe-kbuild-all, Baolu Lu, Thomas Gleixner, Michal Camacho Romero,
	x86, iommu, tboot-devel, linux-kernel, Mateusz Mowka,
	Adam Pawlicki, Pawel Randzio

Hi Michal,

kernel test robot noticed the following build errors:

[auto build test ERROR on tip/x86/core]
[also build test ERROR on tip/master linus/master tip/auto-latest v7.3-rc3 next-20260916]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Michal-Camacho-Romero/x86-tboot-Add-support-for-parsing-DTPR-table-and-disabling-TPRs/20260917-114133
base:   tip/x86/core
patch link:    https://lore.kernel.org/r/20260917094133.765149-1-michal.camacho.romero%40linux.intel.com
patch subject: [PATCH 1/1] x86/tboot: Add support for parsing DTPR table and disabling TPRs
config: x86_64-rhel-9.4 (https://download.01.org/0day-ci/archive/20260918/202609181503.HTEk09e5-lkp@intel.com/config)
compiler: gcc-14 (Debian 14.2.0-19) 14.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260918/202609181503.HTEk09e5-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202609181503.HTEk09e5-lkp@intel.com/

All error/warnings (new ones prefixed by >>):

   arch/x86/kernel/tboot.c: In function 'tboot_check_dtpr_size':
>> arch/x86/kernel/tboot.c:255:16: warning: unused variable 'j' [-Wunused-variable]
     255 |         u32 i, j, ref_tpr_cnt;
         |                ^
   arch/x86/kernel/tboot.c: At top level:
>> arch/x86/kernel/tboot.c:583:8: error: redefinition of 'struct heap_ext_data_elt'
     583 | struct heap_ext_data_elt {
         |        ^~~~~~~~~~~~~~~~~
   In file included from arch/x86/kernel/tboot.c:19:
   include/linux/tboot.h:68:8: note: originally defined here
      68 | struct heap_ext_data_elt {
         |        ^~~~~~~~~~~~~~~~~
   arch/x86/kernel/tboot.c: In function 'tboot_get_dtpr_table':
>> arch/x86/kernel/tboot.c:758:22: warning: comparison of distinct pointer types lacks a cast [-Wcompare-distinct-pointer-types]
     758 |         } while (elt <= sinit_mle_end);
         |                      ^~
   arch/x86/kernel/tboot.c:760:17: warning: comparison of distinct pointer types lacks a cast [-Wcompare-distinct-pointer-types]
     760 |         if (elt >= sinit_mle_end){
         |                 ^~
   arch/x86/kernel/tboot.c: In function 'tboot_parse_dtpr_table':
>> arch/x86/kernel/tboot.c:800:13: warning: variable 'ref_tpr_cnt' set but not used [-Wunused-but-set-variable]
     800 |         u32 ref_tpr_cnt;
         |             ^~~~~~~~~~~


vim +583 arch/x86/kernel/tboot.c

   582	
 > 583	struct heap_ext_data_elt {
   584		u32 type;
   585		u32 size;
   586		u8  data[];
   587	} __packed;
   588	
   589	struct sinit_mle_data {
   590		u32                      version;             /* currently 9 */
   591		struct sha1_hash         bios_acm_id;
   592		u32                      edx_senter_flags;
   593		u64                      mseg_valid;
   594		struct sha1_hash         sinit_hash;
   595		struct sha1_hash         mle_hash;
   596		struct sha1_hash         stm_hash;
   597		struct sha1_hash         lcp_policy_hash;
   598		u32                      lcp_policy_control;
   599		u32                      rlp_wakeup_addr;
   600		u32                      reserved;
   601		u32                      num_mdrs;
   602		u32                      mdrs_off;
   603		u32                      num_vtd_dmars;
   604		u32                      vtd_dmars_off;
   605		u32                      proc_scrtm_status; /* version 8 or later only*/
   606		struct heap_ext_data_elt ext_data_elts[];
   607	} __packed;
   608	
   609	struct acpi_table_header *tboot_get_dmar_table(struct acpi_table_header *dmar_tbl)
   610	{
   611		void *heap_base, *heap_ptr, *config;
   612	
   613		if (!tboot_enabled())
   614			return dmar_tbl;
   615	
   616		/*
   617		 * ACPI tables may not be DMA protected by tboot, so use DMAR copy
   618		 * SINIT saved in SinitMleData in TXT heap (which is DMA protected)
   619		 */
   620	
   621		/* map config space in order to get heap addr */
   622		config = ioremap(TXT_PUB_CONFIG_REGS_BASE, NR_TXT_CONFIG_PAGES *
   623				 PAGE_SIZE);
   624		if (!config)
   625			return NULL;
   626	
   627		/* now map TXT heap */
   628		heap_base = ioremap(*(u64 *)(config + TXTCR_HEAP_BASE),
   629				    *(u64 *)(config + TXTCR_HEAP_SIZE));
   630		iounmap(config);
   631		if (!heap_base)
   632			return NULL;
   633	
   634		/* walk heap to SinitMleData */
   635		/* skip BiosData */
   636		heap_ptr = heap_base + *(u64 *)heap_base;
   637		/* skip OsMleData */
   638		heap_ptr += *(u64 *)heap_ptr;
   639		/* skip OsSinitData */
   640		heap_ptr += *(u64 *)heap_ptr;
   641		/* now points to SinitMleDataSize; set to SinitMleData */
   642		heap_ptr += sizeof(u64);
   643		/* get addr of DMAR table */
   644		dmar_tbl = (struct acpi_table_header *)(heap_ptr +
   645			   ((struct sinit_mle_data *)heap_ptr)->vtd_dmars_off -
   646			   sizeof(u64));
   647	
   648		/* don't unmap heap because dmar.c needs access to this */
   649	
   650		return dmar_tbl;
   651	}
   652	
   653	struct acpi_table_dtpr *tboot_get_dtpr_table(void **heap_base)
   654	{
   655		void *heap_ptr, *config, *sinit_mle_end;
   656		struct sinit_mle_data *sinit_mle;
   657		struct heap_ext_data_elt *elt;
   658		u64 heap_end, heap_size, sinit_mle_size, heap_section_size;
   659	
   660		if (!heap_base)
   661			return NULL;
   662	
   663		if (!tboot_enabled())
   664			return NULL;
   665		/*
   666		 * ACPI tables may not be DMA protected by tboot, so use DMAR copy
   667		 * SINIT saved in SinitMleData in TXT heap (which is DMA protected)
   668		 */
   669	
   670		/* map config space in order to get heap addr */
   671		config = ioremap(TXT_PUB_CONFIG_REGS_BASE, NR_TXT_CONFIG_PAGES *
   672				 PAGE_SIZE);
   673		if (!config)
   674			return NULL;
   675	
   676		/* now map TXT heap */
   677		*heap_base = ioremap(*(u64 *)(config + TXTCR_HEAP_BASE),
   678				    *(u64 *)(config + TXTCR_HEAP_SIZE));
   679		heap_size = *(u64 *)(config + TXTCR_HEAP_SIZE);
   680		heap_end = (u64)*heap_base + heap_size;
   681		iounmap(config);
   682	
   683		if (!(*heap_base))
   684			return NULL;
   685	
   686		/* walk heap to SinitMleData */
   687		/* skip BiosData */
   688		/* get BiosData section size */
   689		heap_section_size = *(u64 *) (*heap_base);
   690		if (!tboot_check_txt_heap_section_bounds(heap_end, heap_base, *heap_base,
   691		                                         heap_section_size, "BiosData")) {
   692			return NULL;
   693		}
   694	
   695		/* skip OsMleData */
   696		heap_ptr = *heap_base + heap_section_size;
   697		/* get OsMleData section size */
   698		heap_section_size = *(u64 *)heap_ptr;
   699		if (!tboot_check_txt_heap_section_bounds(heap_end, heap_base, heap_ptr,
   700		                                         heap_section_size, "OsMleData")) {
   701			return NULL;
   702		}
   703	
   704		/* skip OsSinitData */
   705		heap_ptr += heap_section_size;
   706		/* get OsSinitData section size */
   707		heap_section_size = *(u64 *)heap_ptr;
   708		if (!tboot_check_txt_heap_section_bounds(heap_end, heap_base, heap_ptr,
   709		                                         heap_section_size, "OsSinitData")) {
   710			return NULL;
   711		}
   712	
   713		/* jump to the SinitMleData */
   714		heap_ptr += heap_section_size;
   715		/* now points to SinitMleDataSize; set to SinitMleData */
   716		sinit_mle_size = *(u64 *)heap_ptr;
   717		if(!tboot_check_txt_heap_section_bounds(heap_end, heap_base, heap_ptr,
   718		                                        sinit_mle_size, "SinitMleData")) {
   719			return NULL;
   720		}
   721	
   722		heap_ptr += sizeof(u64);
   723		sinit_mle = (struct sinit_mle_data *)heap_ptr;
   724		sinit_mle_end = (void *)sinit_mle + sinit_mle_size;
   725		if (sizeof(struct sinit_mle_data) > sinit_mle_size) {
   726			pr_err("SinitMleData size is smaller than expected.\n");
   727			goto err;
   728		}
   729	
   730		if (sinit_mle->version < 9) {
   731			pr_err("Unsupported SinitMleData version: %u\n", sinit_mle->version);
   732			goto err;
   733		}
   734	
   735		heap_ptr += sizeof(struct sinit_mle_data);
   736		if (heap_ptr > sinit_mle_end) {
   737			pr_err("SinitMleData header out of bounds.\n");
   738			goto err;
   739		}
   740	
   741		elt = sinit_mle->ext_data_elts;
   742		do {
   743			if ((u8 *)elt + sizeof(*elt) > (u8 *)sinit_mle_end) {
   744				pr_err("SinitMleData element header out of bounds.\n");
   745				goto err;
   746			}
   747	
   748			if (elt->size < sizeof(*elt)) {
   749				pr_err("Invalid SinitMleData element size: %u\n", elt->size);
   750				goto err;
   751			}
   752	
   753			if (elt->type == HEAP_EXTDATA_TYPE_END || elt->type == HEAP_EXTDATA_TYPE_DTPR) {
   754				break;
   755			}
   756	
   757			elt = (void *)elt + elt->size;
 > 758		} while (elt <= sinit_mle_end);
   759	
   760		if (elt >= sinit_mle_end){
   761			pr_err("Reached the end of SinitMleData without finding DTPR nor END"
   762			       " element.\n");
   763			goto err;
   764		}
   765	
   766		if (elt->type == HEAP_EXTDATA_TYPE_END) {
   767			pr_err("DTPR element not found in SinitMleData\n");
   768			iounmap(*heap_base);
   769			*heap_base = NULL;
   770			return NULL;
   771		}
   772	
   773		if ((u8 *)elt + elt->size > (u8 *)sinit_mle_end) {
   774			pr_err("DTPR Table exceeds SinitMleData bounds.\n");
   775			goto err;
   776		}
   777	
   778		if (!tboot_check_dtpr_size((struct acpi_table_dtpr *)elt->data,
   779		                           elt->size - sizeof(*elt))) {
   780			pr_err("Invalid DTPR Table size.\n");
   781			goto err;
   782		}
   783	
   784		return (struct acpi_table_dtpr *)elt->data;
   785	
   786	err:
   787		iounmap(*heap_base);
   788		*heap_base = NULL;
   789		return NULL;
   790	}
   791	

--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

^ permalink raw reply	[flat|nested] 15+ messages in thread

end of thread, other threads:[~2026-09-18  7:36 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-06-03 11:44 [PATCH v1 0/2] x86/tboot: Add Intel TXT Protection Regions (TPR) support Michal Camacho Romero
2026-06-03 11:44 ` [PATCH v1 1/2] x86/tboot: Add support for parsing DTPR table and disabling TPRs Michal Camacho Romero
2026-09-14 13:19   ` [PATCH v2 " Michal Camacho Romero
2026-09-15 23:23     ` Sun, Ning
2026-09-17  9:41       ` [PATCH 1/1] " Michal Camacho Romero
2026-09-18  6:45         ` kernel test robot
2026-09-18  7:35         ` kernel test robot
2026-09-17  9:43       ` [PATCH v3 1/2] " Michal Camacho Romero
2026-06-03 11:45 ` [PATCH v1 2/2] iommu/vt-d: Disable PMRs and skip force-IOMMU when TXT TPRs are active Michal Camacho Romero
2026-06-11  8:49   ` Baolu Lu
2026-08-07  9:16   ` [PATCH v2 " Michal Camacho Romero
2026-08-07 10:14   ` Michal Camacho Romero
2026-08-20  3:28     ` Baolu Lu
2026-09-03  9:33       ` [PATCH v3 " Michal Camacho Romero
2026-09-04  2:19         ` Baolu Lu

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®