diff options
-rw-r--r-- | src/commonlib/include/commonlib/cbmem_id.h | 1 | ||||
-rw-r--r-- | src/commonlib/include/commonlib/coreboot_tables.h | 24 | ||||
-rw-r--r-- | src/drivers/tpm/Kconfig | 11 | ||||
-rw-r--r-- | src/drivers/tpm/Makefile.inc | 4 | ||||
-rw-r--r-- | src/drivers/tpm/ppi.c | 726 | ||||
-rw-r--r-- | src/drivers/tpm/tpm_ppi.h | 90 | ||||
-rw-r--r-- | src/lib/coreboot_table.c | 4 |
7 files changed, 859 insertions, 1 deletions
diff --git a/src/commonlib/include/commonlib/cbmem_id.h b/src/commonlib/include/commonlib/cbmem_id.h index 6e24545110..f58d7b11c2 100644 --- a/src/commonlib/include/commonlib/cbmem_id.h +++ b/src/commonlib/include/commonlib/cbmem_id.h @@ -56,6 +56,7 @@ #define CBMEM_ID_TCPA_TCG_LOG 0x54445041 #define CBMEM_ID_TIMESTAMP 0x54494d45 #define CBMEM_ID_TPM2_TCG_LOG 0x54504d32 +#define CBMEM_ID_TPM_PPI 0x54505049 #define CBMEM_ID_VBOOT_HANDOFF 0x780074f0 /* deprecated */ #define CBMEM_ID_VBOOT_SEL_REG 0x780074f1 /* deprecated */ #define CBMEM_ID_VBOOT_WORKBUF 0x78007343 diff --git a/src/commonlib/include/commonlib/coreboot_tables.h b/src/commonlib/include/commonlib/coreboot_tables.h index c740975bc7..be40c3818f 100644 --- a/src/commonlib/include/commonlib/coreboot_tables.h +++ b/src/commonlib/include/commonlib/coreboot_tables.h @@ -81,6 +81,7 @@ enum { LB_TAG_FMAP = 0x0037, LB_TAG_PLATFORM_BLOB_VERSION = 0x0038, LB_TAG_SMMSTOREV2 = 0x0039, + LB_TAG_TPM_PPI_HANDOFF = 0x003a, LB_TAG_BOARD_CONFIG = 0x0040, /* The following options are CMOS-related */ LB_TAG_CMOS_OPTION_TABLE = 0x00c8, @@ -521,4 +522,27 @@ struct lb_smmstorev2 { uint8_t unused[3]; /* Set to zero */ }; +enum lb_tmp_ppi_tpm_version { + LB_TPM_VERSION_UNSPEC = 0, + LB_TPM_VERSION_TPM_VERSION_1_2, + LB_TPM_VERSION_TPM_VERSION_2, +}; + +/* + * Handoff buffer for TPM Physical Presence Interface. + * * ppi_address Pointer to PPI buffer shared with ACPI + * The layout of the buffer matches the QEMU virtual memory device + * that is generated by QEMU. + * See files 'hw/i386/acpi-build.c' and 'include/hw/acpi/tpm.h' + * for details. + * * tpm_version TPM version: 1 for TPM1.2, 2 for TPM2.0 + * * ppi_version BCD encoded version of TPM PPI interface + */ +struct lb_tpm_physical_presence { + uint32_t tag; + uint32_t size; + uint32_t ppi_address; /* Address of ACPI PPI communication buffer */ + uint8_t tpm_version; /* 1: TPM1.2, 2: TPM2.0 */ + uint8_t ppi_version; /* BCD encoded */ +} __packed; #endif diff --git a/src/drivers/tpm/Kconfig b/src/drivers/tpm/Kconfig index 8508210fc6..baf760b4b0 100644 --- a/src/drivers/tpm/Kconfig +++ b/src/drivers/tpm/Kconfig @@ -5,3 +5,14 @@ config TPM_INIT help This driver automatically initializes the TPM if vboot is not used. The TPM driver init is done during the ramstage chip init phase. + +config TPM_PPI + bool "Generate ACPI code to implement TPM physical presence interface" + depends on TPM1 || TPM2 + depends on HAVE_ACPI_TABLES + depends on !CHROMEOS + default y if PAYLOAD_TIANOCORE + help + This driver automatically generates ACPI tables for the Physical + Presence Interface defined by the TCG. If not activated only a stub + will be generated without any functionality. diff --git a/src/drivers/tpm/Makefile.inc b/src/drivers/tpm/Makefile.inc index 5fc4632912..af6e5a21c1 100644 --- a/src/drivers/tpm/Makefile.inc +++ b/src/drivers/tpm/Makefile.inc @@ -1,3 +1,7 @@ ramstage-$(CONFIG_TPM_INIT) += tpm.c +ifeq ($(CONFIG_TPM_PPI),y) +ramstage-$(CONFIG_HAVE_ACPI_TABLES) += ppi.c +else ramstage-$(CONFIG_HAVE_ACPI_TABLES) += ppi_stub.c +endif diff --git a/src/drivers/tpm/ppi.c b/src/drivers/tpm/ppi.c new file mode 100644 index 0000000000..88dd649954 --- /dev/null +++ b/src/drivers/tpm/ppi.c @@ -0,0 +1,726 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include <types.h> +#include <stddef.h> +#include <acpi/acpi.h> +#include <acpi/acpigen.h> +#include <acpi/acpi_device.h> +#include <cbmem.h> +#include <console/console.h> + +#include "tpm_ppi.h" + +#define BCD(x, y) (((x) << 4) | ((y) << 0)) + +static void set_package_element_op(const char *package_name, unsigned int element, + uint8_t src_op) +{ + acpigen_write_store(); + acpigen_emit_byte(src_op); + acpigen_emit_byte(INDEX_OP); + acpigen_emit_namestring(package_name); + acpigen_write_integer(element); + acpigen_emit_byte(ZERO_OP); /* Ignore Index() Destination */ +} + +static void set_package_element_name(const char *package_name, unsigned int element, + const char *src) +{ + acpigen_write_store(); + acpigen_emit_namestring(src); + acpigen_emit_byte(INDEX_OP); + acpigen_emit_namestring(package_name); + acpigen_write_integer(element); + acpigen_emit_byte(ZERO_OP); /* Ignore Index() Destination */ +} + +/* PPI function is passed in src_op. Converted to Local2. Clobbers Local1 and Local2 */ +static void verify_supported_ppi(uint8_t src_op) +{ + /* + * Old OSes incorrectly pass a Buffer instead of a Package. + * See TCG Physical Presence Interface Specification Chapter 8.1.2 for details. + */ + + /* If (ObjectType(Arg3) == Package) */ + acpigen_write_store(); + acpigen_emit_byte(OBJ_TYPE_OP); + acpigen_emit_byte(src_op); + acpigen_emit_byte(LOCAL1_OP); + acpigen_write_if_lequal_op_int(LOCAL1_OP, 4); + acpigen_get_package_op_element(src_op, 0, LOCAL2_OP); + acpigen_pop_len(); + + /* If (ObjectType(Arg3) == Buffer) */ + acpigen_write_store(); + acpigen_emit_byte(OBJ_TYPE_OP); + acpigen_emit_byte(src_op); + acpigen_emit_byte(LOCAL1_OP); + acpigen_write_if_lequal_op_int(LOCAL1_OP, 3); + acpigen_write_to_integer(src_op, LOCAL2_OP); + acpigen_pop_len(); + + /* Check if it's a valid PPI function */ + acpigen_write_store(); + acpigen_emit_namestring("^FSUP"); + acpigen_emit_byte(LOCAL2_OP); + acpigen_emit_byte(CONFIG(TPM1) ? ONE_OP : ZERO_OP); + acpigen_emit_byte(LOCAL1_OP); + acpigen_write_if_lequal_op_int(LOCAL1_OP, 0); + + /* + * Note: Must fake success for 1-4, 6-13, 15-16, 19-20 + * see "Trusted Execution Environment ACPI Profile" + * + * Even if not available, the TPM 1.2 PPI must be advertised as + * supported. Tests showed that Windows relies on it, even when + * a TPM2.0 is present! + * The functions aren't actually used when a TPM2.0 is present... + * Without this the Windows TPM 2.0 stack refuses to work. + */ + + /* + * Check if we have TPM1.2 but a TPM2 PPI function was called + * or if we have TPM2.0 but a TPM1.2 PPI function was called. + */ + acpigen_write_store(); + acpigen_emit_namestring("^FSUP"); + acpigen_emit_byte(LOCAL2_OP); + acpigen_emit_byte(CONFIG(TPM1) ? ZERO_OP : ONE_OP); + acpigen_emit_byte(LOCAL1_OP); + + acpigen_write_if_lequal_op_int(LOCAL1_OP, 1); + acpigen_write_return_integer(PPI2_RET_SUCCESS); /* As per TPM spec */ + acpigen_pop_len(); + acpigen_write_return_integer(PPI2_RET_NOT_SUPPORTED); + + acpigen_pop_len(); +} + +/* TPM PPI functions */ + +static void tpm_ppi_func0_cb(void *arg) +{ + /* Functions 1-8. */ + u8 buf[] = {0xff, 0x01}; + acpigen_write_return_byte_buffer(buf, 2); +} + + /* + * PPI 1.0: 2.1.1 Get Physical Presence Interface Version + * + * Arg2 (Integer): Function Index = 1 + * Arg3 (Package): Arguments = Empty Package + * + * Returns: Type: String + */ +static void tpm_ppi_func1_cb(void *arg) +{ + if (CONFIG(TPM2)) + /* Interface version: 1.3 */ + acpigen_write_return_string("1.3"); + else + /* Interface version: 1.2 */ + acpigen_write_return_string("1.2"); +} + +/* + * Submit TPM Operation Request to Pre-OS Environment [Windows optional] + * PPI 1.0: 2.1.3 Submit TPM Operation Request to Pre-OS Environment + * + * Supported Revisions: 1 + * Arg1 (Integer): Revision + * Arg2 (Integer): Function Index = 2 + * Arg3 (Package): Arguments = Package: Type: Integer + * Operation Value of the Request + * + * Returns: Type: Integer + * 0: Success + * 1: Operation Value of the Request Not Supported + * 2: General Failure + */ +static void tpm_ppi_func2_cb(void *arg) +{ + /* Revision 1 */ + acpigen_write_to_integer(ARG1_OP, LOCAL0_OP); + acpigen_write_if_lequal_op_int(LOCAL0_OP, 1); + + /* Local2 = ConvertAndVerify(Arg3) */ + verify_supported_ppi(ARG3_OP); + + acpigen_write_store_op_to_namestr(LOCAL2_OP, "^CMDR"); + acpigen_write_store_op_to_namestr(ZERO_OP, "^OARG"); + acpigen_write_store_op_to_namestr(ZERO_OP, "^USER"); + + acpigen_write_return_integer(PPI2_RET_SUCCESS); + acpigen_pop_len(); + + acpigen_write_return_integer(PPI2_RET_GENERAL_FAILURE); +} + +/* + * PPI 1.0: 2.1.4 Get Pending TPM Operation Requested By the OS + * + * Supported Revisions: 1, 2 + * Arg1 (Integer): Revision + * Arg2 (Integer): Function Index = 3 + * Arg3 (Package): Empty package + * + * Returns: Type: Package(Integer, Integer, Integer (optional)) + * Integer 1: + * 0: Success + * 1: General Failure + * Integer 2: + * Pending TPM operation requested by OS + * Integer 3: + * Pending TPM operation argument requested by OS + */ +static void tpm_ppi_func3_cb(void *arg) +{ + acpigen_write_store(); + acpigen_write_integer(PPI3_RET_GENERAL_FAILURE); + acpigen_emit_byte(LOCAL0_OP); + + /* ^TPM3 [0] = PPI3_RET_GENERAL_FAILURE */ + set_package_element_op("^TPM3", 0, LOCAL0_OP); + + /* ^TPM2 [0] = PPI3_RET_GENERAL_FAILURE */ + set_package_element_op("^TPM2", 0, LOCAL0_OP); + + acpigen_write_to_integer(ARG1_OP, LOCAL0_OP); + + /* Revision 1 */ + acpigen_write_if_lequal_op_int(LOCAL0_OP, 1); + + /* ^TPM2 [0] = PPI3_RET_SUCCESS */ + acpigen_write_store(); + acpigen_write_integer(PPI3_RET_SUCCESS); + acpigen_emit_byte(LOCAL1_OP); + set_package_element_op("^TPM2", 0, LOCAL1_OP); + + /* ^TPM2 [1] = ^CMDR */ + set_package_element_name("^TPM2", 1, "^CMDR"); + + acpigen_emit_byte(RETURN_OP); + acpigen_emit_namestring("^TPM2"); + acpigen_pop_len(); + + /* + * A return value of {0, 23, 1} indicates that operation 23 + * with argument 1 is pending. + */ + + /* Revision 2 */ + acpigen_write_if_lequal_op_int(LOCAL0_OP, 2); + + /* ^TPM3 [0] = PPI3_RET_SUCCESS */ + acpigen_write_store(); + acpigen_write_integer(PPI3_RET_SUCCESS); + acpigen_emit_byte(LOCAL1_OP); + set_package_element_op("^TPM3", 0, LOCAL1_OP); + + /* ^TPM3 [1] = ^CMDR */ + set_package_element_name("^TPM3", 1, "^CMDR"); + + /* ^TPM3 [2] = ^OARG */ + set_package_element_name("^TPM3", 2, "^OARG"); + + acpigen_emit_byte(RETURN_OP); + acpigen_emit_namestring("^TPM3"); + acpigen_pop_len(); + + acpigen_emit_byte(RETURN_OP); + acpigen_emit_namestring("^TPM3"); +} + +/* + * PPI 1.0: 2.1.5 Get Platform-Specific Action to Transition to Pre-OS Environment + * + * Arg1 (Integer): Revision + * Arg2 (Integer): Function Index = 4 + * Arg3 (Package): Empty package + * + * Returns: Type: Integer + * 0: None + * 1: Shutdown + * 2: Reboot + * 3: Vendor specific + */ +static void tpm_ppi_func4_cb(void *arg) +{ + /* Pre-OS transition method: reboot. */ + acpigen_write_return_byte(PPI4_RET_REBOOT); +} + +/* + * PPI 1.0: 2.1.6 Return TPM Operation Response to OS Environment + * + * Supported Revisions: 1 + * Arg1 (Integer): Revision + * Arg2 (Integer): Function Index = 5 + * Arg3 (Package): Empty package + * + * Returns: Type: Package(Integer, Integer, Integer) + * Integer 1: + * 0: Success + * 1: General Failure + * Integer 2: + * Most recent TPM operation requested by OS + * Integer 3: + * Response to most recent TPM operation requested by OS + */ +static void tpm_ppi_func5_cb(void *arg) +{ + /* ^TPM3 [0] = PPI5_RET_GENERAL_FAILURE */ + acpigen_write_store(); + acpigen_write_integer(PPI5_RET_GENERAL_FAILURE); + acpigen_emit_byte(LOCAL1_OP); + set_package_element_op("^TPM3", 0, LOCAL1_OP); + + acpigen_write_to_integer(ARG1_OP, LOCAL0_OP); + + /* Revision 1 */ + acpigen_write_if_lequal_op_int(LOCAL0_OP, 1); + + /* ^TPM3 [0] = PPI5_RET_SUCCESS */ + acpigen_write_store(); + acpigen_write_integer(PPI5_RET_SUCCESS); + acpigen_emit_byte(LOCAL1_OP); + set_package_element_op("^TPM3", 0, LOCAL1_OP); + + /* ^TPM3 [1] = ^LCMD */ + set_package_element_name("^TPM3", 1, "^LCMD"); + + /* ^TPM3 [2] = ^RESU */ + set_package_element_name("^TPM3", 2, "^RESU"); + + acpigen_pop_len(); + + acpigen_emit_byte(RETURN_OP); + acpigen_emit_namestring("^TPM3"); +} + +/* + * PPI 1.2: 2.1.6 Submit preferred user language [Windows optional] + * + * Arg1 (Integer): Revision + * Arg2 (Integer): Function Index = 5 + * Arg3 (Package): Empty package + */ +static void tpm_ppi_func6_cb(void *arg) +{ + /* + * Set preferred user language: deprecated and must return 3 aka + * "not implemented". + */ + acpigen_write_return_byte(PPI6_RET_NOT_IMPLEMENTED); +} + +/* + * PPI 1.2: 2.1.7 Submit TPM Operation Request to Pre-OS Environment 2 + * + * Supported Revisions: 1, 2 + * Arg1 (Integer): Revision + * Arg2 (Integer): Function Index = 7 + * Arg3 (Package): Integer + * + * Returns: Type: Integer + * 0: Success + * 1: Not implemented + * 2: General Failure + * 3: Blocked by current BIOS settings + */ +static void tpm_ppi_func7_cb(void *arg) +{ + acpigen_write_to_integer(ARG1_OP, LOCAL0_OP); + + /* Local2 = ConvertAndVerify(Arg3) */ + verify_supported_ppi(ARG3_OP); + + /* If (ObjectType(Arg3) == Buffer) */ + acpigen_write_store(); + acpigen_emit_byte(OBJ_TYPE_OP); + acpigen_emit_byte(ARG3_OP); + acpigen_emit_byte(LOCAL1_OP); + acpigen_write_if_lequal_op_int(LOCAL1_OP, 3); + + /* Enforce use of Revision 1 that doesn't take an optional argument. */ + + /* Local0 = One */ + acpigen_write_store(); + acpigen_emit_byte(ONE_OP); + acpigen_emit_byte(LOCAL0_OP); + + acpigen_pop_len(); + + // FIXME: Only advertise supported functions + + /* Revision 1 */ + acpigen_write_if_lequal_op_int(LOCAL0_OP, 1); + + /* ^CMDR = Local2 */ + acpigen_write_store_op_to_namestr(LOCAL2_OP, "^CMDR"); + + /* ^OARG = Zero */ + acpigen_write_store_op_to_namestr(ZERO_OP, "^OARG"); + + acpigen_write_return_byte(PPI7_RET_SUCCESS); + acpigen_pop_len(); + + /* Revision 2 */ + acpigen_write_if_lequal_op_int(LOCAL0_OP, 2); + /* ^CMDR = Local2 */ + acpigen_write_store_op_to_namestr(LOCAL2_OP, "^CMDR"); + + /* ^OARG = Arg3 [1] */ + acpigen_get_package_op_element(ARG3_OP, 1, LOCAL3_OP); + acpigen_write_store(); + acpigen_emit_byte(LOCAL3_OP); + acpigen_emit_namestring("^OARG"); + + acpigen_write_return_byte(PPI7_RET_SUCCESS); + acpigen_pop_len(); + + acpigen_write_return_byte(PPI7_RET_GENERAL_FAILURE); +} + +/* + * PPI 1.2: 2.1.8 Get User Confirmation Status for Operation + * + * Returns if a command is supported and allowed by firmware + * Supported Revisions: 1 + * Arg1 (Integer): Revision + * Arg2 (Integer): Function Index = 7 + * Arg3 (Package): Integer + * + * Returns: Type: Integer + * 0: Not implemented + * 1: BIOS only + * 2: Blocked for OS by BIOS settings + * 3: Allowed and physical present user required + * 4: Allowed and physical present user not required + */ +static void tpm_ppi_func8_cb(void *arg) +{ + acpigen_write_to_integer(ARG1_OP, LOCAL0_OP); + + /* Revision 1 */ + acpigen_write_if_lequal_op_int(LOCAL0_OP, 1); + acpigen_get_package_op_element(ARG3_OP, 0, LOCAL2_OP); + + /* Check if it's a valid PPI function */ + acpigen_write_store(); + acpigen_emit_namestring("^FSUP"); + acpigen_emit_byte(LOCAL2_OP); + acpigen_emit_byte(CONFIG(TPM1) ? ONE_OP : ZERO_OP); + acpigen_emit_byte(LOCAL1_OP); + acpigen_write_if_lequal_op_int(LOCAL1_OP, 0); + acpigen_write_return_byte(0); /* Not implemented */ + acpigen_pop_len(); + + // FIXME: Only advertise supported functions + + if (CONFIG(TPM1)) { + /* + * Some functions do not require PP depending on configuration. + * Those aren't listed here, so the 'required PP' is always set for those. + */ + static const u32 tpm1_funcs[] = { + TPM_NOOP, + TPM_SET_NOPPICLEAR_TRUE, + TPM_SET_NOPPIMAINTAINANCE_TRUE, + TPM_SET_NOPPIPROVISION_TRUE, + }; + for (size_t i = 0; i < ARRAY_SIZE(tpm1_funcs); i++) { + acpigen_write_if_lequal_op_int(LOCAL2_OP, tpm1_funcs[i]); + acpigen_write_return_integer(PPI8_RET_ALLOWED); + acpigen_pop_len(); /* Pop : If */ + } + } else if (CONFIG(TPM2)) { + /* + * Some functions do not require PP depending on configuration. + * Those aren't listed here, so the 'required PP' is always set for those. + */ + static const u32 tpm2_funcs[] = { + TPM2_NOOP, + TPM2_SET_PP_REQUIRED_FOR_CLEAR_TRUE, + TPM2_SET_PP_REQUIRED_FOR_CHANGE_PCRS_TRUE, + TPM2_SET_PP_REQUIRED_FOR_TURN_ON_TRUE, + TPM2_SET_PP_REQUIRED_FOR_CHANGE_EPS_TRUE, + TPM2_SET_PP_REQUIRED_FOR_TURN_OFF_TRUE, + TPM2_SET_PP_REQUIRED_FOR_ENABLE_BLOCK_SID_TRUE, + TPM2_SET_PP_REQUIRED_FOR_DISABLE_BLOCK_SID_TRUE, + }; + for (size_t i = 0; i < ARRAY_SIZE(tpm2_funcs); i++) { + acpigen_write_if_lequal_op_int(LOCAL2_OP, tpm2_funcs[i]); + acpigen_write_return_integer(PPI8_RET_ALLOWED); + acpigen_pop_len(); /* Pop : If */ + } + } + acpigen_write_return_integer(PPI8_RET_ALLOWED_WITH_PP); + + acpigen_pop_len(); + + acpigen_write_return_integer(PPI8_RET_NOT_IMPLEMENTED); +} + +static void (*tpm_ppi_callbacks[])(void *) = { + tpm_ppi_func0_cb, + tpm_ppi_func1_cb, + tpm_ppi_func2_cb, + tpm_ppi_func3_cb, + tpm_ppi_func4_cb, + tpm_ppi_func5_cb, + tpm_ppi_func6_cb, + tpm_ppi_func7_cb, + tpm_ppi_func8_cb, +}; + +static void tpm_mci_func0_cb(void *arg) +{ + /* Function 1. */ + acpigen_write_return_singleton_buffer(0x3); +} +static void tpm_mci_func1_cb(void *arg) +{ + /* Just return success. */ + acpigen_write_return_byte(0); +} + +static void (*tpm_mci_callbacks[])(void *) = { + tpm_mci_func0_cb, + tpm_mci_func1_cb, +}; + +void tpm_ppi_acpi_fill_ssdt(const struct device *dev) +{ + struct cb_tpm_ppi_payload_handshake *ppib; + + static const struct fieldlist list[] = { + FIELDLIST_OFFSET(0x100),// FIXME: Add support for func + FIELDLIST_NAMESTR("PPIN", 8),// Not used + FIELDLIST_NAMESTR("PPIP", 32),// Not used + FIELDLIST_NAMESTR("RESU", 32),// Result of the last operation (TPM error code) + FIELDLIST_NAMESTR("CMDR", 32),// The command requested by OS. 0 for NOP + FIELDLIST_NAMESTR("OARG", 32),// The command optional argument requested by OS + FIELDLIST_NAMESTR("LCMD", 32),// The last command requested by OS. + FIELDLIST_NAMESTR("FRET", 32),// Not used + }; + static const u8 tpm1_funcs[] = { + TPM_NOOP, + TPM_ENABLE, + TPM_DISABLE, + TPM_ACTIVATE, + TPM_DEACTIVATE, + TPM_CLEAR, + TPM_ENABLE_ACTIVATE, + TPM_DEACTIVATE_DISABLE, + TPM_SETOWNERINSTALL_TRUE, + TPM_SETOWNERINSTALL_FALSE, + TPM_ENABLE_ACTIVATE_SETOWNERINSTALL_TRUE, + TPM_SETOWNERINSTALL_FALSE_DEACTIVATE_DISABLE, + TPM_CLEAR_ENABLE_ACTIVATE, + TPM_SET_NOPPIPROVISION_FALSE, + TPM_SET_NOPPIPROVISION_TRUE, + TPM_ENABLE_ACTIVE_CLEAR, + TPM_ENABLE_ACTIVE_CLEAR_ENABLE_ACTIVE, + }; + static const u8 tpm2_funcs[] = { + TPM2_NOOP, + TPM2_ENABLE, + TPM2_DISABLE, + TPM2_CLEAR, + TPM2_CLEAR_ENABLE_ACTIVE, + TPM2_SET_PP_REQUIRED_FOR_CLEAR_TRUE, + TPM2_SET_PP_REQUIRED_FOR_CLEAR_FALSE, + TPM2_ENABLE_CLEAR, + TPM2_ENABLE_CLEAR2, + TPM2_SET_PCR_BANKS, + TPM2_CHANGE_EPS, + TPM2_SET_PP_REQUIRED_FOR_CHANGE_PCRS_FALSE, + TPM2_SET_PP_REQUIRED_FOR_CHANGE_PCRS_TRUE, + TPM2_SET_PP_REQUIRED_FOR_TURN_ON_FALSE, + TPM2_SET_PP_REQUIRED_FOR_TURN_ON_TRUE, + TPM2_SET_PP_REQUIRED_FOR_TURN_OFF_FALSE, + TPM2_SET_PP_REQUIRED_FOR_TURN_OFF_TRUE, + TPM2_SET_PP_REQUIRED_FOR_CHANGE_EPS_FALSE, + TPM2_SET_PP_REQUIRED_FOR_CHANGE_EPS_TRUE, + TPM2_LOG_ALL_DIGEST, + TPM2_DISABLE_ENDORSMENT_ENABLE_STORAGE_HISTORY, + TPM2_ENABLE_BLOCK_SID, + TPM2_DISABLE_BLOCK_SID, + TPM2_SET_PP_REQUIRED_FOR_ENABLE_BLOCK_SID_TRUE, + TPM2_SET_PP_REQUIRED_FOR_ENABLE_BLOCK_SID_FALSE, + TPM2_SET_PP_REQUIRED_FOR_DISABLE_BLOCK_SID_TRUE, + TPM2_SET_PP_REQUIRED_FOR_DISABLE_BLOCK_SID_FALSE, + }; + + /* + * On hot reset/ACPI S3 the contents are preserved. + */ + ppib = (void *)cbmem_add(CBMEM_ID_TPM_PPI, sizeof(*ppib)); + if (!ppib) { + printk(BIOS_ERR, "PPI: Failed to add CBMEM\n"); + return; + } + printk(BIOS_DEBUG, "PPI: Pending OS request: 0x%x (0x%x)\n", ppib->pprq, ppib->pprm); + printk(BIOS_DEBUG, "PPI: OS response: CMD 0x%x = 0x%x\n", ppib->lppr, ppib->pprp); + + /* Clear unsupported fields */ + ppib->next_step = 0; + ppib->ppin = 1; // Not used by ACPI. Read by EDK-2, must be 1. + ppib->ppip = 0; + ppib->fret = 0; + ppib->next_step = 0; + + bool found = false; + /* Fill in defaults, the TPM command executor may overwrite this list */ + memset(ppib->func, 0, sizeof(ppib->func)); + if (CONFIG(TPM1)) { + for (size_t i = 0; i < ARRAY_SIZE(tpm1_funcs); i++) { + ppib->func[tpm1_funcs[i]] = 1; + if (ppib->pprq == tpm1_funcs[i]) + found = true; + } + } else { + + for (size_t i = 0; i < ARRAY_SIZE(tpm2_funcs); i++) { + ppib->func[tpm2_funcs[i]] = 1; + if (ppib->pprq == tpm2_funcs[i]) + found = true; + } + } + if (!found) { + // Set sane defaults + ppib->pprq = 0; + ppib->pprm = 0; + ppib->pprp = 0; + } + + /* Physical Presence OpRegion */ + struct opregion opreg = OPREGION("PPOP", SYSTEMMEMORY, (uintptr_t)ppib, + sizeof(*ppib)); + + acpigen_write_opregion(&opreg); + acpigen_write_field(opreg.name, list, ARRAY_SIZE(list), + FIELD_ANYACC | FIELD_NOLOCK | FIELD_PRESERVE); + + acpigen_write_name("TPM2"); + acpigen_write_package(2); + acpigen_write_dword(0); + acpigen_write_dword(0); + acpigen_pop_len(); /* Package */ + + acpigen_write_name("TPM3"); + acpigen_write_package(3); + acpigen_write_dword(0); + acpigen_write_dword(0); + acpigen_write_dword(0); + acpigen_pop_len(); /* Package */ + + /* + * Returns One if the firmware implements this function. + * + * Arg0: Integer PPI function + */ + acpigen_write_method_serialized("FUNC", 1); + + acpigen_write_to_integer(ARG0_OP, LOCAL0_OP); + acpigen_write_to_integer(ARG1_OP, LOCAL1_OP); + acpigen_write_if(); + acpigen_emit_byte(LGREATER_OP); + acpigen_emit_byte(LOCAL0_OP); + acpigen_write_integer(VENDOR_SPECIFIC_OFFSET); + acpigen_write_return_integer(0); + acpigen_pop_len(); /* If */ + + /* TPPF = CreateField("PPOP", Local0) */ + acpigen_emit_byte(CREATE_BYTE_OP); + acpigen_emit_namestring("PPOP"); + acpigen_emit_byte(LOCAL0_OP); + acpigen_emit_namestring("TPPF"); + + /* Local0 = ToInteger("TPPF") */ + acpigen_emit_byte(TO_INTEGER_OP); + acpigen_emit_namestring("TPPF"); + acpigen_emit_byte(LOCAL0_OP); + + acpigen_write_return_op(LOCAL0_OP); + acpigen_pop_len(); /* Method */ + + /* + * Returns One if the PPI spec supports this functions. + * That doesn't necessarily mean that the firmware implemtents it, or the + * TPM can execute the function. + * + * Arg0: Integer PPI function + * Arg1: Integer TPMversion (0: TPM2, 1: TPM1.2) + */ + acpigen_write_method("FSUP", 2); + + acpigen_write_to_integer(ARG0_OP, LOCAL0_OP); + acpigen_write_to_integer(ARG1_OP, LOCAL1_OP); + acpigen_write_if(); + acpigen_emit_byte(LGREATER_OP); + acpigen_emit_byte(LOCAL0_OP); + acpigen_write_integer(VENDOR_SPECIFIC_OFFSET); + acpigen_write_return_integer(0); + acpigen_pop_len(); /* If */ + + acpigen_write_if_lequal_op_int(LOCAL1_OP, 1); + for (size_t i = 0; i < ARRAY_SIZE(tpm1_funcs); i++) { + acpigen_write_if_lequal_op_int(LOCAL0_OP, tpm1_funcs[i]); + acpigen_write_return_integer(1); + acpigen_pop_len(); /* Pop : If */ + } + acpigen_pop_len(); /* If */ + + acpigen_write_if_lequal_op_int(LOCAL1_OP, 0); + + for (size_t i = 0; i < ARRAY_SIZE(tpm2_funcs); i++) { + acpigen_write_if_lequal_op_int(LOCAL0_OP, tpm2_funcs[i]); + acpigen_write_return_integer(1); + acpigen_pop_len(); /* Pop : If */ + } + acpigen_pop_len(); /* If */ + + acpigen_write_return_integer(0); + acpigen_pop_len(); /* Method */ + + /* + * _DSM method + */ + struct dsm_uuid ids[] = { + /* Physical presence interface. + * This is used to submit commands like "Clear TPM" to + * be run at next reboot provided that user confirms + * them. + */ + DSM_UUID(TPM_PPI_UUID, &tpm_ppi_callbacks[0], + ARRAY_SIZE(tpm_ppi_callbacks), NULL), + /* Memory clearing on boot: just a dummy. */ + DSM_UUID(TPM_MCI_UUID, &tpm_mci_callbacks[0], + ARRAY_SIZE(tpm_mci_callbacks), NULL), + }; + + acpigen_write_dsm_uuid_arr(ids, ARRAY_SIZE(ids)); +} + +void lb_tpm_ppi(struct lb_header *header) +{ + struct lb_tpm_physical_presence *tpm_ppi; + void *ppib; + + ppib = cbmem_find(CBMEM_ID_TPM_PPI); + if (!ppib) + return; + + tpm_ppi = (struct lb_tpm_physical_presence *)lb_new_record(header); + tpm_ppi->tag = LB_TAG_TPM_PPI_HANDOFF; + tpm_ppi->size = sizeof(*tpm_ppi); + tpm_ppi->ppi_address = (uintptr_t)ppib; + tpm_ppi->tpm_version = CONFIG(TPM1) ? LB_TPM_VERSION_TPM_VERSION_1_2 : + LB_TPM_VERSION_TPM_VERSION_2; + + tpm_ppi->ppi_version = BCD(1, 3); +} diff --git a/src/drivers/tpm/tpm_ppi.h b/src/drivers/tpm/tpm_ppi.h index 7662386575..4d468ba84d 100644 --- a/src/drivers/tpm/tpm_ppi.h +++ b/src/drivers/tpm/tpm_ppi.h @@ -4,6 +4,7 @@ #define _TPM_PPI_H_ #include <device/device.h> +#include <boot/coreboot_tables.h> #if CONFIG(HAVE_ACPI_TABLES) void tpm_ppi_acpi_fill_ssdt(const struct device *dev); @@ -13,7 +14,6 @@ static inline void tpm_ppi_acpi_fill_ssdt(const struct device *dev) } #endif - /* Return codes */ /* Function 2 */ #define PPI2_RET_SUCCESS 0 @@ -55,4 +55,92 @@ static inline void tpm_ppi_acpi_fill_ssdt(const struct device *dev) /* TCG Memory Clear Interface */ #define TPM_MCI_UUID "376054ed-cc13-4675-901c-4756d7f2d45d" +/* + * Physical Presence Interface Specification Version 1.30 Revision 00.52 + * Table 1 Physical Presence Interface Operation Summary for TPM 1.2 + */ +#define TPM_NOOP 0 +#define TPM_ENABLE 1 +#define TPM_DISABLE 2 +#define TPM_ACTIVATE 3 +#define TPM_DEACTIVATE 4 +#define TPM_CLEAR 5 +#define TPM_ENABLE_ACTIVATE 6 +#define TPM_DEACTIVATE_DISABLE 7 +#define TPM_SETOWNERINSTALL_TRUE 8 +#define TPM_SETOWNERINSTALL_FALSE 9 +#define TPM_ENABLE_ACTIVATE_SETOWNERINSTALL_TRUE 10 +#define TPM_SETOWNERINSTALL_FALSE_DEACTIVATE_DISABLE 11 +#define TPM_CLEAR_ENABLE_ACTIVATE 14 +#define TPM_SET_NOPPIPROVISION_FALSE 15 +#define TPM_SET_NOPPIPROVISION_TRUE 16 +#define TPM_SET_NOPPICLEAR_FALSE 17 +#define TPM_SET_NOPPICLEAR_TRUE 18 +#define TPM_SET_NOPPIMAINTAINANCE_FALSE 19 +#define TPM_SET_NOPPIMAINTAINANCE_TRUE 20 +#define TPM_ENABLE_ACTIVE_CLEAR 21 +#define TPM_ENABLE_ACTIVE_CLEAR_ENABLE_ACTIVE 22 + +/* + * Physical Presence Interface Specification Version 1.30 Revision 00.52 + * Table 2 Physical Presence Interface Operation Summary for TPM 2.0 + */ +#define TPM2_NOOP 0 +#define TPM2_ENABLE 1 +#define TPM2_DISABLE 2 +#define TPM2_CLEAR 5 +#define TPM2_CLEAR_ENABLE_ACTIVE 14 +#define TPM2_SET_PP_REQUIRED_FOR_CLEAR_TRUE 17 +#define TPM2_SET_PP_REQUIRED_FOR_CLEAR_FALSE 18 +#define TPM2_ENABLE_CLEAR 21 +#define TPM2_ENABLE_CLEAR2 22 +#define TPM2_SET_PCR_BANKS 23 +#define TPM2_CHANGE_EPS 24 +#define TPM2_SET_PP_REQUIRED_FOR_CHANGE_PCRS_FALSE 25 +#define TPM2_SET_PP_REQUIRED_FOR_CHANGE_PCRS_TRUE 26 +#define TPM2_SET_PP_REQUIRED_FOR_TURN_ON_FALSE 27 +#define TPM2_SET_PP_REQUIRED_FOR_TURN_ON_TRUE 28 +#define TPM2_SET_PP_REQUIRED_FOR_TURN_OFF_FALSE 29 +#define TPM2_SET_PP_REQUIRED_FOR_TURN_OFF_TRUE 30 +#define TPM2_SET_PP_REQUIRED_FOR_CHANGE_EPS_FALSE 31 +#define TPM2_SET_PP_REQUIRED_FOR_CHANGE_EPS_TRUE 32 +#define TPM2_LOG_ALL_DIGEST 33 +#define TPM2_DISABLE_ENDORSMENT_ENABLE_STORAGE_HISTORY 34 +#define TPM2_ENABLE_BLOCK_SID 96 +#define TPM2_DISABLE_BLOCK_SID 97 +#define TPM2_SET_PP_REQUIRED_FOR_ENABLE_BLOCK_SID_TRUE 98 +#define TPM2_SET_PP_REQUIRED_FOR_ENABLE_BLOCK_SID_FALSE 99 +#define TPM2_SET_PP_REQUIRED_FOR_DISABLE_BLOCK_SID_TRUE 100 +#define TPM2_SET_PP_REQUIRED_FOR_DISABLE_BLOCK_SID_FALSE 101 + +#define VENDOR_SPECIFIC_OFFSET 0x80 + + /* + * The layout of the buffer matches the QEMU virtual memory device that is generated + * by QEMU. See files 'hw/i386/acpi-build.c' and 'include/hw/acpi/tpm.h' for details. + */ +struct cb_tpm_ppi_payload_handshake { + uint8_t func[256]; /* Firmware sets values for each supported operation. + * See defined values below. */ + uint8_t ppin; /* SMI interrupt to use. Set by firmware. + * Not supported. */ + uint32_t ppip; /* ACPI function index to pass to SMM code. + * Set by ACPI. Not supported. */ + uint32_t pprp; /* Result of last executed operation. + * Set by firmware. See function index 5 for values. */ + uint32_t pprq; /* Operation request number to execute. + * See 'Physical Presence Interface Operation Summary' + * tables in specs. Set by ACPI. */ + uint32_t pprm; /* Operation request optional parameter. + * Values depend on operation. Set by ACPI. */ + uint32_t lppr; /* Last executed operation request number. + * Copied from pprq field by firmware. */ + uint32_t fret; /* Result code from SMM function. Not supported. */ + uint8_t res1[0x040]; /* Reserved */ + uint8_t next_step; /* Operation to execute after reboot by firmware. + * Used by firmware. */ +} __packed; + +void lb_tpm_ppi(struct lb_header *header); + #endif /* _TPM_PPI_H_ */ diff --git a/src/lib/coreboot_table.c b/src/lib/coreboot_table.c index 996e76ec11..9e0d589250 100644 --- a/src/lib/coreboot_table.c +++ b/src/lib/coreboot_table.c @@ -11,6 +11,7 @@ #include <version.h> #include <boardid.h> #include <device/device.h> +#include <drivers/tpm/tpm_ppi.h> #include <fmap.h> #include <fw_config.h> #include <stdlib.h> @@ -526,6 +527,9 @@ static uintptr_t write_coreboot_table(uintptr_t rom_table_end) /* Board configuration information (including straps) */ lb_board_config(head); + if (CONFIG(TPM_PPI)) + lb_tpm_ppi(head); + /* Add architecture records. */ lb_arch_add_records(head); |