全部博文(685)
分类: LINUX
2015-01-06 11:33:34
1、在ARM-Linux内核的代码中,页面大小采用4KB,区段大小为1MB,并且使页面目录PGDIR对应于ARM的首层映射表,而中间目录PMD则设置成与PGDIR等同,这样就把概念上的三层映射合并成了物理上的二层映射。
#define PAGE_SHIFT 12
#define PAGE_SIZE (1UL << PAGE_SHIFT)
#define PMD_SHIFT 20
#define PGDIR_SHIFT 20
#define PMD_SIZE (1UL << PMD_SHIFT)
#define PGDIR_SIZE (1UL << PGDIR_SHIFT)
这就以为着,页面的大小为4KB;而一个PGDIR和一个PMD所覆盖的区间为1MB。
2、ARM-Linux内核怎样建立起具体页面映射
内核中有个函数create_mapping(),其作用就是为一个给定的区间建立页面映射。调用这个函数前要先准备好一个map_desc数据结构,其源码如下:
struct map_desc {
unsigned long virtual;虚拟地址起点
unsigned long pfn;物理地址起点
unsigned long length;长度
unsigned int type;说明这个区间所属的域,以及是否可读、写、可高速缓存等属性
};
ARM-linux的MMU允许使用16个不同的域,但内核只使用了3个:
* DOMAIN_IO - domain 2 includes all IO only
* DOMAIN_USER - domain 1 includes all user memory only
* DOMAIN_KERNEL - domain 0 includes all kernel memory only
#define DOMAIN_KERNEL0
#define DOMAIN_TABLE 0
#define DOMAIN_USER 1
#define DOMAIN_IO 2
注:这里的值如DOMAIN_USER都只是编号,说明这个域的访问控制位段在域访问控制寄存器中的位置,就好像下标,而控制着具体域的访问方式的是位段的值。每个域访问控制位段包含两位,所以有4种:
3、这样,一个map_desc数据结构就完整描述了一个内存区间,或者说板块的映射,调用create_mapping()就以此结构指针为调用参数。
/*
* Create the page directory entries and any necessary
* page tables for the mapping specified by `md'. We
* are able to cope here with varying sizes and address
* offsets, and we take full advantage of sections and
* supersections.
*/
void __init create_mapping(struct map_desc *md)
{
unsigned long phys, addr, length, end;
const struct mem_type *type;
pgd_t *pgd;
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;
}
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];得到映射类型和属性
/*
* 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);以1MB逐段为单位建立单层映射。
phys += next - addr;
addr = next;
} while (pgd++, addr != end);
}
4、对于1MB的区段,这里采用的是一层映射。为什采用一层映射?
答:我们的目的是在1MB的虚拟地址与物理地址建立起映射,这个目的已经达到。我们并不采用内容的换入/换出,所以没有必要把这1MB空间分成页面。需要映射的时候,MMU以虚拟地址高12位为下标,从首层映射表中找到相应的表项。由于最低2位为10,MMU知道这是一层映射,所以表项的最高12位就是物理段地址,在后面拼接上虚拟地址的低20位,就得到32位的物理地址。
5、对于二层映射表,采用两套互相平行的页面映射表,一套是“逻辑的”,即Linux内核所要求的页面映射表。另一套是“物理的”即ARM MMU所要求的,而通过软件维持二者在逻辑上的一致。