Linux设备树5

Linux设备树5(基于Linux6.6)---machine_desc


一、machine_desc概述

在 Linux 内核中,machine_desc 是一个重要的数据结构,用于描述特定硬件平台或体系结构的基本信息,尤其是在启动过程中。它通常被用于与平台相关的初始化、配置和处理过程相关的信息存储。这是平台相关的配置代码的一部分,尤其是在支持多种硬件架构(如 ARM、x86 等)时,machine_desc 结构帮助内核识别和适配目标硬件。

machine_desc 是一个包含多个字段的结构体,存储了与特定硬件平台相关的各种信息,包括硬件平台的名字、启动时需要调用的初始化函数、与硬件平台相关的中断、内存布局等。它是内核在启动时确定自己运行的平台并进行适配的关键。

二、machine_desc结构体

 arch/arm/include/asm/mach/arch.h

struct machine_desc {
	unsigned int		nr;		/* architecture number	*/
	const char		*name;		/* architecture name	*/
	unsigned long		atag_offset;	/* tagged list (relative) */
	const char *const 	*dt_compat;	/* array of device tree
						 * 'compatible' strings	*/

	unsigned int		nr_irqs;	/* number of IRQs */

#ifdef CONFIG_ZONE_DMA
	phys_addr_t		dma_zone_size;	/* size of DMA-able area */
#endif

	unsigned int		video_start;	/* start of video RAM	*/
	unsigned int		video_end;	/* end of video RAM	*/

	unsigned char		reserve_lp0 :1;	/* never has lp0	*/
	unsigned char		reserve_lp1 :1;	/* never has lp1	*/
	unsigned char		reserve_lp2 :1;	/* never has lp2	*/
	enum reboot_mode	reboot_mode;	/* default restart mode	*/
	unsigned		l2c_aux_val;	/* L2 cache aux value	*/
	unsigned		l2c_aux_mask;	/* L2 cache aux mask	*/
	void			(*l2c_write_sec)(unsigned long, unsigned);
	const struct smp_operations	*smp;	/* SMP operations	*/
	bool			(*smp_init)(void);
	void			(*fixup)(struct tag *, char **);
	void			(*dt_fixup)(void);
	long long		(*pv_fixup)(void);
	void			(*reserve)(void);/* reserve mem blocks	*/
	void			(*map_io)(void);/* IO mapping function	*/
	void			(*init_early)(void);
	void			(*init_irq)(void);
	void			(*init_time)(void);
	void			(*init_machine)(void);
	void			(*init_late)(void);
	void			(*restart)(enum reboot_mode, const char *);
};

三、machine_desc过程分析

Linux6.6内核通过在start_kernel->setup_arch中调用setup_machine_tags来获取。

arch/arm/kernel/atags.h

#ifdef CONFIG_ATAGS
const struct machine_desc *setup_machine_tags(void *__atags_vaddr,
	unsigned int machine_nr);
#else
static inline const struct machine_desc * __init __noreturn
setup_machine_tags(void *__atags_vaddr, unsigned int machine_nr)
{
	early_print("no ATAGS support: can't continue\n");
	while (true);
	unreachable();
}
#endif

调用顺序如下:

 start_kernel
    setup_arch(&command_line);
        setup_machine_tags(__atags_pointer, __machine_arch_type);
 

具体的通过机器号查找对应的machine_desc 。arch/arm/kernel/atags_parse.c

  
const struct machine_desc * __init
setup_machine_tags(phys_addr_t __atags_pointer, unsigned int machine_nr)
{
	struct tag *tags = (struct tag *)&default_tags;
	const struct machine_desc *mdesc = NULL, *p;
	char *from = default_command_line;
 
	default_tags.mem.start = PHYS_OFFSET;
 
	/*
	 * locate machine in the list of supported machines.
	 */
	for_each_machine_desc(p)                     //遍历.arch.info.init段中所有machine_desc 的机器码和bootloader传给内核的机器码
		if (machine_nr == p->nr) {               //对比机器号
			printk("Machine: %s\n", p->name);
			mdesc = p;                           //匹配上则保存在mdesc里面并返回
			break;
		}
 
	if (!mdesc) {
		early_print("\nError: unrecognized/unsupported machine ID"
			    " (r1 = 0x%08x).\n\n", machine_nr);
		dump_machine_table(); /* does not return */
	}
 
	if (__atags_pointer)             //默认bootloader给内核传了tag的地址
		tags = phys_to_virt(__atags_pointer);       //tag存放的是物理地址,转换成虚拟地址
	else if (mdesc->atag_offset)     //如果bootloader每给内个传参的用物理起始地址+machine_desc 给的默认tag的偏移量
		tags = (void *)(PAGE_OFFSET + mdesc->atag_offset);
 
#if defined(CONFIG_DEPRECATED_PARAM_STRUCT)
	/*
	 * If we have the old style parameters, convert them to
	 * a tag list.
	 */
	if (tags->hdr.tag != ATAG_CORE)
		convert_to_tag_list(tags);
#endif
	if (tags->hdr.tag != ATAG_CORE) {
		early_print("Warning: Neither atags nor dtb found\n");
		tags = (struct tag *)&default_tags;
	}
 
	if (mdesc->fixup)
		mdesc->fixup(tags, &from);
 
	if (tags->hdr.tag == ATAG_CORE) {
		if (memblock_phys_mem_size())
			squash_mem_tags(tags);
		save_atags(tags);
		parse_tags(tags);
	}
 
	/* parse_early_param needs a boot_command_line */
	strlcpy(boot_command_line, from, COMMAND_LINE_SIZE);
 
	return mdesc;
}

