From nobody Sun Feb 8 20:33:14 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id ACE053358B5; Mon, 26 Jan 2026 12:22:10 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769430130; cv=none; b=JC4VaQfmagxHMzwZEdhjOJfX6LZtUq1Yc2cTHeuanYYxCE1JG26bOU/du2C8sJIh63QfVuxef4G+AlGvtlJAZC6OYa5tchMe+RGF+aR7MD1s/uBp5gCg4ubcctWgr7uFdP0bREpsKZJSSWFIJOpIg3F5IVzoAPm+nG9kni0fER8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769430130; c=relaxed/simple; bh=KSWmRjOoZPERCjQJyRqqDCBiCUV9pTU8pgWLSF7N9WU=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=EAvC8uCGJiWjlIUcKr8tgJINqm1ZoMVZ4fKgOkTeeUQQ66awxjDCbREK7ySfFeLodyjzH0sfJpvK7hShZue88GAhZVDbcFCx3NykHHGOk2FbetqdfIwqMKgtbiu/5guIhd5V1tmqTVRnYBHd8w3j40ALjNOxbxtSlfFVV20BJo4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=fb6AQgGk; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="fb6AQgGk" Received: by smtp.kernel.org (Postfix) with ESMTPS id 69EC0C2BC86; Mon, 26 Jan 2026 12:22:10 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1769430130; bh=KSWmRjOoZPERCjQJyRqqDCBiCUV9pTU8pgWLSF7N9WU=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=fb6AQgGkn9OVE71Bp4E6lbeGdn2076Qaqjp551tRGrms53SdS04o3UBeO3ijJfin8 AOQPhZWlHG/lnv9pbysrsmP79lWIHSEVq7i2WoYNFcpzIe7/s41NjYuDvAHebqax3Z tY4qNWuyYKbKmDZj8itSbxnp61Pv/HsmBCAKnY+WzzETkAVHeLASWF2Vz1XF8KQNQh OpAQxK8WOH7ftt5ebTzQiqpgV1yEOG7wS/a5pxEmKtcfaJaBYiB2Y1kW/i2FNIn6YW nwl/uR4+7T5U3DrBfbhcgTXSlXmtvoTcr324Keg4gVZ6XTtOSU5kp9NVXVOovdQ/3c A5+mrxwSfh9vQ== Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 605CACF65E7; Mon, 26 Jan 2026 12:22:10 +0000 (UTC) From: SeungJong Ha via B4 Relay Date: Mon, 26 Jan 2026 12:22:10 +0000 Subject: [PATCH RFC 3/3] char: rttyprintk: add Rust TTY printk driver Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260126-rust-tty-printk-driver-v1-3-28604e7e100e@gmail.com> References: <20260126-rust-tty-printk-driver-v1-0-28604e7e100e@gmail.com> In-Reply-To: <20260126-rust-tty-printk-driver-v1-0-28604e7e100e@gmail.com> To: Miguel Ojeda , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , Arnd Bergmann , Greg Kroah-Hartman Cc: rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, SeungJong Ha X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=ed25519-sha256; t=1769430128; l=7897; i=engineer.jjhama@gmail.com; s=20260103; h=from:subject:message-id; bh=a2DRGFsUADXLosl2ifUZW9MiZCCY2Vpnl3B7hhzcS3s=; b=SrHjZWCLbU+bFkMTCRhqi6lLL4GS37SdtqxBHuAv5wz9+GYL9gQLMDzJLiOgy9cTAibetIEXA aQy0JHLOBzVA8mUOJmBcs+je03mqo4kwxlqKz2fgz0trjvyIP6cm2+k X-Developer-Key: i=engineer.jjhama@gmail.com; a=ed25519; pk=G5nmjm+RTiWBpyCvc5xjR1b3li/2zipLSMyz+T4fj5E= X-Endpoint-Received: by B4 Relay for engineer.jjhama@gmail.com/20260103 with auth_id=590 X-Original-From: SeungJong Ha Reply-To: engineer.jjhama@gmail.com From: SeungJong Ha Add a Rust implementation of the ttyprintk driver, demonstrating the new TTY Rust abstractions. This driver creates /dev/rttyprintk which allows user messages to be written to the kernel log via printk, similar to the existing C ttyprintk driver. Features: - Uses the new kernel::tty abstractions for type-safe TTY operations - Implements TtyDevice with open/close/write/write_room/hangup callbacks - Uses DriverPort with SpinLock-protected TpkState for thread-safe buffer management - Buffers up to 508 bytes per line, flushing on newline or buffer full - Messages are logged at KERN_INFO level with [U] prefix The driver serves as a reference implementation for Rust TTY drivers and demonstrates: - Pin-initialization patterns for TTY drivers and ports - Arc-based shared state between driver callbacks - Safe handling of driver_data and driver_state - Integration with kernel printk for output Signed-off-by: SeungJong Ha --- drivers/char/Kconfig | 13 ++++ drivers/char/Makefile | 1 + drivers/char/rttyprintk.rs | 180 +++++++++++++++++++++++++++++++++++++++++= ++++ 3 files changed, 194 insertions(+) diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index d2cfc584e202..66a482024ff4 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -31,6 +31,19 @@ config TTY_PRINTK_LEVEL help Printk log level to use for ttyprintk messages. =20 +config TTY_DEV_RUST_PRINTK + tristate "Rust TTY driver to output user messages via printk" + depends on RUST && TTY + default n + help + If you say Y here, the support for writing user messages (i.e. + console messages) via printk is available, implemented in Rust. + + This is the Rust implementation of the ttyprintk driver, + demonstrating rkernel domain isolation for kernel modules. + + If unsure, say N. + config PRINTER tristate "Parallel printer support" depends on PARPORT diff --git a/drivers/char/Makefile b/drivers/char/Makefile index 1291369b9126..608bb6d724a0 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -5,6 +5,7 @@ =20 obj-y +=3D mem.o random.o obj-$(CONFIG_TTY_PRINTK) +=3D ttyprintk.o +obj-$(CONFIG_TTY_DEV_RUST_PRINTK) +=3D rttyprintk.o obj-y +=3D misc.o obj-$(CONFIG_TEST_MISC_MINOR) +=3D misc_minor_kunit.o obj-$(CONFIG_ATARI_DSP56K) +=3D dsp56k.o diff --git a/drivers/char/rttyprintk.rs b/drivers/char/rttyprintk.rs new file mode 100644 index 000000000000..f5394f605cf4 --- /dev/null +++ b/drivers/char/rttyprintk.rs @@ -0,0 +1,180 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Rust TTY printk driver. +//! +//! Allows user messages to be written to the kernel log via printk. + +use kernel::{ + bindings, + c_str, + new_spinlock, + prelude::*, + sync::{ + Arc, + SpinLock, + }, + tty::{ + self, + port, + DriverPort, + Tty, + }, +}; + +module! { + type: RttyPrintk, + name: "rttyprintk", + authors: ["SeungJong Ha"], + description: "Rust TTY driver to output user messages via printk", + license: "GPL", +} + +const TPK_STR_SIZE: usize =3D 508; +const TPK_MAX_ROOM: u32 =3D 4096; + +/// Mutable state protected by spinlock. +struct TpkState { + curr: usize, + buffer: [u8; TPK_STR_SIZE + 4], +} + +impl TpkState { + const fn new() -> Self { + Self { + curr: 0, + buffer: [0u8; TPK_STR_SIZE + 4], + } + } + + fn flush(&mut self) { + if self.curr > 0 { + self.buffer[self.curr] =3D 0; + // SAFETY: buffer is null-terminated. + unsafe { + bindings::_printk(c_str!("\x016[U] %s\n").as_char_ptr(), s= elf.buffer.as_ptr()); + } + self.curr =3D 0; + } + } + + fn do_write(&mut self, buf: &[u8]) -> usize { + for (i, &c) in buf.iter().enumerate() { + if self.curr >=3D TPK_STR_SIZE { + self.buffer[self.curr] =3D b'\\'; + self.curr +=3D 1; + self.flush(); + } + + match c { + b'\r' =3D> { + self.flush(); + if buf.get(i + 1) =3D=3D Some(&b'\n') { + continue; + } + } + b'\n' =3D> self.flush(), + _ =3D> { + self.buffer[self.curr] =3D c; + self.curr +=3D 1; + } + } + } + buf.len() + } +} + +struct TpkPortOps; +type TpkPort =3D DriverPort; + +#[vtable] +impl port::Operations for TpkPortOps { + type PortData =3D SpinLock; + + fn shutdown(port: &TpkPort) { + port.data().lock().flush(); + } +} + +struct TpkDevice; +type TpkTty =3D Tty, Arc>; + +#[vtable] +impl tty::Operations for TpkDevice { + type DriverData =3D Arc; + type DriverState =3D Arc; + type PortOps =3D TpkPortOps; + + fn open(tty: &TpkTty, _file: *mut bindings::file) -> Result<()> { + // Clone the Arc from driver_state and set it as driver_data. + // This mirrors the original ttyprintk.c pattern where tty->driver= _data is set + // to the port in open(). In practice, since Arc allows shared acc= ess and + // SpinLock protects the state, we could just use driver_state() d= irectly. + // However, we follow the original C code structure for consistenc= y. + let port =3D tty.driver_state().ok_or(ENXIO)?; + tty.set_driver_data(port); + Ok(()) + } + + fn close(tty: &TpkTty, _file: *mut bindings::file) { + // Take and drop driver_data, mirroring tpk_close() which sets + // tty->driver_data =3D NULL. The Arc will be dropped, decrementin= g refcount. + tty.take_driver_data(); + } + + fn write(tty: &TpkTty, buf: &[u8]) -> Result { + // Access port via driver_data (set in open), following original t= typrintk.c. + // SpinLock inside TpkState protects concurrent writes. + let port =3D tty.driver_data().ok_or(ENXIO)?; + Ok(port.data().lock().do_write(buf)) + } + + fn write_room(_tty: &TpkTty) -> u32 { + TPK_MAX_ROOM + } + + fn hangup(_tty: &TpkTty) {} +} + +struct RttyPrintk { + #[allow(dead_code)] + driver: Pin>>, +} + +impl kernel::Module for RttyPrintk { + fn init(module: &'static kernel::ThisModule) -> Result { + pr_info!("Rust TTY printk driver initializing\n"); + + let port =3D Arc::pin_init( + TpkPort::new(new_spinlock!(TpkState::new(), "tpk_lock")), + GFP_KERNEL, + )?; + + let opts =3D tty::Options { + driver_name: c_str!("rttyprintk"), + name: c_str!("rttyprintk"), + major: tty::TTYAUX_MAJOR, + minor_start: 4, + driver_type: tty::DriverType::Console, + flags: tty::flags::RESET_TERMIOS | tty::flags::REAL_RAW | tty:= :flags::UNNUMBERED_NODE, + }; + + // link_port needs a reference, set_driver_state takes ownership o= f the Arc. + let builder =3D tty::TtyDriverBuilder::::new(opts, modu= le)? + .link_port(&port, 0) + .set_driver_state(port); + + let driver =3D KBox::pin_init(builder.build(), GFP_KERNEL)?; + + pr_info!("Rust TTY printk driver registered at /dev/rttyprintk\n"); + + Ok(Self { driver }) + } +} + +impl Drop for RttyPrintk { + fn drop(&mut self) { + // Reclaim the driver state before the driver is unregistered. + self.driver.take_driver_state(); + pr_info!("Rust TTY printk driver unloading\n"); + } +} --=20 2.43.0