1. 程式人生 > >linux iotable_init 靜態對映與核心頁表的建立

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)