如果你的 DTB/DTBO 位于专属的分区(例如 dtb 和 dtbo 分区)中,请使用以下表格结构和头文件格式:

数据结构
dt_table_header 仅适用于 dtb/dtbo 分区;您不能在kernel( image.gz) 末尾处附加此格式。如果您有一个 DTB/DTBO,则仍必须使用此格式(并且,dt_table_header 中的 dt_entry_count 为 1)。
#define DT_TABLE_MAGIC 0xd7b7ab1e
#define DT_TABLE_DEFAULT_PAGE_SIZE 2048
#define DT_TABLE_DEFAULT_VERSION 0
struct dt_table_header { //0x20=32字节uint32_t magic; // DT_TABLE_MAGICuint32_t total_size; // includes dt_table_header + all dt_table_entry // and all dtb/dtbo 整个文件的大小uint32_t header_size; // sizeof(dt_table_header) 固定的大小 0x20uint32_t dt_entry_size; // sizeof(dt_table_entry) 固定的大小 0x20uint32_t dt_entry_count; // number of dt_table_entry dt条目的个数uint32_t dt_entries_offset; // offset to the first dt_table_entry 固定的偏移量 0x20// from head of dt_table_header // 从 dt_table_header 的头部到第一个 dt_table_entry 的偏移量uint32_t page_size; // flash page size we assumeuint32_t version; // DTBO image version, the current version is 0.// The version will be incremented when the// dt_table_header struct is updated.
};struct dt_table_entry { //0x20=32字节uint32_t dt_size;uint32_t dt_offset; // offset from head of dt_table_headeruint32_t id; // optional, must be zero if unuseduint32_t rev; // optional, must be zero if unuseduint32_t custom[4]; // optional, must be zero if unused
};
使用经过压缩的叠加层
Android 9 增加了以下支持:在使用第 1 版设备树表格头文件时,在 DTBO 映像中使用经过压缩的叠加层。 使用 DTBO 头文件 v1 时,dt_table_entry 中标记字段的四个最低有效位会指明 DT 条目的压缩格式。
struct dt_table_entry_v1 { //0x20=32字节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 bitsof 'flags' will be used to indicate the compressionformat of the DT entry as per the enum 'dt_compression_info' */uint32_t custom[3]; /* optional, must be zero if unused */
};
目前,系统支持 zlib 和 gzip 压缩。
enum dt_compression_info {NO_COMPRESSION,ZLIB_COMPRESSION,GZIP_COMPRESSION
};
Android 9 增加了以下支持:在 VtsFirmwareDtboVerification 测试中测试经过压缩的叠加层,以帮助您验证叠加应用的正确性。
代码
/system/libufdt/utils/src/
dt_table.h:
enum DT_TYPE { DTB, ACPI };
void dt_table_header_init(struct dt_table_header *header, enum DT_TYPE dt_type);dt_table.c:
void dt_table_header_init(struct dt_table_header *header, enum DT_TYPE dt_type) {const uint32_t header_size = sizeof(struct dt_table_header);const uint32_t entry_size = sizeof(struct dt_table_entry);dto_memset(header, 0, header_size); //清零void *dto_memset(void *s, int c, size_t n) { return memset(s, c, n); }if (dt_type == ACPI)header->magic = cpu_to_fdt32(ACPI_TABLE_MAGIC); // dtb/dtbo的存储格式是大端字节序elseheader->magic = cpu_to_fdt32(DT_TABLE_MAGIC);header->total_size = cpu_to_fdt32(header_size);header->header_size = cpu_to_fdt32(header_size);header->dt_entry_size = cpu_to_fdt32(entry_size);header->dt_entries_offset = cpu_to_fdt32(header_size);header->page_size = cpu_to_fdt32(DT_TABLE_DEFAULT_PAGE_SIZE);header->version = cpu_to_fdt32(DT_TABLE_DEFAULT_VERSION);
}
//system/libufdt/utils/src/mkdtimg_core.c
output_img_header(writer->img_fp, //输出文件的文件指针writer->entry_count, //dtb文件的个数,动态变化,见该结构体struct dt_image_writer {}的解释writer->dt_offset, //dt文件的偏移量,动态变化,见该结构体struct dt_image_writer {}的解释&writer->global_options) //全局选项
static int output_img_header(FILE *img_fp,uint32_t entry_count, uint32_t total_size,struct dt_global_options *options) {struct dt_table_header header;dt_table_header_init(&header, options->dt_type);header.dt_entry_count = cpu_to_fdt32(entry_count);header.total_size = cpu_to_fdt32(total_size);header.page_size = cpu_to_fdt32(options->page_size);header.version = cpu_to_fdt32(options->version);fseek(img_fp, 0, SEEK_SET);fwrite(&header, sizeof(header), 1, img_fp);return 0;
}
mkdtimg工具链接:
https://android-review.googlesource.com/c/platform/system/libufdt/+/347649
https://cs.android.com/android/platform/superproject/+/master:system/libufdt/utils/src/Android.bp
mkdtimg有哪些功能?
struct command_info {const char *command;void (*usage)(FILE *out_fp, const char *prog_name); //输出命令的帮助信息int (*handler)(int argc, char *argv[], int arg_start); //命令的处理
};static const struct command_info command_infos[] = { //总共有4个子功能{ "help", handle_usage_help, handle_command_help }, //mkdtimg help all打印所有帮助信息{ "dump", handle_usage_dump, handle_command_dump },{ "create", handle_usage_create, handle_command_create },{ "cfg_create", handle_usage_cfg_create, handle_command_cfg_create },{ NULL, NULL, NULL }
};
- 功能1:create 功能
./mkdtimg create <image_file> (<global_option>…) (<dtb_file>> (<entry_option>…) …)
global_options:--dt_type=<type> Device Tree type (dtb|acpi). Default: dtb--page_size=<number> Output page size. Default: 2048--version=<version> DTBO version. Default: 0--id=<number|path> The default value to set property id in dt_table_entry. Default: 0--rev=<number|path>--custom0=<number|path>--custom1=<number|path>--custom2=<number|path>--custom3=<number|path>The value could be a number or a DT node path.<number> could be a 32-bits digit or hex value, ex. 68000, 0x6800.<path> format is <full_node_path>:<property_name>, ex. /board/:id,will read the value in given FTB file with the path.
struct dt_options {#define OPTION_VALUE_SIZE_MAX 512char id[OPTION_VALUE_SIZE_MAX]; char rev[OPTION_VALUE_SIZE_MAX];char custom[4][OPTION_VALUE_SIZE_MAX];
};struct dt_global_options { //对应上面帮助信息的global_optionsstruct dt_options default_options;enum DT_TYPE dt_type;uint32_t page_size;uint32_t version;
};struct dt_image_writer_fdt_info {char filename[1024];uint32_t dt_offset;
};struct dt_image_writer {FILE *img_fp; //制作的镜像的文件指针struct dt_global_options global_options; // create相关的global选项信息struct dt_options entry_options; // create相关的dt条目选项char entry_filename[1024];uint32_t entry_count; //dto的条目个数uint32_t entry_offset; //dto的条目偏移量= 开始为sizeof(struct dt_table_header),后面每次处理一个dt文件,递增sizeof(struct dt_table_entry)uint32_t dt_offset; //dt的偏移量= 开始为writer->entry_offset + sizeof(struct dt_table_entry) * entry_count;后面每次处理一个dt文件,递增 该dt文件的大小struct dt_image_writer_fdt_info *fdt_infos; // fdt信息=malloc(sizeof(struct dt_image_writer_fdt_info) * entry_count);uint32_t fdt_info_count; //fdt信息个数,开始为0,后面每次处理一个dt文件,递增1
};
// mkdtimg create <image_file> (<global_option>...) (<dtb_file>> (<entry_option>...) ...)的实际处理程序
int handle_command_create(int argc, char *argv[], int arg_start) { //arg_start = 2int ret = -1;FILE *img_fp = NULL;if (argc - arg_start < 1) {handle_usage_create(stderr, argv[0]);goto end;}const char *img_filename = argv[arg_start]; //argv[2] = "<image_file>"printf("create image file: %s...\n", img_filename);img_fp = fopen(img_filename, "wb");if (img_fp == NULL) {fprintf(stderr, "Can not create file: %s\n", img_filename);goto end;}ret = output_img_with_args(img_fp, argc, argv, arg_start + 1); //实际的处理程序if (ret < 0) fprintf(stderr, "Can not output image with args\n");end:if (img_fp) {fclose(img_fp);if (ret < 0) unlink(img_filename);}return ret;
}
//output_img_with_args(img_fp, argc, argv, arg_start + 1);
//参数1是输出的文件,
//参数4是从(<global_option>...) (<dtb_file>> (<entry_option>...) ...)中的<global_option>...开始的
static int output_img_with_args(FILE *img_fp, int argc, char *argv[], int arg_start) { //arg_start = 3int entry_count = calculate_args_entry_count(argc, argv, arg_start); //计算参数有多少个dtb条目(非选项参数个数)struct dt_image_writer *writer = dt_image_writer_start(img_fp, entry_count);//根据dtb条目的个数来创建一个(struct dt_image_writer{ ... })结构体变量并初始化,int is_entry = 0;int i;for (i = arg_start; i < argc; i++) {char *arg = argv[i]; //循环的参数char *option, *value;if (parse_arg(&option, &value, arg) != 0) { //解析参数,返回0为解析ok,返回-1解析失败fprintf(stderr, "Wrong syntax: %s\n", arg);return -1;}if (option == NULL) { //解析的返回值option为NULL,说明这是一个文件名/* This is a file name */if (dt_image_writer_add_entry(writer, arg) != 0) { //循环处理dt文件://1. 将该dt文件写到img_fp文件里面,由于开始writer->entry_filename赋值为"\0",所以直接return 0,不写dt文件到img_fp文件//2. 将dt文件名保存在writer->entry_filename//3. 将writer->global_options.default_options的选项赋值到writer->entry_optionsreturn -1;}is_entry = 1;continue;} //否则,解析的返回值option不为NULL,说明这是一个选项,接下来做选项处理int ret = is_entry ? //根据is_entry(是一个条目)这个变量判断来设置条目选项还是全局选项set_entry_options(writer, option, value) :set_global_options(writer, option, value);if (ret != 0) {fprintf(stderr, "Unknown option: %s\n", option);return -1;}} /* for all argv */if (dt_image_writer_end(writer) != 0) { //写最后一个dt文件到镜像,写镜像头到镜像return -1;}return 0;
}
- 功能2:cfg_create 功能
与create功能相同,均是create dtb/dtbo镜像,区别在于create是从命令行读取参数,cfg_create是从配置文件中读取相关参数
$ mkdtimg cfg_create
mkdtimg cfg_create <image_file> <config_file> (…)options:-d, --dtb-dir The path to load dtb files.Default is load from the current path.
- 功能3:dump 功能
$ mkdtimg dump
mkdtimg dump <image_file> (…)options:-o, --output <filename> Output file name.Default is output to stdout.-b, --dtb <filename> Dump dtb/dtbo files from image.Will output to <filename>.0, <filename>.1, etc.
- 功能4:help 功能
$ mkdtimg help
mkdtimg help all mkdtimg helpcommands:help, dump, create, cfg_create













