tpm: Support for TIS selftest done flag

Extend the backend to check whether the TPM_ContinueSelfTest
finished successfully and provide a flag to the TIS front-end
if it successfully finished. The TIS then sets a flag in
all localities in the STS register and keeps it until the next
reset.

Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
Acked-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
This commit is contained in:
Stefan Berger 2015-02-23 09:27:19 -05:00 committed by Michael S. Tsirkin
parent 2eae8c7516
commit fd85908145
4 changed files with 79 additions and 19 deletions

View file

@ -62,6 +62,7 @@ struct tpm_resp_hdr {
#define TPM_FAIL 9 #define TPM_FAIL 9
#define TPM_ORD_ContinueSelfTest 0x53
#define TPM_ORD_GetTicks 0xf1 #define TPM_ORD_GetTicks 0xf1
#endif /* TPM_TPM_INT_H */ #endif /* TPM_TPM_INT_H */

View file

@ -112,14 +112,31 @@ static void tpm_write_fatal_error_response(uint8_t *out, uint32_t out_len)
} }
} }
static bool tpm_passthrough_is_selftest(const uint8_t *in, uint32_t in_len)
{
struct tpm_req_hdr *hdr = (struct tpm_req_hdr *)in;
if (in_len >= sizeof(*hdr)) {
return (be32_to_cpu(hdr->ordinal) == TPM_ORD_ContinueSelfTest);
}
return false;
}
static int tpm_passthrough_unix_tx_bufs(TPMPassthruState *tpm_pt, static int tpm_passthrough_unix_tx_bufs(TPMPassthruState *tpm_pt,
const uint8_t *in, uint32_t in_len, const uint8_t *in, uint32_t in_len,
uint8_t *out, uint32_t out_len) uint8_t *out, uint32_t out_len,
bool *selftest_done)
{ {
int ret; int ret;
bool is_selftest;
const struct tpm_resp_hdr *hdr;
tpm_pt->tpm_op_canceled = false; tpm_pt->tpm_op_canceled = false;
tpm_pt->tpm_executing = true; tpm_pt->tpm_executing = true;
*selftest_done = false;
is_selftest = tpm_passthrough_is_selftest(in, in_len);
ret = tpm_passthrough_unix_write(tpm_pt->tpm_fd, in, in_len); ret = tpm_passthrough_unix_write(tpm_pt->tpm_fd, in, in_len);
if (ret != in_len) { if (ret != in_len) {
@ -149,6 +166,11 @@ static int tpm_passthrough_unix_tx_bufs(TPMPassthruState *tpm_pt,
"packet from TPM\n"); "packet from TPM\n");
} }
if (is_selftest && (ret >= sizeof(struct tpm_resp_hdr))) {
hdr = (struct tpm_resp_hdr *)out;
*selftest_done = (be32_to_cpu(hdr->errcode) == 0);
}
err_exit: err_exit:
if (ret < 0) { if (ret < 0) {
tpm_write_fatal_error_response(out, out_len); tpm_write_fatal_error_response(out, out_len);
@ -160,13 +182,15 @@ err_exit:
} }
static int tpm_passthrough_unix_transfer(TPMPassthruState *tpm_pt, static int tpm_passthrough_unix_transfer(TPMPassthruState *tpm_pt,
const TPMLocality *locty_data) const TPMLocality *locty_data,
bool *selftest_done)
{ {
return tpm_passthrough_unix_tx_bufs(tpm_pt, return tpm_passthrough_unix_tx_bufs(tpm_pt,
locty_data->w_buffer.buffer, locty_data->w_buffer.buffer,
locty_data->w_offset, locty_data->w_offset,
locty_data->r_buffer.buffer, locty_data->r_buffer.buffer,
locty_data->r_buffer.size); locty_data->r_buffer.size,
selftest_done);
} }
static void tpm_passthrough_worker_thread(gpointer data, static void tpm_passthrough_worker_thread(gpointer data,
@ -175,16 +199,19 @@ static void tpm_passthrough_worker_thread(gpointer data,
TPMPassthruThreadParams *thr_parms = user_data; TPMPassthruThreadParams *thr_parms = user_data;
TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(thr_parms->tb); TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(thr_parms->tb);
TPMBackendCmd cmd = (TPMBackendCmd)data; TPMBackendCmd cmd = (TPMBackendCmd)data;
bool selftest_done = false;
DPRINTF("tpm_passthrough: processing command type %d\n", cmd); DPRINTF("tpm_passthrough: processing command type %d\n", cmd);
switch (cmd) { switch (cmd) {
case TPM_BACKEND_CMD_PROCESS_CMD: case TPM_BACKEND_CMD_PROCESS_CMD:
tpm_passthrough_unix_transfer(tpm_pt, tpm_passthrough_unix_transfer(tpm_pt,
thr_parms->tpm_state->locty_data); thr_parms->tpm_state->locty_data,
&selftest_done);
thr_parms->recv_data_callback(thr_parms->tpm_state, thr_parms->recv_data_callback(thr_parms->tpm_state,
thr_parms->tpm_state->locty_number); thr_parms->tpm_state->locty_number,
selftest_done);
break; break;
case TPM_BACKEND_CMD_INIT: case TPM_BACKEND_CMD_INIT:
case TPM_BACKEND_CMD_END: case TPM_BACKEND_CMD_END:

View file

@ -64,6 +64,7 @@
#define TPM_TIS_STS_TPM_GO (1 << 5) #define TPM_TIS_STS_TPM_GO (1 << 5)
#define TPM_TIS_STS_DATA_AVAILABLE (1 << 4) #define TPM_TIS_STS_DATA_AVAILABLE (1 << 4)
#define TPM_TIS_STS_EXPECT (1 << 3) #define TPM_TIS_STS_EXPECT (1 << 3)
#define TPM_TIS_STS_SELFTEST_DONE (1 << 2)
#define TPM_TIS_STS_RESPONSE_RETRY (1 << 1) #define TPM_TIS_STS_RESPONSE_RETRY (1 << 1)
#define TPM_TIS_BURST_COUNT_SHIFT 8 #define TPM_TIS_BURST_COUNT_SHIFT 8
@ -146,6 +147,24 @@ static void tpm_tis_show_buffer(const TPMSizedBuffer *sb, const char *string)
#endif #endif
} }
/*
* Set the given flags in the STS register by clearing the register but
* preserving the SELFTEST_DONE flag and then setting the new flags.
*
* The SELFTEST_DONE flag is acquired from the backend that determines it by
* peeking into TPM commands.
*
* A VM suspend/resume will preserve the flag by storing it into the VM
* device state, but the backend will not remember it when QEMU is started
* again. Therefore, we cache the flag here. Once set, it will not be unset
* except by a reset.
*/
static void tpm_tis_sts_set(TPMLocality *l, uint32_t flags)
{
l->sts &= TPM_TIS_STS_SELFTEST_DONE;
l->sts |= flags;
}
/* /*
* Send a request to the TPM. * Send a request to the TPM.
*/ */
@ -257,7 +276,8 @@ static void tpm_tis_abort(TPMState *s, uint8_t locty)
*/ */
if (tis->aborting_locty == tis->next_locty) { if (tis->aborting_locty == tis->next_locty) {
tis->loc[tis->aborting_locty].state = TPM_TIS_STATE_READY; tis->loc[tis->aborting_locty].state = TPM_TIS_STATE_READY;
tis->loc[tis->aborting_locty].sts = TPM_TIS_STS_COMMAND_READY; tpm_tis_sts_set(&tis->loc[tis->aborting_locty],
TPM_TIS_STS_COMMAND_READY);
tpm_tis_raise_irq(s, tis->aborting_locty, TPM_TIS_INT_COMMAND_READY); tpm_tis_raise_irq(s, tis->aborting_locty, TPM_TIS_INT_COMMAND_READY);
} }
@ -302,7 +322,8 @@ static void tpm_tis_receive_bh(void *opaque)
TPMTISEmuState *tis = &s->s.tis; TPMTISEmuState *tis = &s->s.tis;
uint8_t locty = s->locty_number; uint8_t locty = s->locty_number;
tis->loc[locty].sts = TPM_TIS_STS_VALID | TPM_TIS_STS_DATA_AVAILABLE; tpm_tis_sts_set(&tis->loc[locty],
TPM_TIS_STS_VALID | TPM_TIS_STS_DATA_AVAILABLE);
tis->loc[locty].state = TPM_TIS_STATE_COMPLETION; tis->loc[locty].state = TPM_TIS_STATE_COMPLETION;
tis->loc[locty].r_offset = 0; tis->loc[locty].r_offset = 0;
tis->loc[locty].w_offset = 0; tis->loc[locty].w_offset = 0;
@ -322,12 +343,20 @@ static void tpm_tis_receive_bh(void *opaque)
/* /*
* Callback from the TPM to indicate that the response was received. * Callback from the TPM to indicate that the response was received.
*/ */
static void tpm_tis_receive_cb(TPMState *s, uint8_t locty) static void tpm_tis_receive_cb(TPMState *s, uint8_t locty,
bool is_selftest_done)
{ {
TPMTISEmuState *tis = &s->s.tis; TPMTISEmuState *tis = &s->s.tis;
uint8_t l;
assert(s->locty_number == locty); assert(s->locty_number == locty);
if (is_selftest_done) {
for (l = 0; l < TPM_TIS_NUM_LOCALITIES; l++) {
tis->loc[locty].sts |= TPM_TIS_STS_SELFTEST_DONE;
}
}
qemu_bh_schedule(tis->bh); qemu_bh_schedule(tis->bh);
} }
@ -346,7 +375,7 @@ static uint32_t tpm_tis_data_read(TPMState *s, uint8_t locty)
ret = tis->loc[locty].r_buffer.buffer[tis->loc[locty].r_offset++]; ret = tis->loc[locty].r_buffer.buffer[tis->loc[locty].r_offset++];
if (tis->loc[locty].r_offset >= len) { if (tis->loc[locty].r_offset >= len) {
/* got last byte */ /* got last byte */
tis->loc[locty].sts = TPM_TIS_STS_VALID; tpm_tis_sts_set(&tis->loc[locty], TPM_TIS_STS_VALID);
#ifdef RAISE_STS_IRQ #ifdef RAISE_STS_IRQ
tpm_tis_raise_irq(s, locty, TPM_TIS_INT_STS_VALID); tpm_tis_raise_irq(s, locty, TPM_TIS_INT_STS_VALID);
#endif #endif
@ -714,7 +743,7 @@ static void tpm_tis_mmio_write_intern(void *opaque, hwaddr addr,
break; break;
case TPM_TIS_STATE_IDLE: case TPM_TIS_STATE_IDLE:
tis->loc[locty].sts = TPM_TIS_STS_COMMAND_READY; tpm_tis_sts_set(&tis->loc[locty], TPM_TIS_STS_COMMAND_READY);
tis->loc[locty].state = TPM_TIS_STATE_READY; tis->loc[locty].state = TPM_TIS_STATE_READY;
tpm_tis_raise_irq(s, locty, TPM_TIS_INT_COMMAND_READY); tpm_tis_raise_irq(s, locty, TPM_TIS_INT_COMMAND_READY);
break; break;
@ -733,7 +762,8 @@ static void tpm_tis_mmio_write_intern(void *opaque, hwaddr addr,
/* shortcut to ready state with C/R set */ /* shortcut to ready state with C/R set */
tis->loc[locty].state = TPM_TIS_STATE_READY; tis->loc[locty].state = TPM_TIS_STATE_READY;
if (!(tis->loc[locty].sts & TPM_TIS_STS_COMMAND_READY)) { if (!(tis->loc[locty].sts & TPM_TIS_STS_COMMAND_READY)) {
tis->loc[locty].sts = TPM_TIS_STS_COMMAND_READY; tpm_tis_sts_set(&tis->loc[locty],
TPM_TIS_STS_COMMAND_READY);
tpm_tis_raise_irq(s, locty, TPM_TIS_INT_COMMAND_READY); tpm_tis_raise_irq(s, locty, TPM_TIS_INT_COMMAND_READY);
} }
tis->loc[locty].sts &= ~(TPM_TIS_STS_DATA_AVAILABLE); tis->loc[locty].sts &= ~(TPM_TIS_STS_DATA_AVAILABLE);
@ -755,8 +785,9 @@ static void tpm_tis_mmio_write_intern(void *opaque, hwaddr addr,
switch (tis->loc[locty].state) { switch (tis->loc[locty].state) {
case TPM_TIS_STATE_COMPLETION: case TPM_TIS_STATE_COMPLETION:
tis->loc[locty].r_offset = 0; tis->loc[locty].r_offset = 0;
tis->loc[locty].sts = TPM_TIS_STS_VALID | tpm_tis_sts_set(&tis->loc[locty],
TPM_TIS_STS_DATA_AVAILABLE; TPM_TIS_STS_VALID|
TPM_TIS_STS_DATA_AVAILABLE);
break; break;
default: default:
/* ignore */ /* ignore */
@ -780,7 +811,8 @@ static void tpm_tis_mmio_write_intern(void *opaque, hwaddr addr,
val, size); val, size);
if (tis->loc[locty].state == TPM_TIS_STATE_READY) { if (tis->loc[locty].state == TPM_TIS_STATE_READY) {
tis->loc[locty].state = TPM_TIS_STATE_RECEPTION; tis->loc[locty].state = TPM_TIS_STATE_RECEPTION;
tis->loc[locty].sts = TPM_TIS_STS_EXPECT | TPM_TIS_STS_VALID; tpm_tis_sts_set(&tis->loc[locty],
TPM_TIS_STS_EXPECT | TPM_TIS_STS_VALID);
} }
val >>= shift; val >>= shift;
@ -796,7 +828,7 @@ static void tpm_tis_mmio_write_intern(void *opaque, hwaddr addr,
val >>= 8; val >>= 8;
size--; size--;
} else { } else {
tis->loc[locty].sts = TPM_TIS_STS_VALID; tpm_tis_sts_set(&tis->loc[locty], TPM_TIS_STS_VALID);
} }
} }
@ -809,11 +841,11 @@ static void tpm_tis_mmio_write_intern(void *opaque, hwaddr addr,
#endif #endif
len = tpm_tis_get_size_from_buffer(&tis->loc[locty].w_buffer); len = tpm_tis_get_size_from_buffer(&tis->loc[locty].w_buffer);
if (len > tis->loc[locty].w_offset) { if (len > tis->loc[locty].w_offset) {
tis->loc[locty].sts = TPM_TIS_STS_EXPECT | tpm_tis_sts_set(&tis->loc[locty],
TPM_TIS_STS_VALID; TPM_TIS_STS_EXPECT | TPM_TIS_STS_VALID);
} else { } else {
/* packet complete */ /* packet complete */
tis->loc[locty].sts = TPM_TIS_STS_VALID; tpm_tis_sts_set(&tis->loc[locty], TPM_TIS_STS_VALID);
} }
#ifdef RAISE_STS_IRQ #ifdef RAISE_STS_IRQ
if (needIrq) { if (needIrq) {

View file

@ -56,7 +56,7 @@ struct TPMBackend {
QLIST_ENTRY(TPMBackend) list; QLIST_ENTRY(TPMBackend) list;
}; };
typedef void (TPMRecvDataCB)(TPMState *, uint8_t locty); typedef void (TPMRecvDataCB)(TPMState *, uint8_t locty, bool selftest_done);
typedef struct TPMSizedBuffer { typedef struct TPMSizedBuffer {
uint32_t size; uint32_t size;