分类: LINUX
2013-07-21 22:57:13
原文地址:页表创建create_mapping 作者:lwchsz
arch/arm/mm/mmu.c:
void __init create_mapping(struct map_desc *md)
{
unsigned long phys, addr, length, end;
const struct mem_type *type;
pgd_t *pgd;
//只有虚拟地址处于用户空间(只能是中断向量表)
//但又不是映射为0地址的中断向量表时报错
//只能给系统空间 或者 中断向量 所在的空间创建映射,绝对不可给用户虚拟空间创建映射。
if (md->virtual != vectors_base() && md->virtual < TASK_SIZE) {
printk(KERN_WARNING "BUG: not creating mapping for "
"0x%08llx at 0x%08lx in user region\n",
__pfn_to_phys((u64)md->pfn), md->virtual);
return;
}
//当类型为MT_DEVICE或者MT_ROM,但是他们的虚拟地址
//又处于vmalloc的空间(c0000000----d0000000)
//VMALLOC_END是在我们include/asm-arm/arch-sep4020/vmalloc.h中定义
//所以我们在对寄存器进行静态映射其实也是有限制的
if ((md->type == MT_DEVICE || md->type == MT_ROM) &&
md->virtual >= PAGE_OFFSET && md->virtual < VMALLOC_END) {
printk(KERN_WARNING "BUG: mapping for 0x%08llx at 0x%08lx "
"overlaps vmalloc space\n",
__pfn_to_phys((u64)md->pfn), md->virtual);
}
type = &mem_types[md->type]; //获取memory类型
/*
* Catch 36-bit addresses
*/
//以下已经超出普通嵌入式应用,忽略
if (md->pfn >= 0x100000) {
create_36bit_mapping(md, type);
return;
}
addr = md->virtual & PAGE_MASK; //得到虚拟地址
phys = (unsigned long)__pfn_to_phys(md->pfn); //得到物理地址
length = PAGE_ALIGN(md->length + (md->virtual & ~PAGE_MASK)); //映射长度
if (type->prot_l1 == 0 && ((addr | phys | length) & ~SECTION_MASK)) {
printk(KERN_WARNING "BUG: map for 0x%08lx at 0x%08lx can not "
"be mapped using pages, ignoring.\n",
__pfn_to_phys(md->pfn), addr);
return;
}
pgd = pgd_offset_k(addr);
end = addr + length;
do {
unsigned long next = pgd_addr_end(addr, end);
alloc_init_section(pgd, addr, next, phys, type);
phys += next - addr;
addr = next;
} while (pgd++, addr != end);
}
关联函数:
alloc_init_section(unsigned long virt, unsigned long phys, int prot)
{
//pmdp是一级页表描述符的地址
pmd_t *pmdp = pmd_off_k(virt);
if (virt & (1 << 20))
pmdp++;
//向一级页表描述符地址中写入一级页表描述符
//一级页表描述符:12bit phys | 20 bit prot
//对于内存来说打印信息为: pmdp is c0007000, value is 3000041e
// pmdp is c000707c, value is 31f0041e
*pmdp = __pmd(phys | prot);
flush_pmd_entry(pmdp);
}
alloc_init_page(unsigned long virt, unsigned long phys, unsigned int prot_l1, pgprot_t prot)
{
pmd_t *pmdp = pmd_off_k(virt);
pte_t *ptep;
if (pmd_none(*pmdp)) {
//对于粗颗粒小页变换,一级页表描述符是和二级页表的基地址有关的
//所以这里除了有protl1还有ptep的物理地址
ptep = alloc_bootmem_low_pages(2 * PTRS_PER_PTE *
sizeof(pte_t));
__pmd_populate(pmdp, __pa(ptep) | prot_l1);
}
//根据pmd找pte项(ptep用通俗的语言来说就是二级页表描述符地址)
ptep = pte_offset_kernel(pmdp, virt);
//ptep是二级页表描述符地址,pfn_pte是根据虚拟地址和配置选项得到二级页表描述符
//set_pte函数是在arch/arm/mm/proc_720t.s中实现的
set_pte(ptep, pfn_pte(phys >> PAGE_SHIFT, prot));
}
/*
* Function: arm720_set_pte(pte_t *ptep, pte_t pte)
* Params : r0 = Address to set
* : r1 = value to set
* Purpose : Set a PTE and flush it out of any WB cache
*/
//其实这一步只是往一个二级页表描述符地址里面存放一个二级页表描述符,一个str指令就能完成的,但正是因为之前所说的在linux中有两种pte机制(linux,硬件),所以在配置完了linux的,还需要配置硬件,因此在这里第一行代码之下,都是为了实现硬件的pte的设置。
.align 5
ENTRY(cpu_arm720_set_pte)
str r1, [r0], #-2048 @ linux version
eor r1, r1, #L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_WRITE | L_PTE_DIRTY
bic r2, r1, #PTE_SMALL_AP_MASK
bic r2, r2, #PTE_TYPE_MASK
orr r2, r2, #PTE_TYPE_SMALL
tst r1, #L_PTE_USER @ User?
orrne r2, r2, #PTE_SMALL_AP_URO_SRW
tst r1, #L_PTE_WRITE | L_PTE_DIRTY @ Write and Dirty?
orreq r2, r2, #PTE_SMALL_AP_UNO_SRW
tst r1, #L_PTE_PRESENT | L_PTE_YOUNG @ Present and Young
movne r2, #0
str r2, [r0] @ hardware version
mov pc, lr