在QEMU上运行OpenSBI+Linux+Rootfs

2024-05-07 21:36
文章标签 linux 运行 rootfs qemu opensbi

本文主要是介绍在QEMU上运行OpenSBI+Linux+Rootfs,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在QEMU上运行OpenSBI+Linux+Rootfs

  • 1 编译QEMU
  • 2 安装交叉编译工具
  • 3 编译OpenSBI
  • 4 编译Linux
  • 5 创建根文件系统
    • 5.1 编译busybox
    • 5.2 创建目录结构
    • 5.3 制作文件系统镜像
      • 5.3.1 创建 ext2 文件
      • 5.3.2 将目录结构拷贝进 ext2 文件
      • 5.3.3 取消挂载
  • 6 运行OpenSBI+Linux+Rootfs

本文所使用的版本,如下:

  • qemu-7.1.0
  • 交叉编译工具:riscv64-glibc-ubuntu-20.04-nightly-2023.01.31-nightly
  • opensbi v1.2
  • linux 5.15.158
  • busybox 1.36.1

1 编译QEMU

下载qemu-7.1.0源码

wget https://download.qemu.org/qemu-7.1.0.tar.xz

解压

tar xvJf qemu-7.1.0.tar.xz

进入目录

cd qemu-7.1.0

配置qemu

./configure --target-list=riscv64-softmmu,riscv64-linux-user --prefix=/opt/qemu

riscv64-softmmu 指定生成riscv64 system emulator
riscv64-linux-user 指定生成riscv64 user mode emulator

执行编译

make -j`nproc`

在qemu-7.1.0/build/目录下,生成了可执行程序qemu-system-riscv64

查看qemu版本

./qemu-system-riscv64 --version

可以看到版本号为:QEMU emulator version 7.1.0

安装(可选)

sudo make install

安装时,会将可执行程序qemu-system-riscv64,以及头文件等其他文件,拷贝至/opt/qemu目录下。

到这里,模拟器已经准备好了,我们还需要准备OpenSBI(Bootloader)、Linux Kernel以及文件系统(busybox)。
接下来,我们继续。

2 安装交叉编译工具

我们先安装交叉编译工具,以便后续编译OpenSBI、Linux Kernel以及busybox。

下载编译好的RISC-V交叉编译器:
riscv64-glibc-ubuntu-20.04-nightly-2023.01.31-nightly.tar.gz

解压

tar -xvzf riscv64-glibc-ubuntu-20.04-nightly-2023.01.31-nightly.tar.gz

编辑.bashrc

vim ~/.bashrc

将编译器bin路径(/home/tools/riscv/bin替换为自己的路径),加入.bashrc文件末尾。

export PATH=/home/tools/riscv/bin:$PATH

使路径生效

source ~/.bashrc

最后,查看编译器版本,以验证安装

riscv64-unknown-linux-gnu-gcc -v

编译器环境搭建好了。

3 编译OpenSBI

下载源码

git clone https://github.com/riscv-software-src/opensbi.git

进入目录

cd opensbi/

获取V1.2版本源码

git checkout v1.2

实验时,使用最新版本,会报错,因此这里使用V1.2版本。
具体报错信息,如下:
qemu-system-riscv64: Some ROM regions are overlapping
These ROM regions might have been loaded by direct user request or by default.
They could be BIOS/firmware images, a guest kernel, initrd or some other file loaded into guest memory.
Check whether you intended to load all this guest code, and whether it has been built to load to the correct addresses.
The following two regions overlap (in the memory address space):
fw_jump.elf ELF program header segment 1 (addresses 0x0000000000000000 - 0x0000000000023940)
mrom.reset (addresses 0x0000000000001000 - 0x0000000000001028)

设置环境变量

export CROSS_COMPILE=riscv64-unknown-linux-gnu-

执行编译

make PLATFORM=generic

编译完后,在opensbi/build/platform/generic/firmware/目录下
生成的固件,如下所示:
在这里插入图片描述

