tpm: infrastructure for TPM spaces
Added an ability to virtualize TPM commands into an isolated context
that we call a TPM space because the word context is already heavily
used in the TPM specification. Both the handle areas and bodies (where
necessary) are virtualized.
The mechanism works by adding a new parameter struct tpm_space to the
tpm_transmit() function. This new structure contains the list of virtual
handles and a buffer of page size (currently) for backing storage.
When tpm_transmit() is called with a struct tpm_space instance it will
execute the following sequence:
1. Take locks.
2. Load transient objects from the backing storage by using ContextLoad
and map virtual handles to physical handles.
3. Perform the transaction.
4. Save transient objects to backing storage by using ContextSave and
map resulting physical handle to virtual handle if there is such.
This commit does not implement virtualization support for hmac and
policy sessions.
Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
Tested-by: James Bottomley <James.Bottomley@HansenPartnership.com>
Reviewed-by: James Bottomley <James.Bottomley@HansenPartnership.com>
diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c
index 20b1fe3..d09cf26 100644
--- a/drivers/char/tpm/tpm-interface.c
+++ b/drivers/char/tpm/tpm-interface.c
@@ -328,7 +328,9 @@ unsigned long tpm_calc_ordinal_duration(struct tpm_chip *chip,
}
EXPORT_SYMBOL_GPL(tpm_calc_ordinal_duration);
-static bool tpm_validate_command(struct tpm_chip *chip, const u8 *cmd,
+static bool tpm_validate_command(struct tpm_chip *chip,
+ struct tpm_space *space,
+ const u8 *cmd,
size_t len)
{
const struct tpm_input_header *header = (const void *)cmd;
@@ -340,6 +342,9 @@ static bool tpm_validate_command(struct tpm_chip *chip, const u8 *cmd,
if (len < TPM_HEADER_SIZE)
return false;
+ if (!space)
+ return true;
+
if (chip->flags & TPM_CHIP_FLAG_TPM2 && chip->nr_commands) {
cc = be32_to_cpu(header->ordinal);
@@ -376,15 +381,16 @@ static bool tpm_validate_command(struct tpm_chip *chip, const u8 *cmd,
* 0 when the operation is successful.
* A negative number for system errors (errno).
*/
-ssize_t tpm_transmit(struct tpm_chip *chip, const u8 *buf, size_t bufsiz,
- unsigned int flags)
+ssize_t tpm_transmit(struct tpm_chip *chip, struct tpm_space *space,
+ u8 *buf, size_t bufsiz, unsigned int flags)
{
- const struct tpm_output_header *header = (void *)buf;
- ssize_t rc;
+ struct tpm_output_header *header = (void *)buf;
+ int rc;
+ ssize_t len = 0;
u32 count, ordinal;
unsigned long stop;
- if (!tpm_validate_command(chip, buf, bufsiz))
+ if (!tpm_validate_command(chip, space, buf, bufsiz))
return -EINVAL;
if (bufsiz > TPM_BUFSIZE)
@@ -406,10 +412,14 @@ ssize_t tpm_transmit(struct tpm_chip *chip, const u8 *buf, size_t bufsiz,
if (chip->dev.parent)
pm_runtime_get_sync(chip->dev.parent);
+ rc = tpm2_prepare_space(chip, space, ordinal, buf);
+ if (rc)
+ goto out;
+
rc = chip->ops->send(chip, (u8 *) buf, count);
if (rc < 0) {
dev_err(&chip->dev,
- "tpm_transmit: tpm_send: error %zd\n", rc);
+ "tpm_transmit: tpm_send: error %d\n", rc);
goto out;
}
@@ -442,18 +452,23 @@ ssize_t tpm_transmit(struct tpm_chip *chip, const u8 *buf, size_t bufsiz,
goto out;
out_recv:
- rc = chip->ops->recv(chip, (u8 *) buf, bufsiz);
- if (rc < 0) {
+ len = chip->ops->recv(chip, (u8 *) buf, bufsiz);
+ if (len < 0) {
+ rc = len;
dev_err(&chip->dev,
- "tpm_transmit: tpm_recv: error %zd\n", rc);
+ "tpm_transmit: tpm_recv: error %d\n", rc);
goto out;
- } else if (rc < TPM_HEADER_SIZE) {
+ } else if (len < TPM_HEADER_SIZE) {
rc = -EFAULT;
goto out;
}
- if (rc != be32_to_cpu(header->length))
+ if (len != be32_to_cpu(header->length)) {
+ rc = -EFAULT;
goto out;
+ }
+
+ rc = tpm2_commit_space(chip, space, ordinal, buf, &len);
out:
if (chip->dev.parent)
@@ -461,7 +476,7 @@ ssize_t tpm_transmit(struct tpm_chip *chip, const u8 *buf, size_t bufsiz,
if (!(flags & TPM_TRANSMIT_UNLOCKED))
mutex_unlock(&chip->tpm_mutex);
- return rc;
+ return rc ? rc : len;
}
/**
@@ -480,15 +495,16 @@ ssize_t tpm_transmit(struct tpm_chip *chip, const u8 *buf, size_t bufsiz,
* A negative number for system errors (errno).
* A positive number for a TPM error.
*/
-ssize_t tpm_transmit_cmd(struct tpm_chip *chip, const void *buf,
- size_t bufsiz, size_t min_rsp_body_length,
- unsigned int flags, const char *desc)
+ssize_t tpm_transmit_cmd(struct tpm_chip *chip, struct tpm_space *space,
+ const void *buf, size_t bufsiz,
+ size_t min_rsp_body_length, unsigned int flags,
+ const char *desc)
{
const struct tpm_output_header *header = buf;
int err;
ssize_t len;
- len = tpm_transmit(chip, (const u8 *)buf, bufsiz, flags);
+ len = tpm_transmit(chip, space, (u8 *)buf, bufsiz, flags);
if (len < 0)
return len;
@@ -541,7 +557,7 @@ ssize_t tpm_getcap(struct tpm_chip *chip, u32 subcap_id, cap_t *cap,
tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
tpm_cmd.params.getcap_in.subcap = cpu_to_be32(subcap_id);
}
- rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE,
+ rc = tpm_transmit_cmd(chip, NULL, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE,
min_cap_length, 0, desc);
if (!rc)
*cap = tpm_cmd.params.getcap_out.cap;
@@ -565,7 +581,8 @@ static int tpm_startup(struct tpm_chip *chip, __be16 startup_type)
start_cmd.header.in = tpm_startup_header;
start_cmd.params.startup_in.startup_type = startup_type;
- return tpm_transmit_cmd(chip, &start_cmd, TPM_INTERNAL_RESULT_SIZE, 0,
+ return tpm_transmit_cmd(chip, NULL, &start_cmd,
+ TPM_INTERNAL_RESULT_SIZE, 0,
0, "attempting to start the TPM");
}
@@ -722,8 +739,8 @@ static int tpm_continue_selftest(struct tpm_chip *chip)
struct tpm_cmd_t cmd;
cmd.header.in = continue_selftest_header;
- rc = tpm_transmit_cmd(chip, &cmd, CONTINUE_SELFTEST_RESULT_SIZE, 0, 0,
- "continue selftest");
+ rc = tpm_transmit_cmd(chip, NULL, &cmd, CONTINUE_SELFTEST_RESULT_SIZE,
+ 0, 0, "continue selftest");
return rc;
}
@@ -743,7 +760,7 @@ int tpm_pcr_read_dev(struct tpm_chip *chip, int pcr_idx, u8 *res_buf)
cmd.header.in = pcrread_header;
cmd.params.pcrread_in.pcr_idx = cpu_to_be32(pcr_idx);
- rc = tpm_transmit_cmd(chip, &cmd, READ_PCR_RESULT_SIZE,
+ rc = tpm_transmit_cmd(chip, NULL, &cmd, READ_PCR_RESULT_SIZE,
READ_PCR_RESULT_BODY_SIZE, 0,
"attempting to read a pcr value");
@@ -855,7 +872,7 @@ int tpm_pcr_extend(u32 chip_num, int pcr_idx, const u8 *hash)
cmd.header.in = pcrextend_header;
cmd.params.pcrextend_in.pcr_idx = cpu_to_be32(pcr_idx);
memcpy(cmd.params.pcrextend_in.hash, hash, TPM_DIGEST_SIZE);
- rc = tpm_transmit_cmd(chip, &cmd, EXTEND_PCR_RESULT_SIZE,
+ rc = tpm_transmit_cmd(chip, NULL, &cmd, EXTEND_PCR_RESULT_SIZE,
EXTEND_PCR_RESULT_BODY_SIZE, 0,
"attempting extend a PCR value");
@@ -960,8 +977,8 @@ int tpm_send(u32 chip_num, void *cmd, size_t buflen)
if (chip == NULL)
return -ENODEV;
- rc = tpm_transmit_cmd(chip, cmd, buflen, 0, 0, "attempting tpm_cmd");
-
+ rc = tpm_transmit_cmd(chip, NULL, cmd, buflen, 0, 0,
+ "attempting tpm_cmd");
tpm_put_ops(chip);
return rc;
}
@@ -1062,16 +1079,16 @@ int tpm_pm_suspend(struct device *dev)
cmd.params.pcrextend_in.pcr_idx = cpu_to_be32(tpm_suspend_pcr);
memcpy(cmd.params.pcrextend_in.hash, dummy_hash,
TPM_DIGEST_SIZE);
- rc = tpm_transmit_cmd(chip, &cmd, EXTEND_PCR_RESULT_SIZE,
- EXTEND_PCR_RESULT_BODY_SIZE, 0,
+ rc = tpm_transmit_cmd(chip, NULL, &cmd, EXTEND_PCR_RESULT_SIZE,
+ EXTEND_PCR_RESULT_BODY_SIZE, 0,
"extending dummy pcr before suspend");
}
/* now do the actual savestate */
for (try = 0; try < TPM_RETRY; try++) {
cmd.header.in = savestate_header;
- rc = tpm_transmit_cmd(chip, &cmd, SAVESTATE_RESULT_SIZE, 0,
- 0, NULL);
+ rc = tpm_transmit_cmd(chip, NULL, &cmd, SAVESTATE_RESULT_SIZE,
+ 0, 0, NULL);
/*
* If the TPM indicates that it is too busy to respond to
@@ -1154,7 +1171,7 @@ int tpm_get_random(u32 chip_num, u8 *out, size_t max)
tpm_cmd.header.in = tpm_getrandom_header;
tpm_cmd.params.getrandom_in.num_bytes = cpu_to_be32(num_bytes);
- err = tpm_transmit_cmd(chip, &tpm_cmd,
+ err = tpm_transmit_cmd(chip, NULL, &tpm_cmd,
TPM_GETRANDOM_RESULT_SIZE + num_bytes,
offsetof(struct tpm_getrandom_out,
rng_data),