aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/commonlib/include/commonlib/cbmem_id.h1
-rw-r--r--src/commonlib/include/commonlib/coreboot_tables.h24
-rw-r--r--src/drivers/tpm/Kconfig11
-rw-r--r--src/drivers/tpm/Makefile.inc4
-rw-r--r--src/drivers/tpm/ppi.c726
-rw-r--r--src/drivers/tpm/tpm_ppi.h90
-rw-r--r--src/lib/coreboot_table.c4
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);