K210--运行NOMMU linux

介绍

 这是一个面向初学者的教程,主要介绍如何编译内核以在k210上运行NOMMU linux。 并且,本教程将展示如何交叉编译tcc,以便您可以在k210上使用tcc执行C程序。 内核源可从https://www.kernel.org下载,并应用了Damien Le Moal’s k210 patch@damien-lemoal (Damien Le Moal的k210补丁)


build之前

 在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

	git clone https://github.com/vowstar/k210-linux-nommu.git
	
	cd k210-linux-nommu
	
	export PROJ_ROOT=$(pwd)

Buildroot

 首先我们需要编译工具链,根据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

构建 Rootfs

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

Tiny C 编译器

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!”了。

你可能感兴趣的:(嵌入式linux,K210)