Skip to contentSkip to conversation

Extensible console matching & direct earlycon

PATCH Superseded Peter Hurley 3 participants 20 messages Mar 1, 2015 – Mar 7, 2015

Subsystems (6)

v2 of 3 Superseded Latest is v3 →

lore
Share & tools
lore
#0 lore raw

v2 cover Extensible console matching & direct earlycon

Changes from v1:
* rebased on and requires "console: Fix console name size mismatch"

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               |  53 +++++--------
 11 files changed, 246 insertions(+), 154 deletions(-)
Signature · 1 line
2.3.0
#1 lore raw

PATCH v2 1/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>

---
 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(-)
drivers/tty/serial/earlycon.c +13 -26
index 64fe25a..58d6bcd 100644
@@ -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
drivers/tty/serial/serial_core.c +46 -0

52 lines not on this page · raw message

include/linux/serial_core.h +2 -0
index baf3e1d..cc5c506 100644
@@ -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,
Signature · 1 line
2.3.0
#2 lore raw

PATCH v2 2/13 console: Preserve index after console setup()

In reply to Peter Hurley

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).

---
 kernel/printk/printk.c | 1 -
 1 file changed, 1 deletion(-)
kernel/printk/printk.c +0 -1
index 36a8661..098c2e0 100644
@@ -2480,7 +2480,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;
Signature · 1 line
2.3.0
#3 lore raw

PATCH v2 3/13 console: Add extensible console matching

In reply to Peter Hurley

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.

---
 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               | 52 ++++++++++-------------------
 5 files changed, 67 insertions(+), 77 deletions(-)
drivers/tty/serial/8250/8250_core.c +48 -16
index e3b9570a..5eb95fd 100644
@@ -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
drivers/tty/serial/8250/8250_early.c +0 -23
index c31a22b..49bca65 100644
@@ -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;
-}
include/linux/console.h +1 -2
index 7571a16..9f50fb4 100644
@@ -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;
include/linux/serial_8250.h +0 -2
index a8efa23..f26ae7f 100644
@@ -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);
kernel/printk/printk.c +18 -34

72 lines not on this page · raw message

Signature · 1 line
2.3.0
#4 lore raw

Re: [PATCH v2 -next 03/13] console: Add extensible console matching

Quoted 12 lines · Peter Hurley

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.

Quoted 8 lines
---
 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               | 52 ++++++++++-------------------
 5 files changed, 67 insertions(+), 77 deletions(-)

This didn't apply, so I've stopped here.

thanks,

greg k-h

#5 lore raw

Re: [PATCH v2 -next 03/13] console: Add extensible console matching

Quoted 22 lines · Greg Kroah-Hartman
Quoted 12 lines · Peter Hurley

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.

Quoted 8 lines
---
 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               | 52 ++++++++++-------------------
 5 files changed, 67 insertions(+), 77 deletions(-)
Quoted 1 line

This didn't apply, so I've stopped here.

Hi Greg,

Your tty-testing branch doesn't have "console: Fix console name size mismatch" that you added to tty-linus. I tried to draw attention to that in the series cover letter:

Quoted 2 lines · Peter Hurley

Changes from v1:
* rebased on and requires "console: Fix console name size mismatch"

Regards,
Peter Hurley

#6 lore raw

PATCH v2 4/13 serial: core: Fix kernel doc for uart_console_write()

In reply to Peter Hurley

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

---
 drivers/tty/serial/serial_core.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/tty/serial/serial_core.c +1 -1
index 3f823c26..373cfde 100644
@@ -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
Signature · 1 line
2.3.0
#7 lore raw

PATCH v2 5/13 serial: 8250_early: Remove early_device variable

In reply to Peter Hurley

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()).

---
 drivers/tty/serial/8250/8250_early.c | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)
drivers/tty/serial/8250/8250_early.c +2 -4
index 49bca65..d7b831b 100644
@@ -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;
 }
Signature · 1 line
2.3.0
#8 lore raw

PATCH v2 6/13 serial: earlycon: Move ->uartclk initialize

In reply to Peter Hurley

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()).

---
 drivers/tty/serial/earlycon.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)
drivers/tty/serial/earlycon.c +1 -2
index 58d6bcd..0480c8f 100644
@@ -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);
 
Signature · 1 line
2.3.0
#9 lore raw

PATCH v2 7/13 serial: 8250_early: Assume uart already initialized if no baud option

In reply to Peter Hurley

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.

---
 drivers/tty/serial/8250/8250_early.c | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)
drivers/tty/serial/8250/8250_early.c +8 -2
index d7b831b..1701d00 100644
@@ -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;
Signature · 1 line
2.3.0
#10 lore raw

PATCH v2 8/13 serial: 8250_early: Fix setup() error code

In reply to Peter Hurley

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.

---
 drivers/tty/serial/8250/8250_early.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/tty/serial/8250/8250_early.c +1 -1
