mirror of https://github.com/xemu-project/xemu.git
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:
parent
2eae8c7516
commit
fd85908145
|
@ -62,6 +62,7 @@ struct tpm_resp_hdr {
|
|||
|
||||
#define TPM_FAIL 9
|
||||
|
||||
#define TPM_ORD_ContinueSelfTest 0x53
|
||||
#define TPM_ORD_GetTicks 0xf1
|
||||
|
||||
#endif /* TPM_TPM_INT_H */
|
||||
|
|
|
@ -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,
|
||||
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;
|
||||
bool is_selftest;
|
||||
const struct tpm_resp_hdr *hdr;
|
||||
|
||||
tpm_pt->tpm_op_canceled = false;
|
||||
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);
|
||||
if (ret != in_len) {
|
||||
|
@ -149,6 +166,11 @@ static int tpm_passthrough_unix_tx_bufs(TPMPassthruState *tpm_pt,
|
|||
"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:
|
||||
if (ret < 0) {
|
||||
tpm_write_fatal_error_response(out, out_len);
|
||||
|
@ -160,13 +182,15 @@ err_exit:
|
|||
}
|
||||
|
||||
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,
|
||||
locty_data->w_buffer.buffer,
|
||||
locty_data->w_offset,
|
||||
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,
|
||||
|
@ -175,16 +199,19 @@ static void tpm_passthrough_worker_thread(gpointer data,
|
|||
TPMPassthruThreadParams *thr_parms = user_data;
|
||||
TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(thr_parms->tb);
|
||||
TPMBackendCmd cmd = (TPMBackendCmd)data;
|
||||
bool selftest_done = false;
|
||||
|
||||
DPRINTF("tpm_passthrough: processing command type %d\n", cmd);
|
||||
|
||||
switch (cmd) {
|
||||
case TPM_BACKEND_CMD_PROCESS_CMD:
|
||||
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->tpm_state->locty_number);
|
||||
thr_parms->tpm_state->locty_number,
|
||||
selftest_done);
|
||||
break;
|
||||
case TPM_BACKEND_CMD_INIT:
|
||||
case TPM_BACKEND_CMD_END:
|
||||
|
|
|
@ -64,6 +64,7 @@
|
|||
#define TPM_TIS_STS_TPM_GO (1 << 5)
|
||||
#define TPM_TIS_STS_DATA_AVAILABLE (1 << 4)
|
||||
#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_BURST_COUNT_SHIFT 8
|
||||
|
@ -146,6 +147,24 @@ static void tpm_tis_show_buffer(const TPMSizedBuffer *sb, const char *string)
|
|||
#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.
|
||||
*/
|
||||
|
@ -257,7 +276,8 @@ static void tpm_tis_abort(TPMState *s, uint8_t locty)
|
|||
*/
|
||||
if (tis->aborting_locty == tis->next_locty) {
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -302,7 +322,8 @@ static void tpm_tis_receive_bh(void *opaque)
|
|||
TPMTISEmuState *tis = &s->s.tis;
|
||||
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].r_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.
|
||||
*/
|
||||
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;
|
||||
uint8_t l;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -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++];
|
||||
if (tis->loc[locty].r_offset >= len) {
|
||||
/* 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
|
||||
tpm_tis_raise_irq(s, locty, TPM_TIS_INT_STS_VALID);
|
||||
#endif
|
||||
|
@ -714,7 +743,7 @@ static void tpm_tis_mmio_write_intern(void *opaque, hwaddr addr,
|
|||
break;
|
||||
|
||||
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;
|
||||
tpm_tis_raise_irq(s, locty, TPM_TIS_INT_COMMAND_READY);
|
||||
break;
|
||||
|
@ -733,7 +762,8 @@ static void tpm_tis_mmio_write_intern(void *opaque, hwaddr addr,
|
|||
/* shortcut to ready state with C/R set */
|
||||
tis->loc[locty].state = TPM_TIS_STATE_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);
|
||||
}
|
||||
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) {
|
||||
case TPM_TIS_STATE_COMPLETION:
|
||||
tis->loc[locty].r_offset = 0;
|
||||
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);
|
||||
break;
|
||||
default:
|
||||
/* ignore */
|
||||
|
@ -780,7 +811,8 @@ static void tpm_tis_mmio_write_intern(void *opaque, hwaddr addr,
|
|||
val, size);
|
||||
if (tis->loc[locty].state == TPM_TIS_STATE_READY) {
|
||||
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;
|
||||
|
@ -796,7 +828,7 @@ static void tpm_tis_mmio_write_intern(void *opaque, hwaddr addr,
|
|||
val >>= 8;
|
||||
size--;
|
||||
} 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
|
||||
len = tpm_tis_get_size_from_buffer(&tis->loc[locty].w_buffer);
|
||||
if (len > tis->loc[locty].w_offset) {
|
||||
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);
|
||||
} else {
|
||||
/* 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
|
||||
if (needIrq) {
|
||||
|
|
|
@ -56,7 +56,7 @@ struct TPMBackend {
|
|||
QLIST_ENTRY(TPMBackend) list;
|
||||
};
|
||||
|
||||
typedef void (TPMRecvDataCB)(TPMState *, uint8_t locty);
|
||||
typedef void (TPMRecvDataCB)(TPMState *, uint8_t locty, bool selftest_done);
|
||||
|
||||
typedef struct TPMSizedBuffer {
|
||||
uint32_t size;
|
||||
|
|
Loading…
Reference in New Issue