文档概述
此文档参考以下开源项目,描述了如何构建RISC-V架构的Fedora 39系统镜像,以 QEMU 平台为例,分阶段讲解了根文件系统构建、引导加载器(U-Boot)与内核编译、磁盘分区与镜像打包等关键步骤,并配以命令示例和自动化脚本模板。希望通过描述这个构建流程,能对系统的启动流程与原理有更深入的理解,并依次为参考,扩展开来构建各种不同系统与架构的镜像。
参考开源项目链接:GitHub - chainsx/fedora-riscv-builder
- QEMU平台下的镜像构建流程
- 构建根文件系统(阶段一)
此文档描述的OS镜像构建方式中,最局限性的一点既是根文件系统的构建不是从零起步的(即从所有组件源码编译开始)。我们需要构建什么系统的镜像,就先得需要有一个这个系统的环境,在此环境下再创建出此系统的最小根文件系统rootfs。故接下来的操作是在Fedora 39 riscv系统环境下以root权限用户进行的。
- 具体步骤:
选择一个合适的工作目录。
使用mkdir命令创建”rootfs”目录:
这个目录将成为镜像里”/”(根文件系统)的挂载点和容器。
在”rootfs”目录下“虚拟”地创建一个空的RPM数据库,主要涉及的操作:
| mkdir -p rootfs/var/lib/rpm |
RPM包管理器需要一个数据库目录来记录已安装的包信息。这里手动提前创建RPM数据库的存放路径,避免后面”rpm –initdb”命令报错。
| rpm --root $WORKDIR/rootfs/ --initdb |
使用RPM自带的初始化命令来创建数据库。这样做是为了后续能往这个“假”系统里安全rpm包。
让rootfs拥有Fedora标准的仓库源配置:
如果嫌弃Fedora默认的仓库源配置链接有困难,之后可以自定义修改rootfs/etc/yum.repos.d/目录中的内容。
在rootfs中里再安装一次dnf包本身:
| dnf --installroot=$WORKDIR/rootfs/ install dnf --nogpgcheck -y |
| 这样做的原因是:最开始只有rpm可用,dnf并没有被安装进目标rootfs。安装dnf后,就可以在这个隔离的环境里方便地通过dnf拉取更多软件包、更新元数据、解决依赖。 |
把rootfs目录打包成tarball:
| cd $WORKDIR/rootfstar -zcvf fedora-39-core-rootfs.tar.gz . |
| 生成的fedora-39-core-rootfs.tar.gz就是一个“最小“Fedora 39 RISC-V根文件系统,后续可以:直接导入Docker作为镜像层解包到QEMU/实际机器的分区里也可以在此基础上再用”dnf --installroot“安装其他软件 |
- 整体思路回顾
- 从空目录搭建一个RPM数据库;
- 安装release包,把官方repo文件放进来;
- 可选地对于源配置的修改;
- 安装DNF,以便可复用的包管理;
- 打包成tarball,得到一个rootfs镜像。