在内核支持设备树之后,machine_desc结构体通过DT_MACHINE_START宏来初始化,在代码中, 通过在start_kernel->setup_arch中调用setup_machine_fdt来获取。arch/arm/include/asm/mach/arch.h

/*
 * Set of macros to define architecture features.  This is built into
 * a table by the linker.
 */
#define MACHINE_START(_type,_name)			\
static const struct machine_desc __mach_desc_##_type	\
 __used							\
 __section(".arch.info.init") = {			\
	.nr		= MACH_TYPE_##_type,		\
	.name		= _name,

#define MACHINE_END				\
};

#define DT_MACHINE_START(_name, _namestr)		\
static const struct machine_desc __mach_desc_##_name	\
 __used							\
 __section(".arch.info.init") = {			\
	.nr		= ~0,				\
	.name		= _namestr,

#endif
 

 可以看到这里给机器号赋值为~0;即一个无效值。所以只能通过设备树中的compatible属性来配置了。

调用顺序如下:

 start_kernel
    setup_arch(&command_line);
        mdesc = setup_machine_fdt(__atags_pointer);
 

具体的通过匹配compatible里面的字符串和的machine_desc 中dt_compat 中的字符串来匹配machine_desc。arch/arm64/kernel/setup.c

static void __init setup_machine_fdt(phys_addr_t dt_phys)
{
	int size;
	void *dt_virt = fixmap_remap_fdt(dt_phys, &size, PAGE_KERNEL);
	const char *name;

	if (dt_virt)
		memblock_reserve(dt_phys, size);

	if (!dt_virt || !early_init_dt_scan(dt_virt)) {
		pr_crit("\n"
			"Error: invalid device tree blob at physical address %pa (virtual address 0x%px)\n"
			"The dtb must be 8-byte aligned and must not exceed 2 MB in size\n"
			"\nPlease check your bootloader.",
			&dt_phys, dt_virt);

		/*
		 * Note that in this _really_ early stage we cannot even BUG()
		 * or oops, so the least terrible thing to do is cpu_relax(),
		 * or else we could end-up printing non-initialized data, etc.
		 */
		while (true)
			cpu_relax();
	}

	/* Early fixups are done, map the FDT as read-only now */
	fixmap_remap_fdt(dt_phys, &size, PAGE_KERNEL_RO);

	name = of_flat_dt_get_machine_name();
	if (!name)
		return;

	pr_info("Machine model: %s\n", name);
	dump_stack_set_arch_desc("%s (DT)", name);
}

早期的检查设备树是否有效。early_init_dt_scan-->early_init_dt_verify

drivers/of/fdt.c
bool __init early_init_dt_scan(void *params)
{
	bool status;

	status = early_init_dt_verify(params);
	if (!status)
		return false;

	early_init_dt_scan_nodes();
	return true;
}

bool __init early_init_dt_verify(void *params)
{
	if (!params)
		return false;

	/* check device tree validity */
	if (fdt_check_header(params))
		return false;

	/* Setup flat device-tree pointer */
	initial_boot_params = params;
	of_fdt_crc32 = crc32_be(~0, initial_boot_params,
				fdt_totalsize(initial_boot_params));
	return true;
}


scripts/dtc/libfdt/fdt.c
int fdt_check_header(const void *fdt)
{
	size_t hdrsize;

	/* The device tree must be at an 8-byte aligned address */
	if ((uintptr_t)fdt & 7)
		return -FDT_ERR_ALIGNMENT;

	if (fdt_magic(fdt) != FDT_MAGIC) //检查魔数
		return -FDT_ERR_BADMAGIC;
	if (!can_assume(LATEST)) {
		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;
	}
	hdrsize = fdt_header_size(fdt); //检查dtb大小
	if (!can_assume(VALID_DTB)) {
		if ((fdt_totalsize(fdt) < hdrsize)
		    || (fdt_totalsize(fdt) > INT_MAX))
			return -FDT_ERR_TRUNCATED;

		/* Bounds check memrsv block */
		if (!check_off_(hdrsize, fdt_totalsize(fdt),
				fdt_off_mem_rsvmap(fdt)))
			return -FDT_ERR_TRUNCATED;

		/* Bounds check structure block */
		if (!can_assume(LATEST) && 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;
		}

		/* Bounds check strings block */
		if (!check_block_(hdrsize, fdt_totalsize(fdt),
				  fdt_off_dt_strings(fdt),
				  fdt_size_dt_strings(fdt)))
			return -FDT_ERR_TRUNCATED;
	}

	return 0;
}

查找从dtb文件和.arch.info.init段获取最佳匹配。setup_arch-->setup_machine_fdt-->of_flat_dt_match_machine

drivers/of/fdt.c

  
/**
 * of_flat_dt_match_machine - Iterate match tables to find matching machine.
 *
 * @default_match: A machine specific ptr to return in case of no match.
 * @get_next_compat: callback function to return next compatible match table.
 *
 * Iterate through machine match tables to find the best match for the machine
 * compatible string in the FDT.
 */
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();        // return 0;
    /* 编译查找最佳匹配 */
	while ((data = get_next_compat(&compat))) {     //遍历.arch.info.init中的所有machine_desc并依次返回给data 
		score = of_flat_dt_match(dt_root, compat);    //找到使能匹配到的并返回等级
		if (score > 0 && score < best_score) {        //score 值越小,最佳匹配度越高
			best_data = data;      //最佳的匹配
			best_score = score;    //最佳等级
		}
	}
	if (!best_data) {              //没有找到任何匹配,则说明设备树中传的参数不对
		const char *prop;
		int size;
 
        //打印内核不识别这个设备树列表(可能不是这个内核支持单板的设备树文件)
		pr_err("\n unrecognized device tree list:\n[ ");
 
        /* 依次打印出bootloader传给内核的compatible的所有列表 */
		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;
	}
 
    //这里打印出提示信息,即dtb文件中的model属性
	pr_info("Machine model: %s\n", of_flat_dt_get_machine_name());
 
	return best_data;        //最终返回最佳匹配的machine_desc
}
 
 
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)    //判断machine_desc 没超出组织的段范围
		return NULL;
 
	mdesc++;
	*match = m->dt_compat;       //返回这个machine_desc 中的dt_compat
	return m;
}
 
 
/*
 * of_flat_dt_match - Return true if node matches a list of compatible values
 */
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) { //确认这个machine_desc中的dt_compat有效
		tmp = of_fdt_is_compatible(initial_boot_params, node, *compat);
		if (tmp && (score == 0 || (tmp < score)))
			score = tmp;
		compat++;
	}

	return score;
}
 
 
//忽略大小写比较字符串
#define of_compat_cmp(s1, s2, l)	strcasecmp((s1), (s2))
 
 
/**
 * of_fdt_is_compatible - Return true if given node from the given blob has
 * compat in its compatible list
 * @blob: A device tree blob
 * @node: node to test
 * @compat: compatible string to compare with compatible list.
 *
 * On match, returns a non-zero value with smaller values returned for more
 * specific compatible values.
 */
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;
 
    //获取dtb文件中的compatible属性的值(可能不止一个字符串),cplen获取的总字符串长度
	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)    //比较dtb中的compatible属性值的字符串和machine_desc 中dt_compat中的字符串
			return score;      //匹配到score有效值最小为1
		l = strlen(cp) + 1;
		cp += l;               //若没匹配到,则匹配"compatible"中下一个字符串
		cplen -= l;
	}
 
	return 0;
}
 
 
 
 

