RISC-V64 opensbi啟動過程
掃描二維碼
隨時隨地手機(jī)看文章
RISC-V64 opensbi啟動過程
-
1.說明
-
2.環(huán)境準(zhǔn)備
-
2.1 交叉編譯工具鏈
-
2.2 源代碼準(zhǔn)備
-
3.riscv架構(gòu) gdb調(diào)試方法
-
4.opensbi底層初始化流程
-
4.1 從qemu的加載執(zhí)行開始
-
4.2 opensbi底層初始化
-
4.2 opensbi設(shè)備初始化
-
4.3 二級boot的跳轉(zhuǎn)
-
5.小結(jié)
1.說明
最近有一些riscv的項目做,雖然以前也用過例如k210之類的riscv架構(gòu)的芯片,但是都止于能夠做一些應(yīng)用,并未特別關(guān)注其芯片的體系架構(gòu)方面的東西,但是隨著接觸的芯片架構(gòu)的種類的逐漸的增加,發(fā)現(xiàn)要想使用一款好芯片的,僅僅做上層應(yīng)用并不能完全發(fā)揮出特定架構(gòu)芯片的全部優(yōu)勢。比如aarch64的el層級和虛擬化的模型,mips的mmu特性,以及sparc的窗口寄存器等等,芯片架構(gòu)的特點要是能夠完全的發(fā)揮出來,寫起應(yīng)用起來,那真是覺得很爽的事情。
目前在工作上做一些riscv項目,發(fā)現(xiàn)自己的積累的知識不夠了,還是需要深入到底層去理解,于是需要瘋狂的惡補(bǔ)相關(guān)的知識,看文檔、讀代碼、每天就這樣深入其中,看的多了,想法也很多,很容易就忘記了,有時也做做筆記,晚上下班后再將資料整理一下,如果覺得有些價值的東西,就編寫成文章,分享經(jīng)驗。
學(xué)習(xí)使用riscv64的芯片的架構(gòu),首先可以了解學(xué)習(xí)opensbi,作為芯片啟動的Bios,其作用不言而喻。工欲善其事,必先利其器。一個良好高效的開發(fā)環(huán)境將會使得分析代碼變得得心應(yīng)手。本文在Ubuntu18.04環(huán)境下進(jìn)行測試,在riscv64的qemu上進(jìn)行g(shù)db的單步調(diào)試,主要分析的階段是qemu啟動后,執(zhí)行到opensbi,直到啟動uboot的階段。
opensbi是研究和學(xué)習(xí)riscv底層的一個比較優(yōu)秀的項目,代碼量小,質(zhì)量也很高,很值得推薦的一個開源項目。
關(guān)于opensbi與qemu的環(huán)境搭建,我前面的文章中已經(jīng)提及,這里就不贅述了。
riscv64 qemu上進(jìn)行Linux環(huán)境搭建與開發(fā)記錄
2.環(huán)境準(zhǔn)備
2.1 交叉編譯工具鏈
如果按照之前的文章下載的Linux版本的交叉編譯工具鏈?zhǔn)遣粠в術(shù)db工具,所以可以下載一個bare/rtos版本的gcc。建議下載sifive的riscv的交叉編譯工具鏈
https://www.sifive.com/software
也可以到網(wǎng)盤下載:
https://pan.baidu.com/s/1_C-cFBD3ADVjVFm94bYzNw 提取碼: v38x
下載完成后解壓至自定義的文件夾中即可。
2.2 源代碼準(zhǔn)備
1.qemu最新版
2.opensbi
3.uboot
這些都可以參考文章:
riscv64 qemu上進(jìn)行Linux環(huán)境搭建與開發(fā)記錄
3.riscv架構(gòu) gdb調(diào)試方法
首先需要編譯安裝完成qemu-system-riscv64。
編譯uboot,進(jìn)入uboot:
make CROSS_COMPILE=riscv64-linux- qemu-riscv64_smode_defconfig make CROSS_COMPILE=riscv64-linux- -j4
可見在uboot目錄生成u-boot.bin文件。
編譯opensbi,進(jìn)入opensbi:
git clone https://github.com/riscv/opensbi.git export CROSS_COMPILE=riscv64-linux- make PLATFORM=generic FW_PAYLOAD_PATH=/u-boot.bin
可以生成build/platform/generic/firmware/fw_payload.elf文件。
在控制臺輸入
../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
即可進(jìn)入調(diào)試模式。其中需要知道的是-s -S,如果不加這兩個參數(shù),系統(tǒng)會直接運(yùn)行起來??勺鳛榄h(huán)境搭建是否成功的判斷依據(jù)。
當(dāng)進(jìn)入調(diào)試模式后,當(dāng)前代碼會hold住,可以ctrl+t另外開一個窗口,輸入
../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
然后輸入
(gdb) target remote localhost:1234
如下圖所示:
gdb的命令很多,這里就不展開進(jìn)行細(xì)致的描述了,這里只演示一些基本的操作。
首先可以看到當(dāng)程序加載完成后,已經(jīng)hold等待gdb命令進(jìn)行操作。
輸入list可以列出當(dāng)前的代碼,每次list可以展示10行源代碼。
輸入si可以進(jìn)行匯編級別的單步調(diào)試。
直接回車是繼續(xù)執(zhí)行上一條執(zhí)行的命令。
繼續(xù)向下跳轉(zhuǎn)可以看到函數(shù)的入口和函數(shù)的名稱。
輸入info all-registers可以看到riscv所有寄存器的值的狀態(tài),這里剛到入口處寄存器怎么會有初值呢?這里就是很關(guān)鍵的問題,后面再分析代碼的時候,會詳細(xì)分析這些問題。另外可以分析得出當(dāng)前的函數(shù)的入口0x80000000。
另外經(jīng)常使用的就是打斷點
b 10
表示打斷點在第10行。
b main
表示斷點在main函數(shù)處。
輸入c可以讓程序連續(xù)運(yùn)行,直到遇到斷點才停下來。
這些功能在跟蹤代碼的運(yùn)行流程的時候比較實用,gdb還有許多功能,這里就不介紹了。
4.opensbi底層初始化流程
上面做了這么多環(huán)境搭建方面的工作,目的就是為了方便的分析opensbi的底層初始化步驟和流程。從而更加深刻的了解riscv的架構(gòu)和初始化流程。
4.1 從qemu的加載執(zhí)行開始
首先需要從qemu的源代碼開始進(jìn)行加載分析起,當(dāng)前qemu-system-riscv64支持下面的開發(fā)板:
而在qemu的源代碼中也有一些
這里我們就拿virt進(jìn)行分析。
根據(jù)hw/riscv/virt.c來看,首先可以分析得到外設(shè)分布的地址。
上述可以得到DRAM的地址空間是從0x80000000處開始的,而大小是我們傳遞參數(shù)時傳遞進(jìn)去的。另外也記錄了一些外設(shè)的布局。
qemu加載程序之前的時候,與具體板子相關(guān)的部分,首先進(jìn)入了hw/riscv/virt.c的virt_machine_init。
1.注冊PLIC中斷設(shè)備
這里需要注意的是VIRT_PLIC,在PLIC core這部分與riscv的中斷處理相關(guān)。
2.注冊系統(tǒng)內(nèi)存
這部分的內(nèi)存大小由外部傳遞
3.創(chuàng)建設(shè)備樹
qemu也使用fdt創(chuàng)建了設(shè)備樹,該設(shè)備樹用于opensbi和uboot,這里的設(shè)備樹放在qemu分配的內(nèi)存的尾部。并且會將該參數(shù)傳遞,這就是為什么前面進(jìn)行g(shù)db調(diào)試時,入口處會發(fā)現(xiàn)寄存器上有參數(shù)。
根據(jù)riscv的寄存器的規(guī)則
寄存器a0-a7是用于傳遞函數(shù)參數(shù)的。另外這個設(shè)備樹的參數(shù)會直接傳遞到opensbi進(jìn)行解析和適配。同時uboot也會使用這個設(shè)備樹。
4.2 opensbi底層初始化
最先進(jìn)來的是opensbi/firmware/fw_payload.S的_start函數(shù)。
1.判斷hart id
在riscv模式中會將riscv的core稱為hart
2.代碼重定位
會判斷_load_start與_start是否一致,若不一致,則需要將代碼重定位,該項目不用重定位。
3.清除寄存器值
這里會清除sp、gp、tp、t1-t6、s0-s11、a3-a7。注意保存設(shè)備數(shù)地址的a1、a2不會清除。
5.清除bss段
如果要想c語言執(zhí)行起來,必須要做的事情有兩個,一個是設(shè)置sp棧地址,另外就是清除bss段。
6.設(shè)置sp棧指針
這里棧的指針的地址也很有意思,設(shè)置的bss結(jié)尾,由于棧是向上增加的,所以預(yù)留棧的空間大小為2000。
7.讀取設(shè)備樹中的設(shè)備信息
執(zhí)行call fw_platform_init函數(shù),前面分析過該函數(shù)的原型在opensbi/platform/generic/platform.c。
帶四個參數(shù)。
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");
攜帶四個參數(shù),根據(jù)匯編規(guī)則,正好傳遞a0,a1,a2,a3這四個參數(shù),a0是0,a1是設(shè)備樹地址,a2是設(shè)備樹大小,a3是0。
正好將這些信息利用起來了,然后從設(shè)備樹中解析qemu中設(shè)定相關(guān)的信息。
這樣的好處是只要入口地址一致,就算設(shè)備地址不一樣,也不用重新編譯opensbi了。這就是有了設(shè)備樹的好處。
8.fdt重定位
按照riscv的寄存器使用規(guī)則,a0-a7都是用于存放C語言函數(shù)參數(shù)的,下次執(zhí)行c語言參數(shù)就清除掉了,所以需要把設(shè)備樹從定位,從而讓uboot也知道。
9.跳轉(zhuǎn)到sbi_init
到這里,一些底層初始化的關(guān)鍵性細(xì)節(jié)就結(jié)束了,進(jìn)入sbi的正式的程序中去了。
首先執(zhí)行的opensbi/lib/sbi/sbi_init.c的sbi_init函數(shù)
4.2 opensbi設(shè)備初始化
在進(jìn)入sbi_init會首先判斷是通過S模式還是M模式啟動,這里先知道在qemu的設(shè)備樹中是以S模式啟動。
所以直接會執(zhí)行init_coldboot(scratch, hartid);,該函數(shù)的實現(xiàn)在opensbi/lib/sbi/sbi_scratch.c中。
看一下冷啟動會做那些初始化工作。主要關(guān)注
1.sbi_domain_init
初始化動態(tài)加載的鏡像的模塊
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
準(zhǔn)備下一級的boot
4.3 二級boot的跳轉(zhuǎn)
上述條件為二級boot的跳轉(zhuǎn)準(zhǔn)備了環(huán)境,此時可以加載uboot,或者rtos,或者Linux了。
5.小結(jié)
對于riscv的opensbi的大致的執(zhí)行流程就分析到這里,基本上概括了一個整體的執(zhí)行的流程,具體的細(xì)節(jié)可以通過閱讀代碼,加上代碼注釋進(jìn)行理解。本文只是一個map,大致的流程圖,很多細(xì)節(jié)沒有涉及到。如果去專研具體的細(xì)節(jié)部分,感覺還是有很多東西值得學(xué)習(xí)。opensbi是一個很好的開源項目,對于研究riscv的底層實現(xiàn),以及代碼的通用性上都很值得借鑒和學(xué)習(xí)。