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文章
11222浏览量
208889 -
汇编
+关注
关注
2文章
214浏览量
25898 -
MMU
+关注
关注
0文章
91浏览量
18264 -
地址表
+关注
关注
0文章
4浏览量
808
发布评论请先 登录
相关推荐
评论