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/tboot.c | 473 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 473 insertions(+) create mode 100644 tboot/tboot.c (limited to 'tboot/tboot.c') diff --git a/tboot/tboot.c b/tboot/tboot.c new file mode 100644 index 0000000..a5f773b --- /dev/null +++ b/tboot/tboot.c @@ -0,0 +1,473 @@ +/* + * tboot.c: main entry point and "generic" routines for measured launch + * support + * + * Copyright (c) 2006-2010, Intel Corporation + * All rights reserved. + * + * Copyright (c) 2017 Assured Information Security. + * Ross Philipson + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* counter timeout for waiting for all APs to exit guests */ +#define AP_GUEST_EXIT_TIMEOUT 0x01000000 + +__data long s3_flag = 0; + +#ifdef EFI_DEBUG +static void efi_debug_print_d(efi_xen_tboot_data_t *d) +{ + const efi_file_t *xen_file = efi_get_xen(); + + printk("Xen TBOOT shared data:\n"); + printk(" xen = %p\n", xen_file->u.buffer); + printk(" xen_size = 0x%llx\n", xen_file->size); + printk(" kernel = %p\n", d->kernel); + printk(" kernel_size = 0x%llx\n", d->kernel_size); + printk(" ramdisk = %p\n", d->ramdisk); + printk(" ramdisk_size = 0x%llx\n", d->ramdisk_size); + printk(" memory_map = %p\n", d->memory_map); + printk(" memory_map_size = 0x%llx\n", d->memory_map_size); + printk(" memory_desc_size = 0x%llx\n", d->memory_desc_size); + printk(" _tboot_shared = %p\n", &_tboot_shared); + printk(" shared size = 0x%x\n", sizeof(tboot_shared_t)); +} +#else +#define efi_debug_print_d(d) +#endif + +static void store_section_sizes(void) +{ + uint64_t mle_start_ref; + uint64_t mle_size_ref; + uint64_t bss_start_ref; + uint64_t bss_size_ref; + + /* + * The _mle_start symbol is exactly at the beginning of the .text section. + * The _bss_start symbol is exactly at the beginning of the .bss section. + * The .text and .bss section sizes are gotten from the PE information but + * for a given build, they will always be the same. So that means they can + * be put in the MLE and measured. + */ + + lea_reference(_mle_start, mle_start_ref); + lea_reference(_mle_size, mle_size_ref); + lea_reference(_bss_start, bss_start_ref); + lea_reference(_bss_size, bss_size_ref); + + if ((void*)mle_start_ref != g_text_base) { + printk(TBOOT_ERR"_mle_start != g_text_base\n"); + apply_policy(TB_ERR_FATAL); + } + + *((uint64_t*)mle_size_ref) = g_text_size; + printk(TBOOT_INFO"_mle_start: 0x%llx _mle_size: 0x%llx\n", + mle_start_ref, *((uint64_t*)mle_size_ref)); + + if ((void*)bss_start_ref != g_bss_base) { + printk(TBOOT_ERR"_bss_start != g_bss_base\n"); + apply_policy(TB_ERR_FATAL); + } + + *((uint64_t*)bss_size_ref) = g_bss_size; + printk(TBOOT_INFO"_bss_start: 0x%llx _bss_size: 0x%llx\n", + bss_start_ref, *((uint64_t*)bss_size_ref)); +} + +static tb_error_t verify_platform(void) +{ + return txt_verify_platform(); +} + +static bool is_launched(void) +{ + if ( supports_txt() == TB_ERR_NONE ) + return txt_is_launched(); + else return false; +} + +static bool prepare_cpu(void) +{ + return txt_prepare_cpu(); +} + +static void copy_s3_wakeup_entry(void) +{ + /* TODO deal with S3 later */ +} + +void cpu_wakeup(uint32_t cpuid, uint64_t sipi_vec) +{ + printk(TBOOT_INFO"cpu %u waking up, SIPI vector=%llx\n", cpuid, sipi_vec); + + /* change to real mode and then jump to SIPI vector */ + /* TODO _prot_to_real(sipi_vec); */ +} + +#define ICR_LOW 0x300 + +/*static*/ void startup_rlps(void) +{ + uint32_t rlp_count = ((cpuid_ecx(1) >> 16) & 0xff) - 1; + uint64_t apicbase = rdmsr(MSR_APICBASE) & 0xfffffffffffff000; + + if ( rlp_count == 0 ) + return; + + /* send init ipi to all rlp -- Dest Shorthand: 11, Delivery Mode: 101 */ + writel(apicbase + ICR_LOW, 0xc0500); +} + +void launch_racm(void) +{ + /* TODO this is gonna do GETSEC[ENTERACCS] */ + /* This means IA-32e */ +} + +static void shutdown_system(uint32_t); +void check_racm_result(void) +{ + txt_get_racm_error(); + shutdown_system(TB_SHUTDOWN_HALT); +} + +void begin_initial_launch(void) +{ + const char *cmdline; + const efi_file_t *sinit_file; + acm_hdr_t *sinit; + tb_error_t err; + + /* always load cmdline defaults */ + tboot_parse_cmdline(true); + + /* on pre-SENTER boot, copy command line to buffer in tboot image + * (so that it will be measured); buffer must be 0 -filled */ + if ( !is_launched() && !s3_flag ) { + memset(g_cmdline, '\0', sizeof(g_cmdline)); + cmdline = efi_cfg_get_value(EFI_CONFIG_TBOOT_PARSED, + SECTION_TBOOT, ITEM_OPTIONS); + if (cmdline) + strncpy(g_cmdline, cmdline, sizeof(g_cmdline)-1); + } + + /* always parse cmdline */ + tboot_parse_cmdline(false); + + /* initialize all logging targets */ + printk_init(INIT_PRE_LAUNCH); + + /* DEBUG */ + print_test_chars(); + + printk(TBOOT_INFO"******************* TBOOT *******************\n"); + printk(TBOOT_INFO" %s\n", TBOOT_CHANGESET); + printk(TBOOT_INFO"*********************************************\n"); + + printk(TBOOT_INFO"command line: %s\n", g_cmdline); + + /* DEBUG */ + print_system_values(); + + /* if telled to check revocation acm result, go with simplified path */ + if ( get_tboot_call_racm_check() ) + check_racm_result(); /* never return */ + + /* TODO if (is_launched()) printk(TBOOT_INFO"SINIT ACM successfully returned...\n");*/ + if ( s3_flag ) printk(TBOOT_INFO"Resume from S3...\n"); + + /* clear resume vector on S3 resume so any resets will not use it */ + /* TODO if ( !is_launched() && s3_flag ) set_s3_resume_vector(&_tboot_shared.acpi_sinfo, 0);*/ + + /* we should only be executing on the BSP */ + if ( !(rdmsr(MSR_APICBASE) & APICBASE_BSP) ) { + printk(TBOOT_INFO"entry processor is not BSP\n"); + apply_policy(TB_ERR_FATAL); + } + printk(TBOOT_INFO"BSP is cpu %u\n", get_apicid()); + + /* + * TODO e820 copied here but TBOOT will not be using it. Xen will have to + * do e820 fixup work and present it to dom0. We still need to read the + * min_ram value from the config that used to happen in that call + */ + get_tboot_min_ram(); + + /* make TPM ready for measured launch */ + if (!tpm_detect()) + apply_policy(TB_ERR_TPM_NOT_READY); + + /* we need to make sure this is a (TXT-) capable platform before using */ + /* any of the features, incl. those required to check if the environment */ + /* has already been launched */ + + if (!g_sinit) { + sinit_file = efi_get_platform_sinit(); + if (sinit_file == NULL) + apply_policy(TB_ERR_SINIT_NOT_PRESENT); + /* check if it is newer than BIOS provided version, then copy it to BIOS reserved region */ + g_sinit = copy_sinit((const acm_hdr_t*)sinit_file->u.buffer); + if (g_sinit == NULL) + apply_policy(TB_ERR_SINIT_NOT_PRESENT); + if (!verify_acmod(g_sinit)) + apply_policy(TB_ERR_ACMOD_VERIFY_FAILED); + } + test_virt_to_phys((uint64_t)g_sinit); + + /* read tboot verified launch control policy from TPM-NV (will use default if none in TPM-NV) */ + err = set_policy(); + apply_policy(err); + + /* if telled to call revocation acm, go with simplified path */ + if ( get_tboot_call_racm() ) + launch_racm(); /* never return */ + + /* need to verify that platform supports TXT before we can check error */ + /* (this includes TPM support) */ + err = supports_txt(); + apply_policy(err); + + /* print any errors on last boot, which must be from TXT launch */ + txt_get_error(); + + /* need to verify that platform can perform measured launch */ + err = verify_platform(); + apply_policy(err); + + /* + * Check for modules was here but that is not how things work any longer. + * Xen will load the other modules and have to tell us about them later. + */ + + /* prepare_cpu() will be done later */ + + /* check for error from previous boot */ + printk(TBOOT_INFO"checking previous errors on the last boot.\n\t"); + if ( was_last_boot_error() ) + printk(TBOOT_INFO"last boot has error.\n"); + else + printk(TBOOT_INFO"last boot has no error.\n"); + + if ( !prepare_tpm() ) + apply_policy(TB_ERR_TPM_NOT_READY); + + /* + * Some of the MLE bits can be setup early before we jump off to + * the next gig. First setup the MLE header with offest relative to + * where we are then build the MLE page tables. Also load RIP + * relative addresses for VMCS structures. + */ + txt_init_mle_header(); + + if ( !txt_build_mle_pagetable() ) + apply_policy(TB_ERR_FATAL); + + store_section_sizes(); + + /* This is the end of the line for the initial launch. It is time to start + * Xen and transfer control. The actual SMX launch will be done in a + * callback from Xen after EBS. + */ +} + +void begin_launch(efi_xen_tboot_data_t *xtd) +{ + tb_error_t err; + + /* initialize post EBS logging targets - this must be done first */ + printk_init(INIT_POST_EBS); + + /* store kernel and ramdisk module information */ + if ( !efi_store_xen_tboot_data(xtd) ) + apply_policy(TB_ERR_FATAL); + + /* DEBUG */ + print_system_values(); + + efi_debug_print_d(xtd); + + if ( !efi_scan_memory_map() ) + apply_policy(TB_ERR_FATAL); + + /* DEBUG */ + /*dump_page_tables();*/ + + /* make the CPU ready for measured launch */ + if ( !prepare_cpu() ) + apply_policy(TB_ERR_FATAL); + + /* launch the measured environment */ + err = txt_launch_environment(); + apply_policy(err); + + /* TODO have to figure out how to return to Xen when we pop + * out elsewhere. Save the ret addr and mock up the function + * return. + */ +} + +void post_launch(void) +{ + /* always load cmdline defaults */ + tboot_parse_cmdline(true); + + /* always parse cmdline */ + tboot_parse_cmdline(false); + + /* initialize all logging targets */ + printk_init(INIT_POST_LAUNCH); + + printk(TBOOT_INFO"******************** MLE ********************\n"); + printk(TBOOT_INFO" %s\n", TBOOT_CHANGESET); + printk(TBOOT_INFO"*********************************************\n"); + + /* init the bits needed to run APs in mini-VMs */ + init_vmcs_addrs(); + + /* TODO reparse and load configs stored in the MLE */ + + /* TODO measure the memory map */ + + /* TODO call efi_scan_memory_map again after measured launch to rebuild map */ + + /* TODO hash Xen text section before transferring control back to it */ +} + +void s3_launch(void) +{ + /* TODO deal with later */ +} + +static void shutdown_system(uint32_t shutdown_type) +{ + static const char *types[] = { "TB_SHUTDOWN_REBOOT", "TB_SHUTDOWN_S5", + "TB_SHUTDOWN_S4", "TB_SHUTDOWN_S3", + "TB_SHUTDOWN_HALT" }; + char type[32]; + + if ( shutdown_type >= ARRAY_SIZE(types) ) + snprintf(type, sizeof(type), "unknown: %u", shutdown_type); + else + strncpy(type, types[shutdown_type], sizeof(type)); + printk(TBOOT_INFO"shutdown_system() called for shutdown_type: %s\n", type); + + switch( shutdown_type ) { + case TB_SHUTDOWN_S3: + copy_s3_wakeup_entry(); + /* write our S3 resume vector to ACPI resume addr */ + /* TODO handle S3 later set_s3_resume_vector(&_tboot_shared.acpi_sinfo, TBOOT_S3_WAKEUP_ADDR);*/ + /* fall through for rest of Sx handling */ + case TB_SHUTDOWN_S4: + case TB_SHUTDOWN_S5: + machine_sleep(&_tboot_shared.acpi_sinfo); + /* if machine_sleep() fails, fall through to reset */ + + case TB_SHUTDOWN_REBOOT: + if ( txt_is_powercycle_required() ) { + /* powercycle by writing 0x0a+0x0e to port 0xcf9 */ + /* (supported by all TXT-capable chipsets) */ + outb(0xcf9, 0x0a); + outb(0xcf9, 0x0e); + } + else { + /* soft reset by writing 0xfe to keyboard reset vector 0x64 */ + /* BIOSes (that are not performing some special operation, */ + /* such as update) will turn this into a platform reset as */ + /* expected. */ + outb(0x64, 0xfe); + /* fall back to soft reset by writing 0x06 to port 0xcf9 */ + /* (supported by all TXT-capable chipsets) */ + outb(0xcf9, 0x06); + } + + case TB_SHUTDOWN_HALT: + default: + while ( true ) + halt(); + } +} + +void shutdown(void) +{ + /* TODO fill me in */ +} + +void handle_exception(void) +{ + printk(TBOOT_INFO"received exception; shutting down...\n"); + _tboot_shared.shutdown_type = TB_SHUTDOWN_REBOOT; + shutdown(); +} + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ + -- cgit v1.2.3