版权声明:本文为CSDN博主「程序猿Ricky的日常干货」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/rikeyone/article/details/79975138
这个宏定义是定义在of.h中的,主要目的就是为了方便解析dts文件的。
#ifdef CONFIG_OF
#define _OF_DECLARE(table, name, compat, fn, fn_type) \
static const struct of_device_id __of_table_##name \
__used __section(__##table##_of_table) \
= { .compatible = compat, \
.data = (fn == (fn_type)NULL) ? fn : fn }
#else
#define _OF_DECLARE(table, name, compat, fn, fn_type) \
static const struct of_device_id __of_table_##name \
__attribute__((unused)) \
= { .compatible = compat, \
.data = (fn == (fn_type)NULL) ? fn : fn }
#endif
以上的定义来看,它使用了__section
,其实展开以后可以看到,其实是传递给编译器的一个attribute:
/* Simple shorthand for a section definition */
#ifndef __section
# define __section(S) __attribute__ ((__section__(#S)))
#endif
关于attribute的使用,可以参考gcc相关的文档,简而言之,上面的一段宏定义实现的功能就是创建一个struct of_device_id结构的变量__of_table_XXX
,并把对应变量链接到__XXX_of_table
对应的section中。关于这个section的位置,我们可以通过查看vmlinux.lds文件来找到对应的定义,这个文件是 ld script,是给ld链接器使用的,用来链接生成最终的kernel image的。
编译内核完成后它也会被生成在obj/KERNEL_OBJ/arch/arm64/kernel/vmlinux.lds:
.init.data : {
*(.init.data) *(.meminit.data) *(.init.rodata) . = ALIGN(8); __start_ftrace_events = .; *(_ftrace_events) __stop_ftrace_events = .; __start_ftrace_enum_maps = .; *(_ftrace_enum_map) __stop_ftrace_enu
m_maps = .; *(.meminit.rodata) . = ALIGN(8);__clk_of_table= .; *(__clk_of_table) *(__clk_of_table_end) . = ALIGN(8);__reservedmem_of_table= .; *(__reservedmem_of_table) *(__reservedmem_of_table_end
) . = ALIGN(8); __clksrc_of_table = .; *(__clksrc_of_table) *(__clksrc_of_table_end) . = ALIGN(8); __iommu_of_table = .; *(__iommu_of_table) *(__iommu_of_table_end) . = ALIGN(8); __cpu_method_of_table
= .; *(__cpu_method_of_table) *(__cpu_method_of_table_end) . = ALIGN(8); __cpuidle_method_of_table = .; *(__cpuidle_method_of_table) *(__cpuidle_method_of_table_end) . = ALIGN(32); __dtb_start = .; *(.
dtb.init.rodata) __dtb_end = .; . = ALIGN(8); __irqchip_of_table = .; *(__irqchip_of_table) *(__irqchip_of_table_end) . = ALIGN(8); __irqchip_acpi_probe_table = .; *(__irqchip_acpi_probe_table) __irqch
ip_acpi_probe_table_end = .; . = ALIGN(8); __clksrc_acpi_probe_table = .; *(__clksrc_acpi_probe_table) __clksrc_acpi_probe_table_end = .; . = ALIGN(32); __earlycon_table = .; *(__earlycon_table) __earl
ycon_table_end = .;
. = ALIGN(16); __setup_start = .; *(.init.setup) __setup_end = .;
__initcall_start = .; *(.initcallearly.init) __initcall0_start = .; *(.initcall0.init) *(.initcall0s.init) __initcall1_start = .; *(.initcall1.init) *(.initcall1s.init) __initcall2_start = .; *(.init
call2.init) *(.initcall2s.init) __initcall3_start = .; *(.initcall3.init) *(.initcall3s.init) __initcall4_start = .; *(.initcall4.init) *(.initcall4s.init) __initcall5_start = .; *(.initcall5.init) *(.
initcall5s.init) __initcallrootfs_start = .; *(.initcallrootfs.init) *(.initcallrootfss.init) __initcall6_start = .; *(.initcall6.init) *(.initcall6s.init) __initcall7_start = .; *(.initcall7.init) *(.
initcall7s.init) __initcall_end = .;
__con_initcall_start = .; *(.con_initcall.init) __con_initcall_end = .;
__security_initcall_start = .; *(.security_initcall.init) __security_initcall_end = .;
. = ALIGN(4); __initramfs_start = .; *(.init.ramfs) . = ALIGN(8); *(.init.ramfs.info)
}
从上面的文件中可以看到,lds中会有很多个__XXX__of_table section
的定义,它们都是被放到.init.data section中的。
而针对这段of_table的解析在各个模块对应代码中由各自模块负责解析,我们接下来以__reservedmem_of_table
为例作为说明。
首先它的定义在:
#define RESERVEDMEM_OF_DECLARE(name, compat, init) \
_OF_DECLARE(reservedmem, name, compat, init, reservedmem_of_init_fn)
只要调用RESERVEDMEM_OF_DECLARE声明过的一些函数和name都会生成一个对应的__of_table_name
对应的一个struct of_device_id结构变量,并放置于SECTIONS(__reservedmem_of_table)
中,比如CMA的定义:
RESERVEDMEM_OF_DECLARE(cma, "shared-dma-pool", rmem_cma_setup);
上面的语句展开后其实对应如下:
static const struct of_device_id __of_table_cma
__used __attribute__ ((__section__(__reservedmem_of_table)))
= { .compatible = "shared-dma-pool",
.data = rmem_cma_setup
}
section(__reservedmem_of_table)
对应的解析函数在of_reserved_mem.c中:
static const struct of_device_id __rmem_of_table_sentinel
__used __section(__reservedmem_of_table_end);// 找到对应的 section end
/**
* res_mem_init_node() - call region specific reserved memory init code
*/
static int __init __reserved_mem_init_node(struct reserved_mem *rmem)
{
extern const struct of_device_id __reservedmem_of_table[];
const struct of_device_id *i;
for (i = __reservedmem_of_table; i < &__rmem_of_table_sentinel; i++) { //遍历__reservedmem_of_table section中的内容,
//也就是struct of_device_id结构变量
reservedmem_of_init_fn initfn = i->data;
const char *compat = i->compatible;
if (!of_flat_dt_is_compatible(rmem->fdt_node, compat)) //如果检测到dts中有compatible匹配就进一步执行对应的initfn
continue;
if (initfn(rmem) == 0) {
pr_info("Reserved memory: initialized node %s, compatible id %s\n",
rmem->name, compat);
return 0;
}
}
return -ENOENT;
}
评论