mirror of https://github.com/ARMmbed/mbed-os.git
291 lines
10 KiB
C
291 lines
10 KiB
C
/*
|
|
* Copyright (c) 2016, ARM Limited, All Rights Reserved
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*
|
|
* 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 "rtx_lib.h"
|
|
|
|
/* uVisor uses rtx_memory instead of implementing its own dynamic,
|
|
* non-fixed-size memory allocator. To do this, uVisor creates multiple
|
|
* non-fixed-size allocator pools (one per page) and allocates memory from
|
|
* these pools. uVisor must manage the memory for these pools' control blocks,
|
|
* so it must know the size of these control blocks. */
|
|
|
|
/* The following memory pool control block structs are copied from
|
|
* rtx_memory.c, so that uVisor can manage the memory for these control blocks
|
|
* within pages. */
|
|
typedef struct mem_head_s {
|
|
uint32_t size; // Memory Pool size
|
|
uint32_t used; // Used Memory
|
|
} mem_head_t;
|
|
|
|
// Memory Block Header structure
|
|
typedef struct mem_block_s {
|
|
struct mem_block_s *next; // Next Memory Block in list
|
|
uint32_t info; // Info: length = <31:2>:'00', type = <1:0>
|
|
} mem_block_t;
|
|
/* End copy */
|
|
|
|
#include "secure_allocator.h"
|
|
#include "uvisor-lib/uvisor-lib.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
/* Use printf with caution inside malloc: printf may allocate memory itself,
|
|
so using printf in malloc may lead to recursive calls! */
|
|
#define DPRINTF(...) {}
|
|
|
|
/* offsetof is a gcc built-in function, this is the manual implementation */
|
|
#define OFFSETOF(type, member) ((uint32_t) (&(((type *)(0))->member)))
|
|
|
|
/* Internal structure currently only contains the page table. */
|
|
typedef struct {
|
|
UvisorPageTable table;
|
|
} SecureAllocatorInternal;
|
|
|
|
static inline UvisorPageTable * table(SecureAllocator allocator) {
|
|
return &(((SecureAllocatorInternal *) allocator)->table);
|
|
}
|
|
|
|
SecureAllocator secure_allocator_create_with_pool(
|
|
void * mem,
|
|
size_t bytes)
|
|
{
|
|
SecureAllocatorInternal * allocator = mem;
|
|
/* Signal that this is non-page allocated memory. */
|
|
allocator->table.page_size = bytes;
|
|
allocator->table.page_count = 0;
|
|
/* The internal rtx_Memory memory pool structure must be placed AFTER
|
|
* table.page_origins[0] !!! */
|
|
size_t offset = OFFSETOF(SecureAllocatorInternal, table.page_origins) + sizeof(((UvisorPageTable) {0}).page_origins);
|
|
uintptr_t page_origin = (uintptr_t) mem + offset;
|
|
|
|
/* Align page_origin to a multiple of 8 (because RTX requries 8-byte
|
|
* alignment of the origin). */
|
|
page_origin = (page_origin + (0x8 - 1)) & -0x8;
|
|
offset = page_origin - (uintptr_t) mem;
|
|
size_t size = bytes - offset;
|
|
/* Align size to a multiple of 8 (because RTX requires 8-byte alignment of
|
|
* the size) */
|
|
size &= -0x8;
|
|
|
|
/* Create pool allocator structure inside the memory. */
|
|
if (!osRtxMemoryInit((void *) page_origin, size)) {
|
|
/* Abort if failed. */
|
|
DPRINTF("secure_allocator_create_with_pool: pool allocator %p with offset %d creation failed (size %u bytes)\n\n", page_origin, offset, bytes - offset);
|
|
return NULL;
|
|
}
|
|
/* Remember the pool allocator pointer though. */
|
|
allocator->table.page_origins[0] = (void *) page_origin;
|
|
DPRINTF("secure_allocator_create_with_pool: Created pool allocator %p with offset %d\n\n", page_origin, offset);
|
|
return allocator;
|
|
}
|
|
|
|
SecureAllocator secure_allocator_create_with_pages(
|
|
size_t size,
|
|
size_t maximum_malloc_size)
|
|
{
|
|
const uint32_t page_size = uvisor_get_page_size();
|
|
/* The rtx_Memory allocator puts one pool allocator structure at both the
|
|
* beginning and end of the memory pool. */
|
|
const size_t block_overhead = 2 * sizeof(mem_block_t);
|
|
const size_t page_size_with_overhead = page_size + block_overhead;
|
|
/* Calculate the integer part of required the page count. */
|
|
size_t page_count = size / page_size_with_overhead;
|
|
/* Add another page if the remainder is not zero. */
|
|
if (size - page_count * page_size_with_overhead) {
|
|
page_count++;
|
|
}
|
|
DPRINTF("secure_allocator_create_with_pages: Requesting %u pages for at least %uB\n", page_count, size);
|
|
|
|
/* Compute the maximum allocation within our blocks. */
|
|
size_t maximum_allocation_size = page_size - block_overhead;
|
|
/* If the required maximum allocation is larger than we can provide, abort. */
|
|
if (maximum_malloc_size > maximum_allocation_size) {
|
|
DPRINTF("secure_allocator_create_with_pages: Maximum allocation request %uB is larger then available %uB\n\n", maximum_malloc_size, maximum_allocation_size);
|
|
return NULL;
|
|
}
|
|
|
|
/* Compute the required memory size for the page table. */
|
|
size_t allocator_type_size = sizeof(SecureAllocatorInternal);
|
|
/* Add size for each additional page. */
|
|
allocator_type_size += (page_count - 1) * sizeof(((UvisorPageTable) {0}).page_origins);
|
|
/* Allocate this much memory. */
|
|
SecureAllocatorInternal * const allocator = malloc(allocator_type_size);
|
|
/* If malloc failed, abort. */
|
|
if (allocator == NULL) {
|
|
DPRINTF("secure_allocator_create_with_pages: SecureAllocatorInternal failed to be allocated!\n\n");
|
|
return NULL;
|
|
}
|
|
|
|
/* Prepare the page table. */
|
|
allocator->table.page_size = page_size;
|
|
allocator->table.page_count = page_count;
|
|
/* Get me some pages. */
|
|
if (uvisor_page_malloc((UvisorPageTable *) &(allocator->table))) {
|
|
free(allocator);
|
|
DPRINTF("secure_allocator_create_with_pages: Not enough free pages available!\n\n");
|
|
return NULL;
|
|
}
|
|
|
|
/* Initialize a memory pool structure in all pages. */
|
|
for(size_t ii = 0; ii < page_count; ii++) {
|
|
/* Add each page as a pool. */
|
|
osStatus_t status = osRtxMemoryInit(allocator->table.page_origins[ii], page_size);
|
|
if (status == osOK) {
|
|
DPRINTF("secure_allocator_create_with_pages: Created memory pool allocator %p with offset %d page %u\n", allocator->table.page_origins[ii], 0, ii);
|
|
} else {
|
|
DPRINTF("secure_allocator_create_with_pages: Failed creating memory pool allocator %p with offset %d page %u\n", allocator->table.page_origins[ii], 0, ii);
|
|
}
|
|
}
|
|
DPRINTF("\n");
|
|
/* Aaaand across the line. */
|
|
return (SecureAllocator) allocator;
|
|
}
|
|
|
|
int secure_allocator_destroy(
|
|
SecureAllocator allocator)
|
|
{
|
|
DPRINTF("secure_allocator_destroy: Destroying memory pool allocator at %p\n", table(allocator)->page_origins[0]);
|
|
|
|
/* Check if we are working on statically allocated memory. */
|
|
SecureAllocatorInternal * alloc = (SecureAllocatorInternal * const) allocator;
|
|
if (alloc->table.page_count == 0) {
|
|
DPRINTF("secure_allocator_destroy: %p is not page-backed memory, not freeing!\n", allocator);
|
|
return -1;
|
|
}
|
|
|
|
/* Free all pages. */
|
|
if (uvisor_page_free(&(alloc->table))) {
|
|
DPRINTF("secure_allocator_destroy: Unable to free pages!\n\n");
|
|
return -1;
|
|
}
|
|
|
|
/* Free the allocator structure. */
|
|
free(allocator);
|
|
|
|
DPRINTF("\n");
|
|
return 0;
|
|
}
|
|
|
|
void * secure_malloc(
|
|
SecureAllocator allocator,
|
|
size_t size)
|
|
{
|
|
size_t index = 0;
|
|
do {
|
|
/* Search in this page. */
|
|
void * mem = osRtxMemoryAlloc(table(allocator)->page_origins[index], size, 0);
|
|
/* Return if we found something. */
|
|
if (mem) {
|
|
DPRINTF("secure_malloc: Found %4uB in page %u at %p\n", size, index, mem);
|
|
return mem;
|
|
}
|
|
/* Otherwise, go to the next page. */
|
|
index++;
|
|
} /* Continue search if more pages are available. */
|
|
while (index < table(allocator)->page_count);
|
|
|
|
DPRINTF("secure_malloc: Out of memory in allocator %p \n", allocator);
|
|
/* We found nothing. */
|
|
return NULL;
|
|
}
|
|
|
|
void * secure_aligned_alloc(
|
|
SecureAllocator allocator,
|
|
size_t alignment,
|
|
size_t size)
|
|
{
|
|
/* Alignment must be a power of two! */
|
|
if (alignment & ((1UL << ((31UL - __builtin_clz(alignment)) - 1)))) {
|
|
return NULL;
|
|
}
|
|
/* TODO: THIS IS A NAIVE IMPLEMENTATION, which wastes much memory. */
|
|
void * ptr = secure_malloc(allocator, size + alignment - 1);
|
|
if (ptr == NULL) {
|
|
return NULL;
|
|
}
|
|
return (void *) (((uint32_t) ptr + alignment - 1) & ~(alignment - 1));
|
|
}
|
|
|
|
void * secure_calloc(
|
|
SecureAllocator allocator,
|
|
size_t nmemb,
|
|
size_t size)
|
|
{
|
|
if ((uint64_t) nmemb * size > SIZE_MAX) {
|
|
/* (size * nmemb) has overflowed. */
|
|
return NULL;
|
|
}
|
|
void * ptr = secure_malloc(allocator, size * nmemb);
|
|
if (ptr == NULL) {
|
|
return NULL;
|
|
}
|
|
memset(ptr, 0, size * nmemb);
|
|
return ptr;
|
|
}
|
|
|
|
void * secure_realloc(
|
|
SecureAllocator allocator,
|
|
void * ptr,
|
|
size_t new_size)
|
|
{
|
|
/* TODO: THIS IS A NAIVE IMPLEMENTATION, which always allocates new
|
|
memory, and copies the memory, then frees the old memory. */
|
|
|
|
/* Allocate new memory. */
|
|
void * new_ptr = secure_malloc(allocator, new_size);
|
|
/* If memory allocation failed, abort. */
|
|
if (new_ptr == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
/* Passing NULL as ptr is legal, realloc acts as malloc then. */
|
|
if (ptr) {
|
|
/* Get the size of the ptr memory. */
|
|
size_t size = ((mem_block_t *) ((uint32_t) ptr - sizeof(mem_block_t)))->info & ~0x3;
|
|
/* Copy the memory to the new location, min(new_size, size). */
|
|
memcpy(new_ptr, ptr, new_size < size ? new_size : size);
|
|
/* Free the previous memory. */
|
|
secure_free(allocator, ptr);
|
|
}
|
|
return new_ptr;
|
|
}
|
|
|
|
void secure_free(
|
|
SecureAllocator allocator,
|
|
void * ptr)
|
|
{
|
|
size_t index = 0;
|
|
do {
|
|
/* Search in this page. */
|
|
int ret = osRtxMemoryFree(table(allocator)->page_origins[index], ptr);
|
|
/* Return if free was successful. */
|
|
if (ret == 1) {
|
|
DPRINTF("secure_free: Freed %p in page %u.\n", ptr, index);
|
|
return;
|
|
}
|
|
/* Otherwise, go to the next page. */
|
|
index++;
|
|
} /* Continue search if more pages are available. */
|
|
while (index < table(allocator)->page_count);
|
|
|
|
DPRINTF("secure_free: %p not found in allocator %p!\n", ptr, allocator);
|
|
/* We found nothing. */
|
|
return;
|
|
}
|