Patch "serial: Lock console when calling into driver before registration" has been added to the 5.15-stable tree

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This is a note to let you know that I've just added the patch titled

    serial: Lock console when calling into driver before registration

to the 5.15-stable tree which can be found at:
    http://www.kernel.org/git/?p=linux/kernel/git/stable/stable-queue.git;a=summary

The filename of the patch is:
     serial-lock-console-when-calling-into-driver-before-.patch
and it can be found in the queue-5.15 subdirectory.

If you, or anyone else, feels it should not be added to the stable tree,
please let <stable@xxxxxxxxxxxxxxx> know about it.



commit 55a2cb70ad4b3aa537984140ebab26e2a58661c0
Author: Peter Collingbourne <pcc@xxxxxxxxxx>
Date:   Mon Mar 4 13:43:49 2024 -0800

    serial: Lock console when calling into driver before registration
    
    [ Upstream commit 801410b26a0e8b8a16f7915b2b55c9528b69ca87 ]
    
    During the handoff from earlycon to the real console driver, we have
    two separate drivers operating on the same device concurrently. In the
    case of the 8250 driver these concurrent accesses cause problems due
    to the driver's use of banked registers, controlled by LCR.DLAB. It is
    possible for the setup(), config_port(), pm() and set_mctrl() callbacks
    to set DLAB, which can cause the earlycon code that intends to access
    TX to instead access DLL, leading to missed output and corruption on
    the serial line due to unintended modifications to the baud rate.
    
    In particular, for setup() we have:
    
    univ8250_console_setup()
    -> serial8250_console_setup()
    -> uart_set_options()
    -> serial8250_set_termios()
    -> serial8250_do_set_termios()
    -> serial8250_do_set_divisor()
    
    For config_port() we have:
    
    serial8250_config_port()
    -> autoconfig()
    
    For pm() we have:
    
    serial8250_pm()
    -> serial8250_do_pm()
    -> serial8250_set_sleep()
    
    For set_mctrl() we have (for some devices):
    
    serial8250_set_mctrl()
    -> omap8250_set_mctrl()
    -> __omap8250_set_mctrl()
    
    To avoid such problems, let's make it so that the console is locked
    during pre-registration calls to these callbacks, which will prevent
    the earlycon driver from running concurrently.
    
    Remove the partial solution to this problem in the 8250 driver
    that locked the console only during autoconfig_irq(), as this would
    result in a deadlock with the new approach. The console continues
    to be locked during autoconfig_irq() because it can only be called
    through uart_configure_port().
    
    Although this patch introduces more locking than strictly necessary
    (and in particular it also locks during the call to rs485_config()
    which is not affected by this issue as far as I can tell), it follows
    the principle that it is the responsibility of the generic console
    code to manage the earlycon handoff by ensuring that earlycon and real
    console driver code cannot run concurrently, and not the individual
    drivers.
    
    Signed-off-by: Peter Collingbourne <pcc@xxxxxxxxxx>
    Reviewed-by: John Ogness <john.ogness@xxxxxxxxxxxxx>
    Link: https://linux-review.googlesource.com/id/I7cf8124dcebf8618e6b2ee543fa5b25532de55d8
    Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
    Cc: stable@xxxxxxxxxxxxxxx
    Link: https://lore.kernel.org/r/20240304214350.501253-1-pcc@xxxxxxxxxx
    Signed-off-by: Greg Kroah-Hartman <gregkh@xxxxxxxxxxxxxxxxxxx>
    Signed-off-by: Sasha Levin <sashal@xxxxxxxxxx>

diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c
index 4fce318bc83ca..2d595a646bb89 100644
--- a/drivers/tty/serial/8250/8250_port.c
+++ b/drivers/tty/serial/8250/8250_port.c
@@ -1366,9 +1366,6 @@ static void autoconfig_irq(struct uart_8250_port *up)
 		inb_p(ICP);
 	}
 
-	if (uart_console(port))
-		console_lock();
-
 	/* forget possible initially masked and pending IRQ */
 	probe_irq_off(probe_irq_on());
 	save_mcr = serial8250_in_MCR(up);
@@ -1399,9 +1396,6 @@ static void autoconfig_irq(struct uart_8250_port *up)
 	if (port->flags & UPF_FOURPORT)
 		outb_p(save_ICP, ICP);
 
-	if (uart_console(port))
-		console_unlock();
-
 	port->irq = (irq > 0) ? irq : 0;
 }
 
diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c
index 45b721abaa2f5..b638b2fd2d3d2 100644
--- a/drivers/tty/serial/serial_core.c
+++ b/drivers/tty/serial/serial_core.c
@@ -2410,7 +2410,12 @@ uart_configure_port(struct uart_driver *drv, struct uart_state *state,
 			port->type = PORT_UNKNOWN;
 			flags |= UART_CONFIG_TYPE;
 		}
+		/* Synchronize with possible boot console. */
+		if (uart_console(port))
+			console_lock();
 		port->ops->config_port(port, flags);
+		if (uart_console(port))
+			console_unlock();
 	}
 
 	if (port->type != PORT_UNKNOWN) {
@@ -2418,6 +2423,10 @@ uart_configure_port(struct uart_driver *drv, struct uart_state *state,
 
 		uart_report_port(drv, port);
 
+		/* Synchronize with possible boot console. */
+		if (uart_console(port))
+			console_lock();
+
 		/* Power up port for set_mctrl() */
 		uart_change_pm(state, UART_PM_STATE_ON);
 
@@ -2434,6 +2443,9 @@ uart_configure_port(struct uart_driver *drv, struct uart_state *state,
 			port->rs485_config(port, &port->rs485);
 		spin_unlock_irqrestore(&port->lock, flags);
 
+		if (uart_console(port))
+			console_unlock();
+
 		/*
 		 * If this driver supports console, and it hasn't been
 		 * successfully registered yet, try to re-register it.
diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
index 4bef2963284af..df4d07af6d1e2 100644
--- a/kernel/printk/printk.c
+++ b/kernel/printk/printk.c
@@ -2879,6 +2879,21 @@ static int __init keep_bootcon_setup(char *str)
 
 early_param("keep_bootcon", keep_bootcon_setup);
 
+static int console_call_setup(struct console *newcon, char *options)
+{
+	int err;
+
+	if (!newcon->setup)
+		return 0;
+
+	/* Synchronize with possible boot console. */
+	console_lock();
+	err = newcon->setup(newcon, options);
+	console_unlock();
+
+	return err;
+}
+
 /*
  * This is called by register_console() to try to match
  * the newly registered console with any of the ones selected
@@ -2914,8 +2929,8 @@ static int try_enable_preferred_console(struct console *newcon,
 			if (_braille_register_console(newcon, c))
 				return 0;
 
-			if (newcon->setup &&
-			    (err = newcon->setup(newcon, c->options)) != 0)
+			err = console_call_setup(newcon, c->options);
+			if (err)
 				return err;
 		}
 		newcon->flags |= CON_ENABLED;
@@ -2943,7 +2958,7 @@ static void try_enable_default_console(struct console *newcon)
 	if (newcon->index < 0)
 		newcon->index = 0;
 
-	if (newcon->setup && newcon->setup(newcon, NULL) != 0)
+	if (console_call_setup(newcon, NULL) != 0)
 		return;
 
 	newcon->flags |= CON_ENABLED;




[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Index of Archives]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux