基于Qemu LMT虚拟平台的bringup
在芯片开发的前期,RTL代码还在开发中,FPGA原型验证平台也没有可用的环境。这时候我们可以根据SoC spec在Qemu中开发一个新的Virtual Platform,那么BSP软件团队就可以提前完成一些软件环境的准备,比如最小系统。这样当RTL0.1释放后,我们可以直接复用这些软件在FPGA验证,从而减小搭建软件环境的压力和缩小验证时间。随着Qemu里面模拟的外设越来越多,我们可以把更多的软件function在Qemu里面完成开发和验证,然后再迁移到FPGA原型平台上进行验证,从而完善软件的时序方面的逻辑。通过这样的验证手段,让我们软件准备提前,缩短FPGA的占用时间,减少验证成本。
- Qemu: v7.2.0
- U-Boot: v2022.10
- ATF: v2.10
- OPTEE: v4.0.0
- Linux Kernel: linux-6.1.12
- aarch64-none-linux-gnu-gcc: 10.3.1 20210621
- riscv32-unknown-linux-gnu-gcc: 12.2.0
芯片介绍
芯片是异构系统架构的,包含两种指令集CPU(ARM和RISC-V),因此我们在Qemu里面添加了两个machine:lmt-virt和lmt-safety-virt,前者用于模拟ARM系统部分,后面用于模拟RISC-V系统部分,最后通过Qemu里面的共享内存和chardev socket实现两个machine的联动(multi-process Qemu的交互)。
启动介绍
在运行起来之前,我们先明确最小系统涉及的组件以及它们的启动顺序
- ATF:提供EL3 runtime service,具有整个硬件系统的全部权限
- OPTEE:提供可信执行环境
- SPL:完成Bootloader镜像的加载,以及DDR、Clock等初始化
- U-Boot:加载和启动不同OS的镜像
- Kernel:Linux kernel OS,常用的OS
- initramfs:最小的文件系统,用作临时跳转
- Ubuntu Rootfs:根文件系统,存放在存储介质上,里面包含很多应用、库和工具
启动顺序
- SPL将BL31 ATF、BL32 OPTEE和BL33 U-Boot三个镜像加载到DDR,将RISC-V Firmware加载到Safety SRAM中,然后跳转到BL31 ATF执行
- ATF完成基本的初始化,然后跳转到BL32 OPTEE执行
- OPTEE完成基本的初始化,然后返回到ATF的执行环境中,最后ATF直接跳转到U-Boot执行
- U-Boot完成初始化,外设的初始化,从eMMC/Nor Flash等媒介中加载Kernel、initramfs和dtb等镜像文件,然后跳转到Kernel开始执行
- Kernel完成启动初始化,拉起应用程序
镜像构建
ATF
1 | git clone -b atf-v2.10 https://github.com/chasinglulu/arm-trusted-firmware.git |
OPTEE
1 | git clone -b optee_os-4.0.0 https://github.com/chasinglulu/optee_os.git |
U-Boot
1 | git clone -b uboot-2022.10 https://github.com/chasinglulu/u-boot.git |
Linux Kernel
1 | git clone -b linux-v6.1-rt https://github.com/chasinglulu/linux.git |
lmt-virt运行验证
启动U-Boot+Kernel
1 | qemu-system-aarch64 -M lmt-virt,virt=on,emmc=on -m 4G -display none -device loader,addr=0x400200000,file=/path/to/u-boot/u-boot.bin,cpu-num=0 -drive file=/path/to/emmc.img,format=raw,if=emmc -serial stdio |
virt=on
:使能CPU的EL2secure=off
:关闭CPU的EL3emmc=on
:使能eMMC模拟支持
U-Boot既可以运行在EL2也可以运行在EL3上,但是打开EL3的时候,需要运行ATF,不然Kernel发起PSCI访问会触发panic。
启动日志附件:U-Boot->kernel Booting
启动SPL+U-Boot+Kernel
1 | qemu-system-aarch64 -M lmt-virt,virt=on,secure=on,emmc=on -m 4G -display none -device loader,addr=0x00,file=/path/to/u-boot/spl/u-boot-spl.bin,cpu-num=0 -device loader,addr=0x400200000,file=/path/to/u-boot/u-boot.img -drive file=/path/to/emmc.img,format=raw,if=emmc -serial stdio |
virt=on
:使能CPU的EL2secure=on
:使能CPU的EL3emmc=on
:使能eMMC模拟支持
因为SPL和U-Boot既可以运行在EL2也可以运行在EL3,因此virt=
和secure=
两个选项既可以on
也可以off
,两个选项可以随意组合,用于验证不同的使用场景。
secure=off
:关闭CPU的EL3,Kernel下的PSCI访问请求都由Qemu捕获进行处理。如果使能EL3,必须运行ATF软件,不然Kernel发出PSCI请求会触发panic。
启动日志附件:ATF->U-Boot->kernel Booting
启动ATF+U-Boot+Kernel
1 | qemu-system-aarch64 -M lmt-virt,virt=on,secure=on,emmc=on -m 4G -display none -device loader,addr=0x400104000,file=/path/to/arm-trusted-firmware/build/lmt/release/bl31.bin,cpu-num=0 -device loader,addr=0x400200000,file=/path/to/u-boot/u-boot.bin -drive file=/path/to/emmc.img,format=raw,if=emmc -serial stdio |
virt=on
:使能CPU的EL2secure=on
:使能CPU的EL3emmc=on
:使能eMMC模拟支持
启动日志附件:ATF->U-Boot->kernel Booting
启动SPL+ATF+U-Boot+Kernel
1 | qemu-system-aarch64 -M lmt-virt,virt=on,secure=on,emmc=on -m 4G -display none -device loader,addr=0x0,file=/path/to/u-boot/spl/u-boot-spl.bin,cpu-num=0 -device loader,addr=0x400104000,file=/path/to/arm-trusted-firmware/build/lmt/release/bl31.bin -device loader,addr=0x400200000,file=/path/to/u-boot/u-boot.bin -drive file=/path/to/emmc.img,format=raw,if=emmc -serial stdio |
virt=on
:使能CPU的EL2secure=on
:使能CPU的EL3emmc=on
:使能eMMC模拟支持
启动日志附件:SPL->ATF->U-Boot->kernel Booting
启动ATF+OPTEE+U-Boot+Kernel
1 | qemu-system-aarch64 -M lmt-virt,virt=on,secure=on,emmc=on -m 4G -display none -device loader,addr=0x400104000,file=/path/to/arm-trusted-firmware/build/lmt/release/bl31.bin,cpu-num=0 -device loader,addr=0x400200000,file=/path/to/u-boot/u-boot.bin -device loader,addr=0x404000000,file=/path/to/optee_os/out/arm-plat-ax/core/tee-pager_v2.bin -drive file=/path/to/emmc.img,format=raw,if=emmc -serial stdio |
virt=on
:使能CPU的EL2secure=on
:使能CPU的EL3emmc=on
:使能eMMC模拟支持
启动日志附件:ATF->OPTEE->U-Boot->kernel Booting
启动SPL+ATF+OPTEE+U-Boot+Kernel
1 | qemu-system-aarch64 -M lmt-virt,virt=on,secure=on,emmc=on -m 4G -display none -device loader,addr=0x0,file=/path/to/u-boot/spl/u-boot-spl.bin,cpu-num=0 -device loader,addr=0x400104000,file=/path/to/arm-trusted-firmware/build/lmt/release/bl31.bin -device loader,addr=0x400200000,file=/path/to/u-boot/u-boot.bin -device loader,addr=0x404000000,file=/path/to/optee_os/out/arm-plat-ax/core/tee-pager_v2.bin -drive file=/path/to/emmc.img,format=raw,if=emmc -serial stdio |
virt=on
:使能CPU的EL2secure=on
:使能CPU的EL3emmc=on
:使能eMMC模拟支持
启动日志附件:SPL->ATF->OPTEE->U-Boot->kernel Booting
lmt-safety-virt运行验证
lmt-safety-virt虚拟平台是基于RISC-V 32-bit架构开发的,在U-Boot里面添加了lmt-safety-virt平台的支持。下面是U-Boot编译构建命令:
1 | cd u-boot |
下面是运行验证命令:
1 | qemu-system-riscv32 -M lmt-safety-virt -m 1M -display none -device loader,file=/path/to/u-boot/u-boot-with-spl.bin,addr=0x60c00000,cpu-num=0 -serial stdio |
lmt-virt和lmt-safety-virt联合运行
SoC是异构的,里面即集成了ARM Cortex-A系列的CPU,也集成了RISC-V系列的CPU。因此一种架构的CPU开发一个对应的虚拟平台,两个及以上的虚拟平台对应多个Qemu进程,就涉及到多个Qemu之间的联动,用于仿真异构核之间的交互和控制逻辑。
下面是启动lmt-virt的命令:
1 | qemu-system-aarch64 -M lmt-virt,riscv-memdev=iram-safety -m 4G -display none -device loader,addr=0x400200000,file=/path/to/u-boot/u-boot.bin,cpu-num=0 -object memory-backend-file,size=512K,id=iram-safety,share=on,mem-path=/path/to/qemu-shm,discard-data=on -rp-path /tmp/coemu -serial stdio -semihosting |
/tmp/coemu
:如果目录不存在,mkdir创建riscv-memdev
:指定IRAM的内存后端ID,由machine使用
下面是启动lmt-safety-virt的命令:
1 | qemu-system-riscv32 -M lmt-safety-virt,standalone=off,memdev=iram-safety -m 2M -display none -chardev socket,id=coemu,path=/tmp/coemu/qemu_rport_machine_lmt_soc_cosim_rp -object memory-backend-file,size=512K,id=iram-safety,share=on,mem-path=/path/to/qemu-shm,discard-data=on -serial stdio |
standalone=
:指定machine的运行模式(单独或者联动)memdev=
:指定machine所使用内存后端的ID
AArch64侧的启动日志附件:AArch64 Booting
RISC-V侧的启动日志附件:RISC-V Booting
目前只是做到了AArch64侧能够控制RISC-V侧的启动,两侧的通信交互还没有开发,比如在Linux内核下AArch64与RISC-V进行交互访问等。