Compare commits
67 commits
main
...
4.11_branc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
046814c270 | ||
|
|
1c13f8d85c | ||
|
|
92c1a19c79 | ||
|
|
34010e8adb | ||
|
|
e7e2bd2a59 | ||
|
|
0f93a91548 | ||
|
|
56ce49f10f | ||
|
|
75c35288d8 | ||
|
|
701180f069 | ||
|
|
2b32db6ddc | ||
|
|
60004e276a | ||
|
|
6ffb50080a | ||
|
|
4401498041 | ||
|
|
40dccd9f36 | ||
|
|
2953527a67 | ||
|
|
e7a126fbc2 | ||
|
|
4b776b705f | ||
|
|
30bc0a4b66 | ||
|
|
44b614aef5 | ||
|
|
1d3fbda9ee | ||
|
|
3ec21b07fb | ||
|
|
463aee755e | ||
|
|
4a3e7dd31d | ||
|
|
bf2f0757a7 | ||
|
|
da2827949e | ||
|
|
9f53477768 | ||
|
|
8b22c55855 | ||
|
|
570ae23516 | ||
|
|
019c0049a2 | ||
|
|
2f32b5b5d0 | ||
|
|
8916d12426 | ||
|
|
236ca58a76 | ||
|
|
413027b0c7 | ||
|
|
6f1a75950a | ||
|
|
5434988bac | ||
|
|
6b395cb190 | ||
|
|
b450c8d2cb | ||
|
|
fc8a6fa93a | ||
|
|
c7af5ef509 | ||
|
|
bff4cb0558 | ||
|
|
2085d6f46a | ||
|
|
1474ddb722 | ||
|
|
1c7b526de1 | ||
|
|
76b81ff4c7 | ||
|
|
a695655915 | ||
|
|
6a7531431d | ||
|
|
aae448601c | ||
|
|
ea7fde7070 | ||
|
|
b43431d58e | ||
|
|
fabe8f5a95 | ||
|
|
380c4b447c | ||
|
|
c0736c5a55 | ||
|
|
df7e1f9a43 | ||
|
|
674a825cd7 | ||
|
|
631eac99ed | ||
|
|
ff6db825c3 | ||
|
|
e33ecb0e08 | ||
|
|
98477da405 | ||
|
|
93ac30d189 | ||
|
|
8ac46b937c | ||
|
|
29ce1be9cc | ||
|
|
d491ebfe5b | ||
|
|
9ad9b3a682 | ||
|
|
e81f20c36a | ||
|
|
c3a8d63788 | ||
|
|
5566303362 | ||
|
|
9ede3d51e5 |
106 changed files with 6327 additions and 494 deletions
5
.gitmodules
vendored
5
.gitmodules
vendored
|
|
@ -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
|
||||
|
|
|
|||
2
3rdparty/intel-microcode
vendored
2
3rdparty/intel-microcode
vendored
|
|
@ -1 +1 @@
|
|||
Subproject commit 1dd14da6d1ea5cfbd95923653f31c04aac3aa655
|
||||
Subproject commit 49bb67f32a2e3e631ba1a9a73da1c52e1cac7fd9
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
146
Documentation/mainboard/ocp/monolake.md
Normal file
146
Documentation/mainboard/ocp/monolake.md
Normal file
|
|
@ -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 <path to coreboot image>
|
||||
|
||||
Yosemite-V1 OpenBMC BIOS firmware image update uses fw-util:
|
||||
fw-util slotx --update bios <path to coreboot image>
|
||||
|
||||
## 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
|
||||
13
Makefile.inc
13
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
|
||||
|
|
|
|||
8
configs/config.purism_librem15_v4.txt_build_test
Normal file
8
configs/config.purism_librem15_v4.txt_build_test
Normal file
|
|
@ -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
|
||||
4
configs/config.stm
Normal file
4
configs/config.stm
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
CONFIG_VENDOR_PURISM=y
|
||||
CONFIG_BOARD_PURISM_LIBREM15_V4=y
|
||||
CONFIG_STM=y
|
||||
CONFIG_IED_REGION_SIZE=0
|
||||
17
src/Kconfig
17
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
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -32,6 +32,8 @@
|
|||
#if CONFIG(CHROMEOS)
|
||||
#include <vendorcode/google/chromeos/gnvs.h>
|
||||
#endif
|
||||
#include <drivers/vpd/vpd.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#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;
|
||||
|
|
|
|||
|
|
@ -21,56 +21,14 @@
|
|||
#include <smp/node.h>
|
||||
#include <stddef.h>
|
||||
#include <trace.h>
|
||||
#include <timer.h>
|
||||
|
||||
#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());
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -37,6 +37,8 @@
|
|||
#include <timer.h>
|
||||
#include <thread.h>
|
||||
|
||||
#include <security/intel/stm/SmmStm.h>
|
||||
|
||||
#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.
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
#include <cpu/x86/cache.h>
|
||||
#include <commonlib/helpers.h>
|
||||
#include <console/console.h>
|
||||
#include <security/intel/stm/SmmStm.h>
|
||||
|
||||
#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);
|
||||
}
|
||||
|
|
|
|||
666
src/cpu/x86/smm/smm_module_loaderv2.c
Normal file
666
src/cpu/x86/smm/smm_module_loaderv2.c
Normal file
|
|
@ -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 <stdint.h>
|
||||
#include <string.h>
|
||||
#include <rmodule.h>
|
||||
#include <cpu/x86/smm.h>
|
||||
#include <commonlib/helpers.h>
|
||||
#include <console/console.h>
|
||||
#include <security/intel/stm/SmmStm.h>
|
||||
|
||||
#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);
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
#include <cpu/x86/smm.h>
|
||||
#include <stage_cache.h>
|
||||
#include <types.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
411
src/drivers/ipmi/ipmi_fru.c
Normal file
411
src/drivers/ipmi/ipmi_fru.c
Normal file
|
|
@ -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 <console/console.h>
|
||||
#include <string.h>
|
||||
#include <delay.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
}
|
||||
|
|
@ -15,7 +15,7 @@
|
|||
#include <console/console.h>
|
||||
#include <device/device.h>
|
||||
#include <arch/io.h>
|
||||
#include <delay.h>
|
||||
#include <timer.h>
|
||||
#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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
#include <console/console.h>
|
||||
#include "ipmi_ops.h"
|
||||
#include <string.h>
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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, ...)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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. */
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
||||
|
|
|
|||
|
|
@ -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*
|
||||
|
|
|
|||
|
|
@ -1,2 +1,5 @@
|
|||
config BOARD_FACEBOOK_WATSON
|
||||
bool "Watson"
|
||||
bool "Watson_v1"
|
||||
|
||||
config BOARD_FACEBOOK_WATSON_V2
|
||||
bool "Watson_v2"
|
||||
|
|
|
|||
|
|
@ -14,3 +14,6 @@
|
|||
##
|
||||
|
||||
ramstage-y += irqroute.c
|
||||
|
||||
subdirs-y += variants/$(VARIANT_DIR)
|
||||
CPPFLAGS_common += -I$(src)/mainboard/$(MAINBOARDDIR)/include
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
26
src/mainboard/facebook/watson/include/variants.h
Normal file
26
src/mainboard/facebook/watson/include/variants.h
Normal file
|
|
@ -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 <soc/romstage.h>
|
||||
|
||||
void variant_romstage_fsp_init_params(UPD_DATA_REGION *UpdData);
|
||||
void variant_early_mainboard_romstage_entry(void);
|
||||
|
||||
#endif /* BASEBOARD_VARIANTS_H */
|
||||
|
|
@ -17,6 +17,7 @@
|
|||
#include <stddef.h>
|
||||
#include <soc/romstage.h>
|
||||
#include <drivers/intel/fsp1_0/fsp_util.h>
|
||||
#include <variants.h>
|
||||
|
||||
/**
|
||||
* /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)
|
||||
{
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
romstage-y += romstage.c
|
||||
|
|
@ -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
|
||||
60
src/mainboard/facebook/watson/variants/watson_v2/romstage.c
Normal file
60
src/mainboard/facebook/watson/variants/watson_v2/romstage.c
Normal file
|
|
@ -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 <device/pci_ops.h>
|
||||
#include <soc/lpc.h>
|
||||
#include <soc/pci_devs.h>
|
||||
#include <soc/romstage.h>
|
||||
#include <variants.h>
|
||||
|
||||
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);
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"))
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -38,257 +38,6 @@ DefinitionBlock(
|
|||
#include <acpi/pcie1.asl>
|
||||
}
|
||||
|
||||
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 <acpi/uncore.asl>
|
||||
}
|
||||
|
||||
#include "acpi/mainboard.asl"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <bootstate.h>
|
||||
#include <device/device.h>
|
||||
#include <pc80/mc146818rtc.h>
|
||||
#include <cf9_reset.h>
|
||||
|
|
@ -22,6 +23,7 @@
|
|||
#include <drivers/vpd/vpd.h>
|
||||
#include <console/console.h>
|
||||
#include <drivers/ipmi/ipmi_ops.h>
|
||||
#include <gpio.h>
|
||||
#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);
|
||||
|
|
|
|||
|
|
@ -19,9 +19,6 @@
|
|||
#include <soc/romstage.h>
|
||||
#include <drivers/intel/fsp1_0/fsp_util.h>
|
||||
#include <drivers/vpd/vpd.h>
|
||||
#include <cpu/x86/msr.h>
|
||||
#include <cf9_reset.h>
|
||||
#include <console/console.h>
|
||||
#include <device/pci_ops.h>
|
||||
#include <soc/pci_devs.h>
|
||||
#include <soc/lpc.h>
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -17,9 +17,6 @@
|
|||
#include <stddef.h>
|
||||
#include <soc/romstage.h>
|
||||
#include <drivers/intel/fsp1_0/fsp_util.h>
|
||||
#include <cpu/x86/msr.h>
|
||||
#include <cf9_reset.h>
|
||||
#include <console/console.h>
|
||||
#include <device/pci_ops.h>
|
||||
#include <soc/pci_devs.h>
|
||||
#include <soc/lpc.h>
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -261,7 +261,6 @@ DefinitionBlock(
|
|||
Return (0xff)
|
||||
}
|
||||
|
||||
Name (_ADR, 0x00)
|
||||
Method (_STA, 0, NotSerialized)
|
||||
{
|
||||
Return (0xf)
|
||||
|
|
|
|||
|
|
@ -14,3 +14,4 @@
|
|||
##
|
||||
|
||||
source "src/security/intel/txt/Kconfig"
|
||||
source "src/security/intel/stm/Kconfig"
|
||||
|
|
|
|||
|
|
@ -1 +1,2 @@
|
|||
subdirs-y += txt
|
||||
subdirs-y += stm
|
||||
|
|
|
|||
122
src/security/intel/stm/Kconfig
Normal file
122
src/security/intel/stm/Kconfig
Normal file
|
|
@ -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
|
||||
33
src/security/intel/stm/Makefile
Normal file
33
src/security/intel/stm/Makefile
Normal file
|
|
@ -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
|
||||
20
src/security/intel/stm/Makefile.inc
Normal file
20
src/security/intel/stm/Makefile.inc
Normal file
|
|
@ -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)
|
||||
657
src/security/intel/stm/SmmStm.c
Normal file
657
src/security/intel/stm/SmmStm.c
Normal file
|
|
@ -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 <console/console.h>
|
||||
#include <cpu/x86/cr.h>
|
||||
#include <cpu/x86/mp.h>
|
||||
#include <cpu/x86/msr.h>
|
||||
#include <cpu/x86/cache.h>
|
||||
#include <security/intel/stm/SmmStm.h>
|
||||
#include <string.h>
|
||||
|
||||
#define TXT_EVTYPE_BASE 0x400
|
||||
#define TXT_EVTYPE_STM_HASH (TXT_EVTYPE_BASE + 14)
|
||||
|
||||
#define RDWR_ACCS 3
|
||||
#define FULL_ACCS 7
|
||||
|
||||
#define SIZE_4KB 0x00001000
|
||||
#define SIZE_4MB 0x00400000
|
||||
|
||||
#define PTP_SIZE SIZE_4KB
|
||||
|
||||
#define IA32_PG_P (1 << 0)
|
||||
#define IA32_PG_RW (1 << 1)
|
||||
#define IA32_PG_PS (1 << 7)
|
||||
|
||||
#define STM_PAGE_SHIFT 12
|
||||
#define STM_PAGE_MASK 0xFFF
|
||||
#define STM_SIZE_TO_PAGES(a) (((a) >> STM_PAGE_SHIFT) + (((a)&STM_PAGE_MASK) ? 1 : 0))
|
||||
#define STM_PAGES_TO_SIZE(a) ((a) << STM_PAGE_SHIFT)
|
||||
|
||||
#define STM_ACCESS_DENIED 15
|
||||
#define STM_UNSUPPORTED 3
|
||||
|
||||
#define STM_BUFFER_TOO_SMALL 1
|
||||
|
||||
#define STM_SM_MONITOR_STATE_ENABLED 1
|
||||
|
||||
typedef struct {
|
||||
|
||||
uint64_t vmcs_revision_id : 31;
|
||||
uint64_t always_zero : 1;
|
||||
uint64_t vmcs_size : 13;
|
||||
uint64_t reserved1 : 3;
|
||||
uint64_t vmxon_add_width : 1;
|
||||
uint64_t stm_supported : 1;
|
||||
uint64_t vmcs_memory_type : 4;
|
||||
uint64_t in_out_reporting : 1;
|
||||
uint64_t may_clear_defaults : 1;
|
||||
uint64_t reserved2 : 8;
|
||||
} VMX_BASIC_MSR_BITS;
|
||||
|
||||
typedef union {
|
||||
VMX_BASIC_MSR_BITS bits;
|
||||
uint64_t uint64;
|
||||
msr_t msr;
|
||||
} VMX_BASIC_MSR;
|
||||
|
||||
typedef struct {
|
||||
uint64_t valid : 1;
|
||||
uint64_t reserved1 : 1;
|
||||
uint64_t vmx_off_blockSmi : 1;
|
||||
uint64_t reserved2 : 9;
|
||||
uint64_t mseg_address : 20;
|
||||
uint64_t reserved3 : 32;
|
||||
} SMM_MONITOR_CTL_MSR_BITS;
|
||||
|
||||
extern struct mp_state {
|
||||
struct mp_ops ops;
|
||||
int cpu_count;
|
||||
uintptr_t perm_smbase;
|
||||
size_t perm_smsize;
|
||||
size_t smm_save_state_size;
|
||||
int do_smm;
|
||||
} mp_state;
|
||||
|
||||
typedef union {
|
||||
SMM_MONITOR_CTL_MSR_BITS bits;
|
||||
uint64_t uint64;
|
||||
msr_t msr;
|
||||
} SMM_MONITOR_CTL_MSR;
|
||||
|
||||
// Template of STM_RSC_END structure for copying.
|
||||
|
||||
STM_RSC_END m_rsc_end_node = {
|
||||
{END_OF_RESOURCES, sizeof(STM_RSC_END)},
|
||||
};
|
||||
|
||||
uint8_t *m_stm_resources_ptr = NULL;
|
||||
uint32_t m_stm_resource_total_size = 0x0;
|
||||
uint32_t m_stm_resource_size_used = 0x0;
|
||||
uint32_t m_stm_resource_size_available = 0x0;
|
||||
|
||||
uint8_t *stm_resource_heap = NULL;
|
||||
|
||||
uint32_t m_stm_state = 0;
|
||||
|
||||
/*
|
||||
* Handle single Resource to see if it can be merged into Record.
|
||||
*
|
||||
* @param resource A pointer to resource node to be added
|
||||
* @param record A pointer to record node to be merged
|
||||
*
|
||||
* @retval true resource handled
|
||||
* @retval false resource is not handled
|
||||
*/
|
||||
|
||||
static bool handle_single_resource(STM_RSC *resource, STM_RSC *record)
|
||||
{
|
||||
uint64_t resource_lo = 0;
|
||||
uint64_t resource_hi = 0;
|
||||
uint64_t record_lo = 0;
|
||||
uint64_t record_hi = 0;
|
||||
|
||||
// Calling code is responsible for making sure that
|
||||
// Resource->Header.RscType == (*Record)->Header.RscType
|
||||
// thus we use just one of them as switch variable.
|
||||
|
||||
switch (resource->header.rsc_type) {
|
||||
case MEM_RANGE:
|
||||
case MMIO_RANGE:
|
||||
resource_lo = resource->mem.base;
|
||||
resource_hi = resource->mem.base + resource->mem.length;
|
||||
record_lo = record->mem.base;
|
||||
record_hi = record->mem.base + record->mem.length;
|
||||
if (resource->mem.rwx_attributes != record->mem.rwx_attributes) {
|
||||
if ((resource_lo == record_lo) && (resource_hi == record_hi)) {
|
||||
record->mem.rwx_attributes = resource->mem.rwx_attributes
|
||||
| record->mem.rwx_attributes;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case IO_RANGE:
|
||||
case TRAPPED_IO_RANGE:
|
||||
resource_lo = (uint64_t)resource->io.base;
|
||||
resource_hi = (uint64_t)resource->io.base + (uint64_t)resource->io.length;
|
||||
record_lo = (uint64_t)record->io.base;
|
||||
record_hi = (uint64_t)record->io.base + (uint64_t)record->io.length;
|
||||
break;
|
||||
case PCI_CFG_RANGE:
|
||||
if ((resource->pci_cfg.originating_bus_number
|
||||
!= record->pci_cfg.originating_bus_number)
|
||||
|| (resource->pci_cfg.last_node_index != record->pci_cfg.last_node_index))
|
||||
return false;
|
||||
|
||||
if (memcmp(resource->pci_cfg.pci_device_path, record->pci_cfg.pci_device_path,
|
||||
sizeof(STM_PCI_DEVICE_PATH_NODE)
|
||||
* (resource->pci_cfg.last_node_index + 1))
|
||||
!= 0) {
|
||||
return false;
|
||||
}
|
||||
resource_lo = (uint64_t)resource->pci_cfg.base;
|
||||
resource_hi =
|
||||
(uint64_t)resource->pci_cfg.base + (uint64_t)resource->pci_cfg.length;
|
||||
record_lo = (uint64_t)record->pci_cfg.base;
|
||||
record_hi = (uint64_t)record->pci_cfg.base + (uint64_t)record->pci_cfg.length;
|
||||
if (resource->pci_cfg.rw_attributes != record->pci_cfg.rw_attributes) {
|
||||
if ((resource_lo == record_lo) && (resource_hi == record_hi)) {
|
||||
record->pci_cfg.rw_attributes = resource->pci_cfg.rw_attributes
|
||||
| record->pci_cfg.rw_attributes;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MACHINE_SPECIFIC_REG:
|
||||
|
||||
// Special case - merge MSR masks in place.
|
||||
if (resource->msr.msr_index != record->msr.msr_index)
|
||||
return false;
|
||||
record->msr.read_mask |= resource->msr.read_mask;
|
||||
record->msr.write_mask |= resource->msr.write_mask;
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
// If resources are disjoint
|
||||
if ((resource_hi < record_lo) || (resource_lo > record_hi))
|
||||
return false;
|
||||
|
||||
// If resource is consumed by record.
|
||||
if ((resource_lo >= record_lo) && (resource_hi <= record_hi))
|
||||
return true;
|
||||
|
||||
// Resources are overlapping.
|
||||
// Resource and record are merged.
|
||||
resource_lo = (resource_lo < record_lo) ? resource_lo : record_lo;
|
||||
resource_hi = (resource_hi > record_hi) ? resource_hi : record_hi;
|
||||
|
||||
switch (resource->header.rsc_type) {
|
||||
case MEM_RANGE:
|
||||
case MMIO_RANGE:
|
||||
record->mem.base = resource_lo;
|
||||
record->mem.length = resource_hi - resource_lo;
|
||||
break;
|
||||
case IO_RANGE:
|
||||
case TRAPPED_IO_RANGE:
|
||||
record->io.base = (uint64_t)resource_lo;
|
||||
record->io.length = (uint64_t)(resource_hi - resource_lo);
|
||||
break;
|
||||
case PCI_CFG_RANGE:
|
||||
record->pci_cfg.base = (uint64_t)resource_lo;
|
||||
record->pci_cfg.length = (uint64_t)(resource_hi - resource_lo);
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add resource node.
|
||||
*
|
||||
* @param Resource A pointer to resource node to be added
|
||||
*/
|
||||
static void add_single_resource(STM_RSC *resource)
|
||||
{
|
||||
STM_RSC *record;
|
||||
|
||||
record = (STM_RSC *)m_stm_resources_ptr;
|
||||
|
||||
while (true) {
|
||||
if (record->header.rsc_type == END_OF_RESOURCES)
|
||||
break;
|
||||
|
||||
// Go to next record if resource and record types don't match.
|
||||
if (resource->header.rsc_type != record->header.rsc_type) {
|
||||
record = (STM_RSC *)((void *)record + record->header.length);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Record is handled inside of procedure - don't adjust.
|
||||
if (handle_single_resource(resource, record))
|
||||
return;
|
||||
record = (STM_RSC *)((void *)record + record->header.length);
|
||||
}
|
||||
|
||||
// Add resource to the end of area.
|
||||
memcpy(m_stm_resources_ptr + m_stm_resource_size_used - sizeof(m_rsc_end_node),
|
||||
resource, resource->header.length);
|
||||
memcpy(m_stm_resources_ptr + m_stm_resource_size_used - sizeof(m_rsc_end_node)
|
||||
+ resource->header.length,
|
||||
&m_rsc_end_node, sizeof(m_rsc_end_node));
|
||||
m_stm_resource_size_used += resource->header.length;
|
||||
m_stm_resource_size_available = m_stm_resource_total_size - m_stm_resource_size_used;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add resource list.
|
||||
*
|
||||
* @param resource_list A pointer to resource list to be added
|
||||
* @param num_entries Optional number of entries.
|
||||
* If 0, list must be terminated by END_OF_RESOURCES.
|
||||
*/
|
||||
static void add_resource(STM_RSC *resource_list, uint32_t num_entries)
|
||||
{
|
||||
uint32_t count;
|
||||
uint32_t index;
|
||||
STM_RSC *resource;
|
||||
|
||||
if (num_entries == 0)
|
||||
count = 0xFFFFFFFF;
|
||||
else
|
||||
count = num_entries;
|
||||
|
||||
resource = resource_list;
|
||||
|
||||
for (index = 0; index < count; index++) {
|
||||
if (resource->header.rsc_type == END_OF_RESOURCES)
|
||||
return;
|
||||
add_single_resource(resource);
|
||||
resource = (STM_RSC *)((void *)resource + resource->header.length);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Validate resource list.
|
||||
*
|
||||
* @param resource_list A pointer to resource list to be added
|
||||
* @param num_entries Optional number of entries.
|
||||
* If 0, list must be terminated by END_OF_RESOURCES.
|
||||
*
|
||||
* @retval true resource valid
|
||||
* @retval false resource invalid
|
||||
*/
|
||||
static bool validate_resource(STM_RSC *resource_list, uint32_t num_entries)
|
||||
{
|
||||
uint32_t count;
|
||||
uint32_t index;
|
||||
STM_RSC *resource;
|
||||
uint32_t sub_index;
|
||||
|
||||
// If NumEntries == 0 make it very big. Scan will be terminated by
|
||||
// END_OF_RESOURCES.
|
||||
if (num_entries == 0)
|
||||
count = 0xFFFFFFFF;
|
||||
else
|
||||
count = num_entries;
|
||||
|
||||
// Start from beginning of resource list.
|
||||
resource = resource_list;
|
||||
|
||||
for (index = 0; index < count; index++) {
|
||||
printk(BIOS_DEBUG, "STM: %s (%u) - RscType(%x) length(0x%x)\n", __func__, index,
|
||||
resource->header.rsc_type, resource->header.length);
|
||||
// Validate resource.
|
||||
switch (resource->header.rsc_type) {
|
||||
case END_OF_RESOURCES:
|
||||
if (resource->header.length != sizeof(STM_RSC_END))
|
||||
return false;
|
||||
|
||||
// If we are passed actual number of resources to add,
|
||||
// END_OF_RESOURCES structure between them is considered
|
||||
// an error. If NumEntries == 0 END_OF_RESOURCES is a
|
||||
// termination.
|
||||
if (num_entries != 0)
|
||||
return false;
|
||||
|
||||
// If NumEntries == 0 and list reached end - return
|
||||
// success.
|
||||
return true;
|
||||
|
||||
case MEM_RANGE:
|
||||
case MMIO_RANGE:
|
||||
printk(BIOS_DEBUG, "STM: %s - MEM (0x%0llx, 0x%0llx)\n", __func__,
|
||||
resource->mem.base, resource->mem.length);
|
||||
|
||||
if (resource->header.length != sizeof(STM_RSC_MEM_DESC))
|
||||
return false;
|
||||
|
||||
if (resource->mem.rwx_attributes > FULL_ACCS)
|
||||
return false;
|
||||
break;
|
||||
|
||||
case IO_RANGE:
|
||||
case TRAPPED_IO_RANGE:
|
||||
if (resource->header.length != sizeof(STM_RSC_IO_DESC))
|
||||
return false;
|
||||
|
||||
if ((resource->io.base + resource->io.length) > 0xFFFF)
|
||||
return false;
|
||||
break;
|
||||
|
||||
case PCI_CFG_RANGE:
|
||||
printk(BIOS_DEBUG, "STM: %s - PCI (0x%02x, 0x%08x, 0x%02x, 0x%02x)\n",
|
||||
__func__, resource->pci_cfg.originating_bus_number,
|
||||
resource->pci_cfg.last_node_index,
|
||||
resource->pci_cfg.pci_device_path[0].pci_device,
|
||||
resource->pci_cfg.pci_device_path[0].pci_function);
|
||||
if (resource->header.length
|
||||
!= sizeof(STM_RSC_PCI_CFG_DESC)
|
||||
+ (sizeof(STM_PCI_DEVICE_PATH_NODE)
|
||||
* resource->pci_cfg.last_node_index))
|
||||
return false;
|
||||
for (sub_index = 0; sub_index <= resource->pci_cfg.last_node_index;
|
||||
sub_index++) {
|
||||
if ((resource->pci_cfg.pci_device_path[sub_index].pci_device
|
||||
> 0x1F)
|
||||
|| (resource->pci_cfg.pci_device_path[sub_index]
|
||||
.pci_function
|
||||
> 7))
|
||||
return false;
|
||||
}
|
||||
if ((resource->pci_cfg.base + resource->pci_cfg.length) > 0x1000)
|
||||
return false;
|
||||
break;
|
||||
|
||||
case MACHINE_SPECIFIC_REG:
|
||||
if (resource->header.length != sizeof(STM_RSC_MSR_DESC))
|
||||
return false;
|
||||
break;
|
||||
|
||||
default:
|
||||
printk(BIOS_DEBUG, "STM: %s - Unknown RscType(%x)\n", __func__,
|
||||
resource->header.rsc_type);
|
||||
return false;
|
||||
}
|
||||
resource = (STM_RSC *)((void *)resource + resource->header.length);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get resource list.
|
||||
* EndResource is excluded.
|
||||
*
|
||||
* @param resou rce_list A pointer to resource list to be added
|
||||
* @param num_entries Optional number of entries.
|
||||
* If 0, list must be terminated by END_OF_RESOURCES.
|
||||
*
|
||||
* @retval true resource valid
|
||||
* @retval false resource invalid
|
||||
*/
|
||||
static uint32_t get_resource_size(STM_RSC *resource_list, uint32_t num_entries)
|
||||
{
|
||||
uint32_t count;
|
||||
uint32_t index;
|
||||
STM_RSC *resource;
|
||||
|
||||
resource = resource_list;
|
||||
|
||||
// If NumEntries == 0 make it very big. Scan will be terminated by
|
||||
// END_OF_RESOURCES.
|
||||
if (num_entries == 0)
|
||||
count = 0xFFFFFFFF;
|
||||
else
|
||||
count = num_entries;
|
||||
|
||||
// Start from beginning of resource list.
|
||||
resource = resource_list;
|
||||
|
||||
for (index = 0; index < count; index++) {
|
||||
if (resource->header.rsc_type == END_OF_RESOURCES)
|
||||
break;
|
||||
resource = (STM_RSC *)((void *)resource + resource->header.length);
|
||||
}
|
||||
return (uint32_t)((uint32_t)resource - (uint32_t)resource_list);
|
||||
}
|
||||
|
||||
/*
|
||||
* Add resources in list to database. Allocate new memory areas as needed.
|
||||
*
|
||||
* @param resource_list A pointer to resource list to be added
|
||||
* @param num_entries Optional number of entries.
|
||||
* If 0, list must be terminated by END_OF_RESOURCES.
|
||||
*
|
||||
* @retval SUCCESS If resources are added
|
||||
* @retval INVALID_PARAMETER If nested procedure detected resource failure
|
||||
* @retval OUT_OF_RESOURCES If nested procedure returned it and we cannot
|
||||
* allocate more areas.
|
||||
*/
|
||||
int add_pi_resource(STM_RSC *resource_list, uint32_t num_entries)
|
||||
{
|
||||
size_t resource_size;
|
||||
|
||||
printk(BIOS_DEBUG, "STM: %s - Enter\n", __func__);
|
||||
|
||||
if (!validate_resource(resource_list, num_entries))
|
||||
return -1; // INVALID_PARAMETER;
|
||||
|
||||
resource_size = get_resource_size(resource_list, num_entries);
|
||||
printk(BIOS_DEBUG, "STM: ResourceSize - 0x%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;
|
||||
}
|
||||
120
src/security/intel/stm/SmmStm.h
Normal file
120
src/security/intel/stm/SmmStm.h
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
/* @file
|
||||
* SMM STM support
|
||||
*
|
||||
* Copyright (c) 2015 - 2016, Intel Corporation. All rights reserved.
|
||||
* This program and the accompanying materials are licensed and made
|
||||
* available under the terms and conditions of the BSD License which
|
||||
* accompanies this distribution. The full text of the license may
|
||||
* be found at http://opensource.org/licenses/bsd-license.php.
|
||||
*
|
||||
* THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _SMM_STM_H_
|
||||
#define _SMM_STM_H_
|
||||
|
||||
#include <cpu/x86/msr.h>
|
||||
#include "StmApi.h"
|
||||
|
||||
/*
|
||||
* Load STM image.
|
||||
*
|
||||
* @retval SUCCESS STM is loaded to MSEG
|
||||
* @retval BUFFER_TOO_SMALL MSEG is too small
|
||||
* @retval UNSUPPORTED MSEG is not enabled
|
||||
*/
|
||||
int load_stm_image(uintptr_t mseg);
|
||||
|
||||
void stm_setup(
|
||||
uintptr_t mseg, int cpu, int num_cpus, uintptr_t smbase,
|
||||
uintptr_t smbase_base, uint32_t offset32);
|
||||
|
||||
/*
|
||||
* Add resources in list to database. Allocate new memory areas as needed.
|
||||
*
|
||||
* @param resource_list A pointer to resource list to be added
|
||||
* @param num_entries Optional number of entries.
|
||||
* If 0, list must be terminated by END_OF_RESOURCES.
|
||||
*
|
||||
* @retval SUCCESS If resources are added
|
||||
* @retval INVALID_PARAMETER If nested procedure detected resource failure
|
||||
* @retval OUT_OF_RESOURCES If nested procedure returned it and we cannot
|
||||
* allocate more areas.
|
||||
*/
|
||||
int add_pi_resource(STM_RSC *resource_list, uint32_t num_entries);
|
||||
|
||||
/*
|
||||
* Delete resources in list to database.
|
||||
*
|
||||
* @param resource_list A pointer to resource list to be deleted
|
||||
* NULL means delete all resources.
|
||||
* @param num_entries Optional number of entries.
|
||||
* If 0, list must be terminated by END_OF_RESOURCES.
|
||||
*
|
||||
* @retval SUCCESS If resources are deleted
|
||||
* @retval NVALID_PARAMETER If nested procedure detected resource fail
|
||||
*/
|
||||
int delete_pi_resource(STM_RSC *resource_list, uint32_t num_entries);
|
||||
|
||||
/*
|
||||
* Get BIOS resources.
|
||||
*
|
||||
* @param resource_list A pointer to resource list to be filled
|
||||
* @param resource_size On input it means size of resource list input.
|
||||
* On output it means size of resource list filled,
|
||||
* or the size of resource list to be filled if
|
||||
* size is too small.
|
||||
*
|
||||
* @retval SUCCESS If resources are returned.
|
||||
* @retval BUFFER_TOO_SMALL If resource list buffer is too small to
|
||||
* hold the whole resources.
|
||||
*/
|
||||
int get_pi_resource(STM_RSC *resource_list, uint32_t *resource_size);
|
||||
|
||||
/*
|
||||
* This function notifies the STM of a resource change.
|
||||
*
|
||||
* @param stm_resource BIOS STM resource
|
||||
*/
|
||||
void notify_stm_resource_change(void *stm_resource);
|
||||
|
||||
/*
|
||||
* This function returns the pointer to the STM BIOS resource list.
|
||||
*
|
||||
* @return BIOS STM resource
|
||||
*/
|
||||
void *get_stm_resource(void);
|
||||
|
||||
void setup_smm_descriptor(void *smbase, void *base_smbase, int32_t apic_id,
|
||||
int32_t entry32_off);
|
||||
|
||||
/*
|
||||
* Check STM image size.
|
||||
*
|
||||
* @param stm_image STM image
|
||||
* @param stm_image_size STM image size
|
||||
*
|
||||
* @retval true check pass
|
||||
* @retval false check fail
|
||||
*/
|
||||
bool stm_check_stm_image(void *stm_image, uint32_t stm_image_size);
|
||||
|
||||
/*
|
||||
* Create 4G page table for STM.
|
||||
* 4M Non-PAE page table in IA32 version.
|
||||
*
|
||||
* @param page_table_base The page table base in MSEG
|
||||
*/
|
||||
void stm_gen_4g_pagetable_ia32(uint32_t pagetable_base);
|
||||
|
||||
/*
|
||||
* Create 4G page table for STM.
|
||||
* 2M PAE page table in X64 version.
|
||||
*
|
||||
* @param pagetable_base The page table base in MSEG
|
||||
*/
|
||||
void stm_gen_4g_pagetable_x64(uint32_t pagetable_base);
|
||||
|
||||
#endif
|
||||
726
src/security/intel/stm/StmApi.h
Normal file
726
src/security/intel/stm/StmApi.h
Normal file
|
|
@ -0,0 +1,726 @@
|
|||
/* @file
|
||||
* STM API definition
|
||||
*
|
||||
* Copyright (c) 2015, Intel Corporation. All rights reserved.
|
||||
* This program and the accompanying materials are licensed and made available
|
||||
* under the terms and conditions of the BSD License which accompanies this
|
||||
* distribution. The full text of the license may be found at
|
||||
* http://opensource.org/licenses/bsd-license.php.
|
||||
*
|
||||
* THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND,
|
||||
* EITHER EXPRESS OR IMPLIED.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _STM_API_H_
|
||||
#define _STM_API_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// definition in STM spec
|
||||
|
||||
#define STM_SPEC_VERSION_MAJOR 1
|
||||
#define STM_SPEC_VERSION_MINOR 0
|
||||
|
||||
#pragma pack(push, 1)
|
||||
|
||||
#define STM_HARDWARE_FIELD_FILL_TO_2K (2048 - sizeof(uint32_t) * 8)
|
||||
typedef struct {
|
||||
uint32_t stm_header_revision;
|
||||
uint32_t monitor_features;
|
||||
uint32_t gdtr_limit;
|
||||
uint32_t gdtr_base_offset;
|
||||
uint32_t cs_selector;
|
||||
uint32_t eip_offset;
|
||||
uint32_t esp_offset;
|
||||
uint32_t cr3_offset;
|
||||
uint8_t reserved[STM_HARDWARE_FIELD_FILL_TO_2K];
|
||||
} HARDWARE_STM_HEADER;
|
||||
|
||||
#define STM_FEATURES_IA32E 0x1
|
||||
|
||||
typedef struct {
|
||||
uint32_t intel_64mode_supported : 1;
|
||||
uint32_t ept_supported : 1;
|
||||
uint32_t mbz : 30;
|
||||
} STM_FEAT;
|
||||
|
||||
typedef struct {
|
||||
uint8_t stm_spec_ver_major;
|
||||
uint8_t stm_pec_ver_minor;
|
||||
uint16_t mbz;
|
||||
uint32_t static_image_size;
|
||||
uint32_t per_proc_dynamic_memory_size;
|
||||
uint32_t additional_dynamic_memory_size;
|
||||
STM_FEAT stm_features;
|
||||
uint32_t number_of_rev_ids;
|
||||
uint32_t stm_smm_rev_id[1];
|
||||
|
||||
// The total STM_HEADER should be 4K.
|
||||
} SOFTWARE_STM_HEADER;
|
||||
|
||||
typedef struct {
|
||||
HARDWARE_STM_HEADER hw_stm_hdr;
|
||||
SOFTWARE_STM_HEADER sw_stm_hdr;
|
||||
} STM_HEADER;
|
||||
|
||||
#define SHA1 1
|
||||
#define SHA256 2
|
||||
typedef struct {
|
||||
uint64_t bios_component_base;
|
||||
uint32_t image_size;
|
||||
uint32_t hash_algorithm; // SHA1 or SHA256
|
||||
uint8_t hash[32];
|
||||
} TXT_BIOS_COMPONENT_STATUS;
|
||||
|
||||
#define PAGE_SIZE 4096
|
||||
typedef struct {
|
||||
uint32_t image_size;
|
||||
uint32_t reserved;
|
||||
uint64_t image_page_base[1]; //[NumberOfPages];
|
||||
} TXT_BIOS_COMPONENT_UPDATE;
|
||||
|
||||
typedef struct {
|
||||
uint64_t spe_rip;
|
||||
uint64_t spe_rsp;
|
||||
uint16_t spe_ss;
|
||||
uint16_t page_violation_exception : 1;
|
||||
uint16_t msr_violation_exception : 1;
|
||||
uint16_t register_violation_exception : 1;
|
||||
uint16_t io_violation_exception : 1;
|
||||
uint16_t pci_violation_exception : 1;
|
||||
uint16_t reserved1 : 11;
|
||||
uint32_t reserved2;
|
||||
} STM_PROTECTION_EXCEPTION_HANDLER;
|
||||
|
||||
typedef struct {
|
||||
uint8_t execution_disable_outside_smrr : 1;
|
||||
uint8_t intel_64mode : 1;
|
||||
uint8_t cr4_pae : 1;
|
||||
uint8_t cr4_pse : 1;
|
||||
uint8_t reserved1 : 4;
|
||||
} STM_SMM_ENTRY_STATE;
|
||||
|
||||
typedef struct {
|
||||
uint8_t smram_to_vmcs_restore_required : 1; // BIOS restore hint
|
||||
uint8_t reinitialize_vmcs_required : 1; // BIOS request
|
||||
uint8_t reserved2 : 6;
|
||||
} STM_SMM_RESUME_STATE;
|
||||
|
||||
typedef struct {
|
||||
uint8_t domain_type : 4; // STM input to BIOS on each SM
|
||||
uint8_t x_state_policy : 2; // STM input to BIOS on each SMI
|
||||
uint8_t ept_enabled : 1;
|
||||
uint8_t reserved3 : 1;
|
||||
} STM_SMM_STATE;
|
||||
|
||||
typedef struct {
|
||||
uint64_t signature;
|
||||
uint16_t size;
|
||||
uint8_t smm_descriptor_ver_major;
|
||||
uint8_t smm_descriptor_ver_minor;
|
||||
uint32_t local_apic_id;
|
||||
STM_SMM_ENTRY_STATE smm_entry_state;
|
||||
STM_SMM_RESUME_STATE smm_resume_state;
|
||||
STM_SMM_STATE stm_smm_state;
|
||||
uint8_t reserved4;
|
||||
uint16_t smm_cs;
|
||||
uint16_t smm_ds;
|
||||
uint16_t smm_ss;
|
||||
uint16_t smm_other_segment;
|
||||
uint16_t smm_tr;
|
||||
uint16_t reserved5;
|
||||
uint64_t smm_cr3;
|
||||
uint64_t smm_stm_setup_rip;
|
||||
uint64_t smm_stm_teardown_rip;
|
||||
uint64_t smm_smi_handler_rip;
|
||||
uint64_t smm_smi_handler_rsp;
|
||||
uint64_t smm_gdt_ptr;
|
||||
uint32_t smm_gdt_size;
|
||||
uint32_t required_stm_smm_rev_id;
|
||||
STM_PROTECTION_EXCEPTION_HANDLER stm_protection_exception_handler;
|
||||
uint64_t reserved6;
|
||||
uint64_t bios_hw_resource_requirements_ptr;
|
||||
// extend area
|
||||
uint64_t acpi_rsdp;
|
||||
uint8_t physical_address_bits;
|
||||
} TXT_PROCESSOR_SMM_DESCRIPTOR;
|
||||
|
||||
#define TXT_PROCESSOR_SMM_DESCRIPTOR_SIGNATURE "TXTPSSIG"
|
||||
#define TXT_PROCESSOR_SMM_DESCRIPTOR_VERSION_MAJOR 1
|
||||
#define TXT_PROCESSOR_SMM_DESCRIPTOR_VERSION_MINOR 0
|
||||
|
||||
#define SMM_PSD_OFFSET 0xfb00
|
||||
|
||||
typedef enum {
|
||||
TxtSmmPageViolation = 1,
|
||||
TxtSmmMsrViolation,
|
||||
TxtSmmRegisterViolation,
|
||||
TxtSmmIoViolation,
|
||||
TxtSmmPciViolation
|
||||
} TXT_SMM_PROTECTION_EXCEPTION_TYPE;
|
||||
|
||||
typedef struct {
|
||||
uint32_t rdi;
|
||||
uint32_t rsi;
|
||||
uint32_t rbp;
|
||||
uint32_t rdx;
|
||||
uint32_t rcx;
|
||||
uint32_t rbx;
|
||||
uint32_t rax;
|
||||
uint32_t cr3;
|
||||
uint32_t cr2;
|
||||
uint32_t cr0;
|
||||
uint32_t vmcs_exit_instruction_info;
|
||||
uint32_t vmcs_exit_instruction_length;
|
||||
uint64_t vmcs_exit_qualification;
|
||||
uint32_t error_code; // TXT_SMM_PROTECTION_EXCEPTION_TYPE
|
||||
uint32_t rip;
|
||||
uint32_t cs;
|
||||
uint32_t rflags;
|
||||
uint32_t rsp;
|
||||
uint32_t ss;
|
||||
} STM_PROTECTION_EXCEPTION_STACK_FRAME_IA32;
|
||||
|
||||
typedef struct {
|
||||
uint64_t r15;
|
||||
uint64_t r14;
|
||||
uint64_t r13;
|
||||
uint64_t r12;
|
||||
uint64_t r11;
|
||||
uint64_t r10;
|
||||
uint64_t r9;
|
||||
uint64_t r8;
|
||||
uint64_t rdi;
|
||||
uint64_t rsi;
|
||||
uint64_t rbp;
|
||||
uint64_t rdx;
|
||||
uint64_t rcx;
|
||||
uint64_t rbx;
|
||||
uint64_t rax;
|
||||
uint64_t cr8;
|
||||
uint64_t cr3;
|
||||
uint64_t cr2;
|
||||
uint64_t cr0;
|
||||
uint64_t vmcs_exit_instruction_info;
|
||||
uint64_t vmcs_exit_instruction_length;
|
||||
uint64_t vmcs_exit_qualification;
|
||||
uint64_t error_code; // TXT_SMM_PROTECTION_EXCEPTION_TYPE
|
||||
uint64_t rip;
|
||||
uint64_t cs;
|
||||
uint64_t rflags;
|
||||
uint64_t rsp;
|
||||
uint64_t ss;
|
||||
} STM_PROTECTION_EXCEPTION_STACK_FRAME_X64;
|
||||
|
||||
typedef union {
|
||||
STM_PROTECTION_EXCEPTION_STACK_FRAME_IA32 *ia32_stack_frame;
|
||||
STM_PROTECTION_EXCEPTION_STACK_FRAME_X64 *x64_stack_frame;
|
||||
} STM_PROTECTION_EXCEPTION_STACK_FRAME;
|
||||
|
||||
#define STM_SMM_REV_ID 0x80010100
|
||||
|
||||
typedef struct _STM_SMM_CPU_STATE { // Writable?
|
||||
uint8_t reserved1[0x1d0]; // fc00h
|
||||
uint32_t gdt_base_hi_dword; // fdd0h : NO
|
||||
uint32_t ldt_base_hi_dword; // fdd4h : NO
|
||||
uint32_t idt_base_hi_dword; // fdd8h : NO
|
||||
uint8_t reserved2[0x4]; // fddch
|
||||
uint64_t io_rdi; // fde0h : NO
|
||||
// - restricted
|
||||
uint64_t io_eip; // fde8h : YES
|
||||
uint64_t io_rcx; // fdf0h : NO
|
||||
// - restricted
|
||||
uint64_t io_rsi; // fdf8h : NO
|
||||
// - restricted
|
||||
uint8_t reserved3[0x40]; // fe00h
|
||||
uint32_t cr4; // fe40h : NO
|
||||
uint8_t reserved4[0x48]; // fe44h
|
||||
uint32_t gdt_base_lo_dword; // fe8ch : NO
|
||||
uint32_t gdt_limit; // fe90h : NO
|
||||
// - RESTRICTED
|
||||
uint32_t idt_base_lo_dword; // fe94h : NO
|
||||
uint32_t idt_limit; // fe98h : NO
|
||||
// - RESTRICTED
|
||||
uint32_t ldt_base_lo_dword; // fe9ch : NO
|
||||
uint32_t ldt_limit; // fea0h : NO
|
||||
// - RESTRICTED
|
||||
uint32_t ldt_info; // fea4h : NO
|
||||
// - RESTRICTED
|
||||
uint8_t reserved5[0x30]; // fea8h
|
||||
uint64_t eptp; // fed8h : NO
|
||||
uint32_t enabled_ept; // fee0h : NO
|
||||
uint8_t reserved6[0x14]; // fee4h
|
||||
uint32_t smbase; // fef8h : YES
|
||||
// - NO for STM
|
||||
uint32_t smm_rev_id; // fefch : NO
|
||||
uint16_t io_restart; // ff00h : YES
|
||||
uint16_t auto_halt_restart; // ff02h : YES
|
||||
uint8_t reserved7[0x18]; // ff04h
|
||||
uint64_t r15; // ff1ch : YES
|
||||
uint64_t r14; // ff24h : YES
|
||||
uint64_t r13; // ff2ch : YES
|
||||
uint64_t r12; // ff34h : YES
|
||||
uint64_t r11; // ff3ch : YES
|
||||
uint64_t r10; // ff44h : YES
|
||||
uint64_t r9; // ff4ch : YES
|
||||
uint64_t r8; // ff54h : YES
|
||||
uint64_t rax; // ff5ch : YES
|
||||
uint64_t rcx; // ff64h : YES
|
||||
uint64_t rdx; // ff6ch : YES
|
||||
uint64_t rbx; // ff74h : YES
|
||||
uint64_t rsp; // ff7ch : YES
|
||||
uint64_t rbp; // ff84h : YES
|
||||
uint64_t rsi; // ff8ch : YES
|
||||
uint64_t rdi; // ff94h : YES
|
||||
uint64_t io_mem_addr; // ff9ch : NO
|
||||
uint32_t io_misc; // ffa4h : NO
|
||||
uint32_t es; // ffa8h : NO
|
||||
uint32_t cs; // ffach : NO
|
||||
uint32_t ss; // ffb0h : NO
|
||||
uint32_t ds; // ffb4h : NO
|
||||
uint32_t fs; // ffb8h : NO
|
||||
uint32_t gs; // ffbch : NO
|
||||
uint32_t ldtr; // ffc0h : NO
|
||||
uint32_t tr; // ffc4h : NO
|
||||
uint64_t dr7; // ffc8h : NO
|
||||
uint64_t dr6; // ffd0h : NO
|
||||
uint64_t rip; // ffd8h : YES
|
||||
uint64_t ia32_efer; // ffe0h : YES
|
||||
// - NO for STM
|
||||
uint64_t rflags; // ffe8h : YES
|
||||
uint64_t cr3; // fff0h : NO
|
||||
uint64_t cr0; // fff8h : NO
|
||||
} STM_SMM_CPU_STATE;
|
||||
|
||||
// STM Mapping
|
||||
typedef struct {
|
||||
uint64_t physical_address;
|
||||
uint64_t virtual_ddress;
|
||||
uint32_t Page_count;
|
||||
uint32_t Pat_cache_type;
|
||||
} STM_MAP_ADDRESS_RANGE_DESCRIPTOR;
|
||||
|
||||
#define ST_UC 0x00
|
||||
#define WC 0x01
|
||||
#define WT 0x04
|
||||
#define WP 0x05
|
||||
#define WB 0x06
|
||||
#define UC 0x07
|
||||
#define FOLLOW_MTRR 0xFFFFFFFF
|
||||
|
||||
typedef struct {
|
||||
uint64_t virtual_address;
|
||||
uint32_t length;
|
||||
} STM_UNMAP_ADDRESS_RANGE_DESCRIPTOR;
|
||||
|
||||
typedef struct {
|
||||
uint64_t interrupted_guest_virtual_address;
|
||||
uint32_t length;
|
||||
uint64_t interrupted_cr3;
|
||||
uint64_t interrupted_eptp;
|
||||
uint32_t map_to_smm_guest : 2;
|
||||
uint32_t interrupted_cr4_pae : 1;
|
||||
uint32_t interrupted_cr4_pse : 1;
|
||||
uint32_t interrupted_ia32e_mode : 1;
|
||||
uint32_t reserved1 : 27;
|
||||
uint32_t reserved2;
|
||||
uint64_t physical_address;
|
||||
uint64_t smm_guest_virtual_address;
|
||||
} STM_ADDRESS_LOOKUP_DESCRIPTOR;
|
||||
|
||||
#define DO_NOT_MAP 0
|
||||
#define ONE_TO_ONE 1
|
||||
#define VIRTUAL_ADDRESS_SPECIFIED 3
|
||||
|
||||
// STM_RESOURCE_LIST
|
||||
#define END_OF_RESOURCES 0
|
||||
#define MEM_RANGE 1
|
||||
#define IO_RANGE 2
|
||||
#define MMIO_RANGE 3
|
||||
#define MACHINE_SPECIFIC_REG 4
|
||||
#define PCI_CFG_RANGE 5
|
||||
#define TRAPPED_IO_RANGE 6
|
||||
#define ALL_RESOURCES 7
|
||||
#define REGISTER_VIOLATION 8
|
||||
#define MAX_DESC_TYPE 8
|
||||
|
||||
typedef struct {
|
||||
uint32_t rsc_type;
|
||||
uint16_t length;
|
||||
uint16_t return_status : 1;
|
||||
uint16_t reserved : 14;
|
||||
uint16_t ignore_resource : 1;
|
||||
} STM_RSC_DESC_HEADER;
|
||||
|
||||
typedef struct {
|
||||
STM_RSC_DESC_HEADER Hdr;
|
||||
uint64_t resource_list_continuation;
|
||||
} STM_RSC_END;
|
||||
|
||||
// byte granular Memory range support
|
||||
#define STM_RSC_BGM 0x4
|
||||
|
||||
typedef struct {
|
||||
STM_RSC_DESC_HEADER hdr;
|
||||
uint64_t base;
|
||||
uint64_t length;
|
||||
uint32_t rwx_attributes : 3;
|
||||
uint32_t reserved : 29;
|
||||
uint32_t reserved_2;
|
||||
} STM_RSC_MEM_DESC;
|
||||
|
||||
#define STM_RSC_MEM_R 0x1
|
||||
#define STM_RSC_MEM_W 0x2
|
||||
#define STM_RSC_MEM_X 0x4
|
||||
|
||||
typedef struct {
|
||||
STM_RSC_DESC_HEADER hdr;
|
||||
uint16_t base;
|
||||
uint16_t length;
|
||||
uint32_t reserved;
|
||||
} STM_RSC_IO_DESC;
|
||||
|
||||
// byte granular MMIO range support
|
||||
#define STM_RSC_BGI 0x2
|
||||
|
||||
typedef struct {
|
||||
STM_RSC_DESC_HEADER hdr;
|
||||
uint64_t base;
|
||||
uint64_t length;
|
||||
uint32_t rwx_attributes : 3;
|
||||
uint32_t reserved : 29;
|
||||
uint32_t reserved_2;
|
||||
} STM_RSC_MMIO_DESC;
|
||||
|
||||
#define STM_RSC_MMIO_R 0x1
|
||||
#define STM_RSC_MMIO_W 0x2
|
||||
#define STM_RSC_MMIO_X 0x4
|
||||
|
||||
typedef struct {
|
||||
STM_RSC_DESC_HEADER hdr;
|
||||
uint32_t msr_index;
|
||||
uint32_t kernel_mode_processing : 1;
|
||||
uint32_t reserved : 31;
|
||||
uint64_t read_mask;
|
||||
uint64_t write_mask;
|
||||
} STM_RSC_MSR_DESC;
|
||||
|
||||
// bit granular MSR resource support
|
||||
#define STM_RSC_MSR 0x8
|
||||
|
||||
typedef struct {
|
||||
uint8_t type; // must be 1, indicating Hardware Device Path
|
||||
uint8_t subtype; // must be 1, indicating PCI
|
||||
uint16_t length; // sizeof(STM_PCI_DEVICE_PATH_NODE) which is 6
|
||||
uint8_t pci_function;
|
||||
uint8_t pci_device;
|
||||
} STM_PCI_DEVICE_PATH_NODE;
|
||||
|
||||
typedef struct {
|
||||
STM_RSC_DESC_HEADER hdr;
|
||||
uint16_t rw_attributes : 2;
|
||||
uint16_t reserved : 14;
|
||||
uint16_t base;
|
||||
uint16_t length;
|
||||
uint8_t originating_bus_number;
|
||||
uint8_t last_node_index;
|
||||
STM_PCI_DEVICE_PATH_NODE pci_device_path[1];
|
||||
// STM_PCI_DEVICE_PATH_NODE PciDevicePath[LastNodeIndex + 1];
|
||||
} STM_RSC_PCI_CFG_DESC;
|
||||
|
||||
#define STM_RSC_PCI_CFG_R 0x1
|
||||
#define STM_RSC_PCI_CFG_W 0x2
|
||||
|
||||
typedef struct {
|
||||
STM_RSC_DESC_HEADER hdr;
|
||||
uint16_t base;
|
||||
uint16_t length;
|
||||
uint16_t in : 1;
|
||||
uint16_t out : 1;
|
||||
uint16_t api : 1;
|
||||
uint16_t reserved1 : 13;
|
||||
uint16_t reserved2;
|
||||
} STM_RSC_TRAPPED_IO_DESC;
|
||||
|
||||
typedef struct {
|
||||
STM_RSC_DESC_HEADER hdr;
|
||||
} STM_RSC_ALL_RESOURCES_DESC;
|
||||
|
||||
typedef struct {
|
||||
STM_RSC_DESC_HEADER hdr;
|
||||
uint32_t register_type;
|
||||
uint32_t reserved;
|
||||
uint64_t readMask;
|
||||
uint64_t write_mask;
|
||||
} STM_REGISTER_VIOLATION_DESC;
|
||||
|
||||
typedef enum {
|
||||
stm_register_cr0,
|
||||
stm_register_cr2,
|
||||
stm_register_cr3,
|
||||
stm_register_cr4,
|
||||
stm_register_cr8,
|
||||
stm_register_max,
|
||||
} STM_REGISTER_VIOLATION_TYPE;
|
||||
|
||||
typedef union {
|
||||
STM_RSC_DESC_HEADER header;
|
||||
STM_RSC_END end;
|
||||
STM_RSC_MEM_DESC mem;
|
||||
STM_RSC_IO_DESC io;
|
||||
STM_RSC_MMIO_DESC mmio;
|
||||
STM_RSC_MSR_DESC msr;
|
||||
STM_RSC_PCI_CFG_DESC pci_cfg;
|
||||
STM_RSC_TRAPPED_IO_DESC trapped_io;
|
||||
STM_RSC_ALL_RESOURCES_DESC all;
|
||||
STM_REGISTER_VIOLATION_DESC register_violation;
|
||||
} STM_RSC;
|
||||
|
||||
// VMCS database
|
||||
#define STM_VMCS_DATABASE_REQUEST_ADD 1
|
||||
#define STM_VMCS_DATABASE_REQUEST_REMOVE 0
|
||||
|
||||
// Values for DomainType
|
||||
// Interpreter of DomainType
|
||||
#define DOMAIN_DISALLOWED_IO_OUT (1u << 0)
|
||||
#define DOMAIN_DISALLOWED_IO_IN (1u << 1)
|
||||
#define DOMAIN_INTEGRITY (1u << 2)
|
||||
#define DOMAIN_CONFIDENTIALITY (1u << 3)
|
||||
|
||||
#define DOMAIN_UNPROTECTED 0x00
|
||||
#define DOMAIN_INTEGRITY_PROT_OUT_IN (DOMAIN_INTEGRITY)
|
||||
#define DOMAIN_FULLY_PROT_OUT_IN (DOMAIN_CONFIDENTIALITY | DOMAIN_INTEGRITY)
|
||||
#define DOMAIN_FULLY_PROT \
|
||||
(DOMAIN_CONFIDENTIALITY | DOMAIN_INTEGRITY | DOMAIN_DISALLOWED_IO_IN \
|
||||
| DOMAIN_DISALLOWED_IO_OUT)
|
||||
|
||||
// Values for XStatePolicy
|
||||
#define XSTATE_READWRITE 0x00
|
||||
#define XSTATE_READONLY 0x01
|
||||
#define XSTATE_SCRUB 0x03
|
||||
|
||||
typedef struct {
|
||||
uint64_t vmcs_phys_pointer; // bits 11:0 are reserved and must be 0
|
||||
uint32_t domain_type : 4;
|
||||
uint32_t x_state_policy : 2;
|
||||
uint32_t degradation_policy : 4;
|
||||
uint32_t reserved1 : 22; // Must be 0
|
||||
uint32_t add_or_remove;
|
||||
} STM_VMCS_DATABASE_REQUEST;
|
||||
|
||||
// Event log
|
||||
#define NEW_LOG 1
|
||||
#define CONFIGURE_LOG 2
|
||||
#define START_LOG 3
|
||||
#define STOP_LOG 4
|
||||
#define CLEAR_LOG 5
|
||||
#define DELETE_LOG 6
|
||||
typedef enum {
|
||||
evt_log_started,
|
||||
evt_log_stopped,
|
||||
evt_log_invalid_parameter_detected,
|
||||
evt_handled_protection_exception,
|
||||
// unhandled protection exceptions result in reset & cannot be logged
|
||||
evt_bios_access_to_unclaimed_resource,
|
||||
evt_mle_resource_protection_granted,
|
||||
evt_mle_resource_protection_denied,
|
||||
evt_mle_resource_unprotect,
|
||||
evt_mle_resource_unprotect_error,
|
||||
evt_mle_domain_type_degraded,
|
||||
// add more here
|
||||
evt_mle_max,
|
||||
// Not used
|
||||
evt_invalid = 0xFFFFFFFF,
|
||||
} EVENT_TYPE;
|
||||
|
||||
typedef struct {
|
||||
uint32_t page_count;
|
||||
uint64_t pages[1]; // number of elements is PageCount
|
||||
} STM_EVENT_LOG_MANAGEMENT_REQUEST_DATA_LOG_BUFFER;
|
||||
|
||||
typedef union {
|
||||
STM_EVENT_LOG_MANAGEMENT_REQUEST_DATA_LOG_BUFFER log_buffer;
|
||||
uint32_t event_enable_bitmap; // bitmap of EVENT_TYPE
|
||||
} STM_EVENT_LOG_MANAGEMENT_REQUEST_DATA;
|
||||
|
||||
typedef struct {
|
||||
uint32_t sub_functionindex;
|
||||
STM_EVENT_LOG_MANAGEMENT_REQUEST_DATA data;
|
||||
} STM_EVENT_LOG_MANAGEMENT_REQUEST;
|
||||
|
||||
// VMCALL API Numbers
|
||||
//
|
||||
// API number convention: BIOS facing VMCALL interfaces have bit 16 clear
|
||||
#define STM_API_MAP_ADDRESS_RANGE 0x00000001
|
||||
#define STM_API_UNMAP_ADDRESS_RANGE 0x00000002
|
||||
#define STM_API_ADDRESS_LOOKUP 0x00000003
|
||||
#define STM_API_RETURN_FROM_PROTECTION_EXCEPTION 0x00000004
|
||||
|
||||
// API number convention: MLE facing VMCALL interfaces have bit 16 set
|
||||
//
|
||||
// The STM configuration lifecycle is as follows:
|
||||
// 1. SENTER->SINIT->MLE: MLE begins execution with SMI disabled (masked).
|
||||
// 2. MLE invokes InitializeProtectionVMCALL() to prepare STM for setup of
|
||||
// initial protection profile. This is done on a single CPU and has global
|
||||
// effect.
|
||||
// 3. MLE invokes ProtectResourceVMCALL() to define the initial protection
|
||||
// profile. The protection profile is global across all CPUs.
|
||||
// 4. MLE invokes StartStmVMCALL() to enable the STM to begin receiving SMI
|
||||
// events. This must be done on every logical CPU.
|
||||
// 5. MLE may invoke ProtectResourceVMCALL() or UnProtectResourceVMCALL()
|
||||
// during runtime as many times as necessary.
|
||||
// 6. MLE invokes StopStmVMCALL() to disable the STM. SMI is again masked
|
||||
// following StopStmVMCALL().
|
||||
//
|
||||
#define STM_API_START 0x00010001
|
||||
#define STM_API_STOP 0x00010002
|
||||
#define STM_API_PROTECT_RESOURCE 0x00010003
|
||||
#define STM_API_UNPROTECT_RESOURCE 0x00010004
|
||||
#define STM_API_GET_BIOS_RESOURCES 0x00010005
|
||||
#define STM_API_MANAGE_VMCS_DATABASE 0x00010006
|
||||
#define STM_API_INITIALIZE_PROTECTION 0x00010007
|
||||
#define STM_API_MANAGE_EVENT_LOG 0x00010008
|
||||
|
||||
// Return codes
|
||||
typedef uint32_t STM_STATUS;
|
||||
|
||||
#define STM_SUCCESS 0x00000000
|
||||
#define SMM_SUCCESS 0x00000000
|
||||
// all error codes have bit 31 set
|
||||
// STM errors have bit 16 set
|
||||
#define ERROR_STM_SECURITY_VIOLATION 0x80010001
|
||||
#define ERROR_STM_CACHE_TYPE_NOT_SUPPORTED 0x80010002
|
||||
#define ERROR_STM_PAGE_NOT_FOUND 0x80010003
|
||||
#define ERROR_STM_BAD_CR3 0x80010004
|
||||
#define ERROR_STM_PHYSICAL_OVER_4G 0x80010005
|
||||
#define ERROR_STM_VIRTUAL_SPACE_TOO_SMALL 0x80010006
|
||||
#define ERROR_STM_UNPROTECTABLE_RESOURCE 0x80010007
|
||||
#define ERROR_STM_ALREADY_STARTED 0x80010008
|
||||
#define ERROR_STM_WITHOUT_SMX_UNSUPPORTED 0x80010009
|
||||
#define ERROR_STM_STOPPED 0x8001000A
|
||||
#define ERROR_STM_BUFFER_TOO_SMALL 0x8001000B
|
||||
#define ERROR_STM_INVALID_VMCS_DATABASE 0x8001000C
|
||||
#define ERROR_STM_MALFORMED_RESOURCE_LIST 0x8001000D
|
||||
#define ERROR_STM_INVALID_PAGECOUNT 0x8001000E
|
||||
#define ERROR_STM_LOG_ALLOCATED 0x8001000F
|
||||
#define ERROR_STM_LOG_NOT_ALLOCATED 0x80010010
|
||||
#define ERROR_STM_LOG_NOT_STOPPED 0x80010011
|
||||
#define ERROR_STM_LOG_NOT_STARTED 0x80010012
|
||||
#define ERROR_STM_RESERVED_BIT_SET 0x80010013
|
||||
#define ERROR_STM_NO_EVENTS_ENABLED 0x80010014
|
||||
#define ERROR_STM_OUT_OF_RESOURCES 0x80010015
|
||||
#define ERROR_STM_FUNCTION_NOT_SUPPORTED 0x80010016
|
||||
#define ERROR_STM_UNPROTECTABLE 0x80010017
|
||||
#define ERROR_STM_UNSUPPORTED_MSR_BIT 0x80010018
|
||||
#define ERROR_STM_UNSPECIFIED 0x8001FFFF
|
||||
|
||||
// SMM errors have bit 17 set
|
||||
#define ERROR_SMM_BAD_BUFFER 0x80020001
|
||||
#define ERROR_SMM_INVALID_RSC 0x80020004
|
||||
#define ERROR_SMM_INVALID_BUFFER_SIZE 0x80020005
|
||||
#define ERROR_SMM_BUFFER_TOO_SHORT 0x80020006
|
||||
#define ERROR_SMM_INVALID_LIST 0x80020007
|
||||
#define ERROR_SMM_OUT_OF_MEMORY 0x80020008
|
||||
#define ERROR_SMM_AFTER_INIT 0x80020009
|
||||
#define ERROR_SMM_UNSPECIFIED 0x8002FFFF
|
||||
|
||||
// Errors that apply to both have bits 15, 16, and 17 set
|
||||
#define ERROR_INVALID_API 0x80038001
|
||||
#define ERROR_INVALID_PARAMETER 0x80038002
|
||||
|
||||
// STM TXT.ERRORCODE codes
|
||||
#define STM_CRASH_PROTECTION_EXCEPTION 0xC000F001
|
||||
#define STM_CRASH_PROTECTION_EXCEPTION_FAILURE 0xC000F002
|
||||
#define STM_CRASH_DOMAIN_DEGRADATION_FAILURE 0xC000F003
|
||||
#define STM_CRASH_BIOS_PANIC 0xC000E000
|
||||
|
||||
typedef struct {
|
||||
uint32_t event_serial_number;
|
||||
uint16_t type;
|
||||
uint16_t lock : 1;
|
||||
uint16_t valid : 1;
|
||||
uint16_t read_by_mle : 1;
|
||||
uint16_t wrapped : 1;
|
||||
uint16_t reserved : 12;
|
||||
} LOG_ENTRY_HEADER;
|
||||
|
||||
typedef struct {
|
||||
uint32_t reserved;
|
||||
} ENTRY_EVT_LOG_STARTED;
|
||||
|
||||
typedef struct {
|
||||
uint32_t reserved;
|
||||
} ENTRY_EVT_LOG_STOPPED;
|
||||
|
||||
typedef struct {
|
||||
uint32_t vmcall_api_number;
|
||||
} ENTRY_EVT_LOG_INVALID_PARAM;
|
||||
|
||||
typedef struct {
|
||||
STM_RSC resource;
|
||||
} ENTRY_EVT_LOG_HANDLED_PROTECTION_EXCEPTION;
|
||||
|
||||
typedef struct {
|
||||
STM_RSC resource;
|
||||
} ENTRY_EVT_BIOS_ACCESS_UNCLAIMED_RSC;
|
||||
|
||||
typedef struct {
|
||||
STM_RSC resource;
|
||||
} ENTRY_EVT_MLE_RSC_PROT_GRANTED;
|
||||
|
||||
typedef struct {
|
||||
STM_RSC resource;
|
||||
} ENTRY_EVT_MLE_RSC_PROT_DENIED;
|
||||
|
||||
typedef struct {
|
||||
STM_RSC resource;
|
||||
} ENTRY_EVT_MLE_RSC_UNPROT;
|
||||
|
||||
typedef struct {
|
||||
STM_RSC resource;
|
||||
} ENTRY_EVT_MLE_RSC_UNPROT_ERROR;
|
||||
|
||||
typedef struct {
|
||||
uint64_t vmcs_phys_pointer;
|
||||
uint8_t expected_domain_type;
|
||||
uint8_t degraded_domain_type;
|
||||
} ENTRY_EVT_MLE_DOMAIN_TYPE_DEGRADED;
|
||||
|
||||
typedef union {
|
||||
ENTRY_EVT_LOG_STARTED started;
|
||||
ENTRY_EVT_LOG_STOPPED stopped;
|
||||
ENTRY_EVT_LOG_INVALID_PARAM invalid_param;
|
||||
ENTRY_EVT_LOG_HANDLED_PROTECTION_EXCEPTION
|
||||
handled_protection_exception;
|
||||
ENTRY_EVT_BIOS_ACCESS_UNCLAIMED_RSC bios_unclaimed_rsc;
|
||||
ENTRY_EVT_MLE_RSC_PROT_GRANTED mle_rsc_prot_granted;
|
||||
ENTRY_EVT_MLE_RSC_PROT_DENIED mle_rsc_prot_denied;
|
||||
ENTRY_EVT_MLE_RSC_UNPROT mle_rsc_unprot;
|
||||
ENTRY_EVT_MLE_RSC_UNPROT_ERROR mle_rsc_unprot_error;
|
||||
ENTRY_EVT_MLE_DOMAIN_TYPE_DEGRADED mle_domain_type_degraded;
|
||||
} LOG_ENTRY_DATA;
|
||||
|
||||
typedef struct {
|
||||
LOG_ENTRY_HEADER hdr;
|
||||
LOG_ENTRY_DATA data;
|
||||
} STM_LOG_ENTRY;
|
||||
|
||||
#define STM_LOG_ENTRY_SIZE 256
|
||||
#define STM_CONFIG_SMI_UNBLOCKING_BY_VMX_OFF 0x1
|
||||
|
||||
// TXT debug
|
||||
#define SW_SMI_STM_ADD_RUNTIME_RESOURCES_SUB_FUNC 0
|
||||
#define SW_SMI_STM_READ_BIOS_RESOURCES_SUB_FUNC 1
|
||||
#define SW_SMI_STM_REPLACE_BIOS_RESOURCES_SUB_FUNC 2
|
||||
|
||||
typedef struct {
|
||||
uint32_t buffer_size;
|
||||
uint32_t reserved;
|
||||
// uint8_t Data[];
|
||||
} TXT_BIOS_DEBUG;
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
#endif
|
||||
192
src/security/intel/stm/StmPlatformResource.c
Normal file
192
src/security/intel/stm/StmPlatformResource.c
Normal file
|
|
@ -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 <stdint.h>
|
||||
#include <security/intel/stm/StmApi.h>
|
||||
#include <security/intel/stm/SmmStm.h>
|
||||
#include <security/intel/stm/StmPlatformResource.h>
|
||||
|
||||
#if CONFIG(SOUTHBRIDGE_INTEL_COMMON_PMCLIB)
|
||||
#include <southbridge/intel/common/pmutil.h>
|
||||
#else
|
||||
#include <soc/pm.h>
|
||||
#endif
|
||||
#include <cpu/x86/msr.h>
|
||||
#include <console/console.h>
|
||||
|
||||
#define RDWR_ACCS 3
|
||||
#define FULL_ACCS 7
|
||||
|
||||
// Fixed memory ranges
|
||||
//
|
||||
// TSEG memory!
|
||||
static STM_RSC_MEM_DESC rsc_tseg_memory = {{MEM_RANGE, sizeof(STM_RSC_MEM_DESC)},
|
||||
0,
|
||||
0,
|
||||
FULL_ACCS};
|
||||
|
||||
// Flash part
|
||||
static STM_RSC_MEM_DESC rsc_spi_memory = {
|
||||
{MEM_RANGE, sizeof(STM_RSC_MEM_DESC)},
|
||||
0xFE000000,
|
||||
0x01000000,
|
||||
FULL_ACCS};
|
||||
|
||||
// ACPI
|
||||
static STM_RSC_IO_DESC rsc_pm_io = {{IO_RANGE, sizeof(STM_RSC_IO_DESC)}, 0, 128};
|
||||
|
||||
// PCIE MMIO
|
||||
static STM_RSC_MMIO_DESC rsc_pcie_mmio = {{MMIO_RANGE, sizeof(STM_RSC_MMIO_DESC)},
|
||||
0,
|
||||
0, // Length
|
||||
RDWR_ACCS};
|
||||
|
||||
// Local APIC
|
||||
static STM_RSC_MMIO_DESC rsc_apic_mmio = {{MMIO_RANGE, sizeof(STM_RSC_MMIO_DESC)},
|
||||
0,
|
||||
0x400,
|
||||
RDWR_ACCS};
|
||||
|
||||
// Software SMI
|
||||
static STM_RSC_TRAPPED_IO_DESC rsc_sw_smi_trap_io = {
|
||||
{TRAPPED_IO_RANGE, sizeof(STM_RSC_TRAPPED_IO_DESC)},
|
||||
0xB2,
|
||||
2};
|
||||
|
||||
// End of list
|
||||
static STM_RSC_END rsc_list_end __attribute__((used)) = {
|
||||
{END_OF_RESOURCES, sizeof(STM_RSC_END)}, 0};
|
||||
|
||||
// Common PCI devices
|
||||
//
|
||||
// LPC bridge
|
||||
STM_RSC_PCI_CFG_DESC rsc_lpc_bridge_pci = {
|
||||
{PCI_CFG_RANGE, sizeof(STM_RSC_PCI_CFG_DESC)},
|
||||
RDWR_ACCS,
|
||||
0,
|
||||
0,
|
||||
0x1000,
|
||||
0,
|
||||
0,
|
||||
{
|
||||
{1, 1, sizeof(STM_PCI_DEVICE_PATH_NODE), LPC_FUNCTION,
|
||||
LPC_DEVICE},
|
||||
},
|
||||
};
|
||||
|
||||
// Template for MSR resources.
|
||||
STM_RSC_MSR_DESC rsc_msr_tpl = {
|
||||
{MACHINE_SPECIFIC_REG, sizeof(STM_RSC_MSR_DESC)},
|
||||
};
|
||||
|
||||
// MSR indices to register
|
||||
typedef struct {
|
||||
uint32_t msr_index;
|
||||
uint64_t read_mask;
|
||||
uint64_t write_mask;
|
||||
} MSR_TABLE_ENTRY;
|
||||
|
||||
MSR_TABLE_ENTRY msr_table[] = {
|
||||
// Index Read Write
|
||||
// MASK64 means need access, MASK0 means no need access.
|
||||
{SMRR_PHYSBASE_MSR, MASK64, MASK0},
|
||||
{SMRR_PHYSMASK_MSR, MASK64, MASK0},
|
||||
};
|
||||
|
||||
/*
|
||||
* Fix up PCIE resource.
|
||||
*/
|
||||
static void fixup_pciex_resource(void)
|
||||
{
|
||||
// Find max bus number and PCIEX length
|
||||
rsc_pcie_mmio.length = CONFIG_SA_PCIEX_LENGTH; // 0x10000000;// 256 MB
|
||||
rsc_pcie_mmio.base = CONFIG_MMCONF_BASE_ADDRESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add basic resources to BIOS resource database.
|
||||
*/
|
||||
static void add_simple_resources(void)
|
||||
{
|
||||
int Status = 0;
|
||||
msr_t ReadMsr;
|
||||
|
||||
ReadMsr = rdmsr(SMRR_PHYSBASE_MSR);
|
||||
rsc_tseg_memory.base = ReadMsr.lo & 0xFFFFF000;
|
||||
|
||||
ReadMsr = rdmsr(SMRR_PHYSMASK_MSR);
|
||||
rsc_tseg_memory.length = (~(ReadMsr.lo & 0xFFFFF000) + 1);
|
||||
|
||||
rsc_pm_io.base = (uint16_t)get_pmbase();
|
||||
|
||||
// Local APIC. We assume that all thteads are programmed identically
|
||||
// despite that it is possible to have individual APIC address for
|
||||
// each of the threads. If this is the case this programming should
|
||||
// be corrected.
|
||||
ReadMsr = rdmsr(IA32_APIC_BASE_MSR_INDEX);
|
||||
rsc_apic_mmio.base = ((uint64_t)ReadMsr.lo & 0xFFFFF000) |
|
||||
((uint64_t)(ReadMsr.hi & 0x0000000F) << 32);
|
||||
|
||||
// PCIEX BAR
|
||||
fixup_pciex_resource();
|
||||
|
||||
Status |= add_pi_resource((void *)&rsc_tseg_memory, 1);
|
||||
Status |= add_pi_resource((void *)&rsc_spi_memory, 1);
|
||||
|
||||
Status |= add_pi_resource((void *)&rsc_pm_io, 1);
|
||||
Status |= add_pi_resource((void *)&rsc_pcie_mmio, 1);
|
||||
Status |= add_pi_resource((void *)&rsc_apic_mmio, 1);
|
||||
Status |= add_pi_resource((void *)&rsc_sw_smi_trap_io, 1);
|
||||
|
||||
Status |= add_pi_resource((void *)&rsc_lpc_bridge_pci, 1);
|
||||
|
||||
if (Status != 0)
|
||||
printk(BIOS_DEBUG, "STM - Error in adding simple resources\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Add MSR resources to BIOS resource database.
|
||||
*/
|
||||
static void add_msr_resources(void)
|
||||
{
|
||||
uint32_t Status = 0;
|
||||
uint32_t Index;
|
||||
|
||||
for (Index = 0; Index < ARRAY_SIZE(msr_table); Index++) {
|
||||
|
||||
rsc_msr_tpl.msr_index = (uint32_t)msr_table[Index].msr_index;
|
||||
rsc_msr_tpl.read_mask = (uint64_t)msr_table[Index].read_mask;
|
||||
rsc_msr_tpl.write_mask = (uint64_t)msr_table[Index].write_mask;
|
||||
|
||||
Status |= add_pi_resource((void *)&rsc_msr_tpl, 1);
|
||||
}
|
||||
|
||||
if (Status != 0)
|
||||
printk(BIOS_DEBUG, "STM - Error in adding MSR resources\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Add resources to BIOS resource database.
|
||||
*/
|
||||
|
||||
extern uint8_t *m_stm_resources_ptr;
|
||||
|
||||
void add_resources_cmd(void)
|
||||
{
|
||||
m_stm_resources_ptr = NULL;
|
||||
|
||||
add_simple_resources();
|
||||
|
||||
add_msr_resources();
|
||||
}
|
||||
32
src/security/intel/stm/StmPlatformResource.h
Normal file
32
src/security/intel/stm/StmPlatformResource.h
Normal file
|
|
@ -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
|
||||
213
src/security/intel/stm/StmPlatformSmm.c
Normal file
213
src/security/intel/stm/StmPlatformSmm.c
Normal file
|
|
@ -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 <security/intel/stm/StmApi.h>
|
||||
#include <security/intel/stm/SmmStm.h>
|
||||
#include <security/intel/stm/StmPlatformResource.h>
|
||||
#include <security/tpm/tspi.h>
|
||||
#include <cpu/x86/smm.h>
|
||||
#include <cpu/x86/msr.h>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <cbfs.h>
|
||||
#include <console/console.h>
|
||||
#include <lib.h>
|
||||
#include <stdint.h>
|
||||
#include <arch/rom_segs.h>
|
||||
|
||||
/*
|
||||
* Load STM image to MSEG
|
||||
*
|
||||
* @retval SUCCESS STM is loaded to MSEG
|
||||
*/
|
||||
int load_stm_image(uintptr_t mseg)
|
||||
{
|
||||
int status;
|
||||
void *mseg_base;
|
||||
uint32_t stm_buffer_size;
|
||||
uint32_t stm_image_size;
|
||||
bool stm_status;
|
||||
|
||||
STM_HEADER *stm_header;
|
||||
|
||||
// Extract STM image from FV
|
||||
mseg_base = (void *)mseg;
|
||||
stm_buffer_size = CONFIG_MSEG_SIZE;
|
||||
stm_image_size = 0;
|
||||
|
||||
memset((void *)mseg_base, 0, CONFIG_MSEG_SIZE); // clear the mseg
|
||||
|
||||
stm_image_size = cbfs_boot_load_file("stm.bin", mseg_base,
|
||||
stm_buffer_size, CBFS_TYPE_RAW);
|
||||
printk(BIOS_DEBUG, "STM:loaded into mseg: 0x%p size: %u\n", mseg_base,
|
||||
stm_image_size);
|
||||
/* status is number of bytes loaded */
|
||||
stm_status = stm_check_stm_image(mseg_base, stm_image_size);
|
||||
|
||||
if (!stm_status) {
|
||||
printk(BIOS_DEBUG, "STM: Error in STM image\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
stm_header = mseg_base;
|
||||
|
||||
stm_gen_4g_pagetable_x64((uint32_t)mseg_base
|
||||
+ stm_header->hw_stm_hdr.cr3_offset);
|
||||
|
||||
// Debug stuff
|
||||
printk(BIOS_DEBUG,
|
||||
"STM: Header-Revision %d Features 0x%08x Cr3Offset 0x%08x\n",
|
||||
stm_header->hw_stm_hdr.stm_header_revision,
|
||||
stm_header->hw_stm_hdr.monitor_features,
|
||||
stm_header->hw_stm_hdr.cr3_offset);
|
||||
printk(BIOS_DEBUG,
|
||||
"STM: Header-StaticImageSize: %d Cr3Location: 0x%08x\n",
|
||||
stm_header->sw_stm_hdr.static_image_size,
|
||||
((uint32_t)mseg_base + stm_header->hw_stm_hdr.cr3_offset));
|
||||
|
||||
status = 0; // always return good for now
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
struct descriptor {
|
||||
uint16_t limit;
|
||||
uintptr_t base;
|
||||
} __attribute__((packed));
|
||||
|
||||
|
||||
static void read_gdtr(struct descriptor *gdtr)
|
||||
{
|
||||
__asm__ __volatile__("sgdt %0" : "=m"(*gdtr));
|
||||
}
|
||||
|
||||
void setup_smm_descriptor(void *smbase, void *base_smbase, int32_t apic_id,
|
||||
int32_t entry32_off)
|
||||
{
|
||||
struct descriptor gdtr;
|
||||
void *smbase_processor;
|
||||
//msr_t smbase_msr;
|
||||
|
||||
TXT_PROCESSOR_SMM_DESCRIPTOR *psd;
|
||||
|
||||
smbase_processor = (void *) SMM_DEFAULT_BASE;//we are here
|
||||
psd = smbase + SMM_PSD_OFFSET;
|
||||
|
||||
printk(BIOS_DEBUG,
|
||||
"STM: Smm Descriptor setup: Smbase: %p Smbase_processor: %p Psd: %p\n",
|
||||
smbase,
|
||||
smbase_processor,
|
||||
psd);
|
||||
|
||||
memset(psd, 0, sizeof(TXT_PROCESSOR_SMM_DESCRIPTOR));
|
||||
|
||||
memcpy(&psd->signature, TXT_PROCESSOR_SMM_DESCRIPTOR_SIGNATURE, 8);
|
||||
psd->smm_descriptor_ver_major =
|
||||
TXT_PROCESSOR_SMM_DESCRIPTOR_VERSION_MAJOR;
|
||||
psd->smm_descriptor_ver_minor =
|
||||
TXT_PROCESSOR_SMM_DESCRIPTOR_VERSION_MINOR;
|
||||
psd->smm_smi_handler_rip =
|
||||
(uint64_t)((uintptr_t)base_smbase + SMM_ENTRY_OFFSET +
|
||||
entry32_off);
|
||||
psd->local_apic_id = apic_id;
|
||||
psd->size = sizeof(TXT_PROCESSOR_SMM_DESCRIPTOR);
|
||||
psd->acpi_rsdp = 0;
|
||||
psd->bios_hw_resource_requirements_ptr =
|
||||
(uint64_t)((uintptr_t)get_stm_resource());
|
||||
psd->smm_cs = ROM_CODE_SEG;
|
||||
psd->smm_ds = ROM_DATA_SEG;
|
||||
psd->smm_ss = ROM_DATA_SEG;
|
||||
psd->smm_other_segment = ROM_DATA_SEG;
|
||||
psd->smm_tr = SMM_TASK_STATE_SEG;
|
||||
|
||||
|
||||
// At this point the coreboot smm_stub is relative to the default
|
||||
// smbase and not the one for the smi handler in tseg. So we have
|
||||
// to adjust the gdtr.base
|
||||
|
||||
read_gdtr(&gdtr);
|
||||
|
||||
gdtr.base -= (uintptr_t) smbase_processor;
|
||||
gdtr.base += (uintptr_t) base_smbase;
|
||||
|
||||
psd->smm_gdt_ptr = gdtr.base;
|
||||
psd->smm_gdt_size = gdtr.limit + 1; // the stm will subtract, so add
|
||||
printk(BIOS_DEBUG, "STM: Smm Descriptor setup complete - Smbase: %p Psd: %p\n",
|
||||
smbase, psd);
|
||||
}
|
||||
|
||||
extern uint8_t *stm_resource_heap;
|
||||
|
||||
#define FXSAVE_SIZE 512
|
||||
|
||||
static int stm_load_status = 0;
|
||||
|
||||
void stm_setup(uintptr_t mseg, int cpu, int num_cpus, uintptr_t smbase,
|
||||
uintptr_t base_smbase, uint32_t offset32)
|
||||
{
|
||||
msr_t InitMseg;
|
||||
msr_t MsegChk;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
421
src/security/intel/txt/common.c
Normal file
421
src/security/intel/txt/common.c
Normal file
|
|
@ -0,0 +1,421 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
|
||||
#include <arch/mmio.h>
|
||||
#include <string.h>
|
||||
#include <console/console.h>
|
||||
#include <types.h>
|
||||
#include <cbfs.h>
|
||||
#include <cpu/x86/lapic.h>
|
||||
#include <cpu/x86/cr.h>
|
||||
#include <cpu/x86/mp.h>
|
||||
#include <lib.h>
|
||||
#include <smp/node.h>
|
||||
#include <cf9_reset.h>
|
||||
#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;
|
||||
}
|
||||
117
src/security/intel/txt/getsec.c
Normal file
117
src/security/intel/txt/getsec.c
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
|
||||
#include <stdint.h>
|
||||
#include <cpu/x86/lapic.h>
|
||||
#include <cpu/x86/cr.h>
|
||||
#include <cpu/x86/cache.h>
|
||||
#include <cpu/x86/mp.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
318
src/security/intel/txt/getsec_enteraccs.S
Normal file
318
src/security/intel/txt/getsec_enteraccs.S
Normal file
|
|
@ -0,0 +1,318 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
|
||||
#include <cpu/x86/mtrr.h>
|
||||
#include <cpu/x86/cr.h>
|
||||
#include <cpu/x86/msr.h>
|
||||
|
||||
#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
|
||||
241
src/security/intel/txt/logging.c
Normal file
241
src/security/intel/txt/logging.c
Normal file
|
|
@ -0,0 +1,241 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
|
||||
#include <console/console.h>
|
||||
#if CONFIG(SOC_INTEL_COMMON_BLOCK_SA)
|
||||
#include <intelblocks/systemagent.h>
|
||||
#endif
|
||||
|
||||
#include <arch/mmio.h>
|
||||
#include <string.h>
|
||||
|
||||
#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);
|
||||
}
|
||||
}
|
||||
382
src/security/intel/txt/ramstage.c
Normal file
382
src/security/intel/txt/ramstage.c
Normal file
|
|
@ -0,0 +1,382 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
|
||||
#include <arch/mmio.h>
|
||||
#include <arch/acpi.h>
|
||||
#include <bootstate.h>
|
||||
#include <bootmem.h>
|
||||
#include <console/console.h>
|
||||
#include <stdint.h>
|
||||
#include <cbfs.h>
|
||||
#include <cpu/intel/common/common.h>
|
||||
#include <cpu/x86/msr.h>
|
||||
|
||||
#include <lib.h>
|
||||
#include <device/pci_ops.h>
|
||||
|
||||
#if CONFIG(SOC_INTEL_FSP_BROADWELL_DE)
|
||||
#include <soc/broadwell_de.h>
|
||||
#include <soc/ramstage.h>
|
||||
#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);
|
||||
27
src/security/intel/txt/txt.h
Normal file
27
src/security/intel/txt/txt.h
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
|
||||
#ifndef SECURITY_INTEL_TXT_H_
|
||||
#define SECURITY_INTEL_TXT_H_
|
||||
|
||||
#include <types.h>
|
||||
|
||||
/* 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_ */
|
||||
21
src/security/intel/txt/txt_getsec.h
Normal file
21
src/security/intel/txt/txt_getsec.h
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
|
||||
#ifndef SECURITY_INTEL_TXT_GETSEC_H_
|
||||
#define SECURITY_INTEL_TXT_GETSEC_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
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_ */
|
||||
267
src/security/intel/txt/txt_register.h
Normal file
267
src/security/intel/txt/txt_register.h
Normal file
|
|
@ -0,0 +1,267 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
|
||||
#ifndef SECURITY_INTEL_TXT_REGISTER_H_
|
||||
#define SECURITY_INTEL_TXT_REGISTER_H_
|
||||
|
||||
#include <types.h>
|
||||
#include <stddef.h>
|
||||
|
||||
/*
|
||||
* 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_ */
|
||||
|
|
@ -14,7 +14,8 @@
|
|||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <types.h>
|
||||
#include <stdint.h>
|
||||
#include <security/intel/txt/txt.h>
|
||||
#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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -250,4 +250,7 @@ void pch_log_state(void);
|
|||
|
||||
void enable_pm_timer_emulation(void);
|
||||
|
||||
/* STM Support */
|
||||
uint16_t get_pmbase(void);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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_ */
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
28
src/soc/intel/fsp_broadwell_de/include/soc/pm.h
Normal file
28
src/soc/intel/fsp_broadwell_de/include/soc/pm.h
Normal file
|
|
@ -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 <soc/acpi.h>
|
||||
#endif
|
||||
|
|
@ -13,6 +13,8 @@
|
|||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <cpu/x86/msr.h>
|
||||
#include <soc/msr.h>
|
||||
#include <stddef.h>
|
||||
#include <device/pci_ops.h>
|
||||
#include <device/dram/ddr4.h>
|
||||
|
|
@ -20,6 +22,8 @@
|
|||
#include <soc/memory.h>
|
||||
#include <spd_bin.h>
|
||||
|
||||
#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));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,8 +21,10 @@
|
|||
#include <cbmem.h>
|
||||
#include <console/console.h>
|
||||
#include <console/usb.h>
|
||||
#include <cpu/x86/msr.h>
|
||||
#include <cpu/x86/mtrr.h>
|
||||
#include <cpu/x86/smm.h>
|
||||
#include <cf9_reset.h>
|
||||
#include <program_loading.h>
|
||||
#include <timestamp.h>
|
||||
#include <version.h>
|
||||
|
|
@ -38,6 +40,9 @@
|
|||
#include <soc/ubox.h>
|
||||
#include <build.h>
|
||||
|
||||
#include <security/intel/txt/txt.h>
|
||||
#include <security/intel/txt/txt_register.h>
|
||||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@
|
|||
#include <pc80/i8259.h>
|
||||
#include <pc80/isa-dma.h>
|
||||
#include <soc/iomap.h>
|
||||
#include <soc/intel/common/block/lpc/lpc_def.h>
|
||||
#include <soc/irq.h>
|
||||
#include <soc/lpc.h>
|
||||
#include <soc/pci_devs.h>
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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_ */
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue