u-boot/cmd/tpm-common.c
Simon Glass 3bb4db4c38 tpm: Allow reporting the internal state
It is useful to read information about the current TPM state, where
supported, e.g. for debugging purposes when verified boot fails.

Add support for this to the TPM interface as well as Cr50. Add a simple
sandbox test.

Signed-off-by: Simon Glass <sjg@chromium.org>
Reviewed-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>
Signed-off-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>
2022-09-03 16:59:05 +03:00

408 lines
7.6 KiB
C

// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (c) 2013 The Chromium OS Authors.
*/
#include <common.h>
#include <command.h>
#include <dm.h>
#include <env.h>
#include <malloc.h>
#include <asm/unaligned.h>
#include <linux/string.h>
#include <tpm-common.h>
#include "tpm-user-utils.h"
static struct udevice *tpm_dev;
/**
* Print a byte string in hexdecimal format, 16-bytes per line.
*
* @param data byte string to be printed
* @param count number of bytes to be printed
*/
void print_byte_string(u8 *data, size_t count)
{
int i, print_newline = 0;
for (i = 0; i < count; i++) {
printf(" %02x", data[i]);
print_newline = (i % 16 == 15);
if (print_newline)
putc('\n');
}
/* Avoid duplicated newline at the end */
if (!print_newline)
putc('\n');
}
/**
* Convert a text string of hexdecimal values into a byte string.
*
* @param bytes text string of hexdecimal values with no space
* between them
* @param data output buffer for byte string. The caller has to make
* sure it is large enough for storing the output. If
* NULL is passed, a large enough buffer will be allocated,
* and the caller must free it.
* @param count_ptr output variable for the length of byte string
* Return: pointer to output buffer
*/
void *parse_byte_string(char *bytes, u8 *data, size_t *count_ptr)
{
char byte[3];
size_t count, length;
int i;
if (!bytes)
return NULL;
length = strlen(bytes);
count = length / 2;
if (!data)
data = malloc(count);
if (!data)
return NULL;
byte[2] = '\0';
for (i = 0; i < length; i += 2) {
byte[0] = bytes[i];
byte[1] = bytes[i + 1];
data[i / 2] = (u8)hextoul(byte, NULL);
}
if (count_ptr)
*count_ptr = count;
return data;
}
/**
* report_return_code() - Report any error and return failure or success
*
* @param return_code TPM command return code
* Return: value of enum command_ret_t
*/
int report_return_code(int return_code)
{
if (return_code) {
printf("Error: %d\n", return_code);
return CMD_RET_FAILURE;
} else {
return CMD_RET_SUCCESS;
}
}
/**
* Return number of values defined by a type string.
*
* @param type_str type string
* Return: number of values of type string
*/
int type_string_get_num_values(const char *type_str)
{
return strlen(type_str);
}
/**
* Return total size of values defined by a type string.
*
* @param type_str type string
* Return: total size of values of type string, or 0 if type string
* contains illegal type character.
*/
size_t type_string_get_space_size(const char *type_str)
{
size_t size;
for (size = 0; *type_str; type_str++) {
switch (*type_str) {
case 'b':
size += 1;
break;
case 'w':
size += 2;
break;
case 'd':
size += 4;
break;
default:
return 0;
}
}
return size;
}
/**
* Allocate a buffer large enough to hold values defined by a type
* string. The caller has to free the buffer.
*
* @param type_str type string
* @param count pointer for storing size of buffer
* Return: pointer to buffer or NULL on error
*/
void *type_string_alloc(const char *type_str, u32 *count)
{
void *data;
size_t size;
size = type_string_get_space_size(type_str);
if (!size)
return NULL;
data = malloc(size);
if (data)
*count = size;
return data;
}
/**
* Pack values defined by a type string into a buffer. The buffer must have
* large enough space.
*
* @param type_str type string
* @param values text strings of values to be packed
* @param data output buffer of values
* Return: 0 on success, non-0 on error
*/
int type_string_pack(const char *type_str, char * const values[],
u8 *data)
{
size_t offset;
u32 value;
for (offset = 0; *type_str; type_str++, values++) {
value = simple_strtoul(values[0], NULL, 0);
switch (*type_str) {
case 'b':
data[offset] = value;
offset += 1;
break;
case 'w':
put_unaligned_be16(value, data + offset);
offset += 2;
break;
case 'd':
put_unaligned_be32(value, data + offset);
offset += 4;
break;
default:
return -1;
}
}
return 0;
}
/**
* Read values defined by a type string from a buffer, and write these values
* to environment variables.
*
* @param type_str type string
* @param data input buffer of values
* @param vars names of environment variables
* Return: 0 on success, non-0 on error
*/
int type_string_write_vars(const char *type_str, u8 *data,
char * const vars[])
{
size_t offset;
u32 value;
for (offset = 0; *type_str; type_str++, vars++) {
switch (*type_str) {
case 'b':
value = data[offset];
offset += 1;
break;
case 'w':
value = get_unaligned_be16(data + offset);
offset += 2;
break;
case 'd':
value = get_unaligned_be32(data + offset);
offset += 4;
break;
default:
return -1;
}
if (env_set_ulong(*vars, value))
return -1;
}
return 0;
}
static int tpm_show_device(void)
{
struct udevice *dev;
char buf[80];
int n = 0, rc;
for_each_tpm_device(dev) {
rc = tpm_get_desc(dev, buf, sizeof(buf));
if (rc < 0)
printf("device %d: can't get info\n", n);
else
printf("device %d: %s\n", n, buf);
n++;
};
return 0;
}
static int tpm_set_device(unsigned long num)
{
struct udevice *dev;
unsigned long n = 0;
int rc = CMD_RET_FAILURE;
for_each_tpm_device(dev) {
if (n == num) {
rc = 0;
break;
}
n++;
}
if (!rc)
tpm_dev = dev;
return rc;
}
int get_tpm(struct udevice **devp)
{
int rc;
/*
* To keep a backward compatibility with previous code,
* if a tpm device is not explicitly set, we set the first one.
*/
if (!tpm_dev) {
rc = tpm_set_device(0);
if (rc) {
printf("Couldn't set TPM 0 (rc = %d)\n", rc);
return CMD_RET_FAILURE;
}
}
if (devp)
*devp = tpm_dev;
return 0;
}
int do_tpm_device(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
{
unsigned long num;
int rc;
if (argc == 2) {
num = dectoul(argv[1], NULL);
rc = tpm_set_device(num);
if (rc)
printf("Couldn't set TPM %lu (rc = %d)\n", num, rc);
} else {
rc = tpm_show_device();
}
return rc;
}
int do_tpm_info(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
{
struct udevice *dev;
char buf[80];
int rc;
rc = get_tpm(&dev);
if (rc)
return rc;
rc = tpm_get_desc(dev, buf, sizeof(buf));
if (rc < 0) {
printf("Couldn't get TPM info (%d)\n", rc);
return CMD_RET_FAILURE;
}
printf("%s\n", buf);
return 0;
}
int do_tpm_report_state(struct cmd_tbl *cmdtp, int flag, int argc,
char *const argv[])
{
struct udevice *dev;
char buf[80];
int rc;
rc = get_tpm(&dev);
if (rc)
return rc;
rc = tpm_report_state(dev, buf, sizeof(buf));
if (rc < 0) {
printf("Couldn't get TPM state (%d)\n", rc);
return CMD_RET_FAILURE;
}
printf("%s\n", buf);
return 0;
}
int do_tpm_init(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
{
struct udevice *dev;
int rc;
if (argc != 1)
return CMD_RET_USAGE;
rc = get_tpm(&dev);
if (rc)
return rc;
return report_return_code(tpm_init(dev));
}
int do_tpm(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
{
struct cmd_tbl *tpm_commands, *cmd;
struct tpm_chip_priv *priv;
struct udevice *dev;
unsigned int size;
int ret;
if (argc < 2)
return CMD_RET_USAGE;
ret = get_tpm(&dev);
if (ret)
return ret;
priv = dev_get_uclass_priv(dev);
/* Below getters return NULL if the desired stack is not built */
switch (priv->version) {
case TPM_V1:
tpm_commands = get_tpm1_commands(&size);
break;
case TPM_V2:
tpm_commands = get_tpm2_commands(&size);
break;
default:
tpm_commands = NULL;
}
if (!tpm_commands)
return CMD_RET_USAGE;
cmd = find_cmd_tbl(argv[1], tpm_commands, size);
if (!cmd)
return CMD_RET_USAGE;
return cmd->cmd(cmdtp, flag, argc - 1, argv + 1);
}