grub参数console=

本文主要分析Linux内核如何处理grub参数中的console=ttyS0,115200n8部分,中间还会穿插一些 include/linux/init.h 的内容。
grub参数中的console=有多种形式,根据Documentation/kernel-parameters.txt 文件,

console= [KNL] Output console device and options.

  • tty Use the virtual console device .
  • ttyS[,options]
  • ttyUSB0[,options]
    Use the specified serial port. The options are of the form "bbbbpnf", where "bbbb" is the baud rate, "p" is parity ("n", "o", or "e"), "n" is number of bits, and "f" is flow control ("r" for RTS or omit it). Default is "9600n8".
  • uart[8250],io,[,options]
  • uart[8250],mmio,[,options]
    Start an early, polled-mode console on the 8250/16550 UART at the specified I/O port or MMIO address, switching to the matching ttyS device later. The options are the same as for ttyS, above.
  • hvc Use the hypervisor console device .
    This is for both Xen and PowerPC hypervisors.

本文主要分析参数值为ttyS,uart[8250],io/mmio,[,options]的情况。

cmdline的"console="参数

内核启动过程中,把grub设置的cmdline保存在init/main.cboot_command_line字符数组中,长度最大为256。
start_kernel 函数在输出过boot_command_line信息后,会对其进行转化,分别调用parse_early_param -> parse_early_options -> parse_args(kernel/params.c) -> do_early_param 。其中,parse_args 函数调用 parse_one 完成具体的操作的各个参数如下:

/* @param = "console"
   @val = "ttyS0,115200n8"或者"uart[8250],io,<addr>[,option]"
   @doing = "early options"
   @params = NULL
   @num_params = 0
   @min_level = 0
   @max_level = 0
   @unknown = do_early_param */
static int parse_one(char *param,
             char *val,
             const char *doing,
             const struct kernel_param *params,
             unsigned num_params,
             s16 min_level,
             s16 max_level,
             int (*handle_unknown)(char *param, char *val,
                     const char *doing))
{
    unsigned int i;
    int err;

    /* Find parameter */
    for (i = 0; i < num_params; i++) {
        if (parameq(param, params[i].name)) {
            if (params[i].level < min_level
                || params[i].level > max_level)
                return 0;
            /* No one handled NULL, so do it here. */
            if (!val &&
                !(params[i].ops->flags & KERNEL_PARAM_FL_NOARG))
                return -EINVAL;
            pr_debug("handling %s with %p\n", param,
                params[i].ops->set);
            mutex_lock(&param_lock);
            err = params[i].ops->set(val, &params[i]);
            mutex_unlock(&param_lock);
            return err;
        }
    }

    if (handle_unknown) {
        pr_debug("doing %s: %s='%s'\n", doing, param, val);
        return handle_unknown(param, val, doing);
    }
    
    pr_debug("Unknown argument '%s'\n", param);
    return -ENOENT;
}

最终,handle_unknown(param, val, doing) -> do_early_param(param, val, doing)

/* @param = "console"
   @val = "uart[8250],io,<addr>[,option]"
   @unused = "early options" */
static int __init do_early_param(char *param, char *val, const char *unused)
{
    const struct obs_kernel_param *p;
    /* __setup_start声明在init/main.c文件中,通过lds文件链接到名为
       .init.setup 的段中,这个段通过include/linux/init.h中的宏
       __setup定义.
       因此,这里会遍历所有通过__setup宏定义的函数,选择设置了early=1
       的obs_kernel_param对象,并且str="earlycon"的函数,
       执行其setup_func */
    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;
}

设置了early标志的struct obs_kernel_param对象通过early_param宏定义,在 include/linux/init.h 中:

 #define early_param(str, fn) 
    __setup_param(str, fn, fn, 1)
#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 }

# console=uart[8250],io/mmio,[,options]
serial8250_console定义在 drivers/tty/serial/8250/8250_core.c 中,是基于uart的控制台,初始化函数为serial8250_console_init ,之后调用 include/linux/init.h 中的console_initcall(serial8250_console_init)

这种情况下,do_early_param(param, val, doing) 的参数param="console""val=uart[8250],io/mmio,[,options]"
根据定义在include/linux/serial_core.h 中的

#define EARLYCON_DECLARE(name, func)
static int __init name##_setup_earlycon(char *buf)
{
    return setup_earlycon(buf, __stringify(name), func);
}
early_param("earlycon", name##_setup_earlycon);

drivers/tty/serial/8250/8250_early.c 中的 EARLYCON_DECLARE(uart8250, early_serial8250_setup);EARLYCON_DECLARE(uart, early_serial8250_setup);

static int __init uart[8250]_setup_earlycon(char *buf)
{
    return setup_earlycon(buf, __stringify(uart[8250]), early_serial8250_setup);
 }
static const char __setup_str_uart[8250]_setup_earlycon[] = "earlycon";
stat
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值