index 1701d00..b199c10 100644
@@ -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;
Signature · 1 line
2.3.0
#11 lore raw

PATCH v2 9/13 serial: earlycon: Ignore parse_options() error code

In reply to Peter Hurley

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.

---
 drivers/tty/serial/earlycon.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)
drivers/tty/serial/earlycon.c +1 -2
index 0480c8f..da5e8c8 100644
@@ -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;
Signature · 1 line
2.3.0
#12 lore raw

PATCH v2 10/13 serial: earlycon: Allow earlycon params with name only

In reply to Peter Hurley

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

---
 drivers/tty/serial/earlycon.c | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)
drivers/tty/serial/earlycon.c +7 -4
index da5e8c8..025ea01 100644
@@ -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;
Signature · 1 line
2.3.0
#13 lore raw

Re: [PATCH v2 -next 10/13] serial: earlycon: Allow earlycon params with name only

Quoted 2 lines · Peter Hurley

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

Humm, that should already be the case with ARM semihosting (smh).

Rob

Quoted 34 lines
---
 drivers/tty/serial/earlycon.c | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)
drivers/tty/serial/earlycon.c +7 -5
index da5e8c8..025ea01 100644
@@ -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

#14 lore raw

Re: [PATCH v2 -next 10/13] serial: earlycon: Allow earlycon params with name only

Quoted 7 lines · Rob Herring
Quoted 2 lines · Peter Hurley

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

Humm, that should already be the case with ARM semihosting (smh).

Rob

Then it's passing a bogus options string to parse_options()

Quoted 34 lines
Quoted 34 lines
---
 drivers/tty/serial/earlycon.c | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)
drivers/tty/serial/earlycon.c +7 -5
index da5e8c8..025ea01 100644
@@ -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

#15 lore raw

Re: [PATCH v2 -next 10/13] serial: earlycon: Allow earlycon params with name only

Quoted 10 lines · Peter Hurley
Quoted 7 lines · Rob Herring
Quoted 2 lines · Peter Hurley

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

Humm, that should already be the case with ARM semihosting (smh).

Rob

Then it's passing a bogus options string to parse_options()

No, that's not right.

It's passing an empty string (the '\0' at the end) to parse_options() which is failing with -EINVAL, which upon returning sets buf to NULL.

My way has less obfuscation but the commit log needs to change.

Regards,
Peter Hurley

Quoted 35 lines
Quoted 34 lines
Quoted 34 lines
---
 drivers/tty/serial/earlycon.c | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)
drivers/tty/serial/earlycon.c +7 -5
index da5e8c8..025ea01 100644
@@ -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

#16 lore raw

PATCH v2 11/13 serial: earlycon: Refactor earlycon registration

In reply to Peter Hurley

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

---
 drivers/tty/serial/earlycon.c | 42 +++++++++++++++++++++++++-----------------
 1 file changed, 25 insertions(+), 17 deletions(-)
drivers/tty/serial/earlycon.c +25 -17
index 025ea01..9fb76b6 100644
@@ -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 *))
 {
Signature · 1 line
2.3.0
#17 lore raw

PATCH v2 12/13 serial: earlycon: Enable earlycon without command line param

In reply to Peter Hurley

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.

---
 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(-)
drivers/tty/serial/8250/8250_early.c +1 -6
index b199c10..d272139 100644
@@ -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);
 }
drivers/tty/serial/earlycon.c +73 -19

128 lines not on this page · raw message

include/asm-generic/vmlinux.lds.h +9 -0
index bee5d68..006c1f7 100644
@@ -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							\
include/linux/serial_core.h +11 -8
index cc5c506..a3fd182 100644
@@ -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 *)
Signature · 1 line
2.3.0
#18 lore raw

PATCH v2 13/13 serial: 8250_early: Remove setup_early_serial8250_console()

In reply to Peter Hurley

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.

---
 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(-)
arch/mips/mti-malta/malta-init.c +2 -2
index 6849f53..cec3e18 100644
@@ -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) {
drivers/firmware/pcdp.c +2 -2
index a330492..75273a25 100644
@@ -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
drivers/tty/serial/8250/8250_early.c +0 -5
index d272139..a2b4e58 100644
@@ -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);
-}
include/linux/serial_8250.h +0 -1
index f26ae7f..ca9f87b 100644
@@ -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);
Signature · 1 line
2.3.0
Acked-by on the v2 series
#19 lore raw
In reply to Peter Hurley
Quoted 2 lines · Peter Hurley

Changes from v1:
* rebased on and requires "console: Fix console name size mismatch"

I plan to pick this one up for 4.0 because I need it for "of: Fix premature bootconsole disable with 'stdout-path'".

Quoted 72 lines

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()

Quoted 12 lines
 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               |  53 +++++--------
 11 files changed, 246 insertions(+), 154 deletions(-)

For the series:

Quoted 4 lines

--
2.3.0