LL ) + *type = info_table->v1.chipset_acm_type; + /* there is forward compatibility, so this is just a warning */ + if ( info_table->v1.version != 0x01 ) + printk("ACM info_table version mismatch (%x)\n", + (unsigned int)info_table->v1.version); + } + + return true; +} + +bool is_sinit_acmod(void *acmod_base, uint32_t acmod_size) +{ + uint8_t type; + + if ( !is_acmod(acmod_base, acmod_size, &type) ) + return false; + + if ( type != ACM_CHIPSET_TYPE_SINIT ) { + printk("ACM is not an SINIT ACM (%x)\n", type); + return false; + } + + return true; +} + +bool does_acmod_match_chipset(acm_hdr_t* hdr) +{ + acm_info_table_t *info_table; + acm_chipset_id_list_t *chipset_id_list; + acm_chipset_id_t *chipset_id; + txt_didvid_t didvid; + uint32_t size, id_list_off; + int i; + + /* 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 false; + if ( are_uuids_equal(&(info_table->v2.uuid), &ACM_UUID) ) + id_list_off = info_table->v2.chipset_id_list; + else + id_list_off = info_table->v1.chipset_id_list; + + size = hdr->size * 4; + + /* check that chipset id table is w/in ACM */ + if ( id_list_off + sizeof(acm_chipset_id_t) > size ) { + printk("ACM chipset id list is too big: chipset_id_list=%x\n", + id_list_off); + return false; + } + + chipset_id_list = (acm_chipset_id_list_t *)((uint32_t)hdr + id_list_off); + + /* 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("ACM chipset id entries are too big:" + " chipset_id_list->count=%x\n", chipset_id_list->count); + return false; + } + + /* get chipset device and vendor id info */ + didvid._raw = read_pub_config_reg(TXTCR_DIDVID); + printk("chipset ids: vendor=%x, device=%x, revision=%x\n", + didvid.vendor_id, didvid.device_id, didvid.revision_id); + + printk("%x ACM chipset id entries:\n", chipset_id_list->count); + for ( i = 0; i < chipset_id_list->count; i++ ) { + chipset_id = &(chipset_id_list->chipset_ids[i]); + printk("\tvendor=%x, device=%x, flags=%x, revision=%x, " + "extended=%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 ) ) ) ) + return true; + } + + printk("ACM does not match chipset\n"); + +#ifdef CHIPSET_REVID_BUG + return true; +#else + return false; +#endif +} + +acm_hdr_t *copy_sinit(acm_hdr_t *sinit) +{ + void *sinit_region_base; + uint32_t sinit_region_size; + txt_heap_t *txt_heap; + bios_os_data_t *bios_os_data; + + /* get BIOS-reserved region from LT.SINIT.BASE config reg */ + sinit_region_base = (void*)(uint32_t)read_pub_config_reg(TXTCR_SINIT_BASE); + sinit_region_size = (uint32_t)read_pub_config_reg(TXTCR_SINIT_SIZE); + + /* + * check if BIOS already loaded an SINIT module there + */ + txt_heap = get_txt_heap(); + bios_os_data = get_bios_os_data_start(txt_heap); + /* BIOS has loaded an SINIT module, so verify that it is valid */ + if ( bios_os_data->bios_sinit_size != 0 ) { + printk("BIOS has already loaded an SINIT module\n"); + /* is it a valid SINIT module? */ + if ( is_sinit_acmod(sinit_region_base, + bios_os_data->bios_sinit_size) ) { + /* is it newer than the one we've been provided? */ + if ( ((acm_hdr_t *)sinit_region_base)->date >= sinit->date ) { + printk("BIOS-provided SINIT is newer, so using it\n"); + return (acm_hdr_t *)sinit_region_base; /* yes */ + } + else + printk("BIOS-provided SINIT is older: date=%x\n", + ((acm_hdr_t *)sinit_region_base)->date); + } + } + /* our SINIT is newer than BIOS's (or BIOS did not have one) */ + + /* make sure our SINIT fits in the reserved region */ + if ( (sinit->size * 4) > sinit_region_size ) { + printk("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("copied SINIT (size=%x) to %p\n", sinit->size*4, + sinit_region_base); + + return (acm_hdr_t *)sinit_region_base; +} + + +/* + * 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 + */ +bool verify_acmod(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)acm_hdr & 0xfff ) { + printk("AC mod base not 4K aligned (%p)\n", acm_hdr); + return false; + } + printk("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("AC mod size %x bogus\n", size); + return false; + } + + if ( get_parameters(¶ms) == -1 ) { + printk("get_parameters() failed\n"); + return false; + } + + if ( size > params.acm_max_size ) { + printk("AC mod size too large: %x (max=%x)\n", size, + params.acm_max_size); + return false; + } + + printk("AC mod size OK\n"); + + /* + * perform checks on AC mod structure + */ + + /* print it for debugging */ + print_acm_hdr(acm_hdr, "SINIT"); + + /* entry point is offset from base addr so make sure it is within module */ + if ( acm_hdr->entry_point >= size ) { + printk("AC mod entry (%08x) >= AC mod size (%08x)\n", + acm_hdr->entry_point, size); + 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("AC mod selector [%04x] bogus\n", acm_hdr->seg_sel); + return false; + } + + return true; +} + +/* + * this must be done for each processor so that all have the same + * memory types + */ +void set_mtrrs_for_acmod(acm_hdr_t *hdr) +{ + unsigned long eflags; + unsigned long cr0, cr4; + + /* + * need to do some things before we start changing MTRRs + * + * since this will modify some of the MTRRs, they should be saved first + * so that they can be restored once the AC mod is done + */ + + /* disable interrupts */ + __save_flags(eflags); + __cli(); + + /* save CR0 then disable cache (CRO.CD=1, CR0.NW=0) */ + cr0 = read_cr0(); + write_cr0((cr0 & ~X86_CR0_NW) | X86_CR0_CD); + + /* flush caches */ + wbinvd(); + + /* save CR4 and disable global pages (CR4.PGE=0) */ + cr4 = read_cr4(); + write_cr4(cr4 & ~X86_CR4_PGE); + + /* disable MTRRs */ + set_all_mtrrs(false); + + /* + * now set MTRRs for AC mod and rest of memory + */ + set_mem_type(hdr, hdr->size*4, MTRR_TYPE_WRBACK); + + /* + * now undo some of earlier changes and enable our new settings + */ + + /* flush caches */ + wbinvd(); + + /* enable MTRRs */ + set_all_mtrrs(true); + + /* restore CR0 (cacheing) */ + write_cr0(cr0); + + /* restore CR4 (global pages) */ + write_cr4(cr4); + + /* enable interrupts */ + __restore_flags(eflags); +} + + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff -r de942f6fa6b3 -r 5648dc802679 sboot/txt/errors.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sboot/txt/errors.c Tue Aug 28 16:27:50 2007 -0700 @@ -0,0 +1,113 @@ +/* + * errors.c: parse and return status of Intel(r) TXT error codes + * + * Copyright (c) 2003-2007, 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include + + +static void display_errors(void) +{ + txt_errorcode_t err; + txt_ests_t ests; + txt_e2sts_t e2sts; + txt_errorcode_sw_t sw_err; + acmod_error_t acmod_err; + + /* + * display LT.ERRORODE error + */ + err = (txt_errorcode_t)read_pub_config_reg(TXTCR_ERRORCODE); + printk("LT.ERRORCODE=%Lx\n", err._raw); + + /* AC module error (don't know how to parse other errors) */ + if ( err.valid ) { + if ( err.external == 0 ) /* processor error */ + printk("\t processor error %x\n", (uint32_t)err.type); + else { /* external SW error */ + sw_err._raw = err.type; + if ( sw_err.src == 1 ) /* unknown SW error */ + printk("unknown SW error %x\n", (uint32_t)sw_err.err); + else { /* ACM error */ + acmod_err._raw = sw_err.err; + printk("AC module error : type=%x, progress=%02x, error=%x\n", + (uint32_t)acmod_err.type, (uint32_t)acmod_err.progress, + (uint32_t)acmod_err.error); + } + } + } + + /* + * display LT.ESTS error + */ + ests = (txt_ests_t)read_pub_config_reg(TXTCR_ESTS); + printk("LT.ESTS=%Lx\n", ests._raw); + + /* + * display LT.E2STS error + * - only valid if LT.WAKE-ERROR.STS set in LT.STS reg + */ + if ( ests.txt_wake_error_sts ) { + e2sts = (txt_e2sts_t)read_pub_config_reg(TXTCR_E2STS); + printk("LT.E2STS=%Lx\n", e2sts._raw); + } +} + +bool txt_get_error(void) +{ + txt_errorcode_t err; + + display_errors(); + + err = (txt_errorcode_t)read_pub_config_reg(TXTCR_ERRORCODE); + if ( err.valid ) + return false; + else + return true; +} + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff -r de942f6fa6b3 -r 5648dc802679 sboot/txt/mtrrs.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sboot/txt/mtrrs.c Tue Aug 28 16:27:50 2007 -0700 @@ -0,0 +1,883 @@ +/* + * mtrrs.c: support functions for manipulating MTRRs + * + * Copyright (c) 2003-2007, 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MTRR_TYPE_MIXED -1 +#define MMIO_APIC_BASE 0xFEE00000 +#define NR_MMIO_APIC_PAGES 1 +#define NR_MMIO_IOAPIC_PAGES 1 +#define NR_MMIO_PCICFG_PAGES 1 + +void save_mtrrs(mtrr_state_t *saved_state) +{ + mtrr_cap_t mtrr_cap; + int ndx; + + /* IA32_MTRR_DEF_TYPE MSR */ + rdmsrl(MSR_IA32_MTRR_DEF_TYPE, saved_state->mtrr_def_type.raw); + + /* number variable MTTRRs */ + rdmsrl(MSR_IA32_MTRRCAP, mtrr_cap.raw); + if ( mtrr_cap.vcnt > MAX_VARIABLE_MTRRS ) { + /* print warning but continue saving what we can */ + /* (set_mem_type() won't exceed the array, so we're safe doing this) */ + printk("actual # var MTRRs (%d) > MAX_VARIABLE_MTRRS (%d)\n", + mtrr_cap.vcnt, MAX_VARIABLE_MTRRS); + saved_state->num_var_mtrrs = MAX_VARIABLE_MTRRS; + } + else + saved_state->num_var_mtrrs = mtrr_cap.vcnt; + + /* physmask's and physbase's */ + for ( ndx = 0; ndx < saved_state->num_var_mtrrs; ndx++ ) { + rdmsrl(MTRR_PHYS_MASK0_MSR + ndx*2, + saved_state->mtrr_physmasks[ndx].raw); + rdmsrl(MTRR_PHYS_BASE0_MSR + ndx*2, + saved_state->mtrr_physbases[ndx].raw); + } +} + +static void print_mtrrs(const mtrr_state_t *saved_state) +{ + int i; + printk("mtrr_def_type: e = %d, fe = %d, type = %x\n", + saved_state->mtrr_def_type.e, saved_state->mtrr_def_type.fe, + saved_state->mtrr_def_type.type ); + printk("mtrrs:\n"); + printk("\t\tbase\tmask\ttype\tv\n"); + for ( i = 0; i < saved_state->num_var_mtrrs; i++ ) + printk("\t\t%6.6x\t%6.6x\t%2.2x\t%d\n", + saved_state->mtrr_physbases[i].base, + saved_state->mtrr_physmasks[i].mask, + saved_state->mtrr_physbases[i].type, + saved_state->mtrr_physmasks[i].v ); +} + +/* base should be 4k-bytes aligned, no invalid overlap combination */ +static int get_page_type(const mtrr_state_t *saved_state, uint32_t base) +{ + int i, type = -1; + bool wt = false; + + /* omit whether the fix mtrrs are enabled, just check var mtrrs */ + + base >>= PAGE_SHIFT; + for ( i = 0; i < saved_state->num_var_mtrrs; i++ ) { + const mtrr_physbase_t *base_i = &saved_state->mtrr_physbases[i]; + const mtrr_physmask_t *mask_i = &saved_state->mtrr_physmasks[i]; + + if ( mask_i->v == 0 ) + continue; + if ( (base & mask_i->mask) + != (base_i->base & mask_i->mask) ) + continue; + + type = base_i->type; + if ( type == MTRR_TYPE_UNCACHABLE ) + return MTRR_TYPE_UNCACHABLE; + if ( type == MTRR_TYPE_WRTHROUGH ) + wt = true; + } + if ( wt ) + return MTRR_TYPE_WRTHROUGH; + if ( type != -1 ) + return type; + + return saved_state->mtrr_def_type.type; +} + +static int get_region_type(const mtrr_state_t *saved_state, + uint32_t base, uint32_t pages) +{ + int type; + uint32_t end; + + if ( pages == 0 ) + return MTRR_TYPE_MIXED; + + /* wrap the 4G address space */ + if ( ((uint32_t)(~0) - base) < (pages << PAGE_SHIFT) ) + return MTRR_TYPE_MIXED; + + if ( saved_state->mtrr_def_type.e == 0 ) + return MTRR_TYPE_UNCACHABLE; + + /* align to 4k page boundary */ + base &= PAGE_MASK; + end = base + (pages << PAGE_SHIFT); + + type = get_page_type(saved_state, base); + base += PAGE_SIZE; + for ( ; base < end; base += PAGE_SIZE ) + if ( type != get_page_type(saved_state, base) ) + return MTRR_TYPE_MIXED; + + return type; +} + +static bool validate_mmio_regions(const mtrr_state_t *saved_state) +{ + acpi_table_mcfg_t *acpi_table_mcfg; + acpi_table_ioapic_t *acpi_table_ioapic; + + /* mmio space for TXT private config space should be UC*/ + if ( get_region_type(saved_state, TXT_PRIV_CONFIG_REGS_BASE, + NR_TXT_CONFIG_PAGES) + != MTRR_TYPE_UNCACHABLE ) { + printk("MMIO space for TXT private config space should be UC\n"); + return false; + } + + /* mmio space for TXT public config space should be UC*/ + if ( get_region_type(saved_state, TXT_PUB_CONFIG_REGS_BASE, + NR_TXT_CONFIG_PAGES) + != MTRR_TYPE_UNCACHABLE ) { + printk("MMIO space for TXT public config space should be UC\n"); + return false; + } + + /* mmio space for TPM should be UC */ + if ( get_region_type(saved_state, TPM_LOCALITY_BASE, + NR_TPM_LOCALITY_PAGES * TPM_NR_LOCALITIES) + != MTRR_TYPE_UNCACHABLE ) { + printk("MMIO space for TPM should be UC\n"); + return false; + } + + /* mmio space for APIC should be UC */ + if ( get_region_type(saved_state, MMIO_APIC_BASE, NR_MMIO_APIC_PAGES) + != MTRR_TYPE_UNCACHABLE ) { + printk("MMIO space for APIC should be UC\n"); + return false; + } + + /* mmio space for IOAPIC should be UC */ + acpi_table_ioapic = (acpi_table_ioapic_t *)get_acpi_ioapic_table(); + if ( acpi_table_ioapic == NULL) { + printk("acpi_table_ioapic == NULL\n"); + return false; + } + printk("acpi_table_ioapic @ %p, .address = %x\n", + acpi_table_ioapic, acpi_table_ioapic->address); + if ( get_region_type(saved_state, acpi_table_ioapic->address, + NR_MMIO_IOAPIC_PAGES) + != MTRR_TYPE_UNCACHABLE ) { + printk("MMIO space(%x) for IOAPIC should be UC\n", + acpi_table_ioapic->address); + return false; + } + + /* mmio space for PCI config space should be UC */ + acpi_table_mcfg = (acpi_table_mcfg_t *)get_acpi_mcfg_table(); + if ( acpi_table_mcfg == NULL) { + printk("acpi_table_mcfg == NULL\n"); + return false; + } + printk("acpi_table_mcfg @ %p, .base_address = %x\n", + acpi_table_mcfg, acpi_table_mcfg->base_address); + if ( get_region_type(saved_state, acpi_table_mcfg->base_address, + NR_MMIO_PCICFG_PAGES) + != MTRR_TYPE_UNCACHABLE ) { + printk("MMIO space(%x) for PCI config space should be UC\n", + acpi_table_mcfg->base_address); + return false; + } + + return true; +} + +bool validate_mtrrs(const mtrr_state_t *saved_state) +{ + mtrr_cap_t mtrr_cap; + int ndx; + + /* check is meaningless if MTRRs were disabled */ + if ( saved_state->mtrr_def_type.e == 0 ) + return true; + + /* number variable MTRRs */ + rdmsrl(MSR_IA32_MTRRCAP, mtrr_cap.raw); + if ( mtrr_cap.vcnt < saved_state->num_var_mtrrs ) { + printk("actual # var MTRRs (%d) < saved # (%d)\n", + mtrr_cap.vcnt, saved_state->num_var_mtrrs); + return false; + } + + /* variable MTRRs describing non-contiguous memory regions */ + /* TBD: assert(MAXPHYADDR == 36); */ + for ( ndx = 0; ndx < saved_state->num_var_mtrrs; ndx++ ) { + uint64_t tb; + + if ( saved_state->mtrr_physmasks[ndx].v == 0 ) + continue; + + for ( tb = 0x1; tb != 0x1000000; tb = tb << 1 ) + if ( (tb & saved_state->mtrr_physmasks[ndx].mask) != 0 ) + break; + for ( ; tb != 0x1000000; tb = tb << 1 ) + if ( (tb & saved_state->mtrr_physmasks[ndx].mask) == 0 ) + break; + if ( tb != 0x1000000 ) { + printk("var MTRRs with non-contiguous regions: " + "base=%06x, mask=%06x\n", + (unsigned int) saved_state->mtrr_physbases[ndx].base, + (unsigned int) saved_state->mtrr_physmasks[ndx].mask); + print_mtrrs(saved_state); + return false; + } + } + + /* overlaping regions with invalid memory type combinations */ + for ( ndx = 0; ndx < saved_state->num_var_mtrrs; ndx++ ) { + int i; + const mtrr_physbase_t *base_ndx = &saved_state->mtrr_physbases[ndx]; + const mtrr_physmask_t *mask_ndx = &saved_state->mtrr_physmasks[ndx]; + + if ( mask_ndx->v == 0 ) + continue; + + for ( i = ndx + 1; i < saved_state->num_var_mtrrs; i++ ) { + int j; + const mtrr_physbase_t *base_i = &saved_state->mtrr_physbases[i]; + const mtrr_physmask_t *mask_i = &saved_state->mtrr_physmasks[i]; + + if ( mask_i->v == 0 ) + continue; + + if ( (base_ndx->base & mask_ndx->mask & mask_i->mask) + != (base_i->base & mask_i->mask) + && (base_i->base & mask_i->mask & mask_ndx->mask) + != (base_ndx->base & mask_ndx->mask) ) + continue; + + if ( base_ndx->type == base_i->type ) + continue; + if ( base_ndx->type == MTRR_TYPE_UNCACHABLE + || base_i->type == MTRR_TYPE_UNCACHABLE ) + continue; + if ( base_ndx->type == MTRR_TYPE_WRTHROUGH + && base_i->type == MTRR_TYPE_WRBACK ) + continue; + if ( base_ndx->type == MTRR_TYPE_WRBACK + && base_i->type == MTRR_TYPE_WRTHROUGH ) + continue; + + /* 2 overlapped regions have invalid mem type combination, */ + /* need to check whether there is a third region which has type */ + /* of UNCACHABLE and contains at least one of these two regions. */ + /* If there is, then the combination of these 3 region is valid */ + for ( j = 0; j < saved_state->num_var_mtrrs; j++ ) { + const mtrr_physbase_t *base_j + = &saved_state->mtrr_physbases[j]; + const mtrr_physmask_t *mask_j + = &saved_state->mtrr_physmasks[j]; + + if ( mask_j->v == 0 ) + continue; + + if ( base_j->type != MTRR_TYPE_UNCACHABLE ) + continue; + + if ( (base_ndx->base & mask_ndx->mask & mask_j->mask) + == (base_j->base & mask_j->mask) + && (mask_j->mask & ~mask_ndx->mask) == 0 ) + break; + + if ( (base_i->base & mask_i->mask & mask_j->mask) + == (base_j->base & mask_j->mask) + && (mask_j->mask & ~mask_i->mask) == 0 ) + break; + } + if ( j < saved_state->num_var_mtrrs ) + continue; + + printk("var MTRRs overlaping regions, invalid type combinations\n"); + print_mtrrs(saved_state); + return false; + } + } + + if ( !validate_mmio_regions(saved_state) ) { + printk("Some mmio region should be UC type\n"); + print_mtrrs(saved_state); + return false; + } + + print_mtrrs(saved_state); + return true; +} + +#ifdef MTRR_VALIDATION_UNITTEST + +#define RUN_CASE(caseid) {\ + if ( caseid() )\ + printk("VALIDATE_MTTR_UNIT_TEST: " #caseid " passed\n");\ + else\ + printk("VALIDATE_MTTR_UNIT_TEST: " #caseid " failed\n");\ +} + +static mtrr_state_t g_test_state; + +static bool UNIT_VM_V_01(void) +{ + g_test_state.num_var_mtrrs = 0; + + return validate_mtrrs(&g_test_state); +} + +static bool UNIT_VM_V_02(void) +{ + g_test_state.num_var_mtrrs = 1; + g_test_state.mtrr_physbases[0].type = MTRR_TYPE_UNCACHABLE; + g_test_state.mtrr_physbases[0].reserved1 = 0; + g_test_state.mtrr_physbases[0].base = 0x000000; + g_test_state.mtrr_physbases[0].reserved2 = 0; + g_test_state.mtrr_physmasks[0].reserved1 = 0; + g_test_state.mtrr_physmasks[0].v = 0; + g_test_state.mtrr_physmasks[0].mask = 0x000000; + g_test_state.mtrr_physmasks[0].reserved2 = 0; + + return validate_mtrrs(&g_test_state); +} + +static bool UNIT_VM_V_03(void) +{ + g_test_state.num_var_mtrrs = 1; + g_test_state.mtrr_physbases[0].type = MTRR_TYPE_UNCACHABLE; + g_test_state.mtrr_physbases[0].reserved1 = 0; + g_test_state.mtrr_physbases[0].base = 0x000000; + g_test_state.mtrr_physbases[0].reserved2 = 0; + g_test_state.mtrr_physmasks[0].reserved1 = 0; + g_test_state.mtrr_physmasks[0].v = 1; + g_test_state.mtrr_physmasks[0].mask = 0x000000; + g_test_state.mtrr_physmasks[0].reserved2 = 0; + + return validate_mtrrs(&g_test_state); +} + +static bool UNIT_VM_V_04(void) +{ + g_test_state.num_var_mtrrs = 1; + g_test_state.mtrr_physbases[0].type = MTRR_TYPE_UNCACHABLE; + g_test_state.mtrr_physbases[0].reserved1 = 0; + g_test_state.mtrr_physbases[0].base = 0x000000; + g_test_state.mtrr_physbases[0].reserved2 = 0; + g_test_state.mtrr_physmasks[0].reserved1 = 0; + g_test_state.mtrr_physmasks[0].v = 1; + g_test_state.mtrr_physmasks[0].mask = 0xFFFFFF; + g_test_state.mtrr_physmasks[0].reserved2 = 0; + + return validate_mtrrs(&g_test_state); +} + +static bool UNIT_VM_V_05(void) +{ + g_test_state.num_var_mtrrs = 1; + g_test_state.mtrr_physbases[0].type = MTRR_TYPE_UNCACHABLE; + g_test_state.mtrr_physbases[0].reserved1 = 0; + g_test_state.mtrr_physbases[0].base = 0x000000; + g_test_state.mtrr_physbases[0].reserved2 = 0; + g_test_state.mtrr_physmasks[0].reserved1 = 0; + g_test_state.mtrr_physmasks[0].v = 1; + g_test_state.mtrr_physmasks[0].mask = 0xFFF000; + g_test_state.mtrr_physmasks[0].reserved2 = 0; + + return validate_mtrrs(&g_test_state); +} + +static bool UNIT_VM_V_06(void) +{ + g_test_state.num_var_mtrrs = 2; + g_test_state.mtrr_physbases[0].type = MTRR_TYPE_UNCACHABLE; + g_test_state.mtrr_physbases[0].reserved1 = 0; + g_test_state.mtrr_physbases[0].base = 0x000000; + g_test_state.mtrr_physbases[0].reserved2 = 0; + g_test_state.mtrr_physmasks[0].reserved1 = 0; + g_test_state.mtrr_physmasks[0].v = 1; + g_test_state.mtrr_physmasks[0].mask = 0xFFF000; + g_test_state.mtrr_physmasks[0].reserved2 = 0; + g_test_state.mtrr_physbases[1].type = MTRR_TYPE_UNCACHABLE; + g_test_state.mtrr_physbases[1].reserved1 = 0; + g_test_state.mtrr_physbases[1].base = 0x001000; + g_test_state.mtrr_physbases[1].reserved2 = 0; + g_test_state.mtrr_physmasks[1].reserved1 = 0; + g_test_state.mtrr_physmasks[1].v = 1; + g_test_state.mtrr_physmasks[1].mask = 0xFFF000; + g_test_state.mtrr_physmasks[1].reserved2 = 0; + + return validate_mtrrs(&g_test_state); +} + +static bool UNIT_VM_V_07(void) +{ + g_test_state.num_var_mtrrs = 2; + g_test_state.mtrr_physbases[0].type = MTRR_TYPE_UNCACHABLE; + g_test_state.mtrr_physbases[0].reserved1 = 0; + g_test_state.mtrr_physbases[0].base = 0x000000; + g_test_state.mtrr_physbases[0].reserved2 = 0; + g_test_state.mtrr_physmasks[0].reserved1 = 0; + g_test_state.mtrr_physmasks[0].v = 1; + g_test_state.mtrr_physmasks[0].mask = 0xFFF000; + g_test_state.mtrr_physmasks[0].reserved2 = 0; + g_test_state.mtrr_physbases[1].type = MTRR_TYPE_UNCACHABLE; + g_test_state.mtrr_physbases[1].reserved1 = 0; + g_test_state.mtrr_physbases[1].base = 0x001000; + g_test_state.mtrr_physbases[1].reserved2 = 0; + g_test_state.mtrr_physmasks[1].reserved1 = 0; + g_test_state.mtrr_physmasks[1].v = 0; + g_test_state.mtrr_physmasks[1].mask = 0xFFF000; + g_test_state.mtrr_physmasks[1].reserved2 = 0; + + return validate_mtrrs(&g_test_state); +} + +static bool UNIT_VM_V_08(void) +{ + g_test_state.num_var_mtrrs = 2; + g_test_state.mtrr_physbases[0].type = MTRR_TYPE_WRPROT; + g_test_state.mtrr_physbases[0].reserved1 = 0; + g_test_state.mtrr_physbases[0].base = 0x000800; + g_test_state.mtrr_physbases[0].reserved2 = 0; + g_test_state.mtrr_physmasks[0].reserved1 = 0; + g_test_state.mtrr_physmasks[0].v = 1; + g_test_state.mtrr_physmasks[0].mask = 0xFFF000; + g_test_state.mtrr_physmasks[0].reserved2 = 0; + g_test_state.mtrr_physbases[1].type = MTRR_TYPE_WRPROT; + g_test_state.mtrr_physbases[1].reserved1 = 0; + g_test_state.mtrr_physbases[1].base = 0x000800; + g_test_state.mtrr_physbases[1].reserved2 = 0; + g_test_state.mtrr_physmasks[1].reserved1 = 0; + g_test_state.mtrr_physmasks[1].v = 1; + g_test_state.mtrr_physmasks[1].mask = 0xFFF800; + g_test_state.mtrr_physmasks[1].reserved2 = 0; + + return validate_mtrrs(&g_test_state); +} + +static bool UNIT_VM_V_09(void) +{ + g_test_state.num_var_mtrrs = 2; + g_test_state.mtrr_physbases[0].type = MTRR_TYPE_WRCOMB; + g_test_state.mtrr_physbases[0].reserved1 = 0; + g_test_state.mtrr_physbases[0].base = 0x000800; + g_test_state.mtrr_physbases[0].reserved2 = 0; + g_test_state.mtrr_physmasks[0].reserved1 = 0; + g_test_state.mtrr_physmasks[0].v = 1; + g_test_state.mtrr_physmasks[0].mask = 0xFFF000; + g_test_state.mtrr_physmasks[0].reserved2 = 0; + g_test_state.mtrr_physbases[1].type = MTRR_TYPE_UNCACHABLE; + g_test_state.mtrr_physbases[1].reserved1 = 0; + g_test_state.mtrr_physbases[1].base = 0x000800; + g_test_state.mtrr_physbases[1].reserved2 = 0; + g_test_state.mtrr_physmasks[1].reserved1 = 0; + g_test_state.mtrr_physmasks[1].v = 1; + g_test_state.mtrr_physmasks[1].mask = 0xFFF800; + g_test_state.mtrr_physmasks[1].reserved2 = 0; + + return validate_mtrrs(&g_test_state); +} + +static bool UNIT_VM_V_10(void) +{ + g_test_state.num_var_mtrrs = 2; + g_test_state.mtrr_physbases[0].type = MTRR_TYPE_WRTHROUGH; + g_test_state.mtrr_physbases[0].reserved1 = 0; + g_test_state.mtrr_physbases[0].base = 0x000800; + g_test_state.mtrr_physbases[0].reserved2 = 0; + g_test_state.mtrr_physmasks[0].reserved1 = 0; + g_test_state.mtrr_physmasks[0].v = 1; + g_test_state.mtrr_physmasks[0].mask = 0xFFF000; + g_test_state.mtrr_physmasks[0].reserved2 = 0; + g_test_state.mtrr_physbases[1].type = MTRR_TYPE_WRBACK; + g_test_state.mtrr_physbases[1].reserved1 = 0; + g_test_state.mtrr_physbases[1].base = 0x000800; + g_test_state.mtrr_physbases[1].reserved2 = 0; + g_test_state.mtrr_physmasks[1].reserved1 = 0; + g_test_state.mtrr_physmasks[1].v = 1; + g_test_state.mtrr_physmasks[1].mask = 0xFFF800; + g_test_state.mtrr_physmasks[1].reserved2 = 0; + + return validate_mtrrs(&g_test_state); +} + +static bool UNIT_VM_V_11(void) +{ + g_test_state.num_var_mtrrs = 3; + g_test_state.mtrr_physbases[0].type = MTRR_TYPE_UNCACHABLE; + g_test_state.mtrr_physbases[0].reserved1 = 0; + g_test_state.mtrr_physbases[0].base = 0x000800; + g_test_state.mtrr_physbases[0].reserved2 = 0; + g_test_state.mtrr_physmasks[0].reserved1 = 0; + g_test_state.mtrr_physmasks[0].v = 1; + g_test_state.mtrr_physmasks[0].mask = 0xFFF000; + g_test_state.mtrr_physmasks[0].reserved2 = 0; + g_test_state.mtrr_physbases[1].type = MTRR_TYPE_WRTHROUGH; + g_test_state.mtrr_physbases[1].reserved1 = 0; + g_test_state.mtrr_physbases[1].base = 0x000800; + g_test_state.mtrr_physbases[1].reserved2 = 0; + g_test_state.mtrr_physmasks[1].reserved1 = 0; + g_test_state.mtrr_physmasks[1].v = 1; + g_test_state.mtrr_physmasks[1].mask = 0xFFF800; + g_test_state.mtrr_physmasks[1].reserved2 = 0; + g_test_state.mtrr_physbases[2].type = MTRR_TYPE_WRPROT; + g_test_state.mtrr_physbases[2].reserved1 = 0; + g_test_state.mtrr_physbases[2].base = 0x000800; + g_test_state.mtrr_physbases[2].reserved2 = 0; + g_test_state.mtrr_physmasks[2].reserved1 = 0; + g_test_state.mtrr_physmasks[2].v = 1; + g_test_state.mtrr_physmasks[2].mask = 0xFFF000; + g_test_state.mtrr_physmasks[2].reserved2 = 0; + + return validate_mtrrs(&g_test_state); +} + +static bool UNIT_VM_IV_01(void) +{ + g_test_state.num_var_mtrrs = 17; + + return !validate_mtrrs(&g_test_state); +} + +static bool UNIT_VM_IV_02(void) +{ + g_test_state.num_var_mtrrs = 1; + g_test_state.mtrr_physbases[0].type = MTRR_TYPE_UNCACHABLE; + g_test_state.mtrr_physbases[0].reserved1 = 0; + g_test_state.mtrr_physbases[0].base = 0x000000; + g_test_state.mtrr_physbases[0].reserved2 = 0; + g_test_state.mtrr_physmasks[0].reserved1 = 0; + g_test_state.mtrr_physmasks[0].v = 1; + g_test_state.mtrr_physmasks[0].mask = 0x000001; + g_test_state.mtrr_physmasks[0].reserved2 = 0; + + return !validate_mtrrs(&g_test_state); +} + +static bool UNIT_VM_IV_03(void) +{ + g_test_state.num_var_mtrrs = 1; + g_test_state.mtrr_physbases[0].type = MTRR_TYPE_UNCACHABLE; + g_test_state.mtrr_physbases[0].reserved1 = 0; + g_test_state.mtrr_physbases[0].base = 0x000000; + g_test_state.mtrr_physbases[0].reserved2 = 0; + g_test_state.mtrr_physmasks[0].reserved1 = 0; + g_test_state.mtrr_physmasks[0].v = 1; + g_test_state.mtrr_physmasks[0].mask = 0x800001; + g_test_state.mtrr_physmasks[0].reserved2 = 0; + + return !validate_mtrrs(&g_test_state); +} + +static bool UNIT_VM_IV_04(void) +{ + g_test_state.num_var_mtrrs = 1; + g_test_state.mtrr_physbases[0].type = MTRR_TYPE_UNCACHABLE; + g_test_state.mtrr_physbases[0].reserved1 = 0; + g_test_state.mtrr_physbases[0].base = 0x000000; + g_test_state.mtrr_physbases[0].reserved2 = 0; + g_test_state.mtrr_physmasks[0].reserved1 = 0; + g_test_state.mtrr_physmasks[0].v = 1; + g_test_state.mtrr_physmasks[0].mask = 0x000002; + g_test_state.mtrr_physmasks[0].reserved2 = 0; + + return !validate_mtrrs(&g_test_state); +} + +static bool UNIT_VM_IV_05(void) +{ + g_test_state.num_var_mtrrs = 1; + g_test_state.mtrr_physbases[0].type = MTRR_TYPE_UNCACHABLE; + g_test_state.mtrr_physbases[0].reserved1 = 0; + g_test_state.mtrr_physbases[0].base = 0x000000; + g_test_state.mtrr_physbases[0].reserved2 = 0; + g_test_state.mtrr_physmasks[0].reserved1 = 0; + g_test_state.mtrr_physmasks[0].v = 1; + g_test_state.mtrr_physmasks[0].mask = 0x00FF00; + g_test_state.mtrr_physmasks[0].reserved2 = 0; + + return !validate_mtrrs(&g_test_state); +} + +static bool UNIT_VM_IV_06(void) +{ + g_test_state.num_var_mtrrs = 1; + g_test_state.mtrr_physbases[0].type = MTRR_TYPE_UNCACHABLE; + g_test_state.mtrr_physbases[0].reserved1 = 0; + g_test_state.mtrr_physbases[0].base = 0x000000; + g_test_state.mtrr_physbases[0].reserved2 = 0; + g_test_state.mtrr_physmasks[0].reserved1 = 0; + g_test_state.mtrr_physmasks[0].v = 1; + g_test_state.mtrr_physmasks[0].mask = 0x400000; + g_test_state.mtrr_physmasks[0].reserved2 = 0; + + return !validate_mtrrs(&g_test_state); +} + +static bool UNIT_VM_IV_07(void) +{ + g_test_state.num_var_mtrrs = 2; + g_test_state.mtrr_physbases[0].type = MTRR_TYPE_UNCACHABLE; + g_test_state.mtrr_physbases[0].reserved1 = 0; + g_test_state.mtrr_physbases[0].base = 0x000000; + g_test_state.mtrr_physbases[0].reserved2 = 0; + g_test_state.mtrr_physmasks[0].reserved1 = 0; + g_test_state.mtrr_physmasks[0].v = 1; + g_test_state.mtrr_physmasks[0].mask = 0xFFF000; + g_test_state.mtrr_physmasks[0].reserved2 = 0; + g_test_state.mtrr_physbases[1].type = MTRR_TYPE_UNCACHABLE; + g_test_state.mtrr_physbases[1].reserved1 = 0; + g_test_state.mtrr_physbases[1].base = 0x001000; + g_test_state.mtrr_physbases[1].reserved2 = 0; + g_test_state.mtrr_physmasks[1].reserved1 = 0; + g_test_state.mtrr_physmasks[1].v = 1; + g_test_state.mtrr_physmasks[1].mask = 0xFFF0F0; + g_test_state.mtrr_physmasks[1].reserved2 = 0; + + return !validate_mtrrs(&g_test_state); +} + +static bool UNIT_VM_IV_08(void) +{ + g_test_state.num_var_mtrrs = 2; + g_test_state.mtrr_physbases[0].type = MTRR_TYPE_WRCOMB; + g_test_state.mtrr_physbases[0].reserved1 = 0; + g_test_state.mtrr_physbases[0].base = 0x000800; + g_test_state.mtrr_physbases[0].reserved2 = 0; + g_test_state.mtrr_physmasks[0].reserved1 = 0; + g_test_state.mtrr_physmasks[0].v = 1; + g_test_state.mtrr_physmasks[0].mask = 0xFFF000; + g_test_state.mtrr_physmasks[0].reserved2 = 0; + g_test_state.mtrr_physbases[1].type = MTRR_TYPE_WRTHROUGH; + g_test_state.mtrr_physbases[1].reserved1 = 0; + g_test_state.mtrr_physbases[1].base = 0x000800; + g_test_state.mtrr_physbases[1].reserved2 = 0; + g_test_state.mtrr_physmasks[1].reserved1 = 0; + g_test_state.mtrr_physmasks[1].v = 1; + g_test_state.mtrr_physmasks[1].mask = 0xFFF800; + g_test_state.mtrr_physmasks[1].reserved2 = 0; + + return !validate_mtrrs(&g_test_state); +} + +static bool UNIT_VM_IV_09(void) +{ + g_test_state.num_var_mtrrs = 3; + g_test_state.mtrr_physbases[0].type = MTRR_TYPE_UNCACHABLE; + g_test_state.mtrr_physbases[0].reserved1 = 0; + g_test_state.mtrr_physbases[0].base = 0x000800; + g_test_state.mtrr_physbases[0].reserved2 = 0; + g_test_state.mtrr_physmasks[0].reserved1 = 0; + g_test_state.mtrr_physmasks[0].v = 1; + g_test_state.mtrr_physmasks[0].mask = 0xFFF800; + g_test_state.mtrr_physmasks[0].reserved2 = 0; + g_test_state.mtrr_physbases[1].type = MTRR_TYPE_WRTHROUGH; + g_test_state.mtrr_physbases[1].reserved1 = 0; + g_test_state.mtrr_physbases[1].base = 0x000800; + g_test_state.mtrr_physbases[1].reserved2 = 0; + g_test_state.mtrr_physmasks[1].reserved1 = 0; + g_test_state.mtrr_physmasks[1].v = 1; + g_test_state.mtrr_physmasks[1].mask = 0xFFF000; + g_test_state.mtrr_physmasks[1].reserved2 = 0; + g_test_state.mtrr_physbases[2].type = MTRR_TYPE_WRPROT; + g_test_state.mtrr_physbases[2].reserved1 = 0; + g_test_state.mtrr_physbases[2].base = 0x000800; + g_test_state.mtrr_physbases[2].reserved2 = 0; + g_test_state.mtrr_physmasks[2].reserved1 = 0; + g_test_state.mtrr_physmasks[2].v = 1; + g_test_state.mtrr_physmasks[2].mask = 0xFFF000; + g_test_state.mtrr_physmasks[2].reserved2 = 0; + + return !validate_mtrrs(&g_test_state); +} + +void unit_test_validate_mtrrs(void) +{ + RUN_CASE(UNIT_VM_V_01 ); /* Zero items */ + RUN_CASE(UNIT_VM_V_02 ); /* 1 invalid item */ + RUN_CASE(UNIT_VM_V_03 ); /* 1 valid item. Whole region protected. */ + RUN_CASE(UNIT_VM_V_04 ); /* 1 valid item. 1 page protected. */ + RUN_CASE(UNIT_VM_V_05 ); /* 1 valid item. 2^n pages protected. */ + RUN_CASE(UNIT_VM_V_06 ); /* 2 valid item. 2^n pages protected. */ + RUN_CASE(UNIT_VM_V_07 ); /* 2 items, 1 valid, 1 invalid. 2^n pages protected. */ + RUN_CASE(UNIT_VM_V_08 ); /* 2 overlapped items, with same type. */ + RUN_CASE(UNIT_VM_V_09 ); /* 2 overlapped items, 1 MTRR_TYPE_UNCACHABLE (0) */ + RUN_CASE(UNIT_VM_V_10 ); /* 2 overlapped items, 1 MTRR_TYPE_WRTHROUGH (4), 1 MTRR_TYPE_WRBACK(6) */ + RUN_CASE(UNIT_VM_V_11 ); /* 3 overlapped items, 1 MTRR_TYPE_UNCACHABLE(0), 1 MTRR_TYPE_WRTHROUGH (4), 1 MTRR_TYPE_WRPROT(5) */ + RUN_CASE(UNIT_VM_IV_01); /* 17 items, should be larger than mtrr_cap.vcnt */ + RUN_CASE(UNIT_VM_IV_02); /* 1 valid item, non-contiguous case 1. */ + RUN_CASE(UNIT_VM_IV_03); /* 1 valid item, non-contiguous case 2. */ + RUN_CASE(UNIT_VM_IV_04); /* 1 valid item, non-contiguous case 3. */ + RUN_CASE(UNIT_VM_IV_05); /* 1 valid item, non-contiguous case 4. */ + RUN_CASE(UNIT_VM_IV_06); /* 1 valid item, non-contiguous case 5. */ + RUN_CASE(UNIT_VM_IV_07); /* 2 valid items. One with non-contiguous region. */ + RUN_CASE(UNIT_VM_IV_08); /* 2 overlapped items, 1 MTRR_TYPE_WRCOMB(1), 1 MTRR_TYPE_WRTHROUGH(4) */ + RUN_CASE(UNIT_VM_IV_09); /* 3 overlapped items, 1 MTRR_TYPE_UNCACHABLE(0), 1 MTRR_TYPE_WRTHROUGH (4), 1 MTRR_TYPE_WRPROT(5) */ +} + +#endif /* MTRR_VALIDATION_UNITTEST */ + +void restore_mtrrs(mtrr_state_t *saved_state) +{ + int ndx; + +#ifdef MTRR_VALIDATION_UNITTEST + unit_test_validate_mtrrs(); +#endif /* MTRR_VALIDATION_UNITTEST */ + + /* disable all MTRRs first */ + set_all_mtrrs(false); + + /* physmask's and physbase's */ + for ( ndx = 0; ndx < saved_state->num_var_mtrrs; ndx++ ) { + wrmsrl(MTRR_PHYS_MASK0_MSR + ndx*2, + saved_state->mtrr_physmasks[ndx].raw); + wrmsrl(MTRR_PHYS_BASE0_MSR + ndx*2, + saved_state->mtrr_physbases[ndx].raw); + } + + /* IA32_MTRR_DEF_TYPE MSR */ + wrmsrl(MSR_IA32_MTRR_DEF_TYPE, saved_state->mtrr_def_type.raw); +} + +/* + * set the memory type for specified range (base to base+size) + * to mem_type and everything else to UC + */ +bool set_mem_type(void *base, uint32_t size, uint32_t mem_type) +{ + int num_pages; + int ndx; + mtrr_def_type_t mtrr_def_type; + mtrr_cap_t mtrr_cap; + mtrr_physmask_t mtrr_physmask; + mtrr_physbase_t mtrr_physbase; + + /* + * disable all fixed MTRRs + * set default type to UC + */ + rdmsrl(MSR_IA32_MTRR_DEF_TYPE, mtrr_def_type.raw); + mtrr_def_type.fe = 0; + mtrr_def_type.type = MTRR_TYPE_UNCACHABLE; + wrmsrl(MSR_IA32_MTRR_DEF_TYPE, mtrr_def_type.raw); + + /* + * initially disable all variable MTRRs (we'll enable the ones we use) + */ + rdmsrl(MSR_IA32_MTRRCAP, mtrr_cap.raw); + for ( ndx = 0; ndx < mtrr_cap.vcnt; ndx++ ) { + rdmsrl(MTRR_PHYS_MASK0_MSR + ndx*2, mtrr_physmask.raw); + mtrr_physmask.v = 0; + wrmsrl(MTRR_PHYS_MASK0_MSR + ndx*2, mtrr_physmask.raw); + } + + /* + * map all AC module pages as mem_type + */ + + num_pages = (size + PAGE_SIZE) >> PAGE_SHIFT; + ndx = 0; + + printk("setting MTRRs for acmod: base=%p, size=%x, num_pages=%d\n", + base, size, num_pages); + + while ( num_pages > 0 ) { + uint32_t pages_in_range; + + /* set the base of the current MTRR */ + rdmsrl(MTRR_PHYS_BASE0_MSR + ndx*2, mtrr_physbase.raw); + mtrr_physbase.base = (unsigned long)base >> PAGE_SHIFT; + mtrr_physbase.type = mem_type; + wrmsrl(MTRR_PHYS_BASE0_MSR + ndx*2, mtrr_physbase.raw); + + /* + * calculate MTRR mask + * MTRRs can map pages in power of 2 + * may need to use multiple MTRRS to map all of region + */ + pages_in_range = 1 << (fls(num_pages) - 1); + + rdmsrl(MTRR_PHYS_MASK0_MSR + ndx*2, mtrr_physmask.raw); + mtrr_physmask.mask = ~(pages_in_range - 1); + mtrr_physmask.v = 1; + wrmsrl(MTRR_PHYS_MASK0_MSR + ndx*2, mtrr_physmask.raw); + + /* prepare for the next loop depending on number of pages + * We figure out from the above how many pages could be used in this + * mtrr. Then we decrement the count, increment the base, + * increment the mtrr we are dealing with, and if num_pages is + * still not zero, we do it again. + */ + base += (pages_in_range * PAGE_SIZE); + num_pages -= pages_in_range; + ndx++; + if ( ndx == mtrr_cap.vcnt ) { + printk("exceeded number of var MTRRs when mapping range\n"); + return false; + } + } + + return true; +} + +/* enable/disable all MTRRs */ +void set_all_mtrrs(bool enable) +{ + mtrr_def_type_t mtrr_def_type; + + rdmsrl(MSR_IA32_MTRR_DEF_TYPE, mtrr_def_type.raw); + mtrr_def_type.e = enable ? 1 : 0; + wrmsrl(MSR_IA32_MTRR_DEF_TYPE, mtrr_def_type.raw); +} + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff -r de942f6fa6b3 -r 5648dc802679 sboot/txt/txt.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sboot/txt/txt.c Tue Aug 28 16:27:50 2007 -0700 @@ -0,0 +1,725 @@ +/* + * txt.c: Intel(r) TXT support functions, including initiating measured + * launch, post-launch, AP wakeup, etc. + * + * Copyright (c) 2003-2007, 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern char _start[]; /* start of module */ +extern char _end[]; /* end of module */ +extern char _mle_start[]; /* start of text section */ +extern char _mle_end[]; /* end of text section */ +extern char __start[]; /* sboot entry point in boot.S */ +extern char _txt_wakeup[]; /* RLP join address for GETSEC[WAKEUP] */ + +/* MLE/kernel shared data page (in boot.S) */ +extern mle_kernel_shared_t _mle_kernel_shared; + +/* + * this is the structure whose addr we'll put in TXT heap + * it needs to be within the MLE pages, so force it to the .text section + */ +static const mle_hdr_t g_mle_hdr +__attribute__ ((__section__ (".text"))) = { + guid : MLE_HDR_GUID, + length : sizeof(mle_hdr_t), + version : 0x00010001, + entry_point : (uint32_t)&__start - SBOOT_BASE_ADDR, + first_valid_page : 0, + mle_start_off : (uint32_t)&_mle_start - SBOOT_BASE_ADDR, + mle_end_off : (uint32_t)&_mle_end - SBOOT_BASE_ADDR, +}; + +static void print_file_info(void) +{ + printk("file addresses:\n"); + printk("\t &_start=%p\n", &_start); + printk("\t &_end=%p\n", &_end); + printk("\t &_mle_start=%p\n", &_mle_start); + printk("\t &_mle_end=%p\n", &_mle_end); + printk("\t &__start=%p\n", &__start); + printk("\t &_txt_wakeup=%p\n", &_txt_wakeup); + printk("\t &g_mle_hdr=%p\n", &g_mle_hdr); +} + +static void print_mle_hdr(const mle_hdr_t *mle_hdr) +{ + printk("MLE header:\n"); + printk("\t guid="); print_uuid((uuid_t *)mle_hdr->guid); printk("\n"); + printk("\t length=%x\n", mle_hdr->length); + printk("\t version=%08x\n", mle_hdr->version); + printk("\t entry_point=%08x\n", mle_hdr->entry_point); + printk("\t first_valid_page=%08x\n", mle_hdr->first_valid_page); + printk("\t mle_start_off=%x\n", mle_hdr->mle_start_off); + printk("\t mle_end_off=%x\n", mle_hdr->mle_end_off); +} + +/* + * build_mle_pagetable() + */ + +/* page dir/table entry is phys addr + P + R/W + PWT */ +#define MAKE_PDTE(addr) (((uint64_t)(unsigned long)(addr) & PAGE_MASK) | 0x01) + +/* we assume/know that our image is <2MB and thus fits w/in a single */ +/* PT (512*4KB = 2MB) and thus fixed to 1 pg dir ptr and 1 pgdir and */ +/* 1 ptable = 3 pages and just 1 loop loop for ptable MLE page table */ +/* can only contain 4k pages */ + +static void *build_mle_pagetable(uint32_t mle_start, uint32_t mle_size) +{ + void *ptab_base; + uint32_t ptab_size, mle_off; + void *pg_dir_ptr_tab, *pg_dir, *pg_tab; + uint64_t *pte; + + printk("MLE start=%x, end=%x, size=%x\n", mle_start, mle_start+mle_size, + mle_size); + if ( mle_size > 512*PAGE_SIZE ) { + printk("MLE size too big for single page table\n"); + return NULL; + } + + + /* should start on page boundary */ + if ( mle_start & ~PAGE_MASK ) { + printk("MLE start is not page-aligned\n"); + return NULL; + } + + /* place ptab_base below MLE */ + ptab_size = 3 * PAGE_SIZE; /* pgdir ptr + pgdir + ptab = 3 */ + ptab_base = (void *)((mle_start - ptab_size) & PAGE_MASK); + memset(ptab_base, 0, ptab_size); + printk("ptab_size=%x, ptab_base=%p\n", ptab_size, ptab_base); + + pg_dir_ptr_tab = ptab_base; + pg_dir = pg_dir_ptr_tab + PAGE_SIZE; + pg_tab = pg_dir + PAGE_SIZE; + + /* only use first entry in page dir ptr table */ + *(uint64_t *)pg_dir_ptr_tab = MAKE_PDTE(pg_dir); + + /* only use first entry in page dir */ + *(uint64_t *)pg_dir = MAKE_PDTE(pg_tab); + + pte = pg_tab; + mle_off = 0; + do { + *pte = MAKE_PDTE(mle_start + mle_off); + + pte++; + mle_off += PAGE_SIZE; + } while ( mle_off < mle_size ); + + return ptab_base; +} + +/* size can be NULL */ +static bool find_sinit(multiboot_info_t *mbi, void **base, uint32_t *size) +{ + module_t *mods; + uint32_t size2 = 0; + void *base2 = NULL; + int i; + + if ( base == NULL ) { + printk("find_sinit() base is NULL\n"); + return false; + } + *base = NULL; + if ( size != NULL ) + *size = 0; + + if ( mbi->mods_addr == 0 || mbi->mods_count == 0 ) { + printk("no module info\n"); + return false; + } + + mods = (module_t *)(mbi->mods_addr); + for ( i = mbi->mods_count - 1; i > 0; i-- ) { + base2 = (void *)mods[i].mod_start; + size2 = mods[i].mod_end - (unsigned long)(base2); + /* check if this is really an SINIT AC module */ + if ( is_sinit_acmod(base2, size2) ) + break; + } + /* not found */ + if ( i == 0 ) { + printk("no SINIT AC module found\n"); + return false; + } + printk("user-provided SINIT found: %s\n", (const char *)mods[i].string); + + *base = base2; + if ( size != NULL ) + *size = size2; + return true; +} + +/* TBD: this should come from lcp header and file */ +static uuid_t LCP_UUID = { 0xab0d1925, 0xeee7, 0x48eb, 0xa9fc, + { 0xb, 0xac, 0x5a, 0x26, 0x2d, 0xe }}; + +static bool find_lcp_manifest(multiboot_info_t *mbi, void **base, + uint32_t *size) +{ + module_t *mods; + uint32_t size2 = 0; + void *base2 = NULL; + int i; + + if ( base == NULL ) { + printk("find_lcp_manifest() base is NULL\n"); + return false; + } + *base = NULL; + if ( size != NULL ) + *size = 0; + + if ( mbi->mods_addr == 0 || mbi->mods_count == 0 ) { + printk("no module info\n"); + return false; + } + + mods = (module_t *)(mbi->mods_addr); + for ( i = mbi->mods_count - 1; i > 0; i-- ) { + base2 = (void *)mods[i].mod_start; + size2 = mods[i].mod_end - (unsigned long)(base2); + /* LCP manifest UUID is at beginning of file */ + if ( are_uuids_equal(base2, &LCP_UUID) ) + break; + } + /* not found */ + if ( i == 0 ) { + printk("no LCP manifest found\n"); + return false; + } + printk("LCP manifest found: %s\n", (const char *)mods[i].string); + + *base = base2; + if ( size != NULL ) + *size = size2; + return true; +} + +/* + * sets up TXT heap + */ +static txt_heap_t *init_txt_heap(void *ptab_base, acm_hdr_t *sinit, + multiboot_info_t *mbi) +{ + txt_heap_t *txt_heap; + os_sinit_data_t *os_sinit_data; + os_mle_data_t *os_mle_data; + uint64_t *size; + uint32_t os_sinit_data_ver; + uint64_t max_ram; + void *lcp_base = NULL; + uint32_t lcp_size = 0; + + txt_heap = get_txt_heap(); + + /* + * BIOS to OS/loader data already setup by BIOS + */ + if ( !verify_txt_heap(txt_heap, true) ) + return NULL; + + /* + * OS/loader to MLE data + */ + os_mle_data = get_os_mle_data_start(txt_heap); + size = (uint64_t *)((uint32_t)os_mle_data - sizeof(uint64_t)); + *size = sizeof(*os_mle_data) + sizeof(uint64_t); + memset(os_mle_data, 0, sizeof(*os_mle_data)); + os_mle_data->version = 0x01; + os_mle_data->mbi = mbi; + + /* + * OS/loader to SINIT data + */ + os_sinit_data_ver = get_supported_os_sinit_data_ver(sinit); + printk("SINIT supports os_sinit_data version %x\n", + os_sinit_data_ver); + /* warn if SINIT supports more recent OS to SINIT data version than us */ + if ( os_sinit_data_ver > 0x03 ) + printk("SINIT's os_sinit_data version unsupported (%x)\n", + os_sinit_data_ver); + os_sinit_data = get_os_sinit_data_start(txt_heap); + size = (uint64_t *)((uint32_t)os_sinit_data - sizeof(uint64_t)); + *size = sizeof(*os_sinit_data) + sizeof(uint64_t); + memset(os_sinit_data, 0, sizeof(*os_sinit_data)); + /* common to all versions */ + /* this is phys addr */ + os_sinit_data->mle_ptab = (uint64_t)(unsigned long)ptab_base; + os_sinit_data->mle_size = g_mle_hdr.mle_end_off - g_mle_hdr.mle_start_off; + /* this is linear addr (offset from MLE base) of mle header */ + os_sinit_data->mle_hdr_base = (uint64_t)&g_mle_hdr - (uint64_t)&_mle_start; + /* SINIT supports more recent version than we do, so use our most recent */ + if ( os_sinit_data_ver >= 0x03 ) { + os_sinit_data->version = 0x03; /* 0x03 is the max we support */ + + max_ram = get_max_ram(mbi); + if ( max_ram == 0 ) { + printk("max_ram is 0\n"); + return NULL; + } + + set_vtd_pmrs(os_sinit_data, max_ram); + + find_lcp_manifest(mbi, &lcp_base, &lcp_size); + os_sinit_data->v3.lcp_po_base = (unsigned long)lcp_base; + os_sinit_data->v3.lcp_po_size = lcp_size; + } + else + os_sinit_data->version = 0x01; + print_os_sinit_data(os_sinit_data); + + /* + * SINIT to MLE data will be setup by SINIT + */ + + return txt_heap; +} + +static void txt_wakeup_cpus(void) +{ + struct { + uint16_t limit; + uint32_t base; + } gdt; + uint16_t cs; + mle_join_t mle_join; + + /* RLPs will use our GDT and CS */ + __asm__ __volatile__ ("sgdt (%0) \n" :: "a"(&gdt) : "memory"); + __asm__ __volatile__ ("mov %%cs, %0\n" : "=r"(cs)); + + mle_join.entry_point = (uint32_t)(unsigned long)&_txt_wakeup; + mle_join.seg_sel = cs; + mle_join.gdt_base = gdt.base; + mle_join.gdt_limit = gdt.limit; + + printk("mle_join.entry_point = %x\n", mle_join.entry_point); + printk("mle_join.seg_sel = %x\n", mle_join.seg_sel); + printk("mle_join.gdt_base = %x\n", mle_join.gdt_base); + printk("mle_join.gdt_limit = %x\n", mle_join.gdt_limit); + + write_priv_config_reg(TXTCR_MLE_JOIN, (uint64_t)(unsigned long)&mle_join); + + printk("joining RLPs to MLE with GETSEC[WAKEUP]\n"); + __getsec_wakeup(); + + /* TBD: maybe need wait here for AP to launch mini guest */ + + printk("GETSEC[WAKEUP] completed\n"); +} + +bool txt_is_launched(void) +{ + txt_sts_t sts; + + sts._raw = read_pub_config_reg(TXTCR_STS); + + return sts.senter_done_sts; +} + +bool txt_launch_environment(multiboot_info_t *mbi) +{ + acm_hdr_t *sinit; + void *mle_ptab_base; + os_mle_data_t *os_mle_data; + txt_heap_t *txt_heap; + + /* + * find SINIT AC module in modules list (it should be one of last three) + */ + if ( !find_sinit(mbi, (void **)&sinit, NULL) ) + return false; + + /* check if it matches chipset */ + if ( !does_acmod_match_chipset(sinit) ) { + printk("SINIT does not match chipset\n"); + return false; + } + + /* if it is newer than BIOS-provided version, then copy it to */ + /* BIOS reserved region */ + sinit = copy_sinit(sinit); + if ( sinit == NULL ) + return false; + /* do some checks on it */ + if ( !verify_acmod(sinit) ) + return false; + + /* print some debug info */ + print_file_info(); + print_mle_hdr(&g_mle_hdr); + + /* create MLE page table */ + mle_ptab_base = build_mle_pagetable( + g_mle_hdr.mle_start_off + SBOOT_BASE_ADDR, + g_mle_hdr.mle_end_off - g_mle_hdr.mle_start_off); + if ( mle_ptab_base == NULL ) + return false; + + /* initialize TXT heap */ + txt_heap = init_txt_heap(mle_ptab_base, sinit, mbi); + + /* save MTRRs before we alter them for SINIT launch */ + os_mle_data = get_os_mle_data_start(txt_heap); + save_mtrrs(&(os_mle_data->saved_mtrr_state)); + + /* set MTRRs properly for AC module (SINIT) */ + set_mtrrs_for_acmod(sinit); + + printk("executing GETSEC[SENTER]...\n"); + __getsec_senter((uint32_t)sinit, (sinit->size)*4); + printk("ERROR--we should not get here!\n"); + return false; +} + +bool txt_prepare_platform(void) +{ + tpm_unit_test_before_senter(); + if ( is_tpm_ready(0) ) + return true; + else + return false; +} + +bool txt_prepare_cpu(void) +{ + unsigned long eflags, cr0; + uint64_t mcg_cap, mcg_stat; + int i; + + /* must be running at CPL 0 => this is implicit in even getting this far */ + /* since our bootstrap code loads a GDT, etc. */ + + cr0 = read_cr0(); + + /* must be in protected mode */ + if ( !(cr0 & X86_CR0_PE) ) { + printk("ERR: not in protected mode\n"); + return false; + } + + /* cache must be enabled (CR0.CD = CR0.NW = 0) */ + if ( cr0 & X86_CR0_CD ) { + printk("CR0.CD set\n"); + cr0 &= ~X86_CR0_CD; + } + if ( cr0 & X86_CR0_NW ) { + printk("CR0.NW set\n"); + cr0 &= ~X86_CR0_NW; + } + + /* native FPU error reporting must be enabled for proper */ + /* interaction behavior */ + if ( !(cr0 & X86_CR0_NE) ) { + printk("CR0.NE not set\n"); + cr0 |= X86_CR0_NE; + } + + write_cr0(cr0); + + /* cannot be in virtual-8086 mode (EFLAGS.VM=1) */ + __save_flags(eflags); + if ( eflags & X86_EFLAGS_VM ) { + printk("EFLAGS.VM set\n"); + __restore_flags(eflags | ~X86_EFLAGS_VM); + } + + printk("CR0 and EFLAGS OK\n"); + + /* + * verify that we're not already in a protected environment + */ + if ( txt_is_launched() ) { + printk("already in protected environment\n"); + return false; + } + + /* + * verify all machine check status registers are clear + */ + + /* no machine check in progress (IA32_MCG_STATUS.MCIP=1) */ + rdmsrl(MSR_IA32_MCG_STATUS, mcg_stat); + if ( mcg_stat & 0x04 ) { + printk("machine check in progress\n"); + return false; + } + + /* all machine check regs are clear */ + rdmsrl(MSR_IA32_MCG_CAP, mcg_cap); + for (i = 0; i < (mcg_cap & 0xff); i++) { + rdmsrl(MSR_IA32_MC0_STATUS + 4*i, mcg_stat); + if ( mcg_stat & (1ULL << 63) ) { + printk("MCG[%d] = %Lx ERROR\n", i, mcg_stat); + return false; + } + } + + printk("no machine check errors\n"); + + /* all is well with the processor state */ + printk("CPU is ready for SENTER\n"); + + return true; +} + +bool txt_post_launch(void) +{ + txt_heap_t *txt_heap; + os_mle_data_t *os_mle_data; + + /* TBD: need to handle this gracefully */ + if ( !txt_post_launch_verify_platform() ) + printk("failed to verify platform\n"); + + /* clear error config registers so that we start fresh */ + write_priv_config_reg(TXTCR_ERRORCODE, 0x00000000); + write_priv_config_reg(TXTCR_ESTS, 0xffffffff); /* write 1's to clear */ + + tpm_unit_test_after_senter(); + + /* always set the LT.CMD.SECRETS flag */ + write_priv_config_reg(TXTCR_CMD_SECRETS, 0x01); + read_priv_config_reg(TXTCR_E2STS); /* just a fence, so ignore return */ + printk("set LT.CMD.SECRETS flag\n"); + + /* get saved OS state (os_mvmm_data_t) from LT heap */ + txt_heap = get_txt_heap(); + os_mle_data = get_os_mle_data_start(txt_heap); + + /* restore pre-SENTER MTRRs that were overwritten for SINIT launch */ + /* TBD: we need to validate these before restoring them */ + restore_mtrrs(&(os_mle_data->saved_mtrr_state)); + + /* open TPM locality 1 */ + write_priv_config_reg(TXTCR_CMD_OPEN_LOCALITY1, 0x01); + read_priv_config_reg(TXTCR_E2STS); /* just a fence, so ignore return */ + printk("opened TPM locality 1\n"); + + /* bring RLPs into environment */ + txt_wakeup_cpus(); + + /* enable SMIs (do this after APs have been awakened and sync'ed w/ BSP) */ + printk("enabling SMIs on BSP\n"); + __getsec_smctrl(); + + return true; +} + +void txt_cpu_wakeup(uint32_t cpuid) +{ + txt_heap_t *txt_heap; + os_mle_data_t *os_mle_data; + + printk("cpu %x waking up from TXT sleep\n", cpuid); + + txt_heap = get_txt_heap(); + os_mle_data = get_os_mle_data_start(txt_heap); + + /* apply (validated) (pre-SENTER) MTRRs from BSP to each AP */ + restore_mtrrs(&(os_mle_data->saved_mtrr_state)); + + /* enable SMIs */ + printk("enabling SMIs on cpu %x\n", cpuid); + __getsec_smctrl(); + + handle_init_sipi_sipi(); +} + +bool txt_protect_mem_regions(void) +{ + uint64_t base, size; + txt_heap_t* txt_heap; + sinit_mle_data_t *sinit_mle_data; + sinit_mdr_t *mdrs_base; + uint32_t num_mdrs; + + /* + * TXT has 2 regions of RAM that need to be reserved for use by only the + * hypervisor; not even dom0 should have access: + * TXT heap, SINIT AC module + */ + + /* TXT heap */ + base = read_pub_config_reg(TXTCR_HEAP_BASE); + size = read_pub_config_reg(TXTCR_HEAP_SIZE); + printk("protecting TXT heap (%Lx - %Lx) in e820 table\n", base, + (base + size - 1)); + if ( !e820_protect_region(base, size, E820_UNUSABLE) ) + return false; + + /* SINIT */ + base = read_pub_config_reg(TXTCR_SINIT_BASE); + size = read_pub_config_reg(TXTCR_SINIT_SIZE); + printk("protecting SINIT (%Lx - %Lx) in e820 table\n", base, + (base + size - 1)); + if ( !e820_protect_region(base, size, E820_UNUSABLE) ) + return false; + + /* ensure that memory not marked as good RAM by the MDRs is RESERVED in + the e820 table */ + txt_heap = get_txt_heap(); + sinit_mle_data = get_sinit_mle_data_start(txt_heap); + if ( sinit_mle_data->version <= 0x01 ) { + num_mdrs = sinit_mle_data->v1.num_mdrs; + mdrs_base = sinit_mle_data->v1.mdrs; + } + else { + num_mdrs = sinit_mle_data->v5.num_mdrs; + mdrs_base = (sinit_mdr_t *)(((void *)sinit_mle_data - + sizeof(uint64_t)) + + sinit_mle_data->v5.mdrs_off); + } + printk("verifying e820 table against SINIT MDRs: "); + if ( !verify_e820_map(mdrs_base, num_mdrs) ) { + printk("verification failed.\n"); + return false; + } + printk("verification succeeded.\n"); + +#if 0 + /* + * some BIOSes mark the TPM and LT public space as reserved + * this will cause xen to give these regions to dom_io instead of dom0 + * however, if we mark them as protected, then they won't be given + * to dom_io and dom0 will be allowed to map them as MMIO (not as RAM) + * this should have no effect on system's whose BIOS doesn't do this + */ + + /* TPM localities */ + base = TPM_LOCALITY_BASE; + size = NR_TPM_LOCALITY_PAGES * PAGE_SIZE; + printk("protecting TPM localities (%Lx - %Lx) in e820 table\n", + base, (base + size - 1)); + if ( !e820_protect_region(base, size, E820_PROTECTED) ) + return false; + + /* TXT public space */ + base = TXT_PUB_CONFIG_REGS_BASE; + size = NR_TXT_CONFIG_PAGES * PAGE_SIZE; + printk("protecting TXT Public Space (%Lx - %Lx) in e820 table\n", + base, (base + size - 1)); + if ( !e820_protect_region(base, size, E820_PROTECTED) ) + return false; +#endif + + /* TXT private space */ + base = TXT_PRIV_CONFIG_REGS_BASE; + size = NR_TXT_CONFIG_PAGES * PAGE_SIZE; + printk("protecting TXT Private Space (%Lx - %Lx) in e820 table\n", + base, (base + size - 1)); + if ( !e820_protect_region(base, size, E820_UNUSABLE) ) + return false; + + return true; +} + +void txt_shutdown(void) +{ + /* + * before removing isolation protections (e.g. VMX) on any processor + * we need to return the env to a "safe" state (e.g. clear any secrets, + * cap PCRs, etc.) + */ + + /* scrub any secrets by clearing their memory, then flush cache */ + /* we don't have any secrets to scrub, however */ + ; + + /* TBD: cap dynamic PCRs (17, 18) */ + + /* set LT.CMD.NO-SECRETS flag (i.e. clear SECRETS flag) */ + write_priv_config_reg(TXTCR_CMD_NO_SECRETS, 0x01); + read_priv_config_reg(TXTCR_E2STS); /* fence */ + printk("secrets flag cleared\n"); + + /* close TXT private config space */ + read_priv_config_reg(TXTCR_E2STS); /* fence */ + write_priv_config_reg(TXTCR_CMD_CLOSE_PRIVATE, 0x01); + read_pub_config_reg(TXTCR_E2STS); /* fence */ + printk("private config space closed\n"); + + /* SMXE may not be enabled any more, so set it to make sure */ + write_cr4(read_cr4() | X86_CR4_SMXE); + + /* call GETSEC[SEXIT] */ + printk("executing GETSEC[SEXIT]...\n"); + __getsec_sexit(); + printk("measured environment torn down\n"); + + /* machine shutdown */ + shutdown_system(_mle_kernel_shared.shutdown_type); +} + + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff -r de942f6fa6b3 -r 5648dc802679 sboot/txt/verify.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sboot/txt/verify.c Tue Aug 28 16:27:50 2007 -0700 @@ -0,0 +1,711 @@ +/* + * verify.c: verify that platform and processor supports Intel(r) TXT + * + * Copyright (c) 2003-2007, 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern char _start[]; /* start of module */ +extern char _end[]; /* end of module */ + +/* + * CPUID extended feature info + */ +static unsigned int g_cpuid_ext_feat_info; + +/* + * IA32_FEATURE_CONTROL_MSR + */ +static unsigned long g_feat_ctrl_msr; + + +static void read_processor_info(void) +{ + unsigned long f1, f2; + + /* is CPUID supported? */ + /* (it's supported if ID flag in EFLAGS can be set and cleared) */ + asm("pushf\n\t" + "pushf\n\t" + "pop %0\n\t" + "mov %0,%1\n\t" + "xor %2,%0\n\t" + "push %0\n\t" + "popf\n\t" + "pushf\n\t" + "pop %0\n\t" + "popf\n\t" + : "=&r" (f1), "=&r" (f2) + : "ir" (X86_EFLAGS_ID)); + if ( ((f1^f2) & X86_EFLAGS_ID) == 0 ) { + g_cpuid_ext_feat_info = 0; + return; + } + + g_cpuid_ext_feat_info = cpuid_ecx(1); + + rdmsrl(IA32_FEATURE_CONTROL_MSR, g_feat_ctrl_msr); +} + +static bool supports_vmx(void) +{ + /* check that processor supports VMX instructions */ + if ( !(g_cpuid_ext_feat_info & bitmaskof(X86_FEATURE_VMXE)) ) { + printk("ERR: CPU does not support VMX\n"); + return false; + } + printk("CPU is VMX-capable\n"); + + /* and that VMX is enabled in the feature control MSR */ + if ( !(g_feat_ctrl_msr & IA32_FEATURE_CONTROL_MSR_ENABLE_VMX_IN_SMX) ) { + printk("ERR: VMXON disabled by feature control MSR (%lx)\n", + g_feat_ctrl_msr); + return false; + } + + return true; +} + +static bool supports_smx(void) +{ + /* check that processor supports SMX instructions */ + if ( !(g_cpuid_ext_feat_info & bitmaskof(X86_FEATURE_SMXE)) ) { + printk("ERR: CPU does not support SMX\n"); + return false; + } + printk("CPU is SMX-capable\n"); + + /* + * and that SMX is enabled in the feature control MSR + */ + + /* check that the MSR is locked -- BIOS should always lock it */ + if ( !(g_feat_ctrl_msr & IA32_FEATURE_CONTROL_MSR_LOCK) ) { + printk("ERR: IA32_FEATURE_CONTROL_MSR_LOCK is not locked\n"); + /* in general this should not happen, as BIOS is required to lock */ + /* the MSR; but it may be desirable to allow it sometimes */ +#ifdef PERMISSIVE_BOOT + /* we enable VMX outside of SMX as well so that if there was some */ + /* error in the TXT boot, VMX will continue to work */ + g_feat_ctrl_msr |= IA32_FEATURE_CONTROL_MSR_ENABLE_VMX_IN_SMX | + IA32_FEATURE_CONTROL_MSR_ENABLE_VMX_OUT_SMX | + IA32_FEATURE_CONTROL_MSR_ENABLE_SENTER | + IA32_FEATURE_CONTROL_MSR_SENTER_PARAM_CTL | + IA32_FEATURE_CONTROL_MSR_LOCK; + wrmsrl(IA32_FEATURE_CONTROL_MSR, g_feat_ctrl_msr); + return true; +#else + return false; +#endif + } + + /* check that SENTER (w/ full params) is enabled */ + if ( !(g_feat_ctrl_msr & (IA32_FEATURE_CONTROL_MSR_ENABLE_SENTER | + IA32_FEATURE_CONTROL_MSR_SENTER_PARAM_CTL)) ) { + printk("ERR: SENTER disabled by feature control MSR (%lx)\n", + g_feat_ctrl_msr); + return false; + } + + return true; +} + +static bool supports_txt(void) +{ + capabilities_t cap; + + /* processor must support SMX */ + if ( !supports_smx() || !supports_vmx() ) + return false; + + /* testing for chipset support requires enabling SMX on the processor */ + write_cr4(read_cr4() | X86_CR4_SMXE); + printk("SMX is enabled\n"); + + /* + * verify that an TXT-capable chipset is present and + * check that all needed SMX capabilities are supported + */ + + cap = __getsec_capabilities(0); + if ( cap.chipset_present ) { + if ( cap.senter && cap.sexit && cap.parameters && cap.smctrl && + cap.wakeup ) { + printk("TXT chipset and all needed capabilities present\n"); + return true; + } + else + printk("ERR: insufficient SMX capabilities (%x)\n", cap._raw); + } + else + printk("ERR: TXT-capable chipset not present\n"); + + /* since we are failing, we should clear the SMX flag */ + write_cr4(read_cr4() & ~X86_CR4_SMXE); + + return false; +} + +static void print_bios_os_data(bios_os_data_t *bios_os_data) +{ + printk("bios_os_data:\n"); + printk("\t version=%x\n", bios_os_data->version); + printk("\t bios_sinit_size=%x\n", bios_os_data->bios_sinit_size); + if ( bios_os_data->version >= 0x02 ) { + printk("\t lcp_pd_base=%Lx\n", bios_os_data->v2.lcp_pd_base); + printk("\t lcp_pd_size=%Lx\n", bios_os_data->v2.lcp_pd_size); + printk("\t num_logical_procs=%x\n", + bios_os_data->v2.num_logical_procs); + } +} + +static bool verify_bios_os_data(txt_heap_t *txt_heap) +{ + uint64_t size, heap_size; + bios_os_data_t *bios_os_data; + + /* check size */ + heap_size = read_priv_config_reg(TXTCR_HEAP_SIZE); + size = get_bios_os_data_size(txt_heap); + if ( size == 0 ) { + printk("BIOS to OS data size is 0\n"); + return false; + } + if ( size > heap_size ) { + printk("BIOS to OS data size is larger than heap size " + "(%Lx, heap size=%Lx)\n", size, heap_size); + return false; + } + + bios_os_data = get_bios_os_data_start(txt_heap); + + /* check version */ + /* we assume backwards compatibility but print a warning */ + if ( bios_os_data->version > 0x02 ) + printk("unsupported BIOS to OS data version (%x)\n", + bios_os_data->version); + + /* no field checks (bios_sinit_size field can be 0) */ + + print_bios_os_data(bios_os_data); + + return true; +} + +static void print_os_mle_data(os_mle_data_t *os_mle_data) +{ + printk("os_mle_data:\n"); + printk("\t version=%x\n", os_mle_data->version); + /* TBD: perhaps eventually print saved_mtrr_state field */ + printk("\t mbi=%p\n", os_mle_data->mbi); +} + +static bool verify_os_mle_data(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("OS to MLE data size is 0\n"); + return false; + } + if ( size > heap_size ) { + printk("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) ) { + printk("OS to MLE data size (%Lx) is smaller than " + "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 != 0x01 ) { + printk("unsupported OS to MLE data version (%x)\n", + os_mle_data->version); + return false; + } + + /* field checks */ + if ( os_mle_data->mbi == NULL ) { + printk("OS to MLE data mbi field is NULL\n"); + return false; + } + + print_os_mle_data(os_mle_data); + + return true; +} + +void print_os_sinit_data(os_sinit_data_t *os_sinit_data) +{ + printk("os_sinit_data:\n"); + printk("\t version=%x\n", os_sinit_data->version); + printk("\t mle_ptab=%Lx\n", os_sinit_data->mle_ptab); + printk("\t mle_size=%Lx\n", os_sinit_data->mle_size); + printk("\t mle_hdr_base=%Lx\n", os_sinit_data->mle_hdr_base); + if ( os_sinit_data->version >= 0x03 ) { + printk("\t vtd_pmr_lo_base=%Lx\n", os_sinit_data->v3.vtd_pmr_lo_base); + printk("\t vtd_pmr_lo_size=%Lx\n", os_sinit_data->v3.vtd_pmr_lo_size); + printk("\t vtd_pmr_hi_base=%Lx\n", os_sinit_data->v3.vtd_pmr_hi_base); + printk("\t vtd_pmr_hi_size=%Lx\n", os_sinit_data->v3.vtd_pmr_hi_size); + printk("\t lcp_po_base=%Lx\n", os_sinit_data->v3.lcp_po_base); + printk("\t lcp_po_size=%Lx\n", os_sinit_data->v3.lcp_po_size); + } +} + +static bool verify_os_sinit_data(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("OS to SINIT data size is 0\n"); + return false; + } + if ( size > heap_size ) { + printk("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 */ + if ( os_sinit_data->version > 0x03 ) { + printk("unsupported OS to SINIT data version (%x)\n", + os_sinit_data->version); + return false; + } + + /* if it is version 0x01 then none of the fields get used post-launch */ + /* so no need to verify them (SINIT will have done that) */ + if ( os_sinit_data->version > 0x01 ) { + /* only check minimal size */ + if ( size < sizeof(os_sinit_data_t) ) { + printk("OS to SINIT data size (%Lx) is smaller than " + "os_sinit_data_t (%x)\n", size, sizeof(os_sinit_data_t)); + return false; + } + } + + print_os_sinit_data(os_sinit_data); + + return true; +} + +static void print_sinit_mdrs(sinit_mdr_t mdrs[], uint32_t num_mdrs) +{ + int i; + static char *mem_types[] = {"GOOD", "SMRAM OVERLAY", "SMRAM NON-OVERLAY", + "PCIE EXTENDED CONFIG", "PROTECTED"}; + + printk("\t sinit_mdrs:\n"); + for ( i = 0; i < num_mdrs; i++ ) { + printk("\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("(%s)\n", mem_types[mdrs[i].mem_type]); + else + printk("(%d)\n", (int)mdrs[i].mem_type); + } +} + +static void print_hash(sha1_hash_t hash) +{ + int i; + + for ( i = 0; i < SHA1_SIZE; i++ ) + printk("%02x ", hash[i]); +} + +static void print_sinit_mle_data(sinit_mle_data_t *sinit_mle_data) +{ + printk("sinit_mle_data:\n"); + printk("\t version=%x\n", sinit_mle_data->version); + if ( sinit_mle_data->version == 0x01 ) + print_sinit_mdrs(sinit_mle_data->v1.mdrs, sinit_mle_data->v1.num_mdrs); + else if ( sinit_mle_data->version >= 0x05 ) { + printk("\t bios_acm_id=\n\t"); + print_hash(sinit_mle_data->v5.bios_acm_id); printk("\n"); + printk("\t edx_senter_flags=%x\n", + sinit_mle_data->v5.edx_senter_flags); + printk("\t mseg_valid=%Lx\n", sinit_mle_data->v5.mseg_valid); + printk("\t sinit_hash=\n\t"); + print_hash(sinit_mle_data->v5.sinit_hash); printk("\n"); + printk("\t mle_hash=\n\t"); + print_hash(sinit_mle_data->v5.mle_hash); printk("\n"); + printk("\t stm_hash=\n\t"); + print_hash(sinit_mle_data->v5.stm_hash); printk("\n"); + printk("\t lcp_policy_hash=\n\t"); + print_hash(sinit_mle_data->v5.lcp_policy_hash); printk("\n"); + printk("\t lcp_policy_control=%x\n", + sinit_mle_data->v5.lcp_policy_control); + printk("\t num_mdrs=%x\n", sinit_mle_data->v5.num_mdrs); + printk("\t mdrs_off=%x\n", sinit_mle_data->v5.mdrs_off); + printk("\t num_vtd_dmars=%x\n", sinit_mle_data->v5.num_vtd_dmars); + printk("\t vtd_dmars_off=%x\n", sinit_mle_data->v5.vtd_dmars_off); + print_sinit_mdrs((sinit_mdr_t *)(((void *)sinit_mle_data - sizeof(uint64_t)) + sinit_mle_data->v5.mdrs_off), sinit_mle_data->v5.num_mdrs); + } +} + +static bool verify_sinit_mle_data(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("SINIT to MLE data size is 0\n"); + return false; + } + if ( size > heap_size ) { + printk("SINIT to MLE data size is larger than heap size\n" + "(%Lx, heap size=%Lx)\n", size, heap_size); + /* TBD: un-comment this when have fixed SINIT + return false; */ + } + + sinit_mle_data = get_sinit_mle_data_start(txt_heap); + + /* check version */ + sinit_mle_data = get_sinit_mle_data_start(txt_heap); + if ( sinit_mle_data->version > 0x05 ) { + printk("unsupported SINIT to MLE data version (%x)\n", + sinit_mle_data->version); + return false; + } + + /* 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; +} + +static bool verify_vtd_pmrs(txt_heap_t *txt_heap) +{ + uint64_t max_ram; + os_sinit_data_t *os_sinit_data, tmp_os_sinit_data; + os_mle_data_t *os_mle_data; + + os_sinit_data = get_os_sinit_data_start(txt_heap); + + if ( os_sinit_data->version > 0x01 ) { + /* make sure the VT-d PMRs were actually set to cover what */ + /* we expect */ + /* calculate what they should have been */ + os_mle_data = get_os_mle_data_start(txt_heap); + max_ram = get_max_ram(os_mle_data->mbi); + if ( max_ram == 0 ) { + printk("max_ram is 0\n"); + return false; + } + memset(&tmp_os_sinit_data, 0, sizeof(tmp_os_sinit_data)); + tmp_os_sinit_data.version = os_sinit_data->version; + set_vtd_pmrs(&tmp_os_sinit_data, max_ram); + /* compare to current values */ + if ( (tmp_os_sinit_data.v3.vtd_pmr_lo_base != + os_sinit_data->v3.vtd_pmr_lo_base) || + (tmp_os_sinit_data.v3.vtd_pmr_lo_size != + os_sinit_data->v3.vtd_pmr_lo_size) || + (tmp_os_sinit_data.v3.vtd_pmr_hi_base != + os_sinit_data->v3.vtd_pmr_hi_base) || + (tmp_os_sinit_data.v3.vtd_pmr_hi_size != + os_sinit_data->v3.vtd_pmr_hi_size) ) { + printk("OS to SINIT data VT-d PMR settings do not match:\n"); + print_os_sinit_data(&tmp_os_sinit_data); + print_os_sinit_data(os_sinit_data); + return false; + } + } + + return true; +} + +static bool verify_vtd_dmar(txt_heap_t *txt_heap) +{ + /* get the copy in heap */ + uint32_t heap_dmar = 0; + uint32_t dmar_size = 0; + uint32_t acpi_dmar; + sinit_mle_data_t *sinit_mle_data; + + sinit_mle_data = get_sinit_mle_data_start(txt_heap); + printk("begin verifying vtd_dmar ...\n"); + if ( sinit_mle_data->version >= 0x05 ) { + heap_dmar = (uint32_t)sinit_mle_data - sizeof(uint64_t) + + sinit_mle_data->v5.vtd_dmars_off; + dmar_size = sinit_mle_data->v5.num_vtd_dmars; + printk("version = 0x05, heap_dmar = %08x, dmar_size = %08x\n", + heap_dmar, dmar_size); + } + else { + printk("version = %d, no dmar info.\n", sinit_mle_data->version); + return true; + } + + /* get acpi vt-d DMAR table */ + acpi_dmar = get_acpi_dmar_table(); + + if ( heap_dmar == 0 || acpi_dmar == 0 || dmar_size == 0 ) { + printk("failed to verify VT-d DMAR table:\n" + "\theap_dmar = %08x, acpi_dmar = %08x, dmar_size = %08x\n", + heap_dmar, acpi_dmar, dmar_size); + return false; + } + if ( memcmp((void *)heap_dmar, (void*)acpi_dmar, dmar_size) != 0 ) { + printk("failed to verify VT-d DMAR table: not equal\n"); + return false; + } + + printk("VT-d DMAR table OK\n"); + return true; +} + +void set_vtd_pmrs(os_sinit_data_t *os_sinit_data, uint64_t max_ram) +{ + /* this is phys addr */ + /* + * base must be 2M-aligned and size must be multiple of 2M + * we want to protect all of mem so that any kernel allocations before + * VT-d remapping is enabled are protected + * TBD: we should have a cmdline option to disable this for kernels + * that aren't VT-d -aware + */ + printk("max_ram=%Lx\n", max_ram); +#ifdef VT_D + /* since this code DMA protects all of RAM, it will only work if */ + /* xen enables VT-d translation for dom0, so don't use it if */ + /* xen's VT-d is not enabled */ + if ( max_ram & 0xffffffff00000000UL ) /* > 4GB */ + os_sinit_data->v3.vtd_pmr_lo_size = 0x100000000UL; + else { + /* round up since physical mem will always be a multiple of 2M */ + /* so if max_ram is not a multiple it is because there is */ + /* reserved memory above it */ + os_sinit_data->v3.vtd_pmr_lo_size = + (max_ram + 0x200000UL - 1UL) & ~0x1fffffUL; + } + if ( max_ram & 0xffffffff00000000UL ) { /* > 4GB */ + os_sinit_data->v3.vtd_pmr_hi_base = 0x100000000UL; + os_sinit_data->v3.vtd_pmr_hi_size = + (max_ram - 0x100000000UL) & ~0x1fffffUL; + } +#else + /* else just protect the MLE (i.e. sboot) */ + os_sinit_data->v3.vtd_pmr_lo_base = 0; + os_sinit_data->v3.vtd_pmr_lo_size = + ((((uint32_t)&_end - (uint32_t)&_start) + 0x200000 - 1) & ~0x1fffff); +#endif /* VT_D */ +} + +bool verify_txt_heap(txt_heap_t *txt_heap, bool bios_os_data_only) +{ + uint64_t size1, size2, size3, size4; + + /* verify BIOS to OS data */ + if ( !verify_bios_os_data(txt_heap) ) + return false; + + if ( bios_os_data_only ) + return true; + + /* check that total size is within the heap */ + size1 = get_bios_os_data_size(txt_heap); + size2 = get_os_mle_data_size(txt_heap); + size3 = get_os_sinit_data_size(txt_heap); + size4 = get_sinit_mle_data_size(txt_heap); + if ( (size1 + size2 + size3 + size4) > + read_priv_config_reg(TXTCR_HEAP_SIZE) ) { + printk("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)); + /* TBD: un-comment this when have fixed SINIT + 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; +} + +bool txt_verify_platform(void) +{ + txt_heap_t *txt_heap; + + read_processor_info(); + + /* support Intel(r) TXT (this includes TPM support) */ + if ( !supports_txt() ) + return false; + + /* verify BIOS to OS data */ + txt_heap = get_txt_heap(); + return verify_bios_os_data(txt_heap); +} + +static bool verify_saved_mtrrs(txt_heap_t *txt_heap) +{ + os_mle_data_t *os_mle_data; + os_mle_data = get_os_mle_data_start(txt_heap); + + return validate_mtrrs(&(os_mle_data->saved_mtrr_state)); +} + +bool txt_post_launch_verify_platform(void) +{ + txt_heap_t *txt_heap; + + /* + * verify some of the heap structures + */ + txt_heap = get_txt_heap(); + + if ( !verify_txt_heap(txt_heap, false) ) + return false; + + /* verify the saved MTRRs */ + if ( !verify_saved_mtrrs(txt_heap) ) + return false; + + /* TBD: verify the e820 table against the SINIT MDRs */ + + /* verify that VT-d PMRs were really set to protect all of RAM */ + if ( !verify_vtd_pmrs(txt_heap) ) + return false; + + /* verify the VT-d DMAR tables */ + if ( !verify_vtd_dmar(txt_heap) ) + return false; + + return true; +} + +bool verify_e820_map(sinit_mdr_t* mdrs_base, uint32_t num_mdrs) +{ + sinit_mdr_t* mdr_entry; + sinit_mdr_t tmp_entry; + uint64_t base, length; + uint32_t i, j, pos; + + if ( (mdrs_base == NULL) || (num_mdrs == 0) ) + return false; + + /* sort mdrs */ + for( i = 0; i < num_mdrs; i++ ) { + memcpy(&tmp_entry, &mdrs_base[i], sizeof(sinit_mdr_t)); + pos = i; + for ( j = i + 1; j < num_mdrs; j++ ) { + if ( ( tmp_entry.base > mdrs_base[j].base ) + || (( tmp_entry.base == mdrs_base[j].base ) && ( tmp_entry.length > mdrs_base[j].length )) ) { + memcpy(&tmp_entry, &mdrs_base[j], sizeof(sinit_mdr_t)); + pos = j; + } + } + if ( pos > i ) { + memcpy(&mdrs_base[pos], &mdrs_base[i], sizeof(sinit_mdr_t)); + memcpy(&mdrs_base[i], &tmp_entry, sizeof(sinit_mdr_t)); + } + } + + /* verify e820 map against mdrs */ + /* find all ranges *not* in MDRs: + if any of it is in e820 as RAM then set that to RESERVED. */ + i = 0; + base = 0; + while ( i < num_mdrs ) { + mdr_entry = &mdrs_base[i]; + i++; + if ( mdr_entry->mem_type > MDR_MEMTYPE_GOOD ) + continue; + length = mdr_entry->base - base; + if ( (length > 0) && (!e820_reserve_ram(base, length)) ) + return false; + base = mdr_entry->base + mdr_entry->length; + } + + /* deal with the last gap */ + length = (uint64_t)-1 - base; + return e820_reserve_ram(base, length); +} + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff -r de942f6fa6b3 -r 5648dc802679 sboot/txt/vmcs.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sboot/txt/vmcs.c Tue Aug 28 16:27:50 2007 -0700 @@ -0,0 +1,544 @@ +/* + * vmcs.c: create and manage mini-VT VM for APs to handle INIT-SIPI-SIPI + * + * Copyright (c) 2003-2007, Intel Corporation + * All rights reserved. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* no vmexit on external intr as mini guest only handle INIT & SIPI */ +#define MONITOR_PIN_BASED_EXEC_CONTROLS \ + (PIN_BASED_NMI_EXITING) + +#define MONITOR_CPU_BASED_EXEC_CONTROLS_SUBARCH 0 + +#define MONITOR_VM_EXIT_CONTROLS_SUBARCH 0 + +/* no vmexit on hlt as guest only run this instruction */ +#define MONITOR_CPU_BASED_EXEC_CONTROLS \ + ( MONITOR_CPU_BASED_EXEC_CONTROLS_SUBARCH | \ + CPU_BASED_INVDPG_EXITING | \ + CPU_BASED_MWAIT_EXITING ) + +#define MONITOR_VM_EXIT_CONTROLS \ + ( MONITOR_VM_EXIT_CONTROLS_SUBARCH | \ + VM_EXIT_ACK_INTR_ON_EXIT ) + +/* Basic flags for VM-Entry controls. */ +#define MONITOR_VM_ENTRY_CONTROLS 0x00000000 + +#define GUEST_SEGMENT_LIMIT 0xffffffff +#define EXCEPTION_BITMAP_BP (1 << 3) /* Breakpoint */ +#define EXCEPTION_BITMAP_PG (1 << 14) /* Page Fault */ +#define MONITOR_DEFAULT_EXCEPTION_BITMAP \ + ( EXCEPTION_BITMAP_PG | \ + EXCEPTION_BITMAP_BP ) + +#define load_TR(n) __asm__ __volatile__ ("ltr %%ax" : : "a" ((n)<<3) ) + +DEFINE_SPINLOCK(ap_init_lock); + +/* Dynamic (run-time adjusted) execution control flags. */ +static uint32_t vmx_pin_based_exec_control; +static uint32_t vmx_cpu_based_exec_control; +static uint32_t vmx_vmexit_control; +static uint32_t vmx_vmentry_control; + +static uint32_t vmcs_revision_id; + +extern void print_cr0(const char *s); + +static uint32_t adjust_vmx_controls(uint32_t ctrls, uint32_t msr) +{ + uint32_t vmx_msr_low, vmx_msr_high; + + rdmsr(msr, vmx_msr_low, vmx_msr_high); + + /* TBD: need to handle this error */ + /* Bit == 0 means must be zero. */ + BUG_ON(ctrls & ~vmx_msr_high); + + /* Bit == 1 means must be one. */ + ctrls |= vmx_msr_low; + + return ctrls; +} + +static void vmx_init_vmcs_config(void) +{ + uint32_t vmx_msr_low, vmx_msr_high; + uint32_t _vmx_pin_based_exec_control; + uint32_t _vmx_cpu_based_exec_control; + uint32_t _vmx_vmexit_control; + uint32_t _vmx_vmentry_control; + static int vmcs_config_init = 0; + + _vmx_pin_based_exec_control = + adjust_vmx_controls(MONITOR_PIN_BASED_EXEC_CONTROLS, + MSR_IA32_VMX_PINBASED_CTLS_MSR); + _vmx_cpu_based_exec_control = + adjust_vmx_controls(MONITOR_CPU_BASED_EXEC_CONTROLS, + MSR_IA32_VMX_PROCBASED_CTLS_MSR); + _vmx_vmexit_control = + adjust_vmx_controls(MONITOR_VM_EXIT_CONTROLS, + MSR_IA32_VMX_EXIT_CTLS_MSR); + _vmx_vmentry_control = + adjust_vmx_controls(MONITOR_VM_ENTRY_CONTROLS, + MSR_IA32_VMX_ENTRY_CTLS_MSR); + + rdmsr(MSR_IA32_VMX_BASIC_MSR, vmx_msr_low, vmx_msr_high); + + if ( vmcs_config_init == 0 ) { + vmcs_revision_id = vmx_msr_low; + vmx_pin_based_exec_control = _vmx_pin_based_exec_control; + vmx_cpu_based_exec_control = _vmx_cpu_based_exec_control; + vmx_vmexit_control = _vmx_vmexit_control; + vmx_vmentry_control = _vmx_vmentry_control; + vmcs_config_init = 1; + } + else if ( (vmcs_revision_id != vmx_msr_low) || + (vmx_pin_based_exec_control != _vmx_pin_based_exec_control) || + (vmx_cpu_based_exec_control != _vmx_cpu_based_exec_control) || + (vmx_vmexit_control != _vmx_vmexit_control) || + (vmx_vmentry_control != _vmx_vmentry_control) ) + printk("vmx_init_config different on different AP.\n"); + + /* TBD: handle this as an error */ + /* IA-32 SDM Vol 3B: VMCS size is never greater than 4kB. */ + if ( (vmx_msr_high & 0x1fff) > PAGE_SIZE ) + printk("vmcs size wrong.\n"); +} + +extern uint32_t idle_pg_table[PAGE_SIZE / 4]; +extern char vmcs_end[]; /* end of vmcs region */ + +/* build a 1-level identity-map page table on AP for vmxon*/ +static void build_ap_pagetable(void) +{ + uint32_t pt_entry = 0xe3; /* PRESENT+RW+A+D+4MB */ + uint32_t *pte = &idle_pg_table[0]; + uint32_t sboot_end = (uint32_t)&vmcs_end; + while ( pt_entry <= sboot_end + 0xe3 ) { + *pte = pt_entry; + pt_entry += ( 1 << L2_PAGETABLE_SHIFT ); + pte++; + } + printk("build 4M page table %p.\n", idle_pg_table); +} + +extern char host_vmcs[PAGE_SIZE]; +extern char ap_vmcs[PAGE_SIZE]; + +static bool start_vmx(void) +{ + uint32_t eax, edx; + struct vmcs_struct *vmcs; + + vmcs = (struct vmcs_struct*)host_vmcs; + memset(vmcs, 0, PAGE_SIZE); + + set_in_cr4(X86_CR4_VMXE); + + vmx_init_vmcs_config(); + vmcs->vmcs_revision_id = vmcs_revision_id; + + rdmsr(MSR_EFER, eax, edx); + + /* enable paging as required by vmentry */ + build_ap_pagetable(); + write_cr3((unsigned long)idle_pg_table); + set_in_cr4(X86_CR4_PSE); + write_cr0(read_cr0() | X86_CR0_PG); + + if ( __vmxon((unsigned long)vmcs) ) { + clear_in_cr4(X86_CR4_VMXE); + clear_in_cr4(X86_CR4_PSE); + write_cr0(read_cr0() & ~X86_CR0_PG); + printk("VMXON failed\n"); + return false; + } + + printk("VMXON done.\n"); + return true; +} + +static void stop_vmx(void) +{ + if ( !(read_cr4() & X86_CR4_VMXE) ) + return; + + __vmpclear((unsigned long)ap_vmcs); + __vmxoff(); + + clear_in_cr4(X86_CR4_VMXE); + + /* diable paging to restore AP's state to boot xen */ + write_cr0(read_cr0() & ~X86_CR0_PG); + clear_in_cr4(X86_CR4_PSE); +} + +/* in sboot/common/boot.S */ +extern void vmx_asm_vmexit_handler(void); +extern void _mini_guest(void); + +/* consturct guest/host vmcs: + * make guest vmcs from physical environment, + * so only one binary switch between root and non-root + */ +static void construct_vmcs(void) +{ + struct { + uint16_t limit; + uint32_t base; + } __attribute__ ((packed)) xdt; + unsigned long cr0, cr3, cr4, eflags, rsp; + unsigned int tr; + union vmcs_arbytes arbytes; + uint16_t seg; + + __vmwrite(PIN_BASED_VM_EXEC_CONTROL, vmx_pin_based_exec_control); + __vmwrite(VM_EXIT_CONTROLS, vmx_vmexit_control); + __vmwrite(VM_ENTRY_CONTROLS, vmx_vmentry_control); + __vmwrite(CPU_BASED_VM_EXEC_CONTROL, vmx_cpu_based_exec_control); + + /* segments selectors. */ + __asm__ __volatile__ ("mov %%ss, %0\n" : "=r"(seg)); + __vmwrite(HOST_SS_SELECTOR, seg); + __vmwrite(GUEST_SS_SELECTOR, seg); + + __asm__ __volatile__ ("mov %%ds, %0\n" : "=r"(seg)); + __vmwrite(HOST_DS_SELECTOR, seg); + __vmwrite(GUEST_DS_SELECTOR, seg); + + __asm__ __volatile__ ("mov %%es, %0\n" : "=r"(seg)); + __vmwrite(HOST_ES_SELECTOR, seg); + __vmwrite(GUEST_ES_SELECTOR, seg); + + __asm__ __volatile__ ("mov %%fs, %0\n" : "=r"(seg)); + __vmwrite(HOST_FS_SELECTOR, seg); + __vmwrite(GUEST_FS_SELECTOR, seg); + + __asm__ __volatile__ ("mov %%gs, %0\n" : "=r"(seg)); + __vmwrite(HOST_GS_SELECTOR, seg); + __vmwrite(GUEST_GS_SELECTOR, seg); + + __asm__ __volatile__ ("mov %%cs, %0\n" : "=r"(seg)); + __vmwrite(GUEST_CS_SELECTOR, seg); + __vmwrite(GUEST_RIP, (uint32_t)&_mini_guest); + + __vmwrite(HOST_CS_SELECTOR, seg); + __vmwrite(HOST_RIP, (unsigned long)vmx_asm_vmexit_handler); + + /* segment limits */ + __vmwrite(GUEST_ES_LIMIT, GUEST_SEGMENT_LIMIT); + __vmwrite(GUEST_SS_LIMIT, GUEST_SEGMENT_LIMIT); + __vmwrite(GUEST_DS_LIMIT, GUEST_SEGMENT_LIMIT); + __vmwrite(GUEST_FS_LIMIT, GUEST_SEGMENT_LIMIT); + __vmwrite(GUEST_GS_LIMIT, GUEST_SEGMENT_LIMIT); + __vmwrite(GUEST_CS_LIMIT, GUEST_SEGMENT_LIMIT); + + /* segment AR bytes, see boot.S for details */ + arbytes.bytes = 0; + arbytes.fields.seg_type = 0x3; /* type = 3 */ + arbytes.fields.s = 1; /* code or data, i.e. not system */ + arbytes.fields.dpl = 0; /* DPL = 0 */ + arbytes.fields.p = 1; /* segment present */ + arbytes.fields.default_ops_size = 1; /* 32-bit */ + arbytes.fields.g = 1; + + arbytes.fields.null_bit = 0; /* not null */ + __vmwrite(GUEST_ES_AR_BYTES, arbytes.bytes); + __vmwrite(GUEST_SS_AR_BYTES, arbytes.bytes); + __vmwrite(GUEST_DS_AR_BYTES, arbytes.bytes); + __vmwrite(GUEST_FS_AR_BYTES, arbytes.bytes); + __vmwrite(GUEST_GS_AR_BYTES, arbytes.bytes); + + arbytes.fields.seg_type = 0xb; /* type = 0xb */ + __vmwrite(GUEST_CS_AR_BYTES, arbytes.bytes); + + /* segment BASE */ + __vmwrite(GUEST_ES_BASE, 0); + __vmwrite(GUEST_SS_BASE, 0); + __vmwrite(GUEST_DS_BASE, 0); + __vmwrite(GUEST_FS_BASE, 0); + __vmwrite(GUEST_GS_BASE, 0); + __vmwrite(GUEST_CS_BASE, 0); + + __vmwrite(HOST_FS_BASE, 0); + __vmwrite(HOST_GS_BASE, 0); + + /* Guest LDT and TSS */ + __vmwrite(GUEST_LDTR_SELECTOR, 0); + __vmwrite(GUEST_LDTR_BASE, 0); + __vmwrite(GUEST_LDTR_LIMIT, 0xffff); + + __asm__ __volatile__ ("str (%0) \n" :: "a"(&tr) : "memory"); + if ( tr == 0 ) + printk("tr is 0 on ap, may vmlaunch fail.\n"); + __vmwrite(GUEST_TR_SELECTOR, tr); + __vmwrite(GUEST_TR_BASE, 0); + __vmwrite(GUEST_TR_LIMIT, 0xffff); + + __vmwrite(HOST_TR_SELECTOR, tr); + __vmwrite(HOST_TR_BASE, 0); + + + /* sboot do not use ldt */ + arbytes.bytes = 0; + arbytes.fields.s = 0; /* not code or data segement */ + arbytes.fields.seg_type = 0x2; /* LTD */ + arbytes.fields.p = 1; /* segment present */ + arbytes.fields.default_ops_size = 0; /* 16-bit */ + arbytes.fields.g = 1; + __vmwrite(GUEST_LDTR_AR_BYTES, arbytes.bytes); + + /* setup a TSS for vmentry as zero TR is not allowed */ + arbytes.bytes = 0; + arbytes.fields.s = 0; /* not code or data seg */ + arbytes.fields.seg_type = 0xb; /* 32-bit TSS (busy) */ + arbytes.fields.p = 1; /* segment present */ + arbytes.fields.default_ops_size = 0; /* 16-bit */ + arbytes.fields.g = 1; + __vmwrite(GUEST_TR_AR_BYTES, arbytes.bytes); + + /* GDT */ + __asm__ __volatile__ ("sgdt (%0) \n" :: "a"(&xdt) : "memory"); + __vmwrite(GUEST_GDTR_BASE, xdt.base); + __vmwrite(GUEST_GDTR_LIMIT, xdt.limit); + + __vmwrite(HOST_GDTR_BASE, xdt.base); + + /* IDT */ + __asm__ __volatile__ ("sidt (%0) \n" :: "a"(&xdt) : "memory"); + printk("idt.base=0x%x, limit=0x%x.\n", xdt.base, xdt.limit); + /* TBD: sboot's idt base is not cannonical, so use 0 */ + __vmwrite(GUEST_IDTR_BASE, 0); + __vmwrite(GUEST_IDTR_LIMIT, xdt.limit); + + __vmwrite(HOST_IDTR_BASE, xdt.base); + + /* control registers. */ + cr0 = read_cr0(); + cr3 = read_cr3(); + cr4 = read_cr4(); + __vmwrite(HOST_CR0, cr0); + __vmwrite(HOST_CR4, cr4); + __vmwrite(HOST_CR3, cr3); + + __vmwrite(GUEST_CR0, cr0); + __vmwrite(CR0_READ_SHADOW, cr0); + __vmwrite(GUEST_CR4, cr4); + __vmwrite(CR4_READ_SHADOW, cr4); + __vmwrite(GUEST_CR3, cr3); + + /* debug register */ + __vmwrite(GUEST_DR7, 0); + + /* rflags & rsp */ + __save_flags(eflags); + __vmwrite(GUEST_RFLAGS, eflags); + + __asm__ __volatile__ ("mov %%esp,%0\n\t" :"=r" (rsp)); + __vmwrite(GUEST_RSP, rsp); + __vmwrite(HOST_RSP, rsp); + + /* MSR intercepts. */ + __vmwrite(VM_EXIT_MSR_LOAD_ADDR, 0); + __vmwrite(VM_EXIT_MSR_STORE_ADDR, 0); + __vmwrite(VM_EXIT_MSR_STORE_COUNT, 0); + __vmwrite(VM_EXIT_MSR_LOAD_COUNT, 0); + __vmwrite(VM_ENTRY_MSR_LOAD_COUNT, 0); + + __vmwrite(VM_ENTRY_INTR_INFO_FIELD, 0); + + __vmwrite(CR0_GUEST_HOST_MASK, ~0UL); + __vmwrite(CR4_GUEST_HOST_MASK, ~0UL); + + __vmwrite(PAGE_FAULT_ERROR_CODE_MASK, 0); + __vmwrite(PAGE_FAULT_ERROR_CODE_MATCH, 0); + + __vmwrite(CR3_TARGET_COUNT, 0); + + /* guest need do nothing but vmexit when INIT-SIPI-SIPI */ + __vmwrite(GUEST_ACTIVITY_STATE, GUEST_STATE_HALT); + + __vmwrite(GUEST_INTERRUPTIBILITY_INFO, 0); + __vmwrite(VMCS_LINK_POINTER, ~0UL); + + __vmwrite(VMCS_LINK_POINTER_HIGH, ~0UL); + + __vmwrite(EXCEPTION_BITMAP, MONITOR_DEFAULT_EXCEPTION_BITMAP); + + printk("vmcs setup done.\n"); +} + +static void vmx_create_vmcs(void) +{ + struct vmcs_struct *vmcs = (struct vmcs_struct*)ap_vmcs; + + memset(vmcs, 0, PAGE_SIZE); + + vmcs->vmcs_revision_id = vmcs_revision_id; + + /* vir addr equal to phy addr as we setup identity page table */ + __vmpclear((unsigned long)vmcs); + + __vmptrld((unsigned long)vmcs); + + construct_vmcs(); +} + +static void launch_mini_guest(void) +{ + unsigned long error; + + __vmlaunch(); + + /* should not reach here */ + error = __vmread(VM_INSTRUCTION_ERROR); + printk(" error code %lx\n", error); +} + +static void vmx_failed_vmentry(unsigned int exit_reason) +{ + unsigned int failed_vmentry_reason = (uint16_t)exit_reason; + unsigned long exit_qualification; + + exit_qualification = __vmread(EXIT_QUALIFICATION); + printk("Failed vm entry (exit reason 0x%x) ", exit_reason); + switch ( failed_vmentry_reason ) + { + case EXIT_REASON_INVALID_GUEST_STATE: + printk("caused by invalid guest state (%ld).\n", + exit_qualification); + break; + case EXIT_REASON_MSR_LOADING: + printk("caused by MSR entry %ld loading.\n", exit_qualification); + break; + case EXIT_REASON_MACHINE_CHECK: + printk("caused by machine check.\n"); + break; + default: + printk("reason not known yet!"); + break; + } +} + +/* Vmexit handler for physical INIT-SIPI-SIPI from the BSP + * Do not use printk in this critical path as BSP only + * wait for a short time + */ +void vmx_vmexit_handler(void) +{ + unsigned long error, exit_qual; + unsigned int exit_reason, apicid; + uint32_t sipi_vec = 0; + + static int state = AP_WAIT_INIT; + + exit_reason = __vmread(VM_EXIT_REASON); +/* printk("vmx_vmexit_handler, exit_reason=%x.\n", exit_reason);*/ + + if ( (exit_reason & VMX_EXIT_REASONS_FAILED_VMENTRY) ) { + printk("failed vmentry.\n"); + vmx_failed_vmentry(exit_reason); + goto out; + } + + switch ( exit_reason ) + { + case EXIT_REASON_INIT: + if ( state == AP_WAIT_INIT ) { + state = AP_WAIT_SIPI1; + __vmwrite(GUEST_ACTIVITY_STATE, GUEST_STATE_WAIT_SIPI); + } + break; + case EXIT_REASON_SIPI: + if ( state == AP_WAIT_SIPI1 ) { + state = AP_WAIT_SIPI2; + __vmwrite(GUEST_ACTIVITY_STATE, GUEST_STATE_WAIT_SIPI); + } + else if ( state == AP_WAIT_SIPI2 ) + state = AP_WAIT_DONE; + exit_qual = __vmread(EXIT_QUALIFICATION); + sipi_vec = (exit_qual & 0xffUL) << PAGE_SHIFT; + /* printk("exiting due to SIPI: vector=%x\n", sipi_vec); */ + break; + default: + printk("can't handle vmexit due to 0x%x.\n", exit_reason); + } + + if ( state == AP_WAIT_DONE ) { /* disable VT then jump to xen code */ +/* printk("sucessfully handle INIT-SIPI-SIPI on AP.\n");*/ + stop_vmx(); + state = AP_WAIT_INIT; + memset(host_vmcs, 0, PAGE_SIZE); + memset(ap_vmcs, 0, PAGE_SIZE); + spin_unlock(&ap_init_lock); + apicid = cpuid_ebx(1) >> 24; + cpu_wakeup(apicid, sipi_vec); + return; + } + + /* resume mini guest to handle following signal */ + __vmresume(); + + /* should not reach here */ + error = __vmread(VM_INSTRUCTION_ERROR); + printk(" error code %lx\n", error); + +out: + spin_unlock(&ap_init_lock); +} + +/* Launch a mini guest to handle the physical INIT-SIPI-SIPI from BSP */ +void handle_init_sipi_sipi(void) +{ + /* setup a dummy tss as vmentry require a non-zero host TR */ + load_TR(3); + + /* handle APs one by one by a big spinlock, */ + spin_lock(&ap_init_lock); + + /* prepare a guest for INIT-SIPI-SIPI handling */ + /* 1: setup VMX environment and VMXON */ + if ( !start_vmx() ) + goto out; + + /* 2: setup VMCS */ + vmx_create_vmcs(); + + /* 3: launch VM */ + launch_mini_guest(); + +out: + spin_unlock(&ap_init_lock); +} + + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */