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