0%

qemu tcg中riscv page table walk分析

基本逻辑

我们先看只有一级地址翻译的情况,地址翻译的基本逻辑是,CPU硬件使用VA去自动的查
存放在内存里的页表,最终得到物理地址。没有虚拟化情况下,只需要做一级地址翻译就好,
这种情况下,页表是放在物理内存上的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
block size          1G          2M          4K
+---------+------------+----------+----------+-----------+
|63 39|38 30|29 21|20 12|11 0|
+---------+------------+----------+----------+-----------+
| | | |
| | | v
| | | [11:0] in-page offset
| | +--> [20:12] L3 index
| +--------------+--> [29:21] L2 index
+--------------+--------------+--> [38:30] L1 index
| | |
+---+--------------+--------------+--------------+
| | | | |
SATP----+---+->+--------+ | | |
| | |... | | | |
| | +--------+ | | |
| +->| |--+->+--------+ | |
| +--------+ | |... | | |
| |... | | +--------+ | |
| +--------+ +->| |--+->+--------+ |
| +--------+ | |... | |
| |... | | +--------+ |
| +--------+ +->| | |
| +--------+ |
| |... | |
| memory +--------+ |
+------------------------------------------------+

在虚拟化两级地址翻译的场景下,要先把GVA翻译成GPA,再把GPA翻译成HPA,理解整个翻译
逻辑的关键是明白第一级翻译用的页表是“放在”GPA上的。如下是整个两级地址翻译的流程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
block size          1G          2M          4K
+---------+------------+----------+----------+-----------+
|63 39|38 30|29 21|20 12|11 0| VA
+---------+------------+----------+----------+-----------+
| | | |
| | | v
| | | [11:0] in-page offset
| | +--> [20:12] L3 index
| +--------------+--> [29:21] L2 index
+--------------+--------------+--> [38:30] L1 index
| | |
+---+--------------+--------------+--------------+
| | | | |
SATP----+---+->+--------+ | | |
| | |... | | | |
| | +--------+ | | |
| a +->| | ^ |--+->+--------+ | |
| +---+-+--+ | |... | | |
| |...| | | | +--------+ | |
| +---+-+--+ +->| | ^ |--+->+--------+ |
| | | b +--+--+--+ | |... | |
| | | |..| | | | +--------+ |
| | | +--+--+--+ +->| | ^ | |
| 1 | | | | c +--+--+--+ |
| | | | | |..| | | |
| GPA | | | | +--+--+--+ |
+----------+-+-----------+--+-----------+--+-----+
| | | | | |
+-+-----------+ | | |
| | | | |
+-+--------------+-----------+ |
| | 2 | 3 | 4
v +--------------+--------------+-----------------+
+---+--------------+--------------+--------------+ |
| | | | | |
HGATP----------+---+->+--------+ | | | |
| | |... | | | | |
| | +--------+ | | | |
| +->| |--+->+--------+ | | |
| +--------+ | |... | | | |
| |... | | +--------+ | | |
| +--------+ +->| |--+->+--------+ | |
| +--------+ | |... | | |
| |... | | +--------+ | |
| +--------+ +->| | | |
| +--------+ | |
| |... | | load到页表里的地址
| HPA +--------+ |
+------------------------------------------------+

两级翻译的目的是找见如上VA对应的HPA,这个过程逻辑上分成两部分,第一部分是通过第一
级翻译得到GPA,这个值存在如上c的地址上,我们只要完成第一级翻译的page table walk
就可以得到这个值,第二级翻译是针对GPA再做一次第二级地址翻译得到HPA,其中因为第一
部分的翻译使用的页表在GPA上,得到第一部分翻译要用的页表里的内容时就必须做第二级
翻译,这会导致第一部分的翻译比第二部分的翻译复杂的多。

整个第一部分翻译的流程是,硬件根据SATP中保存的页表基地址和VA的L1 index得到第一级
页表的第一级页表项的地址,因为这个地址是GPA,硬件还要通过一次第二级地址翻译得到GPA
a对应的HPA,硬件继续访问这个HPA得到a处存储的数值,这个第二级地址翻译的过程,我们
在上图中标记为1,可以看见这个过程需要进行4次内存访问,其中三次是访问页表项的内存,
第四次是load到翻译得到的a地址对应的HPA上的值。得到的a地址上的值,是第一级地址翻译
的第二级页表的基地址,它依然是一个GPA,这个基地址和VA的L2 index相加得到第一级地址
翻译的第二级页表项的地址,要得到这个地址上的值,我们依然要像之前一样做一次第二级
地址翻译,同样要做4次地址访问。依此类推,我们得到c地址上保存的值,这个过程又做了
4次地址访问,从c地址上的值,可以得到VA对应的GPA。

第二部分的地址翻译就是用上面得到GPA做一次第二级地址翻译得到对应的HPA,这个过程
需要做3次地址访问。

从整个分析可以看出,当做两级地址翻译时,三级页表在最差情况下要做15次内存访问才能
完成整个翻译过程。

代码分析

具体代码分析直接用注释的形式写到代码里了,可以参考这里