* [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 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
* 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
* [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
* [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
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®