diff options
Diffstat (limited to 'src/security/intel')
-rw-r--r-- | src/security/intel/Kconfig | 1 | ||||
-rw-r--r-- | src/security/intel/Makefile.inc | 1 | ||||
-rw-r--r-- | src/security/intel/stm/Kconfig | 49 | ||||
-rw-r--r-- | src/security/intel/stm/Makefile.inc | 10 | ||||
-rw-r--r-- | src/security/intel/stm/SmmStm.c | 691 | ||||
-rw-r--r-- | src/security/intel/stm/SmmStm.h | 120 | ||||
-rw-r--r-- | src/security/intel/stm/StmApi.h | 726 | ||||
-rw-r--r-- | src/security/intel/stm/StmPlatformResource.c | 188 | ||||
-rw-r--r-- | src/security/intel/stm/StmPlatformResource.h | 32 | ||||
-rw-r--r-- | src/security/intel/stm/StmPlatformSmm.c | 204 |
10 files changed, 2022 insertions, 0 deletions
diff --git a/src/security/intel/Kconfig b/src/security/intel/Kconfig index a4525e7b9b..aa24e8ac68 100644 --- a/src/security/intel/Kconfig +++ b/src/security/intel/Kconfig @@ -14,3 +14,4 @@ ## source "src/security/intel/txt/Kconfig" +source "src/security/intel/stm/Kconfig" diff --git a/src/security/intel/Makefile.inc b/src/security/intel/Makefile.inc index 9388d3f798..e00802ad06 100644 --- a/src/security/intel/Makefile.inc +++ b/src/security/intel/Makefile.inc @@ -1 +1,2 @@ subdirs-y += txt +subdirs-y += stm diff --git a/src/security/intel/stm/Kconfig b/src/security/intel/stm/Kconfig new file mode 100644 index 0000000000..a74eba8522 --- /dev/null +++ b/src/security/intel/stm/Kconfig @@ -0,0 +1,49 @@ + + +config STM + bool "Enable STM" + default n + depends on SMM_TSEG + select USE_BLOBS + + help + Enabling the STM will load a simple hypervisor into SMM that will + restrict the actions of the SMI handler, which is the part of BIOS + that functions in system management mode (SMM). The kernel can + configure the STM to prevent the SMI handler from accessing platform + resources. + The STM closes a vulnerability in Intel TXT (D-RTM) + The SMI handler provides a list of platform resources that it + requires access to the STM during STM startup, which the kernel + cannot override. + An additional capability, called STM-PE, provides a protected + execution capability that allows modules to be executed without + observation and interference. Examples of usage include kernel + introspection and virtualized trusted platform module (vTPM). + Requirement: SMM must be enabled and there must be sufficient room + within the TSEG to fit the MSEG. + +if STM + +menu "SMI Transfer Monitor (STM)" + +config MSEG_SIZE + hex "mseg size" + default 0x400000 + help + STM only - 0x100000 + STM/PE - 0x300000+ depending on the amount of memory needed + for the protected execution virtual + machine (VM/PE) + +config BIOS_RESOURCE_LIST_SIZE + hex "bios_resource_list_size" + default 0x1000 + +config STM_BINARY_FILE + string "STM binary file" + default "3rdparty/blobs/cpu/intel/stm/stm.bin" + +endmenu #STM + +endif diff --git a/src/security/intel/stm/Makefile.inc b/src/security/intel/stm/Makefile.inc new file mode 100644 index 0000000000..1a23fe97f2 --- /dev/null +++ b/src/security/intel/stm/Makefile.inc @@ -0,0 +1,10 @@ + +# put the stm where it can be found + +cbfs-files-$(CONFIG_STM) += stm.bin +stm.bin-file := $(CONFIG_STM_BINARY_FILE) +stm.bin-type := raw + +ramstage-$(CONFIG_STM) += SmmStm.c +ramstage-$(CONFIG_STM) += StmPlatformSmm.c +ramstage-$(CONFIG_STM) += StmPlatformResource.c diff --git a/src/security/intel/stm/SmmStm.c b/src/security/intel/stm/SmmStm.c new file mode 100644 index 0000000000..f23be70217 --- /dev/null +++ b/src/security/intel/stm/SmmStm.c @@ -0,0 +1,691 @@ +/* @file + * SMM STM support + * + * Copyright (c) 2015 - 2016, Intel Corporation. All rights reserved. + * + * This program and the accompanying materials are licensed and made available + * under the terms and conditions of the BSD License which accompanies this + * distribution. The full text of the license may be found at + * http://opensource.org/licenses/bsd-license.php. + * + * THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + * WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR + * IMPLIED. + * + */ + +#include <console/console.h> +#include <cpu/x86/cr.h> +#include <cpu/x86/mp.h> +#include <cpu/x86/msr.h> +#include <cpu/x86/cache.h> +#include <security/intel/stm/SmmStm.h> +#include <string.h> + +#define TXT_EVTYPE_BASE 0x400 +#define TXT_EVTYPE_STM_HASH (TXT_EVTYPE_BASE + 14) + +#define RDWR_ACCS 3 +#define FULL_ACCS 7 + +#define SIZE_4KB 0x00001000 +#define SIZE_4MB 0x00400000 + +#define PTP_SIZE SIZE_4KB + +#define IA32_PG_P (1 << 0) +#define IA32_PG_RW (1 << 1) +#define IA32_PG_PS (1 << 7) + +#define STM_PAGE_SHIFT 12 +#define STM_PAGE_MASK 0xFFF +#define STM_SIZE_TO_PAGES(a) \ + (((a) >> STM_PAGE_SHIFT) + (((a)&STM_PAGE_MASK) ? 1 : 0)) +#define STM_PAGES_TO_SIZE(a) ((a) << STM_PAGE_SHIFT) + +#define STM_ACCESS_DENIED 15 +#define STM_UNSUPPORTED 3 + +#define STM_BUFFER_TOO_SMALL 1 + +#define STM_SM_MONITOR_STATE_ENABLED 1 + +typedef struct { + + uint64_t vmcs_revision_id : 31; + uint64_t always_zero : 1; + uint64_t vmcs_size : 13; + uint64_t reserved1 : 3; + uint64_t vmxon_add_width : 1; + uint64_t stm_supported : 1; + uint64_t vmcs_memory_type : 4; + uint64_t in_out_reporting : 1; + uint64_t may_clear_defaults : 1; + uint64_t reserved2 : 8; +} VMX_BASIC_MSR_BITS; + +typedef union { + VMX_BASIC_MSR_BITS bits; + uint64_t uint64; + msr_t msr; +} VMX_BASIC_MSR; + +typedef struct { + uint64_t valid : 1; + uint64_t reserved1 : 1; + uint64_t vmx_off_blockSmi : 1; + uint64_t reserved2 : 9; + uint64_t mseg_address : 20; + uint64_t reserved3 : 32; +} SMM_MONITOR_CTL_MSR_BITS; + +extern struct mp_state { + struct mp_ops ops; + int cpu_count; + uintptr_t perm_smbase; + size_t perm_smsize; + size_t smm_save_state_size; + int do_smm; +} mp_state; + +typedef union { + SMM_MONITOR_CTL_MSR_BITS bits; + uint64_t uint64; + msr_t msr; +} SMM_MONITOR_CTL_MSR; + +// Template of STM_RSC_END structure for copying. + +STM_RSC_END m_rsc_end_node = { + {END_OF_RESOURCES, sizeof(STM_RSC_END)}, +}; + +uint8_t *m_stm_resources_ptr = NULL; +uint32_t m_stm_resource_total_size = 0x0; +uint32_t m_stm_resource_size_used = 0x0; +uint32_t m_stm_resource_size_available = 0x0; + +uint8_t *stm_resource_heap = NULL; + +uint32_t m_stm_state = 0; + +/* + * Handle single Resource to see if it can be merged into Record. + * + * @param resource A pointer to resource node to be added + * @param record A pointer to record node to be merged + * + * @retval true resource handled + * @retval false resource is not handled + */ + +static bool handle_single_resource(STM_RSC *resource, STM_RSC *record) +{ + uint64_t resource_lo = 0; + uint64_t resource_hi = 0; + uint64_t record_lo = 0; + uint64_t record_hi = 0; + + // Calling code is responsible for making sure that + // Resource->Header.RscType == (*Record)->Header.RscType + // thus we use just one of them as switch variable. + + switch (resource->header.rsc_type) { + case MEM_RANGE: + case MMIO_RANGE: + resource_lo = resource->mem.base; + resource_hi = resource->mem.base + resource->mem.length; + record_lo = record->mem.base; + record_hi = record->mem.base + record->mem.length; + if (resource->mem.rwx_attributes + != record->mem.rwx_attributes) { + if ((resource_lo == record_lo) + && (resource_hi == record_hi)) { + record->mem.rwx_attributes = + resource->mem.rwx_attributes + | record->mem.rwx_attributes; + return true; + } else { + return false; + } + } + break; + case IO_RANGE: + case TRAPPED_IO_RANGE: + resource_lo = (uint64_t)resource->io.base; + resource_hi = (uint64_t)resource->io.base + + (uint64_t)resource->io.length; + record_lo = (uint64_t)record->io.base; + record_hi = + (uint64_t)record->io.base + (uint64_t)record->io.length; + break; + case PCI_CFG_RANGE: + if ((resource->pci_cfg.originating_bus_number + != record->pci_cfg.originating_bus_number) + || (resource->pci_cfg.last_node_index + != record->pci_cfg.last_node_index)) + return false; + + if (memcmp(resource->pci_cfg.pci_device_path, + record->pci_cfg.pci_device_path, + sizeof(STM_PCI_DEVICE_PATH_NODE) + * (resource->pci_cfg.last_node_index + 1)) + != 0) { + return false; + } + resource_lo = (uint64_t)resource->pci_cfg.base; + resource_hi = (uint64_t)resource->pci_cfg.base + + (uint64_t)resource->pci_cfg.length; + record_lo = (uint64_t)record->pci_cfg.base; + record_hi = (uint64_t)record->pci_cfg.base + + (uint64_t)record->pci_cfg.length; + if (resource->pci_cfg.rw_attributes + != record->pci_cfg.rw_attributes) { + if ((resource_lo == record_lo) + && (resource_hi == record_hi)) { + record->pci_cfg.rw_attributes = + resource->pci_cfg.rw_attributes + | record->pci_cfg.rw_attributes; + return true; + } else { + return false; + } + } + break; + case MACHINE_SPECIFIC_REG: + + // Special case - merge MSR masks in place. + if (resource->msr.msr_index != record->msr.msr_index) + return false; + record->msr.read_mask |= resource->msr.read_mask; + record->msr.write_mask |= resource->msr.write_mask; + return true; + default: + return false; + } + + // If resources are disjoint + if ((resource_hi < record_lo) || (resource_lo > record_hi)) + return false; + + // If resource is consumed by record. + if ((resource_lo >= record_lo) && (resource_hi <= record_hi)) + return true; + + // Resources are overlapping. + // Resource and record are merged. + resource_lo = (resource_lo < record_lo) ? resource_lo : record_lo; + resource_hi = (resource_hi > record_hi) ? resource_hi : record_hi; + + switch (resource->header.rsc_type) { + case MEM_RANGE: + case MMIO_RANGE: + record->mem.base = resource_lo; + record->mem.length = resource_hi - resource_lo; + break; + case IO_RANGE: + case TRAPPED_IO_RANGE: + record->io.base = (uint64_t)resource_lo; + record->io.length = (uint64_t)(resource_hi - resource_lo); + break; + case PCI_CFG_RANGE: + record->pci_cfg.base = (uint64_t)resource_lo; + record->pci_cfg.length = (uint64_t)(resource_hi - resource_lo); + break; + default: + return false; + } + + return true; +} + +/* + * Add resource node. + * + * @param Resource A pointer to resource node to be added + */ +static void add_single_resource(STM_RSC *resource) +{ + STM_RSC *record; + + record = (STM_RSC *)m_stm_resources_ptr; + + while (true) { + if (record->header.rsc_type == END_OF_RESOURCES) + break; + + // Go to next record if resource and record types don't match. + if (resource->header.rsc_type != record->header.rsc_type) { + record = (STM_RSC *)((void *)record + + record->header.length); + continue; + } + + // Record is handled inside of procedure - don't adjust. + if (handle_single_resource(resource, record)) + return; + record = (STM_RSC *)((void *)record + record->header.length); + } + + // Add resource to the end of area. + memcpy(m_stm_resources_ptr + m_stm_resource_size_used + - sizeof(m_rsc_end_node), + resource, resource->header.length); + memcpy(m_stm_resources_ptr + m_stm_resource_size_used + - sizeof(m_rsc_end_node) + resource->header.length, + &m_rsc_end_node, sizeof(m_rsc_end_node)); + m_stm_resource_size_used += resource->header.length; + m_stm_resource_size_available = + m_stm_resource_total_size - m_stm_resource_size_used; +} + +/* + * Add resource list. + * + * @param resource_list A pointer to resource list to be added + * @param num_entries Optional number of entries. + * If 0, list must be terminated by END_OF_RESOURCES. + */ +static void add_resource(STM_RSC *resource_list, uint32_t num_entries) +{ + uint32_t count; + uint32_t index; + STM_RSC *resource; + + if (num_entries == 0) + count = 0xFFFFFFFF; + else + count = num_entries; + + resource = resource_list; + + for (index = 0; index < count; index++) { + if (resource->header.rsc_type == END_OF_RESOURCES) + return; + add_single_resource(resource); + resource = + (STM_RSC *)((void *)resource + resource->header.length); + } +} + +/* + * Validate resource list. + * + * @param resource_list A pointer to resource list to be added + * @param num_entries Optional number of entries. + * If 0, list must be terminated by END_OF_RESOURCES. + * + * @retval true resource valid + * @retval false resource invalid + */ +static bool validate_resource(STM_RSC *resource_list, uint32_t num_entries) +{ + uint32_t count; + uint32_t index; + STM_RSC *resource; + uint32_t sub_index; + + // If NumEntries == 0 make it very big. Scan will be terminated by + // END_OF_RESOURCES. + if (num_entries == 0) + count = 0xFFFFFFFF; + else + count = num_entries; + + // Start from beginning of resource list. + resource = resource_list; + + for (index = 0; index < count; index++) { + printk(BIOS_DEBUG, "STM: %s (%u) - RscType(%x) length(0x%x)\n", + __func__, + index, + resource->header.rsc_type, + resource->header.length); + // Validate resource. + switch (resource->header.rsc_type) { + case END_OF_RESOURCES: + if (resource->header.length != sizeof(STM_RSC_END)) + return false; + + // If we are passed actual number of resources to add, + // END_OF_RESOURCES structure between them is considered + // an error. If NumEntries == 0 END_OF_RESOURCES is a + // termination. + if (num_entries != 0) + return false; + + // If NumEntries == 0 and list reached end - return + // success. + return true; + + case MEM_RANGE: + case MMIO_RANGE: + printk(BIOS_DEBUG, + "STM: %s - MEM (0x%0llx, 0x%0llx)\n", + __func__, + resource->mem.base, + resource->mem.length); + + if (resource->header.length != sizeof(STM_RSC_MEM_DESC)) + return false; + + if (resource->mem.rwx_attributes > FULL_ACCS) + return false; + break; + + case IO_RANGE: + case TRAPPED_IO_RANGE: + if (resource->header.length != sizeof(STM_RSC_IO_DESC)) + return false; + + if ((resource->io.base + resource->io.length) > 0xFFFF) + return false; + break; + + case PCI_CFG_RANGE: + printk(BIOS_DEBUG, + "STM: %s - PCI (0x%02x, 0x%08x, 0x%02x, 0x%02x)\n", + __func__, + resource->pci_cfg.originating_bus_number, + resource->pci_cfg.last_node_index, + resource->pci_cfg.pci_device_path[0].pci_device, + resource->pci_cfg.pci_device_path[0] + .pci_function); + if (resource->header.length + != sizeof(STM_RSC_PCI_CFG_DESC) + + (sizeof(STM_PCI_DEVICE_PATH_NODE) + * resource->pci_cfg.last_node_index)) + return false; + for (sub_index = 0; + sub_index <= resource->pci_cfg.last_node_index; + sub_index++) { + if ((resource->pci_cfg + .pci_device_path[sub_index] + .pci_device + > 0x1F) + || (resource->pci_cfg + .pci_device_path[sub_index] + .pci_function + > 7)) + return false; + } + if ((resource->pci_cfg.base + resource->pci_cfg.length) + > 0x1000) + return false; + break; + + case MACHINE_SPECIFIC_REG: + if (resource->header.length != sizeof(STM_RSC_MSR_DESC)) + return false; + break; + + default: + printk(BIOS_DEBUG, "STM: %s - Unknown RscType(%x)\n", + __func__, resource->header.rsc_type); + return false; + } + resource = + (STM_RSC *)((void *)resource + resource->header.length); + } + return true; +} + +/* + * Get resource list. + * EndResource is excluded. + * + * @param resou rce_list A pointer to resource list to be added + * @param num_entries Optional number of entries. + * If 0, list must be terminated by END_OF_RESOURCES. + * + * @retval true resource valid + * @retval false resource invalid + */ +static uint32_t get_resource_size(STM_RSC *resource_list, uint32_t num_entries) +{ + uint32_t count; + uint32_t index; + STM_RSC *resource; + + resource = resource_list; + + // If NumEntries == 0 make it very big. Scan will be terminated by + // END_OF_RESOURCES. + if (num_entries == 0) + count = 0xFFFFFFFF; + else + count = num_entries; + + // Start from beginning of resource list. + resource = resource_list; + + for (index = 0; index < count; index++) { + if (resource->header.rsc_type == END_OF_RESOURCES) + break; + resource = + (STM_RSC *)((void *)resource + resource->header.length); + } + return (uint32_t)((uint32_t)resource - (uint32_t)resource_list); +} + +/* + * Add resources in list to database. Allocate new memory areas as needed. + * + * @param resource_list A pointer to resource list to be added + * @param num_entries Optional number of entries. + * If 0, list must be terminated by END_OF_RESOURCES. + * + * @retval SUCCESS If resources are added + * @retval INVALID_PARAMETER If nested procedure detected resource failure + * @retval OUT_OF_RESOURCES If nested procedure returned it and we cannot + * allocate more areas. + */ +int add_pi_resource(STM_RSC *resource_list, uint32_t num_entries) +{ + size_t resource_size; + + printk(BIOS_DEBUG, "STM: %s - Enter\n", __func__); + + if (!validate_resource(resource_list, num_entries)) + return -1; // INVALID_PARAMETER; + + resource_size = get_resource_size(resource_list, num_entries); + printk(BIOS_DEBUG, "STM: ResourceSize - 0x%08lx\n", resource_size); + if (resource_size == 0) + return -1; // INVALID_PARAMETER; + + if (m_stm_resources_ptr == NULL) { + + // Copy EndResource for initialization + m_stm_resources_ptr = stm_resource_heap; + m_stm_resource_total_size = CONFIG_BIOS_RESOURCE_LIST_SIZE; + memset(m_stm_resources_ptr, 0, CONFIG_BIOS_RESOURCE_LIST_SIZE); + + memcpy(m_stm_resources_ptr, &m_rsc_end_node, + sizeof(m_rsc_end_node)); + m_stm_resource_size_used = sizeof(m_rsc_end_node); + m_stm_resource_size_available = + m_stm_resource_total_size - sizeof(m_rsc_end_node); + wbinvd(); // force to memory + + } else { + if (m_stm_resource_size_available < resource_size) { + printk(BIOS_DEBUG, + "STM: ERROR - not enough space for SMM resource list\n"); + return -1; // OUT_OF_RESOURCES + } + } + + // Check duplication + add_resource(resource_list, num_entries); + + return 0; // SUCCESS; +} + +/* + * Delete resources in list to database. + * + * @param resource_list A pointer to resource list to be deleted + * NULL means delete all resources. + * @param num_entries Optional number of entries. + * If 0, list must be terminated by END_OF_RESOURCES. + * + * @retval SUCCESS If resources are deleted + * @retval INVALID_PARAMETER If nested procedure detected resource failure + */ +int32_t delete_pi_resource(STM_RSC *resource_list, uint32_t num_entries) +{ + if (resource_list != NULL) { + // ASSERT (false); + return -1; // UNSUPPORTED; + } + + // Delete all + memcpy(m_stm_resources_ptr, &m_rsc_end_node, sizeof(m_rsc_end_node)); + m_stm_resource_size_used = sizeof(m_rsc_end_node); + m_stm_resource_size_available = + m_stm_resource_total_size - sizeof(m_rsc_end_node); + return 0; // SUCCESS; +} + +/* + * Get BIOS resources. + * + * @param resource_list A pointer to resource list to be filled + * @param resource_size On input it means size of resource list input. + * On output it means size of resource list filled, + * or the size of resource list to be filled if size is + * too small. + * + * @retval SUCCESS If resources are returned. + * @retval BUFFER_TOO_SMALL If resource list buffer is too small to hold + * the whole resource list. + */ +int32_t get_pi_resource(STM_RSC *resource_list, uint32_t *resource_size) +{ + if (*resource_size < m_stm_resource_size_used) { + *resource_size = (uint32_t)m_stm_resource_size_used; + return -1; // BUFFER_TOO_SMALL; + } + + memcpy(resource_list, m_stm_resources_ptr, m_stm_resource_size_used); + *resource_size = (uint32_t)m_stm_resource_size_used; + return 0; // SUCCESS; +} + +/* + * Get 4K page aligned VMCS size. + * @return 4K page aligned VMCS size + */ +static uint32_t get_vmcs_size(void) +{ + uint32_t this_vmcs_size; + VMX_BASIC_MSR msr_data64; + int stm_support; + + msr_data64.msr = rdmsr(IA32_VMX_BASIC_MSR); + + this_vmcs_size = msr_data64.bits.vmcs_size; + stm_support = msr_data64.bits.stm_supported; + printk(BIOS_DEBUG, "STM: %s: Size %d StmSupport %d\n", __func__, + this_vmcs_size, stm_support); + + // VMCS require 0x1000 alignment + this_vmcs_size = STM_PAGES_TO_SIZE(STM_SIZE_TO_PAGES(this_vmcs_size)); + + return this_vmcs_size; +} + +/* + * Create 4G page table for STM. + * 2M PTEs for x86_64 or 2M PTEs for x86_32. + * + * @param pageable_base The page table base in MSEG + */ +void stm_gen_4g_pagetable_x64(uint32_t pagetable_base) +{ + uint32_t index; + uint32_t sub_index; + uint64_t *pde; + uint64_t *pte; + uint64_t *pml4; + + pml4 = (uint64_t *)(uint32_t)pagetable_base; + pagetable_base += PTP_SIZE; + *pml4 = pagetable_base | IA32_PG_RW | IA32_PG_P; + + pde = (uint64_t *)(uint32_t)pagetable_base; + pagetable_base += PTP_SIZE; + pte = (uint64_t *)(uint32_t)pagetable_base; + + for (index = 0; index < 4; index++) { + *pde = pagetable_base | IA32_PG_RW | IA32_PG_P; + pde++; + pagetable_base += PTP_SIZE; + + for (sub_index = 0; sub_index < SIZE_4KB / sizeof(*pte); + sub_index++) { + *pte = (((index << 9) + sub_index) << 21) | IA32_PG_PS + | IA32_PG_RW | IA32_PG_P; + pte++; + } + } +} + +/* + * Check STM image size. + * + * @param stm_image STM image + * @param stm_imageSize STM image size + * + * @retval true check pass + * @retval false check fail + */ + +bool stm_check_stm_image(void *stm_image, uint32_t stm_imagesize) +{ + uint32_t min_mseg_size; + STM_HEADER *stm_header; + + stm_header = (STM_HEADER *)stm_image; + + // Get Minimal required Mseg size + min_mseg_size = (STM_PAGES_TO_SIZE(STM_SIZE_TO_PAGES( + stm_header->sw_stm_hdr.static_image_size)) + + stm_header->sw_stm_hdr.additional_dynamic_memory_size + + (stm_header->sw_stm_hdr.per_proc_dynamic_memory_size + + get_vmcs_size() * 2) + * mp_state.cpu_count); + if (min_mseg_size < stm_imagesize) + min_mseg_size = stm_imagesize; + + if (stm_header->hw_stm_hdr.cr3_offset + >= stm_header->sw_stm_hdr.static_image_size) { + + // We will create page table, just in case that SINIT does not + // create it. + if (min_mseg_size < stm_header->hw_stm_hdr.cr3_offset + + STM_PAGES_TO_SIZE(6)) { + min_mseg_size = stm_header->hw_stm_hdr.cr3_offset + + STM_PAGES_TO_SIZE(6); + } + } + + // Check if it exceeds MSEG size + if (min_mseg_size > CONFIG_MSEG_SIZE) + return false; + + return true; +} + +/* + * This function return BIOS STM resource. + * Produced by SmmStm. + * Comsumed by SmmMpService when Init. + * + * @return BIOS STM resource + */ +void *get_stm_resource(void) +{ + return m_stm_resources_ptr; +} diff --git a/src/security/intel/stm/SmmStm.h b/src/security/intel/stm/SmmStm.h new file mode 100644 index 0000000000..4f72816cae --- /dev/null +++ b/src/security/intel/stm/SmmStm.h @@ -0,0 +1,120 @@ +/* @file + * SMM STM support + * + * Copyright (c) 2015 - 2016, Intel Corporation. All rights reserved. + * This program and the accompanying materials are licensed and made + * available under the terms and conditions of the BSD License which + * accompanies this distribution. The full text of the license may + * be found at http://opensource.org/licenses/bsd-license.php. + * + * THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + * WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED + * + */ + +#ifndef _SMM_STM_H_ +#define _SMM_STM_H_ + +#include <cpu/x86/msr.h> +#include "StmApi.h" + +/* + * Load STM image. + * + * @retval SUCCESS STM is loaded to MSEG + * @retval BUFFER_TOO_SMALL MSEG is too small + * @retval UNSUPPORTED MSEG is not enabled + */ +int load_stm_image(uintptr_t mseg); + +void stm_setup( + uintptr_t mseg, int cpu, int num_cpus, uintptr_t smbase, + uintptr_t smbase_base, uint32_t offset32); + +/* + * Add resources in list to database. Allocate new memory areas as needed. + * + * @param resource_list A pointer to resource list to be added + * @param num_entries Optional number of entries. + * If 0, list must be terminated by END_OF_RESOURCES. + * + * @retval SUCCESS If resources are added + * @retval INVALID_PARAMETER If nested procedure detected resource failure + * @retval OUT_OF_RESOURCES If nested procedure returned it and we cannot + * allocate more areas. + */ +int add_pi_resource(STM_RSC *resource_list, uint32_t num_entries); + +/* + * Delete resources in list to database. + * + * @param resource_list A pointer to resource list to be deleted + * NULL means delete all resources. + * @param num_entries Optional number of entries. + * If 0, list must be terminated by END_OF_RESOURCES. + * + * @retval SUCCESS If resources are deleted + * @retval NVALID_PARAMETER If nested procedure detected resource fail + */ +int delete_pi_resource(STM_RSC *resource_list, uint32_t num_entries); + +/* + * Get BIOS resources. + * + * @param resource_list A pointer to resource list to be filled + * @param resource_size On input it means size of resource list input. + * On output it means size of resource list filled, + * or the size of resource list to be filled if + * size is too small. + * + * @retval SUCCESS If resources are returned. + * @retval BUFFER_TOO_SMALL If resource list buffer is too small to + * hold the whole resources. + */ +int get_pi_resource(STM_RSC *resource_list, uint32_t *resource_size); + +/* + * This function notifies the STM of a resource change. + * + * @param stm_resource BIOS STM resource + */ +void notify_stm_resource_change(void *stm_resource); + +/* + * This function returns the pointer to the STM BIOS resource list. + * + * @return BIOS STM resource + */ +void *get_stm_resource(void); + +void setup_smm_descriptor(void *smbase, void *base_smbase, int32_t apic_id, + int32_t entry32_off); + +/* + * Check STM image size. + * + * @param stm_image STM image + * @param stm_image_size STM image size + * + * @retval true check pass + * @retval false check fail + */ +bool stm_check_stm_image(void *stm_image, uint32_t stm_image_size); + +/* + * Create 4G page table for STM. + * 4M Non-PAE page table in IA32 version. + * + * @param page_table_base The page table base in MSEG + */ +void stm_gen_4g_pagetable_ia32(uint32_t pagetable_base); + +/* + * Create 4G page table for STM. + * 2M PAE page table in X64 version. + * + * @param pagetable_base The page table base in MSEG + */ +void stm_gen_4g_pagetable_x64(uint32_t pagetable_base); + +#endif diff --git a/src/security/intel/stm/StmApi.h b/src/security/intel/stm/StmApi.h new file mode 100644 index 0000000000..342ceeacf6 --- /dev/null +++ b/src/security/intel/stm/StmApi.h @@ -0,0 +1,726 @@ +/* @file + * STM API definition + * + * Copyright (c) 2015, Intel Corporation. All rights reserved. + * This program and the accompanying materials are licensed and made available + * under the terms and conditions of the BSD License which accompanies this + * distribution. The full text of the license may be found at + * http://opensource.org/licenses/bsd-license.php. + * + * THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + * WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, + * EITHER EXPRESS OR IMPLIED. + * + */ + +#ifndef _STM_API_H_ +#define _STM_API_H_ + +#include <stdint.h> + +// definition in STM spec + +#define STM_SPEC_VERSION_MAJOR 1 +#define STM_SPEC_VERSION_MINOR 0 + +#pragma pack(push, 1) + +#define STM_HARDWARE_FIELD_FILL_TO_2K (2048 - sizeof(uint32_t) * 8) +typedef struct { + uint32_t stm_header_revision; + uint32_t monitor_features; + uint32_t gdtr_limit; + uint32_t gdtr_base_offset; + uint32_t cs_selector; + uint32_t eip_offset; + uint32_t esp_offset; + uint32_t cr3_offset; + uint8_t reserved[STM_HARDWARE_FIELD_FILL_TO_2K]; +} HARDWARE_STM_HEADER; + +#define STM_FEATURES_IA32E 0x1 + +typedef struct { + uint32_t intel_64mode_supported : 1; + uint32_t ept_supported : 1; + uint32_t mbz : 30; +} STM_FEAT; + +typedef struct { + uint8_t stm_spec_ver_major; + uint8_t stm_pec_ver_minor; + uint16_t mbz; + uint32_t static_image_size; + uint32_t per_proc_dynamic_memory_size; + uint32_t additional_dynamic_memory_size; + STM_FEAT stm_features; + uint32_t number_of_rev_ids; + uint32_t stm_smm_rev_id[1]; + + // The total STM_HEADER should be 4K. +} SOFTWARE_STM_HEADER; + +typedef struct { + HARDWARE_STM_HEADER hw_stm_hdr; + SOFTWARE_STM_HEADER sw_stm_hdr; +} STM_HEADER; + +#define SHA1 1 +#define SHA256 2 +typedef struct { + uint64_t bios_component_base; + uint32_t image_size; + uint32_t hash_algorithm; // SHA1 or SHA256 + uint8_t hash[32]; +} TXT_BIOS_COMPONENT_STATUS; + +#define PAGE_SIZE 4096 +typedef struct { + uint32_t image_size; + uint32_t reserved; + uint64_t image_page_base[1]; //[NumberOfPages]; +} TXT_BIOS_COMPONENT_UPDATE; + +typedef struct { + uint64_t spe_rip; + uint64_t spe_rsp; + uint16_t spe_ss; + uint16_t page_violation_exception : 1; + uint16_t msr_violation_exception : 1; + uint16_t register_violation_exception : 1; + uint16_t io_violation_exception : 1; + uint16_t pci_violation_exception : 1; + uint16_t reserved1 : 11; + uint32_t reserved2; +} STM_PROTECTION_EXCEPTION_HANDLER; + +typedef struct { + uint8_t execution_disable_outside_smrr : 1; + uint8_t intel_64mode : 1; + uint8_t cr4_pae : 1; + uint8_t cr4_pse : 1; + uint8_t reserved1 : 4; +} STM_SMM_ENTRY_STATE; + +typedef struct { + uint8_t smram_to_vmcs_restore_required : 1; // BIOS restore hint + uint8_t reinitialize_vmcs_required : 1; // BIOS request + uint8_t reserved2 : 6; +} STM_SMM_RESUME_STATE; + +typedef struct { + uint8_t domain_type : 4; // STM input to BIOS on each SM + uint8_t x_state_policy : 2; // STM input to BIOS on each SMI + uint8_t ept_enabled : 1; + uint8_t reserved3 : 1; +} STM_SMM_STATE; + +typedef struct { + uint64_t signature; + uint16_t size; + uint8_t smm_descriptor_ver_major; + uint8_t smm_descriptor_ver_minor; + uint32_t local_apic_id; + STM_SMM_ENTRY_STATE smm_entry_state; + STM_SMM_RESUME_STATE smm_resume_state; + STM_SMM_STATE stm_smm_state; + uint8_t reserved4; + uint16_t smm_cs; + uint16_t smm_ds; + uint16_t smm_ss; + uint16_t smm_other_segment; + uint16_t smm_tr; + uint16_t reserved5; + uint64_t smm_cr3; + uint64_t smm_stm_setup_rip; + uint64_t smm_stm_teardown_rip; + uint64_t smm_smi_handler_rip; + uint64_t smm_smi_handler_rsp; + uint64_t smm_gdt_ptr; + uint32_t smm_gdt_size; + uint32_t required_stm_smm_rev_id; + STM_PROTECTION_EXCEPTION_HANDLER stm_protection_exception_handler; + uint64_t reserved6; + uint64_t bios_hw_resource_requirements_ptr; + // extend area + uint64_t acpi_rsdp; + uint8_t physical_address_bits; +} TXT_PROCESSOR_SMM_DESCRIPTOR; + +#define TXT_PROCESSOR_SMM_DESCRIPTOR_SIGNATURE "TXTPSSIG" +#define TXT_PROCESSOR_SMM_DESCRIPTOR_VERSION_MAJOR 1 +#define TXT_PROCESSOR_SMM_DESCRIPTOR_VERSION_MINOR 0 + +#define SMM_PSD_OFFSET 0xfb00 + +typedef enum { + TxtSmmPageViolation = 1, + TxtSmmMsrViolation, + TxtSmmRegisterViolation, + TxtSmmIoViolation, + TxtSmmPciViolation +} TXT_SMM_PROTECTION_EXCEPTION_TYPE; + +typedef struct { + uint32_t rdi; + uint32_t rsi; + uint32_t rbp; + uint32_t rdx; + uint32_t rcx; + uint32_t rbx; + uint32_t rax; + uint32_t cr3; + uint32_t cr2; + uint32_t cr0; + uint32_t vmcs_exit_instruction_info; + uint32_t vmcs_exit_instruction_length; + uint64_t vmcs_exit_qualification; + uint32_t error_code; // TXT_SMM_PROTECTION_EXCEPTION_TYPE + uint32_t rip; + uint32_t cs; + uint32_t rflags; + uint32_t rsp; + uint32_t ss; +} STM_PROTECTION_EXCEPTION_STACK_FRAME_IA32; + +typedef struct { + uint64_t r15; + uint64_t r14; + uint64_t r13; + uint64_t r12; + uint64_t r11; + uint64_t r10; + uint64_t r9; + uint64_t r8; + uint64_t rdi; + uint64_t rsi; + uint64_t rbp; + uint64_t rdx; + uint64_t rcx; + uint64_t rbx; + uint64_t rax; + uint64_t cr8; + uint64_t cr3; + uint64_t cr2; + uint64_t cr0; + uint64_t vmcs_exit_instruction_info; + uint64_t vmcs_exit_instruction_length; + uint64_t vmcs_exit_qualification; + uint64_t error_code; // TXT_SMM_PROTECTION_EXCEPTION_TYPE + uint64_t rip; + uint64_t cs; + uint64_t rflags; + uint64_t rsp; + uint64_t ss; +} STM_PROTECTION_EXCEPTION_STACK_FRAME_X64; + +typedef union { + STM_PROTECTION_EXCEPTION_STACK_FRAME_IA32 *ia32_stack_frame; + STM_PROTECTION_EXCEPTION_STACK_FRAME_X64 *x64_stack_frame; +} STM_PROTECTION_EXCEPTION_STACK_FRAME; + +#define STM_SMM_REV_ID 0x80010100 + +typedef struct _STM_SMM_CPU_STATE { // Writable? + uint8_t reserved1[0x1d0]; // fc00h + uint32_t gdt_base_hi_dword; // fdd0h : NO + uint32_t ldt_base_hi_dword; // fdd4h : NO + uint32_t idt_base_hi_dword; // fdd8h : NO + uint8_t reserved2[0x4]; // fddch + uint64_t io_rdi; // fde0h : NO + // - restricted + uint64_t io_eip; // fde8h : YES + uint64_t io_rcx; // fdf0h : NO + // - restricted + uint64_t io_rsi; // fdf8h : NO + // - restricted + uint8_t reserved3[0x40]; // fe00h + uint32_t cr4; // fe40h : NO + uint8_t reserved4[0x48]; // fe44h + uint32_t gdt_base_lo_dword; // fe8ch : NO + uint32_t gdt_limit; // fe90h : NO + // - RESTRICTED + uint32_t idt_base_lo_dword; // fe94h : NO + uint32_t idt_limit; // fe98h : NO + // - RESTRICTED + uint32_t ldt_base_lo_dword; // fe9ch : NO + uint32_t ldt_limit; // fea0h : NO + // - RESTRICTED + uint32_t ldt_info; // fea4h : NO + // - RESTRICTED + uint8_t reserved5[0x30]; // fea8h + uint64_t eptp; // fed8h : NO + uint32_t enabled_ept; // fee0h : NO + uint8_t reserved6[0x14]; // fee4h + uint32_t smbase; // fef8h : YES + // - NO for STM + uint32_t smm_rev_id; // fefch : NO + uint16_t io_restart; // ff00h : YES + uint16_t auto_halt_restart; // ff02h : YES + uint8_t reserved7[0x18]; // ff04h + uint64_t r15; // ff1ch : YES + uint64_t r14; // ff24h : YES + uint64_t r13; // ff2ch : YES + uint64_t r12; // ff34h : YES + uint64_t r11; // ff3ch : YES + uint64_t r10; // ff44h : YES + uint64_t r9; // ff4ch : YES + uint64_t r8; // ff54h : YES + uint64_t rax; // ff5ch : YES + uint64_t rcx; // ff64h : YES + uint64_t rdx; // ff6ch : YES + uint64_t rbx; // ff74h : YES + uint64_t rsp; // ff7ch : YES + uint64_t rbp; // ff84h : YES + uint64_t rsi; // ff8ch : YES + uint64_t rdi; // ff94h : YES + uint64_t io_mem_addr; // ff9ch : NO + uint32_t io_misc; // ffa4h : NO + uint32_t es; // ffa8h : NO + uint32_t cs; // ffach : NO + uint32_t ss; // ffb0h : NO + uint32_t ds; // ffb4h : NO + uint32_t fs; // ffb8h : NO + uint32_t gs; // ffbch : NO + uint32_t ldtr; // ffc0h : NO + uint32_t tr; // ffc4h : NO + uint64_t dr7; // ffc8h : NO + uint64_t dr6; // ffd0h : NO + uint64_t rip; // ffd8h : YES + uint64_t ia32_efer; // ffe0h : YES + // - NO for STM + uint64_t rflags; // ffe8h : YES + uint64_t cr3; // fff0h : NO + uint64_t cr0; // fff8h : NO +} STM_SMM_CPU_STATE; + +// STM Mapping +typedef struct { + uint64_t physical_address; + uint64_t virtual_ddress; + uint32_t Page_count; + uint32_t Pat_cache_type; +} STM_MAP_ADDRESS_RANGE_DESCRIPTOR; + +#define ST_UC 0x00 +#define WC 0x01 +#define WT 0x04 +#define WP 0x05 +#define WB 0x06 +#define UC 0x07 +#define FOLLOW_MTRR 0xFFFFFFFF + +typedef struct { + uint64_t virtual_address; + uint32_t length; +} STM_UNMAP_ADDRESS_RANGE_DESCRIPTOR; + +typedef struct { + uint64_t interrupted_guest_virtual_address; + uint32_t length; + uint64_t interrupted_cr3; + uint64_t interrupted_eptp; + uint32_t map_to_smm_guest : 2; + uint32_t interrupted_cr4_pae : 1; + uint32_t interrupted_cr4_pse : 1; + uint32_t interrupted_ia32e_mode : 1; + uint32_t reserved1 : 27; + uint32_t reserved2; + uint64_t physical_address; + uint64_t smm_guest_virtual_address; +} STM_ADDRESS_LOOKUP_DESCRIPTOR; + +#define DO_NOT_MAP 0 +#define ONE_TO_ONE 1 +#define VIRTUAL_ADDRESS_SPECIFIED 3 + +// STM_RESOURCE_LIST +#define END_OF_RESOURCES 0 +#define MEM_RANGE 1 +#define IO_RANGE 2 +#define MMIO_RANGE 3 +#define MACHINE_SPECIFIC_REG 4 +#define PCI_CFG_RANGE 5 +#define TRAPPED_IO_RANGE 6 +#define ALL_RESOURCES 7 +#define REGISTER_VIOLATION 8 +#define MAX_DESC_TYPE 8 + +typedef struct { + uint32_t rsc_type; + uint16_t length; + uint16_t return_status : 1; + uint16_t reserved : 14; + uint16_t ignore_resource : 1; +} STM_RSC_DESC_HEADER; + +typedef struct { + STM_RSC_DESC_HEADER Hdr; + uint64_t resource_list_continuation; +} STM_RSC_END; + +// byte granular Memory range support +#define STM_RSC_BGM 0x4 + +typedef struct { + STM_RSC_DESC_HEADER hdr; + uint64_t base; + uint64_t length; + uint32_t rwx_attributes : 3; + uint32_t reserved : 29; + uint32_t reserved_2; +} STM_RSC_MEM_DESC; + +#define STM_RSC_MEM_R 0x1 +#define STM_RSC_MEM_W 0x2 +#define STM_RSC_MEM_X 0x4 + +typedef struct { + STM_RSC_DESC_HEADER hdr; + uint16_t base; + uint16_t length; + uint32_t reserved; +} STM_RSC_IO_DESC; + +// byte granular MMIO range support +#define STM_RSC_BGI 0x2 + +typedef struct { + STM_RSC_DESC_HEADER hdr; + uint64_t base; + uint64_t length; + uint32_t rwx_attributes : 3; + uint32_t reserved : 29; + uint32_t reserved_2; +} STM_RSC_MMIO_DESC; + +#define STM_RSC_MMIO_R 0x1 +#define STM_RSC_MMIO_W 0x2 +#define STM_RSC_MMIO_X 0x4 + +typedef struct { + STM_RSC_DESC_HEADER hdr; + uint32_t msr_index; + uint32_t kernel_mode_processing : 1; + uint32_t reserved : 31; + uint64_t read_mask; + uint64_t write_mask; +} STM_RSC_MSR_DESC; + +// bit granular MSR resource support +#define STM_RSC_MSR 0x8 + +typedef struct { + uint8_t type; // must be 1, indicating Hardware Device Path + uint8_t subtype; // must be 1, indicating PCI + uint16_t length; // sizeof(STM_PCI_DEVICE_PATH_NODE) which is 6 + uint8_t pci_function; + uint8_t pci_device; +} STM_PCI_DEVICE_PATH_NODE; + +typedef struct { + STM_RSC_DESC_HEADER hdr; + uint16_t rw_attributes : 2; + uint16_t reserved : 14; + uint16_t base; + uint16_t length; + uint8_t originating_bus_number; + uint8_t last_node_index; + STM_PCI_DEVICE_PATH_NODE pci_device_path[1]; + // STM_PCI_DEVICE_PATH_NODE PciDevicePath[LastNodeIndex + 1]; +} STM_RSC_PCI_CFG_DESC; + +#define STM_RSC_PCI_CFG_R 0x1 +#define STM_RSC_PCI_CFG_W 0x2 + +typedef struct { + STM_RSC_DESC_HEADER hdr; + uint16_t base; + uint16_t length; + uint16_t in : 1; + uint16_t out : 1; + uint16_t api : 1; + uint16_t reserved1 : 13; + uint16_t reserved2; +} STM_RSC_TRAPPED_IO_DESC; + +typedef struct { + STM_RSC_DESC_HEADER hdr; +} STM_RSC_ALL_RESOURCES_DESC; + +typedef struct { + STM_RSC_DESC_HEADER hdr; + uint32_t register_type; + uint32_t reserved; + uint64_t readMask; + uint64_t write_mask; +} STM_REGISTER_VIOLATION_DESC; + +typedef enum { + stm_register_cr0, + stm_register_cr2, + stm_register_cr3, + stm_register_cr4, + stm_register_cr8, + stm_register_max, +} STM_REGISTER_VIOLATION_TYPE; + +typedef union { + STM_RSC_DESC_HEADER header; + STM_RSC_END end; + STM_RSC_MEM_DESC mem; + STM_RSC_IO_DESC io; + STM_RSC_MMIO_DESC mmio; + STM_RSC_MSR_DESC msr; + STM_RSC_PCI_CFG_DESC pci_cfg; + STM_RSC_TRAPPED_IO_DESC trapped_io; + STM_RSC_ALL_RESOURCES_DESC all; + STM_REGISTER_VIOLATION_DESC register_violation; +} STM_RSC; + +// VMCS database +#define STM_VMCS_DATABASE_REQUEST_ADD 1 +#define STM_VMCS_DATABASE_REQUEST_REMOVE 0 + +// Values for DomainType +// Interpreter of DomainType +#define DOMAIN_DISALLOWED_IO_OUT (1u << 0) +#define DOMAIN_DISALLOWED_IO_IN (1u << 1) +#define DOMAIN_INTEGRITY (1u << 2) +#define DOMAIN_CONFIDENTIALITY (1u << 3) + +#define DOMAIN_UNPROTECTED 0x00 +#define DOMAIN_INTEGRITY_PROT_OUT_IN (DOMAIN_INTEGRITY) +#define DOMAIN_FULLY_PROT_OUT_IN (DOMAIN_CONFIDENTIALITY | DOMAIN_INTEGRITY) +#define DOMAIN_FULLY_PROT \ + (DOMAIN_CONFIDENTIALITY | DOMAIN_INTEGRITY | DOMAIN_DISALLOWED_IO_IN \ + | DOMAIN_DISALLOWED_IO_OUT) + +// Values for XStatePolicy +#define XSTATE_READWRITE 0x00 +#define XSTATE_READONLY 0x01 +#define XSTATE_SCRUB 0x03 + +typedef struct { + uint64_t vmcs_phys_pointer; // bits 11:0 are reserved and must be 0 + uint32_t domain_type : 4; + uint32_t x_state_policy : 2; + uint32_t degradation_policy : 4; + uint32_t reserved1 : 22; // Must be 0 + uint32_t add_or_remove; +} STM_VMCS_DATABASE_REQUEST; + +// Event log +#define NEW_LOG 1 +#define CONFIGURE_LOG 2 +#define START_LOG 3 +#define STOP_LOG 4 +#define CLEAR_LOG 5 +#define DELETE_LOG 6 +typedef enum { + evt_log_started, + evt_log_stopped, + evt_log_invalid_parameter_detected, + evt_handled_protection_exception, + // unhandled protection exceptions result in reset & cannot be logged + evt_bios_access_to_unclaimed_resource, + evt_mle_resource_protection_granted, + evt_mle_resource_protection_denied, + evt_mle_resource_unprotect, + evt_mle_resource_unprotect_error, + evt_mle_domain_type_degraded, + // add more here + evt_mle_max, + // Not used + evt_invalid = 0xFFFFFFFF, +} EVENT_TYPE; + +typedef struct { + uint32_t page_count; + uint64_t pages[1]; // number of elements is PageCount +} STM_EVENT_LOG_MANAGEMENT_REQUEST_DATA_LOG_BUFFER; + +typedef union { + STM_EVENT_LOG_MANAGEMENT_REQUEST_DATA_LOG_BUFFER log_buffer; + uint32_t event_enable_bitmap; // bitmap of EVENT_TYPE +} STM_EVENT_LOG_MANAGEMENT_REQUEST_DATA; + +typedef struct { + uint32_t sub_functionindex; + STM_EVENT_LOG_MANAGEMENT_REQUEST_DATA data; +} STM_EVENT_LOG_MANAGEMENT_REQUEST; + +// VMCALL API Numbers +// +// API number convention: BIOS facing VMCALL interfaces have bit 16 clear +#define STM_API_MAP_ADDRESS_RANGE 0x00000001 +#define STM_API_UNMAP_ADDRESS_RANGE 0x00000002 +#define STM_API_ADDRESS_LOOKUP 0x00000003 +#define STM_API_RETURN_FROM_PROTECTION_EXCEPTION 0x00000004 + +// API number convention: MLE facing VMCALL interfaces have bit 16 set +// +// The STM configuration lifecycle is as follows: +// 1. SENTER->SINIT->MLE: MLE begins execution with SMI disabled (masked). +// 2. MLE invokes InitializeProtectionVMCALL() to prepare STM for setup of +// initial protection profile. This is done on a single CPU and has global +// effect. +// 3. MLE invokes ProtectResourceVMCALL() to define the initial protection +// profile. The protection profile is global across all CPUs. +// 4. MLE invokes StartStmVMCALL() to enable the STM to begin receiving SMI +// events. This must be done on every logical CPU. +// 5. MLE may invoke ProtectResourceVMCALL() or UnProtectResourceVMCALL() +// during runtime as many times as necessary. +// 6. MLE invokes StopStmVMCALL() to disable the STM. SMI is again masked +// following StopStmVMCALL(). +// +#define STM_API_START 0x00010001 +#define STM_API_STOP 0x00010002 +#define STM_API_PROTECT_RESOURCE 0x00010003 +#define STM_API_UNPROTECT_RESOURCE 0x00010004 +#define STM_API_GET_BIOS_RESOURCES 0x00010005 +#define STM_API_MANAGE_VMCS_DATABASE 0x00010006 +#define STM_API_INITIALIZE_PROTECTION 0x00010007 +#define STM_API_MANAGE_EVENT_LOG 0x00010008 + +// Return codes +typedef uint32_t STM_STATUS; + +#define STM_SUCCESS 0x00000000 +#define SMM_SUCCESS 0x00000000 +// all error codes have bit 31 set +// STM errors have bit 16 set +#define ERROR_STM_SECURITY_VIOLATION 0x80010001 +#define ERROR_STM_CACHE_TYPE_NOT_SUPPORTED 0x80010002 +#define ERROR_STM_PAGE_NOT_FOUND 0x80010003 +#define ERROR_STM_BAD_CR3 0x80010004 +#define ERROR_STM_PHYSICAL_OVER_4G 0x80010005 +#define ERROR_STM_VIRTUAL_SPACE_TOO_SMALL 0x80010006 +#define ERROR_STM_UNPROTECTABLE_RESOURCE 0x80010007 +#define ERROR_STM_ALREADY_STARTED 0x80010008 +#define ERROR_STM_WITHOUT_SMX_UNSUPPORTED 0x80010009 +#define ERROR_STM_STOPPED 0x8001000A +#define ERROR_STM_BUFFER_TOO_SMALL 0x8001000B +#define ERROR_STM_INVALID_VMCS_DATABASE 0x8001000C +#define ERROR_STM_MALFORMED_RESOURCE_LIST 0x8001000D +#define ERROR_STM_INVALID_PAGECOUNT 0x8001000E +#define ERROR_STM_LOG_ALLOCATED 0x8001000F +#define ERROR_STM_LOG_NOT_ALLOCATED 0x80010010 +#define ERROR_STM_LOG_NOT_STOPPED 0x80010011 +#define ERROR_STM_LOG_NOT_STARTED 0x80010012 +#define ERROR_STM_RESERVED_BIT_SET 0x80010013 +#define ERROR_STM_NO_EVENTS_ENABLED 0x80010014 +#define ERROR_STM_OUT_OF_RESOURCES 0x80010015 +#define ERROR_STM_FUNCTION_NOT_SUPPORTED 0x80010016 +#define ERROR_STM_UNPROTECTABLE 0x80010017 +#define ERROR_STM_UNSUPPORTED_MSR_BIT 0x80010018 +#define ERROR_STM_UNSPECIFIED 0x8001FFFF + +// SMM errors have bit 17 set +#define ERROR_SMM_BAD_BUFFER 0x80020001 +#define ERROR_SMM_INVALID_RSC 0x80020004 +#define ERROR_SMM_INVALID_BUFFER_SIZE 0x80020005 +#define ERROR_SMM_BUFFER_TOO_SHORT 0x80020006 +#define ERROR_SMM_INVALID_LIST 0x80020007 +#define ERROR_SMM_OUT_OF_MEMORY 0x80020008 +#define ERROR_SMM_AFTER_INIT 0x80020009 +#define ERROR_SMM_UNSPECIFIED 0x8002FFFF + +// Errors that apply to both have bits 15, 16, and 17 set +#define ERROR_INVALID_API 0x80038001 +#define ERROR_INVALID_PARAMETER 0x80038002 + +// STM TXT.ERRORCODE codes +#define STM_CRASH_PROTECTION_EXCEPTION 0xC000F001 +#define STM_CRASH_PROTECTION_EXCEPTION_FAILURE 0xC000F002 +#define STM_CRASH_DOMAIN_DEGRADATION_FAILURE 0xC000F003 +#define STM_CRASH_BIOS_PANIC 0xC000E000 + +typedef struct { + uint32_t event_serial_number; + uint16_t type; + uint16_t lock : 1; + uint16_t valid : 1; + uint16_t read_by_mle : 1; + uint16_t wrapped : 1; + uint16_t reserved : 12; +} LOG_ENTRY_HEADER; + +typedef struct { + uint32_t reserved; +} ENTRY_EVT_LOG_STARTED; + +typedef struct { + uint32_t reserved; +} ENTRY_EVT_LOG_STOPPED; + +typedef struct { + uint32_t vmcall_api_number; +} ENTRY_EVT_LOG_INVALID_PARAM; + +typedef struct { + STM_RSC resource; +} ENTRY_EVT_LOG_HANDLED_PROTECTION_EXCEPTION; + +typedef struct { + STM_RSC resource; +} ENTRY_EVT_BIOS_ACCESS_UNCLAIMED_RSC; + +typedef struct { + STM_RSC resource; +} ENTRY_EVT_MLE_RSC_PROT_GRANTED; + +typedef struct { + STM_RSC resource; +} ENTRY_EVT_MLE_RSC_PROT_DENIED; + +typedef struct { + STM_RSC resource; +} ENTRY_EVT_MLE_RSC_UNPROT; + +typedef struct { + STM_RSC resource; +} ENTRY_EVT_MLE_RSC_UNPROT_ERROR; + +typedef struct { + uint64_t vmcs_phys_pointer; + uint8_t expected_domain_type; + uint8_t degraded_domain_type; +} ENTRY_EVT_MLE_DOMAIN_TYPE_DEGRADED; + +typedef union { + ENTRY_EVT_LOG_STARTED started; + ENTRY_EVT_LOG_STOPPED stopped; + ENTRY_EVT_LOG_INVALID_PARAM invalid_param; + ENTRY_EVT_LOG_HANDLED_PROTECTION_EXCEPTION + handled_protection_exception; + ENTRY_EVT_BIOS_ACCESS_UNCLAIMED_RSC bios_unclaimed_rsc; + ENTRY_EVT_MLE_RSC_PROT_GRANTED mle_rsc_prot_granted; + ENTRY_EVT_MLE_RSC_PROT_DENIED mle_rsc_prot_denied; + ENTRY_EVT_MLE_RSC_UNPROT mle_rsc_unprot; + ENTRY_EVT_MLE_RSC_UNPROT_ERROR mle_rsc_unprot_error; + ENTRY_EVT_MLE_DOMAIN_TYPE_DEGRADED mle_domain_type_degraded; +} LOG_ENTRY_DATA; + +typedef struct { + LOG_ENTRY_HEADER hdr; + LOG_ENTRY_DATA data; +} STM_LOG_ENTRY; + +#define STM_LOG_ENTRY_SIZE 256 +#define STM_CONFIG_SMI_UNBLOCKING_BY_VMX_OFF 0x1 + +// TXT debug +#define SW_SMI_STM_ADD_RUNTIME_RESOURCES_SUB_FUNC 0 +#define SW_SMI_STM_READ_BIOS_RESOURCES_SUB_FUNC 1 +#define SW_SMI_STM_REPLACE_BIOS_RESOURCES_SUB_FUNC 2 + +typedef struct { + uint32_t buffer_size; + uint32_t reserved; + // uint8_t Data[]; +} TXT_BIOS_DEBUG; + +#pragma pack(pop) + +#endif diff --git a/src/security/intel/stm/StmPlatformResource.c b/src/security/intel/stm/StmPlatformResource.c new file mode 100644 index 0000000000..6fef515052 --- /dev/null +++ b/src/security/intel/stm/StmPlatformResource.c @@ -0,0 +1,188 @@ +/* @file + * STM platform SMM resource + * + * Copyright (c) 2015, Intel Corporation. All rights reserved. + * This program and the accompanying materials are licensed and made + * available under the terms and conditions of the BSD License which + * accompanies this distribution. The full text of the license may be found + * at http://opensource.org/licenses/bsd-license.php. + * + * THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + * WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR + * IMPLIED. + */ + +#include <stdint.h> +#include <security/intel/stm/StmApi.h> +#include <security/intel/stm/SmmStm.h> +#include <security/intel/stm/StmPlatformResource.h> + +#if CONFIG(SOUTHBRIDGE_INTEL_COMMON_PMCLIB) +#include <southbridge/intel/common/pmutil.h> +#else +#include <soc/pm.h> +#endif +#include <cpu/x86/msr.h> +#include <console/console.h> + +#define RDWR_ACCS 3 +#define FULL_ACCS 7 + +// Fixed memory ranges +// +// TSEG memory! +static STM_RSC_MEM_DESC rsc_tseg_memory = {{MEM_RANGE, sizeof(STM_RSC_MEM_DESC)}, + 0, + 0, + FULL_ACCS}; + +// Flash part +static STM_RSC_MEM_DESC rsc_spi_memory = { + {MEM_RANGE, sizeof(STM_RSC_MEM_DESC)}, + 0xFE000000, + 0x01000000, + FULL_ACCS}; + +// ACPI +static STM_RSC_IO_DESC rsc_pm_io = {{IO_RANGE, sizeof(STM_RSC_IO_DESC)}, 0, 128}; + +// PCIE MMIO +static STM_RSC_MMIO_DESC rsc_pcie_mmio = {{MMIO_RANGE, sizeof(STM_RSC_MMIO_DESC)}, + 0, + 0, // Length + RDWR_ACCS}; + +// Local APIC +static STM_RSC_MMIO_DESC rsc_apic_mmio = {{MMIO_RANGE, sizeof(STM_RSC_MMIO_DESC)}, + 0, + 0x400, + RDWR_ACCS}; + +// Software SMI +static STM_RSC_TRAPPED_IO_DESC rsc_sw_smi_trap_io = { + {TRAPPED_IO_RANGE, sizeof(STM_RSC_TRAPPED_IO_DESC)}, + 0xB2, + 2}; + +// End of list +static STM_RSC_END rsc_list_end __attribute__((used)) = { + {END_OF_RESOURCES, sizeof(STM_RSC_END)}, 0}; + +// Common PCI devices +// +// LPC bridge +STM_RSC_PCI_CFG_DESC rsc_lpc_bridge_pci = { + {PCI_CFG_RANGE, sizeof(STM_RSC_PCI_CFG_DESC)}, + RDWR_ACCS, + 0, + 0, + 0x1000, + 0, + 0, + { + {1, 1, sizeof(STM_PCI_DEVICE_PATH_NODE), LPC_FUNCTION, + LPC_DEVICE}, + }, +}; + +// Template for MSR resources. +STM_RSC_MSR_DESC rsc_msr_tpl = { + {MACHINE_SPECIFIC_REG, sizeof(STM_RSC_MSR_DESC)}, +}; + +// MSR indices to register +typedef struct { + uint32_t msr_index; + uint64_t read_mask; + uint64_t write_mask; +} MSR_TABLE_ENTRY; + +MSR_TABLE_ENTRY msr_table[] = { + // Index Read Write + // MASK64 means need access, MASK0 means no need access. + {SMRR_PHYSBASE_MSR, MASK64, MASK0}, + {SMRR_PHYSMASK_MSR, MASK64, MASK0}, +}; + +/* + * Fix up PCIE resource. + */ +static void fixup_pciex_resource(void) +{ + // Find max bus number and PCIEX length + rsc_pcie_mmio.length = CONFIG_SA_PCIEX_LENGTH; // 0x10000000;// 256 MB + rsc_pcie_mmio.base = CONFIG_MMCONF_BASE_ADDRESS; +} + +/* + * Add basic resources to BIOS resource database. + */ +static void add_simple_resources(void) +{ + int Status = 0; + msr_t ReadMsr; + + ReadMsr = rdmsr(SMRR_PHYSBASE_MSR); + rsc_tseg_memory.base = ReadMsr.lo & 0xFFFFF000; + + ReadMsr = rdmsr(SMRR_PHYSMASK_MSR); + rsc_tseg_memory.length = (~(ReadMsr.lo & 0xFFFFF000) + 1); + + rsc_pm_io.base = (uint16_t)get_pmbase(); + + // Local APIC. We assume that all thteads are programmed identically + // despite that it is possible to have individual APIC address for + // each of the threads. If this is the case this programming should + // be corrected. + ReadMsr = rdmsr(IA32_APIC_BASE_MSR_INDEX); + rsc_apic_mmio.base = ((uint64_t)ReadMsr.lo & 0xFFFFF000) | + ((uint64_t)(ReadMsr.hi & 0x0000000F) << 32); + + // PCIEX BAR + fixup_pciex_resource(); + + Status |= add_pi_resource((void *)&rsc_tseg_memory, 1); + Status |= add_pi_resource((void *)&rsc_spi_memory, 1); + + Status |= add_pi_resource((void *)&rsc_pm_io, 1); + Status |= add_pi_resource((void *)&rsc_pcie_mmio, 1); + Status |= add_pi_resource((void *)&rsc_apic_mmio, 1); + Status |= add_pi_resource((void *)&rsc_sw_smi_trap_io, 1); + + Status |= add_pi_resource((void *)&rsc_lpc_bridge_pci, 1); + + if (Status != 0) + printk(BIOS_DEBUG, "STM - Error in adding simple resources\n"); +} + +/* + * Add MSR resources to BIOS resource database. + */ +static void add_msr_resources(void) +{ + uint32_t Status = 0; + uint32_t Index; + + for (Index = 0; Index < ARRAY_SIZE(msr_table); Index++) { + + rsc_msr_tpl.msr_index = (uint32_t)msr_table[Index].msr_index; + rsc_msr_tpl.read_mask = (uint64_t)msr_table[Index].read_mask; + rsc_msr_tpl.write_mask = (uint64_t)msr_table[Index].write_mask; + + Status |= add_pi_resource((void *)&rsc_msr_tpl, 1); + } + + if (Status != 0) + printk(BIOS_DEBUG, "STM - Error in adding MSR resources\n"); +} + +/* + * Add resources to BIOS resource database. + */ +void add_resources_cmd(void) +{ + + add_simple_resources(); + + add_msr_resources(); +} diff --git a/src/security/intel/stm/StmPlatformResource.h b/src/security/intel/stm/StmPlatformResource.h new file mode 100644 index 0000000000..7db2fc0330 --- /dev/null +++ b/src/security/intel/stm/StmPlatformResource.h @@ -0,0 +1,32 @@ +/* @file + * STM platform SMM resource + * + * Copyright (c) 2015, Intel Corporation. All rights reserved. + * This program and the accompanying materials are licensed and made available + * under the terms and conditions of the BSD License which accompanies this + * distribution. The full text of the license may be found at + * http://opensource.org/licenses/bsd-license.php. + * + * THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + * WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR + * IMPLIED. + */ + +#ifndef _STM_PLATFORM_RESOURCE_H_ +#define _STM_PLATFORM_RESOURCE_H_ + +#define MASK0 0 +#define MASK64 0xFFFFFFFFFFFFFFFFull + +// LPC + +#define LPC_DEVICE 31 +#define LPC_FUNCTION 0 +#define R_ACPI_PM_BASE 0x40 +#define ACPI_PM_BASE_MASK 0xFFF8 + +/* + * Add resources to BIOS resource database. + */ +void add_resources_cmd(void); +#endif diff --git a/src/security/intel/stm/StmPlatformSmm.c b/src/security/intel/stm/StmPlatformSmm.c new file mode 100644 index 0000000000..d7064b07f5 --- /dev/null +++ b/src/security/intel/stm/StmPlatformSmm.c @@ -0,0 +1,204 @@ +/* @file + * STM platform SMM API + * + * Copyright (c) 2015, Intel Corporation. All rights reserved. + * This program and the accompanying materials are licensed and made + * available under the terms and conditions of the BSD License which + * accompanies this distribution. The full text of the license may be found + * at http://opensource.org/licenses/bsd-license.php. + * + * THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + * WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR + * IMPLIED. + * + */ + +#include <security/intel/stm/StmApi.h> +#include <security/intel/stm/SmmStm.h> +#include <security/intel/stm/StmPlatformResource.h> +#include <security/tpm/tspi.h> +#include <cpu/x86/smm.h> +#include <cpu/x86/msr.h> + +#include <stddef.h> +#include <cbfs.h> +#include <console/console.h> +#include <lib.h> +#include <stdint.h> +#include <arch/rom_segs.h> + +/* + * Load STM image to MSEG + * + * @retval SUCCESS STM is loaded to MSEG + */ +int load_stm_image(uintptr_t mseg) +{ + int status; + void *mseg_base; + uint32_t stm_buffer_size; + uint32_t stm_image_size; + bool stm_status; + + STM_HEADER *stm_header; + + // Extract STM image from FV + mseg_base = (void *)mseg; + stm_buffer_size = CONFIG_MSEG_SIZE; + stm_image_size = 0; + + memset((void *)mseg_base, 0, CONFIG_MSEG_SIZE); // clear the mseg + + stm_image_size = cbfs_boot_load_file("stm.bin", mseg_base, + stm_buffer_size, CBFS_TYPE_RAW); + printk(BIOS_DEBUG, "STM:loaded into mseg: 0x%p size: %u\n", mseg_base, + stm_image_size); + /* status is number of bytes loaded */ + stm_status = stm_check_stm_image(mseg_base, stm_image_size); + + if (!stm_status) { + printk(BIOS_DEBUG, "STM: Error in STM image\n"); + return -1; + } + + stm_header = mseg_base; + + stm_gen_4g_pagetable_x64((uint32_t)mseg_base + + stm_header->hw_stm_hdr.cr3_offset); + + // Debug stuff + printk(BIOS_DEBUG, + "STM: Header-Revision %d Features 0x%08x Cr3Offset 0x%08x\n", + stm_header->hw_stm_hdr.stm_header_revision, + stm_header->hw_stm_hdr.monitor_features, + stm_header->hw_stm_hdr.cr3_offset); + printk(BIOS_DEBUG, + "STM: Header-StaticImageSize: %d Cr3Location: 0x%08x\n", + stm_header->sw_stm_hdr.static_image_size, + ((uint32_t)mseg_base + stm_header->hw_stm_hdr.cr3_offset)); + + status = 0; // always return good for now + + return status; +} + +struct descriptor { + uint16_t limit; + uintptr_t base; +} __attribute__((packed)); + + +static void read_gdtr(struct descriptor *gdtr) +{ + __asm__ __volatile__("sgdt %0" : "=m"(*gdtr)); +} + +void setup_smm_descriptor(void *smbase, void *base_smbase, int32_t apic_id, + int32_t entry32_off) +{ + struct descriptor gdtr; + void *smbase_processor; + //msr_t smbase_msr; + + TXT_PROCESSOR_SMM_DESCRIPTOR *psd; + + smbase_processor = (void *) SMM_DEFAULT_BASE;//we are here + psd = smbase + SMM_PSD_OFFSET; + + printk(BIOS_DEBUG, + "STM: Smm Descriptor setup: Smbase: %p Smbase_processor: %p Psd: %p\n", + smbase, + smbase_processor, + psd); + + memset(psd, 0, sizeof(TXT_PROCESSOR_SMM_DESCRIPTOR)); + + memcpy(&psd->signature, TXT_PROCESSOR_SMM_DESCRIPTOR_SIGNATURE, 8); + psd->smm_descriptor_ver_major = + TXT_PROCESSOR_SMM_DESCRIPTOR_VERSION_MAJOR; + psd->smm_descriptor_ver_minor = + TXT_PROCESSOR_SMM_DESCRIPTOR_VERSION_MINOR; + psd->smm_smi_handler_rip = + (uint64_t)((uintptr_t)base_smbase + SMM_ENTRY_OFFSET + + entry32_off); + psd->local_apic_id = apic_id; + psd->size = sizeof(TXT_PROCESSOR_SMM_DESCRIPTOR); + psd->acpi_rsdp = 0; + psd->bios_hw_resource_requirements_ptr = + (uint64_t)((uintptr_t)get_stm_resource()); + psd->smm_cs = ROM_CODE_SEG; + psd->smm_ds = ROM_DATA_SEG; + psd->smm_ss = ROM_DATA_SEG; + psd->smm_other_segment = ROM_DATA_SEG; + psd->smm_tr = SMM_TASK_STATE_SEG; + + + // At this point the coreboot smm_stub is relative to the default + // smbase and not the one for the smi handler in tseg. So we have + // to adjust the gdtr.base + + read_gdtr(&gdtr); + + gdtr.base -= (uintptr_t) smbase_processor; + gdtr.base += (uintptr_t) base_smbase; + + psd->smm_gdt_ptr = gdtr.base; + psd->smm_gdt_size = gdtr.limit + 1; // the stm will subtract, so add + printk(BIOS_DEBUG, "STM: Smm Descriptor setup complete - Smbase: %p Psd: %p\n", + smbase, psd); +} + +extern uint8_t *stm_resource_heap; + +#define FXSAVE_SIZE 512 + +static int stm_load_status = 0; + +void stm_setup(uintptr_t mseg, int cpu, int num_cpus, uintptr_t smbase, + uintptr_t base_smbase, uint32_t offset32) +{ + msr_t InitMseg; + msr_t MsegChk; + uintptr_t addr_calc; // used to calculate the stm resource heap area + + printk(BIOS_DEBUG, "STM: set up for cpu %d/%d\n", cpu, num_cpus); + if (cpu == 0) { + + // need to create the BIOS resource list once + // first calculate the location in SMRAM + addr_calc = (mseg - (CONFIG_SMM_MODULE_STACK_SIZE * num_cpus)); + + if (CONFIG(SSE)) + addr_calc -= FXSAVE_SIZE * num_cpus; + + addr_calc -= CONFIG_BIOS_RESOURCE_LIST_SIZE; + stm_resource_heap = (uint8_t *) addr_calc; + printk(BIOS_DEBUG, "STM: stm_resource_heap located at %p\n", + stm_resource_heap); + //setup the the list + add_resources_cmd(); + + stm_load_status = load_stm_image(mseg); + } + + if (stm_load_status == 0) { + // enable STM for this cpu + InitMseg.lo = mseg | IA32_SMM_MONITOR_VALID; + InitMseg.hi = 0; + + wrmsr(IA32_SMM_MONITOR_CTL_MSR, InitMseg); + + MsegChk = rdmsr(IA32_SMM_MONITOR_CTL_MSR); + + printk(BIOS_DEBUG, "STM: MSEG Initialized (%d) 0x%08x 0x%08x\n", + cpu, MsegChk.hi, MsegChk.lo); + + // setup the descriptor for this cpu + setup_smm_descriptor((void *)smbase, (void *) base_smbase, + cpu, offset32); + } else { + printk(BIOS_DEBUG, + "STM: Error in STM load, STM not enabled: %d\n", + cpu); + } +} |