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/acmod.c | 991 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 991 insertions(+) create mode 100644 tboot/acmod.c (limited to 'tboot/acmod.c') diff --git a/tboot/acmod.c b/tboot/acmod.c new file mode 100644 index 0000000..6601222 --- /dev/null +++ b/tboot/acmod.c @@ -0,0 +1,991 @@ +/* + * acmod.c: support functions for use of Intel(r) TXT Authenticated + * Code (AC) Modules + * + * 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 /* defined in utils/acminfo.c */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif /* IS_INCLUDED */ + +__data acm_hdr_t *g_sinit = 0; + +static acm_info_table_t *get_acmod_info_table(const acm_hdr_t* hdr) +{ + uint32_t user_area_off; + + /* overflow? */ + if ( plus_overflow_u32(hdr->header_len, hdr->scratch_size) ) { + printk(TBOOT_ERR"ACM header length plus scratch size overflows\n"); + return NULL; + } + + if ( multiply_overflow_u32((hdr->header_len + hdr->scratch_size), 4) ) { + printk(TBOOT_ERR"ACM header length and scratch size in bytes overflows\n"); + return NULL; + } + + + /* this fn assumes that the ACM has already passed at least the initial */ + /* is_acmod() checks */ + + user_area_off = (hdr->header_len + hdr->scratch_size) * 4; + + /* overflow? */ + if ( plus_overflow_u32(user_area_off, sizeof(acm_info_table_t)) ) { + printk(TBOOT_ERR"user_area_off plus acm_info_table_t size overflows\n"); + return NULL; + } + + /* check that table is within module */ + if ( user_area_off + sizeof(acm_info_table_t) > hdr->size*4 ) { + printk(TBOOT_ERR"ACM info table size too large: %x\n", + user_area_off + (uint32_t)sizeof(acm_info_table_t)); + return NULL; + } + + /* overflow? */ + if ( plus_overflow_u64((uint64_t)(uintptr_t)hdr, user_area_off) ) { + printk(TBOOT_ERR"hdr plus user_area_off overflows\n"); + return NULL; + } + + return (acm_info_table_t *)((unsigned long long)hdr + user_area_off); +} + +static acm_chipset_id_list_t *get_acmod_chipset_list(const acm_hdr_t* hdr) +{ + acm_info_table_t* info_table; + uint32_t size, id_list_off; + acm_chipset_id_list_t *chipset_id_list; + + /* this fn assumes that the ACM has already passed the is_acmod() checks */ + + info_table = get_acmod_info_table(hdr); + if ( info_table == NULL ) + return NULL; + id_list_off = info_table->chipset_id_list; + + size = hdr->size * 4; + + /* overflow? */ + if ( plus_overflow_u32(id_list_off, sizeof(acm_chipset_id_t)) ) { + printk(TBOOT_ERR"id_list_off plus acm_chipset_id_t size overflows\n"); + return NULL; + } + + /* check that chipset id table is w/in ACM */ + if ( id_list_off + sizeof(acm_chipset_id_t) > size ) { + printk(TBOOT_ERR"ACM chipset id list is too big: chipset_id_list=%x\n", + id_list_off); + return NULL; + } + + /* overflow? */ + if ( plus_overflow_u64((uint64_t)(uintptr_t)hdr, id_list_off) ) { + printk(TBOOT_ERR"hdr plus id_list_off overflows\n"); + return NULL; + } + + chipset_id_list = (acm_chipset_id_list_t *) + ((unsigned long long)hdr + id_list_off); + + /* overflow? */ + if ( multiply_overflow_u32(chipset_id_list->count, + sizeof(acm_chipset_id_t)) ) { + printk(TBOOT_ERR"size of acm_chipset_id_list overflows\n"); + return NULL; + } + if ( plus_overflow_u32(id_list_off + sizeof(acm_chipset_id_t), + chipset_id_list->count * sizeof(acm_chipset_id_t)) ) { + printk(TBOOT_ERR"size of all entries overflows\n"); + return NULL; + } + + /* check that all entries are w/in ACM */ + if ( id_list_off + sizeof(acm_chipset_id_t) + + chipset_id_list->count * sizeof(acm_chipset_id_t) > size ) { + printk(TBOOT_ERR"ACM chipset id entries are too big:" + " chipset_id_list->count=%x\n", chipset_id_list->count); + return NULL; + } + + return chipset_id_list; +} + +static acm_processor_id_list_t *get_acmod_processor_list(const acm_hdr_t* hdr) +{ + acm_info_table_t* info_table; + uint32_t size, id_list_off; + acm_processor_id_list_t *proc_id_list; + + /* this fn assumes that the ACM has already passed the is_acmod() checks */ + + info_table = get_acmod_info_table(hdr); + if ( info_table == NULL ) + return NULL; + id_list_off = info_table->processor_id_list; + + size = hdr->size * 4; + + /* overflow? */ + if ( plus_overflow_u32(id_list_off, sizeof(acm_processor_id_t)) ) { + printk(TBOOT_ERR"id_list_off plus acm_processor_id_t size overflows\n"); + return NULL; + } + + /* check that processor id table is w/in ACM */ + if ( id_list_off + sizeof(acm_processor_id_t) > size ) { + printk(TBOOT_ERR"ACM processor id list is too big: processor_id_list=%x\n", + id_list_off); + return NULL; + } + + /* overflow? */ + if ( plus_overflow_u64((unsigned long long)hdr, id_list_off) ) { + printk(TBOOT_ERR"hdr plus id_list_off overflows\n"); + return NULL; + } + + proc_id_list = (acm_processor_id_list_t *) + ((unsigned long long)hdr + id_list_off); + + /* overflow? */ + if ( multiply_overflow_u32(proc_id_list->count, + sizeof(acm_processor_id_t)) ) { + printk(TBOOT_ERR"size of acm_processor_id_list overflows\n"); + return NULL; + } + if ( plus_overflow_u32(id_list_off + sizeof(acm_processor_id_t), + proc_id_list->count * sizeof(acm_processor_id_t)) ) { + printk(TBOOT_ERR"size of all entries overflows\n"); + return NULL; + } + + /* check that all entries are w/in ACM */ + if ( id_list_off + sizeof(acm_processor_id_t) + + proc_id_list->count * sizeof(acm_processor_id_t) > size ) { + printk(TBOOT_ERR"ACM processor id entries are too big:" + " proc_id_list->count=%x\n", proc_id_list->count); + return NULL; + } + + return proc_id_list; +} + +tpm_info_list_t *get_tpm_info_list(const acm_hdr_t* hdr) +{ + acm_info_table_t* info_table; + uint32_t size, tpm_info_off; + tpm_info_list_t *tpm_info; + + /* this fn assumes that the ACM has already passed the is_acmod() checks */ + + info_table = get_acmod_info_table(hdr); + if ( info_table == NULL ) + return NULL; + tpm_info_off = info_table->tpm_info_list_off; + + size = hdr->size * 4; + + /* overflow? */ + if ( plus_overflow_u32(tpm_info_off, sizeof(tpm_info_list_t)) ) { + printk("tpm_info_off plus tpm_info_list_t size overflows\n"); + return NULL; + } + + /* check that tpm info list is w/in ACM */ + if ( tpm_info_off + sizeof(tpm_info_list_t) > size ) { + printk("TPM info list is too big: tpm_info_list=%x\n", + tpm_info_off); + return NULL; + } + + /* overflow? */ + if ( plus_overflow_u64((unsigned long long)hdr, tpm_info_off) ) { + printk("hdr plus tpm_info_off overflows\n"); + return NULL; + } + + tpm_info = (tpm_info_list_t *) + ((unsigned long long)hdr + tpm_info_off); + + /* overflow? */ + if ( multiply_overflow_u32(tpm_info->count, + sizeof(uint16_t)) ) { + printk("size of tpm_info_list overflows\n"); + return NULL; + } + if ( plus_overflow_u32(tpm_info_off + sizeof(tpm_info_list_t), + tpm_info->count * sizeof(uint16_t)) ) { + printk("size of all entries overflows\n"); + return NULL; + } + + /* check that all entries are w/in ACM */ + if ( tpm_info_off + sizeof(tpm_info_list_t) + + tpm_info->count * sizeof(uint16_t) > size ) { + printk("TPM info list entries are too big:" + " tpm_info_list->count=%x\n", tpm_info->count); + return NULL; + } + + return tpm_info; +} + +void print_txt_caps(const char *prefix, txt_caps_t caps) +{ + printk(TBOOT_DETA"%scapabilities: 0x%08x\n", prefix, caps._raw); + printk(TBOOT_DETA"%s rlp_wake_getsec: %d\n", prefix, caps.rlp_wake_getsec); + printk(TBOOT_DETA"%s rlp_wake_monitor: %d\n", prefix, caps.rlp_wake_monitor); + printk(TBOOT_DETA"%s ecx_pgtbl: %d\n", prefix, caps.ecx_pgtbl); + printk(TBOOT_DETA"%s stm: %d\n", prefix, caps.stm); + printk(TBOOT_DETA"%s pcr_map_no_legacy: %d\n", prefix, caps.pcr_map_no_legacy); + printk(TBOOT_DETA"%s pcr_map_da: %d\n", prefix, caps.pcr_map_da); + printk(TBOOT_DETA"%s platform_type: %d\n", prefix, caps.platform_type); + printk(TBOOT_DETA"%s max_phy_addr: %d\n", prefix, caps.max_phy_addr); +} + +static void print_acm_hdr(const acm_hdr_t *hdr, const char *mod_name) +{ + acm_info_table_t *info_table; + + printk(TBOOT_DETA"AC module header dump for %s:\n", + (mod_name == NULL) ? "?" : mod_name); + + /* header */ + printk(TBOOT_DETA"\t type: 0x%x ", hdr->module_type); + if ( hdr->module_type == ACM_TYPE_CHIPSET ) + printk(TBOOT_DETA"(ACM_TYPE_CHIPSET)\n"); + else + printk(TBOOT_INFO"(unknown)\n"); + printk(TBOOT_DETA"\t subtype: 0x%x ", hdr->module_subtype); + if ( hdr->module_subtype == ACM_SUBTYPE_RESET ) + printk(TBOOT_INFO"(execute at reset)\n"); + else if ( hdr->module_subtype == 0 ) + printk(TBOOT_INFO"\n"); + else + printk(TBOOT_INFO"(unknown)\n"); + printk(TBOOT_DETA"\t length: 0x%x (%u)\n", hdr->header_len, hdr->header_len); + printk(TBOOT_DETA"\t version: %u\n", hdr->header_ver); + printk(TBOOT_DETA"\t chipset_id: 0x%x\n", (uint32_t)hdr->chipset_id); + printk(TBOOT_DETA"\t flags: 0x%x\n", (uint32_t)hdr->flags._raw); + printk(TBOOT_DETA"\t\t pre_production: %d\n", (int)hdr->flags.pre_production); + printk(TBOOT_DETA"\t\t debug_signed: %d\n", (int)hdr->flags.debug_signed); + printk(TBOOT_DETA"\t vendor: 0x%x\n", hdr->module_vendor); + printk(TBOOT_DETA"\t date: 0x%08x\n", hdr->date); + printk(TBOOT_DETA"\t size*4: 0x%x (%u)\n", hdr->size*4, hdr->size*4); + printk(TBOOT_DETA"\t txt_svn: 0x%08x\n", hdr->txt_svn); + printk(TBOOT_DETA"\t se_svn: 0x%08x\n", hdr->se_svn); + printk(TBOOT_DETA"\t code_control: 0x%x\n", hdr->code_control); + printk(TBOOT_DETA"\t entry point: 0x%08x:%08x\n", hdr->seg_sel, + hdr->entry_point); + printk(TBOOT_DETA"\t scratch_size: 0x%x (%u)\n", hdr->scratch_size, + hdr->scratch_size); + + /* info table */ + printk(TBOOT_DETA"\t info_table:\n"); + info_table = get_acmod_info_table(hdr); + if ( info_table == NULL ) { + printk(TBOOT_ERR"\t\t \n"); + return; + } + printk(TBOOT_DETA"\t\t uuid: "); print_uuid(&info_table->uuid); + printk(TBOOT_DETA"\n"); + if ( are_uuids_equal(&(info_table->uuid), &((uuid_t)ACM_UUID_V3)) ) + printk(TBOOT_DETA"\t\t ACM_UUID_V3\n"); + else + printk(TBOOT_DETA"\t\t unknown\n"); + printk(TBOOT_DETA"\t\t chipset_acm_type: 0x%x ", + (uint32_t)info_table->chipset_acm_type); + if ( info_table->chipset_acm_type == ACM_CHIPSET_TYPE_SINIT ) + printk(TBOOT_DETA"(SINIT)\n"); + else if ( info_table->chipset_acm_type == ACM_CHIPSET_TYPE_BIOS ) + printk(TBOOT_DETA"(BIOS)\n"); + else + printk(TBOOT_DETA"(unknown)\n"); + printk(TBOOT_DETA"\t\t version: %u\n", (uint32_t)info_table->version); + printk(TBOOT_DETA"\t\t length: 0x%x (%u)\n", (uint32_t)info_table->length, + (uint32_t)info_table->length); + printk(TBOOT_DETA"\t\t chipset_id_list: 0x%x\n", info_table->chipset_id_list); + printk(TBOOT_DETA"\t\t os_sinit_data_ver: 0x%x\n", info_table->os_sinit_data_ver); + printk(TBOOT_DETA"\t\t min_mle_hdr_ver: 0x%08x\n", info_table->min_mle_hdr_ver); + print_txt_caps("\t\t ", info_table->capabilities); + printk(TBOOT_DETA"\t\t acm_ver: %u\n", (uint32_t)info_table->acm_ver); + + /* chipset list */ + printk(TBOOT_DETA"\t chipset list:\n"); + acm_chipset_id_list_t *chipset_id_list = get_acmod_chipset_list(hdr); + if ( chipset_id_list == NULL ) { + printk(TBOOT_ERR"\t\t \n"); + return; + } + printk(TBOOT_DETA"\t\t count: %u\n", chipset_id_list->count); + for ( unsigned int i = 0; i < chipset_id_list->count; i++ ) { + printk(TBOOT_DETA"\t\t entry %u:\n", i); + acm_chipset_id_t *chipset_id = &(chipset_id_list->chipset_ids[i]); + printk(TBOOT_DETA"\t\t flags: 0x%x\n", chipset_id->flags); + printk(TBOOT_DETA"\t\t vendor_id: 0x%x\n", (uint32_t)chipset_id->vendor_id); + printk(TBOOT_DETA"\t\t device_id: 0x%x\n", (uint32_t)chipset_id->device_id); + printk(TBOOT_DETA"\t\t revision_id: 0x%x\n", + (uint32_t)chipset_id->revision_id); + printk(TBOOT_DETA"\t\t extended_id: 0x%x\n", chipset_id->extended_id); + } + + if ( info_table->version >= 4 ) { + /* processor list */ + printk(TBOOT_DETA"\t processor list:\n"); + acm_processor_id_list_t *proc_id_list = get_acmod_processor_list(hdr); + if ( proc_id_list == NULL ) { + printk(TBOOT_ERR"\t\t \n"); + return; + } + printk(TBOOT_DETA"\t\t count: %u\n", proc_id_list->count); + for ( unsigned int i = 0; i < proc_id_list->count; i++ ) { + printk(TBOOT_DETA"\t\t entry %u:\n", i); + acm_processor_id_t *proc_id = &(proc_id_list->processor_ids[i]); + printk(TBOOT_DETA"\t\t fms: 0x%x\n", proc_id->fms); + printk(TBOOT_DETA"\t\t fms_mask: 0x%x\n", proc_id->fms_mask); + printk(TBOOT_DETA"\t\t platform_id: 0x%Lx\n", (unsigned long long)proc_id->platform_id); + printk(TBOOT_DETA"\t\t platform_mask: 0x%Lx\n", (unsigned long long)proc_id->platform_mask); + } + } + + if ( info_table->version >= 5 ){ + /* tpm infor list */ + printk(TBOOT_DETA"\t TPM info list:\n"); + tpm_info_list_t *info_list = get_tpm_info_list(hdr); + if ( info_list == NULL ) { + printk(TBOOT_ERR"\t\t \n"); + return; + } + printk(TBOOT_DETA"\t\t TPM capability:\n"); + printk(TBOOT_DETA"\t\t ext_policy: 0x%x\n", info_list->capabilities.ext_policy); + printk(TBOOT_DETA"\t\t tpm_family : 0x%x\n", info_list->capabilities.tpm_family); + printk(TBOOT_DETA"\t\t tpm_nv_index_set : 0x%x\n", info_list->capabilities.tpm_nv_index_set); + printk(TBOOT_DETA"\t\t alg count: %u\n", info_list->count); + for ( unsigned int i = 0; i < info_list->count; i++ ) { + printk(TBOOT_DETA"\t\t alg_id: 0x%x\n", info_list->alg_id[i]); + } + } +} + +uint32_t get_supported_os_sinit_data_ver(const acm_hdr_t* hdr) +{ + /* assumes that it passed is_sinit_acmod() */ + + acm_info_table_t *info_table = get_acmod_info_table(hdr); + if ( info_table == NULL ) + return 0; + + return info_table->os_sinit_data_ver; +} + +txt_caps_t get_sinit_capabilities(const acm_hdr_t* hdr) +{ + /* assumes that it passed is_sinit_acmod() */ + + acm_info_table_t *info_table = get_acmod_info_table(hdr); + if ( info_table == NULL || info_table->version < 3 ) + return (txt_caps_t){ 0 }; + + return info_table->capabilities; +} + +static bool is_acmod(const void *acmod_base, uint32_t acmod_size, uint8_t *type, bool quiet) +{ + acm_hdr_t *acm_hdr = (acm_hdr_t *)acmod_base; + + /* first check size */ + if ( acmod_size < sizeof(acm_hdr_t) ) { + if ( !quiet ) + printk(TBOOT_ERR"\t ACM size is too small: acmod_size=%x," + " sizeof(acm_hdr)=%x\n", acmod_size, + (uint32_t)sizeof(acm_hdr) ); + return false; + } + + /* then check overflow */ + if ( multiply_overflow_u32(acm_hdr->size, 4) ) { + if ( !quiet ) + printk(TBOOT_ERR"\t ACM header size in bytes overflows\n"); + return false; + } + + /* then check size equivalency */ + if ( acmod_size != acm_hdr->size * 4 ) { + if ( !quiet ) + printk(TBOOT_ERR"\t ACM size is too small: acmod_size=%x," + " acm_hdr->size*4=%x\n", acmod_size, acm_hdr->size*4); + return false; + } + + /* then check type and vendor */ + if ( (acm_hdr->module_type != ACM_TYPE_CHIPSET) || + (acm_hdr->module_vendor != ACM_VENDOR_INTEL) ) { + if ( !quiet ) + printk(TBOOT_ERR"\t ACM type/vendor mismatch: module_type=%x," + " module_vendor=%x\n", acm_hdr->module_type, + acm_hdr->module_vendor); + return false; + } + + acm_info_table_t *info_table = get_acmod_info_table(acm_hdr); + if ( info_table == NULL ) + return false; + + /* check if ACM UUID is present */ + if ( !are_uuids_equal(&(info_table->uuid), &((uuid_t)ACM_UUID_V3)) ) { + if ( !quiet ) { + printk(TBOOT_ERR"\t unknown UUID: "); print_uuid(&info_table->uuid); + printk(TBOOT_ERR"\n"); + } + return false; + } + + if ( type != NULL ) + *type = info_table->chipset_acm_type; + + if ( info_table->version < 3 ) { + if ( !quiet ) + printk(TBOOT_ERR"\t ACM info_table version unsupported (%u)\n", + (uint32_t)info_table->version); + return false; + } + /* there is forward compatibility, so this is just a warning */ + else if ( info_table->version > 5 ) { + if ( !quiet ) + printk(TBOOT_WARN"\t ACM info_table version mismatch (%u)\n", + (uint32_t)info_table->version); + } + + return true; +} + +bool is_racm_acmod(const void *acmod_base, uint32_t acmod_size, bool quiet) +{ + uint8_t type; + + if ( !is_acmod(acmod_base, acmod_size, &type, quiet) ) + return false; + + if ( type != ACM_CHIPSET_TYPE_BIOS_REVOC && + type != ACM_CHIPSET_TYPE_SINIT_REVOC ) { + printk(TBOOT_ERR"ACM is not an revocation ACM (%x)\n", type); + return false; + } + + if ( acmod_size != 0x8000 && acmod_size != 0x10000 ) { + printk(TBOOT_ERR"ACM is not an RACM, bad size (0x%x)\n", acmod_size); + return false; + } + + return true; +} + +bool is_sinit_acmod(const void *acmod_base, uint32_t acmod_size, bool quiet) +{ + uint8_t type; + + if ( !is_acmod(acmod_base, acmod_size, &type, quiet) ) + return false; + + if ( type != ACM_CHIPSET_TYPE_SINIT ) { + printk(TBOOT_ERR"ACM is not an SINIT ACM (%x)\n", type); + return false; + } + + return true; +} + +bool does_acmod_match_platform(const acm_hdr_t* hdr) +{ + /* used to ensure we don't print chipset/proc info for each module */ + static bool printed_host_info; + + /* this fn assumes that the ACM has already passed the is_acmod() checks */ + + /* get chipset fusing, device, and vendor id info */ + txt_didvid_t didvid; + didvid._raw = read_pub_config_reg(TXTCR_DIDVID); + txt_ver_fsbif_qpiif_t ver; + ver._raw = read_pub_config_reg(TXTCR_VER_FSBIF); + if ( (ver._raw & 0xffffffff) == 0xffffffff || + (ver._raw & 0xffffffff) == 0x00 ) /* need to use VER.QPIIF */ + ver._raw = read_pub_config_reg(TXTCR_VER_QPIIF); + if ( !printed_host_info ) { + printk(TBOOT_DETA"chipset production fused: %x\n", ver.prod_fused ); + printk(TBOOT_DETA"chipset ids: vendor: 0x%x, device: 0x%x, revision: 0x%x\n", + didvid.vendor_id, didvid.device_id, didvid.revision_id); + } + + /* get processor family/model/stepping and platform ID */ + uint64_t platform_id; + uint32_t fms = cpuid_eax(1); + platform_id = rdmsr(MSR_IA32_PLATFORM_ID); + if ( !printed_host_info ) { + printk(TBOOT_DETA"processor family/model/stepping: 0x%x\n", fms ); + printk(TBOOT_DETA"platform id: 0x%Lx\n", (unsigned long long)platform_id); + } + printed_host_info = true; + + /* + * check if chipset fusing is same + */ + if ( ver.prod_fused != !hdr->flags.debug_signed ) { + printk(TBOOT_ERR"\t production/debug mismatch between chipset and ACM\n"); + return false; + } + + /* + * check if chipset vendor/device/revision IDs match + */ + acm_chipset_id_list_t *chipset_id_list = get_acmod_chipset_list(hdr); + if ( chipset_id_list == NULL ) + return false; + + printk(TBOOT_DETA"\t %x ACM chipset id entries:\n", chipset_id_list->count); + unsigned int i; + for ( i = 0; i < chipset_id_list->count; i++ ) { + acm_chipset_id_t *chipset_id = &(chipset_id_list->chipset_ids[i]); + printk(TBOOT_DETA"\t vendor: 0x%x, device: 0x%x, flags: 0x%x, " + "revision: 0x%x, extended: 0x%x\n", + (uint32_t)chipset_id->vendor_id, + (uint32_t)chipset_id->device_id, chipset_id->flags, + (uint32_t)chipset_id->revision_id, chipset_id->extended_id); + + if ( (didvid.vendor_id == chipset_id->vendor_id ) && + (didvid.device_id == chipset_id->device_id ) && + ( ( ( (chipset_id->flags & 0x1) == 0) && + (didvid.revision_id == chipset_id->revision_id) ) || + ( ( (chipset_id->flags & 0x1) == 1) && + ( (didvid.revision_id & chipset_id->revision_id) != 0 ) ) ) ) + break; + } + if ( i >= chipset_id_list->count ) { + printk(TBOOT_ERR"\t chipset id mismatch\n"); + return false; + } + + /* + * check if processor family/model/stepping and platform IDs match + */ + acm_info_table_t *info_table = get_acmod_info_table(hdr); + if ( info_table == NULL ) + return false; + + if ( info_table->version >= 4 ) { + acm_processor_id_list_t *proc_id_list = get_acmod_processor_list(hdr); + if ( proc_id_list == NULL ) + return false; + + printk(TBOOT_DETA"\t %x ACM processor id entries:\n", proc_id_list->count); + for ( i = 0; i < proc_id_list->count; i++ ) { + acm_processor_id_t *proc_id = &(proc_id_list->processor_ids[i]); + printk(TBOOT_DETA"\t fms: 0x%x, fms_mask: 0x%x, platform_id: 0x%Lx, " + "platform_mask: 0x%Lx\n", + proc_id->fms, proc_id->fms_mask, + (unsigned long long)proc_id->platform_id, + (unsigned long long)proc_id->platform_mask); + + if ( (proc_id->fms == (fms & proc_id->fms_mask)) && + (proc_id->platform_id == (platform_id & proc_id->platform_mask)) + ) + break; + } + if ( i >= proc_id_list->count ) { + printk(TBOOT_ERR"\t processor mismatch\n"); + return false; + } + } + + return true; +} + +#ifndef IS_INCLUDED +acm_hdr_t *get_bios_sinit(const void *sinit_region_base) +{ + txt_heap_t *txt_heap = get_txt_heap(); + bios_data_t *bios_data = get_bios_data_start(txt_heap); + + if ( bios_data->bios_sinit_size == 0 ) + return NULL; + + /* BIOS has loaded an SINIT module, so verify that it is valid */ + printk(TBOOT_INFO"BIOS has already loaded an SINIT module\n"); + + /* is it a valid SINIT module? */ + if ( !is_sinit_acmod(sinit_region_base, bios_data->bios_sinit_size, false) || + !does_acmod_match_platform((acm_hdr_t *)sinit_region_base) ) + return NULL; + + return (acm_hdr_t *)sinit_region_base; +} + +static void *alloc_racm_region(uint32_t size) +{ + /* TODO: find a real unused memory place through mbi */ + return (void *)(unsigned long long)(0x2000000 + size - size); /* 32M */ +} + +acm_hdr_t *copy_racm(const acm_hdr_t *racm) +{ + /* find a 32KB aligned memory */ + uint32_t racm_region_size = racm->size*4; + void *racm_region_base = alloc_racm_region(racm_region_size); + printk(TBOOT_DETA"RACM.BASE: %p\n", racm_region_base); + printk(TBOOT_DETA"RACM.SIZE: 0x%x (%u)\n", racm_region_size, racm_region_size); + + /* copy it there */ + memcpy(racm_region_base, racm, racm->size*4); + + printk(TBOOT_DETA"copied RACM (size=%x) to %p\n", racm->size*4, + racm_region_base); + + return (acm_hdr_t *)racm_region_base; +} + +acm_hdr_t *copy_sinit(const acm_hdr_t *sinit) +{ + /* get BIOS-reserved region from TXT.SINIT.BASE config reg */ + void *sinit_region_base = + (void*)(unsigned long long)read_pub_config_reg(TXTCR_SINIT_BASE); + uint32_t sinit_region_size = (uint32_t)read_pub_config_reg(TXTCR_SINIT_SIZE); + printk(TBOOT_DETA"TXT.SINIT.BASE: %p\n", sinit_region_base); + printk(TBOOT_DETA"TXT.SINIT.SIZE: 0x%x (%u)\n", sinit_region_size, sinit_region_size); + + /* + * check if BIOS already loaded an SINIT module there + */ + acm_hdr_t *bios_sinit = get_bios_sinit(sinit_region_base); + if ( bios_sinit != NULL ) { + /* no other SINIT was provided so must use one BIOS provided */ + if ( sinit == NULL ) { + printk(TBOOT_WARN"no SINIT provided by bootloader; using BIOS SINIT\n"); + return bios_sinit; + } + + /* is it newer than the one we've been provided? */ + if ( bios_sinit->date >= sinit->date ) { + printk(TBOOT_INFO"BIOS-provided SINIT is newer, so using it\n"); + return bios_sinit; /* yes */ + } + else + printk(TBOOT_INFO"BIOS-provided SINIT is older: date=%x\n", bios_sinit->date); + } + /* our SINIT is newer than BIOS's (or BIOS did not have one) */ + + /* BIOS SINIT not present or not valid and none provided */ + if ( sinit == NULL ) + return NULL; + + /* overflow? */ + if ( multiply_overflow_u32(sinit->size, 4) ) { + printk(TBOOT_ERR"sinit size in bytes overflows\n"); + return NULL; + } + + /* make sure our SINIT fits in the reserved region */ + if ( (sinit->size * 4) > sinit_region_size ) { + printk(TBOOT_ERR"BIOS-reserved SINIT size (%x) is too small for loaded " + "SINIT (%x)\n", sinit_region_size, sinit->size*4); + return NULL; + } + + /* copy it there */ + memcpy(sinit_region_base, sinit, sinit->size*4); + + printk(TBOOT_DETA"copied SINIT (size=%x) to %p\n", sinit->size*4, + sinit_region_base); + + return (acm_hdr_t *)sinit_region_base; +} +#endif /* IS_INCLUDED */ + +bool verify_racm(const acm_hdr_t *acm_hdr) +{ + getsec_parameters_t params; + uint32_t size; + + /* assumes this already passed is_acmod() test */ + + size = acm_hdr->size * 4; /* hdr size is in dwords, we want bytes */ + + /* + * AC mod must start on 4k page boundary + */ + + if ( (unsigned long long)acm_hdr & 0xfff ) { + printk(TBOOT_ERR"AC mod base not 4K aligned (%p)\n", acm_hdr); + return false; + } + printk(TBOOT_INFO"AC mod base alignment OK\n"); + + /* AC mod size must: + * - be multiple of 64 + * - greater than ??? + * - less than max supported size for this processor + */ + + if ( (size == 0) || ((size % 64) != 0) ) { + printk(TBOOT_ERR"AC mod size %x bogus\n", size); + return false; + } + + if ( !get_parameters(¶ms) ) { + printk(TBOOT_ERR"get_parameters() failed\n"); + return false; + } + + if ( size > params.acm_max_size ) { + printk(TBOOT_ERR"AC mod size too large: %x (max=%x)\n", size, + params.acm_max_size); + return false; + } + + printk(TBOOT_INFO"AC mod size OK\n"); + + /* + * perform checks on AC mod structure + */ + + /* print it for debugging */ + print_acm_hdr(acm_hdr, "RACM"); + + /* entry point is offset from base addr so make sure it is within module */ + if ( acm_hdr->entry_point >= size ) { + printk(TBOOT_ERR"AC mod entry (%08x) >= AC mod size (%08x)\n", + acm_hdr->entry_point, size); + return false; + } + + /* overflow? */ + if ( plus_overflow_u32(acm_hdr->seg_sel, 8) ) { + printk(TBOOT_ERR"seg_sel plus 8 overflows\n"); + return false; + } + + if ( !acm_hdr->seg_sel || /* invalid selector */ + (acm_hdr->seg_sel & 0x07) || /* LDT, PL!=0 */ + (acm_hdr->seg_sel + 8 > acm_hdr->gdt_limit) ) { + printk(TBOOT_ERR"AC mod selector [%04x] bogus\n", acm_hdr->seg_sel); + return false; + } + + return true; +} + +/* + * Do some AC module sanity checks because any violations will cause + * an TXT.RESET. Instead detect these, print a desriptive message, + * and skip SENTER/ENTERACCS + */ +#ifndef IS_INCLUDED /* defined in utils/acminfo.c */ +void verify_IA32_se_svn_status(const acm_hdr_t *acm_hdr) +{ + + printk(TBOOT_INFO"SGX:verify_IA32_se_svn_status is called\n"); + + //check if SGX is enabled by cpuid with ax=7, cx=0 + if ((cpuid_ebx1(7,0) & 0x00000004) == 0){ + printk(TBOOT_ERR"SGX is not enabled, cpuid.ebx: 0x%x\n", cpuid_ebx1(7,0)); + return; + } + printk(TBOOT_INFO"SGX is enabled, cpuid.ebx:0x%x\n", cpuid_ebx1(7,0)); + printk(TBOOT_INFO"Comparing se_svn with ACM Header se_svn\n"); + + if (((rdmsr(MSR_IA32_SE_SVN_STATUS)>>16) & 0xff) != acm_hdr->se_svn) { + printk(TBOOT_INFO"se_svn is not equal to ACM se_svn\n"); + if (!g_tpm->nv_write(g_tpm, 0, g_tpm->sgx_svn_index, 0, (uint8_t *)&(acm_hdr->se_svn), 1)) + printk(TBOOT_ERR"Write sgx_svn_index 0x%x failed. \n", g_tpm->sgx_svn_index); + else + printk(TBOOT_INFO"Write sgx_svn_index with 0x%x successful.\n", acm_hdr->se_svn); + + if ((rdmsr(MSR_IA32_SE_SVN_STATUS) & 0X00000001) !=0) /* reset platform */ + // printk(TBOOT_INFO"SGX:A reset is required in this boot\n"); + outb(0xcf9, 0x06); + } + else + printk(TBOOT_INFO"se_svn is equal to ACM se_svn\n"); + +} + + +bool verify_acmod(const acm_hdr_t *acm_hdr) +{ + getsec_parameters_t params; + uint32_t size; + + /* assumes this already passed is_acmod() test */ + + size = acm_hdr->size * 4; /* hdr size is in dwords, we want bytes */ + + /* + * AC mod must start on 4k page boundary + */ + + if ( (unsigned long long)acm_hdr & 0xfff ) { + printk(TBOOT_ERR"AC mod base not 4K aligned (%p)\n", acm_hdr); + return false; + } + printk(TBOOT_INFO"AC mod base alignment OK\n"); + + /* AC mod size must: + * - be multiple of 64 + * - greater than ??? + * - less than max supported size for this processor + */ + + if ( (size == 0) || ((size % 64) != 0) ) { + printk(TBOOT_ERR"AC mod size %x bogus\n", size); + return false; + } + + if ( !get_parameters(¶ms) ) { + printk(TBOOT_ERR"get_parameters() failed\n"); + return false; + } + + if ( size > params.acm_max_size ) { + printk(TBOOT_ERR"AC mod size too large: %x (max=%x)\n", size, + params.acm_max_size); + return false; + } + + printk(TBOOT_INFO"AC mod size OK\n"); + + /* + * perform checks on AC mod structure + */ + + /* print it for debugging */ + print_acm_hdr(acm_hdr, "SINIT"); + + /* verify SE enablement status */ + verify_IA32_se_svn_status(acm_hdr); + + /* entry point is offset from base addr so make sure it is within module */ + if ( acm_hdr->entry_point >= size ) { + printk(TBOOT_ERR"AC mod entry (%08x) >= AC mod size (%08x)\n", + acm_hdr->entry_point, size); + return false; + } + + /* overflow? */ + if ( plus_overflow_u32(acm_hdr->seg_sel, 8) ) { + printk(TBOOT_ERR"seg_sel plus 8 overflows\n"); + return false; + } + + if ( !acm_hdr->seg_sel || /* invalid selector */ + (acm_hdr->seg_sel & 0x07) || /* LDT, PL!=0 */ + (acm_hdr->seg_sel + 8 > acm_hdr->gdt_limit) ) { + printk(TBOOT_ERR"AC mod selector [%04x] bogus\n", acm_hdr->seg_sel); + return false; + } + + /* + * check for compatibility with this MLE + */ + + acm_info_table_t *info_table = get_acmod_info_table(acm_hdr); + if ( info_table == NULL ) + return false; + + /* check MLE header versions */ + if ( info_table->min_mle_hdr_ver > MLE_HDR_VER ) { + printk(TBOOT_ERR"AC mod requires a newer MLE (0x%08x)\n", + info_table->min_mle_hdr_ver); + return false; + } + + /* check capabilities */ + /* we need to match one of rlp_wake_{getsec, monitor} */ + txt_caps_t caps_mask = { 0 }; + caps_mask.rlp_wake_getsec = caps_mask.rlp_wake_monitor = 1; + + if ( ( ( MLE_HDR_CAPS & caps_mask._raw ) & + ( info_table->capabilities._raw & caps_mask._raw) ) == 0 ) { + printk(TBOOT_ERR"SINIT and MLE not support compatible RLP wake mechanisms\n"); + return false; + } + /* we also expect ecx_pgtbl to be set */ + if ( !info_table->capabilities.ecx_pgtbl ) { + printk(TBOOT_ERR"SINIT does not support launch with MLE pagetable in ECX\n"); + /* TODO when SINIT ready + * return false; + */ + } + + /* check for version of OS to SINIT data */ + /* we don't support old versions */ + if ( info_table->os_sinit_data_ver < MIN_OS_SINIT_DATA_VER ) { + printk(TBOOT_ERR"SINIT's os_sinit_data version unsupported (%u)\n", + info_table->os_sinit_data_ver); + return false; + } + /* only warn if SINIT supports more recent version than us */ + else if ( info_table->os_sinit_data_ver > MAX_OS_SINIT_DATA_VER ) { + printk(TBOOT_WARN"SINIT's os_sinit_data version unsupported (%u)\n", + info_table->os_sinit_data_ver); + } + + return true; +} +#endif /* IS_INCLUDED */ +/* + * Local variables: + * mode: C + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ -- cgit v1.2.3