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