转换步骤:
1. 首先确定转换地址是“程序地址”还是“数据地址”,根据不同,E500会查找L1MMU的I-L1VSP,I-L1TLB4K和D-L1VSP,D-L1TLB4K.
2. 在L1 MMU中根据当前虚拟地址,查找物理地址RPN,命中则返回RPN(Real Page Number),结束转换。否则查找L2 MMU。
3. 在L2 MMU中的TLB1,TLB0根据虚拟地址查找物理地址RPN,命中则返回。
4. TLB0和TLB1都没命中则产生ITLB或DTLB Miss异常,在异常处理程序中搜索在物理内存中的PTE表,获得RPN,并对TLB0进行更新
5. 如果PTE表也没有合适的RPN,则进一步分析虚拟地址,page_fault函数处理这种情况。
powerpc 32位有关页表的常量定义:
PAGE_SHIFT: 12
PAGE_MASK: 0xffff f000
PAGE_SIZE: 0x0000 1000
PTE_SHIFT: 10
PMD_SHIFT: 22
PMD_SIZE: 0x0040 0000
PMD_MASK: 0xffc0 0000
PUD_SHIFT: 22
PUD_SIZE: 0x0040 0000
PUD_MASK: 0xffc0 0000
PGDIR_SHIFT: 22
PGDIR_SIZE: 0x0040 0000
PGDIR_MASK: 0xffc0 0000
linux 支持的结构:
|<-------------------- BITS_PER_LONG ------------------------------------>|
|<--------------------- PGDIR_SHIFT ---------------------->|
|<---------------- PUD_SHIFT ----------------->|
|<---------- PMD_SHIFT -------->|
|<- PAGE_SHIFT->|
-------------------------------------------------------------------------
| PGD | PUD | PMD | PTE | Offset |
-------------------------------------------------------------------------
实际上PowerPC E500支持的结构:
|<-------------------- BITS_PER_LONG ------------------------------------>|
|<-------- PGDIR_SHIFT ---------------------->|
|<---------- PUD_SHIFT ---------------------->|
|<------------------------ PMD_SHIFT -------->|
|<-- PTE_SHIFT -->|
|<------- PAGE_SHIFT ------>|
-------------------------------------------------------------------------
| PGD <10b> | PTE <10b> | <12b> Offset |
-------------------------------------------------------------------------
PTE表的开始建立(内核部分):
主要在下面函数中建立映射表:
分析:以虚拟地址(指程序使用的有效地址,VA表示)f1005000 映射物理地址ffe42000为例分析:
- int map_page(unsigned long va, phys_addr_t pa, int flags)
-
{
-
pmd_t *pd;
-
pte_t *pg;
-
int err = -ENOMEM;
-
-
/* Use upper 10 bits of VA to index the first level map */
-
pd = pmd_offset(pud_offset(pgd_offset_k(va), va), va);
-
/* Use middle 10 bits of VA to index the second-level map */
-
pg = pte_alloc_kernel(pd, va);
-
if (pg != 0) {
-
err = 0;
-
/* The PTE should never be already set nor present in the
-
* hash table
-
*/
-
BUG_ON((pte_val(*pg) & (_PAGE_PRESENT | _PAGE_HASHPTE)) &&
-
flags);
-
set_pte_at(&init_mm, va, pg, pfn_pte(pa >> PAGE_SHIFT,
-
__pgprot(flags)));
-
}
-
return err;
-
}
1. pmd_offset函数是取得PTE表虚拟地址的偏移,由于32位PowerPC没有使用PUD与PMD部分,所以pgd_offset_k的返回值就是PTE表的偏移。
#define pgd_offset_k(address) pgd_offset(&init_mm, address)
-> #define pgd_offset(mm, address) ((mm)->pgd + pgd_index(address))
-> #define pgd_index(address) ((address) >> PGDIR_SHIFT)
-> #define PGDIR_SHIFT (PAGE_SHIFT + PTE_SHIFT) // = 12 + 10 = 22
由于是映射kernel部分,所以用pgd_offset_k,即init_mm为PGD表。
否则用pgd_offset,(mm)->pgd为PGD表来计算。
计算结果为:(设:init_mm->pgd = 0xc064 7000) + 0xf1005000 >> 22 = 0xc064 7000 + 0x3c4 * 4
因为pgd每个entry占4个字节,所以后面的偏移要*4. = 0xc0647f10
2. pte_alloc_kernel
(pd
, va
)
#define pte_alloc_kernel(pmd, address) \
((unlikely(!pmd_present(*(pmd))) && __pte_alloc_kernel(pmd, address))? \
NULL: pte_offset_kernel(pmd, address))
#define pmd_present(pmd) (pmd_val(pmd) & _PMD_PRESENT_MASK)
#define pte_offset_kernel(dir, addr) \
((pte_t *) pmd_page_vaddr(*(dir)) + pte_index(addr))
#define pmd_page_vaddr(pmd) \
((unsigned long) (pmd_val(pmd) & PAGE_MASK))
#define pte_index(address) \
(((address) >> PAGE_SHIFT) & (PTRS_PER_PTE - 1))
#define PTRS_PER_PTE (1 << PTE_SHIFT) = 1 << 10
由此可以看出此函数功能为:
如果与虚拟地址相关联的PMD项为空则分配一页,否则不分配,返回PTE表项的地址。
注意此处返回值是存放PTE表页面的页面虚拟地址,此地址有两部分构成:虚拟地址的高20位(低12位总是为0,因为4K对齐的关系,并且此12位用来存放PTE偏移)+PTE偏移(低12位)此地址的内容为物理地址高20位+12位的标志位。
3 set_pte_at函数相当于把物理地址写入PTE表项。
写入的内容为pfn_pte计算结果,即物理地址的PFN(占20位)+flags(占12位)。即ffe42+flags
阅读(2673) | 评论(0) | 转发(1) |