前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >RISC-V64 opensbi启动过程

RISC-V64 opensbi启动过程

作者头像
bigmagic
发布2020-12-08 17:13:06
7.7K0
发布2020-12-08 17:13:06
举报
文章被收录于专栏:嵌入式iot

RISC-V64 opensbi启动过程

  • 1.说明
  • 2.环境准备
    • 2.1 交叉编译工具链
    • 2.2 源代码准备
  • 3.riscv架构 gdb调试方法
  • 4.opensbi底层初始化流程
    • 4.1 从qemu的加载执行开始
    • 4.2 opensbi底层初始化
    • 4.2 opensbi设备初始化
    • 4.3 二级boot的跳转
  • 5.小结

1.说明

最近有一些riscv的项目做,虽然以前也用过例如k210之类的riscv架构的芯片,但是都止于能够做一些应用,并未特别关注其芯片的体系架构方面的东西,但是随着接触的芯片架构的种类的逐渐的增加,发现要想使用一款好芯片的,仅仅做上层应用并不能完全发挥出特定架构芯片的全部优势。比如aarch64的el层级和虚拟化的模型,mips的mmu特性,以及sparc的窗口寄存器等等,芯片架构的特点要是能够完全的发挥出来,写起应用起来,那真是觉得很爽的事情。

目前在工作上做一些riscv项目,发现自己的积累的知识不够了,还是需要深入到底层去理解,于是需要疯狂的恶补相关的知识,看文档、读代码、每天就这样深入其中,看的多了,想法也很多,很容易就忘记了,有时也做做笔记,晚上下班后再将资料整理一下,如果觉得有些价值的东西,就编写成文章,分享经验。

学习使用riscv64的芯片的架构,首先可以了解学习opensbi,作为芯片启动的Bios,其作用不言而喻。工欲善其事,必先利其器。一个良好高效的开发环境将会使得分析代码变得得心应手。本文在Ubuntu18.04环境下进行测试,在riscv64的qemu上进行gdb的单步调试,主要分析的阶段是qemu启动后,执行到opensbi,直到启动uboot的阶段。

opensbi是研究和学习riscv底层的一个比较优秀的项目,代码量小,质量也很高,很值得推荐的一个开源项目。

关于opensbi与qemu的环境搭建,我前面的文章中已经提及,这里就不赘述了。

riscv64 qemu上进行Linux环境搭建与开发记录

2.环境准备

2.1 交叉编译工具链

如果按照之前的文章下载的Linux版本的交叉编译工具链是不带有gdb工具,所以可以下载一个bare/rtos版本的gcc。建议下载sifive的riscv的交叉编译工具链

代码语言:javascript
复制
https://www.sifive.com/software

也可以到网盘下载:

代码语言:javascript
复制
https://pan.baidu.com/s/1_C-cFBD3ADVjVFm94bYzNw 
提取码: v38x 

下载完成后解压至自定义的文件夹中即可。

2.2 源代码准备

1.qemu最新版

2.opensbi

3.uboot

这些都可以参考文章:

riscv64 qemu上进行Linux环境搭建与开发记录

3.riscv架构 gdb调试方法

首先需要编译安装完成qemu-system-riscv64

编译uboot,进入uboot:

代码语言:javascript
复制
make CROSS_COMPILE=riscv64-linux- qemu-riscv64_smode_defconfig
make CROSS_COMPILE=riscv64-linux- -j4

可见在uboot目录生成u-boot.bin文件。

编译opensbi,进入opensbi:

代码语言:javascript
复制
git clone https://github.com/riscv/opensbi.git
export CROSS_COMPILE=riscv64-linux-
make PLATFORM=generic FW_PAYLOAD_PATH=<uboot_build_directory>/u-boot.bin

可以生成build/platform/generic/firmware/fw_payload.elf文件。

在控制台输入

代码语言:javascript
复制
../riscv64-unknown-elf-gcc-8.3.0-2020.04.0-x86_64-linux-ubuntu14/bin/riscv64-unknown-elf-gdb build/platform/generic/firmware/fw_payload.elf -s -S

即可进入调试模式。其中需要知道的是-s -S,如果不加这两个参数,系统会直接运行起来。可作为环境搭建是否成功的判断依据。

当进入调试模式后,当前代码会hold住,可以ctrl+t另外开一个窗口,输入

代码语言:javascript
复制
../riscv64-unknown-elf-gcc-8.3.0-2020.04.0-x86_64-linux-ubuntu14/bin/riscv64-unknown-elf-gdb build/platform/generic/firmware/fw_payload.elf

然后输入

代码语言:javascript
复制
(gdb) target remote localhost:1234

如下图所示:

gdb的命令很多,这里就不展开进行细致的描述了,这里只演示一些基本的操作。

首先可以看到当程序加载完成后,已经hold等待gdb命令进行操作。

输入list可以列出当前的代码,每次list可以展示10行源代码。

输入si可以进行汇编级别的单步调试。

直接回车是继续执行上一条执行的命令。

继续向下跳转可以看到函数的入口和函数的名称。

输入info all-registers可以看到riscv所有寄存器的值的状态,这里刚到入口处寄存器怎么会有初值呢?这里就是很关键的问题,后面再分析代码的时候,会详细分析这些问题。另外可以分析得出当前的函数的入口0x80000000

另外经常使用的就是打断点

代码语言:javascript
复制
b 10

表示打断点在第10行。

代码语言:javascript
复制
b main

表示断点在main函数处。

输入c可以让程序连续运行,直到遇到断点才停下来。

这些功能在跟踪代码的运行流程的时候比较实用,gdb还有许多功能,这里就不介绍了。

4.opensbi底层初始化流程

