Linux内核物理内存映射,在内核使用内存前,需要初始化内核的页表,初始化页表主要在map_lowmem()
函数中。在映射页表之前,需要把页表的页表项清零,主要在prepare_page_table()
函数中实现。
[start_kernel()->setup_arch()->paging_init()]
static inline void prepare_page_table(void)
{
unsigned long addr;
phys_addr_t end;
/*
* Clear out all the mappings below the kernel image.
*/
for (addr = 0; addr < MODULES_VADDR; addr += PMD_SIZE)
pmd_clear(pmd_off_k(addr));
for ( ; addr < PAGE_OFFSET; addr += PMD_SIZE)
pmd_clear(pmd_off_k(addr));
/*
* Find the end of the first block of lowmem.
*/
end = memblock.memory.regions[0].base + memblock.memory.regions[0].size;
/*
* Clear out all the kernel space mappings, except for the first
* memory bank, up to the vmalloc region.
*/
for (addr = __phys_to_virt(end);
addr < VMALLOC_START; addr += PMD_SIZE)
pmd_clear(pmd_off_k(addr));
}
这里对如下3段地址调用pmd_clear()函数来清除一级页表项的内容。
- 0x0~MODULES_VADDR。
- MODULES_VADDR~PAGE_OFFSET。
- arm_lowmem_limit~VMALLOC_START。
[start_kernel()->setup_arch()->paging_init()->map_lowmem()]
static void init map lowmem(void)
{
struct memblock region *reg;
phys addr t kernel x start = round down( pa( stext), SECTION SIZE);
phys addr t kernel x end = round up( pa( init end), SECTION SIZE);
/* Map all the lowmem memory banks. */
for each memblock(memory, reg) {
phys addr t start = reg->base;
phys addr t end = start + reg->size;
struct map desc map;
if (end > arm lowmem limit)
end = arm lowmem limit;
//映射kernel image区域
map.pfn = phys to pfn(kernel x start);
map.virtual = phys to virt(kernel x start);
map.length = kernel x end - kernel x start;
map.type = MT MEMORY RWX;
create_mapping(&map);
//映射低端内存
if (kernel_x_end < end) {
map.pfn = __phys_to_pfn(kernel_x_end);
map.virtual = __phys_to_virt(kernel_x_end);
map.length = end - kernel_x_end;
map.type = MT_MEMORY_RW;
create_mapping(&map);
}
}
}
真正创建页表是在map_lowmem()函数中,会从内存开始的地方覆盖到arm_lowmem_limit处。这里需要考虑kernel代码段的问题,kernel的代码段从_stext开始,到_init_end结束。以ARM Vexpress平台为例。
- 内存开始地址0x60000000。
- _stext:0x60000000。
- _init_end: 0x60800000。
- arm_lowmem_limit: 0x8f800000。
其中,arm_lowmem_limit地址需要考虑高端内存的情况,该值的计算是在sanity_check_meminfo()函数中。在ARM Vexpress平台中,arm_lowmem_limit等于vmalloc_min,其定义如下:
static void * __initdata vmalloc_min =
(void *)(VMALLOC_END - (240 << 20) - VMALLOC_OFFSET);
phys addr t vmalloc limit = pa(vmalloc min - 1) + 1;
map_lowmem()会对两个内存区间创建映射。
(1)区间1
- 物理地址:0x60000000~0x608000000。
- 虚拟地址:0xc0000000~0xc08000000。
- 属性:可读、可写并且可执行(MT_MEMORY_RWX)。
(2)区间2
- 物理地址:0x60800000~0x8f8000000。
- 虚拟地址:0xc0800000~0xef8000000。
- 属性:可读、可写(MT_MEMORY_RW)。
MT_MEMORY_RWX和MT_MEMORY_RW的区别在于\2RM
页表项有一个XN比特位,XN比特位置为1,表示这段内存区域不允许执行。
映射函数为create_mapping(),这里创建的映射就是物理内存直接映射,或者叫作线性映射。
酷客网相关文章:
评论前必须登录!
注册