tlcl: implement clear, startup, shutdown, self test
[vboot.git] / firmware / lib / tpm2_lite / tlcl.c
1 /*
2  * Copyright 2016 The Chromium OS Authors. All rights reserved.
3  * Use of this source code is governed by a BSD-style license that can be
4  * found in the LICENSE file.
5  *
6  * Some TPM constants and type definitions for standalone compilation for use
7  * in the firmware
8  */
9
10 #include "rollback_index.h"
11 #include "tpm2_marshaling.h"
12 #include "utility.h"
13 #include "tlcl.h"
14
15 static struct tpm2_response *tpm_process_command(TPM_CC command,
16                                                  void *command_body)
17 {
18         /* Command/response buffer. */
19         static uint8_t cr_buffer[TPM_BUFFER_SIZE];
20         uint32_t out_size, in_size;
21         struct tpm2_response *response;
22
23         out_size = tpm_marshal_command(command, command_body,
24                                        cr_buffer, sizeof(cr_buffer));
25         if (out_size < 0) {
26                 VBDEBUG(("command %#x, cr size %d\n",
27                          command, out_size));
28                 return NULL;
29         }
30
31         in_size = sizeof(cr_buffer);
32         if (VbExTpmSendReceive(cr_buffer, out_size,
33                                cr_buffer, &in_size) != TPM_SUCCESS) {
34                 VBDEBUG(("tpm transaction failed for %#x\n", command));
35                 return NULL;
36         }
37
38         response = tpm_unmarshal_response(command, cr_buffer, in_size);
39
40         VBDEBUG(("%s: command %#x, return code %#x\n", __func__, command,
41                  response ? response->hdr.tpm_code : -1));
42
43         return response;
44 }
45
46 static uint32_t tlcl_read_ph_disabled(void)
47 {
48         uint32_t rv;
49         TPM_STCLEAR_FLAGS flags;
50
51         rv = TlclGetSTClearFlags(&flags);
52         if (rv != TPM_SUCCESS)
53                 return rv;
54
55         tpm_set_ph_disabled(!flags.phEnable);
56
57         return TPM_SUCCESS;
58 }
59
60 uint32_t TlclLibInit(void)
61 {
62         uint32_t rv;
63
64         rv = VbExTpmInit();
65         if (rv != TPM_SUCCESS)
66                 return rv;
67
68         rv = tlcl_read_ph_disabled();
69         if (rv != TPM_SUCCESS) {
70                 TlclLibClose();
71                 return rv;
72         }
73
74         return TPM_SUCCESS;
75 }
76
77 uint32_t TlclLibClose(void)
78 {
79         return VbExTpmClose();
80 }
81
82 uint32_t TlclSendReceive(const uint8_t *request, uint8_t *response,
83                          int max_length)
84 {
85         uint32_t rv, resp_size;
86
87         resp_size = max_length;
88         rv = VbExTpmSendReceive(request, tpm_get_packet_size(request),
89                                 response, &resp_size);
90
91         return rv ? rv : tpm_get_packet_response_code(response);
92 }
93
94 int TlclPacketSize(const uint8_t *packet)
95 {
96         return tpm_get_packet_size(packet);
97 }
98
99 uint32_t TlclStartup(void)
100 {
101         struct tpm2_response *response;
102         struct tpm2_startup_cmd startup;
103
104         startup.startup_type = TPM_SU_CLEAR;
105
106         response = tpm_process_command(TPM2_Startup, &startup);
107         if (!response || response->hdr.tpm_code)
108                 return TPM_E_IOERROR;
109
110         return TPM_SUCCESS;
111 }
112
113 uint32_t TlclSaveState(void)
114 {
115         struct tpm2_response *response;
116         struct tpm2_shutdown_cmd shutdown;
117
118         shutdown.shutdown_type = TPM_SU_STATE;
119
120         response = tpm_process_command(TPM2_Shutdown, &shutdown);
121         if (!response || response->hdr.tpm_code)
122                 return TPM_E_IOERROR;
123
124         return TPM_SUCCESS;
125 }
126
127 uint32_t TlclResume(void)
128 {
129         struct tpm2_response *response;
130         struct tpm2_startup_cmd startup;
131
132         startup.startup_type = TPM_SU_STATE;
133
134         response = tpm_process_command(TPM2_Startup, &startup);
135         if (!response || response->hdr.tpm_code)
136                 return TPM_E_IOERROR;
137
138         return TPM_SUCCESS;
139 }
140
141 uint32_t TlclSelfTestFull(void)
142 {
143         struct tpm2_response *response;
144         struct tpm2_self_test_cmd self_test;
145
146         self_test.full_test = 1;
147
148         response = tpm_process_command(TPM2_SelfTest, &self_test);
149         if (!response || response->hdr.tpm_code)
150                 return TPM_E_IOERROR;
151
152         return TPM_SUCCESS;
153 }
154
155 uint32_t TlclContinueSelfTest(void)
156 {
157         struct tpm2_response *response;
158         struct tpm2_self_test_cmd self_test;
159
160         self_test.full_test = 0;
161
162         response = tpm_process_command(TPM2_SelfTest, &self_test);
163         if (!response || response->hdr.tpm_code)
164                 return TPM_E_IOERROR;
165
166         return TPM_SUCCESS;
167 }
168
169 uint32_t TlclDefineSpace(uint32_t index, uint32_t perm, uint32_t size)
170 {
171         VBDEBUG(("%s called, NOT YET IMPLEMENTED\n", __func__));
172         return TPM_SUCCESS;
173 }
174
175 /**
176  * Issue a ForceClear.  The TPM error code is returned.
177  */
178 uint32_t TlclForceClear(void)
179 {
180         struct tpm2_response *response;
181
182         response = tpm_process_command(TPM2_Clear, NULL);
183         if (!response || response->hdr.tpm_code)
184                 return TPM_E_IOERROR;
185
186         return TPM_SUCCESS;
187 }
188
189 uint32_t TlclSetDeactivated(uint8_t flag)
190 {
191         VBDEBUG(("%s called, NOT YET IMPLEMENTED\n", __func__));
192         return TPM_SUCCESS;
193 }
194
195 uint32_t TlclSetEnable(void)
196 {
197         VBDEBUG(("%s called, NOT YET IMPLEMENTED\n", __func__));
198         return TPM_SUCCESS;
199 }
200
201 uint32_t TlclGetFlags(uint8_t* disable,
202                       uint8_t* deactivated,
203                       uint8_t *nvlocked)
204 {
205         /* For TPM2 the flags are always the same */
206         if (disable)
207                 *disable = 0;
208         if (deactivated)
209                 *deactivated = 0;
210         if (nvlocked)
211                 *nvlocked = 1;
212         return TPM_SUCCESS;
213 }
214
215 int TlclIsOwned(void)
216 {
217         VBDEBUG(("%s called, NOT YET IMPLEMENTED\n", __func__));
218         return 0;
219 }
220
221 uint32_t TlclExtend(int pcr_num, const uint8_t *in_digest, uint8_t *out_digest)
222 {
223         VBDEBUG(("%s called, NOT YET IMPLEMENTED\n", __func__));
224         return TPM_SUCCESS;
225 }
226
227 /**
228  * Get the permission bits for the NVRAM space with |index|.
229  */
230 uint32_t TlclGetPermissions(uint32_t index, uint32_t *permissions)
231 {
232         *permissions = 0;
233         VBDEBUG(("%s called, NOT YET IMPLEMENTED\n", __func__));
234         return TPM_SUCCESS;
235 }
236
237 static uint32_t tlcl_get_capability(TPM_CAP cap, TPM_PT property,
238                                     struct get_capability_response **presp)
239 {
240         struct tpm2_response *response;
241         struct tpm2_get_capability_cmd getcap;
242
243         getcap.capability = cap;
244         getcap.property = property;
245         getcap.property_count = 1;
246
247         response = tpm_process_command(TPM2_GetCapability, &getcap);
248         if (!response || response->hdr.tpm_code)
249                 return TPM_E_IOERROR;
250         *presp = &response->cap;
251
252         return TPM_SUCCESS;
253 }
254
255 static uint32_t tlcl_get_tpm_property(TPM_PT property, uint32_t *pvalue)
256 {
257         uint32_t rv;
258         struct get_capability_response *resp;
259         TPML_TAGGED_TPM_PROPERTY *tpm_prop;
260
261         rv = tlcl_get_capability(TPM_CAP_TPM_PROPERTIES, property, &resp);
262         if (rv != TPM_SUCCESS)
263                 return rv;
264
265         if (resp->capability_data.capability != TPM_CAP_TPM_PROPERTIES)
266                 return TPM_E_IOERROR;
267
268         tpm_prop = &resp->capability_data.data.tpm_properties;
269
270         if ((tpm_prop->count != 1) ||
271             (tpm_prop->tpm_property[0].property != property))
272                 return TPM_E_IOERROR;
273
274         *pvalue = tpm_prop->tpm_property[0].value;
275         return TPM_SUCCESS;
276 }
277
278 uint32_t TlclGetPermanentFlags(TPM_PERMANENT_FLAGS *pflags)
279 {
280         return tlcl_get_tpm_property(TPM_PT_PERMANENT,
281                                      (uint32_t *)pflags);
282 }
283
284 uint32_t TlclGetSTClearFlags(TPM_STCLEAR_FLAGS *pflags)
285 {
286         return tlcl_get_tpm_property(TPM_PT_STARTUP_CLEAR,
287                                      (uint32_t *)pflags);
288 }
289
290 uint32_t TlclGetOwnership(uint8_t *owned)
291 {
292         *owned = 0;
293         VBDEBUG(("%s called, NOT YET IMPLEMENTED\n", __func__));
294         return TPM_SUCCESS;
295 }
296
297 static uint32_t tlcl_lock_nv_write(uint32_t index)
298 {
299         struct tpm2_response *response;
300         struct tpm2_nv_write_lock_cmd nv_wl;
301
302         nv_wl.nvIndex = HR_NV_INDEX + index;
303         response = tpm_process_command(TPM2_NV_WriteLock, &nv_wl);
304
305         if (!response || response->hdr.tpm_code)
306                 return TPM_E_INTERNAL_INCONSISTENCY;
307
308         return TPM_SUCCESS;
309 }
310
311 static uint32_t tlcl_disable_platform_hierarchy(void)
312 {
313         struct tpm2_response *response;
314         struct tpm2_hierarchy_control_cmd hc;
315
316         hc.enable = TPM_RH_PLATFORM;
317         hc.state = 0;
318
319         response = tpm_process_command(TPM2_Hierarchy_Control, &hc);
320
321         if (!response || response->hdr.tpm_code)
322                 return TPM_E_INTERNAL_INCONSISTENCY;
323
324         tpm_set_ph_disabled(1);
325         return TPM_SUCCESS;
326 }
327
328 /**
329  * The name of the function was kept to maintain the existing TPM API, but
330  * TPM2.0 does not use the global lock to protect the FW rollback counter.
331  * Instead it calls WriteLock for the FW NVRAM index to prevent future
332  * writes to it.
333  *
334  * It first checks if the platform hierarchy is already disabled, and does
335  * nothing, if so. Otherwise, WriteLock for the index obviously fails.
336  */
337 uint32_t TlclSetGlobalLock(void)
338 {
339         if (tpm_is_ph_disabled())
340                 return TPM_SUCCESS;
341         else
342                 return tlcl_lock_nv_write(FIRMWARE_NV_INDEX);
343 }
344
345 /**
346  * Turn off physical presence and locks it off until next reboot.  The TPM
347  * error code is returned.
348  *
349  * The name of the function was kept to maintain the existing TPM API, but
350  * TPM2.0 does not have to use the Physical Presence concept. Instead it just
351  * removes platform authorization - this makes sure that firmware and kernel
352  * rollback counter spaces can not be modified.
353  *
354  * It also explicitly locks the kernel rollback counter space (the FW rollback
355  * counter space was locked before RW firmware started.)
356  */
357 uint32_t TlclLockPhysicalPresence(void)
358 {
359         uint32_t rv;
360
361         if (tpm_is_ph_disabled())
362                 return TPM_SUCCESS;
363
364         rv = tlcl_lock_nv_write(KERNEL_NV_INDEX);
365         if (rv == TPM_SUCCESS)
366                 rv = tlcl_disable_platform_hierarchy();
367
368         return rv;
369 }
370
371 uint32_t TlclRead(uint32_t index, void* data, uint32_t length)
372 {
373         struct tpm2_nv_read_cmd nv_readc;
374         struct tpm2_response *response;
375
376         Memset(&nv_readc, 0, sizeof(nv_readc));
377
378         nv_readc.nvIndex = HR_NV_INDEX + index;
379         nv_readc.size = length;
380
381         response = tpm_process_command(TPM2_NV_Read, &nv_readc);
382
383         /* Need to map tpm error codes into internal values. */
384         if (!response)
385                 return TPM_E_READ_FAILURE;
386
387         switch (response->hdr.tpm_code) {
388         case 0:
389                 break;
390
391         case 0x28b:
392                 return TPM_E_BADINDEX;
393
394         default:
395                 return TPM_E_READ_FAILURE;
396         }
397
398         if (length > response->nvr.buffer.t.size)
399                 return TPM_E_RESPONSE_TOO_LARGE;
400
401         if (length < response->nvr.buffer.t.size)
402                 return TPM_E_READ_EMPTY;
403
404         Memcpy(data, response->nvr.buffer.t.buffer, length);
405
406         return TPM_SUCCESS;
407 }
408
409 uint32_t TlclWrite(uint32_t index, const void *data, uint32_t length)
410 {
411         struct tpm2_nv_write_cmd nv_writec;
412         struct tpm2_response *response;
413
414         Memset(&nv_writec, 0, sizeof(nv_writec));
415
416         nv_writec.nvIndex = HR_NV_INDEX + index;
417         nv_writec.data.t.size = length;
418         nv_writec.data.t.buffer = data;
419
420         response = tpm_process_command(TPM2_NV_Write, &nv_writec);
421
422         /* Need to map tpm error codes into internal values. */
423         if (!response)
424                 return TPM_E_WRITE_FAILURE;
425
426         return TPM_SUCCESS;
427 }
428
429 uint32_t TlclPCRRead(uint32_t index, void *data, uint32_t length)
430 {
431         VBDEBUG(("%s called, NOT YET IMPLEMENTED\n", __func__));
432         return TPM_SUCCESS;
433 }
434
435 uint32_t TlclWriteLock(uint32_t index)
436 {
437         VBDEBUG(("%s called, NOT YET IMPLEMENTED\n", __func__));
438         return TPM_SUCCESS;
439 }
440
441 uint32_t TlclReadLock(uint32_t index)
442 {
443         VBDEBUG(("%s called, NOT YET IMPLEMENTED\n", __func__));
444         return TPM_SUCCESS;
445 }
446
447 uint32_t TlclGetRandom(uint8_t *data, uint32_t length, uint32_t *size)
448 {
449         *size = 0;
450         VBDEBUG(("%s called, NOT YET IMPLEMENTED\n", __func__));
451         return TPM_E_IOERROR;
452 }