Files
oemcrypto/oemcrypto/opk/serialization/common/shared_buffer_allocator.c
Googler 98f0721662 OEMCrypto and OPK 18.10.0
GitOrigin-RevId: 987123747bd0be50fc5e4e89ec26eaa6d215bc36
2025-06-09 23:55:51 -07:00

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 = &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 = &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;
}