下图引自:https://source.android.com/devices/architecture/dto/partitions
本文中所述工具和源码详见Google代码仓:
git clone https://android.googlesource.com/platform/system/libufdt
dt或者dto镜像在这里Google把它笼统地称作dtbo镜像,他们的格式是一样的,都是把多个dtc编译出来的dtb二进制或者dto二进制打包到一个image,在image的结尾加一个avb的校验签名或者各个厂商自定义的镜像签名。
多个硬件设备可能配置都不一样,寄存器或者内存大小的配置等。每个硬件设备对应一个dtb和dto,把多个dtb或者dto按照图示的格式打包成对应的dt.img和dto.img,这样软件可以做到共镜像,方便了厂商对产品的维护工作。
每个硬件设备怎么识别到对应自己的dtb和dto呢?这里就需要依赖dt_table_entry中的id、rev、custom这几个字段了,它作用是硬件标识。这样bootloader就可以根据硬件设备获取的标识加载匹配的dtb和dto了。
例如:单板配置一和二
jun@ubuntu:~/dtbo/libufdt/utils/tests/data$ cat board1v1.dts
/dts-v1/;
/plugin/;
/ {
compatible = "board_manufacturer,board_model";
board_id = <0x00010000>;
board_rev = <0x00010000>;
/* optional, the soc used by the board */
soc_id = <0x00020000>;
deviceB:deviceB {
};
};
&deviceB {
value = <0x1>;
status = "okay";
};
jun@ubuntu:~/dtbo/libufdt/utils/tests/data$ cat board2v1.dts
/dts-v1/;
/plugin/;
/ {
compatible = "board_manufacturer,board_model";
board_id = <0x00020000>;
board_rev = <0x00010000>;
/* optional, the soc used by the board */
soc_id = <0x00010000>;
deviceA: deviceA {
};
};
&deviceA {
value = <0x1>;
status = "okay";
};
安装dts编译器:
sudo apt install device-tree-compiler
jun@ubuntu:~/dtbo/libufdt/utils/tests/data$ dtc --version
Version: DTC 1.4.5
编译dts到dtb二进制格式:
dtc -O dtb board1v1.dts -o board1v1.dtb
dtc -O dtb board2v1.dts -o board2v1.dtb
使用mkdtboimg.py工具制作生成dt.img
注意:
version参数没有或者是0,代表打包前不对dtb进行压缩处理,如果version参数为1,并且配合flags参数可设置dtb压缩格式。
flags为0,不压缩dtb
flags为1,zlib压缩dtb
flags为2,gzip压缩dtb
jun@ubuntu:~/dtbo/libufdt/utils/tests/data$ ../../src/mkdtboimg.py create dt.img --page_size=4096 --flags=2 --version=1 board1v1.dtb --id=0x10000 board2v1.dtb --id=0x20000
jun@ubuntu:~/dtbo/libufdt/utils/tests/data$ ../../src/mkdtboimg.py dump dt.img
dt_table_header:
magic = d7b7ab1e
total_size = 429
header_size = 32
dt_entry_size = 32
dt_entry_count = 2
dt_entries_offset = 32
page_size = 4096
version = 1
dt_table_entry[0]:
dt_size = 166
dt_offset = 96
id = 00010000
rev = 00000000
custom[0] = 00000002
custom[1] = 00000000
custom[2] = 00000000
custom[3] = 00000000
dt_table_entry[1]:
dt_size = 167
dt_offset = 262
id = 00020000
rev = 00000000
custom[0] = 00000002
custom[1] = 00000000
custom[2] = 00000000
custom[3] = 00000000
dtbo镜像详细的数据结构定义:
#define DT_TABLE_MAGIC 0xd7b7ab1e
#define DT_TABLE_DEFAULT_PAGE_SIZE 2048
#define DT_TABLE_DEFAULT_VERSION 0
struct dt_table_header {
uint32_t magic; /* DT_TABLE_MAGIC */
uint32_t total_size; /* includes dt_table_header + all dt_table_entry
and all dtb/dtbo */
uint32_t header_size; /* sizeof(dt_table_header) */
uint32_t dt_entry_size; /* sizeof(dt_table_entry) */
uint32_t dt_entry_count; /* number of dt_table_entry */
uint32_t dt_entries_offset; /* offset to the first dt_table_entry
from head of dt_table_header.
The value will be equal to header_size if
no padding is appended */
uint32_t page_size; /* flash page size we assume */
uint32_t version; /* DTBO image version, the current version is 0.
The version will be incremented when the dt_table_header
struct is updated. */
};
// 下面定义的是dtb或dto支持的压缩格式,mkdtboimg.py命令行version必须为1,flags可设0~2
enum dt_compression_info {
NO_COMPRESSION, // mkdtboimg.py --version=1 --flags=0
ZLIB_COMPRESSION, // mkdtboimg.py --version=1 --flags=1
GZIP_COMPRESSION // mkdtboimg.py --version=1 --flags=2
};
// 下面这个结构体对应的是mkdtboimg.py脚本命令行参数version是0或默认的情况
struct dt_table_entry {
uint32_t dt_size;
uint32_t dt_offset; /* offset from head of dt_table_header */
uint32_t id; /* optional, must be zero if unused */
uint32_t rev; /* optional, must be zero if unused */
uint32_t custom[4]; /* optional, must be zero if unused */
};
// 下面这个结构体对应的是mkdtboimg.py脚本命令行参数version是1的情况
struct dt_table_entry_v1 {
uint32_t dt_size;
uint32_t dt_offset; /* offset from head of dt_table_header */
uint32_t id; /* optional, must be zero if unused */
uint32_t rev; /* optional, must be zero if unused */
uint32_t flags; /* For version 1 of dt_table_header, the 4 least significant bits
of 'flags' will be used indicate the compression
format of the DT entry as per the enum 'dt_compression_info' */
uint32_t custom[3]; /* optional, must be zero if unused */
};
bootloader中查找硬件设备对于的dtb或者dto示例代码:
#include <stdlib.h>
#include <stdio.h>
#include "../../src/dt_table.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <endian.h>
void *read_dtbo(char *ptn)
{
int ret;
char *dtbo;
struct stat buf = {0};
ret = stat(ptn, &buf);
if (ret != 0) {
printf("stat err\n");
return NULL;
}
dtbo = (char *)malloc(buf.st_size);
ret = open(ptn, O_RDONLY);
if (ret < 0) {
printf("open fail\n");
return NULL;
}
ret = read(ret, dtbo, buf.st_size);
if (ret != buf.st_size) {
printf("read err\n");
return NULL;
}
return dtbo;
}
unsigned int get_board_id(void)
{
return 0x20000;
}
void *load_dtbo(void)
{
char *dtbo;
struct dt_table_header *dtbo_head;
unsigned int board_id;
struct dt_table_entry_v1 *dtbo_entry;
dtbo = read_dtbo("dt.img");
if (!dtbo) {
printf("read_dtbo err!\n");
return NULL;
}
dtbo_head = (struct dt_table_header *)dtbo;
printf("magic %x\n", be32toh(dtbo_head->magic));
if (be32toh(dtbo_head->magic) != DT_TABLE_MAGIC) {
printf("Magic not match!\n");
return NULL;
}
board_id = get_board_id();
for (dtbo_entry = (struct dt_table_entry_v1 *)(dtbo + be32toh(dtbo_head->dt_entries_offset));
(char *)dtbo_entry < dtbo + be32toh(dtbo_head->dt_entries_offset) +
be32toh(dtbo_head->dt_entry_size) * be32toh(dtbo_head->dt_entry_count);
++dtbo_entry) {
if (be32toh(dtbo_entry->id) == board_id) {
printf("dtbo_entry info dt_size %x\n", be32toh(dtbo_entry->dt_size));
printf("dtbo_entry info dt_offset %x\n", be32toh(dtbo_entry->dt_offset));
printf("dtbo_entry info id %x\n", be32toh(dtbo_entry->id));
printf("dtbo_entry info rev %x\n", be32toh(dtbo_entry->rev));
break;
}
}
return dtbo_entry;
}
void main()
{
load_dtbo();
}