Figure 1 构建根文件系统阶段一
- 思考
| 安装DNF的意义? |
| 在一个刚初始化、只有 RPM 数据库但没有包管理工具的空 rootfs 里,把 DNF 安装进去。只有装上 DNF,后面才能在这个 chroot 环境里通过它来一键安装和管理网络、SSH、dracut、驱动等所有系统组件,而不会影响到宿主机系统。 |
| rootfs中何时拥有了完整FHS(Filesystem Hierarchy Standard)结构? |
| 在安装了 fedora-release 包(建立基本目录骨架)并通过 DNF 安装了 filesystem、coreutils、bash、glibc 等基础软件包后,rootfs 才具备完整的 FHS 结构。 |
- 构建根文件系统(阶段二)
- 具体步骤
把rootfs的tarball转移到一个新的Linux系统下完成接下来的整体OS镜像构建任务。该构建环境应该支持apt包管理器,且和架构无关。故此文档使用x86架构下的Ubuntu较新版本系统来进行构建任务。
首先,要在此环境下安装一些必要的包:
| apt updateapt install parted zstd make bison bc flex kpartx xz-utils qemu-user-static libssl-dev gcc-riscv64-linux-gnu -y |
| 此处安装的是后续构建所需的工具和依赖,下面针对每个包的作用做个简要说明:parted:用来操作磁盘镜像的分区表后面会把打包好的rootfs镜像写入到分区中。zstd:支持zstd压缩/解压格式。make:各种开源项目(Linux内核、U-Boot、OpenSBI)的标准构建工具。bison和flex:在编译一些底层工具时,需要生成词法/语法分析器。bc:在Linux内核的Makefile中,用于计算内核版本号等小数运算。kpartx:将镜像文件中的分区映射为/dev/loopXpY设备,方便在宿主机上直接挂载、写入文件。zx-utils:支持XZ(.xz)格式的压缩/解压。qemu-user-static:提供静态编译的QEMU用户态模拟器(比如qemu-riscv64-static),可以让你在x86宿主机上chroot进RISC-V的rootfs,自动用QEMU转译执行里面的二进制,从而在宿主机上安装RISC-V架构的包、运行脚本。libssl-dev:OpenSSL的开发头文件/库。gcc-riscv64-linux-gnu:RISC-V 64位架构的交叉编译器,用于在x86机器上编译生成RISC-V的内核、U-Boot、OpenSBI等二进制。 |
在新的构建环境下,解压rootfs.tar.gz到rootfs目录:
| tar -zxvf rootfs.tar.gz -C ${rootfs_dir} |
复制宿主机的DNS配置到rootfs内,保证chroot之后能正确做域名解析:
| cp -b /etc/resolv.conf ${rootfs_dir}/etc/resolv.conf |
挂载虚拟文件系统,为chroot保证运行环境
| mount --bind /dev ${rootfs_dir}/devmount -t proc /proc ${rootfs_dir}/procmount -t sysfs /sys ${rootfs_dir}/sys |
| 把宿主机的/dev、/proc、/sys挂载到rootfs中对应位置,使得在chroot里能访问设备文件、进程信息和内核接口。这是常见的chroot准备工作,否则很多系统工具(尤其是dnf、systemctl、udev脚本)会因为“找不到/proc或/sys“而报错。 |
在chroot中更新系统并安装额外软件包
| chroot ${rootfs_dir} dnf update -y |
| 进入chroot,更新系统,把基础系统包都升级到最新。 |
| chroot ${rootfs_dir} dnf install alsa-utils haveged wpa_supplicant vim net-tools iproute iputils NetworkManager bluez fedora-release-server passwd hostname -ychroot ${rootfs_dir} dnf install wget openssh-server openssh-clients parted realtek-firmware chkconfig e2fsprogs dracut NetworkManager-wifi -y |
| 安装一系列常用和必须的软件:音频和随机数:alas-utils(音频工具),haveged(用户态熵守护进程,提升系统随机数池);网络管理:wpa_supplicant(Wi-Fi安全握手),NetworkManager及其Wi-Fi插件,bluez(蓝牙支持),net-tools/iproute/iputils(各种网络命令);编辑与下载:vim、wget;SSH远程访问:openssh-server/openssh-client;分区与文件系统:parted、e2fsprogs;引导与初始化:dracut(生成initramfs)、chkconfig(SysV init服务管理);固件支持:realtek-firmware(常见网卡固件);系统基础:passwd(设置密码)、hostname。 |
配置系统基础信息和启动脚本
| echo fedora-riscv > ${rootfs_dir}/etc/hostname |
| 写入主机名为”fedora-riscv”。 |
| cp $build_dir/config/extend-root.sh ${rootfs_dir}/etc/rc.d/init.d/extend-root.shchmod +x ${rootfs_dir}/etc/rc.d/init.d/extend-root.sh |
| 把项目提供的extend-root.sh(通常是用来首次启动时扩展根文件分区到整盘大小的脚本)拷贝到SysV init目录,并设为可执行。 |
修改SSH配置,允许以root登录:
| sed -i "s|#PermitRootLogin prohibit-password|PermitRootLogin yes|g" ${rootfs_dir}/etc/ssh/sshd_config |
在chroot下设置密码、启用脚本并生成initramfs:
| cat << EOF | chroot ${rootfs_dir} /bin/bash echo 'fedora' | passwd --stdin root chkconfig --add extend-root.sh chkconfig extend-root.sh on dracut --no-kernel /boot/initrd.imgEOF |
| 这段通过here-doc批量在chroot里执行:设置root密码为”fedora”;添加并打开SysV init脚本extend-root.sh,保证每次启动时自动运行;重建initramfs(dracut --no-kernel 表示只更新initramfs,不替换内核本身),生成新的/boot/initrd.img,让启动时能正确挂载、扩展分区等。 |
-
- 整体思路回顾
- rootfs tarball解包:在新构建环境里还原rootfs;
- chroot环境挂载:确保包管理和脚本能在chroot中正常运行;
- 更新 & 安装:补齐常用服务与实用工具;
- 系统配置:设置主机名、SSH root登录、初始化脚本;
- 初始化脚本 & initramfs:让首次启动后自动扩展分区并重新打包initramfs镜像。

