这是一个面向初学者的教程,主要介绍如何编译内核以在k210上运行NOMMU linux。 并且,本教程将展示如何交叉编译tcc,以便您可以在k210上使用tcc执行C程序。 内核源可从https://www.kernel.org下载,并应用了Damien Le Moal’s k210 patch@damien-lemoal (Damien Le Moal的k210补丁)
在build之前,您应该先克隆该项目并安装buildroot依赖的软件包。
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 https://github.com/vowstar/k210-linux-nommu.git
cd k210-linux-nommu
export PROJ_ROOT=$(pwd)
首先我们需要编译工具链,根据Damien Le Moal的k210补丁的描述,可以通过修改后的buildroot获得该工具链。 最初计划作为git子模块添加,但是有人说这是针对初学者的教程,因此buildroot源代码已添加到该项目中。
Damien Le Moal@damien-lemoal的原始buildroot:
https://github.com/damien-lemoal/riscv64-nommu-buildroot
运行sh ./prepare_buildroot.sh
将内核归档文件放入riscv64-nommu-buildroot/package/kernel
中,以防止找不到文件错误。
cd $PROJ_ROOT
sh ./prepare_buildroot.sh
构建内核之前,我们应该首先构建riscv64 nommu uClibc工具链。
这个过程需要一个良好的网络连接状况, 因为编译时会下载很多软件。
cd "$PROJ_ROOT/riscv64-nommu-buildroot"
make riscv64_nommu_defconfig
make
安装工具链
cd "$PROJ_ROOT/riscv64-nommu-buildroot"
sudo cp -r output/host /opt/riscv64-uclibc
export PATH=/opt/riscv64-uclibc/bin:$PATH
Busybox
busybox 是clone至 git://git.busybox.net/busybox.git 最初计划作为git子模块添加,但是有人说这是针对初学者的教程,因此busybox源代码已添加到该项目中。
通过修改 $PROJ_ROOT/busybox/configs/k210_nommu_defconfig
文件 以适配 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
安装以后,所有的数据都安装到 $PROJ_ROOT/rootfs_k210
Tinycc - 这个最小的ANSI C编译器. 我们希望在k210上有一个C编译器,可以开发k210 C程序。 因此,我们交叉编译了tcc。
这个Tiny C编译器源码来自于:https://github.com/mirror/tinycc.git
. 最初计划作为git子模块添加,但是有人说这是初学者的教程,因此tinycc源代码已添加到该项目中。
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
如果没有添加 --extra-cflags="-DCONFIG_TCC_STATIC=1", 编译的时候会出现错误:
tcc.h:41:12: fatal error: dlfcn.h: No such file or directory
可以通过这个命令来修复它:-DCONFIG_TCC_STATIC=1
另外,当前的k210 nommu uclibc不支持线程,因此我更改了代码并删除了 -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
编译tcc时,它将消耗大量内存,这使得无法在k210 nommu linux上运行。 @minux仅在10分钟内找到了原因,并按如下所示编辑代码:
--- 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
然后就可以正常工作了。
然后,当tcc -run时,我们遇到了mprotect问题。 @minux找到了原因,k210 nommu linux不需要mprotect。 编辑代码:
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);
然后,它基本上就完整了。
待办事项:添加elf2flt支持。 tcc的输出格式是elf,不能直接在k210 nommu linux下运行,因此需要一些转换。
但是tcc -run
可以工作,例如:
// 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:
RUN TCC
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
linux-5.6-rc1 源码应用了 Damien Le Moal 的 k210 补丁。
感谢下面这些人的贡献, signed-off-by commiters:
开始的计划是作为一个git子模块添加,但是有人说这是初学者的教程,因此内核源代码已添加到该项目中。
为了能够顺利进行编译,已经将ROOTFS k210.cpio二进制文件放入内核源目录中。 该文件不应提交到源目录。 这是一个负面的例子,请不要像我这样。
cd "$PROJ_ROOT/linux-kernel"
export PATH=/opt/riscv64-uclibc/bin:$PATH
make ARCH=riscv CROSS_COMPILE=riscv64-linux- nommu_k210_defconfig
make ARCH=riscv CROSS_COMPILE=riscv64-linux- -j
对k210开发板进行编程,并享受linux的乐趣。 假设您正在使用Sipeed MAIX dan开发板,并且串行端口为/ dev / ttyUSB0。要想使用用户$ {whoami)的串行端口,需要将$ {whoami)添加到 uucp 或/和 Dialout 组中。
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
使用 vi 编辑器添加一个文件: main.c
#include
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;
}
然后运行: 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!!!
最后就可以输出一个“hello world!”了。