美文网首页
QEMU 实验(二): 通过 u-boot 启动 kernel

QEMU 实验(二): 通过 u-boot 启动 kernel

作者: wjundong | 来源:发表于2022-04-11 21:54 被阅读0次

前面实验都是依赖于 QEMU 的 -kernel 来将内核拷贝到内存并启动, 为了模拟更真实的硬件, 我们必须舍弃该方法, 通过 u-boot 来启动

u-boot 和 kernel 的编译和启动参考QEMU 实验(一), 这里不会再过多描述

在 u-boot 命令行中通过 sd 卡启动

首先创建一个 sd 卡的镜像文件用来模拟真实 sdcard, 将内核和设备树放到镜像文件中, 然后使用 u-boot 命令行把内核和设备树加载到内存并运行内核

dd if=/dev/zero of=sdcard.img bs=1024 count=102400
mkfs.fat sdcard.img
mount -o loop sdcard.img /mnt
cp zImage vexpress-v2p-ca9.dtb /mnt
umount /mnt

使用 QEMU 启动 u-boot, 参考

$ qemu-system-arm -M vexpress-a9 -m 256M -kernel u-boot -sd sdcard.img -nographic
=> mmcinfo 
=> load mmc 0:0 0x60008000 zImage
=> load mmc 0:0 0x61000000 vexpress-v2p-ca9.dtb
=> setenv bootargs "root=/dev/mmcblk0 rw console=ttyAMA0"
=> bootz 0x60008000 - 0x61000000
  • mmcinfo 可查看有哪些 mmc 设备
  • load <硬件接口> <设备号:分区> <加载地址> <文件名>
  • setenv bootargs 用来传递环境变量给内核, 这里只告知了将sd卡挂载为可读写根文件系统, 并将控制台定向到串口0
  • bootm <kernel地址> <ramdisk地址> <dtb地址> 可使用 - 略过参数,比如上面不需要 ramdisk,所以中间有 -

内核成功启动后将会去根文件系统运行第一个用户程序 /sbin/init, 但是我们没有拷贝任何程序到 sdcard, 所以内核返回 No working init found. 不过参考QEMU 实验(一)可以很轻松地将 Busybox 拷贝到 SD 卡, 这里不再赘述

从 tftp 网络中启动

  • 安装 tftp 服务器
     apt-get install tftpd-hpa        # tftp server 
     apt-get install tftp-hpa         # tftp client, for test
     mkdir ~/tftpboot
     chmod 777 ~/tftpboot
     vim /etc/default/tftpd-hpa
    

    更改 TFTP_DIRECTORY 为 ~/tftpboot

  • 本地测试
     $ touch ~/tftpboot/a.txt
     $ tftp 127.0.0.1 
     tftp> get a.txt
    
  • u-boot 启动测试
    $ cp zImage vexpress-v2p-ca9.dtb ~/tftpboot
    $ ifconfig                        # 拷贝ip地址, 例如 192.168.1.20
    
    => setenv ipaddr 192.168.1.21     # 设置本机 ip 地址
    => setenv serverip 192.168.1.20   # 设置服务器 ip 地址
    => tftp 0x60008000 zImage
    => tftp 0x61000000 vexpress-v2p-ca9.dtb
    => setenv bootargs "root=/dev/mmcblk0 rw console=ttyAMA0"
    => bootz 0x60008000 - 0x61000000
    
    参考 uboot通过tftp的方式加载Linux内核文件

自动脚本

u-boot 支持运行脚本, 我们可以使用脚本来让u-boot自动运行上面的命令行, 不过 u-boot 并不会直接运行纯文本文件, 他需要对脚本文件使用 mkimage 进行打包, 加入头信息, 这是为了安全考虑的.
新建一个文件 boot.cmd , 然后直接拷贝上面的命令行内容到文件
boot.cmd

load mmc 0:0 0x60008000 zImage
load mmc 0:0 0x61000000 vexpress-v2p-ca9.dtb
setenv bootargs "root=/dev/mmcblk0 rw console=ttyAMA0"
bootz 0x60008000 - 0x61000000

然后执行

mkimage -C none -A arm -T script -d boot.cmd boot.scr

将 boot.scr 拷贝到 sdcard 的根目录, 就像我们拷贝内核和设备树那样, 只要在 u-boot 中能够将它加载进内存即可.

$ qemu-system-arm -M vexpress-a9 -m 256M -kernel u-boot -sd sdcard.img -nographic
=> load mmc 0:0 0x62000000 boot.scr
=> source 0x62000000

