diff --git a/.gitmodules b/.gitmodules index 010ab6b49d..14782d43c8 100644 --- a/.gitmodules +++ b/.gitmodules @@ -34,6 +34,7 @@ url = ../intel-microcode.git update = none ignore = dirty + branch = main [submodule "3rdparty/ffs"] path = 3rdparty/ffs url = ../ffs.git @@ -42,3 +43,7 @@ url = ../amd_blobs update = none ignore = dirty +[submodule "3rdparty/stm"] + path = 3rdparty/stm + url = ../STM + branch = stmpe diff --git a/3rdparty/intel-microcode b/3rdparty/intel-microcode index 1dd14da6d1..49bb67f32a 160000 --- a/3rdparty/intel-microcode +++ b/3rdparty/intel-microcode @@ -1 +1 @@ -Subproject commit 1dd14da6d1ea5cfbd95923653f31c04aac3aa655 +Subproject commit 49bb67f32a2e3e631ba1a9a73da1c52e1cac7fd9 diff --git a/Documentation/mainboard/index.md b/Documentation/mainboard/index.md index 60302cbe0f..39cd9a4d9d 100644 --- a/Documentation/mainboard/index.md +++ b/Documentation/mainboard/index.md @@ -99,6 +99,10 @@ The boards in this section are not real mainboards, but emulators. - [MS-7707](msi/ms7707/ms7707.md) +## OCP + +- [Mono Lake](ocp/monolake.md) + ## Open Cellular - [Elgon](opencellular/elgon.md) diff --git a/Documentation/mainboard/ocp/monolake.md b/Documentation/mainboard/ocp/monolake.md new file mode 100644 index 0000000000..e825bfff7b --- /dev/null +++ b/Documentation/mainboard/ocp/monolake.md @@ -0,0 +1,146 @@ +# OCP Mono Lake + +This page describes coreboot support status for the [OCP] (Open Compute Project) +Mono Lake server platform. + +## Introduction + +OCP Mono Lake server platform is a component of multi-host server Yosemite-V1. +Facebook [introduced Yosemite] in 2015 and the [Mono Lake was accepted] to OCP in 2019. + +Mono Lake server is a single socket BroadwellDE server. + +Yosemite-V1 may host up to 4 Mono Lake servers (blades) in one sled. + +The Yosemite-V1 system is in mass production. Facebook, Intel, ITRenew and partners +jointly develop Open System Firmware (OSF) solution on Mono Lake as an alternative +solution. The OSF solution is based on coreboot/FSP/LinuxBoot stack. A firmware binary +is available from ITRenew. + +coreboot Mono Lake is supported on the coreboot version 4.11 branch. +The Broadwell-DE FSP v1.0 support was dropped after 4.11. + + +## Required blobs + +Mono Lake server OSF solution requires: +- [FSP] blob: SysPro custom FSP is required to support the 16-core Broadwell-DE on Mono Lake +- Microcode: Available through github.com:otcshare/Intel-Generic-Microcode.git +- ME binary: Available under NDA with Intel + +## Payload +- LinuxBoot: Linuxboot is the prefered coreboot payload for Mono Lake. + It can be built following [All about u-root]. + + Tested configuration: + Linux Kernel v5.10.44 + [u-root Mono Lake pull request] + u-root build command: + GO111MODULE=off u-root -build=bb -uinitcmd=systemboot \ + -files="${FLASHROMDIR}/flashrom:bin/flashrom" \ + -files="${VPDDIR}/vpd:bin/vpd" \ + core github.com/u-root/u-root/cmds/boot/{systemboot,pxeboot,boot} \ + github.com/u-root/u-root/cmds/exp/{cbmem,dmidecode,modprobe,ipmidump,netbootxyz} \ + + note: flashrom and vpd binaries must be precompiled + +## Flashing coreboot + +Mono Lake in-band BIOS firmware image update uses [flashrom]: + flashrom -p internal:ich_spi_mode=hwseq -c "Opaque flash chip" --ifd \ + -i bios --noverify-all -w + +Yosemite-V1 OpenBMC BIOS firmware image update uses fw-util: + fw-util slotx --update bios + +## Yosimite-V1 server controls + +To power off/on a Mono Lake host: + power-util slotx off + power-util slotx on + +To connect to coreboot and Linux console through SOL (Serial Over Lan): + sol-util slotx + +## Firmware configurations +[ChromeOS VPD] is used to store most of the firmware configurations. +RO_VPD region holds default values, while RW_VPD region holds customized +values. + +VPD variables supported are: +- firmware_version: This variable holds overall firmware version. coreboot + uses that value to populate smbios type 1 version field. +- bmc_bootorder_override: When it's set to 1 IPMI OEM command can override boot + order. The boot order override is done in the u-root LinuxBoot payload. +- systemboot_log_level: u-root package systemboot log levels, would be mapped to + quiet/verbose in systemboot as that is all we have for now. 5 to 8 would be + mapped to verbose, 0 to 4 and 9 would be mapped to quiet. +- VPDs affecting coreboot are in src/mainboard/ocp/monolake/mainboard.c. + No coreboot VPD are required, uses safe defaults. + +## Working features +The solution is developed using LinuxBoot payload with Linux kernel 5.2.9, +and [u-root] as initramfs. +- BMC integration: + - BMC readiness check + - IPMI commands + - watchdog timer + - POST complete pin acknowledgement + - Check BMC version: ipmidump -device +- Early serial output +- ACPI tables: DMAR/DSDT/FACP/FACS/HPET/MCFG/SPMI/SRAT/SLIT/SSDT/XSDT +- FSP MRC cache support (Skipping memory training upon subsequent reboots) +- Versions + - Check FSP version: cbmem | grep LB_TAG_PLATFORM_BLOB_VERSION + - Check Microcode version: cat /proc/cpuinfo | grep microcode +- Devices: + - Boot drive + - All 5 data drives + - NIC card +- SMBIOS: + - Type 0 – BIOS Information + - Type 1 – System Information + - Type 2 – Baseboard Information + - Type 3 – System Enclosure or Chassis + - Type 4 – Processor Information + - Type 7 – Cache Information + - Type 16 – Physical Memory Array + - Type 17 – Memory Device + - Type 32 – System Boot Information + - Type 38 – IPMI Device Information + - Type 41 – Onboard Devices Extended Information + - Type 127 – End-of-Table +- Power button +- u-root boot +- u-root pxeboot + +## Stress and Performance tests passed + +## Known issues + +### Feature gaps +- flashrom command not able to update ME region +- ACPI BERT table +- PCIe hotplug through VPP (Virtual Pin Ports) +- RO_VPD region as well as other RO regions are not write protected +- Not able to selectively enable/disable core + +## Technology + +```eval_rst ++------------------------+---------------------------------------------+ +| Processor (1 socket) | Broadwell-DE | ++------------------------+---------------------------------------------+ +| BMC | Aspeed AST 1250 | ++------------------------+---------------------------------------------+ +``` + +[OCP]: https://www.opencompute.org +[introduced Yosemite]: https://engineering.fb.com/2015/03/10/core-data/introducing-yosemite-the-first-open-source-modular-chassis-for-high-powered-microservers/ +[Mono Lake was accepted]: https://www.opencompute.org/contributions?query=Tioga%20Pass%20v1.0 +[FSP]: https://doc.coreboot.org/soc/intel/fsp/index.html +[u-root Mono Lake pull request]: https://github.com/u-root/u-root/pull/2045 +[flashrom]: https://flashrom.org/Flashrom +[All about u-root]: https://github.com/linuxboot/book/tree/master/u-root +[u-root]: https://u-root.org/ +[ChromeOS VPD]: https://chromium.googlesource.com/chromiumos/platform/vpd/+/master/README.md diff --git a/Makefile.inc b/Makefile.inc index 66144cd2ac..be198d6580 100644 --- a/Makefile.inc +++ b/Makefile.inc @@ -159,7 +159,7 @@ ws_to_under=$(shell echo '$1' | tr ' \t' '_') ####################################################################### # Helper functions for ramstage postprocess spc := -spc += +spc := $(spc) $(spc) comma := , # Returns all files and dirs below `dir` (recursively). @@ -261,7 +261,16 @@ EMPTY_RESOURCE_TEMPLATE_WARNING = 3150 # Redundant offset remarks are not useful in any way and are masking useful # ones that might indicate an issue so it is better to hide them. REDUNDANT_OFFSET_REMARK = 2158 +# Ignore _HID & _ADR coexisting in Intel Lynxpoint and Broadwell ASL code. +# See cb:38803 & cb:38802 +# "Multiple types (Device object requires either a _HID or _ADR, but not both)" +MULTIPLE_TYPES_WARNING = 3073 + +ifeq ($(CONFIG_SOUTHBRIDGE_INTEL_LYNXPOINT)$(CONFIG_SOC_INTEL_BROADWELL),y) +IGNORED_IASL_WARNINGS = -vw $(EMPTY_RESOURCE_TEMPLATE_WARNING) -vw $(REDUNDANT_OFFSET_REMARK) -vw $(MULTIPLE_TYPES_WARNING) +else IGNORED_IASL_WARNINGS = -vw $(EMPTY_RESOURCE_TEMPLATE_WARNING) -vw $(REDUNDANT_OFFSET_REMARK) +endif define asl_template $(CONFIG_CBFS_PREFIX)/$(1).aml-file = $(obj)/$(1).aml @@ -469,7 +478,7 @@ ADAFLAGS_common += -gnatwa.eeD.HHTU.U.W.Y # Disable style checks for now ADAFLAGS_common += -gnatyN -LDFLAGS_common := --gc-sections -nostdlib -nostartfiles -static --emit-relocs +LDFLAGS_common := --gc-sections -nostdlib --nmagic -static --emit-relocs ifeq ($(CONFIG_WARNINGS_ARE_ERRORS),y) CFLAGS_common += -Werror diff --git a/configs/config.purism_librem15_v4.txt_build_test b/configs/config.purism_librem15_v4.txt_build_test new file mode 100644 index 0000000000..f2de8bc59f --- /dev/null +++ b/configs/config.purism_librem15_v4.txt_build_test @@ -0,0 +1,8 @@ +# Not meant for actual use. Exercises Intel TXT code. Since BIOS +# and SINIT ACM blobs are missing, use something else as placeholder. +CONFIG_VENDOR_PURISM=y +CONFIG_BOARD_PURISM_LIBREM15_V4=y +CONFIG_INTEL_TXT=y +CONFIG_INTEL_TXT_BIOSACM_FILE="3rdparty/blobs/cpu/intel/stm/stm.bin" +CONFIG_INTEL_TXT_SINITACM_FILE="3rdparty/blobs/cpu/intel/stm/stm.bin" +CONFIG_INTEL_TXT_LOGGING=y diff --git a/configs/config.stm b/configs/config.stm new file mode 100644 index 0000000000..59792b2fa5 --- /dev/null +++ b/configs/config.stm @@ -0,0 +1,4 @@ +CONFIG_VENDOR_PURISM=y +CONFIG_BOARD_PURISM_LIBREM15_V4=y +CONFIG_STM=y +CONFIG_IED_REGION_SIZE=0 diff --git a/src/Kconfig b/src/Kconfig index ba9ae86067..43b92a0130 100644 --- a/src/Kconfig +++ b/src/Kconfig @@ -730,6 +730,16 @@ config SMBIOS_ENCLOSURE_TYPE convertible, or tablet enclosure will be used if the appropriate system type is selected. +config VPD_SMBIOS_VERSION + bool "Populates SMBIOS type 0 version from the VPD_RO variable 'firmware_version'" + default n + depends on VPD && GENERATE_SMBIOS_TABLES + help + Selecting this option will read firmware_version from + VPD_RO and override SMBIOS type 0 version. One special + scenario of using this feature is to assign a BIOS version + to a coreboot image without the need to rebuild from source. + endmenu source "payloads/Kconfig" @@ -1021,6 +1031,13 @@ config DEBUG_SPI_FLASH help This option enables additional SPI flash related debug messages. +config DEBUG_IPMI + bool "Output verbose IPMI debug messages" + default n + depends on IPMI_KCS + help + This option enables additional IPMI related debug messages. + if SOUTHBRIDGE_INTEL_BD82X6X && DEFAULT_CONSOLE_LOGLEVEL_8 # Only visible with the right southbridge and loglevel. config DEBUG_INTEL_ME diff --git a/src/arch/x86/include/arch/rom_segs.h b/src/arch/x86/include/arch/rom_segs.h index a19d3de461..c11def6b02 100644 --- a/src/arch/x86/include/arch/rom_segs.h +++ b/src/arch/x86/include/arch/rom_segs.h @@ -18,4 +18,11 @@ #define ROM_DATA_SEG 0x10 #define ROM_CODE_SEG64 0x18 +/* + * This define is placed here to make sure future romstage programmers + * know about it. + * It is used for STM setup code. + */ +#define SMM_TASK_STATE_SEG 0x20 + #endif /* ROM_SEGS_H */ diff --git a/src/arch/x86/postcar_loader.c b/src/arch/x86/postcar_loader.c index b53cbf82af..868b770c18 100644 --- a/src/arch/x86/postcar_loader.c +++ b/src/arch/x86/postcar_loader.c @@ -228,8 +228,6 @@ void run_postcar_phase(struct postcar_frame *pcf) /* As postcar exist, it's end of romstage here */ timestamp_add_now(TS_END_ROMSTAGE); - console_time_report(); - prog_set_arg(&prog, cbmem_top()); prog_run(&prog); diff --git a/src/arch/x86/smbios.c b/src/arch/x86/smbios.c index 725d808d56..f23e1dc3cd 100644 --- a/src/arch/x86/smbios.c +++ b/src/arch/x86/smbios.c @@ -32,6 +32,8 @@ #if CONFIG(CHROMEOS) #include #endif +#include +#include #define update_max(len, max_len, stmt) \ do { \ @@ -381,12 +383,64 @@ static int create_smbios_type17_for_dimm(struct dimm_info *dimm, return t->length + smbios_string_table_len(t->eos); } +#define VERSION_VPD "firmware_version" +static const char *vpd_get_bios_version(void) +{ + int size; + const char *s; + char *version; + + s = vpd_find(VERSION_VPD, &size, VPD_RO); + if (!s) { + printk(BIOS_ERR, "Find version from VPD %s failed\n", VERSION_VPD); + return NULL; + } + + version = malloc(size + 1); + if (!version) { + printk(BIOS_ERR, "Failed to malloc %d bytes for VPD version\n", size + 1); + return NULL; + } + memcpy(version, s, size); + version[size] = '\0'; + printk(BIOS_DEBUG, "Firmware version %s from VPD %s\n", version, VERSION_VPD); + return version; +} + +static const char *get_bios_version(void) +{ + const char *s; + +#define SPACES \ + " " + + if (CONFIG(CHROMEOS)) + return SPACES; + + if (CONFIG(VPD_SMBIOS_VERSION)) { + s = vpd_get_bios_version(); + if (s != NULL) + return s; + } + + s = smbios_mainboard_bios_version(); + if (s != NULL) + return s; + + if (strlen(CONFIG_LOCALVERSION) != 0) { + printk(BIOS_DEBUG, "BIOS version set to CONFIG_LOCALVERSION: '%s'\n", + CONFIG_LOCALVERSION); + return CONFIG_LOCALVERSION; + } + + printk(BIOS_DEBUG, "SMBIOS firmware version is set to coreboot_version: '%s'\n", + coreboot_version); + return coreboot_version; +} + const char *__weak smbios_mainboard_bios_version(void) { - if (strlen(CONFIG_LOCALVERSION)) - return CONFIG_LOCALVERSION; - else - return coreboot_version; + return NULL; } static int smbios_write_type0(unsigned long *current, int handle) @@ -400,27 +454,14 @@ static int smbios_write_type0(unsigned long *current, int handle) t->length = len - 2; t->vendor = smbios_add_string(t->eos, "coreboot"); -#if !CONFIG(CHROMEOS) t->bios_release_date = smbios_add_string(t->eos, coreboot_dmi_date); - t->bios_version = smbios_add_string(t->eos, - smbios_mainboard_bios_version()); -#else -#define SPACES \ - " " - t->bios_release_date = smbios_add_string(t->eos, coreboot_dmi_date); -#if CONFIG(HAVE_ACPI_TABLES) +#if CONFIG(CHROMEOS) && CONFIG(HAVE_ACPI_TABLES) u32 version_offset = (u32)smbios_string_table_len(t->eos); -#endif - t->bios_version = smbios_add_string(t->eos, SPACES); - -#if CONFIG(HAVE_ACPI_TABLES) /* SMBIOS offsets start at 1 rather than 0 */ - chromeos_get_chromeos_acpi()->vbt10 = - (u32)t->eos + (version_offset - 1); + chromeos_get_chromeos_acpi()->vbt10 = (u32)t->eos + (version_offset - 1); #endif -#endif /* CONFIG_CHROMEOS */ - + t->bios_version = smbios_add_string(t->eos, get_bios_version()); uint32_t rom_size = CONFIG_ROM_SIZE; rom_size = MIN(CONFIG_ROM_SIZE, 16 * MiB); t->bios_rom_size = (rom_size / 65535) - 1; @@ -657,7 +698,26 @@ static int smbios_write_type4(unsigned long *current, int handle) t->processor_version = smbios_processor_name(t->eos); t->processor_family = (res.eax > 0) ? 0x0c : 0x6; t->processor_type = 3; /* System Processor */ - t->core_count = (res.ebx >> 16) & 0xff; + /* + * If CPUID leaf 11 is available, calculate "core count" by dividing + * SMT_ID (logical processors in a core) by Core_ID (number of cores). + * This seems to be the way to arrive to a number of cores mentioned on + * ark.intel.com. + */ + if (cpu_have_cpuid() && cpuid_get_max_func() >= 0xb) { + uint32_t leaf_b_cores = 0, leaf_b_threads = 0; + res = cpuid_ext(0xb, 1); + leaf_b_cores = res.ebx; + res = cpuid_ext(0xb, 0); + leaf_b_threads = res.ebx; + /* if hyperthreading is not available, pretend this is 1 */ + if (leaf_b_threads == 0) { + leaf_b_threads = 1; + } + t->core_count = leaf_b_cores / leaf_b_threads; + } else { + t->core_count = (res.ebx >> 16) & 0xff; + } /* Assume we enable all the cores always, capped only by MAX_CPUS */ t->core_enabled = MIN(t->core_count, CONFIG_MAX_CPUS); t->l1_cache_handle = 0xffff; diff --git a/src/console/printk.c b/src/console/printk.c index 4f9f547bc5..15c599dce0 100644 --- a/src/console/printk.c +++ b/src/console/printk.c @@ -21,56 +21,14 @@ #include #include #include -#include #if (!defined(__PRE_RAM__) && CONFIG(HAVE_ROMSTAGE_CONSOLE_SPINLOCK)) || !CONFIG(HAVE_ROMSTAGE_CONSOLE_SPINLOCK) DECLARE_SPIN_LOCK(console_lock) #endif -#define TRACK_CONSOLE_TIME (CONFIG(HAVE_MONOTONIC_TIMER) && \ - (ENV_RAMSTAGE || !CONFIG(CAR_GLOBAL_MIGRATION))) - -static struct mono_time mt_start, mt_stop; -static long console_usecs; - -static void console_time_run(void) -{ - if (TRACK_CONSOLE_TIME) - timer_monotonic_get(&mt_start); -} - -static void console_time_stop(void) -{ - if (TRACK_CONSOLE_TIME) { - timer_monotonic_get(&mt_stop); - console_usecs += mono_time_diff_microseconds(&mt_start, &mt_stop); - } -} - -void console_time_report(void) -{ - if (!TRACK_CONSOLE_TIME) - return; - - printk(BIOS_DEBUG, "Accumulated console time in " ENV_STRING " %ld ms\n", - DIV_ROUND_CLOSEST(console_usecs, USECS_PER_MSEC)); -} - -long console_time_get_and_reset(void) -{ - if (!TRACK_CONSOLE_TIME) - return 0; - - long elapsed = console_usecs; - console_usecs = 0; - return elapsed; -} - void do_putchar(unsigned char byte) { - console_time_run(); console_tx_byte(byte); - console_time_stop(); } static void wrap_putchar(unsigned char byte, void *data) @@ -103,8 +61,6 @@ int do_vprintk(int msg_level, const char *fmt, va_list args) spin_lock(&console_lock); #endif - console_time_run(); - if (log_this == CONSOLE_LOG_FAST) { i = vtxprintf(wrap_putchar_cbmemc, fmt, args, NULL); } else { @@ -112,8 +68,6 @@ int do_vprintk(int msg_level, const char *fmt, va_list args) console_tx_flush(); } - console_time_stop(); - #ifdef __PRE_RAM__ #if CONFIG(HAVE_ROMSTAGE_CONSOLE_SPINLOCK) spin_unlock(romstage_console_lock()); diff --git a/src/cpu/x86/Kconfig b/src/cpu/x86/Kconfig index 85ebd831ea..1c714d1656 100644 --- a/src/cpu/x86/Kconfig +++ b/src/cpu/x86/Kconfig @@ -137,6 +137,14 @@ config SMM_STUB_STACK_SIZE endif +config X86_SMM_LOADER_VERSION2 + bool + default n + depends on HAVE_SMI_HANDLER + help + This option enables SMM module loader that works with server + platforms which may contain more than 32 CPU threads. + config SMM_LAPIC_REMAP_MITIGATION bool default y if NORTHBRIDGE_INTEL_I945 diff --git a/src/cpu/x86/mp_init.c b/src/cpu/x86/mp_init.c index 29ae3de87a..313fb34113 100644 --- a/src/cpu/x86/mp_init.c +++ b/src/cpu/x86/mp_init.c @@ -37,6 +37,8 @@ #include #include +#include + #define MAX_APIC_IDS 256 struct mp_callback { @@ -734,13 +736,39 @@ static void asmlinkage smm_do_relocation(void *arg) * the location of the new SMBASE. If using SMM modules then this * calculation needs to match that of the module loader. */ +#if CONFIG(X86_SMM_LOADER_VERSION2) + perm_smbase = smm_get_cpu_smbase(cpu); + mp_state.perm_smbase = perm_smbase; + if (!perm_smbase) { + printk(BIOS_ERR, "%s: bad SMBASE for CPU %d\n", __func__, cpu); + return; + } +#else perm_smbase = mp_state.perm_smbase; perm_smbase -= cpu * runtime->save_state_size; - - printk(BIOS_DEBUG, "New SMBASE 0x%08lx\n", perm_smbase); +#endif /* Setup code checks this callback for validity. */ + printk(BIOS_INFO, "%s : curr_smbase 0x%x perm_smbase 0x%x, cpu = %d\n", + __func__, (int)curr_smbase, (int)perm_smbase, cpu); mp_state.ops.relocation_handler(cpu, curr_smbase, perm_smbase); + + if (CONFIG(STM)) { + if (is_smm_enabled()) { + uintptr_t mseg; + size_t mseg_size; + + smm_subregion(SMM_SUBREGION_MSEG, &mseg, &mseg_size); + + stm_setup(mseg, p->cpu, runtime->num_cpus, + perm_smbase, + mp_state.perm_smbase, + runtime->start32_offset); + } else { + printk(BIOS_DEBUG, + "STM not loaded because SMM is not enabled!\n"); + } + } } static void adjust_smm_apic_id_map(struct smm_loader_params *smm_params) @@ -754,9 +782,17 @@ static void adjust_smm_apic_id_map(struct smm_loader_params *smm_params) static int install_relocation_handler(int num_cpus, size_t save_state_size) { + int cpus = num_cpus; +#if CONFIG(X86_SMM_LOADER_VERSION2) + /* Default SMRAM size is not big enough to concurrently + * handle relocation for more than ~32 CPU threads + * therefore, relocate 1 by 1. */ + cpus = 1; +#endif + struct smm_loader_params smm_params = { .per_cpu_stack_size = CONFIG_SMM_STUB_STACK_SIZE, - .num_concurrent_stacks = num_cpus, + .num_concurrent_stacks = cpus, .per_cpu_save_state_size = save_state_size, .num_concurrent_save_states = 1, .handler = smm_do_relocation, @@ -766,9 +802,10 @@ static int install_relocation_handler(int num_cpus, size_t save_state_size) if (mp_state.ops.adjust_smm_params != NULL) mp_state.ops.adjust_smm_params(&smm_params, 0); - if (smm_setup_relocation_handler(&smm_params)) + if (smm_setup_relocation_handler(&smm_params)) { + printk(BIOS_ERR, "%s: smm setup failed\n", __func__); return -1; - + } adjust_smm_apic_id_map(&smm_params); return 0; @@ -777,8 +814,13 @@ static int install_relocation_handler(int num_cpus, size_t save_state_size) static int install_permanent_handler(int num_cpus, uintptr_t smbase, size_t smsize, size_t save_state_size) { - /* There are num_cpus concurrent stacks and num_cpus concurrent save - * state areas. Lastly, set the stack size to 1KiB. */ + /* + * All the CPUs will relocate to permanaent handler now. Set parameters + * needed for all CPUs. The placement of each CPUs entry point is + * determined by the loader. This code simply provides the beginning of + * SMRAM region, the number of CPUs who will use the handler, the stack + * size and save state size for each CPU. + */ struct smm_loader_params smm_params = { .per_cpu_stack_size = CONFIG_SMM_MODULE_STACK_SIZE, .num_concurrent_stacks = num_cpus, @@ -790,7 +832,7 @@ static int install_permanent_handler(int num_cpus, uintptr_t smbase, if (mp_state.ops.adjust_smm_params != NULL) mp_state.ops.adjust_smm_params(&smm_params, 1); - printk(BIOS_DEBUG, "Installing SMM handler to 0x%08lx\n", smbase); + printk(BIOS_DEBUG, "Installing permanent SMM handler to 0x%08lx\n", smbase); if (smm_load_module((void *)smbase, smsize, &smm_params)) return -1; @@ -1020,6 +1062,21 @@ static void fill_mp_state(struct mp_state *state, const struct mp_ops *ops) ops->get_smm_info(&state->perm_smbase, &state->perm_smsize, &state->smm_save_state_size); + /* + * Make sure there is enough room for the SMM descriptor + */ + if (CONFIG(STM)) + state->smm_save_state_size += + sizeof(TXT_PROCESSOR_SMM_DESCRIPTOR); + + /* Currently, the CPU SMM save state size is based on a simplistic + * algorithm. (align on 4K) + * note: In the future, this will need to handle newer x86 processors + * that require alignment of the save state on 32K boundaries. + */ + state->smm_save_state_size = + ALIGN_UP(state->smm_save_state_size, 0x1000); + /* * Default to smm_initiate_relocation() if trigger callback isn't * provided. diff --git a/src/cpu/x86/smm/Makefile.inc b/src/cpu/x86/smm/Makefile.inc index 11a4e67199..3064194f72 100644 --- a/src/cpu/x86/smm/Makefile.inc +++ b/src/cpu/x86/smm/Makefile.inc @@ -11,7 +11,11 @@ ## GNU General Public License for more details. ## +ifeq ($(CONFIG_X86_SMM_LOADER_VERSION2),y) +ramstage-y += smm_module_loaderv2.c +else ramstage-y += smm_module_loader.c +endif ifeq ($(CONFIG_ARCH_RAMSTAGE_X86_32),y) $(eval $(call create_class_compiler,smm,x86_32)) diff --git a/src/cpu/x86/smm/smm_module_loader.c b/src/cpu/x86/smm/smm_module_loader.c index c6c6b38737..dd00b7f05c 100644 --- a/src/cpu/x86/smm/smm_module_loader.c +++ b/src/cpu/x86/smm/smm_module_loader.c @@ -17,6 +17,7 @@ #include #include #include +#include #define FXSAVE_SIZE 512 @@ -173,8 +174,9 @@ static void smm_stub_place_staggered_entry_points(char *base, * concurrent areas requested. The save state always lives at the top of SMRAM * space, and the entry point is at offset 0x8000. */ -static int smm_module_setup_stub(void *smbase, struct smm_loader_params *params, - void *fxsave_area) +static int smm_module_setup_stub(void *smbase, size_t smm_size, + struct smm_loader_params *params, + void *fxsave_area) { size_t total_save_state_size; size_t smm_stub_size; @@ -201,6 +203,8 @@ static int smm_module_setup_stub(void *smbase, struct smm_loader_params *params, /* Adjust remaining size to account for save state. */ total_save_state_size = params->per_cpu_save_state_size * params->num_concurrent_save_states; + if (total_save_state_size > size) + return -1; size -= total_save_state_size; /* The save state size encroached over the first SMM entry point. */ @@ -266,7 +270,9 @@ static int smm_module_setup_stub(void *smbase, struct smm_loader_params *params, stub_params->fxsave_area = (uintptr_t)fxsave_area; stub_params->fxsave_area_size = FXSAVE_SIZE; stub_params->runtime.smbase = (uintptr_t)smbase; + stub_params->runtime.smm_size = smm_size; stub_params->runtime.save_state_size = params->per_cpu_save_state_size; + stub_params->runtime.num_cpus = params->num_concurrent_stacks; /* Initialize the APIC id to CPU number table to be 1:1 */ for (i = 0; i < params->num_concurrent_stacks; i++) @@ -305,14 +311,20 @@ int smm_setup_relocation_handler(struct smm_loader_params *params) if (params->num_concurrent_stacks == 0) params->num_concurrent_stacks = CONFIG_MAX_CPUS; - return smm_module_setup_stub(smram, params, fxsave_area_relocation); + return smm_module_setup_stub(smram, SMM_DEFAULT_SIZE, + params, fxsave_area_relocation); } /* The SMM module is placed within the provided region in the following * manner: * +-----------------+ <- smram + size + * | BIOS resource | + * | list (STM) | + * +-----------------+ <- smram + size - CONFIG_BIOS_RESOURCE_LIST_SIZE * | stacks | - * +-----------------+ <- smram + size - total_stack_size + * +-----------------+ <- .. - total_stack_size + * | fxsave area | + * +-----------------+ <- .. - total_stack_size - fxsave_size * | ... | * +-----------------+ <- smram + handler_size + SMM_DEFAULT_SIZE * | handler | @@ -354,6 +366,10 @@ int smm_load_module(void *smram, size_t size, struct smm_loader_params *params) /* Stacks start at the top of the region. */ base = smram; base += size; + + if (CONFIG(STM)) + base -= CONFIG_BIOS_RESOURCE_LIST_SIZE; + params->stack_top = base; /* SMM module starts at offset SMM_DEFAULT_SIZE with the load alignment @@ -382,6 +398,7 @@ int smm_load_module(void *smram, size_t size, struct smm_loader_params *params) /* Does the required amount of memory exceed the SMRAM region size? */ total_size = total_stack_size + handler_size; total_size += fxsave_size + SMM_DEFAULT_SIZE; + if (total_size > size) return -1; @@ -391,5 +408,5 @@ int smm_load_module(void *smram, size_t size, struct smm_loader_params *params) params->handler = rmodule_entry(&smm_mod); params->handler_arg = rmodule_parameters(&smm_mod); - return smm_module_setup_stub(smram, params, fxsave_area); + return smm_module_setup_stub(smram, size, params, fxsave_area); } diff --git a/src/cpu/x86/smm/smm_module_loaderv2.c b/src/cpu/x86/smm/smm_module_loaderv2.c new file mode 100644 index 0000000000..8d01b3c0ee --- /dev/null +++ b/src/cpu/x86/smm/smm_module_loaderv2.c @@ -0,0 +1,666 @@ +/* + * This file is part of the coreboot project. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define FXSAVE_SIZE 512 +#define SMM_CODE_SEGMENT_SIZE 0x10000 +/* FXSAVE area during relocation. While it may not be strictly needed the + SMM stub code relies on the FXSAVE area being non-zero to enable SSE + instructions within SMM mode. */ +static uint8_t fxsave_area_relocation[CONFIG_MAX_CPUS][FXSAVE_SIZE] +__attribute__((aligned(16))); + +/* + * Components that make up the SMRAM: + * 1. Save state - the total save state memory used + * 2. Stack - stacks for the CPUs in the SMM handler + * 3. Stub - SMM stub code for calling into handler + * 4. Handler - C-based SMM handler. + * + * The components are assumed to consist of one consecutive region. + */ + +/* These parameters are used by the SMM stub code. A pointer to the params + * is also passed to the C-base handler. */ +struct smm_stub_params { + u32 stack_size; + u32 stack_top; + u32 c_handler; + u32 c_handler_arg; + u32 fxsave_area; + u32 fxsave_area_size; + struct smm_runtime runtime; +} __packed; + +/* + * The stub is the entry point that sets up protected mode and stacks for each + * CPU. It then calls into the SMM handler module. It is encoded as an rmodule. + */ +extern unsigned char _binary_smmstub_start[]; + +/* Per CPU minimum stack size. */ +#define SMM_MINIMUM_STACK_SIZE 32 + +struct cpu_smm_info { + uint8_t active; + uintptr_t smbase; + uintptr_t entry; + uintptr_t ss_start; + uintptr_t code_start; + uintptr_t code_end; +}; +struct cpu_smm_info cpus[CONFIG_MAX_CPUS] = { 0 }; + +/* + * This method creates a map of all the CPU entry points, save state locations + * and the beginning and end of code segments for each CPU. This map is used + * during relocation to properly align as many CPUs that can fit into the SMRAM + * region. For more information on how SMRAM works, refer to the latest Intel + * developer's manuals (volume 3, chapter 34). SMRAM is divided up into the + * following regions: + * +-----------------+ Top of SMRAM + * | | <- MSEG, FXSAVE + * +-----------------+ + * | common | + * | smi handler | 64K + * | | + * +-----------------+ + * | CPU 0 code seg | + * +-----------------+ + * | CPU 1 code seg | + * +-----------------+ + * | CPU x code seg | + * +-----------------+ + * | | + * | | + * +-----------------+ + * | stacks | + * +-----------------+ <- START of SMRAM + * + * The code below checks when a code segment is full and begins placing the remainder + * CPUs in the lower segments. The entry point for each CPU is smbase + 0x8000 + * and save state is smbase + 0x8000 + (0x8000 - state save size). Save state + * area grows downward into the CPUs entry point. Therefore staggering too many + * CPUs in one 32K block will corrupt CPU0's entry code as the save states move + * downward. + * input : smbase of first CPU (all other CPUs + * will go below this address) + * input : num_cpus in the system. The map will + * be created from 0 to num_cpus. + */ +static int smm_create_map(uintptr_t smbase, unsigned int num_cpus, + const struct smm_loader_params *params) +{ + unsigned int i; + struct rmodule smm_stub; + unsigned int ss_size = params->per_cpu_save_state_size, stub_size; + unsigned int smm_entry_offset = params->smm_main_entry_offset; + unsigned int seg_count = 0, segments = 0, available; + unsigned int cpus_in_segment = 0; + unsigned int base = smbase; + + if (rmodule_parse(&_binary_smmstub_start, &smm_stub)) { + printk(BIOS_ERR, "%s: unable to get SMM module size\n", __func__); + return 0; + } + + stub_size = rmodule_memory_size(&smm_stub); + /* How many CPUs can fit into one 64K segment? */ + available = 0xFFFF - smm_entry_offset - ss_size - stub_size; + if (available > 0) { + cpus_in_segment = available / ss_size; + /* minimum segments needed will always be 1 */ + segments = num_cpus / cpus_in_segment + 1; + printk(BIOS_DEBUG, + "%s: cpus allowed in one segment %d\n", __func__, cpus_in_segment); + printk(BIOS_DEBUG, + "%s: min # of segments needed %d\n", __func__, segments); + } else { + printk(BIOS_ERR, "%s: not enough space in SMM to setup all CPUs\n", __func__); + printk(BIOS_ERR, " save state & stub size need to be reduced\n"); + printk(BIOS_ERR, " or increase SMRAM size\n"); + return 0; + } + + if (sizeof(cpus) / sizeof(struct cpu_smm_info) < num_cpus) { + printk(BIOS_ERR, + "%s: increase MAX_CPUS in Kconfig\n", __func__); + return 0; + } + + for (i = 0; i < num_cpus; i++) { + cpus[i].smbase = base; + cpus[i].entry = base + smm_entry_offset; + cpus[i].ss_start = cpus[i].entry + (smm_entry_offset - ss_size); + cpus[i].code_start = cpus[i].entry; + cpus[i].code_end = cpus[i].entry + stub_size; + cpus[i].active = 1; + base -= ss_size; + seg_count++; + if (seg_count >= cpus_in_segment) { + base -= smm_entry_offset; + seg_count = 0; + } + } + + if (CONFIG_DEFAULT_CONSOLE_LOGLEVEL >= BIOS_DEBUG) { + seg_count = 0; + for (i = 0; i < num_cpus; i++) { + printk(BIOS_DEBUG, "CPU 0x%x\n", i); + printk(BIOS_DEBUG, + " smbase %zx entry %zx\n", + cpus[i].smbase, cpus[i].entry); + printk(BIOS_DEBUG, + " ss_start %zx code_end %zx\n", + cpus[i].ss_start, cpus[i].code_end); + seg_count++; + if (seg_count >= cpus_in_segment) { + printk(BIOS_DEBUG, + "-------------NEW CODE SEGMENT --------------\n"); + seg_count = 0; + } + } + } + return 1; +} + +/* + * This method expects the smm relocation map to be complete. + * This method does not read any HW registers, it simply uses a + * map that was created during SMM setup. + * input: cpu_num - cpu number which is used as an index into the + * map to return the smbase + */ +u32 smm_get_cpu_smbase(unsigned int cpu_num) +{ + if (cpu_num < CONFIG_MAX_CPUS) { + if (cpus[cpu_num].active) + return cpus[cpu_num].smbase; + } + return 0; +} + +/* + * This method assumes that at least 1 CPU has been set up from + * which it will place other CPUs below its smbase ensuring that + * save state does not clobber the first CPUs init code segment. The init + * code which is the smm stub code is the same for all CPUs. They enter + * smm, setup stacks (based on their apic id), enter protected mode + * and then jump to the common smi handler. The stack is allocated + * at the beginning of smram (aka tseg base, not smbase). The stack + * pointer for each CPU is calculated by using its apic id + * (code is in smm_stub.s) + * Each entry point will now have the same stub code which, sets up the CPU + * stack, enters protected mode and then jumps to the smi handler. It is + * important to enter protected mode before the jump because the "jump to + * address" might be larger than the 20bit address supported by real mode. + * SMI entry right now is in real mode. + * input: smbase - this is the smbase of the first cpu not the smbase + * where tseg starts (aka smram_start). All CPUs code segment + * and stack will be below this point except for the common + * SMI handler which is one segment above + * input: num_cpus - number of cpus that need relocation including + * the first CPU (though its code is already loaded) + * input: top of stack (stacks work downward by default in Intel HW) + * output: return -1, if runtime smi code could not be installed. In + * this case SMM will not work and any SMI's generated will + * cause a CPU shutdown or general protection fault because + * the appropriate smi handling code was not installed + */ + +static int smm_place_entry_code(uintptr_t smbase, unsigned int num_cpus, + unsigned int stack_top, const struct smm_loader_params *params) +{ + unsigned int i; + unsigned int size; + if (smm_create_map(smbase, num_cpus, params)) { + /* + * Ensure there was enough space and the last CPUs smbase + * did not encroach upon the stack. Stack top is smram start + * + size of stack. + */ + if (cpus[num_cpus].active) { + if (cpus[num_cpus - 1].smbase + + params->smm_main_entry_offset < stack_top) { + printk(BIOS_ERR, "%s: stack encroachment\n", __func__); + printk(BIOS_ERR, "%s: smbase %zx, stack_top %x\n", + __func__, cpus[num_cpus].smbase, stack_top); + return 0; + } + } + } else { + printk(BIOS_ERR, "%s: unable to place smm entry code\n", __func__); + return 0; + } + + printk(BIOS_INFO, "%s: smbase %zx, stack_top %x\n", + __func__, cpus[num_cpus-1].smbase, stack_top); + + /* start at 1, the first CPU stub code is already there */ + size = cpus[0].code_end - cpus[0].code_start; + for (i = 1; i < num_cpus; i++) { + memcpy((int *)cpus[i].code_start, (int *)cpus[0].code_start, size); + printk(BIOS_DEBUG, + "SMM Module: placing smm entry code at %zx, cpu # 0x%x\n", + cpus[i].code_start, i); + printk(BIOS_DEBUG, "%s: copying from %zx to %zx 0x%x bytes\n", + __func__, cpus[0].code_start, cpus[i].code_start, size); + } + return 1; +} + +/* + * Place stacks in base -> base + size region, but ensure the stacks don't + * overlap the staggered entry points. + */ +static void *smm_stub_place_stacks(char *base, size_t size, + struct smm_loader_params *params) +{ + size_t total_stack_size; + char *stacks_top; + + /* If stack space is requested assume the space lives in the lower + * half of SMRAM. */ + total_stack_size = params->per_cpu_stack_size * + params->num_concurrent_stacks; + printk(BIOS_DEBUG, "%s: cpus: %zx : stack space: needed -> %zx\n", + __func__, params->num_concurrent_stacks, + total_stack_size); + printk(BIOS_DEBUG, " available -> %zx : per_cpu_stack_size : %zx\n", + size, params->per_cpu_stack_size); + + /* There has to be at least one stack user. */ + if (params->num_concurrent_stacks < 1) + return NULL; + + /* Total stack size cannot fit. */ + if (total_stack_size > size) + return NULL; + + /* Stacks extend down to SMBASE */ + stacks_top = &base[total_stack_size]; + printk(BIOS_DEBUG, "%s: exit, stack_top %p\n", __func__, stacks_top); + + return stacks_top; +} + +/* + * Place the staggered entry points for each CPU. The entry points are + * staggered by the per CPU SMM save state size extending down from + * SMM_ENTRY_OFFSET. + */ +static int smm_stub_place_staggered_entry_points(char *base, + const struct smm_loader_params *params, const struct rmodule *smm_stub) +{ + size_t stub_entry_offset; + int rc = 1; + stub_entry_offset = rmodule_entry_offset(smm_stub); + /* Each CPU now has its own stub code, which enters protected mode, + * sets up the stack, and then jumps to common SMI handler + */ + if (params->num_concurrent_save_states > 1 || stub_entry_offset != 0) { + rc = smm_place_entry_code((unsigned int)base, + params->num_concurrent_save_states, + (unsigned int)params->stack_top, params); + } + return rc; +} + +/* + * The stub setup code assumes it is completely contained within the + * default SMRAM size (0x10000) for the default SMI handler (entry at + * 0x30000), but no assumption should be made for the permanent SMI handler. + * The placement of CPU entry points for permanent handler are determined + * by the number of CPUs in the system and the amount of SMRAM. + * There are potentially 3 regions to place + * within the default SMRAM size: + * 1. Save state areas + * 2. Stub code + * 3. Stack areas + * + * The save state and smm stack are treated as contiguous for the number of + * concurrent areas requested. The save state always lives at the top of the + * the CPUS smbase (and the entry point is at offset 0x8000). This allows only a certain + * number of CPUs with staggered entry points until the save state area comes + * down far enough to overwrite/corrupt the entry code (stub code). Therefore, + * an SMM map is created to avoid this corruption, see smm_create_map() above. + * This module setup code works for the default (0x30000) SMM handler setup and the + * permanent SMM handler. + */ +static int smm_module_setup_stub(void *smbase, size_t smm_size, + struct smm_loader_params *params, + void *fxsave_area) +{ + size_t total_save_state_size; + size_t smm_stub_size; + size_t stub_entry_offset; + char *smm_stub_loc; + void *stacks_top; + size_t size; + char *base; + size_t i; + struct smm_stub_params *stub_params; + struct rmodule smm_stub; + unsigned int total_size_all; + base = smbase; + size = smm_size; + + /* The number of concurrent stacks cannot exceed CONFIG_MAX_CPUS. */ + if (params->num_concurrent_stacks > CONFIG_MAX_CPUS) { + printk(BIOS_ERR, "%s: not enough stacks\n", __func__); + return -1; + } + + /* Fail if can't parse the smm stub rmodule. */ + if (rmodule_parse(&_binary_smmstub_start, &smm_stub)) { + printk(BIOS_ERR, "%s: unable to parse smm stub\n", __func__); + return -1; + } + + /* Adjust remaining size to account for save state. */ + total_save_state_size = params->per_cpu_save_state_size * + params->num_concurrent_save_states; + if (total_save_state_size > size) { + printk(BIOS_ERR, + "%s: more state save space needed:need -> %zx:available->%zx\n", + __func__, total_save_state_size, size); + return -1; + } + + size -= total_save_state_size; + + /* The save state size encroached over the first SMM entry point. */ + if (size <= params->smm_main_entry_offset) { + printk(BIOS_ERR, "%s: encroachment over SMM entry point\n", __func__); + printk(BIOS_ERR, "%s: state save size: %zx : smm_entry_offset -> %x\n", + __func__, size, params->smm_main_entry_offset); + return -1; + } + + /* Need a minimum stack size and alignment. */ + if (params->per_cpu_stack_size <= SMM_MINIMUM_STACK_SIZE || + (params->per_cpu_stack_size & 3) != 0) { + printk(BIOS_ERR, "%s: need minimum stack size\n", __func__); + return -1; + } + + smm_stub_loc = NULL; + smm_stub_size = rmodule_memory_size(&smm_stub); + stub_entry_offset = rmodule_entry_offset(&smm_stub); + + /* Put the stub at the main entry point */ + smm_stub_loc = &base[params->smm_main_entry_offset]; + + /* Stub is too big to fit. */ + if (smm_stub_size > (size - params->smm_main_entry_offset)) { + printk(BIOS_ERR, "%s: stub is too big to fit\n", __func__); + return -1; + } + + /* The stacks, if requested, live in the lower half of SMRAM space + * for default handler, but for relocated handler it lives at the beginning + * of SMRAM which is TSEG base + */ + size = params->num_concurrent_stacks * params->per_cpu_stack_size; + stacks_top = smm_stub_place_stacks((char *)params->smram_start, size, params); + if (stacks_top == NULL) { + printk(BIOS_ERR, "%s: not enough space for stacks\n", __func__); + printk(BIOS_ERR, "%s: ....need -> %p : available -> %zx\n", __func__, + base, size); + return -1; + } + params->stack_top = stacks_top; + /* Load the stub. */ + if (rmodule_load(smm_stub_loc, &smm_stub)) { + printk(BIOS_ERR, "%s: load module failed\n", __func__); + return -1; + } + + if (!smm_stub_place_staggered_entry_points(base, params, &smm_stub)) { + printk(BIOS_ERR, "%s: staggered entry points failed\n", __func__); + return -1; + } + + /* Setup the parameters for the stub code. */ + stub_params = rmodule_parameters(&smm_stub); + stub_params->stack_top = (uintptr_t)stacks_top; + stub_params->stack_size = params->per_cpu_stack_size; + stub_params->c_handler = (uintptr_t)params->handler; + stub_params->c_handler_arg = (uintptr_t)params->handler_arg; + stub_params->fxsave_area = (uintptr_t)fxsave_area; + stub_params->fxsave_area_size = FXSAVE_SIZE; + stub_params->runtime.smbase = (uintptr_t)smbase; + stub_params->runtime.smm_size = smm_size; + stub_params->runtime.save_state_size = params->per_cpu_save_state_size; + stub_params->runtime.num_cpus = params->num_concurrent_stacks; + + printk(BIOS_DEBUG, "%s: stack_end = 0x%x\n", + __func__, stub_params->runtime.smbase); + printk(BIOS_DEBUG, + "%s: stack_top = 0x%x\n", __func__, stub_params->stack_top); + printk(BIOS_DEBUG, "%s: stack_size = 0x%x\n", + __func__, stub_params->stack_size); + printk(BIOS_DEBUG, "%s: runtime.smbase = 0x%x\n", + __func__, stub_params->runtime.smbase); + printk(BIOS_DEBUG, "%s: runtime.start32_offset = 0x%x\n", __func__, + stub_params->runtime.start32_offset); + printk(BIOS_DEBUG, "%s: runtime.smm_size = 0x%zx\n", + __func__, smm_size); + printk(BIOS_DEBUG, "%s: per_cpu_save_state_size = 0x%x\n", + __func__, stub_params->runtime.save_state_size); + printk(BIOS_DEBUG, "%s: num_cpus = 0x%x\n", __func__, + stub_params->runtime.num_cpus); + printk(BIOS_DEBUG, "%s: total_save_state_size = 0x%x\n", + __func__, (stub_params->runtime.save_state_size * + stub_params->runtime.num_cpus)); + total_size_all = stub_params->stack_size + + (stub_params->runtime.save_state_size * + stub_params->runtime.num_cpus); + printk(BIOS_DEBUG, "%s: total_size_all = 0x%x\n", __func__, + total_size_all); + + /* Initialize the APIC id to CPU number table to be 1:1 */ + for (i = 0; i < params->num_concurrent_stacks; i++) + stub_params->runtime.apic_id_to_cpu[i] = i; + + /* Allow the initiator to manipulate SMM stub parameters. */ + params->runtime = &stub_params->runtime; + + printk(BIOS_DEBUG, "SMM Module: stub loaded at %p. Will call %p(%p)\n", + smm_stub_loc, params->handler, params->handler_arg); + return 0; +} + +/* + * smm_setup_relocation_handler assumes the callback is already loaded in + * memory. i.e. Another SMM module isn't chained to the stub. The other + * assumption is that the stub will be entered from the default SMRAM + * location: 0x30000 -> 0x40000. + */ +int smm_setup_relocation_handler(struct smm_loader_params *params) +{ + void *smram = (void *)(SMM_DEFAULT_BASE); + printk(BIOS_SPEW, "%s: enter\n", __func__); + /* There can't be more than 1 concurrent save state for the relocation + * handler because all CPUs default to 0x30000 as SMBASE. */ + if (params->num_concurrent_save_states > 1) + return -1; + + /* A handler has to be defined to call for relocation. */ + if (params->handler == NULL) + return -1; + + /* Since the relocation handler always uses stack, adjust the number + * of concurrent stack users to be CONFIG_MAX_CPUS. */ + if (params->num_concurrent_stacks == 0) + params->num_concurrent_stacks = CONFIG_MAX_CPUS; + + params->smm_main_entry_offset = SMM_ENTRY_OFFSET; + params->smram_start = SMM_DEFAULT_BASE; + params->smram_end = SMM_DEFAULT_BASE + SMM_DEFAULT_SIZE; + return smm_module_setup_stub(smram, SMM_DEFAULT_SIZE, + params, fxsave_area_relocation); + printk(BIOS_SPEW, "%s: exit\n", __func__); +} + +/* + *The SMM module is placed within the provided region in the following + * manner: + * +-----------------+ <- smram + size + * | BIOS resource | + * | list (STM) | + * +-----------------+ + * | fxsave area | + * +-----------------+ + * | smi handler | + * | ... | + * +-----------------+ <- cpu0 + * | stub code | <- cpu1 + * | stub code | <- cpu2 + * | stub code | <- cpu3, etc + * | | + * | | + * | | + * | stacks | + * +-----------------+ <- smram start + + * It should be noted that this algorithm will not work for + * SMM_DEFAULT_SIZE SMRAM regions such as the A segment. This algorithm + * expects a region large enough to encompass the handler and stacks + * as well as the SMM_DEFAULT_SIZE. + */ +int smm_load_module(void *smram, size_t size, struct smm_loader_params *params) +{ + struct rmodule smm_mod; + size_t total_stack_size; + size_t handler_size; + size_t module_alignment; + size_t alignment_size; + size_t fxsave_size; + void *fxsave_area; + size_t total_size = 0; + char *base; + + if (size <= SMM_DEFAULT_SIZE) + return -1; + + /* Load main SMI handler at the top of SMRAM + * everything else will go below + */ + base = smram; + base += size; + params->smram_start = (uintptr_t)smram; + params->smram_end = params->smram_start + size; + params->smm_main_entry_offset = SMM_ENTRY_OFFSET; + + /* Fail if can't parse the smm rmodule. */ + if (rmodule_parse(&_binary_smm_start, &smm_mod)) + return -1; + + /* Clear SMM region */ + if (CONFIG(DEBUG_SMI)) + memset(smram, 0xcd, size); + + total_stack_size = params->per_cpu_stack_size * + params->num_concurrent_stacks; + total_size += total_stack_size; + /* Stacks are the base of SMRAM */ + params->stack_top = smram + total_stack_size; + + /* MSEG starts at the top of SMRAM and works down */ + if (CONFIG(STM)) { + base -= CONFIG_BIOS_RESOURCE_LIST_SIZE; + total_size += CONFIG_BIOS_RESOURCE_LIST_SIZE; + } + + /* FXSAVE goes below MSEG */ + if (CONFIG(SSE)) { + fxsave_size = FXSAVE_SIZE * params->num_concurrent_stacks; + fxsave_area = base - fxsave_size; + base -= fxsave_size; + total_size += fxsave_size; + } else { + fxsave_size = 0; + fxsave_area = NULL; + } + + + handler_size = rmodule_memory_size(&smm_mod); + base -= handler_size; + total_size += handler_size; + module_alignment = rmodule_load_alignment(&smm_mod); + alignment_size = module_alignment - + ((uintptr_t)base % module_alignment); + if (alignment_size != module_alignment) { + handler_size += alignment_size; + base += alignment_size; + } + + printk(BIOS_DEBUG, + "%s: total_smm_space_needed %zx, available -> %zx\n", + __func__, total_size, size); + + /* Does the required amount of memory exceed the SMRAM region size? */ + if (total_size > size) { + printk(BIOS_ERR, "%s: need more SMRAM\n", __func__); + return -1; + } + if (handler_size > SMM_CODE_SEGMENT_SIZE) { + printk(BIOS_ERR, "%s: increase SMM_CODE_SEGMENT_SIZE: handler_size = %zx\n", + __func__, handler_size); + return -1; + } + + if (rmodule_load(base, &smm_mod)) + return -1; + + params->handler = rmodule_entry(&smm_mod); + params->handler_arg = rmodule_parameters(&smm_mod); + + printk(BIOS_DEBUG, "%s: smram_start: 0x%p\n", + __func__, smram); + printk(BIOS_DEBUG, "%s: smram_end: %p\n", + __func__, smram + size); + printk(BIOS_DEBUG, "%s: stack_top: %p\n", + __func__, params->stack_top); + printk(BIOS_DEBUG, "%s: handler start %p\n", + __func__, params->handler); + printk(BIOS_DEBUG, "%s: handler_size %zx\n", + __func__, handler_size); + printk(BIOS_DEBUG, "%s: handler_arg %p\n", + __func__, params->handler_arg); + printk(BIOS_DEBUG, "%s: fxsave_area %p\n", + __func__, fxsave_area); + printk(BIOS_DEBUG, "%s: fxsave_size %zx\n", + __func__, fxsave_size); + printk(BIOS_DEBUG, "%s: CONFIG_MSEG_SIZE 0x%x\n", + __func__, CONFIG_MSEG_SIZE); + printk(BIOS_DEBUG, "%s: CONFIG_BIOS_RESOURCE_LIST_SIZE 0x%x\n", + __func__, CONFIG_BIOS_RESOURCE_LIST_SIZE); + + /* CPU 0 smbase goes first, all other CPUs + * will be staggered below + */ + base -= SMM_CODE_SEGMENT_SIZE; + printk(BIOS_DEBUG, "%s: cpu0 entry: %p\n", + __func__, base); + params->smm_entry = (uintptr_t)base + params->smm_main_entry_offset; + return smm_module_setup_stub(base, size, params, fxsave_area); +} diff --git a/src/cpu/x86/smm/smm_stub.S b/src/cpu/x86/smm/smm_stub.S index f0e55f9a18..aa4022389f 100644 --- a/src/cpu/x86/smm/smm_stub.S +++ b/src/cpu/x86/smm/smm_stub.S @@ -42,8 +42,15 @@ fxsave_area_size: smm_runtime: smbase: .long 0 +smm_size: +.long 0 save_state_size: .long 0 +num_cpus: +.long 0 +/* allows the STM to bring up SMM in 32-bit mode */ +start32_offset: +.long smm_trampoline32 - _start /* apic_to_cpu_num is a table mapping the default APIC id to CPU num. If the * APIC id is found at the given index, the contiguous CPU number is index * into the table. */ @@ -90,6 +97,14 @@ smm_relocate_gdt: /* gdt selector 0x10, flat data segment */ .word 0xffff, 0x0000 .byte 0x00, 0x93, 0xcf, 0x00 + + /* gdt selector 0x18, flat code segment (64-bit) */ + .word 0xffff, 0x0000 + .byte 0x00, 0x9b, 0xcf, 0x00 + + /* gdt selector 0x20 tss segment */ + .word 0xffff, 0x0000 + .byte 0x00, 0x8b, 0x80, 0x00 smm_relocate_gdt_end: .align 4 diff --git a/src/cpu/x86/smm/tseg_region.c b/src/cpu/x86/smm/tseg_region.c index a8b8bb7b9a..747dacdca6 100644 --- a/src/cpu/x86/smm/tseg_region.c +++ b/src/cpu/x86/smm/tseg_region.c @@ -17,6 +17,7 @@ #include #include #include +#include /* * Subregions within SMM @@ -25,6 +26,8 @@ * +-------------------------+ * | External Stage Cache | SMM_RESERVED_SIZE * +-------------------------+ + * | STM | MSEG_SIZE + * +-------------------------+ * | code and data | * | (TSEG) | * +-------------------------+ TSEG @@ -35,17 +38,24 @@ int smm_subregion(int sub, uintptr_t *start, size_t *size) size_t sub_size; const size_t ied_size = CONFIG_IED_REGION_SIZE; const size_t cache_size = CONFIG_SMM_RESERVED_SIZE; + const size_t mseg_size = CONFIG_MSEG_SIZE; smm_region(&sub_base, &sub_size); ASSERT(IS_ALIGNED(sub_base, sub_size)); - ASSERT(sub_size > (cache_size + ied_size)); + ASSERT(sub_size > (cache_size + ied_size + mseg_size)); switch (sub) { case SMM_SUBREGION_HANDLER: /* Handler starts at the base of TSEG. */ sub_size -= ied_size; sub_size -= cache_size; + sub_size -= mseg_size; + break; + case SMM_SUBREGION_MSEG: + /* MSEG follows the SMM HANDLER subregion */ + sub_base += sub_size - (ied_size + cache_size + mseg_size); + sub_size = mseg_size; break; case SMM_SUBREGION_CACHE: /* External cache is in the middle of TSEG. */ @@ -88,11 +98,11 @@ void smm_list_regions(void) return; printk(BIOS_DEBUG, "SMM Memory Map\n"); - printk(BIOS_DEBUG, "SMRAM : 0x%zx 0x%zx\n", base, size); + printk(BIOS_DEBUG, "SMRAM : 0x%" PRIxPTR " 0x%zx\n", base, size); for (i = 0; i < SMM_SUBREGION_NUM; i++) { if (smm_subregion(i, &base, &size)) continue; - printk(BIOS_DEBUG, " Subregion %d: 0x%zx 0x%zx\n", i, base, size); + printk(BIOS_DEBUG, " Subregion %d: 0x%" PRIxPTR " 0x%zx\n", i, base, size); } } diff --git a/src/drivers/ipmi/Kconfig b/src/drivers/ipmi/Kconfig index 0f7152d558..b54b23d677 100644 --- a/src/drivers/ipmi/Kconfig +++ b/src/drivers/ipmi/Kconfig @@ -8,3 +8,22 @@ config IPMI_KCS_REGISTER_SPACING depends on IPMI_KCS help KCS status and command register IO port address spacing + +config IPMI_FRU_SINGLE_RW_SZ + int + default 16 + depends on IPMI_KCS + help + The data size in a single IPMI FRU read/write command. + IPMB messages are limited to 32-bytes total. When the + data size is larger than this value, IPMI can complete + reading/writing the data over multiple commands. + +config IPMI_KCS_TIMEOUT_MS + int + default 5000 + depends on IPMI_KCS + help + The time unit is millisecond for each IPMI KCS transfer. + IPMI spec v2.0 rev 1.1 Sec. 9.15, a five-second timeout or + greater is recommended. diff --git a/src/drivers/ipmi/Makefile.inc b/src/drivers/ipmi/Makefile.inc index 9d5b3d418f..973fff82c7 100644 --- a/src/drivers/ipmi/Makefile.inc +++ b/src/drivers/ipmi/Makefile.inc @@ -1,3 +1,4 @@ ramstage-$(CONFIG_IPMI_KCS) += ipmi_kcs.c ramstage-$(CONFIG_IPMI_KCS) += ipmi_kcs_ops.c ramstage-$(CONFIG_IPMI_KCS) += ipmi_ops.c +ramstage-$(CONFIG_IPMI_KCS) += ipmi_fru.c diff --git a/src/drivers/ipmi/ipmi_fru.c b/src/drivers/ipmi/ipmi_fru.c new file mode 100644 index 0000000000..43ee6b3a35 --- /dev/null +++ b/src/drivers/ipmi/ipmi_fru.c @@ -0,0 +1,411 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2019 Wiwynn Corp. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; version 2 of + * the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include + +#include "ipmi_ops.h" + +#define MAX_FRU_BUSY_RETRY 5 +#define READ_FRU_DATA_RETRY_INTERVAL_MS 30 /* From IPMI spec v2.0 rev 1.1 */ +#define OFFSET_LENGTH_MULTIPLIER 8 /* offsets/lengths are multiples of 8 */ +#define NUM_DATA_BYTES(t) (t & 0x3f) /* Encoded in type/length byte */ + +static enum cb_err ipmi_read_fru(const int port, struct ipmi_read_fru_data_req *req, + uint8_t *fru_data) +{ + int ret; + uint8_t total_size; + uint16_t offset = 0; + struct ipmi_read_fru_data_rsp rsp; + int retry_count = 0; + + if (req == NULL || fru_data == NULL) { + printk(BIOS_ERR, "%s failed, null pointer parameter\n", + __func__); + return CB_ERR; + } + + total_size = req->count; + do { + if (req->count > CONFIG_IPMI_FRU_SINGLE_RW_SZ) + req->count = CONFIG_IPMI_FRU_SINGLE_RW_SZ; + + while (retry_count <= MAX_FRU_BUSY_RETRY) { + ret = ipmi_kcs_message(port, IPMI_NETFN_STORAGE, 0x0, + IPMI_READ_FRU_DATA, (const unsigned char *) req, + sizeof(*req), (unsigned char *) &rsp, sizeof(rsp)); + if (rsp.resp.completion_code == 0x81) { + /* Device is busy */ + if (retry_count == MAX_FRU_BUSY_RETRY) { + printk(BIOS_ERR, "IPMI: %s command failed, " + "device busy timeout\n", __func__); + return CB_ERR; + } + printk(BIOS_ERR, "IPMI: FRU device is busy, " + "retry count:%d\n", retry_count); + retry_count++; + mdelay(READ_FRU_DATA_RETRY_INTERVAL_MS); + } else if (ret < sizeof(struct ipmi_rsp) || rsp.resp.completion_code) { + printk(BIOS_ERR, "IPMI: %s command failed (ret=%d resp=0x%x)\n", + __func__, ret, rsp.resp.completion_code); + return CB_ERR; + } + break; + } + retry_count = 0; + memcpy(fru_data + offset, rsp.data, rsp.count); + offset += rsp.count; + total_size -= rsp.count; + req->fru_offset += rsp.count; + req->count = total_size; + } while (total_size > 0); + + return CB_SUCCESS; +} + +/* data: data to check, offset: offset to checksum. */ +static uint8_t checksum(uint8_t *data, int offset) +{ + uint8_t c = 0; + for (; offset > 0; offset--, data++) + c += *data; + return -c; +} + +static uint8_t data2str(const uint8_t *frudata, char *stringdata, uint8_t length) +{ + uint8_t type; + + /* bit[7:6] is the type code. */ + type = ((frudata[0] & 0xc0) >> 6); + if (type != ASCII_8BIT) { + printk(BIOS_ERR, "%s typecode %d is unsupported, FRU string only " + "supports 8-bit ASCII + Latin 1 for now.\n", __func__, type); + return 0; + } + /* In the spec the string data is always the next byte to the type/length byte. */ + memcpy(stringdata, frudata + 1, length); + stringdata[length] = '\0'; + return length; +} + +static void read_fru_board_info_area(const int port, const uint8_t id, + uint8_t offset, struct fru_board_info *info) +{ + uint8_t length; + struct ipmi_read_fru_data_req req; + uint8_t *data_ptr; + + offset = offset * OFFSET_LENGTH_MULTIPLIER; + if (!offset) + return; + req.fru_device_id = id; + /* Read Board Info Area length first. */ + req.fru_offset = offset + 1; + req.count = sizeof(length); + if (ipmi_read_fru(port, &req, &length) != CB_SUCCESS || !length) { + printk(BIOS_ERR, "%s failed, length: %d\n", __func__, length); + return; + } + length = length * OFFSET_LENGTH_MULTIPLIER; + data_ptr = (uint8_t *)malloc(length); + if (!data_ptr) { + printk(BIOS_ERR, "malloc %d bytes for board info failed\n", length); + return; + } + + /* Read Board Info Area data. */ + req.fru_offset = offset; + req.count = length; + if (ipmi_read_fru(port, &req, data_ptr) != CB_SUCCESS) { + printk(BIOS_ERR, "%s failed to read fru\n", __func__); + goto out; + } + if (checksum(data_ptr, length)) { + printk(BIOS_ERR, "Bad FRU board info checksum.\n"); + goto out; + } + /* Read manufacturer string, bit[5:0] is the string length. */ + length = NUM_DATA_BYTES(data_ptr[BOARD_MAN_TYPE_LEN_OFFSET]); + data_ptr += BOARD_MAN_TYPE_LEN_OFFSET; + if (length > 0) { + info->manufacturer = malloc(length + 1); + if (!info->manufacturer) { + printk(BIOS_ERR, "%s failed to malloc %d bytes for " + "manufacturer.\n", __func__, length + 1); + goto out; + } + if (!data2str((const uint8_t *)data_ptr, info->manufacturer, length)) + free(info->manufacturer); + } + + /* Read product name string. */ + data_ptr += length + 1; + length = NUM_DATA_BYTES(data_ptr[0]); + if (length > 0) { + info->product_name = malloc(length+1); + if (!info->product_name) { + printk(BIOS_ERR, "%s failed to malloc %d bytes for " + "product_name.\n", __func__, length + 1); + goto out; + } + if (!data2str((const uint8_t *)data_ptr, info->product_name, length)) + free(info->product_name); + } + + /* Read serial number string. */ + data_ptr += length + 1; + length = NUM_DATA_BYTES(data_ptr[0]); + if (length > 0) { + info->serial_number = malloc(length + 1); + if (!info->serial_number) { + printk(BIOS_ERR, "%s failed to malloc %d bytes for " + "serial_number.\n", __func__, length + 1); + goto out; + } + if (!data2str((const uint8_t *)data_ptr, info->serial_number, length)) + free(info->serial_number); + } + + /* Read part number string. */ + data_ptr += length + 1; + length = NUM_DATA_BYTES(data_ptr[0]); + if (length > 0) { + info->part_number = malloc(length + 1); + if (!info->part_number) { + printk(BIOS_ERR, "%s failed to malloc %d bytes for " + "part_number.\n", __func__, length + 1); + goto out; + } + if (!data2str((const uint8_t *)data_ptr, info->part_number, length)) + free(info->part_number); + } + +out: + free(data_ptr); +} + +static void read_fru_product_info_area(const int port, const uint8_t id, + uint8_t offset, struct fru_product_info *info) +{ + uint8_t length; + struct ipmi_read_fru_data_req req; + uint8_t *data_ptr; + + offset = offset * OFFSET_LENGTH_MULTIPLIER; + if (!offset) + return; + + req.fru_device_id = id; + /* Read Product Info Area length first. */ + req.fru_offset = offset + 1; + req.count = sizeof(length); + if (ipmi_read_fru(port, &req, &length) != CB_SUCCESS || !length) { + printk(BIOS_ERR, "%s failed, length: %d\n", __func__, length); + return; + } + length = length * OFFSET_LENGTH_MULTIPLIER; + data_ptr = (uint8_t *)malloc(length); + if (!data_ptr) { + printk(BIOS_ERR, "malloc %d bytes for product info failed\n", length); + return; + } + + /* Read Product Info Area data. */ + req.fru_offset = offset; + req.count = length; + if (ipmi_read_fru(port, &req, data_ptr) != CB_SUCCESS) { + printk(BIOS_ERR, "%s failed to read fru\n", __func__); + goto out; + } + if (checksum(data_ptr, length)) { + printk(BIOS_ERR, "Bad FRU product info checksum.\n"); + goto out; + } + /* Read manufacturer string, bit[5:0] is the string length. */ + length = NUM_DATA_BYTES(data_ptr[PRODUCT_MAN_TYPE_LEN_OFFSET]); + data_ptr += PRODUCT_MAN_TYPE_LEN_OFFSET; + if (length > 0) { + info->manufacturer = malloc(length + 1); + if (!info->manufacturer) { + printk(BIOS_ERR, "%s failed to malloc %d bytes for " + "manufacturer.\n", __func__, length + 1); + goto out; + } + if (!data2str((const uint8_t *)data_ptr, info->manufacturer, length)) + free(info->manufacturer); + } + + /* Read product_name string. */ + data_ptr += length + 1; + length = NUM_DATA_BYTES(data_ptr[0]); + if (length > 0) { + info->product_name = malloc(length + 1); + if (!info->product_name) { + printk(BIOS_ERR, "%s failed to malloc %d bytes for " + "product_name.\n", __func__, length + 1); + goto out; + } + if (!data2str((const uint8_t *)data_ptr, info->product_name, length)) + free(info->product_name); + } + + /* Read product part/model number. */ + data_ptr += length + 1; + length = NUM_DATA_BYTES(data_ptr[0]); + if (length > 0) { + info->product_partnumber = malloc(length + 1); + if (!info->product_partnumber) { + printk(BIOS_ERR, "%s failed to malloc %d bytes for " + "product_partnumber.\n", __func__, length + 1); + goto out; + } + if (!data2str((const uint8_t *)data_ptr, info->product_partnumber, length)) + free(info->product_partnumber); + } + + /* Read product version string. */ + data_ptr += length + 1; + length = NUM_DATA_BYTES(data_ptr[0]); + if (length > 0) { + info->product_version = malloc(length + 1); + if (!info->product_version) { + printk(BIOS_ERR, "%s failed to malloc %d bytes for " + "product_version.\n", __func__, length + 1); + goto out; + } + if (!data2str((const uint8_t *)data_ptr, info->product_version, length)) + free(info->product_version); + } + + /* Read serial number string. */ + data_ptr += length + 1; + length = NUM_DATA_BYTES(data_ptr[0]); + if (length > 0) { + info->serial_number = malloc(length + 1); + if (!info->serial_number) { + printk(BIOS_ERR, "%s failed to malloc %d bytes for " + "serial_number.\n", __func__, length + 1); + goto out; + } + if (!data2str((const uint8_t *)data_ptr, info->serial_number, length)) + free(info->serial_number); + } + + /* Read asset tag string. */ + data_ptr += length + 1; + length = NUM_DATA_BYTES(data_ptr[0]); + if (length > 0) { + info->asset_tag = malloc(length + 1); + if (!info->asset_tag) { + printk(BIOS_ERR, "%s failed to malloc %d bytes for " + "asset_tag.\n", __func__, length + 1); + goto out; + } + if (!data2str((const uint8_t *)data_ptr, info->asset_tag, length)) + free(info->asset_tag); + } + +out: + free(data_ptr); +} + +void read_fru_areas(const int port, const uint8_t id, uint16_t offset, + struct fru_info_str *fru_info_str) +{ + struct ipmi_read_fru_data_req req; + struct ipmi_fru_common_hdr fru_common_hdr; + + /* Set all the char pointers to 0 first, to avoid mainboard + * overwriting SMBIOS string with any non-NULL char pointer + * by accident. */ + memset(fru_info_str, 0, sizeof(*fru_info_str)); + req.fru_device_id = id; + req.fru_offset = offset; + req.count = sizeof(fru_common_hdr); + /* Read FRU common header first */ + if (ipmi_read_fru(port, &req, (uint8_t *)&fru_common_hdr) == CB_SUCCESS) { + if (checksum((uint8_t *)&fru_common_hdr, sizeof(fru_common_hdr))) { + printk(BIOS_ERR, "Bad FRU common header checksum.\n"); + return; + } + printk(BIOS_DEBUG, "FRU common header: format_version: %x\n" + "product_area_offset: %x\n" + "board_area_offset: %x\n" + "chassis_area_offset: %x\n", + fru_common_hdr.format_version, + fru_common_hdr.product_area_offset, + fru_common_hdr.board_area_offset, + fru_common_hdr.chassis_area_offset); + } else { + printk(BIOS_ERR, "Read FRU common header failed\n"); + return; + } + + read_fru_product_info_area(port, id, fru_common_hdr.product_area_offset, + &fru_info_str->prod_info); + read_fru_board_info_area(port, id, fru_common_hdr.board_area_offset, + &fru_info_str->board_info); + /* ToDo: Add read_fru_chassis_info_area(). */ +} + +void read_fru_one_area(const int port, const uint8_t id, uint16_t offset, + struct fru_info_str *fru_info_str, enum fru_area fru_area) +{ + struct ipmi_read_fru_data_req req; + struct ipmi_fru_common_hdr fru_common_hdr; + + req.fru_device_id = id; + req.fru_offset = offset; + req.count = sizeof(fru_common_hdr); + if (ipmi_read_fru(port, &req, (uint8_t *)&fru_common_hdr) == CB_SUCCESS) { + if (checksum((uint8_t *)&fru_common_hdr, sizeof(fru_common_hdr))) { + printk(BIOS_ERR, "Bad FRU common header checksum.\n"); + return; + } + printk(BIOS_DEBUG, "FRU common header: format_version: %x\n" + "product_area_offset: %x\n" + "board_area_offset: %x\n" + "chassis_area_offset: %x\n", + fru_common_hdr.format_version, + fru_common_hdr.product_area_offset, + fru_common_hdr.board_area_offset, + fru_common_hdr.chassis_area_offset); + } else { + printk(BIOS_ERR, "Read FRU common header failed\n"); + return; + } + + switch (fru_area) { + case PRODUCT_INFO_AREA: + memset(&fru_info_str->prod_info, 0, sizeof(fru_info_str->prod_info)); + read_fru_product_info_area(port, id, fru_common_hdr.product_area_offset, + &fru_info_str->prod_info); + break; + case BOARD_INFO_AREA: + memset(&fru_info_str->board_info, 0, sizeof(fru_info_str->board_info)); + read_fru_board_info_area(port, id, fru_common_hdr.board_area_offset, + &fru_info_str->board_info); + break; + /* ToDo: Add case for CHASSIS_INFO_AREA. */ + default: + printk(BIOS_ERR, "Invalid fru_area: %d\n", fru_area); + break; + } +} diff --git a/src/drivers/ipmi/ipmi_kcs.c b/src/drivers/ipmi/ipmi_kcs.c index d3916198a6..bf7f39384e 100644 --- a/src/drivers/ipmi/ipmi_kcs.c +++ b/src/drivers/ipmi/ipmi_kcs.c @@ -15,7 +15,7 @@ #include #include #include -#include +#include #include "ipmi_kcs.h" #define IPMI_KCS_STATE(_x) ((_x) >> 6) @@ -41,33 +41,29 @@ static unsigned char ipmi_kcs_status(int port) { unsigned char status = inb(IPMI_STAT(port)); - printk(BIOS_SPEW, "%s: 0x%02x\n", __func__, status); + if (CONFIG(DEBUG_IPMI)) + printk(BIOS_SPEW, "%s: 0x%02x\n", __func__, status); return status; } static int wait_ibf_timeout(int port) { - int timeout = 1000; - do { - if (!(ipmi_kcs_status(port) & IPMI_KCS_IBF)) - return 0; - udelay(100); - } while (timeout--); - printk(BIOS_ERR, "wait_ibf timeout!\n"); - return timeout; + if (!wait_ms(CONFIG_IPMI_KCS_TIMEOUT_MS, !(ipmi_kcs_status(port) & IPMI_KCS_IBF))) { + printk(BIOS_ERR, "wait_ibf timeout!\n"); + return 1; + } else { + return 0; + } } static int wait_obf_timeout(int port) { - int timeout = 1000; - do { - if ((ipmi_kcs_status(port) & IPMI_KCS_OBF)) - return 0; - udelay(100); - } while (timeout--); - - printk(BIOS_ERR, "wait_obf timeout!\n"); - return timeout; + if (!wait_ms(CONFIG_IPMI_KCS_TIMEOUT_MS, (ipmi_kcs_status(port) & IPMI_KCS_OBF))) { + printk(BIOS_ERR, "wait_obf timeout!\n"); + return 1; + } else { + return 0; + } } @@ -75,7 +71,8 @@ static int ipmi_kcs_send_data_byte(int port, const unsigned char byte) { unsigned char status; - printk(BIOS_SPEW, "%s: 0x%02x\n", __func__, byte); + if (CONFIG(DEBUG_IPMI)) + printk(BIOS_SPEW, "%s: 0x%02x\n", __func__, byte); outb(byte, IPMI_DATA(port)); @@ -98,7 +95,8 @@ static int ipmi_kcs_send_last_data_byte(int port, const unsigned char byte) { unsigned char status; - printk(BIOS_SPEW, "%s: 0x%02x\n", __func__, byte); + if (CONFIG(DEBUG_IPMI)) + printk(BIOS_SPEW, "%s: 0x%02x\n", __func__, byte); if (wait_ibf_timeout(port)) return 1; @@ -119,7 +117,8 @@ static int ipmi_kcs_send_last_data_byte(int port, const unsigned char byte) static int ipmi_kcs_send_cmd_byte(int port, const unsigned char byte) { - printk(BIOS_SPEW, "%s: 0x%02x\n", __func__, byte); + if (CONFIG(DEBUG_IPMI)) + printk(BIOS_SPEW, "%s: 0x%02x\n", __func__, byte); if (wait_ibf_timeout(port)) return 1; diff --git a/src/drivers/ipmi/ipmi_kcs.h b/src/drivers/ipmi/ipmi_kcs.h index f35802e27b..9a04377a0a 100644 --- a/src/drivers/ipmi/ipmi_kcs.h +++ b/src/drivers/ipmi/ipmi_kcs.h @@ -22,9 +22,16 @@ #define IPMI_BMC_GET_DEVICE_ID 0x01 #define IPMI_IPMI_VERSION_MINOR(x) ((x) >> 4) #define IPMI_IPMI_VERSION_MAJOR(x) ((x) & 0xf) +#define IPMI_BMC_GET_SELFTEST_RESULTS 0x04 +#define IPMI_APP_SELFTEST_RESERVED 0xFF +#define IPMI_APP_SELFTEST_NO_ERROR 0x55 +#define IPMI_APP_SELFTEST_NOT_IMPLEMENTED 0x56 +#define IPMI_APP_SELFTEST_ERROR 0x57 +#define IPMI_APP_SELFTEST_FATAL_HW_ERROR 0x58 #define IPMI_NETFN_FIRMWARE 0x08 #define IPMI_NETFN_STORAGE 0x0a +#define IPMI_READ_FRU_DATA 0x11 #define IPMI_NETFN_TRANSPORT 0x0c #define IPMI_CMD_ACPI_POWERON 0x06 @@ -52,4 +59,11 @@ struct ipmi_devid_rsp { uint8_t product_id[2]; } __packed; +/* Get Self Test Results */ +struct ipmi_selftest_rsp { + struct ipmi_rsp resp; + uint8_t result; + uint8_t param; +} __packed; + #endif diff --git a/src/drivers/ipmi/ipmi_kcs_ops.c b/src/drivers/ipmi/ipmi_kcs_ops.c index 90f19dddb8..5cb8995df9 100644 --- a/src/drivers/ipmi/ipmi_kcs_ops.c +++ b/src/drivers/ipmi/ipmi_kcs_ops.c @@ -59,11 +59,34 @@ static int ipmi_get_device_id(struct device *dev, struct ipmi_devid_rsp *rsp) return 0; } +static int ipmi_get_bmc_self_test_result(struct device *dev, struct ipmi_selftest_rsp *rsp) +{ + int ret; + + ret = ipmi_kcs_message(dev->path.pnp.port, IPMI_NETFN_APPLICATION, 0, + IPMI_BMC_GET_SELFTEST_RESULTS, NULL, 0, (u8 *)rsp, + sizeof(*rsp)); + + if (ret < sizeof(struct ipmi_rsp) || rsp->resp.completion_code) { + printk(BIOS_ERR, "IPMI: %s command failed (ret=%d resp=0x%x)\n", + __func__, ret, rsp->resp.completion_code); + return 1; + } + if (ret != sizeof(*rsp)) { + printk(BIOS_ERR, "IPMI: %s response truncated\n", __func__); + return 1; + } + + return 0; +} + static void ipmi_kcs_init(struct device *dev) { struct ipmi_devid_rsp rsp; uint32_t man_id = 0, prod_id = 0; struct drivers_ipmi_config *conf = NULL; + struct ipmi_selftest_rsp selftestrsp; + uint8_t retry_count; if (!dev->enabled) return; @@ -92,6 +115,42 @@ static void ipmi_kcs_init(struct device *dev) } } + printk(BIOS_INFO, "Get BMC self test result..."); + for (retry_count = 0; retry_count < conf->bmc_boot_timeout; retry_count++) { + if (!ipmi_get_bmc_self_test_result(dev, &selftestrsp)) + break; + + mdelay(1000); + } + + switch (selftestrsp.result) { + case IPMI_APP_SELFTEST_NO_ERROR: /* 0x55 */ + printk(BIOS_DEBUG, "No Error\n"); + break; + case IPMI_APP_SELFTEST_NOT_IMPLEMENTED: /* 0x56 */ + printk(BIOS_DEBUG, "Function Not Implemented\n"); + break; + case IPMI_APP_SELFTEST_ERROR: /* 0x57 */ + printk(BIOS_ERR, "BMC: Corrupted or inaccessible data or device\n"); + /* Don't write tables if communication failed */ + dev->enabled = 0; + break; + case IPMI_APP_SELFTEST_FATAL_HW_ERROR: /* 0x58 */ + printk(BIOS_ERR, "BMC: Fatal Hardware Error\n"); + /* Don't write tables if communication failed */ + dev->enabled = 0; + break; + case IPMI_APP_SELFTEST_RESERVED: /* 0xFF */ + printk(BIOS_DEBUG, "Reserved\n"); + break; + + default: /* Other Device Specific Hardware Error */ + printk(BIOS_ERR, "BMC: Device Specific Error\n"); + /* Don't write tables if communication failed */ + dev->enabled = 0; + break; + } + if (!ipmi_get_device_id(dev, &rsp)) { /* Queried the IPMI revision from BMC */ ipmi_revision_minor = IPMI_IPMI_VERSION_MINOR(rsp.ipmi_version); diff --git a/src/drivers/ipmi/ipmi_ops.c b/src/drivers/ipmi/ipmi_ops.c index 784daeb1fb..8a189bdbe1 100644 --- a/src/drivers/ipmi/ipmi_ops.c +++ b/src/drivers/ipmi/ipmi_ops.c @@ -16,6 +16,7 @@ #include #include "ipmi_ops.h" +#include enum cb_err ipmi_init_and_start_bmc_wdt(const int port, uint16_t countdown, uint8_t action) @@ -104,3 +105,28 @@ enum cb_err ipmi_stop_bmc_wdt(const int port) return CB_SUCCESS; } + +enum cb_err ipmi_get_system_guid(const int port, uint8_t *uuid) +{ + int ret; + struct ipmi_get_system_guid_rsp rsp; + + if (uuid == NULL) { + printk(BIOS_ERR, "%s failed, null pointer parameter\n", + __func__); + return CB_ERR; + } + + ret = ipmi_kcs_message(port, IPMI_NETFN_APPLICATION, 0x0, + IPMI_BMC_GET_SYSTEM_GUID, NULL, 0, + (unsigned char *) &rsp, sizeof(rsp)); + + if (ret < sizeof(struct ipmi_rsp) || rsp.resp.completion_code) { + printk(BIOS_ERR, "IPMI: %s command failed (ret=%d resp=0x%x)\n", + __func__, ret, rsp.resp.completion_code); + return CB_ERR; + } + + memcpy(uuid, rsp.data, 16); + return CB_SUCCESS; +} diff --git a/src/drivers/ipmi/ipmi_ops.h b/src/drivers/ipmi/ipmi_ops.h index f293075e90..dd12786b8e 100644 --- a/src/drivers/ipmi/ipmi_ops.h +++ b/src/drivers/ipmi/ipmi_ops.h @@ -21,6 +21,7 @@ #define IPMI_BMC_RESET_WDG_TIMER 0x22 #define IPMI_BMC_SET_WDG_TIMER 0x24 #define IPMI_BMC_GET_WDG_TIMER 0x25 +#define IPMI_BMC_GET_SYSTEM_GUID 0x37 /* BMC watchdog timeout action */ enum ipmi_bmc_timeout_action_type { @@ -44,6 +45,74 @@ struct ipmi_wdt_rsp { uint16_t present_countdown_val; } __packed; +struct ipmi_get_system_guid_rsp { + struct ipmi_rsp resp; + uint8_t data[16]; +} __packed; + +struct ipmi_read_fru_data_req { + uint8_t fru_device_id; + uint16_t fru_offset; + uint8_t count; /* count to read, 1-based. */ +} __packed; + +struct ipmi_read_fru_data_rsp { + struct ipmi_rsp resp; + uint8_t count; /* count returned, 1-based. */ + uint8_t data[CONFIG_IPMI_FRU_SINGLE_RW_SZ]; +} __packed; + +/* Platform Management FRU Information Storage Definition Spec. */ +#define PRODUCT_MAN_TYPE_LEN_OFFSET 3 +#define BOARD_MAN_TYPE_LEN_OFFSET 6 + +struct ipmi_fru_common_hdr { + uint8_t format_version; + uint8_t internal_use_area_offset; + uint8_t chassis_area_offset; + uint8_t board_area_offset; + uint8_t product_area_offset; + uint8_t multirecord_area_offset; + uint8_t pad; + uint8_t checksum; +} __packed; + +/* The fru_xxx_info only declares the strings that may be added to SMBIOS. */ +struct fru_product_info { + char *manufacturer; + char *product_name; + char *product_partnumber; + char *product_version; + char *serial_number; + char *asset_tag; +}; + +struct fru_board_info { + char *manufacturer; + char *product_name; + char *serial_number; + char *part_number; +}; + +struct fru_info_str { + struct fru_product_info prod_info; + struct fru_board_info board_info; +}; + +enum typecode { + BINARY = 0, + BCD_PLUS = 1, + ASCII_6BIT = 2, + ASCII_8BIT = 3, +}; + +enum fru_area { + INTERNAL_USE_AREA = 0, + CHASSIS_INFO_AREA = 1, + BOARD_INFO_AREA = 2, + PRODUCT_INFO_AREA = 3, + MULTIRECORD_INFO_AREA = 4, +}; /* * Initialize and start BMC FRB2 watchdog timer with the * provided timer countdown and action values. @@ -54,4 +123,16 @@ enum cb_err ipmi_init_and_start_bmc_wdt(const int port, uint16_t countdown, /* Returns CB_SUCCESS on success and CB_ERR if an error occurred */ enum cb_err ipmi_stop_bmc_wdt(const int port); +/* IPMI get BMC system GUID and store it to parameter uuid. + * Returns CB_SUCCESS on success and CB_ERR if an error occurred */ +enum cb_err ipmi_get_system_guid(const int port, uint8_t *uuid); + +/* Read all FRU inventory areas string data into fru_info_str with + * the same FRU device id. */ +void read_fru_areas(const int port, uint8_t id, uint16_t offset, + struct fru_info_str *fru_info_str); + +/* Read a particular FRU inventory area into fru_info_str. */ +void read_fru_one_area(const int port, uint8_t id, uint16_t offset, + struct fru_info_str *fru_info_str, enum fru_area fru_area); #endif diff --git a/src/include/console/console.h b/src/include/console/console.h index 607c96862e..1c2a276af0 100644 --- a/src/include/console/console.h +++ b/src/include/console/console.h @@ -64,11 +64,6 @@ asmlinkage void console_init(void); int console_log_level(int msg_level); void do_putchar(unsigned char byte); -/* Return number of microseconds elapsed from start of stage or the previous - get_and_reset() call. */ -long console_time_get_and_reset(void); -void console_time_report(void); - #define printk(LEVEL, fmt, args...) do_printk(LEVEL, fmt, ##args) #define vprintk(LEVEL, fmt, args) do_vprintk(LEVEL, fmt, args) @@ -92,8 +87,6 @@ static inline int console_log_level(int msg_level) { return 0; } static inline void printk(int LEVEL, const char *fmt, ...) {} static inline void vprintk(int LEVEL, const char *fmt, va_list args) {} static inline void do_putchar(unsigned char byte) {} -static inline long console_time_get_and_reset(void) { return 0; } -static inline void console_time_report(void) {} #endif int do_printk(int msg_level, const char *fmt, ...) diff --git a/src/include/cpu/x86/msr.h b/src/include/cpu/x86/msr.h index 2710e7f1fc..b97caf44a5 100644 --- a/src/include/cpu/x86/msr.h +++ b/src/include/cpu/x86/msr.h @@ -16,6 +16,7 @@ /* Page attribute type MSR */ #define TSC_MSR 0x10 #define IA32_PLATFORM_ID 0x17 +#define IA32_APIC_BASE_MSR_INDEX 0x1B #define IA32_FEATURE_CONTROL 0x3a #define FEATURE_CONTROL_LOCK_BIT (1 << 0) #define FEATURE_ENABLE_VMX (1 << 2) @@ -30,6 +31,10 @@ #define IA32_BIOS_SIGN_ID 0x8b #define IA32_MPERF 0xe7 #define IA32_APERF 0xe8 +/* STM */ +#define IA32_SMM_MONITOR_CTL_MSR 0x9B +#define SMBASE_RO_MSR 0x98 +#define IA32_SMM_MONITOR_VALID (1 << 0) #define IA32_MCG_CAP 0x179 #define MCG_CTL_P (1 << 3) #define MCA_BANKS_MASK 0xff @@ -45,6 +50,9 @@ #define ENERGY_POLICY_POWERSAVE 15 #define IA32_PACKAGE_THERM_INTERRUPT 0x1b2 #define IA32_PLATFORM_DCA_CAP 0x1f8 +#define SMRR_PHYSBASE_MSR 0x1F2 +#define SMRR_PHYSMASK_MSR 0x1F3 +#define IA32_PLATFORM_DCA_CAP 0x1f8 #define IA32_PAT 0x277 #define IA32_MC0_CTL 0x400 #define IA32_MC0_STATUS 0x401 @@ -65,6 +73,9 @@ #define MCA_STATUS_LO_ERRCODE_EXT_SH 16 #define MCA_STATUS_LO_ERRCODE_EXT_MASK (0x3f << MCA_STATUS_LO_ERRCODE_EXT_SH) #define MCA_STATUS_LO_ERRCODE_MASK (0xffff << 0) +#define IA32_VMX_BASIC_MSR 0x480 +#define VMX_BASIC_HI_DUAL_MONITOR (1UL << (49 - 32)) +#define IA32_VMX_MISC_MSR 0x485 #define MC0_ADDR 0x402 #define MC0_MISC 0x403 #define MC0_CTL_MASK 0xC0010044 diff --git a/src/include/cpu/x86/smm.h b/src/include/cpu/x86/smm.h index cf107b121a..b4492963b3 100644 --- a/src/include/cpu/x86/smm.h +++ b/src/include/cpu/x86/smm.h @@ -63,7 +63,11 @@ extern unsigned char _binary_smm_end[]; struct smm_runtime { u32 smbase; + u32 smm_size; u32 save_state_size; + u32 num_cpus; + /* STM's 32bit entry into SMI handler */ + u32 start32_offset; /* The apic_id_to_cpu provides a mapping from APIC id to CPU number. * The CPU number is indicated by the index into the array by matching * the default APIC id and value at the index. The stub loader @@ -114,6 +118,12 @@ void *smm_get_save_state(int cpu); * into this field so the code doing the loading can manipulate the * runtime's assumptions. e.g. updating the APIC id to CPU map to * handle sparse APIC id space. + * The following parameters are only used when X86_SMM_LOADER_VERSION2 is enabled. + * - smm_entry - entry address of first CPU thread, all others will be tiled + * below this address. + * - smm_main_entry_offset - default entry offset (e.g 0x8000) + * - smram_start - smaram starting address + * - smram_end - smram ending address */ struct smm_loader_params { void *stack_top; @@ -127,12 +137,24 @@ struct smm_loader_params { void *handler_arg; struct smm_runtime *runtime; + + /* The following are only used by X86_SMM_LOADER_VERSION2 */ +#if CONFIG(X86_SMM_LOADER_VERSION2) + unsigned int smm_entry; + unsigned int smm_main_entry_offset; + unsigned int smram_start; + unsigned int smram_end; +#endif }; /* Both of these return 0 on success, < 0 on failure. */ int smm_setup_relocation_handler(struct smm_loader_params *params); int smm_load_module(void *smram, size_t size, struct smm_loader_params *params); +#if CONFIG(X86_SMM_LOADER_VERSION2) +u32 smm_get_cpu_smbase(unsigned int cpu_num); +#endif + /* Backup and restore default SMM region. */ void *backup_default_smm_area(void); void restore_default_smm_area(void *smm_save_area); @@ -146,6 +168,8 @@ void smm_region(uintptr_t *start, size_t *size); enum { /* SMM handler area. */ SMM_SUBREGION_HANDLER, + /* MSEG (STM). */ + SMM_SUBREGION_MSEG, /* SMM cache region. */ SMM_SUBREGION_CACHE, /* Chipset specific area. */ diff --git a/src/lib/hardwaremain.c b/src/lib/hardwaremain.c index 3fcf8829f3..51ff330d84 100644 --- a/src/lib/hardwaremain.c +++ b/src/lib/hardwaremain.c @@ -63,7 +63,6 @@ static boot_state_t bs_payload_boot(void *arg); struct boot_state_times { int num_samples; struct mono_time samples[MAX_TIME_SAMPLES]; - long console_usecs[MAX_TIME_SAMPLES]; }; /* The prologue (BS_ON_ENTRY) and epilogue (BS_ON_EXIT) of a state can be @@ -242,9 +241,6 @@ static void bs_sample_time(struct boot_state *state) { struct mono_time *mt; - long console_usecs = console_time_get_and_reset(); - state->times.console_usecs[state->times.num_samples] = console_usecs; - mt = &state->times.samples[state->times.num_samples]; timer_monotonic_get(mt); state->times.num_samples++; @@ -261,10 +257,6 @@ static void bs_report_time(struct boot_state *state) run_time = mono_time_diff_microseconds(&samples[1], &samples[2]); exit_time = mono_time_diff_microseconds(&samples[2], &samples[3]); - entry_time -= state->times.console_usecs[1]; - run_time -= state->times.console_usecs[2]; - exit_time -= state->times.console_usecs[3]; - /* Report with millisecond precision to reduce log diffs. */ entry_time = DIV_ROUND_CLOSEST(entry_time, USECS_PER_MSEC); run_time = DIV_ROUND_CLOSEST(run_time, USECS_PER_MSEC); diff --git a/src/lib/prog_loaders.c b/src/lib/prog_loaders.c index 57874967ec..7f320d9597 100644 --- a/src/lib/prog_loaders.c +++ b/src/lib/prog_loaders.c @@ -70,8 +70,6 @@ void run_romstage(void) timestamp_add_now(TS_END_COPYROM); - console_time_report(); - prog_run(&romstage); fail: @@ -155,8 +153,6 @@ void run_ramstage(void) timestamp_add_now(TS_END_COPYRAM); - console_time_report(); - /* This overrides the arg fetched from the relocatable module */ prog_set_arg(&ramstage, cbmem_top()); diff --git a/src/mainboard/facebook/watson/Kconfig b/src/mainboard/facebook/watson/Kconfig index 3235b6cc99..668b0ba66f 100644 --- a/src/mainboard/facebook/watson/Kconfig +++ b/src/mainboard/facebook/watson/Kconfig @@ -1,17 +1,4 @@ -if BOARD_FACEBOOK_WATSON - -config BOARD_SPECIFIC_OPTIONS - def_bool y - select SOC_INTEL_FSP_BROADWELL_DE - select BOARD_ROMSIZE_KB_16384 - select HAVE_ACPI_TABLES - select HAVE_OPTION_TABLE - select INTEGRATED_UART - select SERIRQ_CONTINUOUS_MODE - select MAINBOARD_USES_IFD_GBE_REGION - select MAINBOARD_HAS_LPC_TPM - select MAINBOARD_HAS_TPM1 - select NO_UART_ON_SUPERIO +if BOARD_FACEBOOK_WATSON || BOARD_FACEBOOK_WATSON_V2 config VBOOT select VBOOT_VBNV_CMOS @@ -21,6 +8,27 @@ config VBOOT select GBB_FLAG_DISABLE_EC_SOFTWARE_SYNC select GBB_FLAG_DISABLE_FWMP +config BOARD_SPECIFIC_OPTIONS + def_bool y + select SOC_INTEL_FSP_BROADWELL_DE + select BOARD_ROMSIZE_KB_16384 + select GENERATE_SMBIOS_TABLES + select HAVE_ACPI_TABLES + select HAVE_OPTION_TABLE + select INTEGRATED_UART + select IPMI_KCS if BOARD_FACEBOOK_WATSON_V2 + select SERIRQ_CONTINUOUS_MODE + select MAINBOARD_USES_IFD_GBE_REGION + select MAINBOARD_HAS_LPC_TPM + select MAINBOARD_HAS_TPM1 if BOARD_FACEBOOK_WATSON + select MAINBOARD_HAS_TPM2 if BOARD_FACEBOOK_WATSON_V2 + select NO_UART_ON_SUPERIO + select VBOOT if BOARD_FACEBOOK_WATSON_V2 + select VBOOT_MEASURED_BOOT if BOARD_FACEBOOK_WATSON_V2 + select VBOOT_STARTS_IN_ROMSTAGE if BOARD_FACEBOOK_WATSON_V2 + select VPD + select VPD_SMBIOS_VERSION + config MAINBOARD_DIR string default "facebook/watson" @@ -29,6 +37,10 @@ config MAINBOARD_PART_NUMBER string default "Watson" +config DEVICETREE + string + default "variants/$(CONFIG_VARIANT_DIR)/devicetree.cb" + config IRQ_SLOT_COUNT int default 18 @@ -37,6 +49,60 @@ config CBFS_SIZE hex default 0x00800000 +config VARIANT_DIR + string + default "watson" if BOARD_FACEBOOK_WATSON + default "watson_v2" if BOARD_FACEBOOK_WATSON_V2 + +config VBOOT_FWID_MODEL + string + default "$(CONFIG_MAINBOARD_VENDOR)_$(CONFIG_MAINBOARD_PART_NUMBER)" + +config VBOOT_FIRMWARE_PRIVKEY + string + depends on BOARD_FACEBOOK_WATSON_V2 + default "$(VBOOT_SOURCE)/tests/devkeys/firmware_data_key.vbprivk" + +config VBOOT_FWID_VERSION + string + depends on BOARD_FACEBOOK_WATSON_V2 + default ".$(KERNELVERSION)" + +config VBOOT_KERNEL_KEY + string + depends on BOARD_FACEBOOK_WATSON_V2 + default "$(VBOOT_SOURCE)/tests/devkeys/kernel_subkey.vbpubk" + +config VBOOT_KEYBLOCK + string + depends on BOARD_FACEBOOK_WATSON_V2 + default "$(VBOOT_SOURCE)/tests/devkeys/firmware.keyblock" + +config VBOOT_KEYBLOCK_VERSION + int + depends on BOARD_FACEBOOK_WATSON_V2 + default 1 + +config VBOOT_KEYBLOCK_PREAMBLE_FLAGS + hex + depends on BOARD_FACEBOOK_WATSON_V2 + default 0x0 + +config VBOOT_RECOVERY_KEY + string + depends on BOARD_FACEBOOK_WATSON_V2 + default "$(VBOOT_SOURCE)/tests/devkeys/recovery_key.vbpubk" + +config VBOOT_ROOT_KEY + string + depends on BOARD_FACEBOOK_WATSON_V2 + default "$(VBOOT_SOURCE)/tests/devkeys/root_key.vbpubk" + +config VBOOT_VBNV_OFFSET + hex + depends on BOARD_FACEBOOK_WATSON_V2 + default 0x26 + config VIRTUAL_ROM_SIZE hex # Set to CONFIG_ROM_SIZE*2 if using concatenated flash chips. @@ -48,10 +114,11 @@ config DRIVERS_UART_8250IO config FMDFILE string - default "src/mainboard/$(CONFIG_MAINBOARD_DIR)/board.fmd" + default "src/mainboard/$(CONFIG_MAINBOARD_DIR)/board.fmd" if BOARD_FACEBOOK_WATSON + default "src/mainboard/$(CONFIG_MAINBOARD_DIR)/vboot-ro.fmd" if BOARD_FACEBOOK_WATSON_V2 config ENABLE_TURBO bool "Enable turbo frequency" default n -endif # BOARD_FACEBOOK_WATSON +endif # BOARD_FACEBOOK_WATSON* diff --git a/src/mainboard/facebook/watson/Kconfig.name b/src/mainboard/facebook/watson/Kconfig.name index ea6c344791..b6a5a66ec2 100644 --- a/src/mainboard/facebook/watson/Kconfig.name +++ b/src/mainboard/facebook/watson/Kconfig.name @@ -1,2 +1,5 @@ config BOARD_FACEBOOK_WATSON - bool "Watson" + bool "Watson_v1" + +config BOARD_FACEBOOK_WATSON_V2 + bool "Watson_v2" diff --git a/src/mainboard/facebook/watson/Makefile.inc b/src/mainboard/facebook/watson/Makefile.inc index 1606476d80..ecad3e74aa 100644 --- a/src/mainboard/facebook/watson/Makefile.inc +++ b/src/mainboard/facebook/watson/Makefile.inc @@ -14,3 +14,6 @@ ## ramstage-y += irqroute.c + +subdirs-y += variants/$(VARIANT_DIR) +CPPFLAGS_common += -I$(src)/mainboard/$(MAINBOARDDIR)/include diff --git a/src/mainboard/facebook/watson/board.fmd b/src/mainboard/facebook/watson/board.fmd index feeb799a8e..6c58d12ebd 100644 --- a/src/mainboard/facebook/watson/board.fmd +++ b/src/mainboard/facebook/watson/board.fmd @@ -9,8 +9,8 @@ FLASH@0xff000000 0x1000000 { FMAP@0x0 0x1000 RW_MISC@0x1000 0xe000 { RW_ELOG@0x0 0x4000 - RW_VPD@0x4000 0x2000 - RW_MISC_UNUSED@0x6000 0x5000 + RW_VPD@0x4000 0x4000 + RW_MISC_UNUSED@0x8000 0x4000 RW_NVRAM@0xc000 0x2000 } UNIFIED_MRC_CACHE@0x10000 0x20000 { @@ -19,8 +19,7 @@ FLASH@0xff000000 0x1000000 { } # This only exists to satisfy tools that specifically # look for RO_VPD. - RO_VPD@0x30000 0x1000 - UNUSED_4@0x31000 0x1cf000 - COREBOOT(CBFS)@0x200000 0x800000 + RO_VPD@0x30000 0x4000 + COREBOOT(CBFS)@0x34000 } } diff --git a/src/mainboard/facebook/watson/include/variants.h b/src/mainboard/facebook/watson/include/variants.h new file mode 100644 index 0000000000..d364b8b007 --- /dev/null +++ b/src/mainboard/facebook/watson/include/variants.h @@ -0,0 +1,26 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2007-2009 coresystems GmbH + * Copyright (C) 2011 Google Inc. + * Copyright (C) Facebook, Inc. and its affiliates + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef BASEBOARD_VARIANTS_H +#define BASEBOARD_VARIANTS_H + +#include + +void variant_romstage_fsp_init_params(UPD_DATA_REGION *UpdData); +void variant_early_mainboard_romstage_entry(void); + +#endif /* BASEBOARD_VARIANTS_H */ diff --git a/src/mainboard/facebook/watson/romstage.c b/src/mainboard/facebook/watson/romstage.c index cf52c01f04..d65ac0ef51 100644 --- a/src/mainboard/facebook/watson/romstage.c +++ b/src/mainboard/facebook/watson/romstage.c @@ -17,6 +17,7 @@ #include #include #include +#include /** * /brief mainboard call for setup that needs to be done before fsp init @@ -24,7 +25,7 @@ */ void early_mainboard_romstage_entry(void) { - + variant_early_mainboard_romstage_entry(); } /** @@ -40,6 +41,19 @@ void late_mainboard_romstage_entry(void) * /brief customize fsp parameters here if needed */ void romstage_fsp_rt_buffer_callback(FSP_INIT_RT_BUFFER *FspRtBuffer) +{ + UPD_DATA_REGION *UpdData = FspRtBuffer->Common.UpdDataRgnPtr; + + /* Variant-specific memory params */ + variant_romstage_fsp_init_params(UpdData); +} + +__weak void variant_romstage_fsp_init_params(UPD_DATA_REGION *UpdData) +{ + +} + +__weak void variant_early_mainboard_romstage_entry(void) { } diff --git a/src/mainboard/facebook/watson/devicetree.cb b/src/mainboard/facebook/watson/variants/watson/devicetree.cb similarity index 100% rename from src/mainboard/facebook/watson/devicetree.cb rename to src/mainboard/facebook/watson/variants/watson/devicetree.cb diff --git a/src/mainboard/facebook/watson/variants/watson_v2/Makefile.inc b/src/mainboard/facebook/watson/variants/watson_v2/Makefile.inc new file mode 100644 index 0000000000..29763fb4f6 --- /dev/null +++ b/src/mainboard/facebook/watson/variants/watson_v2/Makefile.inc @@ -0,0 +1 @@ +romstage-y += romstage.c diff --git a/src/mainboard/facebook/watson/variants/watson_v2/devicetree.cb b/src/mainboard/facebook/watson/variants/watson_v2/devicetree.cb new file mode 100644 index 0000000000..e019a4eb2c --- /dev/null +++ b/src/mainboard/facebook/watson/variants/watson_v2/devicetree.cb @@ -0,0 +1,24 @@ +chip soc/intel/fsp_broadwell_de + register "lpc_lgmr" = "0xb0000001" # CPLD + device cpu_cluster 0 on + device lapic 0 on end + end + device domain 0 on + device pci 00.0 on end # SoC router + device pci 14.0 on end # xHCI Controller + device pci 19.0 on end # Gigabit LAN Controller + device pci 1d.0 on end # EHCI Controller + device pci 1f.0 on # LPC Bridge + chip drivers/pc80/tpm + device pnp 0c31.0 on end + end + chip drivers/ipmi + device pnp ca2.0 on end + register "bmc_i2c_address" = "0x20" + end + end + device pci 1f.2 on end # SATA Controller + device pci 1f.3 on end # SMBus Controller + device pci 1f.5 on end # SATA Controller + end +end diff --git a/src/mainboard/facebook/watson/variants/watson_v2/romstage.c b/src/mainboard/facebook/watson/variants/watson_v2/romstage.c new file mode 100644 index 0000000000..2e571b9713 --- /dev/null +++ b/src/mainboard/facebook/watson/variants/watson_v2/romstage.c @@ -0,0 +1,60 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2007-2009 coresystems GmbH + * Copyright (C) 2011 Google Inc. + * Copyright (C) Facebook, Inc. and its affiliates + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include + +void variant_romstage_fsp_init_params(UPD_DATA_REGION *UpdData) +{ + /* Configure IOU1 as 4*4 lanes */ + UpdData->ConfigIOU1_PciPort3 = 0; + + /* Configure IOU2 as 2*4 lanes */ + UpdData->ConfigIOU2_PciPort1 = 0; + + /* Configure PCH PCIe ports as 8*1 lanes */ + UpdData->PchPciPort1 = 1; + UpdData->PchPciPort2 = 1; + UpdData->PchPciPort3 = 1; + UpdData->PchPciPort4 = 1; + UpdData->PchPciPort5 = 1; + UpdData->PchPciPort6 = 1; + UpdData->PchPciPort7 = 1; + UpdData->PchPciPort8 = 1; + + /* Enable hotplug for PCH PCIe ports */ + UpdData->HotPlug_PchPciPort1 = 1; + UpdData->HotPlug_PchPciPort2 = 1; + UpdData->HotPlug_PchPciPort3 = 1; + UpdData->HotPlug_PchPciPort4 = 1; + UpdData->HotPlug_PchPciPort5 = 1; + UpdData->HotPlug_PchPciPort6 = 1; + UpdData->HotPlug_PchPciPort7 = 1; + UpdData->HotPlug_PchPciPort8 = 1; +} + +void variant_early_mainboard_romstage_entry(void) +{ + // Enable LPC IO ports 0xca2, 0xca8 for IPMI + pci_write_config32(PCH_DEV_LPC, LPC_GEN2_DEC, + (0 << 16) | ALIGN_DOWN(0xca2, 4) | 1); + pci_write_config32(PCH_DEV_LPC, LPC_GEN3_DEC, + (0 << 16) | ALIGN_DOWN(0xca8, 4) | 1); +} diff --git a/src/mainboard/lenovo/t60/variants/t60/overridetree.cb b/src/mainboard/lenovo/t60/variants/t60/overridetree.cb index eee3a4d575..551fff3771 100644 --- a/src/mainboard/lenovo/t60/variants/t60/overridetree.cb +++ b/src/mainboard/lenovo/t60/variants/t60/overridetree.cb @@ -44,6 +44,7 @@ chip northbridge/intel/i945 register "has_bdc_detection" = "1" register "bdc_gpio_num" = "7" register "bdc_gpio_lvl" = "0" + device pnp ff.2 on end end chip superio/nsc/pc87384 device pnp 2e.2 off # Serial Port / IR @@ -62,6 +63,7 @@ chip northbridge/intel/i945 register "regs" = "{ 0x2e, 0xf7, 0x3c, 0x20, 0x01, 0x00, 0x1b, 0x01, 0x54, 0xff, 0xff, 0x07 }" + device i2c 69 on end end end end diff --git a/src/mainboard/lenovo/t60/variants/z61t/overridetree.cb b/src/mainboard/lenovo/t60/variants/z61t/overridetree.cb index d29df3b488..c372b18e38 100644 --- a/src/mainboard/lenovo/t60/variants/z61t/overridetree.cb +++ b/src/mainboard/lenovo/t60/variants/z61t/overridetree.cb @@ -57,6 +57,7 @@ chip northbridge/intel/i945 # vendor clockgen setup register "regs" = "{ 0x6d, 0xff, 0xff, 0x20, 0x41, 0x7f, 0x18, 0x00 }" + device i2c 69 on end end end end diff --git a/src/mainboard/ocp/monolake/Kconfig b/src/mainboard/ocp/monolake/Kconfig index 7d85bbba70..668ef61c00 100644 --- a/src/mainboard/ocp/monolake/Kconfig +++ b/src/mainboard/ocp/monolake/Kconfig @@ -9,7 +9,7 @@ config BOARD_SPECIFIC_OPTIONS select INTEGRATED_UART if FSP_PACKAGE_DEFAULT select HAVE_FSP_BIN if FSP_PACKAGE_DEFAULT select SERIRQ_CONTINUOUS_MODE - select MAINBOARD_USES_IFD_GBE_REGION + select FSP_EHCI1_ENABLE select MAINBOARD_HAS_LPC_TPM select MAINBOARD_HAS_TPM1 select IPMI_KCS @@ -60,4 +60,6 @@ config FMDFILE config IPMI_KCS_REGISTER_SPACING default 4 +config IPMI_FRU_SINGLE_RW_SZ + default 16 endif # BOARD_OCP_MONOLAKE diff --git a/src/mainboard/ocp/monolake/acpi/mainboard.asl b/src/mainboard/ocp/monolake/acpi/mainboard.asl deleted file mode 100644 index 62944ef353..0000000000 --- a/src/mainboard/ocp/monolake/acpi/mainboard.asl +++ /dev/null @@ -1,20 +0,0 @@ -/* - * This file is part of the coreboot project. - * - * Copyright (C) 2012 Google Inc. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; version 2 of - * the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -Device (PWRB) -{ - Name(_HID, EisaId("PNP0C0C")) -} diff --git a/src/mainboard/ocp/monolake/devicetree.cb b/src/mainboard/ocp/monolake/devicetree.cb index 26c95d5d67..4f351b0c4c 100644 --- a/src/mainboard/ocp/monolake/devicetree.cb +++ b/src/mainboard/ocp/monolake/devicetree.cb @@ -5,9 +5,7 @@ chip soc/intel/fsp_broadwell_de device domain 0 on device pci 00.0 on end # SoC router device pci 02.2 off end # IOU0 port C, 10GbE - device pci 02.3 off end # IOU0 port D, 10GbE device pci 14.0 on end # xHCI Controller - device pci 19.0 on end # Gigabit LAN Controller device pci 1d.0 on end # EHCI Controller device pci 1f.0 on chip drivers/pc80/tpm @@ -16,10 +14,12 @@ chip soc/intel/fsp_broadwell_de chip drivers/ipmi # BMC KCS device pnp ca2.0 on end register "bmc_i2c_address" = "0x20" + # On cold boot it takes a while for the BMC to start the IPMI service + register "wait_for_bmc" = "1" + register "bmc_boot_timeout" = "60" end end # LPC Bridge device pci 1f.2 on end # SATA Controller device pci 1f.3 on end # SMBus Controller - device pci 1f.5 on end # SATA Controller end end diff --git a/src/mainboard/ocp/monolake/dsdt.asl b/src/mainboard/ocp/monolake/dsdt.asl index 1248703266..5fe9d44f7e 100644 --- a/src/mainboard/ocp/monolake/dsdt.asl +++ b/src/mainboard/ocp/monolake/dsdt.asl @@ -38,257 +38,6 @@ DefinitionBlock( #include } - Name (PRUN, Package() { - Package() { 0x0008FFFF, 0, \_SB.PCI0.LPC0.LNKA, 0 }, - Package() { 0x0008FFFF, 1, \_SB.PCI0.LPC0.LNKB, 0 }, - Package() { 0x0008FFFF, 2, \_SB.PCI0.LPC0.LNKC, 0 }, - Package() { 0x0008FFFF, 3, \_SB.PCI0.LPC0.LNKD, 0 }, - - Package() { 0x0009FFFF, 0, \_SB.PCI0.LPC0.LNKA, 0 }, - Package() { 0x0009FFFF, 1, \_SB.PCI0.LPC0.LNKB, 0 }, - Package() { 0x0009FFFF, 2, \_SB.PCI0.LPC0.LNKC, 0 }, - Package() { 0x0009FFFF, 3, \_SB.PCI0.LPC0.LNKD, 0 }, - - Package() { 0x000AFFFF, 0, \_SB.PCI0.LPC0.LNKA, 0 }, - Package() { 0x000AFFFF, 1, \_SB.PCI0.LPC0.LNKB, 0 }, - Package() { 0x000AFFFF, 2, \_SB.PCI0.LPC0.LNKC, 0 }, - Package() { 0x000AFFFF, 3, \_SB.PCI0.LPC0.LNKD, 0 }, - - Package() { 0x000BFFFF, 0, \_SB.PCI0.LPC0.LNKA, 0 }, - Package() { 0x000BFFFF, 1, \_SB.PCI0.LPC0.LNKB, 0 }, - Package() { 0x000BFFFF, 2, \_SB.PCI0.LPC0.LNKC, 0 }, - Package() { 0x000BFFFF, 3, \_SB.PCI0.LPC0.LNKD, 0 }, - - Package() { 0x000CFFFF, 0, \_SB.PCI0.LPC0.LNKA, 0 }, - Package() { 0x000CFFFF, 1, \_SB.PCI0.LPC0.LNKB, 0 }, - Package() { 0x000CFFFF, 2, \_SB.PCI0.LPC0.LNKC, 0 }, - Package() { 0x000CFFFF, 3, \_SB.PCI0.LPC0.LNKD, 0 }, - - Package() { 0x000DFFFF, 0, \_SB.PCI0.LPC0.LNKA, 0 }, - Package() { 0x000DFFFF, 1, \_SB.PCI0.LPC0.LNKB, 0 }, - Package() { 0x000DFFFF, 2, \_SB.PCI0.LPC0.LNKC, 0 }, - Package() { 0x000DFFFF, 3, \_SB.PCI0.LPC0.LNKD, 0 }, - - Package() { 0x000EFFFF, 0, \_SB.PCI0.LPC0.LNKA, 0 }, - Package() { 0x000EFFFF, 1, \_SB.PCI0.LPC0.LNKB, 0 }, - Package() { 0x000EFFFF, 2, \_SB.PCI0.LPC0.LNKC, 0 }, - Package() { 0x000EFFFF, 3, \_SB.PCI0.LPC0.LNKD, 0 }, - - Package() { 0x000FFFFF, 0, \_SB.PCI0.LPC0.LNKA, 0 }, - Package() { 0x000FFFFF, 1, \_SB.PCI0.LPC0.LNKB, 0 }, - Package() { 0x000FFFFF, 2, \_SB.PCI0.LPC0.LNKC, 0 }, - Package() { 0x000FFFFF, 3, \_SB.PCI0.LPC0.LNKD, 0 }, - - Package() { 0x0010FFFF, 0, \_SB.PCI0.LPC0.LNKA, 0 }, - Package() { 0x0010FFFF, 1, \_SB.PCI0.LPC0.LNKB, 0 }, - Package() { 0x0010FFFF, 2, \_SB.PCI0.LPC0.LNKC, 0 }, - Package() { 0x0010FFFF, 3, \_SB.PCI0.LPC0.LNKD, 0 }, - - Package() { 0x0011FFFF, 0, \_SB.PCI0.LPC0.LNKA, 0 }, - Package() { 0x0011FFFF, 1, \_SB.PCI0.LPC0.LNKB, 0 }, - Package() { 0x0011FFFF, 2, \_SB.PCI0.LPC0.LNKC, 0 }, - Package() { 0x0011FFFF, 3, \_SB.PCI0.LPC0.LNKD, 0 }, - - Package() { 0x0012FFFF, 0, \_SB.PCI0.LPC0.LNKA, 0 }, - Package() { 0x0012FFFF, 1, \_SB.PCI0.LPC0.LNKB, 0 }, - Package() { 0x0012FFFF, 2, \_SB.PCI0.LPC0.LNKC, 0 }, - Package() { 0x0012FFFF, 3, \_SB.PCI0.LPC0.LNKD, 0 }, - - Package() { 0x0013FFFF, 0, \_SB.PCI0.LPC0.LNKA, 0 }, - Package() { 0x0013FFFF, 1, \_SB.PCI0.LPC0.LNKB, 0 }, - Package() { 0x0013FFFF, 2, \_SB.PCI0.LPC0.LNKC, 0 }, - Package() { 0x0013FFFF, 3, \_SB.PCI0.LPC0.LNKD, 0 }, - - Package() { 0x0014FFFF, 0, \_SB.PCI0.LPC0.LNKA, 0 }, - Package() { 0x0014FFFF, 1, \_SB.PCI0.LPC0.LNKB, 0 }, - Package() { 0x0014FFFF, 2, \_SB.PCI0.LPC0.LNKC, 0 }, - Package() { 0x0014FFFF, 3, \_SB.PCI0.LPC0.LNKD, 0 }, - - Package() { 0x0016FFFF, 0, \_SB.PCI0.LPC0.LNKA, 0 }, - Package() { 0x0016FFFF, 1, \_SB.PCI0.LPC0.LNKB, 0 }, - Package() { 0x0016FFFF, 2, \_SB.PCI0.LPC0.LNKC, 0 }, - Package() { 0x0016FFFF, 3, \_SB.PCI0.LPC0.LNKD, 0 }, - - Package() { 0x0017FFFF, 0, \_SB.PCI0.LPC0.LNKA, 0 }, - Package() { 0x0017FFFF, 1, \_SB.PCI0.LPC0.LNKB, 0 }, - Package() { 0x0017FFFF, 2, \_SB.PCI0.LPC0.LNKC, 0 }, - Package() { 0x0017FFFF, 3, \_SB.PCI0.LPC0.LNKD, 0 }, - - Package() { 0x0018FFFF, 0, \_SB.PCI0.LPC0.LNKA, 0 }, - Package() { 0x0018FFFF, 1, \_SB.PCI0.LPC0.LNKB, 0 }, - Package() { 0x0018FFFF, 2, \_SB.PCI0.LPC0.LNKC, 0 }, - Package() { 0x0018FFFF, 3, \_SB.PCI0.LPC0.LNKD, 0 }, - - Package() { 0x0019FFFF, 0, \_SB.PCI0.LPC0.LNKA, 0 }, - Package() { 0x0019FFFF, 1, \_SB.PCI0.LPC0.LNKB, 0 }, - Package() { 0x0019FFFF, 2, \_SB.PCI0.LPC0.LNKC, 0 }, - Package() { 0x0019FFFF, 3, \_SB.PCI0.LPC0.LNKD, 0 }, - - Package() { 0x001CFFFF, 0, \_SB.PCI0.LPC0.LNKA, 0 }, - Package() { 0x001CFFFF, 1, \_SB.PCI0.LPC0.LNKB, 0 }, - Package() { 0x001CFFFF, 2, \_SB.PCI0.LPC0.LNKC, 0 }, - Package() { 0x001CFFFF, 3, \_SB.PCI0.LPC0.LNKD, 0 }, - - Package() { 0x001DFFFF, 0, \_SB.PCI0.LPC0.LNKA, 0 }, - Package() { 0x001DFFFF, 1, \_SB.PCI0.LPC0.LNKB, 0 }, - Package() { 0x001DFFFF, 2, \_SB.PCI0.LPC0.LNKC, 0 }, - Package() { 0x001DFFFF, 3, \_SB.PCI0.LPC0.LNKD, 0 }, - - Package() { 0x001EFFFF, 0, \_SB.PCI0.LPC0.LNKA, 0 }, - Package() { 0x001EFFFF, 1, \_SB.PCI0.LPC0.LNKB, 0 }, - Package() { 0x001EFFFF, 2, \_SB.PCI0.LPC0.LNKC, 0 }, - Package() { 0x001EFFFF, 3, \_SB.PCI0.LPC0.LNKD, 0 }, - - Package() { 0x001FFFFF, 0, \_SB.PCI0.LPC0.LNKA, 0 }, - Package() { 0x001FFFFF, 1, \_SB.PCI0.LPC0.LNKB, 0 }, - Package() { 0x001FFFFF, 2, \_SB.PCI0.LPC0.LNKC, 0 }, - Package() { 0x001FFFFF, 3, \_SB.PCI0.LPC0.LNKD, 0 }, - }) - - Name (ARUN, Package() { - Package() { 0x0008FFFF, 0, 0, 16 }, - Package() { 0x0008FFFF, 1, 0, 17 }, - Package() { 0x0008FFFF, 2, 0, 18 }, - Package() { 0x0008FFFF, 3, 0, 19 }, - - Package() { 0x0009FFFF, 0, 0, 16 }, - Package() { 0x0009FFFF, 1, 0, 17 }, - Package() { 0x0009FFFF, 2, 0, 18 }, - Package() { 0x0009FFFF, 3, 0, 19 }, - - Package() { 0x000AFFFF, 0, 0, 16 }, - Package() { 0x000AFFFF, 1, 0, 17 }, - Package() { 0x000AFFFF, 2, 0, 18 }, - Package() { 0x000AFFFF, 3, 0, 19 }, - - Package() { 0x000BFFFF, 0, 0, 16 }, - Package() { 0x000BFFFF, 1, 0, 17 }, - Package() { 0x000BFFFF, 2, 0, 18 }, - Package() { 0x000BFFFF, 3, 0, 19 }, - - Package() { 0x000CFFFF, 0, 0, 16 }, - Package() { 0x000CFFFF, 1, 0, 17 }, - Package() { 0x000CFFFF, 2, 0, 18 }, - Package() { 0x000CFFFF, 3, 0, 19 }, - - Package() { 0x000DFFFF, 0, 0, 16 }, - Package() { 0x000DFFFF, 1, 0, 17 }, - Package() { 0x000DFFFF, 2, 0, 18 }, - Package() { 0x000DFFFF, 3, 0, 19 }, - - Package() { 0x000EFFFF, 0, 0, 16 }, - Package() { 0x000EFFFF, 1, 0, 17 }, - Package() { 0x000EFFFF, 2, 0, 18 }, - Package() { 0x000EFFFF, 3, 0, 19 }, - - Package() { 0x000FFFFF, 0, 0, 16 }, - Package() { 0x000FFFFF, 1, 0, 17 }, - Package() { 0x000FFFFF, 2, 0, 18 }, - Package() { 0x000FFFFF, 3, 0, 19 }, - - Package() { 0x0010FFFF, 0, 0, 16 }, - Package() { 0x0010FFFF, 1, 0, 17 }, - Package() { 0x0010FFFF, 2, 0, 18 }, - Package() { 0x0010FFFF, 3, 0, 19 }, - - Package() { 0x0011FFFF, 0, 0, 16 }, - Package() { 0x0011FFFF, 1, 0, 17 }, - Package() { 0x0011FFFF, 2, 0, 18 }, - Package() { 0x0011FFFF, 3, 0, 19 }, - - Package() { 0x0012FFFF, 0, 0, 16 }, - Package() { 0x0012FFFF, 1, 0, 17 }, - Package() { 0x0012FFFF, 2, 0, 18 }, - Package() { 0x0012FFFF, 3, 0, 19 }, - - Package() { 0x0013FFFF, 0, 0, 16 }, - Package() { 0x0013FFFF, 1, 0, 17 }, - Package() { 0x0013FFFF, 2, 0, 18 }, - Package() { 0x0013FFFF, 3, 0, 19 }, - - Package() { 0x0014FFFF, 0, 0, 16 }, - Package() { 0x0014FFFF, 1, 0, 17 }, - Package() { 0x0014FFFF, 2, 0, 18 }, - Package() { 0x0014FFFF, 3, 0, 19 }, - - Package() { 0x0016FFFF, 0, 0, 16 }, - Package() { 0x0016FFFF, 1, 0, 17 }, - Package() { 0x0016FFFF, 2, 0, 18 }, - Package() { 0x0016FFFF, 3, 0, 19 }, - - Package() { 0x0017FFFF, 0, 0, 16 }, - Package() { 0x0017FFFF, 1, 0, 17 }, - Package() { 0x0017FFFF, 2, 0, 18 }, - Package() { 0x0017FFFF, 3, 0, 19 }, - - Package() { 0x0018FFFF, 0, 0, 16 }, - Package() { 0x0018FFFF, 1, 0, 17 }, - Package() { 0x0018FFFF, 2, 0, 18 }, - Package() { 0x0018FFFF, 3, 0, 19 }, - - Package() { 0x0019FFFF, 0, 0, 16 }, - Package() { 0x0019FFFF, 1, 0, 17 }, - Package() { 0x0019FFFF, 2, 0, 18 }, - Package() { 0x0019FFFF, 3, 0, 19 }, - - Package() { 0x001CFFFF, 0, 0, 16 }, - Package() { 0x001CFFFF, 1, 0, 17 }, - Package() { 0x001CFFFF, 2, 0, 18 }, - Package() { 0x001CFFFF, 3, 0, 19 }, - - Package() { 0x001DFFFF, 0, 0, 16 }, - Package() { 0x001DFFFF, 1, 0, 17 }, - Package() { 0x001DFFFF, 2, 0, 18 }, - Package() { 0x001DFFFF, 3, 0, 19 }, - - Package() { 0x001EFFFF, 0, 0, 16 }, - Package() { 0x001EFFFF, 1, 0, 17 }, - Package() { 0x001EFFFF, 2, 0, 18 }, - Package() { 0x001EFFFF, 3, 0, 19 }, - - Package() { 0x001FFFFF, 0, 0, 16 }, - Package() { 0x001FFFFF, 1, 0, 17 }, - Package() { 0x001FFFFF, 2, 0, 18 }, - Package() { 0x001FFFFF, 3, 0, 19 }, - }) - - Device (UNC0) - { - Name (_HID, EisaId ("PNP0A03")) - Name (_UID, 0x3F) - Method (_BBN, 0, NotSerialized) - { - Return (0xff) - } - - Name (_ADR, 0x00) - Method (_STA, 0, NotSerialized) - { - Return (0xf) - } - - Name (_CRS, ResourceTemplate () - { - WordBusNumber (ResourceProducer, MinFixed, MaxFixed, PosDecode, - 0x0000, // Granularity - 0x00FF, // Range Minimum - 0x00FF, // Range Maximum - 0x0000, // Translation Offset - 0x0001, // Length - ,, ) - }) - - Method (_PRT, 0, NotSerialized) - { - If (LEqual (PICM, Zero)) - { - Return (PRUN) - } - - Return (ARUN) - } - } + #include } - - #include "acpi/mainboard.asl" } diff --git a/src/mainboard/ocp/monolake/mainboard.c b/src/mainboard/ocp/monolake/mainboard.c index dffd19f0f0..7887bae5c4 100644 --- a/src/mainboard/ocp/monolake/mainboard.c +++ b/src/mainboard/ocp/monolake/mainboard.c @@ -14,6 +14,7 @@ * GNU General Public License for more details. */ +#include #include #include #include @@ -22,6 +23,7 @@ #include #include #include +#include #include "ipmi.h" /* VPD variable for enabling/disabling FRB2 timer. */ #define FRB2_TIMER "FRB2_TIMER" @@ -30,6 +32,10 @@ #define VPD_LEN 10 /* Default countdown is 15 minutes. */ #define DEFAULT_COUNTDOWN 9000 +#define FRU_DEVICE_ID 0 +static struct fru_info_str fru_strings; +#define MAX_IMC 1 +#define MAX_DIMM_SIZE_GB (32 * MiB) static void init_frb2_wdt(void) { @@ -60,6 +66,44 @@ static void init_frb2_wdt(void) } } +#if CONFIG(GENERATE_SMBIOS_TABLES) +static int write_smbios_type16(struct device *dev, int *handle, unsigned long *current) +{ + struct smbios_type16 *t = (struct smbios_type16 *)*current; + u32 maximum_capacity; + int len = sizeof(struct smbios_type16); + + printk(BIOS_INFO, "Creating SMBIOS tables type 16 (note, ECC information is hard-coded) ..."); + + memset(t, 0, sizeof(struct smbios_type16)); + t->type = SMBIOS_PHYS_MEMORY_ARRAY; + t->location = MEMORY_ARRAY_LOCATION_SYSTEM_BOARD; + t->use = MEMORY_ARRAY_USE_SYSTEM; + /* The ECC setting can`t be confirmed in FSP, so hardcode it. */ + t->memory_error_correction = MEMORY_ARRAY_ECC_SINGLE_BIT; + t->memory_error_information_handle = 0xFFFE; + t->number_of_memory_devices = CONFIG_DIMM_MAX / MAX_IMC; + + maximum_capacity = (u32)(CONFIG_DIMM_MAX * MAX_DIMM_SIZE_GB); + if (maximum_capacity >= 0x80000000) { + t->maximum_capacity = 0x80000000; + t->extended_maximum_capacity = maximum_capacity << 10; + } else { + t->maximum_capacity = (u32)maximum_capacity; + t->extended_maximum_capacity = 0; + } + + *current += len; + t->handle = *handle; + *handle += 1; + t->length = len - 2; + + printk(BIOS_INFO, "done\n"); + + return len; +} +#endif + /* * mainboard_enable is executed as first thing after enumerate_buses(). * This is the earliest point to add customization. @@ -76,6 +120,12 @@ static void mainboard_enable(struct device *dev) clear_ipmi_flags(&rsp); system_reset(); } + + read_fru_areas(BMC_KCS_BASE, FRU_DEVICE_ID, 0, &fru_strings); + +#if (CONFIG(GENERATE_SMBIOS_TABLES)) + dev->ops->get_smbios_data = write_smbios_type16; +#endif } struct chip_operations mainboard_ops = { @@ -95,3 +145,102 @@ void smbios_fill_dimm_locator(const struct dimm_info *dimm, struct smbios_type17 dimm->dimm_num); t->bank_locator = smbios_add_string(t->eos, locator); } + +/* Override SMBIOS uuid from the value from BMC. */ +void smbios_system_set_uuid(u8 *uuid) +{ + ipmi_get_system_guid(BMC_KCS_BASE, uuid); +} +/* Override SMBIOS type 1 data. */ +const char *smbios_system_manufacturer(void) +{ + if (fru_strings.prod_info.manufacturer != NULL) + return (const char *)fru_strings.prod_info.manufacturer; + else + return CONFIG_MAINBOARD_SMBIOS_MANUFACTURER; +} + +const char *smbios_system_product_name(void) +{ + char *prod_name_partnumber; + /* Concatenates IPMI FRU Product Info product name + * and product part number. */ + if (fru_strings.prod_info.product_name != NULL) { + if (fru_strings.prod_info.product_partnumber != NULL) { + /* Append a space after product_name. */ + prod_name_partnumber = strconcat(fru_strings.prod_info.product_name, + " "); + if (!prod_name_partnumber) + return (const char *)fru_strings.prod_info.product_name; + + prod_name_partnumber = strconcat(prod_name_partnumber, + fru_strings.prod_info.product_partnumber); + if (!prod_name_partnumber) + return (const char *)fru_strings.prod_info.product_name; + + return (const char *)prod_name_partnumber; + } else { + return (const char *)fru_strings.prod_info.product_name; + } + } else { + return CONFIG_MAINBOARD_SMBIOS_PRODUCT_NAME; + } +} + +const char *smbios_system_serial_number(void) +{ + if (fru_strings.prod_info.serial_number != NULL) + return (const char *)fru_strings.prod_info.serial_number; + else + return CONFIG_MAINBOARD_SERIAL_NUMBER; +} + +const char *smbios_system_version(void) +{ + if (fru_strings.prod_info.product_version != NULL) + return (const char *)fru_strings.prod_info.product_version; + else + return CONFIG_MAINBOARD_SERIAL_NUMBER; +} +/* Override SMBIOS type 2 data. */ +const char *smbios_mainboard_version(void) +{ + if (fru_strings.board_info.part_number != NULL) + return (const char *)fru_strings.board_info.part_number; + else + return CONFIG_MAINBOARD_VERSION; +} + +const char *smbios_mainboard_manufacturer(void) +{ + if (fru_strings.board_info.manufacturer != NULL) + return (const char *)fru_strings.board_info.manufacturer; + else + return CONFIG_MAINBOARD_SMBIOS_MANUFACTURER; +} + +const char *smbios_mainboard_product_name(void) +{ + if (fru_strings.board_info.product_name != NULL) + return (const char *)fru_strings.board_info.product_name; + else + return CONFIG_MAINBOARD_SMBIOS_PRODUCT_NAME; +} + +const char *smbios_mainboard_serial_number(void) +{ + if (fru_strings.board_info.serial_number != NULL) + return (const char *)fru_strings.board_info.serial_number; + else + return CONFIG_MAINBOARD_SERIAL_NUMBER; +} + +/* Set the BMC BIOS POST complete GPIO (FM_BIOS_POST_CMPLT_N) on payload load. */ +static void bmc_set_post_complete_gpio_callback(void *arg) +{ + /* GPIO 46 FM_BIOS_POST_CMPLT_N */ + gpio_set(46, 0); + printk(BIOS_DEBUG, "BMC: POST complete gpio set\n"); +} + +BOOT_STATE_INIT_ENTRY(BS_PAYLOAD_BOOT, BS_ON_ENTRY, bmc_set_post_complete_gpio_callback, NULL); diff --git a/src/mainboard/ocp/monolake/romstage.c b/src/mainboard/ocp/monolake/romstage.c index ef41b7720e..ebc43de823 100644 --- a/src/mainboard/ocp/monolake/romstage.c +++ b/src/mainboard/ocp/monolake/romstage.c @@ -19,9 +19,6 @@ #include #include #include -#include -#include -#include #include #include #include @@ -193,20 +190,7 @@ static const struct gpio_config gpio_tables[] = { */ void early_mainboard_romstage_entry(void) { - /* - * Sometimes the system boots in an invalid state, where random values - * have been written to MSRs and then the MSRs are locked. - * Seems to always happen on warm reset. - * - * Power cycling or a board_reset() isn't sufficient in this case, so - * issue a full_reset() to "fix" this issue. - */ - msr_t msr = rdmsr(IA32_FEATURE_CONTROL); - if (msr.lo & 1) { - console_init(); - printk(BIOS_EMERG, "Detected broken platform state. Issuing full reset\n"); - full_reset(); - } + } /** diff --git a/src/mainboard/ocp/wedge100s/romstage.c b/src/mainboard/ocp/wedge100s/romstage.c index 108d7a1c4d..b1f8f26796 100644 --- a/src/mainboard/ocp/wedge100s/romstage.c +++ b/src/mainboard/ocp/wedge100s/romstage.c @@ -17,9 +17,6 @@ #include #include #include -#include -#include -#include #include #include #include @@ -44,25 +41,6 @@ void early_mainboard_romstage_entry(void) if (CONFIG(CONSOLE_SERIAL)) ite_enable_serial(SERIAL_DEV, CONFIG_TTYS0_BASE); - - - /* - * Sometimes the system boots in an invalid state, where random values - * have been written to MSRs and then the MSRs are locked. - * Seems to always happen on warm reset. - * - * Power cycling or a board_reset() isn't sufficient in this case, so - * issue a full_reset() to "fix" this issue. - * - * It seems to be a deficiency in the reset logic, as other - * FSP broadwell DE boards are not affected. - */ - msr_t msr = rdmsr(IA32_FEATURE_CONTROL); - if (msr.lo & 1) { - console_init(); - printk(BIOS_EMERG, "Detected broken platform state. Issuing full reset\n"); - full_reset(); - } } /** diff --git a/src/mainboard/siemens/mc_bdx1/dsdt.asl b/src/mainboard/siemens/mc_bdx1/dsdt.asl index 1248703266..76845f549f 100644 --- a/src/mainboard/siemens/mc_bdx1/dsdt.asl +++ b/src/mainboard/siemens/mc_bdx1/dsdt.asl @@ -261,7 +261,6 @@ DefinitionBlock( Return (0xff) } - Name (_ADR, 0x00) Method (_STA, 0, NotSerialized) { Return (0xf) 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..3098d5c901 --- /dev/null +++ b/src/security/intel/stm/Kconfig @@ -0,0 +1,122 @@ + + +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 0x100000 + help + The MSEG_SIZE of 0x100000 assumes that: + IED_REGION_SIZE = 0x400000 + SMM_RESERVED_SIZE = 0x200000 + SMM_TSEG_SIZE = 0x800000 + + To use STM/PE, a larger MSEG_SIZE is necessary. This can be + done by either increasing SMM_TSEG_SIZE or reducing the + IED_REGION_SIZE and/or SMM_RESERVED_SIZE or some combination + of the three. + NOTE: The authors experience is that these configuration + parameters have to be changed at the soc Konfig for them to + be applied. + Minimum sizes: + STM only - 0x100000 - Supports up to 38 processor threads + - 0x200000 - Supports up to 102 processor threads + STM/PE - 0x300000+ depending on the amount of memory needed + for the protected execution virtual + machine (VM/PE) + +config STM_STMPE_ENABLED + bool "STM/PE Enabled" + default n + help + STM/PE provides for additional virtual machines in SMRAM + that provides a protected execution environment for + applications such as introspection, which need to be + protected from malicious code. More information can be + found on the stmpe branch of + https://review.coreboot.org/STM + + +config BIOS_RESOURCE_LIST_SIZE + hex "bios resource list size" + default 0x1000 + help + The BIOS resource list defines the resources that the + SMI handler needs. This list is created during the + coreboot bootup. Unless there has been a lot of elements + added to this list, this value should not change. + +config STM_BINARY_FILE + string "STM binary file" + default "3rdparty/stm/Stm/build/StmPkg/Core/stm.bin" + help + Location of the STM binary file. The default location is + where the file will be located when coreboot builds + the STM. + +config STM_HEAPSIZE + hex "stm heapsize" + default 0x46000 + help + The STM_HEAPSIZE defines the heap space that is available + to the STM. The default size assumes a MSEG_SIZE of 0x100000. + For STM/PE this size should be a minimum of 0x246000. + +config STM_TTYS0_BASE + hex "stm uart" + default TTYS0_BASE if TTYS0_BASE + default 0x000 + help + Defines the serial port for STM console output. 0x000 indicates + no serial port. + +config STM_CBMEM_CONSOLE + bool "STM cbmem console" + default n + depends on CONSOLE_CBMEM + help + Places the STM console output into the cbmem. + +choice + prompt "Select STM console output" + +config STM_CONSOLE_DEBUG + bool "Debug output" + depends on STM_CBMEM_CONSOLE || STM_TTYS0_BASE + help + "Produces all STM console output" + +config STM_CONSOLE_RELEASE + bool "Deactivate console output" + help + "No console output is produced" +endchoice + +endmenu #STM + +endif diff --git a/src/security/intel/stm/Makefile b/src/security/intel/stm/Makefile new file mode 100644 index 0000000000..1493869e80 --- /dev/null +++ b/src/security/intel/stm/Makefile @@ -0,0 +1,33 @@ +# SPDX-License-Identifier: BSD-2-Clause + +project_name=STM +project_dir=../../../../3rdparty/stm/ +build_dir=$(project_dir)/Stm/build +project_git_branch=$(CONFIG_STM_GIT_BRANCH) + +ifeq ($(CONFIG_STM_CONSOLE_DEBUG),y) +STM_BUILD="debug" +endif + +ifeq ($(CONFIG_STM_CONSOLE_RELEASE),y) +STM_BUILD="release" +endif + + +all: build + +build: + echo "STM - Build" + cd $(project_dir)/Stm; \ + mkdir -p build; \ + cd build; \ + cmake .. -DBIOS=coreboot \ + -DUART=$(CONFIG_STM_TTYS0_BASE) \ + -DHEAPSIZE=$(CONFIG_STM_HEAPSIZE) \ + -DCBMEM_ENABLE=$(CONFIG_STM_CBMEM_CONSOLE) \ + -DSTMPE_ENABLED=$(CONFIG_STM_STMPE_ENABLED) \ + -DBUILD=$(STM_BUILD); \ + $(MAKE); + + +.PHONY: build diff --git a/src/security/intel/stm/Makefile.inc b/src/security/intel/stm/Makefile.inc new file mode 100644 index 0000000000..d4da605bd7 --- /dev/null +++ b/src/security/intel/stm/Makefile.inc @@ -0,0 +1,20 @@ + +# 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 + +3rdparty/stm/Stm/build/StmPkg/Core/stm.bin: $(obj)/config.h + $(MAKE) -C src/security/intel/stm \ + CONFIG_STM_TTYS0_BASE=$(CONFIG_STM_TTYS0_BASE) \ + CONFIG_STM_HEAPSIZE=$(CONFIG_STM_HEAPSIZE) \ + CONFIG_STM_CONSOLE_DEBUG=$(CONFIG_STM_CONSOLE_DEBUG) \ + CONFIG_STM_CONSOLE_RELEASE=$(CONFIG_STM_CONSOLE_RELEASE) \ + CONFIG_STM_GIT_BRANCH=$(CONFIG_STM_GIT_BRANCH) \ + CONFIG_STM_STMPE_ENABLED=$(CONFIG_STM_STMPE_ENABLED) \ + CONFIG_STM_CBMEM_CONSOLE=$(CONFIG_STM_CBMEM_CONSOLE) diff --git a/src/security/intel/stm/SmmStm.c b/src/security/intel/stm/SmmStm.c new file mode 100644 index 0000000000..e6f4c0ad61 --- /dev/null +++ b/src/security/intel/stm/SmmStm.c @@ -0,0 +1,657 @@ +/* @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 +#include +#include +#include +#include +#include +#include + +#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%08x\n", (int)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) { + printk(BIOS_ERR, + "STM: ERROR - Configured MSEG size 0x%x less than required MSEG size 0x%x\n", + CONFIG_MSEG_SIZE, min_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 +#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 + +// 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..7aa432de40 --- /dev/null +++ b/src/security/intel/stm/StmPlatformResource.c @@ -0,0 +1,192 @@ +/* @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 +#include +#include +#include + +#if CONFIG(SOUTHBRIDGE_INTEL_COMMON_PMCLIB) +#include +#else +#include +#endif +#include +#include + +#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. + */ + +extern uint8_t *m_stm_resources_ptr; + +void add_resources_cmd(void) +{ + m_stm_resources_ptr = NULL; + + 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..3f29afc096 --- /dev/null +++ b/src/security/intel/stm/StmPlatformSmm.c @@ -0,0 +1,213 @@ +/* @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 +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +/* + * 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; + msr_t vmx_basic; + + 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); + + vmx_basic = rdmsr(IA32_VMX_BASIC_MSR); + + // Does this processor support an STM? + if ((vmx_basic.hi & VMX_BASIC_HI_DUAL_MONITOR) != VMX_BASIC_HI_DUAL_MONITOR) { + printk(BIOS_WARNING, "STM: not supported on CPU %d\n", cpu); + return; + } + + // This code moved here because paralled SMM setup can cause some + // processors to get a bad value. + addr_calc = mseg - CONFIG_BIOS_RESOURCE_LIST_SIZE; + stm_resource_heap = (uint8_t *) addr_calc; + + if (cpu == 0) { + + // need to create the BIOS resource list once + // first calculate the location in SMRAM + 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); + } +} diff --git a/src/security/intel/txt/Kconfig b/src/security/intel/txt/Kconfig index 97d24fd6c9..049705fc6c 100644 --- a/src/security/intel/txt/Kconfig +++ b/src/security/intel/txt/Kconfig @@ -26,14 +26,12 @@ config INTEL_TXT if INTEL_TXT -menu "Intel" - config INTEL_TXT_BIOSACM_FILE string "BIOS ACM file" default "3rdparty/blobs/soc/intel/fsp_broadwell_de/biosacm.bin" if SOC_INTEL_FSP_BROADWELL_DE default "3rdparty/blobs/soc/intel/skylake/biosacm.bin" if SOC_INTEL_COMMON_SKYLAKE_BASE help - Intel TXT BIOS ACM file. This file can be obtained by privileged + Intel TXT BIOS ACM file. This file can be obtained through privileged access to Intel resources. Or for some platforms found inside the blob repository. @@ -42,17 +40,33 @@ config INTEL_TXT_SINITACM_FILE default "3rdparty/blobs/soc/intel/fsp_broadwell_de/sinitacm.bin" if SOC_INTEL_FSP_BROADWELL_DE default "3rdparty/blobs/soc/intel/skylake/sinitacm.bin" if SOC_INTEL_COMMON_SKYLAKE_BASE help - Intel TXT SINIT ACM file. This file can be obtained by privileged + Intel TXT SINIT ACM file. This file can be obtained through privileged access to Intel resources. Or for some platforms found inside the blob repository. +config INTEL_TXT_LOGGING + bool "Enable verbose logging" + help + Print more TXT related debug output. + Use in pre-production environments only! + config INTEL_TXT_BIOSACM_ALIGNMENT hex - default 0x20000 # 128KB + default 0x20000 # 128 KiB help - Exceptions are Ivy- and Sandy Bridge with 64KB and Purely with 256KB - alignment size. Please overwrite it SoC specific. + Exceptions are Ivy and Sandy Bridge with 64 KiB and Purley with 256 KiB + alignment size. If necessary, override from platform-specific Kconfig. -endmenu # Intel +config INTEL_TXT_CBFS_BIOS_POLICY + string + default "txt_bios_policy.bin" + +config INTEL_TXT_CBFS_BIOS_ACM + string + default "txt_bios_acm.bin" + +config INTEL_TXT_CBFS_SINIT_ACM + string + default "txt_sinit_acm.bin" endif diff --git a/src/security/intel/txt/Makefile.inc b/src/security/intel/txt/Makefile.inc index 38eb65d69c..39c3ad1dff 100644 --- a/src/security/intel/txt/Makefile.inc +++ b/src/security/intel/txt/Makefile.inc @@ -1,5 +1,14 @@ ifeq ($(CONFIG_INTEL_TXT),y) +romstage-y += common.c +romstage-$(CONFIG_INTEL_TXT_LOGGING) += logging.c + +ramstage-y += common.c +ramstage-y += getsec.c +ramstage-y += getsec_enteraccs.S +ramstage-y += ramstage.c +ramstage-$(CONFIG_INTEL_TXT_LOGGING) += logging.c + cbfs-files-y += txt_bios_acm.bin txt_bios_acm.bin-file := $(CONFIG_INTEL_TXT_BIOSACM_FILE) txt_bios_acm.bin-type := raw @@ -13,6 +22,8 @@ txt_sinit_acm.bin-align := 0x10 txt_sinit_acm.bin-compression := lzma endif +ifeq ($(CONFIG_CPU_INTEL_FIRMWARE_INTERFACE_TABLE),y) + INTERMEDIATE+=add_acm_fit add_acm_fit: $(obj)/coreboot.pre $(IFITTOOL) $(IFITTOOL) -r COREBOOT -a -n txt_bios_acm.bin -t 2 -s $(CONFIG_CPU_INTEL_NUM_FIT_ENTRIES) -f $< @@ -26,7 +37,9 @@ ibb-files += bootblock INTERMEDIATE+=add_ibb_fit add_ibb_fit: $(obj)/coreboot.pre $(IFITTOOL) - $(foreach file, $(ibb-files), $(shell $(IFITTOOL) -f $< -a -n $(file) -t 7 -s $(CONFIG_CPU_INTEL_NUM_FIT_ENTRIES) \ - -r COREBOOT)) true + $(foreach file, $(ibb-files), $(shell $(IFITTOOL) -f $< -a -n $(file) -t 7 \ + -s $(CONFIG_CPU_INTEL_NUM_FIT_ENTRIES) -r COREBOOT)) true -endif +endif # CPU_INTEL_FIRMWARE_INTERFACE_TABLE + +endif # INTEL_TXT diff --git a/src/security/intel/txt/common.c b/src/security/intel/txt/common.c new file mode 100644 index 0000000000..32350b7e89 --- /dev/null +++ b/src/security/intel/txt/common.c @@ -0,0 +1,421 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "txt.h" +#include "txt_register.h" +#include "txt_getsec.h" + +/** + * Dump the ACM error status bits. + * + * @param acm_error The status register to dump + * @return -1 on error (register is not valid) + * 0 on error (Class > 0 and Major > 0) + * 1 on success (Class == 0 and Major == 0 and progress > 0) + */ +int intel_txt_log_acm_error(const uint32_t acm_error) +{ + if (!(acm_error & ACMERROR_TXT_VALID)) + return -1; + + const uint8_t type = (acm_error & ACMERROR_TXT_TYPE_CODE) + >> ACMERROR_TXT_TYPE_SHIFT; + + switch (type) { + case ACMERROR_TXT_AC_MODULE_TYPE_BIOS: + printk(BIOS_ERR, "BIOSACM"); + break; + case ACMERROR_TXT_AC_MODULE_TYPE_SINIT: + printk(BIOS_ERR, "SINIT"); + break; + default: + printk(BIOS_ERR, "ACM"); + break; + } + printk(BIOS_ERR, ": Error code valid\n"); + + if (acm_error & ACMERROR_TXT_EXTERNAL) + printk(BIOS_ERR, " Caused by: External\n"); + else + printk(BIOS_ERR, " Caused by: Processor\n"); + + const uint32_t class = (acm_error & ACMERROR_TXT_CLASS_CODE) + >> ACMERROR_TXT_CLASS_SHIFT; + const uint32_t major = (acm_error & ACMERROR_TXT_MAJOR_CODE) + >> ACMERROR_TXT_MAJOR_SHIFT; + const uint32_t minor = (acm_error & ACMERROR_TXT_MINOR_CODE) + >> ACMERROR_TXT_MINOR_SHIFT; + const uint32_t progress = (acm_error & ACMERROR_TXT_PROGRESS_CODE) + >> ACMERROR_TXT_PROGRESS_SHIFT; + + if (!minor) { + if (class == 0 && major == 0 && progress > 0) { + printk(BIOS_ERR, " Execution successful\n"); + printk(BIOS_ERR, " Progress code 0x%x\n", progress); + } else { + printk(BIOS_ERR, " Error Class: %x\n", class); + printk(BIOS_ERR, " Error: %x.%x\n", major, progress); + } + } else { + printk(BIOS_ERR, " ACM didn't start\n"); + printk(BIOS_ERR, " Error Type: 0x%x\n", acm_error & 0xffffff); + return -1; + } + + return (acm_error & ACMERROR_TXT_EXTERNAL) && class == 0 && major == 0 && progress > 0; +} + +void intel_txt_log_spad(void) +{ + const uint64_t acm_status = read64((void *)TXT_SPAD); + + printk(BIOS_INFO, "TXT-STS: ACM verification "); + + if (acm_status & ACMSTS_VERIFICATION_ERROR) + printk(BIOS_INFO, "error\n"); + else + printk(BIOS_INFO, "successful\n"); + + printk(BIOS_INFO, "TXT-STS: IBB "); + + if (acm_status & ACMSTS_IBB_MEASURED) + printk(BIOS_INFO, "measured\n"); + else + printk(BIOS_INFO, "not measured\n"); + + printk(BIOS_INFO, "TXT-STS: TXT is "); + + if (acm_status & ACMSTS_TXT_DISABLED) + printk(BIOS_INFO, "disabled\n"); + else + printk(BIOS_INFO, "not disabled\n"); + + printk(BIOS_INFO, "TXT-STS: BIOS is "); + + if (acm_status & ACMSTS_BIOS_TRUSTED) + printk(BIOS_INFO, "trusted\n"); + else + printk(BIOS_INFO, "not trusted\n"); +} + +/* Returns true if secrets might be in memory */ +bool intel_txt_memory_has_secrets(void) +{ + bool ret; + if (!CONFIG(INTEL_TXT)) + return false; + + ret = (read8((void *)TXT_ESTS) & TXT_ESTS_WAKE_ERROR_STS) || + (read64((void *)TXT_E2STS) & TXT_E2STS_SECRET_STS); + + if (ret) + printk(BIOS_CRIT, "TXT-STS: Secrets in memory!\n"); + return ret; +} + +static struct acm_info_table *find_info_table(const void *ptr) +{ + const struct acm_header_v0 *acm_header = (struct acm_header_v0 *)ptr; + + return (struct acm_info_table *)(ptr + + (acm_header->header_len + acm_header->scratch_size) * sizeof(uint32_t)); +} + +/** + * Validate that the provided ACM is useable on this platform. + */ +static int validate_acm(const void *ptr) +{ + const struct acm_header_v0 *acm_header = (struct acm_header_v0 *)ptr; + uint32_t max_size_acm_area = 0; + + if (acm_header->module_type != CHIPSET_ACM) + return ACM_E_TYPE_NOT_MATCH; + + /* Seems inconsistent across generations. */ + if (acm_header->module_sub_type != 0 && acm_header->module_sub_type != 1) + return ACM_E_MODULE_SUB_TYPE_WRONG; + + if (acm_header->module_vendor != INTEL_ACM_VENDOR) + return ACM_E_MODULE_VENDOR_NOT_INTEL; + + if (((acm_header->header_len + acm_header->scratch_size) * sizeof(uint32_t) + + sizeof(struct acm_info_table)) > (acm_header->size & 0xffffff) * sizeof(uint32_t)) { + return ACM_E_SIZE_INCORRECT; + } + + if (!getsec_parameter(NULL, NULL, &max_size_acm_area, NULL, NULL, NULL)) + return ACM_E_CANT_CALL_GETSEC; + + /* + * Causes #GP if acm_header->size > processor internal authenticated + * code area capacity. + * SAFER MODE EXTENSIONS REFERENCE. + * Intel 64 and IA-32 Architectures Software Developer Manuals Vol 2D + */ + const size_t acm_len = 1UL << log2_ceil((acm_header->size & 0xffffff) << 2); + if (max_size_acm_area < acm_len) { + printk(BIOS_ERR, "TEE-TXT: BIOS ACM doesn't fit into AC execution region\n"); + return ACM_E_NOT_FIT_INTO_CPU_ACM_MEM; + } + + struct acm_info_table *info = find_info_table(ptr); + if (!info) + return ACM_E_NO_INFO_TABLE; + if (info->chipset_acm_type != BIOS) + return ACM_E_NOT_BIOS_ACM; + + static const u8 acm_uuid[] = { + 0xaa, 0x3a, 0xc0, 0x7f, 0xa7, 0x46, 0xdb, 0x18, + 0x2e, 0xac, 0x69, 0x8f, 0x8d, 0x41, 0x7f, 0x5a, + }; + if (memcmp(acm_uuid, info->uuid, sizeof(acm_uuid)) != 0) + return ACM_E_UUID_NOT_MATCH; + + if ((acm_header->flags & ACM_FORMAT_FLAGS_DEBUG) == + (read64((void *)TXT_VER_FSBIF) & TXT_VER_PRODUCTION_FUSED)) + return ACM_E_PLATFORM_IS_NOT_PROD; + + return 0; +} + +/* + * Test all bits for TXT execution. + * + * @return 0 on success + */ +int intel_txt_run_bios_acm(const u8 input_params) +{ + struct cbfsf file; + void *acm_data; + struct region_device acm; + size_t acm_len; + int ret; + + if (cbfs_boot_locate(&file, CONFIG_INTEL_TXT_CBFS_BIOS_ACM, NULL)) { + printk(BIOS_ERR, "TEE-TXT: Couldn't locate BIOS ACM in CBFS.\n"); + return -1; + } + + cbfs_file_data(&acm, &file); + acm_data = rdev_mmap_full(&acm); + acm_len = region_device_sz(&acm); + if (!acm_data || acm_len == 0) { + printk(BIOS_ERR, "TEE-TXT: Couldn't map BIOS ACM from CBFS.\n"); + return -1; + } + + /* + * CPU enforces only 4KiB alignment. + * Chapter A.1.1 + * Intel TXT Software Development Guide (Document: 315168-015) + */ + if (!IS_ALIGNED((uintptr_t)acm_data, 4096)) { + printk(BIOS_ERR, "TEE-TXT: BIOS ACM isn't mapped at page boundary.\n"); + rdev_munmap(&acm, acm_data); + return -1; + } + + /* + * Causes #GP if not multiple of 64. + * SAFER MODE EXTENSIONS REFERENCE. + * Intel 64 and IA-32 Architectures Software Developer Manuals Vol 2D + */ + if (!IS_ALIGNED(acm_len, 64)) { + printk(BIOS_ERR, "TEE-TXT: BIOS ACM size isn't multiple of 64.\n"); + rdev_munmap(&acm, acm_data); + return -1; + } + + /* + * The ACM should be aligned to it's size, but that's not possible, as + * some ACMs are not power of two. Use the next power of two for verification. + */ + if (!IS_ALIGNED((uintptr_t)acm_data, (1UL << log2_ceil(acm_len)))) { + printk(BIOS_ERR, "TEE-TXT: BIOS ACM isn't aligned to its size.\n"); + rdev_munmap(&acm, acm_data); + return -1; + } + + if (CONFIG(INTEL_TXT_LOGGING)) + txt_dump_acm_info(acm_data); + + ret = validate_acm(acm_data); + if (ret < 0) { + printk(BIOS_ERR, "TEE-TXT: Validation of ACM failed with: %d\n", ret); + rdev_munmap(&acm, acm_data); + return ret; + } + + /* Call into assembly which invokes the referenced ACM */ + getsec_enteraccs(input_params, (uintptr_t)acm_data, acm_len); + + rdev_munmap(&acm, acm_data); + + const uint64_t acm_status = read64((void *)TXT_SPAD); + if (acm_status & ACMERROR_TXT_VALID) { + printk(BIOS_ERR, "TEE-TXT: FATAL ACM launch error !\n"); + /* + * WARNING ! + * To clear TXT.BIOSACM.ERRORCODE you must issue a cold reboot! + */ + intel_txt_log_acm_error(read32((void *)TXT_BIOSACM_ERRORCODE)); + return -1; + } + if (intel_txt_log_acm_error(read32((void *)TXT_BIOSACM_ERRORCODE)) != 1) + return -1; + + return 0; +} + + /* Returns true if cond is not met */ +static bool check_precondition(const int cond) +{ + printk(BIOS_DEBUG, "%s\n", cond ? "true" : "false"); + return !cond; +} + +/* + * Test all bits that are required for Intel TXT. + * Enable SMX if available. + * + * @return 0 on success + */ +bool intel_txt_prepare_txt_env(void) +{ + bool failure = false; + uint32_t txt_feature_flags = 0; + + unsigned int ecx = cpuid_ecx(1); + + printk(BIOS_DEBUG, "TEE-TXT: CPU supports SMX: "); + failure |= check_precondition(ecx & CPUID_SMX); + + printk(BIOS_DEBUG, "TEE-TXT: CPU supports VMX: "); + failure |= check_precondition(ecx & CPUID_VMX); + + msr_t msr = rdmsr(IA32_FEATURE_CONTROL); + if (!(msr.lo & BIT(0))) { + printk(BIOS_ERR, "TEE-TXT: IA32_FEATURE_CONTROL is not locked\n"); + full_reset(); + } + + printk(BIOS_DEBUG, "TEE-TXT: IA32_FEATURE_CONTROL\n"); + printk(BIOS_DEBUG, " VMXON in SMX enable: "); + failure |= check_precondition(msr.lo & BIT(1)); + + printk(BIOS_DEBUG, " VMXON outside SMX enable: "); + failure |= check_precondition(msr.lo & FEATURE_ENABLE_VMX); + + printk(BIOS_DEBUG, " register is locked: "); + failure |= check_precondition(msr.lo & BIT(0)); + + /* IA32_FEATURE_CONTROL enables getsec instructions */ + printk(BIOS_DEBUG, " GETSEC (all instructions) is enabled: "); + failure |= check_precondition((msr.lo & 0xff00) == 0xff00); + + /* Prevent crash and opt out early */ + if (failure) + return true; + + uint32_t eax = 0; + /* + * GetSec[CAPABILITIES] + * SAFER MODE EXTENSIONS REFERENCE. + * Intel 64 and IA-32 Architectures Software Developer Manuals Vol 2D + * Must check BIT0 of TXT chipset has been detected by CPU. + */ + if (!getsec_capabilities(&eax)) + return true; + + printk(BIOS_DEBUG, "TEE-TXT: GETSEC[CAPABILITIES] returned:\n"); + printk(BIOS_DEBUG, " TXT capable chipset: %s\n", (eax & BIT(0)) ? "true" : "false"); + + printk(BIOS_DEBUG, " ENTERACCS available: %s\n", (eax & BIT(2)) ? "true" : "false"); + printk(BIOS_DEBUG, " EXITAC available: %s\n", (eax & BIT(3)) ? "true" : "false"); + printk(BIOS_DEBUG, " SENTER available: %s\n", (eax & BIT(4)) ? "true" : "false"); + printk(BIOS_DEBUG, " SEXIT available: %s\n", (eax & BIT(5)) ? "true" : "false"); + printk(BIOS_DEBUG, " PARAMETERS available: %s\n", (eax & BIT(6)) ? "true" : "false"); + + /* + * Causes #GP if function is not supported by getsec. + * SAFER MODE EXTENSIONS REFERENCE. + * Intel 64 and IA-32 Architectures Software Developer Manuals Vol 2D + * Order Number: 325383-060US + */ + if ((eax & 0x7d) != 0x7d) + failure = true; + + const uint64_t status = read64((void *)TXT_SPAD); + + if (status & ACMSTS_TXT_DISABLED) { + printk(BIOS_INFO, "TEE-TXT: TXT disabled by BIOS policy in FIT.\n"); + failure = true; + } + + /* + * Only the BSP must call getsec[ENTERACCS]. + * SAFER MODE EXTENSIONS REFERENCE. + * Intel 64 and IA-32 Architectures Software Developer Manuals Vol 2D + * Order Number: 325383-060US + */ + if (!boot_cpu()) { + printk(BIOS_ERR, "TEE-TXT: BSP flag not set in APICBASE_MSR.\n"); + failure = true; + } + + /* + * There must be no MCEs pending. + * Intel 64 and IA-32 Architectures Software Developer Manuals Vol 2D + * Order Number: 325383-060US + */ + msr = rdmsr(IA32_MCG_STATUS); + if (msr.lo & 0x4) { + printk(BIOS_ERR, "TEE-TXT: IA32_MCG_STATUS.MCIP is set.\n"); + failure = true; + } + + if (!getsec_parameter(NULL, NULL, NULL, NULL, NULL, &txt_feature_flags)) { + return true; + } else { + printk(BIOS_DEBUG, "TEE-TXT: Machine Check Register: "); + if (txt_feature_flags & GETSEC_PARAMS_TXT_EXT_MACHINE_CHECK) + printk(BIOS_DEBUG, "preserved\n"); + else + printk(BIOS_DEBUG, "must be clear\n"); + } + + if (!(txt_feature_flags & GETSEC_PARAMS_TXT_EXT_MACHINE_CHECK)) { + /* + * Make sure there are no uncorrectable MCE errors. + * Intel 64 and IA-32 Architectures Software Developer Manuals Vol 2D + */ + msr = rdmsr(IA32_MCG_CAP); + size_t max_mc_msr = msr.lo & MCA_BANKS_MASK; + for (size_t i = 0; i < max_mc_msr; i++) { + msr = rdmsr(IA32_MC0_STATUS + 4 * i); + if (!(msr.hi & MCA_STATUS_HI_UC)) + continue; + + printk(BIOS_ERR, "TEE-TXT: IA32_MC%zd_STATUS.UC is set.\n", i); + failure = true; + break; + } + } + + /* Need to park all APs. */ + if (CONFIG(PARALLEL_MP_AP_WORK)) + mp_park_aps(); + + return failure; +} diff --git a/src/security/intel/txt/getsec.c b/src/security/intel/txt/getsec.c new file mode 100644 index 0000000000..a42607dccc --- /dev/null +++ b/src/security/intel/txt/getsec.c @@ -0,0 +1,117 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include +#include +#include +#include +#include + +#include "txt_register.h" +#include "txt_getsec.h" + +/** + * Check for SMX support and enable it if possible. + * + * Returns false on error, true on success. + */ +static bool getsec_enabled(void) +{ + unsigned int ecx = cpuid_ecx(1); + /* + * Check if SMX and VMX is supported by CPU. + */ + if (!(ecx & CPUID_SMX) || !(ecx & CPUID_VMX)) + return false; + + /* + * Check if SMX, VMX and GetSec instructions haven't been disabled. + */ + msr_t msr = rdmsr(IA32_FEATURE_CONTROL); + if ((msr.lo & 0xff07) != 0xff07) + return false; + + /* + * Enable SMX. Required to execute GetSec instruction. + * Chapter 2.2.4.3 + * Intel TXT Software Development Guide (Document: 315168-015) + */ + write_cr4(read_cr4() | CR4_SMXE); + + return true; +} + +/** + * Get information as returned by getsec[PARAMETER]. + * Arguments can be set to NULL if not needed. + * + * Returns false on error, true on success. + */ +bool getsec_parameter(uint32_t *version_mask, + uint32_t *version_numbers_supported, + uint32_t *max_size_acm_area, + uint32_t *memory_type_mask, + uint32_t *senter_function_disable, + uint32_t *txt_feature_flags) +{ + uint32_t i, eax, ebx, ecx; + + if (!getsec_enabled()) + return false; + + /* + * SAFER MODE EXTENSIONS REFERENCE. + * Intel 64 and IA-32 Architectures Software Developer Manuals Vol 2D + */ + for (i = 0; i < 0x1f; i++) { + /* Getsec[PARAMETERS] */ + asm volatile ("getsec\n" + : "=a" (eax), "=b" (ebx), "=c" (ecx) + : "a" (IA32_GETSEC_PARAMETERS), "b" (i) :); + switch (eax & 0x1f) { + case 0: /* NULL - Exit marker */ + return true; + case 1: /* Supported AC module versions */ + if (version_mask) + *version_mask = ebx; + if (version_numbers_supported) + *version_numbers_supported = ecx; + break; + case 2: /* Max size of authenticated code execution area */ + if (max_size_acm_area) + *max_size_acm_area = eax & ~0x1f; + break; + case 3: /* External memory types supported during AC mode */ + if (memory_type_mask) + *memory_type_mask = eax; + break; + case 4: /* Selective SENTER functionality control */ + if (senter_function_disable) + *senter_function_disable = eax & (0x3f00); + break; + case 5: /* TXT extensions support */ + if (txt_feature_flags) + *txt_feature_flags = eax & (0x60); + break; + } + } + + return true; +} + +/** + * Get capabilities as returned by getsec[CAPABILITIES]. + * + * Returns false on error, true on success. + */ + +bool getsec_capabilities(uint32_t *eax) +{ + if (!getsec_enabled()) + return false; + + asm volatile ("getsec\n" + : "=a" (*eax) + : "a" (IA32_GETSEC_CAPABILITIES), "b" (0) :); + + return true; +} diff --git a/src/security/intel/txt/getsec_enteraccs.S b/src/security/intel/txt/getsec_enteraccs.S new file mode 100644 index 0000000000..3135de79b2 --- /dev/null +++ b/src/security/intel/txt/getsec_enteraccs.S @@ -0,0 +1,318 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include +#include +#include + +#define MTRR_HIGH_MASK $((1 << (CONFIG_CPU_ADDR_BITS - 32)) - 1) + +.macro PUSH_MSR x + movl $(\x), %ecx + rdmsr + push %eax + push %edx +.endm + +.macro POP_MSR x + movl $(\x), %ecx + pop %edx + pop %eax + wrmsr +.endm + +.macro CLEAR_MSR x + movl $(\x), %ecx + xorl %edx, %edx + xorl %eax, %eax + wrmsr +.endm + +.align 4 +.text + +/* + * See "SAFER MODE EXTENSIONS REFERENCE." + * Chapter "GETSEC[ENTERACCS] - Execute Authenticated Chipset Code" for reference. + * Intel 64 and IA-32 Architectures Software Developer Manuals Vol 2D + * + * void getsec_enteraccs(uint32_t esi, + * uint32_t acm_base, + * uint32_t acm_size); + */ +.global getsec_enteraccs +getsec_enteraccs: + + /* Backup current register state */ + pushl %ebp + movl %esp, %ebp + + pushal + + movl %cr0, %eax + pushl %eax + movl %cr4, %eax + pushl %eax + + /* Pushed 10 32bit registers */ + + /* Reserve space on stack for GDT */ + subl $8, %esp + + PUSH_MSR IA32_MISC_ENABLE + PUSH_MSR MTRR_FIX_64K_00000 + PUSH_MSR MTRR_FIX_16K_80000 + PUSH_MSR MTRR_FIX_16K_A0000 + PUSH_MSR MTRR_FIX_4K_C0000 + PUSH_MSR MTRR_FIX_4K_C8000 + PUSH_MSR MTRR_FIX_4K_D0000 + PUSH_MSR MTRR_FIX_4K_D8000 + PUSH_MSR MTRR_FIX_4K_E0000 + PUSH_MSR MTRR_FIX_4K_F0000 + PUSH_MSR MTRR_FIX_4K_F8000 + + /* Push variable MTRRs in ascending order */ + + xorl %ebx, %ebx + jmp cond_push_var_mtrrs + +body_push_var_mtrrs: + + movl %ebx, %ecx + shll %ecx + addl $(MTRR_PHYS_BASE(0)), %ecx + rdmsr + push %eax + push %edx + incl %ecx /* MTRR_PHYS_MASK */ + rdmsr + push %eax + push %edx + + incl %ebx + +cond_push_var_mtrrs: + + movl $(MTRR_CAP_MSR), %ecx + rdmsr + andl $(0xff), %eax + cmp %ebx, %eax + jg body_push_var_mtrrs + + /* + * Disable cache. + * Chapter 2.2.4.3 + * Intel TXT Software Development Guide (Document: 315168-015) + */ + movl %cr0, %eax + orl $(CR0_CD | CR0_NW), %eax + movl %eax, %cr0 + + /* Disable fixed MTRRs */ + movl $(MTRR_DEF_TYPE_MSR), %ecx + rdmsr + andl $(~MTRR_DEF_TYPE_FIX_EN), %eax + wrmsr + + /* + * Clear fixed MTRRs. + * Chapter 2.2.5.1 + * Intel TXT Software Development Guide (Document: 315168-015) + */ + CLEAR_MSR MTRR_FIX_64K_00000 + CLEAR_MSR MTRR_FIX_16K_80000 + CLEAR_MSR MTRR_FIX_16K_A0000 + CLEAR_MSR MTRR_FIX_4K_C0000 + CLEAR_MSR MTRR_FIX_4K_C8000 + CLEAR_MSR MTRR_FIX_4K_D0000 + CLEAR_MSR MTRR_FIX_4K_D8000 + CLEAR_MSR MTRR_FIX_4K_E0000 + CLEAR_MSR MTRR_FIX_4K_F0000 + CLEAR_MSR MTRR_FIX_4K_F8000 + + /* + * Clear variable MTRRs + * Chapter 2.2.5.1 + * Intel TXT Software Development Guide (Document: 315168-015) + */ + movl $(MTRR_CAP_MSR), %ecx + rdmsr + andl $(0xff), %eax + movl %eax, %ebx + + xorl %eax, %eax + xorl %edx, %edx + + jmp cond_clear_var_mtrrs + +body_clear_var_mtrrs: + + decl %ebx + movl %ebx, %ecx + shll %ecx + addl $(MTRR_PHYS_BASE(0)), %ecx + wrmsr + incl %ecx /* MTRR_PHYS_MASK */ + wrmsr + +cond_clear_var_mtrrs: + + cmpl $0, %ebx + jnz body_clear_var_mtrrs + + /* + * Setup BIOS ACM as WB + * Chapter A.1.1 + * Intel TXT Software Development Guide (Document: 315168-015) + */ + movl $(MTRR_PHYS_BASE(0)), %ecx + movl 12(%ebp), %eax /* %eax = acmbase */ + orl $(6), %eax /* MTRR_TYPE_WB */ + movl $0, %edx + wrmsr + + /* Round acmsize to next power of two. Required for MTRR programming. */ + movl $1, %ebx + movl 16(%ebp), %ecx /* %ebx = acmsize */ + dec %ecx + bsr %ecx, %ecx /* find MSB */ + inc %ecx + shl %cl, %ebx + movl $(MTRR_PHYS_MASK(0)), %ecx + xorl %eax, %eax + subl %ebx, %eax /* %eax = 4GIB - log2_ceil(ACM SIZE) */ + orl $((1 << 11)), %eax /* MTRR_PHYS_MASK_VALID */ + movl MTRR_HIGH_MASK, %edx + wrmsr + + /* Enable cache - GPF# if not done */ + movl %cr0, %eax + andl $(~(CR0_CD | CR0_NW)), %eax + movl %eax, %cr0 + + /* Enable Numeric error - GPE# if not done */ + movl %cr0, %eax + orl $(CR0_NE), %eax + movl %eax, %cr0 + + /* Enable SMX and FXSTORE - for getsec */ + movl %cr4, %eax + orl $(CR4_SMXE | CR4_OSFXSR), %eax + movl %eax, %cr4 + + /* + * Save GDT + * Chapter A.1.2 + * Intel TXT Software Development Guide (Document: 315168-015) + */ + sgdt -48(%ebp) + + /* Backup stack pointer */ + movd %esp, %xmm0 + movd %ebp, %xmm1 + + /* + * Get function arguments. + * It's important to pass the exact ACM size as it's used by getsec to verify + * the integrity of ACM. Unlike the size for MTRR programming, which needs to + * be power of two. + * + * The following assembly code is based on tboot's tboot/include/txt/smx.h. + */ + movl 8(%ebp), %esi /* flags */ + movl 12(%ebp), %ebx /* acm_base */ + movl 16(%ebp), %ecx /* acm_size */ + + movl $0, %edx /* reserved, must be zero */ + movl $0, %edi /* must be zero */ + movl $2, %eax /* GetSec[ENTERACCS] */ + + getsec + + /* Restore stack pointer */ + movd %xmm0, %esp + movd %xmm1, %ebp + + /* Reload GDT */ + lgdt -48(%ebp) + + /* Set cs */ + ljmp $0x10, $1f +1: + /* Fix segment registers */ + movl $0x18, %eax + movl %eax, %ds + movl %eax, %es + movl %eax, %ss + movl %eax, %fs + movl %eax, %gs + + /* Disable cache */ + movl %cr0, %eax + orl $(CR0_CD | CR0_NW), %eax + movl %eax, %cr0 + + /* Pop variable MTRRs in descending order */ + + movl $(MTRR_CAP_MSR), %ecx + rdmsr + andl $(0xff), %eax + movl %eax, %ebx + + jmp cond_pop_var_mtrrs + +body_pop_var_mtrrs: + + decl %ebx + movl %ebx, %ecx + shll %ecx + addl $(MTRR_PHYS_MASK(0)), %ecx + pop %edx + pop %eax + wrmsr + decl %ecx /* MTRR_PHYS_BASE */ + pop %edx + pop %eax + wrmsr + +cond_pop_var_mtrrs: + + cmpl $0, %ebx + jne body_pop_var_mtrrs + + POP_MSR MTRR_FIX_4K_F8000 + POP_MSR MTRR_FIX_4K_F0000 + POP_MSR MTRR_FIX_4K_E0000 + POP_MSR MTRR_FIX_4K_D8000 + POP_MSR MTRR_FIX_4K_D0000 + POP_MSR MTRR_FIX_4K_C8000 + POP_MSR MTRR_FIX_4K_C0000 + POP_MSR MTRR_FIX_16K_A0000 + POP_MSR MTRR_FIX_16K_80000 + POP_MSR MTRR_FIX_64K_00000 + POP_MSR IA32_MISC_ENABLE + + /* Enable fixed MTRRs */ + movl $(MTRR_DEF_TYPE_MSR), %ecx + rdmsr + orl $(MTRR_DEF_TYPE_FIX_EN), %eax + wrmsr + + /* Enable cache */ + movl %cr0, %eax + andl $(~(CR0_CD | CR0_NW)), %eax + movl %eax, %cr0 + + /* Pop GDT */ + addl $8, %esp + + popl %eax + movl %eax, %cr4 + popl %eax + movl %eax, %cr0 + + popal + + movl %ebp, %esp + popl %ebp + + ret diff --git a/src/security/intel/txt/logging.c b/src/security/intel/txt/logging.c new file mode 100644 index 0000000000..cf14b55df9 --- /dev/null +++ b/src/security/intel/txt/logging.c @@ -0,0 +1,241 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include +#if CONFIG(SOC_INTEL_COMMON_BLOCK_SA) +#include +#endif + +#include +#include + +#include "txt.h" +#include "txt_register.h" + +/** + * Logs microcode or SINIT ACM errors. + * Does not log SBIOS ACM errors. + */ +static void log_txt_error(const char *phase) +{ + const uint64_t txt_error = read64((void *)TXT_ERROR); + + if (txt_error & ACMERROR_TXT_VALID) { + printk(BIOS_ERR, "%s: Error occurred\n", phase); + + if (txt_error & ACMERROR_TXT_EXTERNAL) + printk(BIOS_ERR, " Caused by: External\n"); + else + printk(BIOS_ERR, " Caused by: Processor\n"); + + printk(BIOS_ERR, " Type: "); + + switch (txt_error & TXT_ERROR_MASK) { + case 0: + printk(BIOS_ERR, "Legacy Shutdown\n"); + break; + case 5: + printk(BIOS_ERR, "Load memory type error in ACM area\n"); + break; + case 6: + printk(BIOS_ERR, "Unrecognized ACM format\n"); + break; + case 7: + printk(BIOS_ERR, "Failure to authenticate\n"); + break; + case 8: + printk(BIOS_ERR, "Invalid ACM format\n"); + break; + case 9: + printk(BIOS_ERR, "Unexpected Snoop hit\n"); + break; + case 10: + printk(BIOS_ERR, "Invalid event\n"); + break; + case 11: + printk(BIOS_ERR, "Invalid MLE\n"); + break; + case 12: + printk(BIOS_ERR, "Machine check event\n"); + break; + case 13: + printk(BIOS_ERR, "VMXAbort\n"); + break; + case 14: + printk(BIOS_ERR, "AC memory corruption\n"); + break; + case 15: + printk(BIOS_ERR, "Illegal voltage/bus ratio\n"); + break; + default: + printk(BIOS_ERR, "unknown\n"); + break; + } + } +} + +/** + * Dump useful informaation about the BIOS ACM state. + * Should run right after console_init() in romstage. + * Resets the platform if TXT reset is active and MLE cannot be established. + **/ +void intel_txt_log_bios_acm_error(void) +{ + uint32_t bios_acm_error; + uint64_t acm_status; + uint64_t txt_error; + + printk(BIOS_INFO, "TEE-TXT: State of ACM and ucode update:\n"); + + bios_acm_error = read32((void *)TXT_BIOSACM_ERRORCODE); + acm_status = read64((void *)TXT_SPAD); + txt_error = read64((void *)TXT_ERROR); + + /* Errors by BIOS ACM or FIT */ + if ((txt_error & ACMERROR_TXT_VALID) && + (acm_status & ACMERROR_TXT_VALID)) { + intel_txt_log_acm_error(read32((void *)TXT_BIOSACM_ERRORCODE)); + log_txt_error("FIT MICROCODE"); + } + /* Errors by SINIT */ + if ((txt_error & ACMERROR_TXT_VALID) && + !(acm_status & ACMERROR_TXT_VALID)) { + intel_txt_log_acm_error(txt_error); + log_txt_error("SINIT"); + } + + /* Check for fatal ACM error and TXT reset */ + uint8_t error = read8((void *)TXT_ESTS); + if (error & TXT_ESTS_TXT_RESET_STS) { + printk(BIOS_CRIT, "TXT-STS: Intel TXT reset detected\n"); + intel_txt_log_acm_error(read32((void *)TXT_ERROR)); + } +} + +/** + * Dump information about the provided ACM. + */ +void txt_dump_acm_info(const struct acm_header_v0 *acm_header) +{ + const struct acm_info_table *info = NULL; + if (!acm_header) + return; + + printk(BIOS_INFO, "ACM @ %p\n", acm_header); + + const size_t acm_size = (acm_header->size & 0xffffff) << 2; + const size_t info_off = (acm_header->header_len + acm_header->scratch_size) * 4; + + if (acm_size > (info_off + sizeof(struct acm_info_table))) + info = (const struct acm_info_table *) + ((const unsigned char *)acm_header + info_off); + + printk(BIOS_INFO, " ACM: Binary Info\n"); + if (acm_header->module_type == CHIPSET_ACM) + printk(BIOS_INFO, " Type: Chipset ACM\n"); + + if (acm_header->module_sub_type == 0) + printk(BIOS_INFO, " Subtype: undefined\n"); + else if (acm_header->module_sub_type == 1) + printk(BIOS_INFO, " Subtype: Run at reset\n"); + + printk(BIOS_INFO, " Header: v%u.%u\n", acm_header->header_version[0], + acm_header->header_version[1]); + + printk(BIOS_INFO, " Chipset: %u\n", acm_header->chipset_id); + printk(BIOS_INFO, " Size: %zu\n", acm_size); + + switch (acm_header->flags) { + case ACM_FORMAT_FLAGS_PW: + printk(BIOS_INFO, " Flags: PW signed (Production Worthy)\n"); + break; + case ACM_FORMAT_FLAGS_NPW: + printk(BIOS_INFO, " Flags: NPW signed (Non Production Worthy)\n"); + break; + case ACM_FORMAT_FLAGS_DEBUG: + printk(BIOS_INFO, " Flags: Debug signed\n"); + break; + } + + if (acm_header->module_vendor == INTEL_ACM_VENDOR) + printk(BIOS_INFO, " Vendor: Intel Corporation\n"); + + printk(BIOS_INFO, " Date: %x\n", acm_header->date); + + switch (acm_header->size) { + case ACM_FORMAT_SIZE_64KB: + printk(BIOS_INFO, " Size: 64KB\n"); + printk(BIOS_INFO, " CBnT: no\n"); + break; + case ACM_FORMAT_SIZE_128KB: + printk(BIOS_INFO, " Size: 128KB\n"); + printk(BIOS_INFO, " CBnT: no\n"); + break; + case ACM_FORMAT_SIZE_256KB: + printk(BIOS_INFO, " Size: 256KB\n"); + printk(BIOS_INFO, " CBnT: yes\n"); + break; + default: + printk(BIOS_INFO, " Size: 0x%08x\n", acm_header->size); + + break; + } + + printk(BIOS_INFO, " TXT SVN: %u\n", acm_header->txt_svn); + printk(BIOS_INFO, " SE SVN: %u\n", acm_header->se_svn); + + if (!info) + return; + printk(BIOS_INFO, " Table info:\n"); + printk(BIOS_INFO, " UUID: "); + for (size_t i = 0; i < sizeof(info->uuid); i++) + printk(BIOS_INFO, "%02X ", info->uuid[i]); + printk(BIOS_INFO, "\n"); + printk(BIOS_INFO, " Chipset acm type: 0x%x\n", info->chipset_acm_type); + printk(BIOS_INFO, " Capabilities: 0x%x\n", info->capabilities); +} + +/** + * Dump information about the chipset's TXT capabilities. + */ +void txt_dump_chipset_info(void) +{ + printk(BIOS_INFO, "TEE-TXT: Chipset Key Hash 0x"); + for (int i = 0; i < TXT_ACM_KEY_HASH_LEN; i++) { + printk(BIOS_INFO, "%llx", read64((void *)TXT_ACM_KEY_HASH + + (i * sizeof(uint64_t)))); + } + printk(BIOS_INFO, "\n"); + + printk(BIOS_INFO, "TEE-TXT: DIDVID 0x%x\n", read32((void *)TXT_DIDVID)); + printk(BIOS_INFO, "TEE-TXT: production fused chipset: %s\n", + (read64((void *)TXT_VER_FSBIF) & TXT_VER_PRODUCTION_FUSED) ? "true" : "false"); +} + +void txt_dump_regions(void) +{ + struct txt_biosdataregion *bdr = NULL; + uintptr_t tseg = 0; + uint64_t reg64; + + reg64 = read64((void *)TXT_HEAP_BASE); + if ((reg64 != 0 && reg64 != ~0UL) && + (read64((void *)(uintptr_t)reg64) >= (sizeof(*bdr) + sizeof(uint64_t)))) + bdr = (void *)((uintptr_t)reg64 + sizeof(uint64_t)); + + printk(BIOS_DEBUG, "TEE-TXT: TSEG 0x%lx\n", tseg * MiB); + printk(BIOS_DEBUG, "TEE-TXT: TXT.HEAP.BASE 0x%llx\n", read64((void *)TXT_HEAP_BASE)); + printk(BIOS_DEBUG, "TEE-TXT: TXT.HEAP.SIZE 0x%llx\n", read64((void *)TXT_HEAP_SIZE)); + printk(BIOS_DEBUG, "TEE-TXT: TXT.SINIT.BASE 0x%llx\n", read64((void *)TXT_SINIT_BASE)); + printk(BIOS_DEBUG, "TEE-TXT: TXT.SINIT.SIZE 0x%llx\n", read64((void *)TXT_SINIT_SIZE)); + printk(BIOS_DEBUG, "TEE-TXT: TXT.MSEG.BASE 0x%llx\n", read64((void *)TXT_MSEG_BASE)); + printk(BIOS_DEBUG, "TEE-TXT: TXT.MSEG.SIZE 0x%llx\n", read64((void *)TXT_MSEG_SIZE)); + + if (bdr) { + printk(BIOS_DEBUG, "TEE-TXT: BiosDataRegion.bios_sinit_size 0x%x\n", + bdr->bios_sinit_size); + printk(BIOS_DEBUG, "TEE-TXT: BiosDataRegion.lcp_pd_size 0x%llx\n", + bdr->lcp_pd_size); + printk(BIOS_DEBUG, "TEE-TXT: BiosDataRegion.lcp_pd_base 0x%llx\n", + bdr->lcp_pd_base); + } +} diff --git a/src/security/intel/txt/ramstage.c b/src/security/intel/txt/ramstage.c new file mode 100644 index 0000000000..76efa96ca8 --- /dev/null +++ b/src/security/intel/txt/ramstage.c @@ -0,0 +1,382 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#if CONFIG(SOC_INTEL_FSP_BROADWELL_DE) +#include +#include +#endif + +#include "txt.h" +#include "txt_register.h" +#include "txt_getsec.h" + +/* FIXME: Seems to work only on some platforms */ +static void log_ibb_measurements(void) +{ + const uint64_t mseg_size = read64((void *)TXT_MSEG_SIZE); + uint64_t mseg_base = read64((void *)TXT_MSEG_BASE); + + if (!mseg_size || !mseg_base || mseg_size <= mseg_base) + return; + /* + * MSEG SIZE and MSEG BASE might contain random values. + * Assume below 4GiB and 8byte aligned. + */ + if (mseg_base & ~0xfffffff8ULL || mseg_size & ~0xfffffff8ULL) + return; + + printk(BIOS_INFO, "TEE-TXT: IBB Hash 0x"); + for (; mseg_base < mseg_size; mseg_base++) + printk(BIOS_INFO, "%02X", read8((void *)(uintptr_t)mseg_base)); + + printk(BIOS_INFO, "\n"); +} + +void bootmem_platform_add_ranges(void) +{ + uint64_t status = read64((void *)TXT_SPAD); + + if (status & ACMSTS_TXT_DISABLED) + return; + + /* Chapter 5.5.5 Intel TXT reserved memory */ + bootmem_add_range(TXT_RESERVED_SPACE, + TXT_RESERVED_SPACE_SIZE, + BM_MEM_RESERVED); + + /* Intel TPM decode memory */ + bootmem_add_range(TXT_TPM_DECODE_AREA, + TXT_RESERVED_SPACE - TXT_TPM_DECODE_AREA, + BM_MEM_RESERVED); + + /* Intel TXT public space memory */ + bootmem_add_range(TXT_PUBLIC_SPACE, + TXT_TPM_DECODE_AREA - TXT_PUBLIC_SPACE, + BM_MEM_RESERVED); + + /* Intel TXT private space memory */ + bootmem_add_range(TXT_PRIVATE_SPACE, + TXT_PUBLIC_SPACE - TXT_PRIVATE_SPACE, + BM_MEM_RESERVED); + + const uint32_t txt_dev_memory = read32((void *)TXT_DPR) & + (TXT_DPR_TOP_ADDR_MASK << TXT_DPR_TOP_ADDR_SHIFT); + const uint32_t txt_dev_size = + (read32((void *)TXT_DPR) >> TXT_DPR_LOCK_SIZE_SHIFT) & + TXT_DPR_LOCK_SIZE_MASK; + + /* Chapter 5.5.6 Intel TXT Device Memory */ + bootmem_add_range(txt_dev_memory - txt_dev_size * MiB, + txt_dev_size * MiB, + BM_MEM_RESERVED); +} + +static bool get_wake_error_status(void) +{ + const uint8_t error = read8((void *)TXT_ESTS); + return !!(error & TXT_ESTS_WAKE_ERROR_STS); +} + +static void check_secrets_txt(void *unused) +{ + uint64_t status = read64((void *)TXT_SPAD); + + if (status & ACMSTS_TXT_DISABLED) + return; + + /* Check for fatal ACM error and TXT reset */ + if (get_wake_error_status()) { + /* + * Check if secrets bit needs to be reset. Only platforms that support + * CONFIG(PLATFORM_HAS_DRAM_CLEAR) will be able to run this code. + * Assume all memory really was cleared. + * + * TXT will issue a platform reset to come up sober. + */ + if (intel_txt_memory_has_secrets()) { + printk(BIOS_INFO, "TEE-TXT: Wiping TEE...\n"); + intel_txt_run_bios_acm(ACMINPUT_CLEAR_SECRETS); + + /* Should never reach this point ... */ + intel_txt_log_acm_error(read32((void *)TXT_BIOSACM_ERRORCODE)); + die("Waiting for platform reset...\n"); + } + } +} + +BOOT_STATE_INIT_ENTRY(BS_POST_DEVICE, BS_ON_ENTRY, check_secrets_txt, NULL); + +/** + * Log TXT startup errors, check all bits for TXT, run BIOSACM using + * GETSEC[ENTERACCS]. + * + * If a "TXT reset" is detected or "memory had secrets" is set, then do nothing as + * 1. Running ACMs will cause a TXT-RESET + * 2. Memory will be scrubbed in BS_DEV_INIT + * 3. TXT-RESET will be issued by code above later + * + */ +static void init_intel_txt(void *unused) +{ + const uint64_t status = read64((void *)TXT_SPAD); + + if (status & ACMSTS_TXT_DISABLED) + return; + + printk(BIOS_INFO, "TEE-TXT: Initializing TEE...\n"); + + intel_txt_log_spad(); + + if (CONFIG(INTEL_TXT_LOGGING)) { + intel_txt_log_bios_acm_error(); + txt_dump_chipset_info(); + } + + printk(BIOS_INFO, "TEE-TXT: Validate TEE...\n"); + + if (intel_txt_prepare_txt_env()) { + printk(BIOS_ERR, "TEE-TXT: Failed to prepare TXT environment\n"); + return; + } + + /* Check for fatal ACM error and TXT reset */ + if (get_wake_error_status()) { + /* Can't run ACMs with TXT_ESTS_WAKE_ERROR_STS set */ + printk(BIOS_ERR, "TEE-TXT: Fatal BIOS ACM error reported\n"); + return; + } + + printk(BIOS_INFO, "TEE-TXT: Testing BIOS ACM calling code...\n"); + + /* + * Test BIOS ACM code. + * ACM should do nothing on reserved functions, and return an error code + * in TXT_BIOSACM_ERRORCODE. Tests showed that this is not true. + * Use special function "NOP" that does 'nothing'. + */ + if (intel_txt_run_bios_acm(ACMINPUT_NOP) < 0) { + printk(BIOS_ERR, "TEE-TXT: Error calling BIOS ACM with NOP function.\n"); + return; + } + + if (status & (ACMSTS_BIOS_TRUSTED | ACMSTS_IBB_MEASURED)) { + log_ibb_measurements(); + + int s3resume = acpi_is_wakeup_s3(); + if (!s3resume) { + printk(BIOS_INFO, "TEE-TXT: Scheck...\n"); + if (intel_txt_run_bios_acm(ACMINPUT_SCHECK) < 0) { + printk(BIOS_ERR, "TEE-TXT: Error calling BIOS ACM.\n"); + return; + } + } + } +} + +BOOT_STATE_INIT_ENTRY(BS_DEV_INIT, BS_ON_EXIT, init_intel_txt, NULL); + +static void push_sinit_heap(u8 **heap_ptr, void *data, size_t data_length) +{ + /* Push size */ + const uint64_t tmp = data_length + 8; + memcpy(*heap_ptr, &tmp, 8); + *heap_ptr += 8; + + if (data_length) { + /* Push data */ + memcpy(*heap_ptr, data, data_length); + *heap_ptr += data_length; + } +} + +/** + * Finalize the TXT device. + * + * - Lock TXT register. + * - Protect TSEG using DMA protected regions. + * - Setup TXT regions. + * - Place SINIT ACM in TXT_SINIT memory segment. + * - Fill TXT BIOSDATA region. + */ +static void lockdown_intel_txt(void *unused) +{ + const uint64_t status = read64((void *)TXT_SPAD); + uintptr_t tseg = 0; + + if (status & ACMSTS_TXT_DISABLED) + return; + + printk(BIOS_INFO, "TEE-TXT: Locking TEE...\n"); + + /* Lock TXT config, unlocks TXT_HEAP_BASE */ + if (intel_txt_run_bios_acm(ACMINPUT_LOCK_CONFIG) < 0) { + printk(BIOS_ERR, "TEE-TXT: Failed to lock registers.\n"); + printk(BIOS_ERR, "TEE-TXT: SINIT won't be supported.\n"); + return; + } + + if (CONFIG(SOC_INTEL_FSP_BROADWELL_DE)) + tseg = sa_get_tseg_base() >> 20; + + /* + * Document Number: 558294 + * Chapter 5.5.6.1 DMA Protection Memory Region + */ + + const u8 dpr_capable = !!(read64((void *)TXT_CAPABILITIES) & + TXT_CAPABILITIES_DPR); + printk(BIOS_INFO, "TEE-TXT: DPR capable %x\n", dpr_capable); + if (dpr_capable) { + + /* Protect 3 MiB below TSEG and lock register */ + write64((void *)TXT_DPR, (TXT_DPR_TOP_ADDR(tseg) | + TXT_DPR_LOCK_SIZE(3) | + TXT_DPR_LOCK_MASK)); + +#if CONFIG(SOC_INTEL_FSP_BROADWELL_DE) + broadwell_de_set_dpr(tseg, 3 * MiB); + broadwell_de_lock_dpr(); +#endif + printk(BIOS_INFO, "TEE-TXT: TXT.DPR 0x%08x\n", + read32((void *)TXT_DPR)); + } + + /* + * Document Number: 558294 + * Chapter 5.5.6.3 Intel TXT Heap Memory Region + */ + write64((void *)TXT_HEAP_SIZE, 0xE0000); + write64((void *)TXT_HEAP_BASE, + ALIGN_DOWN((tseg * MiB) - read64((void *)TXT_HEAP_SIZE), 4096)); + + /* + * Document Number: 558294 + * Chapter 5.5.6.2 SINIT Memory Region + */ + write64((void *)TXT_SINIT_SIZE, 0x20000); + write64((void *)TXT_SINIT_BASE, + ALIGN_DOWN(read64((void *)TXT_HEAP_BASE) - + read64((void *)TXT_SINIT_SIZE), 4096)); + + /* + * BIOS Data Format + * Chapter C.2 + * Intel TXT Software Development Guide (Document: 315168-015) + */ + struct { + struct txt_biosdataregion bdr; + struct txt_heap_acm_element heap_acm; + struct txt_extended_data_element_header end; + } __packed data = {0}; + + /* TPM2.0 requires version 6 of BDT */ + if (CONFIG(TPM2)) + data.bdr.version = 6; + else + data.bdr.version = 5; + + data.bdr.no_logical_procs = dev_count_cpu(); + + void *sinit_base = (void *)(uintptr_t)read64((void *)TXT_SINIT_BASE); + data.bdr.bios_sinit_size = cbfs_boot_load_file(CONFIG_INTEL_TXT_CBFS_SINIT_ACM, + sinit_base, + read64((void *)TXT_SINIT_SIZE), + CBFS_TYPE_RAW); + + if (data.bdr.bios_sinit_size) { + printk(BIOS_INFO, "TEE-TXT: Placing SINIT ACM in memory.\n"); + if (CONFIG(INTEL_TXT_LOGGING)) + txt_dump_acm_info(sinit_base); + } else { + printk(BIOS_ERR, "TEE-TXT: Couldn't locate SINIT ACM in CBFS.\n"); + /* Clear memory */ + memset(sinit_base, 0, read64((void *)TXT_SINIT_SIZE)); + } + + struct cbfsf file; + /* The following have been removed from BIOS Data Table in version 6 */ + if (!cbfs_boot_locate(&file, CONFIG_INTEL_TXT_CBFS_BIOS_POLICY, NULL)) { + struct region_device policy; + + cbfs_file_data(&policy, &file); + void *policy_data = rdev_mmap_full(&policy); + size_t policy_len = region_device_sz(&policy); + + if (policy_data && policy_len) { + /* Point to FIT Type 9 entry in flash */ + data.bdr.lcp_pd_base = (uintptr_t)policy_data; + data.bdr.lcp_pd_size = (uint64_t)policy_len; + rdev_munmap(&policy, policy_data); + } else { + printk(BIOS_ERR, "TEE-TXT: Couldn't map LCP PD Policy from CBFS.\n"); + } + } else { + printk(BIOS_ERR, "TEE-TXT: Couldn't locate LCP PD Policy in CBFS.\n"); + } + + data.bdr.support_acpi_ppi = 0; + data.bdr.platform_type = 0; + + /* Extended elements - ACM addresses */ + data.heap_acm.header.type = HEAP_EXTDATA_TYPE_ACM; + data.heap_acm.header.size = sizeof(data.heap_acm); + if (data.bdr.bios_sinit_size) { + data.heap_acm.num_acms = 2; + data.heap_acm.acm_addrs[1] = (uintptr_t)sinit_base; + } else { + data.heap_acm.num_acms = 1; + } + data.heap_acm.acm_addrs[0] = + (uintptr_t)cbfs_boot_map_with_leak(CONFIG_INTEL_TXT_CBFS_BIOS_ACM, + CBFS_TYPE_RAW, + NULL); + /* Extended elements - End marker */ + data.end.type = HEAP_EXTDATA_TYPE_END; + data.end.size = sizeof(data.end); + + /* Fill TXT.HEAP.BASE with 4 subregions */ + u8 *heap_struct = (void *)((uintptr_t)read64((void *)TXT_HEAP_BASE)); + + /* BiosData */ + push_sinit_heap(&heap_struct, &data, sizeof(data)); + + /* OsMLEData */ + /* FIXME: Does firmware need to write this? */ + push_sinit_heap(&heap_struct, NULL, 0); + + /* OsSinitData */ + /* FIXME: Does firmware need to write this? */ + push_sinit_heap(&heap_struct, NULL, 0); + + /* SinitMLEData */ + /* FIXME: Does firmware need to write this? */ + push_sinit_heap(&heap_struct, NULL, 0); + + /* + * FIXME: Server-TXT capable platforms need to install an STM in SMM and set up MSEG. + */ + + /** + * Chapter 5.10.1 SMM in the Intel TXT for Servers Environment + * Disable MSEG. + */ + write64((void *)TXT_MSEG_SIZE, 0); + write64((void *)TXT_MSEG_BASE, 0); + + if (CONFIG(INTEL_TXT_LOGGING)) + txt_dump_regions(); +} + +BOOT_STATE_INIT_ENTRY(BS_POST_DEVICE, BS_ON_EXIT, lockdown_intel_txt, NULL); diff --git a/src/security/intel/txt/txt.h b/src/security/intel/txt/txt.h new file mode 100644 index 0000000000..fc5c49e67e --- /dev/null +++ b/src/security/intel/txt/txt.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef SECURITY_INTEL_TXT_H_ +#define SECURITY_INTEL_TXT_H_ + +#include + +/* Error codes */ +#define ACM_E_TYPE_NOT_MATCH 0x01 +#define ACM_E_MODULE_SUB_TYPE_WRONG 0x02 +#define ACM_E_MODULE_VENDOR_NOT_INTEL 0x03 +#define ACM_E_SIZE_INCORRECT 0x04 +#define ACM_E_CANT_CALL_GETSEC 0x05 +#define ACM_E_NOT_FIT_INTO_CPU_ACM_MEM 0x06 +#define ACM_E_NO_INFO_TABLE 0x07 +#define ACM_E_NOT_BIOS_ACM 0x08 +#define ACM_E_UUID_NOT_MATCH 0x09 +#define ACM_E_PLATFORM_IS_NOT_PROD 0x10 + +void intel_txt_log_bios_acm_error(void); +int intel_txt_log_acm_error(const uint32_t acm_error); +void intel_txt_log_spad(void); +bool intel_txt_memory_has_secrets(void); +int intel_txt_run_bios_acm(const u8 input_params); +bool intel_txt_prepare_txt_env(void); + +#endif /* SECURITY_INTEL_TXT_H_ */ diff --git a/src/security/intel/txt/txt_getsec.h b/src/security/intel/txt/txt_getsec.h new file mode 100644 index 0000000000..7c4a1a4283 --- /dev/null +++ b/src/security/intel/txt/txt_getsec.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef SECURITY_INTEL_TXT_GETSEC_H_ +#define SECURITY_INTEL_TXT_GETSEC_H_ + +#include + +bool getsec_parameter(uint32_t *version_mask, + uint32_t *version_numbers_supported, + uint32_t *max_size_acm_area, + uint32_t *memory_type_mask, + uint32_t *senter_function_disable, + uint32_t *txt_feature_flags); + +bool getsec_capabilities(uint32_t *eax); + +void getsec_enteraccs(const uint32_t esi, + const uint32_t acm_base, + const uint32_t acm_size); + +#endif /* SECURITY_INTEL_TXT_REGISTER_H_ */ diff --git a/src/security/intel/txt/txt_register.h b/src/security/intel/txt/txt_register.h new file mode 100644 index 0000000000..70bb309f15 --- /dev/null +++ b/src/security/intel/txt/txt_register.h @@ -0,0 +1,267 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef SECURITY_INTEL_TXT_REGISTER_H_ +#define SECURITY_INTEL_TXT_REGISTER_H_ + +#include +#include + +/* + * Document: 315168-016 + * Intel Trusted Execution Technology (Intel TXT) + * Software Development Guide + * Chapter B + */ +#define TXT_BASE 0xfed30000UL + +#define TXT_STS (TXT_BASE + 0) +#define TXT_ESTS (TXT_BASE + 8) +#define TXT_ESTS_TXT_RESET_STS (1 << 0) +/* + * Chapter 6 + * Intel Trusted Execution Technology Lab Handout + */ +#define TXT_ESTS_WAKE_ERROR_STS (1 << 6) + +#define TXT_ERROR (TXT_BASE + 0x30) +#define ACMERROR_TXT_VALID (1ul << 31) +#define ACMERROR_TXT_EXTERNAL (1ul << 30) + +#define ACMERROR_TXT_PROGRESS_SHIFT 16 +#define ACMERROR_TXT_MINOR_SHIFT 15 +#define ACMERROR_TXT_MAJOR_SHIFT 10 +#define ACMERROR_TXT_CLASS_SHIFT 4 +#define ACMERROR_TXT_TYPE_SHIFT 0 + +#define ACMERROR_TXT_PROGRESS_CODE (0xffull << ACMERROR_TXT_PROGRESS_SHIFT) +#define ACMERROR_TXT_MINOR_CODE (0x01ull << ACMERROR_TXT_MINOR_SHIFT) +#define ACMERROR_TXT_MAJOR_CODE (0x1full << ACMERROR_TXT_MAJOR_SHIFT) +#define ACMERROR_TXT_CLASS_CODE (0x3full << ACMERROR_TXT_CLASS_SHIFT) +#define ACMERROR_TXT_TYPE_CODE (0x0full << ACMERROR_TXT_TYPE_SHIFT) + +#define ACMERROR_TXT_AC_MODULE_TYPE_BIOS 0 +#define ACMERROR_TXT_AC_MODULE_TYPE_SINIT 1 + +#define TXT_ERROR_MASK (0x3ff << 0) + +#define TXT_CMD_RESET (TXT_BASE + 0x38) +#define TXT_CMD_CLOSE_PRIVATE (TXT_BASE + 0x48) + +/* Present in Document Number: 315168-016. */ +#define TXT_SPAD (TXT_BASE + 0xa0) +#define ACMSTS_IBB_MEASURED (1ull << 63) +#define ACMSTS_VERIFICATION_ERROR (1ull << 62) +#define ACMSTS_BG_STARTUP_ERROR (1ull << 61) /* CBnT platforms only */ +#define ACMSTS_TXT_DISABLED (1ull << 60) /* disabled by FIT type 0xA record */ +#define ACMSTS_BIOS_TRUSTED (1ull << 59) +#define ACMSTS_MEM_CLEAR_POWER_DOWN (1ull << 47) +#define ACMSTS_TXT_STARTUP_SUCCESS (1ull << 30) + +#define TXT_VER_FSBIF (TXT_BASE + 0x100) +#define TXT_VER_PRODUCTION_FUSED (1ull << 31) + +#define TXT_DIDVID (TXT_BASE + 0x110) + +/* + * Chapter 6 + * Intel Trusted Execution Technology Lab Handout + */ +#define TXT_CAPABILITIES (TXT_BASE + 0x200) +#define TXT_CAPABILITIES_DPR (1ull << 26) +#define TXT_CAPABILITIES_PMRC (1ull << 19) + +#define TXT_VER_QPIIF (TXT_BASE + 0x200) + +#define TXT_SINIT_BASE (TXT_BASE + 0x270) +#define TXT_SINIT_SIZE (TXT_BASE + 0x278) +#define TXT_MLE_JOIN (TXT_BASE + 0x290) + +#define TXT_HEAP_BASE (TXT_BASE + 0x300) +#define TXT_HEAP_SIZE (TXT_BASE + 0x308) +/* + * Chapter 6 + * Intel Trusted Execution Technology Lab Handout + */ +#define TXT_MSEG_BASE (TXT_BASE + 0x310) +#define TXT_MSEG_SIZE (TXT_BASE + 0x318) + +/* + * Chapter 5.4.2.1 + * Intel Trusted Execution Technology Lab Handout + */ +#define TXT_BIOSACM_ERRORCODE (TXT_BASE + 0x328) + +#define TXT_DPR (TXT_BASE + 0x330) +#define TXT_DPR_LOCK_SHIFT 0 +#define TXT_DPR_LOCK_SIZE_SHIFT 4 +#define TXT_DPR_LOCK_SIZE_MASK 0xff +#define TXT_DPR_TOP_ADDR_SHIFT 20 +#define TXT_DPR_TOP_ADDR_MASK 0xfff + +#define TXT_DPR_LOCK_MASK (1 << TXT_DPR_LOCK_SHIFT) +#define TXT_DPR_LOCK_SIZE(x) ((x) << TXT_DPR_LOCK_SIZE_SHIFT) +#define TXT_DPR_TOP_ADDR(x) ((x) << TXT_DPR_TOP_ADDR_SHIFT) + +#define TXT_ACM_KEY_HASH (TXT_BASE + 0x400) +#define TXT_ACM_KEY_HASH_LEN 0x4 + +#define TXT_E2STS (TXT_BASE + 0x8f0) +#define TXT_E2STS_SECRET_STS (1ull << 1) + +/* + * TXT Memory regions + * Chapter 5.3 + * Intel Trusted Execution Technology Lab Handout + */ +#define TXT_PRIVATE_SPACE 0xfed20000UL +#define TXT_PUBLIC_SPACE 0xfed30000UL +#define TXT_TPM_DECODE_AREA 0xfed40000UL +#define TXT_RESERVED_SPACE 0xfed50000UL + +#define TXT_RESERVED_SPACE_SIZE 0x3ffff + +/* ESI flags for GETSEC[ENTERACCS] see Reference Number: 323372-017 */ +#define ACMINPUT_RESET_TPM_AUXILIARY_INDICIES 2 +#define ACMINPUT_NOP 3 +#define ACMINPUT_SCHECK 4 +#define ACMINPUT_CLEAR_SECRETS 5 +#define ACMINPUT_LOCK_CONFIG 6 + +/* + * GetSec EAX value. + * SAFER MODE EXTENSIONS REFERENCE. + * Intel 64 and IA-32 Architectures Software Developer Manuals Vol 2 + * Order Number: 325383-060US + */ +#define IA32_GETSEC_CAPABILITIES 0 +#define IA32_GETSEC_ENTERACCS 2 +#define IA32_GETSEC_SENTER 4 +#define IA32_GETSEC_SEXIT 5 +#define IA32_GETSEC_PARAMETERS 6 +#define IA32_GETSEC_SMCTRL 7 +#define IA32_GETSEC_WAKEUP 8 + +#define GETSEC_PARAMS_TXT_EXT (1ul << 5) +#define GETSEC_PARAMS_TXT_EXT_CRTM_SUPPORT (1ul << 1) +#define GETSEC_PARAMS_TXT_EXT_MACHINE_CHECK (1ul << 6) + +/* ACM defines */ +#define INTEL_ACM_VENDOR 0x00008086 + +#define ACM_FORMAT_FLAGS_PW 0x00000000 +#define ACM_FORMAT_FLAGS_NPW (1 << 14) +#define ACM_FORMAT_FLAGS_DEBUG (1 << 15) + +/* Old ACMs are power of two aligned, newer ACMs are not */ +#define ACM_FORMAT_SIZE_64KB (64 * KiB / 4) +#define ACM_FORMAT_SIZE_128KB (128 * KiB / 4) +#define ACM_FORMAT_SIZE_256KB (256 * KiB / 4) + +/* MSRs */ +#define IA32_MCG_STATUS 0x17a + +typedef enum { + CHIPSET_ACM = 2, +} acm_module_type; + +typedef enum { + BIOS = 0, + SINIT = 1, +} acm_module_sub_type; + +/* + * ACM Header v0.0 without dynamic part + * Chapter A.1 + * Intel TXT Software Development Guide (Document: 315168-015) + */ +struct __packed acm_header_v0 { + uint16_t module_type; + uint16_t module_sub_type; + uint32_t header_len; + uint16_t header_version[2]; + uint16_t chipset_id; + uint16_t flags; + uint32_t module_vendor; + uint32_t date; + uint32_t size; + uint16_t txt_svn; + uint16_t se_svn; + uint32_t code_control; + uint32_t error_entry_point; + uint32_t gdt_limit; + uint32_t gdt_ptr; + uint32_t seg_sel; + uint32_t entry_point; + uint8_t reserved2[64]; + uint32_t key_size; + uint32_t scratch_size; + uint8_t rsa2048_pubkey[256]; + uint32_t pub_exp; + uint8_t rsa2048_sig[256]; + uint32_t scratch[143]; + uint8_t user_area[]; +}; + +struct __packed acm_info_table { + uint8_t uuid[16]; + uint8_t chipset_acm_type; + uint8_t version; + uint16_t length; + uint32_t chipset_id_list; + uint32_t os_sinit_data_ver; + uint32_t min_mle_hdr_ver; + uint32_t capabilities; + uint8_t acm_ver; + uint8_t reserved[3]; +}; + +/* + * Extended Data Elements + * Chapter C.1 + * Intel TXT Software Development Guide (Document: 315168-015) + */ +struct __packed txt_extended_data_element_header { + uint32_t type; + uint32_t size; + uint8_t data[0]; +}; + +#define HEAP_EXTDATA_TYPE_END 0 +#define HEAP_EXTDATA_TYPE_BIOS_SPEC_VER 1 +#define HEAP_EXTDATA_TYPE_ACM 2 +#define HEAP_EXTDATA_TYPE_CUSTOM 4 + +struct __packed txt_heap_acm_element { + struct txt_extended_data_element_header header; + uint32_t num_acms; // must greater 0, smaller than 3 + uint64_t acm_addrs[2]; +}; + +/* + * BIOS Data Format + * Chapter C.2 + * Intel TXT Software Development Guide (Document: 315168-015) + */ +struct __packed txt_biosdataregion { + uint32_t version; + uint32_t bios_sinit_size; + uint64_t lcp_pd_base; + uint64_t lcp_pd_size; + uint32_t no_logical_procs; + uint32_t sinit_flags; + union { + uint32_t mle_flags; + struct { + uint32_t support_acpi_ppi : 1; + uint32_t platform_type : 2; + }; + }; + u8 extended_data_elements[0]; +}; + + +void txt_dump_regions(void); +void txt_dump_chipset_info(void); +void txt_dump_acm_info(const struct acm_header_v0 *acm_header); + +#endif /* SECURITY_INTEL_TXT_REGISTER_H_ */ diff --git a/src/security/memory/memory.c b/src/security/memory/memory.c index c815236c9c..a1bc66372e 100644 --- a/src/security/memory/memory.c +++ b/src/security/memory/memory.c @@ -14,7 +14,8 @@ * GNU General Public License for more details. */ -#include +#include +#include #include "memory.h" /** @@ -27,6 +28,9 @@ bool security_clear_dram_request(void) if (CONFIG(SECURITY_CLEAR_DRAM_ON_REGULAR_BOOT)) return true; + if (CONFIG(INTEL_TXT) && intel_txt_memory_has_secrets()) + return true; + /* TODO: Add TEE environments here */ return false; diff --git a/src/security/tpm/tspi/tspi.c b/src/security/tpm/tspi/tspi.c index 966b8b7c77..795016e725 100644 --- a/src/security/tpm/tspi/tspi.c +++ b/src/security/tpm/tspi/tspi.c @@ -210,11 +210,28 @@ uint32_t tpm_extend_pcr(int pcr, enum vb2_hash_algorithm digest_algo, uint8_t *digest, size_t digest_len, const char *name) { uint32_t result; + uint16_t algorithm = 0; if (!digest) return TPM_E_IOERROR; - result = tlcl_extend(pcr, digest, NULL); +#if CONFIG(TPM2) + switch (digest_algo) { + case VB2_HASH_SHA1: + algorithm = TPM_ALG_SHA1; + break; + case VB2_HASH_SHA256: + algorithm = TPM_ALG_SHA256; + break; + case VB2_HASH_SHA512: + algorithm = TPM_ALG_SHA512; + break; + default: + return TPM_E_HASH_ERROR; + } +#endif + + result = tlcl_extend(pcr, algorithm, digest, digest_len, NULL); if (result != TPM_SUCCESS) return result; diff --git a/src/security/tpm/tss.h b/src/security/tpm/tss.h index 336935d911..c1ba234c2c 100644 --- a/src/security/tpm/tss.h +++ b/src/security/tpm/tss.h @@ -184,7 +184,8 @@ uint32_t tlcl_lock_nv_write(uint32_t index); /** * Perform a TPM_Extend. */ -uint32_t tlcl_extend(int pcr_num, const uint8_t *in_digest, +uint32_t tlcl_extend(int pcr_num, uint16_t algorithm, + const uint8_t *in_digest, size_t in_digest_len, uint8_t *out_digest); /** diff --git a/src/security/tpm/tss/tcg-1.2/tss.c b/src/security/tpm/tss/tcg-1.2/tss.c index b11d6a3d16..4c980d0d79 100644 --- a/src/security/tpm/tss/tcg-1.2/tss.c +++ b/src/security/tpm/tss/tcg-1.2/tss.c @@ -341,7 +341,8 @@ uint32_t tlcl_set_global_lock(void) return tlcl_write(TPM_NV_INDEX0, (uint8_t *) &x, 0); } -uint32_t tlcl_extend(int pcr_num, const uint8_t *in_digest, +uint32_t tlcl_extend(int pcr_num, uint16_t algorithm, + const uint8_t *in_digest, size_t in_digest_len, uint8_t *out_digest) { struct s_tpm_extend_cmd cmd; @@ -350,8 +351,11 @@ uint32_t tlcl_extend(int pcr_num, const uint8_t *in_digest, memcpy(&cmd, &tpm_extend_cmd, sizeof(cmd)); to_tpm_uint32(cmd.buffer + tpm_extend_cmd.pcrNum, pcr_num); - memcpy(cmd.buffer + cmd.inDigest, in_digest, kPcrDigestLength); + if (in_digest_len != kPcrDigestLength) + return TPM_E_HASH_ERROR; + + memcpy(cmd.buffer + cmd.inDigest, in_digest, kPcrDigestLength); result = tlcl_send_receive(cmd.buffer, response, sizeof(response)); if (result != TPM_SUCCESS) return result; diff --git a/src/security/tpm/tss/tcg-2.0/tss.c b/src/security/tpm/tss/tcg-2.0/tss.c index 16e40fe569..ac43549d84 100644 --- a/src/security/tpm/tss/tcg-2.0/tss.c +++ b/src/security/tpm/tss/tcg-2.0/tss.c @@ -127,21 +127,47 @@ uint32_t tlcl_assert_physical_presence(void) } /* - * The caller will provide the digest in a 32 byte buffer, let's consider it a - * sha256 digest. + * The caller will provide the pcr index, digest algorithm and + * a byte buffer to extend into the TPM. */ -uint32_t tlcl_extend(int pcr_num, const uint8_t *in_digest, +uint32_t tlcl_extend(int pcr_num, uint16_t algorithm, + const uint8_t *in_digest, size_t in_digest_len, uint8_t *out_digest) { struct tpm2_pcr_extend_cmd pcr_ext_cmd; struct tpm2_response *response; + uint16_t algorithm_size; pcr_ext_cmd.pcrHandle = HR_PCR + pcr_num; pcr_ext_cmd.digests.count = 1; - pcr_ext_cmd.digests.digests[0].hashAlg = TPM_ALG_SHA256; - memcpy(pcr_ext_cmd.digests.digests[0].digest.sha256, in_digest, - sizeof(pcr_ext_cmd.digests.digests[0].digest.sha256)); + pcr_ext_cmd.digests.digests[0].hashAlg = algorithm; + algorithm_size = tlcl_get_hash_size_from_algo(algorithm); + if (algorithm_size == 0) + return TPM_E_HASH_ERROR; + + if (in_digest_len != algorithm_size) + return TPM_E_HASH_ERROR; + + switch (algorithm) { + case TPM_ALG_SHA1: + memcpy(pcr_ext_cmd.digests.digests[0].digest.sha1, in_digest, in_digest_len); + break; + case TPM_ALG_SHA256: + memcpy(pcr_ext_cmd.digests.digests[0].digest.sha256, in_digest, in_digest_len); + break; + case TPM_ALG_SHA384: + memcpy(pcr_ext_cmd.digests.digests[0].digest.sha384, in_digest, in_digest_len); + break; + case TPM_ALG_SHA512: + memcpy(pcr_ext_cmd.digests.digests[0].digest.sha512, in_digest, in_digest_len); + break; + case TPM_ALG_SM3_256: + memcpy(pcr_ext_cmd.digests.digests[0].digest.sm3_256, in_digest, in_digest_len); + break; + default: + return TPM_E_HASH_ERROR; + } response = tpm_process_command(TPM2_PCR_Extend, &pcr_ext_cmd); printk(BIOS_INFO, "%s: response is %x\n", diff --git a/src/security/vboot/tpm_common.c b/src/security/vboot/tpm_common.c index 0a211c57d4..1db7189d7a 100644 --- a/src/security/vboot/tpm_common.c +++ b/src/security/vboot/tpm_common.c @@ -46,7 +46,7 @@ vb2_error_t vboot_extend_pcr(struct vb2_context *ctx, int pcr, switch (which_digest) { /* SHA1 of (devmode|recmode|keyblock) bits */ case BOOT_MODE_PCR: - return tpm_extend_pcr(pcr, VB2_HASH_SHA256, buffer, size, + return tpm_extend_pcr(pcr, VB2_HASH_SHA1, buffer, size, TPM_PCR_BOOT_MODE); /* SHA256 of HWID */ case HWID_DIGEST_PCR: diff --git a/src/soc/intel/apollolake/include/soc/pm.h b/src/soc/intel/apollolake/include/soc/pm.h index d0b0421561..22e414c803 100644 --- a/src/soc/intel/apollolake/include/soc/pm.h +++ b/src/soc/intel/apollolake/include/soc/pm.h @@ -250,4 +250,7 @@ void pch_log_state(void); void enable_pm_timer_emulation(void); +/* STM Support */ +uint16_t get_pmbase(void); + #endif diff --git a/src/soc/intel/apollolake/pmutil.c b/src/soc/intel/apollolake/pmutil.c index 559adad405..8151afc08d 100644 --- a/src/soc/intel/apollolake/pmutil.c +++ b/src/soc/intel/apollolake/pmutil.c @@ -246,3 +246,9 @@ int vbnv_cmos_failed(void) return rtc_failure; } + +/* STM Support */ +uint16_t get_pmbase(void) +{ + return (uint16_t) ACPI_BASE_ADDRESS; +} diff --git a/src/soc/intel/broadwell/include/soc/pm.h b/src/soc/intel/broadwell/include/soc/pm.h index 18004fa77d..c9074d8a0b 100644 --- a/src/soc/intel/broadwell/include/soc/pm.h +++ b/src/soc/intel/broadwell/include/soc/pm.h @@ -155,4 +155,7 @@ void disable_gpe(uint32_t mask); /* Return the selected ACPI SCI IRQ */ int acpi_sci_irq(void); +/* STM Support */ +uint16_t get_pmbase(void); + #endif diff --git a/src/soc/intel/broadwell/pmutil.c b/src/soc/intel/broadwell/pmutil.c index 00db6156ec..2445dfacf6 100644 --- a/src/soc/intel/broadwell/pmutil.c +++ b/src/soc/intel/broadwell/pmutil.c @@ -458,3 +458,9 @@ int vboot_platform_is_resuming(void) return acpi_sleep_from_pm1(inl(ACPI_BASE_ADDRESS + PM1_CNT)) == ACPI_S3; } + +/* STM Support */ +uint16_t get_pmbase(void) +{ + return (uint16_t) ACPI_BASE_ADDRESS; +} diff --git a/src/soc/intel/cannonlake/include/soc/pm.h b/src/soc/intel/cannonlake/include/soc/pm.h index 5b85e74bf5..356f0bcc6f 100644 --- a/src/soc/intel/cannonlake/include/soc/pm.h +++ b/src/soc/intel/cannonlake/include/soc/pm.h @@ -172,5 +172,8 @@ void pmc_set_disb(void); /* Clear PMCON status bits */ void pmc_clear_pmcon_sts(void); +/* STM Support */ +uint16_t get_pmbase(void); + #endif /* !defined(__ACPI__) */ #endif diff --git a/src/soc/intel/cannonlake/pmutil.c b/src/soc/intel/cannonlake/pmutil.c index 428a89fe4b..5774873180 100644 --- a/src/soc/intel/cannonlake/pmutil.c +++ b/src/soc/intel/cannonlake/pmutil.c @@ -273,3 +273,9 @@ void soc_fill_power_state(struct chipset_power_state *ps) printk(BIOS_DEBUG, "GBLRST_CAUSE: %08x %08x\n", ps->gblrst_cause[0], ps->gblrst_cause[1]); } + +/* STM Support */ +uint16_t get_pmbase(void) +{ + return (uint16_t) ACPI_BASE_ADDRESS; +} diff --git a/src/soc/intel/fsp_broadwell_de/Kconfig b/src/soc/intel/fsp_broadwell_de/Kconfig index 4c45f29618..17a15f6d3b 100644 --- a/src/soc/intel/fsp_broadwell_de/Kconfig +++ b/src/soc/intel/fsp_broadwell_de/Kconfig @@ -23,6 +23,7 @@ config CPU_SPECIFIC_OPTIONS select SUPPORT_CPU_UCODE_IN_CBFS select INTEL_DESCRIPTOR_MODE_CAPABLE select HAVE_SMI_HANDLER + select X86_SMM_LOADER_VERSION2 select TSC_MONOTONIC_TIMER select HAVE_FSP_BIN select CPU_INTEL_FIRMWARE_INTERFACE_TABLE @@ -94,6 +95,10 @@ config HPET_MIN_TICKS hex default 0x80 +config ENABLE_VMX + bool "Enable VMX for virtualization" + default y + ## Broadwell-DE Specific FSP Kconfig source src/soc/intel/fsp_broadwell_de/fsp/Kconfig diff --git a/src/soc/intel/fsp_broadwell_de/acpi.c b/src/soc/intel/fsp_broadwell_de/acpi.c index 5349c30758..498aee1711 100644 --- a/src/soc/intel/fsp_broadwell_de/acpi.c +++ b/src/soc/intel/fsp_broadwell_de/acpi.c @@ -271,11 +271,11 @@ void acpi_fill_in_fadt(acpi_fadt_t *fadt, acpi_facs_t *facs, void *dsdt) fadt->x_gpe0_blk.space_id = ACPI_ADDRESS_SPACE_IO; fadt->x_gpe0_blk.bit_width = 64; /* EventStatus + EventEnable */ fadt->x_gpe0_blk.bit_offset = 0; - fadt->x_gpe0_blk.access_size = ACPI_ACCESS_SIZE_DWORD_ACCESS; + fadt->x_gpe0_blk.access_size = ACPI_ACCESS_SIZE_BYTE_ACCESS; fadt->x_gpe0_blk.addrl = fadt->gpe0_blk; fadt->x_gpe0_blk.addrh = 0x00; - fadt->x_gpe1_blk.space_id = ACPI_ADDRESS_SPACE_IO; + fadt->x_gpe1_blk.space_id = 0; fadt->x_gpe1_blk.bit_width = 0; fadt->x_gpe1_blk.bit_offset = 0; fadt->x_gpe1_blk.access_size = 0; diff --git a/src/soc/intel/fsp_broadwell_de/chip.h b/src/soc/intel/fsp_broadwell_de/chip.h index bf2896238a..95c1c2d959 100644 --- a/src/soc/intel/fsp_broadwell_de/chip.h +++ b/src/soc/intel/fsp_broadwell_de/chip.h @@ -25,6 +25,8 @@ struct soc_intel_fsp_broadwell_de_config { /* PCIe completion timeout value */ int pcie_compltoval; + /* LPC Generic Memory Range Register value */ + uint32_t lpc_lgmr; }; typedef struct soc_intel_fsp_broadwell_de_config config_t; diff --git a/src/soc/intel/fsp_broadwell_de/include/soc/lpc.h b/src/soc/intel/fsp_broadwell_de/include/soc/lpc.h index 3f9c2024f7..01e5a5b28d 100644 --- a/src/soc/intel/fsp_broadwell_de/include/soc/lpc.h +++ b/src/soc/intel/fsp_broadwell_de/include/soc/lpc.h @@ -37,6 +37,7 @@ #define LPC_GEN2_DEC 0x88 #define LPC_GEN3_DEC 0x8c #define LPC_GEN4_DEC 0x90 +#define LGMR 0x98 /* LPC Generic Memory Range */ #define GEN_PMCON_1 0xA0 #define SMI_LOCK (1 << 4) #define SMI_LOCK_GP6 (1 << 5) diff --git a/src/soc/intel/fsp_broadwell_de/include/soc/memory.h b/src/soc/intel/fsp_broadwell_de/include/soc/memory.h index 3bdba2ef56..494ca34da1 100644 --- a/src/soc/intel/fsp_broadwell_de/include/soc/memory.h +++ b/src/soc/intel/fsp_broadwell_de/include/soc/memory.h @@ -27,4 +27,8 @@ void save_dimm_info(void); +/* Determine if memory configuration has been locked by TXT */ +bool memory_config_is_locked(void); + + #endif diff --git a/src/soc/intel/fsp_broadwell_de/include/soc/msr.h b/src/soc/intel/fsp_broadwell_de/include/soc/msr.h index f9fdffb2bf..2bbcf23687 100644 --- a/src/soc/intel/fsp_broadwell_de/include/soc/msr.h +++ b/src/soc/intel/fsp_broadwell_de/include/soc/msr.h @@ -39,4 +39,9 @@ #define MSR_PRMRR_PHYS_BASE 0x1f4 #define MSR_PRMRR_PHYS_MASK 0x1f5 +/* EDS vol 2 */ +#define MSR_LT_MEMORY_LOCKED 0x2e7 +#define MSR_MEM_LOCK_BIT1 (1 << 1) +#define MSR_MEM_LOCK_BIT2 (1 << 2) + #endif /* _SOC_MSR_H_ */ diff --git a/src/soc/intel/fsp_broadwell_de/include/soc/pci_devs.h b/src/soc/intel/fsp_broadwell_de/include/soc/pci_devs.h index 6a68b2f81f..ba5c1deeed 100644 --- a/src/soc/intel/fsp_broadwell_de/include/soc/pci_devs.h +++ b/src/soc/intel/fsp_broadwell_de/include/soc/pci_devs.h @@ -56,6 +56,7 @@ #define LPC_DEV 31 #define LPC_FUNC 0 #define PCH_DEVFN_LPC PCI_DEVFN(LPC_DEV, LPC_FUNC) +#define PCH_DEV_LPC PCI_DEV(BUS0, LPC_DEV, LPC_FUNC) #define SATA_DEV 31 #define SATA_FUNC 2 diff --git a/src/soc/intel/fsp_broadwell_de/include/soc/pm.h b/src/soc/intel/fsp_broadwell_de/include/soc/pm.h new file mode 100644 index 0000000000..c1b6ee9e80 --- /dev/null +++ b/src/soc/intel/fsp_broadwell_de/include/soc/pm.h @@ -0,0 +1,28 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2007-2009 coresystems GmbH + * Copyright (C) 2013 Google Inc. + * Copyright (C) 2015-2016 Intel Corp. + * Copyright (C) 2016-2018 Siemens AG + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _SOC_FSP_BROADWELL_DE_PM_H_ +#define _SOC_FSP_BROADWELL_DE_PM_H_ + +/* + * Brings in get_pmbase so that StmPlatformResource.c can build + * under 4.11 + */ + +#include +#endif diff --git a/src/soc/intel/fsp_broadwell_de/romstage/memory.c b/src/soc/intel/fsp_broadwell_de/romstage/memory.c index afbf97bf57..571ab091ab 100644 --- a/src/soc/intel/fsp_broadwell_de/romstage/memory.c +++ b/src/soc/intel/fsp_broadwell_de/romstage/memory.c @@ -13,6 +13,8 @@ * GNU General Public License for more details. */ +#include +#include #include #include #include @@ -20,6 +22,8 @@ #include #include +#define MAX_SPD_READ_TRIES 3 + static uint32_t get_memory_dclk(void) { uint32_t reg32 = @@ -30,7 +34,7 @@ static uint32_t get_memory_dclk(void) void save_dimm_info(void) { int index = 0; - uint32_t dclk_mhz = 0; + uint32_t dclk_mhz = 0, tries; /* * When talking to SPD chips through IMC slave offset of 0x50 is automagically added @@ -53,9 +57,39 @@ void save_dimm_info(void) for (int slot = 0; slot < CONFIG_DIMM_MAX / IMC_MAX_CHANNELS; slot++) { dimm_attr dimm = {0}; u8 *spd_data = blk.spd_array[index]; - if (spd_decode_ddr4(&dimm, spd_data) == SPD_STATUS_OK) - spd_add_smbios17_ddr4(channel, slot, dclk_mhz, &dimm); + int res = SPD_STATUS_OK; + tries = MAX_SPD_READ_TRIES; + /* + * SMBus controller can't validate data integrity. So on CRC + * error retry a few times. + */ + do { + res = spd_decode_ddr4(&dimm, spd_data); + if (res == SPD_STATUS_CRC_ERROR) { + printk(BIOS_ERR, + "SPD CRC error, channel %u slot %u " + "try %u\n", channel, slot, tries); + get_spd_smbus(&blk); + } + } while (tries-- && res == SPD_STATUS_CRC_ERROR); + index++; + + if (res == SPD_STATUS_CRC_ERROR) { + printk(BIOS_WARNING, "Gave up reading CRC on channel %u" + " slot %u\n", channel, slot); + continue; + } + + if (res == SPD_STATUS_OK) { + spd_add_smbios17_ddr4(channel, slot, dclk_mhz, &dimm); + } } } } + +bool memory_config_is_locked(void) +{ + msr_t msr = rdmsr(MSR_LT_MEMORY_LOCKED); + return (msr.lo & (MSR_MEM_LOCK_BIT1 | MSR_MEM_LOCK_BIT2)); +} diff --git a/src/soc/intel/fsp_broadwell_de/romstage/romstage.c b/src/soc/intel/fsp_broadwell_de/romstage/romstage.c index 8438b1035c..dbf4bc3482 100644 --- a/src/soc/intel/fsp_broadwell_de/romstage/romstage.c +++ b/src/soc/intel/fsp_broadwell_de/romstage/romstage.c @@ -21,8 +21,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -38,6 +40,9 @@ #include #include +#include +#include + static void init_rtc(void) { u16 gen_pmcon3 = pci_read_config16(PCI_DEV(0, LPC_DEV, LPC_FUNC), GEN_PMCON_3); @@ -122,7 +127,24 @@ static void early_iio_hide(void) iio_hide(dev); } } +} +static void check_msr_lock(void) +{ + /* + * Sometimes the system boots in an invalid state, where random values + * have been written to MSRs and then the MSRs are locked. + * Seems to always happen on warm reset. + * + * Power cycling or a board_reset() isn't sufficient in this case, so + * issue a full_reset() to "fix" this issue. + */ + msr_t msr = rdmsr(IA32_FEATURE_CONTROL); + if (msr.lo & 1) { + console_init(); + printk(BIOS_EMERG, "Detected broken platform state. Issuing full reset\n"); + full_reset(); + } } /* Entry from cache-as-ram.inc. */ @@ -143,6 +165,8 @@ void *asmlinkage main(FSP_INFO_HEADER *fsp_info_header) enable_integrated_uart(CONFIG_UART_FOR_CONSOLE); } + check_msr_lock(); + /* Call into mainboard. */ post_code(0x41); early_mainboard_romstage_entry(); @@ -156,6 +180,12 @@ void *asmlinkage main(FSP_INFO_HEADER *fsp_info_header) early_iio_hide(); timestamp_add_now(TS_BEFORE_INITRAM); post_code(0x48); + + if (CONFIG(INTEL_TXT)) { + printk(BIOS_DEBUG, "Check TXT_ERROR register\n"); + intel_txt_log_acm_error(read32((void *)TXT_ERROR)); + } + /* * Call early init to initialize memory and chipset. This function returns * to the romstage_main_continue function with a pointer to the HOB diff --git a/src/soc/intel/fsp_broadwell_de/southcluster.c b/src/soc/intel/fsp_broadwell_de/southcluster.c index fb8af87b62..df562f133d 100644 --- a/src/soc/intel/fsp_broadwell_de/southcluster.c +++ b/src/soc/intel/fsp_broadwell_de/southcluster.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -216,6 +217,17 @@ static void sc_read_resources(struct device *dev) pci_dev_read_resources(dev); sc_add_mmio_resources(dev); sc_add_io_resources(dev); + + const config_t *config = config_of_soc(); + if (config->lpc_lgmr) { + struct resource *res; + res = new_resource(dev, LGMR); + res->base = config->lpc_lgmr & ~(LPC_LGMR_EN); + res->size = 64 * KiB; + res->flags = IORESOURCE_MEM | IORESOURCE_ASSIGNED | IORESOURCE_FIXED + | IORESOURCE_RESERVE; + pci_write_config32(dev, LGMR, config->lpc_lgmr); + } } static void sc_init(struct device *dev) diff --git a/src/soc/intel/icelake/include/soc/pm.h b/src/soc/intel/icelake/include/soc/pm.h index 44888ec747..34c32a9ac2 100644 --- a/src/soc/intel/icelake/include/soc/pm.h +++ b/src/soc/intel/icelake/include/soc/pm.h @@ -171,5 +171,8 @@ void pmc_set_disb(void); /* Clear PMCON status bits */ void pmc_clear_pmcon_sts(void); +/* STM Support */ +uint16_t get_pmbase(void); + #endif /* !defined(__ACPI__) */ #endif diff --git a/src/soc/intel/icelake/pmutil.c b/src/soc/intel/icelake/pmutil.c index 8efd426606..a4971da984 100644 --- a/src/soc/intel/icelake/pmutil.c +++ b/src/soc/intel/icelake/pmutil.c @@ -272,3 +272,9 @@ void soc_fill_power_state(struct chipset_power_state *ps) printk(BIOS_DEBUG, "GBLRST_CAUSE: %08x %08x\n", ps->gblrst_cause[0], ps->gblrst_cause[1]); } + +/* STM Support */ +uint16_t get_pmbase(void) +{ + return (uint16_t) ACPI_BASE_ADDRESS; +} diff --git a/src/soc/intel/quark/acpi.c b/src/soc/intel/quark/acpi.c index ffcd91f13d..5006b19d47 100644 --- a/src/soc/intel/quark/acpi.c +++ b/src/soc/intel/quark/acpi.c @@ -104,3 +104,10 @@ void acpi_fill_in_fadt(acpi_fadt_t *fadt) printk(BIOS_SPEW, " 0x%08x: RESET\n", fadt->reset_reg.addrl); } + +uint16_t get_pmbase(void) +{ + struct device *dev = pcidev_on_root(PCI_DEVICE_NUMBER_QNC_LPC, + PCI_FUNCTION_NUMBER_QNC_LPC); + return (uint16_t) pci_read_config32(dev, R_QNC_LPC_PM1BLK) & B_QNC_LPC_PM1BLK_MASK; +} diff --git a/src/soc/intel/quark/include/soc/pm.h b/src/soc/intel/quark/include/soc/pm.h index a3fb02f7db..e02b8a274e 100644 --- a/src/soc/intel/quark/include/soc/pm.h +++ b/src/soc/intel/quark/include/soc/pm.h @@ -27,4 +27,7 @@ struct chipset_power_state { struct chipset_power_state *get_power_state(void); int fill_power_state(void); +/* STM Support */ +uint16_t get_pmbase(void); + #endif /* _SOC_PM_H_ */ diff --git a/src/soc/intel/skylake/include/soc/pm.h b/src/soc/intel/skylake/include/soc/pm.h index 18b0c15d64..007d29cadc 100644 --- a/src/soc/intel/skylake/include/soc/pm.h +++ b/src/soc/intel/skylake/include/soc/pm.h @@ -197,4 +197,7 @@ static inline int deep_s5_enabled(void) return !!(deep_s5_pol & (S5DC_GATE_SUS | S5AC_GATE_SUS)); } +/* STM Support */ +uint16_t get_pmbase(void); + #endif diff --git a/src/soc/intel/skylake/pmutil.c b/src/soc/intel/skylake/pmutil.c index 329cea9621..aac5d1d939 100644 --- a/src/soc/intel/skylake/pmutil.c +++ b/src/soc/intel/skylake/pmutil.c @@ -266,3 +266,9 @@ void soc_fill_power_state(struct chipset_power_state *ps) printk(BIOS_DEBUG, "GBLRST_CAUSE: %08x %08x\n", ps->gblrst_cause[0], ps->gblrst_cause[1]); } + +/* STM Support */ +uint16_t get_pmbase(void) +{ + return ACPI_BASE_ADDRESS; +} diff --git a/src/soc/intel/tigerlake/include/soc/pm.h b/src/soc/intel/tigerlake/include/soc/pm.h index fb9b67bc23..d2f47e271b 100644 --- a/src/soc/intel/tigerlake/include/soc/pm.h +++ b/src/soc/intel/tigerlake/include/soc/pm.h @@ -177,5 +177,7 @@ void pmc_set_disb(void); /* Clear PMCON status bits */ void pmc_clear_pmcon_sts(void); +/* STM Support */ +uint16_t get_pmbase(void); #endif /* !defined(__ACPI__) */ #endif diff --git a/src/soc/intel/tigerlake/pmutil.c b/src/soc/intel/tigerlake/pmutil.c index 53f86097ee..39659c3c6f 100644 --- a/src/soc/intel/tigerlake/pmutil.c +++ b/src/soc/intel/tigerlake/pmutil.c @@ -274,3 +274,9 @@ void soc_fill_power_state(struct chipset_power_state *ps) printk(BIOS_DEBUG, "GBLRST_CAUSE: %08x %08x\n", ps->gblrst_cause[0], ps->gblrst_cause[1]); } + +/* STM Support */ +uint16_t get_pmbase(void) +{ + return (uint16_t) ACPI_BASE_ADDRESS; +} diff --git a/src/vendorcode/eltan/security/mboot/mboot.c b/src/vendorcode/eltan/security/mboot/mboot.c index c5523a5fd8..499d35299c 100644 --- a/src/vendorcode/eltan/security/mboot/mboot.c +++ b/src/vendorcode/eltan/security/mboot/mboot.c @@ -150,7 +150,8 @@ int mboot_hash_extend_log(uint64_t flags, uint8_t *hashData, uint32_t hashDataLe printk(BIOS_DEBUG, "%s: SHA256 Hash Digest:\n", __func__); mboot_print_buffer(digest->digest.sha256, VB2_SHA256_DIGEST_SIZE); - return (tlcl_extend(newEventHdr->pcrIndex, (uint8_t *)&(newEventHdr->digest), NULL)); + return (tlcl_extend(newEventHdr->pcrIndex, newEventHdr->digest.digests[0].hashAlg, + (uint8_t *)&(newEventHdr->digest), hashDataLen, NULL)); } /* diff --git a/util/xcompile/xcompile b/util/xcompile/xcompile index 2d3da1e00e..13273240c5 100755 --- a/util/xcompile/xcompile +++ b/util/xcompile/xcompile @@ -184,7 +184,8 @@ detect_special_flags() { CFLAGS_GCC="$CFLAGS_GCC -fno-stack-protector" testcc "$GCC" "$CFLAGS_GCC -Wl,--build-id=none" && CFLAGS_GCC="$CFLAGS_GCC -Wl,--build-id=none" - + testcc "$GCC" "$CFLAGS_GCC --param=min-pagesize=1024 $FLAGS_GCC" && + CFLAGS_GCC="$CFLAGS_GCC --param=min-pagesize=1024" case "$architecture" in x86) ;;