Prepare v2024.10
[platform/kernel/u-boot.git] / lib / tpm-common.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (c) 2013 The Chromium OS Authors.
4  * Coypright (c) 2013 Guntermann & Drunck GmbH
5  */
6
7 #define LOG_CATEGORY UCLASS_TPM
8
9 #include <dm.h>
10 #include <log.h>
11 #include <asm/unaligned.h>
12 #include <tpm-common.h>
13 #include "tpm-utils.h"
14
15 enum tpm_version tpm_get_version(struct udevice *dev)
16 {
17         struct tpm_chip_priv *priv = dev_get_uclass_priv(dev);
18
19         return priv->version;
20 }
21
22 int pack_byte_string(u8 *str, size_t size, const char *format, ...)
23 {
24         va_list args;
25         size_t offset = 0, length = 0;
26         u8 *data = NULL;
27         u32 value = 0;
28
29         va_start(args, format);
30         for (; *format; format++) {
31                 switch (*format) {
32                 case 'b':
33                         offset = va_arg(args, size_t);
34                         value = va_arg(args, int);
35                         length = 1;
36                         break;
37                 case 'w':
38                         offset = va_arg(args, size_t);
39                         value = va_arg(args, int);
40                         length = 2;
41                         break;
42                 case 'd':
43                         offset = va_arg(args, size_t);
44                         value = va_arg(args, u32);
45                         length = 4;
46                         break;
47                 case 's':
48                         offset = va_arg(args, size_t);
49                         data = va_arg(args, u8 *);
50                         length = va_arg(args, u32);
51                         break;
52                 default:
53                         debug("Couldn't recognize format string\n");
54                         va_end(args);
55                         return -1;
56                 }
57
58                 if (offset + length > size) {
59                         va_end(args);
60                         return -1;
61                 }
62
63                 switch (*format) {
64                 case 'b':
65                         str[offset] = value;
66                         break;
67                 case 'w':
68                         put_unaligned_be16(value, str + offset);
69                         break;
70                 case 'd':
71                         put_unaligned_be32(value, str + offset);
72                         break;
73                 case 's':
74                         memcpy(str + offset, data, length);
75                         break;
76                 }
77         }
78         va_end(args);
79
80         return 0;
81 }
82
83 int unpack_byte_string(const u8 *str, size_t size, const char *format, ...)
84 {
85         va_list args;
86         size_t offset = 0, length = 0;
87         u8 *ptr8 = NULL;
88         u16 *ptr16 = NULL;
89         u32 *ptr32 = NULL;
90
91         va_start(args, format);
92         for (; *format; format++) {
93                 switch (*format) {
94                 case 'b':
95                         offset = va_arg(args, size_t);
96                         ptr8 = va_arg(args, u8 *);
97                         length = 1;
98                         break;
99                 case 'w':
100                         offset = va_arg(args, size_t);
101                         ptr16 = va_arg(args, u16 *);
102                         length = 2;
103                         break;
104                 case 'd':
105                         offset = va_arg(args, size_t);
106                         ptr32 = va_arg(args, u32 *);
107                         length = 4;
108                         break;
109                 case 's':
110                         offset = va_arg(args, size_t);
111                         ptr8 = va_arg(args, u8 *);
112                         length = va_arg(args, u32);
113                         break;
114                 default:
115                         va_end(args);
116                         debug("Couldn't recognize format string\n");
117                         return -1;
118                 }
119
120                 if (offset + length > size) {
121                         va_end(args);
122                         log_err("Failed to read: size=%zd, offset=%zx, len=%zx\n",
123                                 size, offset, length);
124                         return -1;
125                 }
126
127                 switch (*format) {
128                 case 'b':
129                         *ptr8 = str[offset];
130                         break;
131                 case 'w':
132                         *ptr16 = get_unaligned_be16(str + offset);
133                         break;
134                 case 'd':
135                         *ptr32 = get_unaligned_be32(str + offset);
136                         break;
137                 case 's':
138                         memcpy(ptr8, str + offset, length);
139                         break;
140                 }
141         }
142         va_end(args);
143
144         return 0;
145 }
146
147 u32 tpm_command_size(const void *command)
148 {
149         const size_t command_size_offset = 2;
150
151         return get_unaligned_be32(command + command_size_offset);
152 }
153
154 u32 tpm_return_code(const void *response)
155 {
156         const size_t return_code_offset = 6;
157
158         return get_unaligned_be32(response + return_code_offset);
159 }
160
161 u32 tpm_sendrecv_command(struct udevice *dev, const void *command,
162                          void *response, size_t *size_ptr)
163 {
164         int err, ret;
165         u8 response_buffer[COMMAND_BUFFER_SIZE];
166         size_t response_length;
167         int i;
168         uint size;
169
170         if (response) {
171                 response_length = *size_ptr;
172         } else {
173                 response = response_buffer;
174                 response_length = sizeof(response_buffer);
175         }
176
177         size = tpm_command_size(command);
178
179         /* sanity check, which also helps coverity */
180         if (size > COMMAND_BUFFER_SIZE)
181                 return log_msg_ret("size", -E2BIG);
182
183         log_debug("TPM request [size:%d]: ", size);
184         for (i = 0; i < size; i++)
185                 log_debug("%02x ", ((u8 *)command)[i]);
186         log_debug("\n");
187
188         err = tpm_xfer(dev, command, size, response, &response_length);
189
190         if (err < 0)
191                 return err;
192
193         if (size_ptr)
194                 *size_ptr = response_length;
195
196         ret = tpm_return_code(response);
197
198         log_debug("TPM response [ret:%d]: ", ret);
199         for (i = 0; i < response_length; i++)
200                 log_debug("%02x ", ((u8 *)response)[i]);
201         log_debug("\n");
202
203         return ret;
204 }
205
206 int tpm_init(struct udevice *dev)
207 {
208         return tpm_open(dev);
209 }