u-boot/lib/tpm-v2.c
Tom Rini 467382ca03 lib: Remove <common.h> inclusion from these files
After some header file cleanups to add missing include files, remove
common.h from all files in the lib directory. This primarily means just
dropping the line but in a few cases we need to add in other header
files now.

Reviewed-by: Simon Glass <sjg@chromium.org>
Signed-off-by: Tom Rini <trini@konsulko.com>
2023-12-21 08:54:37 -05:00

1558 lines
40 KiB
C

// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (c) 2023 Linaro Limited
* Copyright (c) 2018 Bootlin
* Author: Miquel Raynal <miquel.raynal@bootlin.com>
*/
#include <dm.h>
#include <dm/of_access.h>
#include <tpm_api.h>
#include <tpm-common.h>
#include <tpm-v2.h>
#include <u-boot/sha1.h>
#include <u-boot/sha256.h>
#include <u-boot/sha512.h>
#include <version_string.h>
#include <asm/io.h>
#include <linux/bitops.h>
#include <linux/unaligned/be_byteshift.h>
#include <linux/unaligned/generic.h>
#include <linux/unaligned/le_byteshift.h>
#include "tpm-utils.h"
const enum tpm2_algorithms tpm2_supported_algorithms[4] = {
TPM2_ALG_SHA1,
TPM2_ALG_SHA256,
TPM2_ALG_SHA384,
TPM2_ALG_SHA512,
};
int tcg2_get_active_pcr_banks(struct udevice *dev, u32 *active_pcr_banks)
{
u32 supported = 0;
u32 pcr_banks = 0;
u32 active = 0;
int rc;
rc = tpm2_get_pcr_info(dev, &supported, &active, &pcr_banks);
if (rc)
return rc;
*active_pcr_banks = active;
return 0;
}
u32 tcg2_event_get_size(struct tpml_digest_values *digest_list)
{
u32 len;
size_t i;
len = offsetof(struct tcg_pcr_event2, digests);
len += offsetof(struct tpml_digest_values, digests);
for (i = 0; i < digest_list->count; ++i) {
u16 l = tpm2_algorithm_to_len(digest_list->digests[i].hash_alg);
if (!l)
continue;
len += l + offsetof(struct tpmt_ha, digest);
}
len += sizeof(u32);
return len;
}
int tcg2_create_digest(struct udevice *dev, const u8 *input, u32 length,
struct tpml_digest_values *digest_list)
{
u8 final[sizeof(union tpmu_ha)];
sha256_context ctx_256;
sha512_context ctx_512;
sha1_context ctx;
u32 active;
size_t i;
u32 len;
int rc;
rc = tcg2_get_active_pcr_banks(dev, &active);
if (rc)
return rc;
digest_list->count = 0;
for (i = 0; i < ARRAY_SIZE(tpm2_supported_algorithms); ++i) {
u32 mask =
tpm2_algorithm_to_mask(tpm2_supported_algorithms[i]);
if (!(active & mask))
continue;
switch (tpm2_supported_algorithms[i]) {
case TPM2_ALG_SHA1:
sha1_starts(&ctx);
sha1_update(&ctx, input, length);
sha1_finish(&ctx, final);
len = TPM2_SHA1_DIGEST_SIZE;
break;
case TPM2_ALG_SHA256:
sha256_starts(&ctx_256);
sha256_update(&ctx_256, input, length);
sha256_finish(&ctx_256, final);
len = TPM2_SHA256_DIGEST_SIZE;
break;
case TPM2_ALG_SHA384:
sha384_starts(&ctx_512);
sha384_update(&ctx_512, input, length);
sha384_finish(&ctx_512, final);
len = TPM2_SHA384_DIGEST_SIZE;
break;
case TPM2_ALG_SHA512:
sha512_starts(&ctx_512);
sha512_update(&ctx_512, input, length);
sha512_finish(&ctx_512, final);
len = TPM2_SHA512_DIGEST_SIZE;
break;
default:
printf("%s: unsupported algorithm %x\n", __func__,
tpm2_supported_algorithms[i]);
continue;
}
digest_list->digests[digest_list->count].hash_alg =
tpm2_supported_algorithms[i];
memcpy(&digest_list->digests[digest_list->count].digest, final,
len);
digest_list->count++;
}
return 0;
}
void tcg2_log_append(u32 pcr_index, u32 event_type,
struct tpml_digest_values *digest_list, u32 size,
const u8 *event, u8 *log)
{
size_t len;
size_t pos;
u32 i;
pos = offsetof(struct tcg_pcr_event2, pcr_index);
put_unaligned_le32(pcr_index, log);
pos = offsetof(struct tcg_pcr_event2, event_type);
put_unaligned_le32(event_type, log + pos);
pos = offsetof(struct tcg_pcr_event2, digests) +
offsetof(struct tpml_digest_values, count);
put_unaligned_le32(digest_list->count, log + pos);
pos = offsetof(struct tcg_pcr_event2, digests) +
offsetof(struct tpml_digest_values, digests);
for (i = 0; i < digest_list->count; ++i) {
u16 hash_alg = digest_list->digests[i].hash_alg;
len = tpm2_algorithm_to_len(hash_alg);
if (!len)
continue;
pos += offsetof(struct tpmt_ha, hash_alg);
put_unaligned_le16(hash_alg, log + pos);
pos += offsetof(struct tpmt_ha, digest);
memcpy(log + pos, (u8 *)&digest_list->digests[i].digest, len);
pos += len;
}
put_unaligned_le32(size, log + pos);
pos += sizeof(u32);
memcpy(log + pos, event, size);
}
static int tcg2_log_append_check(struct tcg2_event_log *elog, u32 pcr_index,
u32 event_type,
struct tpml_digest_values *digest_list,
u32 size, const u8 *event)
{
u32 event_size;
u8 *log;
event_size = size + tcg2_event_get_size(digest_list);
if (elog->log_position + event_size > elog->log_size) {
printf("%s: log too large: %u + %u > %u\n", __func__,
elog->log_position, event_size, elog->log_size);
return -ENOBUFS;
}
log = elog->log + elog->log_position;
elog->log_position += event_size;
tcg2_log_append(pcr_index, event_type, digest_list, size, event, log);
return 0;
}
static int tcg2_log_init(struct udevice *dev, struct tcg2_event_log *elog)
{
struct tcg_efi_spec_id_event *ev;
struct tcg_pcr_event *log;
u32 event_size;
u32 count = 0;
u32 log_size;
u32 active;
u32 mask;
size_t i;
u16 len;
int rc;
rc = tcg2_get_active_pcr_banks(dev, &active);
if (rc)
return rc;
event_size = offsetof(struct tcg_efi_spec_id_event, digest_sizes);
for (i = 0; i < ARRAY_SIZE(tpm2_supported_algorithms); ++i) {
mask = tpm2_algorithm_to_mask(tpm2_supported_algorithms[i]);
if (!(active & mask))
continue;
switch (tpm2_supported_algorithms[i]) {
case TPM2_ALG_SHA1:
case TPM2_ALG_SHA256:
case TPM2_ALG_SHA384:
case TPM2_ALG_SHA512:
count++;
break;
default:
continue;
}
}
event_size += 1 +
(sizeof(struct tcg_efi_spec_id_event_algorithm_size) * count);
log_size = offsetof(struct tcg_pcr_event, event) + event_size;
if (log_size > elog->log_size) {
printf("%s: log too large: %u > %u\n", __func__, log_size,
elog->log_size);
return -ENOBUFS;
}
log = (struct tcg_pcr_event *)elog->log;
put_unaligned_le32(0, &log->pcr_index);
put_unaligned_le32(EV_NO_ACTION, &log->event_type);
memset(&log->digest, 0, sizeof(log->digest));
put_unaligned_le32(event_size, &log->event_size);
ev = (struct tcg_efi_spec_id_event *)log->event;
strlcpy((char *)ev->signature, TCG_EFI_SPEC_ID_EVENT_SIGNATURE_03,
sizeof(ev->signature));
put_unaligned_le32(0, &ev->platform_class);
ev->spec_version_minor = TCG_EFI_SPEC_ID_EVENT_SPEC_VERSION_MINOR_TPM2;
ev->spec_version_major = TCG_EFI_SPEC_ID_EVENT_SPEC_VERSION_MAJOR_TPM2;
ev->spec_errata = TCG_EFI_SPEC_ID_EVENT_SPEC_VERSION_ERRATA_TPM2;
ev->uintn_size = sizeof(size_t) / sizeof(u32);
put_unaligned_le32(count, &ev->number_of_algorithms);
count = 0;
for (i = 0; i < ARRAY_SIZE(tpm2_supported_algorithms); ++i) {
mask = tpm2_algorithm_to_mask(tpm2_supported_algorithms[i]);
if (!(active & mask))
continue;
len = tpm2_algorithm_to_len(tpm2_supported_algorithms[i]);
if (!len)
continue;
put_unaligned_le16(tpm2_supported_algorithms[i],
&ev->digest_sizes[count].algorithm_id);
put_unaligned_le16(len, &ev->digest_sizes[count].digest_size);
count++;
}
*((u8 *)ev + (event_size - 1)) = 0;
elog->log_position = log_size;
return 0;
}
static int tcg2_replay_eventlog(struct tcg2_event_log *elog,
struct udevice *dev,
struct tpml_digest_values *digest_list,
u32 log_position)
{
const u32 offset = offsetof(struct tcg_pcr_event2, digests) +
offsetof(struct tpml_digest_values, digests);
u32 event_size;
u32 count;
u16 algo;
u32 pcr;
u32 pos;
u16 len;
u8 *log;
int rc;
u32 i;
while (log_position + offset < elog->log_size) {
log = elog->log + log_position;
pos = offsetof(struct tcg_pcr_event2, pcr_index);
pcr = get_unaligned_le32(log + pos);
pos = offsetof(struct tcg_pcr_event2, event_type);
if (!get_unaligned_le32(log + pos))
return 0;
pos = offsetof(struct tcg_pcr_event2, digests) +
offsetof(struct tpml_digest_values, count);
count = get_unaligned_le32(log + pos);
if (count > ARRAY_SIZE(tpm2_supported_algorithms) ||
(digest_list->count && digest_list->count != count))
return 0;
pos = offsetof(struct tcg_pcr_event2, digests) +
offsetof(struct tpml_digest_values, digests);
for (i = 0; i < count; ++i) {
pos += offsetof(struct tpmt_ha, hash_alg);
if (log_position + pos + sizeof(u16) >= elog->log_size)
return 0;
algo = get_unaligned_le16(log + pos);
pos += offsetof(struct tpmt_ha, digest);
switch (algo) {
case TPM2_ALG_SHA1:
case TPM2_ALG_SHA256:
case TPM2_ALG_SHA384:
case TPM2_ALG_SHA512:
len = tpm2_algorithm_to_len(algo);
break;
default:
return 0;
}
if (digest_list->count) {
if (algo != digest_list->digests[i].hash_alg ||
log_position + pos + len >= elog->log_size)
return 0;
memcpy(digest_list->digests[i].digest.sha512,
log + pos, len);
}
pos += len;
}
if (log_position + pos + sizeof(u32) >= elog->log_size)
return 0;
event_size = get_unaligned_le32(log + pos);
pos += event_size + sizeof(u32);
if (log_position + pos > elog->log_size)
return 0;
if (digest_list->count) {
rc = tcg2_pcr_extend(dev, pcr, digest_list);
if (rc)
return rc;
}
log_position += pos;
}
elog->log_position = log_position;
elog->found = true;
return 0;
}
static int tcg2_log_parse(struct udevice *dev, struct tcg2_event_log *elog)
{
struct tpml_digest_values digest_list;
struct tcg_efi_spec_id_event *event;
struct tcg_pcr_event *log;
u32 log_active;
u32 calc_size;
u32 active;
u32 count;
u32 evsz;
u32 mask;
u16 algo;
u16 len;
int rc;
u32 i;
u16 j;
if (elog->log_size <= offsetof(struct tcg_pcr_event, event))
return 0;
log = (struct tcg_pcr_event *)elog->log;
if (get_unaligned_le32(&log->pcr_index) != 0 ||
get_unaligned_le32(&log->event_type) != EV_NO_ACTION)
return 0;
for (i = 0; i < sizeof(log->digest); i++) {
if (log->digest[i])
return 0;
}
evsz = get_unaligned_le32(&log->event_size);
if (evsz < offsetof(struct tcg_efi_spec_id_event, digest_sizes) ||
evsz + offsetof(struct tcg_pcr_event, event) > elog->log_size)
return 0;
event = (struct tcg_efi_spec_id_event *)log->event;
if (memcmp(event->signature, TCG_EFI_SPEC_ID_EVENT_SIGNATURE_03,
sizeof(TCG_EFI_SPEC_ID_EVENT_SIGNATURE_03)))
return 0;
if (event->spec_version_minor != TCG_EFI_SPEC_ID_EVENT_SPEC_VERSION_MINOR_TPM2 ||
event->spec_version_major != TCG_EFI_SPEC_ID_EVENT_SPEC_VERSION_MAJOR_TPM2)
return 0;
count = get_unaligned_le32(&event->number_of_algorithms);
if (count > ARRAY_SIZE(tpm2_supported_algorithms))
return 0;
calc_size = offsetof(struct tcg_efi_spec_id_event, digest_sizes) +
(sizeof(struct tcg_efi_spec_id_event_algorithm_size) * count) +
1;
if (evsz != calc_size)
return 0;
rc = tcg2_get_active_pcr_banks(dev, &active);
if (rc)
return rc;
digest_list.count = 0;
log_active = 0;
for (i = 0; i < count; ++i) {
algo = get_unaligned_le16(&event->digest_sizes[i].algorithm_id);
mask = tpm2_algorithm_to_mask(algo);
if (!(active & mask))
return 0;
switch (algo) {
case TPM2_ALG_SHA1:
case TPM2_ALG_SHA256:
case TPM2_ALG_SHA384:
case TPM2_ALG_SHA512:
len = get_unaligned_le16(&event->digest_sizes[i].digest_size);
if (tpm2_algorithm_to_len(algo) != len)
return 0;
digest_list.digests[digest_list.count++].hash_alg = algo;
break;
default:
return 0;
}
log_active |= mask;
}
/* Ensure the previous firmware extended all the PCRs. */
if (log_active != active)
return 0;
/* Read PCR0 to check if previous firmware extended the PCRs or not. */
rc = tcg2_pcr_read(dev, 0, &digest_list);
if (rc)
return rc;
for (i = 0; i < digest_list.count; ++i) {
len = tpm2_algorithm_to_len(digest_list.digests[i].hash_alg);
for (j = 0; j < len; ++j) {
if (digest_list.digests[i].digest.sha512[j])
break;
}
/* PCR is non-zero; it has been extended, so skip extending. */
if (j != len) {
digest_list.count = 0;
break;
}
}
return tcg2_replay_eventlog(elog, dev, &digest_list,
offsetof(struct tcg_pcr_event, event) +
evsz);
}
int tcg2_pcr_extend(struct udevice *dev, u32 pcr_index,
struct tpml_digest_values *digest_list)
{
u32 rc;
u32 i;
for (i = 0; i < digest_list->count; i++) {
u32 alg = digest_list->digests[i].hash_alg;
rc = tpm2_pcr_extend(dev, pcr_index, alg,
(u8 *)&digest_list->digests[i].digest,
tpm2_algorithm_to_len(alg));
if (rc) {
printf("%s: error pcr:%u alg:%08x\n", __func__,
pcr_index, alg);
return rc;
}
}
return 0;
}
int tcg2_pcr_read(struct udevice *dev, u32 pcr_index,
struct tpml_digest_values *digest_list)
{
struct tpm_chip_priv *priv;
u32 rc;
u32 i;
priv = dev_get_uclass_priv(dev);
if (!priv)
return -ENODEV;
for (i = 0; i < digest_list->count; i++) {
u32 alg = digest_list->digests[i].hash_alg;
u8 *digest = (u8 *)&digest_list->digests[i].digest;
rc = tpm2_pcr_read(dev, pcr_index, priv->pcr_select_min, alg,
digest, tpm2_algorithm_to_len(alg), NULL);
if (rc) {
printf("%s: error pcr:%u alg:%08x\n", __func__,
pcr_index, alg);
return rc;
}
}
return 0;
}
int tcg2_measure_data(struct udevice *dev, struct tcg2_event_log *elog,
u32 pcr_index, u32 size, const u8 *data, u32 event_type,
u32 event_size, const u8 *event)
{
struct tpml_digest_values digest_list;
int rc;
if (data)
rc = tcg2_create_digest(dev, data, size, &digest_list);
else
rc = tcg2_create_digest(dev, event, event_size, &digest_list);
if (rc)
return rc;
rc = tcg2_pcr_extend(dev, pcr_index, &digest_list);
if (rc)
return rc;
return tcg2_log_append_check(elog, pcr_index, event_type, &digest_list,
event_size, event);
}
int tcg2_log_prepare_buffer(struct udevice *dev, struct tcg2_event_log *elog,
bool ignore_existing_log)
{
struct tcg2_event_log log;
int rc;
elog->log_position = 0;
elog->found = false;
rc = tcg2_platform_get_log(dev, (void **)&log.log, &log.log_size);
if (!rc) {
log.log_position = 0;
log.found = false;
if (!ignore_existing_log) {
rc = tcg2_log_parse(dev, &log);
if (rc)
return rc;
}
if (elog->log_size) {
if (log.found) {
if (elog->log_size < log.log_position)
return -ENOSPC;
/*
* Copy the discovered log into the user buffer
* if there's enough space.
*/
memcpy(elog->log, log.log, log.log_position);
}
unmap_physmem(log.log, MAP_NOCACHE);
} else {
elog->log = log.log;
elog->log_size = log.log_size;
}
elog->log_position = log.log_position;
elog->found = log.found;
}
/*
* Initialize the log buffer if no log was discovered and the buffer is
* valid. User's can pass in their own buffer as a fallback if no
* memory region is found.
*/
if (!elog->found && elog->log_size)
rc = tcg2_log_init(dev, elog);
return rc;
}
int tcg2_measurement_init(struct udevice **dev, struct tcg2_event_log *elog,
bool ignore_existing_log)
{
int rc;
rc = tcg2_platform_get_tpm2(dev);
if (rc)
return rc;
rc = tpm_auto_start(*dev);
if (rc)
return rc;
rc = tcg2_log_prepare_buffer(*dev, elog, ignore_existing_log);
if (rc) {
tcg2_measurement_term(*dev, elog, true);
return rc;
}
rc = tcg2_measure_event(*dev, elog, 0, EV_S_CRTM_VERSION,
strlen(version_string) + 1,
(u8 *)version_string);
if (rc) {
tcg2_measurement_term(*dev, elog, true);
return rc;
}
return 0;
}
void tcg2_measurement_term(struct udevice *dev, struct tcg2_event_log *elog,
bool error)
{
u32 event = error ? 0x1 : 0xffffffff;
int i;
for (i = 0; i < 8; ++i)
tcg2_measure_event(dev, elog, i, EV_SEPARATOR, sizeof(event),
(const u8 *)&event);
if (elog->log)
unmap_physmem(elog->log, MAP_NOCACHE);
}
__weak int tcg2_platform_get_log(struct udevice *dev, void **addr, u32 *size)
{
const __be32 *addr_prop;
const __be32 *size_prop;
int asize;
int ssize;
*addr = NULL;
*size = 0;
addr_prop = dev_read_prop(dev, "tpm_event_log_addr", &asize);
if (!addr_prop)
addr_prop = dev_read_prop(dev, "linux,sml-base", &asize);
size_prop = dev_read_prop(dev, "tpm_event_log_size", &ssize);
if (!size_prop)
size_prop = dev_read_prop(dev, "linux,sml-size", &ssize);
if (addr_prop && size_prop) {
u64 a = of_read_number(addr_prop, asize / sizeof(__be32));
u64 s = of_read_number(size_prop, ssize / sizeof(__be32));
*addr = map_physmem(a, s, MAP_NOCACHE);
*size = (u32)s;
} else {
struct ofnode_phandle_args args;
phys_addr_t a;
fdt_size_t s;
if (dev_read_phandle_with_args(dev, "memory-region", NULL, 0,
0, &args))
return -ENODEV;
a = ofnode_get_addr_size(args.node, "reg", &s);
if (a == FDT_ADDR_T_NONE)
return -ENOMEM;
*addr = map_physmem(a, s, MAP_NOCACHE);
*size = (u32)s;
}
return 0;
}
__weak int tcg2_platform_get_tpm2(struct udevice **dev)
{
for_each_tpm_device(*dev) {
if (tpm_get_version(*dev) == TPM_V2)
return 0;
}
return -ENODEV;
}
__weak void tcg2_platform_startup_error(struct udevice *dev, int rc) {}
u32 tpm2_startup(struct udevice *dev, enum tpm2_startup_types mode)
{
const u8 command_v2[12] = {
tpm_u16(TPM2_ST_NO_SESSIONS),
tpm_u32(12),
tpm_u32(TPM2_CC_STARTUP),
tpm_u16(mode),
};
int ret;
/*
* Note TPM2_Startup command will return RC_SUCCESS the first time,
* but will return RC_INITIALIZE otherwise.
*/
ret = tpm_sendrecv_command(dev, command_v2, NULL, NULL);
if (ret && ret != TPM2_RC_INITIALIZE)
return ret;
return 0;
}
u32 tpm2_self_test(struct udevice *dev, enum tpm2_yes_no full_test)
{
const u8 command_v2[12] = {
tpm_u16(TPM2_ST_NO_SESSIONS),
tpm_u32(11),
tpm_u32(TPM2_CC_SELF_TEST),
full_test,
};
return tpm_sendrecv_command(dev, command_v2, NULL, NULL);
}
u32 tpm2_auto_start(struct udevice *dev)
{
u32 rc;
rc = tpm2_self_test(dev, TPMI_YES);
if (rc == TPM2_RC_INITIALIZE) {
rc = tpm2_startup(dev, TPM2_SU_CLEAR);
if (rc)
return rc;
rc = tpm2_self_test(dev, TPMI_YES);
}
return rc;
}
u32 tpm2_clear(struct udevice *dev, u32 handle, const char *pw,
const ssize_t pw_sz)
{
/* Length of the message header, up to start of password */
uint offset = 27;
u8 command_v2[COMMAND_BUFFER_SIZE] = {
tpm_u16(TPM2_ST_SESSIONS), /* TAG */
tpm_u32(offset + pw_sz), /* Length */
tpm_u32(TPM2_CC_CLEAR), /* Command code */
/* HANDLE */
tpm_u32(handle), /* TPM resource handle */
/* AUTH_SESSION */
tpm_u32(9 + pw_sz), /* Authorization size */
tpm_u32(TPM2_RS_PW), /* Session handle */
tpm_u16(0), /* Size of <nonce> */
/* <nonce> (if any) */
0, /* Attributes: Cont/Excl/Rst */
tpm_u16(pw_sz), /* Size of <hmac/password> */
/* STRING(pw) <hmac/password> (if any) */
};
int ret;
/*
* Fill the command structure starting from the first buffer:
* - the password (if any)
*/
ret = pack_byte_string(command_v2, sizeof(command_v2), "s",
offset, pw, pw_sz);
offset += pw_sz;
if (ret)
return TPM_LIB_ERROR;
return tpm_sendrecv_command(dev, command_v2, NULL, NULL);
}
u32 tpm2_nv_define_space(struct udevice *dev, u32 space_index,
size_t space_size, u32 nv_attributes,
const u8 *nv_policy, size_t nv_policy_size)
{
/*
* Calculate the offset of the nv_policy piece by adding each of the
* chunks below.
*/
const int platform_len = sizeof(u32);
const int session_hdr_len = 13;
const int message_len = 14;
uint offset = TPM2_HDR_LEN + platform_len + session_hdr_len +
message_len;
u8 command_v2[COMMAND_BUFFER_SIZE] = {
/* header 10 bytes */
tpm_u16(TPM2_ST_SESSIONS), /* TAG */
tpm_u32(offset + nv_policy_size + 2),/* Length */
tpm_u32(TPM2_CC_NV_DEFINE_SPACE),/* Command code */
/* handles 4 bytes */
tpm_u32(TPM2_RH_PLATFORM), /* Primary platform seed */
/* session header 13 bytes */
tpm_u32(9), /* Header size */
tpm_u32(TPM2_RS_PW), /* Password authorisation */
tpm_u16(0), /* nonce_size */
0, /* session_attrs */
tpm_u16(0), /* auth_size */
/* message 14 bytes + policy */
tpm_u16(message_len + nv_policy_size), /* size */
tpm_u32(space_index),
tpm_u16(TPM2_ALG_SHA256),
tpm_u32(nv_attributes),
tpm_u16(nv_policy_size),
/*
* nv_policy
* space_size
*/
};
int ret;
/*
* Fill the command structure starting from the first buffer:
* - the password (if any)
*/
ret = pack_byte_string(command_v2, sizeof(command_v2), "sw",
offset, nv_policy, nv_policy_size,
offset + nv_policy_size, space_size);
if (ret)
return TPM_LIB_ERROR;
return tpm_sendrecv_command(dev, command_v2, NULL, NULL);
}
u32 tpm2_pcr_extend(struct udevice *dev, u32 index, u32 algorithm,
const u8 *digest, u32 digest_len)
{
/* Length of the message header, up to start of digest */
uint offset = 33;
u8 command_v2[COMMAND_BUFFER_SIZE] = {
tpm_u16(TPM2_ST_SESSIONS), /* TAG */
tpm_u32(offset + digest_len), /* Length */
tpm_u32(TPM2_CC_PCR_EXTEND), /* Command code */
/* HANDLE */
tpm_u32(index), /* Handle (PCR Index) */
/* AUTH_SESSION */
tpm_u32(9), /* Authorization size */
tpm_u32(TPM2_RS_PW), /* Session handle */
tpm_u16(0), /* Size of <nonce> */
/* <nonce> (if any) */
0, /* Attributes: Cont/Excl/Rst */
tpm_u16(0), /* Size of <hmac/password> */
/* <hmac/password> (if any) */
/* hashes */
tpm_u32(1), /* Count (number of hashes) */
tpm_u16(algorithm), /* Algorithm of the hash */
/* STRING(digest) Digest */
};
int ret;
if (!digest)
return -EINVAL;
/*
* Fill the command structure starting from the first buffer:
* - the digest
*/
ret = pack_byte_string(command_v2, sizeof(command_v2), "s",
offset, digest, digest_len);
if (ret)
return TPM_LIB_ERROR;
return tpm_sendrecv_command(dev, command_v2, NULL, NULL);
}
u32 tpm2_nv_read_value(struct udevice *dev, u32 index, void *data, u32 count)
{
u8 command_v2[COMMAND_BUFFER_SIZE] = {
/* header 10 bytes */
tpm_u16(TPM2_ST_SESSIONS), /* TAG */
tpm_u32(10 + 8 + 4 + 9 + 4), /* Length */
tpm_u32(TPM2_CC_NV_READ), /* Command code */
/* handles 8 bytes */
tpm_u32(TPM2_RH_PLATFORM), /* Primary platform seed */
tpm_u32(HR_NV_INDEX + index), /* Password authorisation */
/* AUTH_SESSION */
tpm_u32(9), /* Authorization size */
tpm_u32(TPM2_RS_PW), /* Session handle */
tpm_u16(0), /* Size of <nonce> */
/* <nonce> (if any) */
0, /* Attributes: Cont/Excl/Rst */
tpm_u16(0), /* Size of <hmac/password> */
/* <hmac/password> (if any) */
tpm_u16(count), /* Number of bytes */
tpm_u16(0), /* Offset */
};
size_t response_len = COMMAND_BUFFER_SIZE;
u8 response[COMMAND_BUFFER_SIZE];
int ret;
u16 tag;
u32 size, code;
ret = tpm_sendrecv_command(dev, command_v2, response, &response_len);
if (ret)
return log_msg_ret("read", ret);
if (unpack_byte_string(response, response_len, "wdds",
0, &tag, 2, &size, 6, &code,
16, data, count))
return TPM_LIB_ERROR;
return 0;
}
u32 tpm2_nv_write_value(struct udevice *dev, u32 index, const void *data,
u32 count)
{
struct tpm_chip_priv *priv = dev_get_uclass_priv(dev);
uint offset = 10 + 8 + 4 + 9 + 2;
uint len = offset + count + 2;
/* Use empty password auth if platform hierarchy is disabled */
u32 auth = priv->plat_hier_disabled ? HR_NV_INDEX + index :
TPM2_RH_PLATFORM;
u8 command_v2[COMMAND_BUFFER_SIZE] = {
/* header 10 bytes */
tpm_u16(TPM2_ST_SESSIONS), /* TAG */
tpm_u32(len), /* Length */
tpm_u32(TPM2_CC_NV_WRITE), /* Command code */
/* handles 8 bytes */
tpm_u32(auth), /* Primary platform seed */
tpm_u32(HR_NV_INDEX + index), /* Password authorisation */
/* AUTH_SESSION */
tpm_u32(9), /* Authorization size */
tpm_u32(TPM2_RS_PW), /* Session handle */
tpm_u16(0), /* Size of <nonce> */
/* <nonce> (if any) */
0, /* Attributes: Cont/Excl/Rst */
tpm_u16(0), /* Size of <hmac/password> */
/* <hmac/password> (if any) */
tpm_u16(count),
};
size_t response_len = COMMAND_BUFFER_SIZE;
u8 response[COMMAND_BUFFER_SIZE];
int ret;
ret = pack_byte_string(command_v2, sizeof(command_v2), "sw",
offset, data, count,
offset + count, 0);
if (ret)
return TPM_LIB_ERROR;
return tpm_sendrecv_command(dev, command_v2, response, &response_len);
}
u32 tpm2_pcr_read(struct udevice *dev, u32 idx, unsigned int idx_min_sz,
u16 algorithm, void *data, u32 digest_len,
unsigned int *updates)
{
u8 idx_array_sz = max(idx_min_sz, DIV_ROUND_UP(idx, 8));
u8 command_v2[COMMAND_BUFFER_SIZE] = {
tpm_u16(TPM2_ST_NO_SESSIONS), /* TAG */
tpm_u32(17 + idx_array_sz), /* Length */
tpm_u32(TPM2_CC_PCR_READ), /* Command code */
/* TPML_PCR_SELECTION */
tpm_u32(1), /* Number of selections */
tpm_u16(algorithm), /* Algorithm of the hash */
idx_array_sz, /* Array size for selection */
/* bitmap(idx) Selected PCR bitmap */
};
size_t response_len = COMMAND_BUFFER_SIZE;
u8 response[COMMAND_BUFFER_SIZE];
unsigned int pcr_sel_idx = idx / 8;
u8 pcr_sel_bit = BIT(idx % 8);
unsigned int counter = 0;
int ret;
if (pack_byte_string(command_v2, COMMAND_BUFFER_SIZE, "b",
17 + pcr_sel_idx, pcr_sel_bit))
return TPM_LIB_ERROR;
ret = tpm_sendrecv_command(dev, command_v2, response, &response_len);
if (ret)
return ret;
if (digest_len > response_len)
return TPM_LIB_ERROR;
if (unpack_byte_string(response, response_len, "ds",
10, &counter,
response_len - digest_len, data,
digest_len))
return TPM_LIB_ERROR;
if (updates)
*updates = counter;
return 0;
}
u32 tpm2_get_capability(struct udevice *dev, u32 capability, u32 property,
void *buf, size_t prop_count)
{
u8 command_v2[COMMAND_BUFFER_SIZE] = {
tpm_u16(TPM2_ST_NO_SESSIONS), /* TAG */
tpm_u32(22), /* Length */
tpm_u32(TPM2_CC_GET_CAPABILITY), /* Command code */
tpm_u32(capability), /* Capability */
tpm_u32(property), /* Property */
tpm_u32(prop_count), /* Property count */
};
u8 response[COMMAND_BUFFER_SIZE];
size_t response_len = COMMAND_BUFFER_SIZE;
unsigned int properties_off;
int ret;
ret = tpm_sendrecv_command(dev, command_v2, response, &response_len);
if (ret)
return ret;
/*
* In the response buffer, the properties are located after the:
* tag (u16), response size (u32), response code (u32),
* YES/NO flag (u8), TPM_CAP (u32).
*/
properties_off = sizeof(u16) + sizeof(u32) + sizeof(u32) +
sizeof(u8) + sizeof(u32);
memcpy(buf, &response[properties_off], response_len - properties_off);
return 0;
}
static int tpm2_get_num_pcr(struct udevice *dev, u32 *num_pcr)
{
u8 response[(sizeof(struct tpms_capability_data) -
offsetof(struct tpms_capability_data, data))];
u32 properties_offset =
offsetof(struct tpml_tagged_tpm_property, tpm_property) +
offsetof(struct tpms_tagged_property, value);
u32 ret;
memset(response, 0, sizeof(response));
ret = tpm2_get_capability(dev, TPM2_CAP_TPM_PROPERTIES,
TPM2_PT_PCR_COUNT, response, 1);
if (ret)
return ret;
*num_pcr = get_unaligned_be32(response + properties_offset);
if (*num_pcr > TPM2_MAX_PCRS) {
printf("%s: too many pcrs: %u\n", __func__, *num_pcr);
return -E2BIG;
}
return 0;
}
static bool tpm2_is_active_pcr(struct tpms_pcr_selection *selection)
{
int i;
/*
* check the pcr_select. If at least one of the PCRs supports the
* algorithm add it on the active ones
*/
for (i = 0; i < selection->size_of_select; i++) {
if (selection->pcr_select[i])
return true;
}
return false;
}
int tpm2_get_pcr_info(struct udevice *dev, u32 *supported_pcr, u32 *active_pcr,
u32 *pcr_banks)
{
u8 response[(sizeof(struct tpms_capability_data) -
offsetof(struct tpms_capability_data, data))];
struct tpml_pcr_selection pcrs;
u32 num_pcr;
size_t i;
u32 ret;
*supported_pcr = 0;
*active_pcr = 0;
*pcr_banks = 0;
memset(response, 0, sizeof(response));
ret = tpm2_get_capability(dev, TPM2_CAP_PCRS, 0, response, 1);
if (ret)
return ret;
pcrs.count = get_unaligned_be32(response);
/*
* We only support 5 algorithms for now so check against that
* instead of TPM2_NUM_PCR_BANKS
*/
if (pcrs.count > ARRAY_SIZE(tpm2_supported_algorithms) ||
pcrs.count < 1) {
printf("%s: too many pcrs: %u\n", __func__, pcrs.count);
return -EMSGSIZE;
}
ret = tpm2_get_num_pcr(dev, &num_pcr);
if (ret)
return ret;
for (i = 0; i < pcrs.count; i++) {
/*
* Definition of TPMS_PCR_SELECTION Structure
* hash: u16
* size_of_select: u8
* pcr_select: u8 array
*
* The offsets depend on the number of the device PCRs
* so we have to calculate them based on that
*/
u32 hash_offset = offsetof(struct tpml_pcr_selection, selection) +
i * offsetof(struct tpms_pcr_selection, pcr_select) +
i * ((num_pcr + 7) / 8);
u32 size_select_offset =
hash_offset + offsetof(struct tpms_pcr_selection,
size_of_select);
u32 pcr_select_offset =
hash_offset + offsetof(struct tpms_pcr_selection,
pcr_select);
pcrs.selection[i].hash =
get_unaligned_be16(response + hash_offset);
pcrs.selection[i].size_of_select =
__get_unaligned_be(response + size_select_offset);
if (pcrs.selection[i].size_of_select > TPM2_PCR_SELECT_MAX) {
printf("%s: pcrs selection too large: %u\n", __func__,
pcrs.selection[i].size_of_select);
return -ENOBUFS;
}
/* copy the array of pcr_select */
memcpy(pcrs.selection[i].pcr_select, response + pcr_select_offset,
pcrs.selection[i].size_of_select);
}
for (i = 0; i < pcrs.count; i++) {
u32 hash_mask = tpm2_algorithm_to_mask(pcrs.selection[i].hash);
if (hash_mask) {
*supported_pcr |= hash_mask;
if (tpm2_is_active_pcr(&pcrs.selection[i]))
*active_pcr |= hash_mask;
} else {
printf("%s: unknown algorithm %x\n", __func__,
pcrs.selection[i].hash);
}
}
*pcr_banks = pcrs.count;
return 0;
}
u32 tpm2_dam_reset(struct udevice *dev, const char *pw, const ssize_t pw_sz)
{
u8 command_v2[COMMAND_BUFFER_SIZE] = {
tpm_u16(TPM2_ST_SESSIONS), /* TAG */
tpm_u32(27 + pw_sz), /* Length */
tpm_u32(TPM2_CC_DAM_RESET), /* Command code */
/* HANDLE */
tpm_u32(TPM2_RH_LOCKOUT), /* TPM resource handle */
/* AUTH_SESSION */
tpm_u32(9 + pw_sz), /* Authorization size */
tpm_u32(TPM2_RS_PW), /* Session handle */
tpm_u16(0), /* Size of <nonce> */
/* <nonce> (if any) */
0, /* Attributes: Cont/Excl/Rst */
tpm_u16(pw_sz), /* Size of <hmac/password> */
/* STRING(pw) <hmac/password> (if any) */
};
unsigned int offset = 27;
int ret;
/*
* Fill the command structure starting from the first buffer:
* - the password (if any)
*/
ret = pack_byte_string(command_v2, sizeof(command_v2), "s",
offset, pw, pw_sz);
offset += pw_sz;
if (ret)
return TPM_LIB_ERROR;
return tpm_sendrecv_command(dev, command_v2, NULL, NULL);
}
u32 tpm2_dam_parameters(struct udevice *dev, const char *pw,
const ssize_t pw_sz, unsigned int max_tries,
unsigned int recovery_time,
unsigned int lockout_recovery)
{
u8 command_v2[COMMAND_BUFFER_SIZE] = {
tpm_u16(TPM2_ST_SESSIONS), /* TAG */
tpm_u32(27 + pw_sz + 12), /* Length */
tpm_u32(TPM2_CC_DAM_PARAMETERS), /* Command code */
/* HANDLE */
tpm_u32(TPM2_RH_LOCKOUT), /* TPM resource handle */
/* AUTH_SESSION */
tpm_u32(9 + pw_sz), /* Authorization size */
tpm_u32(TPM2_RS_PW), /* Session handle */
tpm_u16(0), /* Size of <nonce> */
/* <nonce> (if any) */
0, /* Attributes: Cont/Excl/Rst */
tpm_u16(pw_sz), /* Size of <hmac/password> */
/* STRING(pw) <hmac/password> (if any) */
/* LOCKOUT PARAMETERS */
/* tpm_u32(max_tries) Max tries (0, always lock) */
/* tpm_u32(recovery_time) Recovery time (0, no lock) */
/* tpm_u32(lockout_recovery) Lockout recovery */
};
unsigned int offset = 27;
int ret;
/*
* Fill the command structure starting from the first buffer:
* - the password (if any)
* - max tries
* - recovery time
* - lockout recovery
*/
ret = pack_byte_string(command_v2, sizeof(command_v2), "sddd",
offset, pw, pw_sz,
offset + pw_sz, max_tries,
offset + pw_sz + 4, recovery_time,
offset + pw_sz + 8, lockout_recovery);
offset += pw_sz + 12;
if (ret)
return TPM_LIB_ERROR;
return tpm_sendrecv_command(dev, command_v2, NULL, NULL);
}
int tpm2_change_auth(struct udevice *dev, u32 handle, const char *newpw,
const ssize_t newpw_sz, const char *oldpw,
const ssize_t oldpw_sz)
{
unsigned int offset = 27;
u8 command_v2[COMMAND_BUFFER_SIZE] = {
tpm_u16(TPM2_ST_SESSIONS), /* TAG */
tpm_u32(offset + oldpw_sz + 2 + newpw_sz), /* Length */
tpm_u32(TPM2_CC_HIERCHANGEAUTH), /* Command code */
/* HANDLE */
tpm_u32(handle), /* TPM resource handle */
/* AUTH_SESSION */
tpm_u32(9 + oldpw_sz), /* Authorization size */
tpm_u32(TPM2_RS_PW), /* Session handle */
tpm_u16(0), /* Size of <nonce> */
/* <nonce> (if any) */
0, /* Attributes: Cont/Excl/Rst */
tpm_u16(oldpw_sz) /* Size of <hmac/password> */
/* STRING(oldpw) <hmac/password> (if any) */
/* TPM2B_AUTH (TPM2B_DIGEST) */
/* tpm_u16(newpw_sz) Digest size, new pw length */
/* STRING(newpw) Digest buffer, new pw */
};
int ret;
/*
* Fill the command structure starting from the first buffer:
* - the old password (if any)
* - size of the new password
* - new password
*/
ret = pack_byte_string(command_v2, sizeof(command_v2), "sws",
offset, oldpw, oldpw_sz,
offset + oldpw_sz, newpw_sz,
offset + oldpw_sz + 2, newpw, newpw_sz);
offset += oldpw_sz + 2 + newpw_sz;
if (ret)
return TPM_LIB_ERROR;
return tpm_sendrecv_command(dev, command_v2, NULL, NULL);
}
u32 tpm2_pcr_setauthpolicy(struct udevice *dev, const char *pw,
const ssize_t pw_sz, u32 index, const char *key)
{
u8 command_v2[COMMAND_BUFFER_SIZE] = {
tpm_u16(TPM2_ST_SESSIONS), /* TAG */
tpm_u32(35 + pw_sz + TPM2_DIGEST_LEN), /* Length */
tpm_u32(TPM2_CC_PCR_SETAUTHPOL), /* Command code */
/* HANDLE */
tpm_u32(TPM2_RH_PLATFORM), /* TPM resource handle */
/* AUTH_SESSION */
tpm_u32(9 + pw_sz), /* Authorization size */
tpm_u32(TPM2_RS_PW), /* session handle */
tpm_u16(0), /* Size of <nonce> */
/* <nonce> (if any) */
0, /* Attributes: Cont/Excl/Rst */
tpm_u16(pw_sz) /* Size of <hmac/password> */
/* STRING(pw) <hmac/password> (if any) */
/* TPM2B_AUTH (TPM2B_DIGEST) */
/* tpm_u16(TPM2_DIGEST_LEN) Digest size length */
/* STRING(key) Digest buffer (PCR key) */
/* TPMI_ALG_HASH */
/* tpm_u16(TPM2_ALG_SHA256) Algorithm of the hash */
/* TPMI_DH_PCR */
/* tpm_u32(index), PCR Index */
};
unsigned int offset = 27;
int ret;
/*
* Fill the command structure starting from the first buffer:
* - the password (if any)
* - the PCR key length
* - the PCR key
* - the hash algorithm
* - the PCR index
*/
ret = pack_byte_string(command_v2, sizeof(command_v2), "swswd",
offset, pw, pw_sz,
offset + pw_sz, TPM2_DIGEST_LEN,
offset + pw_sz + 2, key, TPM2_DIGEST_LEN,
offset + pw_sz + 2 + TPM2_DIGEST_LEN,
TPM2_ALG_SHA256,
offset + pw_sz + 4 + TPM2_DIGEST_LEN, index);
offset += pw_sz + 2 + TPM2_DIGEST_LEN + 2 + 4;
if (ret)
return TPM_LIB_ERROR;
return tpm_sendrecv_command(dev, command_v2, NULL, NULL);
}
u32 tpm2_pcr_setauthvalue(struct udevice *dev, const char *pw,
const ssize_t pw_sz, u32 index, const char *key,
const ssize_t key_sz)
{
u8 command_v2[COMMAND_BUFFER_SIZE] = {
tpm_u16(TPM2_ST_SESSIONS), /* TAG */
tpm_u32(33 + pw_sz + TPM2_DIGEST_LEN), /* Length */
tpm_u32(TPM2_CC_PCR_SETAUTHVAL), /* Command code */
/* HANDLE */
tpm_u32(index), /* Handle (PCR Index) */
/* AUTH_SESSION */
tpm_u32(9 + pw_sz), /* Authorization size */
tpm_u32(TPM2_RS_PW), /* session handle */
tpm_u16(0), /* Size of <nonce> */
/* <nonce> (if any) */
0, /* Attributes: Cont/Excl/Rst */
tpm_u16(pw_sz), /* Size of <hmac/password> */
/* STRING(pw) <hmac/password> (if any) */
/* TPM2B_DIGEST */
/* tpm_u16(key_sz) Key length */
/* STRING(key) Key */
};
unsigned int offset = 27;
int ret;
/*
* Fill the command structure starting from the first buffer:
* - the password (if any)
* - the number of digests, 1 in our case
* - the algorithm, sha256 in our case
* - the digest (64 bytes)
*/
ret = pack_byte_string(command_v2, sizeof(command_v2), "sws",
offset, pw, pw_sz,
offset + pw_sz, key_sz,
offset + pw_sz + 2, key, key_sz);
offset += pw_sz + 2 + key_sz;
if (ret)
return TPM_LIB_ERROR;
return tpm_sendrecv_command(dev, command_v2, NULL, NULL);
}
u32 tpm2_get_random(struct udevice *dev, void *data, u32 count)
{
const u8 command_v2[10] = {
tpm_u16(TPM2_ST_NO_SESSIONS),
tpm_u32(12),
tpm_u32(TPM2_CC_GET_RANDOM),
};
u8 buf[COMMAND_BUFFER_SIZE], response[COMMAND_BUFFER_SIZE];
const size_t data_size_offset = 10;
const size_t data_offset = 12;
size_t response_length = sizeof(response);
u32 data_size;
u8 *out = data;
while (count > 0) {
u32 this_bytes = min((size_t)count,
sizeof(response) - data_offset);
u32 err;
if (pack_byte_string(buf, sizeof(buf), "sw",
0, command_v2, sizeof(command_v2),
sizeof(command_v2), this_bytes))
return TPM_LIB_ERROR;
err = tpm_sendrecv_command(dev, buf, response,
&response_length);
if (err)
return err;
if (unpack_byte_string(response, response_length, "w",
data_size_offset, &data_size))
return TPM_LIB_ERROR;
if (data_size > this_bytes)
return TPM_LIB_ERROR;
if (unpack_byte_string(response, response_length, "s",
data_offset, out, data_size))
return TPM_LIB_ERROR;
count -= data_size;
out += data_size;
}
return 0;
}
u32 tpm2_write_lock(struct udevice *dev, u32 index)
{
u8 command_v2[COMMAND_BUFFER_SIZE] = {
/* header 10 bytes */
tpm_u16(TPM2_ST_SESSIONS), /* TAG */
tpm_u32(10 + 8 + 13), /* Length */
tpm_u32(TPM2_CC_NV_WRITELOCK), /* Command code */
/* handles 8 bytes */
tpm_u32(TPM2_RH_PLATFORM), /* Primary platform seed */
tpm_u32(HR_NV_INDEX + index), /* Password authorisation */
/* session header 9 bytes */
tpm_u32(9), /* Header size */
tpm_u32(TPM2_RS_PW), /* Password authorisation */
tpm_u16(0), /* nonce_size */
0, /* session_attrs */
tpm_u16(0), /* auth_size */
};
return tpm_sendrecv_command(dev, command_v2, NULL, NULL);
}
u32 tpm2_disable_platform_hierarchy(struct udevice *dev)
{
struct tpm_chip_priv *priv = dev_get_uclass_priv(dev);
u8 command_v2[COMMAND_BUFFER_SIZE] = {
/* header 10 bytes */
tpm_u16(TPM2_ST_SESSIONS), /* TAG */
tpm_u32(10 + 4 + 13 + 5), /* Length */
tpm_u32(TPM2_CC_HIER_CONTROL), /* Command code */
/* 4 bytes */
tpm_u32(TPM2_RH_PLATFORM), /* Primary platform seed */
/* session header 9 bytes */
tpm_u32(9), /* Header size */
tpm_u32(TPM2_RS_PW), /* Password authorisation */
tpm_u16(0), /* nonce_size */
0, /* session_attrs */
tpm_u16(0), /* auth_size */
/* payload 5 bytes */
tpm_u32(TPM2_RH_PLATFORM), /* Hierarchy to disable */
0, /* 0=disable */
};
int ret;
ret = tpm_sendrecv_command(dev, command_v2, NULL, NULL);
log_info("ret=%s, %x\n", dev->name, ret);
if (ret)
return ret;
priv->plat_hier_disabled = true;
return 0;
}
u32 tpm2_submit_command(struct udevice *dev, const u8 *sendbuf,
u8 *recvbuf, size_t *recv_size)
{
return tpm_sendrecv_command(dev, sendbuf, recvbuf, recv_size);
}
u32 tpm2_report_state(struct udevice *dev, uint vendor_cmd, uint vendor_subcmd,
u8 *recvbuf, size_t *recv_size)
{
u8 command_v2[COMMAND_BUFFER_SIZE] = {
/* header 10 bytes */
tpm_u16(TPM2_ST_NO_SESSIONS), /* TAG */
tpm_u32(10 + 2), /* Length */
tpm_u32(vendor_cmd), /* Command code */
tpm_u16(vendor_subcmd),
};
int ret;
ret = tpm_sendrecv_command(dev, command_v2, recvbuf, recv_size);
log_debug("ret=%s, %x\n", dev->name, ret);
if (ret)
return ret;
if (*recv_size < 12)
return -ENODATA;
*recv_size -= 12;
memcpy(recvbuf, recvbuf + 12, *recv_size);
return 0;
}
u32 tpm2_enable_nvcommits(struct udevice *dev, uint vendor_cmd,
uint vendor_subcmd)
{
u8 command_v2[COMMAND_BUFFER_SIZE] = {
/* header 10 bytes */
tpm_u16(TPM2_ST_NO_SESSIONS), /* TAG */
tpm_u32(10 + 2), /* Length */
tpm_u32(vendor_cmd), /* Command code */
tpm_u16(vendor_subcmd),
};
int ret;
ret = tpm_sendrecv_command(dev, command_v2, NULL, NULL);
log_debug("ret=%s, %x\n", dev->name, ret);
if (ret)
return ret;
return 0;
}