内核对设备树的处理(1)

目录

  • 一、从源头分析_内核head.S对dtb的处理
  • 二、对设备树中平台信息的处理(选择machine_desc)
  • 三、对设备树中运行时配置信息的处理

一、从源头分析_内核head.S对dtb的处理

(1)bootloader启动内核时,会设置r0,r1,r2三个寄存器,
  r0一般设置为0;
  r1一般设置为machine id (在使用设备树时该参数没有被使用);
  r2一般设置ATAGS或DTB的开始地址

bootloader给内核传递的参数时有2种方法:ATAGS 或 DTB

(2)分析linux-4.19-rc3/arch/arm/kernel/head.S文件,了解其对dtb的处理

主要做了以下工作:
a. __lookup_processor_type : 使用汇编指令读取CPU ID, 根据该ID找到对应的proc_info_list结构体(里面含有这类CPU的初始化函数、信息)

/*head-common.S*/
__lookup_processor_type:
	adr	r3, __lookup_processor_type_data
	ldmia	r3, {r4 - r6}
	sub	r3, r3, r4			@ get offset between virt&phys
	add	r5, r5, r3			@ convert virt addresses to
	add	r6, r6, r3			@ physical address space
1:	ldmia	r5, {r3, r4}			@ value, mask
	and	r4, r4, r9			@ mask wanted bits
	teq	r3, r4
	beq	2f
	add	r5, r5, #PROC_INFO_SZ		@ sizeof(proc_info_list)
	cmp	r5, r6
	blo	1b
	mov	r5, #0				@ unknown processor
2:	ret	lr
ENDPROC(__lookup_processor_type)

b. __vet_atags : 判断是否存在可用的ATAGS或DTB

