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>
This commit is contained in:
Christian Walter 2019-07-05 19:46:30 +02:00 committed by Philipp Deppenwiese
parent c703814e95
commit 7706a04c60
7 changed files with 546 additions and 2 deletions

17
src/drivers/crb/Kconfig Normal file
View File

@ -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

View File

@ -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

22
src/drivers/crb/chip.h Normal file
View File

@ -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 */

150
src/drivers/crb/tis.c Normal file
View File

@ -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__ */

280
src/drivers/crb/tpm.c Normal file
View File

@ -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;
}

70
src/drivers/crb/tpm.h Normal file
View File

@ -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);

View File

@ -28,7 +28,7 @@ config TPM2
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 @@ config USER_TPM2
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.