一、参考资源
- 官网
//https://www.devicetree.org/
//可下载pdf手册 devicetree-specification-v0.4-rc1.pdf
//https://github.com/devicetree-org/devicetree-specification/tree/main/source 可网页翻译
- 指导文档
//内核文件中
Documentation/devicetree/bindings 指导如果编写设备树的协议文档
- 示例源码
//厂商BSP中提供的设备树 ~/bsp/Linux_for_Tegra/source/public/hardware/nvidia
//内核中发布的设备树 ~/kernel-4.9/arch/arm64/boot/dts/nvidia
二、编译运行
官方内核中发布的
编译
$ cd linux-4.9.307
$ make dtbs /* 编译生成设备树dtb 因为前面导入Nvidia的配置了,相关CONFIG已经选好,自动找到设备树目录编译
DTC arch/arm64/boot/dts/nvidia/tegra210-p2371-0000.dtb
DTC arch/arm64/boot/dts/nvidia/tegra210-p2371-2180.dtb
DTC arch/arm64/boot/dts/nvidia/tegra210-p2571.dtb
DTC arch/arm64/boot/dts/nvidia/tegra210-smaug.dtb
*/
{//arch/arm64/boot/dts/nvidia/Makefile
dtb-$(CONFIG_ARCH_TEGRA_132_SOC) += tegra132-norrin.dtb
dtb-$(CONFIG_ARCH_TEGRA_210_SOC) += tegra210-p2371-0000.dtb
dtb-$(CONFIG_ARCH_TEGRA_210_SOC) += tegra210-p2371-2180.dtb
dtb-$(CONFIG_ARCH_TEGRA_210_SOC) += tegra210-p2571.dtb
dtb-$(CONFIG_ARCH_TEGRA_210_SOC) += tegra210-smaug.dtb //指定编译的设备树名
always := $(dtb-y)
clean-files := *.dtb
}
tegra210-smaug.dts //描述开发板
tegra210.dtsi //描述cpu
注 //内核里发布的设备树,和厂商BSP给的有差异,以BSP里为准 (原因可能是厂商未同步发布到新版内核中)
运行
# setenv bootargs initrd=0xa0000000,0x800000 root=/dev/ram0 rw rootwait rootfstype=ext2 console=ttyS0,115200 init=/linuxrc ip=192.168.9.9 loglevel=8
# setenv yhai_bootcmd ext4load mmc 1:1 0x84000000 /home/yhbd/Image \; ext4load mmc 1:1 83100000 /home/yhbd/tegra210-smaug.dtb \; ext4load mmc 1:1 0xa0000000 /home/yhbd/ramdisk.img.gz \; booti 0x84000000 - 83100000
# mw 83100000 0 50 //注意:必须把设备树拷贝到的内存地址 清零,避免用之前残留的(否则会发现,用不存在或有问题的dtb文件也能启动内核)
# run yhai_bootcmd //运行(注:如发现和期望不一致,可手动的ext4load 下载每个文件看大小,甚至用md命令看二进制的值)
# cat /sys/firmware/devicetree/base/compatible //查看实际加载的设备树的名字
三、加载解析
U-BOOT与内核匹配检查
# bd /*查看板子信息结构体
arch_number = 0x0000000000000000 机器码(板子id) -> 在uboot和内核之间进行比对适配
内核启动后start kernel 报检测id错误Error: unrecognized/unsupported machine ID,就是不匹配导致。
boot_params = 0x0000000080000100 启动参数bootargs的存放地址
fdt_blob = 0x00000000fc5fa380
*/
# booti 0x84000000 - 83100000 //0x84000000为内核地址 -表示忽略ramdisk地址 83100000为设备树的地址
arm32 //启动有fdt addr时,会 dtb中的 compatible 属性与内核 DT_MACHINE_START 列表匹配,有则匹配能启动
//没有fdt addr时,则检测 机器ID 与 MACHINE_START(老版本)
arm64 //取消匹配检查
内核的设备树安装
arch/arm64/kernel/setup.c
//arch/arm64/kernel/setup.c
void __init setup_arch(char **cmdline_p)
{
pr_info("Boot CPU: AArch64 Processor [%08x]\n", read_cpuid_id());
setup_machine_fdt(__fdt_pointer); //设备树安装(__fdt_pointer 是 DTB位于内存中的物理地址)
unflatten_device_tree();//非扁平化,也就是把dtb构造成树的结构(避免每次都扫描dtb,慢)
}
static void __init setup_machine_fdt(phys_addr_t dt_phys)
{
const char *name;
void *dt_virt = fixmap_remap_fdt(dt_phys); /*因MMU已打开,要转为内核中能访问的虚地址
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);
if (!dt_virt)
return NULL;
memblock_reserve(dt_phys, size); 把这片内存给保留起来 -> 后续的内存分配中就不会使用DTB占用的内存
return dt_virt;
}
*/
if (!dt_virt || !early_init_dt_scan(dt_virt)) { //dtb扫描,完成早期初始化
pr_crit("\n" //blob: 是一种二进制数据格式
"Error: invalid device tree blob at physical address %pa (virtual address 0x%p)\n"
"The dtb must be 8-byte aligned and must not exceed 2 MB in size\n"
"\nPlease check your bootloader.",
&dt_phys, dt_virt);
while (true)
cpu_relax();
}
//更改了 dump_stack_set_arch_desc("%s (DT)", of_flat_dt_get_machine_name); 为下面方式添加了机器名的输出
name = of_flat_dt_get_machine_name(); /*获取设备的机器名(对应设备树里的model 和 compatible)
const char * __init of_flat_dt_get_machine_name(void)
{
const char *name;
unsigned long dt_root = of_get_flat_dt_root();
name = of_get_flat_dt_prop(dt_root, "model", NULL);
if (!name)
name = of_get_flat_dt_prop(dt_root, "compatible", NULL);
return name;
}
*/
if (!name){
pr_info("of_flat_dt_get_machine_name fail\n");
return;
}
pr_info("Machine model: %s\n", name); //输出设备名:如 NVIDIA Jetson Nano Developer Kit(对应设备树里的model) -> 看到该信息,才说明设备树加载成功
dump_stack_set_arch_desc("%s (DT)", name);
}
内核对设备树扫描和检测
drivers/of/fdt.c
//drivers/of/fdt.c
bool __init early_init_dt_scan(void *params)
{
bool status;
status = early_init_dt_verify(params); //设备树合法性验证
if (!status)
return false;
early_init_dt_scan_nodes(); /* 设备树的节点扫描 完成早期初始化
void __init early_init_dt_scan_nodes(void)
{
//扫描 /chosen 节点信息 (把bootargs属性值拷贝到boot_command_line)
of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line);
//扫描根节点的属性信息 {size,address}-cells
of_scan_flat_dt(early_init_dt_scan_root, NULL);
//扫描 memory节点,完成内存配置初始化
of_scan_flat_dt(early_init_dt_scan_memory, NULL);
}
*/
return true;
}
bool __init early_init_dt_verify(void *params)
{
if (!params)
return false;
/* check device tree validity */
if (fdt_check_header(params)) /*检查dtb的头信息
int fdt_check_header(const void *fdt)
{
if (fdt_magic(fdt) == FDT_MAGIC) { //检查dtb的幻数
//define FDT_MAGIC 0xd00dfeed 必须和前面dtb 开始二级制的值一致(00000000: d00d feed)
if (fdt_version(fdt) < FDT_FIRST_SUPPORTED_VERSION)
return -FDT_ERR_BADVERSION;
if (fdt_last_comp_version(fdt) > FDT_LAST_SUPPORTED_VERSION)
return -FDT_ERR_BADVERSION;
} else if (fdt_magic(fdt) == FDT_SW_MAGIC) {
if (fdt_size_dt_struct(fdt) == 0)
return -FDT_ERR_BADSTATE;
} else {
return -FDT_ERR_BADMAGIC;
}
return 0;
}
*/
return false;
/* Setup flat device-tree pointer */
initial_boot_params = params; //把DTB基地址放入到initial_boot_params
of_fdt_crc32 = crc32_be(~0, initial_boot_params, //计算crc32校验码
fdt_totalsize(initial_boot_params));
return true;
}
查看DTB二进制
$ vim -b arch/arm64/boot/dts/nvidia/tegra210-smaug.dtb
:%!xxd //转换为十六进制显示,如
%!xxd -r //返回原显示
//对应结构体
struct fdt_header {
fdt32_t magic; /* magic word FDT_MAGIC */
fdt32_t totalsize; /* total size of DT block */
fdt32_t off_dt_struct; /* offset to structure */
fdt32_t off_dt_strings; /* offset to strings */
fdt32_t off_mem_rsvmap; /* offset to memory reserve map */
fdt32_t version; /* format version */
};