上面做了这么多环境搭建方面的工作,目的就是为了方便的分析opensbi的底层初始化步骤和流程。从而更加深刻的了解riscv的架构和初始化流程。

4.1 从qemu的加载执行开始

首先需要从qemu的源代码开始进行加载分析起,当前qemu-system-riscv64支持下面的开发板:

而在qemu的源代码中也有一些

这里我们就拿virt进行分析。

根据hw/riscv/virt.c来看,首先可以分析得到外设分布的地址。

上述可以得到DRAM的地址空间是从0x80000000处开始的,而大小是我们传递参数时传递进去的。另外也记录了一些外设的布局。

qemu加载程序之前的时候,与具体板子相关的部分,首先进入了hw/riscv/virt.cvirt_machine_init

1.注册PLIC中断设备

这里需要注意的是VIRT_PLIC,在PLIC core这部分与riscv的中断处理相关。

2.注册系统内存

这部分的内存大小由外部传递

3.创建设备树

qemu也使用fdt创建了设备树,该设备树用于opensbi和uboot,这里的设备树放在qemu分配的内存的尾部。并且会将该参数传递,这就是为什么前面进行gdb调试时,入口处会发现寄存器上有参数。

根据riscv的寄存器的规则

寄存器a0-a7是用于传递函数参数的。另外这个设备树的参数会直接传递到opensbi进行解析和适配。同时uboot也会使用这个设备树。

4.2 opensbi底层初始化

最先进来的是opensbi/firmware/fw_payload.S_start函数。

1.判断hart id

在riscv模式中会将riscv的core称为hart

2.代码重定位

会判断_load_start_start是否一致,若不一致,则需要将代码重定位,该项目不用重定位。

3.清除寄存器值

这里会清除spgptpt1-t6s0-s11a3-a7。注意保存设备数地址的a1a2不会清除。

5.清除bss段

如果要想c语言执行起来,必须要做的事情有两个,一个是设置sp栈地址,另外就是清除bss段。

6.设置sp栈指针

这里栈的指针的地址也很有意思,设置的bss结尾,由于栈是向上增加的,所以预留栈的空间大小为2000。

7.读取设备树中的设备信息

执行call fw_platform_init函数,前面分析过该函数的原型在opensbi/platform/generic/platform.c

带四个参数。

代码语言:javascript
复制
unsigned long fw_platform_init(unsigned long arg0, unsigned long arg1,
                                unsigned long arg2, unsigned long arg3,
                                unsigned long arg4)
{
        const char *model;
        void *fdt = (void *)arg1;
        u32 hartid, hart_count = 0;
        int rc, root_offset, cpus_offset, cpu_offset, len;

        root_offset = fdt_path_offset(fdt, "/");
        if (root_offset < 0)
                goto fail;

        fw_platform_lookup_special(fdt, root_offset);

        model = fdt_getprop(fdt, root_offset, "model", &len);
        if (model)
                sbi_strncpy(platform.name, model, sizeof(platform.name));

        if (generic_plat && generic_plat->features)
                platform.features = generic_plat->features(generic_plat_match);

        cpus_offset = fdt_path_offset(fdt, "/cpus");

携带四个参数,根据汇编规则,正好传递a0,a1,a2,a3这四个参数,a0是0,a1是设备树地址,a2是设备树大小,a3是0。

正好将这些信息利用起来了,然后从设备树中解析qemu中设定相关的信息。

这样的好处是只要入口地址一致,就算设备地址不一样,也不用重新编译opensbi了。这就是有了设备树的好处。

8.fdt重定位

按照riscv的寄存器使用规则,a0-a7都是用于存放C语言函数参数的,下次执行c语言参数就清除掉了,所以需要把设备树从定位,从而让uboot也知道。

9.跳转到sbi_init

到这里,一些底层初始化的关键性细节就结束了,进入sbi的正式的程序中去了。

首先执行的opensbi/lib/sbi/sbi_init.csbi_init函数

4.2 opensbi设备初始化

在进入sbi_init会首先判断是通过S模式还是M模式启动,这里先知道在qemu的设备树中是以S模式启动。

所以直接会执行init_coldboot(scratch, hartid);,该函数的实现在opensbi/lib/sbi/sbi_scratch.c中。

看一下冷启动会做那些初始化工作。主要关注

1.sbi_domain_init

初始化动态加载的镜像的模块

2.sbi_platform_early_init

平台的早期初始化

3.sbi_console_init

控制台初始化,从这里开始,就可以使用串口输出了。

4.sbi_platform_irqchip_init

irq中断初始化

5.sbi_ipi_init

核间中断初始化

6.sbi_tlb_init

mmu的tlb表的初始化

7.sbi_timer_init

timer初始化

8.sbi_hsm_prepare_next_jump

准备下一级的boot

4.3 二级boot的跳转

上述条件为二级boot的跳转准备了环境,此时可以加载uboot,或者rtos,或者Linux了。

5.小结

对于riscv的opensbi的大致的执行流程就分析到这里,基本上概括了一个整体的执行的流程,具体的细节可以通过阅读代码,加上代码注释进行理解。本文只是一个map,大致的流程图,很多细节没有涉及到。如果去专研具体的细节部分,感觉还是有很多东西值得学习。opensbi是一个很好的开源项目,对于研究riscv的底层实现,以及代码的通用性上都很值得借鉴和学习。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-12-05,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 嵌入式IoT 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • RISC-V64 opensbi启动过程
    • 1.说明
      • 2.环境准备
        • 2.1 交叉编译工具链
        • 2.2 源代码准备
      • 3.riscv架构 gdb调试方法
        • 4.opensbi底层初始化流程
          • 4.1 从qemu的加载执行开始
          • 4.2 opensbi底层初始化
          • 4.2 opensbi设备初始化
          • 4.3 二级boot的跳转
        • 5.小结
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档