/* * 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: */