文章目录
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文件的拷贝长度,拷贝完成后校验文件是否正确。下面根据代码逐个解答。
-
dtb文件存放在内存的地址有环境变量 “fdt_addr_r” 指定
/* 从环境变量中获取dtb文件读取到内存中的地址 */ fdt_addr = env_get_ulong("fdt_addr_r", 16, 0);
-
确认文件名
在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” 文件,通过下面打印可以确认:
-
从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);
-
校验内存中的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、问题现象
-
uboot下使用 fdt 命令新增属性失败。
=> fdt set /pcie@fe190000 local-mac 22-22-22-22-22 libfdt fdt_setprop(): FDT_ERR_NOSPACE
-
调用 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个步骤,
- 将emmc中的dtb文件读取到内存地址;
- 对内存中的dtb文件进行头部信息magic字段校验以及真个dtb文件hash校验。
- 调用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、验证以及风险
-
修改属性能成功
=> fdt print /pcie@fe190000 status status = "disabled"
-
新增属性能成功
=> fdt set /pcie@fe190000 local-mac 22-22-22-22-22 => fdt print /pcie@fe190000 local-mac local-mac = "22-22-22-22-22"
-
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
函数中;原因如下:
- 在执行此函数之前
boot_relocate_fdt
函数已经将dtb文件头部信息 totalsize:0x21ae4 改成 0x24ae4增加了0x3000 字节的空间,所以do_fixup_by_path
函数修改或新增属性有足够的空间。 - 此函数本身就要将uboot中的环境变量更新到dtb文件中,比如
serial-number
,local-mac-address
,bootargs
等环境变量更新到dtb文件中。 - 修改完成后
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
+
注意点
- 这种方式并不可取,尽管能修改成功;但是需要考虑dtb文件头部的 totalsize 字段规定的大小;
/memreserve/ a100000 22000
属性为dtb文件预留的内存空间是否足够。 - 需要使用映射过后的虚拟地址dt_virt来修改dtb文件;修改完成了dtb预留的内存空间会设置为只读属性。