* [PATCH v7 0/2] x86/tboot: Add Intel TXT Protection Regions (TPR) support
@ 2026-10-29 11:57 Michal Camacho Romero
2026-10-29 11:57 ` [PATCH v7 1/2] x86/tboot: Add support for parsing DTPR table and disabling TPRs Michal Camacho Romero
2026-10-29 11:57 ` [PATCH v7 2/2] iommu/vt-d: Disable TXT TPRs before enabling queued invalidation Michal Camacho Romero
0 siblings, 2 replies; 6+ messages in thread
From: Michal Camacho Romero @ 2026-10-29 11:57 UTC (permalink / raw)
To: Lu Baolu, Thomas Gleixner, Ning Sun
Cc: Michal Camacho Romero, x86, iommu, tboot-devel, linux-kernel,
Mateusz Mowka, Adam Pawlicki, Pawel Randzio,
Michal Camacho Romero
This series adds support for parsing the Intel TXT DMA Protection Ranges
(DTPR) table and tearing down the SINIT ACM-established TXT Protection
Regions (TPRs) during early kernel boot.
Background
==========
When a measured launch is performed, the SINIT ACM programs TXT
Protection Regions (TPRs) that block all inbound DMA requests.
These ranges also cover the Intel IOMMU's own queued
invalidation (QI) ring. If the TPRs are still active when QI is first
enabled, the very first invalidation faults with an Invalidation Queue
Error (IQE) and the boot hangs. The kernel must earlier locate and disable
the TPRs, before any QI is enabled, after which the IOMMU is the sole runtime
DMA protection.
Patch 1 adds the tboot-internal DTPR parsing and TPR teardown helpers.
Patch 2 wires them into the Intel VT-d driver, disabling TPRs in
parse_dmar_table() (which runs before every dmar_enable_qi() call site)
once DMAR parsing has succeeded.
Changes since v6
================
- Patch 2 retitled "iommu/vt-d: Disable PMRs and skip force-IOMMU when
TXT TPRs are active" -> "iommu/vt-d: Disable TXT TPRs before enabling
queued invalidation", reflecting a substantially reworked approach:
* TPRs are now torn down early in parse_dmar_table(), before any
dmar_enable_qi() (the real fix for the IQE boot hang on IR/x2apic
systems), instead of in intel_iommu_init() after translation is
enabled.
* The tboot_force_iommu() is no longer modified. The IOMMU is forced on
exactly as before, so booting intel_iommu=off on a TPR-capable
platform behaves as upstream.
* All TPR/DTPR/txt_heap states are kept internal to tboot.c. The IOMMU
driver now uses only parameterless helpers (tboot_check_tprs(),
tboot_teardown_tprs(), tboot_is_tpr_protection_enabled(),
tboot_free_txt_heap_mem()). All TBOOT state leaks into the driver have been
eliminated.
* TPRs teardown runs only on a successful DMAR Table parse.
* Using intel_iommu=tboot_noforce now suppresses the TPR-related panic
- Patch 1 is functionally unchanged; it only picks up checkpatch --strict
cleanups: statics are no longer explicitly initialised, the TPR count is
now signed (s64), and tab indentation, pointer/cast style,
NULL-comparison and brace style, and unsplit log strings were fixed.
Michal Camacho Romero (2):
x86/tboot: Add support for parsing DTPR table and disabling TPRs
iommu/vt-d: Disable TXT TPRs before enabling queued invalidation
arch/x86/kernel/tboot.c | 515 ++++++++++++++++++++++++++++++++++--
drivers/iommu/intel/dmar.c | 19 ++
drivers/iommu/intel/iommu.c | 18 ++
include/linux/tboot.h | 28 +-
4 files changed, 560 insertions(+), 20 deletions(-)
--
2.43.0
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH v7 1/2] x86/tboot: Add support for parsing DTPR table and disabling TPRs
2026-10-29 11:57 [PATCH v7 0/2] x86/tboot: Add Intel TXT Protection Regions (TPR) support Michal Camacho Romero
@ 2026-10-29 11:57 ` Michal Camacho Romero
2026-10-30 4:59 ` Borislav Petkov
2026-10-30 12:22 ` kernel test robot
2026-10-29 11:57 ` [PATCH v7 2/2] iommu/vt-d: Disable TXT TPRs before enabling queued invalidation Michal Camacho Romero
1 sibling, 2 replies; 6+ messages in thread
From: Michal Camacho Romero @ 2026-10-29 11:57 UTC (permalink / raw)
To: Lu Baolu, Thomas Gleixner, Ning Sun
Cc: Michal Camacho Romero, x86, iommu, tboot-devel, linux-kernel,
Mateusz Mowka, Adam Pawlicki, Pawel Randzio,
Michal Camacho Romero
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_check_txt_heap_section_bounds() - function checks if a given
TXT heap section is within
the bounds of the heap.
* tboot_check_tprs() - function locates and caches
the DTPR table and counts the
number of the enabled TPRs.
* tboot_is_tpr_protection_enabled() - function reports whether any
TPR region is currently
enabled.
* tboot_disable_tprs() - function iterates over TPR
instances and disables each
active TPR region by setting
TPR_BASE_DISABLE_BIT (bit 4) in
the TPRn_BASE register via MMIO.
* tboot_free_txt_heap_mem() - function releases the ioremap
mapping of the TXT heap.
* tboot_teardown_tprs() - function disables any enabled
TPRs and releases the TXT heap
mapping in a single call.
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
Tested-by: Michal Camacho Romero <michal.camacho.romero@linux.intel.com>
Signed-off-by: Michal Camacho Romero <michal.camacho.romero@intel.com>
---
arch/x86/kernel/tboot.c | 515 ++++++++++++++++++++++++++++++++++++++--
include/linux/tboot.h | 28 ++-
2 files changed, 523 insertions(+), 20 deletions(-)
diff --git a/arch/x86/kernel/tboot.c b/arch/x86/kernel/tboot.c
index 46b8f1f16676e..47da21ad0f3e2 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>
@@ -42,6 +43,15 @@ static struct tboot *tboot __read_mostly;
static u8 tboot_uuid[16] __initdata = TBOOT_UUID;
+/* Global pointer to TXT HEAP base */
+static void __iomem *txt_heap;
+
+/* Global pointer to the DTPR table */
+static struct acpi_table_dtpr *dtpr;
+
+/* Number of active TPRs */
+static s64 num_of_enabled_tprs;
+
bool tboot_enabled(void)
{
return tboot != NULL;
@@ -223,6 +233,143 @@ static int tboot_setup_sleep(void)
#endif
+static bool tboot_check_txt_heap_section_bounds(const u64 heap_end,
+ void *heap_section,
+ u64 *heap_section_size,
+ const char *section_name)
+{
+ if (!heap_section) {
+ pr_err("%s section is NULL\n", section_name);
+ return false;
+ }
+
+ if (!heap_section_size) {
+ pr_err("%s section size pointer is NULL\n", section_name);
+ return false;
+ }
+
+ *heap_section_size = 0;
+
+ if ((u64)heap_section > heap_end || heap_end - (u64)heap_section < 8) {
+ pr_err("%s size field exceeds heap boundary\n", section_name);
+ return false;
+ }
+
+ /* Read the size of the current TXT Heap section */
+ *heap_section_size = *(u64 __force *)heap_section;
+
+ if (*heap_section_size < 8) {
+ pr_err("%s size is too small\n", section_name);
+ return false;
+ }
+
+ if (*heap_section_size > heap_end - (u64)heap_section) {
+ pr_err("%s exceeds heap boundary\n", section_name);
+ return false;
+ }
+
+ return true;
+}
+
+static bool tboot_check_dtpr_size(const struct heap_ext_data_elt *elt)
+{
+ u64 dtpr_payload_size, dtpr_offset;
+ u32 i, ref_tpr_cnt;
+
+ struct acpi_table_dtpr *tmp_dtpr = NULL;
+ struct acpi_tpr_instance *tpr_inst = NULL;
+ struct acpi_tpr_aux_sr *tpr_aux_srl = NULL;
+
+ if (!elt) {
+ pr_err("DTPR element heap extension data is NULL\n");
+ return false;
+ }
+
+ tmp_dtpr = (struct acpi_table_dtpr *)elt->data;
+ dtpr_payload_size = elt->size - sizeof(*elt);
+
+ if (dtpr_payload_size < sizeof(struct acpi_table_dtpr)) {
+ pr_err("DTPR element payload too small for a DTPR header\n");
+ return false;
+ }
+
+ if (tmp_dtpr->header.length != dtpr_payload_size) {
+ pr_err("Mismatch between DTPR table length and payload size\n");
+ return false;
+ }
+
+ dtpr_offset = sizeof(struct acpi_table_dtpr);
+
+ if (dtpr_offset > dtpr_payload_size) {
+ pr_err("TPR header exceeds table's declared size\n");
+ return false;
+ }
+
+ if (tmp_dtpr->ins_cnt < 1) {
+ pr_err("DTPR table declares no TPR instances\n");
+ return false;
+ }
+
+ for (i = 0; i < tmp_dtpr->ins_cnt; i++) {
+ /* prove this instance's header is in bounds before reading it */
+ if (dtpr_offset + sizeof(struct acpi_tpr_instance) > dtpr_payload_size) {
+ pr_err("TPR instance No.%u header exceeds DTPR table size\n", i);
+ return false;
+ }
+
+ /* iterate to the current TPR instance */
+ tpr_inst = (struct acpi_tpr_instance *)((u8 *)tmp_dtpr + dtpr_offset);
+
+ if (tpr_inst->tpr_cnt < 2) {
+ pr_err("TPR Instance %u has less than 2 TPRs, further DTPR processing interrupted.\n",
+ i);
+ return false;
+ }
+
+ /* all instances must carry the same TPR count as the first */
+ if (i == 0) {
+ ref_tpr_cnt = tpr_inst->tpr_cnt;
+ } else if (tpr_inst->tpr_cnt != ref_tpr_cnt) {
+ pr_err("TPR Instance %u has inconsistent TPR count: expected %u, found %u\n",
+ i, ref_tpr_cnt, tpr_inst->tpr_cnt);
+ return false;
+ }
+
+ /* iterate over each TPR instance */
+ dtpr_offset += sizeof(struct acpi_tpr_instance) +
+ (u64)tpr_inst->tpr_cnt * sizeof(struct acpi_tpr_array);
+
+ /* verify TPR array size for this instance */
+ if (dtpr_offset > dtpr_payload_size) {
+ pr_err("TPR instance No.%u TPR entries exceed DTPR table size\n",
+ i);
+ return false;
+ }
+ }
+
+ tpr_aux_srl = (struct acpi_tpr_aux_sr *)((u8 *)tmp_dtpr + dtpr_offset);
+ dtpr_offset += sizeof(u32);
+
+ if (dtpr_offset > dtpr_payload_size) {
+ pr_err("TPR SRL count field exceeds DTPR table size\n");
+ return false;
+ }
+
+ dtpr_offset += (u64)tpr_aux_srl->srl_cnt *
+ sizeof(struct acpi_tpr_serialize_request);
+ if (dtpr_offset > dtpr_payload_size) {
+ pr_err("TPR SRL entries exceed DTPR table size\n");
+ return false;
+ }
+
+ if (dtpr_offset < dtpr_payload_size) {
+ pr_err("DTPR table is smaller than expected\n");
+ return false;
+ }
+
+ return true;
+}
+
void tboot_shutdown(u32 shutdown_type)
{
void (*shutdown)(void);
@@ -454,26 +601,28 @@ struct sha1_hash {
};
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)
{
- void *heap_base, *heap_ptr, *config;
+ void *heap_ptr, *config;
if (!tboot_enabled())
return dmar_tbl;
@@ -490,15 +639,15 @@ struct acpi_table_header *tboot_get_dmar_table(struct acpi_table_header *dmar_tb
return NULL;
/* now map TXT heap */
- heap_base = ioremap(*(u64 *)(config + TXTCR_HEAP_BASE),
+ txt_heap = ioremap(*(u64 *)(config + TXTCR_HEAP_BASE),
*(u64 *)(config + TXTCR_HEAP_SIZE));
iounmap(config);
- if (!heap_base)
+ if (!txt_heap)
return NULL;
/* walk heap to SinitMleData */
/* skip BiosData */
- heap_ptr = heap_base + *(u64 *)heap_base;
+ heap_ptr = txt_heap + *(u64 *)txt_heap;
/* skip OsMleData */
heap_ptr += *(u64 *)heap_ptr;
/* skip OsSinitData */
@@ -514,3 +663,331 @@ struct acpi_table_header *tboot_get_dmar_table(struct acpi_table_header *dmar_tb
return dmar_tbl;
}
+
+static struct acpi_table_dtpr *tboot_get_dtpr_table(void)
+{
+ 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 (!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_size = *(u64 __force *)(config + TXTCR_HEAP_SIZE);
+ if (!txt_heap)
+ txt_heap = ioremap(*(u64 __force *)(config + TXTCR_HEAP_BASE), heap_size);
+
+ iounmap(config);
+
+ if (!txt_heap) {
+ pr_err("Failed to map TXT heap\n");
+ return NULL;
+ }
+
+ if (heap_size == 0 || (u64)txt_heap + heap_size < (u64)txt_heap) {
+ pr_err("TXT heap size invalid or overflows\n");
+ goto teardown;
+ }
+
+ heap_end = (u64)txt_heap + heap_size;
+
+ /* walk heap to SinitMleData */
+ /* skip BiosData */
+ /* get BiosData section size */
+ if (!tboot_check_txt_heap_section_bounds(heap_end, txt_heap,
+ &heap_section_size, "BiosData")) {
+ goto teardown;
+ }
+
+ /* skip OsMleData */
+ heap_ptr = txt_heap + heap_section_size;
+ /* get OsMleData section size */
+ if (!tboot_check_txt_heap_section_bounds(heap_end, heap_ptr,
+ &heap_section_size, "OsMleData")) {
+ goto teardown;
+ }
+
+ /* skip OsSinitData */
+ heap_ptr += heap_section_size;
+ /* get OsSinitData section size */
+ if (!tboot_check_txt_heap_section_bounds(heap_end, heap_ptr,
+ &heap_section_size, "OsSinitData")) {
+ goto teardown;
+ }
+
+ /* jump to the SinitMleData */
+ heap_ptr += heap_section_size;
+ sinit_mle_size = 0;
+ /* now points to SinitMleDataSize; set to SinitMleData */
+ if (!tboot_check_txt_heap_section_bounds(heap_end, heap_ptr,
+ &sinit_mle_size, "SinitMleData")) {
+ goto teardown;
+ }
+
+ 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 teardown;
+ }
+
+ if (sinit_mle->version < 9) {
+ pr_err("Unsupported SinitMleData version: %u\n", sinit_mle->version);
+ goto teardown;
+ }
+
+ heap_ptr += sizeof(struct sinit_mle_data);
+ if (heap_ptr > sinit_mle_end) {
+ pr_err("SinitMleData header out of bounds.\n");
+ goto teardown;
+ }
+
+ /*
+ * The SinitMleData extended-data area is a self-describing list of
+ * variable-length elements with no stored element count; it is
+ * terminated only by a HEAP_EXTDATA_TYPE_END element. Traversal must
+ * therefore be driven by each element's own size field. Forward
+ * progress and termination are guaranteed on every iteration:
+ * - the 8-byte header is bounds-checked before type/size are read,
+ * - elt->size >= sizeof(*elt) enforces a strictly-positive advance,
+ * - the "elt <= sinit_mle_end" cap bounds the number of steps.
+ */
+ 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 teardown;
+ }
+
+ if (elt->size < sizeof(*elt)) {
+ pr_err("Invalid SinitMleData element size: %u\n", elt->size);
+ goto teardown;
+ }
+
+ if (elt->type == HEAP_EXTDATA_TYPE_END ||
+ elt->type == HEAP_EXTDATA_TYPE_DTPR) {
+ break;
+ }
+
+ elt = (void *)elt + elt->size;
+ } while ((void *)elt <= sinit_mle_end);
+
+ if ((void *)elt >= sinit_mle_end) {
+ pr_err("Reached the end of SinitMleData without finding DTPR nor END element.\n");
+ goto teardown;
+ }
+
+ if (elt->type == HEAP_EXTDATA_TYPE_END) {
+ pr_err("DTPR element not found in SinitMleData\n");
+ goto teardown;
+ }
+
+ if ((u8 *)elt + elt->size > (u8 *)sinit_mle_end) {
+ pr_err("DTPR Table exceeds SinitMleData bounds.\n");
+ goto teardown;
+ }
+
+ if (!tboot_check_dtpr_size(elt)) {
+ pr_err("Invalid DTPR Table size.\n");
+ goto teardown;
+ }
+
+ return (struct acpi_table_dtpr *)elt->data;
+
+teardown:
+ tboot_free_txt_heap_mem();
+ return NULL;
+}
+
+void tboot_check_tprs(void)
+{
+ struct acpi_tpr_instance *tpr_inst;
+ struct acpi_tpr_array *tpr_arr;
+ u32 *instance_cnt;
+ void __iomem *base;
+ u32 i, j;
+ u64 tpr_base_val;
+
+ dtpr = tboot_get_dtpr_table();
+
+ if (!txt_heap) {
+ pr_warn("Heap base is NULL.\n");
+ return;
+ }
+
+ if (!dtpr) {
+ pr_warn("DTPR Table is NULL.\n");
+ goto teardown;
+ }
+
+ if (!tboot_enabled()) {
+ pr_warn("TBOOT is not enabled. TPRs weren't set by TBOOT nor SINIT ACM.\n");
+ goto teardown;
+ }
+
+ 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));
+
+ /*
+ * Each TPR base address has already been verified by the SINIT ACM,
+ * so it can be treated as trustworthy and used safely for I/O
+ * mapping.
+ */
+ base = ioremap(tpr_arr->base, 16);
+ if (!base) {
+ pr_warn("TPR Instance %u, TPR No.%u verification failure.\n",
+ i, j);
+ continue;
+ }
+
+ tpr_base_val = readq(base);
+ pr_info("TPR instance %u, TPR %u:base 0x%llx, limit 0x%llx\n", i, j,
+ tpr_base_val, readq((u8 __iomem *)base + 8));
+
+ /* Verify if the TPR base is enabled */
+ if ((tpr_base_val & (u64)TPR_BASE_DISABLE_BIT) == 0)
+ num_of_enabled_tprs++;
+
+ iounmap(base);
+ }
+
+ tpr_inst = (struct acpi_tpr_instance *)((u8 *)tpr_inst +
+ sizeof(*tpr_inst) + j * sizeof(struct acpi_tpr_array));
+ }
+
+ pr_info("Number of enabled TPRs: %lld\n", num_of_enabled_tprs);
+ if (num_of_enabled_tprs > 0)
+ pr_info("TPR protection is active. PMR will be disabled\n");
+
+ return;
+
+teardown:
+ tboot_free_txt_heap_mem();
+}
+
+s64 tboot_disable_tprs(void)
+{
+ struct acpi_tpr_instance *tpr_inst;
+ struct acpi_tpr_array *tpr_arr;
+ u32 *instance_cnt;
+ void __iomem *base;
+ u32 i, j;
+ u64 tpr_base_val;
+ s64 ret = 0;
+
+ if (!txt_heap) {
+ pr_warn("TXT Heap base is NULL.\n");
+ return -1;
+ }
+
+ if (!dtpr) {
+ pr_warn("DTPR Table is NULL.\n");
+ ret = -1;
+ goto teardown;
+ }
+
+ if (!tboot_enabled()) {
+ pr_warn("TBOOT is not enabled. TPR weren't set by TBOOT and SINIT ACM.\n");
+ ret = -1;
+ goto teardown;
+ }
+
+ 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));
+
+ /*
+ * Each TPR base address has already been verified by the SINIT ACM,
+ * so it can be treated as trustworthy and used safely for I/O
+ * mapping.
+ */
+ base = ioremap(tpr_arr->base, 16);
+ if (!base) {
+ pr_warn("TPR Instance %u, TPR No.%u disabling failure.\n",
+ i, j);
+ continue;
+ }
+
+ tpr_base_val = readq(base);
+ pr_info("TPR instance %u, TPR %u:base 0x%llx, limit 0x%llx\n", i, j,
+ tpr_base_val, readq((u8 __iomem *)base + 8));
+
+ /* If the given TPR is active, disable it */
+ if ((tpr_base_val & (u64)TPR_BASE_DISABLE_BIT) == 0) {
+ writeq(tpr_base_val | TPR_BASE_DISABLE_BIT, base);
+ num_of_enabled_tprs--;
+ }
+ iounmap(base);
+ }
+
+ tpr_inst = (struct acpi_tpr_instance *)((u8 *)tpr_inst +
+ sizeof(*tpr_inst) + j * sizeof(struct acpi_tpr_array));
+ }
+
+ if (num_of_enabled_tprs > 0) {
+ pr_err("TPR disabling failed for some instances.\n");
+ pr_err("Number of still enabled TPRs: %lld\n", num_of_enabled_tprs);
+ } else if (num_of_enabled_tprs < 0) {
+ pr_err("Number of enabled TPRs is negative, which indicates an error.\n");
+ pr_err("TPR disabling resulted in a negative count: %lld\n",
+ num_of_enabled_tprs);
+ } else {
+ pr_info("All of TPRs have been successfully disabled.\n");
+ }
+
+ ret = 0;
+ goto teardown;
+
+teardown:
+ tboot_free_txt_heap_mem();
+ return ret;
+}
+
+s64 tboot_teardown_tprs(void)
+{
+ s64 ret = 0;
+
+ if (tboot_is_tpr_protection_enabled() && dtpr)
+ ret = tboot_disable_tprs(); /* uses cached dtpr + txt_heap */
+
+ tboot_free_txt_heap_mem(); /* releases internal mapping */
+ dtpr = NULL;
+ return ret; /* 0 = success / nothing to do */
+}
+
+void tboot_free_txt_heap_mem(void)
+{
+ if (txt_heap) {
+ iounmap(txt_heap);
+ txt_heap = NULL;
+ }
+}
+
+bool tboot_is_tpr_protection_enabled(void)
+{
+ return num_of_enabled_tprs > 0;
+}
diff --git a/include/linux/tboot.h b/include/linux/tboot.h
index d2279160ef39d..d7c00024b26e0 100644
--- a/include/linux/tboot.h
+++ b/include/linux/tboot.h
@@ -24,10 +24,17 @@ 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
+/*TPR Base Disable BIT*/
+#define TPR_BASE_DISABLE_BIT BIT(4)
+
struct tboot_mac_region {
u64 start; /* must be 64 byte -aligned */
u32 size; /* must be 64 byte -granular */
@@ -58,6 +65,15 @@ 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;
+ u8 data[];
+} __packed;
+
/*
* shared memory page used for communication between tboot and kernel
*/
@@ -122,19 +138,29 @@ struct tboot {
0xaa, 0x19, 0xea, 0x4d, 0x5, 0x7a, 0x8}
bool tboot_enabled(void);
+extern s64 tboot_disable_tprs(void);
+extern s64 tboot_teardown_tprs(void);
+extern void tboot_check_tprs(void);
+extern void tboot_free_txt_heap_mem(void);
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 bool tboot_is_tpr_protection_enabled(void);
#else
-#define tboot_enabled() 0
+#define tboot_enabled() 0
+#define tboot_disable_tprs() 0
+#define tboot_teardown_tprs() 0
+#define tboot_check_tprs() do { } while (0)
#define tboot_probe() do { } while (0)
#define tboot_shutdown(shutdown_type) do { } while (0)
#define tboot_sleep(sleep_state, pm1a_control, pm1b_control) \
do { } while (0)
#define tboot_get_dmar_table(dmar_tbl) (dmar_tbl)
+#define tboot_free_txt_heap_mem() do { } while (0)
+#define tboot_is_tpr_protection_enabled() 0
#endif /* !CONFIG_INTEL_TXT */
--
2.43.0
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH v7 2/2] iommu/vt-d: Disable TXT TPRs before enabling queued invalidation
2026-10-29 11:57 [PATCH v7 0/2] x86/tboot: Add Intel TXT Protection Regions (TPR) support Michal Camacho Romero
2026-10-29 11:57 ` [PATCH v7 1/2] x86/tboot: Add support for parsing DTPR table and disabling TPRs Michal Camacho Romero
@ 2026-10-29 11:57 ` Michal Camacho Romero
1 sibling, 0 replies; 6+ messages in thread
From: Michal Camacho Romero @ 2026-10-29 11:57 UTC (permalink / raw)
To: Lu Baolu, Thomas Gleixner, Ning Sun
Cc: Michal Camacho Romero, x86, iommu, tboot-devel, linux-kernel,
Mateusz Mowka, Adam Pawlicki, Pawel Randzio,
Michal Camacho Romero
When the SINIT ACM establishes TXT Protection Regions (TPRs), they block
all inbound DMA to their protected range, including the IOMMU's own
queued invalidation (QI) ring. If the TPRs are still active when QI is
first enabled, the first invalidation faults with an Invalidation Queue
Error and the boot hangs. On systems with interrupt remapping enabled
this happens very early, in intel_setup_irq_remapping() ->
dmar_enable_qi(), well before init_dmars().
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
Tested-by: Michal Camacho Romero <michal.camacho.romero@linux.intel.com>
Signed-off-by: Michal Camacho Romero <michal.camacho.romero@linux.intel.com>
---
drivers/iommu/intel/dmar.c | 19 +++++++++++++++++++
drivers/iommu/intel/iommu.c | 18 ++++++++++++++++++
2 files changed, 37 insertions(+)
diff --git a/drivers/iommu/intel/dmar.c b/drivers/iommu/intel/dmar.c
index ba675b08cd208..5e59aa46721e6 100644
--- a/drivers/iommu/intel/dmar.c
+++ b/drivers/iommu/intel/dmar.c
@@ -675,6 +675,25 @@ parse_dmar_table(void)
if (ret == 0 && drhd_count == 0)
pr_warn(FW_BUG "No DRHD structure found in DMAR table\n");
+ /*
+ * Only on a successful DMAR parse: if SINIT programmed TXT TPRs for DMA
+ * protection, disable them here, before any queued invalidation (QI) is
+ * enabled. TPRs block all inbound DMA to their protected range, including
+ * the IOMMU's own QI ring, so leaving them active makes the first queued
+ * invalidation fault (Invalidation Queue Error) and hangs the boot on any
+ * system with interrupt remapping enabled.
+ *
+ * If parsing failed, dmar_table_init() propagates the error and both the
+ * IOMMU and IRQ remmaping preparation will be aborted, before QI will
+ * be enabled, so the TPRs are left in place and SINIT's DMA protection
+ * will be retained.
+ */
+ if (ret == 0 && tboot_enabled()) {
+ tboot_check_tprs();
+ if (tboot_teardown_tprs() != 0)
+ pr_warn("tboot: Failed to disable TPRs. DMA is still blocked.\n");
+ }
+
return ret;
}
diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c
index 2e3b3ab216f8f..8cb9096b8d732 100644
--- a/drivers/iommu/intel/iommu.c
+++ b/drivers/iommu/intel/iommu.c
@@ -2648,6 +2648,15 @@ int __init intel_iommu_init(void)
init_no_remapping_devices();
+ /*
+ * SINIT TPRs (if any were active) should be already torn down during DMAR
+ * Table parsing process. If the teardown failed, DMA (including the IOMMU's
+ * own QI ring) is still blocked, so refuse to continue a forced launch
+ * rather than hang mid-init.
+ */
+ if (tboot_is_tpr_protection_enabled() && !intel_iommu_tboot_noforce)
+ panic("tboot: Failed to disable TPRs. DMA is still blocked.\n");
+
ret = init_dmars();
if (ret) {
if (dmar_policy_force_on())
@@ -2698,6 +2707,14 @@ int __init intel_iommu_init(void)
iommu_disable_protect_mem_regions(iommu);
}
+
+ /*
+ * Release the TXT heap mapping if it is still held. TPRs (and the
+ * heap) are normally torn down in parse_dmar_table(); this is a
+ * safety net for paths where no TPRs were active.
+ */
+ tboot_free_txt_heap_mem();
+
up_read(&dmar_global_lock);
pr_info("Intel(R) Virtualization Technology for Directed I/O\n");
@@ -2707,6 +2724,7 @@ int __init intel_iommu_init(void)
return 0;
out_free_dmar:
+ tboot_free_txt_heap_mem();
intel_iommu_free_dmars();
up_write(&dmar_global_lock);
return ret;
--
2.43.0
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH v7 1/2] x86/tboot: Add support for parsing DTPR table and disabling TPRs
2026-10-27 16:13 [PATCH v6 1/2] x86/tboot: Add support for parsing DTPR table and disabling TPRs Sun, Ning
@ 2026-10-29 13:40 ` Michal Camacho Romero
0 siblings, 0 replies; 6+ messages in thread
From: Michal Camacho Romero @ 2026-10-29 13:40 UTC (permalink / raw)
To: Ning Sun
Cc: Lu Baolu, Thomas Gleixner, Michal Camacho Romero, 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_check_txt_heap_section_bounds() - function checks if a given
TXT heap section is within
the bounds of the heap.
* tboot_check_tprs() - function locates and caches
the DTPR table and counts the
number of the enabled TPRs.
* tboot_is_tpr_protection_enabled() - function reports whether any
TPR region is currently
enabled.
* tboot_disable_tprs() - function iterates over TPR
instances and disables each
active TPR region by setting
TPR_BASE_DISABLE_BIT (bit 4) in
the TPRn_BASE register via MMIO.
* tboot_free_txt_heap_mem() - function releases the ioremap
mapping of the TXT heap.
* tboot_teardown_tprs() - function disables any enabled
TPRs and releases the TXT heap
mapping in a single call.
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
Tested-by: Michal Camacho Romero <michal.camacho.romero@linux.intel.com>
Signed-off-by: Michal Camacho Romero <michal.camacho.romero@intel.com>
---
Changes since v6:
- Functionally unchanged; only picks up checkpatch --strict cleanups:
statics are no longer explicitly initialised, the TPR count is now
signed (s64), and tab indentation, pointer/cast style, NULL-comparison
and brace style, and unsplit log strings were fixed.
arch/x86/kernel/tboot.c | 515 ++++++++++++++++++++++++++++++++++++++--
include/linux/tboot.h | 28 ++-
2 files changed, 523 insertions(+), 20 deletions(-)
diff --git a/arch/x86/kernel/tboot.c b/arch/x86/kernel/tboot.c
index 46b8f1f16676e..47da21ad0f3e2 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>
@@ -42,6 +43,15 @@ static struct tboot *tboot __read_mostly;
static u8 tboot_uuid[16] __initdata = TBOOT_UUID;
+/* Global pointer to TXT HEAP base */
+static void __iomem *txt_heap;
+
+/* Global pointer to the DTPR table */
+static struct acpi_table_dtpr *dtpr;
+
+/* Number of active TPRs */
+static s64 num_of_enabled_tprs;
+
bool tboot_enabled(void)
{
return tboot != NULL;
@@ -223,6 +233,143 @@ static int tboot_setup_sleep(void)
#endif
+static bool tboot_check_txt_heap_section_bounds(const u64 heap_end,
+ void *heap_section,
+ u64 *heap_section_size,
+ const char *section_name)
+{
+ if (!heap_section) {
+ pr_err("%s section is NULL\n", section_name);
+ return false;
+ }
+
+ if (!heap_section_size) {
+ pr_err("%s section size pointer is NULL\n", section_name);
+ return false;
+ }
+
+ *heap_section_size = 0;
+
+ if ((u64)heap_section > heap_end || heap_end - (u64)heap_section < 8) {
+ pr_err("%s size field exceeds heap boundary\n", section_name);
+ return false;
+ }
+
+ /* Read the size of the current TXT Heap section */
+ *heap_section_size = *(u64 __force *)heap_section;
+
+ if (*heap_section_size < 8) {
+ pr_err("%s size is too small\n", section_name);
+ return false;
+ }
+
+ if (*heap_section_size > heap_end - (u64)heap_section) {
+ pr_err("%s exceeds heap boundary\n", section_name);
+ return false;
+ }
+
+ return true;
+}
+
+static bool tboot_check_dtpr_size(const struct heap_ext_data_elt *elt)
+{
+ u64 dtpr_payload_size, dtpr_offset;
+ u32 i, ref_tpr_cnt;
+
+ struct acpi_table_dtpr *tmp_dtpr = NULL;
+ struct acpi_tpr_instance *tpr_inst = NULL;
+ struct acpi_tpr_aux_sr *tpr_aux_srl = NULL;
+
+ if (!elt) {
+ pr_err("DTPR element heap extension data is NULL\n");
+ return false;
+ }
+
+ tmp_dtpr = (struct acpi_table_dtpr *)elt->data;
+ dtpr_payload_size = elt->size - sizeof(*elt);
+
+ if (dtpr_payload_size < sizeof(struct acpi_table_dtpr)) {
+ pr_err("DTPR element payload too small for a DTPR header\n");
+ return false;
+ }
+
+ if (tmp_dtpr->header.length != dtpr_payload_size) {
+ pr_err("Mismatch between DTPR table length and payload size\n");
+ return false;
+ }
+
+ dtpr_offset = sizeof(struct acpi_table_dtpr);
+
+ if (dtpr_offset > dtpr_payload_size) {
+ pr_err("TPR header exceeds table's declared size\n");
+ return false;
+ }
+
+ if (tmp_dtpr->ins_cnt < 1) {
+ pr_err("DTPR table declares no TPR instances\n");
+ return false;
+ }
+
+ for (i = 0; i < tmp_dtpr->ins_cnt; i++) {
+ /* prove this instance's header is in bounds before reading it */
+ if (dtpr_offset + sizeof(struct acpi_tpr_instance) > dtpr_payload_size) {
+ pr_err("TPR instance No.%u header exceeds DTPR table size\n", i);
+ return false;
+ }
+
+ /* iterate to the current TPR instance */
+ tpr_inst = (struct acpi_tpr_instance *)((u8 *)tmp_dtpr + dtpr_offset);
+
+ if (tpr_inst->tpr_cnt < 2) {
+ pr_err("TPR Instance %u has less than 2 TPRs, further DTPR processing interrupted.\n",
+ i);
+ return false;
+ }
+
+ /* all instances must carry the same TPR count as the first */
+ if (i == 0) {
+ ref_tpr_cnt = tpr_inst->tpr_cnt;
+ } else if (tpr_inst->tpr_cnt != ref_tpr_cnt) {
+ pr_err("TPR Instance %u has inconsistent TPR count: expected %u, found %u\n",
+ i, ref_tpr_cnt, tpr_inst->tpr_cnt);
+ return false;
+ }
+
+ /* iterate over each TPR instance */
+ dtpr_offset += sizeof(struct acpi_tpr_instance) +
+ (u64)tpr_inst->tpr_cnt * sizeof(struct acpi_tpr_array);
+
+ /* verify TPR array size for this instance */
+ if (dtpr_offset > dtpr_payload_size) {
+ pr_err("TPR instance No.%u TPR entries exceed DTPR table size\n",
+ i);
+ return false;
+ }
+ }
+
+ tpr_aux_srl = (struct acpi_tpr_aux_sr *)((u8 *)tmp_dtpr + dtpr_offset);
+ dtpr_offset += sizeof(u32);
+
+ if (dtpr_offset > dtpr_payload_size) {
+ pr_err("TPR SRL count field exceeds DTPR table size\n");
+ return false;
+ }
+
+ dtpr_offset += (u64)tpr_aux_srl->srl_cnt *
+ sizeof(struct acpi_tpr_serialize_request);
+ if (dtpr_offset > dtpr_payload_size) {
+ pr_err("TPR SRL entries exceed DTPR table size\n");
+ return false;
+ }
+
+ if (dtpr_offset < dtpr_payload_size) {
+ pr_err("DTPR table is smaller than expected\n");
+ return false;
+ }
+
+ return true;
+}
+
void tboot_shutdown(u32 shutdown_type)
{
void (*shutdown)(void);
@@ -454,26 +601,28 @@ struct sha1_hash {
};
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)
{
- void *heap_base, *heap_ptr, *config;
+ void *heap_ptr, *config;
if (!tboot_enabled())
return dmar_tbl;
@@ -490,15 +639,15 @@ struct acpi_table_header *tboot_get_dmar_table(struct acpi_table_header *dmar_tb
return NULL;
/* now map TXT heap */
- heap_base = ioremap(*(u64 *)(config + TXTCR_HEAP_BASE),
+ txt_heap = ioremap(*(u64 *)(config + TXTCR_HEAP_BASE),
*(u64 *)(config + TXTCR_HEAP_SIZE));
iounmap(config);
- if (!heap_base)
+ if (!txt_heap)
return NULL;
/* walk heap to SinitMleData */
/* skip BiosData */
- heap_ptr = heap_base + *(u64 *)heap_base;
+ heap_ptr = txt_heap + *(u64 *)txt_heap;
/* skip OsMleData */
heap_ptr += *(u64 *)heap_ptr;
/* skip OsSinitData */
@@ -514,3 +663,331 @@ struct acpi_table_header *tboot_get_dmar_table(struct acpi_table_header *dmar_tb
return dmar_tbl;
}
+
+static struct acpi_table_dtpr *tboot_get_dtpr_table(void)
+{
+ 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 (!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_size = *(u64 __force *)(config + TXTCR_HEAP_SIZE);
+ if (!txt_heap)
+ txt_heap = ioremap(*(u64 __force *)(config + TXTCR_HEAP_BASE), heap_size);
+
+ iounmap(config);
+
+ if (!txt_heap) {
+ pr_err("Failed to map TXT heap\n");
+ return NULL;
+ }
+
+ if (heap_size == 0 || (u64)txt_heap + heap_size < (u64)txt_heap) {
+ pr_err("TXT heap size invalid or overflows\n");
+ goto teardown;
+ }
+
+ heap_end = (u64)txt_heap + heap_size;
+
+ /* walk heap to SinitMleData */
+ /* skip BiosData */
+ /* get BiosData section size */
+ if (!tboot_check_txt_heap_section_bounds(heap_end, txt_heap,
+ &heap_section_size, "BiosData")) {
+ goto teardown;
+ }
+
+ /* skip OsMleData */
+ heap_ptr = txt_heap + heap_section_size;
+ /* get OsMleData section size */
+ if (!tboot_check_txt_heap_section_bounds(heap_end, heap_ptr,
+ &heap_section_size, "OsMleData")) {
+ goto teardown;
+ }
+
+ /* skip OsSinitData */
+ heap_ptr += heap_section_size;
+ /* get OsSinitData section size */
+ if (!tboot_check_txt_heap_section_bounds(heap_end, heap_ptr,
+ &heap_section_size, "OsSinitData")) {
+ goto teardown;
+ }
+
+ /* jump to the SinitMleData */
+ heap_ptr += heap_section_size;
+ sinit_mle_size = 0;
+ /* now points to SinitMleDataSize; set to SinitMleData */
+ if (!tboot_check_txt_heap_section_bounds(heap_end, heap_ptr,
+ &sinit_mle_size, "SinitMleData")) {
+ goto teardown;
+ }
+
+ 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 teardown;
+ }
+
+ if (sinit_mle->version < 9) {
+ pr_err("Unsupported SinitMleData version: %u\n", sinit_mle->version);
+ goto teardown;
+ }
+
+ heap_ptr += sizeof(struct sinit_mle_data);
+ if (heap_ptr > sinit_mle_end) {
+ pr_err("SinitMleData header out of bounds.\n");
+ goto teardown;
+ }
+
+ /*
+ * The SinitMleData extended-data area is a self-describing list of
+ * variable-length elements with no stored element count; it is
+ * terminated only by a HEAP_EXTDATA_TYPE_END element. Traversal must
+ * therefore be driven by each element's own size field. Forward
+ * progress and termination are guaranteed on every iteration:
+ * - the 8-byte header is bounds-checked before type/size are read,
+ * - elt->size >= sizeof(*elt) enforces a strictly-positive advance,
+ * - the "elt <= sinit_mle_end" cap bounds the number of steps.
+ */
+ 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 teardown;
+ }
+
+ if (elt->size < sizeof(*elt)) {
+ pr_err("Invalid SinitMleData element size: %u\n", elt->size);
+ goto teardown;
+ }
+
+ if (elt->type == HEAP_EXTDATA_TYPE_END ||
+ elt->type == HEAP_EXTDATA_TYPE_DTPR) {
+ break;
+ }
+
+ elt = (void *)elt + elt->size;
+ } while ((void *)elt <= sinit_mle_end);
+
+ if ((void *)elt >= sinit_mle_end) {
+ pr_err("Reached the end of SinitMleData without finding DTPR nor END element.\n");
+ goto teardown;
+ }
+
+ if (elt->type == HEAP_EXTDATA_TYPE_END) {
+ pr_err("DTPR element not found in SinitMleData\n");
+ goto teardown;
+ }
+
+ if ((u8 *)elt + elt->size > (u8 *)sinit_mle_end) {
+ pr_err("DTPR Table exceeds SinitMleData bounds.\n");
+ goto teardown;
+ }
+
+ if (!tboot_check_dtpr_size(elt)) {
+ pr_err("Invalid DTPR Table size.\n");
+ goto teardown;
+ }
+
+ return (struct acpi_table_dtpr *)elt->data;
+
+teardown:
+ tboot_free_txt_heap_mem();
+ return NULL;
+}
+
+void tboot_check_tprs(void)
+{
+ struct acpi_tpr_instance *tpr_inst;
+ struct acpi_tpr_array *tpr_arr;
+ u32 *instance_cnt;
+ void __iomem *base;
+ u32 i, j;
+ u64 tpr_base_val;
+
+ dtpr = tboot_get_dtpr_table();
+
+ if (!txt_heap) {
+ pr_warn("Heap base is NULL.\n");
+ return;
+ }
+
+ if (!dtpr) {
+ pr_warn("DTPR Table is NULL.\n");
+ goto teardown;
+ }
+
+ if (!tboot_enabled()) {
+ pr_warn("TBOOT is not enabled. TPRs weren't set by TBOOT nor SINIT ACM.\n");
+ goto teardown;
+ }
+
+ 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));
+
+ /*
+ * Each TPR base address has already been verified by the SINIT ACM,
+ * so it can be treated as trustworthy and used safely for I/O
+ * mapping.
+ */
+ base = ioremap(tpr_arr->base, 16);
+ if (!base) {
+ pr_warn("TPR Instance %u, TPR No.%u verification failure.\n",
+ i, j);
+ continue;
+ }
+
+ tpr_base_val = readq(base);
+ pr_info("TPR instance %u, TPR %u:base 0x%llx, limit 0x%llx\n", i, j,
+ tpr_base_val, readq((u8 __iomem *)base + 8));
+
+ /* Verify if the TPR base is enabled */
+ if ((tpr_base_val & (u64)TPR_BASE_DISABLE_BIT) == 0)
+ num_of_enabled_tprs++;
+
+ iounmap(base);
+ }
+
+ tpr_inst = (struct acpi_tpr_instance *)((u8 *)tpr_inst +
+ sizeof(*tpr_inst) + j * sizeof(struct acpi_tpr_array));
+ }
+
+ pr_info("Number of enabled TPRs: %lld\n", num_of_enabled_tprs);
+ if (num_of_enabled_tprs > 0)
+ pr_info("TPR protection is active. PMR will be disabled\n");
+
+ return;
+
+teardown:
+ tboot_free_txt_heap_mem();
+}
+
+s64 tboot_disable_tprs(void)
+{
+ struct acpi_tpr_instance *tpr_inst;
+ struct acpi_tpr_array *tpr_arr;
+ u32 *instance_cnt;
+ void __iomem *base;
+ u32 i, j;
+ u64 tpr_base_val;
+ s64 ret = 0;
+
+ if (!txt_heap) {
+ pr_warn("TXT Heap base is NULL.\n");
+ return -1;
+ }
+
+ if (!dtpr) {
+ pr_warn("DTPR Table is NULL.\n");
+ ret = -1;
+ goto teardown;
+ }
+
+ if (!tboot_enabled()) {
+ pr_warn("TBOOT is not enabled. TPR weren't set by TBOOT and SINIT ACM.\n");
+ ret = -1;
+ goto teardown;
+ }
+
+ 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));
+
+ /*
+ * Each TPR base address has already been verified by the SINIT ACM,
+ * so it can be treated as trustworthy and used safely for I/O
+ * mapping.
+ */
+ base = ioremap(tpr_arr->base, 16);
+ if (!base) {
+ pr_warn("TPR Instance %u, TPR No.%u disabling failure.\n",
+ i, j);
+ continue;
+ }
+
+ tpr_base_val = readq(base);
+ pr_info("TPR instance %u, TPR %u:base 0x%llx, limit 0x%llx\n", i, j,
+ tpr_base_val, readq((u8 __iomem *)base + 8));
+
+ /* If the given TPR is active, disable it */
+ if ((tpr_base_val & (u64)TPR_BASE_DISABLE_BIT) == 0) {
+ writeq(tpr_base_val | TPR_BASE_DISABLE_BIT, base);
+ num_of_enabled_tprs--;
+ }
+ iounmap(base);
+ }
+
+ tpr_inst = (struct acpi_tpr_instance *)((u8 *)tpr_inst +
+ sizeof(*tpr_inst) + j * sizeof(struct acpi_tpr_array));
+ }
+
+ if (num_of_enabled_tprs > 0) {
+ pr_err("TPR disabling failed for some instances.\n");
+ pr_err("Number of still enabled TPRs: %lld\n", num_of_enabled_tprs);
+ } else if (num_of_enabled_tprs < 0) {
+ pr_err("Number of enabled TPRs is negative, which indicates an error.\n");
+ pr_err("TPR disabling resulted in a negative count: %lld\n",
+ num_of_enabled_tprs);
+ } else {
+ pr_info("All of TPRs have been successfully disabled.\n");
+ }
+
+ ret = 0;
+ goto teardown;
+
+teardown:
+ tboot_free_txt_heap_mem();
+ return ret;
+}
+
+s64 tboot_teardown_tprs(void)
+{
+ s64 ret = 0;
+
+ if (tboot_is_tpr_protection_enabled() && dtpr)
+ ret = tboot_disable_tprs(); /* uses cached dtpr + txt_heap */
+
+ tboot_free_txt_heap_mem(); /* releases internal mapping */
+ dtpr = NULL;
+ return ret; /* 0 = success / nothing to do */
+}
+
+void tboot_free_txt_heap_mem(void)
+{
+ if (txt_heap) {
+ iounmap(txt_heap);
+ txt_heap = NULL;
+ }
+}
+
+bool tboot_is_tpr_protection_enabled(void)
+{
+ return num_of_enabled_tprs > 0;
+}
diff --git a/include/linux/tboot.h b/include/linux/tboot.h
index d2279160ef39d..d7c00024b26e0 100644
--- a/include/linux/tboot.h
+++ b/include/linux/tboot.h
@@ -24,10 +24,17 @@ 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
+/*TPR Base Disable BIT*/
+#define TPR_BASE_DISABLE_BIT BIT(4)
+
struct tboot_mac_region {
u64 start; /* must be 64 byte -aligned */
u32 size; /* must be 64 byte -granular */
@@ -58,6 +65,15 @@ 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;
+ u8 data[];
+} __packed;
+
/*
* shared memory page used for communication between tboot and kernel
*/
@@ -122,19 +138,29 @@ struct tboot {
0xaa, 0x19, 0xea, 0x4d, 0x5, 0x7a, 0x8}
bool tboot_enabled(void);
+extern s64 tboot_disable_tprs(void);
+extern s64 tboot_teardown_tprs(void);
+extern void tboot_check_tprs(void);
+extern void tboot_free_txt_heap_mem(void);
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 bool tboot_is_tpr_protection_enabled(void);
#else
-#define tboot_enabled() 0
+#define tboot_enabled() 0
+#define tboot_disable_tprs() 0
+#define tboot_teardown_tprs() 0
+#define tboot_check_tprs() do { } while (0)
#define tboot_probe() do { } while (0)
#define tboot_shutdown(shutdown_type) do { } while (0)
#define tboot_sleep(sleep_state, pm1a_control, pm1b_control) \
do { } while (0)
#define tboot_get_dmar_table(dmar_tbl) (dmar_tbl)
+#define tboot_free_txt_heap_mem() do { } while (0)
+#define tboot_is_tpr_protection_enabled() 0
#endif /* !CONFIG_INTEL_TXT */
--
2.43.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 related [flat|nested] 6+ messages in thread
* Re: [PATCH v7 1/2] x86/tboot: Add support for parsing DTPR table and disabling TPRs
2026-10-29 11:57 ` [PATCH v7 1/2] x86/tboot: Add support for parsing DTPR table and disabling TPRs Michal Camacho Romero
@ 2026-10-30 4:59 ` Borislav Petkov
2026-10-30 12:22 ` kernel test robot
1 sibling, 0 replies; 6+ messages in thread
From: Borislav Petkov @ 2026-10-30 4:59 UTC (permalink / raw)
To: Michal Camacho Romero
Cc: Lu Baolu, Thomas Gleixner, Ning Sun, Michal Camacho Romero, x86,
iommu, tboot-devel, linux-kernel, Mateusz Mowka, Adam Pawlicki,
Pawel Randzio
On Thu, Oct 29, 2026 at 12:57:26PM +0100, Michal Camacho Romero wrote:
> Tested-by: Michal Camacho Romero <michal.camacho.romero@linux.intel.com>
I sure hope you did test it... :-)
In any case, you don't need to add that tag to your own patch as that is
implied.
Reality tells a different story, unfortunately... :-/
> Signed-off-by: Michal Camacho Romero <michal.camacho.romero@intel.com>
> ---
> arch/x86/kernel/tboot.c | 515 ++++++++++++++++++++++++++++++++++++++--
> include/linux/tboot.h | 28 ++-
> 2 files changed, 523 insertions(+), 20 deletions(-)
Below only stylistic issues we pay attention to in the tip tree. Pls have
a look at
https://docs.kernel.org/process/maintainer-tip.html
and fix up coding style to adhere to the tip tree's standard.
> diff --git a/arch/x86/kernel/tboot.c b/arch/x86/kernel/tboot.c
> index 46b8f1f16676e..47da21ad0f3e2 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>
> @@ -42,6 +43,15 @@ static struct tboot *tboot __read_mostly;
>
> static u8 tboot_uuid[16] __initdata = TBOOT_UUID;
>
> +/* Global pointer to TXT HEAP base */
> +static void __iomem *txt_heap;
> +
> +/* Global pointer to the DTPR table */
> +static struct acpi_table_dtpr *dtpr;
> +
> +/* Number of active TPRs */
> +static s64 num_of_enabled_tprs;
> +
> bool tboot_enabled(void)
> {
> return tboot != NULL;
> @@ -223,6 +233,143 @@ static int tboot_setup_sleep(void)
>
> #endif
>
> +static bool tboot_check_txt_heap_section_bounds(const u64 heap_end,
> + void *heap_section,
> + u64 *heap_section_size,
> + const char *section_name)
Are those aligned on the opening brace? Check all our patches pls.
> +{
> + if (!heap_section) {
> + pr_err("%s section is NULL\n", section_name);
> + return false;
> + }
> +
> + if (!heap_section_size) {
> + pr_err("%s section size pointer is NULL\n", section_name);
> + return false;
> + }
> +
> + *heap_section_size = 0;
> +
> + if ((u64)heap_section > heap_end || heap_end - (u64)heap_section < 8) {
> + pr_err("%s size field exceeds heap boundary\n", section_name);
> + return false;
> + }
> +
> + /* Read the size of the current TXT Heap section */
> + *heap_section_size = *(u64 __force *)heap_section;
> +
> + if (*heap_section_size < 8) {
> + pr_err("%s size is too small\n", section_name);
> + return false;
> + }
> +
> + if (*heap_section_size > heap_end - (u64)heap_section) {
> + pr_err("%s exceeds heap boundary\n", section_name);
> + return false;
> + }
> +
> + return true;
> +}
> +
> +static bool tboot_check_dtpr_size(const struct heap_ext_data_elt *elt)
> +{
> + u64 dtpr_payload_size, dtpr_offset;
> + u32 i, ref_tpr_cnt;
> +
> + struct acpi_table_dtpr *tmp_dtpr = NULL;
> + struct acpi_tpr_instance *tpr_inst = NULL;
> + struct acpi_tpr_aux_sr *tpr_aux_srl = NULL;
> +
> + if (!elt) {
> + pr_err("DTPR element heap extension data is NULL\n");
> + return false;
> + }
> +
> + tmp_dtpr = (struct acpi_table_dtpr *)elt->data;
> + dtpr_payload_size = elt->size - sizeof(*elt);
> +
> + if (dtpr_payload_size < sizeof(struct acpi_table_dtpr)) {
> + pr_err("DTPR element payload too small for a DTPR header\n");
> + return false;
> + }
> +
> + if (tmp_dtpr->header.length != dtpr_payload_size) {
> + pr_err("Mismatch between DTPR table length and payload size\n");
> + return false;
> + }
> +
> + dtpr_offset = sizeof(struct acpi_table_dtpr);
> +
> + if (dtpr_offset > dtpr_payload_size) {
> + pr_err("TPR header exceeds table's declared size\n");
> + return false;
> + }
> +
> + if (tmp_dtpr->ins_cnt < 1) {
> + pr_err("DTPR table declares no TPR instances\n");
> + return false;
> + }
> +
> + for (i = 0; i < tmp_dtpr->ins_cnt; i++) {
> + /* prove this instance's header is in bounds before reading it */
> + if (dtpr_offset + sizeof(struct acpi_tpr_instance) > dtpr_payload_size) {
> + pr_err("TPR instance No.%u header exceeds DTPR table size\n", i);
> + return false;
> + }
> +
> + /* iterate to the current TPR instance */
> + tpr_inst = (struct acpi_tpr_instance *)((u8 *)tmp_dtpr + dtpr_offset);
This line is not iterating over anything.
Please check all your comments and tone them down.
> +
> + if (tpr_inst->tpr_cnt < 2) {
> + pr_err("TPR Instance %u has less than 2 TPRs, further DTPR processing interrupted.\n",
> + i);
Ugly linebreak.
Check the rest for more.
> + return false;
> + }
> +
> + /* all instances must carry the same TPR count as the first */
> + if (i == 0) {
if (!i)
> + ref_tpr_cnt = tpr_inst->tpr_cnt;
> + } else if (tpr_inst->tpr_cnt != ref_tpr_cnt) {
> + pr_err("TPR Instance %u has inconsistent TPR count: expected %u, found %u\n",
> + i, ref_tpr_cnt, tpr_inst->tpr_cnt);
> + return false;
> + }
> +
> + /* iterate over each TPR instance */
> + dtpr_offset += sizeof(struct acpi_tpr_instance) +
> + (u64)tpr_inst->tpr_cnt * sizeof(struct acpi_tpr_array);
> +
> + /* verify TPR array size for this instance */
> + if (dtpr_offset > dtpr_payload_size) {
> + pr_err("TPR instance No.%u TPR entries exceed DTPR table size\n",
> + i);
> + return false;
> + }
> + }
> +
> + tpr_aux_srl = (struct acpi_tpr_aux_sr *)((u8 *)tmp_dtpr + dtpr_offset);
> + dtpr_offset += sizeof(u32);
> +
> + if (dtpr_offset > dtpr_payload_size) {
> + pr_err("TPR SRL count field exceeds DTPR table size\n");
> + return false;
> + }
> +
> + dtpr_offset += (u64)tpr_aux_srl->srl_cnt *
> + sizeof(struct acpi_tpr_serialize_request);
> + if (dtpr_offset > dtpr_payload_size) {
> + pr_err("TPR SRL entries exceed DTPR table size\n");
> + return false;
> + }
> +
> + if (dtpr_offset < dtpr_payload_size) {
> + pr_err("DTPR table is smaller than expected\n");
> + return false;
> + }
> +
> + return true;
> +}
> +
> void tboot_shutdown(u32 shutdown_type)
> {
> void (*shutdown)(void);
...
> @@ -514,3 +663,331 @@ struct acpi_table_header *tboot_get_dmar_table(struct acpi_table_header *dmar_tb
>
> return dmar_tbl;
> }
> +
> +static struct acpi_table_dtpr *tboot_get_dtpr_table(void)
> +{
> + 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;
The tip-tree preferred ordering of variable declarations at the
beginning of a function is reverse fir tree order::
struct long_struct_name *descriptive_name;
unsigned long foo, bar;
unsigned int tmp;
int ret;
The above is faster to parse than the reverse ordering::
int ret;
unsigned int tmp;
unsigned long foo, bar;
struct long_struct_name *descriptive_name;
And even more so than random ordering::
unsigned long foo, bar;
int ret;
struct long_struct_name *descriptive_name;
unsigned int tmp;
Please check all your code.
> +
> + 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_size = *(u64 __force *)(config + TXTCR_HEAP_SIZE);
> + if (!txt_heap)
> + txt_heap = ioremap(*(u64 __force *)(config + TXTCR_HEAP_BASE), heap_size);
> +
> + iounmap(config);
> +
> + if (!txt_heap) {
> + pr_err("Failed to map TXT heap\n");
> + return NULL;
> + }
> +
> + if (heap_size == 0 || (u64)txt_heap + heap_size < (u64)txt_heap) {
> + pr_err("TXT heap size invalid or overflows\n");
> + goto teardown;
> + }
> +
> + heap_end = (u64)txt_heap + heap_size;
> +
> + /* walk heap to SinitMleData */
> + /* skip BiosData */
> + /* get BiosData section size */
> + if (!tboot_check_txt_heap_section_bounds(heap_end, txt_heap,
> + &heap_section_size, "BiosData")) {
> + goto teardown;
> + }
> +
> + /* skip OsMleData */
> + heap_ptr = txt_heap + heap_section_size;
> + /* get OsMleData section size */
> + if (!tboot_check_txt_heap_section_bounds(heap_end, heap_ptr,
> + &heap_section_size, "OsMleData")) {
Ugly linebreaks. Let it stick out. Ditto for the rest.
> + goto teardown;
> + }
> +
> + /* skip OsSinitData */
> + heap_ptr += heap_section_size;
> + /* get OsSinitData section size */
> + if (!tboot_check_txt_heap_section_bounds(heap_end, heap_ptr,
> + &heap_section_size, "OsSinitData")) {
> + goto teardown;
> + }
> +
> + /* jump to the SinitMleData */
> + heap_ptr += heap_section_size;
> + sinit_mle_size = 0;
> + /* now points to SinitMleDataSize; set to SinitMleData */
> + if (!tboot_check_txt_heap_section_bounds(heap_end, heap_ptr,
> + &sinit_mle_size, "SinitMleData")) {
> + goto teardown;
> + }
> +
> + 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 teardown;
> + }
> +
> + if (sinit_mle->version < 9) {
> + pr_err("Unsupported SinitMleData version: %u\n", sinit_mle->version);
> + goto teardown;
> + }
> +
> + heap_ptr += sizeof(struct sinit_mle_data);
> + if (heap_ptr > sinit_mle_end) {
> + pr_err("SinitMleData header out of bounds.\n");
> + goto teardown;
> + }
> +
> + /*
> + * The SinitMleData extended-data area is a self-describing list of
> + * variable-length elements with no stored element count; it is
> + * terminated only by a HEAP_EXTDATA_TYPE_END element. Traversal must
> + * therefore be driven by each element's own size field. Forward
> + * progress and termination are guaranteed on every iteration:
> + * - the 8-byte header is bounds-checked before type/size are read,
> + * - elt->size >= sizeof(*elt) enforces a strictly-positive advance,
> + * - the "elt <= sinit_mle_end" cap bounds the number of steps.
> + */
> + 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 teardown;
> + }
> +
> + if (elt->size < sizeof(*elt)) {
> + pr_err("Invalid SinitMleData element size: %u\n", elt->size);
> + goto teardown;
> + }
> +
> + if (elt->type == HEAP_EXTDATA_TYPE_END ||
> + elt->type == HEAP_EXTDATA_TYPE_DTPR) {
> + break;
> + }
> +
> + elt = (void *)elt + elt->size;
> + } while ((void *)elt <= sinit_mle_end);
> +
> + if ((void *)elt >= sinit_mle_end) {
> + pr_err("Reached the end of SinitMleData without finding DTPR nor END element.\n");
> + goto teardown;
> + }
> +
> + if (elt->type == HEAP_EXTDATA_TYPE_END) {
> + pr_err("DTPR element not found in SinitMleData\n");
> + goto teardown;
> + }
> +
> + if ((u8 *)elt + elt->size > (u8 *)sinit_mle_end) {
> + pr_err("DTPR Table exceeds SinitMleData bounds.\n");
> + goto teardown;
> + }
> +
> + if (!tboot_check_dtpr_size(elt)) {
> + pr_err("Invalid DTPR Table size.\n");
> + goto teardown;
> + }
> +
> + return (struct acpi_table_dtpr *)elt->data;
> +
> +teardown:
> + tboot_free_txt_heap_mem();
> + return NULL;
> +}
> +
> +s64 tboot_teardown_tprs(void)
> +{
> + s64 ret = 0;
> +
> + if (tboot_is_tpr_protection_enabled() && dtpr)
> + ret = tboot_disable_tprs(); /* uses cached dtpr + txt_heap */
> +
> + tboot_free_txt_heap_mem(); /* releases internal mapping */
> + dtpr = NULL;
> + return ret; /* 0 = success / nothing to do */
> +}
> +
> +void tboot_free_txt_heap_mem(void)
> +{
> + if (txt_heap) {
> + iounmap(txt_heap);
> + txt_heap = NULL;
> + }
> +}
> +
> +bool tboot_is_tpr_protection_enabled(void)
> +{
> + return num_of_enabled_tprs > 0;
> +}
> diff --git a/include/linux/tboot.h b/include/linux/tboot.h
> index d2279160ef39d..d7c00024b26e0 100644
> --- a/include/linux/tboot.h
> +++ b/include/linux/tboot.h
> @@ -24,10 +24,17 @@ 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
>
> +/*TPR Base Disable BIT*/
> +#define TPR_BASE_DISABLE_BIT BIT(4)
> +
> struct tboot_mac_region {
> u64 start; /* must be 64 byte -aligned */
> u32 size; /* must be 64 byte -granular */
Please put side comments over the line they comment.
check your whole code.
Thx.
--
Regards/Gruss,
Boris.
https://people.kernel.org/tglx/notes-about-netiquette
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH v7 1/2] x86/tboot: Add support for parsing DTPR table and disabling TPRs
2026-10-29 11:57 ` [PATCH v7 1/2] x86/tboot: Add support for parsing DTPR table and disabling TPRs Michal Camacho Romero
2026-10-30 4:59 ` Borislav Petkov
@ 2026-10-30 12:22 ` kernel test robot
1 sibling, 0 replies; 6+ messages in thread
From: kernel test robot @ 2026-10-30 12:22 UTC (permalink / raw)
To: Michal Camacho Romero, Lu Baolu, Thomas Gleixner, Ning Sun
Cc: oe-kbuild-all, Michal Camacho Romero, x86, iommu, tboot-devel,
linux-kernel, Mateusz Mowka, Adam Pawlicki, Pawel Randzio
Hi Michal,
kernel test robot noticed the following build warnings:
[auto build test WARNING on tip/master]
[also build test WARNING on tip/x86/core linus/master v7.3 next-20261029]
[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/20261029-125726
base: tip/master
patch link: https://lore.kernel.org/r/8549af9ea60b73e120be377877c26d929f1ad25e.1793273244.git.michal.camacho.romero%40linux.intel.com
patch subject: [PATCH v7 1/2] x86/tboot: Add support for parsing DTPR table and disabling TPRs
config: i386-allyesconfig (https://download.01.org/0day-ci/archive/20261030/202610302057.2LOBfy3p-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/20261030/202610302057.2LOBfy3p-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/202610302057.2LOBfy3p-lkp@intel.com/
All warnings (new ones prefixed by >>):
arch/x86/kernel/tboot.c: In function 'tboot_check_txt_heap_section_bounds':
>> arch/x86/kernel/tboot.c:253:13: warning: cast from pointer to integer of different size [-Wpointer-to-int-cast]
253 | if ((u64)heap_section > heap_end || heap_end - (u64)heap_section < 8) {
| ^
arch/x86/kernel/tboot.c:253:56: warning: cast from pointer to integer of different size [-Wpointer-to-int-cast]
253 | if ((u64)heap_section > heap_end || heap_end - (u64)heap_section < 8) {
| ^
arch/x86/kernel/tboot.c:266:45: warning: cast from pointer to integer of different size [-Wpointer-to-int-cast]
266 | if (*heap_section_size > heap_end - (u64)heap_section) {
| ^
arch/x86/kernel/tboot.c: In function 'tboot_get_dtpr_table':
arch/x86/kernel/tboot.c:699:31: warning: cast from pointer to integer of different size [-Wpointer-to-int-cast]
699 | if (heap_size == 0 || (u64)txt_heap + heap_size < (u64)txt_heap) {
| ^
arch/x86/kernel/tboot.c:699:59: warning: cast from pointer to integer of different size [-Wpointer-to-int-cast]
699 | if (heap_size == 0 || (u64)txt_heap + heap_size < (u64)txt_heap) {
| ^
arch/x86/kernel/tboot.c:704:22: warning: cast from pointer to integer of different size [-Wpointer-to-int-cast]
704 | heap_end = (u64)txt_heap + heap_size;
| ^
arch/x86/kernel/tboot.c: In function 'tboot_check_tprs':
arch/x86/kernel/tboot.c:862:40: error: implicit declaration of function 'readq'; did you mean 'readl'? [-Wimplicit-function-declaration]
862 | tpr_base_val = readq(base);
| ^~~~~
| readl
In file included from include/asm-generic/bug.h:31,
from arch/x86/include/asm/bug.h:195,
from arch/x86/include/asm/alternative.h:9,
from arch/x86/include/asm/atomic.h:7,
from include/linux/atomic.h:7,
from include/linux/rcupdate.h:25,
from include/linux/init_task.h:5,
from arch/x86/kernel/tboot.c:9:
>> include/linux/kern_levels.h:5:25: warning: format '%llx' expects argument of type 'long long unsigned int', but argument 5 has type 'int' [-Wformat=]
5 | #define KERN_SOH "\001" /* ASCII Start Of Header */
| ^~~~~~
include/linux/printk.h:483:25: note: in definition of macro 'printk_index_wrap'
483 | _p_func(_fmt, ##__VA_ARGS__); \
| ^~~~
include/linux/printk.h:584:9: note: in expansion of macro 'printk'
584 | printk(KERN_INFO pr_fmt(fmt), ##__VA_ARGS__)
| ^~~~~~
include/linux/kern_levels.h:14:25: note: in expansion of macro 'KERN_SOH'
14 | #define KERN_INFO KERN_SOH "6" /* informational */
| ^~~~~~~~
include/linux/printk.h:584:16: note: in expansion of macro 'KERN_INFO'
584 | printk(KERN_INFO pr_fmt(fmt), ##__VA_ARGS__)
| ^~~~~~~~~
arch/x86/kernel/tboot.c:863:25: note: in expansion of macro 'pr_info'
863 | pr_info("TPR instance %u, TPR %u:base 0x%llx, limit 0x%llx\n", i, j,
| ^~~~~~~
arch/x86/kernel/tboot.c: In function 'tboot_disable_tprs':
>> include/linux/kern_levels.h:5:25: warning: format '%llx' expects argument of type 'long long unsigned int', but argument 5 has type 'int' [-Wformat=]
5 | #define KERN_SOH "\001" /* ASCII Start Of Header */
| ^~~~~~
include/linux/printk.h:483:25: note: in definition of macro 'printk_index_wrap'
483 | _p_func(_fmt, ##__VA_ARGS__); \
| ^~~~
include/linux/printk.h:584:9: note: in expansion of macro 'printk'
584 | printk(KERN_INFO pr_fmt(fmt), ##__VA_ARGS__)
| ^~~~~~
include/linux/kern_levels.h:14:25: note: in expansion of macro 'KERN_SOH'
14 | #define KERN_INFO KERN_SOH "6" /* informational */
| ^~~~~~~~
include/linux/printk.h:584:16: note: in expansion of macro 'KERN_INFO'
584 | printk(KERN_INFO pr_fmt(fmt), ##__VA_ARGS__)
| ^~~~~~~~~
arch/x86/kernel/tboot.c:936:25: note: in expansion of macro 'pr_info'
936 | pr_info("TPR instance %u, TPR %u:base 0x%llx, limit 0x%llx\n", i, j,
| ^~~~~~~
arch/x86/kernel/tboot.c:941:33: error: implicit declaration of function 'writeq'; did you mean 'writel'? [-Wimplicit-function-declaration]
941 | writeq(tpr_base_val | TPR_BASE_DISABLE_BIT, base);
| ^~~~~~
| writel
vim +253 arch/x86/kernel/tboot.c
235
236 static bool tboot_check_txt_heap_section_bounds(const u64 heap_end,
237 void *heap_section,
238 u64 *heap_section_size,
239 const char *section_name)
240 {
241 if (!heap_section) {
242 pr_err("%s section is NULL\n", section_name);
243 return false;
244 }
245
246 if (!heap_section_size) {
247 pr_err("%s section size pointer is NULL\n", section_name);
248 return false;
249 }
250
251 *heap_section_size = 0;
252
> 253 if ((u64)heap_section > heap_end || heap_end - (u64)heap_section < 8) {
254 pr_err("%s size field exceeds heap boundary\n", section_name);
255 return false;
256 }
257
258 /* Read the size of the current TXT Heap section */
259 *heap_section_size = *(u64 __force *)heap_section;
260
261 if (*heap_section_size < 8) {
262 pr_err("%s size is too small\n", section_name);
263 return false;
264 }
265
266 if (*heap_section_size > heap_end - (u64)heap_section) {
267 pr_err("%s exceeds heap boundary\n", section_name);
268 return false;
269 }
270
271 return true;
272 }
273
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2026-10-30 12:23 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-29 11:57 [PATCH v7 0/2] x86/tboot: Add Intel TXT Protection Regions (TPR) support Michal Camacho Romero
2026-10-29 11:57 ` [PATCH v7 1/2] x86/tboot: Add support for parsing DTPR table and disabling TPRs Michal Camacho Romero
2026-10-30 4:59 ` Borislav Petkov
2026-10-30 12:22 ` kernel test robot
2026-10-29 11:57 ` [PATCH v7 2/2] iommu/vt-d: Disable TXT TPRs before enabling queued invalidation Michal Camacho Romero
-- strict thread matches above, loose matches on Subject: below --
2026-10-27 16:13 [PATCH v6 1/2] x86/tboot: Add support for parsing DTPR table and disabling TPRs Sun, Ning
2026-10-29 13:40 ` [PATCH v7 " Michal Camacho Romero
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.