Figure 2 构建根文件系统阶段二
- 思考
| mount --bind /dev ${rootfs_dir}/devmount -t proc /proc ${rootfs_dir}/procmount -t sysfs /sys ${rootfs_dir}/sys此类挂载命令的具体解释和意义,chroot需要和这些操作打配合? |
| 这些挂载操作的目的都是在新的 rootfs 环境中,暴露出宿主机已有的特殊文件系统,让 chroot 进去后的进程能正常访问硬件设备、进程信息和内核接口——否则很多命令(包括 DNF 安装、udev 规则执行、模块加载脚本等)都会因为找不到 /dev、/proc 或 /sys 而报错。只有在这些特殊文件系统都挂好之后,再执行 chroot ${rootfs_dir},进入那个环境的 shell 或脚本才能像在“真正的”系统里一样工作,正确识别和管理设备、加载模块、执行服务安装。 |
| dracut以及initrd.img之间的关系? |
| dracut是一个生成 initrd.img (或 initramfs)的工具,根据系统当前驱动和配置打包出启动时的临时根文件系统。 initrd.img是由 dracut (或其他同类工具)创建的初始内存盘镜像,内核在启动时加载它来完成早期驱动加载和根文件系统挂载。 |
- 编译Linux Kernel镜像
- 具体步骤
克隆源码:
进入项目源码,安装定制的内核配置:
| cp $build_dir/config/linux-qemu-current.config arch/riscv/configs/linux-qemu-current_defconfig |
| 将此项目事先准备好的config文件复制到内核源码中RISC-V架构的配置目录,这样接下来可以以这份配置为基础生成.config。 |
生成 .config文件:
| make ARCH=riscv CROSS_COMPILE=riscv64-linux-gnu- linux-qemu-current_defconfig |
| 交叉编译 |
编译内核和内核模块:
| make ARCH=riscv CROSS_COMPILE=riscv64-linux-gnu- -j$(nproc) |
安装内核模块到临时目录:
| make ARCH=riscv CROSS_COMPILE=riscv64-linux-gnu- modules_install INSTALL_MOD_PATH=kmod |
拷贝模块到rootfs:
| cp -rfp linux/kmod/lib/modules/* rootfs/lib/modules |
-
- 整体思路回顾
- 拉取指定版本的内核源码;
- 用定制的defconfig生成内核配置.confgi,保证编译出来的内核满足QEMU或开发板上对设备、文件系统、网络等的需求。
- 交叉编译 生成针对RISC-V架构的内核映像和模块。
- 安装模块到临时目录再拷贝到rootfs,让新内核在启动时能够加载到所有必要的模块。
- 编译U-Boot
- 具体步骤
克隆U-Boot源码:
进入源码目录,加载默认配置:
| make ARCH=riscv CROSS_COMPILE=riscv64-linux-gnu- qemu-riscv64_smode_defconfig |
编译U-Boot
| make ARCH=riscv CROSS_COMPILE=riscv64-linux-gnu- -j$(nproc) |
| 编译流程会生成:SPL(二级引导加载器,用于板级最初启动)u-boot.bin(主引导程序,可解析设备树并加载Linux)设备树二进制(.dtb文件) |
拷贝输出固件:
| cp u-boot-nodtb.bin $build_dir/firmware |
| u-boot-nodtb.bin是U-Boot主映像(u-boot.bin)与SPL合并后的二进制,但不内嵌任何.dtb;将其复制出来,供后面写入镜像分区或打包镜像时使用。 |
-
- 整体思路回顾
- 浅克隆指定版本;
- Defconfig;
- 交叉编译;
- 分离设备树;
- 思考
| U-Boot的版本有什么需求吗?需要考虑和内核、opensbi、rootfs以及硬件环境的适配吗? |
| 选择 U-Boot 版本时要注意以下兼容性:硬件平台支持必须选用含有你目标板硬件驱动(SoC、存储、网络控制器等)的 U-Boot 版本。不同板卡的 DeviceTree、驱动补丁各异。RISC-V 架构支持需要包含 RISC-V 相关的 defconfig(如 qemu-riscv64_smode_defconfig 或你板子的专用 defconfig),并且交叉编译工具链要匹配。OpenSBI 协同U-Boot 要和 OpenSBI 约定好的入口点和跳转方式保持一致。常见做法是先用 OpenSBI 加载,然后跳转到 U-Boot;二者版本(API/ABI)应尽量匹配。内核兼容性U-Boot 加载内核时会解析 DeviceTree 和传递启动参数,内核版本更新后要确保 U-Boot 的 DTB 生成脚本或二进制能匹配内核所需的设备描述。RootFS 与文件系统如果你用 extlinux/ext4/VFAT 等做启动分区和根分区,U-Boot 要编译进对对应文件系统的支持(比如对 FAT/ext4 的驱动)。总结:U-Boot 版本选型不仅要看它本身的 RISC-V 能力,还要和 OpenSBI、Linux 内核、DeviceTree、启动分区格式、目标硬件驱动链路紧密配合,才能保证一键无误地加载并启动系统。 |
- 编译opensbi
- 具体步骤
浅克隆指定版本的OpenSBI:
进入源码目录,编译OpenSBI通用平台固件:
| make PLATFORM=generic PLATFORM_RISCV_XLEN=64 CROSS_COMPILE=riscv64-linux-gnu- -j$(nproc) |
| 编译结果会在build/platform/generic/firmware/下生成若干固件镜像,其中最常用的是:fw_jump.bin:一个非常精简的OpenSBI固件,执行硬件初始化后直接跳转到下一级引导(通常是U-Boot);fw_dynamic.bin:包含更复杂功能(如动态补丁、可选模块),体积更大、用于更完整的固件场景。 |
拷贝输出固件:
| cp build/platform/generic/firmware/fw_jump.bin $build_dir/firmware |
-
- 思考
| opensbi是什么,作用为何? |
| OpenSBI(Open Supervisor Binary Interface)是 RISC-V 平台上运行在机器模式(M-mode)的一段固件,实现了 SBI(监督二进制接口)标准。作用硬件初始化:上电后在 M-mode 下完成中断控制器、定时器、串口等底层外设的配置。提供 SBI 服务:为运行在监督模式(S-mode)的操作系统(如 Linux)暴露统一的调用接口(例如启动其他核、读写时钟、系统重启等),屏蔽不同硬件的实现差异。模式切换:在完成 M-mode 初始化后,将控制权安全地传递给引导加载程序(如 U-Boot)或直接进入操作系统的 S-mode,确保系统可在标准化环境下启动。 |
- 生成并组装OS镜像
- 具体步骤
计算镜像大小并创建空镜像:
| size=`du -sh --block-size=1MiB ${build_dir}/rootfs | cut -f 1 | xargs`size=$(($size+720))losetup -Dimg_file=${build_dir}/rootfs.imgdd if=/dev/zero of=${img_file} bs=1MiB count=$size status=progress && sync |
| 计算所需空间du -sh --block-size=1MiB ${build_dir}/rootfs:统计已经准备好的rootfs目录在目标镜像中解包后所占空间(以MiB为单位)。size=$(($size+720)):在rootfs大小基础上多加720MiB,预留给/boot、日志、升级等空间。清理所有loop设备losetup -D:将宿主机上所有losetup设备先解除绑定,防止后面创建失败或冲突。用零填充创建镜像文件dd if=/dev/zero of=${img_file} bs=1MiB count=$size:生成一个大小为$size MiB的空白镜像文件;status=progress:实时显示写入进度,sync确保磁盘缓存刷新。 |
在镜像上创建分区表与分区:
| parted ${img_file} mklabel gpt mkpart primary fat32 32768s 524287sparted ${img_file} mkpart primary ext4 524288s 100% |
| mklabel gpt:给镜像文件打GPT分区表,支持大容量和多分区。第一分区:类型fat32,起始扇区32768s(通常是16MiB处,留足对齐空间)、结束扇区524287s(大约为256MiB);用作/boot或引导分区,格式化成VFAT方便U-Boot/extlinux读取。(why)第二个分区:类型ext4,从524288s(256MiB对齐)到镜像末尾,用作根文件系统。注意:在有些环境下,对一镜像文件执行上述的两次parted,最后结果可能不符合预期中的生成两个分区,也许会只生产了一个。建议尝试使用parted进入它的命令行模式下执行打分区表和创建两个分区的操作。 |
挂载镜像分区为loop设备
| device=`losetup -f --show -P ${img_file}`trap 'LOSETUP_D_IMG' EXITkpartx -va ${device}loopX=${device##*\/}partprobe ${device} |
| 关联loop设备losetup -f --show -P rootfs.img:自动寻找空闲 loop 设备(如 /dev/loop0),把整个镜像映射到该设备,并用 -P 自动扫描分区;返回值 device 如 /dev/loop0。创建设备映射节点kpartx -va /dev/loop0:为其中的 GPT 分区创建设备映射 /dev/mapper/loop0p1、loop0p2;partprobe:告诉内核重新读取分区表,确保后续工具能识别。 |
格式化并挂载分区:
| sdbootp=/dev/mapper/${loopX}p1sdrootp=/dev/mapper/${loopX}p2mkfs.vfat -n fedora-boot ${sdbootp}mkfs.ext4 -L fedora-root ${sdrootp}mkdir -p ${root_mnt} ${boot_mnt}mount ${sdbootp} ${boot_mnt}mount ${sdrootp} ${root_mnt} |
| 设置环境变量sdbootp、sdrootp 分别指向引导与根分区映射设备格式化mkfs.vfat -n fedora-boot:把 p1 做成 VFAT 并打 label;mkfs.ext4 -L fedora-root:把 p2 做成 ext4 并打 label。挂载创建并分别挂载到自定义的 ${boot_mnt}、${root_mnt}目录,后续把内容写入这两个挂载点。 |
配置引导加载器:
| mkdir -p $boot_mnt/extlinux |
| extlinux(Syslinux的一部分)用于U-Boot/extlinux模式下读取extlinux.conf来启动内核。 |
| line=$(blkid | grep $sdrootp)uuid=${line#*UUID=\"}uuid=${uuid%%\"*} |
| 用blkid读取根分区的UUID,并提取出来,保证extlinux.conf中的根文件系统挂载指向正确的标签。 |
| echo "label Fedora kernel /Image initrd /initrd.img append console=ttyS0,115200 root=UUID=${uuid} rootfstype=ext4 rootwait rw earlycon loglevel=7 init=/lib/systemd/systemd" \> $boot_mnt/extlinux/extlinux.conf |
| 生成extlinux.conf引导配置:kernel:指向前面拷贝的Linux内核镜像/Image;initrd:指向initramfs /initrd.img;append:内核命令行,打开串口控制台、指定根分区、文件系统类型、初始化程序等。 |
拷贝固件、内核和更新fstab:
| cp $build_dir/firmware/fw_jump.bin $boot_mntcp $build_dir/linux/arch/riscv/boot/Image $boot_mnt |
| 把OpenSBI固件(fw_jump.bin)和RISC-V内核镜像(Image)复制到引导分区。 |
| echo "LABEL=fedora-root / ext4 defaults,noatime 0 0" > ${build_dir}/rootfs/etc/fstabecho "LABEL=fedora-boot /boot vfat defaults,noatime 0 0" >> ${build_dir}/rootfs/etc/fstab |
| 在根文件系统的fstab中写入两行,确保启动后能正确挂载根和/boot分区。 |
填充/boot与根文件系统
| cp -rfp ${build_dir}/rootfs/boot/* $boot_mntrm -rf ${build_dir}/rootfs/boot/* |
| 移动原先解包并chroot后存放在rootfs/boot下的启动文件(initrd、旧内核等)到实际的boot分区,保持逻辑分离;清空rootfs/boot目录,为后面rsync做准备。 |
| rsync -avHAXq ${build_dir}/rootfs/* ${root_mnt}syncsleep 10 |
| 用rsync将整个根文件系统(除了/boot)复制到挂载的根分区,再sync和稍等保证数据完全落盘。 |
卸载和收尾:
| umount $sdrootpumount $sdbootpLOSETUP_D_IMGUMOUNT_ALLlosetup -Dkpartx -d ${img_file} |
| 按照反向顺序卸载根和引导分区;确保所有绑定和挂载被清理;再次losetup -D解除所有loop设备,最后kpartx -d删除为镜像创建的/dev/mapper分区节点。 |
-
- 整体思路回顾
- 创建一个足够大的空白镜像文件;
- GPT分区:一个FAT32引导分区 + 一个ext4根分区;
- 挂载并格式化分区;
- 生成extlinux引导配置,填充固件(OpenSBI),内核和initramfs;
- 写入根文件系统并配置fstab;
- 卸载和清理,得到一个可直接刷入设备或用于QEMU启动的完整rootfs.img。

Figure 3 生成并组装OS镜像
- 思考
| losetup命令是什么,作用为何? |
| losetup 是 Linux 下用来管理 Loop 设备(虚拟块设备)的命令。它的主要作用:将一个常规文件或设备 映射成一个块设备(如 /dev/loop0),使得该文件能像真实磁盘分区一样被挂载、格式化、读写。管理已创建的 Loop 设备,查看、设置、释放映射关系。所以,通过 losetup,你可以在无需真实硬件的情况下,用文件来模拟磁盘分区,这在制作和测试磁盘镜像时非常方便。 |
| extlinux是什么,作用为何?与U-Boot之间的联系? |
| 它的主要作用为在启动分区(格式为 ext2/3/4)上读取并解析一个名为 extlinux.conf 的文本配置文件,根据配置加载指定的内核映像、initrd(初始内存盘)以及传递给内核的启动参数。与U-Boot的联系:在 RISC-V 嵌入式场景中,U-Boot 作为第一阶段引导程序,完成底层硬件初始化后,会去启动分区(通常是 FAT 或 EXT 分区)寻找并加载第二阶段的引导器。如果该分区上安装的是 extlinux(即包含 extlinux/extlinux.conf),U-Boot 会调用其内置的 EXT 驱动来挂载分区,读取并执行 extlinux 配置,进而加载内核和 initrd。换句话说,U-Boot ↔ extlinux 的配合让我们不用在 U-Boot 代码里硬编码内核路径或启动参数,只需通过 extlinux.conf 灵活地管理多个启动项和参数。这样既简化了 U-Boot 配置,又提高了启动流程的可维护性。 |
| fstab是什么,作用为何? |
| /etc/fstab 是系统启动时自动挂载文件系统的配置文件:内核或 init 系统读取它,根据配置自动将各个分区或远程文件系统挂载到指定位置,无需手动执行 mount。 |
| 根文件系统中的/boot,为何要把内容复制到boot分区,还要清空/boot目录? |
| 因为我们把启动相关的文件(内核镜像、initrd、OpenSBI 固件、extlinux 配置等)放在了一个独立的启动分区上,所以要: 复制到启动分区:让 U-Boot 或固件在挂载根分区之前就能直接从启动分区加载这些文件; 清空 rootfs 下的 /boot:避免在真正启动后再挂载启动分区时出现文件冲突或冗余,确保真正的启动文件都只在启动分区中。 |
| rsync命令行工具是什么,为什么不用cp? |
| 在镜像制作和系统部署时,用 rsync -avHAX(递归、保属性、保硬链、保ACL、保扩展属性)能更可靠、高效地将完整的 rootfs 内容复制到挂载点,而不仅仅是机械地拷贝文件。 |
| kpartx命令行工具是什么? |
| kpartx 是一个基于 Linux Device-Mapper 的工具,用于将磁盘映像文件或块设备中的分区表映射成单独的块设备节点。读取磁盘镜像(如 .img)或裸设备上的分区表(MBR/GPT)。自动为每个分区创建对应的 /dev/mapper/<映射名>p<分区号> 设备节点,便于后续挂载或操作。 |
- 使用QEMU启动镜像
现在,rootfs镜像文件已经包含了VFAT引导分区以及EXT4根分区。还准备好了编译完成的OpenSBI固件以及U-Boot内核镜像。现在可以使用qemu工具来启动此OS镜像,具体命令如下:
| qemu-system-riscv64 -machine virt -nographic \-smp 8,core=8,threads=1,socket=1 -m 2G \-bios fw_jump.bin \-kernel u-boot-nodtb.bin \-drive file=rootfs.img,format=raw,id=hd0 \-device virtio-blk-device,drive=hd0 \-audiodev pa,id=snd0 \-object rng-random,filename=/dev/urandom,id=rng0 \-device virtio-rng-device,rng=rng0 \-device virtio-net-device,netdev=usernet \-netdev user,id=usernet \-device qemu-xhci -usb -device usb-kbd -device usb-tablet -device usb-audio,audiodev=snd0 |
| 基本机器与控制台设置-machine virt-nographicCPU与内存配置-smp 8,core=8,threads=1,socket=1-m 2G引导链:固件(OpenSBI)-> U-Boot-bios fw_jump.bin把OpenSBI固件当作”BIOS”加载。虚拟机“上电”后,首先执行此文件,完成最底层硬件初始化并提供SBI接口。-kernel u-boot-nodtb.bin在OpenSBI完成后,qemu会把这个文件当作“内核”加载---这里实际上是U-Boot主映像(不带DTB)。U-Boot接管控制台后再去加载真正的Linux。根文件系统:虚拟磁盘-drive file=rootfs.img,format=raw,id=hd0把前面生成的rootfs.img当作一块原始磁盘(raw),标识为hd0。-device virtio-blk-device,drive=hd0在虚拟机里挂载一个Virtio块设备,对应这块磁盘。Linux内核的virtio驱动会识别成/dev/vda,并读取其中的GPT分区、文件系统、fstab。外设模拟启动流程QEMU上电--》加载fw_jump.bin(OpenSBI)OpenSBI初始化后--》跳转至加载的U-Boot(-kernel u-boot-nodtb.bin)U-Boot执行extlinux.conf或手动命令---》把设备树、Linux内核镜像Image、initrd.img加载到内存U-Boot将控制权交给Linux-》Linux内核启动,挂载Virtio块设备(rootfs.img)的根分区 |
所有评论(0)