CHERIoT RTOS
A compartmentalised RTOS for CHERIoT hardware
Loading...
Searching...
No Matches
queue.h
Go to the documentation of this file.
1// Copyright Microsoft and CHERIoT Contributors.
2// SPDX-License-Identifier: MIT
3/**
4 * \file
5 *
6 * This file contains the interface for a simple message queue. This is split
7 * into two layers. The core functionality is implemented as a shared library.
8 * This allows message queues to be used for inter-thread communication without
9 * requiring cross-compartment calls except in the blocking cases (reading from
10 * an empty queue, writing to a full queue).
11 *
12 * These library interfaces are then wrapped in a compartment, which provides
13 * sealed capabilities that authorise sending or receiving messages via a
14 * queue. The compartment interface can be used between mutually distrusting
15 * compartments. Neither endpoint can corrupt the queue state, though there is
16 * of course no guarantee that the sender will send valid data.
17 *
18 * Both sets of queues support multiple senders and multiple receivers. This
19 * does *not* guarantee priority propagation and so a high-priority thread
20 * sending a message may be starved by a low-priority thread that attempts to
21 * send a message over the same queue and is preempted by a medium-priority
22 * thread.
23 */
24
25#pragma once
26
27#include "cdefs.h"
28#include <allocator.h>
29#include <multiwaiter.h>
30#include <stddef.h>
31#include <stdint.h>
32#include <stdlib.h>
33#include <timeout.h>
34#include <token.h>
35
36/**
37 * Structure representing a queue. This structure represents the queue
38 * metadata, the buffer is stored at the end.
39 *
40 * A queue is a ring buffer of fixed-sized elements with a producer and consumer
41 * counter.
42 */
43struct MessageQueue
44{
45 /**
46 * The size of one element in this queue. This should not be modified after
47 * construction.
48 */
50 /**
51 * The size of the queue. This should not be modified after construction.
52 */
53 size_t queueSize;
54 /**
55 * The producer counter.
56 */
57 _Atomic(uint32_t) producer;
58 /**
59 * The consumer counter.
60 */
61 _Atomic(uint32_t) consumer;
62#ifdef __cplusplus
63 MessageQueue(size_t elementSize, size_t queueSize)
65 {
66 }
67#endif
68};
69
70/**
71 * Permissions on message queue handles.
72 */
73enum [[clang::flag_enum]] MessageQueuePermission
74{
75 /**
76 * This handle may be used to send messages.
77 */
79 /**
80 * This handle may be used to receive messages.
81 */
83 /**
84 * This handle may be used to deallocate the queue.
85 */
87};
88
89_Static_assert(sizeof(struct MessageQueue) % sizeof(void *) == 0,
90 "MessageQueue structure must end correctly aligned for storing "
91 "capabilities.");
92
93__BEGIN_DECLS
94
95/**
96 * Returns the allocation size needed for a queue with the specified number and
97 * size of elements. This can be used to statically allocate queues.
98 *
99 * Returns the allocation size on success, or `-EINVAL` if the arguments would
100 * cause an overflow.
101 */
102ssize_t __cheri_libcall queue_allocation_size(size_t elementSize,
103 size_t elementCount);
104
105/**
106 * Allocates space for a queue using `heapCapability` and stores a handle to it
107 * via `outQueue`.
108 *
109 * The queue has space for `elementCount` entries. Each entry is a fixed
110 * size, `elementSize` bytes.
111 *
112 * Returns 0 on success, `-ENOMEM` on allocation failure, and `-EINVAL` if the
113 * arguments are invalid (for example, if the requested number of elements
114 * multiplied by the element size would overflow).
115 */
116int __cheri_libcall queue_create(TimeoutArgument timeout,
117 AllocatorCapability heapCapability,
118 struct MessageQueue **outQueue,
119 size_t elementSize,
120 size_t elementCount);
121
122/**
123 * Stops the queue. This prevents any future send and receive calls from
124 * working, as a prelude to destruction. Queues allocated by the library
125 * should use `queue_destroy` instead of this API, this exists to support
126 * deadlock-free deallocation of queues embedded in other structures or where
127 * the memory is managed externally in some other way.
128 *
129 * Returns 0 on success.
130 */
131int __cheriot_libcall queue_stop(struct MessageQueue *handle);
132
133/**
134 * Destroys a queue. This wakes up all threads waiting to produce or consume,
135 * and makes them fail to acquire the lock, before deallocating the underlying
136 * allocation.
137 *
138 * Returns 0 on success. This can fail only if deallocation would fail and will,
139 * in these cases, return the same error codes as `heap_free`.
140 *
141 * This function will check the heap capability first and will avoid upgrading
142 * the locks if freeing the queue would fail.
143 */
144int __cheri_libcall queue_destroy(AllocatorCapability heapCapability,
145 struct MessageQueue *handle);
146
147/**
148 * Send a message to the queue specified by `handle`. This expects to be able
149 * to copy the number of bytes specified by `elementSize` when the queue was
150 * created from `src`.
151 *
152 * Returns 0 on success. On failure, returns `-ETIMEDOUT` if the timeout was
153 * exhausted, `-EINVAL` on invalid arguments.
154 *
155 * This expects to be called with a valid queue handle. It does not validate
156 * that this is correct.
157 */
158int __cheri_libcall queue_send(TimeoutArgument timeout,
159 struct MessageQueue *handle,
160 const void *src);
161
162/**
163 * Send multiple messages to the queue specified by `handle`. This expects to
164 * be able to copy `count` times the number of bytes specified by `elementSize`
165 * when the queue was created from `src`.
166 *
167 * Returns the number of elements sent on success. On failure, returns
168 * `-ETIMEDOUT` if the timeout was exhausted, `-EINVAL` on invalid arguments.
169 *
170 * This expected to be called with a valid queue handle. It does not validate
171 * that this is correct.
172 */
173int __cheri_libcall queue_send_multiple(TimeoutArgument timeout,
174 struct MessageQueue *handle,
175 const void *src,
176 size_t count);
177
178/**
179 * Receive a message over a queue specified by `handle`. This expects to be
180 * able to copy the number of bytes specified by `elementSize`. The message is
181 * copied to `dst`, which must have sufficient permissions and space to hold
182 * the message.
183 *
184 * Returns 0 on success, `-ETIMEDOUT` if the timeout was exhausted, `-EINVAL` on
185 * invalid arguments.
186 */
187int __cheri_libcall queue_receive(TimeoutArgument timeout,
188 struct MessageQueue *handle,
189 void *dst);
190
191/**
192 * Receive multiple messages to the queue specified by `handle`. This expects
193 * to be able to copy `count` times the number of bytes specified by
194 * `elementSize` when the queue was created to `dst`.
195 *
196 * Returns the number of elements sent on success. On failure, returns
197 * `-ETIMEDOUT` if the timeout was exhausted, `-EINVAL` on invalid arguments.
198 *
199 * This expected to be called with a valid queue handle. It does not validate
200 * that this is correct.
201 */
202int __cheri_libcall queue_receive_multiple(TimeoutArgument timeout,
203 struct MessageQueue *handle,
204 void *dst,
205 size_t count);
206
207/**
208 * Returns the number of items in the queue specified by `handle` via `items`.
209 *
210 * Returns 0 on success. This has no failure mechanisms, but is intended to
211 * have the same interface as the version that operates on a sealed queue
212 * handle.
213 *
214 * Note: This interface is inherently racy. The number of items in the queue
215 * may change in between the return of this function and the caller acting on
216 * the result.
217 */
218int __cheri_libcall queue_items_remaining(struct MessageQueue *handle,
219 size_t *items);
220
221/**
222 * Allocate a new message queue that is managed by the message queue
223 * compartment. The resulting queue handle (returned in `outQueue`) is a
224 * sealed capability to a queue that can be used for both sending and
225 * receiving.
226 */
227int __cheri_compartment("message_queue")
228 queue_create_sealed(TimeoutArgument timeout,
229 AllocatorCapability heapCapability,
230 CHERI_SEALED(struct MessageQueue *) * outQueue,
231 size_t elementSize,
232 size_t elementCount);
233
234/**
235 * Reset a queue to its initial state.
236 *
237 * Returns 0 on success, `-ETIMEDOUT` if this cannot be done in the available
238 * timeout.
239 */
240int __cheri_libcall queue_reset(TimeoutArgument timeout,
241 struct MessageQueue *queue);
242
243/**
244 * Destroy a queue. This requires a handle with `MessageQueuePermitDestroy`
245 * permission.
246 *
247 * Returns 0 on success, `-EINVAL` if `queueHandle` is not a valid queue handle
248 * or lacks the relevant permissions.
249 */
250int __cheri_compartment("message_queue")
251 queue_destroy_sealed(TimeoutArgument timeout,
252 AllocatorCapability heapCapability,
253 CHERI_SEALED(struct MessageQueue *) queueHandle);
254
255/**
256 * Send a message via a sealed queue endpoint. This behaves in the same way as
257 * `queue_send`, except that it will return `-EINVAL` if the endpoint is not a
258 * valid sending endpoint and may return `-ECOMPARTMENTFAIL` if the queue is
259 * destroyed during the call.
260 */
261int __cheri_compartment("message_queue")
262 queue_send_sealed(TimeoutArgument timeout,
263 CHERI_SEALED(struct MessageQueue *) handle,
264 const void *src);
265
266/**
267 * Send multiple messages via a sealed queue endpoint. This behaves in the
268 * same way as `queue_send_multiple`, except that it will return `-EINVAL` if
269 * the endpoint is not a valid sending endpoint and may return
270 * `-ECOMPARTMENTFAIL` if the queue is destroyed during the call.
271 */
272int __cheri_compartment("message_queue")
273 queue_send_multiple_sealed(TimeoutArgument timeout,
274 CHERI_SEALED(struct MessageQueue *) handle,
275 const void *src,
276 size_t count);
277
278/**
279 * Receive a message via a sealed queue endpoint. This behaves in the same way
280 * as `queue_receive`, except that it will return `-EINVAL` if the endpoint is
281 * not a valid receiving endpoint and may return `-ECOMPARTMENTFAIL` if the
282 * queue is destroyed during the call.
283 */
284int __cheri_compartment("message_queue")
285 queue_receive_sealed(TimeoutArgument timeout,
286 CHERI_SEALED(struct MessageQueue *) handle,
287 void *dst);
288
289/**
290 * Receive multiple messages via a sealed queue endpoint. This behaves in the
291 * same way as `queue_receive_multiple`, except that it will return `-EINVAL` if
292 * the endpoint is not a valid receiving endpoint and may return
293 * `-ECOMPARTMENTFAIL` if the queue is destroyed during the call.
294 */
295int __cheri_compartment("message_queue")
296 queue_receive_multiple_sealed(TimeoutArgument timeout,
297 CHERI_SEALED(struct MessageQueue *) handle,
298 void *dst,
299 size_t count);
300
301/**
302 * Returns, via `items`, the number of items in the queue specified by `handle`.
303 * Returns 0 on success.
304 *
305 * This call is intended to be fast and so does minimal checking of arguments.
306 * It does not mutate state or acquire any locks and so may return
307 * `-ECOMPARTMENTFAIL` for any failure case.
308 */
309int __cheri_compartment("message_queue")
310 queue_items_remaining_sealed(CHERI_SEALED(struct MessageQueue *) handle,
311 size_t *items);
312
313/**
314 * Initialise an event waiter source so that it will wait for the queue to be
315 * ready to receive. Note that this is inherently racy because another consumer
316 * may drain the queue before this consumer wakes up.
317 */
318void __cheri_libcall
320 struct MessageQueue *handle);
321
322/**
323 * Initialise an event waiter source so that it will wait for the queue to be
324 * ready to send. Note that this is inherently racy because another producer
325 * may fill the queue before this consumer wakes up.
326 */
327void __cheri_libcall
329 struct MessageQueue *handle);
330
331/**
332 * Initialise an event waiter source as in `multiwaiter_queue_receive_init`,
333 * using a sealed queue endpoint. The `source` argument must be a receive
334 * endpoint.
335 *
336 * Returns 0 on success, `-EINVAL` on invalid arguments. May return
337 * `-ECOMPARTMENTFAIL` if the queue is deallocated in the middle of this call.
338 */
339int __cheri_compartment("message_queue")
341 CHERI_SEALED(struct MessageQueue *)
342 handle);
343
344/**
345 * Initialise an event waiter source as in `multiwaiter_queue_send_init`,
346 * using a sealed queue endpoint. The `source` argument must be a send
347 * endpoint.
348 *
349 * Returns 0 on success, `-EINVAL` on invalid arguments. May return
350 * `-ECOMPARTMENTFAIL` if the queue is deallocated in the middle of this call.
351 */
352int __cheri_compartment("message_queue")
354 CHERI_SEALED(struct MessageQueue *)
355 handle);
356
357/**
358 * Returns the permissions held by this message queue handle. This is a
359 * bitmask of `MessageQueuePermission` values.
360 */
361static inline int queue_permissions(CHERI_SEALED(struct MessageQueue *) handle)
362{
363 return token_permissions_get(handle);
364}
365
366/**
367 * Returns a copy of `handle` with a subset of permissions. The `permissions`
368 * argument is a bitmask of `MessageQueuePermission` values. The returned
369 * handle has only the permissions that are both already present on `handle`
370 * and enumerated in `permissions`.
371 */
372static inline CHERI_SEALED(struct MessageQueue *)
373 queue_permissions_and(CHERI_SEALED(struct MessageQueue *) handle,
374 int permissions)
375{
376 return token_permissions_and(handle, permissions);
377}
378
379/**
380 * Convert a queue handle returned from `queue_create_sealed` into one that can
381 * be used *only* for receiving.
382 *
383 * Returns 0 on success and writes the resulting restricted handle via
384 * `outHandle`. Returns `-ENOMEM` on allocation failure or `-EINVAL` if the
385 * handle is not valid.
386 */
387static inline int __attribute__((
388 deprecated("Restricted handles have been replaced by permissions on queue "
389 "capabilities, controlled with queue_permissions_and")))
391 AllocatorCapability heapCapability,
392 CHERI_SEALED(struct MessageQueue *) handle,
393 CHERI_SEALED(struct MessageQueue *) *
394 outHandle)
395{
396 *outHandle = queue_permissions_and(handle, MessageQueuePermitReceive);
397 return 0;
398}
399
400/**
401 * Convert a queue handle returned from `queue_create_sealed` into one that can
402 * be used *only* for sending.
403 *
404 * Returns 0 on success and writes the resulting restricted handle via
405 * `outHandle`. Returns `-ENOMEM` on allocation failure or `-EINVAL` if the
406 * handle is not valid.
407 */
408static inline int __attribute__((
409 deprecated("Restricted handles have been replaced by permissions on queue "
410 "capabilities, controlled with queue_permissions_and")))
411queue_send_handle_create_sealed(TimeoutArgument timeout,
412 AllocatorCapability heapCapability,
413 CHERI_SEALED(struct MessageQueue *) handle,
414 CHERI_SEALED(struct MessageQueue *) * outHandle)
415{
416 *outHandle = queue_permissions_and(handle, MessageQueuePermitSend);
417 return 0;
418}
419
420__END_DECLS
CHERIoT RTOS heap allocator interface.
This file provides interfaces to the multi-waiter system.
int __cheri_libcall queue_send(TimeoutArgument timeout, struct MessageQueue *handle, const void *src)
Send a message to the queue specified by handle.
int queue_send_multiple_sealed(TimeoutArgument timeout, CHERI_SEALED(struct MessageQueue *) handle, const void *src, size_t count)
Send multiple messages via a sealed queue endpoint.
int __cheri_libcall queue_send_multiple(TimeoutArgument timeout, struct MessageQueue *handle, const void *src, size_t count)
Send multiple messages to the queue specified by handle.
int queue_destroy_sealed(TimeoutArgument timeout, AllocatorCapability heapCapability, CHERI_SEALED(struct MessageQueue *) queueHandle)
Destroy a queue.
static CHERI_SEALED(struct MessageQueue *) queue_permissions_and(CHERI_SEALED(struct MessageQueue *) handle
Returns a copy of handle with a subset of permissions.
int __cheri_libcall queue_destroy(AllocatorCapability heapCapability, struct MessageQueue *handle)
Destroys a queue.
int queue_create_sealed(TimeoutArgument timeout, AllocatorCapability heapCapability, CHERI_SEALED(struct MessageQueue *) *outQueue, size_t elementSize, size_t elementCount)
Allocate a new message queue that is managed by the message queue compartment.
int __cheriot_libcall queue_stop(struct MessageQueue *handle)
Stops the queue.
int __cheri_libcall queue_create(TimeoutArgument timeout, AllocatorCapability heapCapability, struct MessageQueue **outQueue, size_t elementSize, size_t elementCount)
Allocates space for a queue using heapCapability and stores a handle to it via outQueue.
int queue_receive_sealed(TimeoutArgument timeout, CHERI_SEALED(struct MessageQueue *) handle, void *dst)
Receive a message via a sealed queue endpoint.
static int queue_receive_handle_create_sealed(TimeoutArgument timeout, AllocatorCapability heapCapability, CHERI_SEALED(struct MessageQueue *) handle, CHERI_SEALED(struct MessageQueue *) *outHandle)
Convert a queue handle returned from queue_create_sealed into one that can be used only for receiving...
Definition queue.h:390
int __cheri_libcall queue_items_remaining(struct MessageQueue *handle, size_t *items)
Returns the number of items in the queue specified by handle via items.
int __cheri_libcall queue_receive(TimeoutArgument timeout, struct MessageQueue *handle, void *dst)
Receive a message over a queue specified by handle.
int queue_items_remaining_sealed(CHERI_SEALED(struct MessageQueue *) handle, size_t *items)
Returns, via items, the number of items in the queue specified by handle.
int __cheri_libcall queue_reset(TimeoutArgument timeout, struct MessageQueue *queue)
Reset a queue to its initial state.
int queue_send_sealed(TimeoutArgument timeout, CHERI_SEALED(struct MessageQueue *) handle, const void *src)
Send a message via a sealed queue endpoint.
void __cheri_libcall multiwaiter_queue_send_init(struct EventWaiterSource *source, struct MessageQueue *handle)
Initialise an event waiter source so that it will wait for the queue to be ready to send.
ssize_t __cheri_libcall queue_allocation_size(size_t elementSize, size_t elementCount)
Returns the allocation size needed for a queue with the specified number and size of elements.
int queue_receive_multiple_sealed(TimeoutArgument timeout, CHERI_SEALED(struct MessageQueue *) handle, void *dst, size_t count)
Receive multiple messages via a sealed queue endpoint.
static int queue_permissions(CHERI_SEALED(struct MessageQueue *) handle)
Returns the permissions held by this message queue handle.
Definition queue.h:361
int multiwaiter_queue_receive_init_sealed(struct EventWaiterSource *source, handle)
Initialise an event waiter source as in multiwaiter_queue_receive_init, using a sealed queue endpoint...
void __cheri_libcall multiwaiter_queue_receive_init(struct EventWaiterSource *source, struct MessageQueue *handle)
Initialise an event waiter source so that it will wait for the queue to be ready to receive.
static int queue_send_handle_create_sealed(TimeoutArgument timeout, AllocatorCapability heapCapability, CHERI_SEALED(struct MessageQueue *) handle, CHERI_SEALED(struct MessageQueue *) *outHandle)
Convert a queue handle returned from queue_create_sealed into one that can be used only for sending.
Definition queue.h:411
MessageQueuePermission
Permissions on message queue handles.
Definition queue.h:74
@ MessageQueuePermitReceive
This handle may be used to receive messages.
Definition queue.h:82
@ MessageQueuePermitDestroy
This handle may be used to deallocate the queue.
Definition queue.h:86
@ MessageQueuePermitSend
This handle may be used to send messages.
Definition queue.h:78
int __cheri_libcall queue_receive_multiple(TimeoutArgument timeout, struct MessageQueue *handle, void *dst, size_t count)
Receive multiple messages to the queue specified by handle.
int multiwaiter_queue_send_init_sealed(struct EventWaiterSource *source, handle)
Initialise an event waiter source as in multiwaiter_queue_send_init, using a sealed queue endpoint.
Structure describing a change to the set of managed event sources for an event waiter.
Definition multiwaiter.h:41
Structure representing a queue.
Definition queue.h:44
size_t queueSize
The size of the queue.
Definition queue.h:53
size_t elementSize
The size of one element in this queue.
Definition queue.h:49
_Atomic(uint32_t) producer
The producer counter.
_Atomic(uint32_t) consumer
The consumer counter.
This file contains the types used for timeouts across scheduler APIs.
APIs for dealing with type-safe opaque handles (sealed objects).
static int token_permissions_get(CHERI_SEALED(void *) object)
Returns the user permissions bitmap for the sealed object.
Definition token.h:160
token_permissions_and(CHERI_SEALED(T *) object, int permissions)
Mask the user permissions of a sealed object.
Definition token.h:148