ARMv7 emulator written in Rust
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Type Name Latest commit message Commit time
Failed to load latest commit information.
capstone-rs @ 7be4e13


ARMv7 emulator written in Rust (C-style, not really idiomatic), emulated board -- Versatile Express

  • hardware: ARMv7 CPU including MMU and CP15; UART, timer and interrupt controller
  • OS: vanilla Linux 3.2 with custom config (turned off SMP, Thumb, hardware FP, etc)
  • rudimentary breakpoins, watchpoints, single stepping
  • boots to prompt


  • about 1 millions instruction per second, which is 60-70 times slower than qemu
  • 4000+ lines of code, excluding asserts
  • 73 ARM instruction implemented
  • about 130 millions instruction executed in total


  • Rust, tested with stable 1.12
  • capstone for disassembly, included


Install Rust, e.g.

curl -sSf | sh

fetch capstone bindings

git submodule init
git submodule update

Run script, which:

  • runs release build, which is much faster than debug
  • sets explicit LIBRARY_PATH and LD_LIBRARY_PATH to keep out incompatible upstream capstone changes
  • redirects stdout with debug information to /dev/null


(only the most relevant parts from and cpu/, simplified)

Kernel, atags and initramfs are loaded into memory:

let mut mem: Memory = Memory::new();
add_memory_regions(&mut mem);
mem.load(ATAGS_ADDR,   "bin/atags");
mem.load(ZIMAGE_ADDR,  "bin/zImage");
mem.load(RAMDISK_ADDR, "bin/initramfs");

CPU is created and started, r0-r2 registers are set accordingly to Linux on ARM convention:

let mut cpu: CPU = CPU::new(mem, ZIMAGE_ADDR as u32);
// r0 = 0,
cpu.set_reg(0, 0);
// r1 = machine type number (Versatile Express)
cpu.set_reg(1, 2272);
/*  r2 = physical address of tagged list in system RAM, or
         physical address of device tree block (dtb) in system RAM */
cpu.set_reg(2, ATAGS_ADDR as u32);

unsafe { cpu.start(capstone, args.verbose); }

CPU starts in Supervisor mode with disabled interrupts:

self.set_cpsr_bits(CPSR_M, ProcessorMode::Svc as u32);
self.set_cpsr_bit(CPSR_I, 1);
self.set_cpsr_bit(CPSR_F, 1);
self.set_cpsr_bit(CPSR_A, 1);

Main loop: each instruction can return a new address (e.g. jump), if not then execute the next one:

let next = match self.execute_insn(insn) {
     Some(addr) => addr,
	 None => self.get_pc() + 4,

Omitted above but important:

  • timer tick, triggered every 100 instructions
  • UART TX interrupt, basically triggered once kernel enables it
  • memory fault, which may happen during instruction execution or fetching a new one


kernel and ramdisk compilation (except for the custom config)

Boot log

Uncompressing Linux... done, booting the kernel.
Initializing cgroup subsys cpuset
Linux version 3.2.0 (ivan@T400) (gcc version 4.7.3 (Ubuntu/Linaro 4.7.3-12ubuntu1) ) #5 Fri Apr 15 14:56:26 CEST 2016
Ignoring unrecognised tag 0x00000000
bootconsole [earlycon0] enabled
sched_clock: 32 bits at 24MHz, resolution 41ns, wraps every 178956ms
Kernel command line: console=ttyAMA0 earlyprintk=ttyAMA0 root=/dev/ram rdinit=/bin/sh
PID hash table entries: 64 (order: -4, 256 bytes)
Dentry cache hash table entries: 2048 (order: 1, 8192 bytes)
Inode-cache hash table entries: 1024 (order: 0, 4096 bytes)
Memory: 16MB = 16MB total
Memory: 10776k/10776k available, 5608k reserved, 0K highmem
Virtual kernel memory layout:
    vector  : 0xffff0000 - 0xffff1000   (   4 kB)
    fixmap  : 0xfff00000 - 0xfffe0000   ( 896 kB)
    vmalloc : 0x81800000 - 0xf8000000   (1896 MB)
    lowmem  : 0x80000000 - 0x81000000   (  16 MB)
    modules : 0x7f000000 - 0x80000000   (  16 MB)
      .text : 0x80008000 - 0x803daaf4   (3915 kB)
      .init : 0x803db000 - 0x803fe000   ( 140 kB)
      .data : 0x803fe000 - 0x80420b20   ( 139 kB)
       .bss : 0x80420b44 - 0x8043d76c   ( 116 kB)
SLUB: Genslabs=13, HWalign=32, Order=0-3, MinObjects=0, CPUs=1, Nodes=1
Console: colour dummy device 80x30
Calibrating delay loop... 99.73 BogoMIPS (lpj=498688)
pid_max: default: 32768 minimum: 301
Mount-cache hash table entries: 512
CPU: Testing write buffer coherency: ok
hw perfevents: enabled with ARMv7 Cortex-A9 PMU driver, 1 counters available
NET: Registered protocol family 16
L2x0 series cache controller enabled
l2x0: 8 ways, CACHE_ID 0x00000000, AUX_CTRL 0x00400000, Cache size: 65536 B
hw-breakpoint: debug architecture 0x4 unsupported.
Serial: AMBA PL011 UART driver
mb:uart0: ttyAMA0 at MMIO 0x10009000 (irq = 37) is a PL011 rev1
console [ttyAMA0] enabled, bootconsole disabled
console [ttyAMA0] enabled, bootconsole disabled
bio: create slab <bio-0> at 0
SCSI subsystem initialized
usbcore: registered new interface driver usbfs
usbcore: registered new interface driver hub
usbcore: registered new device driver usb
Advanced Linux Sound Architecture Driver Version 1.0.24.
Switching to clocksource v2m-timer1
NET: Registered protocol family 2
IP route cache hash table entries: 1024 (order: 0, 4096 bytes)
TCP established hash table entries: 512 (order: 0, 4096 bytes)
TCP bind hash table entries: 512 (order: -1, 2048 bytes)
TCP: Hash tables configured (established 512 bind 512)
TCP reno registered
UDP hash table entries: 256 (order: 0, 4096 bytes)
UDP-Lite hash table entries: 256 (order: 0, 4096 bytes)
NET: Registered protocol family 1
RPC: Registered named UNIX socket transport module.
RPC: Registered udp transport module.
RPC: Registered tcp transport module.
RPC: Registered tcp NFSv4.1 backchannel transport module.
Unpacking initramfs...
Freeing initrd memory: 1072K
NetWinder Floating Point Emulator V0.97 (double precision)
JFFS2 version 2.2. (NAND) © 2001-2006 Red Hat, Inc.
msgmni has been set to 23
io scheduler noop registered (default)
smsc911x: Driver version 2008-10-21
smsc911x: Device not READY in 100ms aborting
isp1760 isp1760: NXP ISP1760 USB Host Controller
isp1760 isp1760: new USB bus registered, assigned bus number 1
isp1760 isp1760: Scratch test failed.
isp1760 isp1760: can't setup
isp1760 isp1760: USB bus 1 deregistered
isp1760: Failed to register the HCD device
Initializing USB Mass Storage driver...
usbcore: registered new interface driver usb-storage
USB Mass Storage support registered.
mousedev: PS/2 mouse device common for all mice
usbcore: registered new interface driver usbhid
usbhid: USB HID core driver
ALSA device list:
  No soundcards found.
oprofile: using arm/armv7-ca9
TCP cubic registered
NET: Registered protocol family 17
drivers/rtc/hctosys.c: unable to open rtc device (rtc0)
Freeing init memory: 140K
/bin/sh: can't access tty; job control turned off
/ # uname -a
uname -a
Linux rarm 3.2.0 #5 Fri Apr 15 14:56:26 CEST 2016 armv7l GNU/Linux