Linux Serial subsystem development
 help / color / mirror / Atom feed
* Re: [PATCH v2 1/2] serial: sh-sci: Avoid divide-by-zero fault
From: Hugo Villeneuve @ 2026-04-08 18:15 UTC (permalink / raw)
  To: Biju Das
  Cc: biju.das.au, Greg Kroah-Hartman, Jiri Slaby, Geert Uytterhoeven,
	Thierry Bultel, wsa+renesas, Prabhakar Mahadev Lad,
	linux-kernel@vger.kernel.org, linux-serial@vger.kernel.org,
	linux-renesas-soc@vger.kernel.org
In-Reply-To: <TYCPR01MB11332859E901171C91C543061865B2@TYCPR01MB11332.jpnprd01.prod.outlook.com>

Hi Biju,

On Wed, 8 Apr 2026 17:25:19 +0000
Biju Das <biju.das.jz@bp.renesas.com> wrote:

> Hi Hugo,
> 
> > -----Original Message-----
> > From: Hugo Villeneuve <hugo@hugovil.com>
> > Sent: 08 April 2026 17:52
> > Subject: Re: [PATCH v2 1/2] serial: sh-sci: Avoid divide-by-zero fault
> > 
> > Hi Biju,
> > 
> > On Wed, 8 Apr 2026 16:35:44 +0000
> > Biju Das <biju.das.jz@bp.renesas.com> wrote:
> > 
> > > Hi Hugo,
> > >
> > > Thanks for the feedback.
> > >
> > > > -----Original Message-----
> > > > From: Hugo Villeneuve <hugo@hugovil.com>
> > > > Sent: 08 April 2026 17:31
> > > > Subject: Re: [PATCH v2 1/2] serial: sh-sci: Avoid divide-by-zero
> > > > fault
> > > >
> > > > Hi Biju,
> > > >
> > > > On Wed,  8 Apr 2026 15:20:58 +0100
> > > > Biju <biju.das.au@gmail.com> wrote:
> > > >
> > > > > From: Biju Das <biju.das.jz@bp.renesas.com>
> > > > >
> > > > > uart_update_timeout() computes a timeout value by dividing by the
> > > > > baud rate. If baud is zero — which can occur when the hardware
> > > > > returns an unsupported or invalid rate — this results in a divide-by-zero fault.
> > > >
> > > > baud is returned by uart_get_baud_rate(), so this is not returned by the hardware?
> > >
> > > You are tight, Will update commit description.
> > 
> > How can uart_get_baud_rate() return a zero value? If I am not mistaken even for the B0 case, it will
> > return 9600?
> 
> As per the comment and code, this API can return 0.
> 
> * If the new baud rate is invalid, try the @old termios setting. If it's still
> * invalid, we try 9600 baud. If that is also invalid 0 is returned.
> 
> In drives/tty currently only 1 driver is checking the return value
> and it calls panic
> 
> https://elixir.bootlin.com/linux/v7.0-rc7/source/drivers/tty/serial/apbuart.c#L214

Hmmm, more than 1:

icom.c:
    if (!baud)
         baud = 9600;    /* B0 transition handled in rs_set_termios */

8250/8250_fintek.c:
    if (!baud)
         goto exit;

> I believe we should call panic, if baud =0, instead of proceeding.
> Geert, any thoughts??

There once was a warning, removed by:

commit 23bf72faaebdf2cb199c0ef8cf96467b10904b35
Author: Max Filippov <jcmvbkbc@gmail.com>
Date:   Tue Oct 10 01:59:22 2023 -0700
    serial: core: tidy invalid baudrate handling in uart_get_baud_rate
    ...
    Clarify that 0 can be (and always could be) returned from the
    uart_get_baud_rate. Don't issue a warning in that case.
    ...

-- 
Hugo Villeneuve

^ permalink raw reply

* RE: [PATCH v2 1/2] serial: sh-sci: Avoid divide-by-zero fault
From: Biju Das @ 2026-04-08 19:02 UTC (permalink / raw)
  To: Hugo Villeneuve
  Cc: biju.das.au, Greg Kroah-Hartman, Jiri Slaby, Geert Uytterhoeven,
	Thierry Bultel, wsa+renesas, Prabhakar Mahadev Lad,
	linux-kernel@vger.kernel.org, linux-serial@vger.kernel.org,
	linux-renesas-soc@vger.kernel.org
In-Reply-To: <20260408141515.fc210b4b3c86f7a61f680dd1@hugovil.com>

Hi Hugo,

> -----Original Message-----
> From: Hugo Villeneuve <hugo@hugovil.com>
> Sent: 08 April 2026 19:15
> Subject: Re: [PATCH v2 1/2] serial: sh-sci: Avoid divide-by-zero fault
> 
> Hi Biju,
> 
> On Wed, 8 Apr 2026 17:25:19 +0000
> Biju Das <biju.das.jz@bp.renesas.com> wrote:
> 
> > Hi Hugo,
> >
> > > -----Original Message-----
> > > From: Hugo Villeneuve <hugo@hugovil.com>
> > > Sent: 08 April 2026 17:52
> > > Subject: Re: [PATCH v2 1/2] serial: sh-sci: Avoid divide-by-zero
> > > fault
> > >
> > > Hi Biju,
> > >
> > > On Wed, 8 Apr 2026 16:35:44 +0000
> > > Biju Das <biju.das.jz@bp.renesas.com> wrote:
> > >
> > > > Hi Hugo,
> > > >
> > > > Thanks for the feedback.
> > > >
> > > > > -----Original Message-----
> > > > > From: Hugo Villeneuve <hugo@hugovil.com>
> > > > > Sent: 08 April 2026 17:31
> > > > > Subject: Re: [PATCH v2 1/2] serial: sh-sci: Avoid divide-by-zero
> > > > > fault
> > > > >
> > > > > Hi Biju,
> > > > >
> > > > > On Wed,  8 Apr 2026 15:20:58 +0100 Biju <biju.das.au@gmail.com>
> > > > > wrote:
> > > > >
> > > > > > From: Biju Das <biju.das.jz@bp.renesas.com>
> > > > > >
> > > > > > uart_update_timeout() computes a timeout value by dividing by
> > > > > > the baud rate. If baud is zero — which can occur when the
> > > > > > hardware returns an unsupported or invalid rate — this results in a divide-by-zero fault.
> > > > >
> > > > > baud is returned by uart_get_baud_rate(), so this is not returned by the hardware?
> > > >
> > > > You are tight, Will update commit description.
> > >
> > > How can uart_get_baud_rate() return a zero value? If I am not
> > > mistaken even for the B0 case, it will return 9600?
> >
> > As per the comment and code, this API can return 0.
> >
> > * If the new baud rate is invalid, try the @old termios setting. If
> > it's still
> > * invalid, we try 9600 baud. If that is also invalid 0 is returned.
> >
> > In drives/tty currently only 1 driver is checking the return value and
> > it calls panic
> >
> > https://elixir.bootlin.com/linux/v7.0-rc7/source/drivers/tty/serial/ap
> > buart.c#L214
> 
> Hmmm, more than 1:

> 
> icom.c:
>     if (!baud)
>          baud = 9600;    /* B0 transition handled in rs_set_termios */

A zero return from uart_get_baud_rate() is a normal, recoverable condition
(unsupported rate requested by userspace) and must not crash the kernel.

Or drop the check like other tty drivers, as SCIF/RSCI IP support 9600 baud rate.

Cheers,
Biju

^ permalink raw reply

* Re: [PATCH] tty: serial: pch_uart: add check for dma_alloc_coherent()
From: 俞朝阳 @ 2026-04-09  5:46 UTC (permalink / raw)
  To: Andy Shevchenko
  Cc: gregkh, jirislaby, kees, fourier.thomas, linux-serial,
	linux-kernel, gszhai, Zhaoyang Yu
In-Reply-To: <adZWm7GxCU_-T0zc@ashevche-desk.local>

Hi Andy,

Thank you for the review and the suggestions!

> Looks like it deserves a Fixes tag.

Agreed. I will find the original commit that introduced this issue and add the proper Fixes tag in the v2 patch. I'll send it out shortly.

> Also ideally this driver should be converted to one of 8250 cases.
> The latter has already DMA support. Note, if you are going this
> direction, I have the hardware to test.

Thank you very much for offering to test on the actual hardware! That is incredibly helpful. 

I agree that converting this driver to the 8250 framework is the right direction. Since that would be a larger refactoring effort, I think it might be best to apply this quick NULL pointer fix first so that the current driver is safe. 

After this fix is settled, I am very interested in taking a look at the 8250 conversion as a follow-up project. I might need some time to study the 8250 DMA framework, but I will definitely reach out with a patch series for you to test when I get there!

Best regards,
Zhaoyang Yu

^ permalink raw reply

* [PATCH v2] tty: serial: pch_uart: add check for dma_alloc_coherent()
From: Zhaoyang Yu @ 2026-04-09  5:41 UTC (permalink / raw)
  To: andriy.shevchenko
  Cc: gregkh, jirislaby, kees, fourier.thomas, linux-serial,
	linux-kernel, gszhai, 23120469, Zhaoyang Yu, stable

Add a check for dma_alloc_coherent() failure to prevent a potential
NULL pointer dereference in dma_handle_rx(). Properly release DMA
channels and the PCI device reference using a goto ladder if the
allocation fails.

Fixes: 3c6a483275f4 ("Serial: EG20T: add PCH_UART driver")
Cc: stable@vger.kernel.org
Signed-off-by: Zhaoyang Yu <2426767509@qq.com>
---
Changes in v2:
- Added the Fixes tag for the initial PCH_UART driver commit, per Andy's review.

 drivers/tty/serial/pch_uart.c | 19 +++++++++++++------
 1 file changed, 13 insertions(+), 6 deletions(-)

diff --git a/drivers/tty/serial/pch_uart.c b/drivers/tty/serial/pch_uart.c
index 6729d8e83c3c..ba1fcd663fe2 100644
--- a/drivers/tty/serial/pch_uart.c
+++ b/drivers/tty/serial/pch_uart.c
@@ -689,8 +689,7 @@ static void pch_request_dma(struct uart_port *port)
 	if (!chan) {
 		dev_err(priv->port.dev, "%s:dma_request_channel FAILS(Tx)\n",
 			__func__);
-		pci_dev_put(dma_dev);
-		return;
+		goto err_pci_get;
 	}
 	priv->chan_tx = chan;
 
@@ -704,18 +703,26 @@ static void pch_request_dma(struct uart_port *port)
 	if (!chan) {
 		dev_err(priv->port.dev, "%s:dma_request_channel FAILS(Rx)\n",
 			__func__);
-		dma_release_channel(priv->chan_tx);
-		priv->chan_tx = NULL;
-		pci_dev_put(dma_dev);
-		return;
+		goto err_req_tx;
 	}
 
 	/* Get Consistent memory for DMA */
 	priv->rx_buf_virt = dma_alloc_coherent(port->dev, port->fifosize,
 				    &priv->rx_buf_dma, GFP_KERNEL);
+	if (!priv->rx_buf_virt)
+		goto err_req_rx;
 	priv->chan_rx = chan;
 
 	pci_dev_put(dma_dev);
+	return;
+
+err_req_rx:
+	dma_release_channel(chan);
+err_req_tx:
+	dma_release_channel(priv->chan_tx);
+	priv->chan_tx = NULL;
+err_pci_get:
+	pci_dev_put(dma_dev);
 }
 
 static void pch_dma_rx_complete(void *arg)
-- 
2.50.1


^ permalink raw reply related

* RE: [PATCH v2 1/2] serial: sh-sci: Avoid divide-by-zero fault
From: Biju Das @ 2026-04-09  7:40 UTC (permalink / raw)
  To: Hugo Villeneuve
  Cc: biju.das.au, Greg Kroah-Hartman, Jiri Slaby, Geert Uytterhoeven,
	Thierry Bultel, wsa+renesas, Prabhakar Mahadev Lad,
	linux-kernel@vger.kernel.org, linux-serial@vger.kernel.org,
	linux-renesas-soc@vger.kernel.org
In-Reply-To: <TYCPR01MB113326DDA1FC854689CE34A6C865B2@TYCPR01MB11332.jpnprd01.prod.outlook.com>



