early_console是Kernel初始化初期建立起来用于串行输出的设备,源码在earlycon.c中实现。
1. 初始化入口
Kernel中 early_param 通常作为模块初始化入口元素的定义,在Kernel初始化时执行解析和模块初始化。Linux Kernel中众多模块或驱动子系统均通过这种方式定义其初始化入口。在Kernel初始化汇编代码执行完跳转到start_kernel之后,setup_arch调用parse_early_param,进而在其中执行 early_param 的解析,具体如下:
start_kernel->setup_arch->parse_early_param->parse_early_options->do_early_param
实际上在parse_early_options中调用parse_args,并且将do_early_param作为参数传入,进而继续调用parse_one且传入参数do_early_param,parse_one将参数解析成功后执行do_early_param:
/* Check for early params. */
static int __init do_early_param(char *param, char *val,
const char *unused, void *arg)
{
const struct obs_kernel_param *p;
for (p = __setup_start; p < __setup_end; p++) {
if ((p->early && parameq(param, p->str)) ||
(strcmp(param, "console") == 0 &&
strcmp(p->str, "earlycon") == 0)
) {
if (p->setup_func(val) != 0)
pr_warn("Malformed early option '%s'\n", param);
}
}
/* We accept everything at this stage. */
return 0;
}
在vmlinux.lds.h(include/asm-generic)中可以看到对于.init.setup段的定义:
#define INIT_SETUP(initsetup_align) \
. = ALIGN(initsetup_align); \
__setup_start = .; \
KEEP(*(.init.setup)) \
__setup_end = .;
其对__setup_start和__setup_end地址也做了划定,那么此处的do_early_param函数就是对.init.setup段内定义的参数进行遍历并调用器setup_func成员进行初始化。
而earlycon的early_param的定义如下:
/* early_param wrapper for setup_earlycon() */
static int __init param_setup_earlycon(char *buf)
{
int err;
/* Just 'earlycon' is a valid param for devicetree and ACPI SPCR. */
if (!buf || !buf[0]) {
if (IS_ENABLED(CONFIG_ACPI_SPCR_TABLE)) {
earlycon_acpi_spcr_enable = true;
return 0;
} else if (!buf) {
return early_init_dt_scan_chosen_stdout();
}
}
err = setup_earlycon(buf);
if (err == -ENOENT || err == -EALREADY)
return 0;
return err;
}
early_param("earlycon", param_setup_earlycon);
这里通过early_param来定义,需要先分析一下early_param:
struct obs_kernel_param {
const char *str;
int (*setup_func)(char *);
int early;
};
/*
* Only for really core code. See moduleparam.h for the normal way.
*
* Force the alignment so the compiler doesn't space elements of the
* obs_kernel_param "array" too far apart in .init.setup.
*/
#define __setup_param(str, unique_id, fn, early) \
static const char __setup_str_##unique_id[] __initconst \
__aligned(1) = str; \
static struct obs_kernel_param __setup_##unique_id \
__used __section(.init.setup) \
__attribute__((aligned((sizeof(long))))) \
= { __setup_str_##unique_id, fn, early }
#define __setup(str, fn) \
__setup_param(str, fn, fn, 0)
/*
* NOTE: fn is as per module_param, not __setup!
* Emits warning if fn returns non-zero.
*/
#define early_param(str, fn) \
__setup_param(str, fn, fn, 1)
就是一系列的宏,展开如下:
early_param("earlycon", param_setup_earlycon);
__setup_param("earlycon", param_setup_earlycon, param_setup_earlycon, 1);
static const char __setup_str_param_setup_earlycon[] __initconst __aligned(1) = "earlycon";
static struct obs_kernel_param __setup_param_setup_earlycon
__used_section(.init.setup) __attribute__((aligned((sizeof(long)))))
= {
.str = __setup_str_param_setup_earlycon,
.setup_func = param_setup_earlycon,
.early = 1
};
通过展开可以看出其实际作用是定义了一个类型为struct obs_kernel_param的静态结构并赋予初值,并且使用了编译器