生成的固件有三种类型:dynamic、jump和payload

  • dynamic:带有动态信息的固件
  • jump:指定下一级的boot地址跳转
  • payload:包含下一级boot的二进制内容,通常是uboot/linux

这里我们使用jump类型固件(fw_jump.elf),OpenSBI运行后,可以直接跳转到kernel运行。

因为opensbi本身就是一个bootloader,因此可以不使用uboot引导kernel,通过opensbi的jump固件,可以直接跳转到kernel启动。

4 编译Linux

Linux内核官网:https://www.kernel.org,进去找到linux-5.15.158版。

这里,可直接下载linux-5.15.158源码:
https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.15.158.tar.xz

解压

tar -xf linux-5.15.158.tar.xz

进入目录

cd linux-5.15.158

配置环境变量

export ARCH=riscv
export CROSS_COMPILE=riscv64-unknown-linux-gnu-

使用linux默认配置

make defconfig

执行编译

make -j`nproc`

编译完成后,在linux-5.15.158/arch/riscv/boot下生成可执行文件Image

5 创建根文件系统

5.1 编译busybox

下载busybox源码

git clone https://git.busybox.net/busybox
cd busybox

打开busybox配置界面

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

配置为生成静态二进制可执行程序,选择“Build static binary (no shared libs)”

在这里插入图片描述

如果是动态编译,那么在kernel中运行时,需要携带运行库。
没有运行库,就会报错: Starting init: /sbin/init exists but couldn’t execute it (error -8)

执行编译与安装

make ARCH=riscv CROSS_COMPILE=riscv64-unknown-linux-gnu- -j`nproc`
make ARCH=riscv CROSS_COMPILE=riscv64-unknown-linux-gnu- install -j`nproc`
cd ..

源码目录下,生成了二进制文件: busybox

5.2 创建目录结构

创建主要目录

mkdir root && cd root
mkdir -p bin etc etc/init.d dev lib proc opt sbin sys tmp usr usr/bin usr/lib usr/sbin

将busybox文件,复制到bin目录中

cp /path/to/busybox/busybox bin/busybox

生成指向init文件的符号链接

ln -s ../bin/busybox sbin/init
ln -s sbin/init init
ln -s ../bin/busybox bin/sh

接下来,解释这些命令。

  • ln -s ../bin/busybox sbin/init:表示在sbin目录下创建一个名为 init 的符号链接,该链接指向父目录中的 bin 子目录下的 busybox 文件。
    在标准的 Linux 发行版中,/sbin/init 是初始化系统的入口点,即这个init文件,就是init进程,它是内核启动的第一个用户级进程。
    因此,需要使用 BusyBox 替代标准的 init 进程。
    需要将 BusyBox 的路径链接到 /sbin/init 或 /init,以便在系统启动时能够找到并执行它。
  • ln -s sbin/init init:表示在当前目录下创建一个名为init的符号链接,该链接指向sbin/init文件。
  • ln -s ../bin/busybox bin/sh:表示在bin目录下创建一个名为sh的符号链接,该链接指向父目录中的 bin 子目录下的 busybox 文件。
    许多脚本和程序都依赖于 /bin/sh 的存在。
    通过创建这个链接,可以确保这些脚本能够在使用BusyBox的系统中正常运行。
    比如,当 init 进程先执行,然后会调用 /etc/init.d/rcS 启动脚本,此时如果没有/bin/sh的话,就会报错:can’t run ‘/etc/init.d/rcS’: No such file or directory


    若没有找到init文件时,会报以下错误:
    [ 0.584936] Run /sbin/init as init process
    [ 0.593521] Run /etc/init as init process
    [ 0.594686] Run /bin/init as init process
    [ 0.595995] Run /bin/sh as init process
    [ 0.596717] Kernel panic - not syncing: No working init found.

创建必要设备节点

sudo mknod dev/sda b 8 0 
sudo mknod dev/console c 5 1

