TTY/Serial driver fixes for 6.12-rc4

Here are some small tty and serial driver fixes for 6.12-rc4.  Included
 in here are:
   - qcom-geni serial driver fixes, wow what a mess of a UART chip that
     thing is...
   - vt infoleak fix for odd font sizes
   - imx serial driver bugfix
   - yet-another n_gsm ldisc bugfix, slowly chipping down the issues in
     that piece of code.
 
 All of these have been in linux-next for over a week with no reported
 issues.
 
 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
 -----BEGIN PGP SIGNATURE-----
 
 iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCZxUGDQ8cZ3JlZ0Brcm9h
 aC5jb20ACgkQMUfUDdst+ymS+ACfQaKXHHy0WZ/kB22Eif7KgKsx+D0AnjxH+H5l
 knORLhrVW+V41wyk9QM/
 =qbdY
 -----END PGP SIGNATURE-----

Merge tag 'tty-6.12-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty

Pull tty/serial driver fixes from Greg KH:
 "Here are some small tty and serial driver fixes for 6.12-rc4:

   - qcom-geni serial driver fixes, wow what a mess of a UART chip that
     thing is...

   - vt infoleak fix for odd font sizes

   - imx serial driver bugfix

   - yet-another n_gsm ldisc bugfix, slowly chipping down the issues in
     that piece of code

  All of these have been in linux-next for over a week with no reported
  issues"

* tag 'tty-6.12-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty:
  serial: qcom-geni: rename suspend functions
  serial: qcom-geni: drop unused receive parameter
  serial: qcom-geni: drop flip buffer WARN()
  serial: qcom-geni: fix rx cancel dma status bit
  serial: qcom-geni: fix receiver enable
  serial: qcom-geni: fix dma rx cancellation
  serial: qcom-geni: fix shutdown race
  serial: qcom-geni: revert broken hibernation support
  serial: qcom-geni: fix polled console initialisation
  serial: imx: Update mctrl old_status on RTSD interrupt
  tty: n_gsm: Fix use-after-free in gsm_cleanup_mux
  vt: prevent kernel-infoleak in con_font_get()
This commit is contained in:
Linus Torvalds 2024-10-20 13:03:30 -07:00
commit c01ac4b944
5 changed files with 67 additions and 57 deletions

View File

@ -3157,6 +3157,8 @@ static void gsm_cleanup_mux(struct gsm_mux *gsm, bool disc)
mutex_unlock(&gsm->mutex); mutex_unlock(&gsm->mutex);
/* Now wipe the queues */ /* Now wipe the queues */
tty_ldisc_flush(gsm->tty); tty_ldisc_flush(gsm->tty);
guard(spinlock_irqsave)(&gsm->tx_lock);
list_for_each_entry_safe(txq, ntxq, &gsm->tx_ctrl_list, list) list_for_each_entry_safe(txq, ntxq, &gsm->tx_ctrl_list, list)
kfree(txq); kfree(txq);
INIT_LIST_HEAD(&gsm->tx_ctrl_list); INIT_LIST_HEAD(&gsm->tx_ctrl_list);

View File

@ -762,6 +762,21 @@ static irqreturn_t __imx_uart_rtsint(int irq, void *dev_id)
imx_uart_writel(sport, USR1_RTSD, USR1); imx_uart_writel(sport, USR1_RTSD, USR1);
usr1 = imx_uart_readl(sport, USR1) & USR1_RTSS; usr1 = imx_uart_readl(sport, USR1) & USR1_RTSS;
/*
* Update sport->old_status here, so any follow-up calls to
* imx_uart_mctrl_check() will be able to recognize that RTS
* state changed since last imx_uart_mctrl_check() call.
*
* In case RTS has been detected as asserted here and later on
* deasserted by the time imx_uart_mctrl_check() was called,
* imx_uart_mctrl_check() can detect the RTS state change and
* trigger uart_handle_cts_change() to unblock the port for
* further TX transfers.
*/
if (usr1 & USR1_RTSS)
sport->old_status |= TIOCM_CTS;
else
sport->old_status &= ~TIOCM_CTS;
uart_handle_cts_change(&sport->port, usr1); uart_handle_cts_change(&sport->port, usr1);
wake_up_interruptible(&sport->port.state->port.delta_msr_wait); wake_up_interruptible(&sport->port.state->port.delta_msr_wait);

View File

