linux 内核页表 tlb,Linux中的mips64 tlb管理

本文详细介绍了Linux MIPS64处理器中TLB(Translation Lookaside Buffer)的管理,包括tlb条目、相关寄存器、指令以及异常处理。重点讨论了tlb miss时的refill handler流程,涉及到页表查找、pte更新和TLB写入等步骤。同时,文章提到了不同tlb异常的处理函数,如tlb load、store和modify,并指出它们如何进入do_page_fault处理。此外,还阐述了MIPS64中tlb异常处理的C代码生成机器码的实现,以及异常向量和异常处理过程中的栈帧保存和恢复。
摘要由CSDN通过智能技术生成

关于tlb的描述可参考mips run2以及mips64官方手册。

tlb条目:tlb entry

寄存器:entryHi, entryLo0, entryLo1, mask, index, wired

指令:tlbr, tlbwi, tlbwr, tlbprobe

使用时请注意配合hazard,防止遇险;

请先了解tlb entry与entryHi, entryLo0, entryLo1,mask的对应关系。

每个core以及每个core的vCPU都有独立的tlb(vCPU还可配置共享tlb,一般配置的都是独立的;L1[L2]cache也类似);

mips64 tlb miss入口地址为ebase+0x80,但使用了ebase偏移[0x0,0x100)的空间(ebase+0x0为mips32的tlb miss入口,Linux中被mips64利用了);可参考tlbex.c中的build_r4000_tlb_refill_handler()生成的tlb miss处理;

tlb miss后从页表中加载物理地址并生成tlb entry,之后可能会发生tlb invalid或tlb modify异常;tlb invalid包括tlb load/tlb store两种情况;

tlb modify表示tlb entry是有效的,但权限为只读;可用于COW机制;

tlb refill handler处理过程中可能再次会发生异常(如页表不在内存中)

tlb load/tlb store/tlb modify都会进do_page_fault();

huge tlb可用于提高性能,如对于大容量的数据访问,可将大块数据生成一个huge tlb entry;

