|  | // Copyright Microsoft and CHERIoT Contributors. | 
|  | // SPDX-License-Identifier: MIT | 
|  |  | 
|  | #pragma once | 
|  | #include <__debug.h> | 
|  | #include <cheri.hh> | 
|  | #include <compartment.h> | 
|  | #include <concepts> | 
|  | #include <cstddef> | 
|  | #include <platform-uart.hh> | 
|  | #include <string.h> | 
|  | #include <switcher.h> | 
|  |  | 
|  | #include <array> | 
|  | #include <string_view> | 
|  | #include <type_traits> | 
|  |  | 
|  | namespace DebugConcepts | 
|  | { | 
|  | /// Helper concept for matching booleans | 
|  | template<typename T> | 
|  | concept IsBool = std::is_same_v<T, bool>; | 
|  |  | 
|  | /// Helper concept for matching enumerations. | 
|  | template<typename T> | 
|  | concept IsEnum = std::is_enum_v<T>; | 
|  |  | 
|  | /// Concept for something that can be lazily called to produce a bool. | 
|  | template<typename T> | 
|  | concept LazyAssertion = requires(T v) | 
|  | { | 
|  | { | 
|  | v() | 
|  | } -> IsBool; | 
|  | }; | 
|  |  | 
|  | template<typename T> | 
|  | concept IsPointerButNotCString = | 
|  | std::is_pointer_v<T> && !std::is_same_v<T, const char *>; | 
|  |  | 
|  | template<typename T> | 
|  | concept IsConvertibleToAddress = | 
|  | std::convertible_to<T, ptraddr_t> && !IsEnum<T>; | 
|  |  | 
|  | } // namespace DebugConcepts | 
|  |  | 
|  | /** | 
|  | * Abstract class for writing debug output.  This is used for custom output. | 
|  | * | 
|  | * This may be changed in the future to provide better support for custom | 
|  | * formatting. | 
|  | */ | 
|  | struct DebugWriter | 
|  | { | 
|  | /** | 
|  | * Write a single character. | 
|  | */ | 
|  | virtual void write(char) = 0; | 
|  | /** | 
|  | * Write a C string. | 
|  | */ | 
|  | virtual void write(const char *) = 0; | 
|  | /** | 
|  | * Write a string view. | 
|  | */ | 
|  | virtual void write(std::string_view) = 0; | 
|  | /** | 
|  | * Write a 32-bit unsigned integer. | 
|  | */ | 
|  | virtual void write(uint32_t) = 0; | 
|  | /** | 
|  | * Write a 32-bit signed integer. | 
|  | */ | 
|  | virtual void write(int32_t) = 0; | 
|  | /** | 
|  | * Write a 64-bit unsigned integer. | 
|  | */ | 
|  | virtual void write(uint64_t) = 0; | 
|  | /** | 
|  | * Write a 64-bit signed integer. | 
|  | */ | 
|  | virtual void write(int64_t) = 0; | 
|  | }; | 
|  |  | 
|  | /** | 
|  | * Helper function for writing enumerations.  Enumerations are written using | 
|  | * magic_enum to provide a string and then a numeric value. | 
|  | */ | 
|  | template<typename T> | 
|  | void debug_enum_helper(uintptr_t    value, | 
|  | DebugWriter &writer) requires DebugConcepts::IsEnum<T> | 
|  | { | 
|  | writer.write(magic_enum::enum_name<T>(static_cast<T>(value))); | 
|  | writer.write('('); | 
|  | writer.write(uint32_t(value)); | 
|  | writer.write(')'); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Callback for custom types in debug output.  This should use the second | 
|  | * argument to write the first argument to the debug output. | 
|  | */ | 
|  | using DebugCallback = void (*)(uintptr_t, DebugWriter &); | 
|  |  | 
|  | /** | 
|  | * Adaptor that turns an argument of type `T` into a `DebugFormatArgument`. | 
|  | * | 
|  | * Users may specialise these to provide custom formatters.  See the | 
|  | * specialisation for enumerations for an example. | 
|  | */ | 
|  | template<typename T> | 
|  | struct DebugFormatArgumentAdaptor; | 
|  |  | 
|  | /** | 
|  | * Boolean specialisation, prints "true" or "false". | 
|  | */ | 
|  | template<> | 
|  | struct DebugFormatArgumentAdaptor<bool> | 
|  | { | 
|  | __always_inline static DebugFormatArgument construct(bool value) | 
|  | { | 
|  | return {static_cast<uintptr_t>(value), | 
|  | DebugFormatArgumentKind::DebugFormatArgumentBool}; | 
|  | } | 
|  | }; | 
|  |  | 
|  | /** | 
|  | * Character specialisation, prints the character. | 
|  | */ | 
|  | template<> | 
|  | struct DebugFormatArgumentAdaptor<char> | 
|  | { | 
|  | __always_inline static DebugFormatArgument construct(char value) | 
|  | { | 
|  | return {static_cast<uintptr_t>(value), | 
|  | DebugFormatArgumentKind::DebugFormatArgumentCharacter}; | 
|  | } | 
|  | }; | 
|  |  | 
|  | /** | 
|  | * Unsigned character specialisation, prints the character as a hex number. | 
|  | */ | 
|  | template<> | 
|  | struct DebugFormatArgumentAdaptor<uint8_t> | 
|  | { | 
|  | __always_inline static DebugFormatArgument construct(uint8_t value) | 
|  | { | 
|  | return {static_cast<uintptr_t>(value), | 
|  | DebugFormatArgumentKind::DebugFormatArgumentUnsignedNumber32}; | 
|  | } | 
|  | }; | 
|  |  | 
|  | /** | 
|  | * Unsigned 16-bit integer specialisation, prints the integer as a hex number. | 
|  | */ | 
|  | template<> | 
|  | struct DebugFormatArgumentAdaptor<uint16_t> | 
|  | { | 
|  | __always_inline static DebugFormatArgument construct(uint16_t value) | 
|  | { | 
|  | return {static_cast<uintptr_t>(value), | 
|  | DebugFormatArgumentKind::DebugFormatArgumentUnsignedNumber32}; | 
|  | } | 
|  | }; | 
|  |  | 
|  | /** | 
|  | * Unsigned 32-bit integer specialisation, prints the integer as a hex number. | 
|  | */ | 
|  | template<> | 
|  | struct DebugFormatArgumentAdaptor<uint32_t> | 
|  | { | 
|  | __always_inline static DebugFormatArgument construct(uint32_t value) | 
|  | { | 
|  | return {static_cast<uintptr_t>(value), | 
|  | DebugFormatArgumentKind::DebugFormatArgumentUnsignedNumber32}; | 
|  | } | 
|  | }; | 
|  |  | 
|  | /** | 
|  | * Unsigned 64-bit integer specialisation, prints the integer as a hex number. | 
|  | * | 
|  | * All smaller sizes are handled by zero extending to 32 bits.  We treat 64-bit | 
|  | * separately because it requires decomposing the two halves for printing and | 
|  | * that's redundant overhead for the majority of cases. | 
|  | */ | 
|  | template<> | 
|  | struct DebugFormatArgumentAdaptor<uint64_t> | 
|  | { | 
|  | __always_inline static DebugFormatArgument construct(uint64_t value) | 
|  | { | 
|  | uintptr_t fudgedValue; | 
|  | memcpy(&fudgedValue, &value, sizeof(fudgedValue)); | 
|  | return {fudgedValue, | 
|  | DebugFormatArgumentKind::DebugFormatArgumentUnsignedNumber64}; | 
|  | } | 
|  | }; | 
|  |  | 
|  | /** | 
|  | * Signed 8-bit integer specialisations, print the integer as a decimal number. | 
|  | */ | 
|  | template<> | 
|  | struct DebugFormatArgumentAdaptor<int8_t> | 
|  | { | 
|  | __always_inline static DebugFormatArgument construct(int8_t value) | 
|  | { | 
|  | return {static_cast<uintptr_t>(value), | 
|  | DebugFormatArgumentKind::DebugFormatArgumentSignedNumber32}; | 
|  | } | 
|  | }; | 
|  |  | 
|  | /** | 
|  | * Signed 16-bit integer specialisations, print the integer as a decimal number. | 
|  | */ | 
|  | template<> | 
|  | struct DebugFormatArgumentAdaptor<int16_t> | 
|  | { | 
|  | __always_inline static DebugFormatArgument construct(int16_t value) | 
|  | { | 
|  | return {static_cast<uintptr_t>(value), | 
|  | DebugFormatArgumentKind::DebugFormatArgumentSignedNumber32}; | 
|  | } | 
|  | }; | 
|  |  | 
|  | /** | 
|  | * Signed 32-bit integer specialisations, print the integer as a decimal number. | 
|  | */ | 
|  | template<> | 
|  | struct DebugFormatArgumentAdaptor<int32_t> | 
|  | { | 
|  | __always_inline static DebugFormatArgument construct(int32_t value) | 
|  | { | 
|  | return {static_cast<uintptr_t>(value), | 
|  | DebugFormatArgumentKind::DebugFormatArgumentSignedNumber32}; | 
|  | } | 
|  | }; | 
|  |  | 
|  | /** | 
|  | * Signed 64-bit integer specialisations, print the integer as a decimal number. | 
|  | * | 
|  | * All smaller sizes are handled by sign extending to 32 bits.  We treat 64-bit | 
|  | * separately because it requires 64-bit division to convert a 64-bit integer | 
|  | * to a decimal and that, in turn, requires libcalls. | 
|  | */ | 
|  | template<> | 
|  | struct DebugFormatArgumentAdaptor<int64_t> | 
|  | { | 
|  | __always_inline static DebugFormatArgument construct(int64_t value) | 
|  | { | 
|  | static_assert(sizeof(uintptr_t) == sizeof(uint64_t)); | 
|  | uintptr_t fudgedValue; | 
|  | memcpy(&fudgedValue, &value, sizeof(fudgedValue)); | 
|  | return {fudgedValue, | 
|  | DebugFormatArgumentKind::DebugFormatArgumentSignedNumber64}; | 
|  | } | 
|  | }; | 
|  |  | 
|  | /** | 
|  | * C string specialisation, prints the string as-is. | 
|  | */ | 
|  | template<> | 
|  | struct DebugFormatArgumentAdaptor<const char *> | 
|  | { | 
|  | __always_inline static DebugFormatArgument construct(const char *value) | 
|  | { | 
|  | return {reinterpret_cast<uintptr_t>(value), | 
|  | DebugFormatArgumentKind::DebugFormatArgumentCString}; | 
|  | } | 
|  | }; | 
|  |  | 
|  | /** | 
|  | * String view specialisation, prints the string as-is. | 
|  | * | 
|  | * Note that this relies on the string view persisting for the duration of the | 
|  | * call.  It passes a pointer to the string-view argument. | 
|  | */ | 
|  | template<> | 
|  | struct DebugFormatArgumentAdaptor<std::string_view> | 
|  | { | 
|  | __always_inline static DebugFormatArgument | 
|  | construct(std::string_view &value) | 
|  | { | 
|  | return {reinterpret_cast<uintptr_t>(&value), | 
|  | DebugFormatArgumentKind::DebugFormatArgumentStringView}; | 
|  | } | 
|  | }; | 
|  |  | 
|  | /** | 
|  | * String view specialisation, use the C string handler. | 
|  | */ | 
|  | template<> | 
|  | struct DebugFormatArgumentAdaptor<std::string> | 
|  | { | 
|  | __always_inline static DebugFormatArgument construct(std::string &value) | 
|  | { | 
|  | return DebugFormatArgumentAdaptor<const char *>::construct( | 
|  | value.c_str()); | 
|  | } | 
|  | }; | 
|  |  | 
|  | /** | 
|  | * Enum specialisation, prints the enum as a string and then the numeric value. | 
|  | * | 
|  | * This specialisation uses the generic printing facility in the library call | 
|  | * and passes a callback that will map the enumeration to a string. | 
|  | */ | 
|  | template<DebugConcepts::IsEnum T> | 
|  | struct DebugFormatArgumentAdaptor<T> | 
|  | { | 
|  | __always_inline static DebugFormatArgument construct(T value) | 
|  | { | 
|  | #ifdef CHERIOT_AVOID_CAPRELOCS | 
|  | return {static_cast<uintptr_t>(value), | 
|  | DebugFormatArgumentKind::DebugFormatArgumentUnsignedNumber32}; | 
|  | #else | 
|  | return {static_cast<uintptr_t>(value), | 
|  | reinterpret_cast<uintptr_t>(&debug_enum_helper<T>)}; | 
|  | #endif | 
|  | } | 
|  | }; | 
|  |  | 
|  | /** | 
|  | * Permission set specialisation. | 
|  | */ | 
|  | template<> | 
|  | struct DebugFormatArgumentAdaptor<CHERI::PermissionSet> | 
|  | { | 
|  | __always_inline static DebugFormatArgument | 
|  | construct(CHERI::PermissionSet value) | 
|  | { | 
|  | return {static_cast<uintptr_t>(value.as_raw()), | 
|  | DebugFormatArgumentKind::DebugFormatArgumentPermissionSet}; | 
|  | } | 
|  | }; | 
|  |  | 
|  | /** | 
|  | * Null pointer specialisation. | 
|  | */ | 
|  | template<> | 
|  | struct DebugFormatArgumentAdaptor<std::nullptr_t> | 
|  | { | 
|  | __always_inline static DebugFormatArgument construct(std::nullptr_t) | 
|  | { | 
|  | return {0, DebugFormatArgumentKind::DebugFormatArgumentPointer}; | 
|  | } | 
|  | }; | 
|  |  | 
|  | /** | 
|  | * Pointer specialisation, prints the pointer as a full capability. | 
|  | */ | 
|  | template<DebugConcepts::IsPointerButNotCString T> | 
|  | struct DebugFormatArgumentAdaptor<T> | 
|  | { | 
|  | __always_inline static DebugFormatArgument construct(T value) | 
|  | { | 
|  | return {reinterpret_cast<uintptr_t>( | 
|  | static_cast<const volatile void *>(value)), | 
|  | DebugFormatArgumentKind::DebugFormatArgumentPointer}; | 
|  | } | 
|  | }; | 
|  |  | 
|  | /** | 
|  | * Specialisation for the CHERI capability wrapper class, prints the capability | 
|  | * in the same format as bare pointers. | 
|  | */ | 
|  | template<typename T> | 
|  | struct DebugFormatArgumentAdaptor<CHERI::Capability<T>> | 
|  | { | 
|  | __always_inline static DebugFormatArgument | 
|  | construct(CHERI::Capability<T> value) | 
|  | { | 
|  | return {reinterpret_cast<uintptr_t>( | 
|  | static_cast<const volatile void *>(value)), | 
|  | DebugFormatArgumentKind::DebugFormatArgumentPointer}; | 
|  | } | 
|  | }; | 
|  |  | 
|  | /** | 
|  | * Specialisation for things that can be converted to addresses, prints as an | 
|  | * unsigned number. | 
|  | */ | 
|  | template<DebugConcepts::IsConvertibleToAddress T> | 
|  | struct DebugFormatArgumentAdaptor<T> | 
|  | { | 
|  | __always_inline static DebugFormatArgument construct(ptraddr_t value) | 
|  | { | 
|  | return {static_cast<uintptr_t>(value), | 
|  |  | 
|  | DebugFormatArgumentKind::DebugFormatArgumentUnsignedNumber32}; | 
|  | } | 
|  | }; | 
|  |  | 
|  | /** | 
|  | * Recursive helper that maps from a tuple representing the arguments into a | 
|  | * type-erased array. | 
|  | */ | 
|  | template<size_t I> | 
|  | __always_inline inline void map_debug_argument(DebugFormatArgument *arguments, | 
|  | auto &&argumentTuple) | 
|  | { | 
|  | arguments[I] = | 
|  | DebugFormatArgumentAdaptor< | 
|  | std::remove_cvref_t<decltype(std::get<I>(argumentTuple))>>{} | 
|  | .construct(std::get<I>(argumentTuple)); | 
|  | if constexpr (I > 0) | 
|  | { | 
|  | map_debug_argument<I - 1>(arguments, argumentTuple); | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Convert `args` into a type-erased array of `DebugFormatArgument`s in | 
|  | * `arguments`. | 
|  | */ | 
|  | template<typename... Args> | 
|  | __always_inline inline void | 
|  | make_debug_arguments_list(DebugFormatArgument *arguments, Args... args) | 
|  | { | 
|  | if constexpr (sizeof...(Args) > 0) | 
|  | { | 
|  | map_debug_argument<sizeof...(Args) - 1>(arguments, | 
|  | std::forward_as_tuple(args...)); | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Library function that writes a debug message.  This runs with interrupts | 
|  | * disabled (to avoid interleaving) and prints an array of debug messages. This | 
|  | * is intended to allow a single call to print multiple format strings without | 
|  | * requiring the format strings to be copied, so that the debugging APIs can | 
|  | * wrap a user-provided format string. | 
|  | */ | 
|  | __cheri_libcall void debug_log_message_write(const char          *context, | 
|  | const char          *format, | 
|  | DebugFormatArgument *messages, | 
|  | size_t               messageCount); | 
|  |  | 
|  | __cheri_libcall void debug_report_failure(const char          *kind, | 
|  | const char          *file, | 
|  | const char          *function, | 
|  | int                  line, | 
|  | const char          *fmt, | 
|  | DebugFormatArgument *arguments, | 
|  | size_t               argumentCount); | 
|  |  | 
|  | namespace | 
|  | { | 
|  | /** | 
|  | * Helper class wrapping a string for use as a template argument.  This is | 
|  | * used to describe a context for conditional debugging that will be | 
|  | * prefixed to debug lines. | 
|  | */ | 
|  | template<size_t N> | 
|  | struct DebugContext | 
|  | { | 
|  | /** | 
|  | * Constructor, captures the string argument. | 
|  | */ | 
|  | constexpr DebugContext(const char (&str)[N]) | 
|  | { | 
|  | std::copy_n(str, N, value); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Implicit conversion to a C string. | 
|  | */ | 
|  | constexpr operator const char *() const | 
|  | { | 
|  | return value; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * The captured string.  Must be public for this class to meet the | 
|  | * requirements of a structural type for use as a template argument. | 
|  | */ | 
|  | char value[N]; | 
|  | }; | 
|  |  | 
|  | /** | 
|  | * Our libc++ does not currently provide source_location, but our clang | 
|  | * provides the necessary builtins for one. | 
|  | */ | 
|  | struct SourceLocation | 
|  | { | 
|  | /** | 
|  | * Explicitly construct a source location. | 
|  | */ | 
|  | constexpr SourceLocation(int         lineNumber, | 
|  | int         columnNumber, | 
|  | const char *fileName, | 
|  | const char *functionName) | 
|  | : lineNumber(lineNumber), | 
|  | columnNumber(columnNumber), | 
|  | fileName(fileName), | 
|  | functionName(functionName) | 
|  | { | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Construct a source location for the caller. | 
|  | */ | 
|  | static constexpr SourceLocation __always_inline | 
|  | current(int         lineNumber   = __builtin_LINE(), | 
|  | int         columnNumber = __builtin_COLUMN(), | 
|  | const char *fileName     = __builtin_FILE(), | 
|  | const char *functionName = __builtin_FUNCTION()) noexcept | 
|  | { | 
|  | return {lineNumber, columnNumber, fileName, functionName}; | 
|  | } | 
|  |  | 
|  | /// Returns the line number for this source location. | 
|  | [[nodiscard]] __always_inline constexpr int line() const noexcept | 
|  | { | 
|  | return lineNumber; | 
|  | } | 
|  | /// Returns the column number for this source location. | 
|  | [[nodiscard]] __always_inline constexpr int column() const noexcept | 
|  | { | 
|  | return columnNumber; | 
|  | } | 
|  | /// Returns the file name for this source location. | 
|  | [[nodiscard]] __always_inline constexpr const char * | 
|  | file_name() const noexcept | 
|  | { | 
|  | return fileName; | 
|  | } | 
|  | /// Returns the function name for this source location. | 
|  | [[nodiscard]] __always_inline constexpr const char * | 
|  | function_name() const noexcept | 
|  | { | 
|  | return functionName; | 
|  | } | 
|  |  | 
|  | private: | 
|  | /// The line number of this source location. | 
|  | int lineNumber; | 
|  | /// The column number of this source location. | 
|  | int columnNumber; | 
|  | /// The file name of this source location. | 
|  | const char *fileName; | 
|  | /// The function name of this source location. | 
|  | const char *functionName; | 
|  | }; | 
|  |  | 
|  | /** | 
|  | * Conditional debug class.  Used to control conditional output and | 
|  | * assertion checking.  Enables debug log messages and assertions if | 
|  | * `Enabled` is true.  Uses `Context` to print additional detail on debug | 
|  | * lines.  Each line is prefixed with the context string in magenta to make | 
|  | * it easy to see debug output from different subsystems in the same trace. | 
|  | * | 
|  | * This class is expected to be used as a type alias, something like: | 
|  | * | 
|  | * ```c++ | 
|  | * constexpr bool DebugFoo = DEBUG_FOO; | 
|  | * using Debug = ConditionalDebug<DebugFoo, "Foo">; | 
|  | * ``` | 
|  | */ | 
|  | template<bool Enabled, DebugContext Context> | 
|  | class ConditionalDebug | 
|  | { | 
|  | public: | 
|  | /** | 
|  | * Log a message. | 
|  | * | 
|  | * This function does nothing if the `Enabled` condition is false. | 
|  | */ | 
|  | template<typename... Args> | 
|  | static void log(const char *fmt, Args... args) | 
|  | { | 
|  | if constexpr (Enabled) | 
|  | { | 
|  | asm volatile("" ::: "memory"); | 
|  | DebugFormatArgument arguments[sizeof...(Args)]; | 
|  | make_debug_arguments_list(arguments, args...); | 
|  | const char *context = Context; | 
|  | debug_log_message_write( | 
|  | context, fmt, arguments, sizeof...(Args)); | 
|  | asm volatile("" ::: "memory"); | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Helper to report failure. | 
|  | * | 
|  | * This must not take the `SourceLocation` directly because doing so | 
|  | * prevents the compiler from decomposing and subsequently | 
|  | * constant-propagating its fields in the caller, resulting in 24 bytes | 
|  | * of stack space consumed for every assert or invariant. | 
|  | */ | 
|  | template<typename... Args> | 
|  | static inline void report_failure(const char *kind, | 
|  | const char *file, | 
|  | const char *function, | 
|  | int         line, | 
|  | const char *fmt, | 
|  | Args... args) | 
|  | { | 
|  | // Ensure that the compiler does not reorder messages. | 
|  | DebugFormatArgument arguments[sizeof...(Args)]; | 
|  | make_debug_arguments_list(arguments, args...); | 
|  | const char *context = Context; | 
|  | debug_report_failure( | 
|  | kind, file, function, line, fmt, arguments, sizeof...(Args)); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Function-like class for invariants that is expected to be used via | 
|  | * the deduction guide as: | 
|  | * | 
|  | * ConditionalDebug::Invariant(someCondition, "A message...", ...); | 
|  | * | 
|  | * Invariants are checked unconditionally but will log a verbose | 
|  | * message only if `Enabled` is true. | 
|  | */ | 
|  | template<typename... Args> | 
|  | struct Invariant | 
|  | { | 
|  | /** | 
|  | * Constructor, performs the invariant check. | 
|  | */ | 
|  | __always_inline | 
|  | Invariant(bool        condition, | 
|  | const char *fmt, | 
|  | Args... args, | 
|  | SourceLocation loc = SourceLocation::current()) | 
|  | { | 
|  | if (__predict_false(!condition)) | 
|  | { | 
|  | if constexpr (Enabled) | 
|  | { | 
|  | report_failure("Invariant", | 
|  | loc.file_name(), | 
|  | loc.function_name(), | 
|  | loc.line(), | 
|  | fmt, | 
|  | std::forward<Args>(args)...); | 
|  | } | 
|  | __builtin_trap(); | 
|  | } | 
|  | } | 
|  | }; | 
|  | /** | 
|  | * Function-like class for assertions that is expected to be used via | 
|  | * the deduction guide as: | 
|  | * | 
|  | * ConditionalDebug::Assert(someCondition, "A message...", ...); | 
|  | * | 
|  | * Assertions are checked only if `Enabled` is true. | 
|  | */ | 
|  | template<typename... Args> | 
|  | struct Assert | 
|  | { | 
|  | /** | 
|  | * Constructor, performs the assertion check. | 
|  | */ | 
|  | template<typename T> | 
|  | requires DebugConcepts::IsBool<T> __always_inline | 
|  | Assert(T           condition, | 
|  | const char *fmt, | 
|  | Args... args, | 
|  | SourceLocation loc = SourceLocation::current()) | 
|  | { | 
|  | if constexpr (Enabled) | 
|  | { | 
|  | if (__predict_false(!condition)) | 
|  | { | 
|  | report_failure("Assertion", | 
|  | loc.file_name(), | 
|  | loc.function_name(), | 
|  | loc.line(), | 
|  | fmt, | 
|  | std::forward<Args>(args)...); | 
|  | __builtin_trap(); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Constructor, performs an assertion check. | 
|  | * | 
|  | * This version is passed a lambda that returns a bool, rather than | 
|  | * a boolean condition.  This allows the compiler to completely | 
|  | * elide the contents of the lambda in builds where this component | 
|  | * is not being debugged.  This version should be used for places | 
|  | * where the assertion condition has side effects. | 
|  | */ | 
|  | template<typename T> | 
|  | requires DebugConcepts::LazyAssertion<T> __always_inline | 
|  | Assert(T         &&condition, | 
|  | const char *fmt, | 
|  | Args... args, | 
|  | SourceLocation loc = SourceLocation::current()) | 
|  | { | 
|  | if constexpr (Enabled) | 
|  | { | 
|  | if (__predict_false(!condition())) | 
|  | { | 
|  | report_failure("Assertion", | 
|  | loc.file_name(), | 
|  | loc.function_name(), | 
|  | loc.line(), | 
|  | fmt, | 
|  | std::forward<Args>(args)...); | 
|  | __builtin_trap(); | 
|  | } | 
|  | } | 
|  | } | 
|  | }; | 
|  |  | 
|  | /** | 
|  | * Deduction guide, allows `Invariant` to be used as if it were a | 
|  | * function. | 
|  | */ | 
|  | template<typename T, typename... Ts> | 
|  | Invariant(T, const char *, Ts &&...) -> Invariant<Ts...>; | 
|  |  | 
|  | /** | 
|  | * Overt wrapper function around Invariant.  Sometimes template | 
|  | * deduction guides just don't cut it.  At the cost of a | 
|  | * std::make_tuple at the call site, we can still take advantage | 
|  | * of much of the machinery here. | 
|  | */ | 
|  | template<typename T, typename... Args> | 
|  | __always_inline static void | 
|  | invariant(T                   condition, | 
|  | const char         *fmt, | 
|  | std::tuple<Args...> args = std::make_tuple(), | 
|  | SourceLocation      loc  = SourceLocation::current()) | 
|  | { | 
|  | std::apply( | 
|  | [&](Args... iargs) { | 
|  | Invariant<Args...>( | 
|  | condition, fmt, std::forward<Args>(iargs)..., loc); | 
|  | }, | 
|  | args); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Deduction guide, allows `Assert` to be used as if it were a | 
|  | * function. | 
|  | */ | 
|  | template<typename... Ts> | 
|  | Assert(bool, const char *, Ts &&...) -> Assert<Ts...>; | 
|  |  | 
|  | /** | 
|  | * Deduction guide, allows `Assert` to be used as if it were a | 
|  | * function with a lambda argument. | 
|  | */ | 
|  | template<typename... Ts> | 
|  | Assert(auto, const char *, Ts &&...) -> Assert<Ts...>; | 
|  |  | 
|  | /** | 
|  | * Overt wrapper function around Assert.  Sometimes template | 
|  | * deduction guides just don't cut it.  At the cost of a | 
|  | * std::make_tuple at the call site, we can still take advantage | 
|  | * of much of the machinery here. | 
|  | */ | 
|  | template<typename T, typename... Args> | 
|  | __always_inline static void | 
|  | assertion(T                   condition, | 
|  | const char         *fmt, | 
|  | std::tuple<Args...> args = std::make_tuple(), | 
|  | SourceLocation      loc  = SourceLocation::current()) | 
|  | { | 
|  | std::apply( | 
|  | [&](Args... iargs) { | 
|  | Assert<Args...>( | 
|  | condition, fmt, std::forward<Args>(iargs)..., loc); | 
|  | }, | 
|  | args); | 
|  | } | 
|  | }; | 
|  |  | 
|  | enum class StackCheckMode | 
|  | { | 
|  | Disabled, | 
|  | Logging, | 
|  | Asserting, | 
|  | }; | 
|  |  | 
|  | /** | 
|  | * Check the (dynamic) stack usage of a function, including all of its | 
|  | * callees.  This is intended to be used in compartment entry points to | 
|  | * check the stack size that is required for that entry point.  Use this | 
|  | * with some test vectors that explore different code paths to identify the | 
|  | * maximum stack usage.  This can then be used with the | 
|  | * [[cheriot::minimum_stack]] attribute to ensure that the switcher will | 
|  | * never invoke the entry point with insufficient stack. | 
|  | * | 
|  | * Note that this records only the stack usage for the current compartment | 
|  | * invocation (including any invoked shared libraries).  If this | 
|  | * compartment calls others, then a larger stack may be required.  This | 
|  | * failure is recoverable: cross-compartment calls should always be assumed | 
|  | * to potentially fail.  This check is to ensure that an attacker cannot | 
|  | * cause stack overflow to crash the compartment, not to ensure that an | 
|  | * attacker cannot cause stack overflow to make the operation that they | 
|  | * requested fail. | 
|  | * | 
|  | * Stack checks run in one of three modes: | 
|  | * | 
|  | *  - Disabled: The checks do not run. | 
|  | *  - Logging: The checks run and print a message if the stack usage | 
|  | *    exceeds expectations. | 
|  | *  - Asserting: After printing the message, the function will trap. | 
|  | * | 
|  | * In logging mode, one message will be printed for each invocation of the | 
|  | * function that exceeds the previous maximum stack usage. | 
|  | * | 
|  | * An instance of this should be created as a local variable on entry to a | 
|  | * function.  The destructor (which prints the message) will then run after | 
|  | * any other destructors, ensuring the most accurate stack usage | 
|  | * measurement. | 
|  | * | 
|  | * Unfortunately, default arguments for templates are not evaluated in the | 
|  | * enclosing scope and so `__builtin_FUNCTION()` cannot be used here. | 
|  | * Instead, we must pass this explicitly, typically as something like: | 
|  | * | 
|  | * ```c++ | 
|  | * StackUsageCheck<DebugFlag, 128, __PRETTY_FUNCTION__> stackCheck; | 
|  | * ``` | 
|  | */ | 
|  | template<StackCheckMode Mode, size_t Expected, DebugContext Fn> | 
|  | class StackUsageCheck | 
|  | { | 
|  | /** | 
|  | * The expected maximum.  This class is templated on the function name | 
|  | * and so there is one copy of this per function. | 
|  | */ | 
|  | static inline size_t stackUsage = Expected; | 
|  |  | 
|  | public: | 
|  | /** | 
|  | * Default constructor, does nothing. | 
|  | */ | 
|  | StackUsageCheck() = default; | 
|  |  | 
|  | /** | 
|  | * Destructor, runs at the end of the function to print the message. | 
|  | */ | 
|  | __always_inline ~StackUsageCheck() | 
|  | { | 
|  | if constexpr (Mode != StackCheckMode::Disabled) | 
|  | { | 
|  | ptraddr_t lowest = stack_lowest_used_address(); | 
|  | ptraddr_t highest = | 
|  | CHERI::Capability{__builtin_cheri_stack_get()}.top(); | 
|  | size_t used = highest - lowest; | 
|  | if (used > stackUsage) | 
|  | { | 
|  | stackUsage = used; | 
|  | ConditionalDebug<true, Fn>::log("Stack used: {} bytes", | 
|  | stackUsage); | 
|  | if constexpr (Mode == StackCheckMode::Asserting) | 
|  | { | 
|  | __builtin_trap(); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | }; | 
|  |  | 
|  | } // namespace |