> -----Original Message-----
> From: Biju Das
> Sent: 08 April 2026 20:02
> To: Hugo Villeneuve <hugo@hugovil.com>
> Cc: biju.das.au <biju.das.au@gmail.com>; Greg Kroah-Hartman <gregkh@linuxfoundation.org>; Jiri Slaby
> <jirislaby@kernel.org>; Geert Uytterhoeven <geert+renesas@glider.be>; Thierry Bultel
> <thierry.bultel.yh@bp.renesas.com>; wsa+renesas <wsa+renesas@sang-engineering.com>; Prabhakar Mahadev
> Lad <prabhakar.mahadev-lad.rj@bp.renesas.com>; linux-kernel@vger.kernel.org; linux-
> serial@vger.kernel.org; linux-renesas-soc@vger.kernel.org
> Subject: RE: [PATCH v2 1/2] serial: sh-sci: Avoid divide-by-zero fault
> 
> Hi Hugo,
> 
> > -----Original Message-----
> > From: Hugo Villeneuve <hugo@hugovil.com>
> > Sent: 08 April 2026 19:15
> > Subject: Re: [PATCH v2 1/2] serial: sh-sci: Avoid divide-by-zero fault
> >
> > Hi Biju,
> >
> > On Wed, 8 Apr 2026 17:25:19 +0000
> > Biju Das <biju.das.jz@bp.renesas.com> wrote:
> >
> > > Hi Hugo,
> > >
> > > > -----Original Message-----
> > > > From: Hugo Villeneuve <hugo@hugovil.com>
> > > > Sent: 08 April 2026 17:52
> > > > Subject: Re: [PATCH v2 1/2] serial: sh-sci: Avoid divide-by-zero
> > > > fault
> > > >
> > > > Hi Biju,
> > > >
> > > > On Wed, 8 Apr 2026 16:35:44 +0000
> > > > Biju Das <biju.das.jz@bp.renesas.com> wrote:
> > > >
> > > > > Hi Hugo,
> > > > >
> > > > > Thanks for the feedback.
> > > > >
> > > > > > -----Original Message-----
> > > > > > From: Hugo Villeneuve <hugo@hugovil.com>
> > > > > > Sent: 08 April 2026 17:31
> > > > > > Subject: Re: [PATCH v2 1/2] serial: sh-sci: Avoid
> > > > > > divide-by-zero fault
> > > > > >
> > > > > > Hi Biju,
> > > > > >
> > > > > > On Wed,  8 Apr 2026 15:20:58 +0100 Biju
> > > > > > <biju.das.au@gmail.com>
> > > > > > wrote:
> > > > > >
> > > > > > > From: Biju Das <biju.das.jz@bp.renesas.com>
> > > > > > >
> > > > > > > uart_update_timeout() computes a timeout value by dividing
> > > > > > > by the baud rate. If baud is zero — which can occur when the
> > > > > > > hardware returns an unsupported or invalid rate — this results in a divide-by-zero fault.
> > > > > >
> > > > > > baud is returned by uart_get_baud_rate(), so this is not returned by the hardware?
> > > > >
> > > > > You are tight, Will update commit description.
> > > >
> > > > How can uart_get_baud_rate() return a zero value? If I am not
> > > > mistaken even for the B0 case, it will return 9600?
> > >
> > > As per the comment and code, this API can return 0.
> > >
> > > * If the new baud rate is invalid, try the @old termios setting. If
> > > it's still
> > > * invalid, we try 9600 baud. If that is also invalid 0 is returned.
> > >
> > > In drives/tty currently only 1 driver is checking the return value
> > > and it calls panic
> > >
> > > https://elixir.bootlin.com/linux/v7.0-rc7/source/drivers/tty/serial/
> > > ap
> > > buart.c#L214
> >
> > Hmmm, more than 1:
> 
> >
> > icom.c:
> >     if (!baud)
> >          baud = 9600;    /* B0 transition handled in rs_set_termios */
> 
> A zero return from uart_get_baud_rate() is a normal, recoverable condition (unsupported rate requested
> by userspace) and must not crash the kernel.
> 
> Or drop the check like other tty drivers, as SCIF/RSCI IP support 9600 baud rate.

May be setting a buadrate 115200 is safe in this cas like earlyprintk??
I will send next version setting buad = 115200, if uart_get_baud_rate() returns 0.

Cheers,
Biju

^ permalink raw reply

* Re: [PATCH v2 1/2] serial: sh-sci: Avoid divide-by-zero fault
From: Hugo Villeneuve @ 2026-04-09 14:16 UTC (permalink / raw)
  To: Biju Das
  Cc: biju.das.au, Greg Kroah-Hartman, Jiri Slaby, Geert Uytterhoeven,
	Thierry Bultel, wsa+renesas, Prabhakar Mahadev Lad,
	linux-kernel@vger.kernel.org, linux-serial@vger.kernel.org,
	linux-renesas-soc@vger.kernel.org
In-Reply-To: <TYCPR01MB113322203884106612468FB4286582@TYCPR01MB11332.jpnprd01.prod.outlook.com>

Hi Biju,

On Thu, 9 Apr 2026 07:40:02 +0000
Biju Das <biju.das.jz@bp.renesas.com> wrote:

> 
> 
> > -----Original Message-----
> > From: Biju Das
> > Sent: 08 April 2026 20:02
> > To: Hugo Villeneuve <hugo@hugovil.com>
> > Cc: biju.das.au <biju.das.au@gmail.com>; Greg Kroah-Hartman <gregkh@linuxfoundation.org>; Jiri Slaby
> > <jirislaby@kernel.org>; Geert Uytterhoeven <geert+renesas@glider.be>; Thierry Bultel
> > <thierry.bultel.yh@bp.renesas.com>; wsa+renesas <wsa+renesas@sang-engineering.com>; Prabhakar Mahadev
> > Lad <prabhakar.mahadev-lad.rj@bp.renesas.com>; linux-kernel@vger.kernel.org; linux-
> > serial@vger.kernel.org; linux-renesas-soc@vger.kernel.org
> > Subject: RE: [PATCH v2 1/2] serial: sh-sci: Avoid divide-by-zero fault
> > 
> > Hi Hugo,
> > 
> > > -----Original Message-----
> > > From: Hugo Villeneuve <hugo@hugovil.com>
> > > Sent: 08 April 2026 19:15
> > > Subject: Re: [PATCH v2 1/2] serial: sh-sci: Avoid divide-by-zero fault
> > >
> > > Hi Biju,
> > >
> > > On Wed, 8 Apr 2026 17:25:19 +0000
> > > Biju Das <biju.das.jz@bp.renesas.com> wrote:
> > >
> > > > Hi Hugo,
> > > >
> > > > > -----Original Message-----
> > > > > From: Hugo Villeneuve <hugo@hugovil.com>
> > > > > Sent: 08 April 2026 17:52
> > > > > Subject: Re: [PATCH v2 1/2] serial: sh-sci: Avoid divide-by-zero
> > > > > fault
> > > > >
> > > > > Hi Biju,
> > > > >
> > > > > On Wed, 8 Apr 2026 16:35:44 +0000
> > > > > Biju Das <biju.das.jz@bp.renesas.com> wrote:
> > > > >
> > > > > > Hi Hugo,
> > > > > >
> > > > > > Thanks for the feedback.
> > > > > >
> > > > > > > -----Original Message-----
> > > > > > > From: Hugo Villeneuve <hugo@hugovil.com>
> > > > > > > Sent: 08 April 2026 17:31
> > > > > > > Subject: Re: [PATCH v2 1/2] serial: sh-sci: Avoid
> > > > > > > divide-by-zero fault
> > > > > > >
> > > > > > > Hi Biju,
> > > > > > >
> > > > > > > On Wed,  8 Apr 2026 15:20:58 +0100 Biju
> > > > > > > <biju.das.au@gmail.com>
> > > > > > > wrote:
> > > > > > >
> > > > > > > > From: Biju Das <biju.das.jz@bp.renesas.com>
> > > > > > > >
> > > > > > > > uart_update_timeout() computes a timeout value by dividing
> > > > > > > > by the baud rate. If baud is zero — which can occur when the
> > > > > > > > hardware returns an unsupported or invalid rate — this results in a divide-by-zero fault.
> > > > > > >
> > > > > > > baud is returned by uart_get_baud_rate(), so this is not returned by the hardware?
> > > > > >
> > > > > > You are tight, Will update commit description.
> > > > >
> > > > > How can uart_get_baud_rate() return a zero value? If I am not
> > > > > mistaken even for the B0 case, it will return 9600?
> > > >
> > > > As per the comment and code, this API can return 0.
> > > >
> > > > * If the new baud rate is invalid, try the @old termios setting. If
> > > > it's still
> > > > * invalid, we try 9600 baud. If that is also invalid 0 is returned.
> > > >
> > > > In drives/tty currently only 1 driver is checking the return value
> > > > and it calls panic
> > > >
> > > > https://elixir.bootlin.com/linux/v7.0-rc7/source/drivers/tty/serial/
> > > > ap
> > > > buart.c#L214
> > >
> > > Hmmm, more than 1:
> > 
> > >
> > > icom.c:
> > >     if (!baud)
> > >          baud = 9600;    /* B0 transition handled in rs_set_termios */
> > 
> > A zero return from uart_get_baud_rate() is a normal, recoverable condition (unsupported rate requested
> > by userspace) and must not crash the kernel.
> > 
> > Or drop the check like other tty drivers, as SCIF/RSCI IP support 9600 baud rate.
> 
> May be setting a buadrate 115200 is safe in this cas like earlyprintk??
> I will send next version setting buad = 115200, if uart_get_baud_rate() returns 0.

Is it logical to proceed with configuration if it returned zero?

Also we still pass 0 as a minimum value in uart_get_baud_rate(), so
that a baud rate of 75, for example, would be valid, but would trigger a
fault later (division by zero). Wouldn't it be a good idea to also set a
proper minimum baud rate?

-- 
Hugo Villeneuve

^ permalink raw reply

* Re: [PATCH] tty: ipwireless: fix memory leak in do_go_offline()
From: Qingfang Deng @ 2026-04-10  4:01 UTC (permalink / raw)
  To: dsterba
  Cc: Jiri Kosina, David Sterba, Greg Kroah-Hartman, Jiri Slaby,
	Stephen Blackheath, Ben Martel, linux-kernel, linux-serial
In-Reply-To: <20260312120036.GF5735@suse.cz>

Hi, Greg and David,

On Thu, Mar 12, 2026 at 8:00 PM David Sterba <dsterba@suse.cz> wrote:
>
> Thanks, but the driver is going to be deleted.

Even if this is being removed from tty-next, the fix should be applied
and backported to -stable.

Regards,
Qingfang

^ permalink raw reply

* Re: [PATCH] tty: ipwireless: fix memory leak in do_go_offline()
From: Greg Kroah-Hartman @ 2026-04-10  4:54 UTC (permalink / raw)
  To: Qingfang Deng
  Cc: dsterba, Jiri Kosina, David Sterba, Jiri Slaby,
	Stephen Blackheath, Ben Martel, linux-kernel, linux-serial
In-Reply-To: <CALW65jY1qjiLNd_viqgiXodwGe1VzQx_qKUU2rRHMUp8TK7j_g@mail.gmail.com>

On Fri, Apr 10, 2026 at 12:01:23PM +0800, Qingfang Deng wrote:
> Hi, Greg and David,
> 
> On Thu, Mar 12, 2026 at 8:00 PM David Sterba <dsterba@suse.cz> wrote:
> >
> > Thanks, but the driver is going to be deleted.
> 
> Even if this is being removed from tty-next, the fix should be applied
> and backported to -stable.

We can just delete it from stable trees, as obviously no one is using
it.

thanks,

greg k-h

^ permalink raw reply

* [PATCH RFC] vt: tty: use krefs to fix a potential UAF between kbd_keycode and con_shutdown
From: Wentao Guan @ 2026-04-10  6:55 UTC (permalink / raw)
  To: syzbot+098cefc0911c68db5dab
  Cc: gregkh, jirislaby, linux-kernel, linux-serial, syzkaller-bugs,
	Wentao Guan, stable, syzbot+702b7f311487703dbb18
In-Reply-To: <69d56a91.050a0220.28fc4.0003.GAE@google.com>

syzbot report an KASAN: slab-use-after-free Read in kbd_event (2),
which allocated by alloc_tty_struct->tty_init_dev, accessed by kbd_keycode,
released by tty_release_struct:
tty_release_struct->release_tty->tty->ops->shutdown->con_shutdown.
accessed by
kbd_keycode drivers/tty/vt/keyboard.c:1435
kbd_event+0x3330/0x40d0 drivers/tty/vt/keyboard.c:1515

Use tty_port_tty_get to get a tty ref in kbd_keycode to prevent the UAF,
tty_release_struct use console_lock not protect access tty_struct from
kbd_keycode or another function, so convert it to tty_port_tty_set in
con_install, con_shutdown.

The change is similar as
commit 4a90f09b20f4622dcbff1f0e1e6bae1704f8ad8c ("tty: usb-serial krefs").

