Philipp Deppenwiese has submitted this change and it was merged. ( https://review.coreboot.org/c/coreboot/+/34106 )
Change subject: drivers/crb: Add CRB driver for TPM2 support ......................................................................
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 --- A src/drivers/crb/Kconfig A src/drivers/crb/Makefile.inc A src/drivers/crb/chip.h A src/drivers/crb/tis.c A src/drivers/crb/tpm.c A src/drivers/crb/tpm.h M src/security/tpm/Kconfig 7 files changed, 546 insertions(+), 2 deletions(-)
Approvals: build bot (Jenkins): Verified Philipp Deppenwiese: Looks good to me, approved
diff --git a/src/drivers/crb/Kconfig b/src/drivers/crb/Kconfig new file mode 100644 index 0000000..bfd8be0 --- /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 0000000..3f12b36 --- /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 0000000..8e74a68 --- /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 0000000..c110151 --- /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 0000000..0393417 --- /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 0000000..9bbed19 --- /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); diff --git a/src/security/tpm/Kconfig b/src/security/tpm/Kconfig index e6414d3..3af6d69 100644 --- a/src/security/tpm/Kconfig +++ b/src/security/tpm/Kconfig @@ -28,7 +28,7 @@ default y if MAINBOARD_HAS_TPM2 || USER_TPM2 depends on MAINBOARD_HAS_I2C_TPM_GENERIC || MAINBOARD_HAS_LPC_TPM \ || MAINBOARD_HAS_I2C_TPM_ATMEL || MAINBOARD_HAS_I2C_TPM_CR50 \ - || MAINBOARD_HAS_SPI_TPM_CR50 + || MAINBOARD_HAS_SPI_TPM_CR50 || MAINBOARD_HAS_CRB_TPM
config MAINBOARD_HAS_TPM1 bool @@ -58,7 +58,7 @@ bool "2.0" depends on MAINBOARD_HAS_I2C_TPM_GENERIC || MAINBOARD_HAS_LPC_TPM \ || MAINBOARD_HAS_I2C_TPM_ATMEL || MAINBOARD_HAS_I2C_TPM_CR50 \ - || MAINBOARD_HAS_SPI_TPM_CR50 + || MAINBOARD_HAS_SPI_TPM_CR50 || MAINBOARD_HAS_CRB_TPM help Enable this option to enable TPM 2.0 support in coreboot.