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 里面的一些函数做体系架构相关的初始化。
2925

被折叠的 条评论
为什么被折叠?



