From 12b80f60099a01a5c1e861c24a4fd245c6ff7032 Mon Sep 17 00:00:00 2001 From: Ross Philipson Date: Wed, 4 Feb 2009 11:17:52 -0500 Subject: [PATCH] tboot patch 5/5 - use protected DMAR The VT-d DMAR ACPI tables may not be DMA protected by tboot. However, SINIT saves a copy of them in the SinitMleData struct in the TXT heap (which is DMA protected). So we should read the DMAR table from that copy if launched by tboot. Changes to be committed: modified: xen/arch/x86/tboot.c modified: xen/drivers/passthrough/vtd/dmar.c modified: xen/include/asm-x86/tboot.h --- xen/arch/x86/tboot.c | 124 +++++++++++++++++++++++++---- xen/drivers/passthrough/vtd/dmar.c | 5 +- xen/include/asm-x86/tboot.h | 3 + 3 files changed, 114 insertions(+), 18 deletions(-) diff --git a/xen/arch/x86/tboot.c b/xen/arch/x86/tboot.c index 4577d0f..608d34d 100644 --- a/xen/arch/x86/tboot.c +++ b/xen/arch/x86/tboot.c @@ -20,6 +20,10 @@ static const uuid_t tboot_shared_uuid = TBOOT_SHARED_UUID; extern char __init_begin[], __per_cpu_start[], __per_cpu_end[], __bss_start[]; +/* used by tboot_protect_mem_regions() and/or tboot_parse_dmar_table() */ +static uint64_t txt_heap_base, txt_heap_size; +static uint64_t sinit_base, sinit_size; + /* * TXT configuration registers (offsets from TXT_{PUB, PRIV}_CONFIG_REGS_BASE) */ @@ -37,10 +41,33 @@ extern char __init_begin[], __per_cpu_start[], __per_cpu_end[], __bss_start[]; #define TXTCR_HEAP_BASE 0x0300 #define TXTCR_HEAP_SIZE 0x0308 +#define SHA1_SIZE 20 +typedef uint8_t sha1_hash_t[SHA1_SIZE]; + +typedef struct __packed { + uint32_t version; /* currently 6 */ + sha1_hash_t bios_acm_id; + uint32_t edx_senter_flags; + uint64_t mseg_valid; + sha1_hash_t sinit_hash; + sha1_hash_t mle_hash; + sha1_hash_t stm_hash; + sha1_hash_t lcp_policy_hash; + uint32_t lcp_policy_control; + uint32_t rlp_wakeup_addr; + uint32_t reserved; + uint32_t num_mdrs; + uint32_t mdrs_off; + uint32_t num_vtd_dmars; + uint32_t vtd_dmars_off; +} sinit_mle_data_t; + void __init tboot_probe(void) { tboot_shared_t *tboot_shared; unsigned long p_tboot_shared; + uint32_t map_base, map_size; + unsigned long map_addr; /* Look for valid page-aligned address for shared page. */ p_tboot_shared = simple_strtoul(opt_tboot, NULL, 0); @@ -68,6 +95,30 @@ void __init tboot_probe(void) printk(" shutdown_entry: 0x%08x\n", tboot_shared->shutdown_entry); printk(" tboot_base: 0x%08x\n", tboot_shared->tboot_base); printk(" tboot_size: 0x%x\n", tboot_shared->tboot_size); + + /* these will be needed by tboot_protect_mem_regions() and/or + tboot_parse_dmar_table(), so get them now */ + + map_base = PFN_DOWN(TXT_PUB_CONFIG_REGS_BASE); + map_size = PFN_UP(NR_TXT_CONFIG_PAGES * PAGE_SIZE); + map_addr = (unsigned long)__va(map_base << PAGE_SHIFT); + if ( map_pages_to_xen(map_addr, map_base, map_size, __PAGE_HYPERVISOR) ) + return; + + /* TXT Heap */ + txt_heap_base = + *(uint64_t *)__va(TXT_PUB_CONFIG_REGS_BASE + TXTCR_HEAP_BASE); + txt_heap_size = + *(uint64_t *)__va(TXT_PUB_CONFIG_REGS_BASE + TXTCR_HEAP_SIZE); + + /* SINIT */ + sinit_base = + *(uint64_t *)__va(TXT_PUB_CONFIG_REGS_BASE + TXTCR_SINIT_BASE); + sinit_size = + *(uint64_t *)__va(TXT_PUB_CONFIG_REGS_BASE + TXTCR_SINIT_SIZE); + + destroy_xen_mappings((unsigned long)__va(map_base << PAGE_SHIFT), + (unsigned long)__va((map_base + map_size) << PAGE_SHIFT)); } void tboot_shutdown(uint32_t shutdown_type) @@ -125,29 +176,18 @@ int tboot_in_measured_env(void) int tboot_protect_mem_regions(void) { - uint64_t base, size; - uint32_t map_base, map_size; - unsigned long map_addr; - if ( !tboot_in_measured_env() ) return 1; - map_base = PFN_DOWN(TXT_PUB_CONFIG_REGS_BASE); - map_size = PFN_UP(NR_TXT_CONFIG_PAGES * PAGE_SIZE); - map_addr = (unsigned long)__va(map_base << PAGE_SHIFT); - if ( map_pages_to_xen(map_addr, map_base, map_size, __PAGE_HYPERVISOR) ) - return 0; - /* TXT Heap */ - base = *(uint64_t *)__va(TXT_PUB_CONFIG_REGS_BASE + TXTCR_HEAP_BASE); - size = *(uint64_t *)__va(TXT_PUB_CONFIG_REGS_BASE + TXTCR_HEAP_SIZE); - if ( !reserve_e820_unusable(&e820, base, base + size) ) + if ( txt_heap_base == 0 || + !reserve_e820_unusable(&e820, txt_heap_base, + txt_heap_base + txt_heap_size) ) return 0; /* SINIT */ - base = *(uint64_t *)__va(TXT_PUB_CONFIG_REGS_BASE + TXTCR_SINIT_BASE); - size = *(uint64_t *)__va(TXT_PUB_CONFIG_REGS_BASE + TXTCR_SINIT_SIZE); - if ( !reserve_e820_unusable(&e820, base, base + size) ) + if ( sinit_base == 0 || + !reserve_e820_unusable(&e820, sinit_base, sinit_base + sinit_size) ) return 0; /* TXT Private Space */ @@ -155,10 +195,60 @@ int tboot_protect_mem_regions(void) TXT_PRIV_CONFIG_REGS_BASE + NR_TXT_CONFIG_PAGES * PAGE_SIZE) ) return 0; + return 1; +} + +int __init tboot_parse_dmar_table(acpi_table_handler dmar_handler) +{ + uint32_t map_base, map_size; + unsigned long map_vaddr; + void *heap_ptr; + struct acpi_table_header *dmar_table; + int rc; + + if ( !tboot_in_measured_env() ) + return acpi_table_parse(ACPI_SIG_DMAR, dmar_handler); + + /* ACPI tables may not be DMA protected by tboot, so use DMAR copy */ + /* SINIT saved in SinitMleData in TXT heap (which is DMA protected) */ + + if ( txt_heap_base == 0 ) + return 1; + + /* map TXT heap into Xen addr space */ + map_base = PFN_DOWN(txt_heap_base); + map_size = PFN_UP(txt_heap_size); + map_vaddr = (unsigned long)__va(map_base << PAGE_SHIFT); + if ( map_pages_to_xen(map_vaddr, map_base, map_size, __PAGE_HYPERVISOR) ) + return 1; + + /* walk heap to SinitMleData */ + heap_ptr = __va(txt_heap_base); + /* skip BiosData */ + heap_ptr += *(uint64_t *)heap_ptr; + /* skip OsMleData */ + heap_ptr += *(uint64_t *)heap_ptr; + /* skip OsSinitData */ + heap_ptr += *(uint64_t *)heap_ptr; + /* now points to SinitMleDataSize; set to SinitMleData */ + heap_ptr += sizeof(uint64_t); + /* get addr of DMAR table */ + dmar_table = (struct acpi_table_header *)(heap_ptr + + ((sinit_mle_data_t *)heap_ptr)->vtd_dmars_off - sizeof(uint64_t)); + + rc = dmar_handler(dmar_table); + destroy_xen_mappings((unsigned long)__va(map_base << PAGE_SHIFT), (unsigned long)__va((map_base + map_size) << PAGE_SHIFT)); - return 1; + /* acpi_parse_dmar() zaps APCI DMAR signature in TXT heap table */ + /* but dom0 will read real table, so must zap it there too */ + dmar_table = NULL; + acpi_get_table(ACPI_SIG_DMAR, 0, &dmar_table); + if ( dmar_table != NULL ) + ((struct acpi_table_dmar *)dmar_table)->header.signature[0] = '\0'; + + return rc; } /* diff --git a/xen/drivers/passthrough/vtd/dmar.c b/xen/drivers/passthrough/vtd/dmar.c index 43107b3..ffcda32 100644 --- a/xen/drivers/passthrough/vtd/dmar.c +++ b/xen/drivers/passthrough/vtd/dmar.c @@ -28,6 +28,7 @@ #include #include #include +#include #include "dmar.h" int vtd_enabled = 1; @@ -496,7 +497,9 @@ int acpi_dmar_init(void) if ( !iommu_enabled ) goto fail; - rc = acpi_table_parse(ACPI_SIG_DMAR, acpi_parse_dmar); + /* ACPI tables may not be DMA protected by tboot, so use DMAR copy */ + /* SINIT saved in SinitMleData in TXT heap (which is DMA protected) */ + rc = tboot_parse_dmar_table(acpi_parse_dmar); if ( rc ) goto fail; diff --git a/xen/include/asm-x86/tboot.h b/xen/include/asm-x86/tboot.h index 4aa89c0..471e11f 100644 --- a/xen/include/asm-x86/tboot.h +++ b/xen/include/asm-x86/tboot.h @@ -37,6 +37,8 @@ #ifndef __TBOOT_H__ #define __TBOOT_H__ +#include + #ifndef __packed #define __packed __attribute__ ((packed)) #endif @@ -110,6 +112,7 @@ void tboot_probe(void); void tboot_shutdown(uint32_t shutdown_type); int tboot_in_measured_env(void); int tboot_protect_mem_regions(void); +int tboot_parse_dmar_table(acpi_table_handler dmar_handler); #endif /* __TBOOT_H__ */ -- 2.39.5