手册(1.0.3) 4.2.5.1原文如下:
LDDIR 指令用于在软件页表遍历过程中目录项的访问。
LDDIR 指令中的 8 比特立即数 level 用于指示当前访问的是哪一级页表。 level=1 对应 CSR.PWCL 中的
PT, level=2 对应 CSR.PWCL 中的 Dir1, level=3 对应 CSR.PWCL 中的 Dir2, level=4 对应 CSR.PWCH 中的
Dir3。
如果通用寄存器 rj 的第[14:13]位不等于 0, 表明此时通用寄存器 rj 的值是一个已标记页表大小信息的
大页( HugePage) 页表项。 此情况下将通用寄存器 rj 中的值直接写入到通用寄存器 rd 中。
如果通用寄存器 rj 的第[14:13]位等于 0 且它的第[6]位是 1, 表明此时通用寄存器 rj 的值是一个尚未标
记页表大小信息的一个大页( HugePage) 的页表项。 此情况下将通用寄存器 rj 值的[14:13]位替换为 level[1:0]
后, 整体写入到通用寄存器 rd 中。
如果通用寄存器 rj 的第[14:13]位等于 0 且它的第[6]位是 0, 表明此时通用寄存器 rj 的值是第 level 级页
表的基址的物理地址。 此情况下执行 LDDIR 指令, 会根据当前处理的 TLB 重填地址访问第 level 级页表,
取回下一级页表的基址, 写入到通用寄存器 rd 中。 注意, 第 level 级页表的下一级页表并不限于第 level-1 级
页表。
修改为:
LDDIR 指令用于在软件页表遍历过程中目录项的访问。
LDDIR 指令中的 8 比特立即数 level 用于指示当前访问的是哪一级页表。level=1 对应 CSR.PWCL 中的Dir1,level=2 对应 CSR.PWCL 中的 Dir2,level=3 对应 CSR.PWCH 中的 Dir3,level=4 对应 CSR.PWCH 中的 Dir4。
如果通用寄存器 rj 的第[6]位是 0,表明此时通用寄存器 rj 的值是第 level 级页表的基址的物理地址。此情况下执行 LDDIR 指令,会根据当前处理的 TLB 重填地址访问第 level 级页表,取回下一级页表的基址,写入到通用寄存器 rd 中。注意,第 level 级页表的下一级页表并不限于第 level-1 级,软件可以选择跳过某些level。
如果通用寄存器 rj 的第[6]位是 1, 表明此时通用寄存器 rj 的值是一个大页( HugePage) 的页表项。进一步地,如果通用寄存器 rj 的第[14:13]位等于 0,则表明大页页表项尚未添加其对应哪一级页表的标记。此情况下将通用寄存器 rj 值的[14:13]位替换为 level[1:0] 后, 整体写入到通用寄存器 rd 中。 如果通用寄存器 rj 的第[14:13]位不等于 0, 表明此时通用寄存器 rj 的值已添加其对应哪一级页表的标记。此情况下将通用寄存器 rj 中的值直接写入到通用寄存器 rd 中。内存中的页表项不需要添加标记。
## 实例
在目前的linux/loongarch内核中,支持4KB/16KB/64KB三种页面大小,2-4级页表。
初始化页表控制寄存器配置的代码如下:
```c
unsigned long pwctl0, pwctl1;
unsigned long pgd_i = 0, pgd_w = 0;
unsigned long pud_i = 0, pud_w = 0;
unsigned long pmd_i = 0, pmd_w = 0;
unsigned long pte_i = 0, pte_w = 0;
pgd_i = PGDIR_SHIFT;
pgd_w = PAGE_SHIFT - 3;
#if CONFIG_PGTABLE_LEVELS > 3
pud_i = PUD_SHIFT;
pud_w = PAGE_SHIFT - 3;
#endif
#if CONFIG_PGTABLE_LEVELS > 2
pmd_i = PMD_SHIFT;
pmd_w = PAGE_SHIFT - 3;
#endif
pte_i = PAGE_SHIFT;
pte_w = PAGE_SHIFT - 3;
pwctl0 = pte_i | pte_w << 5 | pmd_i << 10 | pmd_w << 15 | pud_i << 20 | pud_w << 25;
pwctl1 = pgd_i | pgd_w << 6;
csr_write64(pwctl0, LOONGARCH_CSR_PWCTL0);
csr_write64(pwctl1, LOONGARCH_CSR_PWCTL1);
```
tlb重填例外处理代码如下:
```C
SYM_FUNC_START(handle_tlb_refill)
csrwr t0, LOONGARCH_CSR_TLBRSAVE
csrrd t0, LOONGARCH_CSR_PGD
lddir t0, t0, 3
#if CONFIG_PGTABLE_LEVELS > 3
lddir t0, t0, 2
#endif
#if CONFIG_PGTABLE_LEVELS > 2
lddir t0, t0, 1
#endif
ldpte t0, 0
ldpte t0, 1
tlbfill
csrrd t0, LOONGARCH_CSR_TLBRSAVE
ertn
SYM_FUNC_END(handle_tlb_refill)
```
一种典型的配置为页面大小16KB,3级页表,此时:
PAGE_SHIFT=14, PMD_SHIFT=25, PGD_SHIFT=36, CONFIG_PGTABLE_LEVELS=3,一共可以表示48位虚地址。
```C
pwctl0 = 14 | 11 << 5 | 25 << 10 | 11 << 15 | 0 << 20 | 0 << 25;
pwctl1 = 36 | 11 << 6;
```
tlbrefill的核心部分是这几条指令:
```ASM
lddir t0, t0, 3
lddir t0, t0, 1
ldpte t0, 0
ldpte t0, 1
tlbfill
```
第一条指令的t0为PGD值(由所访问的虚拟地址的bit47选择从CSR.PGDL还是CSR.PGDH来),软件应保证其bit6为0。如level3的页表中某些成为大页,那么它的大小将是2^36=64GB,装入该表项时lddir指令将会把t0[14:13]设置为level[1:0],即3。随后的lddir会直接复制t0,即不改变。这样,用同样的指令序列可以处理大页和非大页的情况,避免做条件判断。
## 答疑
1. 页大小为4KB时,页表基地址可能bit 14/13非0,如何保证大页的正常使用?
软件应该保证大页的页表项14/13位为0。大页的PS最小为2M,因此14/13位没有有效地址位。最后一级页表的页表项有可能存在14/13位非0的情况,但lddir/ldpte正常不应该以他们为输入参数, lddir的输入是dir1-dir4页目录基地址,ldpte处理pt页表基地址。
2. 基本页的bit6是G位,大页的H位也是bit6,会不会冲突?
G位仅需要在最后一级的表项使用,因此lddir/ldpte看不到,不会冲突。
最佳答案