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 0

tlb_load *(0xa0210000 + 0x200)

gdb调试:

b *0xa0210200
x /32i 0xa0210200

trap_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
        ertn
vmalloc_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_KS2

csrrd传入出错虚拟地址(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
        tlbsrch

0xff2309c的_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_TLBELO1

csrwr 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)

标签: linux, kernel, tlb

添加新评论