2015-02-24 16:37:28

by Peter Hurley

[permalink] [raw]
Subject: [PATCH -next 00/13] Extensible console matching & direct earlycon

Hi Greg & Andrew,

This patch series implements:
1. console-definable (aka extensible) matching
2. generic earlycon-to-console handoff via extensible matching
3. arch/prom support for direct earlycon


Extensible console matching

Extensible console matching enables the console itself to define the
conditions for a "command line" match. This mimics the design of
device matching in the driver model. Two important use-cases which this
feature enables are generic earlycon-to-console handoff and support
for driver migration.

Earlycon-to-console handoff was implemented in 2007. Console command
lines of the form:
console=uart,io,0x3f8,115200n8
start an earlycon and later allow the 8250 driver console to takeover.
Unfortunately this implementation requires direct coupling between
the earlycon and the console and is facilitated by ugly hacks like
editing the console command line in-place.

Extensible console matching allows the 8250 driver to directly match
that console command line instead, and enables other serial drivers
to trivially support console handoff themselves.

In addition, extensible console matching allows a new driver to
provide support for a different driver's console. This requirement
stems from needing to minimize breakage when migrating serial drivers.
Since many devices are based on the original 8250/16550 designs
but sometimes have features incompatible with the existing 8250 driver
support, the initial driver is often standalone. When/if the standalone
driver is migrated to the 8250 driver, the problem of console names in
the command line remains. Extensible console matching enables a simple
migration path.


Direct earlycon

This feature enables arches and proms to start an earlycon directly,
rather than requiring an "earlycon=" command line parameter.
Devicetree can already do this via the 'linux,stdout-path' property,
but arch and prom code requires direct coupling to the serial driver.

This support is implemented by judicious refactoring and the same
construct that devicetree and early_param use: a link table containing
the necessary information (name and setup() function) to find and
bind the appropriate earlycon "driver".


NB: I combined these two features in this series because their
implementations heavily overlap in the same source files.

Regards,

Peter Hurley (13):
serial: earlycon: Refactor parse_options into serial core
console: Preserve index after console setup()
console: Add extensible console matching
serial: core: Fix kernel doc for uart_console_write()
serial: 8250_early: Remove early_device variable
serial: earlycon: Move ->uartclk initialize
serial: 8250_early: Assume uart already initialized if no baud option
serial: 8250_early: Fix setup() error code
serial: earlycon: Ignore parse_options() error code
serial: earlycon: Allow earlycon params with name only
serial: earlycon: Refactor earlycon registration
serial: earlycon: Enable earlycon without command line param
serial: 8250_early: Remove setup_early_serial8250_console()

arch/mips/mti-malta/malta-init.c | 4 +-
drivers/firmware/pcdp.c | 4 +-
drivers/tty/serial/8250/8250_core.c | 64 ++++++++++++----
drivers/tty/serial/8250/8250_early.c | 51 +++----------
drivers/tty/serial/earlycon.c | 140 ++++++++++++++++++++++++-----------
drivers/tty/serial/serial_core.c | 48 +++++++++++-
include/asm-generic/vmlinux.lds.h | 9 +++
include/linux/console.h | 3 +-
include/linux/serial_8250.h | 3 -
include/linux/serial_core.h | 21 ++++--
kernel/printk/printk.c | 51 +++++--------
11 files changed, 245 insertions(+), 153 deletions(-)

--
2.3.0


2015-02-24 16:37:31

by Peter Hurley

[permalink] [raw]
Subject: [PATCH -next 01/13] serial: earlycon: Refactor parse_options into serial core

Prepare to support console-defined matching; refactor the command
line parameter string processing from parse_options() into a
new core function, uart_parse_earlycon(), which decodes command line
parameters of the form:
earlycon=<name>,io|mmio|mmio32,<addr>,<options>
console=<name>,io|mmio|mmio32,<addr>,<options>
earlycon=<name>,0x<addr>,<options>
console=<name>,0x<addr>,<options>

Signed-off-by: Peter Hurley <[email protected]>
---
drivers/tty/serial/earlycon.c | 39 ++++++++++++----------------------
drivers/tty/serial/serial_core.c | 46 ++++++++++++++++++++++++++++++++++++++++
include/linux/serial_core.h | 2 ++
3 files changed, 61 insertions(+), 26 deletions(-)

diff --git a/drivers/tty/serial/earlycon.c b/drivers/tty/serial/earlycon.c
index 64fe25a..58d6bcd 100644
--- a/drivers/tty/serial/earlycon.c
+++ b/drivers/tty/serial/earlycon.c
@@ -54,44 +54,31 @@ static void __iomem * __init earlycon_map(unsigned long paddr, size_t size)
return base;
}

