fixmap_remap_fdt

本文来分析fixmap_remap_fdt函数的代码
输入:dtb的物理地址
输出:dtb映射后的虚拟地址
功能:为dtb所在的物理内存建立映射

void *__init fixmap_remap_fdt(phys_addr_t dt_phys)
{
	void *dt_virt;
	int size;
	dt_virt = __fixmap_remap_fdt(dt_phys, &size, PAGE_KERNEL_RO);
	memblock_reserve(dt_phys, size);
	return dt_virt;
}

映射使用的flag是PAGE_KERNEL_RO;
物理地址就是dtb做2MB对齐后的地址,在当前项目中dtb在0x13408000,对齐后就是0x13400000
虚拟地址就是FIX_FDT对应的fixmap地址:0xffffffbefe800000
映射区域大小不确定,需要从dtb中读取出来。这就有问题了,我们做这个映射就是为了访问dtb,而现在为了完成映射却需要先访问dtb。解决方法是:先对dtb进行2MB大小的映射,映射完成后就可以读取dtb的前面几个字节,其中有dtb的size,最后再根据这个size来确定最后的映射大小。因为对dtb大小有要求,必须小于2M,我们这个项目中第一次进行2M映射后就已经满足要求了。

[9950] booting linux @ 0x10080000, ramdisk @ 0x13608000 (2083882), tags/device tree @ 0x13408000
[    0.000000]     fixed   : 0xffffffbefe7fb000 - 0xffffffbefec00000   (  4116 KB)

FIXADDR_TOP = 0xffffffbefec00000
FIX_FDT = 1024
#define __fix_to_virt(x)	(FIXADDR_TOP - ((x) << PAGE_SHIFT))

再看__fixmap_remap_fdt的实现:
由于section map需要保证物理地址和虚拟地址都是2M对齐,而我们的物理地址是0x13408000,因此会有个offset=0x8000,所以返回的dt_virt需要在dt_virt_base基础上再加上offset,因此最终的结果是0xFFFFFFBEFE808000,它会被保存在变量initial_boot_params中,后面会使用到。
第一次映射是在VA:dt_virt_base,PA:0x13400000,size=2M进行的,注意调用的是create_mapping_noalloc,也就是不允许申请page来完成映射。对于section map,需要两个页表,分别是pgd和pmd,pgd自然使用nit_mm.pgd,其实就是swapper_pg_dir,pmd则使用的是bm_pmd,因为fixmap的地址空间非常小,也就几MB大小(0xffffffbefe800000和0xffffffbefe7fb000完全在同一个pgd范围内,一个pgd可以表示的范围是1G),而之前early_fixmap_init已经完成了swapper_pg_dir[251]=bm_pmd,这里只需要bm_pmd[500]=0x13400000即可

void *__init __fixmap_remap_fdt(phys_addr_t dt_phys, int *size, pgprot_t prot)
{
	const u64 dt_virt_base = __fix_to_virt(FIX_FDT);
	int offset;
	void *dt_virt;

	offset = dt_phys % SWAPPER_BLOCK_SIZE;
	dt_virt = (void *)dt_virt_base + offset;

	/* map the first chunk so we can read the size from the header */
	create_mapping_noalloc(round_down(dt_phys, SWAPPER_BLOCK_SIZE),
			dt_virt_base, SWAPPER_BLOCK_SIZE, prot);

	*size = fdt_totalsize(dt_virt);
	if (*size > MAX_FDT_SIZE)
		return NULL;
	if (offset + *size > SWAPPER_BLOCK_SIZE)
		create_mapping_noalloc(round_down(dt_phys, SWAPPER_BLOCK_SIZE), dt_virt_base,
			       round_up(offset + *size, SWAPPER_BLOCK_SIZE), prot);
	return dt_virt;
}

ffffff8009cfd000 b bm_pmd
在这里插入图片描述
可以看到,0xffffff8009cfd000+500*8=0xffffff8009cfdfa0,对应的值是0x13400f91,其实就是0x13400000,因为后面12bit是flag,其中最后是1不是3正好表面这个是block map,不是page table。

最后再说下dtb的大小限制。
可以看到,dtb的大小限制在2MB内,我们项目实际只有600KB。
既然由此限制,为何还存在第二次map的可能?原因是dtb虽然整体小于2M,但是起始物理地址可能不是2MB对齐,如果它正好包含了一个2M的边界,那即便很小,也会占用4MB,但是因为整体小于2M,所以绝对不会占用3个2M,这也就是第二次map的原因了。

#define MAX_FDT_SIZE		SZ_2M

此函数执行完成后,内核就可以访问dtb了。

还有点要提下,这里最后会把这块空间添加到memblock的reserve区域中:
memblock_reserve(dt_phys, size);
此时此刻,memblock还不清楚物理内存地址范围,那么现在可以调用memblock_reserve函数吗?可以的。
以前我的误解:memblock_reserve标记保留的区域,一定是在memblock中的memory区域范围内的。
当然这个说法也不错,那时从memblock_reserve的代码看,它不关注Memory的状态,也就是内存整体的物理地址范围它不在乎。所以在还没有调用memblock_add前是可以memblock_reserve的。
至于之前的想法,那是因为一般地调用顺序总是先从memblock申请内存,在memblock_reserve,而申请到的空间一定是在memory区域内的,所以之前的说法也对。

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值