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