/* Copyright 2019 The Chromium OS Authors. All rights reserved. * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ /* Stuff from tpm2 directory. */ #include "nvmem_test.h" #include "console.h" #include "nvmem.h" #include "util.h" #define NVMEM_CR50_SIZE 272 uint32_t s_evictNvStart; uint32_t s_evictNvEnd; /* Calculate size of TPM NVMEM. */ #define MOCK_NV_MEMORY_SIZE \ (NVMEM_PARTITION_SIZE - sizeof(struct nvmem_tag) - NVMEM_CR50_SIZE) uint32_t nvmem_user_sizes[NVMEM_NUM_USERS] = {MOCK_NV_MEMORY_SIZE, NVMEM_CR50_SIZE}; /* * Sizes of the reserved objects stored in the TPM NVMEM. Note that the second * last object is in fact a variable size field starting with 4 bytes of size * and then up to 512 bytes of actual index data. The array below assumes that * the full 512 bytes of the index space are used. */ const uint16_t res_sizes[] = {4, 2, 2, 2, 66, 66, 66, 66, 66, 66, 34, 34, 34, 66, 66, 66, 8, 4, 134, 28, 3, 4, 4, 4, 4, 4, 2, 15, 2, 8, 4, 4, 4, 96, 2844, 424, 516, 8}; static uint16_t res_addrs[ARRAY_SIZE(res_sizes)]; BOOL NvEarlyStageFindHandle(TPM_HANDLE handle) { size_t i; res_addrs[0] = 0; for (i = 1; i < ARRAY_SIZE(res_addrs); i++) res_addrs[i] = res_addrs[i - 1] + res_sizes[i - 1]; s_evictNvStart = res_addrs[i - 1] + res_sizes[i - 1]; s_evictNvEnd = MOCK_NV_MEMORY_SIZE; return 0; } void NvGetReserved(UINT32 index, NV_RESERVED_ITEM *ri) { uint32_t index_size; if (index >= ARRAY_SIZE(res_sizes)) { ri->size = 0; return; } ri->offset = res_addrs[index]; if (index != NV_RAM_INDEX_SPACE) { ri->size = res_sizes[index]; return; } memcpy(&index_size, nvmem_cache_base(NVMEM_TPM) + ri->offset, sizeof(index_size)); if (index_size == ~0) /* Must be starting with empty flash memeory. */ index_size = 0; ri->size = index_size + sizeof(index_size); } UINT16 UINT16_Marshal(UINT16 *source, BYTE **buffer, INT32 *size) { uint16_t value; if (!size || (*size < sizeof(value))) return 0; value = htobe16(*source); memcpy(*buffer, &value, sizeof(value)); *buffer += sizeof(value); *size -= sizeof(value); return sizeof(value); } UINT16 UINT32_Marshal(UINT32 *source, BYTE **buffer, INT32 *size) { uint32_t value; if (!size || (*size < sizeof(value))) return 0; value = htobe32(*source); memcpy(*buffer, &value, sizeof(value)); *buffer += sizeof(value); *size -= sizeof(value); return sizeof(value); } UINT16 UINT64_Marshal(UINT64 *source, BYTE **buffer, INT32 *size) { uint64_t value; if (!size || (*size < sizeof(value))) return 0; value = htobe64(*source); memcpy(*buffer, &value, sizeof(value)); *buffer += sizeof(value); *size -= sizeof(value); return sizeof(value); } UINT16 TPM2B_DIGEST_Marshal(TPM2B_DIGEST *source, BYTE **buffer, INT32 *size) { UINT16 total_size; INT32 i; uint8_t *p; total_size = UINT16_Marshal(&source->t.size, buffer, size); p = *buffer; for (i = 0; (i < source->t.size) && *size; ++i) { *p++ = source->t.buffer[i]; *size -= 1; } total_size += i; *buffer = p; return total_size; } uint16_t TPM2B_AUTH_Marshal(TPM2B_AUTH *source, BYTE **buffer, INT32 *size) { return TPM2B_DIGEST_Marshal(source, buffer, size); } uint16_t TPM2B_NONCE_Marshal(TPM2B_AUTH *source, BYTE **buffer, INT32 *size) { return TPM2B_DIGEST_Marshal(source, buffer, size); } TPM_RC UINT16_Unmarshal(UINT16 *target, BYTE **buffer, INT32 *size) { uint16_t value; if (!size || *size < sizeof(value)) return TPM_RC_INSUFFICIENT; memcpy(&value, *buffer, sizeof(value)); *target = be16toh(value); *buffer += sizeof(value); *size -= sizeof(value); return TPM_RC_SUCCESS; } TPM_RC UINT32_Unmarshal(UINT32 *target, BYTE **buffer, INT32 *size) { uint32_t value; if (!size || *size < sizeof(value)) return TPM_RC_INSUFFICIENT; memcpy(&value, *buffer, sizeof(value)); *target = be32toh(value); *buffer += sizeof(value); *size -= sizeof(value); return TPM_RC_SUCCESS; } TPM_RC UINT64_Unmarshal(UINT64 *target, BYTE **buffer, INT32 *size) { uint64_t value; if (!size || *size < sizeof(value)) return TPM_RC_INSUFFICIENT; memcpy(&value, *buffer, sizeof(value)); *target = be64toh(value); *buffer += sizeof(value); *size -= sizeof(value); return TPM_RC_SUCCESS; } TPM_RC TPM2B_DIGEST_Unmarshal(TPM2B_DIGEST *target, BYTE **buffer, INT32 *size) { TPM_RC result; INT32 i; uint8_t *p; result = UINT16_Unmarshal(&target->t.size, buffer, size); if (result != TPM_RC_SUCCESS) return result; if (target->t.size == 0) return TPM_RC_SUCCESS; if ((target->t.size > sizeof(TPMU_HA)) || (target->t.size > *size)) return TPM_RC_SIZE; p = *buffer; for (i = 0; i < target->t.size; ++i) target->t.buffer[i] = *p++; *buffer = p; *size -= i; return TPM_RC_SUCCESS; } TPM_RC TPM2B_AUTH_Unmarshal(TPM2B_AUTH *target, BYTE **buffer, INT32 *size) { return TPM2B_DIGEST_Unmarshal(target, buffer, size); } TPM_RC TPM2B_NONCE_Unmarshal(TPM2B_AUTH *target, BYTE **buffer, INT32 *size) { return TPM2B_DIGEST_Unmarshal(target, buffer, size); } #define ITER_INIT (~0) static void *get_cache_addr(size_t offset) { return (void *)(((uintptr_t)nvmem_cache_base(NVMEM_TPM)) + offset); } static void read_from_cache(size_t offset, size_t size, void *dest) { nvmem_read(offset, size, dest, NVMEM_TPM); } static void write_to_cache(size_t offset, size_t size, void *src) { nvmem_write(offset, size, src, NVMEM_TPM); } /* Copies of the appropriate functions from NV.c in TPM2 library. */ static uint32_t nv_next(uint32_t *iter) { uint32_t currentIter; if (*iter == ITER_INIT) *iter = s_evictNvStart; if ((*iter + sizeof(uint32_t) > s_evictNvEnd) || !*iter) return 0; currentIter = *iter; read_from_cache(*iter, sizeof(uint32_t), iter); if (!*iter || (*iter == ITER_INIT)) return 0; return currentIter + sizeof(uint32_t); } static uint32_t nv_get_end(void) { uint32_t iter = ITER_INIT; uint32_t endAddr = s_evictNvStart; uint32_t currentAddr; while ((currentAddr = nv_next(&iter)) != 0) endAddr = currentAddr; if (endAddr != s_evictNvStart) { /* Read offset. */ endAddr -= sizeof(uint32_t); read_from_cache(endAddr, sizeof(uint32_t), &endAddr); } return endAddr; } size_t add_evictable_obj(void *obj, size_t obj_size) { uint32_t end_addr; uint32_t next_addr; uint32_t list_end = 0; end_addr = nv_get_end(); next_addr = end_addr + sizeof(uint32_t) + obj_size; if (next_addr >= s_evictNvEnd) { ccprintf("%s: could not fit %zd bytes!\n", __func__, obj_size); return 0; } /* Write next pointer */ write_to_cache(end_addr, sizeof(uint32_t), &next_addr); /* Write entity data. */ write_to_cache(end_addr + sizeof(uint32_t), obj_size, obj); /* Write the end of list if it fits. */ if (next_addr + sizeof(uint32_t) <= s_evictNvEnd) write_to_cache(next_addr, sizeof(list_end), &list_end); return obj_size; } /* * It is the responsibility of the caller to pass the proper address of an * object in the cache. */ void drop_evictable_obj(void *obj) { uint32_t next_addr; uint32_t list_end = 0; uint32_t obj_addr; obj_addr = (uintptr_t)obj - (uintptr_t)nvmem_cache_base(NVMEM_TPM); read_from_cache(obj_addr - sizeof(next_addr), sizeof(next_addr), &next_addr); ccprintf("%s:%d dropping obj at cache addr %x, offset %x, addr %pP " "next addr %x aka %x (off s_evictNvStart)\n", __func__, __LINE__, obj_addr - s_evictNvStart, obj_addr, obj, next_addr, next_addr - s_evictNvStart); /* * Now, to make it easier to add objects behind the current one, let's * pretend there is no more objects. */ write_to_cache(obj_addr - sizeof(next_addr), sizeof(list_end), &list_end); if (!next_addr || (next_addr == s_evictNvEnd)) return; /* * Iterate over objects starting with next_addr, copying them into * obj_addr. */ obj_addr = next_addr; while (1) { uint32_t next_next_addr; uint32_t next_obj_size; read_from_cache(next_addr, sizeof(next_next_addr), &next_next_addr); if (!next_next_addr || (next_next_addr == s_evictNvEnd)) return; next_obj_size = next_next_addr - obj_addr - sizeof(uint32_t); add_evictable_obj( (void *)((uintptr_t)nvmem_cache_base(NVMEM_TPM) + next_addr + sizeof(uint32_t)), next_obj_size); next_addr = next_next_addr; obj_addr += next_obj_size + sizeof(next_obj_size); } } void *evictable_offs_to_addr(uint16_t offset) { return (void *)((uintptr_t)get_cache_addr(s_evictNvStart) + offset); }