uboot下动态修改dtb文件

1、uboot中读取dtb文件

   以rk3588平台举例说明,rk平台uboot和kernel的镜像文件都含有dtb文件,这里只考虑内核镜像文件image末尾包含的dtb文件如何读取,因为内核镜像文件image末尾包含的dtb文件会在内核启动后生效,而uboot镜像文件包含的dtb文件只会在uboot下生效,只影响uboot的外设功能,相对改动可能性很小。

1.1、uboot重定位阶段读取dtb文件到内存

relocation_return
	board_init_r		/* src\common\board_r.c */
		initcall_run_list		/* src\common\board_r.c */
			static init_fnc_t init_sequence_r[]		/* src\common\board_r.c */
				board_init		/* src\arch\arm\mach-rockchip\board.c */
					init_kernel_dtb		/* src\arch\arm\mach-rockchip\kernel_dtb.c */
						rockchip_read_dtb_file		/* src\arch\arm\mach-rockchip\boot_rkimg.c */
							rockchip_read_resource_dtb
								resource_read_hwid_dtb
									resource_init_list
							rk_board_early_fdt_fixup(fdt_addr);	/* 这是厂家预留的修改dtb文件的接口,目前空实现 */
							android_fdt_overlay_apply((void *)fdt_addr);
							dh_fdt_dynamic_fixup_ethernet; /* 这是大华特有修改mac配置接口,获取硬件hwid来修正gmac的dtb文件 */
								do_fixup_by_path(fdt, "/ethernet@fe1b0000", "status", "disa", 5, 0);

这部分代码是uboot重定位的流程,里面包含了对dtb文件的读取,我们重点关注 int init_kernel_dtb(void) 函数的具体实现,里面有dtb文件读取的细节。
小结:dtb文件的读取实在uboot重定位的流程中实现的

1.2、dtb文件读取的实现细节

理解 int init_kernel_dtb(void) 函数的具体实现:

