Linux终端控制台console和earlycon分析

本文深入探讨Linux Kernel的earlycon初始化过程,包括早期参数解析、setup_earlycon函数、命令行参数定义、根据device-tree初始化以及通过EARLYCON_DECLARE宏定义earlycon的方式。分析了从start_kernel到register_console的整个流程。
摘要由CSDN通过智能技术生成

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的静态结构并赋予初值,并且使用了编译器

  • 0
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值