Maybe reproduce in:
CPU A		CPU B		CPU C
open /dev/tty
		close /dev/tty
				tty!=NULL
		release_tty()
				access tty_struct

Cc: stable@kernel.org
Reported-by: syzbot+098cefc0911c68db5dab@syzkaller.appspotmail.com
Closes: https://syzbot.org/bug?extid=098cefc0911c68db5dab
Reported-by: syzbot+702b7f311487703dbb18@syzkaller.appspotmail.com
Closes: https://syzbot.org/bug?extid=702b7f311487703dbb18
Signed-off-by: Wentao Guan <guanwentao@uniontech.com>
---
 drivers/tty/vt/keyboard.c | 20 +++++++++++++-------
 drivers/tty/vt/vt.c       |  5 ++---
 2 files changed, 15 insertions(+), 10 deletions(-)

diff --git a/drivers/tty/vt/keyboard.c b/drivers/tty/vt/keyboard.c
index 13bc048f45e86..173c447525ff8 100644
--- a/drivers/tty/vt/keyboard.c
+++ b/drivers/tty/vt/keyboard.c
@@ -517,6 +517,10 @@ static void fn_hold(struct vc_data *vc)
 	 * Note: SCROLLOCK will be set (cleared) by stop_tty (start_tty);
 	 * these routines are also activated by ^S/^Q.
 	 * (And SCROLLOCK can also be set by the ioctl KDSKBLED.)
+	 *
+	 * kbd_keycode(), only from kbd_keycode via k_handler[], already holds a
+	 * reference to the tty via tty_port_tty_get(), so we can safely
+	 * access port->tty here without an extra kref.
 	 */
 	if (tty->flow.stopped)
 		start_tty(tty);
@@ -1378,7 +1382,7 @@ static void kbd_keycode(unsigned int keycode, int down, bool hw_raw)
 	struct keyboard_notifier_param param = { .vc = vc, .value = keycode, .down = down };
 	int rc;
 
-	tty = vc->port.tty;
+	tty = tty_port_tty_get(&vc->port);
 
 	if (tty && (!tty->driver_data)) {
 		/* No driver data? Strange. Okay we fix it then. */
@@ -1438,7 +1442,7 @@ static void kbd_keycode(unsigned int keycode, int down, bool hw_raw)
 		 * characters get aren't echoed locally. This makes key repeat
 		 * usable with slow applications and under heavy loads.
 		 */
-		return;
+		goto out;
 	}
 
 	param.shift = shift_final = (shift_state | kbd->slockstate) ^ kbd->lockstate;
@@ -1452,7 +1456,7 @@ static void kbd_keycode(unsigned int keycode, int down, bool hw_raw)
 					   KBD_UNBOUND_KEYCODE, &param);
 		do_compute_shiftstate();
 		kbd->slockstate = 0;
-		return;
+		goto out;
 	}
 
 	if (keycode < NR_KEYS)
@@ -1460,7 +1464,7 @@ static void kbd_keycode(unsigned int keycode, int down, bool hw_raw)
 	else if (keycode >= KEY_BRL_DOT1 && keycode <= KEY_BRL_DOT8)
 		keysym = U(K(KT_BRL, keycode - KEY_BRL_DOT1 + 1));
 	else
-		return;
+		goto out;
 
 	type = KTYP(keysym);
 
@@ -1471,7 +1475,7 @@ static void kbd_keycode(unsigned int keycode, int down, bool hw_raw)
 		if (rc != NOTIFY_STOP)
 			if (down && !(raw_mode || kbd->kbdmode == VC_OFF))
 				k_unicode(vc, keysym, !down);
-		return;
+		goto out;
 	}
 
 	type -= 0xf0;
@@ -1489,10 +1493,10 @@ static void kbd_keycode(unsigned int keycode, int down, bool hw_raw)
 	rc = atomic_notifier_call_chain(&keyboard_notifier_list,
 					KBD_KEYSYM, &param);
 	if (rc == NOTIFY_STOP)
-		return;
+		goto out;
 
 	if ((raw_mode || kbd->kbdmode == VC_OFF) && type != KT_SPEC && type != KT_SHIFT)
-		return;
+		goto out;
 
 	(*k_handler[type])(vc, KVAL(keysym), !down);
 
@@ -1501,6 +1505,8 @@ static void kbd_keycode(unsigned int keycode, int down, bool hw_raw)
 
 	if (type != KT_SLOCK)
 		kbd->slockstate = 0;
