tlcl: automatically detect if platform hierarchy is disabled
[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         VBDEBUG(("%s called, NOT YET IMPLEMENTED\n", __func__));
86         return TPM_SUCCESS;
87 }
88
89 int TlclPacketSize(const uint8_t *packet)
90 {
91         VBDEBUG(("%s called, NOT YET IMPLEMENTED\n", __func__));
92         return 0;
93 }
94
95 uint32_t TlclStartup(void)
96 {
97         VBDEBUG(("%s called, NOT YET IMPLEMENTED\n", __func__));
98         return TPM_SUCCESS;
99 }
100
101 uint32_t TlclSaveState(void)
102 {
103         VBDEBUG(("%s called, NOT YET IMPLEMENTED\n", __func__));
104         return TPM_SUCCESS;
105 }
106
107 uint32_t TlclResume(void)
108 {
109         VBDEBUG(("%s called, NOT YET IMPLEMENTED\n", __func__));
110         return TPM_SUCCESS;
111 }
112
113 uint32_t TlclSelfTestFull(void)
114 {
115         VBDEBUG(("%s called, NOT YET IMPLEMENTED\n", __func__));
116         return TPM_SUCCESS;
117 }
118
119 uint32_t TlclContinueSelfTest(void)
120 {
121         VBDEBUG(("%s called, NOT YET IMPLEMENTED\n", __func__));
122         return TPM_SUCCESS;
123 }
124
125 uint32_t TlclDefineSpace(uint32_t index, uint32_t perm, uint32_t size)
126 {
127         VBDEBUG(("%s called, NOT YET IMPLEMENTED\n", __func__));
128         return TPM_SUCCESS;
129 }
130
131 /**
132  * Issue a ForceClear.  The TPM error code is returned.
133  */
134 uint32_t TlclForceClear(void)
135 {
136         VBDEBUG(("%s called, NOT YET IMPLEMENTED\n", __func__));
137         return TPM_SUCCESS;
138 }
139
140 uint32_t TlclSetDeactivated(uint8_t flag)
141 {
142         VBDEBUG(("%s called, NOT YET IMPLEMENTED\n", __func__));
143         return TPM_SUCCESS;
144 }
145
146 uint32_t TlclSetEnable(void)
147 {
148         VBDEBUG(("%s called, NOT YET IMPLEMENTED\n", __func__));
149         return TPM_SUCCESS;
150 }
151
152 uint32_t TlclGetFlags(uint8_t* disable,
153                       uint8_t* deactivated,
154                       uint8_t *nvlocked)
155 {
156         /* For TPM2 the flags are always the same */
157         if (disable)
158                 *disable = 0;
159         if (deactivated)
160                 *deactivated = 0;
161         if (nvlocked)
162                 *nvlocked = 1;
163         return TPM_SUCCESS;
164 }
165
166 int TlclIsOwned(void)
167 {
168         VBDEBUG(("%s called, NOT YET IMPLEMENTED\n", __func__));
169         return 0;
170 }
171
172 uint32_t TlclExtend(int pcr_num, const uint8_t *in_digest, uint8_t *out_digest)
173 {
174         VBDEBUG(("%s called, NOT YET IMPLEMENTED\n", __func__));
175         return TPM_SUCCESS;
176 }
177
178 /**
179  * Get the permission bits for the NVRAM space with |index|.
180  */
181 uint32_t TlclGetPermissions(uint32_t index, uint32_t *permissions)
182 {
183         *permissions = 0;
184         VBDEBUG(("%s called, NOT YET IMPLEMENTED\n", __func__));
185         return TPM_SUCCESS;
186 }
187
188 static uint32_t tlcl_get_capability(TPM_CAP cap, TPM_PT property,
189                                     struct get_capability_response **presp)
190 {
191         struct tpm2_response *response;
192         struct tpm2_get_capability_cmd getcap;
193
194         getcap.capability = cap;
195         getcap.property = property;
196         getcap.property_count = 1;
197
198         response = tpm_process_command(TPM2_GetCapability, &getcap);
199         if (!response || response->hdr.tpm_code)
200                 return TPM_E_IOERROR;
201         *presp = &response->cap;
202
203         return TPM_SUCCESS;
204 }
205
206 static uint32_t tlcl_get_tpm_property(TPM_PT property, uint32_t *pvalue)
207 {
208         uint32_t rv;
209         struct get_capability_response *resp;
210         TPML_TAGGED_TPM_PROPERTY *tpm_prop;
211
212         rv = tlcl_get_capability(TPM_CAP_TPM_PROPERTIES, property, &resp);
213         if (rv != TPM_SUCCESS)
214                 return rv;
215
216         if (resp->capability_data.capability != TPM_CAP_TPM_PROPERTIES)
217                 return TPM_E_IOERROR;
218
219         tpm_prop = &resp->capability_data.data.tpm_properties;
220
221         if ((tpm_prop->count != 1) ||
222             (tpm_prop->tpm_property[0].property != property))
223                 return TPM_E_IOERROR;
224
225         *pvalue = tpm_prop->tpm_property[0].value;
226         return TPM_SUCCESS;
227 }
228
229 uint32_t TlclGetPermanentFlags(TPM_PERMANENT_FLAGS *pflags)
230 {
231         return tlcl_get_tpm_property(TPM_PT_PERMANENT,
232                                      (uint32_t *)pflags);
233 }
234
235 uint32_t TlclGetSTClearFlags(TPM_STCLEAR_FLAGS *pflags)
236 {
237         return tlcl_get_tpm_property(TPM_PT_STARTUP_CLEAR,
238                                      (uint32_t *)pflags);
239 }
240
241 uint32_t TlclGetOwnership(uint8_t *owned)
242 {
243         *owned = 0;
244         VBDEBUG(("%s called, NOT YET IMPLEMENTED\n", __func__));
245         return TPM_SUCCESS;
246 }
247
248 static uint32_t tlcl_lock_nv_write(uint32_t index)
249 {
250         struct tpm2_response *response;
251         struct tpm2_nv_write_lock_cmd nv_wl;
252
253         nv_wl.nvIndex = HR_NV_INDEX + index;
254         response = tpm_process_command(TPM2_NV_WriteLock, &nv_wl);
255
256         if (!response || response->hdr.tpm_code)
257                 return TPM_E_INTERNAL_INCONSISTENCY;
258
259         return TPM_SUCCESS;
260 }
261
262 static uint32_t tlcl_disable_platform_hierarchy(void)
263 {
264         struct tpm2_response *response;
265         struct tpm2_hierarchy_control_cmd hc;
266
267         hc.enable = TPM_RH_PLATFORM;
268         hc.state = 0;
269
270         response = tpm_process_command(TPM2_Hierarchy_Control, &hc);
271
272         if (!response || response->hdr.tpm_code)
273                 return TPM_E_INTERNAL_INCONSISTENCY;
274
275         tpm_set_ph_disabled(1);
276         return TPM_SUCCESS;
277 }
278
279 /**
280  * Turn off physical presence and locks it off until next reboot.  The TPM
281  * error code is returned.
282  *
283  * The name of the function was kept to maintain the existing TPM API, but
284  * TPM2.0 does not have to use the Physical Presence concept. Instead it just
285  * removes platform authorization - this makes sure that firmware and kernel
286  * rollback counter spaces can not be modified.
287  *
288  * It also explicitly locks the kernel rollback counter space (the FW rollback
289  * counter space was locked before RW firmware started.)
290  */
291 uint32_t TlclLockPhysicalPresence(void)
292 {
293         uint32_t rv;
294
295         rv = tlcl_lock_nv_write(KERNEL_NV_INDEX);
296         if (rv == TPM_SUCCESS)
297                 rv = tlcl_disable_platform_hierarchy();
298
299         return rv;
300 }
301
302 uint32_t TlclRead(uint32_t index, void* data, uint32_t length)
303 {
304         struct tpm2_nv_read_cmd nv_readc;
305         struct tpm2_response *response;
306
307         Memset(&nv_readc, 0, sizeof(nv_readc));
308
309         nv_readc.nvIndex = HR_NV_INDEX + index;
310         nv_readc.size = length;
311
312         response = tpm_process_command(TPM2_NV_Read, &nv_readc);
313
314         /* Need to map tpm error codes into internal values. */
315         if (!response)
316                 return TPM_E_READ_FAILURE;
317
318         switch (response->hdr.tpm_code) {
319         case 0:
320                 break;
321
322         case 0x28b:
323                 return TPM_E_BADINDEX;
324
325         default:
326                 return TPM_E_READ_FAILURE;
327         }
328
329         if (length > response->nvr.buffer.t.size)
330                 return TPM_E_RESPONSE_TOO_LARGE;
331
332         if (length < response->nvr.buffer.t.size)
333                 return TPM_E_READ_EMPTY;
334
335         Memcpy(data, response->nvr.buffer.t.buffer, length);
336
337         return TPM_SUCCESS;
338 }
339
340 uint32_t TlclWrite(uint32_t index, const void *data, uint32_t length)
341 {
342         struct tpm2_nv_write_cmd nv_writec;
343         struct tpm2_response *response;
344
345         Memset(&nv_writec, 0, sizeof(nv_writec));
346
347         nv_writec.nvIndex = HR_NV_INDEX + index;
348         nv_writec.data.t.size = length;
349         nv_writec.data.t.buffer = data;
350
351         response = tpm_process_command(TPM2_NV_Write, &nv_writec);
352
353         /* Need to map tpm error codes into internal values. */
354         if (!response)
355                 return TPM_E_WRITE_FAILURE;
356
357         return TPM_SUCCESS;
358 }
359
360 uint32_t TlclPCRRead(uint32_t index, void *data, uint32_t length)
361 {
362         VBDEBUG(("%s called, NOT YET IMPLEMENTED\n", __func__));
363         return TPM_SUCCESS;
364 }
365
366 uint32_t TlclWriteLock(uint32_t index)
367 {
368         VBDEBUG(("%s called, NOT YET IMPLEMENTED\n", __func__));
369         return TPM_SUCCESS;
370 }
371
372 uint32_t TlclReadLock(uint32_t index)
373 {
374         VBDEBUG(("%s called, NOT YET IMPLEMENTED\n", __func__));
375         return TPM_SUCCESS;
376 }
377
378 uint32_t TlclGetRandom(uint8_t *data, uint32_t length, uint32_t *size)
379 {
380         *size = 0;
381         VBDEBUG(("%s called, NOT YET IMPLEMENTED\n", __func__));
382         return TPM_E_IOERROR;
383 }