基于qemu-riscv64模拟器运行debian(续)

上一篇[http://www.cnovirt.com/archives/616] 说明了如何基于qemu-riscv64运行由debian提供的完整系统镜像。

该镜像中包含的kernel是5.5.0。

本篇介绍如何编译最新版本的内核,并加入到debian镜像中。

我的宿主机仍然是Ubuntu 18.04 LTS(X86_64)。

交叉编译器(重要)

上一篇中,我们安装了Ubuntu 18.04自带的gcc-riscv64-linux-gnu这个包,实际上隐含安装和指向的是gcc-7-riscv64-linux-gnu编译器。

如果继续使用这个版本的交叉编译器,当我们启动内核时,内核报错

ext4: #The unexpected relocation type ‘R_RISCV_ALIGN’ from PC = XXX#

其原因是:gcc-7-riscv64-linux-gnu不支持-mno-relax选项,导致arch/riscv/Makefile中如下这一行不生效

KBUILD_CFLAGS_MODULE += $(call cc-option,-mno-relax)

结果编译生成的模块(ext4.ko等)会默认包含R_RISCV_ALIGN一类的relaxation指示信息。

通过查看arch/riscv/kernel/module.c文件中apply_r_riscv_align_rela函数的实现,我们发现内核在加载模块时,会检查并处理各种relaxation types,目前不支持R_RISCV_ALIGN这一类型,直接报错。

幸运的是Ubuntu 18.04 LTS还包含一个高版本的交叉编译器,它支持-mno-relax选项。

所以需要卸载gcc-7-riscv64-linux-gnu并安装gcc-8-riscv64-linux-gnu;然后创建链接/usr/bin/riscv64-linux-gnu-gcc指向/usr/bin/riscv64-linux-gnu-gcc-8

update-alternatives --install /usr/bin/riscv64-linux-gnu-gcc riscv64-linux-gnu-gcc /usr/bin/riscv64-linux-gnu-gcc-8 1

下载内核

从国内的镜像下载内核代码并切换到开发分支for-next。这个镜像服务应该是阿里提供的,他们干了一件有意义的大好事,表扬一下 :)

git clone http://kernel.source.codeaurora.cn/pub/scm/linux/kernel/git/riscv/linux.git

git checkout for-next

建立配置

为了方便,直接以debian镜像中包含的config-5.5.0-1-riscv64为模板创建.config。

确保当前目录为kernel源码的根目录

guestmount -a image.qcow2 -m /dev/sda1 /mnt/

cp /mnt/boot/config-5.5.0-1-riscv64 ./.config

guestunmount /mnt

编辑.config,找到CONFIG_SYSTEM_TRUSTED_KEYS=”debian/certs/debian-uefi-certs.pem”这一行,

改为CONFIG_SYSTEM_TRUSTED_KEYS=””,因为我们没有这个证书。

make ARCH=riscv CROSS_COMPILE=riscv64-linux-gnu- menuconfig

此时弹出menuconfig交互界面,直接选择Save,然后Exit。这样就得到配置文件.config。

编译内核

make ARCH=riscv CROSS_COMPILE=riscv64-linux-gnu- -j $(nproc)

创建一个输出目录/tmp/output,准备接收生成的内核与模块文件目录。

mkdir /tmp/output

make ARCH=riscv install INSTALL_PATH=/tmp/output

make ARCH=riscv modules_install INSTALL_MOD_PATH=/tmp/output

建立FIT Image格式的内核文件

在用u-boot启动这个新内核之前,我们还是要把vmlinuz转换成U-boot FIT Image。

假设编译产生的原始文件为vmlinuz-5.7.0-rc1,将其更名为image

ITS文件的内容如下:

/*
 * Simple U-Boot uImage source file containing a single kernel and FDT blob
 */

/dts-v1/;

/ {
    description = "Simple image with single Linux kernel and FDT blob";
    #address-cells = ;

    images {
        kernel {
            description = "Linux kernel";
            data = /incbin/("./output/vmlinuz-5.7.0-rc1");
            type = "kernel";
            arch = "riscv";
            os = "linux";
            compression = "none";
            load = ;
            entry = ;
            hash-1 {
                algo = "crc32";
            };
            hash-2 {
                algo = "sha1";
            };
        };
        fdt-1 {
            description = "Flattened Device Tree blob";
            data = /incbin/("./qemu-virt.dtb");
            type = "flat_dt";
            arch = "riscv";
            compression = "none";
            hash-1 {
                algo = "crc32";
            };
            hash-2 {
                algo = "sha1";
            };
        };
    };

    configurations {
        default = "conf-1";
        conf-1 {
            description = "Boot Linux kernel with FDT blob";
            kernel = "kernel";
            fdt = "fdt-1";
        };
    };
};

注意”data = /incbin/(“./image”);”这一行指向是原始vmlinuz文件。

另外,确保qemu-virt.dtb在当前目录。延用上一篇的方法生成的qemu-virt.dtb,这个没有变化。

执行mkimage转换格式:

~/u-boot/tools/mkimage -f ./kernel_fdt.its ./vmlinuz-5.7.0-rc1

这样就生成了新的vmlinuz-5.7.0-rc1,只不过新文件的格式是FIT image。

把新内核加入debian镜像

新的内核文件和模块文件目录都在/tmp/output目录下,且vmlinuz已经是FIT Image格式。

下面我们用guestmount把debian镜像挂到/mnt目录,执行替换:

guestmount -a image.qcow2 -m /dev/sda1 /mnt/

cp ./output/* /mnt/boot/

cp -r ./output/lib/modules/5.7.0-rc1 /mnt/lib/modules/

guestunmount /mnt

生成initrd

现在debian镜像中已经包含了新的内核即模块,我们还需要生成initrd,然后再启用新内核。

启动qemu进入虚拟机,此时仍然是原内核5.5.0。

首先,修改/etc/initramfs-tools/initramfs.conf,设置MODULES=dep(原来是most),这样生成的initrd大约只有15M。而如果采用默认值most,生成的initrd将会达到300M以上,这样内核在解压此内存盘时会超出内存界限而崩溃。

然后在/boot目录下,执行如下脚本生成initrd。注意5.7.0-rc1是当前实验的内核版本。

mkinitramfs -o ./initrd.img-5.7.0-rc1 5.7.0-rc1

启用新内核

下面让u-boot产生新的启动项。

首先,修改/etc/default/u-boot中的引导参数U_BOOT_PARAMETERS,把它的值改为

“rw noquiet root=LABEL=rootfs earlycon=uart8250,mmio,0x100000000,115200n8 console=ttyS0”

如此将会引入earlycon,这样就可以显示内核启动早期的信息,便于排查错误。

然后更新u-boot引导启动项

u-boot-update

启动新内核

再次启动qemu,此次会出现新内核的引导项,正常就是第1项。

启动后可以在shell下执行uname -a确认启动新内核成功。

0 条回复 A 作者 M 管理员 E
    所有的伟大,都源于一个勇敢的开始!
欢迎您,新朋友,感谢参与互动!欢迎您 {{author}},您在本站有{{commentsCount}}条评论