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