A few months ago, we wrote that Western Digital was working on Linux & BusyBox RISC-V NOMMU, and managed to boot a minimal Linux OS on Kendryte K210 powered Sipeed Maix Go board.
RISC-V NOMMU support was scheduled for Linux 5.5, and now that the new kernel has been released, Damien Le Moal has pushed the code allowing to build Linux and a busybox based roofs for RISC-V 64-bit NOMMU platforms using buildroot.
I could start the build following the instructions on Github, but it failed as a Linux 5.6 RC1 tarball was missing. But I noticed “Vowstart” picked up on Damien’s work, and wrote detailed instructions. So let’s try the build out using a machine running Ubuntu 18.04.
We’ll have to make sure dependencies are installed first:
1 2 |
sudo apt install build-essential device-tree-compiler bison \ flex file git curl wget cpio python unzip rsync bc texinfo |
Then we can retrieve the source code and do some preparations (e.g. extract Linux 5.6 RC1 tarball):
1 2 3 4 |
git clone https://github.com/vowstar/k210-linux-nommu.git cd k210-linux-nommu export PROJ_ROOT=$(pwd) sh ./prepare_buildroot.sh |
The next step is to build the toolchain. It will take a long while because there’s a lot of code to build and download from the Internet:
1 2 3 |
cd "$PROJ_ROOT/riscv64-nommu-buildroot" make riscv64_nommu_defconfig make -j8 |
This ended successfully with:
1 2 3 4 5 6 7 8 9 10 11 12 |
mkdir -p /home/jaufranc/edev/Kendryte/k210-linux-nommu/riscv64-nommu-buildroot/output/target/etc ( \ echo "NAME=Buildroot"; \ echo "VERSION=2020.02-git-g6cb52b1e6"; \ echo "ID=buildroot"; \ echo "VERSION_ID=2020.02-git"; \ echo "PRETTY_NAME=\"Buildroot 2020.02-git\"" \ ) > /home/jaufranc/edev/Kendryte/k210-linux-nommu/riscv64-nommu-buildroot/output/target/usr/lib/os-release ln -sf ../usr/lib/os-release /home/jaufranc/edev/Kendryte/k210-linux-nommu/riscv64-nommu-buildroot/output/target/etc >>> Sanitizing RPATH in target tree PER_PACKAGE_DIR=/home/jaufranc/edev/Kendryte/k210-linux-nommu/riscv64-nommu-buildroot/output/per-package /home/jaufranc/edev/Kendryte/k210-linux-nommu/riscv64-nommu-buildroot/support/scripts/fix-rpath target touch /home/jaufranc/edev/Kendryte/k210-linux-nommu/riscv64-nommu-buildroot/output/target/usr |
We can now install the RISCV64 toolchain which will use for cross-compilation:
1 2 |
sudo cp -r output/host /opt/riscv64-uclibc export PATH=/opt/riscv64-uclibc/bin:$PATH |
Next up is buildroot build for Kendryte K210 NOMMU processor:
1 2 3 4 |
cd "$PROJ_ROOT/busybox" make k210_nommu_defconfig make SKIP_STRIP=y make SKIP_STRIP=y install |
The last step copies the file into $PROJ_ROOT/rootfs_k210
folder.
They also decided to build the Tiny C Compiler in order to be able to build code on the board itself. It’s not really necessary, as on such low-end hardware most people would likely prefer to cross-compile their code instead, but let’s go ahead anyway:
1 2 3 4 |
cd "$PROJ_ROOT/tinycc" ./configure --prefix=/usr --cross-prefix=riscv64-linux- --cpu=riscv64 --extra-cflags="-DCONFIG_TCC_STATIC=1" --extra-ldflags=-Wl,-elf2flt=-r make make DESTDIR=../rootfs_k210 install |
They also made a script to setup and copy k210 rootfs CPIO image into linux-kernel/k210.cpio
.
1 2 |
<span class="pl-c1">cd</span> <span class="pl-smi">$PROJ_ROOT</span> sh ./prepare_k210_cpio.sh |
We can now finally build the Linux 5.6-RC1 kernel:
1 2 3 |
cd "$PROJ_ROOT/linux-kernel" make ARCH=riscv CROSS_COMPILE=riscv64-linux- nommu_k210_defconfig make ARCH=riscv CROSS_COMPILE=riscv64-linux- -j |
I don’t have a Sipeed MAIX board on hand, so I have not tried that part, but you can flash the resulting image as follows assuming your board is connected over /dev/ttyUSB:
1 2 3 4 5 |
sudo usermod -a -G uucp $(whoami) sudo usermod -a -G dialout $(whoami) sudo python3 -m pip install kflash kflash -B dan -b 3000000 -p /dev/ttyUSB0 arch/riscv/boot/loader.bin python3 -m serial.tools.miniterm --raw --filter colorize /dev/ttyUSB0 115200 |
The first two lines are to add the current user to dialout group to get access to /dev/ttyUSB0 without having to be root. The third one installs kflash utility, followed by the command that does dump the image to the internal flash, and the last one is to get terminal access to the board.
That means you can now run Linux on low-cost RISC-V hardware such as the Sipeed MAIX Bit sold for around $14 and up. Note that’s technically uCLinux, you’d have to work with just 8MB RAM and handle stack overflow issues commonly experienced in processors without a memory management unit.
Jean-Luc started CNX Software in 2010 as a part-time endeavor, before quitting his job as a software engineering manager, and starting to write daily news, and reviews full time later in 2011.
Support CNX Software! Donate via cryptocurrencies, become a Patron on Patreon, or purchase goods on Amazon or Aliexpress
Thanks for the tip, I might try it on my m5stickv device which I still do not use. It will still be useless but it could be fun.
I think the fun with nommu usually only lasts from seeing the kernel output scroll up to just after hitting enter at the busybox login prompt. After that it’s a type of cruel torture as it tries to pretend it’s something usable between the shell dying over and over, running out of memory, rebooting etc.
Having played with ELKS about 20 years ago just to see a Linux prompt on a tiny x86-based router, I know pretty well what you mean 🙂
Are you aware of some legitimate use-cases of Linux on platforms without MMU with such limited memory? This is not trolling, I think this is really cool.
Many years ago, I used to develop software for Linux IPTV boxes and digital signage players based on a Sigma Designs processor without MMU. That was around 2006-2008. We did not have much experience with such systems, and we really struggled to get a stable system until we realized kernel panics were due to stack overflows. I suppose now that the costs of processors with MMU, memory, and storage have come down dramatically, it does not make that much sense.
>a stable system until we realized
>kernel panics were due to stack
>overflows
I thought the first thing anyone did was double the stack, check if the problem goes away and then forget about it until the stacks are so big the rest of the system no longer works..
You don’t really need an mmu to detect stack overflows though. With the cortex m’s almost useless mpu you can stick a no access region at the end of the stack to catch it or if you’re really in a bind get the stack somewhere where if it overflows it’ll trigger a bus exception.
Alas even the big players doing WiFi firmware that’s in everyone’s phones apparently can’t work out the 4 or so register values needed.
How did people cope in Z80 etc with no mmu, how did the world manage.
They ran toy operating systems on them and had no threat model?
Yeah right, CPM, forth, word proccessing, programing in machine code. Not todays toy programmers having their handheld by mummy .
The threat models existed. The key differences were 1: the (general) lack of remote access, and 2: shared machines almost always enjoyed some mutual trust between users. These days remote access and mutually-untrusting users are the pervasive expectations. Those operating systems had a broader attack surface once you take away the network. They didn’t have secure boot. They didn’t have a kernel enforcing local privileges. They didn’t have IO ports that were hardened to tampering. They didn’t have full disk encryption, memory encryption, TPMs, or even a concept of a root of trust. They survived without incident only because they were sparsely located, very few people had the skills or desire to exploit them, you couldn’t Google for POC exploits to teach yourself, they were very expensive and generally locked away in special rooms, and the art of Spear Phishing was still unheard of.
>how did the world manage.
Memory protection is a lot less of an issue when your “OS” is in ROM, you basically have one “thread” that has less code than what it takes to render a button on a modern UI, your external data sources don’t go much beyond a hardwired keyboard, your concurrency issues are limited not shitting on the main program from interrupts.
Z80 had disk drives, modems, god those modems where slow, below 56kps. Also not forgetting the Apple and Comodore machine.
The IBM PCj was a 8088 business machine.
And you don’t remember that shit crashing all the time?
The Amiga, based on the Motorola 68k series, also had no MMU, and even on the few Amigas that did, they didn’t make use of them. How did we program them? We allocated a little extra stack either side of arrays and strings and filled them with 0’s, then we checked whether those 0’s had been overwritten, then we searched for the responsible code and fixed it. And a lot of the time the system heap manager was corrupt and caused a Guru Meditation, and we’d just reboot and try again. When I got to write in Turbo C++ under Win3.1 which used the MMU to catch stack overflows and would immediately dump me back into the IDE, I thought that was the most brilliant thing I’d ever seen in my life, lol.
Stack overflows were only one of many many hurdles to programming in those days. We were writing our own drivers for the hardware (banging the hardware directly for performance reasons, or supporting custom hardware), managing interrupts which meant writing re-entrant code and managing nested ISR vectors. Sometimes we needed to even create an independent stack because the stack space wasn’t sufficient for our ISR. Sometimes we needed to reflect those events to service routines in the application because we mustn’t leave the IRQ mask on long enough to actually service the request. Sometimes it was the only way to avoid recursion of interrupts that would result in deadlocks. All these things had potential race conditions that were very challenging to debug, and all of those things are pretty much solved for us these days unless we’re doing stand-alone embedded development.
And yeah, in those days 9600bps (V.29) modems were commonplace, 28.8k (V.34) were the Cadillac of modems, and a few crazy people shelled out for the half-ISDN/V.42/K56Flex/X2 bastard-standard modems that never ever worked at their rated speeds thanks to the complete lack of standards compliance in the industry. Suddenly soft-modems AKA WinModems appeared on the scene to save the day, promising to meet compliance with the ITU’s final recommendations via firmware updates. Sadly these modems were unusable in DOS and Linux.
Not sure why people voted your question down… it’s a perfectly valid question.
TCC! Fabrice Bellard made once a demo of a Linux sources being compiled by TCC, and then booted a kernel out of it. Amazing.
TCC is also blindingly faster than GCC/LLVM/MSVC etc. It’s so fast that you can use it as a scripting engine for reasonably small projects, and that’s part of what makes it a great addon for this uCLinux installation, since all projects built on this tiny system are going to be small by definition, and this is literally orders of magnitude faster than microPython or mJS.
Pre-openwrt wifi SOC ISL3893 was without mmu with ucdist, a nigthmare to have software compiling for it…
http://isl3893.wikidot.com/
http://isl3893.sf.net/
/me can’t believe there is something worse than old MIPS SoCs with 2.6 kernels
In the mmu-less era, we built communication gateways with ucLinux on Motorola 68328 (dragonball) processors. Nostalgia…
dragonball should still work in mainline if you want to try it.
@CNXSoft: Correction – if you want to use the KPU then you only have access to 6MB of RAM, since 2MB is reserved for the KPU. Probably best to configure the kernel not to touch that memory since you’ll corrupt things if the KPU starts up.
see section 3.4 of the K210 datasheet here:
https://s3.cn-north-1.amazonaws.com.cn/dl.kendryte.com/documents/kendryte_datasheet_20181011163248_en.pdf
I got the maix:bit and the maixduino kits with camera and LCD displays. There’s a bug in the camera library but it’s an easy fix, and so far I can’t get the MAIX’s ESP8266 WiSOC talking to the K210. My buddy wants to build a telepresence bot and this seems like an ideal platform with the machine hearing/vision, and wide range of re-programmable GPIO/ADC/DAC/PWM pins available.
Thanks for the guide, the linked instructions had some parts that didn’t work.
I pushed it down to my MAIX and… get a kernel panic!
[ 0.265071] Freeing unused kernel memory: 1712K
[ 0.268862] This architecture does not have kernel memory protection.
[ 0.275274] Run /sbin/init as init process
[ 0.279469] Run /etc/init as init process
[ 0.283457] Run /bin/init as init process
————————[ 0.294233] unexpected interrupt cause 0x8000000000000009
[ 0.294244] ————[ cut here ]————
[ 0.304217] kernel BUG at arch/riscv/kernel/irq.c:43!
[ 0.309252] Kernel BUG [#1]
Still, this is much more successul than my previous hacking on this platform, so I’m happy.
Just tried Damien’s prebuild kernel w/built-in cpio: much better
Means my earlier build probably has a broken rootfs, so at least it’s a simple fix.
[ 0.232785] Freeing unused kernel memory: 320K
[ 0.236490] This architecture does not have kernel memory protection.
[ 0.242906] Run /sbin/init as init process
[ 0.247103] Run /etc/init as init process
[ 0.251097] Run /bin/init as init process
—————————–
| Kendryte K210 NOMMU Linux |
—————————–
Mounting /proc
Starting shell
BusyBox v1.32.0.git (2020-02-12 17:51:45 JST) hush – the humble shell
Enter ‘help’ for a list of built-in commands.
/ #
Linux 0.11 (The version released in December 1991) ported to K210:
https://github.com/lizhirui/K210-Linux0.11
hey anyone has tried this with the current linux 5.9 kernel? it should be able to create an image with the official kernel in the meanwhile. any ideas?