-static int __init parse_options(struct earlycon_device *device,
- char *options)
+static int __init parse_options(struct earlycon_device *device, char *options)
{
struct uart_port *port = &device->port;
- int mmio, mmio32, length;
+ int length;
unsigned long addr;

- if (!options)
- return -ENODEV;
+ if (uart_parse_earlycon(options, &port->iotype, &addr, &options))
+ return -EINVAL;

- mmio = !strncmp(options, "mmio,", 5);
- mmio32 = !strncmp(options, "mmio32,", 7);
- if (mmio || mmio32) {
- port->iotype = (mmio ? UPIO_MEM : UPIO_MEM32);
- options += mmio ? 5 : 7;
- addr = simple_strtoul(options, NULL, 0);
+ switch (port->iotype) {
+ case UPIO_MEM32:
+ port->regshift = 2; /* fall-through */
+ case UPIO_MEM:
port->mapbase = addr;
- if (mmio32)
- port->regshift = 2;
- } else if (!strncmp(options, "io,", 3)) {
- port->iotype = UPIO_PORT;
- options += 3;
- addr = simple_strtoul(options, NULL, 0);
+ break;
+ case UPIO_PORT:
port->iobase = addr;
- mmio = 0;
- } else if (!strncmp(options, "0x", 2)) {
- port->iotype = UPIO_MEM;
- addr = simple_strtoul(options, NULL, 0);
- port->mapbase = addr;
- } else {
+ break;
+ default:
return -EINVAL;
}

port->uartclk = BASE_BAUD * 16;

- options = strchr(options, ',');
if (options) {
- options++;
device->baud = simple_strtoul(options, NULL, 0);
length = min(strcspn(options, " ") + 1,
(size_t)(sizeof(device->options)));
@@ -100,7 +87,7 @@ static int __init parse_options(struct earlycon_device *device,

if (port->iotype == UPIO_MEM || port->iotype == UPIO_MEM32)
pr_info("Early serial console at MMIO%s 0x%llx (options '%s')\n",
- mmio32 ? "32" : "",
+ (port->iotype == UPIO_MEM32) ? "32" : "",
(unsigned long long)port->mapbase,
device->options);
else
diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c
index 6a1055a..3f823c26 100644
--- a/drivers/tty/serial/serial_core.c
+++ b/drivers/tty/serial/serial_core.c
@@ -1810,6 +1810,52 @@ uart_get_console(struct uart_port *ports, int nr, struct console *co)
}

/**
+ * uart_parse_earlycon - Parse earlycon options
+ * @p: ptr to 2nd field (ie., just beyond '<name>,')
+ * @iotype: ptr for decoded iotype (out)
+ * @addr: ptr for decoded mapbase/iobase (out)
+ * @options: ptr for <options> field; NULL if not present (out)
+ *
+ * Decodes earlycon kernel command line parameters of the form
+ * earlycon=<name>,io|mmio|mmio32,<addr>,<options>
+ * console=<name>,io|mmio|mmio32,<addr>,<options>
+ *
+ * The optional form
+ * earlycon=<name>,0x<addr>,<options>
+ * console=<name>,0x<addr>,<options>
+ * is also accepted; the returned @iotype will be UPIO_MEM.
+ *
+ * Returns 0 on success or -EINVAL on failure
+ */
+int uart_parse_earlycon(char *p, unsigned char *iotype, unsigned long *addr,
+ char **options)
+{
+ if (strncmp(p, "mmio,", 5) == 0) {
+ *iotype = UPIO_MEM;
+ p += 5;
+ } else if (strncmp(p, "mmio32,", 7) == 0) {
+ *iotype = UPIO_MEM32;
+ p += 7;
+ } else if (strncmp(p, "io,", 3) == 0) {
+ *iotype = UPIO_PORT;
+ p += 3;
+ } else if (strncmp(p, "0x", 2) == 0) {
+ *iotype = UPIO_MEM;
+ } else {
+ return -EINVAL;
+ }
+
+ *addr = simple_strtoul(p, NULL, 0);
+ p = strchr(p, ',');
+ if (p)
+ p++;
+
+ *options = p;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(uart_parse_earlycon);
+
+/**
* uart_parse_options - Parse serial port baud/parity/bits/flow control.
* @options: pointer to option string
* @baud: pointer to an 'int' variable for the baud rate.
diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h
index baf3e1d..cc5c506 100644
--- a/include/linux/serial_core.h
+++ b/include/linux/serial_core.h
@@ -354,6 +354,8 @@ early_param("earlycon", name ## _setup_earlycon);

struct uart_port *uart_get_console(struct uart_port *ports, int nr,
struct console *c);
+int uart_parse_earlycon(char *p, unsigned char *iotype, unsigned long *addr,
+ char **options);
void uart_parse_options(char *options, int *baud, int *parity, int *bits,
int *flow);
int uart_set_options(struct uart_port *port, struct console *co, int baud,
--
2.3.0

2015-02-24 16:37:34

by Peter Hurley

[permalink] [raw]
Subject: [PATCH -next 02/13] console: Preserve index after console setup()

Before register_console() calls the setup() method of the matched
console, the registering console index is already equal to the index
from the console command line; ie. newcon->index == c->index.

This change is also required to support extensible console matching;
(the command line index may have no relation to the console index
assigned by the console-defined match() function).

Signed-off-by: Peter Hurley <[email protected]>
---
kernel/printk/printk.c | 1 -
1 file changed, 1 deletion(-)

diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
index 02d6b6d..ce5c297 100644
--- a/kernel/printk/printk.c
+++ b/kernel/printk/printk.c
@@ -2479,7 +2479,6 @@ void register_console(struct console *newcon)
newcon->setup(newcon, console_cmdline[i].options) != 0)
break;
newcon->flags |= CON_ENABLED;
- newcon->index = c->index;
if (i == selected_console) {
newcon->flags |= CON_CONSDEV;
preferred_console = selected_console;
--
2.3.0

2015-02-24 16:40:56

by Peter Hurley

[permalink] [raw]
Subject: [PATCH -next 03/13] console: Add extensible console matching

Add match() method to struct console which allows the console to
perform console command line matching instead of (or in addition to)
default console matching (ie., by fixed name and index).

The match() method returns 0 to indicate a successful match; normal
console matching occurs if no match() method is defined or the
match() method returns non-zero. The match() method is expected to set
the console index if required.

Re-implement earlycon-to-console-handoff with direct matching of
"console=uart|uart8250,..." to the 8250 ttyS console.

Signed-off-by: Peter Hurley <[email protected]>
---
drivers/tty/serial/8250/8250_core.c | 64 +++++++++++++++++++++++++++---------
drivers/tty/serial/8250/8250_early.c | 23 -------------
include/linux/console.h | 3 +-
include/linux/serial_8250.h | 2 --
kernel/printk/printk.c | 50 ++++++++++------------------
5 files changed, 66 insertions(+), 76 deletions(-)

diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c
index e3b9570a..5eb95fd 100644
--- a/drivers/tty/serial/8250/8250_core.c
+++ b/drivers/tty/serial/8250/8250_core.c
@@ -3337,9 +3337,54 @@ static int serial8250_console_setup(struct console *co, char *options)
return uart_set_options(port, co, baud, parity, bits, flow);
}

-static int serial8250_console_early_setup(void)
+/**
+ * serial8250_console_match - non-standard console matching
+ * @co: registering console
+ * @name: name from console command line
+ * @idx: index from console command line
+ * @options: ptr to option string from console command line
+ *
+ * Only attempts to match console command lines of the form:
+ * console=uart<>,io|mmio|mmio32,<addr>,<options>
+ * console=uart<>,<addr>,options
+ * This form is used to register an initial earlycon boot console and
+ * replace it with the serial8250_console at 8250 driver init.
+ *
+ * Performs console setup for a match (as required by interface)
+ *
+ * Returns 0 if console matches; otherwise non-zero to use default matching
+ */
+static int serial8250_console_match(struct console *co, char *name, int idx,
+ char *options)
{
- return serial8250_find_port_for_earlycon();
+ char match[] = "uart"; /* 8250-specific earlycon name */
+ unsigned char iotype;
+ unsigned long addr;
+ int i;
+
+ if (strncmp(name, match, 4) != 0)
+ return -ENODEV;
+
+ if (uart_parse_earlycon(options, &iotype, &addr, &options))
+ return -ENODEV;
+
+ /* try to match the port specified on the command line */
+ for (i = 0; i < nr_uarts; i++) {
+ struct uart_port *port = &serial8250_ports[i].port;
+
+ if (port->iotype != iotype)
+ continue;
+ if ((iotype == UPIO_MEM || iotype == UPIO_MEM32) &&
+ (port->mapbase != addr))
+ continue;
+ if (iotype == UPIO_PORT && port->iobase != addr)
+ continue;
+
+ co->index = i;
+ return serial8250_console_setup(co, options);
+ }
+
+ return -ENODEV;
}

static struct console serial8250_console = {
@@ -3347,7 +3392,7 @@ static struct console serial8250_console = {
.write = serial8250_console_write,
.device = uart_console_device,
.setup = serial8250_console_setup,
- .early_setup = serial8250_console_early_setup,
+ .match = serial8250_console_match,
.flags = CON_PRINTBUFFER | CON_ANYTIME,
.index = -1,
.data = &serial8250_reg,
@@ -3361,19 +3406,6 @@ static int __init serial8250_console_init(void)
}
console_initcall(serial8250_console_init);

-int serial8250_find_port(struct uart_port *p)
-{
- int line;
- struct uart_port *port;
-
- for (line = 0; line < nr_uarts; line++) {
- port = &serial8250_ports[line].port;
- if (uart_match_port(p, port))
- return line;
- }
- return -ENODEV;
-}
-
#define SERIAL8250_CONSOLE &serial8250_console
#else
#define SERIAL8250_CONSOLE NULL
diff --git a/drivers/tty/serial/8250/8250_early.c b/drivers/tty/serial/8250/8250_early.c
index c31a22b..49bca65 100644
--- a/drivers/tty/serial/8250/8250_early.c
+++ b/drivers/tty/serial/8250/8250_early.c
@@ -173,26 +173,3 @@ int __init setup_early_serial8250_console(char *cmdline)

return setup_earlycon(cmdline, match, early_serial8250_setup);
}
-
-int serial8250_find_port_for_earlycon(void)
-{
- struct earlycon_device *device = early_device;
- struct uart_port *port = device ? &device->port : NULL;
- int line;
- int ret;
-
- if (!port || (!port->membase && !port->iobase))
- return -ENODEV;
-
- line = serial8250_find_port(port);
- if (line < 0)
- return -ENODEV;
-
- ret = update_console_cmdline("uart", 8250,
- "ttyS", line, device->options);
- if (ret < 0)
- ret = update_console_cmdline("uart", 0,
- "ttyS", line, device->options);
-
- return ret;
-}
diff --git a/include/linux/console.h b/include/linux/console.h
index 7571a16..9f50fb4 100644
--- a/include/linux/console.h
+++ b/include/linux/console.h
@@ -123,7 +123,7 @@ struct console {
struct tty_driver *(*device)(struct console *, int *);
void (*unblank)(void);
int (*setup)(struct console *, char *);
- int (*early_setup)(void);
+ int (*match)(struct console *, char *name, int idx, char *options);
short flags;
short index;
int cflag;
@@ -141,7 +141,6 @@ extern int console_set_on_cmdline;
extern struct console *early_console;

extern int add_preferred_console(char *name, int idx, char *options);
-extern int update_console_cmdline(char *name, int idx, char *name_new, int idx_new, char *options);
extern void register_console(struct console *);
extern int unregister_console(struct console *);
extern struct console *console_drivers;
diff --git a/include/linux/serial_8250.h b/include/linux/serial_8250.h
index a8efa23..f26ae7f 100644
--- a/include/linux/serial_8250.h
+++ b/include/linux/serial_8250.h
@@ -118,8 +118,6 @@ void serial8250_resume_port(int line);

extern int early_serial_setup(struct uart_port *port);

-extern int serial8250_find_port(struct uart_port *p);
-extern int serial8250_find_port_for_earlycon(void);
extern unsigned int serial8250_early_in(struct uart_port *port, int offset);
extern void serial8250_early_out(struct uart_port *port, int offset, int value);
extern int setup_early_serial8250_console(char *cmdline);
diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
index ce5c297..040be39 100644
--- a/kernel/printk/printk.c
+++ b/kernel/printk/printk.c
@@ -2017,24 +2017,6 @@ int add_preferred_console(char *name, int idx, char *options)
return __add_preferred_console(name, idx, options, NULL);
}

-int update_console_cmdline(char *name, int idx, char *name_new, int idx_new, char *options)
-{
- struct console_cmdline *c;
- int i;
-
- for (i = 0, c = console_cmdline;
- i < MAX_CMDLINECONSOLES && c->name[0];
- i++, c++)
- if (strcmp(c->name, name) == 0 && c->index == idx) {
- strlcpy(c->name, name_new, sizeof(c->name));
- c->options = options;
- c->index = idx_new;
- return i;
- }
- /* not found */
- return -1;
-}
-
bool console_suspend_enabled = true;
EXPORT_SYMBOL(console_suspend_enabled);

@@ -2436,9 +2418,6 @@ void register_console(struct console *newcon)
if (preferred_console < 0 || bcon || !console_drivers)
preferred_console = selected_console;

- if (newcon->early_setup)
- newcon->early_setup();
-
/*
* See if we want to use this console driver. If we
* didn't select a console we take the first one
@@ -2464,20 +2443,25 @@ void register_console(struct console *newcon)
for (i = 0, c = console_cmdline;
i < MAX_CMDLINECONSOLES && c->name[0];
i++, c++) {
- if (strcmp(c->name, newcon->name) != 0)
- continue;
- if (newcon->index >= 0 &&
- newcon->index != c->index)
- continue;
- if (newcon->index < 0)
- newcon->index = c->index;
+ if (!newcon->match ||
+ newcon->match(newcon, c->name, c->index, c->options) != 0) {
+ /* default matching */
+ if (strcmp(c->name, newcon->name) != 0)
+ continue;
+ if (newcon->index >= 0 &&
+ newcon->index != c->index)
+ continue;
+ if (newcon->index < 0)
+ newcon->index = c->index;

- if (_braille_register_console(newcon, c))
- return;
+ if (_braille_register_console(newcon, c))
+ return;
+
+ if (newcon->setup &&
+ newcon->setup(newcon, c->options) != 0)
+ break;
+ }

- if (newcon->setup &&
- newcon->setup(newcon, console_cmdline[i].options) != 0)
- break;
newcon->flags |= CON_ENABLED;
if (i == selected_console) {
newcon->flags |= CON_CONSDEV;
--
2.3.0

2015-02-24 16:37:37

by Peter Hurley

[permalink] [raw]
Subject: [PATCH -next 04/13] serial: core: Fix kernel doc for uart_console_write()

'/**' is required to start a kernel-doc comment block.

Signed-off-by: Peter Hurley <[email protected]>
---
drivers/tty/serial/serial_core.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c
index 3f823c26..373cfde 100644
--- a/drivers/tty/serial/serial_core.c
+++ b/drivers/tty/serial/serial_core.c
@@ -1766,7 +1766,7 @@ static const struct file_operations uart_proc_fops = {
#endif

#if defined(CONFIG_SERIAL_CORE_CONSOLE) || defined(CONFIG_CONSOLE_POLL)
-/*
+/**
* uart_console_write - write a console message to a serial port
* @port: the port to write the message
* @s: array of characters
--
2.3.0

2015-02-24 16:37:40

by Peter Hurley

[permalink] [raw]
Subject: [PATCH -next 05/13] serial: 8250_early: Remove early_device variable

early_device was only required for serial8250_find_port_for_earlycon(),
which was replaced by extensible console matching.

Fixup early_serial8250_write() to get the earlycon_device * from
console->data (which is initialized by {of_}setup_earlycon()).

Signed-off-by: Peter Hurley <[email protected]>
---
drivers/tty/serial/8250/8250_early.c | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/drivers/tty/serial/8250/8250_early.c b/drivers/tty/serial/8250/8250_early.c
index 49bca65..d7b831b 100644
--- a/drivers/tty/serial/8250/8250_early.c
+++ b/drivers/tty/serial/8250/8250_early.c
@@ -36,8 +36,6 @@
#include <asm/io.h>
#include <asm/serial.h>

-static struct earlycon_device *early_device;
-
unsigned int __weak __init serial8250_early_in(struct uart_port *port, int offset)
{
switch (port->iotype) {
@@ -90,7 +88,8 @@ static void __init serial_putc(struct uart_port *port, int c)
static void __init early_serial8250_write(struct console *console,
const char *s, unsigned int count)
{
- struct uart_port *port = &early_device->port;
+ struct earlycon_device *device = console->data;
+ struct uart_port *port = &device->port;
unsigned int ier;

/* Save the IER and disable interrupts preserving the UUE bit */
@@ -157,7 +156,6 @@ static int __init early_serial8250_setup(struct earlycon_device *device,

init_port(device);

- early_device = device;
device->con->write = early_serial8250_write;
return 0;
}
--
2.3.0

2015-02-24 16:40:00

by Peter Hurley

[permalink] [raw]
Subject: [PATCH -next 06/13] serial: earlycon: Move ->uartclk initialize

Initializing the ->uartclk field is not related to option parsing;
relocate from parse_options() to setup_earlycon() (which mirrors the
behavior of of_setup_earlycon()).

Signed-off-by: Peter Hurley <[email protected]>
---
drivers/tty/serial/earlycon.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/drivers/tty/serial/earlycon.c b/drivers/tty/serial/earlycon.c
index 58d6bcd..0480c8f 100644
--- a/drivers/tty/serial/earlycon.c
+++ b/drivers/tty/serial/earlycon.c
@@ -76,8 +76,6 @@ static int __init parse_options(struct earlycon_device *device, char *options)
return -EINVAL;
}

- port->uartclk = BASE_BAUD * 16;
-
if (options) {
device->baud = simple_strtoul(options, NULL, 0);
length = min(strcspn(options, " ") + 1,
@@ -121,6 +119,7 @@ int __init setup_earlycon(char *buf, const char *match,
if (!err)
buf = NULL;

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

--
2.3.0

2015-02-24 16:37:45

by Peter Hurley

[permalink] [raw]
Subject: [PATCH -next 07/13] serial: 8250_early: Assume uart already initialized if no baud option

The <baud><parity><bit> option string is not supplied if the earlycon
is started via devicetree and OF_EARLYCON_DECLARE(). The option string
is also not required if started via kernel command line parameters of
the form:
earlycon=uart,mmio,<addr>
console=uart,mmio,<addr>

If earlycon_device->baud is 0, then an option string was not supplied.
In this case, assume the uart has already been initialized by the
bootloader or firmware.

Signed-off-by: Peter Hurley <[email protected]>
---
drivers/tty/serial/8250/8250_early.c | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/drivers/tty/serial/8250/8250_early.c b/drivers/tty/serial/8250/8250_early.c
index d7b831b..1701d00 100644
--- a/drivers/tty/serial/8250/8250_early.c
+++ b/drivers/tty/serial/8250/8250_early.c
@@ -149,12 +149,18 @@ static int __init early_serial8250_setup(struct earlycon_device *device,
return 0;

if (!device->baud) {
+ struct uart_port *port = &device->port;
+ unsigned int ier;
+
device->baud = probe_baud(&device->port);
snprintf(device->options, sizeof(device->options), "%u",
device->baud);
- }

- init_port(device);
+ /* assume the device was initialized, only mask interrupts */
+ ier = serial8250_early_in(port, UART_IER);
+ serial8250_early_out(port, UART_IER, ier & UART_IER_UUE);
+ } else
+ init_port(device);

device->con->write = early_serial8250_write;
return 0;
--
2.3.0

2015-02-24 16:37:47

by Peter Hurley

[permalink] [raw]
Subject: [PATCH -next 08/13] serial: 8250_early: Fix setup() error code

If parsing failed to decode a valid uart addr, return -ENODEV instead
of success. Although setup_earlycon() will detect the failure anyway
(because the write() method has not been set), that behavior is not
obvious and should not be relied on.

Signed-off-by: Peter Hurley <[email protected]>
---
drivers/tty/serial/8250/8250_early.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/tty/serial/8250/8250_early.c b/drivers/tty/serial/8250/8250_early.c
index 1701d00..b199c10 100644
--- a/drivers/tty/serial/8250/8250_early.c
+++ b/drivers/tty/serial/8250/8250_early.c
@@ -146,7 +146,7 @@ static int __init early_serial8250_setup(struct earlycon_device *device,
const char *options)
{
if (!(device->port.membase || device->port.iobase))
- return 0;
+ return -ENODEV;

if (!device->baud) {
struct uart_port *port = &device->port;
--
2.3.0

2015-02-24 16:37:49

by Peter Hurley

[permalink] [raw]
Subject: [PATCH -next 09/13] serial: earlycon: Ignore parse_options() error code

Because setup_earlycon() continues to attempt console registration
if an error occurred parsing the option string, the actual value of
the error code from parse_options() is ignored.

Signed-off-by: Peter Hurley <[email protected]>
---
drivers/tty/serial/earlycon.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/drivers/tty/serial/earlycon.c b/drivers/tty/serial/earlycon.c
index 0480c8f..da5e8c8 100644
--- a/drivers/tty/serial/earlycon.c
+++ b/drivers/tty/serial/earlycon.c
@@ -114,9 +114,8 @@ int __init setup_earlycon(char *buf, const char *match,

buf += len + 1;

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

port->uartclk = BASE_BAUD * 16;
--
2.3.0

2015-02-24 16:39:16

by Peter Hurley

[permalink] [raw]
Subject: [PATCH -next 10/13] serial: earlycon: Allow earlycon params with name only

Allow earlycon param strings of the form
earlycon=<name>

Signed-off-by: Peter Hurley <[email protected]>
---
drivers/tty/serial/earlycon.c | 11 +++++++----
1 file changed, 7 insertions(+), 4 deletions(-)

diff --git a/drivers/tty/serial/earlycon.c b/drivers/tty/serial/earlycon.c
index da5e8c8..025ea01 100644
--- a/drivers/tty/serial/earlycon.c
+++ b/drivers/tty/serial/earlycon.c
@@ -109,13 +109,16 @@ int __init setup_earlycon(char *buf, const char *match,
len = strlen(match);
if (strncmp(buf, match, len))
return 0;
- if (buf[len] && (buf[len] != ','))
- return 0;

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

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

port->uartclk = BASE_BAUD * 16;
--
2.3.0

2015-02-24 16:38:37

by Peter Hurley

[permalink] [raw]
Subject: [PATCH -next 11/13] serial: earlycon: Refactor earlycon registration

Separate earlycon matching from registration; add register_earlycon
which initializes and registers the matched earlycon.

Signed-off-by: Peter Hurley <[email protected]>
---
drivers/tty/serial/earlycon.c | 42 +++++++++++++++++++++++++-----------------
1 file changed, 25 insertions(+), 17 deletions(-)

diff --git a/drivers/tty/serial/earlycon.c b/drivers/tty/serial/earlycon.c
index 025ea01..9fb76b6 100644
--- a/drivers/tty/serial/earlycon.c
+++ b/drivers/tty/serial/earlycon.c
@@ -96,27 +96,13 @@ static int __init parse_options(struct earlycon_device *device, char *options)
return 0;
}

-int __init setup_earlycon(char *buf, const char *match,
- int (*setup)(struct earlycon_device *, const char *))
+
+static int __init
+register_earlycon(char *buf, int (*setup)(struct earlycon_device *, const char *))
{
int err;
- size_t len;
struct uart_port *port = &early_console_dev.port;

- if (!buf || !match || !setup)
- return 0;
-
- len = strlen(match);
- if (strncmp(buf, match, len))
- return 0;
-
- if (buf[len]) {
- if (buf[len] != ',')
- return 0;
- buf += len + 1;
- } else
- buf = NULL;
-
/* On parsing error, pass the options buf to the setup function */
if (buf && !parse_options(&early_console_dev, buf))
buf = NULL;
@@ -136,6 +122,28 @@ int __init setup_earlycon(char *buf, const char *match,
return 0;
}

+int __init setup_earlycon(char *buf, const char *match,
+ int (*setup)(struct earlycon_device *, const char *))
+{
+ size_t len;
+
+ if (!buf || !match || !setup)
+ return 0;
+
+ len = strlen(match);
+ if (strncmp(buf, match, len))
+ return 0;
+
+ if (buf[len]) {
+ if (buf[len] != ',')
+ return 0;
+ buf += len + 1;
+ } else
+ buf = NULL;
+
+ return register_earlycon(buf, setup);
+}
+
int __init of_setup_earlycon(unsigned long addr,
int (*setup)(struct earlycon_device *, const char *))
{
--
2.3.0

2015-02-24 16:37:52

by Peter Hurley

[permalink] [raw]
Subject: [PATCH -next 12/13] 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.

Signed-off-by: Peter Hurley <[email protected]>
---
drivers/tty/serial/8250/8250_early.c | 7 +--
drivers/tty/serial/earlycon.c | 92 ++++++++++++++++++++++++++++--------
include/asm-generic/vmlinux.lds.h | 9 ++++
include/linux/serial_core.h | 19 ++++----
4 files changed, 94 insertions(+), 33 deletions(-)

diff --git a/drivers/tty/serial/8250/8250_early.c b/drivers/tty/serial/8250/8250_early.c
index b199c10..d272139 100644
--- a/drivers/tty/serial/8250/8250_early.c
+++ b/drivers/tty/serial/8250/8250_early.c
@@ -170,10 +170,5 @@ EARLYCON_DECLARE(uart, early_serial8250_setup);

int __init setup_early_serial8250_console(char *cmdline)
{
- char match[] = "uart8250";
-
- if (cmdline && cmdline[4] == ',')
- match[4] = '\0';
-
- return setup_earlycon(cmdline, match, early_serial8250_setup);
+ return setup_earlycon(cmdline);
}
diff --git a/drivers/tty/serial/earlycon.c b/drivers/tty/serial/earlycon.c
index 9fb76b6..5fdc9f3 100644
--- a/drivers/tty/serial/earlycon.c
+++ b/drivers/tty/serial/earlycon.c
@@ -10,6 +10,9 @@
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/console.h>
#include <linux/kernel.h>
#include <linux/init.h>
@@ -34,6 +37,10 @@ static struct earlycon_device early_console_dev = {
.con = &early_con,
};

+extern struct earlycon_id __earlycon_table[];
+static const struct earlycon_id __earlycon_table_sentinel
+ __used __section(__earlycon_table_end);
+
static const struct of_device_id __earlycon_of_table_sentinel
__used __section(__earlycon_of_table_end);

@@ -96,9 +103,7 @@ static int __init parse_options(struct earlycon_device *device, char *options)
return 0;
}

-
-static int __init
-register_earlycon(char *buf, int (*setup)(struct earlycon_device *, const char *))
+static int __init register_earlycon(char *buf, const struct earlycon_id *match)
{
int err;
struct uart_port *port = &early_console_dev.port;
@@ -112,7 +117,7 @@ register_earlycon(char *buf, int (*setup)(struct earlycon_device *, const char *
port->membase = earlycon_map(port->mapbase, 64);

early_console_dev.con->data = &early_console_dev;
- err = setup(&early_console_dev, buf);
+ err = match->setup(&early_console_dev, buf);
if (err < 0)
return err;
if (!early_console_dev.con->write)
@@ -122,27 +127,76 @@ register_earlycon(char *buf, int (*setup)(struct earlycon_device *, const char *
return 0;
}

-int __init setup_earlycon(char *buf, const char *match,
- int (*setup)(struct earlycon_device *, const char *))
+/**
+ * setup_earlycon - match and register earlycon console
+ * @buf: earlycon param string
+ *
+ * Registers the earlycon console matching the earlycon specified
+ * in the param string @buf. Acceptable param strings are of the form
+ * <name>,io|mmio|mmio32,<addr>,<options>
+ * <name>,0x<addr>,<options>
+ * <name>,<options>
+ * <name>
+ *
+ * Only for the third form does the earlycon setup() method receive the
+ * <options> string in the 'options' parameter; all other forms set
+ * the parameter to NULL.
+ *
+ * Returns 0 if an attempt to register the earlycon was made,
+ * otherwise negative error code
+ */
+int __init setup_earlycon(char *buf)
{
- size_t len;
+ const struct earlycon_id *match;

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

- len = strlen(match);
- if (strncmp(buf, match, len))
- return 0;
+ if (early_con.flags & CON_ENABLED)
+ return -EALREADY;

- if (buf[len]) {
- if (buf[len] != ',')
- return 0;
- buf += len + 1;
- } else
- buf = NULL;
+ for (match = __earlycon_table; match->name[0]; match++) {
+ size_t len = strlen(match->name);

- return register_earlycon(buf, setup);
+ 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;
+}
+
+/* early_param wrapper for setup_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])
+ return 0;
+
+ err = setup_earlycon(buf);
+ if (err == -ENOENT) {
+ pr_warn("no match for %s\n", buf);
+ err = 0;
+ } else if (err == -EALREADY) {
+ pr_warn("already registered\n");
+ err = 0;
+ }
+ return err;
}
+early_param("earlycon", param_setup_earlycon);

int __init of_setup_earlycon(unsigned long addr,
int (*setup)(struct earlycon_device *, const char *))
diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
index bee5d68..006c1f7 100644
--- a/include/asm-generic/vmlinux.lds.h
+++ b/include/asm-generic/vmlinux.lds.h
@@ -150,6 +150,14 @@
#define TRACE_SYSCALLS()
#endif

+#ifdef CONFIG_SERIAL_EARLYCON
+#define EARLYCON_TABLE() . = ALIGN(8); \
+ VMLINUX_SYMBOL(__earlycon_table) = .; \
+ *(__earlycon_table) \
+ *(__earlycon_table_end)
+#else
+#define EARLYCON_TABLE()
+#endif

#define ___OF_TABLE(cfg, name) _OF_TABLE_##cfg(name)
#define __OF_TABLE(cfg, name) ___OF_TABLE(cfg, name)
@@ -502,6 +510,7 @@
CPU_METHOD_OF_TABLES() \
KERNEL_DTB() \
IRQCHIP_OF_MATCH_TABLE() \
+ EARLYCON_TABLE() \
EARLYCON_OF_TABLES()

#define INIT_TEXT \
diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h
index cc5c506..a3fd182 100644
--- a/include/linux/serial_core.h
+++ b/include/linux/serial_core.h
@@ -336,18 +336,21 @@ struct earlycon_device {
char options[16]; /* e.g., 115200n8 */
unsigned int baud;
};
-int setup_earlycon(char *buf, const char *match,
- int (*setup)(struct earlycon_device *, const char *));

+struct earlycon_id {
+ char name[16];
+ int (*setup)(struct earlycon_device *, const char *options);
+};
+
+extern int setup_earlycon(char *buf);
extern int of_setup_earlycon(unsigned long addr,
int (*setup)(struct earlycon_device *, const char *));

-#define EARLYCON_DECLARE(name, func) \
-static int __init name ## _setup_earlycon(char *buf) \
-{ \
- return setup_earlycon(buf, __stringify(name), func); \
-} \
-early_param("earlycon", name ## _setup_earlycon);
+#define EARLYCON_DECLARE(_name, func) \
+ static const struct earlycon_id __earlycon_##_name \
+ __used __section(__earlycon_table) \
+ = { .name = __stringify(_name), \
+ .setup = func }

#define OF_EARLYCON_DECLARE(name, compat, fn) \
_OF_DECLARE(earlycon, name, compat, fn, void *)
--
2.3.0

2015-02-24 16:37:57

by Peter Hurley

[permalink] [raw]
Subject: [PATCH -next 13/13] serial: 8250_early: Remove setup_early_serial8250_console()

setup_earlycon() will now match and register the desired earlycon
from the param string (as if 'earlycon=...' had been set on the
command line). Use setup_earlycon() from existing arch call sites
which start an earlycon directly.

Cc: Khalid Aziz <[email protected]>
Cc: Tony Luck <[email protected]>
Cc: Fenghua Yu <[email protected]>
Cc: Ralf Baechle <[email protected]>
Signed-off-by: Peter Hurley <[email protected]>
---
arch/mips/mti-malta/malta-init.c | 4 ++--
drivers/firmware/pcdp.c | 4 ++--
drivers/tty/serial/8250/8250_early.c | 5 -----
include/linux/serial_8250.h | 1 -
4 files changed, 4 insertions(+), 10 deletions(-)

diff --git a/arch/mips/mti-malta/malta-init.c b/arch/mips/mti-malta/malta-init.c
index 6849f53..cec3e18 100644
--- a/arch/mips/mti-malta/malta-init.c
+++ b/arch/mips/mti-malta/malta-init.c
@@ -14,7 +14,7 @@
#include <linux/init.h>
#include <linux/string.h>
#include <linux/kernel.h>
-#include <linux/serial_8250.h>
+#include <linux/serial_core.h>

#include <asm/cacheflush.h>
#include <asm/smp-ops.h>
@@ -75,7 +75,7 @@ static void __init console_config(void)
if ((strstr(fw_getcmdline(), "earlycon=")) == NULL) {
sprintf(console_string, "uart8250,io,0x3f8,%d%c%c", baud,
parity, bits);
- setup_early_serial8250_console(console_string);
+ setup_earlycon(console_string);
}

if ((strstr(fw_getcmdline(), "console=")) == NULL) {
diff --git a/drivers/firmware/pcdp.c b/drivers/firmware/pcdp.c
index a330492..75273a25 100644
--- a/drivers/firmware/pcdp.c
+++ b/drivers/firmware/pcdp.c
@@ -15,7 +15,7 @@
#include <linux/console.h>
#include <linux/efi.h>
#include <linux/serial.h>
-#include <linux/serial_8250.h>
+#include <linux/serial_core.h>
#include <asm/vga.h>
#include "pcdp.h"

@@ -43,7 +43,7 @@ setup_serial_console(struct pcdp_uart *uart)
}

add_preferred_console("uart", 8250, &options[9]);
- return setup_early_serial8250_console(options);
+ return setup_earlycon(options);
#else
return -ENODEV;
#endif
diff --git a/drivers/tty/serial/8250/8250_early.c b/drivers/tty/serial/8250/8250_early.c
index d272139..a2b4e58 100644
--- a/drivers/tty/serial/8250/8250_early.c
+++ b/drivers/tty/serial/8250/8250_early.c
@@ -167,8 +167,3 @@ static int __init early_serial8250_setup(struct earlycon_device *device,
}
EARLYCON_DECLARE(uart8250, early_serial8250_setup);
EARLYCON_DECLARE(uart, early_serial8250_setup);
-
-int __init setup_early_serial8250_console(char *cmdline)
-{
- return setup_earlycon(cmdline);
-}
diff --git a/include/linux/serial_8250.h b/include/linux/serial_8250.h
index f26ae7f..ca9f87b 100644
--- a/include/linux/serial_8250.h
+++ b/include/linux/serial_8250.h
@@ -120,7 +120,6 @@ extern int early_serial_setup(struct uart_port *port);

extern unsigned int serial8250_early_in(struct uart_port *port, int offset);
extern void serial8250_early_out(struct uart_port *port, int offset, int value);
-extern int setup_early_serial8250_console(char *cmdline);
extern void serial8250_do_set_termios(struct uart_port *port,
struct ktermios *termios, struct ktermios *old);
extern int serial8250_do_startup(struct uart_port *port);
--
2.3.0

2015-02-24 19:27:42

by Rob Herring (Arm)

[permalink] [raw]
Subject: Re: [PATCH -next 00/13] Extensible console matching & direct earlycon

On Tue, Feb 24, 2015 at 10:36 AM, Peter Hurley <[email protected]> wrote:
> Hi Greg & Andrew,
>
> This patch series implements:
> 1. console-definable (aka extensible) matching
> 2. generic earlycon-to-console handoff via extensible matching
> 3. arch/prom support for direct earlycon
>
>
> Extensible console matching
>
> Extensible console matching enables the console itself to define the
> conditions for a "command line" match. This mimics the design of
> device matching in the driver model. Two important use-cases which this
> feature enables are generic earlycon-to-console handoff and support
> for driver migration.
>
> Earlycon-to-console handoff was implemented in 2007. Console command
> lines of the form:
> console=uart,io,0x3f8,115200n8
> start an earlycon and later allow the 8250 driver console to takeover.
> Unfortunately this implementation requires direct coupling between
> the earlycon and the console and is facilitated by ugly hacks like
> editing the console command line in-place.
>
> Extensible console matching allows the 8250 driver to directly match
> that console command line instead, and enables other serial drivers
> to trivially support console handoff themselves.
>
> In addition, extensible console matching allows a new driver to
> provide support for a different driver's console. This requirement
> stems from needing to minimize breakage when migrating serial drivers.
> Since many devices are based on the original 8250/16550 designs
> but sometimes have features incompatible with the existing 8250 driver
> support, the initial driver is often standalone. When/if the standalone
> driver is migrated to the 8250 driver, the problem of console names in
> the command line remains. Extensible console matching enables a simple
> migration path.
>
>
> Direct earlycon
>
> This feature enables arches and proms to start an earlycon directly,
> rather than requiring an "earlycon=" command line parameter.
> Devicetree can already do this via the 'linux,stdout-path' property,
> but arch and prom code requires direct coupling to the serial driver.
>
> This support is implemented by judicious refactoring and the same
> construct that devicetree and early_param use: a link table containing
> the necessary information (name and setup() function) to find and
> bind the appropriate earlycon "driver".

I've skimmed thru this and it looks like a great improvement.

One problem we have currently with DT stdout-path and earlycon is a
preferred console does not get registered, so the console will get
switched to tty0 and you lose your console. The problem is DT does not
know the console name to register a preferred console. It looks like
this series may help this problem, but I'm not sure and wanted your
thoughts.

Rob

2015-02-24 19:53:36

by Peter Hurley

[permalink] [raw]
Subject: Re: [PATCH -next 00/13] Extensible console matching & direct earlycon

Hi Rob,

On 02/24/2015 02:27 PM, Rob Herring wrote:
> On Tue, Feb 24, 2015 at 10:36 AM, Peter Hurley <[email protected]> wrote:
>> Hi Greg & Andrew,
>>
>> This patch series implements:
>> 1. console-definable (aka extensible) matching
>> 2. generic earlycon-to-console handoff via extensible matching
>> 3. arch/prom support for direct earlycon
>>
>>
>> Extensible console matching
>>
>> Extensible console matching enables the console itself to define the
>> conditions for a "command line" match. This mimics the design of
>> device matching in the driver model. Two important use-cases which this
>> feature enables are generic earlycon-to-console handoff and support
>> for driver migration.
>>
>> Earlycon-to-console handoff was implemented in 2007. Console command
>> lines of the form:
>> console=uart,io,0x3f8,115200n8
>> start an earlycon and later allow the 8250 driver console to takeover.
>> Unfortunately this implementation requires direct coupling between
>> the earlycon and the console and is facilitated by ugly hacks like
>> editing the console command line in-place.
>>
>> Extensible console matching allows the 8250 driver to directly match
>> that console command line instead, and enables other serial drivers
>> to trivially support console handoff themselves.
>>
>> In addition, extensible console matching allows a new driver to
>> provide support for a different driver's console. This requirement
>> stems from needing to minimize breakage when migrating serial drivers.
>> Since many devices are based on the original 8250/16550 designs
>> but sometimes have features incompatible with the existing 8250 driver
>> support, the initial driver is often standalone. When/if the standalone
>> driver is migrated to the 8250 driver, the problem of console names in
>> the command line remains. Extensible console matching enables a simple
>> migration path.
>>
>>
>> Direct earlycon
>>
>> This feature enables arches and proms to start an earlycon directly,
>> rather than requiring an "earlycon=" command line parameter.
>> Devicetree can already do this via the 'linux,stdout-path' property,
>> but arch and prom code requires direct coupling to the serial driver.
>>
>> This support is implemented by judicious refactoring and the same
>> construct that devicetree and early_param use: a link table containing
>> the necessary information (name and setup() function) to find and
>> bind the appropriate earlycon "driver".
>
> I've skimmed thru this and it looks like a great improvement.
>
> One problem we have currently with DT stdout-path and earlycon is a
> preferred console does not get registered, so the console will get
> switched to tty0 and you lose your console. The problem is DT does not
> know the console name to register a preferred console. It looks like
> this series may help this problem, but I'm not sure and wanted your
> thoughts.

I thought that of_alias_scan() + of_console_check() caused DT stdout-path
to add_preferred_console() the driver console @ port registration time
via uart_add_one_port() -> of_console_check().

Is that not how that works?

Regards,
Peter Hurley

2015-02-24 20:20:36

by Rob Herring (Arm)

[permalink] [raw]
Subject: Re: [PATCH -next 00/13] Extensible console matching & direct earlycon

On Tue, Feb 24, 2015 at 1:53 PM, Peter Hurley <[email protected]> wrote:
> Hi Rob,
>
> On 02/24/2015 02:27 PM, Rob Herring wrote:
>> On Tue, Feb 24, 2015 at 10:36 AM, Peter Hurley <[email protected]> wrote:
>>> Hi Greg & Andrew,
>>>
>>> This patch series implements:
>>> 1. console-definable (aka extensible) matching
>>> 2. generic earlycon-to-console handoff via extensible matching
>>> 3. arch/prom support for direct earlycon
>>>
>>>
>>> Extensible console matching
>>>
>>> Extensible console matching enables the console itself to define the
>>> conditions for a "command line" match. This mimics the design of
>>> device matching in the driver model. Two important use-cases which this
>>> feature enables are generic earlycon-to-console handoff and support
>>> for driver migration.
>>>
>>> Earlycon-to-console handoff was implemented in 2007. Console command
>>> lines of the form:
>>> console=uart,io,0x3f8,115200n8
>>> start an earlycon and later allow the 8250 driver console to takeover.
>>> Unfortunately this implementation requires direct coupling between
>>> the earlycon and the console and is facilitated by ugly hacks like
>>> editing the console command line in-place.
>>>
>>> Extensible console matching allows the 8250 driver to directly match
>>> that console command line instead, and enables other serial drivers
>>> to trivially support console handoff themselves.
>>>
>>> In addition, extensible console matching allows a new driver to
>>> provide support for a different driver's console. This requirement
>>> stems from needing to minimize breakage when migrating serial drivers.
>>> Since many devices are based on the original 8250/16550 designs
>>> but sometimes have features incompatible with the existing 8250 driver
>>> support, the initial driver is often standalone. When/if the standalone
>>> driver is migrated to the 8250 driver, the problem of console names in
>>> the command line remains. Extensible console matching enables a simple
>>> migration path.
>>>
>>>
>>> Direct earlycon
>>>
>>> This feature enables arches and proms to start an earlycon directly,
>>> rather than requiring an "earlycon=" command line parameter.
>>> Devicetree can already do this via the 'linux,stdout-path' property,
>>> but arch and prom code requires direct coupling to the serial driver.
>>>
>>> This support is implemented by judicious refactoring and the same
>>> construct that devicetree and early_param use: a link table containing
>>> the necessary information (name and setup() function) to find and
>>> bind the appropriate earlycon "driver".
>>
>> I've skimmed thru this and it looks like a great improvement.
>>
>> One problem we have currently with DT stdout-path and earlycon is a
>> preferred console does not get registered, so the console will get
>> switched to tty0 and you lose your console. The problem is DT does not
>> know the console name to register a preferred console. It looks like
>> this series may help this problem, but I'm not sure and wanted your
>> thoughts.
>
> I thought that of_alias_scan() + of_console_check() caused DT stdout-path
> to add_preferred_console() the driver console @ port registration time
> via uart_add_one_port() -> of_console_check().
>
> Is that not how that works?

Yes, I believe that is how it works with earlycon not enabled. This
doesn't work when earlycon is enabled with just "earlycon" on the
command line. The fix I have is here[1], but I don't like putting DT
specifics into the console code.

Rob

[1] https://lkml.org/lkml/2014/7/25/333

2015-02-25 13:03:09

by Peter Hurley

[permalink] [raw]
Subject: Re: [PATCH -next 01/13] serial: earlycon: Refactor parse_options into serial core

Hi Joe,

checkpatch warns on the line below:

On 02/24/2015 11:36 AM, Peter Hurley wrote:
> Prepare to support console-defined matching; refactor the command
> line parameter string processing from parse_options() into a
> new core function, uart_parse_earlycon(), which decodes command line
> parameters of the form:
> earlycon=<name>,io|mmio|mmio32,<addr>,<options>
> console=<name>,io|mmio|mmio32,<addr>,<options>
> earlycon=<name>,0x<addr>,<options>
> console=<name>,0x<addr>,<options>
>
> Signed-off-by: Peter Hurley <[email protected]>
> ---
> drivers/tty/serial/earlycon.c | 39 ++++++++++++----------------------
> drivers/tty/serial/serial_core.c | 46 ++++++++++++++++++++++++++++++++++++++++
> include/linux/serial_core.h | 2 ++
> 3 files changed, 61 insertions(+), 26 deletions(-)
>
> diff --git a/drivers/tty/serial/earlycon.c b/drivers/tty/serial/earlycon.c
> index 64fe25a..58d6bcd 100644
> --- a/drivers/tty/serial/earlycon.c
> +++ b/drivers/tty/serial/earlycon.c
> @@ -54,44 +54,31 @@ static void __iomem * __init earlycon_map(unsigned long paddr, size_t size)
> return base;
> }
>
> -static int __init parse_options(struct earlycon_device *device,
> - char *options)
> +static int __init parse_options(struct earlycon_device *device, char *options)
> {
> struct uart_port *port = &device->port;
> - int mmio, mmio32, length;
> + int length;
> unsigned long addr;
>
> - if (!options)
> - return -ENODEV;
> + if (uart_parse_earlycon(options, &port->iotype, &addr, &options))
> + return -EINVAL;
>
> - mmio = !strncmp(options, "mmio,", 5);
> - mmio32 = !strncmp(options, "mmio32,", 7);
> - if (mmio || mmio32) {
> - port->iotype = (mmio ? UPIO_MEM : UPIO_MEM32);
> - options += mmio ? 5 : 7;
> - addr = simple_strtoul(options, NULL, 0);
> + switch (port->iotype) {
> + case UPIO_MEM32:
> + port->regshift = 2; /* fall-through */
> + case UPIO_MEM:
> port->mapbase = addr;
> - if (mmio32)
> - port->regshift = 2;
> - } else if (!strncmp(options, "io,", 3)) {
> - port->iotype = UPIO_PORT;
> - options += 3;
> - addr = simple_strtoul(options, NULL, 0);
> + break;
> + case UPIO_PORT:
> port->iobase = addr;
> - mmio = 0;
> - } else if (!strncmp(options, "0x", 2)) {
> - port->iotype = UPIO_MEM;
> - addr = simple_strtoul(options, NULL, 0);
> - port->mapbase = addr;
> - } else {
> + break;
> + default:
> return -EINVAL;
> }
>
> port->uartclk = BASE_BAUD * 16;
>
> - options = strchr(options, ',');
> if (options) {
> - options++;
> device->baud = simple_strtoul(options, NULL, 0);
> length = min(strcspn(options, " ") + 1,
> (size_t)(sizeof(device->options)));
> @@ -100,7 +87,7 @@ static int __init parse_options(struct earlycon_device *device,
>
> if (port->iotype == UPIO_MEM || port->iotype == UPIO_MEM32)
> pr_info("Early serial console at MMIO%s 0x%llx (options '%s')\n",
> - mmio32 ? "32" : "",
> + (port->iotype == UPIO_MEM32) ? "32" : "",
> (unsigned long long)port->mapbase,
> device->options);
> else
> diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c
> index 6a1055a..3f823c26 100644
> --- a/drivers/tty/serial/serial_core.c
> +++ b/drivers/tty/serial/serial_core.c
> @@ -1810,6 +1810,52 @@ uart_get_console(struct uart_port *ports, int nr, struct console *co)
> }
>
> /**
> + * uart_parse_earlycon - Parse earlycon options
> + * @p: ptr to 2nd field (ie., just beyond '<name>,')
> + * @iotype: ptr for decoded iotype (out)
> + * @addr: ptr for decoded mapbase/iobase (out)
> + * @options: ptr for <options> field; NULL if not present (out)
> + *
> + * Decodes earlycon kernel command line parameters of the form
> + * earlycon=<name>,io|mmio|mmio32,<addr>,<options>
> + * console=<name>,io|mmio|mmio32,<addr>,<options>
> + *
> + * The optional form
> + * earlycon=<name>,0x<addr>,<options>
> + * console=<name>,0x<addr>,<options>
> + * is also accepted; the returned @iotype will be UPIO_MEM.
> + *
> + * Returns 0 on success or -EINVAL on failure
> + */
> +int uart_parse_earlycon(char *p, unsigned char *iotype, unsigned long *addr,
> + char **options)
> +{
> + if (strncmp(p, "mmio,", 5) == 0) {
> + *iotype = UPIO_MEM;
> + p += 5;
> + } else if (strncmp(p, "mmio32,", 7) == 0) {
> + *iotype = UPIO_MEM32;
> + p += 7;
> + } else if (strncmp(p, "io,", 3) == 0) {
> + *iotype = UPIO_PORT;
> + p += 3;
> + } else if (strncmp(p, "0x", 2) == 0) {
> + *iotype = UPIO_MEM;
> + } else {
> + return -EINVAL;
> + }
> +
> + *addr = simple_strtoul(p, NULL, 0);

WARNING: simple_strtoul is obsolete, use kstrtoul instead
#136: FILE: drivers/tty/serial/serial_core.c:1848:
+ *addr = simple_strtoul(p, NULL, 0);


simple_strtoul() is _not_ obsolete.
kstrtoul() cannot parse the <addr> field in a string of the form:

console=<name>,io|mmio|mmio32,<addr>,<options>

kstrtoul() returns EINVAL without the result.

It was pretty fun debugging that without a console :/

Regards,
Peter Hurley

> + p = strchr(p, ',');
> + if (p)
> + p++;
> +
> + *options = p;
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(uart_parse_earlycon);
> +
> +/**
> * uart_parse_options - Parse serial port baud/parity/bits/flow control.
> * @options: pointer to option string
> * @baud: pointer to an 'int' variable for the baud rate.
> diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h
> index baf3e1d..cc5c506 100644
> --- a/include/linux/serial_core.h
> +++ b/include/linux/serial_core.h
> @@ -354,6 +354,8 @@ early_param("earlycon", name ## _setup_earlycon);
>
> struct uart_port *uart_get_console(struct uart_port *ports, int nr,
> struct console *c);
> +int uart_parse_earlycon(char *p, unsigned char *iotype, unsigned long *addr,
> + char **options);
> void uart_parse_options(char *options, int *baud, int *parity, int *bits,
> int *flow);
> int uart_set_options(struct uart_port *port, struct console *co, int baud,
>

2015-02-26 14:48:53

by Peter Hurley

[permalink] [raw]
Subject: Re: [PATCH -next 00/13] Extensible console matching & direct earlycon

Hi Rob,

On 02/24/2015 03:20 PM, Rob Herring wrote:
> On Tue, Feb 24, 2015 at 1:53 PM, Peter Hurley <[email protected]> wrote:

[...]

>>>> Direct earlycon
>>>>
>>>> This feature enables arches and proms to start an earlycon directly,
>>>> rather than requiring an "earlycon=" command line parameter.
>>>> Devicetree can already do this via the 'linux,stdout-path' property,
>>>> but arch and prom code requires direct coupling to the serial driver.
>>>>
>>>> This support is implemented by judicious refactoring and the same
>>>> construct that devicetree and early_param use: a link table containing
>>>> the necessary information (name and setup() function) to find and
>>>> bind the appropriate earlycon "driver".
>>>
>>> I've skimmed thru this and it looks like a great improvement.
>>>
>>> One problem we have currently with DT stdout-path and earlycon is a
>>> preferred console does not get registered, so the console will get
>>> switched to tty0 and you lose your console. The problem is DT does not
>>> know the console name to register a preferred console. It looks like
>>> this series may help this problem, but I'm not sure and wanted your
>>> thoughts.
>>
>> I thought that of_alias_scan() + of_console_check() caused DT stdout-path
>> to add_preferred_console() the driver console @ port registration time
>> via uart_add_one_port() -> of_console_check().
>>
>> Is that not how that works?
>
> Yes, I believe that is how it works with earlycon not enabled. This
> doesn't work when earlycon is enabled with just "earlycon" on the
> command line. The fix I have is here[1], but I don't like putting DT
> specifics into the console code.

After much gnashing of teeth and pulling of hair yesterday, I managed
to mock up the situation you describe, but I need to study it in more
detail. Some things I did learn:

1. The serial console _does_ come back up when using stdout-path but the
line settings don't match, because the serial core sets them to the
default of 9600n8 if unspecified.

2. The line settings can now be set with stdout-path like,
stdout-path = "serial0:115200n8"
but this breaks DT earlycon (as I wrote in the other email you were
cc'd on).

3. omap doesn't support ioremap() at early param parsing :(

4. the ARM arch doesn't support fixmap hacking at early param parsing :(

Regards,
Peter Hurley

2015-02-26 14:58:39

by Rob Herring (Arm)

[permalink] [raw]
Subject: Re: [PATCH -next 00/13] Extensible console matching & direct earlycon

On Thu, Feb 26, 2015 at 8:48 AM, Peter Hurley <[email protected]> wrote:
> Hi Rob,
>
> On 02/24/2015 03:20 PM, Rob Herring wrote:
>> On Tue, Feb 24, 2015 at 1:53 PM, Peter Hurley <[email protected]> wrote:
>
> [...]
>
>>>>> Direct earlycon
>>>>>
>>>>> This feature enables arches and proms to start an earlycon directly,
>>>>> rather than requiring an "earlycon=" command line parameter.
>>>>> Devicetree can already do this via the 'linux,stdout-path' property,
>>>>> but arch and prom code requires direct coupling to the serial driver.
>>>>>
>>>>> This support is implemented by judicious refactoring and the same
>>>>> construct that devicetree and early_param use: a link table containing
>>>>> the necessary information (name and setup() function) to find and
>>>>> bind the appropriate earlycon "driver".
>>>>
>>>> I've skimmed thru this and it looks like a great improvement.
>>>>
>>>> One problem we have currently with DT stdout-path and earlycon is a
>>>> preferred console does not get registered, so the console will get
>>>> switched to tty0 and you lose your console. The problem is DT does not
>>>> know the console name to register a preferred console. It looks like
>>>> this series may help this problem, but I'm not sure and wanted your
>>>> thoughts.
>>>
>>> I thought that of_alias_scan() + of_console_check() caused DT stdout-path
>>> to add_preferred_console() the driver console @ port registration time
>>> via uart_add_one_port() -> of_console_check().
>>>
>>> Is that not how that works?
>>
>> Yes, I believe that is how it works with earlycon not enabled. This
>> doesn't work when earlycon is enabled with just "earlycon" on the
>> command line. The fix I have is here[1], but I don't like putting DT
>> specifics into the console code.
>
> After much gnashing of teeth and pulling of hair yesterday, I managed
> to mock up the situation you describe, but I need to study it in more
> detail. Some things I did learn:
>
> 1. The serial console _does_ come back up when using stdout-path but the
> line settings don't match, because the serial core sets them to the
> default of 9600n8 if unspecified.

That may have been what I saw as I tested on QEMU which ignores the
baud rate. But it does stop between the time tty0 is enabled and the
"real" serial console which is a time period we really want the
console.

> 2. The line settings can now be set with stdout-path like,
> stdout-path = "serial0:115200n8"
> but this breaks DT earlycon (as I wrote in the other email you were
> cc'd on).

Right. We should fix libfdt.

> 3. omap doesn't support ioremap() at early param parsing :(

ARM in general does not.

> 4. the ARM arch doesn't support fixmap hacking at early param parsing :(

There's a patch on the list to enable it. It's been slow as fixmap has
been a moving target.

Rob

2015-02-26 15:54:43

by Peter Hurley

[permalink] [raw]
Subject: Re: [PATCH -next 00/13] Extensible console matching & direct earlycon

On 02/26/2015 09:58 AM, Rob Herring wrote:
> On Thu, Feb 26, 2015 at 8:48 AM, Peter Hurley <[email protected]> wrote:
>> Hi Rob,
>>
>> On 02/24/2015 03:20 PM, Rob Herring wrote:
>>> On Tue, Feb 24, 2015 at 1:53 PM, Peter Hurley <[email protected]> wrote:
>>
>> [...]
>>
>>>>>> Direct earlycon
>>>>>>
>>>>>> This feature enables arches and proms to start an earlycon directly,
>>>>>> rather than requiring an "earlycon=" command line parameter.
>>>>>> Devicetree can already do this via the 'linux,stdout-path' property,
>>>>>> but arch and prom code requires direct coupling to the serial driver.
>>>>>>
>>>>>> This support is implemented by judicious refactoring and the same
>>>>>> construct that devicetree and early_param use: a link table containing
>>>>>> the necessary information (name and setup() function) to find and
>>>>>> bind the appropriate earlycon "driver".
>>>>>
>>>>> I've skimmed thru this and it looks like a great improvement.
>>>>>
>>>>> One problem we have currently with DT stdout-path and earlycon is a
>>>>> preferred console does not get registered, so the console will get
>>>>> switched to tty0 and you lose your console. The problem is DT does not
>>>>> know the console name to register a preferred console. It looks like
>>>>> this series may help this problem, but I'm not sure and wanted your
>>>>> thoughts.
>>>>
>>>> I thought that of_alias_scan() + of_console_check() caused DT stdout-path
>>>> to add_preferred_console() the driver console @ port registration time
>>>> via uart_add_one_port() -> of_console_check().
>>>>
>>>> Is that not how that works?
>>>
>>> Yes, I believe that is how it works with earlycon not enabled. This
>>> doesn't work when earlycon is enabled with just "earlycon" on the
>>> command line. The fix I have is here[1], but I don't like putting DT
>>> specifics into the console code.
>>
>> After much gnashing of teeth and pulling of hair yesterday, I managed
>> to mock up the situation you describe, but I need to study it in more
>> detail. Some things I did learn:
>>
>> 1. The serial console _does_ come back up when using stdout-path but the
>> line settings don't match, because the serial core sets them to the
>> default of 9600n8 if unspecified.
>
> That may have been what I saw as I tested on QEMU which ignores the
> baud rate. But it does stop between the time tty0 is enabled and the
> "real" serial console which is a time period we really want the
> console.

Yeah, that's what my mock-up showed as well:

[ 0.000000] Machine model: TI AM335x BeagleBone Black
[ 0.000000] earlycon:serial0 options:115200
[ 0.000000] early_omap8250_setup: uart @ 44e09000
[ 0.000000] bootconsole [uart0] enabled
....
[ 0.000349] Console: colour dummy device 80x30
[ 0.000367] console [tty0] enabled
[ 0.000395] bootconsole [uart0] disabled
[ 0.000441] Calibrating delay loop... 995.32 BogoMIPS (lpj=1990656)
....
[ 1.097762] Serial: 8250/16550 driver, 32 ports, IRQ sharing disabled
[ 1.107659] 44e09000.serial: ttyS0 at MMIO 0x44e09000 (irq = 154, base_baud = 3000000) is a 8250
[ 1.888536] console [ttyS0] enabled
[ 1.893036] 481a8000.serial: ttyS4 at MMIO 0x481a8000 (irq = 155, base_baud = 3000000) is a 8250
[ 1.902810] 481aa000.serial: ttyS5 at MMIO 0x481aa000 (irq = 156, base_baud = 3000000) is a 8250


>> 2. The line settings can now be set with stdout-path like,
>> stdout-path = "serial0:115200n8"
>> but this breaks DT earlycon (as I wrote in the other email you were
>> cc'd on).
>
> Right. We should fix libfdt.
>
>> 3. omap doesn't support ioremap() at early param parsing :(
>
> ARM in general does not.

I was looking at mach-bcm to see how it was that the iotable_init() didn't
blow up, but maybe it does?

>> 4. the ARM arch doesn't support fixmap hacking at early param parsing :(
>
> There's a patch on the list to enable it. It's been slow as fixmap has
> been a moving target.

Thanks. That's similar to what I cribbed from ARM64 arch last night.

Regards,
Peter Hurley

2015-02-26 16:10:08

by Rob Herring (Arm)

[permalink] [raw]
Subject: Re: [PATCH -next 00/13] Extensible console matching & direct earlycon

On Thu, Feb 26, 2015 at 9:54 AM, Peter Hurley <[email protected]> wrote:
> On 02/26/2015 09:58 AM, Rob Herring wrote:
>> On Thu, Feb 26, 2015 at 8:48 AM, Peter Hurley <[email protected]> wrote:
>>> Hi Rob,
>>>
>>> On 02/24/2015 03:20 PM, Rob Herring wrote:
>>>> On Tue, Feb 24, 2015 at 1:53 PM, Peter Hurley <[email protected]> wrote:

[...]

>>> 3. omap doesn't support ioremap() at early param parsing :(
>>
>> ARM in general does not.
>
> I was looking at mach-bcm to see how it was that the iotable_init() didn't
> blow up, but maybe it does?

iotable_init is basically just a list of page table entries to create
and uses the early page table setup code.

Rob

2015-02-28 17:18:24

by Peter Hurley

[permalink] [raw]
Subject: Re: [PATCH -next 03/13] console: Add extensible console matching

On 02/24/2015 11:37 AM, Peter Hurley wrote:
> Add match() method to struct console which allows the console to
> perform console command line matching instead of (or in addition to)
> default console matching (ie., by fixed name and index).
>
> The match() method returns 0 to indicate a successful match; normal
> console matching occurs if no match() method is defined or the
> match() method returns non-zero. The match() method is expected to set
> the console index if required.
>
> Re-implement earlycon-to-console-handoff with direct matching of
> "console=uart|uart8250,..." to the 8250 ttyS console.
>
> Signed-off-by: Peter Hurley <[email protected]>
> ---
> drivers/tty/serial/8250/8250_core.c | 64 +++++++++++++++++++++++++++---------
> drivers/tty/serial/8250/8250_early.c | 23 -------------
> include/linux/console.h | 3 +-
> include/linux/serial_8250.h | 2 --
> kernel/printk/printk.c | 50 ++++++++++------------------
> 5 files changed, 66 insertions(+), 76 deletions(-)
>
> diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c
> index e3b9570a..5eb95fd 100644
> --- a/drivers/tty/serial/8250/8250_core.c
> +++ b/drivers/tty/serial/8250/8250_core.c
> @@ -3337,9 +3337,54 @@ static int serial8250_console_setup(struct console *co, char *options)
> return uart_set_options(port, co, baud, parity, bits, flow);
> }
>
> -static int serial8250_console_early_setup(void)
> +/**
> + * serial8250_console_match - non-standard console matching
> + * @co: registering console
> + * @name: name from console command line
> + * @idx: index from console command line
> + * @options: ptr to option string from console command line
> + *
> + * Only attempts to match console command lines of the form:
> + * console=uart<>,io|mmio|mmio32,<addr>,<options>
> + * console=uart<>,<addr>,options
> + * This form is used to register an initial earlycon boot console and
> + * replace it with the serial8250_console at 8250 driver init.
> + *
> + * Performs console setup for a match (as required by interface)
> + *
> + * Returns 0 if console matches; otherwise non-zero to use default matching
> + */
> +static int serial8250_console_match(struct console *co, char *name, int idx,
> + char *options)
> {
> - return serial8250_find_port_for_earlycon();
> + char match[] = "uart"; /* 8250-specific earlycon name */
> + unsigned char iotype;
> + unsigned long addr;
> + int i;
> +
> + if (strncmp(name, match, 4) != 0)
> + return -ENODEV;
> +
> + if (uart_parse_earlycon(options, &iotype, &addr, &options))
> + return -ENODEV;
> +
> + /* try to match the port specified on the command line */
> + for (i = 0; i < nr_uarts; i++) {
> + struct uart_port *port = &serial8250_ports[i].port;
> +
> + if (port->iotype != iotype)
> + continue;
> + if ((iotype == UPIO_MEM || iotype == UPIO_MEM32) &&
> + (port->mapbase != addr))
> + continue;
> + if (iotype == UPIO_PORT && port->iobase != addr)
> + continue;
> +
> + co->index = i;
> + return serial8250_console_setup(co, options);
> + }
> +
> + return -ENODEV;
> }
>
> static struct console serial8250_console = {
> @@ -3347,7 +3392,7 @@ static struct console serial8250_console = {
> .write = serial8250_console_write,
> .device = uart_console_device,
> .setup = serial8250_console_setup,
> - .early_setup = serial8250_console_early_setup,
> + .match = serial8250_console_match,
> .flags = CON_PRINTBUFFER | CON_ANYTIME,
> .index = -1,
> .data = &serial8250_reg,
> @@ -3361,19 +3406,6 @@ static int __init serial8250_console_init(void)
> }
> console_initcall(serial8250_console_init);
>
> -int serial8250_find_port(struct uart_port *p)
> -{
> - int line;
> - struct uart_port *port;
> -
> - for (line = 0; line < nr_uarts; line++) {
> - port = &serial8250_ports[line].port;
> - if (uart_match_port(p, port))
> - return line;
> - }
> - return -ENODEV;
> -}
> -
> #define SERIAL8250_CONSOLE &serial8250_console
> #else
> #define SERIAL8250_CONSOLE NULL
> diff --git a/drivers/tty/serial/8250/8250_early.c b/drivers/tty/serial/8250/8250_early.c
> index c31a22b..49bca65 100644
> --- a/drivers/tty/serial/8250/8250_early.c
> +++ b/drivers/tty/serial/8250/8250_early.c
> @@ -173,26 +173,3 @@ int __init setup_early_serial8250_console(char *cmdline)
>
> return setup_earlycon(cmdline, match, early_serial8250_setup);
> }
> -
> -int serial8250_find_port_for_earlycon(void)
> -{
> - struct earlycon_device *device = early_device;
> - struct uart_port *port = device ? &device->port : NULL;
> - int line;
> - int ret;
> -
> - if (!port || (!port->membase && !port->iobase))
> - return -ENODEV;
> -
> - line = serial8250_find_port(port);
> - if (line < 0)
> - return -ENODEV;
> -
> - ret = update_console_cmdline("uart", 8250,
> - "ttyS", line, device->options);
> - if (ret < 0)
> - ret = update_console_cmdline("uart", 0,
> - "ttyS", line, device->options);
> -
> - return ret;
> -}
> diff --git a/include/linux/console.h b/include/linux/console.h
> index 7571a16..9f50fb4 100644
> --- a/include/linux/console.h
> +++ b/include/linux/console.h
> @@ -123,7 +123,7 @@ struct console {
> struct tty_driver *(*device)(struct console *, int *);
> void (*unblank)(void);
> int (*setup)(struct console *, char *);
> - int (*early_setup)(void);
> + int (*match)(struct console *, char *name, int idx, char *options);
> short flags;
> short index;
> int cflag;
> @@ -141,7 +141,6 @@ extern int console_set_on_cmdline;
> extern struct console *early_console;
>
> extern int add_preferred_console(char *name, int idx, char *options);
> -extern int update_console_cmdline(char *name, int idx, char *name_new, int idx_new, char *options);
> extern void register_console(struct console *);
> extern int unregister_console(struct console *);
> extern struct console *console_drivers;
> diff --git a/include/linux/serial_8250.h b/include/linux/serial_8250.h
> index a8efa23..f26ae7f 100644
> --- a/include/linux/serial_8250.h
> +++ b/include/linux/serial_8250.h
> @@ -118,8 +118,6 @@ void serial8250_resume_port(int line);
>
> extern int early_serial_setup(struct uart_port *port);
>
> -extern int serial8250_find_port(struct uart_port *p);
> -extern int serial8250_find_port_for_earlycon(void);
> extern unsigned int serial8250_early_in(struct uart_port *port, int offset);
> extern void serial8250_early_out(struct uart_port *port, int offset, int value);
> extern int setup_early_serial8250_console(char *cmdline);
> diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
> index ce5c297..040be39 100644
> --- a/kernel/printk/printk.c
> +++ b/kernel/printk/printk.c
> @@ -2017,24 +2017,6 @@ int add_preferred_console(char *name, int idx, char *options)
> return __add_preferred_console(name, idx, options, NULL);
> }
>
> -int update_console_cmdline(char *name, int idx, char *name_new, int idx_new, char *options)
> -{
> - struct console_cmdline *c;
> - int i;
> -
> - for (i = 0, c = console_cmdline;
> - i < MAX_CMDLINECONSOLES && c->name[0];
> - i++, c++)
> - if (strcmp(c->name, name) == 0 && c->index == idx) {
> - strlcpy(c->name, name_new, sizeof(c->name));
> - c->options = options;
> - c->index = idx_new;
> - return i;
> - }
> - /* not found */
> - return -1;
> -}
> -
> bool console_suspend_enabled = true;
> EXPORT_SYMBOL(console_suspend_enabled);
>
> @@ -2436,9 +2418,6 @@ void register_console(struct console *newcon)
> if (preferred_console < 0 || bcon || !console_drivers)
> preferred_console = selected_console;
>
> - if (newcon->early_setup)
> - newcon->early_setup();
> -
> /*
> * See if we want to use this console driver. If we
> * didn't select a console we take the first one
> @@ -2464,20 +2443,25 @@ void register_console(struct console *newcon)
> for (i = 0, c = console_cmdline;
> i < MAX_CMDLINECONSOLES && c->name[0];
> i++, c++) {
> - if (strcmp(c->name, newcon->name) != 0)
> - continue;

Andrew,

I just discovered that struct console_cmdline::name[] and
struct console::name[] are not the same size, which is a bug that
should probably go into -stable.

I'll respin a v2 of this series on top of that bug fix, which I'll
send shortly.

Regards,
Peter Hurley

> - if (newcon->index >= 0 &&
> - newcon->index != c->index)
> - continue;
> - if (newcon->index < 0)
> - newcon->index = c->index;
> + if (!newcon->match ||
> + newcon->match(newcon, c->name, c->index, c->options) != 0) {
> + /* default matching */
> + if (strcmp(c->name, newcon->name) != 0)
> + continue;
> + if (newcon->index >= 0 &&
> + newcon->index != c->index)
> + continue;
> + if (newcon->index < 0)
> + newcon->index = c->index;
>
> - if (_braille_register_console(newcon, c))
> - return;
> + if (_braille_register_console(newcon, c))
> + return;
> +
> + if (newcon->setup &&
> + newcon->setup(newcon, c->options) != 0)
> + break;
> + }
>
> - if (newcon->setup &&
> - newcon->setup(newcon, console_cmdline[i].options) != 0)
> - break;
> newcon->flags |= CON_ENABLED;
> if (i == selected_console) {
> newcon->flags |= CON_CONSDEV;
>