blob: e3354fa7f2099e33767b3e8b9705e9b328f76fd2 [file] [log] [blame]
// Copyright Microsoft and CHERIoT Contributors.
// SPDX-License-Identifier: MIT
#include <compartment.h>
#include <debug.hh>
#include <fail-simulator-on-error.h>
#include <microvium/microvium.h>
#include <platform-uart.hh>
/// Expose debugging features unconditionally for this compartment.
using Debug = ConditionalDebug<true, "JavaScript hello compartment">;
namespace
{
/**
* JavaScript bytecode. The initialiser is generated by running
*
* ```
* microvium --output-bytes hello.js > bytecode.inc
* ```
*/
uint8_t bytecode[] =
#include "bytecode.inc"
;
/// Constant for the print function exposed to JavaScript->C++ FFI
static constexpr mvm_HostFunctionID ImportPrint = 1;
/// Constant for the say_hello function exposed to C++->JavaScript FFI
static constexpr mvm_VMExportID ExportHello = 1234;
/**
* Print a string passed from JavaScript.
*/
mvm_TeError print(mvm_VM *vm,
mvm_HostFunctionID funcID,
mvm_Value *result,
mvm_Value *args,
uint8_t argCount)
{
auto uart = MMIO_CAPABILITY(Uart, uart);
// Iterate over the arguments.
for (unsigned i = 0; i < argCount; i++)
{
// Coerce the argument to a string and get it as a C string
const char *str = mvm_toStringUtf8(vm, args[i], nullptr);
// Write each character to the UART
for (; *str != '\0'; str++)
{
uart->blocking_write(*str);
}
// Try uncommenting the following line to see how it impacts total
// heap usage.
// mvm_runGC(vm, false);
}
// Write a trailing newline
uart->blocking_write('\n');
// Unconditionally return success
return MVM_E_SUCCESS;
}
/**
* Callback from microvium that resolves imports. This just resolves the
* `print` function as the import corresponding to the `ImportPrint` ID.
*/
mvm_TeError resolve_import(mvm_HostFunctionID funcID,
void *context,
mvm_TfHostFunction *out)
{
if (funcID == ImportPrint)
{
*out = print;
return MVM_E_SUCCESS;
}
return MVM_E_UNRESOLVED_IMPORT;
}
/**
* Helper that deletes a Microvium VM when used with a C++ unique pointer.
*/
struct MVMDeleter
{
void operator()(mvm_VM *mvm) const
{
mvm_free(mvm);
}
};
} // namespace
/// Thread entry point.
void __cheri_compartment("hello") say_hello()
{
mvm_TeError err;
std::unique_ptr<mvm_VM, MVMDeleter> vm;
// Load the JavaScript bytecode snapshot
{
mvm_VM *rawVm;
err = mvm_restore(&rawVm,
bytecode,
sizeof(bytecode),
MALLOC_CAPABILITY,
::resolve_import);
Debug::Assert(
err == MVM_E_SUCCESS, "Failed to parse bytecode: {}", err);
vm.reset(rawVm);
}
// Get a handle to the JavaScript `say_hello` function.
mvm_Value sayHello;
err = mvm_resolveExports(vm.get(), &ExportHello, &sayHello, 1);
Debug::Assert(
err == MVM_E_SUCCESS, "Failed to get say_hello function: {}", err);
// Call the function:
err = mvm_call(vm.get(), sayHello, nullptr, nullptr, 0);
Debug::Assert(
err == MVM_E_SUCCESS, "Failed to call say_hello function: {}", err);
mvm_TsMemoryStats stats;
// Helper lambda to report the current memory usage.
auto reportMemory = [&]() {
mvm_getMemoryStats(vm.get(), &stats);
Debug::log(
"Microvium is using {} bytes of memory, including {} bytes of heap",
stats.totalSize,
stats.virtualHeapUsed);
};
// See how much memory is used at the end of the invocation
reportMemory();
// Run the GC to shrink the heap as much as possible and report usage.
Debug::log("Running GC");
mvm_runGC(vm.get(), true);
reportMemory();
// Report the peak usage
Debug::log("Peak heap used: {} bytes, peak stack used: {} bytes",
stats.virtualHeapHighWaterMark,
stats.stackHighWaterMark);
}