为什么要卸载earlycon ?

来源:互联网 发布:win10系统优化技巧 编辑:程序博客网 时间:2024/06/07 17:01
当在kernel commandline 传递earlycon后系统对earlycon的解析是在drivers/tty/serial/earlycon.c 中
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);
当buf不为0,也就是earlycon后面会跟一串设定,例如earlycon=pl011,mmio,0x12345678. 这个时候就会调用setup_earlycon
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_table到__earlycon_table_end,之间找到和kernel commandline 匹配的,例如我们这里就是匹配pl011 这个字符串。如果匹配到了就调用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 中会调用
static void __init earlycon_init(struct earlycon_device *device,
                 const char *name)
{
    struct console *earlycon = device->con;
    struct uart_port *port = &device->port;
    const char *s;
    size_t len;

    /* scan backwards from end of string for first non-numeral */
    for (s = name + strlen(name);
         s > name && s[-1] >= '0' && s[-1] <= '9';
         s--)
        ;
    if (*s)
        earlycon->index = simple_strtoul(s, NULL, 10);
    len = s - name;
    strlcpy(earlycon->name, name, min(len + 1, sizeof(earlycon->name)));
    earlycon->data = &early_console_dev;

    if (port->iotype == UPIO_MEM || port->iotype == UPIO_MEM16 ||
        port->iotype == UPIO_MEM32 || port->iotype == UPIO_MEM32BE)
        pr_info("%s%d at MMIO%s %pa (options '%s')\n",
            earlycon->name, earlycon->index,
            (port->iotype == UPIO_MEM) ? "" :
            (port->iotype == UPIO_MEM16) ? "16" :
            (port->iotype == UPIO_MEM32) ? "32" : "32be",
            &port->mapbase, device->options);
    else
        pr_info("%s%d at I/O port 0x%lx (options '%s')\n",
            earlycon->name, earlycon->index,
            port->iobase, device->options);
}
在earlycon_init 中有一个关键的赋值earlycon->data = &early_console_dev;
static struct console early_con = {
    .name =        "uart",        /* fixed up at earlycon registration */
    .flags =    CON_PRINTBUFFER | CON_BOOT,
    .index =    0,
};

static struct earlycon_device early_console_dev = {
    .con = &early_con,
};
从这里知道原来CON_BOOT 这个flag是在这里赋值的
回到register_earlycon 中继续调用    err = match->setup(&early_console_dev, buf);针对本例,

OF_EARLYCON_DECLARE(pl011, "arm,pl011", pl011_early_console_setup);
而#define OF_EARLYCON_DECLARE(_name, compat, fn)                \
    static const struct earlycon_id __UNIQUE_ID(__earlycon_##_name)    \
         EARLYCON_USED_OR_UNUSED __section(__earlycon_table)    \
        = { .name = __stringify(_name),                \
            .compatible = compat,                \
            .setup = fn  }
可见是通过OF_EARLYCON_DECLARE将pl011_early_console_setup 放到__earlycon_table到__earlycon_table_end 之间的,所以这里对应的setup函数为pl011_early_console_setup
static int __init pl011_early_console_setup(struct earlycon_device *device,
                        const char *opt)
{
    if (!device->port.membase)
        return -ENODEV;

    pl011_check_busy_workaround();

    device->con->write = pl011_early_write;
    return 0;
}
从pl011_early_console_setup 中可以得知device->con->write指向的函数为pl011_early_write
static void pl011_early_write(struct console *con, const char *s, unsigned n)
{
    struct earlycon_device *dev = con->data;

    uart_console_write(&dev->port, s, n, pl011_putc);
}
在uart_console_write 中最终调用pl011_putc 来将字符串显示到串口上
static void pl011_putc(struct uart_port *port, int c)
{
    while (readl(port->membase + UART01x_FR) & UART01x_FR_TXFF)
        cpu_relax();
    if (port->iotype == UPIO_MEM32)
        writel(c, port->membase + UART01x_DR);
    else
        writeb(c, port->membase + UART01x_DR);
    if (unlikely(pl011_workaround_busy)) {
        while (!(readl(port->membase + UART01x_FR) & UART011_FR_TXFE))
            cpu_relax();
    } else {
        while (readl(port->membase + UART01x_FR) & UART01x_FR_BUSY)
            cpu_relax();
    }
}
从pl011_putc 实现可以看到实际就是直接通过writel来写寄存器而已.
所以从earlycon的实现可以知道,earlycon就是直接写寄存器而已,所以earlycon的flag中包含CON_BOOT 这个flag。所以当真正的console使用DMA输出字符的时候,会将包含CON_BOOT的console卸载掉
    if (bcon &&
        ((newcon->flags & (CON_CONSDEV | CON_BOOT)) == CON_CONSDEV) &&
        !keep_bootcon) {
        /* We need to iterate through all boot consoles, to make
         * sure we print everything out, before we unregister them.
         */
        for_each_console(bcon)
            if (bcon->flags & CON_BOOT)
                unregister_console(bcon);
    }
这段code是register_console 中卸载包含CON_BOOT 这个flag。

0 0
原创粉丝点击