relocate
relocate重定向,就是在開(kāi)啟mmu。開(kāi)啟mmu的操作就是將一級(jí)頁(yè)表的地址以及權(quán)限寫(xiě)到satp
寄存器中,這就算開(kāi)啟mmu了。
#ifdef CONFIG_MMU
la a0, early_pg_dir //跳轉(zhuǎn)到relocate前,先把第一級(jí)頁(yè)表early_pg_dir的地址存入a0
call relocate //跳轉(zhuǎn)到relocate,開(kāi)啟MMU
#endif
relocate有兩次開(kāi)啟mmu的操作,第一次開(kāi)啟mmu使用的是setup_vm()
建立的trampoline_gd_dir
頁(yè)表,這頁(yè)表保存的是kernel
的前2M
內(nèi)存。第二次開(kāi)啟MMU使用的是early_pg_dir
頁(yè)表,這個(gè)頁(yè)表映射了整個(gè)kernel內(nèi)存以及dtb
的4M空間。
如果trampoline_pg_dir
或者early_pg_dir
這兩個(gè)頁(yè)表的映射沒(méi)弄好的話(huà),開(kāi)啟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
的虛擬地址寫(xiě)入stvec
寄存器
因?yàn)橐坏╅_(kāi)啟MMU,地址都變成了虛擬地址,原來(lái)訪(fǎng)問(wèn)的都是物理地址,開(kā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è)表要寫(xiě)入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)行地址翻譯,即不開(kāi)啟MMU
。這里STAP_MODE
為sv39
,即0x8
。將early_pg_dir
地址和SATP_MODE
進(jìn)行或運(yùn)算后,即可得到寫(xiě)入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
- 第一次開(kāi)啟MMU,使用trampoline_pg_dir頁(yè)表
satp
值的計(jì)算和上述是一樣的。開(kāi)啟MMU
之前,通過(guò)sfence.vma
命令先刷新TLB
。此時(shí)開(kāi)啟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)于第二次開(kāi)啟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)注
87文章
11292瀏覽量
209322 -
匯編
+關(guān)注
關(guān)注
2文章
214瀏覽量
25927 -
MMU
+關(guān)注
關(guān)注
0文章
91瀏覽量
18283 -
地址表
+關(guān)注
關(guān)注
0文章
4瀏覽量
810
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論