设备树在内核传递与属性处理
文章目录:
DeviceTree(1) - 内核传递与属性处理
DeviceTree(2) - platform_device
DeviceTree(3) - 中断
DeviceTree(4) - 设备树中的中断
1 head.s处理
Kernel4.14 head.s文件分析(arch/arm/kernel/head.S arch/arm/kernel/head-common.S)
总结:
将bootloader传递过来的r1,赋值给了C变量__machine_arch_type
将bootloader传递过来的r2,赋值给了C变量__atags_pointer
a. __lookup_processor_type
根据r9中存储的机器ID,在__proc_info_list中找到CPU,完成初始化
mrc p15, 0, r9, c0, c0 @ get processor id
bl __lookup_processor_type @ r5=procinfo r9=cpuid
movs r10, r5 @ invalid processor (r5=0)?
THUMB( it eq ) @ force fixup-able long branch encoding
beq __error_p @ yes, error 'p'
__lookup_processor_type_data:
.long .
.long __proc_info_begin // C语言变量首地址
.long __proc_info_end
.size __lookup_processor_type_data, . - __lookup_processor_type_data
b. __vet_atags
/*
* r1 = machine no, r2 = atags or dtb,
* r8 = phys_offset, r9 = cpuid, r10 = procinfo
*/
// dtb文件存在头部,根据r2所指内存是否符合dtb中fdt_header的magic
bl __vet_atags // 判断atags或dtb是否有效
#ifdef CONFIG_SMP_ON_UP
bl __fixup_smp
#endif
c. __create_page_tables
- 创建页表项,建立虚拟地址与物理地址映射关系
d. __enable_mmu
- 启动MMU
e. __mmap_switched
- 从物理地址切换到虚拟地址
/*
* The following fragment of code is executed with the MMU on in MMU mode,
* and uses absolute addresses; this is not position independent.
*
* r0 = cp#15 control register
* r1 = machine ID
* r2 = atags/dtb pointer // r2中始终存在atags/dtb首地址
* r9 = processor ID
*/
__INIT
__mmap_switched:
adr r3, __mmap_switched_data // r3 = __mmap_switched_data
ldmia r3!, {r4, r5, r6, r7}
cmp r4, r5 @ Copy data segment if needed
1: cmpne r5, r6
ldrne fp, [r4], #4
strne fp, [r5], #4
bne 1b
mov fp, #0 @ Clear BSS (and zero fp)
1: cmp r6, r7
strcc fp, [r6],#4
bcc 1b
ARM( ldmia r3, {r4, r5, r6, r7, sp}) // 将r3连续的5个地址单元分别赋给r4, r5, r6, r7, sp
THUMB( ldmia r3, {r4, r5, r6, r7} )
THUMB( ldr sp, [r3, #16] )
str r9, [r4] @ Save processor ID
str r1, [r5] @ Save machine type
str r2, [r6] @ Save atags pointer // 将r2地址中的值存储到r6所指地址中
cmp r7, #0
strne r0, [r7] @ Save control register values
mov lr, #0
b start_kernel
ENDPROC(__mmap_switched)
// 上面的意思时r2中存储的atags或dtb中值会被存储到__mmap_switched_data地址的内存中
__mmap_switched_data:
.long __data_loc @ r4
.long _sdata @ r5
.long __bss_start @ r6
.long _end @ r7
.long processor_id @ r4 // C语言变量,在汇编中出现,代表变量地址
.long __machine_arch_type @ r5
.long __atags_pointer @ r6 // 将atags或dtb写到变量__atags_pointer中
#ifdef CONFIG_CPU_CP15
.long cr_alignment @ r7
#else
.long 0 @ r7
#endif
.long init_thread_union + THREAD_START_SP @ sp
.size __mmap_switched_data, . - __mmap_switched_data
__FINIT
.text
f. init/main.c
asmlinkage __visible void __init start_kernel(void)
2 平台信息处理
2.1 dts文件声明支持何种machine_desc
/ {
model = "STMicroelectronics STM32F429i-DISCO board";
compatible = "st,stm32f429i-disco", "st,stm32f429";// 该属性序列存在优先级选择,多个字符串时,优劣程度从第一个到最后一个排列
2.2 machine_desc支持哪些单板
static const char *const stm32_compat[] __initconst = {
"st,stm32f429",
"st,stm32f469",
"st,stm32f746",
"st,stm32h743",
NULL
};
DT_MACHINE_START(STM32DT, "STM32 (Device Tree Support)")
.dt_compat = stm32_compat, // 表明支持哪些单板
.restart = armv7m_restart,
MACHINE_END
2.3 有多个machine_desc跟dts吻合,怎么选择
使用dts中compatible与machine_desc中dt_compat比较
规则:吻合的compatible取值位置越低越好
内核处理dtb流程:主要
start_kernel // init/main.c
setup_arch(&command_line); // arch/arm/kernel/setup.c
const struct machine_desc *mdesc; // 定义machine_desc结构
mdesc = setup_machine_fdt(__atags_pointer); // arch/arm/kernel/devtree.c
early_init_dt_verify(phys_to_virt(dt_phys)) // phys_to_virt物理地址转虚拟地址
// 设备树指针params = phys_to_virt(dt_phys) = phys_to_virt(__atags_pointer)
// __atags_pointer就是在汇编中r2 = atags or dtb
initial_boot_params = params; // drivers/of/fdt.c
of_flat_dt_match_machine(mdesc_best, arch_get_next_mach);
{
// 循环取每一个compatible,计算得分
while ((data = get_next_compat(&compat))) {
score = of_flat_dt_match(dt_root, compat);
/*of_flat_dt_match
int of_fdt_match(const void *blob, unsigned long node,
const char *const *compat)
{
unsigned int tmp, score = 0;
if (!compat)
return 0;
while (*compat) {
tmp = of_fdt_is_compatible(blob, node, *compat);
if (tmp && (score == 0 || (tmp < score)))
score = tmp;
compat++;
}
return score;
}
*/
if (score > 0 && score < best_score) {
best_data = data;
best_score = score;
}
}
}
mdesc = setup_machine_tags(__atags_pointer, __machine_arch_type); // 机器ID的匹配
machine_desc = mdesc;
machine_name = mdesc->name;
unflatten_device_tree();
<三>设备树节点中配置信息处理
start_kernel // init/main.c
setup_arch(&command_line); // arch/arm/kernel/setup.c
early_init_dt_scan_nodes(); // arch/arm/kernel/devtree.c
{ // drivers/of/fdt.c
/* Retrieve various information from the /chosen node */
of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line);
/* Initialize {size,address}-cells info */
of_scan_flat_dt(early_init_dt_scan_root, NULL);
/* Setup memory, calling early_init_dt_add_memory_arch */
of_scan_flat_dt(early_init_dt_scan_memory, NULL);
}
处理chosen中bootargs参数
int __init early_init_dt_scan_chosen(unsigned long node, const char *uname,
int depth, void *data)
{
int l = 0;
const char *p = NULL;
char *cmdline = data;
pr_debug("search \"chosen\", depth: %d, uname: %s\n", depth, uname);
if (depth != 1 || !cmdline ||
(strcmp(uname, "chosen") != 0 && strcmp(uname, "chosen@0") != 0))
return 0;
early_init_dt_check_for_initrd(node);
/* Put CONFIG_CMDLINE in if forced or if data had nothing in it to start */
if (overwrite_incoming_cmdline || !cmdline[0])
strlcpy(cmdline, config_cmdline, COMMAND_LINE_SIZE);
/* Retrieve command line unless forcing */
if (read_dt_cmdline)
p = of_get_flat_dt_prop(node, "bootargs", &l);
if (p != NULL && l > 0) {
if (concat_cmdline) {
int cmdline_len;
int copy_len;
strlcat(cmdline, " ", COMMAND_LINE_SIZE);
cmdline_len = strlen(cmdline);
copy_len = COMMAND_LINE_SIZE - cmdline_len - 1;
copy_len = min((int)l, copy_len);
strncpy(cmdline + cmdline_len, p, copy_len);
cmdline[cmdline_len + copy_len] = '\0';
} else {
strlcpy(cmdline, p, min((int)l, COMMAND_LINE_SIZE));
}
}
pr_debug("Command line is: %s\n", (char*)data);
/* break now */
return 1;
}
处理memory中指明表示参数格式的信息
{
#size-cells:表值大小用多少个32位标识
#address-cells:表地址用多少个32位标识
}
int __init early_init_dt_scan_root(unsigned long node, const char *uname,
int depth, void *data)
{
const __be32 *prop;
if (depth != 0)
return 0;
dt_root_size_cells = OF_ROOT_NODE_SIZE_CELLS_DEFAULT;
dt_root_addr_cells = OF_ROOT_NODE_ADDR_CELLS_DEFAULT;
prop = of_get_flat_dt_prop(node, "#size-cells", NULL);
if (prop)
dt_root_size_cells = be32_to_cpup(prop); // 大端到CPU模式转换
pr_debug("dt_root_size_cells = %x\n", dt_root_size_cells);
prop = of_get_flat_dt_prop(node, "#address-cells", NULL);
if (prop)
dt_root_addr_cells = be32_to_cpup(prop);
pr_debug("dt_root_addr_cells = %x\n", dt_root_addr_cells);
/* break now */
return 1;
}
处理memory中属性信息
{
根据early_init_dt_scan_root中拿到的标识进行偏移运算
}
int __init early_init_dt_scan_memory(unsigned long node, const char *uname,
int depth, void *data)
{
const char *type = of_get_flat_dt_prop(node, "device_type", NULL);
const __be32 *reg, *endp;
int l;
bool hotpluggable;
/* We are scanning "memory" nodes only */
if (type == NULL) {
/*
* The longtrail doesn't have a device_type on the
* /memory node, so look for the node called /memory@0.
*/
if (!IS_ENABLED(CONFIG_PPC32) || depth != 1 || strcmp(uname, "memory@0") != 0)
return 0;
} else if (strcmp(type, "memory") != 0)
return 0;
reg = of_get_flat_dt_prop(node, "linux,usable-memory", &l);
if (reg == NULL)
reg = of_get_flat_dt_prop(node, "reg", &l);
if (reg == NULL)
return 0;
endp = reg + (l / sizeof(__be32));
hotpluggable = of_get_flat_dt_prop(node, "hotpluggable", NULL);
pr_debug("memory scan node %s, reg size %d,\n", uname, l);
while ((endp - reg) >= (dt_root_addr_cells + dt_root_size_cells)) {
u64 base, size;
base = dt_mem_next_cell(dt_root_addr_cells, ®);
size = dt_mem_next_cell(dt_root_size_cells, ®);
if (size == 0)
continue;
pr_debug(" - %llx , %llx\n", (unsigned long long)base,
(unsigned long long)size);
early_init_dt_add_memory_arch(base, size); // memblock_add(base, size);
if (!hotpluggable)
continue;
if (early_init_dt_mark_hotplug_memory_arch(base, size))
pr_warn("failed to mark hotplug range 0x%llx - 0x%llx\n",
base, base + size);
}
return 0;
}