看arm linux mm的一些总结和问题

来源:互联网 发布:微课编辑软件 编辑:程序博客网 时间:2024/05/22 15:48
最近在看arm linux 的mm部分,看的是2.6.8.1,芯片是INTEL PXA255,参考资料有arm linux演艺、《情景分析》等。一遍看下来只能说似懂非懂。这里有几个基础的问题,大家看看我的理解是否正确,另外还有一个小问题我没有理解。 

arm 的mmu支持4K,16K,64K等几种页表和1M的段表(section),arm linux用的应该是4K(small page)页表和1M的section。 
1M的段表地址比较简单, 
[31:20] table index, 2048 
[19:0] section index, 1M 

4K页表的地址在ARM里是这样设置的: 
[31:20] first-level table index,4096 
[19:12] second-level table index,256 
[11:0] page index 

而在arm linux里PGD, PMD, PT的划分又如下: 
[31:21] PGD, 2048 
[20] PMD, 2 
[19:12] PT, 512 
但这里的PMD又形同虚设,因为PT一次总是操作相隔256的2的表项,高的为linux,低的为H/W 
因此 
PTRS_PER_PGD = 2048 
PTRS_PER_PTE = 512 
PERS_PE_PMD = 1 

地址转换的时候,先找到PGD,这是存放在CP15 C2里面的,tssk->mm->pgd里存放每个任务的pgd。switch_mm的时候放入CP15 C2 
cpu_switch_mm(next->pgd, next); 
mcr p15, 0, r0, c2, c0, 0 @ load page table pointer, r0 = next->pgd 

硬件根据CP15 C2的内容找到一个first-level descriptor table,也就是linux里的PGD,其高18位加上虚拟地址va的[31:20] (first-level table index),读取这个地址的内容(第一次读取内存),得到first-level descriptor。 
如果first-level descriptor的最低2位是10,表示是section descriptor,则高12位加上va[19:0]就是物理地址。 
如果first-level descriptor的最低2位是01,表示是Coarse page table descriptor,高22位(second-level table的地址)加上va[19:12](second-level table index),这个地址读到second-level descriptor(第二次读取内存),高20位加上va[11:0],就是最后的物理地址。 

以上工作都是硬件自动完成的,arm linux 要做的,就是设置PGD表(first-level table)和页表(second-level table)。 

比如内核的PGD,放在swapper_pg_dir数组的下标0项,同时保存在init_task->mm->pgd中。 

但是看到分配页表的函数create_mapping,有个小问题。 

static void __init create_mapping(struct map_desc *md) 

... 

while ((virt & 0xfffff || (virt + off) & 0xfffff) && length >= PAGE_SIZE) { 
alloc_init_page(virt, virt + off, prot_l1, prot_pte); 

virt += PAGE_SIZE; 
length -= PAGE_SIZE; 
}// 如果地址不是1M对齐的,则先给头上一部分分配页表,按页大小(4K)分配、填充页表 

while (length >= (PGDIR_SIZE / 2)) { 
alloc_init_section(virt, virt + off, prot_sect); 

virt += (PGDIR_SIZE / 2); 
length -= (PGDIR_SIZE / 2); 
}// 这里的地址已经是1M对齐的了,设置PGD对应项,按1M分配、填充段表 

while (length >= PAGE_SIZE) { 
alloc_init_page(virt, virt + off, prot_l1, prot_pte); 

virt += PAGE_SIZE; 
length -= PAGE_SIZE; 
}// 1M对齐剩下的尾巴,分配页表 

... 


这里都要设置descriptor的保护位,也就是没有被地址用到的那些位,表示一些状态属性。 
alloc_init_section只需要段保护位prot_sect就可以了,这个没什么问题,比如我用的板子, 
prot_sect = 0x42e 
// 010000101110 
// section descriptor 
// cache and buffer 
// domain = 1,kernel 
// ap = 01, read/write 

而alloc_init_page由于有first-level table 和 second-level table,需要两个保护位,分别是prot_l1, prot_pte 
奇怪的是,代入的两个数值 
prot_pte = 0x0 
// 没有任何设置 
prot_l1 = 0x20 
// domain = 1,kernel 
因为类型为MEMORY的存储设备,默认的prot_pte和prot_l1都是0。 

虽然我用的PXA255开发板,地址是1M对齐的,且后面没有多余的地址,不需要前后两段分配页表(相信多数的处理器和开发板也是这样),但是既然有这样的代码,就应该赋给正确的值。 


后面还有一段,映射中断向量表所在的区域 
init_maps->physical = virt_to_phys(init_maps); 
init_maps->virtual = vectors_base(); 
init_maps->length = PAGE_SIZE; 
init_maps->type = MT_VECTORS; 

create_mapping(init_maps); 
这里面的保护位就没什么问题 
prot_pte = 0xcb 
// 11001011 
// Extended small page base address 
// cache not buffer 
// read only? 
// TEX = 11 
prot_l1 = 0x1 
// Coarse page table base address 
// domain = 0, user 

哪位研究过arm linux mm的朋友能解释一下? 


下面是对上面用到的几个函数的展开分析。 
static inline void 
alloc_init_section(unsigned long virt, unsigned long phys, int prot) 

pmd_t *pmdp; 

pmdp = pmd_offset(pgd_offset_k(virt), virt); 
// 展开为(pmd_t *)init_mm->pgd + (virt) >> 21, the high 11bit of va 
// 得到虚拟地址virt 在 first-level table中的位置 

if (virt & (1 << 20)) 
pmdp++; // linux 的 pmd 

set_pmd(pmdp, __pmd(phys | prot)); 
// *pmdp = phys | prot 




static inline void 
alloc_init_page(unsigned long virt, unsigned long phys, unsigned int prot_l1, pgprot_t prot) 

pmd_t *pmdp; 
pte_t *ptep; 

pmdp = pmd_offset(pgd_offset_k(virt), virt); 
// 展开为(pmd_t *)init_mm->pgd + (virt) >> 21, the high 11bit of va 
// 得到虚拟地址virt 在 first-level table中的位置 


if (pmd_none(*pmdp)) { // 如果pmdp没设置过,分配一个second_level table 
unsigned long pmdval; 
ptep = alloc_bootmem_low_pages(2 * PTRS_PER_PTE * 
sizeof(pte_t)); 
// alloc second_level table 

pmdval = __pa(ptep) | prot_l1; 
pmdp[0] = __pmd(pmdval); 
pmdp[1] = __pmd(pmdval + 256 * sizeof(pte_t)); 
// 在这里填充first-level table对应项 
flush_pmd_entry(pmdp); 

ptep = pte_offset_kernel(pmdp, virt); 

set_pte(ptep, pfn_pte(phys >> PAGE_SHIFT, prot)); 
// str r1, [r0] 
// *ptep = pfn_pte(phys >> PAGE_SHIFT, prot),物理地址高20位加上保护位,放到一个second_level table 中的对应项 
原创粉丝点击