int init_kernel_dtb(void)										/* \src\arch\arm\mach-rockchip\kernel_dtb.c */
	if (gd->ram_size <= SZ_128M)								/* 针对内存小于128M的情况,设备树存放地址从 fdt_addr1_r 中获取 */
		fdt_addr = env_get_ulong("fdt_addr1_r", 16, 0);
	fdt_addr = env_get_ulong("fdt_addr_r", 16, 0);				/* 从环境变量中获取dtb文件读取到内存中的地址 */
	/* 将dtb文件读取到第一步指定的内存地址中,\src\arch\arm\mach-rockchip\boot_rkimg.c */
	ret = rockchip_read_dtb_file((void *)fdt_addr);				
		/* 1、初始化emmc设备,只执行第一步就退出\src\arch\arm\mach-rockchip\resource_img.c */
		int resource_traverse_init_list(void)					
			int fit_image_init_resource(void)
					/* a、初始化emmc存储设备,获取块设备信息 dev_desc,后续读写emmc都需要使用这个结构体 */
					dev_desc = rockchip_get_bootdev();
					/* b、从环境变量中获取需要访问的分区名字,boot分区,对应uboot打印:boot mode: None */
					if (rockchip_get_boot_mode() == BOOT_MODE_RECOVERY)
					/* c、根据 分区名字 去emmc中获取分区存储位置信息,存在 disk_partition_t part 中 */
					ret = part_get_info_by_name(dev_desc, part_name, &part)
					/* d、boot分区开始的文件是内核,而不是dtb文件,所以校验dtb头部信息会失败,下面直接返回
						从emmc中读出dtb文件到内存中,dtb文件内存地址为fit,对应uboot打印:FIT: No fdt blob */
					fit = fit_get_blob(dev_desc, &part);
					/* 不执行 e、这里就可能会去获取镜像文件 */
					ret = fit_image_load_resource(fit, dev_desc, &part, &rsce);
					/* 不执行 f、创建资源列表,便于后续访问 */
					ret = resource_create_ram_list(dev_desc, (void *)rsce);
		/* 2、关于dtb文件的具体操作 */
		ret = rkimg_traverse_read_dtb(fdt, locate);
			/* 2.1、读取dtb资源到指定的内存地址中,hash 和 hash_size 存储在文件中用于读取文件之后的hash校验  */
			ret = rockchip_read_resource_dtb(fdt_addr, &hash, &hash_size);
				/* a、两个作用:第一有效:访问emmc的entry部分,建立资源列表 struct resource_file *file 
						第二目前没有使用:通过hwid或者adc来获取dtb的文件名,通过文件名在第b步中读取dtb文件 */rockchip_read_resource_dtb
				struct resource_file *resource_read_hwid_dtb(void)
					/* 加打印确定在这里只调用了一次,为uImage中镜像后的文件创建资源列表。 */
					int resource_init_list(void)	/* 具体实现看下面详解 */
				/* b、根据文件名字遍历 entrys_dtbs_head 列表找到dtb文件的入口信息 struct resource_file *file; */
				file = get_default_dtb();
				/* c、根据dtb文件的入口信息读取dtb文件到环境变量设置的 fdt_addr 地址中  */
				ret = rockchip_read_resource_file(fdt_addr, file->name, 0, 0);
				printf("DTB: %s\n", file->name);								/* 打印:DTB: rk-kernel.dtb */
			/* 2.2、校验内存中dtb文件的头部magic是否正确 */
			if (fdt_check_header(fdt)) {
			/* 2.3、校验内存中dtb文件的hash值,避免读取数据错误的问题出现 */
			if (hash_size && fdt_check_hash(fdt, fdt_totalsize(fdt), hash, hash_size))
		/* 3、修正dtb资源,各个平台预留修改dtb信息的接口 */
		rk_board_early_fdt_fixup(fdt_addr);									/* 这是厂家预留的修改dtb文件的接口,目前空实现 */
		android_fdt_overlay_apply((void *)fdt_addr);						/* 未执行,因为 hdr->header_version == 0 */
		dh_fdt_dynamic_fixup_ethernet(fdt_addr); 										/* 这是大华特有修改mac配置接口,获取硬件hwid来修正gmac的dtb文件 */

这里关注以下事情:

uboot读取dtb的文件涉及rk-uboot在uboot下如何管理文件,涉及从emmc中找到目标文件进行读取;这部分不进行展开。
uboot读取dtb文件需要考虑dtb文件名,dtb文件存放到什么内存地址,dtb文件的拷贝长度,拷贝完成后校验文件是否正确。下面根据代码逐个解答。

  1. dtb文件存放在内存的地址有环境变量 “fdt_addr_r” 指定

    /* 从环境变量中获取dtb文件读取到内存中的地址 */
    fdt_addr = env_get_ulong("fdt_addr_r", 16, 0);
    
  2. 确认文件名
    在u-boot\arch\arm\mach-rockchip\resource_img.c文件中

    #define DEFAULT_DTB_FILE		"rk-kernel.dtb"
    /* b、根据文件名字遍历 entrys_dtbs_head 列表找到dtb文件的入口信息 struct resource_file *file; */
    file = get_default_dtb();
    	return target_file ? : get_file_info(DEFAULT_DTB_FILE);
    

    所以正常情况是默认使用 “rk-kernel.dtb” 文件,通过下面打印可以确认:在这里插入图片描述

  3. 从emmc中读取"rk-kernel.dtb"文件到"fdt_addr_r"内存地址
    rk厂家uboot下的文件信息都是通过struct resource_file *file;结构体来维护,这里不探究rk厂家uboot下文件管理方式,里面包含拷贝数据长度。将emmc上的"rk-kernel.dtb"文件拷贝到"fdt_addr_r"内存地址是通过下面函数实现的,这里有emmc块设备的读取操作。

    ret = rockchip_read_resource_file(fdt_addr, file->name, 0, 0);
    
  4. 校验内存中的dtb文件
    将emmc中的dtb文件拷贝到内存后,会对内存上的dtb文件进行hash校验,保证dtb文件的准确性。

/* 2、关于dtb文件的具体操作 */
ret = rkimg_traverse_read_dtb(fdt, locate);
	/* 2.1、读取dtb资源到指定的内存地址中,hash 和 hash_size 存储在文件中用于读取文件之后的hash校验  */
	ret = rockchip_read_resource_dtb(fdt_addr, &hash, &hash_size);
		/* a、两个作用:第一有效:访问emmc的entry部分,建立资源列表 struct resource_file *file 
				第二目前没有使用:通过hwid或者adc来获取dtb的文件名,通过文件名在第b步中读取dtb文件 */rockchip_read_resource_dtb
		struct resource_file *resource_read_hwid_dtb(void)
			/* 加打印确定在这里只调用了一次,为uImage中镜像后的文件创建资源列表。 */
			int resource_init_list(void)	/* 具体实现看下面详解 */
		/* b、根据文件名字遍历 entrys_dtbs_head 列表找到dtb文件的入口信息 struct resource_file *file; */
		file = get_default_dtb();
		/* c、根据dtb文件的入口信息读取dtb文件到环境变量设置的 fdt_addr 地址中  */
		ret = rockchip_read_resource_file(fdt_addr, file->name, 0, 0);
		printf("DTB: %s\n", file->name);								/* 打印:DTB: rk-kernel.dtb */
	/* 2.2、校验内存中dtb文件的头部magic是否正确 */
	if (fdt_check_header(fdt)) {
	/* 2.3、校验内存中dtb文件的hash值,避免读取数据错误的问题出现 */
	if (hash_size && fdt_check_hash(fdt, fdt_totalsize(fdt), hash, hash_size))

1.3、修改dtb文件的问题

int init_kernel_dtb(void)
	ret = rockchip_read_dtb_file((void *)fdt_addr);	
	rk_board_early_fdt_fixup(fdt_addr);									/* 这是厂家预留的修改dtb文件的接口,目前空实现 */
	/* 这是大华特有修改mac配置接口,获取硬件hwid来修正gmac的dtb文件 */	
	dh_fdt_dynamic_fixup_ethernet(fdt_addr); 											

有一种方案是在dtb文件读取到内存之后立马去修改dtb文件,但是这里修改属性会有很大的问题;因为dtb文件有个头部结构体,在这里插入图片描述
头部结构体会记录dtb文件的分段信息,包括dtb文件的总大小;当我们调用 do_fixup_by_path 函数去修改或者新增属性时,可能会导致dtb文件增加,而dtb文件头部 totalsize 成员记录dtb的总大小并没有改变, do_fixup_by_path 函数检测到修改属性后的dtb文件大小大于 totalsize 成员记录的大小,则会返回错误,修改属性失败。
这个问题和第二章的问题是相同的原因,解决方案在第二章节说明。

2、uboot下通过fdt修改dtb文件失败

2.1、问题现象

  1. uboot下使用 fdt 命令新增属性失败。

    => fdt set /pcie@fe190000 local-mac 22-22-22-22-22
    libfdt fdt_setprop(): FDT_ERR_NOSPACE
    
  2. 调用 do_fixup_by_path(fdt, ethernet_path, “status”, “disabled”, 9, 0) 接口将 status 的属性 okay 增加字节修改为 diabled,结果被截断只有disa,即空间只能有4个字节。

    => fdt print /pcie@fe190000 status 
    status = "disa"
    

2.2、问题原因

根据错误打印:libfdt fdt_setprop(): FDT_ERR_NOSPACE
调用do_fixup_by_path 函数新增属性或者修改属性时,dtb文件的大小会变化;底层接口新增属性时会进行校验,dtb文件新增属性之后的大小是否超过 dtb头部结构体 totalsize 限制的大小。具体实现如下:

static int _fdt_splice(void *fdt, void *splicepoint, int oldlen, int newlen)
{
	char *p = splicepoint;
	char *end = (char *)fdt + _fdt_data_size(fdt);

	if ((end - oldlen + newlen) > ((char *)fdt + fdt_totalsize(fdt)))
		return -FDT_ERR_NOSPACE;
	memmove(p + newlen, p + oldlen, end - p - oldlen);
	return 0;
}

在uboot的cmd界面使用功能fdt命令查看dtb文件的头部信息:

=> fdt header
No FDT memory address configured. Default at 0x0a100000
magic:                  0xd00dfeed
totalsize:              0x21ae4 (137956)
off_dt_struct:          0x38
off_dt_strings:         0x2055c
size_dt_strings:        0x1588
size_dt_struct:         0x20524

dtb头部信息限制dtb文件的大小为: totalsize: 0x21ae4 (137956)
dtb文件实际的大小为:off_dt_strings + size_dt_strings = 0x2055c + 0x1588 = 0x21ae4
结论:目前dtb文件的实际大小和dtb头部信息 totalsize: 0x21ae4 限制的大小刚好相同。
因此新增属性或修改属性(将5字节okay改为9字节disabled)会提示失败。

2.3、解决方案

在读取完dtb文件之后,调用 fdt_set_totalsize 接口将 dtb 头部信息 totalsize 字段增加为 0x24ae4 = 0x21ae4 + 0x3000 (CONFIG_SYS_FDT_PAD)即可,这样后续调用 do_fixup_by_path 函数修改或新增属性时就不会出现dtb文件大小校验失败的问题。

ret = rockchip_read_dtb_file((void *)fdt_addr);函数下包含以下两步骤
/* 2、关于dtb文件的具体操作 */
ret = rkimg_traverse_read_dtb(fdt, locate);
	/* 2.1、读取dtb资源到指定的内存地址中,hash 和 hash_size 存储在文件中用于读取文件之后的hash校验  */
	ret = rockchip_read_resource_dtb(fdt_addr, &hash, &hash_size);
		/* a、两个作用:第一有效:访问emmc的entry部分,建立资源列表 struct resource_file *file 
				第二目前没有使用:通过hwid或者adc来获取dtb的文件名,通过文件名在第b步中读取dtb文件 */rockchip_read_resource_dtb
		struct resource_file *resource_read_hwid_dtb(void)
			/* 加打印确定在这里只调用了一次,为uImage中镜像后的文件创建资源列表。 */
			int resource_init_list(void)	/* 具体实现看下面详解 */
		/* b、根据文件名字遍历 entrys_dtbs_head 列表找到dtb文件的入口信息 struct resource_file *file; */
		file = get_default_dtb();
		/* c、根据dtb文件的入口信息读取dtb文件到环境变量设置的 fdt_addr 地址中  */
		ret = rockchip_read_resource_file(fdt_addr, file->name, 0, 0);
		printf("DTB: %s\n", file->name);								/* 打印:DTB: rk-kernel.dtb */
	/* 2.2、校验内存中dtb文件的头部magic是否正确 */
	if (fdt_check_header(fdt)) {
	/* 2.3、校验内存中dtb文件的hash值,避免读取数据错误的问题出现 */
	if (hash_size && fdt_check_hash(fdt, fdt_totalsize(fdt), hash, hash_size))
/* 3、修正dtb资源,各个平台预留修改dtb信息的接口 */
rk_board_early_fdt_fixup(fdt_addr);									/* 这是厂家预留的修改dtb文件的接口,目前空实现 */
android_fdt_overlay_apply((void *)fdt_addr);						/* 未执行,因为 hdr->header_version == 0 */
dh_fdt_dynamic_fixup_ethernet(fdt_addr); 										/* 这是大华特有修改mac配置接口,获取硬件hwid来修正gmac的dtb文件 */

根据上面代码流程主要关注3个步骤,

  1. 将emmc中的dtb文件读取到内存地址;
  2. 对内存中的dtb文件进行头部信息magic字段校验以及真个dtb文件hash校验。
  3. 调用rk_board_early_fdt_fixup或类似接口修正dtb文件的属性
    为了解决属性修改失败的问题,我们需要增加

将 dtb 头部信息 totalsize 字段增加为 0x24ae4 = 0x21ae4 + 0x3000 (CONFIG_SYS_FDT_PAD),
注意:这段代码需要在1和2步骤之后执行,在3步骤之前执行,具体为:

int rockchip_read_dtb_file(void *fdt)
	ret = rkimg_traverse_read_dtb(fdt, locate);
		/* 1、从emmc读取dtb文件到内存中 */
		ret = rockchip_read_resource_dtb(fdt, &hash, &hash_size);
		/* 2、校验dtb文件:magic和整个文件的hash */
		if (fdt_check_header(fdt))
		if (hash_size && fdt_check_hash(fdt, fdt_totalsize(fdt), hash, hash_size))
	/* 需要修改的部分代码 */
	fdt_size = fdt_totalsize(fdt_addr);
	printf("%s %d: fdtaddress = %p size = %x\n", __func__, __LINE__, fdt_addr, fdt_size);
	fdt_size = fdt_size + CONFIG_SYS_FDT_PAD;
	fdt_set_totalsize(fdt_addr, fdt_size);
	printf("%s %d: fdtaddress = %p size = %x\n", __func__, __LINE__, fdt_addr, fdt_totalsize(fdt_addr));
	
	/* 3、修改dtb文件的属性 */
	rk_board_early_fdt_fixup(fdt);

2.4、验证以及风险

  1. 修改属性能成功

    => fdt print /pcie@fe190000 status 
    status = "disabled"
    
  2. 新增属性能成功

    	=> fdt set /pcie@fe190000 local-mac 22-22-22-22-22
    	=> fdt print /pcie@fe190000 local-mac
    	local-mac = "22-22-22-22-22"
    
  3. dtb文件头部信息的 totalsize 确实变大了

    => fdt header
    No FDT memory address configured. Default at 0x0a100000
    magic:                  0xd00dfeed
    totalsize:              0x24ae4 (150244)
    off_dt_struct:          0x38
    off_dt_strings:         0x20564
    off_mem_rsvmap:         0x28
    version:                17
    last_comp_version:      16
    boot_cpuid_phys:        0x0
    size_dt_strings:        0x1588
    size_dt_struct:         0x2052c
    number mem_rsv:         0x0
    

3、uboot下修改dtb文件恰当的时机

3.1、恰当的修改时机

uboot阶段结束,执行bootm命令引导内核启动时也会修改dtb文件,将uboot环境变量中的信息更新到dtb文件中,比如:
dtb文件预留内存空间: /memreserve/ a100000 22000;
将uboot环境变量中的mac地址同步到dtb文件中: local-mac-address = [42 6d ffffff86 ffffff88 ffffffb8 15];
更新dtb文件中的 bootargs 参数。

uboot执行bootm命令后执行以下程序:

android_bootloader_boot_kernel(load_address);
	do_bootm_states
		/* src\common\bootm.c,本质调用 static boot_os_fn *boot_os[] = do_bootm_linux,\src\common\bootm_os.c */
		boot_fn = bootm_os_get_boot_func(images->os.os);
			boot_prep_linux(images);		/* \src\arch\arc\lib\bootm.c */
				image_setup_linux			/* \src\common\image.c */
					boot_relocate_fdt		/* 1、这里会将dtb文件头部信息 totalsize:0x21ae4 改成  0x24ae4 */
					image_setup_libfdt		/* 2、这里会对dtb文件进行修改,修改和新增一些属性。\src\common\image-fdt.c */
						fdt_root			/* 3、在dtb文件中新增  serial-number = "af7e309c4986274f"; 属性 */
						fdt_fixup_ethernet	/* 4、新增千兆网卡的mac地址 local-mac-address = [42 6d ffffff86 ffffff88 ffffffb8 15];   \src\common\fdt_support.c */
						fdt_shrink_to_minimum	/* 5、重新计算增加属性之后dtb文件的大小,进行预留和字节对齐之后确定dtb文件大小为:0x22000 字节,
													修改dtb文件增加内存预留的属性:/memreserve/ a100000 22000;这部分预留的内存空间存放dtb文件 */

所以修改dtb文件最佳的地方在 image_setup_libfdt函数中;原因如下:

  1. 在执行此函数之前 boot_relocate_fdt 函数已经将dtb文件头部信息 totalsize:0x21ae4 改成 0x24ae4增加了0x3000 字节的空间,所以 do_fixup_by_path 函数修改或新增属性有足够的空间。
  2. 此函数本身就要将uboot中的环境变量更新到dtb文件中,比如 serial-numberlocal-mac-addressbootargs等环境变量更新到dtb文件中。
  3. 修改完成后 fdt_shrink_to_minimum 函数会重新计算dtb文件的大小,并在dtb文件增加/memreserve/ a100000 22000 属性,在内核中为dtb文件保留预留内存空间。

小结:最佳的修改点在 \src\common\image-fdt.c 文件的 image_setup_libfdt 函数中

3.2、具体修改

prop_fix_st prop_fix[] = {
	{	/* gmac1 status = "disabled"; */
		.node_patch = "/ethernet@fe1c0000",
		.prop_name = "status",
	},{	/* pcie3x2: pcie@fe160000 */
		.node_patch = "/pcie@fe160000",
		.prop_name = "reset-gpios",
	},{
		.node_patch = "/phy@fee10000",
		.prop_name = "rockchip,ext-refclk",
	},{
		.node_patch = "/pcie@fe190000",
		.prop_name = "max-link-speed",
	}
};

int uboot_dtb_fixed(void *blob) 
{
	int nodeoff = 0;
	int gpio3_ndoff = 0;
	uint32_t gpio3_phandle = 0;
	fdt32_t gpio[3] = {0};

	gpio3_ndoff = fdt_path_offset(blob,"/pinctrl/gpio@fec40000");	/* gpio3: gpio@fec40000 */ 
	gpio3_phandle = fdt_get_phandle(blob, gpio3_ndoff);

	/* 将gmac0&1关闭掉,status = "disabled"; */
	nodeoff = fdt_path_offset(blob, prop_fix[0].node_patch);
	fdt_set_node_status(blob, nodeoff, FDT_STATUS_DISABLED, 0);

	/* 扩展板光口和sas:pcie@fe160000,光卡复位引脚为:GPIO3_A4_d */
	gpio[0] = cpu_to_fdt32(gpio3_phandle); 
	gpio[1] = cpu_to_fdt32(RK_PA6);
	gpio[2] = cpu_to_fdt32(GPIO_ACTIVE_LOW);
	do_fixup_by_path(blob, prop_fix[1].node_patch, prop_fix[1].prop_name, gpio, sizeof(gpio), 0);

	do_fixup_by_path(blob, prop_fix[2].node_patch, prop_fix[2].prop_name, NULL, 0, 1);

	gpio[0] = cpu_to_fdt32(2); 
	do_fixup_by_path(blob, prop_fix[3].node_patch, prop_fix[3].prop_name, &gpio[0], 4, 0);

	return 0;
}

int image_setup_libfdt(bootm_headers_t *images, void *blob,
		       int of_size, struct lmb *lmb)
	if (arch_fixup_fdt(blob) < 0)
	if (fdt_root(blob) < 0)
	if (fdt_chosen(blob) < 0)
	uboot_dtb_fixed(void *blob) 
	fdt_fixup_ethernet(blob);

4、rk-uboot下emmc中文件管理

int resource_init_list(void)	
	/* 1、初始化存储介质,并确认或修正环境变量devtype和devnum,获取emmc的设备信息,包括读写emmc的接口 */
	dev_desc = rockchip_get_bootdev();
		boot_devtype_init();		/* 初始化介质,确认环境变量devtype和devnum */
		dev_type = get_bootdev_type();
		devnum = env_get_ulong("devnum", 10, 0);
		/* 获取块设备描述符 struct blk_desc ,包括存储介质类型emmc,数量,大小,读写擦除的接口;Get the block device descriptor for the given device number */
		dev_desc = blk_get_devnum_by_type(dev_type, devnum);
	/* 2、获取boot分区中镜像文件的头部信息和 镜像文件结束的block号 rsce_base,方便后面获取镜像文件后面的数据 */
	rsce_base = get_resource_base_sector(dev_desc, &hdr);	/* 打印:Found DTB in boot part */
		/* a、获取 boot 分区的信息,存在 struct disk_partition 结构体中,即变量 part */
		if (part_get_info_by_name(dev_desc, "boot", &part) < 0)
		/* b、根据 boot 分区在emmc中的信息,读取镜像文件头部信息 struct andr_img_hdr *hdr; */
		hdr = populate_andr_img_hdr(dev_desc, &part);
			/* 分配镜像头结构体空间 */
			andr_hdr = (struct andr_img_hdr *)malloc(1 * dev_desc->blksz);
			/* 读boot分区第一个 blksz 到 andr_hdr 中,不全,但只需要前512个字节的校验信息就行 */
			if (blk_dread(dev_desc, part_boot->start, 1, andr_hdr) != 1) 
			/* 校验信息通过之后重新读取整个 struct andr_img_hdr 信息到 andr_hdr ,并返回 */
			return extract_boot_image_v012_header(dev_desc, part_boot);
		/* c、根据 boot 分区所在的block序号,通过uImage头部信息来计算uImage文件中镜像文件后面的其他文件信息的block序号
		uImage中的内容:hdr,kernel,ramdisk 按照page_size(2048个字节,2kB)对齐存放在emmc中。
		而emmc是以block为读取单位	*/
			rsce_base = part.start * dev_desc->blksz;		/* 镜像头所在的字节地址 */
			rsce_base += hdr->page_size;	/* uImage文件中镜像头部有1704个字节,并且按照page_size对齐,即 2048个字节,2kB对齐 */
			rsce_base += ALIGN(hdr->ramdisk_size, hdr->page_size);	/* 0字节,看uImage确定的确为0 */
			rsce_base = DIV_ROUND_UP(rsce_base, dev_desc->blksz);	/* 将字节转化为emmc中的第 rsce_base 个block */
	/* 3、获取uImage文件中内核代码段后面文件的入口信息,根据文件入口信息创建链表管理文件,具体文件有:
		=> dump_resource 
		Resources:
				rk-kernel.dtb: 0x0000b1ac(sector,rk-kernel.dtb文件在emmc中开始的block), 0x00021ae4(bytes,文件的大小)
				logo.bmp: 0x0000b2ba(sector), 0x00003288(bytes)
				logo_kernel.bmp: 0x0000b2d4(sector), 0x0000575c(bytes)
		DTBs:
				rk-kernel.dtb: 0x0000b1ac(sector),0x00021ae4(bytes)) */
	resource_create_list
		/* a、读镜像文件之后的 struct resource_img_hdr *hdr (16字节)信息,记录所有文件的数量以及每个文件入口信息如何组织 */
		if (blk_dread(dev_desc, rsce_base, 1, hdr) != 1)
		/* b、根据a中hdr信息来读取所有文件的头部信息,entryx 入口信息 struct resource_entry */	
		if (blk_dread(dev_desc, rsce_base + hdr->c_offset, blknum, data) != blknum)	
		/* c、根据b中的入口信息创建资源链表 
			e_num:第几个文件的入口信息,hdr->e_nums 文件数量,打印为3
			hdr->e_blks :一个文件入口信息使用几个block,打印为1
			dev_desc->blksz:一个block有多少个字节,512
			size = e_num * hdr->e_blks * dev_desc->blksz:第e_num个文件入口信息的偏移。
			注意:f_offset 是相对 rsce_base = 45480 的偏移,即以 struct resource_img_hdr 头部首地址为基地址进行偏移
			1111 resource_create_list 298 e_num = 0 filename = rk-kernel.dtb tag = ENTRrk-kernel.dtb f_offset = 4 size = 137956 
			1111 resource_create_list 298 e_num = 1 filename = logo.bmp tag = ENTRlogo.bmp  f_offset = 274 size = 12936 
			1111 resource_create_list 298 e_num = 2 filename = logo_kernel.bmp tag = ENTRlogo_kernel.bmp  f_offset = 300 size = 22364 
			打印的信息和 dump_resource 的信息一致,uImage 只是有文件的入口信息,但只是去读取dtb文件,其他log文件不在这里读取。*/
		for (e_num = 0; e_num < hdr->e_nums; e_num++) {
				size = e_num * hdr->e_blks * dev_desc->blksz;
				entry = (struct resource_entry *)(data + size);
				add_file_to_list(entry, rsce_base, false);
			printf("1111 %s %d e_num = %d filename = %s f_offset = %d size = %d \n",
					__FUNCTION__, __LINE__, e_num, entry->name, entry->f_offset,  entry->f_size);
		}

注意:在创建资源列表过程中会有个结构体的转化。

入口信息:struct resource_entry *entry;
资源管理链表:struct resource_file *file;
将入口信息struct resource_entry结构体成员全部赋值给struct resource_file结构体,然后将struct resource_file *file添加到链表组织起来。
static int add_file_to_list(struct resource_entry *entry, int rsce_base, bool ram)

5、内核下动态修改dtb文件

--- src/arch/arm64/kernel/setup.c       (revision 16724)
+++ src/arch/arm64/kernel/setup.c       (working copy)
@@ -50,6 +50,7 @@
 #include <asm/efi.h>
 #include <asm/xen/hypervisor.h>
 #include <asm/mmu_context.h>
+#include <linux/libfdt.h>

 static int num_standard_resources;
 static struct resource *standard_resources;
@@ -183,6 +184,67 @@
        early_fdt_ptr = fixmap_remap_fdt(dt_phys, &fdt_size, PAGE_KERNEL);
 }

+/**
+ * fdt_find_and_setprop: Find a node and set it's property
+ *
+ * @fdt: ptr to device tree
+ * @node: path of node,节点名字全路径
+ * @prop: property name,属性名字
+ * @val: ptr to new value
+ * @len: length of new property value,属性值的长度
+ * @create: flag to create the property if it doesn't exist,如果没有找到属性则是否创建属性 *
+ * Convenience function to directly set a property given the path to the node.
+ */
+int fdt_find_and_setprop(void *fdt, const char *node, const char *prop,
+                        const void *val, int len, int create)
+{
+       /* 1、根据节点全路径找到节点的偏移地址 */
+       int nodeoff = fdt_path_offset(fdt, node);
+
+       if (nodeoff < 0)
+               return nodeoff;
+       /* 2、判断是否有属性;如果create=0且没有找到属性则返回 */
+       if ((!create) && (fdt_get_property(fdt, nodeoff, prop, NULL) == NULL))
+               return 0; /* create flag not set; so exit quietly */
+       /* 3、修改或者新建属性 */
+       return fdt_setprop(fdt, nodeoff, prop, val, len);
+}
+
+
+/*
+入参:path:节点全路径名字
+prop:在指定节点下需要修改或者新增的属性名
+val:需要修改或者新增属性的值
+len:属性值的长度
+create:如果没有找到属性则是否创建属性功能,修改指定节点下指定属性的值,如果没有找到指定属性则是否创建属性 */
+void do_fixup_by_path(void *fdt, const char *path, const char *prop,
+                     const void *val, int len, int create)
+{
+#if defined(DEBUG)
+       int i;
+       debug("Updating property '%s/%s' = ", path, prop);
+       for (i = 0; i < len; i++)
+               debug(" %.2x", *(u8*)(val+i));
+       debug("\n");
+#endif
+       int rc = fdt_find_and_setprop(fdt, path, prop, val, len, create);
+       if (rc)
+               printk("Unable to update property %s:%s, err=%s\n",
+                       path, prop, fdt_strerror(rc));
+}
+
+void do_fixup_by_path_u32(void *fdt, const char *path, const char *prop,
+                         u32 val, int create)
+{
+       fdt32_t tmp = cpu_to_fdt32(val);
+       do_fixup_by_path(fdt, path, prop, &tmp, sizeof(tmp), create);
+}
+
+int dtb_r8125_fixed(void *fdt)
+{
+       do_fixup_by_path(fdt, "/pcie@fe190000", "resettest-gpios", "fdt-fix-up", sizeof("fdt-fix-up") ,1);
+       return 0;
+}
 static void __init setup_machine_fdt(phys_addr_t dt_phys)
 {
        int size;
@@ -202,7 +264,10 @@
                while (true)
                        cpu_relax();
        }
-
+       /* 修改设备树的时机在此,此时 boot_command_line 已经解析出来了,虚拟地址也可用,从这里拿信息去修改dtb文件 */
+       if (dt_virt && !dtb_r8125_fixed(dt_virt)) {
+               pr_info("dtb_r8125_fixed() success\n");
+       }
        /* Early fixups are done, map the FDT as read-only now */
        fixmap_remap_fdt(dt_phys, &size, PAGE_KERNEL_RO);

@@ -325,6 +390,7 @@
 static void dgs_init(void);
 #endif
 #endif
+

注意点

  1. 这种方式并不可取,尽管能修改成功;但是需要考虑dtb文件头部的 totalsize 字段规定的大小;/memreserve/ a100000 22000 属性为dtb文件预留的内存空间是否足够。
  2. 需要使用映射过后的虚拟地址dt_virt来修改dtb文件;修改完成了dtb预留的内存空间会设置为只读属性。
  • 9
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值