summaryrefslogtreecommitdiff
path: root/src/drivers
diff options
context:
space:
mode:
authorChristian Walter <christian.walter@9elements.com>2019-07-05 19:46:30 +0200
committerPhilipp Deppenwiese <zaolin.daisuki@gmail.com>2019-07-31 10:58:36 +0000
commit7706a04c603474400234cc72a27a61070845eca2 (patch)
treed1c5c05697f415ba825e4b3ab63a4277c36f458e /src/drivers
parentc703814e951376bef0945934dccff9158d54db7d (diff)
downloadcoreboot-7706a04c603474400234cc72a27a61070845eca2.tar.xz
drivers/crb: Add CRB driver for TPM2 support
Add the Command Response Buffer which is defined in the TPM 2.0 Specs. CRB can be specified with MAINBOARD_HAS_CRB_TPM, even though it is actually SoC/SB specific. Change-Id: I477e45963fe3cdbc02cda9ae99c19142747e4b46 Signed-off-by: Christian Walter <christian.walter@9elements.com> Reviewed-on: https://review.coreboot.org/c/coreboot/+/34106 Tested-by: build bot (Jenkins) <no-reply@coreboot.org> Reviewed-by: Philipp Deppenwiese <zaolin.daisuki@gmail.com>
Diffstat (limited to 'src/drivers')
-rw-r--r--src/drivers/crb/Kconfig17
-rw-r--r--src/drivers/crb/Makefile.inc5
-rw-r--r--src/drivers/crb/chip.h22
-rw-r--r--src/drivers/crb/tis.c150
-rw-r--r--src/drivers/crb/tpm.c280
-rw-r--r--src/drivers/crb/tpm.h70
6 files changed, 544 insertions, 0 deletions
diff --git a/src/drivers/crb/Kconfig b/src/drivers/crb/Kconfig
new file mode 100644
index 0000000000..bfd8be06d6
--- /dev/null
+++ b/src/drivers/crb/Kconfig
@@ -0,0 +1,17 @@
+config CRB_TPM
+ bool
+ help
+ CRB TPM driver is enabled!
+
+config CRB_TPM_BASE_ADDRESS
+ hex
+ default 0xfed40000
+ help
+ Base Address of the CRB TPM Command Structure
+
+config MAINBOARD_HAS_CRB_TPM
+ bool
+ default n
+ select CRB_TPM
+ help
+ Mainboard has Command Response Buffer support
diff --git a/src/drivers/crb/Makefile.inc b/src/drivers/crb/Makefile.inc
new file mode 100644
index 0000000000..3f12b36923
--- /dev/null
+++ b/src/drivers/crb/Makefile.inc
@@ -0,0 +1,5 @@
+bootblock-$(CONFIG_CRB_TPM) += tis.c tpm.c
+verstage-$(CONFIG_CRB_TPM) += tis.c tpm.c
+romstage-$(CONFIG_CRB_TPM) += tis.c tpm.c
+ramstage-$(CONFIG_CRB_TPM) += tis.c tpm.c
+postcar-$(CONFIG_CRB_TPM) += tis.c tpm.c
diff --git a/src/drivers/crb/chip.h b/src/drivers/crb/chip.h
new file mode 100644
index 0000000000..8e74a68f97
--- /dev/null
+++ b/src/drivers/crb/chip.h
@@ -0,0 +1,22 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright 2014 Google Inc. All Rights Reserved.
+ *
+ * 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 DRIVERS_CRB_CHIP_H
+#define DRIVERS_CRB_CHIP_H
+
+typedef struct drivers_crb_config {
+} tpm_config_t;
+
+#endif /* DRIVERS_CRB_CHIP_H */
diff --git a/src/drivers/crb/tis.c b/src/drivers/crb/tis.c
new file mode 100644
index 0000000000..c110151766
--- /dev/null
+++ b/src/drivers/crb/tis.c
@@ -0,0 +1,150 @@
+/*
+ * 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 <arch/early_variables.h>
+#include <console/console.h>
+#include <security/tpm/tis.h>
+#include <arch/acpigen.h>
+#include <device/device.h>
+
+#include "tpm.h"
+#include "chip.h"
+
+static unsigned tpm_is_open CAR_GLOBAL;
+
+static const struct {
+ uint16_t vid;
+ uint16_t did;
+ const char *device_name;
+} dev_map[] = {
+ {0x1ae0, 0x0028, "CR50"},
+ {0xa13a, 0x8086, "Intel iTPM"}
+};
+
+static const char *tis_get_dev_name(struct tpm2_info *info)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(dev_map); i++)
+ if ((dev_map[i].vid == info->vendor_id) && (dev_map[i].did == info->device_id))
+ return dev_map[i].device_name;
+ return "Unknown";
+}
+
+
+int tis_open(void)
+{
+ if (car_get_var(tpm_is_open)) {
+ printk(BIOS_ERR, "%s called twice.\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+int tis_close(void)
+{
+ if (car_get_var(tpm_is_open)) {
+
+ /*
+ * Do we need to do something here, like waiting for a
+ * transaction to stop?
+ */
+ car_set_var(tpm_is_open, 0);
+ }
+
+ return 0;
+}
+
+int tis_init(void)
+{
+ struct tpm2_info info;
+
+ // Wake TPM up (if necessary)
+ if (tpm2_init() != 0)
+ return -1;
+
+ tpm2_get_info(&info);
+
+ printk(BIOS_INFO, "Initialized TPM device %s revision %d\n", tis_get_dev_name(&info),
+ info.revision);
+
+ return 0;
+}
+
+
+int tis_sendrecv(const uint8_t *sendbuf, size_t sbuf_size, uint8_t *recvbuf, size_t *rbuf_len)
+{
+ int len = tpm2_process_command(sendbuf, sbuf_size, recvbuf, *rbuf_len);
+
+ if (len == 0)
+ return -1;
+
+ *rbuf_len = len;
+
+ return 0;
+}
+
+#ifdef __RAMSTAGE__
+
+static void crb_tpm_fill_ssdt(struct device *dev)
+{
+ const char *path = acpi_device_path(dev);
+ if (!path) {
+ path = "\\_SB_.TPM";
+ printk(BIOS_DEBUG, "Using default TPM2 ACPI path: '%s'\n", path);
+ }
+
+ /* Device */
+ acpigen_write_device(path);
+
+ acpigen_write_name_string("_HID", "MSFT0101");
+ acpigen_write_name_string("_CID", "MSFT0101");
+
+ acpigen_write_name_integer("_UID", 1);
+
+ acpigen_write_STA(ACPI_STATUS_DEVICE_ALL_ON);
+
+ /* Resources */
+ acpigen_write_name("_CRS");
+ acpigen_write_resourcetemplate_header();
+ acpigen_write_mem32fixed(1, TPM_CRB_BASE_ADDRESS, 0x5000);
+
+ acpigen_write_resourcetemplate_footer();
+
+ acpigen_pop_len(); /* Device */
+}
+
+static const char *crb_tpm_acpi_name(const struct device *dev)
+{
+ return "TPM";
+}
+
+static struct device_operations crb_ops = {
+ .read_resources = DEVICE_NOOP,
+ .set_resources = DEVICE_NOOP,
+#if CONFIG(HAVE_ACPI_TABLES)
+ .acpi_name = crb_tpm_acpi_name,
+ .acpi_fill_ssdt_generator = crb_tpm_fill_ssdt,
+#endif
+
+};
+
+static void enable_dev(struct device *dev)
+{
+ dev->ops = &crb_ops;
+}
+
+struct chip_operations drivers_crb_ops = {CHIP_NAME("CRB TPM").enable_dev = enable_dev};
+
+#endif /* __RAMSTAGE__ */
diff --git a/src/drivers/crb/tpm.c b/src/drivers/crb/tpm.c
new file mode 100644
index 0000000000..0393417e74
--- /dev/null
+++ b/src/drivers/crb/tpm.c
@@ -0,0 +1,280 @@
+/*.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * This is a driver for a CRB Interface.
+ *
+ * The general flow looks like this:
+ *
+ * TPM starts in IDLE Mode
+ *
+ * IDLE --> READY --> Command Receiption
+ * ^ |
+ * | v
+ -- Cmd Complete <-- Command Execution
+ */
+
+#include <timer.h>
+#include <arch/early_variables.h>
+#include <console/console.h>
+#include <arch/mmio.h>
+#include <delay.h>
+#include <string.h>
+#include <endian.h>
+#include <soc/pci_devs.h>
+#include <device/pci_ops.h>
+
+#include "tpm.h"
+
+static struct control_area {
+ uint32_t request;
+ uint32_t status;
+ uint32_t cancel;
+ uint32_t start;
+ uint64_t interrupt_control;
+ uint32_t command_size;
+ void *command_bfr;
+ uint32_t response_size;
+ void *response_bfr;
+} control_area;
+
+static uint8_t cur_loc = 0;
+
+/* Read Control Area Structure back */
+static void crb_readControlArea(void)
+{
+ control_area.request = read32(CRB_REG(cur_loc, CRB_REG_REQUEST));
+ control_area.status = read32(CRB_REG(cur_loc, CRB_REG_STATUS));
+ control_area.cancel = read32(CRB_REG(cur_loc, CRB_REG_CANCEL));
+ control_area.interrupt_control = read64(CRB_REG(cur_loc, CRB_REG_INT_CTRL));
+ control_area.command_size = read32(CRB_REG(cur_loc, CRB_REG_CMD_SIZE));
+ control_area.command_bfr = (void *)(uint32_t)read64(CRB_REG(cur_loc, CRB_REG_CMD_ADDR));
+ control_area.response_size = read32(CRB_REG(cur_loc, CRB_REG_RESP_SIZE));
+ control_area.response_bfr =
+ (void *)(uint32_t)read64(CRB_REG(cur_loc, CRB_REG_RESP_ADDR));
+}
+
+/* Wait for Reg to be expected Value */
+static int crb_wait_for_reg32(const void *addr, uint32_t timeoutMs, uint32_t mask,
+ uint32_t expectedValue)
+{
+ uint32_t regValue;
+ struct stopwatch sw;
+
+ // Set up a timer which breaks the loop after timeout
+ stopwatch_init_msecs_expire(&sw, timeoutMs);
+
+ while (1) {
+ // Now check if the TPM is in IDLE mode
+ regValue = read32(addr);
+
+ if ((regValue & mask) == expectedValue)
+ return 0;
+
+ if (stopwatch_expired(&sw)) {
+ printk(BIOS_ERR,
+ "CRB_WAIT: Error - Returning Zero with RegValue: %08x, Mask: %08x, Expected: %08x\n",
+ regValue, mask, expectedValue);
+ return -1;
+ }
+ }
+}
+
+/* CRB PROBE
+ *
+ * Checks if the CRB Interface is ready
+ */
+static int crb_probe(void)
+{
+ uint64_t tpmStatus = read64(CRB_REG(cur_loc, CRB_REG_INTF_ID));
+ printk(BIOS_SPEW, "Interface ID Reg. %llx\n", tpmStatus);
+
+ if ((tpmStatus & CRB_INTF_REG_CAP_CRB) == 0) {
+ printk(BIOS_DEBUG, "TPM: CRB Interface is not supported.\n");
+ return -1;
+ }
+
+ if ((tpmStatus & (0xf)) != 1) {
+ printk(BIOS_DEBUG,
+ "TPM: CRB Interface is not active. System needs reboot in order to active TPM.\n");
+ write32(CRB_REG(cur_loc, CRB_REG_INTF_ID), CRB_INTF_REG_INTF_SEL);
+ return -1;
+ }
+
+ write32(CRB_REG(cur_loc, CRB_REG_INTF_ID), CRB_INTF_REG_INTF_SEL);
+ write32(CRB_REG(cur_loc, CRB_REG_INTF_ID), CRB_INTF_REG_INTF_LOCK);
+
+ return 0;
+}
+
+/*
+ * Get active Locality
+ *
+ * Get the active locality
+ */
+static uint8_t crb_activate_locality(void)
+{
+
+ uint8_t locality = (read8(CRB_REG(0, CRB_REG_LOC_STATE)) >> 2) & 0x07;
+ printk(BIOS_SPEW, "Active locality: %i\n", locality);
+
+ int rc = crb_wait_for_reg32(CRB_REG(locality, CRB_REG_LOC_STATE), 750,
+ LOC_STATE_LOC_ASSIGN, LOC_STATE_LOC_ASSIGN);
+ if (!rc && (locality == 0))
+ return locality;
+
+ if (rc)
+ write8(CRB_REG(locality, CRB_REG_LOC_CTRL), LOC_CTRL_REQ_ACCESS);
+
+
+ rc = crb_wait_for_reg32(CRB_REG(locality, CRB_REG_LOC_STATE), 750, LOC_STATE_LOC_ASSIGN,
+ LOC_STATE_LOC_ASSIGN);
+ if (rc) {
+ printk(BIOS_ERR, "TPM: Error - No Locality has been assigned TPM-wise.\n");
+ return 0;
+ }
+
+ rc = crb_wait_for_reg32(CRB_REG(locality, CRB_REG_LOC_STATE), 1500,
+ LOC_STATE_REG_VALID_STS, LOC_STATE_REG_VALID_STS);
+ if (rc) {
+ printk(BIOS_ERR, "TPM: Error - LOC_STATE Register %u contains errors.\n",
+ locality);
+ return 0;
+ }
+
+
+ return locality;
+}
+
+/* Switch Device into a Ready State */
+static int crb_switch_to_ready(void)
+{
+ /* Transition into ready state */
+ write8(CRB_REG(cur_loc, CRB_REG_REQUEST), 0x1);
+ int rc = crb_wait_for_reg32(CRB_REG(cur_loc, CRB_REG_REQUEST), 200,
+ CRB_REG_REQUEST_CMD_RDY, 0x0);
+ if (rc) {
+ printk(BIOS_ERR,
+ "TPM: Error - TPM did not transition into ready state in time.\n");
+ return -1;
+ }
+
+ /* Check TPM_CRB_CTRL_STS[0] to be "0" - no unrecoverable error */
+ rc = crb_wait_for_reg32(CRB_REG(cur_loc, CRB_REG_STATUS), 500, CRB_REG_STATUS_ERROR,
+ 0x0);
+ if (rc) {
+ printk(BIOS_ERR, "TPM: Fatal Error - Could not recover.\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * tpm2_init
+ *
+ * Even though the TPM does not need an initialization we check
+ * if the TPM responds and is in IDLE mode, which should be the
+ * normal bring up mode.
+ *
+ */
+int tpm2_init(void)
+{
+
+
+ if (crb_probe()) {
+ printk(BIOS_ERR, "TPM: Probe failed.\n");
+ return -1;
+ }
+
+ /* Read back control area structure */
+ crb_readControlArea();
+
+ /* Good to go. */
+ printk(BIOS_SPEW, "TPM: CRB TPM initialized successfully\n");
+
+ return 0;
+}
+
+/*
+ * tpm2_process_command
+ */
+size_t tpm2_process_command(const void *tpm2_command, size_t command_size, void *tpm2_response,
+ size_t max_response)
+{
+ int rc;
+
+ if (command_size > control_area.command_size) {
+ printk(BIOS_ERR, "TPM: Command size is too big.\n");
+ return -1;
+ }
+
+ if (control_area.response_size < max_response) {
+ printk(BIOS_ERR, "TPM: Response size could be too big.\n");
+ return -1;
+ }
+
+ cur_loc = crb_activate_locality();
+
+ // Check if CMD bit is cleared.
+ rc = crb_wait_for_reg32(CRB_REG(0, CRB_REG_START), 250, CRB_REG_START_START, 0x0);
+ if (rc) {
+ printk(BIOS_ERR, "TPM: Error - Cmd Bit not cleared.\n");
+ return -1;
+ }
+
+ if (crb_switch_to_ready())
+ return -1;
+
+ // Write to Command Buffer
+ memcpy(control_area.command_bfr, tpm2_command, command_size);
+
+ // Write Start Bit
+ write8(CRB_REG(cur_loc, CRB_REG_START), 0x1);
+
+ // Poll for Response
+ rc = crb_wait_for_reg32(CRB_REG(cur_loc, CRB_REG_START), 3500, CRB_REG_START_START, 0);
+ if (rc) {
+ printk(BIOS_DEBUG, "TPM: Command Timed out.\n");
+ return -1;
+ }
+
+ // Check for errors
+ rc = crb_wait_for_reg32(CRB_REG(cur_loc, CRB_REG_STATUS), 200, CRB_REG_STATUS_ERROR, 0);
+ if (rc) {
+ printk(BIOS_DEBUG, "TPM: Command errored.\n");
+ return -1;
+ }
+
+ // Get Response Length
+ uint32_t length = be32_to_cpu(read32(control_area.response_bfr + 2));
+
+ /* Response has to have at least 6 bytes */
+ if (length < 6)
+ return 1;
+
+ // Copy Response
+ memcpy(tpm2_response, control_area.response_bfr, length);
+
+ if (crb_switch_to_ready()) {
+ printk(BIOS_DEBUG, "TPM: Can not transition into ready state again.\n");
+ return -1;
+ }
+
+ return length;
+}
+
+/*
+ * tp2_get_info
+ *
+ * Returns information about the TPM
+ *
+ */
+void tpm2_get_info(struct tpm2_info *tpm2_info)
+{
+ uint64_t interfaceReg = read64(CRB_REG(cur_loc, CRB_REG_INTF_ID));
+
+ tpm2_info->vendor_id = (interfaceReg >> 48) & 0xFFFF;
+ tpm2_info->device_id = (interfaceReg >> 32) & 0xFFFF;
+ tpm2_info->revision = (interfaceReg >> 24) & 0xFF;
+}
diff --git a/src/drivers/crb/tpm.h b/src/drivers/crb/tpm.h
new file mode 100644
index 0000000000..9bbed198f0
--- /dev/null
+++ b/src/drivers/crb/tpm.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2016 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * This is a driver for a Command Response Buffer Interface
+ */
+
+/* CRB driver */
+/* address of locality 0 (CRB) */
+#define TPM_CRB_BASE_ADDRESS CONFIG_CRB_TPM_BASE_ADDRESS
+
+#define CRB_REG(LOCTY, REG) \
+ (void *)(CONFIG_CRB_TPM_BASE_ADDRESS + (LOCTY << 12) + REG)
+
+/* hardware registers */
+#define CRB_REG_LOC_STATE 0x00
+#define CRB_REG_LOC_CTRL 0x08
+#define CRB_REG_LOC_STS 0x0C
+
+/* LOC_CTRL BIT MASKS */
+#define LOC_CTRL_REQ_ACCESS 0x01
+
+/* LOC STATE BIT MASKS */
+#define LOC_STATE_LOC_ASSIGN 0x02
+#define LOC_STATE_REG_VALID_STS 0x80
+
+/* LOC STS BIT MASKS */
+#define LOC_STS_GRANTED 0x01
+
+#define CRB_REG_INTF_ID 0x30
+#define CRB_REG_REQUEST 0x40
+#define CRB_REG_STATUS 0x44
+#define CRB_REG_CANCEL 0x48
+#define CRB_REG_START 0x4C
+#define CRB_REG_INT_CTRL 0x50
+#define CRB_REG_CMD_SIZE 0x58
+#define CRB_REG_CMD_ADDR 0x5C
+#define CRB_REG_RESP_SIZE 0x64
+#define CRB_REG_RESP_ADDR 0x68
+
+/* CRB INTF BIT MASK */
+#define CRB_INTF_REG_CAP_CRB (1<<14)
+#define CRB_INTF_REG_INTF_SEL (1<<17)
+#define CRB_INTF_REG_INTF_LOCK (1<<19)
+
+
+/*REQUEST Register related */
+#define CRB_REG_REQUEST_CMD_RDY 0x01
+#define CRB_REG_REQUEST_GO_IDLE 0x02
+
+/* STATUS Register related */
+#define CRB_REG_STATUS_ERROR 0x01
+#define CRB_REG_STATUS_IDLE 0x02
+
+/* START Register related */
+#define CRB_REG_START_START 0x01
+
+/* TPM Info Struct */
+struct tpm2_info {
+ uint16_t vendor_id;
+ uint16_t device_id;
+ uint16_t revision;
+};
+
+
+int tpm2_init(void);
+void tpm2_get_info(struct tpm2_info *tpm2_info);
+size_t tpm2_process_command(const void *tpm2_command, size_t command_size,
+ void *tpm2_response, size_t max_response);