| From 7f8fdd4dbffc05982b96caf586f77a014b2a9353 Mon Sep 17 00:00:00 2001 |
| From: Yunhui Cui <cuiyunhui@bytedance.com> |
| Date: Wed, 23 Jul 2025 10:33:22 +0800 |
| Subject: serial: 8250: fix panic due to PSLVERR |
| |
| From: Yunhui Cui <cuiyunhui@bytedance.com> |
| |
| commit 7f8fdd4dbffc05982b96caf586f77a014b2a9353 upstream. |
| |
| When the PSLVERR_RESP_EN parameter is set to 1, the device generates |
| an error response if an attempt is made to read an empty RBR (Receive |
| Buffer Register) while the FIFO is enabled. |
| |
| In serial8250_do_startup(), calling serial_port_out(port, UART_LCR, |
| UART_LCR_WLEN8) triggers dw8250_check_lcr(), which invokes |
| dw8250_force_idle() and serial8250_clear_and_reinit_fifos(). The latter |
| function enables the FIFO via serial_out(p, UART_FCR, p->fcr). |
| Execution proceeds to the serial_port_in(port, UART_RX). |
| This satisfies the PSLVERR trigger condition. |
| |
| When another CPU (e.g., using printk()) is accessing the UART (UART |
| is busy), the current CPU fails the check (value & ~UART_LCR_SPAR) == |
| (lcr & ~UART_LCR_SPAR) in dw8250_check_lcr(), causing it to enter |
| dw8250_force_idle(). |
| |
| Put serial_port_out(port, UART_LCR, UART_LCR_WLEN8) under the port->lock |
| to fix this issue. |
| |
| Panic backtrace: |
| [ 0.442336] Oops - unknown exception [#1] |
| [ 0.442343] epc : dw8250_serial_in32+0x1e/0x4a |
| [ 0.442351] ra : serial8250_do_startup+0x2c8/0x88e |
| ... |
| [ 0.442416] console_on_rootfs+0x26/0x70 |
| |
| Fixes: c49436b657d0 ("serial: 8250_dw: Improve unwritable LCR workaround") |
| Link: https://lore.kernel.org/all/84cydt5peu.fsf@jogness.linutronix.de/T/ |
| Signed-off-by: Yunhui Cui <cuiyunhui@bytedance.com> |
| Reviewed-by: John Ogness <john.ogness@linutronix.de> |
| Cc: stable <stable@kernel.org> |
| Link: https://lore.kernel.org/r/20250723023322.464-2-cuiyunhui@bytedance.com |
| [ Applied fix to serial8250_do_startup() instead of serial8250_initialize() ] |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| drivers/tty/serial/8250/8250_port.c | 3 +-- |
| 1 file changed, 1 insertion(+), 2 deletions(-) |
| |
| --- a/drivers/tty/serial/8250/8250_port.c |
| +++ b/drivers/tty/serial/8250/8250_port.c |
| @@ -2370,9 +2370,8 @@ int serial8250_do_startup(struct uart_po |
| /* |
| * Now, initialize the UART |
| */ |
| - serial_port_out(port, UART_LCR, UART_LCR_WLEN8); |
| - |
| spin_lock_irqsave(&port->lock, flags); |
| + serial_port_out(port, UART_LCR, UART_LCR_WLEN8); |
| if (up->port.flags & UPF_FOURPORT) { |
| if (!up->port.irq) |
| up->port.mctrl |= TIOCM_OUT1; |