|  | // Copyright Microsoft and CHERIoT Contributors. | 
|  | // SPDX-License-Identifier: MIT | 
|  |  | 
|  | #include "microvium-ffi.hh" | 
|  | #include "secret.h" | 
|  | #include <compartment.h> | 
|  | #include <cstdint> | 
|  | #include <cstdlib> | 
|  | #include <debug.hh> | 
|  | #include <riscvreg.h> | 
|  | #include <type_traits> | 
|  | #include <vector> | 
|  |  | 
|  | /// Expose debugging features unconditionally for this compartment. | 
|  | using Debug = ConditionalDebug<true, "JavaScript compartment">; | 
|  | using CHERI::Capability; | 
|  |  | 
|  | /// Thread entry point. | 
|  | void __cheri_compartment("js") run() | 
|  | { | 
|  | // Set the secret value on startup. | 
|  | set_secret(); | 
|  |  | 
|  | while (true) | 
|  | { | 
|  | // Get a capability to the UART. | 
|  | auto uart = MMIO_CAPABILITY(Uart, uart); | 
|  | // Create a vector to hold the bytecode. | 
|  | std::vector<uint8_t> bytecode; | 
|  | // Helper that reads a character and errors if it isn't the expected | 
|  | // one. | 
|  | auto skip = [&](char byte) { | 
|  | char c = uart->blocking_read(); | 
|  | Debug::Assert(byte == c, "Read '{}', expected '{}'", c, byte); | 
|  | }; | 
|  | // Helper that reads from the UART until a specific character is seen | 
|  | auto skipUntil = [&](char byte) { | 
|  | while (uart->blocking_read() != byte) {} | 
|  | }; | 
|  | // Helper that converts a hex character to an integer.  Returns 0 on | 
|  | // invalid values. | 
|  | auto hexByteToNumber = [](char c) { | 
|  | switch (c) | 
|  | { | 
|  | case '0' ... '9': | 
|  | return c - '0'; | 
|  | case 'a' ... 'f': | 
|  | return c - 'a' + 10; | 
|  | case 'A' ... 'F': | 
|  | return c - 'A' + 10; | 
|  | } | 
|  | return 0; | 
|  | }; | 
|  |  | 
|  | // Read values in the form generated by microvium's hex output: a | 
|  | // series of hex bytes in braces. | 
|  | skipUntil('{'); | 
|  | while (true) | 
|  | { | 
|  | skip('0'); | 
|  | skip('x'); | 
|  | uint8_t byte = hexByteToNumber(uart->blocking_read()) << 4; | 
|  | byte += hexByteToNumber(uart->blocking_read()); | 
|  | bytecode.push_back(byte); | 
|  | char c = uart->blocking_read(); | 
|  | if (c == '}') | 
|  | { | 
|  | break; | 
|  | } | 
|  | Debug::Assert(c == ',', "Expected comma or close brace, read {}", c); | 
|  | } | 
|  |  | 
|  | Debug::log("Read {} bytes of bytecode", bytecode.size()); | 
|  | Debug::log("{} bytes of heap available", | 
|  | heap_quota_remaining(MALLOC_CAPABILITY)); | 
|  |  | 
|  | //////////////////////////////////////////////////////////////////////// | 
|  | // We've now read the bytecode into a buffer.  Spin up the JavaScript | 
|  | // VM to execute it. | 
|  | //////////////////////////////////////////////////////////////////////// | 
|  |  | 
|  | // Allocate the space for the VM capability registers on the stack and | 
|  | // record its location. | 
|  | // **Note**: This must be on the stack and in same compartment as the | 
|  | // JavaScript interpreter, so that the callbacks can re-derive it from | 
|  | // csp. | 
|  | AttackerRegisterState state; | 
|  | attackerRegisterStateAddress = Capability{&state}.address(); | 
|  |  | 
|  | mvm_TeError                         err; | 
|  | std::unique_ptr<mvm_VM, MVMDeleter> vm; | 
|  | // Create a Microvium VM from the bytecode. | 
|  | { | 
|  | mvm_VM *rawVm; | 
|  | err = mvm_restore( | 
|  | &rawVm,            /* Out pointer to the VM */ | 
|  | bytecode.data(),   /* Bytecode data */ | 
|  | bytecode.size(),   /* Bytecode length */ | 
|  | MALLOC_CAPABILITY, /* Capability used to allocate memory */ | 
|  | ::resolve_import); /* Callback used to resolve FFI imports */ | 
|  | // If this is not valid bytecode, give up. | 
|  | Debug::Assert( | 
|  | err == MVM_E_SUCCESS, "Failed to parse bytecode: {}", err); | 
|  | vm.reset(rawVm); | 
|  | } | 
|  |  | 
|  | // Get a handle to the JavaScript `run` function. | 
|  | mvm_Value run; | 
|  | err = mvm_resolveExports(vm.get(), &ExportRun, &run, 1); | 
|  | if (err != MVM_E_SUCCESS) | 
|  | { | 
|  | Debug::log("Failed to get run function: {}", err); | 
|  | } | 
|  | else | 
|  | { | 
|  | // Call the function: | 
|  | err = mvm_call(vm.get(), run, nullptr, nullptr, 0); | 
|  | if (err != MVM_E_SUCCESS) | 
|  | { | 
|  | Debug::log("Failed to call run function: {}", err); | 
|  | } | 
|  | } | 
|  | } | 
|  | } |