TLB基础建设
问题背景
用户对内存页配置页属性成功,但页属性未生效。
例如:对分配到的某块内存进行memset(),然后设置只读属性,再进行memset()时不触发写保护。
int ret; int x[DEFAULT_PAGE_SIZE] __attribute__((aligned(DEFAULT_PAGE_SIZE))) = { 0 }; memset(x, 0x10, sizeof(x)); ret = os_set_attribute(x, sizeof(x); USER_ONLY_READ); if (ret != 0) { printk("%s[%d]: os_set_attribute() failed!!!\n", __FUNCTION__, __LINE__); } memset(x, 0x10, sizeof(x)); return 0 |
简介
ls2k芯片采用MIPS架构,32位,可参考GS264文档中对核心寄存器介绍,本问题的根因是由于配置页属性后未及时刷新TLB,导致在未触发换页等置换页表属性的操作时,TLB中的内容与以配置到内存中的页属性不同步引起。ls2k与TLB相关的主要是协处理器C0中的寄存器,均在GS264。
页属性的检测,读写权限等是通过MMU实现的,MMU在将虚拟地址(逻辑地址)映射物理地址过程中会检测虚拟内存页属性。
ls2k芯片TLB表项总共64项,可通过index寄存器,结合tlbr/tlbwi/tlbp/tlbwr等指令对指定TLB表项进行读写操作。
MIPS架构TLB表项用户是可观察的,x86架构TLB似乎不透明。
ls2k芯片TLB表项由固定TLB表项和随机TLB表项两部分组成,总计64项,固定和随机的分界线通过wired寄存器低位划分。
TLB亦称为快表 (页表缓冲)(转换后备缓冲器)TLB:translation lookaside buffer。
刷新TLB涉及的寄存器
INDEX: 是TLB访问指令tlbr/tlbwi/tlbp/tlbwr的基础,访问INDEX指向的表项。TLB表项通过虚地址访问,TLB表项总计64项,虚地址为0-63。
ENTRYLO0/ENTRYLO1:低位存放页属性,高位存放物理页框号。
WIRED:划分固定TLB表项和随机TLB表项数量。TLB表项总计64项,以WIRED值为分隔,0-WIRED是固定TLB,WIRED-63是随机TLB。固定TLB:不将其从TLB中换出,随机TLB:可能在缺页时被置换。
ENTRYHI:低位存放ASID,高位存放虚拟页表号。
STATUS:控制寄存器,控制CPU中断等。
刷新TLB涉及的指令
tlbr:读取INDEX指定TLB表项内容到寄存器。
tlbwi:将寄存器内容写入INDEX指定TLB表项。
刷新指定TLB流程
函数原型:
/* v_addr[in]: 虚拟地址,用于计算虚拟页号,通过与ENTRYHI中的虚拟页号对比,实现刷新指定虚拟页属性。 attr[in]: 虚拟页属性。 */ void mmu_update_tlb(uint32 v_addr, uint32 attr); |
虚拟地址装虚拟页号VPN:
#define TLB_VPN_MASK 13 #define TLB_VADDR_TO_VPN(v_addr) ((v_addr) >> TLB_VPN_MASK) |
寄存器ENTRYHI值转VPN:
// 虚拟页地址是32bit,ENTRYHI是64bit寄存器 #define TLB_ENTRYHI_TO_VPN(entryhi) ((uint32)((entryhi) >> TLB_VPN_MASK) & 0x07ffffff) |
刷新TLB流程:
void mmu_update_tlb(uint32 v_addr, uint32 attr) { // 禁止当前CPU所有中断; // 保存当前线程ASID // 预置参数 // 遍历TLB项 // 如果v_addr的虚拟页号与ENTRYHI寄存器中的虚拟页号一致 // 更新该TLB // 恢复当前线程ASID // 恢复当前CPU所有中断; } |
注:可参考Linux配置页属性后的流程,以及刷新MIPS架构TLB流程。
文件:tlb-r3k.c (arch\mips\mm)
结语
TLB不同步是我初入系统领域解决的首个较难问题,一周都在查mmu/cache/内存/tlb/mips/ls2k寄存器/虚拟内存等各种资料,得以实现刷新指定TLB表项,以及新增抓取所有TLB项到内存块的接口,在此过程中还用纯汇编写了一份清理所有TLB表项的接口(最初的解决方案,后来认为设置某页属性清掉整个随机TLB,太暴力以及影响系统性能就否定了,但接口保留了下来。)
备注
配置页属性可以参考Linux的change_protection()接口,配置页属性简单理解就是内存页的读写权限修改,当然不同cpu也会有差异,如:mips的还有工作模式之类的属性。
unsigned long change_protection(struct vm_area_struct *vma, unsigned long start, unsigned long end, pgprot_t newprot, int dirty_accountable, int prot_numa) { unsigned long pages; if (is_vm_hugetlb_page(vma)) pages = hugetlb_change_protection(vma, start, end, newprot); else pages = change_protection_range(vma, start, end, newprot, dirty_accountable, prot_numa); return pages; } |