Awesome
k210-linux-nommu
Introduction
This is a tutorial for beginners, mainly describes how to compile the kernel to run NOMMU linux on k210. And, this tutorial will show how to cross compile a tcc so that you can execute a C program with tcc on the k210. The kernel source is download from https://www.kernel.org, and applied Damien Le Moal's k210 patch @damien-lemoal
Before build
Before build, you should clone this project first and install the packages that buildroot depends on.
Debian bullseye or Ubuntu 18.04.4
DEBIAN_FRONTEND=noninteractive apt-get update -qq && \
DEBIAN_FRONTEND=noninteractive apt-get install -yq \
build-essential \
device-tree-compiler \
bison \
flex \
file \
git \
curl \
wget \
cpio \
python \
unzip \
rsync \
bc
Fedora 31 or CentOS 8
RUN dnf -y update && \
dnf -y groupinstall 'Development Tools' && \
dnf -y --enablerepo=PowerTools install \
autoconf \
gperf \
bison \
flex \
wget \
curl \
git \
python36 \
perl \
sudo \
cpio
Git clone
git clone https://github.com/JAicewizard/k210-linux-nommu.git
cd k210-linux-nommu
export PROJ_ROOT=$(pwd)
Init kernel tree
First we need to download the kernel sources. This is done by
cd $PROJ_ROOT
git submodule update --init
This might take a while as the kernel tree is very large.
If you want to try out a specific version you can run the following to start using kernel version x.y
cd "$PROJ_ROOT/linux-kernel"
git checkout vx.y
To top it off we need to apply a small patch that allows us to start a shell:
cd "PROJ_ROOT/linux-kernel"
git apply ../fixk210.patch
Buildroot
After this we need to compile the toolchain, according to the description of Damien Le Moal's k210 patch, the toolchain can be obtained through modified buildroot. Originally planned to be added as a git submodule, but some people said that this is a tutorial for beginners, so the buildroot source code was added to this project.
Original buildroot by Damien Le Moal @damien-lemoal:
https://github.com/damien-lemoal/riscv64-nommu-buildroot
Move kernel
Run sh ./prepare_buildroot.sh
to put kernel archive into riscv64-nommu-buildroot/package/kernel
to prevent file not found error.
cd $PROJ_ROOT
sh ./prepare_buildroot.sh
Build toolchain
Before build kernel, we should build riscv64 nommu uClibc toolchain first.
This process needs to ensure a good network connection. A lot of software is downloaded when compiling.
cd "$PROJ_ROOT/riscv64-nommu-buildroot"
make riscv64_nommu_defconfig
make
Install toolchain
cd "$PROJ_ROOT/riscv64-nommu-buildroot"
sudo cp -r output/host /opt/riscv64-uclibc
export PATH=/opt/riscv64-uclibc/bin:$PATH
Build Rootfs
Busybox
This busybox clone from git://git.busybox.net/busybox.git. Originally planned to be added as a git submodule, but some people said that this is a tutorial for beginners, so the busybox source code was added to this project.
I modified the $PROJ_ROOT/busybox/configs/k210_nommu_defconfig
file to fit k210 nommu linux.
export PATH=/opt/riscv64-uclibc/bin:$PATH
cd "$PROJ_ROOT/busybox"
make k210_nommu_defconfig
make SKIP_STRIP=y
make SKIP_STRIP=y install
After install, all the data installed to $PROJ_ROOT/rootfs_k210
Tiny C Compiler
C Scripting Everywhere - The Smallest ANSI C compiler. We have a wish is to have a C compiler on k210 that can develop k210 C programs. So we cross-compiled tcc. This process is done by @minux and me together.
The Tiny C Compiler source code from https://github.com/mirror/tinycc.git. Originally planned to be added as a git submodule, but some people said that this is a tutorial for beginners, so the tinycc source code was added to this project.
export PATH=/opt/riscv64-uclibc/bin:$PATH
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
If not add --extra-cflags="-DCONFIG_TCC_STATIC=1"
, when compile it will get error:
tcc.h:41:12: fatal error: dlfcn.h: No such file or directory
@minux fix it using -DCONFIG_TCC_STATIC=1
Also the current k210 nommu uclibc have no threading support, so I changed the code and removed the -lpthread
:
--- a/Makefile
+++ b/Makefile
@@ -30,7 +30,8 @@ ifdef CONFIG_WIN32
CFGWIN = -win
NATIVE_TARGET = $(ARCH)-win$(if $(findstring arm,$(ARCH)),ce,32)
else
- LIBS=-lm -lpthread
+ #LIBS=-lm -lpthread
+ LIBS=-lm
ifneq ($(CONFIG_ldl),no)
LIBS+=-ldl
endif
When tcc is compiled, it consumes a lot of memory, which makes it impossible to run on k210 nommu linux. @minux found the reason in only 10 minutes, and edit the code as follows:
--- a/tccpp.c
+++ b/tccpp.c
@@ -130,9 +130,9 @@ ST_FUNC void expect(const char *msg)
#define TAL_DEBUG_FILE_LEN 40
#endif
-#define TOKSYM_TAL_SIZE (768 * 1024) /* allocator for tiny TokenSym in table_ident */
-#define TOKSTR_TAL_SIZE (768 * 1024) /* allocator for tiny TokenString instances */
-#define CSTR_TAL_SIZE (256 * 1024) /* allocator for tiny CString instances */
+#define TOKSYM_TAL_SIZE (64 * 1024) /* allocator for tiny TokenSym in table_ident */
+#define TOKSTR_TAL_SIZE (64 * 1024) /* allocator for tiny TokenString instances */
+#define CSTR_TAL_SIZE (16 * 1024) /* allocator for tiny CString instances */
#define TOKSYM_TAL_LIMIT 256 /* prefer unique limits to distinguish allocators debug msgs */
#define TOKSTR_TAL_LIMIT 128 /* 32 * sizeof(int) */
#define CSTR_TAL_LIMIT 1024
And then it is works.
Then, we encountered the mprotect problem when tcc -run
. @minux found the reason, k210 nommu linux don't need mprotect. Edit the code:
diff --git a/tccrun.c b/tccrun.c
index 4bf709d..42a0852 100644
--- a/tccrun.c
+++ b/tccrun.c
@@ -304,6 +304,13 @@ static int tcc_relocate_ex(TCCState *s1, void *ptr, addr_t ptr_diff)
static void set_pages_executable(TCCState *s1, void *ptr, unsigned long length)
{
+#if defined TCC_TARGET_RISCV64
+ /* RISC-V NON MMU don't need mprotect */
+ void __clear_cache(void *beginning, void *end);
+ __clear_cache(ptr, (char *)ptr + length);
+ return;
+#endif
+
#ifdef _WIN32
unsigned long old_protect;
VirtualProtect(ptr, length, PAGE_EXECUTE_READWRITE, &old_protect);
It almost works.
TODO: Add elf2flt support. The tcc output format is elf, can't run under k210 nommu linux directly, so need some convert.
But tcc -run
works, example:
// main.c
int fib(int n){
if(n < 2){
return 1;
}
return fib(n-1) + fib(n-2);
}
int _start() {
int i;
for (i = 0; i < 15; i++)
printf("%d ", fib(i));
printf("Hello world from K210!!!\n");
return 0;
}
Run with:
tcc -run -nostdlib main.c
The result:
k210 cpio image
Run sh ./prepare_k210_cpio.sh
to put k210 rootfs cpio image into linux-kernel/k210.cpio
to update images.
cd $PROJ_ROOT
sh ./prepare_k210_cpio.sh
Build Kernel
The linux-5.6-rc1 source applied Damien Le Moal's k210 patch.
Thank you so much for their efforts, signed-off-by commiters:
- Damien Le Moal damien.lemoal@wdc.com
- Anup Patel anup.patel@wdc.com
- Christoph Hellwig hch@lst.de
Originally planned to be added as a git submodule, but some people said that this is a tutorial for beginners, so the kernel source code was added to this project.
In order to be able to compile smoothly, I shamelessly put the ROOTFS k210.cpio
binary file into the kernel source directory. This file should not be submitted to the source directory. This is a negative example, please do not do this like me.
export PATH=/opt/riscv64-uclibc/bin:$PATH
cd "$PROJ_ROOT/linux-kernel"
make ARCH=riscv CROSS_COMPILE=riscv64-linux- nommu_k210_defconfig
make ARCH=riscv CROSS_COMPILE=riscv64-linux- -j
Build kernel with SD support
You can also use the SD card for rootfs. This can be done with the patched config as follows:
export PATH=/opt/riscv64-uclibc/bin:$PATH
cd "$PROJ_ROOT/linux-kernel"
make ARCH=riscv CROSS_COMPILE=riscv64-linux- nommu_k210_sdcard_defconfig
make ARCH=riscv CROSS_COMPILE=riscv64-linux- -j
Play
Program the k210 board and enjoy linux. Suppose you are using Sipeed MAIX dan development board and the serial port is /dev/ttyUSB0
.
To use serial port whith user $(whoami), you need add $(whoami) into uucp or/and dialout group.
sudo usermod -a -G uucp $(whoami)
sudo usermod -a -G dialout $(whoami)
sudo python3 -m pip install kflash
su $(whoami)
kflash -B dan -b 3000000 -p /dev/ttyUSB0 arch/riscv/boot/loader.bin
python3 -m serial.tools.miniterm --raw --filter colorize /dev/ttyUSB0 115200
Using vi editor to add a file main.c
#include <stdio.h>
int fib(int n){
if(n < 2){
return 1;
}
return fib(n-1) + fib(n-2);
}
int main(int argc, char* argv[]) {
int i;
for (i = 0; i < 15; i++)
printf("%d ", fib(i));
printf("Hello world from K210!!!\n");
return 0;
}
And run with tcc -run main.c
tcc -run main.c
1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 Hello world from K210!!!
You will get the hello world output.
Enjoy!
Using u-boot
This is a short list of commands to download and build u-boot, and setup linux booting.
These instructions are MAIX-BIT specifix, for other boards you need diferent configs. Look these up for your board. The u-boot config and the CONFIG_SOC_CANAAN_K210_DTB_SOURCE
, arch/riscv/configs/nommu_k210_sdcard_defconfig
, and the dtb moved to the SD card needs to be changes
Clone the sources:
git clone https://source.denx.de/u-boot/u-boot.git
Build for RISC-V:
export PATH=/opt/riscv64-uclibc/bin:$PATH
make sipeed_maix_bitm_defconfig
make CROSS_COMPILE=<your cross compile prefix>
prepare SD card
Grab an SD card, and partition it with partition 1 being FAT32 and partition 2 EXT2.
If you intend to use non-SDcard linux config, the second partition is not needed, and all instructions using it can be skipped.
Mount both partiions, here the mountpoints will be called P1 and P2.
Go to the u-boot root and execute:
tools/mkimage -A riscv -O linux -T kernel -C none -a 0x80000000 -e 0x80000000 -n linux -d {Path to linux "Image" file} P1/uImage
Note that the Image file is called Image, it is not the loader.bin used above, but is in the same directory.
Copy the dtb over (required by u-boot)
cd $PROJ_ROOT
cp arch/riscv/boot/dts/canaan/sipeed_maix_bit.dtb P1/k210.dtb
Copy the rootfs:
cd "$PROJ_ROOT/rootfs_k210"
cp * P2 -r
Unmount the SD card, put it in your device.
Then flash u-boot:
kflash -tp /dev/ttyUSB0 -B goE u-boot-dtb.bin
This will automatically open the miniterm, you should see u-boot booting. It should show a countdown until automatic boot, once it hits 0 it should boot linux with the shell as earlier.