17
#include "qemu/osdep.h"
18
19
#include "qemu/module.h"
20
+#include "qemu/error-report.h"
21
#include "qapi/error.h"
22
#include "system/address-spaces.h"
23
#include "hw/core/qdev-properties.h"
67
#define CRB_INTF_CAP_CRB_CHUNK 0b1
68
69
#define CRB_CTRL_CMD_SIZE (TPM_CRB_ADDR_SIZE - A_CRB_DATA_BUFFER)
70
+#define TPM_HEADER_SIZE 10
71
72
enum crb_loc_ctrl {
73
CRB_LOC_CTRL_REQUEST_ACCESS = BIT(0),
83
84
enum crb_start {
85
CRB_START_INVOKE = BIT(0),
86
+ CRB_START_RSP_RETRY = BIT(1),
87
+ CRB_START_NEXT_CHUNK = BIT(2),
88
};
89
90
enum crb_cancel {
127
return ARRAY_FIELD_EX32(s->regs, CRB_LOC_STATE, activeLocality);
128
}
129
130
+static bool tpm_crb_append_command_request(CRBState *s)
131
+{
132
+ /*
133
+ * The linux guest writes the TPM command to the MMIO region in chunks.
134
+ * This function appends a chunk from the MMIO region to internal
135
+ * command_buffer.
136
+ */
137
+ void *mem = memory_region_get_ram_ptr(&s->cmdmem);
138
+ uint32_t to_copy = 0;
139
+ uint32_t total_request_size = 0;
140
+
141
+ /*
142
+ * The initial call extracts the total TPM command size
143
+ * from its header. For the subsequent calls, the data already
144
+ * appended in the command_buffer is used to calculate the total
145
+ * size, as its header stays the same.
146
+ */
147
+ if (s->command_buffer->len == 0) {
148
+ total_request_size = tpm_cmd_get_size(mem);
149
+ if (total_request_size < TPM_HEADER_SIZE) {
150
+ ARRAY_FIELD_DP32(s->regs, CRB_CTRL_STS, tpmSts, 1);
151
+ ARRAY_FIELD_DP32(s->regs, CRB_CTRL_START, Start, 0);
152
+ ARRAY_FIELD_DP32(s->regs, CRB_CTRL_START, nextChunk, 0);
153
+ tpm_crb_clear_internal_buffers(s);
154
+ error_report("Command size %" PRIu32 " less than "
155
+ "TPM header size %" PRIu32,
156
+ total_request_size, (uint32_t)TPM_HEADER_SIZE);
157
+ return false;
158
+ }
159
+ } else {
160
+ total_request_size = tpm_cmd_get_size(s->command_buffer->data);
161
+ }
162
+ total_request_size = MIN(total_request_size, s->be_buffer_size);
163
+
164
+ if (total_request_size > s->command_buffer->len) {
165
+ uint32_t remaining = total_request_size - s->command_buffer->len;
166
+ to_copy = MIN(remaining, CRB_CTRL_CMD_SIZE);
167
+ g_byte_array_append(s->command_buffer, (guint8 *)mem, to_copy);
168
+ }
169
+ return true;
170
+}
171
+
172
+static void tpm_crb_fill_command_response(CRBState *s)
173
+{
174
+ /*
175
+ * Response from the tpm backend will be stored in the internal
176
+ * response_buffer. This function will serve that accumulated response
177
+ * to the linux guest in chunks by writing it back to MMIO region.
178
+ */
179
+ void *mem = memory_region_get_ram_ptr(&s->cmdmem);
180
+ uint32_t remaining = s->response_buffer->len - s->response_offset;
181
+ uint32_t to_copy = MIN(CRB_CTRL_CMD_SIZE, remaining);
182
+
183
+ memcpy(mem, s->response_buffer->data + s->response_offset, to_copy);
184
+
185
+ if (to_copy < CRB_CTRL_CMD_SIZE) {
186
+ memset((guint8 *)mem + to_copy, 0, CRB_CTRL_CMD_SIZE - to_copy);
187
+ }
188
+
189
+ s->response_offset += to_copy;
190
+ memory_region_set_dirty(&s->cmdmem, 0, CRB_CTRL_CMD_SIZE);
191
+}
192
+
193
static void tpm_crb_mmio_write(void *opaque, hwaddr addr,
194
uint64_t val, unsigned size)
195
{
220
}
221
break;
222
case A_CRB_CTRL_START:
156
- if (val == CRB_START_INVOKE &&
157
- !(s->regs[R_CRB_CTRL_START] & CRB_START_INVOKE) &&
158
- tpm_crb_get_active_locty(s) == locty) {
159
- void *mem = memory_region_get_ram_ptr(&s->cmdmem);
160
-
223
+ if (tpm_crb_get_active_locty(s) != locty) {
224
+ break;
225
+ }
226
+ if (s->regs[R_CRB_CTRL_START] & CRB_START_INVOKE) {
227
+ /*
228
+ * Backend TPM is busy processing a request.
229
+ */
230
+ break;
231
+ }
232
+ if (val & CRB_START_INVOKE) {
233
+ if (!tpm_crb_append_command_request(s)) {
234
+ break;
235
+ }
236
ARRAY_FIELD_DP32(s->regs, CRB_CTRL_START, Start, 1);
237
+ g_byte_array_set_size(s->response_buffer, s->be_buffer_size);
238
s->cmd = (TPMBackendCmd) {
163
- .in = mem,
164
- .in_len = MIN(tpm_cmd_get_size(mem), s->be_buffer_size),
165
- .out = mem,
166
- .out_len = s->be_buffer_size,
239
+ .in = s->command_buffer->data,
240
+ .in_len = s->command_buffer->len,
241
+ .out = s->response_buffer->data,
242
+ .out_len = s->response_buffer->len,
243
};
168
-
244
tpm_backend_deliver_request(s->tpmbe, &s->cmd);
245
+ } else if (val & CRB_START_NEXT_CHUNK) {
246
+ if (!s->cap_chunk) {
247
+ break;
248
+ }
249
+ /*
250
+ * nextChunk is used both while sending and receiving data.
251
+ * To distinguish between the two, response_buffer is checked.
252
+ * If it does not have data, then that means we have not yet
253
+ * sent the command to the tpm backend, and therefore call
254
+ * tpm_crb_append_command_request().
255
+ */
256
+ if (s->response_buffer->len > 0 &&
257
+ s->response_offset < s->response_buffer->len) {
258
+ tpm_crb_fill_command_response(s);
259
+ } else {
260
+ if (!tpm_crb_append_command_request(s)) {
261
+ break;
262
+ }
263
+ }
264
+ ARRAY_FIELD_DP32(s->regs, CRB_CTRL_START, nextChunk, 0);
265
+ } else if (val & CRB_START_RSP_RETRY) {
266
+ if (!s->cap_chunk) {
267
+ break;
268
+ }
269
+ if (s->response_buffer->len > 0) {
270
+ s->response_offset = 0;
271
+ tpm_crb_fill_command_response(s);
272
+ }
273
+ ARRAY_FIELD_DP32(s->regs, CRB_CTRL_START, crbRspRetry, 0);
274
+ ARRAY_FIELD_DP32(s->regs, CRB_CTRL_START, nextChunk, 0);
275
}
276
break;
277
case A_CRB_LOC_CTRL:
316
if (ret != 0) {
317
ARRAY_FIELD_DP32(s->regs, CRB_CTRL_STS,
318
tpmSts, 1); /* fatal error */
319
+ tpm_crb_clear_internal_buffers(s);
320
+ } else {
321
+ uint32_t actual_resp_size = tpm_cmd_get_size(s->response_buffer->data);
322
+ uint32_t total_resp_size = MIN(actual_resp_size, s->be_buffer_size);
323
+ g_byte_array_set_size(s->response_buffer, total_resp_size);
324
+ s->response_offset = 0;
325
}
215
- memory_region_set_dirty(&s->cmdmem, 0, CRB_CTRL_CMD_SIZE);
326
+ /*
327
+ * Send the first chunk. Subsequent chunks will be sent
328
+ * on receiving nextChunk from the guest
329
+ */
330
+ tpm_crb_fill_command_response(s);
331
+ ARRAY_FIELD_DP32(s->regs, CRB_CTRL_START, nextChunk, 0);
332
+ ARRAY_FIELD_DP32(s->regs, CRB_CTRL_START, crbRspRetry, 0);
333
+ g_byte_array_set_size(s->command_buffer, 0);
334
}
335
336
static enum TPMVersion tpm_crb_get_version(TPMIf *ti)
405
s->regs[R_CRB_CTRL_RSP_SIZE] = CRB_CTRL_CMD_SIZE;
406
s->regs[R_CRB_CTRL_RSP_ADDR] = TPM_CRB_ADDR_BASE + A_CRB_DATA_BUFFER;
407
290
- s->be_buffer_size = MIN(tpm_backend_get_buffer_size(s->tpmbe),
291
- CRB_CTRL_CMD_SIZE);
408
+ s->be_buffer_size = tpm_backend_get_buffer_size(s->tpmbe);
409
410
if (tpm_backend_startup_tpm(s->tpmbe, s->be_buffer_size) < 0) {
411
exit(1);