创建初始化脚本

vim etc/init.d/rcS

在rcS中填入如下内容

#!/bin/sh
/bin/busybox mount -o remount rw /
/bin/busybox --install -smount -t proc proc /proc
mount -t sysfs sys /sys

接下来,解释这些命令。

  • /bin/busybox mount -o remount rw /:使用 BusyBox 中的 mount 命令,来重新挂载(remount)根文件系统(/)为读写模式(rw)。
    从Kernel日志:VFS: Mounted root (ext2 filesystem) readonly on device 254:0,发现默认按照只读挂载,因此本命令就是,以可读写重新挂载。
  • /bin/busybox --install -s :将 BusyBox 安装到当前系统中,包括在/bin目录下,创建大量常用命令的符号链接(可作为单独命令使用),会进行写入操作,因此需要文件系统可写。

赋予rcS文件可执行权限

chmod +x etc/init.d/rcS

此时root目录结构,如下所示:

在这里插入图片描述

到这里,root目录下,已经具备了一个linux文件系统的基本目录了。

5.3 制作文件系统镜像

我们,将上述root目录结构,打包成ext2镜像文件。

5.3.1 创建 ext2 文件

生成4M大小的,空白镜像文件rootfs.ext2

dd if=/dev/zero of=rootfs.ext2 bs=1M count=4

将镜像文件rootfs.ext2,格式化为ext2文件系统

mkfs.ext2 rootfs.ext2

5.3.2 将目录结构拷贝进 ext2 文件

创建目录

mkdir /mnt/ext2/

将rootfs.ext2,挂载到/mnt/ext2/目录

mount -t ext2 rootfs.ext2 /mnt/ext2/

挂载到/mnt/ext2/目录后,当你在 /mnt/ext2/ 下写入文件或修改文件时,这些更改会反映到 rootfs.ext2 文件中。

拷贝root目录内容,至/mnt/ext2/

