为什么要卸载earlycon ?

xiaoxiao2021-02-28  86

当在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。
转载请注明原文地址: https://www.6miu.com/read-28095.html

最新回复(0)