linux iotable_init 静态映射与内核页表的建立

arm32 linux3.18 mach-vexpress

常用的ioremap或者of_iomap都是动态映射,静态映射的接口是iotable_init

void __init iotable_init(struct map_desc *io_desc, int nr)
struct map_desc {
	unsigned long virtual;
	unsigned long pfn;
	unsigned long length;
	unsigned int type;
};

如map_desc所示通过实例化这个结构体我们可以创建对某个物理地址的固定的虚拟地址映射

iotable_init 一般是在machine desc 的map_io的call函数里具体的call stack如下

start_kernel-->setup_arch-->paging_init-->devicemaps_init-->mdesc->map_io()

iotable_init会调用函数create_mapping来创建内核页表和映射关系,不只是iotable_init 包括内核页表的创建都是通过该函数

盗一张3+1的32bit linux 的memeory layout 对比kernel启动过程中的LOG帮助后面理解

log 出自 start_kernel-->mm_init-->mem_init

/*
 * 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)
{
	if (md->virtual != vectors_base() && md->virtual < TASK_SIZE) {
		pr_warn("BUG: not creating mapping for 0x%08llx at 0x%08lx in user region\n",
			(long long)__pfn_to_phys((u64)md->pfn), md->virtual);
		return;
	}
//vectors_base是中断向量表的位置0xffff0000  TASK_SIZE是userspace的空间大小为0x7f000000 
//iotable_init 是从vmalloc区域拿地址空间,或者说low memory区,所以先判断所申请的虚拟地址是否在此范围内
	if ((md->type == MT_DEVICE || md->type == MT_ROM) &&
	    md->virtual >= PAGE_OFFSET && md->virtual < FIXADDR_START &&
	    (md->virtual < VMALLOC_START || md->virtual >= VMALLOC_END)) {
		pr_warn("BUG: mapping for 0x%08llx at 0x%08lx out of vmalloc space\n",
			(long long)__pfn_to_phys((u64)md->pfn), md->virtual);
	}
//PAGE_OFFSET是kernel space虚拟地址的起始0x80000000 是2+2的memory分布,跟TASK_SIZE gap 了16M
//FIXADDR_START 是kernel space的永久映射区 0xffc00000
//vmalloc_start 0xa0800000 vmaloc_end 0xff000000 大概1.5G 
//vmalloc区域大小也会有boot cmdline vmalloc=  决定
	
	type = &mem_types[md->type];

	addr = md->virtual & PAGE_MASK;
//分配的地址以page为offset
	phys = __pfn_to_phys(md->pfn);
//从页框号获得要映射的物理地址
	length = PAGE_ALIGN(md->length + (md->virtual & ~PAGE_MASK));
//分配的size也是以page为offset
	if (type->prot_l1 == 0 && ((addr | phys | length) & ~SECTION_MASK)) {
		printk(KERN_WARNING "BUG: map for 0x%08llx at 0x%08lx can not "
		       "be mapped using pages, ignoring.\n",
		       (long long)__pfn_to_phys(md->pfn), addr);
		return;
	}

	pgd = pgd_offset_k(addr);
//根据虚拟地址去查询kernel页表swapper_pg_dir找到对应的pgd(虚拟地址在text段的前0x4000,16k)
	end = addr + length;
	do {
		unsigned long next = pgd_addr_end(addr, end);

		alloc_init_pud(pgd, addr, next, phys, type);

		phys += next - addr;
		addr = next;
	} while (pgd++, addr != end);
}

/* to find an entry in a kernel page-table-directory */
#define pgd_offset_k(addr)	pgd_offset(&init_mm, addr)
#define pgd_offset(mm, addr) ((mm)->pgd + pgd_index(addr))
/* to find an entry in a page-table-directory */
#define pgd_index(addr)		((addr) >> PGDIR_SHIFT)

猜你喜欢

转载自blog.csdn.net/shenhuxi_yu/article/details/81107096