分类: LINUX
2014-12-08 14:16:39
Written by leeming
Paging_init中重要函数附录:
由于阅读直观起见,在paging_init函数中只是放了一次嵌套的代码,并没有继续嵌套代码,但是还有一个函数对于我们来说也是非常重要的,它就是在bootmem_init(mi); devicemaps_init(mdesc);这两个函数中都涉及的create_mapping,它也是创建页表的直接操作者。
1.void __init create_mapping(struct map_desc *md)
{
unsigned long virt, length;
int prot_sect, prot_l1, domain;
pgprot_t prot_pte;
unsigned long off = (u32)__pfn_to_phys(md->pfn);
//只有虚拟地址处于用户空间(只能是中断向量表)
//但又不是映射为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);
}
//获取相应类型的页表项参数,具体的参见paging_init
//函数中的build_mem_type_table()函数
domain = mem_types[md->type].domain;
prot_pte = __pgprot(mem_types[md->type].prot_pte);
//并没有对域的权限做限定,系统其实就用了几个域:内核0,用户1,io3,好像还有个table(页表域),对这几个域的设定(协处理器的c3配置)已经在系统启动的时候head.s中已经完成。
prot_l1 = mem_types[md->type].prot_l1 | PMD_DOMAIN(domain);
prot_sect = mem_types[md->type].prot_sect | PMD_DOMAIN(domain);
/*
* Catch 36-bit addresses
*/
//以下已经超出普通嵌入式应用,忽略
if(md->pfn >= 0x100000) {
if(domain) {
printk(KERN_ERR "MM: invalid domain in supersection "
"mapping for 0x%08llx at 0x%08lx\n",
__pfn_to_phys((u64)md->pfn), md->virtual);
return;
}
if((md->virtual | md->length | __pfn_to_phys(md->pfn))
& ~SUPERSECTION_MASK) {
printk(KERN_ERR "MM: cannot create mapping for "
"0x%08llx at 0x%08lx invalid alignment\n",
__pfn_to_phys((u64)md->pfn), md->virtual);
return;
}
/*
* Shift bits [35:32] of address into bits [23:20] of PMD
* (See ARMv6 spec).
*/
off |= (((md->pfn >> (32 - PAGE_SHIFT)) & 0xF) << 20);
}
virt = md->virtual;
off -= virt;
length = md->length;
//不是合法的一级描述符,并且虚拟地址,偏移量,长度 有不是
if (mem_types[md->type].prot_l1 == 0 &&
(virt & 0xfffff || (virt + off) & 0xfffff || (virt + length) & 0xfffff)) {
printk(KERN_WARNING "BUG: map for 0x%08lx at 0x%08lx can not "
"be mapped using pages, ignoring.\n",
__pfn_to_phys(md->pfn), md->virtual);
return;
}
//如果长度不是
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;
}
/*此处删除高版本arm的代码*/
/*
* A section mapping covers half a "pgdir" entry.
*/
while (length >= (PGDIR_SIZE / 2)) {
//prot_sect是段描述符
alloc_init_section(virt, virt + off, prot_sect);
virt += (PGDIR_SIZE / 2);
length -= (PGDIR_SIZE / 2);
}
//如果大小不是刚好
while (length >= PAGE_SIZE) {
alloc_init_page(virt, virt + off, prot_l1, prot_pte);
virt += PAGE_SIZE;
length -= PAGE_SIZE;
}
}
2. 关联函数:
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 c
*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