Notes on building TinyEMU and booting the Linux kernel on Emscripten

Last time (Prepare RISC-V development environment with buildroot and crosstool-ng), the kernel was started with qemu, so this time it works on the Web browser. Try starting it with TinyEMU (, which is a RISC-V emulator.


Seen from the parent kernel panic.


This time, I did not use TinyEMU standard I / O library at all, but tried to configure and start a VM from the C language side of Emscripten.

-** The boot loader must be the one that comes with TinyEMU **. The attached loader has been modified, and the console does not come out with a normal loader. --The build itself just builds with Emscripten 2.0.2 ╩╗emcc` and works with Emscripten standard HTML output.

Build TinyEMU

The build itself is not particularly difficult, and if you compile and output * .c appropriately, you can make something that works.

emcc jsemu.c softfp.c virtio.c fs.c fs_net.c fs_wget.c fs_utils.c simplefb.c pci.c ^
json.c block_net.c iomem.c cutils.c aes.c sha256.c riscv_cpu.c riscv_machine.c machine.c ^
--llvm-opts 2 -Wall -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -MMD -fno-strict-aliasing ^
-DCONFIG_FS_NET -O3 --memory-init-file 0 --closure 0 -s NO_EXIT_RUNTIME=1 ^
-s "EXPORTED_FUNCTIONS=['_console_queue_char','_vm_start','_fs_import_file','_display_key_event','_display_mouse_event','_display_wheel_event','_net_write_packet','_net_set_carrier','_main']" ^
-s "EXTRA_EXPORTED_RUNTIME_METHODS=[\"ccall\", \"cwrap\"]" ^
--js-library js/lib.js  -s WASM=1 -s TOTAL_MEMORY=67108864 -s ALLOW_MEMORY_GROWTH=1  ^
--source-map-base http://localhost:6931/ --preload-file kernel --preload-file bbl32.bin emmain.c -o run.html

Initial setup routine (so-called main)

In normal TinyEMU, VM setting is done from the JavaScript side, but it was a little inconvenient to unify with the native version, so I decided to prepare the main function.

The boot loader bbl32.bin and the Linux kernel kernel are once embedded in Emscripten's --preload-file application side and read by C language file I / O.

You can use the kernel built last time ( as it is, but the boot loader bbl32.bin comes with TinyEMU ( You must use the one from /diskimage-linux-riscv-2018-09-23.tar.gz).

TinyEMU specific (?) HTIF implementation

TinyEMU does not emulate so-called UART devices, but implements HTIF (Host-Target IF), which was implemented by Spike (RISC-V official emulator).

However, unlike other implementations such as qemu, TinyEMU's HTIF implementation has a fixed address, and the bootloader is patched to convey the address.

#define HTIF_BASE_ADDR 0x40008000
diff --git a/bbl/ b/bbl/
index 26f5816..615c3dc 100644
--- a/bbl/
+++ b/bbl/
@@ -43,15 +43,10 @@ SECTIONS
   _etext = .;
-  /* HTIF, isolated onto separate page                                  */
+  /* HTIF I/Os                                                          */
-  . = ALIGN(0x1000);
-  .htif :
-  {
-    PROVIDE( __htif_base = .);
-    *(.htif)
-  }
-  . = ALIGN(0x1000);
+  tohost = 0x40008000;
+  fromhost = 0x40008008;

A patch to this linker script fixes the addresses of tohost and fromhost.

volatile uint64_t tohost __attribute__((section(".htif")));
volatile uint64_t fromhost __attribute__((section(".htif")));

Originally, these addresses are acquired when the emulator loads.

In the first place, HTIF itself is an old specification, so it may not matter now, but I also want a more serious protocol.

Use HTIF at 32bit

Since HTIF is a 64-bit wide interface, it cannot be used with a 32-bit architecture. (If writing conflicts with two or more CPUs, it cannot be processed safely)

Since TinyEMU does not support multiprocessors, it seems that this area is not particularly concerned, ** By declaring the register with 32 bit width and making it a code that is activated at the timing when the upper word is written ** 32 bit / 64 bit Both are supported.

... qemu supports multiprocessors, so I don't think the same policy will work. ..


I was prepared that the RISC-V 32bit environment was prepared too recently and it is variously behind 64bit, but it is a bit that pure debugging I / F like HTIF does not support 32bit. It was unexpected.

TODO: You have to set -s SINGLE_FILE = 1 to distribute with npm, and make the kernel and bootloader provided externally.

Recommended Posts

Notes on building TinyEMU and booting the Linux kernel on Emscripten
Notes on building Python and pyenv on Mac
Notes on using OpenCL on Linux on the RX6800
Compiling the Linux kernel (Linux 5.x on Ubuntu 20.04)
Specify the volume on linux and make a sound
About the --enable-shared option when building Python on Linux
I tried installing the Linux kernel on virtualbox + vagrant
Notes on tf.function and Tracing
Notes on * args and ** kargs
What is the Linux kernel?
Install the JDK on Linux
Recording and playback on Linux
Notes on pyenv and Atom
Find the most F-word commit on Linux (git and later)
Listed data structures in the Linux kernel and their operations
Paste the link on linux
Notes on Python and dictionary types
Try the Linux kernel lockdown mechanism
Survey on building and running kivi
Notes on using post-receive and post-merge
Notes on using matplotlib on the server
Notes on installing Ubuntu 18.04 on the XPS 15 7590
Read the file with python and delete the line breaks [Notes on reading the file]
[UE4] Build DedicatedServer on Windows and Linux
Install wsl2 and master linux on windows
A quick overview of the Linux kernel
Install and launch k3s on Manjaro Linux
Install and Configure TigerVNC server on Linux
Learn sshd_config and authorized_keys (on Amazon Linux 2)
Add lines and text on the image
Replacing rmtrash on Mac and replacing rm on Linux
On Linux (Ubuntu), tune the Trackpad and set the function to a three-finger swipe