blob: 561856f837f5c8acfa481fea347751d13f9d5f74 [file] [log] [blame]
// SPDX-License-Identifier: BSD-2-Clause
/*
* Copyright (c) 2017, Linaro Limited
*/
#include <crypto/crypto.h>
#include <initcall.h>
#include <kernel/early_ta.h>
#include <kernel/linker.h>
#include <kernel/user_ta.h>
#include <kernel/user_ta_store.h>
#include <stdio.h>
#include <string.h>
#include <trace.h>
#include <utee_defines.h>
#include <util.h>
#include <zlib.h>
struct user_ta_store_handle {
const struct early_ta *early_ta;
size_t offs;
z_stream strm;
};
#define for_each_early_ta(_ta) \
for (_ta = &__rodata_early_ta_start; _ta < &__rodata_early_ta_end; \
_ta = (const struct early_ta *) \
ROUNDUP((vaddr_t)_ta + sizeof(*_ta) + _ta->size, \
__alignof__(struct early_ta)))
static const struct early_ta *find_early_ta(const TEE_UUID *uuid)
{
const struct early_ta *ta;
for_each_early_ta(ta)
if (!memcmp(&ta->uuid, uuid, sizeof(*uuid)))
return ta;
return NULL;
}
static void *zalloc(void *opaque __unused, unsigned int items,
unsigned int size)
{
return malloc(items * size);
}
static void zfree(void *opaque __unused, void *address)
{
free(address);
}
static bool decompression_init(z_stream *strm,
const struct early_ta *ta)
{
int st;
strm->next_in = ta->ta;
strm->avail_in = ta->size;
strm->zalloc = zalloc;
strm->zfree = zfree;
st = inflateInit(strm);
if (st != Z_OK) {
EMSG("Decompression initialization error (%d)", st);
return false;
}
return true;
}
static TEE_Result early_ta_open(const TEE_UUID *uuid,
struct user_ta_store_handle **h)
{
struct user_ta_store_handle *handle;
const struct early_ta *ta;
bool st;
ta = find_early_ta(uuid);
if (!ta)
return TEE_ERROR_ITEM_NOT_FOUND;
handle = calloc(1, sizeof(*handle));
if (!handle)
return TEE_ERROR_OUT_OF_MEMORY;
if (ta->uncompressed_size) {
st = decompression_init(&handle->strm, ta);
if (!st) {
free(handle);
return TEE_ERROR_BAD_FORMAT;
}
}
handle->early_ta = ta;
*h = handle;
return TEE_SUCCESS;
}
static TEE_Result early_ta_get_size(const struct user_ta_store_handle *h,
size_t *size)
{
const struct early_ta *ta = h->early_ta;
if (ta->uncompressed_size)
*size = ta->uncompressed_size;
else
*size = ta->size;
return TEE_SUCCESS;
}
static TEE_Result early_ta_get_tag(const struct user_ta_store_handle *h,
uint8_t *tag, unsigned int *tag_len)
{
TEE_Result res = TEE_SUCCESS;
void *ctx = NULL;
if (!tag || *tag_len < TEE_SHA256_HASH_SIZE) {
*tag_len = TEE_SHA256_HASH_SIZE;
return TEE_ERROR_SHORT_BUFFER;
}
*tag_len = TEE_SHA256_HASH_SIZE;
res = crypto_hash_alloc_ctx(&ctx, TEE_ALG_SHA256);
if (res)
return res;
res = crypto_hash_init(ctx);
if (res)
goto out;
res = crypto_hash_update(ctx, h->early_ta->ta, h->early_ta->size);
if (res)
goto out;
res = crypto_hash_final(ctx, tag, *tag_len);
out:
crypto_hash_free_ctx(ctx);
return res;
}
static TEE_Result read_uncompressed(struct user_ta_store_handle *h, void *data,
size_t len)
{
uint8_t *src = (uint8_t *)h->early_ta->ta + h->offs;
if (h->offs + len > h->early_ta->size)
return TEE_ERROR_BAD_PARAMETERS;
if (data)
memcpy(data, src, len);
h->offs += len;
return TEE_SUCCESS;
}
static TEE_Result read_compressed(struct user_ta_store_handle *h, void *data,
size_t len)
{
z_stream *strm = &h->strm;
size_t total = 0;
uint8_t *tmpbuf = NULL;
TEE_Result ret;
size_t out;
int st;
if (data) {
strm->next_out = data;
strm->avail_out = len;
} else {
/*
* inflate() does not support a NULL strm->next_out. So, to
* discard data, we have to allocate a temporary buffer. 1K
* seems reasonable.
*/
strm->avail_out = MIN(len, 1024U);
tmpbuf = malloc(strm->avail_out);
if (!tmpbuf) {
EMSG("Out of memory");
return TEE_ERROR_OUT_OF_MEMORY;
}
strm->next_out = tmpbuf;
}
/*
* Loop until we get as many bytes as requested, or an error occurs.
* inflate() returns:
* - Z_OK when progress was made, but neither the end of the input
* stream nor the end of the output buffer were met.
* - Z_STREAM_END when the end of the intput stream was reached.
* - Z_BUF_ERROR when there is still input to process but the output
* buffer is full (not a "hard" error, decompression can proceeed
* later).
*/
do {
out = strm->total_out;
st = inflate(strm, Z_SYNC_FLUSH);
out = strm->total_out - out;
total += out;
FMSG("%zu bytes", out);
if (!data) {
/*
* Reset the pointer to throw away what we've just read
* and read again as much as possible.
*/
strm->next_out = tmpbuf;
strm->avail_out = MIN(len - total, 1024U);
}
} while ((st == Z_OK || st == Z_BUF_ERROR) && (total != len));
if (st != Z_OK && st != Z_STREAM_END) {
EMSG("Decompression error (%d)", st);
ret = TEE_ERROR_GENERIC;
goto out;
}
ret = TEE_SUCCESS;
out:
free(tmpbuf);
return ret;
}
static TEE_Result early_ta_read(struct user_ta_store_handle *h, void *data,
size_t len)
{
if (h->early_ta->uncompressed_size)
return read_compressed(h, data, len);
else
return read_uncompressed(h, data, len);
}
static void early_ta_close(struct user_ta_store_handle *h)
{
if (h->early_ta->uncompressed_size)
inflateEnd(&h->strm);
free(h);
}
TEE_TA_REGISTER_TA_STORE(2) = {
.description = "early TA",
.open = early_ta_open,
.get_size = early_ta_get_size,
.get_tag = early_ta_get_tag,
.read = early_ta_read,
.close = early_ta_close,
};
static TEE_Result early_ta_init(void)
{
const struct early_ta *ta;
char __maybe_unused msg[60] = { '\0', };
for_each_early_ta(ta) {
if (ta->uncompressed_size)
snprintf(msg, sizeof(msg),
" (compressed, uncompressed %u)",
ta->uncompressed_size);
else
msg[0] = '\0';
DMSG("Early TA %pUl size %u%s", (void *)&ta->uuid, ta->size,
msg);
}
return TEE_SUCCESS;
}
service_init(early_ta_init);