From 4c87a1868835d05f1cadae7b8ad6a7c95d9d9c0e Mon Sep 17 00:00:00 2001 From: Ross Philipson Date: Tue, 14 Mar 2017 15:40:33 -0400 Subject: Initial commit of EFI TBOOT work from internal project. Signed-off-by: Ross Philipson --- tboot/heap.c | 818 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 818 insertions(+) create mode 100644 tboot/heap.c (limited to 'tboot/heap.c') diff --git a/tboot/heap.c b/tboot/heap.c new file mode 100644 index 0000000..0fadb57 --- /dev/null +++ b/tboot/heap.c @@ -0,0 +1,818 @@ +/* + * heap.c: fns for verifying and printing the Intel(r) TXT heap data structs + * + * Copyright (c) 2003-2011, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of the Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef IS_INCLUDED +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +/* + * extended data elements + */ + +/* HEAP_BIOS_SPEC_VER_ELEMENT */ +static void print_bios_spec_ver_elt(const heap_ext_data_element_t *elt) +{ + const heap_bios_spec_ver_elt_t *bios_spec_ver_elt = + (const heap_bios_spec_ver_elt_t *)elt->data; + + printk(TBOOT_INFO"\t\t BIOS_SPEC_VER:\n"); + printk(TBOOT_INFO"\t\t major: 0x%x\n", bios_spec_ver_elt->spec_ver_major); + printk(TBOOT_INFO"\t\t minor: 0x%x\n", bios_spec_ver_elt->spec_ver_minor); + printk(TBOOT_INFO"\t\t rev: 0x%x\n", bios_spec_ver_elt->spec_ver_rev); +} + +static bool verify_bios_spec_ver_elt(const heap_ext_data_element_t *elt) +{ + const heap_bios_spec_ver_elt_t *bios_spec_ver_elt = + (const heap_bios_spec_ver_elt_t *)elt->data; + + if ( elt->size != sizeof(*elt) + sizeof(*bios_spec_ver_elt) ) { + printk(TBOOT_ERR"HEAP_BIOS_SPEC_VER element has wrong size (%u)\n", elt->size); + return false; + } + + /* any values are allowed */ + return true; +} + +/* HEAP_ACM_ELEMENT */ +static void print_acm_elt(const heap_ext_data_element_t *elt) +{ + const heap_acm_elt_t *acm_elt = (const heap_acm_elt_t *)elt->data; + + printk(TBOOT_DETA"\t\t ACM:\n"); + printk(TBOOT_DETA"\t\t num_acms: %u\n", acm_elt->num_acms); + for ( unsigned int i = 0; i < acm_elt->num_acms; i++ ) + printk(TBOOT_DETA"\t\t acm_addrs[%u]: 0x%jx\n", i, acm_elt->acm_addrs[i]); +} + +static bool verify_acm_elt(const heap_ext_data_element_t *elt) +{ + const heap_acm_elt_t *acm_elt = (const heap_acm_elt_t *)elt->data; + + if ( elt->size != sizeof(*elt) + sizeof(*acm_elt) + + acm_elt->num_acms*sizeof(uint64_t) ) { + printk(TBOOT_ERR"HEAP_ACM element has wrong size (%u)\n", elt->size); + return false; + } + + /* no addrs is not error, but print warning */ + if ( acm_elt->num_acms == 0 ) + printk(TBOOT_WARN"HEAP_ACM element has no ACM addrs\n"); + + for ( unsigned int i = 0; i < acm_elt->num_acms; i++ ) { + if ( acm_elt->acm_addrs[i] == 0 ) { + printk(TBOOT_ERR"HEAP_ACM element ACM addr (%u) is NULL\n", i); + return false; + } + + if ( acm_elt->acm_addrs[i] >= 0x100000000UL ) { + printk(TBOOT_ERR"HEAP_ACM element ACM addr (%u) is >4GB (0x%jx)\n", i, + acm_elt->acm_addrs[i]); + return false; + } + + /* not going to check if ACM addrs are valid ACMs */ + } + + return true; +} + +/* HEAP_CUSTOM_ELEMENT */ +static void print_custom_elt(const heap_ext_data_element_t *elt) +{ + const heap_custom_elt_t *custom_elt = (const heap_custom_elt_t *)elt->data; + + printk(TBOOT_DETA"\t\t CUSTOM:\n"); + printk(TBOOT_DETA"\t\t size: %u\n", elt->size); + printk(TBOOT_DETA"\t\t uuid: "); print_uuid(&custom_elt->uuid); + printk(TBOOT_DETA"\n"); +} + +static bool verify_custom_elt(const heap_ext_data_element_t *elt) +{ + const heap_custom_elt_t *custom_elt = (const heap_custom_elt_t *)elt->data; + + if ( elt->size < sizeof(*elt) + sizeof(*custom_elt) ) { + printk(TBOOT_ERR"HEAP_CUSTOM element has wrong size (%u)\n", elt->size); + return false; + } + + /* any values are allowed */ + return true; +} + +/* HEAP_EVENT_LOG_POINTER_ELEMENT */ +static inline void print_heap_hash(const sha1_hash_t hash) +{ + print_hash((const tb_hash_t *)hash, TB_HALG_SHA1); +} + +void print_event(const tpm12_pcr_event_t *evt) +{ + printk(TBOOT_DETA"\t\t\t Event:\n"); + printk(TBOOT_DETA"\t\t\t PCRIndex: %u\n", evt->pcr_index); + printk(TBOOT_DETA"\t\t\t Type: 0x%x\n", evt->type); + printk(TBOOT_DETA"\t\t\t Digest: "); + print_heap_hash(evt->digest); + printk(TBOOT_DETA"\t\t\t Data: %u bytes", evt->data_size); + print_hex("\t\t\t ", evt->data, evt->data_size); +} + +static void print_evt_log(const event_log_container_t *elog) +{ + printk(TBOOT_DETA"\t\t\t Event Log Container:\n"); + printk(TBOOT_DETA"\t\t\t Signature: %s\n", elog->signature); + printk(TBOOT_DETA"\t\t\t ContainerVer: %u.%u\n", + elog->container_ver_major, elog->container_ver_minor); + printk(TBOOT_DETA"\t\t\t PCREventVer: %u.%u\n", + elog->pcr_event_ver_major, elog->pcr_event_ver_minor); + printk(TBOOT_DETA"\t\t\t Size: %u\n", elog->size); + printk(TBOOT_DETA"\t\t\t EventsOffset: [%u,%u)\n", + elog->pcr_events_offset, elog->next_event_offset); + + const tpm12_pcr_event_t *curr, *next; + curr = (tpm12_pcr_event_t *)((void*)elog + elog->pcr_events_offset); + next = (tpm12_pcr_event_t *)((void*)elog + elog->next_event_offset); + + while ( curr < next ) { + print_event(curr); + curr = (void *)curr + sizeof(*curr) + curr->data_size; + } +} + +static bool verify_evt_log(const event_log_container_t *elog) +{ + if ( elog == NULL ) { + printk(TBOOT_ERR"Event log container pointer is NULL\n"); + return false; + } + + if ( memcmp(elog->signature, EVTLOG_SIGNATURE, sizeof(elog->signature)) ) { + printk(TBOOT_ERR"Bad event log container signature: %s\n", elog->signature); + return false; + } + + if ( elog->size != MAX_EVENT_LOG_SIZE ) { + printk(TBOOT_ERR"Bad event log container size: 0x%x\n", elog->size); + return false; + } + + /* no need to check versions */ + + if ( elog->pcr_events_offset < sizeof(*elog) || + elog->next_event_offset < elog->pcr_events_offset || + elog->next_event_offset > elog->size ) { + printk(TBOOT_ERR"Bad events offset range: [%u, %u)\n", + elog->pcr_events_offset, elog->next_event_offset); + return false; + } + + return true; +} + +static void print_evt_log_ptr_elt(const heap_ext_data_element_t *elt) +{ + const heap_event_log_ptr_elt_t *elog_elt = + (const heap_event_log_ptr_elt_t *)elt->data; + + printk(TBOOT_DETA"\t\t EVENT_LOG_POINTER:\n"); + printk(TBOOT_DETA"\t\t size: %u\n", elt->size); + printk(TBOOT_DETA"\t\t elog_addr: 0x%jx\n", elog_elt->event_log_phys_addr); + + if ( elog_elt->event_log_phys_addr ) + print_evt_log((event_log_container_t *)(unsigned long long) + elog_elt->event_log_phys_addr); +} + +static bool verify_evt_log_ptr_elt(const heap_ext_data_element_t *elt) +{ + const heap_event_log_ptr_elt_t *elog_elt = + (const heap_event_log_ptr_elt_t *)elt->data; + + if ( elt->size != sizeof(*elt) + sizeof(*elog_elt) ) { + printk(TBOOT_ERR"HEAP_EVENT_LOG_POINTER element has wrong size (%u)\n", + elt->size); + return false; + } + + return verify_evt_log((event_log_container_t *)(unsigned long long) + elog_elt->event_log_phys_addr); +} + +void print_event_2(void *evt, uint16_t alg) +{ + uint32_t hash_size, data_size; + void *next = evt; + + hash_size = get_hash_size(alg); + if ( hash_size == 0 ) + return; + + printk(TBOOT_DETA"\t\t\t Event:\n"); + printk(TBOOT_DETA"\t\t\t PCRIndex: %u\n", *((uint32_t *)next)); + + if ( *((uint32_t *)next) > 24 && *((uint32_t *)next) != 0xFF ) { + printk(TBOOT_DETA"\t\t\t Wrong Event Log.\n"); + return; + } + + next += sizeof(uint32_t); + printk(TBOOT_DETA"\t\t\t Type: 0x%x\n", *((uint32_t *)next)); + + if ( *((uint32_t *)next) > 0xFFF ) { + printk(TBOOT_DETA"\t\t\t Wrong Event Log.\n"); + return; + } + + next += sizeof(uint32_t); + printk(TBOOT_DETA"\t\t\t Digest: "); + print_hex(NULL, (uint8_t *)next, hash_size); + next += hash_size; + data_size = *(uint32_t *)next; + printk(TBOOT_DETA"\t\t\t Data: %u bytes", data_size); + if ( data_size > 4096 ) { + printk(TBOOT_DETA"\t\t\t Wrong Event Log.\n"); + return; + } + + next += sizeof(uint32_t); + if ( data_size ) + print_hex("\t\t\t ", (uint8_t *)next, data_size); + else + printk(TBOOT_DETA"\n"); +} + +static void print_evt_log_ptr_elt_2(const heap_ext_data_element_t *elt) +{ + const heap_event_log_ptr_elt2_t *elog_elt = + (const heap_event_log_ptr_elt2_t *)elt->data; + const heap_event_log_descr_t *log_descr; + + printk(TBOOT_DETA"\t\t EVENT_LOG_PTR:\n"); + printk(TBOOT_DETA"\t\t size: %u\n", elt->size); + printk(TBOOT_DETA"\t\t count: %d\n", elog_elt->count); + + for ( unsigned int i=0; icount; i++ ) { + log_descr = &elog_elt->event_log_descr[i]; + printk(TBOOT_DETA"\t\t\t Log Descrption:\n"); + printk(TBOOT_DETA"\t\t\t Alg: %u\n", log_descr->alg); + printk(TBOOT_DETA"\t\t\t Size: %u\n", log_descr->size); + printk(TBOOT_DETA"\t\t\t EventsOffset: [%u,%u)\n", + log_descr->pcr_events_offset, + log_descr->next_event_offset); + + if (log_descr->pcr_events_offset == log_descr->next_event_offset) { + printk(TBOOT_DETA"\t\t\t No Event Log.\n"); + continue; + } + + uint32_t hash_size, data_size; + hash_size = get_hash_size(log_descr->alg); + if ( hash_size == 0 ) + return; + + void *curr, *next; + + curr = (void *)(unsigned long long)log_descr->phys_addr + + log_descr->pcr_events_offset; + next = (void *)(unsigned long long)log_descr->phys_addr + + log_descr->next_event_offset; + + //It is required for each of the non-SHA1 event log the first entry to be the following + //TPM1.2 style TCG_PCR_EVENT record specifying type of the log: + //TCG_PCR_EVENT.PCRIndex = 0 + //TCG_PCR_EVENT.EventType = 0x03 // EV_NO_ACTION per TCG EFI + // Platform specification + //TCG_PCR_EVENT.Digest = {00…00} // 20 zeros + //TCG_PCR_EVENT.EventDataSize = sizeof(TCG_LOG_DESCRIPTOR). + //TCG_PCR_EVENT.EventData = TCG_LOG_DESCRIPTOR + //The digest of this record MUST NOT be extended into any PCR. + + if (log_descr->alg != TB_HALG_SHA1){ + print_event_2(curr, TB_HALG_SHA1); + curr += sizeof(tpm12_pcr_event_t) + sizeof(tpm20_log_descr_t); + } + + while ( curr < next ) { + print_event_2(curr, log_descr->alg); + data_size = *(uint32_t *)(curr + 2*sizeof(uint32_t) + hash_size); + curr += 3*sizeof(uint32_t) + hash_size + data_size; + } + } +} + +static bool verify_evt_log_ptr_elt_2(const heap_ext_data_element_t *elt) +{ + if ( !elt ) + return false; + + return true; +} + +static void print_ext_data_elts(const heap_ext_data_element_t elts[]) +{ + const heap_ext_data_element_t *elt = elts; + + printk(TBOOT_DETA"\t ext_data_elts[]:\n"); + while ( elt->type != HEAP_EXTDATA_TYPE_END ) { + switch ( elt->type ) { + case HEAP_EXTDATA_TYPE_BIOS_SPEC_VER: + print_bios_spec_ver_elt(elt); + break; + case HEAP_EXTDATA_TYPE_ACM: + print_acm_elt(elt); + break; + case HEAP_EXTDATA_TYPE_CUSTOM: + print_custom_elt(elt); + break; + case HEAP_EXTDATA_TYPE_TPM_EVENT_LOG_PTR: + print_evt_log_ptr_elt(elt); + break; + case HEAP_EXTDATA_TYPE_TPM_EVENT_LOG_PTR_2: + print_evt_log_ptr_elt_2(elt); + break; + default: + printk(TBOOT_WARN"\t\t unknown element: type: %u, size: %u\n", + elt->type, elt->size); + break; + } + elt = (void *)elt + elt->size; + } +} + +static bool verify_ext_data_elts(const heap_ext_data_element_t elts[], + size_t elts_size) +{ + const heap_ext_data_element_t *elt = elts; + + while ( true ) { + if ( elts_size < sizeof(*elt) ) { + printk(TBOOT_ERR"heap ext data elements too small\n"); + return false; + } + if ( elts_size < elt->size || elt->size == 0 ) { + printk(TBOOT_ERR"invalid element size: type: %u, size: %u\n", + elt->type, elt->size); + return false; + } + switch ( elt->type ) { + case HEAP_EXTDATA_TYPE_END: + return true; + case HEAP_EXTDATA_TYPE_BIOS_SPEC_VER: + if ( !verify_bios_spec_ver_elt(elt) ) + return false; + break; + case HEAP_EXTDATA_TYPE_ACM: + if ( !verify_acm_elt(elt) ) + return false; + break; + case HEAP_EXTDATA_TYPE_CUSTOM: + if ( !verify_custom_elt(elt) ) + return false; + break; + case HEAP_EXTDATA_TYPE_TPM_EVENT_LOG_PTR: + if ( !verify_evt_log_ptr_elt(elt) ) + return false; + break; + case HEAP_EXTDATA_TYPE_TPM_EVENT_LOG_PTR_2: + if ( !verify_evt_log_ptr_elt_2(elt) ) + return false; + break; + default: + printk(TBOOT_WARN"unknown element: type: %u, size: %u\n", elt->type, + elt->size); + break; + } + elts_size -= elt->size; + elt = (void *)elt + elt->size; + } + return true; +} + + +static void print_bios_data(const bios_data_t *bios_data, uint64_t size) +{ + printk(TBOOT_DETA"bios_data (@%p, %jx):\n", bios_data, + *((uint64_t *)bios_data - 1)); + printk(TBOOT_DETA"\t version: %u\n", bios_data->version); + printk(TBOOT_DETA"\t bios_sinit_size: 0x%x (%u)\n", bios_data->bios_sinit_size, + bios_data->bios_sinit_size); + printk(TBOOT_DETA"\t lcp_pd_base: 0x%jx\n", bios_data->lcp_pd_base); + printk(TBOOT_DETA"\t lcp_pd_size: 0x%jx (%ju)\n", bios_data->lcp_pd_size, + bios_data->lcp_pd_size); + printk(TBOOT_DETA"\t num_logical_procs: %u\n", bios_data->num_logical_procs); + if ( bios_data->version >= 3 ) + printk(TBOOT_DETA"\t flags: 0x%08jx\n", bios_data->flags); + if ( bios_data->version >= 4 && size > sizeof(*bios_data) + sizeof(size) ) + print_ext_data_elts(bios_data->ext_data_elts); +} + +bool verify_bios_data(const txt_heap_t *txt_heap) +{ + uint64_t heap_base = read_pub_config_reg(TXTCR_HEAP_BASE); + uint64_t heap_size = read_pub_config_reg(TXTCR_HEAP_SIZE); + printk(TBOOT_DETA"TXT.HEAP.BASE: 0x%jx\n", heap_base); + printk(TBOOT_DETA"TXT.HEAP.SIZE: 0x%jx (%ju)\n", heap_size, heap_size); + + /* verify that heap base/size are valid */ + if ( txt_heap == NULL || heap_base == 0 || heap_size == 0 ) + return false; + + /* check size */ + uint64_t size = get_bios_data_size(txt_heap); + if ( size == 0 ) { + printk(TBOOT_ERR"BIOS data size is 0\n"); + return false; + } + if ( size > heap_size ) { + printk(TBOOT_ERR"BIOS data size is larger than heap size " + "(%jx, heap size=%jx)\n", size, heap_size); + return false; + } + + bios_data_t *bios_data = get_bios_data_start(txt_heap); + + /* check version */ + if ( bios_data->version < 2 ) { + printk(TBOOT_ERR"unsupported BIOS data version (%u)\n", bios_data->version); + return false; + } + /* we assume backwards compatibility but print a warning */ + if ( bios_data->version > 4 ) + printk(TBOOT_WARN"unsupported BIOS data version (%u)\n", bios_data->version); + + /* all TXT-capable CPUs support at least 1 core */ + if ( bios_data->num_logical_procs < 1 ) { + printk(TBOOT_ERR"BIOS data has incorrect num_logical_procs (%u)\n", + bios_data->num_logical_procs); + return false; + } + else if ( bios_data->num_logical_procs > NR_CPUS ) { + printk(TBOOT_ERR"BIOS data specifies too many CPUs (%u)\n", + bios_data->num_logical_procs); + return false; + } + + if ( bios_data->version >= 4 && size > sizeof(*bios_data) ) { + if ( !verify_ext_data_elts(bios_data->ext_data_elts, + size - sizeof(*bios_data)) ) + return false; + } + + print_bios_data(bios_data, size); + + return true; +} + +#ifndef IS_INCLUDED + +static void print_os_mle_data(const os_mle_data_t *os_mle_data) +{ + printk(TBOOT_DETA"os_mle_data (@%p, %Lx):\n", os_mle_data, + *((uint64_t *)os_mle_data - 1)); + printk(TBOOT_DETA"\t version: %u\n", os_mle_data->version); + /* TBD: perhaps eventually print saved_mtrr_state field */ +} + +static bool verify_os_mle_data(const txt_heap_t *txt_heap) +{ + uint64_t size, heap_size; + os_mle_data_t *os_mle_data; + + /* check size */ + heap_size = read_priv_config_reg(TXTCR_HEAP_SIZE); + size = get_os_mle_data_size(txt_heap); + if ( size == 0 ) { + printk(TBOOT_ERR"OS to MLE data size is 0\n"); + return false; + } + if ( size > heap_size ) { + printk(TBOOT_ERR"OS to MLE data size is larger than heap size " + "(%Lx, heap size=%Lx)\n", size, heap_size); + return false; + } + if ( size != (sizeof(os_mle_data_t) + sizeof(size)) ) { + printk(TBOOT_ERR"OS to MLE data size (%Lx) is not equal to " + "os_mle_data_t size (%x)\n", size, sizeof(os_mle_data_t)); + return false; + } + + os_mle_data = get_os_mle_data_start(txt_heap); + + /* check version */ + /* since this data is from our pre-launch to post-launch code only, it */ + /* should always be this */ + if ( os_mle_data->version != 3 ) { + printk(TBOOT_ERR"unsupported OS to MLE data version (%u)\n", + os_mle_data->version); + return false; + } + + /* NOTE remove lctx_addr - not really used */ + + print_os_mle_data(os_mle_data); + + return true; +} + +/* + * Make sure version is in [MIN_OS_SINIT_DATA_VER, MAX_OS_SINIT_DATA_VER] + * before calling calc_os_sinit_data_size + */ +uint64_t calc_os_sinit_data_size(uint32_t version) +{ + uint64_t size[] = { + offsetof(os_sinit_data_t, efi_rsdt_ptr) + sizeof(uint64_t), + sizeof(os_sinit_data_t) + sizeof(uint64_t), + sizeof(os_sinit_data_t) + sizeof(uint64_t) + + 2 * sizeof(heap_ext_data_element_t) + + sizeof(heap_event_log_ptr_elt_t) + }; + + if ( g_tpm->major == TPM20_VER_MAJOR ) { + u32 count; + if ( g_tpm->extpol == TB_EXTPOL_AGILE ) + count = g_tpm->banks; + else if ( g_tpm->extpol == TB_EXTPOL_EMBEDDED ) + count = g_tpm->alg_count; + else + count = 1; + + size[2] = sizeof(os_sinit_data_t) + sizeof(uint64_t) + + 2 * sizeof(heap_ext_data_element_t) + + 4 + count*sizeof(heap_event_log_descr_t); + } + + if ( version >= 6 ) + return size[2]; + else + return size[version - MIN_OS_SINIT_DATA_VER]; +} + +void print_os_sinit_data(const os_sinit_data_t *os_sinit_data) +{ + printk(TBOOT_DETA"os_sinit_data (@%p, %Lx):\n", os_sinit_data, + *((uint64_t *)os_sinit_data - 1)); + printk(TBOOT_DETA"\t version: %u\n", os_sinit_data->version); + printk(TBOOT_DETA"\t flags: %u\n", os_sinit_data->flags); + printk(TBOOT_DETA"\t mle_ptab: 0x%Lx\n", os_sinit_data->mle_ptab); + printk(TBOOT_DETA"\t mle_size: 0x%Lx (%Lu)\n", os_sinit_data->mle_size, + os_sinit_data->mle_size); + printk(TBOOT_DETA"\t mle_hdr_base: 0x%Lx\n", os_sinit_data->mle_hdr_base); + printk(TBOOT_DETA"\t vtd_pmr_lo_base: 0x%Lx\n", os_sinit_data->vtd_pmr_lo_base); + printk(TBOOT_DETA"\t vtd_pmr_lo_size: 0x%Lx\n", os_sinit_data->vtd_pmr_lo_size); + printk(TBOOT_DETA"\t vtd_pmr_hi_base: 0x%Lx\n", os_sinit_data->vtd_pmr_hi_base); + printk(TBOOT_DETA"\t vtd_pmr_hi_size: 0x%Lx\n", os_sinit_data->vtd_pmr_hi_size); + printk(TBOOT_DETA"\t lcp_po_base: 0x%Lx\n", os_sinit_data->lcp_po_base); + printk(TBOOT_DETA"\t lcp_po_size: 0x%Lx (%Lu)\n", os_sinit_data->lcp_po_size, + os_sinit_data->lcp_po_size); + print_txt_caps("\t ", os_sinit_data->capabilities); + if ( os_sinit_data->version >= 5 ) + printk(TBOOT_DETA"\t efi_rsdt_ptr: 0x%Lx\n", os_sinit_data->efi_rsdt_ptr); + if ( os_sinit_data->version >= 6 ) + print_ext_data_elts(os_sinit_data->ext_data_elts); +} + +static bool verify_os_sinit_data(const txt_heap_t *txt_heap) +{ + uint64_t size, heap_size; + os_sinit_data_t *os_sinit_data; + + /* check size */ + heap_size = read_priv_config_reg(TXTCR_HEAP_SIZE); + size = get_os_sinit_data_size(txt_heap); + if ( size == 0 ) { + printk(TBOOT_ERR"OS to SINIT data size is 0\n"); + return false; + } + if ( size > heap_size ) { + printk(TBOOT_ERR"OS to SINIT data size is larger than heap size " + "(%Lx, heap size=%Lx)\n", size, heap_size); + return false; + } + + os_sinit_data = get_os_sinit_data_start(txt_heap); + + /* check version (but since we create this, it should always be OK) */ + if ( os_sinit_data->version < MIN_OS_SINIT_DATA_VER || + os_sinit_data->version > MAX_OS_SINIT_DATA_VER ) { + printk(TBOOT_ERR"unsupported OS to SINIT data version (%u)\n", + os_sinit_data->version); + return false; + } + + if ( size != calc_os_sinit_data_size(os_sinit_data->version) ) { + printk(TBOOT_ERR"OS to SINIT data size (%Lx) does not match for version (%x)\n", + size, sizeof(os_sinit_data_t)); + return false; + } + + if ( os_sinit_data->version >= 6 ) { + if ( !verify_ext_data_elts(os_sinit_data->ext_data_elts, + size - sizeof(*os_sinit_data)) ) + return false; + } + + print_os_sinit_data(os_sinit_data); + + return true; +} + +static void print_sinit_mdrs(const sinit_mdr_t mdrs[], uint32_t num_mdrs) +{ + static const char *mem_types[] = {"GOOD", "SMRAM OVERLAY", + "SMRAM NON-OVERLAY", + "PCIE EXTENDED CONFIG", "PROTECTED"}; + + printk(TBOOT_DETA"\t sinit_mdrs:\n"); + for ( unsigned int i = 0; i < num_mdrs; i++ ) { + printk(TBOOT_DETA"\t\t %016Lx - %016Lx ", mdrs[i].base, + mdrs[i].base + mdrs[i].length); + if ( mdrs[i].mem_type < sizeof(mem_types)/sizeof(mem_types[0]) ) + printk(TBOOT_DETA"(%s)\n", mem_types[mdrs[i].mem_type]); + else + printk(TBOOT_DETA"(%d)\n", (int)mdrs[i].mem_type); + } +} + +static void print_sinit_mle_data(const sinit_mle_data_t *sinit_mle_data) +{ + printk(TBOOT_DETA"sinit_mle_data (@%p, %Lx):\n", sinit_mle_data, + *((uint64_t *)sinit_mle_data - 1)); + printk(TBOOT_DETA"\t version: %u\n", sinit_mle_data->version); + printk(TBOOT_DETA"\t bios_acm_id: \n\t"); + print_heap_hash(sinit_mle_data->bios_acm_id); + printk(TBOOT_DETA"\t edx_senter_flags: 0x%08x\n", + sinit_mle_data->edx_senter_flags); + printk(TBOOT_DETA"\t mseg_valid: 0x%Lx\n", sinit_mle_data->mseg_valid); + printk(TBOOT_DETA"\t sinit_hash:\n\t"); print_heap_hash(sinit_mle_data->sinit_hash); + printk(TBOOT_DETA"\t mle_hash:\n\t"); print_heap_hash(sinit_mle_data->mle_hash); + printk(TBOOT_DETA"\t stm_hash:\n\t"); print_heap_hash(sinit_mle_data->stm_hash); + printk(TBOOT_DETA"\t lcp_policy_hash:\n\t"); + print_heap_hash(sinit_mle_data->lcp_policy_hash); + printk(TBOOT_DETA"\t lcp_policy_control: 0x%08x\n", + sinit_mle_data->lcp_policy_control); + printk(TBOOT_DETA"\t rlp_wakeup_addr: 0x%x\n", sinit_mle_data->rlp_wakeup_addr); + printk(TBOOT_DETA"\t num_mdrs: %u\n", sinit_mle_data->num_mdrs); + printk(TBOOT_DETA"\t mdrs_off: 0x%x\n", sinit_mle_data->mdrs_off); + printk(TBOOT_DETA"\t num_vtd_dmars: %u\n", sinit_mle_data->num_vtd_dmars); + printk(TBOOT_DETA"\t vtd_dmars_off: 0x%x\n", sinit_mle_data->vtd_dmars_off); + print_sinit_mdrs((sinit_mdr_t *) + (((void *)sinit_mle_data - sizeof(uint64_t)) + + sinit_mle_data->mdrs_off), sinit_mle_data->num_mdrs); + if ( sinit_mle_data->version >= 8 ) + printk(TBOOT_DETA"\t proc_scrtm_status: 0x%08x\n", + sinit_mle_data->proc_scrtm_status); + if ( sinit_mle_data->version >= 9 ) + print_ext_data_elts(sinit_mle_data->ext_data_elts); +} + +static bool verify_sinit_mle_data(const txt_heap_t *txt_heap) +{ + uint64_t size, heap_size; + sinit_mle_data_t *sinit_mle_data; + + /* check size */ + heap_size = read_priv_config_reg(TXTCR_HEAP_SIZE); + size = get_sinit_mle_data_size(txt_heap); + if ( size == 0 ) { + printk(TBOOT_ERR"SINIT to MLE data size is 0\n"); + return false; + } + if ( size > heap_size ) { + printk(TBOOT_ERR"SINIT to MLE data size is larger than heap size\n" + "(%Lx, heap size=%Lx)\n", size, heap_size); + return false; + } + + sinit_mle_data = get_sinit_mle_data_start(txt_heap); + + /* check version */ + if ( sinit_mle_data->version < 6 ) { + printk(TBOOT_ERR"unsupported SINIT to MLE data version (%u)\n", + sinit_mle_data->version); + return false; + } + else if ( sinit_mle_data->version > 9 ) { + printk(TBOOT_WARN"unsupported SINIT to MLE data version (%u)\n", + sinit_mle_data->version); + } + + /* this data is generated by SINIT and so is implicitly trustworthy, */ + /* so we don't need to validate it's fields */ + + print_sinit_mle_data(sinit_mle_data); + + return true; +} + +bool verify_txt_heap(const txt_heap_t *txt_heap, bool bios_data_only) +{ + /* verify BIOS to OS data */ + if ( !verify_bios_data(txt_heap) ) + return false; + + if ( bios_data_only ) + return true; + + /* check that total size is within the heap */ + uint64_t size1 = get_bios_data_size(txt_heap); + uint64_t size2 = get_os_mle_data_size(txt_heap); + uint64_t size3 = get_os_sinit_data_size(txt_heap); + uint64_t size4 = get_sinit_mle_data_size(txt_heap); + + /* overflow? */ + if ( plus_overflow_u64(size1, size2) ) { + printk(TBOOT_ERR"TXT heap data size overflows\n"); + return false; + } + if ( plus_overflow_u64(size3, size4) ) { + printk(TBOOT_ERR"TXT heap data size overflows\n"); + return false; + } + if ( plus_overflow_u64(size1 + size2, size3 + size4) ) { + printk(TBOOT_ERR"TXT heap data size overflows\n"); + return false; + } + + if ( (size1 + size2 + size3 + size4) > + read_priv_config_reg(TXTCR_HEAP_SIZE) ) { + printk(TBOOT_ERR"TXT heap data sizes (%Lx, %Lx, %Lx, %Lx) are larger than\n" + "heap total size (%Lx)\n", size1, size2, size3, size4, + read_priv_config_reg(TXTCR_HEAP_SIZE)); + return false; + } + + /* verify OS to MLE data */ + if ( !verify_os_mle_data(txt_heap) ) + return false; + + /* verify OS to SINIT data */ + if ( !verify_os_sinit_data(txt_heap) ) + return false; + + /* verify SINIT to MLE data */ + if ( !verify_sinit_mle_data(txt_heap) ) + return false; + + return true; +} + +#endif + +/* + * Local variables: + * mode: C + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ -- cgit v1.2.3