Skip to content

Commit

Permalink
i2c: Add nuvoton quirk, disallowing i2cdetect as it locks TPM
Browse files Browse the repository at this point in the history
In TPM 2.0 Firmware 1.3.0.1 and 1.3.1.0 (at least) there exists
a bug where if you send the wrong thing to the TPM it may lock the
bus, with no way of recovery except powering the TPM off/on.

On our current systems, the only way to power the TPM off/on is to pull
the power on the system (*NOT* just power off/on to host from BMC).

So, this patch adds the ability to do things to the i2c request really
early on, well before it hits any hardware, such as quickly drop it.

Signed-off-by: Stewart Smith <stewart@linux.vnet.ibm.com>
  • Loading branch information
stewartsmith committed Dec 2, 2016
1 parent cf6ec98 commit 157468a
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 0 deletions.
5 changes: 5 additions & 0 deletions core/i2c.c
Expand Up @@ -118,6 +118,11 @@ static int opal_i2c_request(uint64_t async_token, uint32_t bus_id,
req->user_data = (void *)(unsigned long)async_token;
req->bus = bus;

if (i2c_check_quirk(req, &rc)) {
i2c_free_req(req);
return rc;
}

/* Finally, queue the OPAL i2c request and return */
rc = i2c_queue_req(req);
if (rc) {
Expand Down
9 changes: 9 additions & 0 deletions include/i2c.h
Expand Up @@ -29,6 +29,8 @@ struct i2c_bus {
void (*set_req_timeout)(struct i2c_request *req,
uint64_t duration);
uint64_t (*run_req)(struct i2c_request *req);
int (*check_quirk)(void *data, struct i2c_request *req, int *rc);
void *check_quirk_data;
};

/*
Expand Down Expand Up @@ -97,6 +99,13 @@ static inline uint64_t i2c_run_req(struct i2c_request *req)
return 0;
}

static inline int i2c_check_quirk(struct i2c_request *req, int *rc)
{
if (req->bus->check_quirk)
return req->bus->check_quirk(req->bus->check_quirk_data, req, rc);
return 0;
}

/* P8 implementation details */
extern void p8_i2c_init(void);
extern void p8_i2c_interrupt(uint32_t chip_id);
Expand Down
28 changes: 28 additions & 0 deletions libstb/drivers/tpm_i2c_nuvoton.c
Expand Up @@ -22,6 +22,7 @@
#include "../tpm_chip.h"
#include "tpm_i2c_interface.h"
#include "tpm_i2c_nuvoton.h"
#include <opal-api.h>

//#define DBG(fmt, ...) prlog(PR_DEBUG, fmt, ##__VA_ARGS__)
#define DBG(fmt, ...)
Expand Down Expand Up @@ -507,10 +508,33 @@ static struct tpm_driver tpm_i2c_nuvoton_driver = {
.transmit = tpm_transmit,
};

static int nuvoton_tpm_quirk(void *data, struct i2c_request *req, int *rc)
{
struct tpm_dev *tpm_device = data;

/* If we're doing i2cdetect on the TPM, pretent we just NACKed
* it due to errata in nuvoton firmware where if we let this
* request go through, it would steal the bus and you'd end up
* in a nice world of pain.
*/
if (tpm_device->bus_id == req->bus->opal_id &&
tpm_device->xscom_base == req->dev_addr &&
((req->op == I2C_READ && req->rw_len == 1) ||
(req->op == I2C_WRITE && req->rw_len == 0))) {
*rc = OPAL_I2C_TIMEOUT;
prlog(PR_DEBUG,"NUVOTON: Squashed i2c probe to avoid locking "
"I2C bus\n");
return 1;
}

return 0;
}

void tpm_i2c_nuvoton_probe(void)
{
struct tpm_dev *tpm_device = NULL;
struct dt_node *node = NULL;
struct i2c_bus *bus;

dt_for_each_compatible(dt_root, node, "nuvoton,npct650") {
if (!dt_node_is_enabled(node))
Expand Down Expand Up @@ -551,6 +575,10 @@ void tpm_i2c_nuvoton_probe(void)
if (tpm_register_chip(node, tpm_device,
&tpm_i2c_nuvoton_driver))
free(tpm_device);
bus = i2c_find_bus_by_id(tpm_device->bus_id);
assert(bus->check_quirk == NULL);
bus->check_quirk = nuvoton_tpm_quirk;
bus->check_quirk_data = tpm_device;
}
return;
disable:
Expand Down

0 comments on commit 157468a

Please sign in to comment.