Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

serial: earlycon: Enable earlycon without command line param

Earlycon matching can only be triggered if 'earlycon=...' has been
specified on the kernel command line. To workaround this limitation
requires tight coupling between arches and specific serial drivers
in order to start an earlycon. Devicetree avoids this limitation
with a link table that contains the required data to match earlycons.

Mirror this approach for earlycon match by name. Re-purpose
EARLYCON_DECLARE to generate a table entry which associates name with
setup() function. Re-purpose setup_earlycon() to scan this table for
an earlycon match, which is registered if found.

Declare one "earlycon" early_param, which calls setup_earlycon().

This design allows setup_earlycon() to be called directly with a
param string (as if 'earlycon=...' had been set on the command line).
Re-registration (either directly or by early_param) is prevented.

Acked-by: Rob Herring <robh@kernel.org>
Signed-off-by: Peter Hurley <peter@hurleysoftware.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Peter Hurley and committed by
Greg Kroah-Hartman
470ca0de 7c53cb3d

+94 -33
+1 -6
drivers/tty/serial/8250/8250_early.c
··· 170 170 171 171 int __init setup_early_serial8250_console(char *cmdline) 172 172 { 173 - char match[] = "uart8250"; 174 - 175 - if (cmdline && cmdline[4] == ',') 176 - match[4] = '\0'; 177 - 178 - return setup_earlycon(cmdline, match, early_serial8250_setup); 173 + return setup_earlycon(cmdline); 179 174 }
+73 -19
drivers/tty/serial/earlycon.c
··· 10 10 * it under the terms of the GNU General Public License version 2 as 11 11 * published by the Free Software Foundation. 12 12 */ 13 + 14 + #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 15 + 13 16 #include <linux/console.h> 14 17 #include <linux/kernel.h> 15 18 #include <linux/init.h> ··· 36 33 static struct earlycon_device early_console_dev = { 37 34 .con = &early_con, 38 35 }; 36 + 37 + extern struct earlycon_id __earlycon_table[]; 38 + static const struct earlycon_id __earlycon_table_sentinel 39 + __used __section(__earlycon_table_end); 39 40 40 41 static const struct of_device_id __earlycon_of_table_sentinel 41 42 __used __section(__earlycon_of_table_end); ··· 103 96 return 0; 104 97 } 105 98 106 - 107 - static int __init 108 - register_earlycon(char *buf, int (*setup)(struct earlycon_device *, const char *)) 99 + static int __init register_earlycon(char *buf, const struct earlycon_id *match) 109 100 { 110 101 int err; 111 102 struct uart_port *port = &early_console_dev.port; ··· 117 112 port->membase = earlycon_map(port->mapbase, 64); 118 113 119 114 early_console_dev.con->data = &early_console_dev; 120 - err = setup(&early_console_dev, buf); 115 + err = match->setup(&early_console_dev, buf); 121 116 if (err < 0) 122 117 return err; 123 118 if (!early_console_dev.con->write) ··· 127 122 return 0; 128 123 } 129 124 130 - int __init setup_earlycon(char *buf, const char *match, 131 - int (*setup)(struct earlycon_device *, const char *)) 125 + /** 126 + * setup_earlycon - match and register earlycon console 127 + * @buf: earlycon param string 128 + * 129 + * Registers the earlycon console matching the earlycon specified 130 + * in the param string @buf. Acceptable param strings are of the form 131 + * <name>,io|mmio|mmio32,<addr>,<options> 132 + * <name>,0x<addr>,<options> 133 + * <name>,<options> 134 + * <name> 135 + * 136 + * Only for the third form does the earlycon setup() method receive the 137 + * <options> string in the 'options' parameter; all other forms set 138 + * the parameter to NULL. 139 + * 140 + * Returns 0 if an attempt to register the earlycon was made, 141 + * otherwise negative error code 142 + */ 143 + int __init setup_earlycon(char *buf) 132 144 { 133 - size_t len; 145 + const struct earlycon_id *match; 134 146 135 - if (!buf || !match || !setup) 136 - return 0; 147 + if (!buf || !buf[0]) 148 + return -EINVAL; 137 149 138 - len = strlen(match); 139 - if (strncmp(buf, match, len)) 140 - return 0; 150 + if (early_con.flags & CON_ENABLED) 151 + return -EALREADY; 141 152 142 - if (buf[len]) { 143 - if (buf[len] != ',') 144 - return 0; 145 - buf += len + 1; 146 - } else 147 - buf = NULL; 153 + for (match = __earlycon_table; match->name[0]; match++) { 154 + size_t len = strlen(match->name); 148 155 149 - return register_earlycon(buf, setup); 156 + if (strncmp(buf, match->name, len)) 157 + continue; 158 + 159 + if (buf[len]) { 160 + if (buf[len] != ',') 161 + continue; 162 + buf += len + 1; 163 + } else 164 + buf = NULL; 165 + 166 + return register_earlycon(buf, match); 167 + } 168 + 169 + return -ENOENT; 150 170 } 171 + 172 + /* early_param wrapper for setup_earlycon() */ 173 + static int __init param_setup_earlycon(char *buf) 174 + { 175 + int err; 176 + 177 + /* 178 + * Just 'earlycon' is a valid param for devicetree earlycons; 179 + * don't generate a warning from parse_early_params() in that case 180 + */ 181 + if (!buf || !buf[0]) 182 + return 0; 183 + 184 + err = setup_earlycon(buf); 185 + if (err == -ENOENT) { 186 + pr_warn("no match for %s\n", buf); 187 + err = 0; 188 + } else if (err == -EALREADY) { 189 + pr_warn("already registered\n"); 190 + err = 0; 191 + } 192 + return err; 193 + } 194 + early_param("earlycon", param_setup_earlycon); 151 195 152 196 int __init of_setup_earlycon(unsigned long addr, 153 197 int (*setup)(struct earlycon_device *, const char *))
+9
include/asm-generic/vmlinux.lds.h
··· 150 150 #define TRACE_SYSCALLS() 151 151 #endif 152 152 153 + #ifdef CONFIG_SERIAL_EARLYCON 154 + #define EARLYCON_TABLE() . = ALIGN(8); \ 155 + VMLINUX_SYMBOL(__earlycon_table) = .; \ 156 + *(__earlycon_table) \ 157 + *(__earlycon_table_end) 158 + #else 159 + #define EARLYCON_TABLE() 160 + #endif 153 161 154 162 #define ___OF_TABLE(cfg, name) _OF_TABLE_##cfg(name) 155 163 #define __OF_TABLE(cfg, name) ___OF_TABLE(cfg, name) ··· 511 503 CPU_METHOD_OF_TABLES() \ 512 504 KERNEL_DTB() \ 513 505 IRQCHIP_OF_MATCH_TABLE() \ 506 + EARLYCON_TABLE() \ 514 507 EARLYCON_OF_TABLES() 515 508 516 509 #define INIT_TEXT \
+11 -8
include/linux/serial_core.h
··· 337 337 char options[16]; /* e.g., 115200n8 */ 338 338 unsigned int baud; 339 339 }; 340 - int setup_earlycon(char *buf, const char *match, 341 - int (*setup)(struct earlycon_device *, const char *)); 342 340 341 + struct earlycon_id { 342 + char name[16]; 343 + int (*setup)(struct earlycon_device *, const char *options); 344 + }; 345 + 346 + extern int setup_earlycon(char *buf); 343 347 extern int of_setup_earlycon(unsigned long addr, 344 348 int (*setup)(struct earlycon_device *, const char *)); 345 349 346 - #define EARLYCON_DECLARE(name, func) \ 347 - static int __init name ## _setup_earlycon(char *buf) \ 348 - { \ 349 - return setup_earlycon(buf, __stringify(name), func); \ 350 - } \ 351 - early_param("earlycon", name ## _setup_earlycon); 350 + #define EARLYCON_DECLARE(_name, func) \ 351 + static const struct earlycon_id __earlycon_##_name \ 352 + __used __section(__earlycon_table) \ 353 + = { .name = __stringify(_name), \ 354 + .setup = func } 352 355 353 356 #define OF_EARLYCON_DECLARE(name, compat, fn) \ 354 357 _OF_DECLARE(earlycon, name, compat, fn, void *)