一篇分析RISC-V Linux匯編啟動(dòng)過程
RISC-V Linux的匯編啟動(dòng)部分比較簡(jiǎn)單,不算復(fù)雜。有兩個(gè)部分比較核心:頁(yè)表創(chuàng)建和重定向。頁(yè)表創(chuàng)建是用C語(yǔ)言寫的,今天先分析匯編部分,先帶大家分析整體匯編啟動(dòng)流程,然后分析重定向。
注意:本文基于linux5.10.111內(nèi)核
匯編啟動(dòng)流程
先從整體分析匯編做的事情,有個(gè)大體框架。
路徑:arch/riscv/kernel/head.S
,入口是ENTRY(_start_kernel)

從ENTRY(_start_kernel)
開始進(jìn)行啟動(dòng)前的一些初始化,建立頁(yè)表前的主要工作:
關(guān)閉所有中斷
加載全局指針gp
disable FPU
選擇一個(gè)核啟動(dòng)
清楚bss段
保存hart id和dtb地址
設(shè)置sp指針
上述工作完成,會(huì)開始臨時(shí)頁(yè)表的創(chuàng)建,跳轉(zhuǎn)到C函數(shù)setup_vm建立臨時(shí)頁(yè)表
重定向
設(shè)置異常向量地址,重載C環(huán)境
最后跳轉(zhuǎn)到C函數(shù)start_kernel,開始C語(yǔ)言部分初始化,匯編部分執(zhí)行完畢
完整_start_kernel匯編代碼:
匯編中非常重要的一個(gè)部分就是頁(yè)表的創(chuàng)建,關(guān)乎著后面的程序能不能繼續(xù)往下跑。setup_vm創(chuàng)建頁(yè)表后就會(huì)開始執(zhí)行relocate重定向,這個(gè)重定向主要開啟mmu,下面分析relocate的匯編。
【文章福利】小編推薦自己的Linux內(nèi)核技術(shù)交流群:【749907784】整理了一些個(gè)人覺得比較好的學(xué)習(xí)書籍、視頻資料共享在群文件里面,有需要的可以自行添加哦?。。。ê曨l教程、電子書、實(shí)戰(zhàn)項(xiàng)目及代碼)??


relocate
relocate重定向,就是在開啟mmu。開啟mmu的操作就是將一級(jí)頁(yè)表的地址以及權(quán)限寫到satp
寄存器中,這就算開啟mmu了。
relocate有兩次開啟mmu的操作,第一次開啟mmu使用的是setup_vm()
建立的trampoline_gd_dir
頁(yè)表,這頁(yè)表保存的是kernel
的前2M
內(nèi)存。第二次開啟MMU使用的是early_pg_dir
頁(yè)表,這個(gè)頁(yè)表映射了整個(gè)kernel內(nèi)存以及dtb
的4M空間。
如果trampoline_pg_dir
或者early_pg_dir
這兩個(gè)頁(yè)表的映射沒弄好的話,開啟MMU的時(shí)候就會(huì)失敗,所以頁(yè)表的建立十分關(guān)鍵。頁(yè)表創(chuàng)建后續(xù)再深究,下面分析relocate匯編代碼。
計(jì)算返回地址
返回地址就是
ra
加上虛擬地址和物理地址之間的偏移量,這個(gè)是固定偏移量。PAGE_OFFSET
是kernel
入口地址對(duì)應(yīng)的虛擬地址,_start
就是kernel
入口地址的虛擬地址,PAGE_OFFSET
?-?_start
就得到它們之間的偏移,然后再和ra相加,就是返回地址。
將異常入口1f
的虛擬地址寫入stvec
寄存器
因?yàn)橐坏╅_啟MMU,地址都變成了虛擬地址,原來(lái)訪問的都是物理地址,開啟MMU時(shí),地址發(fā)生了改變,VA != PA
,從而進(jìn)入異常,所以要先設(shè)置異常入口地址,此時(shí)的異常入口為1f
。
提前計(jì)算切換到
early_pg_dir
頁(yè)表要寫入satp
的值
再進(jìn)入relocate之前,就已經(jīng)把early_pg_dir賦值給a0了,所以a0是early_pg_dir。srl是邏輯右移,mmu使用的是sv39,虛擬地址39位,物理地址56位:

低12位是偏移量,所以PAGE_SHIFT
等于12,將early_pg_dir
地址右移12位存到a2
。根據(jù)satp寄存器定義:

MODE
等于0x8
代表使用sv39 mmu
,0x0
代表不進(jìn)行地址翻譯,即不開啟MMU
。這里STAP_MODE
為sv39
,即0x8
。將early_pg_dir
地址和SATP_MODE
進(jìn)行或運(yùn)算后,即可得到寫入satp
寄存器的值,最后保存到a2
。
第一次開啟MMU,使用trampoline_pg_dir頁(yè)表
satp
值的計(jì)算和上述是一樣的。開啟MMU
之前,通過sfence.vma
命令先刷新TLB
。此時(shí)開啟MMU
,就會(huì)進(jìn)入下面的標(biāo)號(hào)為1
的匯編段
進(jìn)入異常1f
段,重新設(shè)置異常入口為.Lsecondary_park
,然后切換到early_pg_dir
頁(yè)表,相當(dāng)于第二次開啟MMU。此時(shí),如果之前建立的early_pg_dir
頁(yè)表不對(duì),則會(huì)就進(jìn)入.Lsecondary_park
。.Lsecondary_park
里面是個(gè)wfi
指令,是個(gè)死循環(huán)。
完整relocate匯編代碼:
總結(jié)
以上就是RISC-V Linux的匯編啟動(dòng)流程,雖說RISC-V的指令不復(fù)雜,但要理解這個(gè)匯編啟動(dòng)的部分,還是需要一點(diǎn)基礎(chǔ)和時(shí)間。另外,大多數(shù)人工作中基本用不上匯編,只有真正用上了理解才會(huì)比較深。希望本文能夠幫助到有需要的人。
原文作者:嵌入式Linux充電站
