| // SPDX-License-Identifier: GPL-2.0+ |
| /* |
| * Copyright (c) 2013 The Chromium OS Authors. |
| * Coypright (c) 2013 Guntermann & Drunck GmbH |
| */ |
| |
| #include <common.h> |
| #include <dm.h> |
| #include <asm/unaligned.h> |
| #include <tpm-common.h> |
| #include "tpm-utils.h" |
| |
| 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); |
| 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(const void *command, void *response, size_t *size_ptr) |
| { |
| struct udevice *dev; |
| int err, ret; |
| u8 response_buffer[COMMAND_BUFFER_SIZE]; |
| size_t response_length; |
| |
| if (response) { |
| response_length = *size_ptr; |
| } else { |
| response = response_buffer; |
| response_length = sizeof(response_buffer); |
| } |
| |
| ret = uclass_first_device_err(UCLASS_TPM, &dev); |
| if (ret) |
| return ret; |
| err = tpm_xfer(dev, command, tpm_command_size(command), |
| response, &response_length); |
| |
| if (err < 0) |
| return TPM_LIB_ERROR; |
| if (size_ptr) |
| *size_ptr = response_length; |
| |
| return tpm_return_code(response); |
| } |
| |
| int tpm_init(void) |
| { |
| struct udevice *dev; |
| int err; |
| |
| err = uclass_first_device_err(UCLASS_TPM, &dev); |
| if (err) |
| return err; |
| |
| return tpm_open(dev); |
| } |