From: Marc-André Lureau marcandre.lureau@redhat.com
Hi,
The following series implements a limited TPM CRB driver. The TIS device with a TPM 2.0 seems to be ignored by Windows 10, so I implemented a simple CRB device that I will send shortly on the qemu-devel. With the CRB device, Windows 10 correctly recognized and exchange with a TPM 2.0.
As long as the device isn't in qemu, I suppose this series should remain RFC.
Feedback welcome!
Marc-André Lureau (4): x86: add readq() tpm: generalize init_timeout() tpm: use get_tpm_version() callback WIP: add TPM CRB device support
src/hw/tpm_drivers.c | 226 ++++++++++++++++++++++++++++++++++++++++++++++++--- src/hw/tpm_drivers.h | 26 ++++++ src/x86.h | 5 ++ 3 files changed, 245 insertions(+), 12 deletions(-)
From: Marc-André Lureau marcandre.lureau@redhat.com
Signed-off-by: Marc-André Lureau marcandre.lureau@redhat.com --- src/x86.h | 5 +++++ 1 file changed, 5 insertions(+)
diff --git a/src/x86.h b/src/x86.h index 4aea65c..c7bb60d 100644 --- a/src/x86.h +++ b/src/x86.h @@ -211,6 +211,11 @@ static inline void writeb(void *addr, u8 val) { barrier(); *(volatile u8 *)addr = val; } +static inline u64 readq(const void *addr) { + u64 val = *(volatile const u64 *)addr; + barrier(); + return val; +} static inline u32 readl(const void *addr) { u32 val = *(volatile const u32 *)addr; barrier();
On 10/06/2017 11:33 AM, marcandre.lureau@redhat.com wrote:
From: Marc-André Lureau marcandre.lureau@redhat.com
Signed-off-by: Marc-André Lureau marcandre.lureau@redhat.com
Reviewed-by: Philippe Mathieu-Daudé f4bug@amsat.org
src/x86.h | 5 +++++ 1 file changed, 5 insertions(+)
diff --git a/src/x86.h b/src/x86.h index 4aea65c..c7bb60d 100644 --- a/src/x86.h +++ b/src/x86.h @@ -211,6 +211,11 @@ static inline void writeb(void *addr, u8 val) { barrier(); *(volatile u8 *)addr = val; } +static inline u64 readq(const void *addr) {
- u64 val = *(volatile const u64 *)addr;
- barrier();
- return val;
+} static inline u32 readl(const void *addr) { u32 val = *(volatile const u32 *)addr; barrier();
From: Marc-André Lureau marcandre.lureau@redhat.com
It seems both TIS & CRB devices share the same timeout. Make initialization function generic for now.
Signed-off-by: Marc-André Lureau marcandre.lureau@redhat.com --- src/hw/tpm_drivers.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-)
diff --git a/src/hw/tpm_drivers.c b/src/hw/tpm_drivers.c index a137e62..0daaef2 100644 --- a/src/hw/tpm_drivers.c +++ b/src/hw/tpm_drivers.c @@ -102,26 +102,31 @@ static TPMVersion tis_get_tpm_version(void) return TPM_VERSION_1_2; }
-static u32 tis_init(void) +static void init_timeout(int driver) { - if (!CONFIG_TCGBIOS) - return 1; - - writeb(TIS_REG(0, TIS_REG_INT_ENABLE), 0); - - if (tpm_drivers[TIS_DRIVER_IDX].durations == NULL) { + if (tpm_drivers[driver].durations == NULL) { u32 *durations = tpm_default_dur; memcpy(durations, tpm_default_durations, sizeof(tpm_default_durations)); - tpm_drivers[TIS_DRIVER_IDX].durations = durations; + tpm_drivers[driver].durations = durations; }
- if (tpm_drivers[TIS_DRIVER_IDX].timeouts == NULL) { + if (tpm_drivers[driver].timeouts == NULL) { u32 *timeouts = tpm_default_to; memcpy(timeouts, tis_default_timeouts, sizeof(tis_default_timeouts)); - tpm_drivers[TIS_DRIVER_IDX].timeouts = timeouts; + tpm_drivers[driver].timeouts = timeouts; } +} + +static u32 tis_init(void) +{ + if (!CONFIG_TCGBIOS) + return 1; + + writeb(TIS_REG(0, TIS_REG_INT_ENABLE), 0); + + init_timeout(TIS_DRIVER_IDX);
return 1; }
On 10/06/2017 11:33 AM, marcandre.lureau@redhat.com wrote:
From: Marc-André Lureau marcandre.lureau@redhat.com
Reviewed-by: Philippe Mathieu-Daudé f4bug@amsat.org
It seems both TIS & CRB devices share the same timeout. Make initialization function generic for now.
Signed-off-by: Marc-André Lureau marcandre.lureau@redhat.com
src/hw/tpm_drivers.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-)
diff --git a/src/hw/tpm_drivers.c b/src/hw/tpm_drivers.c index a137e62..0daaef2 100644 --- a/src/hw/tpm_drivers.c +++ b/src/hw/tpm_drivers.c @@ -102,26 +102,31 @@ static TPMVersion tis_get_tpm_version(void) return TPM_VERSION_1_2; }
-static u32 tis_init(void) +static void init_timeout(int driver) {
- if (!CONFIG_TCGBIOS)
return 1;
- writeb(TIS_REG(0, TIS_REG_INT_ENABLE), 0);
- if (tpm_drivers[TIS_DRIVER_IDX].durations == NULL) {
- if (tpm_drivers[driver].durations == NULL) { u32 *durations = tpm_default_dur; memcpy(durations, tpm_default_durations, sizeof(tpm_default_durations));
tpm_drivers[TIS_DRIVER_IDX].durations = durations;
}tpm_drivers[driver].durations = durations;
- if (tpm_drivers[TIS_DRIVER_IDX].timeouts == NULL) {
- if (tpm_drivers[driver].timeouts == NULL) { u32 *timeouts = tpm_default_to; memcpy(timeouts, tis_default_timeouts, sizeof(tis_default_timeouts));
tpm_drivers[TIS_DRIVER_IDX].timeouts = timeouts;
}tpm_drivers[driver].timeouts = timeouts;
+}
+static u32 tis_init(void) +{
if (!CONFIG_TCGBIOS)
return 1;
writeb(TIS_REG(0, TIS_REG_INT_ENABLE), 0);
init_timeout(TIS_DRIVER_IDX);
return 1;
}
From: Marc-André Lureau marcandre.lureau@redhat.com
As originally intended, I guess.
Signed-off-by: Marc-André Lureau marcandre.lureau@redhat.com --- src/hw/tpm_drivers.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/hw/tpm_drivers.c b/src/hw/tpm_drivers.c index 0daaef2..5cee9d8 100644 --- a/src/hw/tpm_drivers.c +++ b/src/hw/tpm_drivers.c @@ -346,6 +346,7 @@ struct tpm_driver tpm_drivers[TPM_NUM_DRIVERS] = { .durations = NULL, .set_timeouts = set_timeouts, .probe = tis_probe, + .get_tpm_version = tis_get_tpm_version, .init = tis_init, .activate = tis_activate, .ready = tis_ready, @@ -367,7 +368,7 @@ tpmhw_probe(void) if (td->probe() != 0) { td->init(); TPMHW_driver_to_use = i; - return tis_get_tpm_version(); + return td->get_tpm_version(); } } return TPM_VERSION_NONE;
From: Marc-André Lureau marcandre.lureau@redhat.com
The CRB device was introduced with TPM 2.0 to be physical-bus agnostic and defined in TCG PC Client Platform TPM Profile (PTP) Specification Family “2.0” Level 00 Revision 01.03 v22
It seems to be required with Windows 10. It is also a simpler device than FIFO/TIS.
This WIP patch doesn't support locality other than 0. The BIOS doesn't seem to require other localities, so that code seems a bit pointless.
In theory, the cmd/resp buffers could be located above 4G, but I don't know how seabios could reach it in 32bit mode (my qemu WIP allocates next to 0xfed40000).
Signed-off-by: Marc-André Lureau marcandre.lureau@redhat.com --- src/hw/tpm_drivers.c | 198 ++++++++++++++++++++++++++++++++++++++++++++++++++- src/hw/tpm_drivers.h | 26 +++++++ 2 files changed, 223 insertions(+), 1 deletion(-)
diff --git a/src/hw/tpm_drivers.c b/src/hw/tpm_drivers.c index 5cee9d8..3b8f6e1 100644 --- a/src/hw/tpm_drivers.c +++ b/src/hw/tpm_drivers.c @@ -36,7 +36,8 @@ struct tpm_driver { extern struct tpm_driver tpm_drivers[];
#define TIS_DRIVER_IDX 0 -#define TPM_NUM_DRIVERS 1 +#define CRB_DRIVER_IDX 1 +#define TPM_NUM_DRIVERS 2
#define TPM_INVALID_DRIVER 0xf
@@ -57,6 +58,11 @@ static const u32 tpm_default_durations[3] = { static u32 tpm_default_dur[3]; static u32 tpm_default_to[4];
+static u32 crb_cmd_size; +static void *crb_cmd; +static u32 crb_resp_size; +static void *crb_resp; + /* if device is not there, return '0', '1' otherwise */ static u32 tis_probe(void) { @@ -338,6 +344,181 @@ static u32 tis_waitrespready(enum tpmDurationType to_t) return rc; }
+/* if device is not there, return '0', '1' otherwise */ +static u32 crb_probe(void) +{ + if (!CONFIG_TCGBIOS) + return 0; + + u32 ifaceid = readl(CRB_REG(0, CRB_REG_INTF_ID)); + + if ((ifaceid & 0xf) != 0xf) { + if ((ifaceid & 0xf) == 1) { + /* CRB is active */ + return 1; + } + if ((ifaceid & (1 << 14)) == 0) { + /* CRB cannot be selected */ + return 0; + } + /* write of 1 to bits 17-18 selects CRB */ + writel(CRB_REG(0, CRB_REG_INTF_ID), (1 << 17)); + /* lock it */ + writel(CRB_REG(0, CRB_REG_INTF_ID), (1 << 19)); + } + + /* no support for 64 bit addressing yet */ + if (readl(CRB_REG(0, CRB_REG_CTRL_CMD_HADDR))) + return 1; + + u64 addr = readq(CRB_REG(0, CRB_REG_CTRL_RSP_ADDR)); + if (addr > __UINT32_MAX__) + return 1; + + return 0; +} + +static TPMVersion crb_get_tpm_version(void) +{ + /* CRB is supposed to be TPM 2.0 only */ + return TPM_VERSION_2; +} + +static u32 crb_init(void) +{ + if (!CONFIG_TCGBIOS) + return 1; + + crb_cmd = (void*)readl(CRB_REG(0, CRB_REG_CTRL_CMD_LADDR)); + crb_cmd_size = readl(CRB_REG(0, CRB_REG_CTRL_CMD_SIZE)); + crb_resp = (void*)readl(CRB_REG(0, CRB_REG_CTRL_RSP_ADDR)); + crb_resp_size = readl(CRB_REG(0, CRB_REG_CTRL_RSP_SIZE)); + + init_timeout(CRB_DRIVER_IDX); + + return 0; +} + +static u32 crb_wait_reg(u8 locty, u8 reg, u32 time, u8 mask, u8 expect) +{ + if (!CONFIG_TCGBIOS) + return 0; + + u32 rc = 1; + u32 end = timer_calc_usec(time); + + for (;;) { + u8 sts = readl(CRB_REG(locty, reg)); + if ((sts & mask) == expect) { + rc = 0; + break; + } + if (timer_check(end)) { + warn_timeout(); + break; + } + yield(); + } + return rc; +} + +static u32 crb_activate(u8 locty) +{ + if (!CONFIG_TCGBIOS) + return 0; + + /* TODO activate locty */ + + return 0; +} + +static u32 crb_find_active_locality(void) +{ + if (!CONFIG_TCGBIOS) + return 0; + + /* TODO return current locty */ + + return 0; +} + +#define CRB_CTRL_REQ_CMD_READY 0b1 +#define CRB_START_INVOKE 0b1 +#define CRB_CTRL_STS_ERROR 0b1 + +static u32 crb_ready(void) +{ + if (!CONFIG_TCGBIOS) + return 0; + + u32 rc = 0; + u8 locty = crb_find_active_locality(); + u32 timeout_c = tpm_drivers[CRB_DRIVER_IDX].timeouts[TIS_TIMEOUT_TYPE_C]; + + writel(CRB_REG(locty, CRB_REG_CTRL_REQ), CRB_CTRL_REQ_CMD_READY); + rc = crb_wait_reg(locty, CRB_REG_CTRL_REQ, timeout_c, + CRB_CTRL_REQ_CMD_READY, 0); + + return rc; +} + +static u32 crb_senddata(const u8 *const data, u32 len) +{ + if (!CONFIG_TCGBIOS) + return 0; + + if (len > crb_cmd_size) + return 1; + + u8 locty = tis_find_active_locality(); + memcpy(crb_cmd, data, len); + writel(CRB_REG(locty, CRB_REG_CTRL_START), CRB_START_INVOKE); + + return 0; +} + +static u32 crb_readresp(u8 *buffer, u32 *len) +{ + if (!CONFIG_TCGBIOS) + return 0; + + u8 locty = tis_find_active_locality(); + if (readl(CRB_REG(locty, CRB_REG_CTRL_STS)) & CRB_CTRL_STS_ERROR) + return 1; + + if (*len < 6) + return 1; + + memcpy(buffer, crb_resp, 6); + u32 expected = be32_to_cpu(*(u32 *) &buffer[2]); + if (expected > *len || expected < 6) + return 1; + + memcpy(buffer + 6, crb_resp + 6, expected - 6); + + return 0; +} + + +static u32 crb_waitdatavalid(void) +{ + return 0; +} + +static u32 crb_waitrespready(enum tpmDurationType to_t) +{ + if (!CONFIG_TCGBIOS) + return 0; + + u32 rc = 0; + u8 locty = crb_find_active_locality(); + u32 timeout_a = tpm_drivers[CRB_DRIVER_IDX].timeouts[TIS_TIMEOUT_TYPE_A]; + + rc = crb_wait_reg(locty, CRB_REG_CTRL_START, timeout_a, + CRB_START_INVOKE, 0); + + return rc; +}
struct tpm_driver tpm_drivers[TPM_NUM_DRIVERS] = { [TIS_DRIVER_IDX] = @@ -355,6 +536,21 @@ struct tpm_driver tpm_drivers[TPM_NUM_DRIVERS] = { .waitdatavalid = tis_waitdatavalid, .waitrespready = tis_waitrespready, }, + [CRB_DRIVER_IDX] = + { + .timeouts = NULL, + .durations = NULL, + .set_timeouts = set_timeouts, + .probe = crb_probe, + .get_tpm_version = crb_get_tpm_version, + .init = crb_init, + .activate = crb_activate, + .ready = crb_ready, + .senddata = crb_senddata, + .readresp = crb_readresp, + .waitdatavalid = crb_waitdatavalid, + .waitrespready = crb_waitrespready, + }, };
static u8 TPMHW_driver_to_use = TPM_INVALID_DRIVER; diff --git a/src/hw/tpm_drivers.h b/src/hw/tpm_drivers.h index 56fd9e8..adf1839 100644 --- a/src/hw/tpm_drivers.h +++ b/src/hw/tpm_drivers.h @@ -24,6 +24,32 @@ int tpmhw_transmit(u8 locty, struct tpm_req_header *req, enum tpmDurationType to_t); void tpmhw_set_timeouts(u32 timeouts[4], u32 durations[3]);
+/* CRB driver */ +/* address of locality 0 (CRB) */ +#define TPM_CRB_BASE_ADDRESS 0xfed40000 + +#define CRB_REG(LOCTY, REG) \ + (void *)(TPM_CRB_BASE_ADDRESS + (LOCTY << 12) + REG) + +/* hardware registers */ +#define CRB_REG_LOC_STATE 0x0 +#define CRB_REG_LOC_CTRL 0x8 +#define CRB_REG_LOC_STS 0xC +#define CRB_REG_INTF_ID 0x30 +#define CRB_REG_CTRL_EXT 0x38 +#define CRB_REG_CTRL_REQ 0x40 +#define CRB_REG_CTRL_STS 0x44 +#define CRB_REG_CTRL_CANCEL 0x48 +#define CRB_REG_CTRL_START 0x4C +#define CRB_REG_INT_ENABLE 0x50 +#define CRB_REG_INT_STS 0x54 +#define CRB_REG_CTRL_CMD_SIZE 0x58 +#define CRB_REG_CTRL_CMD_LADDR 0x5C +#define CRB_REG_CTRL_CMD_HADDR 0x60 +#define CRB_REG_CTRL_RSP_SIZE 0x64 +#define CRB_REG_CTRL_RSP_ADDR 0x68 +#define CRB_REG_DATA_BUFFER 0x80 + /* TIS driver */ /* address of locality 0 (TIS) */ #define TPM_TIS_BASE_ADDRESS 0xfed40000
On 10/06/2017 10:33 AM, marcandre.lureau@redhat.com wrote:
From: Marc-André Lureau marcandre.lureau@redhat.com
The CRB device was introduced with TPM 2.0 to be physical-bus agnostic and defined in TCG PC Client Platform TPM Profile (PTP) Specification Family “2.0” Level 00 Revision 01.03 v22
It seems to be required with Windows 10. It is also a simpler device than FIFO/TIS.
This WIP patch doesn't support locality other than 0. The BIOS doesn't seem to require other localities, so that code seems a bit pointless.
In theory, the cmd/resp buffers could be located above 4G, but I don't know how seabios could reach it in 32bit mode (my qemu WIP allocates next to 0xfed40000).
This looks all really good to me.
The TPM TIS is supposed to be the most widespread interface, so I am surprised that Win 10 does not use it.
Regards, Stefan
Signed-off-by: Marc-André Lureau marcandre.lureau@redhat.com
src/hw/tpm_drivers.c | 198 ++++++++++++++++++++++++++++++++++++++++++++++++++- src/hw/tpm_drivers.h | 26 +++++++ 2 files changed, 223 insertions(+), 1 deletion(-)
diff --git a/src/hw/tpm_drivers.c b/src/hw/tpm_drivers.c index 5cee9d8..3b8f6e1 100644 --- a/src/hw/tpm_drivers.c +++ b/src/hw/tpm_drivers.c @@ -36,7 +36,8 @@ struct tpm_driver { extern struct tpm_driver tpm_drivers[];
#define TIS_DRIVER_IDX 0 -#define TPM_NUM_DRIVERS 1 +#define CRB_DRIVER_IDX 1 +#define TPM_NUM_DRIVERS 2
#define TPM_INVALID_DRIVER 0xf
@@ -57,6 +58,11 @@ static const u32 tpm_default_durations[3] = { static u32 tpm_default_dur[3]; static u32 tpm_default_to[4];
+static u32 crb_cmd_size; +static void *crb_cmd; +static u32 crb_resp_size; +static void *crb_resp;
- /* if device is not there, return '0', '1' otherwise */ static u32 tis_probe(void) {
@@ -338,6 +344,181 @@ static u32 tis_waitrespready(enum tpmDurationType to_t) return rc; }
+/* if device is not there, return '0', '1' otherwise */ +static u32 crb_probe(void) +{
- if (!CONFIG_TCGBIOS)
return 0;
- u32 ifaceid = readl(CRB_REG(0, CRB_REG_INTF_ID));
- if ((ifaceid & 0xf) != 0xf) {
if ((ifaceid & 0xf) == 1) {
/* CRB is active */
return 1;
}
if ((ifaceid & (1 << 14)) == 0) {
/* CRB cannot be selected */
return 0;
}
/* write of 1 to bits 17-18 selects CRB */
writel(CRB_REG(0, CRB_REG_INTF_ID), (1 << 17));
/* lock it */
writel(CRB_REG(0, CRB_REG_INTF_ID), (1 << 19));
- }
- /* no support for 64 bit addressing yet */
- if (readl(CRB_REG(0, CRB_REG_CTRL_CMD_HADDR)))
return 1;
- u64 addr = readq(CRB_REG(0, CRB_REG_CTRL_RSP_ADDR));
- if (addr > __UINT32_MAX__)
return 1;
- return 0;
+}
+static TPMVersion crb_get_tpm_version(void) +{
- /* CRB is supposed to be TPM 2.0 only */
- return TPM_VERSION_2;
+}
+static u32 crb_init(void) +{
- if (!CONFIG_TCGBIOS)
return 1;
- crb_cmd = (void*)readl(CRB_REG(0, CRB_REG_CTRL_CMD_LADDR));
- crb_cmd_size = readl(CRB_REG(0, CRB_REG_CTRL_CMD_SIZE));
- crb_resp = (void*)readl(CRB_REG(0, CRB_REG_CTRL_RSP_ADDR));
- crb_resp_size = readl(CRB_REG(0, CRB_REG_CTRL_RSP_SIZE));
- init_timeout(CRB_DRIVER_IDX);
- return 0;
+}
+static u32 crb_wait_reg(u8 locty, u8 reg, u32 time, u8 mask, u8 expect) +{
- if (!CONFIG_TCGBIOS)
return 0;
- u32 rc = 1;
- u32 end = timer_calc_usec(time);
- for (;;) {
u8 sts = readl(CRB_REG(locty, reg));
if ((sts & mask) == expect) {
rc = 0;
break;
}
if (timer_check(end)) {
warn_timeout();
break;
}
yield();
- }
- return rc;
+}
+static u32 crb_activate(u8 locty) +{
- if (!CONFIG_TCGBIOS)
return 0;
- /* TODO activate locty */
- return 0;
+}
+static u32 crb_find_active_locality(void) +{
- if (!CONFIG_TCGBIOS)
return 0;
- /* TODO return current locty */
- return 0;
+}
+#define CRB_CTRL_REQ_CMD_READY 0b1 +#define CRB_START_INVOKE 0b1 +#define CRB_CTRL_STS_ERROR 0b1
+static u32 crb_ready(void) +{
- if (!CONFIG_TCGBIOS)
return 0;
- u32 rc = 0;
- u8 locty = crb_find_active_locality();
- u32 timeout_c = tpm_drivers[CRB_DRIVER_IDX].timeouts[TIS_TIMEOUT_TYPE_C];
- writel(CRB_REG(locty, CRB_REG_CTRL_REQ), CRB_CTRL_REQ_CMD_READY);
- rc = crb_wait_reg(locty, CRB_REG_CTRL_REQ, timeout_c,
CRB_CTRL_REQ_CMD_READY, 0);
- return rc;
+}
+static u32 crb_senddata(const u8 *const data, u32 len) +{
- if (!CONFIG_TCGBIOS)
return 0;
- if (len > crb_cmd_size)
return 1;
- u8 locty = tis_find_active_locality();
- memcpy(crb_cmd, data, len);
- writel(CRB_REG(locty, CRB_REG_CTRL_START), CRB_START_INVOKE);
- return 0;
+}
+static u32 crb_readresp(u8 *buffer, u32 *len) +{
- if (!CONFIG_TCGBIOS)
return 0;
- u8 locty = tis_find_active_locality();
- if (readl(CRB_REG(locty, CRB_REG_CTRL_STS)) & CRB_CTRL_STS_ERROR)
return 1;
- if (*len < 6)
return 1;
- memcpy(buffer, crb_resp, 6);
- u32 expected = be32_to_cpu(*(u32 *) &buffer[2]);
- if (expected > *len || expected < 6)
return 1;
- memcpy(buffer + 6, crb_resp + 6, expected - 6);
- return 0;
+}
+static u32 crb_waitdatavalid(void) +{
- return 0;
+}
+static u32 crb_waitrespready(enum tpmDurationType to_t) +{
- if (!CONFIG_TCGBIOS)
return 0;
- u32 rc = 0;
- u8 locty = crb_find_active_locality();
- u32 timeout_a = tpm_drivers[CRB_DRIVER_IDX].timeouts[TIS_TIMEOUT_TYPE_A];
- rc = crb_wait_reg(locty, CRB_REG_CTRL_START, timeout_a,
CRB_START_INVOKE, 0);
- return rc;
+}
struct tpm_driver tpm_drivers[TPM_NUM_DRIVERS] = { [TIS_DRIVER_IDX] = @@ -355,6 +536,21 @@ struct tpm_driver tpm_drivers[TPM_NUM_DRIVERS] = { .waitdatavalid = tis_waitdatavalid, .waitrespready = tis_waitrespready, },
[CRB_DRIVER_IDX] =
{
.timeouts = NULL,
.durations = NULL,
.set_timeouts = set_timeouts,
.probe = crb_probe,
.get_tpm_version = crb_get_tpm_version,
.init = crb_init,
.activate = crb_activate,
.ready = crb_ready,
.senddata = crb_senddata,
.readresp = crb_readresp,
.waitdatavalid = crb_waitdatavalid,
.waitrespready = crb_waitrespready,
},
};
static u8 TPMHW_driver_to_use = TPM_INVALID_DRIVER;
diff --git a/src/hw/tpm_drivers.h b/src/hw/tpm_drivers.h index 56fd9e8..adf1839 100644 --- a/src/hw/tpm_drivers.h +++ b/src/hw/tpm_drivers.h @@ -24,6 +24,32 @@ int tpmhw_transmit(u8 locty, struct tpm_req_header *req, enum tpmDurationType to_t); void tpmhw_set_timeouts(u32 timeouts[4], u32 durations[3]);
+/* CRB driver */ +/* address of locality 0 (CRB) */ +#define TPM_CRB_BASE_ADDRESS 0xfed40000
+#define CRB_REG(LOCTY, REG) \
- (void *)(TPM_CRB_BASE_ADDRESS + (LOCTY << 12) + REG)
+/* hardware registers */ +#define CRB_REG_LOC_STATE 0x0 +#define CRB_REG_LOC_CTRL 0x8 +#define CRB_REG_LOC_STS 0xC +#define CRB_REG_INTF_ID 0x30 +#define CRB_REG_CTRL_EXT 0x38 +#define CRB_REG_CTRL_REQ 0x40 +#define CRB_REG_CTRL_STS 0x44 +#define CRB_REG_CTRL_CANCEL 0x48 +#define CRB_REG_CTRL_START 0x4C +#define CRB_REG_INT_ENABLE 0x50 +#define CRB_REG_INT_STS 0x54 +#define CRB_REG_CTRL_CMD_SIZE 0x58 +#define CRB_REG_CTRL_CMD_LADDR 0x5C +#define CRB_REG_CTRL_CMD_HADDR 0x60 +#define CRB_REG_CTRL_RSP_SIZE 0x64 +#define CRB_REG_CTRL_RSP_ADDR 0x68 +#define CRB_REG_DATA_BUFFER 0x80
- /* TIS driver */ /* address of locality 0 (TIS) */ #define TPM_TIS_BASE_ADDRESS 0xfed40000
On 10/06/2017 10:50 AM, Stefan Berger wrote:
On 10/06/2017 10:33 AM, marcandre.lureau@redhat.com wrote:
From: Marc-André Lureau marcandre.lureau@redhat.com
The CRB device was introduced with TPM 2.0 to be physical-bus agnostic and defined in TCG PC Client Platform TPM Profile (PTP) Specification Family “2.0” Level 00 Revision 01.03 v22
It seems to be required with Windows 10. It is also a simpler device than FIFO/TIS.
This WIP patch doesn't support locality other than 0. The BIOS doesn't seem to require other localities, so that code seems a bit pointless.
In theory, the cmd/resp buffers could be located above 4G, but I don't know how seabios could reach it in 32bit mode (my qemu WIP allocates next to 0xfed40000).
The following fixes a bug:
diff --git a/src/hw/tpm_drivers.c b/src/hw/tpm_drivers.c index 3b8f6e1..2551b11 100644 --- a/src/hw/tpm_drivers.c +++ b/src/hw/tpm_drivers.c @@ -491,10 +491,13 @@ static u32 crb_readresp(u8 *buffer, u32 *len)
memcpy(buffer, crb_resp, 6); u32 expected = be32_to_cpu(*(u32 *) &buffer[2]); - if (expected > *len || expected < 6) + if (expected < 6) return 1;
- memcpy(buffer + 6, crb_resp + 6, expected - 6); +#define MIN(x,y) ((x) < (y) ? (x) : (y)) + *len = MIN(expected, *len); + + memcpy(buffer + 6, crb_resp + 6, *len - 6);
return 0; }
On 10/06/2017 10:33 AM, marcandre.lureau@redhat.com wrote:
From: Marc-André Lureau marcandre.lureau@redhat.com
The CRB device was introduced with TPM 2.0 to be physical-bus agnostic and defined in TCG PC Client Platform TPM Profile (PTP) Specification Family “2.0” Level 00 Revision 01.03 v22
It seems to be required with Windows 10. It is also a simpler device than FIFO/TIS.
This WIP patch doesn't support locality other than 0. The BIOS doesn't seem to require other localities, so that code seems a bit pointless.
In theory, the cmd/resp buffers could be located above 4G, but I don't know how seabios could reach it in 32bit mode (my qemu WIP allocates next to 0xfed40000).
Signed-off-by: Marc-André Lureau marcandre.lureau@redhat.com
Some more changes necessary once we run longer commands from a TPM 2 menu:
diff --git a/src/hw/tpm_drivers.c b/src/hw/tpm_drivers.c index 3b8f6e1..f12924e 100644 --- a/src/hw/tpm_drivers.c +++ b/src/hw/tpm_drivers.c @ -405,7 +405,12 @@ static u32 crb_wait_reg(u8 locty, u8 reg, u32 time, u8 mask, u8 expect) return 0;
u32 rc = 1; - u32 end = timer_calc_usec(time); + u32 end; + + if (time < 1000000) + end = timer_calc_usec(time); + else + end = timer_calc(time / 1000);
for (;;) { u8 sts = readl(CRB_REG(locty, reg)); @@ -512,9 +515,9 @@ static u32 crb_waitrespready(enum tpmDurationType to_t)
u32 rc = 0; u8 locty = crb_find_active_locality(); - u32 timeout_a = tpm_drivers[CRB_DRIVER_IDX].timeouts[TIS_TIMEOUT_TYPE_A]; + u32 timeout = tpm_drivers[CRB_DRIVER_IDX].durations[to_t];
- rc = crb_wait_reg(locty, CRB_REG_CTRL_START, timeout_a, + rc = crb_wait_reg(locty, CRB_REG_CTRL_START, timeout, CRB_START_INVOKE, 0);
return rc;
The 1st part fixes what seem to be u32 overflows when using 60s durations. The commands immediately time out. I will fix this in the TIS driver as well.
The 2nd part uses command duration to wait until a response is ready.
Stefan
src/hw/tpm_drivers.c | 198 ++++++++++++++++++++++++++++++++++++++++++++++++++- src/hw/tpm_drivers.h | 26 +++++++ 2 files changed, 223 insertions(+), 1 deletion(-)
diff --git a/src/hw/tpm_drivers.c b/src/hw/tpm_drivers.c index 5cee9d8..3b8f6e1 100644 --- a/src/hw/tpm_drivers.c +++ b/src/hw/tpm_drivers.c @@ -36,7 +36,8 @@ struct tpm_driver { extern struct tpm_driver tpm_drivers[];
#define TIS_DRIVER_IDX 0 -#define TPM_NUM_DRIVERS 1 +#define CRB_DRIVER_IDX 1 +#define TPM_NUM_DRIVERS 2
#define TPM_INVALID_DRIVER 0xf
@@ -57,6 +58,11 @@ static const u32 tpm_default_durations[3] = { static u32 tpm_default_dur[3]; static u32 tpm_default_to[4];
+static u32 crb_cmd_size; +static void *crb_cmd; +static u32 crb_resp_size; +static void *crb_resp;
- /* if device is not there, return '0', '1' otherwise */ static u32 tis_probe(void) {
@@ -338,6 +344,181 @@ static u32 tis_waitrespready(enum tpmDurationType to_t) return rc; }
+/* if device is not there, return '0', '1' otherwise */ +static u32 crb_probe(void) +{
- if (!CONFIG_TCGBIOS)
return 0;
- u32 ifaceid = readl(CRB_REG(0, CRB_REG_INTF_ID));
- if ((ifaceid & 0xf) != 0xf) {
if ((ifaceid & 0xf) == 1) {
/* CRB is active */
return 1;
}
if ((ifaceid & (1 << 14)) == 0) {
/* CRB cannot be selected */
return 0;
}
/* write of 1 to bits 17-18 selects CRB */
writel(CRB_REG(0, CRB_REG_INTF_ID), (1 << 17));
/* lock it */
writel(CRB_REG(0, CRB_REG_INTF_ID), (1 << 19));
- }
- /* no support for 64 bit addressing yet */
- if (readl(CRB_REG(0, CRB_REG_CTRL_CMD_HADDR)))
return 1;
- u64 addr = readq(CRB_REG(0, CRB_REG_CTRL_RSP_ADDR));
- if (addr > __UINT32_MAX__)
return 1;
- return 0;
+}
+static TPMVersion crb_get_tpm_version(void) +{
- /* CRB is supposed to be TPM 2.0 only */
- return TPM_VERSION_2;
+}
+static u32 crb_init(void) +{
- if (!CONFIG_TCGBIOS)
return 1;
- crb_cmd = (void*)readl(CRB_REG(0, CRB_REG_CTRL_CMD_LADDR));
- crb_cmd_size = readl(CRB_REG(0, CRB_REG_CTRL_CMD_SIZE));
- crb_resp = (void*)readl(CRB_REG(0, CRB_REG_CTRL_RSP_ADDR));
- crb_resp_size = readl(CRB_REG(0, CRB_REG_CTRL_RSP_SIZE));
- init_timeout(CRB_DRIVER_IDX);
- return 0;
+}
+static u32 crb_wait_reg(u8 locty, u8 reg, u32 time, u8 mask, u8 expect) +{
- if (!CONFIG_TCGBIOS)
return 0;
- u32 rc = 1;
- u32 end = timer_calc_usec(time);
- for (;;) {
u8 sts = readl(CRB_REG(locty, reg));
if ((sts & mask) == expect) {
rc = 0;
break;
}
if (timer_check(end)) {
warn_timeout();
break;
}
yield();
- }
- return rc;
+}
+static u32 crb_activate(u8 locty) +{
- if (!CONFIG_TCGBIOS)
return 0;
- /* TODO activate locty */
- return 0;
+}
+static u32 crb_find_active_locality(void) +{
- if (!CONFIG_TCGBIOS)
return 0;
- /* TODO return current locty */
- return 0;
+}
+#define CRB_CTRL_REQ_CMD_READY 0b1 +#define CRB_START_INVOKE 0b1 +#define CRB_CTRL_STS_ERROR 0b1
+static u32 crb_ready(void) +{
- if (!CONFIG_TCGBIOS)
return 0;
- u32 rc = 0;
- u8 locty = crb_find_active_locality();
- u32 timeout_c = tpm_drivers[CRB_DRIVER_IDX].timeouts[TIS_TIMEOUT_TYPE_C];
- writel(CRB_REG(locty, CRB_REG_CTRL_REQ), CRB_CTRL_REQ_CMD_READY);
- rc = crb_wait_reg(locty, CRB_REG_CTRL_REQ, timeout_c,
CRB_CTRL_REQ_CMD_READY, 0);
- return rc;
+}
+static u32 crb_senddata(const u8 *const data, u32 len) +{
- if (!CONFIG_TCGBIOS)
return 0;
- if (len > crb_cmd_size)
return 1;
- u8 locty = tis_find_active_locality();
- memcpy(crb_cmd, data, len);
- writel(CRB_REG(locty, CRB_REG_CTRL_START), CRB_START_INVOKE);
- return 0;
+}
+static u32 crb_readresp(u8 *buffer, u32 *len) +{
- if (!CONFIG_TCGBIOS)
return 0;
- u8 locty = tis_find_active_locality();
- if (readl(CRB_REG(locty, CRB_REG_CTRL_STS)) & CRB_CTRL_STS_ERROR)
return 1;
- if (*len < 6)
return 1;
- memcpy(buffer, crb_resp, 6);
- u32 expected = be32_to_cpu(*(u32 *) &buffer[2]);
- if (expected > *len || expected < 6)
return 1;
- memcpy(buffer + 6, crb_resp + 6, expected - 6);
- return 0;
+}
+static u32 crb_waitdatavalid(void) +{
- return 0;
+}
+static u32 crb_waitrespready(enum tpmDurationType to_t) +{
- if (!CONFIG_TCGBIOS)
return 0;
- u32 rc = 0;
- u8 locty = crb_find_active_locality();
- u32 timeout_a = tpm_drivers[CRB_DRIVER_IDX].timeouts[TIS_TIMEOUT_TYPE_A];
- rc = crb_wait_reg(locty, CRB_REG_CTRL_START, timeout_a,
CRB_START_INVOKE, 0);
- return rc;
+}
struct tpm_driver tpm_drivers[TPM_NUM_DRIVERS] = { [TIS_DRIVER_IDX] = @@ -355,6 +536,21 @@ struct tpm_driver tpm_drivers[TPM_NUM_DRIVERS] = { .waitdatavalid = tis_waitdatavalid, .waitrespready = tis_waitrespready, },
[CRB_DRIVER_IDX] =
{
.timeouts = NULL,
.durations = NULL,
.set_timeouts = set_timeouts,
.probe = crb_probe,
.get_tpm_version = crb_get_tpm_version,
.init = crb_init,
.activate = crb_activate,
.ready = crb_ready,
.senddata = crb_senddata,
.readresp = crb_readresp,
.waitdatavalid = crb_waitdatavalid,
.waitrespready = crb_waitrespready,
},
};
static u8 TPMHW_driver_to_use = TPM_INVALID_DRIVER;
diff --git a/src/hw/tpm_drivers.h b/src/hw/tpm_drivers.h index 56fd9e8..adf1839 100644 --- a/src/hw/tpm_drivers.h +++ b/src/hw/tpm_drivers.h @@ -24,6 +24,32 @@ int tpmhw_transmit(u8 locty, struct tpm_req_header *req, enum tpmDurationType to_t); void tpmhw_set_timeouts(u32 timeouts[4], u32 durations[3]);
+/* CRB driver */ +/* address of locality 0 (CRB) */ +#define TPM_CRB_BASE_ADDRESS 0xfed40000
+#define CRB_REG(LOCTY, REG) \
- (void *)(TPM_CRB_BASE_ADDRESS + (LOCTY << 12) + REG)
+/* hardware registers */ +#define CRB_REG_LOC_STATE 0x0 +#define CRB_REG_LOC_CTRL 0x8 +#define CRB_REG_LOC_STS 0xC +#define CRB_REG_INTF_ID 0x30 +#define CRB_REG_CTRL_EXT 0x38 +#define CRB_REG_CTRL_REQ 0x40 +#define CRB_REG_CTRL_STS 0x44 +#define CRB_REG_CTRL_CANCEL 0x48 +#define CRB_REG_CTRL_START 0x4C +#define CRB_REG_INT_ENABLE 0x50 +#define CRB_REG_INT_STS 0x54 +#define CRB_REG_CTRL_CMD_SIZE 0x58 +#define CRB_REG_CTRL_CMD_LADDR 0x5C +#define CRB_REG_CTRL_CMD_HADDR 0x60 +#define CRB_REG_CTRL_RSP_SIZE 0x64 +#define CRB_REG_CTRL_RSP_ADDR 0x68 +#define CRB_REG_DATA_BUFFER 0x80
- /* TIS driver */ /* address of locality 0 (TIS) */ #define TPM_TIS_BASE_ADDRESS 0xfed40000
On Fri, Oct 06, 2017 at 04:33:23PM +0200, marcandre.lureau@redhat.com wrote:
From: Marc-André Lureau marcandre.lureau@redhat.com
Hi,
The following series implements a limited TPM CRB driver. The TIS device with a TPM 2.0 seems to be ignored by Windows 10, so I implemented a simple CRB device that I will send shortly on the qemu-devel. With the CRB device, Windows 10 correctly recognized and exchange with a TPM 2.0.
As long as the device isn't in qemu, I suppose this series should remain RFC.
Thanks. The patches look fine to me, but the corresponding patches will need to be committed to qemu before seabios.
-Kevin
On 10/06/2017 10:33 AM, marcandre.lureau@redhat.com wrote:
From: Marc-André Lureau marcandre.lureau@redhat.com
Hi,
The following series implements a limited TPM CRB driver. The TIS device with a TPM 2.0 seems to be ignored by Windows 10, so I implemented a simple CRB device that I will send shortly on the qemu-devel. With the CRB device, Windows 10 correctly recognized and exchange with a TPM 2.0.
As long as the device isn't in qemu, I suppose this series should remain RFC.
I think you can repost it now...
Hi
On Sun, Feb 11, 2018 at 5:53 PM, Stefan Berger stefanb@linux.vnet.ibm.com wrote:
On 10/06/2017 10:33 AM, marcandre.lureau@redhat.com wrote:
From: Marc-André Lureau marcandre.lureau@redhat.com
Hi,
The following series implements a limited TPM CRB driver. The TIS device with a TPM 2.0 seems to be ignored by Windows 10, so I implemented a simple CRB device that I will send shortly on the qemu-devel. With the CRB device, Windows 10 correctly recognized and exchange with a TPM 2.0.
As long as the device isn't in qemu, I suppose this series should remain RFC.
I think you can repost it now...
Indeed, the device is now in qemu. However, I don't have much time / priority to work on seabios support for now. Since you have additional patches on top, and you are experienced with that, perhaps you could send the whole series including my patches (adding your reviewed-by / signed-off and some text if you modified my patches)? I suppose that would work for Kevin too?
thanks