1 /*
2  * Copyright (c) 2021, Arm Limited and Contributors. All rights reserved.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
7 #include <stdbool.h>
8 #include <stdint.h>
9 #include <stddef.h>
10 #include "crypto_context_pool.h"
11 
12 static void add_to_free_list(struct crypto_context_pool *pool,
13 	struct crypto_context *context);
14 
15 static uint32_t alloc_op_handle(struct crypto_context_pool *pool);
16 static bool op_handle_in_use(struct crypto_context_pool *pool, uint32_t candidate);
17 
18 
crypto_context_pool_init(struct crypto_context_pool * pool)19 void crypto_context_pool_init(struct crypto_context_pool *pool)
20 {
21 	pool->free = NULL;
22 	pool->active_head = NULL;
23 	pool->active_tail = NULL;
24 	pool->most_recent_op_handle = 0;
25 
26 	for (size_t i = 0; i < CRYPTO_CONTEXT_POOL_SIZE; i++) {
27 
28 		add_to_free_list(pool, &pool->contexts[i]);
29 	}
30 }
31 
crypto_context_pool_deinit(struct crypto_context_pool * pool)32 void crypto_context_pool_deinit(struct crypto_context_pool *pool)
33 {
34 	(void)pool;
35 }
36 
crypto_context_pool_alloc(struct crypto_context_pool * pool,enum crypto_context_op_id usage,uint32_t client_id,uint32_t * op_handle)37 struct crypto_context *crypto_context_pool_alloc(struct crypto_context_pool *pool,
38 	enum crypto_context_op_id usage,
39 	uint32_t client_id,
40 	uint32_t *op_handle)
41 {
42 	struct crypto_context *context = NULL;
43 
44 	/* Re-cycle least-recently used context if there are no free contexts */
45 	if (!pool->free && pool->active_tail) crypto_context_pool_free(pool, pool->active_tail);
46 
47 	/* Active context are held in a linked list in most recently allocated order */
48 	if (pool->free) {
49 
50 		context = pool->free;
51 		pool->free = context->next;
52 
53 		context->next = pool->active_head;
54 		context->prev = NULL;
55 		pool->active_head = context;
56 
57 		if (!pool->active_tail) pool->active_tail = context;
58 		if (context->next) context->next->prev = context;
59 
60 		context->usage = usage;
61 		context->client_id = client_id;
62 
63 		context->op_handle = alloc_op_handle(pool);
64 		*op_handle = context->op_handle;
65 	}
66 
67 	return context;
68 }
69 
crypto_context_pool_free(struct crypto_context_pool * pool,struct crypto_context * context)70 void crypto_context_pool_free(struct crypto_context_pool *pool,
71 	struct crypto_context *context)
72 {
73 	/* Remove from active list */
74 	if (context->prev) {
75 		context->prev->next = context->next;
76 	}
77 	else {
78 		pool->active_head = context->next;
79 	}
80 
81 	if (context->next) {
82 		context->next->prev = context->prev;
83 	}
84 	else {
85 		pool->active_tail = context->prev;
86 	}
87 
88 	/* Add to free list */
89 	add_to_free_list(pool, context);
90 }
91 
crypto_context_pool_find(struct crypto_context_pool * pool,enum crypto_context_op_id usage,uint32_t client_id,uint32_t op_handle)92 struct crypto_context *crypto_context_pool_find(struct crypto_context_pool *pool,
93 	enum crypto_context_op_id usage,
94 	uint32_t client_id,
95 	uint32_t op_handle)
96 {
97 	/* Finds an active context that looks as though it legitimately belongs to the
98 	 * requesting client.  Defends against bad behaviour from the client such
99 	 * as misusing a context for a different operation from the one that was
100 	 * setup.
101 	 */
102 	struct crypto_context *found = NULL;
103 	struct crypto_context *context = pool->active_head;
104 
105 	while (context) {
106 
107 		if ((context->op_handle == op_handle) &&
108 			(context->usage == usage) &&
109 			(context->client_id == client_id)) {
110 
111 			found = context;
112 			break;
113 		}
114 
115 		context = context->next;
116 	}
117 
118 	return found;
119 }
120 
add_to_free_list(struct crypto_context_pool * pool,struct crypto_context * context)121 static void add_to_free_list(struct crypto_context_pool *pool,
122 	struct crypto_context *context)
123 {
124 	context->usage = CRYPTO_CONTEXT_OP_ID_NONE;
125 	context->op_handle = 0;
126 	context->next = pool->free;
127 	context->prev = NULL;
128 	pool->free = context;
129 }
130 
alloc_op_handle(struct crypto_context_pool * pool)131 static uint32_t alloc_op_handle(struct crypto_context_pool *pool)
132 {
133 	/* op handles need to be unique and to minimize the probability
134 	 * of a client using a stale handle that collides with a legitmately
135 	 * active one, use a rolling 32-bit integer.
136 	 */
137 	uint32_t candidate = pool->most_recent_op_handle + 1;
138 
139 	while (op_handle_in_use(pool, candidate)) ++candidate;
140 
141 	pool->most_recent_op_handle = candidate;
142 
143 	return candidate;
144 }
145 
op_handle_in_use(struct crypto_context_pool * pool,uint32_t candidate)146 static bool op_handle_in_use(struct crypto_context_pool *pool, uint32_t candidate)
147 {
148 	bool in_use = false;
149 	struct crypto_context *context = pool->active_head;
150 
151 	while (context && !in_use) {
152 
153 		in_use = (candidate == context->op_handle);
154 		context = context->next;
155 	}
156 
157 	return in_use;
158 }
159