MMU RV32 Linux running natively on an ESP32-S31 microcontroller.
Module tested: ESP32-S31-WROOM-3 E1H16R16V (ESP32-S31 Core Board/Korvo).
WARNING: Experimental Definitely not something you want for production.
Install esptool, Espressif's tool for flashing ESP32s:
$ pip install esptoolThen download the binaries in Release, connect your board through USB-UART, and flash the board per the provided command below (change /dev/ttyUSB0 to your actual serial device):
$ esptool -p /dev/ttyUSB0 -b 2000000 erase-flash
$ esptool -p /dev/ttyUSB0 -b 2000000 write-flash \
--flash-mode dio --flash-freq 80m --flash-size 16MB \
0x2000 bootloader.bin \
0x8000 partition-table.bin \
0x20000 hello_world.bin \
0x220000 fw_payload.bin \
0x400000 xipImage \
0xA00000 rootfs.sqfs| Feature | Status |
|---|---|
| Buildroot rootfs | 🟢 Stable |
| Reboot | 🟢 Stable |
| Poweroff | 🔴 Not Implemented |
| Linux native wireless | 🟡 Experimental |
| - WiFi | 🟡 Experimental |
| - Bluetooth Dual Mode | 🟡 Experimental |
| Dual hart SMP | 🟡 Experimental |
Note: There has been a major CLIC driver change since 8/21/26's dual hart SMP commit, these drivers below haven't been tested.
| Feature | Status |
|---|---|
| AXI GDMA | 🟡 Experimental |
| AHB GDMA | 🟡 Experimental |
| Cache driver | 🟡 Experimental |
| TRNG | 🟡 Experimental |
| eFuse | 🟡 Experimental |
| Watchdog | 🟡 Experimental |
| PWM, counter, analog peripherals | 🟡 Experimental |
| CLIC/CLINT interrupt driver | 🟡 Experimental |
| Flash MTD driver | 🟡 Experimental |
| Timers | 🟠 WIP |
| Clock tree | 🟠 WIP |
| Security accelerators | 🟠 WIP |
| LP subsystem & IPC | 🔴 Not Implemented |
| PMP/APM | 🔴 Not Implemented (properly) |
| Feature | Status |
|---|---|
| UART0 console | 🟢 Stable |
| UART1/2 | 🟡 Experimental |
| GMAC Ethernet | 🟡 Experimental |
| SDMMC | 🟡 Experimental |
| GPIO | 🟡 Experimental |
| pinctrl/GPIO Matrix | 🟡 Experimental |
| USB | 🟠 WIP |
| I2C | 🔴 Not Implemented |
| I2S | 🔴 Not Implemented |
| SPI | 🔴 Not Implemented |
| RMT | 🔴 Not Implemented |
| USB Serial/JTAG | 🔴 Not Implemented |
🟢 Stable — Fully tested and working | 🟡 Experimental — Seems working; not thoroughly tested | 🟠 WIP - Functions not fully implemented
Refer to the Build Instructions.
(For more hardware references, see docs/ folder)
This port was done before S31 TRM is available, therefore these guessworks were made:
S31 uses CLIC and CLINT similar to P4. Linux expects PLIC. Therefore a custom CLIC driver is needed. I referenced this CLIC patch from disdi to get the CLIC working.
Also, standard RISC-V interrupt CSRs are not usable, presumably because, from P4's TRM, CLINT interrupts are routed to CLIC and mtvec.MODE is hardwired to 0x3 (CLIC mode). Patches needed to make OpenSBI interrupts work.
S31's supervisor mode is not standard and has absolutely no usage in ESP-IDF so a lot of these CSR uses were mostly guessed from either P4's TRM or CSR probing (see docs/). For example, the use of sclicbase(?) and the lack of sie.
S31 implemented SCLIC (Supervisor CLIC?) which is confusing since there is no known standardization; According to all laws of esp-idf, mcliccfg.NMBITS is not writable. IT IS WRITABLE! And setting it to 0b01 enables writes to the clicintattr[i].MODE field and thus enabling the use of S-mode interrupts.
To save the precious 16MB PSRAM memory, OpenSBI was modified to use XIP in flash and internal SRAM (hence the 3915901 KB firmware size in OpenSBI banner, since flash and SRAM mappings are not continuous).
In mainline linux, XIP support on RISC-V was removed, so 6.12 was used instead which has proper XIP support.
Edit: SMP support is added. Espressif's radio blobs exposes a set of OSI (OS interfaces). Radio support is accomplished by emulating a compatible OSI using Linux kthreads.
I noticed folks on Hacker News questioning the use of AI-generated code. For transparency:
- Yes, it is heavily agent-assisted. It do work on real S31 dev boards (there's console output above and binary releases to prove that.) I understand the esp32 microcontroller architecture to some extent, but I barely know how to port Linux to other RISC-V platforms; what I did is to tell the agent something like "Go implement an IPC transport that uses a shared SRAM buffer and an IPC interrupt doorbell" or "sdmmc uses designware ip; search esp-idf usage and port the existing Linux driver over." An AI agent on its own would never discover S31's bespoke hardware behavior without my guidance, for example, that the register
mcliccfghas writable bits, despite esp-idf saying otherwise. However I admit that AI assistance is the direct reason why I am able to progress this fast, and I did learn a lot about kernel development during the process.
