relocate
relocate重定向,就是在開啟mmu。開啟mmu的操作就是將一級頁表的地址以及權限寫到satp
寄存器中,這就算開啟mmu了。
#ifdef CONFIG_MMU
la a0, early_pg_dir //跳轉到relocate前,先把第一級頁表early_pg_dir的地址存入a0
call relocate //跳轉到relocate,開啟MMU
#endif
relocate有兩次開啟mmu的操作,第一次開啟mmu使用的是setup_vm()
建立的trampoline_gd_dir
頁表,這頁表保存的是kernel
的前2M
內存。第二次開啟MMU使用的是early_pg_dir
頁表,這個頁表映射了整個kernel內存以及dtb
的4M空間。
如果trampoline_pg_dir
或者early_pg_dir
這兩個頁表的映射沒弄好的話,開啟MMU的時候就會失敗,所以頁表的建立十分關鍵。頁表創建后續再深究,下面分析relocate匯編代碼。
- 計算返回地址
返回地址就是ra
加上虛擬地址和物理地址之間的偏移量,這個是固定偏移量。PAGE_OFFSET
是kernel
入口地址對應的虛擬地址,_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
寄存器
因為一旦開啟MMU,地址都變成了虛擬地址,原來訪問的都是物理地址,開啟MMU時,地址發生了改變,VA != PA
,從而進入異常,所以要先設置異常入口地址,此時的異常入口為1f
。
/* Point stvec to virtual address of intruction after satp write */
la a2, 1f
add a2, a2, a1
csrw CSR_TVEC, a2
- 提前計算切換到
early_pg_dir
頁表要寫入satp
的值
再進入relocate之前,就已經把early_pg_dir賦值給a0了,所以a0是early_pg_dir。srl是邏輯右移,mmu使用的是sv39,虛擬地址39位,物理地址56位:
低12位是偏移量,所以PAGE_SHIFT
等于12,將early_pg_dir
地址右移12位存到a2
。根據satp寄存器定義:
MODE
等于0x8
代表使用sv39 mmu
,0x0
代表不進行地址翻譯,即不開啟MMU
。這里STAP_MODE
為sv39
,即0x8
。將early_pg_dir
地址和SATP_MODE
進行或運算后,即可得到寫入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頁表
satp
值的計算和上述是一樣的。開啟MMU
之前,通過sfence.vma
命令先刷新TLB
。此時開啟MMU
,就會進入下面的標號為1
的匯編段
la a0, trampoline_pg_dir
srl a0, a0, PAGE_SHIFT
or a0, a0, a1
sfence.vma
csrw CSR_SATP, a0
進入異常1f
段,重新設置異常入口為.Lsecondary_park
,然后切換到early_pg_dir
頁表,相當于第二次開啟MMU。此時,如果之前建立的early_pg_dir
頁表不對,則會就進入.Lsecondary_park
。.Lsecondary_park
里面是個wfi
指令,是個死循環。
完整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
+關注
關注
87文章
11292瀏覽量
209331 -
匯編
+關注
關注
2文章
214瀏覽量
25927 -
MMU
+關注
關注
0文章
91瀏覽量
18283 -
地址表
+關注
關注
0文章
4瀏覽量
810
發布評論請先 登錄
相關推薦
評論