blob: 753cb9fbe12db0fc8482ab4989fb145070c0c936 [file] [log] [blame]
/*
* Copyright (c) 2021, Arm Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <stdbool.h>
#include <stdint.h>
#include <stddef.h>
#include "crypto_context_pool.h"
static void add_to_free_list(struct crypto_context_pool *pool,
struct crypto_context *context);
static uint32_t alloc_op_handle(struct crypto_context_pool *pool);
static bool op_handle_in_use(struct crypto_context_pool *pool, uint32_t candidate);
void crypto_context_pool_init(struct crypto_context_pool *pool)
{
pool->free = NULL;
pool->active_head = NULL;
pool->active_tail = NULL;
pool->most_recent_op_handle = 0;
for (size_t i = 0; i < CRYPTO_CONTEXT_POOL_SIZE; i++) {
add_to_free_list(pool, &pool->contexts[i]);
}
}
void crypto_context_pool_deinit(struct crypto_context_pool *pool)
{
(void)pool;
}
struct crypto_context *crypto_context_pool_alloc(struct crypto_context_pool *pool,
enum crypto_context_op_id usage,
uint32_t client_id,
uint32_t *op_handle)
{
struct crypto_context *context = NULL;
/* Re-cycle least-recently used context if there are no free contexts */
if (!pool->free && pool->active_tail) crypto_context_pool_free(pool, pool->active_tail);
/* Active context are held in a linked list in most recently allocated order */
if (pool->free) {
context = pool->free;
pool->free = context->next;
context->next = pool->active_head;
context->prev = NULL;
pool->active_head = context;
if (!pool->active_tail) pool->active_tail = context;
if (context->next) context->next->prev = context;
context->usage = usage;
context->client_id = client_id;
context->op_handle = alloc_op_handle(pool);
*op_handle = context->op_handle;
}
return context;
}
void crypto_context_pool_free(struct crypto_context_pool *pool,
struct crypto_context *context)
{
/* Remove from active list */
if (context->prev) {
context->prev->next = context->next;
}
else {
pool->active_head = context->next;
}
if (context->next) {
context->next->prev = context->prev;
}
else {
pool->active_tail = context->prev;
}
/* Add to free list */
add_to_free_list(pool, context);
}
struct crypto_context *crypto_context_pool_find(struct crypto_context_pool *pool,
enum crypto_context_op_id usage,
uint32_t client_id,
uint32_t op_handle)
{
/* Finds an active context that looks as though it legitimately belongs to the
* requesting client. Defends against bad behaviour from the client such
* as misusing a context for a different operation from the one that was
* setup.
*/
struct crypto_context *found = NULL;
struct crypto_context *context = pool->active_head;
while (context) {
if ((context->op_handle == op_handle) &&
(context->usage == usage) &&
(context->client_id == client_id)) {
found = context;
break;
}
context = context->next;
}
return found;
}
static void add_to_free_list(struct crypto_context_pool *pool,
struct crypto_context *context)
{
context->usage = CRYPTO_CONTEXT_OP_ID_NONE;
context->op_handle = 0;
context->next = pool->free;
context->prev = NULL;
pool->free = context;
}
static uint32_t alloc_op_handle(struct crypto_context_pool *pool)
{
/* op handles need to be unique and to minimize the probability
* of a client using a stale handle that collides with a legitmately
* active one, use a rolling 32-bit integer.
*/
uint32_t candidate = pool->most_recent_op_handle + 1;
while (op_handle_in_use(pool, candidate)) ++candidate;
pool->most_recent_op_handle = candidate;
return candidate;
}
static bool op_handle_in_use(struct crypto_context_pool *pool, uint32_t candidate)
{
bool in_use = false;
struct crypto_context *context = pool->active_head;
while (context && !in_use) {
in_use = (candidate == context->op_handle);
context = context->next;
}
return in_use;
}