340 lines
11 KiB
C
340 lines
11 KiB
C
/*
|
|
* Copyright 2020 Google LLC. All Rights Reserved. This file and proprietary
|
|
* source code may only be used and distributed under the Widevine
|
|
* License Agreement.
|
|
*/
|
|
|
|
#include <string.h>
|
|
|
|
#include "log_macros.h"
|
|
#include "message_debug.h"
|
|
#include "oemcrypto_overflow.h"
|
|
#include "shared_buffer_allocator.h"
|
|
#include "tos_shared_memory_interface.h"
|
|
#include "tos_transport_interface.h"
|
|
|
|
/*
|
|
* The Shared Buffer Allocator provides functions for allocating and
|
|
* iterating over shared buffers. A shared buffer is a chunk of
|
|
* shared memory that is allocated for passing an OEMCrypto parameter
|
|
* between the REE and the TEE.
|
|
*
|
|
* The Shared Memory Interface supplies a block of shared memory that
|
|
* all shared buffers are allocated from. This block of shared memory
|
|
* gets carved up into contiguous shared buffers which are defined
|
|
* by an offset from the beginning of the block and a length.
|
|
* Allocating a shared buffer means carving out the next consecutive
|
|
* buffer of the requested size from the shared memory block.
|
|
*
|
|
* Each shared buffer is referenced by an index number. Given an index
|
|
* number, the offset and length of the allocation can be looked
|
|
* up. The sequence of allocations in the REE during serialization is
|
|
* guaranteed to be the same as the sequence of allocations in the TEE
|
|
* during deserialization, so corresponding shared buffer indexes in
|
|
* the REE and the TEE appear at the same offsets and sizes in the
|
|
* shared memory block.
|
|
*
|
|
* At the beginning of each OEMCrypto API call the allocator is reset
|
|
* so there are no allocations. During request packing in the REE, a
|
|
* shared buffer is allocated for each input or output parameter of
|
|
* type OEMCrypto_SharedMemory*. During request unpacking in
|
|
* the TEE, a shared buffer is similarly allocated for each parameter.
|
|
*
|
|
* When the dispatcher in the TEE initializes arguments that will be
|
|
* passed into the OEMCrypto API function in the TA, it iterates over
|
|
* the allocated shared buffers, passing the address of each
|
|
* consecutive shared buffer into the appropriate parameter in the
|
|
* OEMCrypto function call. The OEMCrypto function then operates on
|
|
* the data in the shared buffers, reading input data and writing
|
|
* output data directly from/to the shared memory. After the API call
|
|
* completes, the response message is packed and delivered to
|
|
* the REE which can then access the result from the shared memory.
|
|
*/
|
|
|
|
/*
|
|
* Descriptor for a shared buffer allocation
|
|
*/
|
|
typedef struct {
|
|
uint8_t* address; /* Address of the parameter in non-shared memory */
|
|
size_t offset; /* Offset into the shared memory block of this buffer */
|
|
size_t length; /* size of the buffer in bytes */
|
|
bool copy_in; /* copy into buffer from |address| in finalize? */
|
|
bool copy_out; /* copy from the buffer into |address| in finalize? */
|
|
bool is_output; /* true if the parameter is an output parameter */
|
|
} Allocation;
|
|
|
|
/*
|
|
* The number of shared buffers that can be allocated
|
|
*/
|
|
#define MAX_SHARED_BUFFERS 32
|
|
static Allocation allocations_[MAX_SHARED_BUFFERS];
|
|
|
|
/*
|
|
* Define indexes for shared buffers. The indexes are managed
|
|
* internally, they are not exposed directly to the caller. For each
|
|
* OEMCrypto call the index starts at 0 and increments sequentially on
|
|
* each use.
|
|
*
|
|
* |next_buffer_index_| is incremented each time a shared buffer is
|
|
* allocated with OPK_SharedBuffer_Allocate(), and when the next
|
|
* buffer is addressed using OPK_SharedBuffer_NextBuffer(). The code
|
|
* generator guarantees that buffers are allocated in the same order
|
|
* in both the REE and the TEE.
|
|
*/
|
|
|
|
/*
|
|
* The index of the next buffer to be allocated
|
|
*/
|
|
static size_t next_buffer_index_ = 0;
|
|
|
|
/*
|
|
* The offset into the shared memory block of the next buffer
|
|
* allocation
|
|
*/
|
|
static size_t next_buffer_offset_ = 0;
|
|
|
|
/*
|
|
* The number of buffers allocated
|
|
*/
|
|
static size_t buffer_count_ = 0;
|
|
|
|
/*
|
|
* Initialize shared memory state variables.
|
|
*/
|
|
bool OPK_SharedBuffer_Initialize(void) {
|
|
if (TOS_SharedMemory_Allocate(TOS_SharedMemory_AvailableSize())) {
|
|
OPK_SharedBuffer_Reset();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* Release shared memory resources
|
|
*/
|
|
void OPK_SharedBuffer_Terminate(void) {
|
|
OPK_SharedBuffer_Reset();
|
|
TOS_SharedMemory_Release();
|
|
}
|
|
|
|
/*
|
|
* Set the allocator to its initial state, where there are no buffers
|
|
* allocated.
|
|
*/
|
|
void OPK_SharedBuffer_Reset(void) {
|
|
memset(&allocations_[0], 0, buffer_count_ * sizeof(allocations_[0]));
|
|
next_buffer_offset_ = 0;
|
|
next_buffer_index_ = 0;
|
|
buffer_count_ = 0;
|
|
}
|
|
|
|
/*
|
|
* Allocate a buffer from the serialization shared memory block. This
|
|
* reserves a range of bytes of shared memory to store the
|
|
* serialization parameter referenced by |address|. |copy_in| and
|
|
* |copy_out| indicate if the contents of the parameter need to be
|
|
* copied when OPK_SharedBuffer_Finalize is called.
|
|
*
|
|
* Parameters:
|
|
* address - location of the parameter in non-shared REE
|
|
* memory. This will be the source or destination of the copy
|
|
* operation if copy_in or copy_out is true.
|
|
* size - the size of the buffer to allocate
|
|
* copy_in - set to true if data must be copied from |address| into
|
|
* the buffer
|
|
* copy_out - set to true if data must be copied to |address| from
|
|
* the buffer
|
|
* is_output - set to true if the buffer is associated with an output
|
|
* parameter type
|
|
*
|
|
* Returns:
|
|
* The address of the allocation or NULL if allocation fails
|
|
*/
|
|
ODK_MessageStatus OPK_SharedBuffer_Allocate(uint8_t* address, size_t size,
|
|
bool copy_in, bool copy_out,
|
|
bool is_output,
|
|
uint8_t** shared_address) {
|
|
size_t index = next_buffer_index_;
|
|
if (shared_address == NULL) {
|
|
return MESSAGE_STATUS_NULL_POINTER_ERROR;
|
|
}
|
|
if (index >= MAX_SHARED_BUFFERS) {
|
|
LOGE("Exceeded max shared buffer allocations");
|
|
return MESSAGE_STATUS_MAP_SHARED_MEMORY_FAILED;
|
|
}
|
|
size_t new_offset = 0;
|
|
if (OPK_AddOverflowUX(next_buffer_offset_, size, &new_offset)) {
|
|
return MESSAGE_STATUS_OVERFLOW_ERROR;
|
|
}
|
|
if (new_offset > TOS_SharedMemory_GetSize()) {
|
|
LOGE("Insufficient shared memory");
|
|
return MESSAGE_STATUS_BUFFER_TOO_LARGE;
|
|
}
|
|
|
|
Allocation* alloc = &allocations_[index];
|
|
alloc->address = address;
|
|
alloc->offset = next_buffer_offset_;
|
|
alloc->length = size;
|
|
alloc->copy_in = copy_in;
|
|
alloc->copy_out = copy_out;
|
|
alloc->is_output = is_output;
|
|
|
|
uint8_t* buffer_address = TOS_SharedMemory_GetAddress() + next_buffer_offset_;
|
|
next_buffer_offset_ = new_offset;
|
|
next_buffer_index_++;
|
|
buffer_count_++;
|
|
*shared_address = buffer_address;
|
|
return MESSAGE_STATUS_OK;
|
|
}
|
|
|
|
/*
|
|
* Called after all shared buffer allocations have been completed
|
|
* during packing, this function synchronizes shared memory
|
|
* buffers with non-shared memory by copying data into the
|
|
* the buffers. It also prepares the indexes for iteration over the
|
|
* buffers that have been allocated.
|
|
*/
|
|
void OPK_SharedBuffer_FinalizePacking(void) {
|
|
/* Finalize copy ins */
|
|
OPK_SharedMemory_CopyVector copy_vec[MAX_SHARED_BUFFERS];
|
|
size_t vec_len = 0;
|
|
|
|
for (size_t i = 0; i < buffer_count_; i++) {
|
|
Allocation* alloc = &allocations_[i];
|
|
if (alloc->copy_in) {
|
|
MDBG(" -> " BLUE "copy in " COLOR_OFF "%zu bytes from %p to %p",
|
|
alloc->length, (const void*)alloc->address,
|
|
(const void*)TOS_SharedMemory_GetAddress() + alloc->offset);
|
|
MDBG(" -> %s", OPK_Memory_Str(alloc->address, alloc->length));
|
|
OPK_SharedMemory_CopyVector* copy = ©_vec[vec_len++];
|
|
copy->address = alloc->address;
|
|
copy->offset = alloc->offset;
|
|
copy->length = alloc->length;
|
|
}
|
|
}
|
|
TOS_SharedMemory_Finalize(copy_vec, vec_len, NULL, 0);
|
|
|
|
/* Prepare next_buffer_index for iteration of allocated buffers */
|
|
next_buffer_index_ = 0;
|
|
}
|
|
|
|
/*
|
|
* Called after all shared buffer allocations have been completed
|
|
* during unpacking, this function synchronizes shared memory
|
|
* buffers with REE non-shared memory by copying data out of the
|
|
* the buffers.
|
|
*/
|
|
void OPK_SharedBuffer_FinalizeUnpacking(void) {
|
|
/* Finalize copy outs */
|
|
OPK_SharedMemory_CopyVector copy_vec[MAX_SHARED_BUFFERS];
|
|
size_t vec_len = 0;
|
|
|
|
for (size_t i = 0; i < buffer_count_; i++) {
|
|
Allocation* alloc = &allocations_[i];
|
|
if (alloc->copy_out) {
|
|
MDBG(" -> " BLUE "copy out" COLOR_OFF " %zu bytes from %p to %p",
|
|
alloc->length,
|
|
(const void*)TOS_SharedMemory_GetAddress() + alloc->offset,
|
|
(const void*)alloc->address);
|
|
MDBG(" -> %s",
|
|
OPK_Memory_Str(
|
|
(const void*)TOS_SharedMemory_GetAddress() + alloc->offset,
|
|
alloc->length));
|
|
OPK_SharedMemory_CopyVector* copy = ©_vec[vec_len++];
|
|
copy->address = alloc->address;
|
|
copy->offset = alloc->offset;
|
|
copy->length = alloc->length;
|
|
}
|
|
}
|
|
TOS_SharedMemory_Finalize(NULL, 0, copy_vec, vec_len);
|
|
|
|
/* Prepare next_buffer_index for iteration of allocated buffers */
|
|
next_buffer_index_ = 0;
|
|
}
|
|
|
|
/*
|
|
* Advance the current index to the next shared buffer that has been
|
|
* allocated. The current buffer is at |next_buffer_index_| - 1. The
|
|
* address of the buffer referenced by the current index can be
|
|
* obtained from OPK_SharedBuffer_GetAddress(). On return,
|
|
* |next_buffer_index_| will be incremented by 1, if there is another
|
|
* allocation.
|
|
*
|
|
* Parameters:
|
|
* None
|
|
*
|
|
* Returns:
|
|
* The address of the next shared buffer, or NULL if there isn't one
|
|
*/
|
|
uint8_t* OPK_SharedBuffer_NextBuffer(void) {
|
|
if (next_buffer_index_ < buffer_count_) {
|
|
Allocation* alloc = &allocations_[next_buffer_index_++];
|
|
return TOS_SharedMemory_GetAddress() + alloc->offset;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Advance the current index to the next shared output buffer that has
|
|
* been allocated. The current buffer is at |next_buffer_index_| - 1.
|
|
* On return, |next_buffer_index_| is incremented until it references
|
|
* the next buffer of type SHARED_BUFFER_TYPE_OUTPUT, as long there
|
|
* are additional allocations. On return |next_buffer_index| will
|
|
* reference the output buffer, i.e. it will be equal to the output
|
|
* buffer's index + 1.
|
|
*
|
|
* Parameters:
|
|
* None
|
|
*
|
|
* Returns:
|
|
* The address of the next shared output buffer, or NULL if there is no
|
|
* next output buffer allocation
|
|
*/
|
|
uint8_t* OPK_SharedBuffer_NextOutputBuffer(void) {
|
|
while (next_buffer_index_ < buffer_count_) {
|
|
Allocation* alloc = &allocations_[next_buffer_index_++];
|
|
if (alloc->is_output) {
|
|
return TOS_SharedMemory_GetAddress() + alloc->offset;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Get the address of the currently referenced shared memory segment which
|
|
* is at next_buffer_index_ - 1.
|
|
*
|
|
* Parameters:
|
|
* None
|
|
*
|
|
* Returns:
|
|
* The address of the allocation at next_allocation_index - 1
|
|
* NULL if there are no matching allocations.
|
|
*/
|
|
uint8_t* OPK_SharedBuffer_GetAddress(void) {
|
|
if (next_buffer_index_ == 0 || next_buffer_index_ > buffer_count_) {
|
|
return NULL;
|
|
}
|
|
Allocation* alloc = &allocations_[next_buffer_index_ - 1];
|
|
return TOS_SharedMemory_GetAddress() + alloc->offset;
|
|
}
|
|
|
|
/*
|
|
* Get the size of the currently referenced shared buffer allocation which
|
|
* is at next_buffer_index_ - 1.
|
|
*
|
|
* Parameters:
|
|
* None
|
|
*
|
|
* Returns:
|
|
* The size of the allocation at next_allocation_index - 1
|
|
* 0 if there are no matching allocations.
|
|
*/
|
|
size_t OPK_SharedBuffer_GetCurrentBufferSize(void) {
|
|
if (next_buffer_index_ == 0 || next_buffer_index_ > buffer_count_) {
|
|
return 0;
|
|
}
|
|
Allocation* alloc = &allocations_[next_buffer_index_ - 1];
|
|
return alloc->length;
|
|
}
|