blob: 2642d9fbf736450ca0733203f1e1571ed19f2332 [file] [log] [blame]
// 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");
}