本篇文档是基于韦东山老师的设备树专题学习总结而来。
Linux uses DT data for three major purposes:
- platform identification,
- runtime configuration, and
- device population.
1 内核head.S对dtb的简单处理
1.1 Kernel startup entry point stext
在stext函数中主要做下面几件事情:
- __lookup_processor_type : 使用汇编指令读取CPU ID, 根据该ID找到对应的proc_info_list结构体(里面含有这类CPU的初始化函数、信息)
- __vet_atags : 判断是否存在可用的ATAGS或DTB
- __create_page_tables : 创建页表, 即创建虚拟地址和物理地址的映射关系
- __enable_mmu : 使能MMU, 以后就要使用虚拟地址了
- __mmap_switched : 上述函数里将会调用__mmap_switched
- 把bootloader传入的r0 r1 r2参数, 保存到对应的C变量
processor_id ,__machine_arch_type ,__atags_pointer 中
.arm
__HEAD
ENTRY(stext)
ARM_BE8(setend be ) @ ensure we are in BE8 mode
THUMB( badr r9, 1f ) @ Kernel is always entered in ARM.
THUMB( bx r9 ) @ If this is a Thumb-2 kernel,
THUMB( .thumb ) @ switch to Thumb now.
THUMB(1: )
#ifdef CONFIG_ARM_VIRT_EXT
bl __hyp_stub_install
#endif
@ ensure svc mode and all interrupts masked
safe_svcmode_maskall r9
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'
#ifdef CONFIG_ARM_LPAE
mrc p15, 0, r3, c0, c1, 4 @ read ID_MMFR0
and r3, r3, #0xf @ extract VMSA support
cmp r3, #5 @ long-descriptor translation table format?
THUMB( it lo ) @ force fixup-able long branch encoding
blo __error_lpae @ only classic page table format
#endif
#ifndef CONFIG_XIP_KERNEL
adr r3, 2f
ldmia r3, {r4, r8}
sub r4, r3, r4 @ (PHYS_OFFSET - PAGE_OFFSET)
add r8, r8, r4 @ PHYS_OFFSET
#else
ldr r8, =PLAT_PHYS_OFFSET @ always constant in this case
#endif
bl __vet_atags
#ifdef CONFIG_SMP_ON_UP
bl __fixup_smp
#endif
#ifdef CONFIG_ARM_PATCH_PHYS_VIRT
bl __fixup_pv_table
#endif
bl __create_page_tables
ldr r13, =__mmap_switched @ address to jump to after
@ mmu has been enabled
badr lr, 1f @ return (PIC) address
#ifdef CONFIG_ARM_LPAE
mov r5, #0 @ high TTBR0
mov r8, r4, lsr #12 @ TTBR1 is swapper_pg_dir pfn
#else
mov r8, r4 @ set TTBR1 to swapper_pg_dir
#endif`在这里插入代码片`
ldr r12, [r10, #PROCINFO_INITFUNC]
add r12, r12, r10
ret r12
1: b __enable_mmu
ENDPROC(stext)
.ltorg
#ifndef CONFIG_XIP_KERNEL
2: .long .
.long PAGE_OFFSET
#endif
1.2 __enable_mmu和__turn_mmu_on
- 在stext函数中将
__mmap_switched 的地址放入到r13 寄存器中,ldr r13, =__mmap_switched @ address to jump to after - 在__turn_mmu_on中,在使能mmu之后会通过
mov r3, r13 汇编指令,将r13 的值放入r3 中,然后通过ret r3 返回到__enable_mmu 函数中,最终会调用到__mmap_switched 函数
1.2.1 __enable_mmu
__enable_mmu:
#if defined(CONFIG_ALIGNMENT_TRAP) && __LINUX_ARM_ARCH__ < 6
orr r0, r0, #CR_A
#else
bic r0, r0, #CR_A
#endif
#ifdef CONFIG_CPU_DCACHE_DISABLE
bic r0, r0, #CR_C
#endif
#ifdef CONFIG_CPU_BPREDICT_DISABLE
bic r0, r0, #CR_Z
#endif
#ifdef CONFIG_CPU_ICACHE_DISABLE
bic r0, r0, #CR_I
#endif
#ifdef CONFIG_ARM_LPAE
mcrr p15, 0, r4, r5, c2 @ load TTBR0
#else
mov r5, #DACR_INIT
mcr p15, 0, r5, c3, c0, 0 @ load domain access register
mcr p15, 0, r4, c2, c0, 0 @ load page table pointer
#endif
b __turn_mmu_on
ENDPROC(__enable_mmu)
1.2.1 __turn_mmu_on
.align 5
.pushsection .idmap.text, "ax"
ENTRY(__turn_mmu_on)
mov r0, r0
instr_sync
mcr p15, 0, r0, c1, c0, 0 @ write control reg
mrc p15, 0, r3, c0, c0, 0 @ read id reg
instr_sync
mov r3, r3
mov r3, r13
ret r3
__turn_mmu_on_end:
ENDPROC(__turn_mmu_on)
.popsection
1.3 __mmap_switched
adr r4, __mmap_switched_data 将__mmap_switched_data 的地址赋值给r4 寄存器,- __bss_start
- 把bootloader传来的r0值, 赋给了C变量: processor_id 把bootloader传来的r1值, 赋给了C变量: __machine_arch_type 把bootloader传来的r2值, 赋给了C变量: __atags_pointer // tags 或 dtb首地址
b start_kernel
1.3.1 __mmap_switched
__mmap_switched 是在MMU模式下打开MMU时执行的。
__INIT
__mmap_switched:
mov r7, r1
mov r8, r2
mov r10, r0
adr r4, __mmap_switched_data
mov fp, #0
#if defined(CONFIG_XIP_DEFLATED_DATA)
ARM( ldr sp, [r4], #4 )
THUMB( ldr sp, [r4] )
THUMB( add r4, #4 )
bl __inflate_kernel_data @ decompress .data to RAM
teq r0, #0
bne __error
#elif defined(CONFIG_XIP_KERNEL)
ARM( ldmia r4!, {r0, r1, r2, sp} )
THUMB( ldmia r4!, {r0, r1, r2, r3} )
THUMB( mov sp, r3 )
sub r2, r2, r1
bl memcpy @ copy .data to RAM
#endif
ARM( ldmia r4!, {r0, r1, sp} )
THUMB( ldmia r4!, {r0, r1, r3} )
THUMB( mov sp, r3 )
sub r2, r1, r0
mov r1, #0
bl memset @ clear .bss
ldmia r4, {r0, r1, r2, r3}
str r9, [r0] @ Save processor ID
str r7, [r1] @ Save machine type
str r8, [r2] @ Save atags pointer
cmp r3, #0
strne r10, [r3] @ Save control register values
mov lr, #0
b start_kernel
ENDPROC(__mmap_switched)
1.3.2 __mmap_switched_data
.align 2
.type __mmap_switched_data, %object
__mmap_switched_data:
#ifdef CONFIG_XIP_KERNEL
#ifndef CONFIG_XIP_DEFLATED_DATA
.long _sdata @ r0
.long __data_loc @ r1
.long _edata_loc @ r2
#endif
.long __bss_stop @ sp (temporary stack in .bss)
#endif
.long __bss_start @ r0
.long __bss_stop @ r1
.long init_thread_union + THREAD_START_SP @ sp
.long processor_id @ r0
.long __machine_arch_type @ r1
.long __atags_pointer @ r2
#ifdef CONFIG_CPU_CP15
.long cr_alignment @ r3
#else
M_CLASS(.long exc_ret) @ r3
AR_CLASS(.long 0) @ r3
#endif
.size __mmap_switched_data, . - __mmap_switched_data
__FINIT
.text
2 设备树中平台信息的处理(选择machine_desc)
- dts中声明想要什么样的machine_desc
- 每个machine_desc表明能支持哪些单板
- 当有多个machine_desc和DTS匹配时,如何选择对应的单板
2.1 dts中声明想要什么样的machine_desc
在设备树的根节点的compatible 属性表明要支持哪些machine_desc,“兼容”属性包含一个已排序的字符串列表,从机器的确切名称开始,然后是一个可选的兼容板列表,从最兼容到最不兼容。
The 'compatible' property contains a sorted list of strings starting
with the exact name of the machine, followed by an optional list of
boards it is compatible with sorted from most compatible to least. For
example, the root compatible properties for the TI BeagleBoard and its
successor, the BeagleBoard xM board might look like, respectively:
compatible = "ti,omap3-beagleboard", "ti,omap3450", "ti,omap3";
compatible = "ti,omap3-beagleboard-xm", "ti,omap3450", "ti,omap3";
2.2 每个machine_desc表明能支持哪些单板
下面以 (linux-5.6.15\arch\arm\mach-bcm\bcm_nsp.c) 为例介绍,bcm_nsp_dt_compat 的值为"brcm,nsp" ,则表明当前的machine_desc支持"brcm,nsp"这个单板。
static const char *const bcm_nsp_dt_compat[] __initconst = {
"brcm,nsp",
NULL,
};
DT_MACHINE_START(NSP_DT, "Broadcom Northstar Plus SoC")
.l2c_aux_val = 0,
.l2c_aux_mask = ~0,
.dt_compat = bcm_nsp_dt_compat,
MACHINE_END
2.3 多个machine_desc和.dts compatible 匹配
当有多个machine_desc和DTS的compatible 匹配时,如何选择对应的单板呢, 根据score来评判最适合的单板。 调用过程如下所示:
函数调用过程:
start_kernel
setup_arch(&command_line);
mdesc = setup_machine_fdt(__atags_pointer);
early_init_dt_verify(phys_to_virt(dt_phys)
initial_boot_params = params;
mdesc = of_flat_dt_match_machine(mdesc_best, arch_get_next_mach);
while ((data = get_next_compat(&compat))) {
score = of_flat_dt_match(dt_root, compat);
if (score > 0 && score < best_score) {
best_data = data;
best_score = score;
}
}
machine_desc = mdesc;
2.3.1 setup_arch
在setup_arch函数中对设备树的处理主要是关注下面几点:
- __atags_pointer 存放的是dtb活着tag的地址,用于后面dtb或者tag的解析
- setup_machine_fdt先去检查是否是dtb传参格式
- setup_machine_tags检查tag的传参方式
- machine_desc = mdesc; (const struct machine_desc *machine_desc __initdata;)
- machine_name = mdesc->name;(static const char *machine_name;)
void __init setup_arch(char **cmdline_p)
{
const struct machine_desc *mdesc;
setup_processor();
mdesc = setup_machine_fdt(__atags_pointer);
if (!mdesc)
mdesc = setup_machine_tags(__atags_pointer, __machine_arch_type);
if (!mdesc) {
early_print("\nError: invalid dtb and unrecognized/unsupported machine ID\n");
early_print(" r1=0x%08x, r2=0x%08x\n", __machine_arch_type,
__atags_pointer);
if (__atags_pointer)
early_print(" r2[]=%*ph\n", 16,
phys_to_virt(__atags_pointer));
dump_machine_table();
}
machine_desc = mdesc;
machine_name = mdesc->name;
dump_stack_set_arch_desc("%s", mdesc->name);
if (mdesc->reboot_mode != REBOOT_HARD)
reboot_mode = mdesc->reboot_mode;
init_mm.start_code = (unsigned long) _text;
init_mm.end_code = (unsigned long) _etext;
init_mm.end_data = (unsigned long) _edata;
init_mm.brk = (unsigned long) _end;
strlcpy(cmd_line, boot_command_line, COMMAND_LINE_SIZE);
*cmdline_p = cmd_line;
early_fixmap_init();
early_ioremap_init();
parse_early_param();
#ifdef CONFIG_MMU
early_mm_init(mdesc);
#endif
setup_dma_zone(mdesc);
xen_early_init();
efi_init();
adjust_lowmem_bounds();
arm_memblock_init(mdesc);
adjust_lowmem_bounds();
early_ioremap_reset();
paging_init(mdesc);
request_standard_resources(mdesc);
if (mdesc->restart)
arm_pm_restart = mdesc->restart;
unflatten_device_tree();
arm_dt_init_cpu_maps();
psci_dt_init();
#ifdef CONFIG_SMP
if (is_smp()) {
if (!mdesc->smp_init || !mdesc->smp_init()) {
if (psci_smp_available())
smp_set_ops(&psci_smp_ops);
else if (mdesc->smp)
smp_set_ops(mdesc->smp);
}
smp_init_cpus();
smp_build_mpidr_hash();
}
#endif
if (!is_smp())
hyp_mode_check();
reserve_crashkernel();
#ifdef CONFIG_GENERIC_IRQ_MULTI_HANDLER
handle_arch_irq = mdesc->handle_irq;
#endif
#ifdef CONFIG_VT
#if defined(CONFIG_VGA_CONSOLE)
conswitchp = &vga_con;
#endif
#endif
if (mdesc->init_early)
mdesc->init_early();
}
2.3.2 setup_machine_fdt
当dtb被传递给内核时的machine设置
early_init_dt_verify(phys_to_virt(dt_phys)) mdesc = of_flat_dt_match_machine(mdesc_best, arch_get_next_mach) early_init_dt_scan_nodes();
const struct machine_desc * __init setup_machine_fdt(unsigned int dt_phys)
{
const struct machine_desc *mdesc, *mdesc_best = NULL;
#if defined(CONFIG_ARCH_MULTIPLATFORM) || defined(CONFIG_ARM_SINGLE_ARMV7M)
DT_MACHINE_START(GENERIC_DT, "Generic DT based system")
.l2c_aux_val = 0x0,
.l2c_aux_mask = ~0x0,
MACHINE_END
mdesc_best = &__mach_desc_GENERIC_DT;
#endif
if (!dt_phys || !early_init_dt_verify(phys_to_virt(dt_phys)))
return NULL;
mdesc = of_flat_dt_match_machine(mdesc_best, arch_get_next_mach);
if (!mdesc) {
const char *prop;
int size;
unsigned long dt_root;
early_print("\nError: unrecognized/unsupported "
"device tree compatible list:\n[ ");
dt_root = of_get_flat_dt_root();
prop = of_get_flat_dt_prop(dt_root, "compatible", &size);
while (size > 0) {
early_print("'%s' ", prop);
size -= strlen(prop) + 1;
prop += strlen(prop) + 1;
}
early_print("]\n\n");
dump_machine_table();
}
if (mdesc->dt_fixup)
mdesc->dt_fixup();
early_init_dt_scan_nodes();
__machine_arch_type = mdesc->nr;
return mdesc;
}
2.3.2.1 early_init_dt_verify
在early_init_dt_verify函数中主要处理下面三件事情,首先检查设备树header,如果是,这设置initial_boot_params 和of_fdt_crc32 。
- fdt_check_header(params))
- initial_boot_params = params;
- of_fdt_crc32 = crc32_be(~0, initial_boot_params, fdt_totalsize(initial_boot_params));
bool __init early_init_dt_verify(void *params)
{
if (!params)
return false;
if (fdt_check_header(params))
return false;
initial_boot_params = params;
of_fdt_crc32 = crc32_be(~0, initial_boot_params,
fdt_totalsize(initial_boot_params));
return true;
}
2.3.2.2 fdt_check_header
在fdt_check_header函数中均是做的dtb的格式校验相关的工作,我们以下面header magic和hdrsize两个为例来说明,其他都是去校验dtb 格式里面的其他属性。
fdt_magic(fdt) != FDT_MAGIC 检查header magic是否匹配hdrsize = fdt_header_size(fdt); 获取hdrsize
int fdt_check_header(const void *fdt)
{
size_t hdrsize;
if (fdt_magic(fdt) != FDT_MAGIC)
return -FDT_ERR_BADMAGIC;
hdrsize = fdt_header_size(fdt);
if ((fdt_version(fdt) < FDT_FIRST_SUPPORTED_VERSION)
|| (fdt_last_comp_version(fdt) > FDT_LAST_SUPPORTED_VERSION))
return -FDT_ERR_BADVERSION;
if (fdt_version(fdt) < fdt_last_comp_version(fdt))
return -FDT_ERR_BADVERSION;
if ((fdt_totalsize(fdt) < hdrsize)
|| (fdt_totalsize(fdt) > INT_MAX))
return -FDT_ERR_TRUNCATED;
if (!check_off_(hdrsize, fdt_totalsize(fdt), fdt_off_mem_rsvmap(fdt)))
return -FDT_ERR_TRUNCATED;
if (fdt_version(fdt) < 17) {
if (!check_off_(hdrsize, fdt_totalsize(fdt),
fdt_off_dt_struct(fdt)))
return -FDT_ERR_TRUNCATED;
} else {
if (!check_block_(hdrsize, fdt_totalsize(fdt),
fdt_off_dt_struct(fdt),
fdt_size_dt_struct(fdt)))
return -FDT_ERR_TRUNCATED;
}
if (!check_block_(hdrsize, fdt_totalsize(fdt),
fdt_off_dt_strings(fdt), fdt_size_dt_strings(fdt)))
return -FDT_ERR_TRUNCATED;
return 0;
}
2.3.2.3 arch_get_next_mach
从__arch_info_begin 位置开始依次取出machine_desc,然后将m->dt_compat 取出。 其中__arch_info_begin是在MACHINE_START中标注.section .arch.info.init 的起始位置,__arch_info_end标记.arch.info.init 段的结束。
2.3.2.3.1 arch_get_next_mach
static const void * __init arch_get_next_mach(const char *const **match)
{
static const struct machine_desc *mdesc = __arch_info_begin;
const struct machine_desc *m = mdesc;
if (m >= __arch_info_end)
return NULL;
mdesc++;
*match = m->dt_compat;
return m;
}
2.3.2.3.2 __arch_info_begin和__arch_info_end
- MACHINE_START去标记把当前的machine_desc 信息放在
.arch.info.init 段中 __arch_info_begin 和__arch_info_end 分别标记.arch.info.init 段的开始和结束
.init.arch.info : {
__arch_info_begin = .;
*(.arch.info.init)
__arch_info_end = .;
}
#define MACHINE_START(_type,_name) \
static const struct machine_desc __mach_desc_##_type \
__used \
__attribute__((__section__(".arch.info.init"))) = { \
.nr = MACH_TYPE_##_type, \
.name = _name,
#define MACHINE_END \
};
2.3.2.4 of_flat_dt_match_machine
迭代匹配的tables以寻找匹配的machine。该函数的主要功能是在while循环里面去做的,目的就是为了找出最匹配的machine_desc。
const void * __init of_flat_dt_match_machine(const void *default_match,
const void * (*get_next_compat)(const char * const**))
{
const void *data = NULL;
const void *best_data = default_match;
const char *const *compat;
unsigned long dt_root;
unsigned int best_score = ~1, score = 0;
dt_root = of_get_flat_dt_root();
while ((data = get_next_compat(&compat))) {
score = of_flat_dt_match(dt_root, compat);
if (score > 0 && score < best_score) {
best_data = data;
best_score = score;
}
}
if (!best_data) {
const char *prop;
int size;
pr_err("\n unrecognized device tree list:\n[ ");
prop = of_get_flat_dt_prop(dt_root, "compatible", &size);
if (prop) {
while (size > 0) {
printk("'%s' ", prop);
size -= strlen(prop) + 1;
prop += strlen(prop) + 1;
}
}
printk("]\n\n");
return NULL;
}
pr_info("Machine model: %s\n", of_flat_dt_get_machine_name());
return best_data;
}
2.3.2.5 of_flat_dt_match
如果节点与compatible values的列表相匹配,则返回true initial_boot_params 是在early_init_dt_verify 函数中检测是dtb 格式时,将dtb地址赋值给initial_boot_params 的。
static int __init of_flat_dt_match(unsigned long node, const char *const *compat)
{
unsigned int tmp, score = 0;
if (!compat)
return 0;
while (*compat) {
tmp = of_fdt_is_compatible(initial_boot_params, node, *compat);
if (tmp && (score == 0 || (tmp < score)))
score = tmp;
compat++;
}
return score;
}
2.3.2.6 of_fdt_is_compatible
of_fdt_is_compatible函数去在compatible list去检查匹配的compatible string,匹配的结果越靠前这分数越低,表明匹配度越高。 一下面的例子为例来说明: compatible = “ti,omap3-beagleboard”, “ti,omap3450”, “ti,omap3”;
- 当匹配
"ti,omap3-beagleboard" 时,这score = 1 - 当匹配
"ti,omap3450" 时,这score = 2 - 当匹配
"ti,omap3" 时,这score = 3
static int of_fdt_is_compatible(const void *blob,
unsigned long node, const char *compat)
{
const char *cp;
int cplen;
unsigned long l, score = 0;
cp = fdt_getprop(blob, node, "compatible", &cplen);
if (cp == NULL)
return 0;
while (cplen > 0) {
score++;
if (of_compat_cmp(cp, compat, strlen(compat)) == 0)
return score;
l = strlen(cp) + 1;
cp += l;
cplen -= l;
}
return 0;
}
2.4 对设备树中运行时配置信息的处理
该部分的处理放在early_init_dt_scan_nodes 函数中进行,主要做了下面三件事情。
/chosen 节点中bootargs 属性的值, 存入全局变量: boot_command_line - 确定根节点的这2个属性的值:
#address-cells, #size-cells 存入全局变量: dt_root_addr_cells , dt_root_size_cells - 解析
/memory 中的reg属性, 提取出"base, size", 最终调用memblock_add(base, size);
2.4.1 函数调用过程
函数调用过程:
start_kernel
setup_arch(&command_line);
mdesc = setup_machine_fdt(__atags_pointer);
early_init_dt_scan_nodes();
of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line);
of_scan_flat_dt(early_init_dt_scan_root, NULL);
of_scan_flat_dt(early_init_dt_scan_memory, NULL);
2.4.2 early_init_dt_scan_nodes
early_init_dt_scan_chosen Retrieve various information from the /chosen node early_init_dt_scan_root Initialize {size,address}-cells info early_init_dt_scan_memory Setup memory, calling early_init_dt_add_memory_arch
void __init early_init_dt_scan_nodes(void)
{
int rc = 0;
rc = of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line);
if (!rc)
pr_warn("No chosen node found, continuing without\n");
of_scan_flat_dt(early_init_dt_scan_root, NULL);
of_scan_flat_dt(early_init_dt_scan_memory, NULL);
}
2.4.3 of_scan_flat_dt
扫描扁平化的设备树,并对每个blob调用回调函数记性对应的数据解析。
int __init of_scan_flat_dt(int (*it)(unsigned long node,
const char *uname, int depth,
void *data),
void *data)
{
const void *blob = initial_boot_params;
const char *pathp;
int offset, rc = 0, depth = -1;
if (!blob)
return 0;
for (offset = fdt_next_node(blob, -1, &depth);
offset >= 0 && depth >= 0 && !rc;
offset = fdt_next_node(blob, offset, &depth)) {
pathp = fdt_get_name(blob, offset, NULL);
if (*pathp == '/')
pathp = kbasename(pathp);
rc = it(offset, pathp, depth, data);
}
return rc;
}
2.4.4 early_init_dt_scan_chosen
解析/chosen 节点的信息,将其放入boot_command_line 全局变量中。 data代表的就是boot_command_line 。
early_init_dt_check_for_initrd Decode initrd location from flat tree p = of_get_flat_dt_prop(node, "bootargs", &l); rng_seed = of_get_flat_dt_prop(node, "rng-seed", &l); fdt_nop_property(initial_boot_params, node, "rng-seed");
int __init early_init_dt_scan_chosen(unsigned long node, const char *uname,
int depth, void *data)
{
int l;
const char *p;
const void *rng_seed;
pr_debug("search \"chosen\", depth: %d, uname: %s\n", depth, uname);
if (depth != 1 || !data ||
(strcmp(uname, "chosen") != 0 && strcmp(uname, "chosen@0") != 0))
return 0;
early_init_dt_check_for_initrd(node);
p = of_get_flat_dt_prop(node, "bootargs", &l);
if (p != NULL && l > 0)
strlcpy(data, p, min(l, COMMAND_LINE_SIZE));
#ifdef CONFIG_CMDLINE
#if defined(CONFIG_CMDLINE_EXTEND)
strlcat(data, " ", COMMAND_LINE_SIZE);
strlcat(data, CONFIG_CMDLINE, COMMAND_LINE_SIZE);
#elif defined(CONFIG_CMDLINE_FORCE)
strlcpy(data, CONFIG_CMDLINE, COMMAND_LINE_SIZE);
#else
if (!((char *)data)[0])
strlcpy(data, CONFIG_CMDLINE, COMMAND_LINE_SIZE);
#endif
#endif
pr_debug("Command line is: %s\n", (char*)data);
rng_seed = of_get_flat_dt_prop(node, "rng-seed", &l);
if (rng_seed && l > 0) {
add_bootloader_randomness(rng_seed, l);
fdt_nop_property(initial_boot_params, node, "rng-seed");
of_fdt_crc32 = crc32_be(~0, initial_boot_params,
fdt_totalsize(initial_boot_params));
}
return 1;
}
2.4.5 early_init_dt_scan_root
获取设备树根节点下的的#size-cells 和#address-cells 信息
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);
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);
return 1;
}
2.4.6 early_init_dt_scan_memory
遍历并解析内存节点(memory nodes),最终将解析出的memory node内存节点通过early_init_dt_add_memory_arch 函数调用memblock_add(base, size); 添加到系统中。
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;
if (type == NULL || 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);
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;
}
2.5 dtb转换为device_node
2.5.1 函数调用过程
start_kernel
setup_arch(&command_line);
arm_memblock_init(mdesc);
early_init_fdt_reserve_self();
early_init_dt_reserve_memory_arch(__pa(initial_boot_params),
fdt_totalsize(initial_boot_params),
0);
early_init_fdt_scan_reserved_mem();
unflatten_device_tree();
__unflatten_device_tree(initial_boot_params, NULL, &of_root,
early_init_dt_alloc_memory_arch, false);
size = unflatten_dt_nodes(blob, NULL, dad, NULL);
mem = dt_alloc(size + 4, __alignof__(struct device_node));
unflatten_dt_nodes(blob, mem, dad, mynodes);
populate_node
np = unflatten_dt_alloc(mem, sizeof(struct device_node) + allocl,
__alignof__(struct device_node));
np->full_name = fn = ((char *)np) + sizeof(*np);
populate_properties
pp = unflatten_dt_alloc(mem, sizeof(struct property),
__alignof__(struct property));
pp->name = (char *)pname;
pp->length = sz;
pp->value = (__be32 *)val;
|