/*head-common.S*/
__vet_atags:
	tst	r2, #0x3			@ aligned?
	bne	1f

	ldr	r5, [r2, #0]
#ifdef CONFIG_OF_FLATTREE
	ldr	r6, =OF_DT_MAGIC		@ is it a DTB?
	cmp	r5, r6
	beq	2f
#endif
	cmp	r5, #ATAG_CORE_SIZE		@ is first tag ATAG_CORE?
	cmpne	r5, #ATAG_CORE_SIZE_EMPTY
	bne	1f
	ldr	r5, [r2, #4]
	ldr	r6, =ATAG_CORE
	cmp	r5, r6
	bne	1f

2:	ret	lr				@ atag/dtb pointer is ok

1:	mov	r2, #0
	ret	lr
ENDPROC(__vet_atags)

c. __create_page_tables : 创建页表, 即创建虚拟地址和物理地址的映射关系

/*head.S文件*/
__create_page_tables:
	pgtbl	r4, r8				@ page table address

	/*
	 * Clear the swapper page table
	 */
	mov	r0, r4
	mov	r3, #0
	add	r6, r0, #PG_DIR_SIZE
1:	str	r3, [r0], #4
	str	r3, [r0], #4
	str	r3, [r0], #4
	str	r3, [r0], #4
	teq	r0, r6
	bne	1b

d. __enable_mmu : 使能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)

e. __mmap_switched: 上述函数里将会调用__mmap_switched

__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 @跳转开始执行C程序
ENDPROC(__mmap_switched)

f. 把bootloader传入的r2参数, 保存到变量__atags_pointer中

g. 调用C函数start_kernel

head.S/head-common.S  : 
把bootloader传来的r1值, 赋给了C变量: __machine_arch_type
把bootloader传来的r2值, 赋给了C变量: __atags_pointer     // dtb首地址

二、对设备树中平台信息的处理(选择machine_desc)

model = "SMDK24440";
compatible = "samsung,smdk2440","samsung,smdk2410","samsung,smdk24xx";
				最好                 次之             第三选择
/*最想要那个machine_desc*/

a. 设备树根节点的compatible属性列出了一系列的字符串,表示它兼容的单板名,从"最兼容"到次之的machine

b. 内核中有多个machine_desc(description),其中有dt_compat成员, 它指向一个字符串数组, 里面表示该machine_desc支持哪些单板

/*linux-4.19-rc3/arch/arm/mach-s3c24xx/mach-smdk2440.c
本文章基于韦东山老师的JZ2440开发板进行讲解
*/

内核对设备树的处理(1)_第1张图片
c. 使用compatile属性的值,跟每一个machine_desc.dt_compat比较, 成绩为"吻合的compatile属性值的位置", 成绩越低越匹配, 对应的machine_desc即被选中
内核对设备树的处理(1)_第2张图片
内核对设备树的处理(1)_第3张图片

函数调用过程:
start_kernel // init/main.c
    setup_arch(&command_line);  // arch/arm/kernel/setup.c
        mdesc = setup_machine_fdt(__atags_pointer);  // arch/arm/kernel/devtree.c
                    early_init_dt_verify(phys_to_virt(dt_phys)  // 判断是否有效的dtb, drivers/of/ftd.c
                                    initial_boot_params = params;
                    mdesc = of_flat_dt_match_machine(mdesc_best, arch_get_next_mach);  // 找到最匹配的machine_desc, drivers/of/ftd.c
                                    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;

三、对设备树中运行时配置信息的处理

// SPDX-License-Identifier: GPL-2.0
/*
 * SAMSUNG SMDK2440 board device tree source
 *
 * Copyright (c) 2018 [email protected]
 * dtc -I dtb -O dts -o jz2440.dts jz2440.dtb
 */
 
#define S3C2410_GPA(_nr)	((0<<16) + (_nr))
#define S3C2410_GPB(_nr)	((1<<16) + (_nr))
#define S3C2410_GPC(_nr)	((2<<16) + (_nr))
#define S3C2410_GPD(_nr)	((3<<16) + (_nr))
#define S3C2410_GPE(_nr)	((4<<16) + (_nr))
#define S3C2410_GPF(_nr)	((5<<16) + (_nr))
#define S3C2410_GPG(_nr)	((6<<16) + (_nr))
#define S3C2410_GPH(_nr)	((7<<16) + (_nr))
#define S3C2410_GPJ(_nr)	((8<<16) + (_nr))
#define S3C2410_GPK(_nr)	((9<<16) + (_nr))
#define S3C2410_GPL(_nr)	((10<<16) + (_nr))
#define S3C2410_GPM(_nr)	((11<<16) + (_nr))

/dts-v1/;

/ {
	model = "SMDK24440";
	compatible = "samsung,smdk2440";

	#address-cells = <1>;
	#size-cells = <1>;
		
	memory@30000000 {
		device_type = "memory";
		reg =  <0x30000000 0x4000000>;
	};
/*
	cpus {
		cpu {
			compatible = "arm,arm926ej-s";
		};
	};
*/	
	chosen {
		bootargs = "noinitrd root=/dev/mtdblock4 rw init=/linuxrc console=ttySAC0,115200";
	};

	
	led {
		compatible = "jz2440_led";
		reg = <S3C2410_GPF(5) 1>;
	};
};
函数调用过程:
start_kernel // init/main.c
    setup_arch(&command_line);  // arch/arm/kernel/setup.c
        mdesc = setup_machine_fdt(__atags_pointer);  // arch/arm/kernel/devtree.c
        early_init_dt_scan_nodes();      // drivers/of/ftd.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);
            	rc = it(offset, pathp, depth, data);//回调函数

a. /chosen节点中bootargs属性的值, 存入全局变量: boot_command_line

b. 确定根节点的这2个属性的值: #address-cells, #size-cells存入全局变量: dt_root_addr_cells, dt_root_size_cells

c. 解析/memory中的reg属性, 提取出"base, size", 最终调用memblock_add(base, size);

early_init_dt_scan_memory()
	early_init_dt_add_memory_arch()
		memblock_add(base, size);

注:本文章参考了《韦东山老师嵌入式课程》笔记,并结合了自己的实际开发经历以及网上他人的技术文章,综合整理得到。如有侵权,联系删除!水平有限,欢迎各位在评论区交流。

你可能感兴趣的:(初窥Linux内核,设备树学习,嵌入式硬件,linux,驱动开发,arm,设备树)