| // Copyright Microsoft and CHERIoT Contributors. |
| // SPDX-License-Identifier: MIT |
| |
| #pragma once |
| #include <cdefs.h> |
| #include <stdbool.h> |
| |
| /** |
| * Helper macro for MMIO and pre-shared object imports, should not be used |
| * directly. |
| */ |
| #define IMPORT_CAPABILITY_WITH_PERMISSIONS_HELPER(type, \ |
| name, \ |
| prefix, \ |
| mangledName, \ |
| permitLoad, \ |
| permitStore, \ |
| permitLoadStoreCapabilities, \ |
| permitLoadMutable) \ |
| ({ \ |
| type *ret; /* NOLINT(bugprone-macro-parentheses) */ \ |
| __asm(".ifndef " mangledName "\n" \ |
| " .type " mangledName ",@object\n" \ |
| " .section .compartment_imports." #name \ |
| ",\"awG\",@progbits," #name ",comdat\n" \ |
| " .globl " mangledName "\n" \ |
| " .p2align 3\n" mangledName ":\n" \ |
| " .word " #prefix #name "\n" \ |
| " .word " #prefix #name "_end - " #prefix #name " + %c1\n" \ |
| " .size " mangledName ", 8\n" \ |
| " .previous\n" \ |
| ".endif\n" \ |
| "1:" \ |
| " auipcc %0," \ |
| " %%cheriot_compartment_hi(" mangledName ")\n" \ |
| " clc %0, %%cheriot_compartment_lo_i(1b)(%0)\n" \ |
| : "=C"(ret) \ |
| : "i"(((permitLoad) ? (1 << 31) : 0) + \ |
| ((permitStore) ? (1 << 30) : 0) + \ |
| ((permitLoadStoreCapabilities) ? (1 << 29) : 0) + \ |
| ((permitLoadMutable) ? (1 << 28) : 0))); \ |
| ret; \ |
| }) |
| |
| /** |
| * Helper macro, should not be used directly. |
| */ |
| #define MMIO_CAPABILITY_WITH_PERMISSIONS_HELPER(type, \ |
| name, \ |
| mangledName, \ |
| permitLoad, \ |
| permitStore, \ |
| permitLoadStoreCapabilities, \ |
| permitLoadMutable) \ |
| IMPORT_CAPABILITY_WITH_PERMISSIONS_HELPER(type, \ |
| name, \ |
| __export_mem_, \ |
| mangledName, \ |
| permitLoad, \ |
| permitStore, \ |
| permitLoadStoreCapabilities, \ |
| permitLoadMutable) |
| |
| /** |
| * Provide a capability of the type `volatile type *` referring to the MMIO |
| * region exported in the linker script with `name` as its name. This macro |
| * can be used only in code (it cannot be used to initialise a global). |
| * |
| * The last arguments specify the set of permissions that this capability |
| * holds. MMIO capabilities are always global and without store local. They |
| * may optionally omit additional capabilities. |
| */ |
| #define MMIO_CAPABILITY_WITH_PERMISSIONS(type, \ |
| name, \ |
| permitLoad, \ |
| permitStore, \ |
| permitLoadStoreCapabilities, \ |
| permitLoadMutable) \ |
| MMIO_CAPABILITY_WITH_PERMISSIONS_HELPER( \ |
| volatile type, /* NOLINT(bugprone-macro-parentheses) */ \ |
| name, \ |
| "__import_mem_" #name "_" #permitLoad "_" #permitStore \ |
| "_" #permitLoadStoreCapabilities "_" #permitLoadMutable, \ |
| permitLoad, \ |
| permitStore, \ |
| permitLoadStoreCapabilities, \ |
| permitLoadMutable) |
| |
| /** |
| * Provide a capability of the type `volatile type *` referring to the MMIO |
| * region exported in the linker script with `name` as its name. This macro |
| * can be used only in code (it cannot be used to initialise a global). |
| * |
| * MMIO capabilities produced by this macro have load and store permissions but |
| * cannot hold capabilities. For richer permissions use |
| * MMIO_CAPABILITY_WITH_PERMISSIONS. |
| */ |
| #define MMIO_CAPABILITY(type, name) \ |
| MMIO_CAPABILITY_WITH_PERMISSIONS(type, name, true, true, false, false) |
| |
| /** |
| * Provide a capability of the type `type *` referring to the pre-shared object |
| * with `name` as its name. This macro can be used only in code (it cannot be |
| * used to initialise a global). |
| * |
| * The last arguments specify the set of permissions that this capability |
| * holds. Pre-shared objects are always global and without store local. They |
| * may optionally omit additional permissions. |
| */ |
| #define SHARED_OBJECT_WITH_PERMISSIONS(type, \ |
| name, \ |
| permitLoad, \ |
| permitStore, \ |
| permitLoadStoreCapabilities, \ |
| permitLoadMutable) \ |
| IMPORT_CAPABILITY_WITH_PERMISSIONS_HELPER( \ |
| type, /* NOLINT(bugprone-macro-parentheses) */ \ |
| name, \ |
| __cheriot_shared_object_, \ |
| "__import_cheriot_shared_object_" #name "_" #permitLoad "_" #permitStore \ |
| "_" #permitLoadStoreCapabilities "_" #permitLoadMutable, \ |
| permitLoad, \ |
| permitStore, \ |
| permitLoadStoreCapabilities, \ |
| permitLoadMutable) |
| |
| /** |
| * Provide a capability of the type `type *` referring to the pre-shared object |
| * with `name` as its name. This macro can be used only in code (it cannot be |
| * used to initialise a global). |
| * |
| * Pre-shared object capabilities produced by this macro have load, store, |
| * load-mutable, and load/store-capability permissions. To define a reduced |
| * set of permissions use `SHARED_OBJECT_WITH_PERMISSIONS`. |
| */ |
| #define SHARED_OBJECT(type, name) \ |
| SHARED_OBJECT_WITH_PERMISSIONS(type, name, true, true, true, true) |
| |
| /** |
| * Macro to test whether a device with a specific name exists in the board |
| * definition for the current target. |
| */ |
| #define DEVICE_EXISTS(x) defined(DEVICE_EXISTS_##x) |
| |
| /** |
| * Helper macro, used by `STATIC_SEALING_TYPE`. Do not use this directly, it |
| * exists to avoid error-prone copying and pasting of the mangled name for a |
| * static sealing type. |
| */ |
| #define CHERIOT_EMIT_STATIC_SEALING_TYPE(name) \ |
| ({ \ |
| SKey ret; /* NOLINT(bugprone-macro-parentheses) */ \ |
| __asm( \ |
| ".ifndef __import." name "\n" \ |
| " .type __import." name ",@object\n" \ |
| " .section .compartment_imports." name ",\"awG\",@progbits," name \ |
| ",comdat\n" \ |
| " .globl __import." name "\n" \ |
| " .p2align 3\n" \ |
| "__import." name ":\n" \ |
| " .word __export." name "\n" \ |
| " .word 0\n" \ |
| " .previous\n" \ |
| ".endif\n" \ |
| ".ifndef __export." name "\n" \ |
| " .type __export." name ",@object\n" \ |
| " .section .compartment_exports." name ",\"awG\",@progbits," name \ |
| ",comdat\n" \ |
| " .globl __export." name "\n" \ |
| " .p2align 2\n" \ |
| "__export." name ":\n" \ |
| " .half 0\n" /* function start and stack size initialised to 0 */ \ |
| " .byte 0\n" \ |
| " .byte 0b100000\n" /* Set the flag that indicates that this is a \ |
| sealing key. */ \ |
| " .size __export." name ", 4\n" \ |
| " .previous\n" \ |
| ".endif\n" \ |
| "1:\n" \ |
| " auipcc %0, %%cheriot_compartment_hi(__import." name ")\n" \ |
| " clc %0, %%cheriot_compartment_lo_i(1b)(%0)\n" \ |
| : "=C"(ret)); \ |
| ret; \ |
| }) |
| |
| /** |
| * Helper macro that evaluates to the compartment of the current compilation |
| * unit, as a string. |
| */ |
| #define COMPARTMENT_NAME_STRING __XSTRING(__CHERI_COMPARTMENT__) |
| |
| /** |
| * Macro that evaluates to a static sealing type that is local to this |
| * compartment. |
| */ |
| #define STATIC_SEALING_TYPE(name) \ |
| CHERIOT_EMIT_STATIC_SEALING_TYPE("sealing_type." COMPARTMENT_NAME_STRING \ |
| "." #name) |
| |
| /** |
| * Forward-declare a static sealed object. This declares an object of type |
| * `type` that can be referenced with the `STATIC_SEALED_VALUE` macro using |
| * `name`. The pointer returned by the latter macro will be sealed with the |
| * sealing key exported from `compartment` as `keyName` with the |
| * `STATIC_SEALING_TYPE` macro. |
| * |
| * The object created with this macro can be accessed only by code that has |
| * access to the sealing key. |
| */ |
| #define DECLARE_STATIC_SEALED_VALUE(type, compartment, keyName, name) \ |
| struct __##name##_type; /* NOLINT(bugprone-macro-parentheses) */ \ |
| extern __if_cxx("C") struct __##name##_type \ |
| { \ |
| uint32_t key; \ |
| uint32_t padding; \ |
| type body; \ |
| } name /* NOLINT(bugprone-macro-parentheses) */ |
| |
| /** |
| * Define a static sealed object. This creates an object of type `type`, |
| * initialised with `initialiser`, that can be referenced with the |
| * `STATIC_SEALED_VALUE` macro using `name`. The pointer returned by the |
| * latter macro will be sealed with the sealing key exported from `compartment` |
| * as `keyName` with the `STATIC_SEALING_TYPE` macro. |
| * |
| * The object created with this macro can be accessed only by code that has |
| * access to the sealing key. |
| */ |
| #define DEFINE_STATIC_SEALED_VALUE( \ |
| type, compartment, keyName, name, initialiser, ...) \ |
| extern __if_cxx("C") int __sealing_key_##compartment##_##keyName __asm( \ |
| "__export.sealing_type." #compartment "." #keyName); \ |
| __attribute__((section(".sealed_objects"), used)) \ |
| __if_cxx(inline) struct __##name##_type \ |
| name = /* NOLINT(bugprone-macro-parentheses) */ \ |
| {(uint32_t)&__sealing_key_##compartment##_##keyName, \ |
| 0, \ |
| {initialiser, ##__VA_ARGS__}} |
| |
| /** |
| * Helper macro that declares and defines a sealed value. |
| */ |
| #define DECLARE_AND_DEFINE_STATIC_SEALED_VALUE( \ |
| type, compartment, keyName, name, initialiser, ...) \ |
| DECLARE_STATIC_SEALED_VALUE( \ |
| type, \ |
| compartment, \ |
| keyName, \ |
| name); /* NOLINT(bugprone-macro-parentheses) */ \ |
| DEFINE_STATIC_SEALED_VALUE( \ |
| type, \ |
| compartment, \ |
| keyName, \ |
| name, \ |
| initialiser, \ |
| ##__VA_ARGS__); /* NOLINT(bugprone-macro-parentheses) */ |
| |
| /** |
| * Returns a sealed capability to the named object created with |
| * `DECLARE_STATIC_SEALED_VALUE`. |
| */ |
| #define STATIC_SEALED_VALUE(name) \ |
| ({ \ |
| struct SObjStruct *ret; /* NOLINT(bugprone-macro-parentheses) */ \ |
| __asm(".ifndef __import.sealed_object." #name "\n" \ |
| " .type __import.sealed_object." #name ",@object\n" \ |
| " .section .compartment_imports." #name \ |
| ",\"awG\",@progbits," #name ",comdat\n" \ |
| " .globl __import.sealed_object." #name "\n" \ |
| " .p2align 3\n" \ |
| "__import.sealed_object." #name ":\n" \ |
| " .word " #name "\n" \ |
| " .word %c1\n" \ |
| " .size __import.sealed_object." #name ", 8\n" \ |
| " .previous\n" \ |
| ".endif\n" \ |
| "1:" \ |
| " auipcc %0," \ |
| " %%cheriot_compartment_hi(__import.sealed_object." #name \ |
| ")\n" \ |
| " clc %0, %%cheriot_compartment_lo_i(1b)(%0)\n" \ |
| : "=C"(ret) \ |
| : "i"(sizeof(__typeof__(name)))); \ |
| ret; \ |
| }) |