|  | // Copyright Microsoft and CHERIoT Contributors. | 
|  | // SPDX-License-Identifier: MIT | 
|  |  | 
|  | #include <debug.hh> | 
|  | #include <thread.h> | 
|  |  | 
|  | using namespace CHERI; | 
|  |  | 
|  | namespace | 
|  | { | 
|  | /** | 
|  | * Printer for debug messages.  This implements the `DebugWriter` interface | 
|  | * so that it can be used with custom callbacks. | 
|  | * | 
|  | * This is not exposed for subclassing and so is final to allow internal | 
|  | * calls to avoid the vtable.  Only callbacks use the vtable. | 
|  | */ | 
|  | struct DebugPrinter final : DebugWriter | 
|  | { | 
|  | /** | 
|  | * Write a character, delegating to `Output`. | 
|  | */ | 
|  | void write(char c) override | 
|  | { | 
|  | MMIO_CAPABILITY(Uart, uart)->blocking_write(c); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Write a null-terminated C string. | 
|  | */ | 
|  | void write(const char *str) override | 
|  | { | 
|  | for (; *str; ++str) | 
|  | { | 
|  | write(*str); | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Write a string view. | 
|  | */ | 
|  | void write(std::string_view str) override | 
|  | { | 
|  | for (char c : str) | 
|  | { | 
|  | write(c); | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Outputs the permission set using the format G RWcgml Xa SU0 as | 
|  | * described in [/docs/Debugging.md]. | 
|  | */ | 
|  | void write(CHERI::PermissionSet permissions) | 
|  | { | 
|  | using namespace CHERI; | 
|  | auto perm = [&](Permission p, char c) -> char { | 
|  | if (permissions.contains(p)) | 
|  | { | 
|  | return c; | 
|  | } | 
|  | return '-'; | 
|  | }; | 
|  | write(perm(Permission::Global, 'G')); | 
|  | write(' '); | 
|  | write(perm(Permission::Load, 'R')); | 
|  | write(perm(Permission::Store, 'W')); | 
|  | write(perm(Permission::LoadStoreCapability, 'c')); | 
|  | write(perm(Permission::LoadGlobal, 'g')); | 
|  | write(perm(Permission::LoadMutable, 'm')); | 
|  | write(perm(Permission::StoreLocal, 'l')); | 
|  | write(' '); | 
|  | write(perm(Permission::Execute, 'X')); | 
|  | write(perm(Permission::AccessSystemRegisters, 'a')); | 
|  | write(' '); | 
|  | write(perm(Permission::Seal, 'S')); | 
|  | write(perm(Permission::Unseal, 'U')); | 
|  | write(perm(Permission::User0, '0')); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Write a raw pointer as a hex string. | 
|  | */ | 
|  | void write(void *ptr) | 
|  | { | 
|  | const CHERI::Capability C{ptr}; | 
|  |  | 
|  | write(C.address()); | 
|  | write(" (v:"); | 
|  | write(C.is_valid()); | 
|  | write(' '); | 
|  | write(C.base()); | 
|  | write('-'); | 
|  | write(C.top()); | 
|  | write(" l:"); | 
|  | write(C.length()); | 
|  | write(" o:"); | 
|  | write(C.type()); | 
|  | write(" p: "); | 
|  | write(C.permissions()); | 
|  | write(')'); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Write a signed integer, as a decimal string. | 
|  | */ | 
|  | void write(int32_t s) override | 
|  | { | 
|  | if (s < 0) | 
|  | { | 
|  | write('-'); | 
|  | s = 0 - s; | 
|  | } | 
|  | std::array<char, 10> buf; | 
|  | const char           Digits[] = "0123456789"; | 
|  | for (int i = int(buf.size() - 1); i >= 0; i--) | 
|  | { | 
|  | buf[static_cast<size_t>(i)] = Digits[s % 10]; | 
|  | s /= 10; | 
|  | } | 
|  | bool skipZero = true; | 
|  | for (auto c : buf) | 
|  | { | 
|  | if (skipZero && (c == '0')) | 
|  | { | 
|  | continue; | 
|  | } | 
|  | skipZero = false; | 
|  | write(c); | 
|  | } | 
|  | if (skipZero) | 
|  | { | 
|  | write('0'); | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Write a signed integer, as a decimal string. | 
|  | */ | 
|  | void write(int64_t s) override | 
|  | { | 
|  | if (s < 0) | 
|  | { | 
|  | write('-'); | 
|  | s = 0 - s; | 
|  | } | 
|  | std::array<char, 20> buf; | 
|  | const char           Digits[] = "0123456789"; | 
|  | for (int i = int(buf.size() - 1); i >= 0; i--) | 
|  | { | 
|  | buf[static_cast<size_t>(i)] = Digits[s % 10]; | 
|  | s /= 10; | 
|  | } | 
|  | bool skipZero = true; | 
|  | for (auto c : buf) | 
|  | { | 
|  | if (skipZero && (c == '0')) | 
|  | { | 
|  | continue; | 
|  | } | 
|  | skipZero = false; | 
|  | write(c); | 
|  | } | 
|  | if (skipZero) | 
|  | { | 
|  | write('0'); | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Write a 32-bit unsigned integer to the buffer as hex with no prefix. | 
|  | */ | 
|  | void append_hex_word(uint32_t s) | 
|  | { | 
|  | std::array<char, 8> buf; | 
|  | const char          Hexdigits[] = "0123456789abcdef"; | 
|  | // Length of string including null terminator | 
|  | static_assert(sizeof(Hexdigits) == 0x11); | 
|  | for (long i = long(buf.size() - 1); i >= 0; i--) | 
|  | { | 
|  | buf.at(static_cast<size_t>(i)) = Hexdigits[s & 0xf]; | 
|  | s >>= 4; | 
|  | } | 
|  | bool skipZero = true; | 
|  | for (auto c : buf) | 
|  | { | 
|  | if (skipZero && (c == '0')) | 
|  | { | 
|  | continue; | 
|  | } | 
|  | skipZero = false; | 
|  | write(c); | 
|  | } | 
|  | if (skipZero) | 
|  | { | 
|  | write('0'); | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Write a 32-bit unsigned integer to the buffer as hex. | 
|  | */ | 
|  | void write(uint32_t s) override | 
|  | { | 
|  | write('0'); | 
|  | write('x'); | 
|  | append_hex_word(s); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Write a 64-bit unsigned integer to the buffer as hex. | 
|  | */ | 
|  | void write(uint64_t s) override | 
|  | { | 
|  | write('0'); | 
|  | write('x'); | 
|  | uint32_t hi = static_cast<uint32_t>(s >> 32); | 
|  | uint32_t lo = static_cast<uint32_t>(s); | 
|  | if (hi != 0) | 
|  | { | 
|  | append_hex_word(hi); | 
|  | } | 
|  | append_hex_word(lo); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Format a message, using the provided arguments. | 
|  | */ | 
|  | void format(const char          *fmt, | 
|  | DebugFormatArgument *arguments, | 
|  | size_t               argumentsCount) | 
|  | { | 
|  | // If there are no format arguments, just write the string. | 
|  | if (argumentsCount == 0) | 
|  | { | 
|  | write(fmt); | 
|  | return; | 
|  | } | 
|  | size_t argumentIndex = 0; | 
|  | for (const char *s = fmt; *s != 0; ++s) | 
|  | { | 
|  | if (s[0] == '{' && s[1] == '}') | 
|  | { | 
|  | s++; | 
|  | if (argumentIndex >= argumentsCount) | 
|  | { | 
|  | write("<missing argument>"); | 
|  | continue; | 
|  | } | 
|  | auto            &argument = arguments[argumentIndex++]; | 
|  | Capability<void> kind{ | 
|  | reinterpret_cast<void *>(argument.kind)}; | 
|  | if (kind.is_valid()) | 
|  | { | 
|  | reinterpret_cast<DebugCallback>(kind.get())( | 
|  | argument.value, *this); | 
|  | } | 
|  | else | 
|  | { | 
|  | switch ( | 
|  | static_cast<DebugFormatArgumentKind>(argument.kind)) | 
|  | { | 
|  | case DebugFormatArgumentKind:: | 
|  | DebugFormatArgumentBool: | 
|  | write(static_cast<bool>(argument.value) | 
|  | ? "true" | 
|  | : "false"); | 
|  | break; | 
|  | case DebugFormatArgumentKind:: | 
|  | DebugFormatArgumentCharacter: | 
|  | write(static_cast<char>(argument.value)); | 
|  | break; | 
|  | case DebugFormatArgumentKind:: | 
|  | DebugFormatArgumentPointer: | 
|  | write(reinterpret_cast<void *>(argument.value)); | 
|  | break; | 
|  | case DebugFormatArgumentKind:: | 
|  | DebugFormatArgumentSignedNumber32: | 
|  | write(static_cast<int32_t>(argument.value)); | 
|  | break; | 
|  | case DebugFormatArgumentKind:: | 
|  | DebugFormatArgumentUnsignedNumber32: | 
|  | write(static_cast<uint32_t>(argument.value)); | 
|  | break; | 
|  | case DebugFormatArgumentKind:: | 
|  | DebugFormatArgumentSignedNumber64: | 
|  | { | 
|  | int64_t value; | 
|  | memcpy(&value, &argument.value, sizeof(value)); | 
|  | write(value); | 
|  | break; | 
|  | } | 
|  | case DebugFormatArgumentKind:: | 
|  | DebugFormatArgumentUnsignedNumber64: | 
|  | { | 
|  | uint64_t value; | 
|  | memcpy(&value, &argument.value, sizeof(value)); | 
|  | write(value); | 
|  | break; | 
|  | } | 
|  | case DebugFormatArgumentKind:: | 
|  | DebugFormatArgumentCString: | 
|  | write(reinterpret_cast<const char *>( | 
|  | argument.value)); | 
|  | break; | 
|  | case DebugFormatArgumentKind:: | 
|  | DebugFormatArgumentStringView: | 
|  | write(*reinterpret_cast<std::string_view *>( | 
|  | argument.value)); | 
|  | break; | 
|  | case DebugFormatArgumentKind:: | 
|  | DebugFormatArgumentPermissionSet: | 
|  | write(CHERI::PermissionSet::from_raw( | 
|  | argument.value)); | 
|  | break; | 
|  | default: | 
|  | write("<invalid argument kind>"); | 
|  | break; | 
|  | } | 
|  | }; | 
|  | continue; | 
|  | } | 
|  | write(*s); | 
|  | } | 
|  | } | 
|  | }; | 
|  |  | 
|  | } // namespace | 
|  |  | 
|  | [[cheri::interrupt_state(disabled)]] void | 
|  | debug_log_message_write(const char          *context, | 
|  | const char          *format, | 
|  | DebugFormatArgument *messages, | 
|  | size_t               messageCount) | 
|  | { | 
|  | DebugPrinter printer; | 
|  | printer.write("\x1b[35m"); | 
|  | printer.write(context); | 
|  | #if 0 | 
|  | printer.write(" [Thread "); | 
|  | printer.write(thread_id_get()); | 
|  | printer.write("]\033[0m: "); | 
|  | #else | 
|  | printer.write("\033[0m: "); | 
|  | #endif | 
|  | printer.format(format, messages, messageCount); | 
|  | printer.write("\n"); | 
|  | } | 
|  |  | 
|  | [[cheri::interrupt_state(disabled)]] void | 
|  | debug_report_failure(const char          *kind, | 
|  | const char          *file, | 
|  | const char          *function, | 
|  | int                  line, | 
|  | const char          *format, | 
|  | DebugFormatArgument *arguments, | 
|  | size_t               argumentCount) | 
|  | { | 
|  | DebugPrinter printer; | 
|  | printer.write("\x1b[35m"); | 
|  | printer.write(file); | 
|  | printer.write(":"); | 
|  | printer.write(line); | 
|  | printer.write("\x1b[31m "); | 
|  | printer.write(kind); | 
|  | printer.write(" failure\x1b[35m in "); | 
|  | printer.write(function); | 
|  | printer.write("\x1b[36m\n"); | 
|  | printer.format(format, arguments, argumentCount); | 
|  | printer.write("\n"); | 
|  | } |