-
Notifications
You must be signed in to change notification settings - Fork 136
/
Copy pathNVMem.c
657 lines (553 loc) · 18.6 KB
/
NVMem.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
/* Microsoft Reference Implementation for TPM 2.0
*
* The copyright in this software is being made available under the BSD License,
* included below. This software may be subject to other third party and
* contributor rights, including patent rights, and no such rights are granted
* under this license.
*
* Copyright (c) Microsoft Corporation
*
* All rights reserved.
*
* BSD License
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this list
* of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice, this
* list of conditions and the following disclaimer in the documentation and/or
* other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ""AS IS""
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
//** Description
//
// This file contains the NV read and write access methods. This implementation
// uses RAM/file and does not manage the RAM/file as NV blocks.
// The implementation may become more sophisticated over time.
//
#include "TpmError.h"
#include "Admin.h"
#include "VendorString.h"
#include "stdint.h"
#include "malloc.h"
#include "string.h"
#include <tee_internal_api.h>
#include <tee_internal_api_extensions.h>
#ifdef CFG_TA_FTPM_RPMB_STORAGE
#define CHOOSEN_TEE_STORAGE TEE_STORAGE_PRIVATE_RPMB
#else
#define CHOOSEN_TEE_STORAGE TEE_STORAGE_PRIVATE
#endif
//
// Overall size of NV, not just the TPM's NV storage
//
#define NV_CHIP_MEMORY_SIZE (NV_MEMORY_SIZE + NV_TPM_STATE_SIZE)
//
// OpTEE still has an all or nothing approach to reads/writes. To provide
// more performant access to storage, break up NV accross 1Kbyte blocks.
//
// Note that NV_CHIP_MEMORY_SIZE *MUST* be a factor of NV_BLOCK_SIZE.
//
#define NV_BLOCK_SIZE 0x200
#define NV_BLOCK_COUNT ((NV_CHIP_MEMORY_SIZE) / (NV_BLOCK_SIZE))
//
// For cleaner descriptor validation
//
#define IS_VALID(a) ((a) != (TEE_HANDLE_NULL))
//
// Storage flags
//
#define TA_STORAGE_FLAGS (TEE_DATA_FLAG_ACCESS_READ | \
TEE_DATA_FLAG_ACCESS_WRITE | \
TEE_DATA_FLAG_ACCESS_WRITE_META)
//
// The base Object ID for fTPM storage
//
static const UINT32 s_StorageObjectID = 0x54504D00; // 'TPM00'
//
// Object handle list for persistent storage objects containing NV
//
static TEE_ObjectHandle s_NVStore[NV_BLOCK_COUNT] = { TEE_HANDLE_NULL };
//
// Bitmap for NV blocks. Moving from UINT64 requires change to NV_DIRTY_ALL.
//
static UINT64 s_blockMap = 0x0ULL;
//
// Shortcut for 'dirty'ing all NV blocks. Note the type.
//
#if NV_BLOCK_COUNT < 64
#define NV_DIRTY_ALL ((UINT64)((0x1ULL << NV_BLOCK_COUNT) - 1))
#elif NV_BLOCK_COUNT == 64
#define NV_DIRTY_ALL (~(0x0ULL))
#else
#error "NV block count exceeds 64 bit block map. Adjust block or NV size."
#endif
//
// NV state
//
static BOOL s_NVChipFileNeedsManufacture = FALSE;
static BOOL s_NVInitialized = FALSE;
static UCHAR s_NV[NV_CHIP_MEMORY_SIZE];
//
// Firmware revision
//
static const UINT32 firmwareV1 = FIRMWARE_V1;
static const UINT32 firmwareV2 = FIRMWARE_V2;
//
// Revision fro NVChip
//
static UINT64 s_chipRevision = 0;
//
// This offset puts the revision field immediately following the TPM Admin
// state. The Admin space in NV is down to ~16 bytes but is padded out to
// 256bytes to avoid alignment issues and allow for growth.
//
#define NV_CHIP_REVISION_OFFSET ((NV_MEMORY_SIZE) + (TPM_STATE_SIZE))
VOID
_plat__NvInitFromStorage()
{
DMSG("_plat__NvInitFromStorage()");
UINT32 i;
BOOL initialized;
UINT32 objID;
UINT32 bytesRead;
TEE_Result Result;
// Don't re-initialize.
if (s_NVInitialized) {
return;
}
//
// If the NV file is successfully read from the storage then
// initialized must be set. We are setting initialized to true
// here but if an error is encountered reading the NV file it will
// be reset.
//
initialized = TRUE;
// Collect storage objects and init NV.
for (i = 0; i < NV_BLOCK_COUNT; i++) {
// Form storage object ID for this block.
objID = s_StorageObjectID + i;
// Attempt to open TEE persistent storage object.
Result = TEE_OpenPersistentObject(CHOOSEN_TEE_STORAGE,
(void *)&objID,
sizeof(objID),
TA_STORAGE_FLAGS,
&s_NVStore[i]);
// If the open failed, try to create this storage object.
if (Result != TEE_SUCCESS) {
// There was an error, fail the init, NVEnable can retry.
if (Result != TEE_ERROR_ITEM_NOT_FOUND) {
#ifdef fTPMDebug
DMSG("Failed to open fTPM storage object");
#endif
goto Error;
}
// Storage object was not found, create it.
Result = TEE_CreatePersistentObject(CHOOSEN_TEE_STORAGE,
(void *)&objID,
sizeof(objID),
TA_STORAGE_FLAGS,
NULL,
(void *)&(s_NV[i * NV_BLOCK_SIZE]),
NV_BLOCK_SIZE,
&s_NVStore[i]);
// There was an error, fail the init, NVEnable can retry.
if (Result != TEE_SUCCESS) {
#ifdef fTPMDebug
DMSG("Failed to create fTPM storage object");
#endif
goto Error;
}
// A clean storage object was created, we must (re)manufacture.
s_NVChipFileNeedsManufacture = TRUE;
// To ensure NV is consistent, force a write back of all NV blocks
s_blockMap = NV_DIRTY_ALL;
// Need to re-initialize
initialized = FALSE;
#ifdef fTPMDebug
IMSG("Created fTPM storage object, i: 0x%x, s: 0x%x, id: 0x%x, h:0x%x\n",
i, NV_BLOCK_SIZE, objID, s_NVStore[i]);
#endif
}
else {
// Successful open, now read fTPM storage object.
Result = TEE_ReadObjectData(s_NVStore[i],
(void *)&(s_NV[i * NV_BLOCK_SIZE]),
NV_BLOCK_SIZE,
&bytesRead);
// Give up on failed or incomplete reads.
if ((Result != TEE_SUCCESS) || (bytesRead != NV_BLOCK_SIZE)) {
#ifdef fTPMDebug
DMSG("Failed to read fTPM storage object");
#endif
goto Error;
}
#ifdef fTPMDebug
IMSG("Read fTPM storage object, i: 0x%x, s: 0x%x, id: 0x%x, h:0x%x\n",
i, bytesRead, objID, s_NVStore[i]);
#endif
}
/* Close object now, it will be opened back upon update */
TEE_CloseObject(s_NVStore[i]);
s_NVStore[i] = TEE_HANDLE_NULL;
}
// Storage objects are open and valid, next validate revision
s_chipRevision = ((((UINT64)firmwareV2) << 32) | (firmwareV1));
if ((s_chipRevision != *(UINT64*)&(s_NV[NV_CHIP_REVISION_OFFSET]))) {
// Failure to validate revision, re-init.
memset(s_NV, 0, NV_CHIP_MEMORY_SIZE);
// Dirty the block map, we're going to re-init.
s_blockMap = NV_DIRTY_ALL;
// Init with proper revision
s_chipRevision = ((((UINT64)firmwareV2) << 32) | (firmwareV1));
*(UINT64*)&(s_NV[NV_CHIP_REVISION_OFFSET]) = s_chipRevision;
#ifdef fTPMDebug
DMSG("Failed to validate revision.");
#endif
// Force (re)manufacture.
s_NVChipFileNeedsManufacture = TRUE;
// Need to re-initialize
initialized = FALSE;
return;
}
s_NVInitialized = initialized;
return;
Error:
s_NVInitialized = FALSE;
for (i = 0; i < NV_BLOCK_COUNT; i++) {
if (IS_VALID(s_NVStore[i])) {
TEE_CloseObject(s_NVStore[i]);
s_NVStore[i] = TEE_HANDLE_NULL;
}
}
return;
}
static void
_plat__NvWriteBack()
{
UINT32 i;
UINT32 objID;
TEE_Result Result;
// Exit if no dirty blocks.
if ((!s_blockMap) || (!s_NVInitialized)) {
return;
}
#ifdef fTPMDebug
DMSG("bMap: 0x%x\n", s_blockMap);
#endif
// Write dirty blocks.
for (i = 0; i < NV_BLOCK_COUNT; i++) {
if ((s_blockMap & (0x1ULL << i))) {
// Form storage object ID for this block.
objID = s_StorageObjectID + i;
// Open TEE persistent storage object: shall not fail
Result = TEE_OpenPersistentObject(CHOOSEN_TEE_STORAGE,
(void *)&objID, sizeof(objID),
TA_STORAGE_FLAGS,
&s_NVStore[i]);
if (Result != TEE_SUCCESS) {
goto Error;
}
// Move data position associated with handle to start of block.
Result = TEE_SeekObjectData(s_NVStore[i], 0, TEE_DATA_SEEK_SET);
if (Result != TEE_SUCCESS) {
goto Error;
}
// Write out this block.
Result = TEE_WriteObjectData(s_NVStore[i],
(void *)&(s_NV[i * NV_BLOCK_SIZE]),
NV_BLOCK_SIZE);
if (Result != TEE_SUCCESS) {
goto Error;
}
// Close file to not waste secure resource in the dear TEE
TEE_CloseObject(s_NVStore[i]);
s_NVStore[i] = TEE_HANDLE_NULL;
// Clear dirty bit.
s_blockMap &= ~(0x1ULL << i);
}
}
return;
Error:
// Error path.
#ifdef fTPMDebug
DMSG("NV write back failed");
#endif
s_NVInitialized = FALSE;
for (i = 0; i < NV_BLOCK_COUNT; i++) {
if (IS_VALID(s_NVStore[i])) {
TEE_CloseObject(s_NVStore[i]);
s_NVStore[i] = TEE_HANDLE_NULL;
}
}
return;
}
BOOL
_plat__NvNeedsManufacture()
{
return s_NVChipFileNeedsManufacture;
}
//***_plat__NVEnable()
// Enable NV memory.
//
// This version just pulls in data from a file. In a real TPM, with NV on chip,
// this function would verify the integrity of the saved context. If the NV
// memory was not on chip but was in something like RPMB, the NV state would be
// read in, decrypted and integrity checked.
//
// The recovery from an integrity failure depends on where the error occurred. It
// it was in the state that is discarded by TPM Reset, then the error is
// recoverable if the TPM is reset. Otherwise, the TPM must go into failure mode.
// return type: int
// 0 if success
// > 0 if receive recoverable error
// <0 if unrecoverable error
LIB_EXPORT int
_plat__NVEnable(
void *platParameter // IN: platform specific parameters
)
{
UNREFERENCED_PARAMETER(platParameter);
DMSG("_plat__NVEnable()");
UINT32 retVal = 0;
UINT32 firmwareV1 = FIRMWARE_V1;
UINT32 firmwareV2 = FIRMWARE_V2;
// Don't re-open the backing store.
if (s_NVInitialized) {
return 0;
}
// Clear NV
memset(s_NV, 0, NV_CHIP_MEMORY_SIZE);
// Prepare for potential failure to retreieve NV from storage
s_chipRevision = ((((UINT64)firmwareV2) << 32) | (firmwareV1));
*(UINT64*)&(s_NV[NV_CHIP_REVISION_OFFSET]) = s_chipRevision;
// Pick up our NV memory.
_plat__NvInitFromStorage();
// Were we successful?
if (!s_NVInitialized) {
// Arriving here means one of two things: Either there existed no
// NV state before we came along and we just (re)initialized our
// storage. Or there is an error condition preventing us from
// accessing storage. Check which is the case.
if (s_NVChipFileNeedsManufacture == FALSE) {
// This condition means we cannot access storage. However, it
// isn't up to the platform layer to decide what to do in this
// case. The decision to proceed is made in the fTPM init code
// in TA_CreateEntryPoint. Here, we're going to make sure that,
// should we decide not to just TEE_Panic, we can continue
// execution after (re)manufacture. Later an attempt at re-init
// can be made by calling _plat__NvInitFromStorage again.
retVal = 0;
}
else {
retVal = 1;
}
// Going to manufacture, zero flags
g_chipFlags.flags = 0;
// Save flags
_admin__SaveChipFlags();
// Now we're done
s_NVInitialized = TRUE;
return retVal;
}
else {
// In the transition out of UEFI to Windows, we may not tear down
// the TA. We close out one session and start another. This means
// our s_NVChipFileNeedsManufacture flag, if set, will be stale.
// Make sure we don't re-manufacture.
s_NVChipFileNeedsManufacture = FALSE;
// We successfully initialized NV now pickup TPM state.
_admin__RestoreChipFlags();
// Success
retVal = 1;
}
return retVal;
}
//***_plat__NVDisable()
// Disable NV memory
LIB_EXPORT void
_plat__NVDisable(
void
)
{
UINT32 i;
if (!s_NVInitialized) {
return;
}
// Final write
_plat__NvWriteBack();
// Close out all handles
for (i = 0; i < NV_BLOCK_COUNT; i++) {
if (IS_VALID(s_NVStore[i])) {
TEE_CloseObject(s_NVStore[i]);
s_NVStore[i] = TEE_HANDLE_NULL;
}
}
// We're no longer init-ed
s_NVInitialized = FALSE;
return;
}
//***_plat__IsNvAvailable()
// Check if NV is available
// return type: int
// 0 NV is available
// 1 NV is not available due to write failure
// 2 NV is not available due to rate limit
LIB_EXPORT int
_plat__IsNvAvailable(
void
)
{
// This is not enabled for OpTEE TA. Storage is always available.
return 0;
}
//***_plat__NvMemoryRead()
// Function: Read a chunk of NV memory
LIB_EXPORT void
_plat__NvMemoryRead(
unsigned int startOffset, // IN: read start
unsigned int size, // IN: size of bytes to read
void *data // OUT: data buffer
)
{
pAssert((startOffset + size) <= NV_CHIP_MEMORY_SIZE);
pAssert(s_NV != NULL);
memcpy(data, &s_NV[startOffset], size);
}
//*** _plat__NvIsDifferent()
// This function checks to see if the NV is different from the test value. This is
// so that NV will not be written if it has not changed.
// return value: int
// TRUE(1) the NV location is different from the test value
// FALSE(0) the NV location is the same as the test value
LIB_EXPORT int
_plat__NvIsDifferent(
unsigned int startOffset, // IN: read start
unsigned int size, // IN: size of bytes to read
void *data // IN: data buffer
)
{
return (memcmp(&s_NV[startOffset], data, size) != 0);
}
static
void
_plat__MarkDirtyBlocks (
unsigned int startOffset,
unsigned int size
)
{
unsigned int blockEnd;
unsigned int blockStart;
unsigned int i;
//
// Integer math will round down to the start of the block.
// blockEnd is actually the last block + 1.
//
blockStart = startOffset / NV_BLOCK_SIZE;
blockEnd = (startOffset + size) / NV_BLOCK_SIZE;
if ((startOffset + size) % NV_BLOCK_SIZE != 0) {
blockEnd += 1;
}
for (i = blockStart; i < blockEnd; i++) {
s_blockMap |= (0x1ULL << i);
}
}
//***_plat__NvMemoryWrite()
// This function is used to update NV memory. The "write" is to a memory copy of
// NV. At the end of the current command, any changes are written to
// the actual NV memory.
// NOTE: A useful optimization would be for this code to compare the current
// contents of NV with the local copy and note the blocks that have changed. Then
// only write those blocks when _plat__NvCommit() is called.
LIB_EXPORT void
_plat__NvMemoryWrite(
unsigned int startOffset, // IN: write start
unsigned int size, // IN: size of bytes to write
void *data // OUT: data buffer
)
{
pAssert(startOffset + size <= NV_CHIP_MEMORY_SIZE);
pAssert(s_NV != NULL);
_plat__MarkDirtyBlocks(startOffset, size);
memcpy(&s_NV[startOffset], data, size);
}
//***_plat__NvMemoryClear()
// Function is used to set a range of NV memory bytes to an implementation-dependent
// value. The value represents the erase state of the memory.
LIB_EXPORT void
_plat__NvMemoryClear(
unsigned int start, // IN: clear start
unsigned int size // IN: number of bytes to clear
)
{
pAssert(start + size <= NV_MEMORY_SIZE);
_plat__MarkDirtyBlocks(start, size);
memset(&s_NV[start], 0, size);
}
//***_plat__NvMemoryMove()
// Function: Move a chunk of NV memory from source to destination
// This function should ensure that if there overlap, the original data is
// copied before it is written
LIB_EXPORT void
_plat__NvMemoryMove(
unsigned int sourceOffset, // IN: source offset
unsigned int destOffset, // IN: destination offset
unsigned int size // IN: size of data being moved
)
{
pAssert(sourceOffset + size <= NV_CHIP_MEMORY_SIZE);
pAssert(destOffset + size <= NV_CHIP_MEMORY_SIZE);
pAssert(s_NV != NULL);
_plat__MarkDirtyBlocks(sourceOffset, size);
_plat__MarkDirtyBlocks(destOffset, size);
memmove(&s_NV[destOffset], &s_NV[sourceOffset], size);
}
//***_plat__NvCommit()
// This function writes the local copy of NV to NV for permanent store. It will write
// NV_MEMORY_SIZE bytes to NV. If a file is use, the entire file is written.
// return type: int
// 0 NV write success
// non-0 NV write fail
LIB_EXPORT int
_plat__NvCommit(
void
)
{
_plat__NvWriteBack();
return 0;
}
//***_plat__SetNvAvail()
// Set the current NV state to available. This function is for testing purpose
// only. It is not part of the platform NV logic
LIB_EXPORT void
_plat__SetNvAvail(
void
)
{
// NV will not be made unavailable on this platform
return;
}
//***_plat__ClearNvAvail()
// Set the current NV state to unavailable. This function is for testing purpose
// only. It is not part of the platform NV logic
LIB_EXPORT void
_plat__ClearNvAvail(
void
)
{
// The anti-set; not on this platform.
return;
}