tlb异常相关的简要概述
该内核源码为linux5.14中的32位loongarch架构下tlb异常相关部分。下面为相关部分的关键源代码和函数。
mm/tlbex-32.S
handle_tlb_load
handle_tlb_store
handle_tlb_modify
heandle_tlb_refill
kernel/traps.c
ebase
eentry+0x4000
LOONGARCH_CSR_EENTRY 例外入口地址
0x200 * 32 = 0x4000
tlbrentr
vec_size = 0x200
1 | do_raise_exception_err: 14 0 |
tlb_load *(0xa0210000 + 0x200)
gdb调试:
1 | b *0xa0210200 |
trap_init
1 | void __init trap_init(void) |
kerne/geneX.S
except_vec_vi_handler
loongarch32/irq.c
plat_irq_dispatch 中断
0x800 时钟中断
arch/loongarch/mm/tlb.c
__update_tlb
arch/loongarch/mm/fault.c
do_page_fault
mm/tlbex-32.S中handle_tlb_load函数
1 | SYM_FUNC_START(handle_tlb_load) |
1 | /* 判断Page table bits的_PAGE_PRESENT是否为0 */ |
1 | leave_load: |
1 | vmalloc_load: |
传入的BADV为0x0022614c。其中PGD为0xa24b7000,*0xa24b7000 = 0xa24bc000。
PTE基址 = PGD基址 + PGD_OFFSET = 0xa24bc000 + (0x000(10bits)<<2) = 0xa24bc000
该PTE基址 + PTE_OFFSET = 0xa24bc000 + (0x226(10bits)<<2) = 0xa24bc898 = 包含物理页号、页状态位的物理地址
将(*0xa24bc898)0xff3a09c的page_valid位置1,即改为0xff3a09d,其中0xff3a(高20bits为物理页号)
tlb取0xa24bc898、0xa24bc89c两页表项按规则填入TLBLO0和TLBLO1中
填入规则:31:12(PPN)→8:27(PPN),7:0(page状态位)→7:0(page状态位),最高4位清0
handle_tlb_load执行过程
进入tlb_load过程时,首先保存现场(0x1213b8),即执行csrwr,将r1的地址0x1213b8存到了0x32地址的内存处。
1 | SYM_FUNC_START(handle_tlb_load) |
csrrd传入出错虚拟地址(BADV)0x2134d4到r12寄存器,0x2134d4比0xc0000000小,故继续顺序执行。
1 | lu12i.w ra, 0xc |
将PGD基址0xa23e1000传入r13寄存器,其内存的值为0xa23de000
1 | csrrd t1, LOONGARCH_CSR_PGD |
先获取t0(BADV)0x002134d4的前10bits再偏移2位,得到PGD_OFFSET(0x000),加上PGD(0xa23e1000),存到r13寄存器,为0xa23e1000。
1 | vmalloc_done_load: |
接着获取t0(0x002134d4)的中间10bits再偏移2位,得到PTE_OFFSET(213<<2 = 0x84c),最后加上t1寄存器内存处的值0xa23de000,得到地址0xa23de84c存到t0寄存器。
1 | csrrd t0, LOONGARCH_CSR_BADV |
把r12内存处的值(*0xa23de84c= 0)写入r13寄存器,再刷新tlb。
1 | label_smp_pgtable_load: |
因为_PAGE_PRESENT为0(页不存在),所以跳转到nopage_tlb_load处,即PC跳转到0xa02102b0处执行。
1 | andi ra, t1, _PAGE_PRESENT |
接着恢复现场(r1=0x1213b8),跳转到tlb_do_page_fault函数处执行。
1 | nopage_tlb_load: |
第二次tlb_load:
继续(gdb) c之后,可以看到第二次触发tlb_load的BADV也为0x2134d4。
继续(gdb) si调试到PTE+PTE_OFFSET可以得到r12为0x23de84c,此时该内存地址中有值,为0xff2309c,存到r13寄存器。
1 | vmalloc_done_load: |
0xff2309c的_PAGE_PRESENT(第8位,判断该页是否存在)为1,并将_PAGE_VALID(第1位,判断该页是否有效)置1,即0xff2309d,将0xff2309d写回r12寄存器的内存地址处(*0xa23de84c=0xff2309d)。注意:该_PAGE_PRESENT、_PAGE_VALID都为页表的软件位,tlb只有硬件位。
1 | andi ra, t1, _PAGE_PRESENT |
由于tlb一次存取两页,其中第3位表示奇偶页。故将0xa23de848和0xa23de84c内存地址处的值(包含PPN、页表标志位的地址)按规则存入TLBLO0和TLBLO1中。由于TLBLO0寄存器的31:8位表示PPN,而页表31:12位表示PPN,故需要将该页表*0xa23de848的高20位PPN写入到TLBLO0寄存器的31:8位(其中高4位清0),低8位标志位写入到TLBLO0寄存器的7:0位。所以TLBO0 = ( t1 >>12 << 8) | (t1 & 0xff)。所以通过csrwr写入寄存器,TLBLO0寄存器按0xff229c写入 。TLBLO1寄存器按0xff239d写入,将旧值写回t0,t1。由于TLBLO0寄存器实际第8位只读恒为0,故第8位写忽略。
1 | ori t0, t0, 0x4 |
csrwr t0, LOONGARCH_CSR_TLBELO1
接着恢复现场,从例外处理现场返回。
1 | tlbwr |
总结
BADV: 0x002134d4
*0xa23de848 = 0xff2209c, TLBLO0 = ff229c(实际为ff221c,第8位恒为0,其中第1位页有效位为0);
*0xa23de84c = 0xff2309d, TLBLO0 = ff239d(实际为ff231d,第8位恒为0,其中第1位页有效位为1);
1 | do_raise_exception_err: 29 0 |
参考
四种TLB异常
TLB 异常总共有 4 种:TLB/XTLB 重填异常(TLB Refill Exception,意味着 TLB 中没有对应项),TLB 加载无效异常(TLB Load Invalid Exception,意味着读请求、TLB 中有对应项、但对应项无效),TLB 存储无效异常(TLB Store Invalid Exception,意味着写请求、TLB 中有对应项、但对应项无效),TLB 修改异常(TLB Modify Exception,意味着写请求,TLB 中有对应项、也有效、但对应项只读)。TLB/XTLB 重填异常有专门的入口向量(向量 0 和向量 1),而其他几种异常使用通用入口向量(向量 3)。
BADV 出错虚地址
虚拟地址结构
32位系统的页面大小4KB。PGD和PTE占10位,故页表项4B,页目录表4KB,页目录表中每个有效表项对应一个 4KB 页表。
虚拟地址 10:10:12 (PGD:PTE:PAGE_OFFSET)
PGD 全局目录基址
PGDL+PGDOFFSET = BADV对应PGD物理地址
TLBSRCH
TLBELO0,1
ld.w和st.w指令
1 | ld.w rd, rj, 0 /* 将rj内存中的数据装入到rd寄存器 */ |
Load 用于把内存中的数据装载到寄存器中。 rd = *rj
Store用于把寄存器中的数据存入内存。 *rj = rd
DBAR 栅障指令
ERTN
TLBFILL
Page table bits
1 | /* Page table bits */ |
虚拟地址划分:最低的2GB(USEG)既缓存又分页;随后的512MB(KSEG0)缓存但不分页,对应物理地址的低 512MB(虚拟地址去掉高三位即为物理地址);接下来的512MB(KSEG1)既不缓存又不分页,同样对应物理地址的低 512MB(虚拟地址去掉高三位即为物理地址);最后的 1GB(KSEG2)对应物理既缓存又分页。
其他
tlb_load
1 | SYM_FUNC_START(handle_tlb_store) |
tlb_modify
1 | SYM_FUNC_START(handle_tlb_modify) |
tlb_refill
1 | SYM_FUNC_START(handle_tlb_refill) |