relocate
relocate重定向,就是在開啟mmu。開啟mmu的操作就是將一級(jí)頁(yè)表的地址以及權(quán)限寫到satp寄存器中,這就算開啟mmu了。
#ifdef CONFIG_MMU
la a0, early_pg_dir //跳轉(zhuǎn)到relocate前,先把第一級(jí)頁(yè)表early_pg_dir的地址存入a0
call relocate //跳轉(zhuǎn)到relocate,開啟MMU
#endif
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相加,就是返回地址。
/* Relocate return address */
li a1, PAGE_OFFSET
la a2, _start
sub a1, a1, a2
add ra, ra, a1
- 將異常入口
1f的虛擬地址寫入stvec寄存器
因?yàn)橐坏╅_啟MMU,地址都變成了虛擬地址,原來(lái)訪問的都是物理地址,開啟MMU時(shí),地址發(fā)生了改變,VA != PA,從而進(jìn)入異常,所以要先設(shè)置異常入口地址,此時(shí)的異常入口為1f。
/* Point stvec to virtual address of intruction after satp write */
la a2, 1f
add a2, a2, a1
csrw CSR_TVEC, a2
- 提前計(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。
/* Compute satp for kernel page tables, but don't load it yet */
srl a2, a0, PAGE_SHIFT
li a1, SATP_MODE //sv39 mmu
or a2, a2, a1
- 第一次開啟MMU,使用trampoline_pg_dir頁(yè)表
satp值的計(jì)算和上述是一樣的。開啟MMU之前,通過sfence.vma命令先刷新TLB。此時(shí)開啟MMU,就會(huì)進(jìn)入下面的標(biāo)號(hào)為1的匯編段
la a0, trampoline_pg_dir
srl a0, a0, PAGE_SHIFT
or a0, a0, a1
sfence.vma
csrw CSR_SATP, a0
進(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匯編代碼:
relocate:
/* Relocate return address */
li a1, PAGE_OFFSET
la a2, _start
sub a1, a1, a2
add ra, ra, a1
/* Point stvec to virtual address of intruction after satp write */
la a2, 1f
add a2, a2, a1
csrw CSR_TVEC, a2
/* Compute satp for kernel page tables, but don't load it yet */
srl a2, a0, PAGE_SHIFT
li a1, SATP_MODE
or a2, a2, a1
/*
* Load trampoline page directory, which will cause us to trap to
* stvec if VA != PA, or simply fall through if VA == PA. We need a
* full fence here because setup_vm() just wrote these PTEs and we need
* to ensure the new translations are in use.
*/
la a0, trampoline_pg_dir
srl a0, a0, PAGE_SHIFT
or a0, a0, a1
sfence.vma
csrw CSR_SATP, a0
.align 2
1:
/* Set trap vector to spin forever to help debug */
la a0, .Lsecondary_park
csrw CSR_TVEC, a0
/* Reload the global pointer */
.option push
.option norelax
la gp, __global_pointer$
.option pop
/*
* Switch to kernel page tables. A full fence is necessary in order to
* avoid using the trampoline translations, which are only correct for
* the first superpage. Fetching the fence is guarnteed to work
* because that first superpage is translated the same way.
*/
csrw CSR_SATP, a2
sfence.vma
ret
-
Linux
+關(guān)注
關(guān)注
88文章
11631瀏覽量
218048 -
匯編
+關(guān)注
關(guān)注
2文章
214瀏覽量
27156 -
MMU
+關(guān)注
關(guān)注
0文章
92瀏覽量
19117 -
地址表
+關(guān)注
關(guān)注
0文章
5瀏覽量
988
發(fā)布評(píng)論請(qǐng)先 登錄
Linux后臺(tái)程序重定向的問題
linux shell數(shù)據(jù)重定向(輸入重定向與輸出重定向)詳細(xì)分析
Linux Shell系列教程之(十六) Shell輸入輸出重定向
實(shí)現(xiàn)printf函數(shù)的重定向
DNS 重定向的缺陷
實(shí)現(xiàn)重定向printf()和scanf() 函數(shù)案例分析
單片機(jī)printf重定向的函數(shù)免費(fèi)下載
ICMP重定向抓包分析 什么情況下路由器要向源發(fā)送ICMP重定向
Keil下使用STlink重定向printf的配置

Linux匯編啟動(dòng)relocate重定向分析
評(píng)論