The LRVM project consists in a simple but complete 32-bit virtual machine.
The project's goal is to make a virtual machine which is powerful enough to play with, but simple enough to use it as a learning material on how VM work and making simple proof-of-concept (POC) programs.
The main features are:
- Motherboard emulation, allowing to plug-in virtual components ;
- MMIO emulation for mapping components into memory ;
- MMU emulation for translating virtual addresses into physical ones and managing userland permissions ;
- Supervisor/userland mode ;
- A very simple and friendly API ;
- Debugging tools such as a full-powered assembler and disassembler
It is split into several crates:
lrvm
: LRVM's core, which contains the motherboard, MMIO, CPU and MMU emulationlrvm_aux
: A set of useful auxiliary componentslrvm_tools
: A set of tools to deal more easily with LRVM, including a strongly-typed assembler and a string-based one
Various examples can be found in the examples/
directory.
An arbitrary number of components can be connected to the virtual motherboard and accessed through memory mapping (MMIO).
Many components are available in the lrvm_aux
crate.
LRVM is designed to be easy to learn and use, which means performance is not a primary goal. The CPU works as a single-core interpreter ; there is no multi-threading nor just-in-time compilation (JIT) as this would complexify this project a lot, which would be contrary to its main goal.
You can run a little benchmark to see how many instructions per second your computer can run with LRVM. The benchmark is essentially made of arithmetic instructions, which are the slowest ones as they require to compute the resulting arithmetic flags.
To run the benchmark:
cd examples/benchmark
cargo run --release
For reference, on an Intel Core i7-9700K with DDR4 @ 2667 MHz, we get a result of ~ 80 MIPS (Million Instructions Per Second) with LTO enabled. On a Ryzen 7900 with DDR5 @ 5600 MHz, we get ~ 140 MIPS.
The documentation is made of several parts :
- A step-by-step tutorial explaining how to set up a VM and running it with debugging tools
- An architecture document describing how the VM works, the structure of registers, memory, etc.
- An hardware specifications document describing how auxiliary components work
The virtual machine's specifications (registers, computation, ISA, etc.) are available here.
LRVM uses a lightweight language assembly called LASM (Lightweight Assembly). LASM follows the assembly language specifications and is assembled using CustomASM (a huge thanks to their author for making this great library).
Here is an example of a simple LASM program:
main:
add a0, 1
cmp a0, 10
ifeq
halt
jp main
This program adds 1
to the a0
register until it reaches 10
, then it halts the processor.
To see more about the LASM language, see the tutorial.
You can install the Visual Studio Code extension to get syntax highlighting for LRVM's Lightweight Assembly (LASM) language.
Or you can build the sources from the vscode-lasm
directory:
cd vscode-lang
vsce package
code --install-extension lrvm-lasm-*.vsix
# Enjoy!
As some examples depends on other, and Cargo does not provide a way to prevent tests from running concurrently, you should not use a simple cargo test
on LRVM's crates but instead:
cargo test -- --test-threads=1