+out:
+	tty_kref_put(tty);
 }
 
 static void kbd_event(struct input_handle *handle, unsigned int event_type,
diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c
index e2df99e3d4580..acded112cae2b 100644
--- a/drivers/tty/vt/vt.c
+++ b/drivers/tty/vt/vt.c
@@ -3661,7 +3661,7 @@ static int con_install(struct tty_driver *driver, struct tty_struct *tty)
 		return ret;
 
 	tty->driver_data = vc;
-	vc->port.tty = tty;
+	tty_port_tty_set(&vc->port, tty);
 	tty_port_get(&vc->port);
 
 	if (!tty->winsize.ws_row && !tty->winsize.ws_col) {
@@ -3693,8 +3693,7 @@ static void con_shutdown(struct tty_struct *tty)
 	struct vc_data *vc = tty->driver_data;
 	BUG_ON(vc == NULL);
 
-	guard(console_lock)();
-	vc->port.tty = NULL;
+	tty_port_tty_set(&vc->port, NULL);
 }
 
 static void con_cleanup(struct tty_struct *tty)
-- 
2.30.2


^ permalink raw reply related

* [PATCH] serial: sh-sci: fix memory region release in error path
From: zenghongling @ 2026-04-10  9:21 UTC (permalink / raw)
  To: gregkh, jirislaby, geert+renesas, biju.das.jz, wsa+renesas,
	thierry.bultel.yh, prabhakar.mahadev-lad.rj
  Cc: linux-kernel, linux-serial, zhongling0719, zenghongling,
	kernel test robot

The sci_request_port() function uses request_mem_region() to reserve
I/O memory, but in the error path when sci_remap_port() fails, it
incorrectly calls release_resource() instead of release_mem_region().

This mismatch can cause resource accounting issues. Fix it by using
the correct release function, consistent with sci_release_port().

Reported-by: kernel test robot <lkp@intel.com>
Signed-off-by: zenghongling <zenghongling@kylinos.cn>
---
 drivers/tty/serial/sh-sci.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c
index bd7486315338..9e619db27237 100644
--- a/drivers/tty/serial/sh-sci.c
+++ b/drivers/tty/serial/sh-sci.c
@@ -3024,7 +3024,7 @@ int sci_request_port(struct uart_port *port)
 
 	ret = sci_remap_port(port);
 	if (unlikely(ret != 0)) {
-		release_resource(res);
+		release_mem_region(port->mapbase, sport->reg_size);
 		return ret;
 	}
 
-- 
2.25.1


^ permalink raw reply related

* Re: [PATCH] serial: sh-sci: fix memory region release in error path
From: Greg KH @ 2026-04-10 10:46 UTC (permalink / raw)
  To: zenghongling
  Cc: jirislaby, geert+renesas, biju.das.jz, wsa+renesas,
	thierry.bultel.yh, prabhakar.mahadev-lad.rj, linux-kernel,
	linux-serial, zhongling0719, kernel test robot
In-Reply-To: <20260410092143.30971-1-zenghongling@kylinos.cn>

On Fri, Apr 10, 2026 at 05:21:43PM +0800, zenghongling wrote:
> The sci_request_port() function uses request_mem_region() to reserve
> I/O memory, but in the error path when sci_remap_port() fails, it
> incorrectly calls release_resource() instead of release_mem_region().
> 
> This mismatch can cause resource accounting issues. Fix it by using
> the correct release function, consistent with sci_release_port().
> 
> Reported-by: kernel test robot <lkp@intel.com>

The kernel test robot reported this?  Where is that report?

> Signed-off-by: zenghongling <zenghongling@kylinos.cn>

Can you use your name please, not your email alias.

thanks,

greg k-h

^ permalink raw reply

* [PATCH tty v2 0/2] 8250: Add console flow control
From: John Ogness @ 2026-04-10 14:48 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Jiri Slaby
  Cc: linux-kernel, Ilpo Järvinen, Andy Shevchenko, linux-serial,
	Ingo Molnar, Thomas Gleixner, Osama Abdelkader, Andy Shevchenko,
	Jiayuan Chen, Gerhard Engleder, Dr. David Alan Gilbert,
	Joseph Tilahun

Hi,

This is v2 of a series to implement console flow control for the
8250 serial driver. v1 is here [0].

The 8250 driver already has code in place to support console flow
control. However, there is no way to activate it and it is
incomplete. This series provides the necessary missing pieces while
attempting to be as conservative as possible, so as not to introduce
any side effects into the many 8250 variants or other non-8250 serial
drivers.

Changes since v1:

- Prepend a patch to perform an extra LSR wait after CTS assertion if
  the initial LSR wait timed out.

- Close a window in serial8250_register_8250_port() where console
  flow control was briefly disabled.

- Add port lock synchronization to the port->status RMW update in
  uart_set_options().

John Ogness

[0] https://lore.kernel.org/lkml/20260331141502.6233-1-john.ogness@linutronix.de

John Ogness (2):
  serial: 8250: Check LSR timeout on console flow control
  serial: 8250: Add support for console hardware flow control

 drivers/tty/serial/8250/8250_core.c |  6 +++++-
 drivers/tty/serial/8250/8250_port.c | 22 ++++++++++++++++++----
 drivers/tty/serial/serial_core.c    | 21 ++++++++++++++++++++-
 include/linux/serial_core.h         |  8 ++++++++
 4 files changed, 51 insertions(+), 6 deletions(-)


base-commit: a1a81aef99e853dec84241d701fbf587d713eb5b
-- 
2.47.3


^ permalink raw reply

* [PATCH tty v2 1/2] serial: 8250: Check LSR timeout on console flow control
From: John Ogness @ 2026-04-10 14:48 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Jiri Slaby
  Cc: linux-kernel, Ilpo Järvinen, Andy Shevchenko, linux-serial
In-Reply-To: <20260410144949.16581-1-john.ogness@linutronix.de>

wait_for_xmitr() calls wait_for_lsr() to wait for the transmission
registers to be empty. wait_for_lsr() can timeout after a reasonable
amount of time.

When console flow control is active (UPF_CONS_FLOW), wait_for_xmitr()
additionally polls CTS, waiting for the peer to signal that it is
ready to receive more data.

If hardware flow control is enabled (auto CTS) and the peer deasserts
CTS, wait_for_lsr() will timeout. If additionally console flow
control is active and while polling CTS the peer asserts CTS, the
console will assume it can immediately transmit, even though the
transmission registers may not be empty. This can lead to data loss.

Avoid this problem by performing an extra wait_for_lsr() upon CTS
assertion if wait_for_lsr() previously timed out.

Signed-off-by: John Ogness <john.ogness@linutronix.de>
---
 drivers/tty/serial/8250/8250_port.c | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c
index af78cc02f38e7..a739350e634f9 100644
--- a/drivers/tty/serial/8250/8250_port.c
+++ b/drivers/tty/serial/8250/8250_port.c
@@ -1984,16 +1984,20 @@ static bool wait_for_lsr(struct uart_8250_port *up, int bits)
 static void wait_for_xmitr(struct uart_8250_port *up, int bits)
 {
 	unsigned int tmout;
+	bool tx_ready;
 
-	wait_for_lsr(up, bits);
+	tx_ready = wait_for_lsr(up, bits);
 
 	/* Wait up to 1s for flow control if necessary */
 	if (up->port.flags & UPF_CONS_FLOW) {
 		for (tmout = 1000000; tmout; tmout--) {
 			unsigned int msr = serial_in(up, UART_MSR);
 			up->msr_saved_flags |= msr & MSR_SAVE_FLAGS;
-			if (msr & UART_MSR_CTS)
+			if (msr & UART_MSR_CTS) {
+				if (!tx_ready)
+					wait_for_lsr(up, bits);
 				break;
+			}
 			udelay(1);
 			touch_nmi_watchdog();
 		}
-- 
2.47.3


^ permalink raw reply related

* [PATCH tty v2 2/2] serial: 8250: Add support for console hardware flow control
From: John Ogness @ 2026-04-10 14:48 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Jiri Slaby
  Cc: linux-kernel, Ilpo Järvinen, Ingo Molnar, Thomas Gleixner,
	Osama Abdelkader, Andy Shevchenko, Jiayuan Chen, Gerhard Engleder,
	Dr. David Alan Gilbert, Joseph Tilahun, linux-serial
In-Reply-To: <20260410144949.16581-1-john.ogness@linutronix.de>

The kernel documentation specifies that the console option 'r' can
be used to enable hardware flow control for console writes. The 8250
driver does include code for hardware flow control on the console if
the UPF_CONS_FLOW flag is set, but there is no code path that sets
this flag. However, that is not the only issue. The problems are:

1. Specifying the console option 'r' does not lead to UPF_CONS_FLOW
   being set.

2. Even if UPF_CONS_FLOW would be set, serial8250_register_8250_port()
   clears it.

3. When the console option 'r' is specified, uart_set_options()
   attempts to initialize the port for CRTSCTS. However, afterwards
   it does not set the UPSTAT_CTS_ENABLE status bit and therefore on
   boot, uart_cts_enabled() is always false. This policy bit is
   important for console drivers as a criteria if they may poll CTS.

4. Even though uart_set_options() attempts to initialize the port
   for CRTSCTS, the 8250 set_termios() callback does not enable the
   RTS signal (TIOCM_RTS) and thus the hardware is not properly
   initialized for CTS polling.

5. Even if modem control was properly setup for CTS polling
   (TIOCM_RTS), uart_configure_port() clears TIOCM_RTS, thus
   breaking CTS polling.

6. wait_for_xmitr() and serial8250_console_write() use the
   UPF_CONS_FLOW bit to decide if CTS polling should occur. However,
   the condition should also include a check that it is not in RS485
   mode and CRTSCTS is actually enabled in the hardware.

Address all these issues as conservatively as possible by gating them
behind checks focussed on the user specifying console hardware flow
control support and the hardware being configured for CTS polling
at the time of the write to the UART.

Since checking the UPSTAT_CTS_ENABLE status bit is a part of the new
condition gate, these changes also support runtime termios updates to
disable/enable CRTSCTS.

Signed-off-by: John Ogness <john.ogness@linutronix.de>
---
 drivers/tty/serial/8250/8250_core.c |  6 +++++-
 drivers/tty/serial/8250/8250_port.c | 14 ++++++++++++--
 drivers/tty/serial/serial_core.c    | 21 ++++++++++++++++++++-
 include/linux/serial_core.h         |  8 ++++++++
 4 files changed, 45 insertions(+), 4 deletions(-)

diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c
index a428e88938eb7..ff4c9972d4576 100644
--- a/drivers/tty/serial/8250/8250_core.c
+++ b/drivers/tty/serial/8250/8250_core.c
@@ -693,6 +693,7 @@ static void serial_8250_overrun_backoff_work(struct work_struct *work)
 int serial8250_register_8250_port(const struct uart_8250_port *up)
 {
 	struct uart_8250_port *uart;
+	upf_t console_hwflow;
 	int ret;
 
 	if (up->port.uartclk == 0)
@@ -716,6 +717,9 @@ int serial8250_register_8250_port(const struct uart_8250_port *up)
 	if (uart->port.type == PORT_8250_CIR)
 		return -ENODEV;
 
+	/* Preserve specified console hardware flow control. */
+	console_hwflow = uart->port.flags & UPF_CONS_FLOW;
+
 	if (uart->port.dev)
 		uart_remove_one_port(&serial8250_reg, &uart->port);
 
@@ -729,7 +733,7 @@ int serial8250_register_8250_port(const struct uart_8250_port *up)
 	uart->port.fifosize     = up->port.fifosize;
 	uart->port.regshift     = up->port.regshift;
 	uart->port.iotype       = up->port.iotype;
-	uart->port.flags        = up->port.flags | UPF_BOOT_AUTOCONF;
+	uart->port.flags        = up->port.flags | UPF_BOOT_AUTOCONF | console_hwflow;
 	uart->bugs		= up->bugs;
 	uart->port.mapbase      = up->port.mapbase;
 	uart->port.mapsize      = up->port.mapsize;
diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c
index a739350e634f9..6c8830943b0c0 100644
--- a/drivers/tty/serial/8250/8250_port.c
+++ b/drivers/tty/serial/8250/8250_port.c
@@ -1989,7 +1989,7 @@ static void wait_for_xmitr(struct uart_8250_port *up, int bits)
 	tx_ready = wait_for_lsr(up, bits);
 
 	/* Wait up to 1s for flow control if necessary */
-	if (up->port.flags & UPF_CONS_FLOW) {
+	if (uart_console_hwflow_active(&up->port)) {
 		for (tmout = 1000000; tmout; tmout--) {
 			unsigned int msr = serial_in(up, UART_MSR);
 			up->msr_saved_flags |= msr & MSR_SAVE_FLAGS;
@@ -2786,6 +2786,12 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios,
 		serial8250_set_efr(port, termios);
 		serial8250_set_divisor(port, baud, quot, frac);
 		serial8250_set_fcr(port, termios);
+		/* Consoles manually poll CTS for hardware flow control. */
+		if (uart_console(port) &&
+		    !(port->rs485.flags & SER_RS485_ENABLED)
+		    && termios->c_cflag & CRTSCTS) {
+			port->mctrl |= TIOCM_RTS;
+		}
 		serial8250_set_mctrl(port, port->mctrl);
 	}
 
@@ -3355,7 +3361,7 @@ void serial8250_console_write(struct uart_8250_port *up, const char *s,
 		 * it regardless of the CTS state. Therefore, only use fifo
 		 * if we don't use control flow.
 		 */
-		!(up->port.flags & UPF_CONS_FLOW);
+		!uart_console_hwflow_active(&up->port);
 
 	if (likely(use_fifo))
 		serial8250_console_fifo_write(up, s, count);
@@ -3425,6 +3431,10 @@ int serial8250_console_setup(struct uart_port *port, char *options, bool probe)
 	if (ret)
 		return ret;
 
+	/* Allow user-specified hardware flow control. */
+	if (flow == 'r')
+		port->flags |= UPF_CONS_FLOW;
+
 	if (port->dev)
 		pm_runtime_get_sync(port->dev);
 
diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c
index 89cebdd278410..a9ea10df4fb8b 100644
--- a/drivers/tty/serial/serial_core.c
+++ b/drivers/tty/serial/serial_core.c
@@ -2235,6 +2235,18 @@ uart_set_options(struct uart_port *port, struct console *co,
 	port->mctrl |= TIOCM_DTR;
 
 	port->ops->set_termios(port, &termios, &dummy);
+
+	/*
+	 * If console hardware flow control was specified and is supported,
+	 * the related policy UPSTAT_CTS_ENABLE must be set to allow console
+	 * drivers to identify if CTS should be used for polling.
+	 */
+	if (flow == 'r' && (termios.c_cflag & CRTSCTS)) {
+		/* Synchronize @status RMW update against the console. */
+		guard(uart_port_lock_irq)(port);
+		port->status |= UPSTAT_CTS_ENABLE;
+	}
+
 	/*
 	 * Allow the setting of the UART parameters with a NULL console
 	 * too:
@@ -2541,7 +2553,14 @@ uart_configure_port(struct uart_driver *drv, struct uart_state *state,
 		 * We probably don't need a spinlock around this, but
 		 */
 		scoped_guard(uart_port_lock_irqsave, port) {
-			port->mctrl &= TIOCM_DTR;
+			unsigned int mask = TIOCM_DTR;
+
+			/* Console hardware flow control polls CTS. */
+			if (uart_console_hwflow_active(port))
+				mask |= TIOCM_RTS;
+
+			port->mctrl &= mask;
+
 			if (!(port->rs485.flags & SER_RS485_ENABLED))
 				port->ops->set_mctrl(port, port->mctrl);
 		}
diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h
index 666430b478997..07bd3bd6c8355 100644
--- a/include/linux/serial_core.h
+++ b/include/linux/serial_core.h
@@ -1163,6 +1163,14 @@ static inline bool uart_softcts_mode(struct uart_port *uport)
 	return ((uport->status & mask) == UPSTAT_CTS_ENABLE);
 }
 
+static inline bool uart_console_hwflow_active(struct uart_port *uport)
+{
+	return uart_console(uport) &&
+	       !(uport->rs485.flags & SER_RS485_ENABLED) &&
+	       (uport->flags & UPF_CONS_FLOW) &&
+	       uart_cts_enabled(uport);
+}
+
 /*
  * The following are helper functions for the low level drivers.
  */
-- 
2.47.3


^ permalink raw reply related

* [PATCH 0/5] serial: core: improve safety of uart_get_baud_rate()
From: Hugo Villeneuve @ 2026-04-10 15:20 UTC (permalink / raw)
  To: gregkh, jirislaby
  Cc: hugo, biju.das.jz, linux-kernel, linux-serial, Hugo Villeneuve

From: Hugo Villeneuve <hvilleneuve@dimonoff.com>

serial: core: improve safety of uart_get_baud_rate()

Hello,
this patch series brings some improvements to uart_get_baud_rate() by
making it safer.

This originate from a review and discussions of a patch by Biju Das [1] to
fix a potential division by zero bug in the Renesas serial drivers
sh-sci and rsci.

First the comments in uart_get_baud_rate() are extremely outdated and no
longer match what the code does, which does not help when trying to fully
understand what the function does. One of the patch in this series updates
them.

All drivers, except apbuart and icom, call uart_get_baud_rate() and do not
check the returned baud rate, because it is assumed that it cannot return a
zero value. However, not all devices support a speed of 9600, and for these
uart_get_baud_rate() could return zero for the B0 case. This in turn
could trigger a "Division by zero in kernel" fault.

To be clear, this doesn't originate from a crash report, but from analyzing
the code during the above mentioned review. To test this potential bug, I
simply modified the "min" baud rate passed as argument to
uart_get_baud_rate() to 19200 for the sc16is7xx driver, and was able to
trigger the fault with "stty /dev/ttySC0 0" set to a zero baud rate
(hang up).

Historically, there was commit 16ae2a877bf4 ("serial: Fix crash if the minimum rate of the device is > 9600 baud")
in 2010 to try to prevent a zero value being returned, and added a warning
to that effect.

Then commit 23bf72faaebd ("serial: core: tidy invalid baudrate handling in uart_get_baud_rate")
in 2023 removed the warning:

  uart_get_baud_rate has input parameters 'min' and 'max' limiting the
  range of acceptable baud rates from the caller's perspective. If neither
  current or old termios structures have acceptable baud rate setting and
  9600 is not in the min/max range either the function returns 0 and
  issues a warning.
  However for a UART that does not support speed of 9600 baud this is
  expected behavior.
  Clarify that 0 can be (and always could be) returned from the
  uart_get_baud_rate. Don't issue a warning in that case.

To remove any ambiguity, and to make sure all drivers handle the returned
value the same, make sure uart_get_baud_rate() will always return a
non-zero value in all scenarios.

This ensures that for devices that do not support a speed of 9600, and B0
is the desired baud rate specified in termios, then the returned value will
be 9600 or the input parameters 'min', whichever is the greatest.

Tested on a custom board with a VAR-SOM-6UL SOM and a sc16is752 DUART.

Thank you.

Link [1] https://lore.kernel.org/all/20260408142105.310210-1-biju.das.jz@bp.renesas.com/raw

Hugo Villeneuve (5):
  serial: icom: remove check for zero baud rate from
    uart_get_baud_rate()
  serial: apbuart: remove check for zero baud rate from
    uart_get_baud_rate()
  serial: core: update uart_get_baud_rate() obsolete comments
  serial: core: simplify clipping logic in uart_get_baud_rate()
  serial: core: prevent division by zero by always returning non-zero
    baud rate

 drivers/tty/serial/apbuart.c     |  2 --
 drivers/tty/serial/icom.c        |  2 --
 drivers/tty/serial/serial_core.c | 34 ++++++++++++++------------------
 3 files changed, 15 insertions(+), 23 deletions(-)


base-commit: a1a81aef99e853dec84241d701fbf587d713eb5b
-- 
2.47.3


^ permalink raw reply

* [PATCH 1/5] serial: icom: remove check for zero baud rate from uart_get_baud_rate()
From: Hugo Villeneuve @ 2026-04-10 15:20 UTC (permalink / raw)
  To: gregkh, jirislaby
  Cc: hugo, biju.das.jz, linux-kernel, linux-serial, Hugo Villeneuve
In-Reply-To: <20260410152022.2146488-1-hugo@hugovil.com>

From: Hugo Villeneuve <hvilleneuve@dimonoff.com>

The minimum baud rate supported by this driver is 300, so even for the B0
case, uart_get_baud_rate() will return 9600, not zero. This check predates
commit 16ae2a877bf4 ("serial: Fix crash if the minimum rate of the device
is > 9600 baud") and is no longer necessary so remove it.

Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
---
 drivers/tty/serial/icom.c | 2 --
 1 file changed, 2 deletions(-)

diff --git a/drivers/tty/serial/icom.c b/drivers/tty/serial/icom.c
index bcdc072084860..b6399d86a0bc3 100644
--- a/drivers/tty/serial/icom.c
+++ b/drivers/tty/serial/icom.c
@@ -1396,8 +1396,6 @@ static void icom_set_termios(struct uart_port *port, struct ktermios *termios,
 	baud = uart_get_baud_rate(port, termios, old_termios,
 				  icom_acfg_baud[0],
 				  icom_acfg_baud[BAUD_TABLE_LIMIT]);
-	if (!baud)
-		baud = 9600;	/* B0 transition handled in rs_set_termios */
 
 	for (index = 0; index < BAUD_TABLE_LIMIT; index++) {
 		if (icom_acfg_baud[index] == baud) {
-- 
2.47.3


^ permalink raw reply related

* [PATCH 5/5] serial: core: prevent division by zero by always returning non-zero baud rate
From: Hugo Villeneuve @ 2026-04-10 15:20 UTC (permalink / raw)
  To: gregkh, jirislaby
  Cc: hugo, biju.das.jz, linux-kernel, linux-serial, Hugo Villeneuve
In-Reply-To: <20260410152022.2146488-1-hugo@hugovil.com>

From: Hugo Villeneuve <hvilleneuve@dimonoff.com>

If a device has a minimum baud rate > 9600 bauds, and a new termios baud
rate of 0 (hang up) is requested, uart_get_baud_rate() will return 0.
Most drivers do not check this return value and call uart_update_timeout()
with this zero baud rate, which will trigger a "Division by zero in kernel"
fault:

  stty -F /dev/ttySC0 0

  Division by zero in kernel.
  ...

Fix by returning the larger of 9600 or min for the B0 case. This now
ensures that a non-zero baud rate is returned, and greatly simplifies the
code.

Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
---
Tested with the sc16is7xx driver by setting the minimum baudrate to 19200.

If a "Fixes" tag is needed, this would probably be:
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
This would also imply, probably, to merge this patch with the previous ones
(touching serial_core.c) to make porting easier to stable trees...
---
 drivers/tty/serial/serial_core.c | 29 ++++++++++++-----------------
 1 file changed, 12 insertions(+), 17 deletions(-)

diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c
index f89c0dc295163..075a69164aa7c 100644
--- a/drivers/tty/serial/serial_core.c
+++ b/drivers/tty/serial/serial_core.c
@@ -462,11 +462,10 @@ EXPORT_SYMBOL(uart_update_timeout);
  *
  * Decode the termios structure into a numeric baud rate, taking account of the
  * magic 38400 baud rate (with spd_* flags), and mapping the %B0 rate to 9600
- * baud.
+ * baud or min argument, whichever is greater.
  *
  * If the new baud rate is invalid, try the @old termios setting. If it's still
  * invalid, clip to the nearest chip supported rate.
- * If that is also invalid 0 is returned.
  *
  * The @termios structure is updated to reflect the baud rate we're actually
  * going to be using. Don't do this for the case where B0 is requested ("hang
@@ -481,7 +480,6 @@ uart_get_baud_rate(struct uart_port *port, struct ktermios *termios,
 	unsigned int try;
 	unsigned int baud;
 	unsigned int altbaud;
-	int hung_up = 0;
 	upf_t flags = port->flags & UPF_SPD_MASK;
 
 	switch (flags) {
@@ -515,10 +513,8 @@ uart_get_baud_rate(struct uart_port *port, struct ktermios *termios,
 		/*
 		 * Special case: B0 rate.
 		 */
-		if (baud == 0) {
-			hung_up = 1;
-			baud = 9600;
-		}
+		if (baud == 0)
+			return max(min, 9600);
 
 		if (baud >= min && baud <= max)
 			return baud;
@@ -530,9 +526,7 @@ uart_get_baud_rate(struct uart_port *port, struct ktermios *termios,
 		termios->c_cflag &= ~CBAUD;
 		if (old) {
 			baud = tty_termios_baud_rate(old);
-			if (!hung_up)
-				tty_termios_encode_baud_rate(termios,
-								baud, baud);
+			tty_termios_encode_baud_rate(termios, baud, baud);
 			old = NULL;
 			continue;
 		}
@@ -541,15 +535,16 @@ uart_get_baud_rate(struct uart_port *port, struct ktermios *termios,
 		 * As a last resort, if the range cannot be met then clip to
 		 * the nearest chip supported rate.
 		 */
-		if (!hung_up) {
-			if (baud <= min)
-				baud = min + 1;
-			else
-				baud = max - 1;
+		if (baud <= min)
+			baud = min + 1;
+		else
+			baud = max - 1;
 
-			tty_termios_encode_baud_rate(termios, baud, baud);
-		}
+		tty_termios_encode_baud_rate(termios, baud, baud);
 	}
+
+	/* Should never happen */
+	WARN_ON(1);
 	return 0;
 }
 EXPORT_SYMBOL(uart_get_baud_rate);
-- 
2.47.3


^ permalink raw reply related

* [PATCH 2/5] serial: apbuart: remove check for zero baud rate from uart_get_baud_rate()
From: Hugo Villeneuve @ 2026-04-10 15:20 UTC (permalink / raw)
  To: gregkh, jirislaby
  Cc: hugo, biju.das.jz, linux-kernel, linux-serial, Hugo Villeneuve
In-Reply-To: <20260410152022.2146488-1-hugo@hugovil.com>

From: Hugo Villeneuve <hvilleneuve@dimonoff.com>

The minimum baud rate supported by this driver is 0, so even for the B0
case, uart_get_baud_rate() will return 9600, not zero. This check predates
commit 16ae2a877bf4 ("serial: Fix crash if the minimum rate of the device
is > 9600 baud") and is no longer necessary so remove it.

Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
---
 drivers/tty/serial/apbuart.c | 2 --
 1 file changed, 2 deletions(-)

diff --git a/drivers/tty/serial/apbuart.c b/drivers/tty/serial/apbuart.c
index 3e46341cfff8a..afb04d727203e 100644
--- a/drivers/tty/serial/apbuart.c
+++ b/drivers/tty/serial/apbuart.c
@@ -210,8 +210,6 @@ static void apbuart_set_termios(struct uart_port *port,
 
 	/* Ask the core to calculate the divisor for us. */
 	baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / 16);
-	if (baud == 0)
-		panic("invalid baudrate %i\n", port->uartclk / 16);
 
 	/* uart_get_divisor calc a *16 uart freq, apbuart is *8 */
 	quot = (uart_get_divisor(port, baud)) * 2;
-- 
2.47.3


^ permalink raw reply related

* [PATCH 3/5] serial: core: update uart_get_baud_rate() obsolete comments
From: Hugo Villeneuve @ 2026-04-10 15:20 UTC (permalink / raw)
  To: gregkh, jirislaby
  Cc: hugo, biju.das.jz, linux-kernel, linux-serial, Hugo Villeneuve
In-Reply-To: <20260410152022.2146488-1-hugo@hugovil.com>

From: Hugo Villeneuve <hvilleneuve@dimonoff.com>

Update obsolete comments to match the actual code.

Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
---
 drivers/tty/serial/serial_core.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c
index 89cebdd278410..e6a8ab40442d9 100644
--- a/drivers/tty/serial/serial_core.c
+++ b/drivers/tty/serial/serial_core.c
@@ -465,7 +465,8 @@ EXPORT_SYMBOL(uart_update_timeout);
  * baud.
  *
  * If the new baud rate is invalid, try the @old termios setting. If it's still
- * invalid, we try 9600 baud. If that is also invalid 0 is returned.
+ * invalid, clip to the nearest chip supported rate.
+ * If that is also invalid 0 is returned.
  *
  * The @termios structure is updated to reflect the baud rate we're actually
  * going to be using. Don't do this for the case where B0 is requested ("hang
@@ -523,7 +524,7 @@ uart_get_baud_rate(struct uart_port *port, struct ktermios *termios,
 			return baud;
 
 		/*
-		 * Oops, the quotient was zero.  Try again with
+		 * If the range cannot be met then try again with
 		 * the old baud rate if possible.
 		 */
 		termios->c_cflag &= ~CBAUD;
-- 
2.47.3


^ permalink raw reply related

* [PATCH 4/5] serial: core: simplify clipping logic in uart_get_baud_rate()
From: Hugo Villeneuve @ 2026-04-10 15:20 UTC (permalink / raw)
  To: gregkh, jirislaby
  Cc: hugo, biju.das.jz, linux-kernel, linux-serial, Hugo Villeneuve
In-Reply-To: <20260410152022.2146488-1-hugo@hugovil.com>

From: Hugo Villeneuve <hvilleneuve@dimonoff.com>

Simplify the clipping logic in uart_get_baud_rate() to improve code
readability.

Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
---
 drivers/tty/serial/serial_core.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c
index e6a8ab40442d9..f89c0dc295163 100644
--- a/drivers/tty/serial/serial_core.c
+++ b/drivers/tty/serial/serial_core.c
@@ -543,11 +543,11 @@ uart_get_baud_rate(struct uart_port *port, struct ktermios *termios,
 		 */
 		if (!hung_up) {
 			if (baud <= min)
-				tty_termios_encode_baud_rate(termios,
-							min + 1, min + 1);
+				baud = min + 1;
 			else
-				tty_termios_encode_baud_rate(termios,
-							max - 1, max - 1);
+				baud = max - 1;
+
+			tty_termios_encode_baud_rate(termios, baud, baud);
 		}
 	}
 	return 0;
-- 
2.47.3


^ permalink raw reply related

* [PATCH] serial: zs: Fix swapped RI/DSR modem line transition counting
From: Maciej W. Rozycki @ 2026-04-10 17:19 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Jiri Slaby; +Cc: linux-serial, linux-kernel

Fix a thinko in the status interrupt handler that has caused counters 
for the RI and DSR modem line transitions to be used for the other line 
each.

Fixes: 8b4a40809e53 ("zs: move to the serial subsystem")
Signed-off-by: Maciej W. Rozycki <macro@orcam.me.uk>
---
 drivers/tty/serial/zs.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

linux-serial-zs-dsr-rng.diff
Index: linux-macro/drivers/tty/serial/zs.c
===================================================================
--- linux-macro.orig/drivers/tty/serial/zs.c
+++ linux-macro/drivers/tty/serial/zs.c
@@ -679,9 +679,9 @@ static void zs_status_handle(struct zs_p
 			uart_handle_dcd_change(uport,
 					       zport->mctrl & TIOCM_CAR);
 		if (delta & TIOCM_RNG)
-			uport->icount.dsr++;
-		if (delta & TIOCM_DSR)
 			uport->icount.rng++;
+		if (delta & TIOCM_DSR)
+			uport->icount.dsr++;
 
 		if (delta)
 			wake_up_interruptible(&uport->state->port.delta_msr_wait);

^ permalink raw reply

* [PATCH v4 0/4] rust: add basic serial device bus abstractions
From: Markus Probst via B4 Relay @ 2026-04-11 15:10 UTC (permalink / raw)
  To: Rob Herring, Greg Kroah-Hartman, Jiri Slaby, Miguel Ojeda,
	Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
	Alice Ryhl, Trevor Gross, Danilo Krummrich, Kari Argillander,
	Rafael J. Wysocki, Viresh Kumar, Boqun Feng, David Airlie,
	Simona Vetter, Boqun Feng
  Cc: linux-serial, linux-kernel, rust-for-linux, linux-pm, driver-core,
	dri-devel, Markus Probst

This patch series adds the serdev device bus rust abstraction into the
kernel.

This abstraction will be used by a driver,
which targets the MCU devices in Synology devices.

Kari Argillander also messaged me, stating that he wants to write a
watchdog driver with this abstraction (needing initial device data).

@Rob: Are you willing to maintain these rust abstractions yourself,
as you are the expert on this subsystem, otherwise I would take care of
it with a "SERIAL DEVICE BUS [RUST]" section in the MAINTAINERS file. In
the second case, I assume you are going to pick those patches as-is into
your tree, after they have been reviewed?

Signed-off-by: Markus Probst <markus.probst@posteo.de>
---
Changes in v4:
- fixed not selecting rust serdev abstraction in sample
- Link to v3: https://lore.kernel.org/r/20260313-rust_serdev-v3-0-c9a3af214f7f@posteo.de

Changes in v3:
- fix vertical import style
- add Kconfig entry for the rust abstraction
- fix documentation in include/linux/serdev.h
- rename private_data to rust_private_data
- fix `complete_all` <-> `wait_for_completion` typo
- move drvdata_borrow call after the completion
- Link to v2: https://lore.kernel.org/r/20260306-rust_serdev-v2-0-e9b23b42b255@posteo.de

Changes in v2:
- fix documentation in `serdev::Driver::write` and
  `serdev::Driver::write_all`
- remove use of `dev_info` in probe from the sample
- remove `properties_parse` from the sample
- add optional `baudrate` property to the sample
- remove 1. patch
- remove `TryFrom<&device::Device<Ctx>> for &serdev::Device<Ctx>`
  implementation
- fix import style
- add patch to return reference in `devres::register` to fix safety
  issue
- add patch to add private data to serdev_device, to fix
  `Device.drvdata()` from failing
- simplify abstraction by removing ability to receive the initial
  transmission. It may be added later in a separate patch series if
  needed.
- Link to v1: https://lore.kernel.org/r/20251220-rust_serdev-v1-0-e44645767621@posteo.de

---
Markus Probst (4):
      rust: devres: return reference in `devres::register`
      serdev: add rust private data to serdev_device
      rust: add basic serial device bus abstractions
      samples: rust: add Rust serial device bus sample device driver

 drivers/tty/serdev/Kconfig         |   7 +
 include/linux/serdev.h             |  15 +-
 rust/bindings/bindings_helper.h    |   1 +
 rust/helpers/helpers.c             |   1 +
 rust/helpers/serdev.c              |  22 ++
 rust/kernel/cpufreq.rs             |   3 +-
 rust/kernel/devres.rs              |  15 +-
 rust/kernel/drm/driver.rs          |   3 +-
 rust/kernel/lib.rs                 |   2 +
 rust/kernel/serdev.rs              | 536 +++++++++++++++++++++++++++++++++++++
 samples/rust/Kconfig               |  11 +
 samples/rust/Makefile              |   1 +
 samples/rust/rust_driver_serdev.rs |  86 ++++++
 13 files changed, 693 insertions(+), 10 deletions(-)
---
base-commit: c369299895a591d96745d6492d4888259b004a9e
change-id: 20251217-rust_serdev-ee5481e9085c



^ permalink raw reply

* [PATCH v4 4/4] samples: rust: add Rust serial device bus sample device driver
From: Markus Probst via B4 Relay @ 2026-04-11 15:10 UTC (permalink / raw)
  To: Rob Herring, Greg Kroah-Hartman, Jiri Slaby, Miguel Ojeda,
	Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
	Alice Ryhl, Trevor Gross, Danilo Krummrich, Kari Argillander,
	Rafael J. Wysocki, Viresh Kumar, Boqun Feng, David Airlie,
	Simona Vetter, Boqun Feng
  Cc: linux-serial, linux-kernel, rust-for-linux, linux-pm, driver-core,
	dri-devel, Markus Probst
In-Reply-To: <20260411-rust_serdev-v4-0-845e960c6627@posteo.de>

From: Markus Probst <markus.probst@posteo.de>

Add a sample Rust serial device bus device driver illustrating the usage
of the serial device bus abstractions.

This drivers probes through either a match of device / driver name or a
match within the OF ID table.

Signed-off-by: Markus Probst <markus.probst@posteo.de>
---
 samples/rust/Kconfig               | 11 +++++
 samples/rust/Makefile              |  1 +
 samples/rust/rust_driver_serdev.rs | 86 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 98 insertions(+)

diff --git a/samples/rust/Kconfig b/samples/rust/Kconfig
index c49ab9106345..31d62533ef25 100644
--- a/samples/rust/Kconfig
+++ b/samples/rust/Kconfig
@@ -161,6 +161,17 @@ config SAMPLE_RUST_DRIVER_AUXILIARY
 
 	  If unsure, say N.
 
+config SAMPLE_RUST_DRIVER_SERDEV
+	tristate "Serial Device Bus Device Driver"
+	select RUST_SERIAL_DEV_BUS_ABSTRACTIONS
+	help
+	  This option builds the Rust serial device bus driver sample.
+
+	  To compile this as a module, choose M here:
+	  the module will be called rust_driver_serdev.
+
+	  If unsure, say N.
+
 config SAMPLE_RUST_SOC
 	tristate "SoC Driver"
 	select SOC_BUS
diff --git a/samples/rust/Makefile b/samples/rust/Makefile
index 6c0aaa58cccc..b986b681cde5 100644
--- a/samples/rust/Makefile
+++ b/samples/rust/Makefile
@@ -14,6 +14,7 @@ obj-$(CONFIG_SAMPLE_RUST_DRIVER_PLATFORM)	+= rust_driver_platform.o
 obj-$(CONFIG_SAMPLE_RUST_DRIVER_USB)		+= rust_driver_usb.o
 obj-$(CONFIG_SAMPLE_RUST_DRIVER_FAUX)		+= rust_driver_faux.o
 obj-$(CONFIG_SAMPLE_RUST_DRIVER_AUXILIARY)	+= rust_driver_auxiliary.o
+obj-$(CONFIG_SAMPLE_RUST_DRIVER_SERDEV)		+= rust_driver_serdev.o
 obj-$(CONFIG_SAMPLE_RUST_CONFIGFS)		+= rust_configfs.o
 obj-$(CONFIG_SAMPLE_RUST_SOC)			+= rust_soc.o
 
diff --git a/samples/rust/rust_driver_serdev.rs b/samples/rust/rust_driver_serdev.rs
new file mode 100644
index 000000000000..8cf3fb451b22
--- /dev/null
+++ b/samples/rust/rust_driver_serdev.rs
@@ -0,0 +1,86 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Rust Serial device bus device driver sample.
+
+use kernel::{
+    acpi,
+    device::{
+        Bound,
+        Core, //
+    },
+    of,
+    prelude::*,
+    serdev,
+    sync::aref::ARef, //
+};
+
+struct SampleDriver {
+    sdev: ARef<serdev::Device>,
+}
+
+kernel::of_device_table!(
+    OF_TABLE,
+    MODULE_OF_TABLE,
+    <SampleDriver as serdev::Driver>::IdInfo,
+    [(of::DeviceId::new(c"test,rust_driver_serdev"), ())]
+);
+
+kernel::acpi_device_table!(
+    ACPI_TABLE,
+    MODULE_ACPI_TABLE,
+    <SampleDriver as serdev::Driver>::IdInfo,
+    [(acpi::DeviceId::new(c"LNUXBEEF"), ())]
+);
+
+#[vtable]
+impl serdev::Driver for SampleDriver {
+    type IdInfo = ();
+    const OF_ID_TABLE: Option<of::IdTable<Self::IdInfo>> = Some(&OF_TABLE);
+    const ACPI_ID_TABLE: Option<acpi::IdTable<Self::IdInfo>> = Some(&ACPI_TABLE);
+
+    fn probe(
+        sdev: &serdev::Device<Core>,
+        _info: Option<&Self::IdInfo>,
+    ) -> impl PinInit<Self, Error> {
+        let dev = sdev.as_ref();
+
+        dev_dbg!(dev, "Probe Rust Serial device bus device driver sample.\n");
+
+        if sdev
+            .set_baudrate(
+                dev.fwnode()
+                    .and_then(|fwnode| fwnode.property_read(c"baudrate").optional())
+                    .unwrap_or(115200),
+            )
+            .is_err()
+        {
+            return Err(EINVAL);
+        }
+        sdev.set_flow_control(false);
+        sdev.set_parity(serdev::Parity::None)?;
+
+        Ok(Self { sdev: sdev.into() })
+    }
+
+    fn receive(sdev: &serdev::Device<Bound>, _this: Pin<&Self>, data: &[u8]) -> usize {
+        let _ = sdev.write_all(data, serdev::Timeout::Max);
+        data.len()
+    }
+}
+
+impl Drop for SampleDriver {
+    fn drop(&mut self) {
+        dev_dbg!(
+            self.sdev.as_ref(),
+            "Remove Rust Serial device bus device driver sample.\n"
+        );
+    }
+}
+
+kernel::module_serdev_device_driver! {
+    type: SampleDriver,
+    name: "rust_driver_serdev",
+    authors: ["Markus Probst"],
+    description: "Rust Serial device bus device driver",
+    license: "GPL v2",
+}

-- 
2.52.0



^ permalink raw reply related

* [PATCH v4 3/4] rust: add basic serial device bus abstractions
From: Markus Probst via B4 Relay @ 2026-04-11 15:10 UTC (permalink / raw)
  To: Rob Herring, Greg Kroah-Hartman, Jiri Slaby, Miguel Ojeda,
	Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
	Alice Ryhl, Trevor Gross, Danilo Krummrich, Kari Argillander,
	Rafael J. Wysocki, Viresh Kumar, Boqun Feng, David Airlie,
	Simona Vetter, Boqun Feng
  Cc: linux-serial, linux-kernel, rust-for-linux, linux-pm, driver-core,
	dri-devel, Markus Probst
In-Reply-To: <20260411-rust_serdev-v4-0-845e960c6627@posteo.de>

From: Markus Probst <markus.probst@posteo.de>

Implement the basic serial device bus abstractions required to write a
serial device bus device driver with or without the need for initial device
data. This includes the following data structures:

The `serdev::Driver` trait represents the interface to the driver.

The `serdev::Device` abstraction represents a `struct serdev_device`.

In order to provide the Serdev specific parts to a generic
`driver::Registration` the `driver::RegistrationOps` trait is
implemented by `serdev::Adapter`.

Signed-off-by: Markus Probst <markus.probst@posteo.de>
---
 drivers/tty/serdev/Kconfig      |   7 +
 rust/bindings/bindings_helper.h |   1 +
 rust/helpers/helpers.c          |   1 +
 rust/helpers/serdev.c           |  22 ++
 rust/kernel/lib.rs              |   2 +
 rust/kernel/serdev.rs           | 536 ++++++++++++++++++++++++++++++++++++++++
 6 files changed, 569 insertions(+)

diff --git a/drivers/tty/serdev/Kconfig b/drivers/tty/serdev/Kconfig
index 46ae732bfc68..e6dfe949ad01 100644
--- a/drivers/tty/serdev/Kconfig
+++ b/drivers/tty/serdev/Kconfig
@@ -9,6 +9,13 @@ menuconfig SERIAL_DEV_BUS
 
 	  Note that you typically also want to enable TTY port controller support.
 
+config RUST_SERIAL_DEV_BUS_ABSTRACTIONS
+	bool "Rust Serial device bus abstractions"
+	depends on RUST
+	select SERIAL_DEV_BUS
+	help
+	  This enables the Rust abstraction for the serial device bus API.
+
 if SERIAL_DEV_BUS
 
 config SERIAL_DEV_CTRL_TTYPORT
diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
index 083cc44aa952..ab521ba42673 100644
--- a/rust/bindings/bindings_helper.h
+++ b/rust/bindings/bindings_helper.h
@@ -80,6 +80,7 @@
 #include <linux/regulator/consumer.h>
 #include <linux/sched.h>
 #include <linux/security.h>
+#include <linux/serdev.h>
 #include <linux/slab.h>
 #include <linux/sys_soc.h>
 #include <linux/task_work.h>
diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c
index a3c42e51f00a..9b87e9591cfd 100644
--- a/rust/helpers/helpers.c
+++ b/rust/helpers/helpers.c
@@ -53,6 +53,7 @@
 #include "regulator.c"
 #include "scatterlist.c"
 #include "security.c"
+#include "serdev.c"
 #include "signal.c"
 #include "slab.c"
 #include "spinlock.c"
diff --git a/rust/helpers/serdev.c b/rust/helpers/serdev.c
new file mode 100644
index 000000000000..c52b78ca3fc7
--- /dev/null
+++ b/rust/helpers/serdev.c
@@ -0,0 +1,22 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/serdev.h>
+
+__rust_helper
+void rust_helper_serdev_device_driver_unregister(struct serdev_device_driver *sdrv)
+{
+	serdev_device_driver_unregister(sdrv);
+}
+
+__rust_helper
+void rust_helper_serdev_device_put(struct serdev_device *serdev)
+{
+	serdev_device_put(serdev);
+}
+
+__rust_helper
+void rust_helper_serdev_device_set_client_ops(struct serdev_device *serdev,
+					      const struct serdev_device_ops *ops)
+{
+	serdev_device_set_client_ops(serdev, ops);
+}
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index d93292d47420..5107c9c1be07 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -144,6 +144,8 @@
 pub mod scatterlist;
 pub mod security;
 pub mod seq_file;
+#[cfg(CONFIG_RUST_SERIAL_DEV_BUS_ABSTRACTIONS)]
+pub mod serdev;
 pub mod sizes;
 pub mod slice;
 #[cfg(CONFIG_SOC_BUS)]
diff --git a/rust/kernel/serdev.rs b/rust/kernel/serdev.rs
new file mode 100644
index 000000000000..d9fea4bd4439
--- /dev/null
+++ b/rust/kernel/serdev.rs
@@ -0,0 +1,536 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Abstractions for the serial device bus.
+//!
+//! C header: [`include/linux/serdev.h`](srctree/include/linux/serdev.h)
+
+use crate::{
+    acpi,
+    device,
+    devres,
+    driver,
+    error::{
+        from_result,
+        to_result,
+        VTABLE_DEFAULT_ERROR, //
+    },
+    of,
+    prelude::*,
+    sync::Completion,
+    time::{
+        msecs_to_jiffies,
+        Jiffies,
+        Msecs, //
+    },
+    types::{
+        AlwaysRefCounted,
+        Opaque, //
+    }, //
+};
+
+use core::{
+    cell::UnsafeCell,
+    marker::PhantomData,
+    mem::offset_of,
+    num::NonZero,
+    ptr::NonNull, //
+};
+
+/// Parity bit to use with a serial device.
+#[repr(u32)]
+pub enum Parity {
+    /// No parity bit.
+    None = bindings::serdev_parity_SERDEV_PARITY_NONE,
+    /// Even partiy.
+    Even = bindings::serdev_parity_SERDEV_PARITY_EVEN,
+    /// Odd parity.
+    Odd = bindings::serdev_parity_SERDEV_PARITY_ODD,
+}
+
+/// Timeout in Jiffies.
+pub enum Timeout {
+    /// Wait for a specific amount of [`Jiffies`].
+    Jiffies(NonZero<Jiffies>),
+    /// Wait for a specific amount of [`Msecs`].
+    Milliseconds(NonZero<Msecs>),
+    /// Wait as long as possible.
+    ///
+    /// This is equivalent to [`kernel::task::MAX_SCHEDULE_TIMEOUT`].
+    Max,
+}
+
+impl Timeout {
+    fn into_jiffies(self) -> isize {
+        match self {
+            Self::Jiffies(value) => value.get().try_into().unwrap_or_default(),
+            Self::Milliseconds(value) => {
+                msecs_to_jiffies(value.get()).try_into().unwrap_or_default()
+            }
+            Self::Max => 0,
+        }
+    }
+}
+
+/// An adapter for the registration of serial device bus device drivers.
+pub struct Adapter<T: Driver>(T);
+
+// SAFETY:
+// - `bindings::serdev_device_driver` is a C type declared as `repr(C)`.
+// - `Drvdata<T>` is the type of the driver's device private data.
+// - `struct serdev_device_driver` embeds a `struct device_driver`.
+// - `DEVICE_DRIVER_OFFSET` is the correct byte offset to the embedded `struct device_driver`.
+unsafe impl<T: Driver + 'static> driver::DriverLayout for Adapter<T> {
+    type DriverType = bindings::serdev_device_driver;
+    type DriverData = T;
+    const DEVICE_DRIVER_OFFSET: usize = core::mem::offset_of!(Self::DriverType, driver);
+}
+
+// SAFETY: A call to `unregister` for a given instance of `DriverType` is guaranteed to be valid if
+// a preceding call to `register` has been successful.
+unsafe impl<T: Driver + 'static> driver::RegistrationOps for Adapter<T> {
+    unsafe fn register(
+        sdrv: &Opaque<Self::DriverType>,
+        name: &'static CStr,
+        module: &'static ThisModule,
+    ) -> Result {
+        let of_table = match T::OF_ID_TABLE {
+            Some(table) => table.as_ptr(),
+            None => core::ptr::null(),
+        };
+
+        let acpi_table = match T::ACPI_ID_TABLE {
+            Some(table) => table.as_ptr(),
+            None => core::ptr::null(),
+        };
+
+        // SAFETY: It's safe to set the fields of `struct serdev_device_driver` on initialization.
+        unsafe {
+            (*sdrv.get()).driver.name = name.as_char_ptr();
+            (*sdrv.get()).probe = Some(Self::probe_callback);
+            (*sdrv.get()).remove = Some(Self::remove_callback);
+            (*sdrv.get()).driver.of_match_table = of_table;
+            (*sdrv.get()).driver.acpi_match_table = acpi_table;
+        }
+
+        // SAFETY: `sdrv` is guaranteed to be a valid `DriverType`.
+        to_result(unsafe { bindings::__serdev_device_driver_register(sdrv.get(), module.0) })
+    }
+
+    unsafe fn unregister(sdrv: &Opaque<Self::DriverType>) {
+        // SAFETY: `sdrv` is guaranteed to be a valid `DriverType`.
+        unsafe { bindings::serdev_device_driver_unregister(sdrv.get()) };
+    }
+}
+
+#[pin_data]
+struct PrivateData {
+    #[pin]
+    probe_complete: Completion,
+    error: UnsafeCell<bool>,
+}
+
+impl<T: Driver + 'static> Adapter<T> {
+    const OPS: &'static bindings::serdev_device_ops = &bindings::serdev_device_ops {
+        receive_buf: if T::HAS_RECEIVE {
+            Some(Self::receive_buf_callback)
+        } else {
+            None
+        },
+        write_wakeup: Some(bindings::serdev_device_write_wakeup),
+    };
+
+    extern "C" fn probe_callback(sdev: *mut bindings::serdev_device) -> kernel::ffi::c_int {
+        // SAFETY: The serial device bus only ever calls the probe callback with a valid pointer to
+        // a `struct serdev_device`.
+        //
+        // INVARIANT: `sdev` is valid for the duration of `probe_callback()`.
+        let sdev = unsafe { &*sdev.cast::<Device<device::CoreInternal>>() };
+        let id_info = <Self as driver::Adapter>::id_info(sdev.as_ref());
+
+        from_result(|| {
+            let private_data = devres::register(
+                sdev.as_ref(),
+                try_pin_init!(PrivateData {
+                    probe_complete <- Completion::new(),
+                    error: false.into(),
+                }),
+                GFP_KERNEL,
+            )?;
+
+            // SAFETY: `sdev.as_raw()` is guaranteed to be a valid pointer to `serdev_device`.
+            unsafe {
+                (*sdev.as_raw()).rust_private_data =
+                    (&raw const *private_data).cast::<c_void>().cast_mut()
+            };
+
+            // SAFETY: `sdev.as_raw()` is guaranteed to be a valid pointer to `serdev_device`.
+            unsafe { bindings::serdev_device_set_client_ops(sdev.as_raw(), Self::OPS) };
+
+            // SAFETY: The serial device bus only ever calls the probe callback with a valid pointer
+            // to a `serdev_device`.
+            to_result(unsafe {
+                bindings::devm_serdev_device_open(sdev.as_ref().as_raw(), sdev.as_raw())
+            })?;
+
+            let data = T::probe(sdev, id_info);
+            let result = sdev.as_ref().set_drvdata(data);
+
+            // SAFETY: We have exclusive access to `private_data.error`.
+            unsafe { *private_data.error.get() = result.is_err() };
+
+            private_data.probe_complete.complete_all();
+
+            result.map(|()| 0)
+        })
+    }
+
+    extern "C" fn remove_callback(sdev: *mut bindings::serdev_device) {
+        // SAFETY: The serial device bus only ever calls the remove callback with a valid pointer
+        // to a `struct serdev_device`.
+        //
+        // INVARIANT: `sdev` is valid for the duration of `remove_callback()`.
+        let sdev = unsafe { &*sdev.cast::<Device<device::CoreInternal>>() };
+
+        // SAFETY: `remove_callback` is only ever called after a successful call to
+        // `probe_callback`, hence it's guaranteed that `Device::set_drvdata()` has been called
+        // and stored a `Pin<KBox<T>>`.
+        let data = unsafe { sdev.as_ref().drvdata_borrow::<T>() };
+
+        T::unbind(sdev, data);
+    }
+
+    extern "C" fn receive_buf_callback(
+        sdev: *mut bindings::serdev_device,
+        buf: *const u8,
+        length: usize,
+    ) -> usize {
+        // SAFETY: The serial device bus only ever calls the receive buf callback with a valid
+        // pointer to a `struct serdev_device`.
+        //
+        // INVARIANT: `sdev` is valid for the duration of `receive_buf_callback()`.
+        let sdev = unsafe { &*sdev.cast::<Device<device::CoreInternal>>() };
+
+        // SAFETY:
+        // - The serial device bus only ever calls the receive buf callback with a valid pointer to
+        //   a `struct serdev_device`.
+        // - `receive_buf_callback` is only ever called after a successful call to
+        //   `probe_callback`, hence it's guaranteed that `sdev.private_data` is a pointer
+        //   to a valid `PrivateData`.
+        let private_data = unsafe { &*(*sdev.as_raw()).rust_private_data.cast::<PrivateData>() };
+
+        private_data.probe_complete.wait_for_completion();
+
+        // SAFETY: No one has exclusive access to `private_data.error`.
+        if unsafe { *private_data.error.get() } {
+            return length;
+        }
+
+        // SAFETY: `receive_buf_callback` is only ever called after a successful call to
+        // `probe_callback`, hence it's guaranteed that `Device::set_drvdata()` has been called
+        // and stored a `Pin<KBox<T>>`.
+        let data = unsafe { sdev.as_ref().drvdata_borrow::<T>() };
+
+        // SAFETY: `buf` is guaranteed to be non-null and has the size of `length`.
+        let buf = unsafe { core::slice::from_raw_parts(buf, length) };
+
+        T::receive(sdev, data, buf)
+    }
+}
+
+impl<T: Driver + 'static> driver::Adapter for Adapter<T> {
+    type IdInfo = T::IdInfo;
+
+    fn of_id_table() -> Option<of::IdTable<Self::IdInfo>> {
+        T::OF_ID_TABLE
+    }
+
+    fn acpi_id_table() -> Option<acpi::IdTable<Self::IdInfo>> {
+        T::ACPI_ID_TABLE
+    }
+}
+
+/// Declares a kernel module that exposes a single serial device bus device driver.
+///
+/// # Examples
+///
+/// ```ignore
+/// kernel::module_serdev_device_driver! {
+///     type: MyDriver,
+///     name: "Module name",
+///     authors: ["Author name"],
+///     description: "Description",
+///     license: "GPL v2",
+/// }
+/// ```
+#[macro_export]
+macro_rules! module_serdev_device_driver {
+    ($($f:tt)*) => {
+        $crate::module_driver!(<T>, $crate::serdev::Adapter<T>, { $($f)* });
+    };
+}
+
+/// The serial device bus device driver trait.
+///
+/// Drivers must implement this trait in order to get a serial device bus device driver registered.
+///
+/// # Examples
+///
+///```
+/// # use kernel::{
+///     acpi,
+///     bindings,
+///     device::{
+///         Bound,
+///         Core, //
+///     },
+///     of,
+///     serdev, //
+/// };
+///
+/// struct MyDriver;
+///
+/// kernel::of_device_table!(
+///     OF_TABLE,
+///     MODULE_OF_TABLE,
+///     <MyDriver as serdev::Driver>::IdInfo,
+///     [
+///         (of::DeviceId::new(c"test,device"), ())
+///     ]
+/// );
+///
+/// kernel::acpi_device_table!(
+///     ACPI_TABLE,
+///     MODULE_ACPI_TABLE,
+///     <MyDriver as serdev::Driver>::IdInfo,
+///     [
+///         (acpi::DeviceId::new(c"LNUXBEEF"), ())
+///     ]
+/// );
+///
+/// #[vtable]
+/// impl serdev::Driver for MyDriver {
+///     type IdInfo = ();
+///     const OF_ID_TABLE: Option<of::IdTable<Self::IdInfo>> = Some(&OF_TABLE);
+///     const ACPI_ID_TABLE: Option<acpi::IdTable<Self::IdInfo>> = Some(&ACPI_TABLE);
+///
+///     fn probe(
+///         sdev: &serdev::Device<Core>,
+///         _id_info: Option<&Self::IdInfo>,
+///     ) -> impl PinInit<Self, Error> {
+///         sdev.set_baudrate(115200);
+///         sdev.write_all(b"Hello\n", serdev::Timeout::Max)?;
+///         Ok(MyDriver)
+///     }
+/// }
+///```
+#[vtable]
+pub trait Driver: Send {
+    /// The type holding driver private data about each device id supported by the driver.
+    // TODO: Use associated_type_defaults once stabilized:
+    //
+    // ```
+    // type IdInfo: 'static = ();
+    // ```
+    type IdInfo: 'static;
+
+    /// The table of OF device ids supported by the driver.
+    const OF_ID_TABLE: Option<of::IdTable<Self::IdInfo>> = None;
+
+    /// The table of ACPI device ids supported by the driver.
+    const ACPI_ID_TABLE: Option<acpi::IdTable<Self::IdInfo>> = None;
+
+    /// Serial device bus device driver probe.
+    ///
+    /// Called when a new serial device bus device is added or discovered.
+    /// Implementers should attempt to initialize the device here.
+    fn probe(
+        sdev: &Device<device::Core>,
+        id_info: Option<&Self::IdInfo>,
+    ) -> impl PinInit<Self, Error>;
+
+    /// Serial device bus device driver unbind.
+    ///
+    /// Called when a [`Device`] is unbound from its bound [`Driver`]. Implementing this callback
+    /// is optional.
+    ///
+    /// This callback serves as a place for drivers to perform teardown operations that require a
+    /// `&Device<Core>` or `&Device<Bound>` reference. For instance.
+    ///
+    /// Otherwise, release operations for driver resources should be performed in `Self::drop`.
+    fn unbind(sdev: &Device<device::Core>, this: Pin<&Self>) {
+        let _ = (sdev, this);
+    }
+
+    /// Serial device bus device data receive callback.
+    ///
+    /// Called when data got received from device.
+    ///
+    /// Returns the number of bytes accepted.
+    fn receive(sdev: &Device<device::Bound>, this: Pin<&Self>, data: &[u8]) -> usize {
+        let _ = (sdev, this, data);
+        build_error!(VTABLE_DEFAULT_ERROR)
+    }
+}
+
+/// The serial device bus device representation.
+///
+/// This structure represents the Rust abstraction for a C `struct serdev_device`. The
+/// implementation abstracts the usage of an already existing C `struct serdev_device` within Rust
+/// code that we get passed from the C side.
+///
+/// # Invariants
+///
+/// A [`Device`] instance represents a valid `struct serdev_device` created by the C portion of
+/// the kernel.
+#[repr(transparent)]
+pub struct Device<Ctx: device::DeviceContext = device::Normal>(
+    Opaque<bindings::serdev_device>,
+    PhantomData<Ctx>,
+);
+
+impl<Ctx: device::DeviceContext> Device<Ctx> {
+    fn as_raw(&self) -> *mut bindings::serdev_device {
+        self.0.get()
+    }
+}
+
+impl Device<device::Bound> {
+    /// Set the baudrate in bits per second.
+    ///
+    /// Common baudrates are 115200, 9600, 19200, 57600, 4800.
+    ///
+    /// Use [`Device::write_flush`] before calling this if you have written data prior to this call.
+    pub fn set_baudrate(&self, speed: u32) -> Result<(), u32> {
+        // SAFETY: `self.as_raw()` is guaranteed to be a pointer to a valid `serdev_device`.
+        let ret = unsafe { bindings::serdev_device_set_baudrate(self.as_raw(), speed) };
+        if ret == speed {
+            Ok(())
+        } else {
+            Err(ret)
+        }
+    }
+
+    /// Set if flow control should be enabled.
+    ///
+    /// Use [`Device::write_flush`] before calling this if you have written data prior to this call.
+    pub fn set_flow_control(&self, enable: bool) {
+        // SAFETY: `self.as_raw()` is guaranteed to be a pointer to a valid `serdev_device`.
+        unsafe { bindings::serdev_device_set_flow_control(self.as_raw(), enable) };
+    }
+
+    /// Set parity to use.
+    ///
+    /// Use [`Device::write_flush`] before calling this if you have written data prior to this call.
+    pub fn set_parity(&self, parity: Parity) -> Result {
+        // SAFETY: `self.as_raw()` is guaranteed to be a pointer to a valid `serdev_device`.
+        to_result(unsafe { bindings::serdev_device_set_parity(self.as_raw(), parity as u32) })
+    }
+
+    /// Write data to the serial device until the controller has accepted all the data or has
+    /// been interrupted by a timeout or signal.
+    ///
+    /// Note that any accepted data has only been buffered by the controller. Use
+    /// [ Device::wait_until_sent`] to make sure the controller write buffer has actually been
+    /// emptied.
+    ///
+    /// Returns the number of bytes written (less than `data.len()` if interrupted).
+    /// [`kernel::error::code::ETIMEDOUT`] or [`kernel::error::code::ERESTARTSYS`] if interrupted
+    /// before any bytes were written.
+    pub fn write_all(&self, data: &[u8], timeout: Timeout) -> Result<usize> {
+        // SAFETY:
+        // - `self.as_raw()` is guaranteed to be a pointer to a valid `serdev_device`.
+        // - `data.as_ptr()` is guaranteed to be a valid array pointer with the size of
+        //   `data.len()`.
+        let ret = unsafe {
+            bindings::serdev_device_write(
+                self.as_raw(),
+                data.as_ptr(),
+                data.len(),
+                timeout.into_jiffies(),
+            )
+        };
+        // CAST: negative return values are guaranteed to be between `-MAX_ERRNO` and `-1`,
+        // which always fit into a `i32`.
+        to_result(ret as i32).map(|()| ret.unsigned_abs())
+    }
+
+    /// Write data to the serial device.
+    ///
+    /// If you want to write until the controller has accepted all the data, use
+    /// [`Device::write_all`].
+    ///
+    /// Note that any accepted data has only been buffered by the controller. Use
+    /// [ Device::wait_until_sent`] to make sure the controller write buffer has actually been
+    /// emptied.
+    ///
+    /// Returns the number of bytes written (less than `data.len()` if not enough room in the
+    /// write buffer).
+    pub fn write(&self, data: &[u8]) -> Result<u32> {
+        // SAFETY:
+        // - `self.as_raw()` is guaranteed to be a pointer to a valid `serdev_device`.
+        // - `data.as_ptr()` is guaranteed to be a valid array pointer with the size of
+        //   `data.len()`.
+        let ret =
+            unsafe { bindings::serdev_device_write_buf(self.as_raw(), data.as_ptr(), data.len()) };
+
+        to_result(ret as i32).map(|()| ret.unsigned_abs())
+    }
+
+    /// Send data to the serial device immediately.
+    ///
+    /// Note that this doesn't guarantee that the data has been transmitted.
+    /// Use [`Device::wait_until_sent`] for this purpose.
+    pub fn write_flush(&self) {
+        // SAFETY: `self.as_raw()` is guaranteed to be a pointer to a valid `serdev_device`.
+        unsafe { bindings::serdev_device_write_flush(self.as_raw()) };
+    }
+
+    /// Wait for the data to be sent.
+    ///
+    /// After this function, the write buffer of the controller should be empty.
+    pub fn wait_until_sent(&self, timeout: Timeout) {
+        // SAFETY: `self.as_raw()` is guaranteed to be a pointer to a valid `serdev_device`.
+        unsafe { bindings::serdev_device_wait_until_sent(self.as_raw(), timeout.into_jiffies()) };
+    }
+}
+
+// SAFETY: `serdev::Device` is a transparent wrapper of `struct serdev_device`.
+// The offset is guaranteed to point to a valid device field inside `serdev::Device`.
+unsafe impl<Ctx: device::DeviceContext> device::AsBusDevice<Ctx> for Device<Ctx> {
+    const OFFSET: usize = offset_of!(bindings::serdev_device, dev);
+}
+
+// SAFETY: `Device` is a transparent wrapper of a type that doesn't depend on `Device`'s generic
+// argument.
+kernel::impl_device_context_deref!(unsafe { Device });
+kernel::impl_device_context_into_aref!(Device);
+
+// SAFETY: Instances of `Device` are always reference-counted.
+unsafe impl AlwaysRefCounted for Device {
+    fn inc_ref(&self) {
+        self.as_ref().inc_ref();
+    }
+
+    unsafe fn dec_ref(obj: NonNull<Self>) {
+        // SAFETY: The safety requirements guarantee that the refcount is non-zero.
+        unsafe { bindings::serdev_device_put(obj.cast().as_ptr()) }
+    }
+}
+
+impl<Ctx: device::DeviceContext> AsRef<device::Device<Ctx>> for Device<Ctx> {
+    fn as_ref(&self) -> &device::Device<Ctx> {
+        // SAFETY: By the type invariant of `Self`, `self.as_raw()` is a pointer to a valid
+        // `struct serdev_device`.
+        let dev = unsafe { &raw mut (*self.as_raw()).dev };
+
+        // SAFETY: `dev` points to a valid `struct device`.
+        unsafe { device::Device::from_raw(dev) }
+    }
+}
+
+// SAFETY: A `Device` is always reference-counted and can be released from any thread.
+unsafe impl Send for Device {}
+
+// SAFETY: `Device` can be shared among threads because all methods of `Device`
+// (i.e. `Device<Normal>) are thread safe.
+unsafe impl Sync for Device {}

-- 
2.52.0



^ permalink raw reply related

* [PATCH v4 1/4] rust: devres: return reference in `devres::register`
From: Markus Probst via B4 Relay @ 2026-04-11 15:10 UTC (permalink / raw)
  To: Rob Herring, Greg Kroah-Hartman, Jiri Slaby, Miguel Ojeda,
	Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
	Alice Ryhl, Trevor Gross, Danilo Krummrich, Kari Argillander,
	Rafael J. Wysocki, Viresh Kumar, Boqun Feng, David Airlie,
	Simona Vetter, Boqun Feng
  Cc: linux-serial, linux-kernel, rust-for-linux, linux-pm, driver-core,
	dri-devel, Markus Probst
In-Reply-To: <20260411-rust_serdev-v4-0-845e960c6627@posteo.de>

From: Markus Probst <markus.probst@posteo.de>

Return the reference to the initialized data in the `devres::register`
function.

This is needed in a following commit (rust: add basic serial device bus
abstractions).

Signed-off-by: Markus Probst <markus.probst@posteo.de>
---
 rust/kernel/cpufreq.rs    |  3 ++-
 rust/kernel/devres.rs     | 15 +++++++++++++--
 rust/kernel/drm/driver.rs |  3 ++-
 3 files changed, 17 insertions(+), 4 deletions(-)

diff --git a/rust/kernel/cpufreq.rs b/rust/kernel/cpufreq.rs
index f5adee48d40c..31bf7e685097 100644
--- a/rust/kernel/cpufreq.rs
+++ b/rust/kernel/cpufreq.rs
@@ -1052,7 +1052,8 @@ pub fn new_foreign_owned(dev: &Device<Bound>) -> Result
     where
         T: 'static,
     {
-        devres::register(dev, Self::new()?, GFP_KERNEL)
+        devres::register(dev, Self::new()?, GFP_KERNEL)?;
+        Ok(())
     }
 }
 
diff --git a/rust/kernel/devres.rs b/rust/kernel/devres.rs
index 6afe196be42c..f882bace8601 100644
--- a/rust/kernel/devres.rs
+++ b/rust/kernel/devres.rs
@@ -326,15 +326,26 @@ fn register_foreign<P>(dev: &Device<Bound>, data: P) -> Result
 /// }
 ///
 /// fn from_bound_context(dev: &Device<Bound>) -> Result {
-///     devres::register(dev, Registration::new(), GFP_KERNEL)
+///     devres::register(dev, Registration::new(), GFP_KERNEL)?;
+///     Ok(())
 /// }
 /// ```
-pub fn register<T, E>(dev: &Device<Bound>, data: impl PinInit<T, E>, flags: Flags) -> Result
+pub fn register<'a, T, E>(
+    dev: &'a Device<Bound>,
+    data: impl PinInit<T, E>,
+    flags: Flags,
+) -> Result<&'a T>
 where
     T: Send + 'static,
     Error: From<E>,
 {
     let data = KBox::pin_init(data, flags)?;
 
+    let data_ptr = &raw const *data;
+
     register_foreign(dev, data)
+        // SAFETY: `dev` is valid for the lifetime of 'a. As long as there is a reference to
+        // `Device<Bound>`, it is guaranteed that the device is not unbound and data has not been
+        // dropped. Thus `data_ptr` is also valid for the lifetime of 'a.
+        .map(|()| unsafe { &*data_ptr })
 }
diff --git a/rust/kernel/drm/driver.rs b/rust/kernel/drm/driver.rs
index e09f977b5b51..51e0c7e30cc2 100644
--- a/rust/kernel/drm/driver.rs
+++ b/rust/kernel/drm/driver.rs
@@ -145,7 +145,8 @@ pub fn new_foreign_owned(
 
         let reg = Registration::<T>::new(drm, flags)?;
 
-        devres::register(dev, reg, GFP_KERNEL)
+        devres::register(dev, reg, GFP_KERNEL)?;
+        Ok(())
     }
 
     /// Returns a reference to the `Device` instance for this registration.

-- 
2.52.0



^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox