earlycon

当通过cmdline 传递给kernel earlycon这个参数时候的解析函数如下:
static int __init param_setup_earlycon(char *buf)
{
    int err;

    /*
     * Just 'earlycon' is a valid param for devicetree earlycons;
     * don't generate a warning from parse_early_params() in that case
     */
    if (!buf || !buf[0]) {
        if (IS_ENABLED(CONFIG_ACPI_SPCR_TABLE)) {
            earlycon_init_is_deferred = true;
            return 0;
        } else {
            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);
从param_setup_earlycon 可以看出 buf 可以为null,也就是通过cmdline 只传递earlycon,在这种情况下如果使能acpi的spcr table的话,就会设置earlycon_init_is_deferred = true,这样就在设定earlycon参数的时候是从spcr的acpi表中解析的,不明白的可以参考前一篇博文.
如果buf部位null,也就是按照类似这样的方式传递earlycon=pl01118,mmio,0xa01b0000,0的话,就会直接调用setup_earlycon 来设定console的参数
int __init setup_earlycon(char *buf)
{
    const struct earlycon_id *match;

    if (!buf || !buf[0])
        return -EINVAL;

    if (early_con.flags & CON_ENABLED)
        return -EALREADY;

    for (match = __earlycon_table; match < __earlycon_table_end; match++) {
        size_t len = strlen(match->name);

        if (strncmp(buf, match->name, len))
            continue;

        if (buf[len]) {
            if (buf[len] != ',')
                continue;
            buf += len + 1;
        } else
            buf = NULL;

        return register_earlycon(buf, match);
    }

    return -ENOENT;
}
在setup_earlycon 中首先判断earlycon是否已经enable了
    if (early_con.flags & CON_ENABLED)
        return -EALREADY;
如果已经enable,就直接返回了,也就是说系统中只能有一个earlycon
否则就调用for循环从__earlycon_table到__earlycon_table_end 这段空间中已经定义好的earlycon,那这段空间到底定义了多少earlycon呢?这点可以通过vim System.map 来查看.
ffff00000907ad60 T __earlycon_table
ffff00000907ae00 t __UNIQUE_ID___earlycon_uart17
ffff00000907aea0 t __UNIQUE_ID___earlycon_uart16
ffff00000907af40 t __UNIQUE_ID___earlycon_ns16550a15
ffff00000907afe0 t __UNIQUE_ID___earlycon_ns1655014
ffff00000907b080 t __UNIQUE_ID___earlycon_uart13
ffff00000907b120 t __UNIQUE_ID___earlycon_uart825012
ffff00000907b260 t __UNIQUE_ID___earlycon_pl01118
ffff00000907b300 t __UNIQUE_ID___earlycon_msm_serial_dm25
ffff00000907b3a0 t __UNIQUE_ID___earlycon_msm_serial24
ffff00000907b440 t __UNIQUE_ID___earlycon_ar3700_uart13
ffff00000907b4e0 t __UNIQUE_ID___earlycon_ar3700_uart12
ffff00000907b580 T __earlycon_table_end
我们这个例子就匹配的是__UNIQUE_ID___earlycon_pl01118
需要注意的是,即使在apci 下这些earlycon也是通过OF_EARLYCON_DECLARE 来申明的,这给人感觉很想devicetree的code其实不是,这里吐槽一下。

OF_EARLYCON_DECLARE(pl011, "arm,pl011", pl011_early_console_setup);

从setup_earlycon 调用        if (strncmp(buf, match->name, len)) 来匹配,可知只要是通过name来匹配的。
匹配成功后就调用register_earlycon
static int __init register_earlycon(char *buf, const struct earlycon_id *match)
{
    int err;
    struct uart_port *port = &early_console_dev.port;

    /* On parsing error, pass the options buf to the setup function */
    if (buf && !parse_options(&early_console_dev, buf))
        buf = NULL;

    spin_lock_init(&port->lock);
    port->uartclk = BASE_BAUD * 16;
    if (port->mapbase)
        port->membase = earlycon_map(port->mapbase, 64);

    earlycon_init(&early_console_dev, match->name);
    err = match->setup(&early_console_dev, buf);
    if (err < 0)
        return err;
    if (!early_console_dev.con->write)
        return -ENODEV;

    register_console(early_console_dev.con);
    return 0;
}
在register_earlycon 中会调用setup函数也就是前面的pl011_early_console_setup。
这个函数最终调用register_console 来注册console,可见在kernel初始化阶段earlycon就是console


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值