blob: 9a0a9f17b30f0c59fc4c104f74f955a0fc663cb4 [file] [log] [blame]
/*
* SPDX-License-Identifier: Apache-2.0
* SPDX-FileCopyrightText: Copyright The Mbed TLS Contributors
* SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <arch_helpers.h>
#include <assert.h>
#include <cpuid.h>
#include <debug.h>
#include <errno.h>
#include <mbedtls/memory_buffer_alloc.h>
#include <mbedtls/platform.h>
#include <memory_alloc.h>
#include <sizes.h>
#include <string.h>
#define MAGIC1 UL(0xFF00AA55)
#define MAGIC2 UL(0xEE119966)
#define MAX_BT 20
#if defined(MBEDTLS_MEMORY_DEBUG)
#error MBEDTLS_MEMORY_DEBUG is not supported by this allocator.
#endif
#if defined(MBEDTLS_MEMORY_BUFFER_ALLOC_C)
/*
* If MBEDTLS_MEMORY_BUFFER_ALLOC_C is defined then the allocator from mbedTLS
* is going to be used, which is not desired.
*/
#error MBEDTLS_MEMORY_BUFFER_ALLOC_C is defined
#endif /* MBEDTLS_MEMORY_BUFFER_ALLOC_C */
#if defined(MBEDTLS_MEMORY_BACKTRACE)
#error MBEDTLS_MEMORY_BACKTRACE is not supported by this allocator.
#endif /* MBEDTLS_MEMORY_BACKTRACE */
#if defined(MBEDTLS_THREADING_C)
/*
* This allocator doesn't support multithreading. On the other hand it
* handles multiple heaps
*/
#error MBEDTLS_THREADING_C is not supported by this allocator.
#endif /* MBEDTLS_THREADING_C */
#if defined(MBEDTLS_SELF_TEST)
#error MBEDTLS_SELF_TEST is not supported by this allocator.
#endif /* MBEDTLS_SELF_TEST */
/* Array of heaps per CPU */
static struct buffer_alloc_ctx *ctx_per_cpu[MAX_CPUS];
static inline struct buffer_alloc_ctx *get_heap_ctx(void)
{
struct buffer_alloc_ctx *ctx;
unsigned int cpu_id = my_cpuid();
assert(cpu_id < MAX_CPUS);
ctx = ctx_per_cpu[cpu_id];
/* Programming error if heap is not assigned */
if (ctx == NULL) {
ERROR(" No heap assigned to this CPU %u\n", cpu_id);
panic();
}
return ctx;
}
static int verify_header(struct memory_header_s *hdr)
{
if (hdr->magic1 != MAGIC1) {
return 1;
}
if (hdr->magic2 != MAGIC2) {
return 1;
}
if (hdr->alloc > 1UL) {
return 1;
}
if ((hdr->prev != NULL) && (hdr->prev == hdr->next)) {
return 1;
}
if ((hdr->prev_free != NULL) && (hdr->prev_free == hdr->next_free)) {
return 1;
}
return 0;
}
static int verify_chain(struct buffer_alloc_ctx *heap)
{
struct memory_header_s *prv = heap->first;
struct memory_header_s *cur;
if ((prv == NULL) || (verify_header(prv) != 0)) {
return 1;
}
if (heap->first->prev != NULL) {
return 1;
}
cur = heap->first->next;
while (cur != NULL) {
if (verify_header(cur) != 0) {
return 1;
}
if (cur->prev != prv) {
return 1;
}
prv = cur;
cur = cur->next;
}
return 0;
}
static void *buffer_alloc_calloc_with_heap(struct buffer_alloc_ctx *heap,
size_t n,
size_t size)
{
struct memory_header_s *new;
struct memory_header_s *cur = heap->first_free;
unsigned char *p;
void *ret;
size_t original_len;
size_t len;
if ((heap->buf == NULL) || (heap->first == NULL)) {
return NULL;
}
original_len = n * size;
len = original_len;
if ((n == 0UL) || (size == 0UL) || ((len / n) != size)) {
return NULL;
} else if (len > ((size_t)-MBEDTLS_MEMORY_ALIGN_MULTIPLE)) {
return NULL;
}
if ((len % MBEDTLS_MEMORY_ALIGN_MULTIPLE) != 0) {
len -= len % MBEDTLS_MEMORY_ALIGN_MULTIPLE;
len += MBEDTLS_MEMORY_ALIGN_MULTIPLE;
}
/* Find block that fits */
while (cur != NULL) {
if (cur->size >= len) {
break;
}
cur = cur->next_free;
}
if (cur == NULL) {
return NULL;
}
if (cur->alloc != 0UL) {
assert(false);
}
/* Found location, split block if > memory_header + 4 room left */
if ((cur->size - len) <
(sizeof(struct memory_header_s) + MBEDTLS_MEMORY_ALIGN_MULTIPLE)) {
cur->alloc = 1UL;
/* Remove from free_list */
if (cur->prev_free != NULL) {
cur->prev_free->next_free = cur->next_free;
} else {
heap->first_free = cur->next_free;
}
if (cur->next_free != NULL) {
cur->next_free->prev_free = cur->prev_free;
}
cur->prev_free = NULL;
cur->next_free = NULL;
if ((heap->verify & MBEDTLS_MEMORY_VERIFY_ALLOC) != 0) {
assert(verify_chain(heap) == 0);
}
ret = (unsigned char *) cur + sizeof(struct memory_header_s);
(void)memset(ret, 0, original_len);
return ret;
}
p = ((unsigned char *) cur) + sizeof(struct memory_header_s) + len;
new = (struct memory_header_s *) p;
new->size = cur->size - len - sizeof(struct memory_header_s);
new->alloc = 0;
new->prev = cur;
new->next = cur->next;
new->magic1 = MAGIC1;
new->magic2 = MAGIC2;
if (new->next != NULL) {
new->next->prev = new;
}
/* Replace cur with new in free_list */
new->prev_free = cur->prev_free;
new->next_free = cur->next_free;
if (new->prev_free != NULL) {
new->prev_free->next_free = new;
} else {
heap->first_free = new;
}
if (new->next_free != NULL) {
new->next_free->prev_free = new;
}
cur->alloc = 1;
cur->size = len;
cur->next = new;
cur->prev_free = NULL;
cur->next_free = NULL;
if ((heap->verify & MBEDTLS_MEMORY_VERIFY_ALLOC) != 0) {
assert(verify_chain(heap) == 0);
}
ret = (unsigned char *) cur + sizeof(struct memory_header_s);
(void)memset(ret, 0, original_len);
return ret;
}
void *buffer_alloc_calloc(size_t n, size_t size)
{
struct buffer_alloc_ctx *heap = get_heap_ctx();
assert(heap);
return buffer_alloc_calloc_with_heap(heap, n, size);
}
static void buffer_alloc_free_with_heap(struct buffer_alloc_ctx *heap,
void *ptr)
{
struct memory_header_s *hdr;
struct memory_header_s *old = NULL;
unsigned char *p = (unsigned char *) ptr;
if ((ptr == NULL) || (heap->buf == NULL) || (heap->first == NULL)) {
return;
}
if ((p < heap->buf) || (p >= (heap->buf + heap->len))) {
assert(0);
}
p -= sizeof(struct memory_header_s);
hdr = (struct memory_header_s *) p;
assert(verify_header(hdr) == 0);
if (hdr->alloc != 1) {
assert(0);
}
hdr->alloc = 0;
/* Regroup with block before */
if ((hdr->prev != NULL) && (hdr->prev->alloc == 0UL)) {
hdr->prev->size += sizeof(struct memory_header_s) + hdr->size;
hdr->prev->next = hdr->next;
old = hdr;
hdr = hdr->prev;
if (hdr->next != NULL) {
hdr->next->prev = hdr;
}
(void)memset(old, 0, sizeof(struct memory_header_s));
}
/* Regroup with block after */
if ((hdr->next != NULL) && (hdr->next->alloc == 0UL)) {
hdr->size += sizeof(struct memory_header_s) + hdr->next->size;
old = hdr->next;
hdr->next = hdr->next->next;
if ((hdr->prev_free != NULL) || (hdr->next_free != NULL)) {
if (hdr->prev_free != NULL) {
hdr->prev_free->next_free = hdr->next_free;
} else {
heap->first_free = hdr->next_free;
}
if (hdr->next_free != NULL) {
hdr->next_free->prev_free = hdr->prev_free;
}
}
hdr->prev_free = old->prev_free;
hdr->next_free = old->next_free;
if (hdr->prev_free != NULL) {
hdr->prev_free->next_free = hdr;
} else {
heap->first_free = hdr;
}
if (hdr->next_free != NULL) {
hdr->next_free->prev_free = hdr;
}
if (hdr->next != NULL) {
hdr->next->prev = hdr;
}
(void)memset(old, 0, sizeof(struct memory_header_s));
}
/*
* Prepend to free_list if we have not merged
* (Does not have to stay in same order as prev / next list)
*/
if (old == NULL) {
hdr->next_free = heap->first_free;
if (heap->first_free != NULL) {
heap->first_free->prev_free = hdr;
}
heap->first_free = hdr;
}
if ((heap->verify & MBEDTLS_MEMORY_VERIFY_FREE) != 0) {
assert(verify_chain(heap));
}
}
void buffer_alloc_free(void *ptr)
{
struct buffer_alloc_ctx *heap = get_heap_ctx();
assert(heap);
buffer_alloc_free_with_heap(heap, ptr);
}
int buffer_alloc_ctx_assign(struct buffer_alloc_ctx *ctx)
{
unsigned int cpuid = my_cpuid();
assert(cpuid < MAX_CPUS);
if (ctx == NULL) {
return -EINVAL;
}
if (ctx_per_cpu[cpuid] != NULL) {
/* multiple assign */
return -EINVAL;
}
ctx_per_cpu[cpuid] = ctx;
return 0;
}
void buffer_alloc_ctx_unassign(void)
{
unsigned int cpuid = my_cpuid();
assert(cpuid < MAX_CPUS);
/* multiple unassign */
assert(ctx_per_cpu[cpuid] != NULL);
ctx_per_cpu[cpuid] = NULL;
}
/* NOTE: This function is not currently expected to be called. */
void mbedtls_memory_buffer_set_verify(int verify)
{
struct buffer_alloc_ctx *heap = get_heap_ctx();
assert(heap);
heap->verify = verify;
}
int mbedtls_memory_buffer_alloc_verify(void)
{
struct buffer_alloc_ctx *heap = get_heap_ctx();
assert(heap);
return verify_chain(heap);
}
void mbedtls_memory_buffer_alloc_init(unsigned char *buf, size_t len)
{
/* The heap structure is obtained from the REC
* while the buffer is passed in the init function.
* This way the interface can remain the same.
*/
struct buffer_alloc_ctx *heap = get_heap_ctx();
assert(heap);
(void)memset(heap, 0, sizeof(struct buffer_alloc_ctx));
if (len < sizeof(struct memory_header_s) +
MBEDTLS_MEMORY_ALIGN_MULTIPLE) {
return;
} else if (((size_t)buf % MBEDTLS_MEMORY_ALIGN_MULTIPLE) != 0U) {
/* Adjust len first since buf is used in the computation */
len -= MBEDTLS_MEMORY_ALIGN_MULTIPLE
- ((size_t)buf % MBEDTLS_MEMORY_ALIGN_MULTIPLE);
buf += MBEDTLS_MEMORY_ALIGN_MULTIPLE
- ((size_t)buf % MBEDTLS_MEMORY_ALIGN_MULTIPLE);
}
(void)memset(buf, 0, len);
heap->buf = buf;
heap->len = len;
heap->first = (struct memory_header_s *)buf;
heap->first->size = len - sizeof(struct memory_header_s);
heap->first->magic1 = MAGIC1;
heap->first->magic2 = MAGIC2;
heap->first_free = heap->first;
}
void mbedtls_memory_buffer_alloc_free(void)
{
struct buffer_alloc_ctx *heap = get_heap_ctx();
assert(heap);
(void)memset(heap, 0, sizeof(struct buffer_alloc_ctx));
}