source <addr> 将执行在放在内存 addr 处的 .scr 脚本

让 u-boot 启动后自动加载 sd 卡中的 kernel

在 u-boot 中, 通过 printenv 可以看到环境变量信息, 其中有如下信息

boot_scripts=boot.scr.uimg boot.scr
boot_targets=mmc1 mmc0 pxe dhcp 

变量 boot_scripts 指定了u-boot启动后默认加载的命令脚本, boot_targets 指定了从那个地方加载, 据此我们可以生成一个 boot.scr 放在 SD 卡, u-boot 启动后会自动加载并执行. 但是在前一节自动脚本中, 我们也放在了sdcard 且名称也为 boot.scr 但是为什么没有自己启动? 这是因为 u-boot 默认是识别设备的第一个分区, 我们没有给sdcard进行分区, 因此无法启动. (只有一个分区和不分区是不同的, 不分区压根没有分区表, 也就没分区的概念, 分区后即使只有一个分区, 也是有分区表的)

此外不要忘记更改 load mmc 的磁盘位置, 要更改为分区1
boot.cmd

load mmc 0:1 0x60008000 zImage
load mmc 0:1 0x61000000 vexpress-v2p-ca9.dtb
setenv bootargs "root=/dev/mmcblk0p1 rw console=ttyAMA0"
bootz 0x60008000 - 0x61000000

重新生成一下:

mkimage -C none -A arm -T script -d boot.cmd boot.scr

然后是重新生成 sdcard.img:

dd if=/dev/zero of=sdcard.img bs=1024 count=102400
fdisk sdcard.img                                        # 简单起见这里只分一个区
losetup -f                                              # 查询可用的块设备地址, 记下来如 /dev/loop16
losetup -P /dev/loop16 sdcard.img                       # 关联块设备   
lsblk                                                   # 可以看到块设备已经关联, 并且看到分区信息   
mkfs.fat  /dev/loop16p1                                 # 格式化第一分区                       
mount /dev/loop16p1 /mnt                                # 挂载块设备的第一分区
cp boot.scr zImage vexpress-v2p-ca9.dtb /mnt            # 复制到 第一分区 
umount /mnt                                             # 卸载设备

启动

qemu-system-arm -M vexpress-a9 -m 1024M -kernel u-boot -sd sdcard.img -nographic

[注意] 这里设置了 1024M 内存, 因为vexpress-a9板子执行 printenv 时看到环境变量默认 scriptaddr=0x88000000, 启动脚本检测存在后会被拷贝到这个地方并使用 source 执行. 而 bdinfo 显示DRAM bank 启始地址是 0x60000000, 如果内存只有 256M, 则RAM到不了0x88000000, 是无法加载启动脚本的. 虽然这一项可以更改该参数并重新编译 u-boot, 不过, 目前最好还是按官方原本设置为好.

启动后, u-boot 会被 Qemu 加载到内存, 然后 u-boot 初始化设备, 并探测到 sdcard 的第一分区有 boot.scr, 它会将其加载到 scriptaddr 地址处并执行, 而执行的 boot.scr 被我们定义为 加载同目录下的内核以及设备树并执行, 因此 Linux 内核被成功启动.

u-boot 打包

参考

可将 u-boot kernel dtb rootfs 都打包在一个镜像文件中然后下载到 NorFlash 中直接运行, 使用 dd 进行操作

mkimage -A arm -C none -O linux -T kernel -d zImage -a 0x00010000 -e 0x00010000 zImage.uimg
mkimage -A arm -C none -O linux -T ramdisk -d rootfs.img.gz -a 0x00800000 -e 0x00800000 rootfs.uimg
dd if=/dev/zero of=flash.bin bs=1 count=6M
dd if=u-boot.bin of=flash.bin conv=notrunc bs=1
dd if=zImage.uimg of=flash.bin conv=notrunc bs=1 seek=2M
dd if=rootfs.uimg of=flash.bin conv=notrunc bs=1 seek=4M

然后可以直接将文件加载到内存运行:

qemu-system-arm -M vexpress-a9 -device loader,file=flash.bin,addr=0x60800000,cpu-num=0,force-raw=on -nographic 

相关文章

网友评论

      本文标题:QEMU 实验(二): 通过 u-boot 启动 kernel

      本文链接:https://www.haomeiwen.com/subject/hhvwsrtx.html