linux内核启动设置页表,Linux内核页表初始化

Linux在内核启动过程中start_kernel->setup_arch会调用如下两个函数对页表进行初始化和建立。

static inline void prepare_page_table(void)

{

unsigned long addr;

/*

* Clear out all the mappings below the kernel image.

*/

//初始化0~MODULES_VADDR(0xBF000000)地址空间的页表,0xBF000000~0xC0000000的16M空间用来存放kernel module library

//关于PGDIR_SIZE=2M和pmd_clear下面单独叙述

for (addr = 0; addr < MODULES_VADDR; addr += PGDIR_SIZE)

pmd_clear(pmd_off_k(addr));

#ifdef CONFIG_XIP_KERNEL

//XIP内核(即kernel image存放在Nor flash等可偏上执行的存储体中)存放在kernel module area,不能影响kernel image所在的地址空间的页表,etext为kernel代码段结束地址,加一页,应该是为kernel数据段保留足够的空间

/* The XIP kernel is mapped in the module area -- skip over it */

addr = ((unsigned long)_etext + PGDIR_SIZE - 1) & PGDIR_MASK;

#endif

for ( ; addr < PAGE_OFFSET; addr += PGDIR_SIZE)//否则kernel module在使用时才动态加载,现在需要初始化该区域的页表

pmd_clear(pmd_off_k(addr));

/*

* Clear out all the kernel space mappings, except for the first

* memory bank, up to the end of the vmalloc region.

*/

//由于第一个Bank存储着kernel image,这段空间的页表在head.S已经建立完成,以后无论什么动作都不能影响这段页表

for (addr = __phys_to_virt(bank_phys_end(&meminfo.bank[0]));

addr < VMALLOC_END; addr += PGDIR_SIZE)

pmd_clear(pmd_off_k(addr));

}

由于ARM采用两级映射,pmd不占用字段,pmd=pgd,而这里一次清了两个pgd,和pgd的定义正好对应,所以在for循环时每次步长为PGDIR_SIZE(2M),根本的原因是ARM linux使用了ARM的两个段,在用户态为__USER_CS__USER_DS,在内核态为__KERNEL_CS和__KERNEL_DS

初始化页表里也会flush对应的TLB。

typedef unsigned long pgd_t[2];

#define pmd_clear(pmdp)\

do {\

pmdp[0] = __pmd(0);\

pmdp[1] = __pmd(0);\

clean_pmd_entry(pmdp);\

} while (0)

static inline pmd_t *pmd_off_k(unsigned long virt)

{

return pmd_off(pgd_offset_k(virt), virt);

}

#define pgd_offset_k(addr)pgd_offset(&init_mm, addr)

/* to find an entry in a page-table-directory */

#define pgd_index(addr)((addr) >> PGDIR_SHIFT)

#define pgd_offset(mm, addr)((mm)->pgd+pgd_index(addr))

/* to find an entry in a kernel page-table-directory */

#define pgd_offset_k(addr)pgd_offset(&init_mm, addr)

/* Find an entry in the second-level page table.. */

#define pmd_offset(dir, addr)((pmd_t *)(dir))

/* Find an entry in the third-level page table.. */

#define __pte_index(addr)(((addr) >> PAGE_SHIFT) & (PTRS_PER_PTE - 1))

map_lowmem函数会为地段物理内存的每个bank逐一调用map_memory_bank做页表映射,再调用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.

*/

static void __init create_mapping(struct map_desc *md)

{

unsigned long phys, addr, length, end;

const struct mem_type *type;

pgd_t *pgd;

//在内核为个bank建立页表时,它虚拟地址不可能落在user空间

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;

}

//高端内存通过alloc_page+kmap的形式映射,在初始化阶段不可能有bank的虚拟地址落在VMALLOC线性地址空间

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];

//如果有大于4G的物理内存,单独建立

/*

* 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));//将bank整页对齐

if (type->prot_l1 == 0 && ((addr | phys | length) & ~SECTION_MASK)) {//不能section(1M)对齐的

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);

}

static void __init alloc_init_section(pgd_t *pgd, unsigned long addr,

unsigned long end, unsigned long phys,

const struct mem_type *type)

{

pmd_t *pmd = pmd_offset(pgd, addr);

/*

* Try a section mapping - end, addr and phys must all be aligned

* to a section boundary. Note that PMDs refer to the individual

* L1 entries, whereas PGDs refer to a group of L1 entries making

* up one logical pointer to an L2 table.

*/

if (((addr | end | phys) & ~SECTION_MASK) == 0) {//完整的一个section(1M)直接生成段页表

pmd_t *p = pmd;

if (addr & SECTION_SIZE)

pmd++;

do {

*pmd = __pmd(phys | type->prot_sect);//填充段页表地址和section相关属性

phys += SECTION_SIZE;

} while (pmd++, addr += SECTION_SIZE, addr != end);

flush_pmd_entry(p);

} else {//不是段对齐的,要分配二级页表

/*

* No need to loop; pte's aren't interested in the

* individual L1 entries.

*/

alloc_init_pte(pmd, addr, end, __phys_to_pfn(phys), type);

}

}

static void __init alloc_init_pte(pmd_t *pmd, unsigned long addr,

unsigned long end, unsigned long pfn,

const struct mem_type *type)

{

pte_t *pte;

if (pmd_none(*pmd)) {//check pmd指向的L1页表中的页表项是否存在,不存在的话使用Bootmem分配所需的二级页表空间

pte = alloc_bootmem_low_pages(2 * PTRS_PER_PTE * sizeof(pte_t));

__pmd_populate(pmd, __pa(pte) | type->prot_l1);//填充二级页表,即pte的物理地址和prot_l1

}

pte = pte_offset_kernel(pmd, addr);//建立pte

do {

set_pte_ext(pte, pfn_pte(pfn, __pgprot(type->prot_pte)), 0);//set_pte_ext与平台相关,它完成硬件页表和内核页表两者的创建

pfn++;

} while (pte++, addr += PAGE_SIZE, addr != end);}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值