EntryHi:
VPN2(虚拟页号):这部分是程序地址的高位(低位的页内地址0-13位略去了)。0-12位属于页内地址,程序地址的第13位在两个可能的输出值之间进行选择。(注意:当重填异常发生后,硬件自动设置该寄存器的值;当想要写入另外的tlb表项或检测tlb时,只能手工设置)
ASID(地址空间标识符):这部分时操作系统对当前地址空间的标识。异常不会影响该域。
R(64位版本):这是一个地址区域选择符。可以看作是EntryHi(VPN2)的更多位,只不过是64位MIPS虚拟地址的最高位。
PageMask:设置tlb域映射的页的大小。
EntryLo:
PFN:这部分是和VPN2域对应的地址转换的物理地址的高位部分。
C:一个三位的域,用来设置“高速缓冲算法”。普遍包括:“不用高速缓冲”“可用高速缓冲但不要一致性”
D:写允许位。置位位1允许写入,0导致该地址转换的写入操作发生自陷。
V:有效位,为0则对该项的任何操作都导致异常。
G:当tlb表项G位置位时,tlb项将仅仅在VPN上匹配,而不管tlb项的ASID域是否匹配EntryHi中的值。这就提供了一种有效的机制来实现所有进程共享的地址空间部分。(注意:两个EntryLo寄存器在tlb表项中的G位应该一致,要不就会出现问题)
Context(PTEBase):该域只是保存驻留内存页表 的起始地址的高位(适当的对齐)。该起始地址必须选的在第22位及以下为0--即8MB的边界。
Context(BadVPN2)/XContext(BadVPN2):在tlb相关异常之后,该处保存页地址,正好就是BadVAddr的高位。
XContext(PTEBase):8GB边界对齐
XContext(R):段选择符。
tlb指令:
tlbr #read TLb entry at index
tlbwi #write Tlb entry at index
tlbwr #write tlb entry selected by random
tlbp #tlb lookup
硬件就介绍到这里:
现在说一下为什么需要进行存储器地址转换:
1,隐藏和保护
用户特权级的程序只能访问程序地址位于kuseg存储区的数据,这样一个程序只能到达操作系统允许的内存去。此外,可以单独指定每个页面为可写或写保护,操作系统甚至可以停止一个意外覆盖自己代码的程序
2,给程序分配连续的存储空间
有了MMU,操作系统可以从物理分散页面构造连续的程序空间,n允许我们从一个简单的固定大小页的缓冲池中分配存储器
3,扩展地址范围
有些cpu不能直接访问他们全部的物理存储器范围。MIPS32 CPU尽管时32位体系结构,对地址映射的安排为地址空间窗口kseg0 kseg1为物理储存器的低512M。如果需要更大范围的存储器映射,必须经过MMU。
4,使存储器映射适应你的程序
有了MMU,你的程序可以使用适合自己的地址。在大的操作系统里,可能同一个程序有许多拷贝同时运行,让这些拷贝都用同样的程序地址要容易的多
5,按需调页
程序运行时所有的内存就好像已经分配了一样,但是操作系统h实际上只在n真正用到时才给出。访问未分配的地址区将产生一个异常,然后由操作系统处理;操作系统加载适当的数据到存储器让程序运行
6,重定位
程序入口点和预先声明的数据的地址在程序编译/构建时是固定的,可对于用共享库构建的程序的位置无关代码就不对了。MMU允许程序在物理地址的任意地址运行。
tlb表项格式:
这里看几个图,依照图示进行说明:
图1:
图2:
图3:
*************************重点***********************
mmu地址转换过程:
1,
虚拟地址分为两部分,最低有效位部分(通常是低12位)不做转换直接传递---这样转换总是以页(4K)为单位进行
2,
较高有效位,即VPN,与当前线程的ASID拼接在一起形成一个唯一的页地址。
3,
然后在tlb看是否由该页的转换页,如果有,就给出高位的物理地址,我们就得到了要用的地址(如果G位打开,就不需要比较ASID)
4,
通常有些额外的位与PFN放在一起用来控制访问权限。
5,
如果tlb没有匹配的项,系统必须找出(从驻留主存的页表信息)或者创建适当的页表项,加载进tlb然后再次运行转换过程。
伪代码示例:
tlb_refill:
lui k1,%hi(pgd_current) #页表基地址
mfc0 k0,c0_badvaddr #获取发生tlb的指令地址
lw k1,%lo(pgd_current)(k1) #
srl k0,k0,24
sll k0,k0,2 #shift and mask VA for PGD index
addu k1,k1,k0 #得到一个PGD表项的指针
mfc0 k0,c0_context #再次从context得到va
lw k1,0(k1) #得到一级页表
srl k0,k0,1 #context是2x64bit的入口,我们需要32位
andi k0,k0,0xff8 #mask得到PTEBase
lw k0,0(k1) #从一级页表load tlb entry
lw k1,4(k1) #
srl k0,k0,0x6 #mask 标志位
mtc0 k0,c0_entrylo0 #放入entrylo0中
srl k1,k1,0x6
mtc0 k1,co_entrylo1
ehb
tlbwr #随机写入tlb中
eret
tlbmiss32: #32位cpu
.set noreorder
.set noat
TLBmiss32:
mfc0 k1,c0_context #加载页表指针
lw k0,0(k1) #加载页表项
lw k1,8(k1) #
mtc0 k0,c0_entrylo0 #放入对应的寄存器
mtc0 k1,c0_entrylo1 #
ehb
tlbwr #随机写入tlb
eret
.set at
.set reorder
tlbmiss64 #64位cpu
.set noreorder
.set noat
TLBmiss64:
mfc0 k1,c0_xcontext
lw k0,0(k1)
lw k1,8(k1)
mtc0 k0,c0_entrylo0
mtc0 k1,c0_entrylo1
ehb
tlbwr
eret
.set at
.set reorder