直接和kernel编译在一起,生成zImage-dtb,dtb的位置在kernel起始地址偏移0x2C的位置,然后和kernel一起打包到bootimage里。
独立编译出dt.img, 然后打包到bootimage里。
dtb怎么编译进bootimage的?
makefile文件分析
.config 文件里生成的内容如下
CONFIG_ARCH_MSM8916=y
CONFIG_ARCH_MSM8937=y
CONFIG_ARCH_MSM8917=y
CONFIG_ARCH_MSM8920=y
CONFIG_ARCH_MSM8940=y
CONFIG_ARCH_MSM8953=y
kernel/msm-3.18/arch/arm/boot/dts/qcom/Makefile
(只要这些arch定义了 这些dtb文件都会被包含进去)
dtb-$(CONFIG_ARCH_MDMCALIFORNIUM) += mdmcalifornium-sim.dtb \
mdmcalifornium-rumi.dtb \
…
mdmcalifornium-v1.1-nand-mtp.dtb \
mdmcalifornium-v1.1-nand-dualwifi-mtp.dtb
dtb-$(CONFIG_ARCH_MSM8937) += msm8937-rumi.dtb \
msm8937-pmi8950-cdp.dtb \
.......
apq8037-pmi8950-mtp.dtb \
apq8037-pmi8937-mtp.dtb
dtb-$(CONFIG_ARCH_MSM8917) += msm8917-rumi.dtb \
apq8017-pmi8937-cdp.dtb \
…
msm8917-qgp-tmo.dtb \
msm8917-pmi8937-qrd-sku5.dtb
.config 文件里生成的内容如下
CONFIG_USE_OF=y
CONFIG_ATAGS=y
# CONFIG_DEPRECATED_PARAM_STRUCT is not set
CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE=y
CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE_NAMES=
kernel/msm-3.18/arch/arm/boot/Makefile
DTB_NAMES := $(subst $\",,$(CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE_NAMES))
ifneq ($(DTB_NAMES),)
DTB_LIST := $(addsuffix .dtb,$(DTB_NAMES))
else
DTB_LIST := $(dtb-y)
endif
DTB_OBJS := $(addprefix $(obj)/dts/qcom/,$(DTB_LIST))
因为CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE_NAMES 这个为空,
所以
DTB_LIST := $(dtb-y)
DTB_OBJS := $(obj)/dts/qcom/$(dtb-y)
需要编译zImage-dtb
# Default target when executing plain make
ifeq ($(CONFIG_XIP_KERNEL),y)
KBUILD_IMAGE := xipImage
else ifeq ($(CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE),y)
KBUILD_IMAGE := zImage-dtb
else
KBUILD_IMAGE := zImage
endif
$(obj)/Image: vmlinux FORCE
$(call if_changed,objcopy)
@$(kecho) ' Kernel: $@ is ready'
$(obj)/compressed/vmlinux: $(obj)/Image FORCE
$(Q)$(MAKE) $(build)=$(obj)/compressed $@
$(obj)/zImage: $(obj)/compressed/vmlinux FORCE
$(call if_changed,objcopy)
@$(kecho) ' Kernel: $@ is ready'
$(obj)/zImage-dtb: $(obj)/zImage $(DTB_OBJS) FORCE
$(call if_changed,cat)
@echo ' Kernel: $@ is ready'
可以看出zImage-dtb 依赖于 $(obj)/zImage $(DTB_OBJS)
%.dtb: | scripts
$(Q)$(MAKE) $(build)=$(boot)/dts MACHINE=$(MACHINE) $(boot)/dts/$@
dtbs: scripts
$(Q)$(MAKE) $(build)=$(boot)/dts MACHINE=$(MACHINE) dtbs
$(foreach DIR, $(DTSSUBDIR), $(Q)$(MAKE) $(build)=$(boot)/dts/$(DIR) MACHINE=$(MACHINE) dtbs)
zImage-dtb: vmlinux scripts dtbs
$(Q)$(MAKE) $(build)=$(boot) MACHINE=$(MACHINE) DTSSUBDIR=$(DTSSUBDIR) $(boot)/$@
查看kernel的二进制文件,查看0x2C位置的值,此处存放的值是第一个dtb文件的偏移地址
从图中可以看出dtb所在的地址为0x008e7db0 + 0x800 = 0x008e85b0
0x008e85b0地址可以看到magic 0xd00dfeed(大端)
0x008e85b0 + 0x0002bef5 = 0x009144A5为下一个dtb的起始地址
0x009144A5地址可以看到新的dtb开始,看到magic 0xd00dfeed(大端)
app\aboot\aboot.c
boot_linux_from_mmc
dt_size = hdr->dt_size;
if(dt_size) {
…
}else{
dtb = dev_tree_appended()
}
void *dev_tree_appended(void *kernel, uint32_t kernel_size, uint32_t dtb_offset, void *tags)
{
if (dtb_offset)
app_dtb_offset = dtb_offset;
else
memcpy((void*) &app_dtb_offset, (void*) (kernel + DTB_OFFSET), sizeof(uint32_t));
....
while (((uintptr_t)dtb + sizeof(struct fdt_header)) < (uintptr_t)kernel_end) {
struct fdt_header dtb_hdr;
uint32_t dtb_size;
/* the DTB could be unaligned, so extract the header,
* and operate on it separately */
memcpy(&dtb_hdr, dtb, sizeof(struct fdt_header));
if (fdt_check_header((const void *)&dtb_hdr) != 0 ||
fdt_check_header_ext((const void *)&dtb_hdr) != 0 ||
((uintptr_t)dtb + (uintptr_t)fdt_totalsize((const void *)&dtb_hdr) < (uintptr_t)dtb) ||
((uintptr_t)dtb + (uintptr_t)fdt_totalsize((const void *)&dtb_hdr) > (uintptr_t)kernel_end))
break;
dtb_size = fdt_totalsize(&dtb_hdr);
dev_tree_compatible(dtb, dtb_size, dt_entry_queue);
/* goto the next device tree if any */
dtb += dtb_size;
}
best_match_dt_entry = platform_dt_match_best(dt_entry_queue);
if (best_match_dt_entry){
bestmatch_tag = (void *)best_match_dt_entry->offset;
bestmatch_tag_size = best_match_dt_entry->size;
dprintf(INFO, "Best match DTB tags %u/%08x/0x%08x/%x/%x/%x/%x/%x/%x/%x\n",
best_match_dt_entry->platform_id, best_match_dt_entry->variant_id,
best_match_dt_entry->board_hw_subtype, best_match_dt_entry->soc_rev,
best_match_dt_entry->pmic_rev[0], best_match_dt_entry->pmic_rev[1],
best_match_dt_entry->pmic_rev[2], best_match_dt_entry->pmic_rev[3],
best_match_dt_entry->offset, best_match_dt_entry->size);
dprintf(INFO, "Using pmic info 0x%0x/0x%x/0x%x/0x%0x for device 0x%0x/0x%x/0x%x/0x%0x\n",
best_match_dt_entry->pmic_rev[0], best_match_dt_entry->pmic_rev[1],
best_match_dt_entry->pmic_rev[2], best_match_dt_entry->pmic_rev[3],
board_pmic_target(0), board_pmic_target(1),
board_pmic_target(2), board_pmic_target(3));
}
.......................
return NULL;
}
其中DTB_OFFSET的定义为0x2C,与上一节的图相对应。
根据偏移值找到dtb的位置,然后对每个dtb进行解析,找到最匹配的那个dtb,这个是有dev_tree_compatible(dtb, dtb_size, dt_entry_queue);函数来实现的。
dev_tree_compatible(void *dtb, uint32_t dtb_size, struct dt_entry_node *dtb_list)
platform_dt_absolute_match(cur_dt_entry, dtb_list)
dev_tree_compatible会调用platform_dt_absolute_match来判断
static int platform_dt_absolute_match(struct dt_entry *cur_dt_entry, struct dt_entry_node *dt_list)
{
uint32_t cur_dt_hlos_ddr;
uint32_t cur_dt_hw_platform;
uint32_t cur_dt_hw_subtype;
uint32_t cur_dt_msm_id;
dt_node *dt_node_tmp = NULL;
/* Platform-id
* bit no |31 24|23 16|15 0|
* |reserved|foundry-id|msm-id|
*/
cur_dt_msm_id = (cur_dt_entry->platform_id & 0x0000ffff);
cur_dt_hw_platform = (cur_dt_entry->variant_id & 0x000000ff);
cur_dt_hw_subtype = (cur_dt_entry->board_hw_subtype & 0xff);
/* Determine the bits 10:8 to check the DT with the DDR Size */
cur_dt_hlos_ddr = (cur_dt_entry->board_hw_subtype & 0x700);
/* 1. must match the msm_id, platform_hw_id, platform_subtype and DDR size
* soc, board major/minor, pmic major/minor must less than board info
* 2. find the matched DTB then return 1
* 3. otherwise return 0
*/
if((cur_dt_msm_id == (board_platform_id() & 0x0000ffff)) &&
(cur_dt_hw_platform == board_hardware_id()) &&
(cur_dt_hw_subtype == board_hardware_subtype()) &&
(cur_dt_hlos_ddr == (target_get_hlos_subtype() & 0x700)) &&
(cur_dt_entry->soc_rev <= board_soc_version()) &&
((cur_dt_entry->variant_id & 0x00ffff00) <= (board_target_id() & 0x00ffff00)) &&
((cur_dt_entry->pmic_rev[0] & 0x00ffff00) <= (board_pmic_target(0) & 0x00ffff00)) &&
((cur_dt_entry->pmic_rev[1] & 0x00ffff00) <= (board_pmic_target(1) & 0x00ffff00)) &&
((cur_dt_entry->pmic_rev[2] & 0x00ffff00) <= (board_pmic_target(2) & 0x00ffff00)) &&
((cur_dt_entry->pmic_rev[3] & 0x00ffff00) <= (board_pmic_target(3) & 0x00ffff00))) {
dt_node_tmp = dt_entry_list_init();
memcpy((char*)dt_node_tmp->dt_entry_m,(char*)cur_dt_entry, sizeof(struct dt_entry));
dprintf(SPEW, "Add DTB entry %u/%08x/0x%08x/%x/%x/%x/%x/%x/%x/%x\n",
dt_node_tmp->dt_entry_m->platform_id, dt_node_tmp->dt_entry_m->variant_id,
dt_node_tmp->dt_entry_m->board_hw_subtype, dt_node_tmp->dt_entry_m->soc_rev,
dt_node_tmp->dt_entry_m->pmic_rev[0], dt_node_tmp->dt_entry_m->pmic_rev[1],
dt_node_tmp->dt_entry_m->pmic_rev[2], dt_node_tmp->dt_entry_m->pmic_rev[3],
dt_node_tmp->dt_entry_m->offset, dt_node_tmp->dt_entry_m->size);
insert_dt_entry_in_queue(dt_list, dt_node_tmp);
return 1;
}
return 0;
}
board_platform_id() board_hardware_id() board_hardware_subtype() target_get_hlos_subtype() board_soc_version()board_target_id()
几乎都来自board结构体
struct board_data {
uint32_t platform; //board_platform_id
uint32_t foundry_id;
uint32_t chip_serial;
uint32_t platform_version;//board_soc_version
uint32_t platform_hw;//board_hardware_id()
uint32_t platform_subtype;//board_hardware_subtype
uint32_t target;//board_target_id()
uint32_t baseband;
struct board_pmic_data pmic_info[MAX_PMIC_DEVICES];
uint32_t platform_hlos_subtype;//target_get_hlos_subtype即board_hlos_subtype
uint32_t num_pmics;
uint32_t pmic_array_offset;
struct board_pmic_data *pmic_info_array;
};
board在platform\msm_shared\board.c 文件里赋值
board_init()
platform_detect()
board.platform = board_info_v11.board_info_v3.msm_id;
board.platform_version = board_info_v11.board_info_v3.msm_version;
board.platform_hw = board_info_v11.board_info_v3.hw_platform;
board.platform_subtype = board_info_v11.platform_subtype;
/*
* fill in board.target with variant_id information
* bit no |31 24 |23 16|15 8|7 0|
* board.target = |subtype|plat_hw_ver major|plat_hw_ver minor|hw_platform|
*
*/
board.target = (((board_info_v11.platform_subtype & 0xff) << 24) |
(((board_info_v11.platform_version >> 16) & 0xff) << 16) |
((board_info_v11.platform_version & 0xff) << 8) |
(board_info_v11.board_info_v3.hw_platform & 0xff));
board.foundry_id = board_info_v11.foundry_id;
board.chip_serial = board_info_v11.chip_serial;
board.num_pmics = board_info_v11.num_pmics;
board.pmic_array_offset = board_info_v11.pmic_array_offset;
}
/* HLOS subtype
* bit no |31 20 | 19 16|15 13 |12 11 | 10 8 | 7 0|
* board.platform_hlos_subtype = |reserved | Boot device |Reserved | Panel | DDR detection | subtype|
* | bits | | bits | Detection |
*/
board.platform_hlos_subtype = (board_get_ddr_subtype() << 8) | (platform_get_boot_dev() << 16) | (platform_detect_panel() << 11);
根据代码里给的注释,从smem里获得
platform_dt_absolute_match过程
static int platform_dt_absolute_match(struct dt_entry *cur_dt_entry, struct dt_entry_node *dt_list)
{
/* Platform-id
* bit no |31 24|23 16|15 0|
* |reserved|foundry-id|msm-id|
*/
cur_dt_msm_id = (cur_dt_entry->platform_id & 0x0000ffff);
cur_dt_hw_platform = (cur_dt_entry->variant_id & 0x000000ff);
cur_dt_hw_subtype = (cur_dt_entry->board_hw_subtype & 0xff);
/* Determine the bits 10:8 to check the DT with the DDR Size */
cur_dt_hlos_ddr = (cur_dt_entry->board_hw_subtype & 0x700);
/* 1. must match the msm_id, platform_hw_id, platform_subtype and DDR size
* soc, board major/minor, pmic major/minor must less than board info
* 2. find the matched DTB then return 1
* 3. otherwise return 0
*/
if((cur_dt_msm_id == (board_platform_id() & 0x0000ffff)) &&
(cur_dt_hw_platform == board_hardware_id()) &&
(cur_dt_hw_subtype == board_hardware_subtype()) &&
(cur_dt_hlos_ddr == (target_get_hlos_subtype() & 0x700)) &&
(cur_dt_entry->soc_rev <= board_soc_version()) &&
((cur_dt_entry->variant_id & 0x00ffff00) <= (board_target_id() & 0x00ffff00)) &&
((cur_dt_entry->pmic_rev[0] & 0x00ffff00) <= (board_pmic_target(0) & 0x00ffff00)) &&
((cur_dt_entry->pmic_rev[1] & 0x00ffff00) <= (board_pmic_target(1) & 0x00ffff00)) &&
((cur_dt_entry->pmic_rev[2] & 0x00ffff00) <= (board_pmic_target(2) & 0x00ffff00)) &&
((cur_dt_entry->pmic_rev[3] & 0x00ffff00) <= (board_pmic_target(3) & 0x00ffff00))) {
dt_node_tmp = dt_entry_list_init();
memcpy((char*)dt_node_tmp->dt_entry_m,(char*)cur_dt_entry, sizeof(struct dt_entry));
... ...
}
对比之前需要从dts里将信息提取出来,主要是通过读取三个属性值然后提取
pmic_prop = (const char *)fdt_getprop(dtb, root_offset, "qcom,pmic-id", &len_pmic_id);
board_prop = (const char *)fdt_getprop(dtb, root_offset, "qcom,board-id", &len_board_id);
plat_prop = (const char *)fdt_getprop(dtb, root_offset, “qcom,msm-id”, &len_plat_id);
board_data[i].variant_id = fdt32_to_cpu(((struct board_id *)board_prop)->variant_id);
board_data[i].platform_subtype = fdt32_to_cpu(((struct board_id *)board_prop)->platform_subtype);
dt_entry_array[k].board_hw_subtype = board_data[j].platform_subtype;
platform_data[i].platform_id = fdt32_to_cpu(((struct plat_id *)plat_prop)->platform_id);
platform_data[i].soc_rev = fdt32_to_cpu(((struct plat_id *)plat_prop)->soc_rev);
pmic_data[i].pmic_version[0]= fdt32_to_cpu(((struct pmic_id *)pmic_prop)->pmic_version[0]);
pmic_data[i].pmic_version[1]= fdt32_to_cpu(((struct pmic_id *)pmic_prop)->pmic_version[1]);
pmic_data[i].pmic_version[2]= fdt32_to_cpu(((struct pmic_id *)pmic_prop)->pmic_version[2]);
pmic_data[i].pmic_version[3]= fdt32_to_cpu(((struct pmic_id *)pmic_prop)->pmic_version[3]);
must match the msm_id, platform_hw_id, platform_subtype and DDR size
可见 msm_id, platform_hw_platform, platform_subtype and DDR size 这几个需要完全匹配才可以
cur_dt_msm_id = (cur_dt_entry->platform_id & 0x0000ffff);
来自"qcom,msm-id"
platform_hw_platform = (cur_dt_entry->variant_id & 0x000000ff);
来自 "qcom,board-id"
cur_dt_hw_subtype = (cur_dt_entry->board_hw_subtype & 0xff);
同样来自 "qcom,board-id"
总之,最终会找到一个最合适的dtb,也有可能因为配置不对找不到匹配的dtb文件。
由此可见,在dtb未匹配时,需要修改sbl里的id,或者同步修改dts里的board id,让两者匹配才可以。
device\qcom\common\generate_extra_images.mk
#----------------------------------------------------------------------
# Generate device tree image (dt.img)
#----------------------------------------------------------------------
ifneq ($(strip $(TARGET_NO_KERNEL)),true)
ifeq ($(strip $(BOARD_KERNEL_SEPARATED_DT)),true)
ifeq ($(strip $(BUILD_TINY_ANDROID)),true)
include device/qcom/common/dtbtool/Android.mk
endif
DTBTOOL := $(HOST_OUT_EXECUTABLES)/dtbTool$(HOST_EXECUTABLE_SUFFIX)
INSTALLED_DTIMAGE_TARGET := $(PRODUCT_OUT)/dt.img
possible_dtb_dirs = $(KERNEL_OUT)/arch/$(TARGET_KERNEL_ARCH)/boot/dts/ $(KERNEL_OUT)/arch/arm/boot/dts/ $(KERNEL_OUT)/arch/arm/boot/
dtb_dir = $(firstword $(wildcard $(possible_dtb_dirs)))
define build-dtimage-target
$(call pretty,"Target dt image: $(INSTALLED_DTIMAGE_TARGET)")
$(hide) $(DTBTOOL) -o $@ -s $(BOARD_KERNEL_PAGESIZE) -p $(KERNEL_OUT)/scripts/dtc/ $(dtb_dir)
$(hide) chmod a+r $@
endef
$(INSTALLED_DTIMAGE_TARGET): $(DTBTOOL) $(INSTALLED_KERNEL_TARGET)
$(build-dtimage-target)
ALL_DEFAULT_INSTALLED_MODULES += $(INSTALLED_DTIMAGE_TARGET)
ALL_MODULES.$(LOCAL_MODULE).INSTALLED += $(INSTALLED_DTIMAGE_TARGET)
endif
endif
可见是使用dtbtool
dtbtool代码定义在device\qcom\common\dtbtool\dtbtool.c, 与这个文件同一目录下的dtbtool.txt是对dtbtool的使用说明。
1.1) Android boot image Header:
1.2) Layout:
A) header (as above - 1 page)
B) kernel (n pages)
C) ramdisk (m pages)
D) second stage (o pages)
2) QC table of device tree
可以看出magic是QCDT,version是3,num_entries是0x89
可以看出offset是0x1800, size是0x26800 (0x1800+0x26800=0x28000)
0x28000是另一个dtb的开始
int boot_linux_from_mmc(void)
{
...............
if(dt_size) {
dt_table_offset = ((uint32_t)image_addr + page_size + kernel_actual + ramdisk_actual + second_actual);
table = (struct dt_table*) dt_table_offset;
if (dev_tree_validate(table, hdr->page_size, &dt_hdr_size) != 0) {
dprintf(CRITICAL, "ERROR: Cannot validate Device Tree Table \n");
return -1;
}
/* Its Error if, dt_hdr_size (table->num_entries * dt_entry size + Dev_Tree Header)
goes beyound hdr->dt_size*/
if (dt_hdr_size > ROUND_TO_PAGE(dt_size,hdr->page_size)) {
dprintf(CRITICAL, "ERROR: Invalid Device Tree size \n");
return -1;
}
/* Find index of device tree within device tree table */
if(dev_tree_get_entry_info(table, &dt_entry) != 0){
dprintf(CRITICAL, "ERROR: Getting device tree address failed\n");
return -1;
}
if(dt_entry.offset > (UINT_MAX - dt_entry.size)) {
dprintf(CRITICAL, "ERROR: Device tree contents are Invalid\n");
return -1;
}
/* Ensure we are not overshooting dt_size with the dt_entry selected */
if ((dt_entry.offset + dt_entry.size) > dt_size) {
dprintf(CRITICAL, "ERROR: Device tree contents are Invalid\n");
return -1;
}
if (is_gzip_package((unsigned char *)dt_table_offset + dt_entry.offset, dt_entry.size))
{
unsigned int compressed_size = 0;
out_addr += out_len;
out_avai_len -= out_len;
dprintf(INFO, "decompressing dtb: start\n");
rc = decompress((unsigned char *)dt_table_offset + dt_entry.offset,
dt_entry.size, out_addr, out_avai_len,
&compressed_size, &dtb_size);
if (rc)
{
dprintf(CRITICAL, "decompressing dtb failed!!!\n");
ASSERT(0);
}
dprintf(INFO, "decompressing dtb: done\n");
best_match_dt_addr = out_addr;
} else {
best_match_dt_addr = (unsigned char *)dt_table_offset + dt_entry.offset;
dtb_size = dt_entry.size;
}
/* Validate and Read device device tree in the tags_addr */
if (check_aboot_addr_range_overlap(hdr->tags_addr, dtb_size) ||
check_ddr_addr_range_bound(hdr->tags_addr, dtb_size))
{
dprintf(CRITICAL, "Device tree addresses are not valid\n");
return -1;
}
memmove((void *)hdr->tags_addr, (char *)best_match_dt_addr, dtb_size);
} else {
/* Validate the tags_addr */
if (check_aboot_addr_range_overlap(hdr->tags_addr, kernel_actual) ||
check_ddr_addr_range_bound(hdr->tags_addr, kernel_actual))
{
dprintf(CRITICAL, "Device tree addresses are not valid.\n");
return -1;
}
/*
* If appended dev tree is found, update the atags with
* memory address to the DTB appended location on RAM.
* Else update with the atags address in the kernel header
*/
void *dtb;
dtb = dev_tree_appended(
(void*)(image_addr + page_size +
patched_kernel_hdr_size),
hdr->kernel_size, dtb_offset,
(void *)hdr->tags_addr);
if (!dtb) {
dprintf(CRITICAL, "ERROR: Appended Device Tree Blob not found\n");
return -1;
}
}
....................
}
在这种情况下dt_size是非0的,走第一个分支,第二个分支是zImage-dtb那种方式使用的
int dev_tree_get_entry_info(struct dt_table *table, struct dt_entry *dt_entry_info)
for(i = 0; found == 0 && i < table->num_entries; i++)
platform_dt_absolute_match(cur_dt_entry, dt_entry_queue);
best_match_dt_entry = platform_dt_match_best(dt_entry_queue);
调用关系如上,对每一个dt_entry调用platform_dt_absolute_match,找到最匹配的,匹配方法与zImage-dtb方式类似,不再赘述。
device tree最终编译成dtb文件,而高通平台为了让一个bootimage支持多种不同的配置(甚至是不同的board,不同的平台),将众多的dtb文件一起同时编译和打包。为了区分匹配不同的平台,高通平台需要再sbl里配置id,与device tree里的qcom,pmic-id和qcom,board-id相呼应,这两者需要匹配,才能实现dtb文件的正确加载,然后由lk传递给kernel。而dtb的识别是在lk里实现的,lk代码里对dt.img方式和zImage-dtb方式做了兼容,可以同时识别这两种方式。