在内核启动初期,打印的提示信息如上所示。

最后给出setup_arch()函数的原型arch/arm/kernel/setup.c

void __init setup_arch(char **cmdline_p)
{
	const struct machine_desc *mdesc = NULL;
	void *atags_vaddr = NULL;

	if (__atags_pointer)
		atags_vaddr = FDT_VIRT_BASE(__atags_pointer);

	setup_processor();
	if (atags_vaddr) {
		mdesc = setup_machine_fdt(atags_vaddr);
		if (mdesc)
			memblock_reserve(__atags_pointer,
					 fdt_totalsize(atags_vaddr));
	}
	if (!mdesc)
		mdesc = setup_machine_tags(atags_vaddr, __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, atags_vaddr);
		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;

	setup_initial_init_mm(_text, _etext, _edata, _end);

	/* populate cmd_line too for later use, preserving boot_command_line */
	strscpy(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();
	arm_efi_init();
	/*
	 * Make sure the calculation for lowmem/highmem is set appropriately
	 * before reserving/allocating any memory
	 */
	adjust_lowmem_bounds();
	arm_memblock_init(mdesc);
	/* Memory may have been removed so recalculate the bounds. */
	adjust_lowmem_bounds();

	early_ioremap_reset();

	paging_init(mdesc);
	kasan_init();
	request_standard_resources(mdesc);

	if (mdesc->restart) {
		__arm_pm_restart = mdesc->restart;
		register_restart_handler(&arm_restart_nb);
	}

	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_VT
#if defined(CONFIG_VGA_CONSOLE)
	conswitchp = &vga_con;
#endif
#endif

	if (mdesc->init_early)
		mdesc->init_early();
}

可以看到获取了最佳匹配的machine_desc ,设备树节点生成后,就是调用machine_desc 里面的一些函数做体系架构相关的初始化。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值