blob: 0c85f14c5ca63a48a7a6b8cd3013d7c9d0570eea [file] [log] [blame]
// Copyright Microsoft and CHERIoT Contributors.
// SPDX-License-Identifier: MIT
#include "tests.hh"
#include <compartment.h>
#include <simulator.h>
#include <string>
using namespace CHERI;
using namespace std::string_literals;
namespace
{
/// Have we detected a crash in any of the compartments?
volatile bool crashDetected = false;
/**
* Read the cycle counter.
*/
int rdcycle()
{
int cycles;
#ifdef SAIL
// On Sail, report the number of instructions, the cycle count is
// meaningless.
__asm__ volatile("csrr %0, minstret" : "=r"(cycles));
#elifdef IBEX
__asm__ volatile("csrr %0, mcycle" : "=r"(cycles));
#else
__asm__ volatile("rdcycle %0" : "=r"(cycles));
#endif
return cycles;
}
/**
* Call `fn` and log a message informing the user how long it took.
*/
void run_timed(const char *msg, auto &&fn)
{
bool failed = false;
int startCycles = rdcycle();
if constexpr (std::is_same_v<std::invoke_result_t<decltype(fn)>, void>)
{
fn();
}
else
{
failed = (fn() != 0);
}
int cycles = rdcycle();
if (failed)
{
debug_log("{} failed", msg);
crashDetected = true;
}
else
debug_log("{} finished in {} cycles", msg, cycles - startCycles);
}
} // namespace
extern "C" enum ErrorRecoveryBehaviour
compartment_error_handler(struct ErrorState *frame, size_t mcause, size_t mtval)
{
if (mcause == 0x2)
{
debug_log("Test failure in test runner");
#ifdef SIMULATION
simulation_exit(1);
#endif
return ErrorRecoveryBehaviour::ForceUnwind;
}
debug_log("mcause: {}, pcc: {}", mcause, frame->pcc);
auto [reg, cause] = CHERI::extract_cheri_mtval(mtval);
debug_log("Error {} in register {}", reg, cause);
debug_log("Current test crashed");
crashDetected = true;
return ErrorRecoveryBehaviour::InstallContext;
}
/**
* Test suite entry point. Runs all of the tests that we have defined.
*/
void __cheri_compartment("test_runner") run_tests()
{
// magic_enum is a pretty powerful stress-test of various bits of linkage.
// In generating `enum_values`, it generates constant strings and pointers
// to them in COMDAT sections. These require merging across compilation
// units during our first link stage and then require cap relocs so that
// they are all correctly bounded. This works as a good smoke test of our
// linker script.
debug_log("Checking that rel-ro caprelocs work. This will crash if they "
"don't. CHERI Permissions are:");
for (auto &permission : magic_enum::enum_values<CHERI::Permission>())
{
debug_log("{}", permission);
}
/*
* Add a test to check the PermissionSet Iterator. We swap the permissions
* order to match it to the order the Iterator implements: the Iterator
* returns then in value order, and not insertion order.
*/
static constexpr PermissionSet Permissions{
Permission::Load, Permission::Store, Permission::LoadStoreCapability};
static Permission permissionArray[] = {
Permission::Store, Permission::Load, Permission::LoadStoreCapability};
size_t index = 0;
for (auto permission : Permissions)
{
TEST_EQUAL(permission,
permissionArray[index++],
"Iterator of PermissionSet failed");
}
// These need to be checked visually
debug_log("Trying to print 8-bit integer: {}", uint8_t(0x12));
debug_log("Trying to print unsigned 8-bit integer: {}", int8_t(34));
debug_log("Trying to print char: {}", 'c');
debug_log("Trying to print 32-bit integer: {}", 12345);
debug_log("Trying to print 64-bit integer: {}", 123456789012345LL);
debug_log("Trying to print unsigned 32-bit integer: {}", 0x12345U);
debug_log("Trying to print unsigned 64-bit integer: {}",
0x123456789012345ULL);
const char *testString = "Hello, world! with some trailing characters";
// Make sure that we don't print the trailing characters
debug_log("Trying to print std::string_view: {}",
std::string_view{testString, 13});
const std::string S = "I am a walrus"s;
debug_log("Trying to print std::string: {}", S);
// Test stack pointer recovery in the root compartment.
CHERI::Capability<void> csp{__builtin_cheri_stack_get()};
CHERI::Capability<void> originalCSP{switcher_recover_stack()};
csp.address() = originalCSP.address();
TEST(csp == originalCSP,
"Original stack pointer: {}\ndoes not match current stack pointer: {}",
originalCSP,
csp);
run_timed("All tests", []() {
run_timed("Debug helpers (C++)", test_debug_cxx);
run_timed("Debug helpers (C)", test_debug_c);
run_timed("MMIO", test_mmio);
run_timed("Unwind cleanup", test_unwind_cleanup);
run_timed("stdio", test_stdio);
run_timed("Static sealing", test_static_sealing);
run_timed("Crash recovery", test_crash_recovery);
run_timed("Compartment calls", test_compartment_call);
run_timed("check_pointer", test_check_pointer);
run_timed("Misc APIs", test_misc);
run_timed("Stacks exhaustion in the switcher", test_stack);
run_timed("Thread pool", test_thread_pool);
run_timed("Global Constructors", test_global_constructors);
run_timed("Queue", test_queue);
run_timed("Futex", test_futex);
run_timed("Locks", test_locks);
run_timed("List", test_list);
run_timed("Event groups", test_eventgroup);
run_timed("Multiwaiter", test_multiwaiter);
run_timed("Allocator", test_allocator);
});
TEST(crashDetected == false, "One or more tests failed");
// Exit the simulator if we are running in simulation.
#ifdef SIMULATION
simulation_exit();
#endif
// Infinite loop if we're not in simulation.
while (true)
{
yield();
}
}