CHERIoT RTOS
A compartmentalised RTOS for CHERIoT hardware
Loading...
Searching...
No Matches
compartment-macros.h
Go to the documentation of this file.
1// Copyright Microsoft and CHERIoT Contributors.
2// SPDX-License-Identifier: MIT
3
4#pragma once
6
7/**
8 * \file
9 *
10 * Macros for interacting with the compartmentalisation model in CHERIoT.
11 */
12
13/**
14 * Provide a capability of the type `volatile type *` referring to the MMIO
15 * region exported in the linker script with `name` as its name. This macro
16 * can be used only in code (it cannot be used to initialise a global).
17 *
18 * The last arguments specify the set of permissions that this capability holds:
19 *
20 * - `permitLoad` if this should have load / read (R) permission.
21 * - `permitStore` if this should have store / write (W) permission.
22 * - `permitLoadStoreCapabilities` if this should have load/store capability
23 * permission (C).
24 * - `permitLoadMutable` if this should have load-mutable (m) permission. This
25 * permission requires R and C.
26 * - `permitLoadGlobal` if this should have load-global (g) permission. This
27 * permission requires R and C.
28 *
29 * At least one out of `permitLoad` and `permitStore` should be provided.
30 *
31 * MMIO capabilities are always global (G) and without store-local (l)
32 * permission.
33 *
34 * \hideinitializer
35 */
36#if defined(__has_attribute) && __has_attribute(cheriot_mmio)
37# define MMIO_CAPABILITY_WITH_PERMISSIONS(type, \
38 name, \
39 permitLoad, \
40 permitStore, \
41 permitLoadStoreCapabilities, \
42 permitLoadMutable, \
43 permitLoadGlobal) \
44 MMIO_CAPABILITY_WITH_PERMISSIONS_INNER( \
45 volatile type, \
46 name, \
47 permitLoad, \
48 permitStore, \
49 permitLoadStoreCapabilities, \
50 permitLoadMutable, \
51 permitLoadGlobal, \
52 CHERIOT_COMPARTMENT_MACROS_TOKEN_PASTE( \
53 __cheriot_mmio__, \
54 CHERIOT_COMPARTMENT_MACROS_TOKEN_PASTE(name, __COUNTER__)))
55
56#else
57# define MMIO_CAPABILITY_WITH_PERMISSIONS(type, \
58 name, \
59 permitLoad, \
60 permitStore, \
61 permitLoadStoreCapabilities, \
62 permitLoadMutable, \
63 permitLoadGlobal) \
64 MMIO_CAPABILITY_WITH_PERMISSIONS_HELPER( \
65 volatile type, /* NOLINT(bugprone-macro-parentheses) */ \
66 name, \
67 "__import_mem_" #name "_" #permitLoad "_" #permitStore \
68 "_" #permitLoadStoreCapabilities "_" #permitLoadMutable \
69 "_" #permitLoadGlobal, \
70 permitLoad, \
71 permitStore, \
72 permitLoadStoreCapabilities, \
73 permitLoadMutable, \
74 permitLoadGlobal)
75
76#endif
77
78/**
79 * Provide a capability of the type `volatile type *` referring
80 * to the MMIO region exported in the linker script with `name`
81 * as its name. This macro can be used only in code (it cannot
82 * be used to initialise a global).
83 *
84 * MMIO capabilities produced by this macro have load and store
85 * permissions but cannot hold capabilities. For richer
86 * permissions use MMIO_CAPABILITY_WITH_PERMISSIONS.
87 *
88 * \hideinitializer
89 */
90#define MMIO_CAPABILITY(type, name) \
91 MMIO_CAPABILITY_WITH_PERMISSIONS( \
92 type, name, true, true, false, false, false)
93
94/**
95 * Provide a capability of the type `type *` referring to the
96 * pre-shared object with `name` as its name. This macro can be
97 * used only in code (it cannot be used to initialise a global).
98 *
99 * The last arguments specify the set of permissions that this
100 * capability holds: Load Data (LD), Store Data (SD), Memory
101 * Capabilities (MC), Load Mutable (LM), and Load Global (LG).
102 *
103 * Capabilities to pre-shared objects are always global (G) and without
104 * store-local (l) permission.
105 *
106 * \hideinitializer
107 */
108#if defined(__has_attribute) && __has_attribute(cheriot_shared_object)
109# define SHARED_OBJECT_WITH_PERMISSIONS(type, \
110 name, \
111 permitLoad, \
112 permitStore, \
113 permitLoadStoreCapabilities, \
114 permitLoadMutable, \
115 permitLoadGlobal) \
116 SHARED_OBJECT_WITH_PERMISSIONS_INNER( \
117 type, \
118 name, \
119 permitLoad, \
120 permitStore, \
121 permitLoadStoreCapabilities, \
122 permitLoadMutable, \
123 permitLoadGlobal, \
124 CHERIOT_COMPARTMENT_MACROS_TOKEN_PASTE( \
125 __cheriot_so__, \
126 CHERIOT_COMPARTMENT_MACROS_TOKEN_PASTE(name, __COUNTER__)))
127#else
128# define SHARED_OBJECT_WITH_PERMISSIONS(type, \
129 name, \
130 permitLoad, \
131 permitStore, \
132 permitLoadStoreCapabilities, \
133 permitLoadMutable, \
134 permitLoadGlobal) \
135 IMPORT_CAPABILITY_WITH_PERMISSIONS_HELPER( \
136 type, /* NOLINT(bugprone-macro-parentheses) */ \
137 name, \
138 __cheriot_shared_object_, \
139 "__import_cheriot_shared_object_" #name "_" #permitLoad \
140 "_" #permitStore "_" #permitLoadStoreCapabilities \
141 "_" #permitLoadMutable "_" #permitLoadGlobal, \
142 permitLoad, \
143 permitStore, \
144 permitLoadStoreCapabilities, \
145 permitLoadMutable, \
146 permitLoadGlobal)
147#endif
148
149/**
150 * Provide a capability of the type `type *` referring to the
151 * pre-shared object with `name` as its name. This macro can be
152 * used only in code (it cannot be used to initialise a global).
153 *
154 * Pre-shared object capabilities produced by this macro have
155 * load, store, load-mutable, and load/store-capability
156 * permissions. To define a reduced set of permissions use
157 * `SHARED_OBJECT_WITH_PERMISSIONS`.
158 *
159 * \hideinitializer
160 */
161#define SHARED_OBJECT(type, name) \
162 SHARED_OBJECT_WITH_PERMISSIONS(type, name, true, true, true, true, true)
163
164/**
165 * Provide a capability of the type `type *` referring to the
166 * pre-shared object with `name` as its name. This macro can be
167 * used only in code (it cannot be used to initialise a global).
168 *
169 * Pre-shared object capabilities produced by this macro have
170 * the indicated load and store permission, but no
171 * load/store-capability permissions (and, therefore, no
172 * load-mutable or load-global permissions).
173 *
174 * \hideinitializer
175 */
176#define SHARED_OBJECT_WITH_DATA_PERMISSIONS( \
177 type, name, permitLoad, permitStore) \
178 SHARED_OBJECT_WITH_PERMISSIONS( \
179 type, name, permitLoad, permitStore, false, false, false)
180
181/**
182 * Macro to test whether a device with a specific name exists in
183 * the board definition for the current target.
184 *
185 * \hideinitializer
186 */
187#define DEVICE_EXISTS(x) defined(DEVICE_EXISTS_##x)
188
189/**
190 * Macro that evaluates to a static sealing type that is local
191 * to this compartment.
192 *
193 * The `name` is a unique identifier. This matches the `keyName` passed to
194 * `DECLARE_STATIC_SEALED_VALUE` and related macros and will appear in the
195 * audit log when linking the firmware image. Names within a compartment may
196 * be unique but two compartments may expose sealing types of the same name
197 * without conflicts.
198 *
199 * \hideinitializer
200 */
201#if defined(__has_builtin) && __has_builtin(__builtin_cheriot_sealing_type)
202# define STATIC_SEALING_TYPE(name) \
203 static_cast<TokenKey>( \
204 __builtin_cheriot_sealing_type(#name)) /* NOLINT */
205#else
206# define STATIC_SEALING_TYPE(name) \
207 CHERIOT_EMIT_STATIC_SEALING_TYPE( \
208 "sealing_type." COMPARTMENT_NAME_STRING "." #name)
209#endif
210
211/**
212 * Forward-declare a static sealed object. This declares an
213 * object of type `type` that can be referenced with the
214 * `STATIC_SEALED_VALUE` macro using `name`. The pointer
215 * returned by the latter macro will be sealed with the sealing
216 * key exported from `compartment` as `keyName` with the
217 * `STATIC_SEALING_TYPE` macro.
218 *
219 * The object created with this macro can be accessed only by
220 * code that has access to the sealing key.
221 *
222 * Unlike `DECLARE_STATIC_SEALED_VALUE`, this allows the type
223 * that value should be read as to be different to the type used
224 * to initialise it. The type of the value in
225 * `STATIC_SEALED_VALUE` will be `CHERI_SEALED(valueType*)`.
226 * This is useful for variable length arrays at the end of the
227 * structure.
228 *
229 * \hideinitializer
230 */
231#if defined(__has_attribute) && __has_attribute(cheriot_sealed) && 0
232# define DECLARE_STATIC_SEALED_VALUE_EXPLICIT_TYPE( \
233 type, valueType, compartment, keyName, name) \
234 extern valueType __sealed_type_placeholder_##name; \
235 typedef type __##name##_type \
236 __attribute__((cheriot_sealed(#compartment, #keyName))); \
237 extern __##name##_type name;
238#else
239# define DECLARE_STATIC_SEALED_VALUE_EXPLICIT_TYPE( \
240 type, valueType, compartment, keyName, name) \
241 /* Implementation detail: This declaration in the reserved namespace \
242 * exists only so that __typeof__ can be used later to determine the \
243 * original type. This will be removed once the compiler understands \
244 * static sealed objects natively. */ \
245 extern valueType __sealed_type_placeholder_##name; \
246 _Alignas(_Alignof(type) < 8 ? 8 : _Alignof(type)) extern __if_cxx( \
247 "C") struct __##name##_type \
248 { \
249 uint32_t key; \
250 uint32_t padding; \
251 type body; \
252 }(name); \
253 /* Make sure the type that we're casting this to is not bigger than \
254 * the value that we've emitted. */ \
255 _Static_assert(sizeof(__sealed_type_placeholder_##name) <= \
256 sizeof((name).body))
257#endif
258
259/**
260 * Forward-declare a static sealed object. This declares an
261 * object of type `type` that can be referenced with the
262 * `STATIC_SEALED_VALUE` macro using `name`. The pointer
263 * returned by the latter macro will be sealed with the sealing
264 * key exported from `compartment` as `keyName` with the
265 * `STATIC_SEALING_TYPE` macro.
266 *
267 * The object created with this macro can be accessed only by
268 * code that has access to the sealing key.
269 *
270 * \hideinitializer
271 */
272#define DECLARE_STATIC_SEALED_VALUE(type, compartment, keyName, name) \
273 DECLARE_STATIC_SEALED_VALUE_EXPLICIT_TYPE( \
274 type, type, compartment, keyName, name)
275
276/**
277 * Define a static sealed object. This creates an object of
278 * type `type`, initialised with `initialiser`, that can be
279 * referenced with the `STATIC_SEALED_VALUE` macro using `name`.
280 * The pointer returned by the latter macro will be sealed with
281 * the sealing key exported from `compartment` as `keyName` with
282 * the `STATIC_SEALING_TYPE` macro.
283 *
284 * The object created with this macro can be accessed only by
285 * code that has access to the sealing key.
286 *
287 * If you do not need a separate forward definition, use
288 * `DECLARE_AND_DEFINE_STATIC_SEALED_VALUE` instead.
289 *
290 * \hideinitializer
291 */
292#if defined(__has_attribute) && __has_attribute(cheriot_sealed) && 0
293# define DEFINE_STATIC_SEALED_VALUE( \
294 type, compartment, keyName, name, initialiser, ...) \
295 __if_cxx(inline) _Alignas(_Alignof(type) < 8 ? 8 : _Alignof(type)) \
296 __##name##_type name = {initialiser, ##__VA_ARGS__}
297#else
298# define DEFINE_STATIC_SEALED_VALUE( \
299 type, compartment, keyName, name, initialiser, ...) \
300 extern __if_cxx("C") int \
301 __sealing_key_##compartment##_##keyName __asm( \
302 "__export.sealing_type." #compartment "." #keyName); \
303 __attribute__((section(".sealed_objects"), used)) __if_cxx( \
304 inline) _Alignas(_Alignof(type) < 8 \
305 ? 8 \
306 : _Alignof(type)) struct __##name##_type \
307 name = /* NOLINT(bugprone-macro-parentheses) */ \
308 {(uint32_t)&__sealing_key_##compartment##_##keyName, \
309 0, \
310 {initialiser, ##__VA_ARGS__}}
311#endif
312
313/**
314 * Helper macro that declares and defines a sealed value.
315 *
316 * Unlike `DECLARE_AND_DEFINE_STATIC_SEALED_VALUE`, this allows
317 * the type that value should be read as to be different to the
318 * type used to initialise it. The type of the value in
319 * `STATIC_SEALED_VALUE` will be `CHERI_SEALED(valueType*)`.
320 * This is useful for variable length arrays at the end of the
321 * structure.
322 *
323 * \hideinitializer
324 */
325#define DECLARE_AND_DEFINE_STATIC_SEALED_VALUE_EXPLICIT_TYPE( \
326 type, valueType, compartment, keyName, name, initialiser, ...) \
327 DECLARE_STATIC_SEALED_VALUE_EXPLICIT_TYPE( \
328 type, \
329 valueType, \
330 compartment, \
331 keyName, \
332 name); /* NOLINT(bugprone-macro-parentheses) */ \
333 DEFINE_STATIC_SEALED_VALUE( \
334 type, \
335 compartment, \
336 keyName, \
337 name, \
338 initialiser, \
339 ##__VA_ARGS__); /* NOLINT(bugprone-macro-parentheses) */
340
341/**
342 * Helper macro that declares and defines a sealed value.
343 *
344 * The arguments for this are the same as `DEFINE_STATIC_SEALED_VALUE`. Use
345 * this version when you do not need a separate forward declaration.
346 *
347 * \hideinitializer
348 */
349#define DECLARE_AND_DEFINE_STATIC_SEALED_VALUE( \
350 type, compartment, keyName, name, initialiser, ...) \
351 DECLARE_AND_DEFINE_STATIC_SEALED_VALUE_EXPLICIT_TYPE( \
352 type, type, compartment, keyName, name, initialiser, ##__VA_ARGS__)
353
354/**
355 * Returns a sealed capability to the named object created with
356 * `DECLARE_STATIC_SEALED_VALUE`.
357 *
358 * \hideinitializer
359 */
360#if defined(__has_attribute) && __has_attribute(cheriot_sealed) && 0
361# define STATIC_SEALED_VALUE(name, ...) \
362 (CHERI_SEALED(__typeof__(__sealed_type_placeholder_##name) *))(&name)
363#else
364# define STATIC_SEALED_VALUE(name) \
365 ({ \
366 CHERI_SEALED(__typeof__(__sealed_type_placeholder_##name) *) \
367 ret; /* NOLINT(bugprone-macro-parentheses) */ \
368 __asm(".ifndef __import.sealed_object." #name "\n" \
369 " .type __import.sealed_object." #name ",@object\n" \
370 " .section .compartment_imports." #name \
371 ",\"awG\",@progbits," #name "\n" \
372 " .weak __import.sealed_object." #name "\n" \
373 " .p2align 3\n" \
374 "__import.sealed_object." #name ":\n" \
375 " .word " #name "\n" \
376 " .word %c1\n" \
377 " .size __import.sealed_object." #name ", 8\n" \
378 " .previous\n" \
379 ".endif\n" \
380 "1:" \
381 " auipcc %0," \
382 " " \
383 "%%cheriot_compartment_hi(__import.sealed_object." #name \
384 ")\n" \
385 " clc %0, %%cheriot_compartment_lo_i(1b)(%0)\n" \
386 : "=C"(ret) \
387 : "i"(sizeof(__typeof__(name)))); \
388 ret; \
389 })
390#endif
391
392/**
393 * Declare a CHERIoT initialiser function. This macro can be
394 * used in place of a prototype for a function called
395 * `function`. Initialiser functions are called in ascending
396 * priority order.
397 *
398 * Initialiser functions take no arguments and return `void`.
399 *
400 * \hideinitializer
401 */
402#define CHERIOT_INITIALISER(function, priority) \
403 __asm(" .section .cheriot_initialiser." #priority ",\"aR\",@progbits\n" \
404 " .p2align 3\n" \
405 " .word __export_" COMPARTMENT_NAME_STRING "_" #function "\n" \
406 " .word 0\n" \
407 " .previous\n"); \
408 __if_cxx(extern "C") __cheriot_callback \
409 [[cheriot::interrupt_state(disabled)]] void \
410 function(__if_c(void))
Helper macros used by compartment-macros.h.