# loongarch架构介绍#[三]地址翻译 原创 精华
作者:蒋卫峰 李涛
前言
本文是loongarch架构介绍系列的第三篇文章。前面的第一篇文章介绍了loongarch架构中的基础指令和使用,第二篇文章介绍了内存模型、原子指令与栅障指令的使用。
虚拟内存系统是软硬协同的一个经典案例,如今主流的架构和操作系统都已经支持虚拟内存机制。本文介绍loongarch虚拟内存系统中的地址翻译部分,主要包括地址翻译模式、loongarch中多级页表和相关的配置。另外本文中许多寄存器等资源属于特权资源,因此也会介绍loongarch中的csr特权指令和特权级作为背景知识。
1. 特权级和csr特权指令
虚拟内存相关的寄存器等资源属于特权资源,因此在介绍loongarch虚拟内存相关机制之前,先对loongarch中的特权指令和特权级进行介绍。
1.1 特权级
loongarch架构中有4个特权等级(Privilege Level,简称PLV),分别为PLV0-PLV3。其中PLV0的权限最高,也是唯一可以使用特权指令并访问所有特权资源的特权等级。而PLV3的权限最低。并且PLV1-PLV3这三个特权级下大部分情况下都不能执行特权执行以访问特权资源。
例如,在Linux系统loongarch架构相关代码中,内核态处于PLV0,而用户态处于PLV3。
loongarch架构中有许多控制状态寄存器(CSR),在loongarch资料中一般用CSR.xxx来表示某个控制状态寄存器。cpu当前所处的特权级就由当前模式信息寄存器CSR.CRMD中的PLV域确定,即通过配置CSR.CRMD.PLV可以设置当前特权级。
1.2 csr特权指令
csr特权指令用于访问和配置控制状态寄存器,有以下几种:
-
csrrd:读取CSR。如
csrrd rd, csr_num
表示将csr_num对应的CSR的值写入到通用寄存器rd中。其中csr_num为loongarch中控制状态寄存器的编号,如CSR.CRMD对应的编号为0。如下面代码将CSR.CRMD中值读到临时寄存器t0中:
csrrd t0, 0 // CSR.CRMD对应的编号为0
-
csrwr:写入CSR。如
csrwr rd, csr_num
表示将通用寄存器rd中的值写入csr_num对应的CSR,同时将csr_num对应CSR中的旧值返回到rd中。如使用下面代码可以设置特权级为PLV3:
li.d t0, 0xXXXXXXXXXXXXXXX3 // 最低两位为3 csrwr t0, 0
-
csrxchg:如
csrxchg rd, rj, csr_num
表示将通用寄存器rj中的值作为掩码mask,将rd中值写入到csr_num对应CSR中对应掩码为1的那些比特,该CSR中的其余比特不变。同时将csr_num对应CSR中的旧值返回到rd中。如以下代码同样可以设置特权级为PLV3:
li.d t0, 0x3 csrxchg t0, t0, 0 // 只将CSR.CRMD最后两位置1
下图为CSR.CRMD表中PLV部分描述:
2. 地址空间
loongarch中的虚拟地址空间大小为:
-
LA32中虚拟地址空间大小为$2^{32}$字节
-
LA64中虚拟地址空间大小为$2^{64}$字节
loongarch中的物理地址空间大小为$2^{PALEN}$字节,其中:
-
LA32中,PALEN为一个不超过36的正整数,通常为32
-
LA64中,PALEN为一个不超过60的正整数
-
PALEN由具体的实现决定,软件可以通过CPUCFG指令读取PALEN的值
3. 地址翻译模式
loongarch虚实地址转换机制首先由其地址翻译模式所确定。
loongarch中有两种地址翻译模式:
-
直接地址翻译模式:在该模式下,物理地址直接等于虚拟地址的低PALEN位,不足补0。相当于没有进行映射。
-
映射地址翻译模式:在该模式下,又分为两种模式:
-
直接映射地址翻译模式(简称直接映射模式):相当于线性映射。具体细节见后文中说明。
-
页表映射地址翻译模式(简称页表映射模式):相当于分页机制。具体细节见后文中说明。
-
其中,直接地址翻译模式和映射地址翻译模式不能够同时启用。但在映射地址翻译模式下,可以同时启用直接映射模式和页表映射模式,进行地址翻译时,会优先使用直接映射模式,如果无法使用直接映射模式,才会使用页表映射模式进行翻译。
3.1 地址翻译模式的配置
loongarch中通过控制寄存器CSR.CRMD配置地址翻译模式:
- CSR.CRMD中DA=1且PG=0:直接地址翻译模式
- CSR.CRMD中DA=0且PG=1:映射地址翻译模式
- 其他DA和PG值的组合的行为未定义
CSR.CRMD表中DA和PG部分如下图:
类似于上文中特权级的设置,地址翻译模式的设置同样使用csrwr等指令。
3.2 直接映射模式及其配置
loongarch中的直接映射模式是一种线性的映射方式,有点类似于分段。loongarch中通过配置直接映射配置窗口寄存器,来配置直接映射。
共有4个直接映射配置窗口寄存器,CSR.DMW0-CSR.DMW3,配置一个窗口相当于映射一段内存区域。其中CSR.DMW0和CSR.DMW1映射的内存区域可以同时用于取指和load/store操作,而CSR.DMW2和CSR.DMW3映射的内存区域仅用于load/store操作。
下面对直接映射模式配置和映射规则进行说明。
3.2.1 LA32
下面以LA32、且PALEN=32为例进行说明。
LA32中直接映射配置窗口寄存器内容如下图:
其中:
-
PLV0等域为使能CSR.DMW寄存器,且与访问特权级相关
-
MAT域与内存一致性模型和内存访问类型相关,可参考上一篇文章
-
PSEG域为配置的物理地址最高3位
-
VSEG域为配置的虚拟地址最高3位
例如,以下代码配置CSR.DMW0,将虚拟地址0x80000000-0x9fffffff映射到物理地址0x0-0x1fffffff:
li.w t0, 0x80000011
csrwr t0, 0x180 // 0x180对应CSR.DMW0
解释如下:
-
0x80000011中,VSEG为0b100,表示匹配的虚拟地址的最高3位为0b100,即虚拟地址0x80000000-0x9fffffff
-
0x80000011中,PSEG为0b0,表示匹配的物理地址的最高3位为0b0,即对应物理地址0x0-0x1fffffff
3.2.2 LA64
下面以LA64、且PALEN=48为例进行说明。
LA64中直接映射配置窗口寄存器内容如下图:
其中:
-
PLV0等域、MAT域与LA32中作用相同
-
VSEG域与LA32中类似,但是有4位
例如,以下代码配置CSR.DMW0,将虚拟地址0x9000000000000000-0x9000ffffffffffff映射到物理地址0x0-0xffffffffffff:
li.d t0, 0x9000000000000011
csrwr t0, 0x180 // 0x180对应CSR.DMW0
解释如下:
-
0x9000000000000011中,VSEG为0x9,表示匹配的虚拟地址的最高4位为0x9,即虚拟地址0x9000000000000000-0x9000ffffffffffff
-
PALEN=48,对应的物理地址为整个$2^{48}$大小的地址范围,即0x0-0xffffffffffff
3.3 页表映射模式及其配置
3.3.1 页表基址
在开启页表映射模式之后,首先需要配置页表基址。
loongarch中可以同时有两个pgd(页全局目录)基址,地址分别存储于寄存器CSR.PGDL(低半地址空间全局目录基址)和CSR.PGDH(高半地址空间全局目录基址),分别对应低半地址空间和高半地址空间。CSR.PGDL和CSR.PGDH如下图:
上图中,GRLEN表示寄存器长度,VALEN表示虚拟地址长度。LA32中GRLEN为32,VALEN为32;LA64中GRLEN为64,VALEN为64。
低半地址空间和高半地址空间实际上就是对虚拟地址空间对半分成了两个部分。虚拟地址最高位为0则表示处于低半地址空间;为1则表示处于高半地址空间。两个部分的地址都用对应部分的pgd进行地址转换,互不干扰。
在Linux中,CSR.PGDH存放内核进程的pgd,CSR.PGDL中存放用户进程的pgd。loongarch中相当于在硬件上又对内核空间和用户空间的页表进行了隔离。
3.3.2 页表分级
loongarch中可以自定义页表的分级,使用页表前需要配置页表分级。
loongarch中页表的具体分级由寄存器CSR.PWCL和CSR.PWCH决定,这两个寄存器分别控制低半部分和高半部分的页表分级,其中CSR.PWCH只存在于LA64。注:这里的低半部分和高半部分指的是寄存器分成了两部分。CSR.PWCL和CSR.PWCH分别如下图:
下图是一个多级页表的示例:
解释如下:
-
其中xxbase表示该级在虚拟地址中的起始位置,xxwidth表示该级在虚拟地址中的长度
-
以LA64为例,配置Dir4_base、PTbase等域和Dir4_width、PTwidth等域,相当于指定了虚拟地址按分级页表规则的解析方式
-
虚拟地址的解析过程示例如下:
-
首先根据最高位选择CSR.PGDH或CSR.PGDL中的pgd,即目录4的基址
-
将虚拟地址中Dir4_base和Dir4_width指定范围的值作为索引,在目录4查找对应目录3的基址
-
将虚拟地址中Dir3_base和Dir3_width指定范围的值作为索引,在目录3查找对应目录2的基址
-
将虚拟地址中Dir2_base和Dir2_width指定范围的值作为索引,在目录2查找对应目录1的基址
-
将虚拟地址中Dir1_base和Dir1_width指定范围的值作为索引,在目录1查找对应末级页表的基址
-
将虚拟地址中PTbase和PTwidth指定范围的值作为索引,在末级页表中查找最终页表项
-
其中具体页表项相关信息见后续文章。
3.3.3 配置
本小节以Linux源码中loongarch下页表基址和分级的配置进行分析。
代码如下:
static void setup_ptwalker(void)
{
unsigned long pwctl0, pwctl1;
unsigned long pgd_i = 0, pgd_w = 0; // Dir3_base和Dir3_width
unsigned long pud_i = 0, pud_w = 0; // Dir2_base和Dir2_width
unsigned long pmd_i = 0, pmd_w = 0; // Dir1_base和Dir1_width
unsigned long pte_i = 0, pte_w = 0; // PTbase和PTwidth
// PTEWidth为0,表示每个表项为64bit
// Dir4_base和Dir4_width未设置
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.PWCL和CSR.PWCH,LOONGARCH_CSR_PWCTL0为CSR.PWCL
// LOONGARCH_CSR_PWCTL1为CSR.PWCH
csr_write64(pwctl0, LOONGARCH_CSR_PWCTL0);
csr_write64(pwctl1, LOONGARCH_CSR_PWCTL1);
// 设置CSR.PGDL和CSR.PGDH
// CSR.PGDH中swapper_pg_dir为内核空间pgd
// CSR.PGDL为用户空间pgd,暂时设置为invalid_pg_dir,到进程创建等操作时分配
csr_write64((long)swapper_pg_dir, LOONGARCH_CSR_PGDH);
csr_write64((long)invalid_pg_dir, LOONGARCH_CSR_PGDL);
...
}
总结
本文介绍了地址翻译模式以及相关的配置。下一篇文章将继续对loongarch虚拟内存系统中的其他部分。
更多原创内容请关注:深开鸿技术团队
入门到精通、技巧到案例,系统化分享OpenHarmony开发技术,欢迎投稿和订阅,让我们一起携手前行共建生态。
终于等到第三章了,前排学习
学习
多级页表展示的很清晰