@ -147,6 +147,7 @@ static struct uart_driver qcom_geni_uart_driver;
static void __qcom_geni_serial_cancel_tx_cmd(struct uart_port *uport); static void __qcom_geni_serial_cancel_tx_cmd(struct uart_port *uport);
static void qcom_geni_serial_cancel_tx_cmd(struct uart_port *uport); static void qcom_geni_serial_cancel_tx_cmd(struct uart_port *uport);
static int qcom_geni_serial_port_setup(struct uart_port *uport);
static inline struct qcom_geni_serial_port *to_dev_port(struct uart_port *uport) static inline struct qcom_geni_serial_port *to_dev_port(struct uart_port *uport)
{ {
@ -395,6 +396,23 @@ static void qcom_geni_serial_poll_put_char(struct uart_port *uport,
writel(c, uport->membase + SE_GENI_TX_FIFOn); writel(c, uport->membase + SE_GENI_TX_FIFOn);
qcom_geni_serial_poll_tx_done(uport); qcom_geni_serial_poll_tx_done(uport);
} }
static int qcom_geni_serial_poll_init(struct uart_port *uport)
{
struct qcom_geni_serial_port *port = to_dev_port(uport);
int ret;
if (!port->setup) {
ret = qcom_geni_serial_port_setup(uport);
if (ret)
return ret;
}
if (!qcom_geni_serial_secondary_active(uport))
geni_se_setup_s_cmd(&port->se, UART_START_READ, 0);
return 0;
}
#endif #endif
#ifdef CONFIG_SERIAL_QCOM_GENI_CONSOLE #ifdef CONFIG_SERIAL_QCOM_GENI_CONSOLE
@ -562,7 +580,7 @@ static void handle_rx_console(struct uart_port *uport, u32 bytes, bool drop)
} }
#endif /* CONFIG_SERIAL_QCOM_GENI_CONSOLE */ #endif /* CONFIG_SERIAL_QCOM_GENI_CONSOLE */
static void handle_rx_uart(struct uart_port *uport, u32 bytes, bool drop) static void handle_rx_uart(struct uart_port *uport, u32 bytes)
{ {
struct qcom_geni_serial_port *port = to_dev_port(uport); struct qcom_geni_serial_port *port = to_dev_port(uport);
struct tty_port *tport = &uport->state->port; struct tty_port *tport = &uport->state->port;
@ -570,9 +588,8 @@ static void handle_rx_uart(struct uart_port *uport, u32 bytes, bool drop)
ret = tty_insert_flip_string(tport, port->rx_buf, bytes); ret = tty_insert_flip_string(tport, port->rx_buf, bytes);
if (ret != bytes) { if (ret != bytes) {
dev_err(uport->dev, "%s:Unable to push data ret %d_bytes %d\n", dev_err_ratelimited(uport->dev, "failed to push data (%d < %u)\n",
__func__, ret, bytes); ret, bytes);
WARN_ON_ONCE(1);
} }
uport->icount.rx += ret; uport->icount.rx += ret;
tty_flip_buffer_push(tport); tty_flip_buffer_push(tport);
@ -787,17 +804,27 @@ static void qcom_geni_serial_start_rx_fifo(struct uart_port *uport)
static void qcom_geni_serial_stop_rx_dma(struct uart_port *uport) static void qcom_geni_serial_stop_rx_dma(struct uart_port *uport)
{ {
struct qcom_geni_serial_port *port = to_dev_port(uport); struct qcom_geni_serial_port *port = to_dev_port(uport);
bool done;
if (!qcom_geni_serial_secondary_active(uport)) if (!qcom_geni_serial_secondary_active(uport))
return; return;
geni_se_cancel_s_cmd(&port->se); geni_se_cancel_s_cmd(&port->se);
qcom_geni_serial_poll_bit(uport, SE_GENI_S_IRQ_STATUS, done = qcom_geni_serial_poll_bit(uport, SE_DMA_RX_IRQ_STAT,
S_CMD_CANCEL_EN, true); RX_EOT, true);
if (done) {
if (qcom_geni_serial_secondary_active(uport)) writel(RX_EOT | RX_DMA_DONE,
uport->membase + SE_DMA_RX_IRQ_CLR);
} else {
qcom_geni_serial_abort_rx(uport); qcom_geni_serial_abort_rx(uport);
writel(1, uport->membase + SE_DMA_RX_FSM_RST);
qcom_geni_serial_poll_bit(uport, SE_DMA_RX_IRQ_STAT,
RX_RESET_DONE, true);
writel(RX_RESET_DONE | RX_DMA_DONE,
uport->membase + SE_DMA_RX_IRQ_CLR);
}
if (port->rx_dma_addr) { if (port->rx_dma_addr) {
geni_se_rx_dma_unprep(&port->se, port->rx_dma_addr, geni_se_rx_dma_unprep(&port->se, port->rx_dma_addr,
DMA_RX_BUF_SIZE); DMA_RX_BUF_SIZE);
@ -846,7 +873,7 @@ static void qcom_geni_serial_handle_rx_dma(struct uart_port *uport, bool drop)
} }
if (!drop) if (!drop)
handle_rx_uart(uport, rx_in, drop); handle_rx_uart(uport, rx_in);
ret = geni_se_rx_dma_prep(&port->se, port->rx_buf, ret = geni_se_rx_dma_prep(&port->se, port->rx_buf,
DMA_RX_BUF_SIZE, DMA_RX_BUF_SIZE,
@ -1096,10 +1123,12 @@ static void qcom_geni_serial_shutdown(struct uart_port *uport)
{ {
disable_irq(uport->irq); disable_irq(uport->irq);
uart_port_lock_irq(uport);
qcom_geni_serial_stop_tx(uport); qcom_geni_serial_stop_tx(uport);
qcom_geni_serial_stop_rx(uport); qcom_geni_serial_stop_rx(uport);
qcom_geni_serial_cancel_tx_cmd(uport); qcom_geni_serial_cancel_tx_cmd(uport);
uart_port_unlock_irq(uport);
} }
static void qcom_geni_serial_flush_buffer(struct uart_port *uport) static void qcom_geni_serial_flush_buffer(struct uart_port *uport)
@ -1152,7 +1181,6 @@ static int qcom_geni_serial_port_setup(struct uart_port *uport)
false, true, true); false, true, true);
geni_se_init(&port->se, UART_RX_WM, port->rx_fifo_depth - 2); geni_se_init(&port->se, UART_RX_WM, port->rx_fifo_depth - 2);
geni_se_select_mode(&port->se, port->dev_data->mode); geni_se_select_mode(&port->se, port->dev_data->mode);
qcom_geni_serial_start_rx(uport);
port->setup = true; port->setup = true;
return 0; return 0;
@ -1168,6 +1196,11 @@ static int qcom_geni_serial_startup(struct uart_port *uport)
if (ret) if (ret)
return ret; return ret;
} }
uart_port_lock_irq(uport);
qcom_geni_serial_start_rx(uport);
uart_port_unlock_irq(uport);
enable_irq(uport->irq); enable_irq(uport->irq);
return 0; return 0;
@ -1253,7 +1286,6 @@ static void qcom_geni_serial_set_termios(struct uart_port *uport,
unsigned int avg_bw_core; unsigned int avg_bw_core;
unsigned long timeout; unsigned long timeout;
qcom_geni_serial_stop_rx(uport);
/* baud rate */ /* baud rate */
baud = uart_get_baud_rate(uport, termios, old, 300, 4000000); baud = uart_get_baud_rate(uport, termios, old, 300, 4000000);
@ -1269,7 +1301,7 @@ static void qcom_geni_serial_set_termios(struct uart_port *uport,
dev_err(port->se.dev, dev_err(port->se.dev,
"Couldn't find suitable clock rate for %u\n", "Couldn't find suitable clock rate for %u\n",
baud * sampling_rate); baud * sampling_rate);
goto out_restart_rx; return;
} }
dev_dbg(port->se.dev, "desired_rate = %u, clk_rate = %lu, clk_div = %u\n", dev_dbg(port->se.dev, "desired_rate = %u, clk_rate = %lu, clk_div = %u\n",
@ -1360,8 +1392,6 @@ static void qcom_geni_serial_set_termios(struct uart_port *uport,
writel(stop_bit_len, uport->membase + SE_UART_TX_STOP_BIT_LEN); writel(stop_bit_len, uport->membase + SE_UART_TX_STOP_BIT_LEN);
writel(ser_clk_cfg, uport->membase + GENI_SER_M_CLK_CFG); writel(ser_clk_cfg, uport->membase + GENI_SER_M_CLK_CFG);
writel(ser_clk_cfg, uport->membase + GENI_SER_S_CLK_CFG); writel(ser_clk_cfg, uport->membase + GENI_SER_S_CLK_CFG);
out_restart_rx:
qcom_geni_serial_start_rx(uport);
} }
#ifdef CONFIG_SERIAL_QCOM_GENI_CONSOLE #ifdef CONFIG_SERIAL_QCOM_GENI_CONSOLE
@ -1582,7 +1612,7 @@ static const struct uart_ops qcom_geni_console_pops = {
#ifdef CONFIG_CONSOLE_POLL #ifdef CONFIG_CONSOLE_POLL
.poll_get_char = qcom_geni_serial_get_char, .poll_get_char = qcom_geni_serial_get_char,
.poll_put_char = qcom_geni_serial_poll_put_char, .poll_put_char = qcom_geni_serial_poll_put_char,
.poll_init = qcom_geni_serial_port_setup, .poll_init = qcom_geni_serial_poll_init,
#endif #endif
.pm = qcom_geni_serial_pm, .pm = qcom_geni_serial_pm,
}; };
@ -1749,7 +1779,7 @@ static void qcom_geni_serial_remove(struct platform_device *pdev)
uart_remove_one_port(drv, &port->uport); uart_remove_one_port(drv, &port->uport);
} }
static int qcom_geni_serial_sys_suspend(struct device *dev) static int qcom_geni_serial_suspend(struct device *dev)
{ {
struct qcom_geni_serial_port *port = dev_get_drvdata(dev); struct qcom_geni_serial_port *port = dev_get_drvdata(dev);
struct uart_port *uport = &port->uport; struct uart_port *uport = &port->uport;
@ -1766,7 +1796,7 @@ static int qcom_geni_serial_sys_suspend(struct device *dev)
return uart_suspend_port(private_data->drv, uport); return uart_suspend_port(private_data->drv, uport);
} }
static int qcom_geni_serial_sys_resume(struct device *dev) static int qcom_geni_serial_resume(struct device *dev)
{ {
int ret; int ret;
struct qcom_geni_serial_port *port = dev_get_drvdata(dev); struct qcom_geni_serial_port *port = dev_get_drvdata(dev);
@ -1781,38 +1811,6 @@ static int qcom_geni_serial_sys_resume(struct device *dev)
return ret; return ret;
} }
static int qcom_geni_serial_sys_hib_resume(struct device *dev)
{
int ret = 0;
struct uart_port *uport;
struct qcom_geni_private_data *private_data;
struct qcom_geni_serial_port *port = dev_get_drvdata(dev);
uport = &port->uport;
private_data = uport->private_data;
if (uart_console(uport)) {
geni_icc_set_tag(&port->se, QCOM_ICC_TAG_ALWAYS);
geni_icc_set_bw(&port->se);
ret = uart_resume_port(private_data->drv, uport);
/*
* For hibernation usecase clients for
* console UART won't call port setup during restore,
* hence call port setup for console uart.
*/
qcom_geni_serial_port_setup(uport);
} else {
/*
* Peripheral register settings are lost during hibernation.
* Update setup flag such that port setup happens again
* during next session. Clients of HS-UART will close and
* open the port during hibernation.
*/
port->setup = false;
}
return ret;
}
static const struct qcom_geni_device_data qcom_geni_console_data = { static const struct qcom_geni_device_data qcom_geni_console_data = {
.console = true, .console = true,
.mode = GENI_SE_FIFO, .mode = GENI_SE_FIFO,
@ -1824,12 +1822,7 @@ static const struct qcom_geni_device_data qcom_geni_uart_data = {
}; };
static const struct dev_pm_ops qcom_geni_serial_pm_ops = { static const struct dev_pm_ops qcom_geni_serial_pm_ops = {
.suspend = pm_sleep_ptr(qcom_geni_serial_sys_suspend), SYSTEM_SLEEP_PM_OPS(qcom_geni_serial_suspend, qcom_geni_serial_resume)
.resume = pm_sleep_ptr(qcom_geni_serial_sys_resume),
.freeze = pm_sleep_ptr(qcom_geni_serial_sys_suspend),
.poweroff = pm_sleep_ptr(qcom_geni_serial_sys_suspend),
.restore = pm_sleep_ptr(qcom_geni_serial_sys_hib_resume),
.thaw = pm_sleep_ptr(qcom_geni_serial_sys_hib_resume),
}; };
static const struct of_device_id qcom_geni_serial_match_table[] = { static const struct of_device_id qcom_geni_serial_match_table[] = {

View File

@ -4726,7 +4726,7 @@ static int con_font_get(struct vc_data *vc, struct console_font_op *op)
return -EINVAL; return -EINVAL;
if (op->data) { if (op->data) {
font.data = kvmalloc(max_font_size, GFP_KERNEL); font.data = kvzalloc(max_font_size, GFP_KERNEL);
if (!font.data) if (!font.data)
return -ENOMEM; return -ENOMEM;
} else } else

View File

@ -258,8 +258,8 @@ struct geni_se {
#define RX_DMA_PARITY_ERR BIT(5) #define RX_DMA_PARITY_ERR BIT(5)
#define RX_DMA_BREAK GENMASK(8, 7) #define RX_DMA_BREAK GENMASK(8, 7)
#define RX_GENI_GP_IRQ GENMASK(10, 5) #define RX_GENI_GP_IRQ GENMASK(10, 5)
#define RX_GENI_CANCEL_IRQ BIT(11)
#define RX_GENI_GP_IRQ_EXT GENMASK(13, 12) #define RX_GENI_GP_IRQ_EXT GENMASK(13, 12)
#define RX_GENI_CANCEL_IRQ BIT(14)
/* SE_HW_PARAM_0 fields */ /* SE_HW_PARAM_0 fields */
#define TX_FIFO_WIDTH_MSK GENMASK(29, 24) #define TX_FIFO_WIDTH_MSK GENMASK(29, 24)