cp -pr root/* /mnt/ext2/

也就是,将目录结构拷贝进 rootfs.ext2 文件中了。

5.3.3 取消挂载

umount /mnt/ext2/
sync		# 强制内容立刻写入文件

到这里,我们已经对rootfs.ext2文件写入完毕,写入的内容,主要包括busybox、目录结构、rcS文件等。

rootfs.ext2,即为可供 Linux kernel 挂载所用的根文件系统。

6 运行OpenSBI+Linux+Rootfs

将fw_jump.elf、Image、rootfs.ext2,拷贝到qemu-7.1.0/build/目录下。

新建start-qemu.sh脚本,输入以下内容:

#!/bin/sh./qemu-system-riscv64 -M virt \
-bios fw_jump.elf \
-kernel Image \
-append "rootwait root=/dev/vda ro" \
-drive file=rootfs.ext2,format=raw,id=hd0 \
-device virtio-blk-device,drive=hd0 \
-netdev user,id=net0 -device virtio-net-device,netdev=net0 -nographic
  • bios,指定OpenSBI可执行文件fw_jump.elf
  • kernel,指定Linux镜像Image
  • drive,指定根文件系统rootfs.ext2

修改权限

chmod 777 start-qemu.sh

在QEMU中,运行OpenSBI+Linux+Rootfs

./start-qemu.sh

运行效果,如下所示:

在这里插入图片描述

成功进入Linux!

在这里插入图片描述

基本命令可用,使用poweroff命令可关机。

如果Kernel,发生panic异常时,通过Ctrl+C无法退出。
可以通过以下命令,来退出qemu:
kill $(ps aux | grep qemu | grep -v grep | awk '{print $2}')


参考链接:

  • 《手把手教你在QEMU上运行RISC-V Linux》
  • 《在 QEMU 上运行 RISC-V 64 位版本的 Linux》

这篇关于在QEMU上运行OpenSBI+Linux+Rootfs的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/968455

相关文章

Linux join命令的使用及说明

《Linuxjoin命令的使用及说明》`join`命令用于在Linux中按字段将两个文件进行连接,类似于SQL的JOIN,它需要两个文件按用于匹配的字段排序,并且第一个文件的换行符必须是LF,`jo... 目录一. 基本语法二. 数据准备三. 指定文件的连接key四.-a输出指定文件的所有行五.-o指定输出

Linux jq命令的使用解读

《Linuxjq命令的使用解读》jq是一个强大的命令行工具,用于处理JSON数据,它可以用来查看、过滤、修改、格式化JSON数据,通过使用各种选项和过滤器,可以实现复杂的JSON处理任务... 目录一. 简介二. 选项2.1.2.2-c2.3-r2.4-R三. 字段提取3.1 普通字段3.2 数组字段四.

Linux kill正在执行的后台任务 kill进程组使用详解

《Linuxkill正在执行的后台任务kill进程组使用详解》文章介绍了两个脚本的功能和区别,以及执行这些脚本时遇到的进程管理问题,通过查看进程树、使用`kill`命令和`lsof`命令,分析了子... 目录零. 用到的命令一. 待执行的脚本二. 执行含子进程的脚本,并kill2.1 进程查看2.2 遇到的

Linux云服务器手动配置DNS的方法步骤

《Linux云服务器手动配置DNS的方法步骤》在Linux云服务器上手动配置DNS(域名系统)是确保服务器能够正常解析域名的重要步骤,以下是详细的配置方法,包括系统文件的修改和常见问题的解决方案,需要... 目录1. 为什么需要手动配置 DNS?2. 手动配置 DNS 的方法方法 1:修改 /etc/res

Linux创建服务使用systemctl管理详解

《Linux创建服务使用systemctl管理详解》文章指导在Linux中创建systemd服务,设置文件权限为所有者读写、其他只读,重新加载配置,启动服务并检查状态,确保服务正常运行,关键步骤包括权... 目录创建服务 /usr/lib/systemd/system/设置服务文件权限:所有者读写js,其他

Linux下利用select实现串口数据读取过程

《Linux下利用select实现串口数据读取过程》文章介绍Linux中使用select、poll或epoll实现串口数据读取,通过I/O多路复用机制在数据到达时触发读取,避免持续轮询,示例代码展示设... 目录示例代码(使用select实现)代码解释总结在 linux 系统里,我们可以借助 select、

Linux挂载linux/Windows共享目录实现方式

《Linux挂载linux/Windows共享目录实现方式》:本文主要介绍Linux挂载linux/Windows共享目录实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地... 目录文件共享协议linux环境作为服务端(NFS)在服务器端安装 NFS创建要共享的目录修改 NFS 配

linux系统中java的cacerts的优先级详解

《linux系统中java的cacerts的优先级详解》文章讲解了Java信任库(cacerts)的优先级与管理方式,指出JDK自带的cacerts默认优先级更高,系统级cacerts需手动同步或显式... 目录Java 默认使用哪个?如何检查当前使用的信任库?简要了解Java的信任库总结了解 Java 信

Linux命令rm如何删除名字以“-”开头的文件

《Linux命令rm如何删除名字以“-”开头的文件》Linux中,命令的解析机制非常灵活,它会根据命令的开头字符来判断是否需要执行命令选项,对于文件操作命令(如rm、ls等),系统默认会将命令开头的某... 目录先搞懂:为啥“-”开头的文件删不掉?两种超简单的删除方法(小白也能学会)方法1:用“--”分隔命

Linux五种IO模型的使用解读

《Linux五种IO模型的使用解读》文章系统解析了Linux的五种IO模型(阻塞、非阻塞、IO复用、信号驱动、异步),重点区分同步与异步IO的本质差异,强调同步由用户发起,异步由内核触发,通过对比各模... 目录1.IO模型简介2.五种IO模型2.1 IO模型分析方法2.2 阻塞IO2.3 非阻塞IO2.4