由_OF_DECLARE引发对内核SECTION段解析的思考(基于kernel-4.9)

版权声明:本文为博主原创文章,未经博主允许不得转载。 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;
}
阅读更多

没有更多推荐了,返回首页