当通过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
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