tlb异常处理
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
do_raise_exception_err: 14 0
mips_cpu_do_interrupt enter: PC 0018c16c EPC 0018c024 RFEPC 0x000000000018c024 TLB refill exception
mips_cpu_do_interrupt: PC a0201000 EPC 0x0018c16c cause 63(refill)
EXST 003f0000 EXCFG 0x00071c1c BADVA 0x002134d4 ENV_PGDL 0xa23e1000 BADI 0x00000000 SYS_NUM 2381672 cpu 0 asid 0x1
exception_return: EPC 0x18c16c
do_raise_exception_err: 29 0
mips_cpu_do_interrupt enter: PC 0018c16c EPC 0018c16c RFEPC 0x000000000018c16c TLB load exception
mips_cpu_do_interrupt: PC a0214000 EPC 0x0018c16c cause 1
EXST 00010000 EXCFG 0x00071c1c BADVA 0x002134d4 ENV_PGDL 0xa23e1000 BADI 0x00000000 SYS_NUM 2381672 cpu 0 asid 0x1
do_raise_exception_err: 65538 0tlb_load *(0xa0210000 + 0x200)
gdb调试:
b *0xa0210200
x /32i 0xa0210200trap_init
void __init trap_init(void)
{
set_handler(EXCCODE_GENERIC * vec_size , except_vec_vi_handler, vec_size);
...
set_handler(EXCCODE_TLBL * vec_size, handle_tlb_load, vec_size);
...
}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函数
SYM_FUNC_START(handle_tlb_load)
/* 保存现场 */
csrwr ra, EXCEPTION_KS2
lu12i.w ra, 0xc
slli.w ra, ra, 0x10
csrrd t0, LOONGARCH_CSR_BADV
bgeu t0, ra, vmalloc_load
csrrd t1, LOONGARCH_CSR_PGD
vmalloc_done_load:
/* Get PGD offset in bytes */
srli.w t0, t0, 0x16
andi t0, t0, 0x3ff
slli.w t0, t0, 0x2 /* 页内偏移为12位,故t0为PGD OFFSET(也为12bit) */
add.w t1, t1, t0 /* PGD基址 + PGD_OFFSET = 该PTE基址 */
ld.w t1, t1, 0 /* 从t1内存中取出数据(页目录号)放到t1中 */
csrrd t0, LOONGARCH_CSR_BADV
srli.w t0, t0, 0xc
andi t0, t0, 0x3ff /* 获取虚拟地址中间的PTE10位 */
slli.w t0, t0, 0x2 /* 获得PTE_OFFSET */
add.w t0, t0, t1 /* 该PTE基址 + PTE_OFFSET = 包含物理页号的物理地址*/
ld.w t1, t0, 0 /* 由物理页号的物理地址取出Page table bits */
tlbsrch /* 使用ASID和TLBEHI查询TLB *//* 判断Page table bits的_PAGE_PRESENT是否为0 */
andi ra, t1, _PAGE_PRESENT
beq ra, zero, nopage_tlb_load
ori t1, t1, _PAGE_VALID /* 将PTEE中_PAGE_VALID置1 */
st.w t1, t0, 0 /* 更新page table bits */
/* tlb一次取两页,t0的第3位为0时,0表示偶数物理页,1表示基数物理页 */
ori t0, t0, 0x4
xori t0, t0, 0x4
/* 只要PTEE的7:0位(flag位)和31:11位(PPN),
* 并且将31:11位(PPN)移到31:8位上(高4位清0),存入TLBELO0(1)寄存器。(见TLB表项寄存器)
*/
ld.w t1, t0, 0 /* 将t0内存的数据(PTE页表项)存到t1 */
srli.w ra, t1, 0xc
slli.w ra, ra, 0x8
andi ra, ra, 0xff
or t1, t1, ra
csrwr t1, LOONGARCH_CSR_TLBELO0
ld.w t0, t0, 0x4
srli.w ra, t0, 0xc
slli.w ra, ra, 0x8
andi t0, t0, 0xff
and.w t0, t0, ra
csrwr t0, LOONGARCH_CSR_TLBELO1
tlbwr /* 将TLBELO0/1写入TLB表项 */leave_load:
csrrd t0, EXCEPTION_KS0 /* 恢复现场 */
csrrd t1, EXCEPTION_KS1
csrrd ra, EXCEPTION_KS2
ertnvmalloc_load:
la.abs t1, swapper_pg_dir
b vmalloc_done_load
nopage_tlb_load:
dbar 0
csrrd ra, EXCEPTION_KS2
la.abs t0, tlb_do_page_fault_0
jirl $r0, t0, 0
SYM_FUNC_END(handle_tlb_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地址的内存处。
SYM_FUNC_START(handle_tlb_load)
csrwr ra, EXCEPTION_KS2csrrd传入出错虚拟地址(BADV)0x2134d4到r12寄存器,0x2134d4比0xc0000000小,故继续顺序执行。
lu12i.w ra, 0xc
slli.w ra, ra, 0x10
csrrd t0, LOONGARCH_CSR_BADV
bgeu t0, ra, vmalloc_load将PGD基址0xa23e1000传入r13寄存器,其内存的值为0xa23de000
csrrd t1, LOONGARCH_CSR_PGD先获取t0(BADV)0x002134d4的前10bits再偏移2位,得到PGD_OFFSET(0x000),加上PGD(0xa23e1000),存到r13寄存器,为0xa23e1000。
vmalloc_done_load:
/* Get PGD offset in bytes */
srli.w t0, t0, 0x16
andi t0, t0, 0x3ff
slli.w t0, t0, 0x2
add.w t1, t1, t0接着获取t0(0x002134d4)的中间10bits再偏移2位,得到PTE_OFFSET(213<<2 = 0x84c),最后加上t1寄存器内存处的值0xa23de000,得到地址0xa23de84c存到t0寄存器。
csrrd t0, LOONGARCH_CSR_BADV
ld.w t1, t1, 0
srli.w t0, t0, 0xc
andi t0, t0, 0x3ff
slli.w t0, t0, 0x2
add.w t0, t0, t1把r12内存处的值(*0xa23de84c= 0)写入r13寄存器,再刷新tlb。
label_smp_pgtable_load:
ld.w t1, t0, 0
tlbsrch因为_PAGE_PRESENT为0(页不存在),所以跳转到nopage_tlb_load处,即PC跳转到0xa02102b0处执行。
andi ra, t1, _PAGE_PRESENT
beq ra, zero, nopage_tlb_load接着恢复现场(r1=0x1213b8),跳转到tlb_do_page_fault函数处执行。
nopage_tlb_load:
dbar 0
csrrd ra, EXCEPTION_KS2
la.abs t0, tlb_do_page_fault_0
jirl $r0, t0, 0第二次tlb_load:
继续(gdb) c之后,可以看到第二次触发tlb_load的BADV也为0x2134d4。
继续(gdb) si调试到PTE+PTE_OFFSET可以得到r12为0x23de84c,此时该内存地址中有值,为0xff2309c,存到r13寄存器。
vmalloc_done_load:
/* Get PGD offset in bytes */
srli.w t0, t0, 0x16
andi t0, t0, 0x3ff
slli.w t0, t0, 0x2
add.w t1, t1, t0
csrrd t0, LOONGARCH_CSR_BADV
ld.w t1, t1, 0
srli.w t0, t0, 0xc
andi t0, t0, 0x3ff
slli.w t0, t0, 0x2
add.w t0, t0, t1
label_smp_pgtable_load:
ld.w t1, t0, 0
tlbsrch0xff2309c的_PAGE_PRESENT(第8位,判断该页是否存在)为1,并将_PAGE_VALID(第1位,判断该页是否有效)置1,即0xff2309d,将0xff2309d写回r12寄存器的内存地址处(*0xa23de84c=0xff2309d)。注意:该_PAGE_PRESENT、_PAGE_VALID都为页表的软件位,tlb只有硬件位。
andi ra, t1, _PAGE_PRESENT
beq ra, zero, nopage_tlb_load
ori t1, t1, _PAGE_VALID
st.w t1, t0, 0由于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位写忽略。
ori t0, t0, 0x4
xori t0, t0, 0x4
ld.w t1, t0, 0
srli.w ra, t1, 0xc
slli.w ra, ra, 0x8
andi t1, t1, 0xff
add.w t1, t1, ra
csrwr t1, LOONGARCH_CSR_TLBELO0
ld.w t0, t0, 0x4
srli.w ra, t0, 0xc
slli.w ra, ra, 0x8
andi t0, t0, 0xff
add.w t0, t0, ra
csrwr t0, LOONGARCH_CSR_TLBELO1csrwr t0, LOONGARCH_CSR_TLBELO1
接着恢复现场,从例外处理现场返回。
tlbwr
csrrd t0, LOONGARCH_CSR_KS0
csrrd t1, LOONGARCH_CSR_KS1
csrrd ra, EXCEPTION_KS2
ertn总结
BADV: 0x002134d4
*0xa23de848 = 0xff2209c, TLBLO0 = ff229c(实际为ff221c,第8位恒为0,其中第1位页有效位为0);
*0xa23de84c = 0xff2309d, TLBLO0 = ff239d(实际为ff231d,第8位恒为0,其中第1位页有效位为1);
do_raise_exception_err: 29 0
mips_cpu_do_interrupt enter: PC 0018c16c EPC 0018c16c RFEPC 0x000000000018c16c TLB load exception
mips_cpu_do_interrupt: PC a0214000 EPC 0x0018c16c cause 1
EXST 00010000 EXCFG 0x00071c1c BADVA 0x002134d4 ENV_PGDL 0xa23e1000 BADI 0x00000000 SYS_NUM 2381672 cpu 0 asid 0x1参考
四种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指令
ld.w rd, rj, 0 /* 将rj内存中的数据装入到rd寄存器 */
st.w rd, rj, 0 /* 将rd寄存器中的数据存入rj内存 */Load 用于把内存中的数据装载到寄存器中。 rd = *rj
Store用于把寄存器中的数据存入内存。 *rj = rd
DBAR 栅障指令
ERTN
TLBFILL
Page table bits
/* Page table bits */
......
#define _PAGE_VALID_SHIFT 0
#define _PAGE_DIRTY_SHIFT 1
#define _PAGE_PLV_SHIFT 2 /* 2~3, two bits */
#define _CACHE_SHIFT 4 /* 4~5, two bits */
#define _PAGE_GLOBAL_SHIFT 6
#define _PAGE_HUGE_SHIFT 6 /* HUGE is a PMD bit */
#define _PAGE_PRESENT_SHIFT 7
#define _PAGE_WRITE_SHIFT 8
#define _PAGE_PROTNONE_SHIFT 9
#define _PAGE_SPECIAL_SHIFT 10
#define _PAGE_HGLOBAL_SHIFT 12 /* HGlobal is a PMD bit */
#define _PAGE_PFN_SHIFT 12
#define _PAGE_PFN_END_SHIFT 31
......虚拟地址划分:最低的2GB(USEG)既缓存又分页;随后的512MB(KSEG0)缓存但不分页,对应物理地址的低 512MB(虚拟地址去掉高三位即为物理地址);接下来的512MB(KSEG1)既不缓存又不分页,同样对应物理地址的低 512MB(虚拟地址去掉高三位即为物理地址);最后的 1GB(KSEG2)对应物理既缓存又分页。
其他
tlb_load
SYM_FUNC_START(handle_tlb_store)
csrwr ra, EXCEPTION_KS2
lu12i.w ra, 0xc
slli.w ra, ra, 0x10
csrrd t0, LOONGARCH_CSR_BADV
bgeu t0, ra, vmalloc_store
csrrd t1, LOONGARCH_CSR_PGD
vmalloc_done_store:
/* Get PGD offset in bytes */
srli.w t0, t0, 0x16
andi t0, t0, 0x3ff
slli.w t0, t0, 2
add.w t1, t1, t0
csrrd t0, LOONGARCH_CSR_BADV
ld.w t1, t1, 0
srli.w t0, t0, 0xc
andi t0, t0, 0x3ff
slli.w t0, t0, 0x2
add.w t1, t1, t0
label_smp_pgtable_store:
ld.w t0, t1, 0
tlbsrch
srli.w ra, t0, _PAGE_PRESENT_SHIFT
andi ra, ra, ((_PAGE_PRESENT | _PAGE_WRITE) >> _PAGE_PRESENT_SHIFT)
xori ra, ra, ((_PAGE_PRESENT | _PAGE_WRITE) >> _PAGE_PRESENT_SHIFT)
bne ra, $r0, nopage_tlb_store
ori t0, t0, (_PAGE_VALID | _PAGE_DIRTY)
st.w t0, t1, 0
// beq t0, $r0, label_smp_pgtable_store
ori t1, t1, 4
xori t1, t1, 4
ld.w t0, t1, 0
srli.w ra, t0, 0xc
slli.w ra, ra, 0x8
andi t0, t0, 0xff
add.w t0, t0, ra
csrwr t0, LOONGARCH_CSR_TLBELO0
ld.w t1, t1, 4
srli.w ra, t1, 0xc
slli.w ra, ra, 0x8
andi t1, t1, 0xff
add.w t1, t1, ra
csrwr t1, LOONGARCH_CSR_TLBELO1
tlbwr
csrrd t0, LOONGARCH_CSR_KS0
csrrd t1, LOONGARCH_CSR_KS1
csrrd ra, EXCEPTION_KS2
ertn
vmalloc_store:
la.abs t1, swapper_pg_dir
b vmalloc_done_store
nopage_tlb_store:
dbar 0
csrrd ra, EXCEPTION_KS2
la.abs t0, tlb_do_page_fault_1
jirl $r0, t0, 0
SYM_FUNC_END(handle_tlb_store)tlb_modify
SYM_FUNC_START(handle_tlb_modify)
csrwr ra, EXCEPTION_KS2
/*
* The vmalloc handling is not in the hotpath.
*/
lu12i.w ra, 0xc
slli.w ra, ra, 0x10
csrrd t0, LOONGARCH_CSR_BADV
bgeu t0, ra, vmalloc_modify
csrrd t1, LOONGARCH_CSR_PGD
vmalloc_done_modify:
/* Get PGD offset in bytes */
srli.w t0, t0, 0x16
andi t0, t0, 0x3ff
slli.w t0, t0, 2
add.w t1, t1, t0
csrrd t0, LOONGARCH_CSR_BADV
ld.w t1, t1, 0
srli.w t0, t0, 0xc
andi t0, t0, 0x3ff
slli.w t0, t0, 0x2
add.w t1, t1, t0
label_smp_pgtable_modify:
ld.w t0, t1, 0
tlbsrch
srli.w ra, t0, _PAGE_WRITE_SHIFT
andi ra, ra, 1
beq ra, $r0, nopage_tlb_modify
ori t0, t0, (_PAGE_VALID | _PAGE_DIRTY)
st.w t0, t1, 0
// beq t0, $r0, label_smp_pgtable_modify
ori t1, t1, 4
xori t1, t1, 4
ld.w t0, t1, 0
srli.w ra, t0, 0xc
slli.w ra, ra, 0x8
andi t0, t0, 0xff
add.w t0, t0, ra
csrwr t0, LOONGARCH_CSR_TLBELO0
ld.w t1, t1, 0x4
srli.w ra, t1, 0xc
slli.w ra, ra, 0x8
andi t1, t1, 0xff
add.w t1, t1, ra
csrwr t1, LOONGARCH_CSR_TLBELO1
tlbwr
csrrd t0, LOONGARCH_CSR_KS0
csrrd t1, LOONGARCH_CSR_KS1
csrrd ra, EXCEPTION_KS2
ertn
vmalloc_modify:
la.abs t1, swapper_pg_dir
b vmalloc_done_modify
nopage_tlb_modify:
dbar 0
csrrd ra, EXCEPTION_KS2
la.abs t0, tlb_do_page_fault_1
jirl $r0, t0, 0
SYM_FUNC_END(handle_tlb_modify)tlb_refill
SYM_FUNC_START(handle_tlb_refill)
csrwr t0, LOONGARCH_CSR_KS0
csrwr t1, LOONGARCH_CSR_KS1
csrwr ra, EXCEPTION_KS2
csrrd t0, LOONGARCH_CSR_PGD
csrrd t1, LOONGARCH_CSR_BADV
srli.w t1, t1, 0x16
slli.w t1, t1, 0x2
add.w t0, t0, t1
ld.w t0, t0, 0
csrrd t1, LOONGARCH_CSR_BADV
srli.w t1, t1, 0xa
andi t1, t1, 0xff8
add.w t0, t0, t1
ld.w t1, t0, 0
srli.w ra, t1, 0xc
slli.w ra, ra, 0x8
andi t1, t1, 0xff
add.w t1, t1, ra
csrwr t1, LOONGARCH_CSR_TLBELO0
ld.w t1, t0, 0x4
srli.w ra, t1, 0xc
slli.w ra, ra, 0x8
andi t1, t1, 0xff
add.w t1, t1, ra
csrwr t1, LOONGARCH_CSR_TLBELO1
tlbfill
csrrd t0, LOONGARCH_CSR_KS0
csrrd t1, LOONGARCH_CSR_KS1
csrrd ra, EXCEPTION_KS2
ertn
SYM_FUNC_END(handle_tlb_refill) 


