还有一点重要的区别,tlb miss异常走的是ebase异常向量入口(,其它tlb异常走的是generic向量入口即ebase+0x180,见genex.S中的实现,通常的实现为except_vec3_generic;

tlb异常不会调用SAVE_SOME宏,关于此宏见后面的描述,但如果进入do_page_fault()就会调SAME_SOME,见tlbex-fault.S中关于tlb_do_page_fault宏的实现。

内核态的地址一般用的是unmapped区域,不会发生tlb miss,除非在内核态访问了mapped空间。

如内核态使用0x98000000_00000000开始处,cached unmapped区域;来个mips64的地址空间图:3ed55e181bc2e7f6c6a95e5fafb43010.png

由于mips处理器的型号比较多,tlb处理方式也不尽相同,TLB处理在Linux中的实现用的是C代码生成机器码的实现;主要位于tlb-r4k.c和tlbex.c中,其实也很简单,就是实现了mips64手册中关于指令机器码字段的解析,主要数据结构为enum opcode和struct insn insn_table[],build_insn()根据这个表生成指令机器码;另外,这套框架中还提供了一些label的生成方式,用于生成跳转偏移地址;

per_cpu_trap_init()调用下面函数构建出来各自的tlb异常处理:

tlb miss   -> build_r4000_tlb_refill_handler();

tlb load   -> build_r4000_tlb_load_handler();

tlb store  -> build_r4000_tlb_store_handler();

tlb modify -> build_r4000_tlb_modify_handler();

最后拷至[ebase+0x0, ebase+0x100)中。

这些代码,Linux标准实现中,是多个CPU共享的。

关于r4k,请参考mips run2:

MIPS III: The 64-bit instruction set introduced by the R4000.

介绍之前再来个mips寄存器的图:

k0,k1专用于中断和异常中;gp在内核中用于存放thread_info起始地址即kernel stack base;

62572ebbe740edd887904f1c795b7a44.png

1. tlb refill handler(处理tlb miss的函数)

current_cpu_data.cputype为CPU_CAVIUM_OCTEON为例进行讲解

在static __initdata u32 tlb_handler[128]中产生机器指令,最终拷至static __initdata u32 final_handler[64]中

(每一个异常向量允许32条指令,每条指令4 bytes)

build_r4000_tlb_refill_handler()

一、build_get_pmde64()

功能:获得bad address的pmd entry存入ptr中。(注:tmp即k0, ptr即k1)

从全局变量unsigned long pgd_current[NR_CPUS]中获取pgd指针 // 此值和CP0_CONTEXT值已在per_cpu_trap_init中的TLBMISS_HANDLER_SETUP初始化

C0_BADVADDR -> k0;

if k0 < 0, goto label_vmalloc;

C0_CONTEXT -> k1;

k1 >> 23; //C0_CONTEXT中存有当前smp id

lui(rel_hi) -> k0;

k1+k0 -> k1

C0_BADVADDR -> k0;

ld rel_lo+k1 -> k1; //得到当前smp id的pgd地址

build label: label_vmalloc_done以用于relocs重定向此地址(branch到某个lable的指令需要重定向到此目标地址);

(k0>>(PGDIR_SHIFT-3)) -> k0; // 从bad address中得到其在pgd中的偏移

(k0&(PTRS_PER_PGD-1)<<3) -> k0;

k1+k0 -> k1; // 获得其pgd的物理地址,下面开始找pmd

C0_BADVADDR -> k0; // 再次取得bad address,没办法,寄存器不够用

ld k1 -> k1; // k1中存放pmd的地址

(k0>>(PMD_SHIFT-3)) -> k0; // 从bad address中得到其在pmd中的偏移

(k0&(PTRS_PER_PMD-1)<<3) -> k0;

k1+k0 -> k1; // 获得bad address的pmd entry(即pte的地址)存在k1中

二、build_get_ptep()

C0_XCONTEXT -> k0;

[k1] -> k1; // 用的是lw指令

k0&0xff0 -> k0;

k1+k0 -> k1; // k1中存放的是pte的地址

三、build_update_entries()

偶数pte项 -> k0; // 用的是ld指令

奇数pte项 -> k1; // 用的是ld指令

k0 >> 6;

k1 >> 6; // 转化成entrylo格式

k0 -> C0_ENTRYLO0;

k1 -> C0_ENTRYLO1;

四、build_tlb_write_entry()

tlbwr; // 将C0_ENTRYLO0, C0_ENTRYLO1和C0_ENTRYHi写入random所指示的tlb项中

五、收尾工作

build label: label_leave;

eret;

2. tlb load

current_cpu_data.cputype为CPU_CAVIUM_OCTEON为例进行讲解

u32 __tlb_handler_align handle_tlbl[128]; // handle_tlbl最终在trap_init()中被设置成通用异常向量入口:

// set_except_vector(2, handle_tlbl);

从except_vec3_generic进入:

一、build_r4000_tlbchange_handler_head()

build_get_pmde64()将pmd存入k1中; // pte: k0, ptr: k1

C0_BADVADDR -> k0;

[k1] -> k1; // lw指令获得pmd中的pte指针

k0>>(PAGE_SHIFT+PTE_ORDER-PTE_T_LOG2) -> k0; // k0>>12+0-3即k0>>9

k0&((PTRS_PER_PTE-1)

k0+k1 -> k1; // 获得pte项的指针即页面的地址

build label: label_smp_pgtable_change;

[k1] -> k0; // ld指令获得偶数pte项

tlbp;

二、build_pte_present()

k0&(_PAGE_PRESENT|_PAGE_READ) -> k0;

k0^(_PAGE_PRESENT|_PAGE_READ) -> k0;

如果k0有值说明pte不在内存中或者不能读,则跳转至label_nopage_tlbl;

[k1] -> k0; // 在delay slot中重新load页面项,ld指令获得偶数pte项

三、build_make_valid()

将k0中的pte置上_PAGE_VALID和_PAGE_ACCESSED标志;

四、build_r4000_tlbchange_handler_tail()

build_update_entries();

build_tlb_write_entry(); // tlb_indexed,注意index寄存器在tlbp的时候已经设置,在tlb load场景中一定存在这样的index值;

// 在tlb refiller中,请注意使用tlbw_random;

eret;

五、

生成label_nopage_tlbl;

j tlb_do_page_fault_0; // tlbex-fault.S中的宏实现,最终调用do_page_fault();

// ra中存放ret_from_exception,do_page_fault()返回后会执行;

// ret_from_exception,在entry.S中; 如果配有强占,则#define __ret_from_irq ret_from_exception

附录:

stackframe.h:

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值