网站建设的常见问题,福建专业网站建设公司,企业互联网营销推广方案,做网站赚广告费好做吗本文主要记录了在友善之臂使用的基于瑞芯微github上uboot的rkdevelop分支#xff0c;以及瑞星微官方的stable-4.4-rk3399-linux分支中#xff0c;uboot如何给linux内核所需要的kernel-dtb的探究过程。 目录
0 - uboot代码准备
1 - 友善之臂版uboot如何获取要加载的设备树
… 本文主要记录了在友善之臂使用的基于瑞芯微github上uboot的rkdevelop分支以及瑞星微官方的stable-4.4-rk3399-linux分支中uboot如何给linux内核所需要的kernel-dtb的探究过程。 目录
0 - uboot代码准备
1 - 友善之臂版uboot如何获取要加载的设备树
1.1 - get_fdt_name 获取设备树dtb文件名
1.2 - get_content() 获取所需dtb的地址
2 - 瑞芯微github上stable-4.4版uboot如何获取设备树
2.1 - uboot-dtb
2.2 - kernel-dtb
2.2.1 - get_file_info() 直接在 resource.img 中查找 rk-kernel.dtb
2.2.2 - EARLY_DISTRO中寻找指定dtb
2.2.3 - HWID判断使用dtb 需要知道的一点是在arm64架构下linux已经弃用mach_xx等文件夹来描述板级信息供linux内核启动时去匹配根节点下的compatible属性来找到对应设备树。
取而代之是单纯的接收bootloader传递过来的单一设备树文件dtb所在的内存地址。所以当多个dtb文件存在镜像中或者内存中时如何找到需要的dtb的这个任务就落在了bootloader肩上。
0 - uboot代码准备
本文中主要涉及到了两个版本的uboot
友善之臂基于瑞芯微 rkdevelop 分支进行修改过的uboot瑞芯微官方github上stable-4.4-rk3399-linux 分支的uboot
分别可以通过以下方式获取。
https://gitlab.com/friendlyelec/rk3399-android-8.1/-/tree/master/u-boothttps://github.com/rockchip-linux/u-boot/tree/stable-4.4-rk3399-linux
1 - 友善之臂版uboot如何获取要加载的设备树
友善之臂用的版本是2014.10版的uboot
首先引用源自 common/cmd_bootrk.c rk_load_image_from_storage() /* loader fdt from resource if content.load_addr NULL */
#ifdef CONFIG_OF_LIBFDTif (!content.load_addr) {#ifdef CONFIG_OF_FROM_RESOURCEputs(load fdt from resouce.\n);content rkimage_load_fdt(get_disk_partition(RESOURCE_NAME));#endif}跟踪看到board/rockchip/common/rkloader/rkimage.c rkimage_load_fdt()
resource_content rkimage_load_fdt(const disk_partition_t* ptn)
{resource_content content;snprintf(content.path, sizeof(content.path), %s, get_fdt_name());content.load_addr 0;#ifndef CONFIG_RESOURCE_PARTITIONreturn content;
#elseif (!ptn)return content;if (!strcmp((char*)ptn-name, BOOT_NAME)|| !strcmp((char*)ptn-name, RECOVERY_NAME)) {//load from bootimgs second data area.unsigned long blksz ptn-blksz;int offset 0;rk_boot_img_hdr *hdr NULL;
#ifdef CONFIG_RK_NVME_BOOT_ENhdr memalign(SZ_4K, blksz 2);
#elsehdr memalign(ARCH_DMA_MINALIGN, blksz 2);
#endifif (StorageReadLba(ptn-start, (void *) hdr, 1 2) ! 0) {return content;}if (!memcmp(hdr-magic, BOOT_MAGIC,BOOT_MAGIC_SIZE) hdr-second_size) {//compute second data areas offset.offset ptn-start (hdr-page_size / blksz);offset ALIGN(hdr-kernel_size, hdr-page_size) / blksz;offset ALIGN(hdr-ramdisk_size, hdr-page_size) / blksz;if (get_content(offset, content))load_content(content);}return content;}//load from spec partition.if (get_content(ptn-start, content))load_content(content);return content;
#endif
}对于上面两个红框处我们分别讨论
1.1 - get_fdt_name 获取设备树dtb文件名
其代码本质是调用 getenv 从环境变量中取得 dtb_name变量上的字符串
static const char* get_fdt_name(void)
{char *name getenv(dtb_name);if (name)return name;if (!gBootInfo.fdt_name[0]) {return FDT_PATH;}return gBootInfo.fdt_name;
}而这个“dtb_name“变量中包含的字符串就是我们需要的dtb文件名。它是在 board/rockchip/rk33xx/rk33xx.c rk33xx.c文件中被写入的
/** Board revision list: GPIO4_D1 | GPIO4_D0* 0b00 - NanoPC-T4* 0b01 - NanoPi M4** Extended by ADC_IN4* Group A:* 0x04 - NanoPi NEO4* 0x06 - SOC-RK3399** Group B:* 0x21 - NanoPi M4 Ver2.0*/
static int pcb_rev -1;static void bd_hwrev_init(void)
{gpio_direction_input(GPIO_BANK4 | GPIO_D0);gpio_direction_input(GPIO_BANK4 | GPIO_D1);pcb_rev gpio_get_value(GPIO_BANK4 | GPIO_D0);pcb_rev | (gpio_get_value(GPIO_BANK4 | GPIO_D1) 1);if (pcb_rev 0x3) {/* Revision group A: 0x04 ~ 0x13 */pcb_rev 0x4 get_adc_index(4);} else if (pcb_rev 0x1) {int idx get_adc_index(4);/* Revision group B: 0x21 ~ 0x2f */if (idx 0) {pcb_rev 0x20 idx;}}
}/* To override __weak symbols */
u32 get_board_rev(void)
{return pcb_rev;
}static void set_dtb_name(void)
{char info[64] {0, };snprintf(info, ARRAY_SIZE(info),rk3399-nanopi4-rev%02x.dtb, get_board_rev());setenv(dtb_name, info);
}我们因此知道了友善之臂的nanopi系列板子是通过板上 GPIO4_D1、GPIO4_D0 的输入电平以及 ADC_IN4 脚的输入电压来判断载入什么dtb的
1.2 - get_content() 获取所需dtb的地址 rkimage_load_fdt() 函数中将 “dev_name” 中的设备树名写入到content.path变量中。在 common/resource.c get_content() 函数中
bool get_content(int base_offset, resource_content* content) {bool ret false;index_tbl_entry entry;debug(get_content: base_offset 0x%x\n, base_offset);if (!base_offset) {base_offset get_base_offset();}if (!base_offset) {FBTERR(base offset is NULL!\n);goto end;}if (!get_entry(base_offset, content-path, entry))goto end;content-content_offset entry.content_offset base_offset;content-content_size entry.content_size;ret true;
end:return ret;
} 将 content.path 传入 get_entry() 函数其函数修改传入的 entry 参数变量。get_entry() 函数也在common/resource.c
static bool get_entry(int base_offset, const char* file_path,index_tbl_entry* entry) {。。。ret get_entry_ram(header, table, header.tbl_entry_num* header.tbl_entry_size * BLOCK_SIZE,file_path, entry);end:if (table) {free(table);}return ret;
}可知其调用 get_entry_ram 函数将指定dtb文件地址解析并传送到 entry 中。在 common/resource.c get_entry_ram()函数中
。。。
for (i 0; i header.tbl_entry_num; i) {//TODO: support tbl_entry_sizememcpy(entry, table i * header.tbl_entry_size * BLOCK_SIZE,sizeof(*entry));if (memcmp(entry-tag, INDEX_TBL_ENTR_TAG,sizeof(entry-tag))) {FBTERR(Something wrong with index entry:%d!\n, i);goto end;}FBTDBG(Lookup entry(%d):\n\tpath:%s\n\toffset:%d\tsize:%d\n,i, entry-path, entry-content_offset,entry-content_size);if (!strncmp(entry-path, file_path, sizeof(entry-path)))break;}
。。。 从resource镜像的头部中寻找对应的设备树名一条一条比对当比对当前在entry-path中的设备树名与需要的设备树名比对相同时退出。 resource.img 镜像大概长这样 2 - 瑞芯微github上stable-4.4版uboot如何获取设备树
stable-4.4版本的uboot2017.09使用类似现在linux的架构目录。uboot类似kernel也有了自己的设备树方便自身加载初始化。因此这里分两个部分讨论一个是uboot自己的设备树 uboot-dtb 一个是linux需要的设备树 kernel-dtb。
2.1 - uboot-dtb
在 uboot/configs 目录下存放各种defconfig其中包含有一项配置例如firefly的firefly-rk3399 直接指定了使用什么默认设备树
在 uboot\arch\arm\dts\rk3399-firefly.dts 中我们可以看到
/** Copyright (c) 2017 Fuzhou Rockchip Electronics Co., Ltd.** SPDX-License-Identifier: GPL-2.0*//dts-v1/;
#include dt-bindings/pwm/pwm.h
#include dt-bindings/pinctrl/rockchip.h
#include rk3399.dtsi
#include rk3399-sdram-ddr3-1600.dtsi
#include rk3399-u-boot.dtsi/ {model Firefly-RK3399 Board;compatible firefly,firefly-rk3399, rockchip,rk3399;依据这个来进行uboot中设备树的匹配但是注意这个是uboot自用的设备树并不是要传递给kernel的
2.2 - kernel-dtb
在 arch\arm\mach-rockchip\resource_img.c rockchip_read_dtb_file()函数中我们可以看到
。。。
int rockchip_read_dtb_file(void *fdt_addr)
{struct resource_file *file;char *def_dtb DTB_FILE;int ret;/* search order: rk-kernel.dtb - distro - hwid */file get_file_info(NULL, def_dtb);if (!file) {
#ifdef CONFIG_ROCKCHIP_EARLY_DISTRO_DTBret rockchip_read_distro_dtb(fdt_addr);if (ret 0)return ret; /* found load done */
#endif
#ifdef CONFIG_ROCKCHIP_HWID_DTBfile rockchip_read_hwid_dtb();
#endifif (!file)return -ENODEV;}
。。。 从resource.img中获取kernel-dtb有三个阶段图中的红、绿、蓝三框。
2.2.1 - get_file_info() 直接在 resource.img 中查找 rk-kernel.dtb
首先是去resource.img中寻找有没有叫“rk-kernel.dtb”的这个设备树文件宏定义DTB_FILE在 arch\arm\mach-rockchip\resource_img.c 中
static struct resource_file *get_file_info(struct resource_img_hdr *hdr,const char *name)
{struct resource_file *file;struct list_head *node;if (list_empty(entrys_head)) {if (init_resource_list(hdr))return NULL;}list_for_each(node, entrys_head) {file list_entry(node, struct resource_file, link);if (!strcmp(file-name, name))return file;}return NULL;
} 2.2.2 - EARLY_DISTRO中寻找指定dtb
如果 2.2.1 搜索失败了没有rk-kernel.dtb且定义了 CONFIG_ROCKCHIP_EARLY_DISTRO_DTB 则调用 rockchip_read_distro_dtb(fdt_addr)函数去寻找在arch\arm\mach-rockchip\resource_img.c 中
#ifdef CONFIG_ROCKCHIP_EARLY_DISTRO_DTB
static int rockchip_read_distro_dtb(void *fdt_addr)
{const char *cmd part list ${devtype} ${devnum} -bootable devplist;char *devnum, *devtype, *devplist;char devnum_part[12];char fdt_hex_str[19];char *fs_argv[5];int size;int ret;if (!rockchip_get_bootdev() || !fdt_addr)return -ENODEV;ret run_command_list(cmd, -1, 0);if (ret)return ret;
。。。 在 arch\arm\mach-rockchip\Kconfig中我们可以看到如果定义了 ROCKCHIP_EARLY_DISTRO_DTB则我们还可以自己定义在镜像文件中dtb的名称 2.2.3 - HWID判断使用dtb
如果2.2.1失败了没有rk-kernel.dtb且定义了CONFIG_ROCKCHIP_HWID_DTB 则调用rockchip_read_hwid_dtb()函数读取硬件引脚来判断使用什么dtb在arch\arm\mach-rockchip\resource_img.c 中 /* Get according to hardware id(GPIO/ADC) */
static struct resource_file *rockchip_read_hwid_dtb(void)
{struct resource_file *file;struct list_head *node;/* Find dtb file according to hardware id(GPIO/ADC) */list_for_each(node, entrys_head) {file list_entry(node, struct resource_file, link);if (!strstr(file-name, .dtb))continue;if (strstr(file-name, KEY_WORDS_ADC_CTRL) strstr(file-name, KEY_WORDS_ADC_CH) !rockchip_read_dtb_by_adc(file-name)) {return file;} else if (strstr(file-name, KEY_WORDS_GPIO) !rockchip_read_dtb_by_gpio(file-name)) {return file;}}return NULL;
}