| // 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"); |
| } |