瑞芯微Rockchip开发者社区
直播中

李玉鑫

7年用户 1253经验值
私信 关注
[问答]

uboot如何给linux内核所需要的kernel-dtb呢

uboot如何获取要加载的设备树呢?uboot如何给linux内核所需要的kernel-dtb呢?

回帖(2)

张梅

2022-3-4 15:44:44
0 - uboot代码准备

本文中主要涉及到了两个版本的uboot



  • 友善之臂基于瑞芯微 rkdevelop 分支进行修改过的uboot
  • 瑞芯微官方github上stable-4.4-rk3399-linux 分支的uboot

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_LIBFDT
        if (!content.load_addr) {
                #ifdef CONFIG_OF_FROM_RESOURCE
                puts("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_PARTITION
        return content;
#else
        if (!ptn)
                return content;

        if (!strcmp((char*)ptn->name, BOOT_NAME)
                        || !strcmp((char*)ptn->name, RECOVERY_NAME)) {
                //load from bootimg's second data area.
                unsigned long blksz = ptn->blksz;
                int offset = 0;
                rk_boot_img_hdr *hdr = NULL;
#ifdef CONFIG_RK_NVME_BOOT_EN
                hdr = memalign(SZ_4K, blksz << 2);
#else
                hdr = memalign(ARCH_DMA_MINALIGN, blksz << 2);
#endif
                if (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 area's 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:
*  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%xn", 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_size
                memcpy(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):ntpath:%sntoffset:%dtsize:%dn",
                                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版本的uboot(2017.09)使用类似现在linux的架构目录。uboot类似kernel也有了自己的设备树,方便自身加载初始化。因此这里分两个部分讨论,一个是uboot自己的设备树 uboot-dtb ,一个是linux需要的设备树 kernel-dtb。

2.1 - uboot-dtb


在 uboot/configs 目录下存放各种defconfig,其中包含有一项配置,例如firefly的firefly-rk3399



直接指定了使用什么默认设备树

在 ubootarcharmdtsrk3399-firefly.dts 中我们可以看到

/*
* Copyright (c) 2017 Fuzhou Rockchip Electronics Co., Ltd.
*
* SPDX-License-Identifier:     GPL-2.0+
*/

/dts-v1/;
#include
#include
#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


在 archarmmach-rockchipresource_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_DTB
                ret = rockchip_read_distro_dtb(fdt_addr);
                if (ret > 0)
                        return ret; /* found & load done */
#endif
#ifdef CONFIG_ROCKCHIP_HWID_DTB
                file = rockchip_read_hwid_dtb();
#endif
                if (!file)
                        return -ENODEV;
        }
。。。


从resource.img中获取kernel-dtb有三个阶段(图中的红、绿、蓝三框)。

< >2.2.1 - get_file_info() 直接在 resource.img 中查找 rk-kernel.dtb
举报

杨晓静

2022-3-4 15:44:47
首先是去resource.img中寻找有没有叫“rk-kernel.dtb”的这个设备树文件(宏定义DTB_FILE),在 archarmmach-rockchipresource_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)函数去寻找,在archarmmach-rockchipresource_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;
。。。


在 archarmmach-rockchipKconfig中我们可以看到,如果定义了 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,在archarmmach-rockchipresource_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;
}


举报

更多回帖

发帖
×
20
完善资料,
赚取积分