龙芯技术社区
直播中

离霜

1年用户 21经验值
擅长:嵌入式技术 EDA/IC设计 处理器/DSP 接口/总线/驱动 操作系统
私信 关注
[问答]

loongarch是如何区分大页和基本页的?

在开发loongarch架构的操作系统的时候,我遇到了这样的问题:我不知道硬件是如何区分大页和基本页的。
如图,关于基本页和大页的格式在手册中是这样的叙述的:
图片.png

即便手册中阐述了基本页和大页的格式差别,但是我在开发中仍然不知道相关的指令lddir的工作方式。
如图,lddir指令在手册中是这样叙述的:
图片.png

手册中的"第[14:13]位"是意义不明的,页表项格式说明中也没有详细解释,这使我对此感到困惑,硬件到底依据什么来区分大页和基本页?lddir的行为到底是怎么样的?如果我希望使用lddir指令来软件遍历页表,我应该如何做?

回帖(3)

jf_38522704

2024-3-30 12:05:22
手册(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看不到,不会冲突。

举报

人走了

2024-4-1 18:32:14
对于loongarch架构的操作系统,硬件区分大页和基本页是根据页表项的格式中的标志位来实现的。在loongarch手册中,页表项的格式会详细说明各个标志位的含义。

在您提供的截图中,"第[14:13]位"是用于区分大页和基本页的标志位。具体来说,该标志位描述了页表项所对应的页的大小。如果该标志位为0b00,表示页的大小是基本页;如果该标志位为0b01,表示页的大小是大页。此外,还可能有其他值用于描述不同大小的页(比如0b10、0b11等)。

至于lddir指令的工作方式,该指令用于加载页表项的内容到寄存器中。通过该指令,操作系统可以读取和修改页表项的各个字段,从而实现虚拟内存管理的功能。

如果您希望使用lddir指令来软件遍历页表,可以通过以下步骤实现:
1. 从根页表的基址开始,使用lddir指令加载根页表项到寄存器中。
2. 根据页表项中的标志位(如大页标志位)判断当前页的大小。
3. 如果当前页是大页,可以直接处理该页,然后跳转到下一个页表项。
4. 如果当前页是基本页,在基本页表中找到对应页表项的地址,并使用lddir指令加载该页表项到寄存器中。
5. 重复步骤2~4,直到遍历完整个页表。

需要注意的是,具体的页表结构和页表项的格式可能会因操作系统和硬件架构的不同而有所差异。因此,在实际开发中,您需要参考loongarch的具体手册和文档,了解该架构下的页表结构和页表项的格式,以及相应的操作指令和标志位的含义。
举报

jf_38522704

2024-4-2 18:02:42
手册(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看不到,不会冲突。
举报

更多回帖

发帖
×
20
完善资料,
赚取积分