| // SPDX-License-Identifier: GPL-2.0+ | |
| /* | |
| * Copyright (c) 2013 The Chromium OS Authors. | |
| * Coypright (c) 2013 Guntermann & Drunck GmbH | |
| */ | |
| #define LOG_CATEGORY UCLASS_TPM | |
| #include <common.h> | |
| #include <dm.h> | |
| #include <log.h> | |
| #include <asm/unaligned.h> | |
| #include <tpm-common.h> | |
| #include "tpm-utils.h" | |
| enum tpm_version tpm_get_version(struct udevice *dev) | |
| { | |
| struct tpm_chip_priv *priv = dev_get_uclass_priv(dev); | |
| return priv->version; | |
| } | |
| int pack_byte_string(u8 *str, size_t size, const char *format, ...) | |
| { | |
| va_list args; | |
| size_t offset = 0, length = 0; | |
| u8 *data = NULL; | |
| u32 value = 0; | |
| va_start(args, format); | |
| for (; *format; format++) { | |
| switch (*format) { | |
| case 'b': | |
| offset = va_arg(args, size_t); | |
| value = va_arg(args, int); | |
| length = 1; | |
| break; | |
| case 'w': | |
| offset = va_arg(args, size_t); | |
| value = va_arg(args, int); | |
| length = 2; | |
| break; | |
| case 'd': | |
| offset = va_arg(args, size_t); | |
| value = va_arg(args, u32); | |
| length = 4; | |
| break; | |
| case 's': | |
| offset = va_arg(args, size_t); | |
| data = va_arg(args, u8 *); | |
| length = va_arg(args, u32); | |
| break; | |
| default: | |
| debug("Couldn't recognize format string\n"); | |
| va_end(args); | |
| return -1; | |
| } | |
| if (offset + length > size) { | |
| va_end(args); | |
| return -1; | |
| } | |
| switch (*format) { | |
| case 'b': | |
| str[offset] = value; | |
| break; | |
| case 'w': | |
| put_unaligned_be16(value, str + offset); | |
| break; | |
| case 'd': | |
| put_unaligned_be32(value, str + offset); | |
| break; | |
| case 's': | |
| memcpy(str + offset, data, length); | |
| break; | |
| } | |
| } | |
| va_end(args); | |
| return 0; | |
| } | |
| int unpack_byte_string(const u8 *str, size_t size, const char *format, ...) | |
| { | |
| va_list args; | |
| size_t offset = 0, length = 0; | |
| u8 *ptr8 = NULL; | |
| u16 *ptr16 = NULL; | |
| u32 *ptr32 = NULL; | |
| va_start(args, format); | |
| for (; *format; format++) { | |
| switch (*format) { | |
| case 'b': | |
| offset = va_arg(args, size_t); | |
| ptr8 = va_arg(args, u8 *); | |
| length = 1; | |
| break; | |
| case 'w': | |
| offset = va_arg(args, size_t); | |
| ptr16 = va_arg(args, u16 *); | |
| length = 2; | |
| break; | |
| case 'd': | |
| offset = va_arg(args, size_t); | |
| ptr32 = va_arg(args, u32 *); | |
| length = 4; | |
| break; | |
| case 's': | |
| offset = va_arg(args, size_t); | |
| ptr8 = va_arg(args, u8 *); | |
| length = va_arg(args, u32); | |
| break; | |
| default: | |
| va_end(args); | |
| debug("Couldn't recognize format string\n"); | |
| return -1; | |
| } | |
| if (offset + length > size) { | |
| va_end(args); | |
| log_err("Failed to read: size=%zd, offset=%zx, len=%zx\n", | |
| size, offset, length); | |
| return -1; | |
| } | |
| switch (*format) { | |
| case 'b': | |
| *ptr8 = str[offset]; | |
| break; | |
| case 'w': | |
| *ptr16 = get_unaligned_be16(str + offset); | |
| break; | |
| case 'd': | |
| *ptr32 = get_unaligned_be32(str + offset); | |
| break; | |
| case 's': | |
| memcpy(ptr8, str + offset, length); | |
| break; | |
| } | |
| } | |
| va_end(args); | |
| return 0; | |
| } | |
| u32 tpm_command_size(const void *command) | |
| { | |
| const size_t command_size_offset = 2; | |
| return get_unaligned_be32(command + command_size_offset); | |
| } | |
| u32 tpm_return_code(const void *response) | |
| { | |
| const size_t return_code_offset = 6; | |
| return get_unaligned_be32(response + return_code_offset); | |
| } | |
| u32 tpm_sendrecv_command(struct udevice *dev, const void *command, | |
| void *response, size_t *size_ptr) | |
| { | |
| int err, ret; | |
| u8 response_buffer[COMMAND_BUFFER_SIZE]; | |
| size_t response_length; | |
| int i; | |
| uint size; | |
| if (response) { | |
| response_length = *size_ptr; | |
| } else { | |
| response = response_buffer; | |
| response_length = sizeof(response_buffer); | |
| } | |
| size = tpm_command_size(command); | |
| /* sanity check, which also helps coverity */ | |
| if (size > COMMAND_BUFFER_SIZE) | |
| return log_msg_ret("size", -E2BIG); | |
| log_debug("TPM request [size:%d]: ", size); | |
| for (i = 0; i < size; i++) | |
| log_debug("%02x ", ((u8 *)command)[i]); | |
| log_debug("\n"); | |
| err = tpm_xfer(dev, command, size, response, &response_length); | |
| if (err < 0) | |
| return err; | |
| if (size_ptr) | |
| *size_ptr = response_length; | |
| ret = tpm_return_code(response); | |
| log_debug("TPM response [ret:%d]: ", ret); | |
| for (i = 0; i < response_length; i++) | |
| log_debug("%02x ", ((u8 *)response)[i]); | |
| log_debug("\n"); | |
| return ret; | |
| } | |
| int tpm_init(struct udevice *dev) | |
| { | |
| return tpm_open(dev); | |
| } |