From nobody Sun Feb 8 05:42:06 2026 Received: from DM5PR21CU001.outbound.protection.outlook.com (mail-centralusazon11011013.outbound.protection.outlook.com [52.101.62.13]) (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 71E8633D6EB; Thu, 22 Jan 2026 20:45:32 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=52.101.62.13 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769114742; cv=fail; b=Yp5TFeijIYczH6jf05gJJnJ3bqu9ycVBMGik7Wiu0ujOhmrfO7Qby1EksCeHXZp2MDpFfkTvBjL4R3MaGELEjNlu0NtwL7x0+M1SR011n1ZOdyap3WAPJGSsolTMBpX/t4fG5zmnqrBJoYkdK4fh0xl/1pMulDkLdeC1/uHW7Rw= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769114742; c=relaxed/simple; bh=QLZBCk5n1FgQjNa1HdggTlHl1LIvFcpwirDZAoBac0U=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=V4y1pEH9jAEiA/JzlZfQYiE5reoIKq1COiz8ccpTYjlqmh0UzTg5tl701Mwxk/+tYt+5eSeo68z3C+D0tyeNdVpwaTKsuhi4+NauXocpTib6hRgErrkedr0krYNkYDRbffcXJtxTMB8RILsh7UDGMQ0Awxb6BiT4IOtMdff+jbk= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=nvidia.com; spf=fail smtp.mailfrom=nvidia.com; dkim=pass (2048-bit key) header.d=Nvidia.com header.i=@Nvidia.com header.b=rJclaDbO; arc=fail smtp.client-ip=52.101.62.13 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=nvidia.com Authentication-Results: smtp.subspace.kernel.org; spf=fail smtp.mailfrom=nvidia.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=Nvidia.com header.i=@Nvidia.com header.b="rJclaDbO" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=s/5ib50F0iz8JKiHkKxkJS10pDYhwiXfTjkY1qvz9KB2f/FdcWC/s9gqJ3v9OwRi+5Ftt22oFAfmubFbU6KNyyE0qtbM0l4RMq6nNuq0UyZmGFVY1lnveel/zp7g/A0mr+wr0gyn1tA45272thDv3BJZ8Y7UAAbMKcM6yfFGZ7prM1VlbdvtFhFybhROLPDhRsT9iPtE4Z3cI4FQlmCX9MjkleqX8vRcHKiF3/kLyWAkxNQexziuTdWUkLjzz9klYmPH10e6GOi/48yQTV7c4OfDOg55FLJEt5MCyrcmXS7MzViObrjLgTKi7hNf+yGXE/K8Gk6KkW8ej2NTjHjy+A== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector10001; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=CkhklPhIXpJDHdgb+N+P3aeQFyW8A63jllzosny7reI=; b=Vheo/wPWfsIba1yrg2dj3/06PrQWe6ndgsIUQfg7/tRUFgyuzuRTFzBFiy3iXIO6OCsMRdZ4hMQ/HVOaQ9o4j5AX8UD6lVwW9OkbL3TTxTaVnDF7JEjq6dm0kEIiqdutinpcSrzydHb6p7V10CNxBhiHu+9TRzxUXFVy3KNiudaPGKsQq/UIPA3xRPQANf9OtujNpHQGzsuEPpAxApBDQayarOPXkCE9fU8ohrsSPw4ty4sfD5HL9L0v3PGtmoz2CzU/sG8CTLFg5sPHQANXcgsTHi2XDTXSueJ/gVJh/whp6QTcfVxpigd/3xI3fhiTlLWcSXzfyMTaIB+RtR80BA== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass (sender ip is 216.228.117.160) smtp.rcpttodomain=vger.kernel.org smtp.mailfrom=nvidia.com; dmarc=pass (p=reject sp=reject pct=100) action=none header.from=nvidia.com; dkim=none (message not signed); arc=none (0) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=Nvidia.com; s=selector2; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=CkhklPhIXpJDHdgb+N+P3aeQFyW8A63jllzosny7reI=; b=rJclaDbO5XLmTWnifJMgoBwAH3f2G3ZYc/wrH3GYAxl4DHIp7O+MM/Jgqdry6qawD4omhC/czNagSet7ClGNAff/4IL5SvJ4pb5ZU363Qe4IDL5VZYgJRdvKOecCk/8rmGCfPoYAzv+Tf3uHEPLCMYzdX3ISlg6uDTqCVrK29DtbbOio0HdeMpxRNcoX97f9+Znzu4Q7d2XS8Dkgj1pTmO8qPFis+y6cOGR3WUTJCQa6eoWf5iVz34i23Y65lcPhxrV9t+stW11X3Yc2jV9Co5DR61gfbOnyboFX8WkMO1wIHDDGKI4E1NAbM0M1sn2QUx9He0dEEgkr51tPxls8nA== Received: from BLAPR05CA0044.namprd05.prod.outlook.com (2603:10b6:208:335::27) by IA1PR12MB8588.namprd12.prod.outlook.com (2603:10b6:208:44f::19) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.9542.11; Thu, 22 Jan 2026 20:45:25 +0000 Received: from BN2PEPF00004FC0.namprd04.prod.outlook.com (2603:10b6:208:335:cafe::f3) by BLAPR05CA0044.outlook.office365.com (2603:10b6:208:335::27) with Microsoft SMTP Server (version=TLS1_3, cipher=TLS_AES_256_GCM_SHA384) id 15.20.9564.3 via Frontend Transport; Thu, 22 Jan 2026 20:45:24 +0000 X-MS-Exchange-Authentication-Results: spf=pass (sender IP is 216.228.117.160) smtp.mailfrom=nvidia.com; dkim=none (message not signed) header.d=none;dmarc=pass action=none header.from=nvidia.com; Received-SPF: Pass (protection.outlook.com: domain of nvidia.com designates 216.228.117.160 as permitted sender) receiver=protection.outlook.com; client-ip=216.228.117.160; helo=mail.nvidia.com; pr=C Received: from mail.nvidia.com (216.228.117.160) by BN2PEPF00004FC0.mail.protection.outlook.com (10.167.243.186) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.9564.3 via Frontend Transport; Thu, 22 Jan 2026 20:45:25 +0000 Received: from rnnvmail201.nvidia.com (10.129.68.8) by mail.nvidia.com (10.129.200.66) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.2562.20; Thu, 22 Jan 2026 12:42:47 -0800 Received: from rnnvmail205.nvidia.com (10.129.68.10) by rnnvmail201.nvidia.com (10.129.68.8) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.2562.20; Thu, 22 Jan 2026 12:42:47 -0800 Received: from inno-vm-xubuntu (10.127.8.13) by mail.nvidia.com (10.129.68.10) with Microsoft SMTP Server id 15.2.2562.20 via Frontend Transport; Thu, 22 Jan 2026 12:42:40 -0800 From: Zhi Wang To: , , CC: , , , , , , , , , , , , , , , , , , , , , , , , , Zhi Wang , Jason Gunthorpe Subject: [PATCH v2 1/2] rust: introduce abstractions for fwctl Date: Thu, 22 Jan 2026 22:42:30 +0200 Message-ID: <20260122204232.15988-2-zhiw@nvidia.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260122204232.15988-1-zhiw@nvidia.com> References: <20260122204232.15988-1-zhiw@nvidia.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-NV-OnPremToCloud: ExternallySecured X-EOPAttributedMessage: 0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: BN2PEPF00004FC0:EE_|IA1PR12MB8588:EE_ X-MS-Office365-Filtering-Correlation-Id: b82b2c06-a35a-424e-b222-08de59f72bb5 X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|36860700013|82310400026|7416014|1800799024|376014; X-Microsoft-Antispam-Message-Info: =?us-ascii?Q?E9pxa/i0N2tP8S0T26ZDpr9SnFbL9Eolo7Jx29JTUh72LvdjE7OrhipKxFRV?= =?us-ascii?Q?jc9jD03TyAeAfdCumLC6oK3fo5M0EJa0m+x5b9AXZtQg/wEx+s3P7vd1AIGx?= =?us-ascii?Q?vpwqXk3QxGWNRX1H9k2fgzx1QN+oykitRXgbOCQOle0Gb4UBzd/cwWBfDqog?= =?us-ascii?Q?0pbo0jc5gFySpbWmE1SOMgqXTYR8n2Hs2zmIiAzbvEPgpgs4j3ektthdRBZ1?= =?us-ascii?Q?yQjqgyd/Rb4o4qsI8UlwZXv1o1I5xM1whSxzzda1TbD7f1HTX4iuULwwFIut?= =?us-ascii?Q?F0V+FaWlJb0sKYlqdx9w9TSsOMhQrHaM+G4AIV4x0YJcSluKNV8aZy25zo3l?= =?us-ascii?Q?BK6gRvsU/Ezg0QxVJJxgoIqLTAsLm9nh28+LiddELWF0Hyazg7KGNyNuKLzW?= =?us-ascii?Q?dyF9P9cNWfqk7Rh8/3CfvD6p9we7SLgpvCCY1KNTlRrblBAUtXvD0QZDeiA+?= =?us-ascii?Q?aQESpzTX3TUZGbw1uohUW5RVt/4Itc9I2NJGUtNDopDRaLiubOp9/BlkDcoc?= =?us-ascii?Q?YLcYSRT67JDKtVfAnr6qzGWfPOVljufJ2Ep+KgP9k9B++/hJ8Fp5kOOpptUS?= =?us-ascii?Q?gY4eM6s0QTVqLUJ1JJR8rQxfFEPoMPMualKN8pe03U1viOtj/9Ol/RL4wX9N?= =?us-ascii?Q?kxMjCgtu/rLTdde4d3oRIKkc1sIFbPTZJQEsORireD1KnU52XYiIagk66cg7?= =?us-ascii?Q?MnA8Qln5YEkGIqE40NxvysXMiVSdruu6Vag7ZCyzNHboliaex0IS6RDATZnx?= =?us-ascii?Q?tPHe1edfNsCa4z0eX055/U8fTSJaQWk5k4ReWRgIYcU006c4OeykHogjxRtY?= =?us-ascii?Q?X9FijADXkMIHCwMQ0TvXZiKJrxWndTZYiLEZ91tg1ar2Lx3OrUWczG7xj4Ip?= =?us-ascii?Q?J56cc/NkCVqmKh+ky80wiBJYvFouKHoYnSK/Z+8drGCsJoH0Jkw1SVLkE4uw?= =?us-ascii?Q?n0GauBM9DTpufhbuq/6+TRWM2KaDrsq1JnHRaoSX8P8m7SautONPJsDNPZhN?= =?us-ascii?Q?uvliuKRxytvnvrbgKymKXVKqPb0Lh8U7Keg71+ckSH2qfUq/c6KWr6WHXBo9?= =?us-ascii?Q?Jns2XGxaELAUNQl6EZhHkJD9QHmipeXpXPZAIFDRckuW2JOvj98yz53qZ1lF?= =?us-ascii?Q?PV2Khfs481ZaG8sKa/VPm9OEyoA7uJPLKovGUzrm9/tfT2lW8z5d9nFIx0yi?= =?us-ascii?Q?xH8eUSMT0WHxyA9B6A18xvoTuF5qtNaR5MxkUKgbbhZbWGGdtKDvmvchq1PU?= =?us-ascii?Q?QKHjQH05I34g5fjUJC9TL52I2S2VO975GOkeBdJH1WmXjq5PJALFwQykuH7m?= =?us-ascii?Q?txErHDld5A+lukCjcFS2QTU7A/QlNMjWYn++Pq7JATmll2Y0mKaJ/Z2geeHr?= =?us-ascii?Q?EH0PlabMHWZg6fFTa3AZZOzz2AWDqgabLLO9EJbn7IVd6GiDUlieiX1uEK7F?= =?us-ascii?Q?+6mrjbby9ExB3GUVvqNAvknYDDeJpDACoytVe8ExuPf3xNM5jbwtmM6OdcU0?= =?us-ascii?Q?DQ1TguhbzLSjxBP1b027ozQrIGqRCYMP2hweA1On0PrFUKtZTUTwcrOkr0qq?= =?us-ascii?Q?sC26XFWudVr25oaVG9sXZ1+8NtMDjGY2zF0WaDnJzOCI/1tx3WKpUrLev8Ny?= =?us-ascii?Q?YpuNdaLxbogaqJvf7CPjSIIeEPr5L/c5tB/Rujqw8S8v20ql50lzmP1NyqRb?= =?us-ascii?Q?dqRYEw=3D=3D?= X-Forefront-Antispam-Report: CIP:216.228.117.160;CTRY:US;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:mail.nvidia.com;PTR:dc6edge1.nvidia.com;CAT:NONE;SFS:(13230040)(36860700013)(82310400026)(7416014)(1800799024)(376014);DIR:OUT;SFP:1101; X-OriginatorOrg: Nvidia.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 22 Jan 2026 20:45:25.6520 (UTC) X-MS-Exchange-CrossTenant-Network-Message-Id: b82b2c06-a35a-424e-b222-08de59f72bb5 X-MS-Exchange-CrossTenant-Id: 43083d15-7273-40c1-b7db-39efd9ccc17a X-MS-Exchange-CrossTenant-OriginalAttributedTenantConnectingIp: TenantId=43083d15-7273-40c1-b7db-39efd9ccc17a;Ip=[216.228.117.160];Helo=[mail.nvidia.com] X-MS-Exchange-CrossTenant-AuthSource: BN2PEPF00004FC0.namprd04.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Anonymous X-MS-Exchange-CrossTenant-FromEntityHeader: HybridOnPrem X-MS-Exchange-Transport-CrossTenantHeadersStamped: IA1PR12MB8588 Content-Type: text/plain; charset="utf-8" Introduce safe wrappers around `struct fwctl_device` and `struct fwctl_uctx`, allowing rust drivers to register fwctl devices and implement their control and RPC logic in safe rust. Cc: Danilo Krummrich Cc: Jason Gunthorpe Signed-off-by: Zhi Wang --- drivers/fwctl/Kconfig | 12 + include/uapi/fwctl/fwctl.h | 1 + rust/bindings/bindings_helper.h | 1 + rust/helpers/fwctl.c | 17 ++ rust/helpers/helpers.c | 3 +- rust/kernel/fwctl.rs | 456 ++++++++++++++++++++++++++++++++ rust/kernel/lib.rs | 2 + 7 files changed, 491 insertions(+), 1 deletion(-) create mode 100644 rust/helpers/fwctl.c create mode 100644 rust/kernel/fwctl.rs diff --git a/drivers/fwctl/Kconfig b/drivers/fwctl/Kconfig index b5583b12a011..d8538249f3ae 100644 --- a/drivers/fwctl/Kconfig +++ b/drivers/fwctl/Kconfig @@ -8,6 +8,18 @@ menuconfig FWCTL manipulating device FLASH, debugging, and other activities that don't fit neatly into an existing subsystem. =20 +config RUST_FWCTL_ABSTRACTIONS + bool "Rust fwctl abstractions" + depends on RUST + select FWCTL + help + This enables the Rust abstractions for the fwctl device firmware + access framework. It provides safe wrappers around struct fwctl_device + and struct fwctl_uctx, allowing Rust drivers to register fwctl devices + and implement their control and RPC logic in safe Rust. + + If unsure, say N. + if FWCTL config FWCTL_MLX5 tristate "mlx5 ConnectX control fwctl driver" diff --git a/include/uapi/fwctl/fwctl.h b/include/uapi/fwctl/fwctl.h index 716ac0eee42d..eea1020ad180 100644 --- a/include/uapi/fwctl/fwctl.h +++ b/include/uapi/fwctl/fwctl.h @@ -45,6 +45,7 @@ enum fwctl_device_type { FWCTL_DEVICE_TYPE_MLX5 =3D 1, FWCTL_DEVICE_TYPE_CXL =3D 2, FWCTL_DEVICE_TYPE_PDS =3D 4, + FWCTL_DEVICE_TYPE_RUST_FWCTL_TEST =3D 8, }; =20 /** diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helpe= r.h index 9fdf76ca630e..2c50d5bab0cf 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -56,6 +56,7 @@ #include #include #include +#include #include #include #include diff --git a/rust/helpers/fwctl.c b/rust/helpers/fwctl.c new file mode 100644 index 000000000000..bb4a028e7afb --- /dev/null +++ b/rust/helpers/fwctl.c @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include + +#if IS_ENABLED(CONFIG_RUST_FWCTL_ABSTRACTIONS) + +struct fwctl_device *rust_helper_fwctl_get(struct fwctl_device *fwctl) +{ + return fwctl_get(fwctl); +} + +void rust_helper_fwctl_put(struct fwctl_device *fwctl) +{ + fwctl_put(fwctl); +} + +#endif diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c index 79c72762ad9c..19a505473bef 100644 --- a/rust/helpers/helpers.c +++ b/rust/helpers/helpers.c @@ -27,8 +27,9 @@ #include "dma.c" #include "drm.c" #include "err.c" -#include "irq.c" #include "fs.c" +#include "fwctl.c" +#include "irq.c" #include "io.c" #include "jump_label.c" #include "kunit.c" diff --git a/rust/kernel/fwctl.rs b/rust/kernel/fwctl.rs new file mode 100644 index 000000000000..4065c948784d --- /dev/null +++ b/rust/kernel/fwctl.rs @@ -0,0 +1,456 @@ +// SPDX-License-Identifier: GPL-2.0-only + +//! Abstractions for the fwctl. +//! +//! This module provides bindings for working with fwctl devices in kernel= modules. +//! +//! C header: [`include/linux/fwctl.h`] + +use crate::{ + bindings, + container_of, + device, + devres::Devres, + prelude::*, + types::{ + ARef, + Opaque, // + }, // +}; +use core::{ + marker::PhantomData, + ptr::NonNull, + slice, // +}; + +/// Represents a fwctl device type. +/// +/// This enum corresponds to the C `enum fwctl_device_type` and is used to= identify +/// the specific firmware control interface implemented by a device. +#[repr(u32)] +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum DeviceType { + /// Error/invalid device type. + Error =3D bindings::fwctl_device_type_FWCTL_DEVICE_TYPE_ERROR, + /// MLX5 device type. + Mlx5 =3D bindings::fwctl_device_type_FWCTL_DEVICE_TYPE_MLX5, + /// CXL device type. + Cxl =3D bindings::fwctl_device_type_FWCTL_DEVICE_TYPE_CXL, + /// PDS device type. + Pds =3D bindings::fwctl_device_type_FWCTL_DEVICE_TYPE_PDS, + /// Rust fwctl test device type. + RustFwctlTest =3D bindings::fwctl_device_type_FWCTL_DEVICE_TYPE_RUST_F= WCTL_TEST, +} + +impl From for u32 { + fn from(device_type: DeviceType) -> Self { + device_type as u32 + } +} + +/// A fwctl device. +/// +/// Wraps the C `struct fwctl_device` and manages its reference count. +/// +/// # Invariants +/// +/// Instances of this type represent a valid `struct fwctl_device` created= by the C portion +/// of the kernel. +#[repr(transparent)] +pub struct Device(Opaque); + +impl Device { + /// # Safety + /// + /// `ptr` must be a valid pointer to a `struct fwctl_device`. + unsafe fn from_raw<'a>(ptr: *mut bindings::fwctl_device) -> &'a Self { + // CAST: `Self` is a transparent wrapper around `bindings::fwctl_d= evice`. + // SAFETY: By the safety requirement, `ptr` is valid. + unsafe { &*ptr.cast() } + } + + fn as_raw(&self) -> *mut bindings::fwctl_device { + self.0.get() + } + + /// Returns the parent device. + pub fn parent(&self) -> &device::Device { + // SAFETY: By the type invariant, `self.as_raw()` is a valid point= er to a + // `struct fwctl_device`, which always has a parent device. + let parent_dev =3D unsafe { (*self.as_raw()).dev.parent }; + // SAFETY: `parent_dev` points to a valid `struct device`. The par= ent device + // is guaranteed to be valid for the lifetime of the fwctl_device. + unsafe { device::Device::from_raw(parent_dev) } + } +} + +impl AsRef for Device { + fn as_ref(&self) -> &device::Device { + // SAFETY: By the type invariant of `Self`, `self.as_raw()` is a p= ointer to a valid + // `struct fwctl_device`. + let dev =3D unsafe { core::ptr::addr_of_mut!((*self.as_raw()).dev)= }; + + // SAFETY: `dev` points to a valid `struct device`. + unsafe { device::Device::from_raw(dev) } + } +} + +// SAFETY: The fwctl_device is reference counted through the embedded `str= uct device`, +// and inc_ref/dec_ref use fwctl_get/fwctl_put to manage its lifetime. +unsafe impl crate::sync::aref::AlwaysRefCounted for Device { + fn inc_ref(&self) { + // SAFETY: The existence of a shared reference guarantees that the= refcount is non-zero. + // `self.as_raw()` is a valid pointer to a `struct fwctl_device`. + unsafe { bindings::fwctl_get(self.as_raw()) }; + } + + unsafe fn dec_ref(obj: NonNull) { + // CAST: `Self` is a transparent wrapper of `bindings::fwctl_devic= e`. + let fwctl: *mut bindings::fwctl_device =3D obj.cast().as_ptr(); + + // SAFETY: By the type invariant, `fwctl` is a valid pointer to a = `struct fwctl_device`. + unsafe { bindings::fwctl_put(fwctl) }; + } +} + +// 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 are th= read-safe. +unsafe impl Sync for Device {} + +/// The registration of a fwctl device. +/// +/// This type represents the registration of a [`struct fwctl_device`]. It= should always be +/// used within a [`Devres`] wrapper to ensure proper lifetime management.= When dropped, +/// the fwctl device will be unregistered and freed. +/// +/// [`Devres`] guarantees that the device is unregistered before the paren= t device is unbound. +/// +/// [`struct fwctl_device`]: srctree/include/linux/device/fwctl.h +pub struct Registration { + device: ARef, + _marker: PhantomData, +} + +impl Registration { + /// Allocate and register a new fwctl device under the given parent de= vice. + /// + /// The returned [`Devres`] wrapper ensures that the fwctl device is u= nregistered + /// before the parent device is unbound. + pub fn new<'a>( + parent: &'a device::Device, + ) -> impl PinInit, Error> + 'a + where + T: 'a, + { + pin_init::pin_init_scope(move || { + let ops =3D core::ptr::from_ref::(&VTable= ::::VTABLE).cast_mut(); + + // SAFETY: `_fwctl_alloc_device()` allocates a new `fwctl_devi= ce` + // and initializes its embedded `struct device`. The `ops` poi= nter + // points to a static VTABLE that outlives the device. The par= ent + // device is guaranteed to be bound to a driver (Device= ), + // ensuring it remains valid during allocation. + let dev =3D unsafe { + bindings::_fwctl_alloc_device( + parent.as_raw(), + ops, + core::mem::size_of::(), + ) + }; + + if dev.is_null() { + return Err(ENOMEM); + } + + // SAFETY: dev is guaranteed to be a valid pointer from `_fwct= l_alloc_device()`. + let ret =3D unsafe { bindings::fwctl_register(dev) }; + if ret !=3D 0 { + // SAFETY: dev is guaranteed to be a valid pointer from `_= fwctl_alloc_device()`. + unsafe { + bindings::fwctl_put(dev); + } + return Err(Error::from_errno(ret)); + } + + // SAFETY: dev is guaranteed to be a valid pointer from `_fwct= l_alloc_device()`. + let device =3D unsafe { + let dev_ref =3D Device::from_raw(dev); + // SAFETY: We just verified dev is non-null above, and Dev= ice::from_raw + // returns a reference, so NonNull::new_unchecked is safe. + ARef::from_raw(NonNull::new_unchecked( + core::ptr::from_ref(dev_ref).cast_mut(), + )) + }; + + Ok(Devres::new( + parent, + Self { + device, + _marker: PhantomData, + }, + )) + }) + } + + fn as_raw(&self) -> *mut bindings::fwctl_device { + self.device.as_raw() + } +} + +impl Drop for Registration { + fn drop(&mut self) { + // SAFETY: `fwctl_unregister()` expects a valid registered device. + // By the type invariant, `self.device` holds a valid fwctl_device. + unsafe { + bindings::fwctl_unregister(self.as_raw()); + } + // The ARef will automatically call fwctl_put() when dropp= ed. + } +} + +// SAFETY: `Registration` can be sent to other threads because: +// - It only contains a `NonNull` pointer and a PhantomData = marker +// - The underlying C fwctl_device is thread-safe with internal locking +// - `Drop` calls `fwctl_unregister()/fwctl_put()` which are safe from any= sleepable context +unsafe impl Send for Registration {} + +// SAFETY: `Registration` can be shared between threads because: +// - It provides no methods for mutation (except Drop, which takes &mut se= lf) +// - The underlying C fwctl_device is protected by internal locking (regis= tration_lock) +// - Multiple threads can safely hold immutable references to the same Reg= istration +unsafe impl Sync for Registration {} + +/// Trait implemented by each Rust driver that integrates with the fwctl s= ubsystem. +/// +/// Each implementation corresponds to a specific device type and provides +/// the vtable used by the core `fwctl` layer to manage per-FD user contex= ts +/// and handle RPC requests. +pub trait Operations: Sized { + /// Driver user context type. + type UserCtx; + + /// fwctl device type. + const DEVICE_TYPE: DeviceType; + + /// Called when a new user context is opened by userspace. + fn open( + fwctl_uctx: &Opaque, + ) -> Result, Error>; + + /// Called when the user context is being closed. + fn close(uctx: &mut UserCtx); + + /// Return device or context information to userspace. + fn info(uctx: &mut UserCtx) -> Result, Error>; + + /// Called when a userspace RPC request is received. + fn fw_rpc( + uctx: &mut UserCtx, + scope: u32, + rpc_in: &mut [u8], + out_len: *mut usize, + ) -> Result>, Error>; +} + +/// Represents a per-FD user context (`struct fwctl_uctx`). +#[repr(C)] +#[pin_data] +pub struct UserCtx { + /// The core fwctl user context shared with the C implementation. + #[pin] + fwctl_uctx: Opaque, + + /// Driver-specific data associated with this user context. + #[pin] + uctx: T, +} + +impl UserCtx { + /// Converts a raw C pointer to `struct fwctl_uctx` into a reference t= o the + /// enclosing `UserCtx`. + /// + /// # Safety + /// * `ptr` must be a valid pointer to a `fwctl_uctx` that is embedded + /// inside an existing `UserCtx` instance. + /// * The caller must ensure that the lifetime of the returned referen= ce + /// does not outlive the underlying object managed on the C side. + unsafe fn from_raw<'a>(ptr: *mut bindings::fwctl_uctx) -> &'a mut Self= { + // SAFETY: `ptr` was originally created from a valid `UserCtx`. + // We cast through `Opaque` since `fwctl_uctx` is wrapped in `Opaq= ue`. + unsafe { &mut *container_of!(Opaque::cast_from(ptr), UserCtx, f= wctl_uctx).cast_mut() } + } + + /// Returns a reference to the parent device from a raw `fwctl_uctx` p= ointer. + pub fn parent_device_from_raw( + fwctl_uctx: &Opaque, + ) -> &device::Device { + // SAFETY: `fwctl_uctx` is initialized by the fwctl subsystem + // and guaranteed to remain valid. + let raw_fwctl =3D unsafe { (*fwctl_uctx.get()).fwctl }; + // SAFETY: `raw_fwctl` is a valid pointer to a `fwctl_device`, and= its `dev.parent` + // field points to a valid parent device. + let raw_dev =3D unsafe { (*raw_fwctl).dev.parent }; + + // SAFETY: `raw_dev` points to a live device object. + let dev: &device::Device =3D unsafe { device::Device::from_raw(raw= _dev) }; + + // SAFETY: The device is guaranteed to be bound. + unsafe { dev.as_bound() } + } + + /// Returns a reference to the parent device of this user context. + pub fn get_parent_device(&self) -> &device::Device { + Self::parent_device_from_raw(&self.fwctl_uctx) + } +} + +impl core::ops::Deref for UserCtx { + type Target =3D T; + + fn deref(&self) -> &Self::Target { + &self.uctx + } +} + +impl core::ops::DerefMut for UserCtx { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.uctx + } +} + +/// Static vtable mapping Rust trait methods to C callbacks. +pub struct VTable(PhantomData); + +impl VTable { + /// Static instance of `fwctl_ops` used by the C core to call into Rus= t. + pub const VTABLE: bindings::fwctl_ops =3D bindings::fwctl_ops { + device_type: T::DEVICE_TYPE as u32, + uctx_size: core::mem::size_of::>(), + open_uctx: Some(Self::open_uctx_callback), + close_uctx: Some(Self::close_uctx_callback), + info: Some(Self::info_callback), + fw_rpc: Some(Self::fw_rpc_callback), + }; + + /// Called when a new user context is opened by userspace. + /// # Safety + /// + /// `uctx` must be a valid pointer to an initialized `fwctl_uctx` stru= cture, + /// embedded within a C-allocated `UserCtx` with sufficien= t space. + unsafe extern "C" fn open_uctx_callback(uctx: *mut bindings::fwctl_uct= x) -> ffi::c_int { + // SAFETY: `uctx` points to valid, initialized `fwctl_uctx` struct= ure. + let fwctl_uctx_ref =3D unsafe { &*Opaque::cast_from(uctx) }; + + let initializer =3D match T::open(fwctl_uctx_ref) { + Ok(init) =3D> init, + Err(e) =3D> return e.to_errno(), + }; + + let uctx_offset =3D core::mem::offset_of!(UserCtx, uct= x); + + // SAFETY: The C side allocated enough space for the entire UserCt= x. + let uctx_ptr: *mut T::UserCtx =3D unsafe { uctx.cast::().add(u= ctx_offset).cast() }; + + // Initialize the uctx field in-place using the pin initializer. + // SAFETY: + // - uctx_ptr points to valid allocated memory + // - The memory is properly aligned (guaranteed by #[repr(C)] and = our compile-time check) + // - The memory is uninitialized, which is what PinInit expects + // - After this call, the memory will be properly initialized + match unsafe { initializer.__pinned_init(uctx_ptr.cast()) } { + Ok(()) =3D> 0, + Err(e) =3D> e.to_errno(), + } + } + + /// Called when the user context is being closed. + /// # Safety + /// + /// `uctx` must be a valid pointer to an initialized `fwctl_uctx` stru= cture, + /// embedded within a fully initialized `UserCtx`. + unsafe extern "C" fn close_uctx_callback(uctx: *mut bindings::fwctl_uc= tx) { + // SAFETY: `uctx` is guaranteed by the fwctl subsystem to be a val= id pointer. + let ctx =3D unsafe { UserCtx::::from_raw(uctx) }; + T::close(ctx); + } + + /// Returns device or context information. + /// # Safety + /// + /// - `uctx` must be a valid pointer to an initialized `fwctl_uctx` st= ructure, + /// embedded within a fully initialized `UserCtx`. + /// - `length` must be a valid pointer to write the output length. + unsafe extern "C" fn info_callback( + uctx: *mut bindings::fwctl_uctx, + length: *mut usize, + ) -> *mut ffi::c_void { + // SAFETY: `uctx` is guaranteed by the fwctl subsystem to be a val= id pointer. + let ctx =3D unsafe { UserCtx::::from_raw(uctx) }; + + match T::info(ctx) { + Ok(kvec) =3D> { + // The ownership of the buffer is now transferred to the f= oreign + // caller. It must eventually be released by fwctl framewo= rk. + let (ptr, len, _cap) =3D kvec.into_raw_parts(); + + // SAFETY: `length` is a valid out-parameter provided by t= he C + // caller. Write the number of bytes in the returned buffe= r. + unsafe { + *length =3D len; + } + + ptr.cast::() + } + + Err(e) =3D> Error::to_ptr(e), + } + } + + /// Called when a user-space RPC request is received. + /// # Safety + /// + /// - `uctx` must be a valid pointer to an initialized `fwctl_uctx` st= ructure, + /// embedded within a fully initialized `UserCtx`. + /// - `rpc_in` must be a valid pointer to `in_len` bytes of readable/w= ritable memory. + /// - `out_len` must be a valid pointer to write the output length. + unsafe extern "C" fn fw_rpc_callback( + uctx: *mut bindings::fwctl_uctx, + scope: u32, + rpc_in: *mut ffi::c_void, + in_len: usize, + out_len: *mut usize, + ) -> *mut ffi::c_void { + // SAFETY: `uctx` is guaranteed by the fwctl framework to be a val= id pointer. + let ctx =3D unsafe { UserCtx::::from_raw(uctx) }; + + // SAFETY: Creating a mutable slice from `rpc_in`: + // - `rpc_in` is non-null and properly aligned: guaranteed by the = fwctl subsystem + // - `rpc_in` points to `in_len` consecutive properly initialized = bytes + // - The memory is valid for reads and writes for the lifetime of = the returned slice + // - The total size `in_len` does not exceed `isize::MAX`: checked= by the fwctl subsystem + // - No other references to this memory exist during this callback + let rpc_in_slice: &mut [u8] =3D + unsafe { slice::from_raw_parts_mut(rpc_in.cast::(), in_len= ) }; + + match T::fw_rpc(ctx, scope, rpc_in_slice, out_len) { + // Driver allocates a new output buffer. + Ok(Some(kvec)) =3D> { + // The ownership of the buffer is now transferred to the f= oreign + // caller. It must eventually be released by fwctl subsyst= em. + let (ptr, len, _cap) =3D kvec.into_raw_parts(); + + // SAFETY: `out_len` is a valid writable pointer provided = by the C caller. + unsafe { *out_len =3D len }; + + ptr.cast::() + } + + // Driver re-uses the existing input buffer and writes the out= _len. + Ok(None) =3D> rpc_in, + + Err(e) =3D> Error::to_ptr(e), + } + } +} diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 6d637e2fed1b..956e865c17ea 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -97,6 +97,8 @@ pub mod firmware; pub mod fmt; pub mod fs; +#[cfg(CONFIG_RUST_FWCTL_ABSTRACTIONS)] +pub mod fwctl; #[cfg(CONFIG_I2C =3D "y")] pub mod i2c; pub mod id_pool; --=20 2.51.0 From nobody Sun Feb 8 05:42:06 2026 Received: from BL2PR02CU003.outbound.protection.outlook.com (mail-eastusazon11011023.outbound.protection.outlook.com [52.101.52.23]) (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 B6377392C2A; Thu, 22 Jan 2026 21:00:17 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=52.101.52.23 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769115623; cv=fail; b=fRoEZLVzdQdyKKgfv1HZlZ0rvSyCRRHhMEHgvHH7GvgS+IAlbb7uL+mD1I5L6K4NvRFoLNToWylOi5NDGmy6Y6WxUaGNw3NYjFu/t1qJALcEp7PEWPrSY7Ud5dv1tw6msaDj6s/7uCVaKhbZnf3TFeE2HLF1uVioK7cJ6ePFZWI= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769115623; c=relaxed/simple; bh=38IuTkEPXPuOy7xAfjGfGmEoZ7L7OoUt2O5K2aXks54=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=g4GcaDmHy16FH8YqRjMmHWmOTFiF6U7NwTsdDdTDPXJ3nJCsejgNTG3QCsNBolcwszvm3Hww7B4VosfJsi8J0kq+dj1iBLFTgSPX3ztqNGL0AABV3mxrOFDWwmuUnUkA4AjIA+7lM5zb9Ub+aFhiL/dDEXQrbzW+5PvQX9vxteM= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=nvidia.com; spf=fail smtp.mailfrom=nvidia.com; dkim=pass (2048-bit key) header.d=Nvidia.com header.i=@Nvidia.com header.b=jYmr3Bu+; arc=fail smtp.client-ip=52.101.52.23 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=nvidia.com Authentication-Results: smtp.subspace.kernel.org; spf=fail smtp.mailfrom=nvidia.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=Nvidia.com header.i=@Nvidia.com header.b="jYmr3Bu+" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=fpCv9B0QrryOqU1TlRTihqbxd7ROYgHLf5ZBrMMFrGtBBF+OModSM02AyF84zDJO8fHUqY5JMQriQ5JDnbLFDnGtekSYQjCmDJSf+kgZgsrg+szcq4lkwmvsFd/xkCBH9QURPAjBAzWuJEq8oamo0zv4IJafB7fK4fTbMCNRv92+JB/HjKfeHfLoJvMxsmVnakqocw3fgcBbuan60DfLM16Ne6sve6H0NoZ6E2zUB6kE52Ne+ewqeosMW+xNzUncWJh2SLeTPoBkHTgKQuDdgX+h3nuyiK1R9vc5kDG5h/lB8sUCgsmgj9MCx8QtfMns9XmJ1dg/Wqsk+KMgDwM8RA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector10001; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=HsIK77hQFJrLddVsg0DgyO0xQICSpWSe1A5LCoCvd7I=; b=AOm4C11Lf7oQX43Ybnb8gdGhbWp9Thqit7hud7a8rzOSefNszqslkIRq+UHwX18pUMGifcTn6//cTGY0Ouolt8TKeXlVrMP8Y4Ad8bIvn4hItiKUGoDU1LIsmxgkP8uxUFKW1FbPddWjqVSt8oSUMY6lckd8/poKRowEUg0cdjtTxadB66UJFCLYCo5sgRoFtHaVQscQt7HYXYz1UJ1DKyQjl60yCSx1JHczktuDq5ALn6hd058CgCsK/tJ2RuRZveOJOcjvQ70vsBCcZA8WgXlOK+baUfYdx+1WyMrOxP2ABmlKMy/PeQs9lXfFWsbRcWl1x2oarPHFJVL0ebChFg== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass (sender ip is 216.228.117.161) smtp.rcpttodomain=vger.kernel.org smtp.mailfrom=nvidia.com; dmarc=pass (p=reject sp=reject pct=100) action=none header.from=nvidia.com; dkim=none (message not signed); arc=none (0) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=Nvidia.com; s=selector2; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=HsIK77hQFJrLddVsg0DgyO0xQICSpWSe1A5LCoCvd7I=; b=jYmr3Bu+RBvVskQ759SKYkhk8OFEHtn8dNG6P2Jqns6lSWHhbhX/Tx6enf54nZKqIwhd8+XfieqI02uYsVqiZUP0/pRF7ART8i2DyJ+/1iPW81iMd4SoFx7JXmmRsPxRt1c6rSG+nvYO/ZPMbzrd+/yqUDEWQLZiEwhN/sbJPNfXLVK+ejxvwyimqM7y1YNGUxz7bEDEEXCd6D20JKR8y43Yo+ndmTP7+TcsBB+I76+gzbnHnc5I6EF0M/3RQul9riWbLcmAO8novhV32spZ9dROUmgjng8CNVH6nxZ2hvHCBsvg/5f3cU0uARFQDc3mCBqNQLKaOtJqMht1L6kaEw== Received: from BY3PR04CA0010.namprd04.prod.outlook.com (2603:10b6:a03:217::15) by MN2PR12MB4471.namprd12.prod.outlook.com (2603:10b6:208:26f::16) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.9542.11; Thu, 22 Jan 2026 21:00:13 +0000 Received: from SJ5PEPF000001D4.namprd05.prod.outlook.com (2603:10b6:a03:217:cafe::9f) by BY3PR04CA0010.outlook.office365.com (2603:10b6:a03:217::15) with Microsoft SMTP Server (version=TLS1_3, cipher=TLS_AES_256_GCM_SHA384) id 15.20.9542.11 via Frontend Transport; Thu, 22 Jan 2026 21:00:02 +0000 X-MS-Exchange-Authentication-Results: spf=pass (sender IP is 216.228.117.161) smtp.mailfrom=nvidia.com; dkim=none (message not signed) header.d=none;dmarc=pass action=none header.from=nvidia.com; Received-SPF: Pass (protection.outlook.com: domain of nvidia.com designates 216.228.117.161 as permitted sender) receiver=protection.outlook.com; client-ip=216.228.117.161; helo=mail.nvidia.com; pr=C Received: from mail.nvidia.com (216.228.117.161) by SJ5PEPF000001D4.mail.protection.outlook.com (10.167.242.56) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.9564.3 via Frontend Transport; Thu, 22 Jan 2026 21:00:13 +0000 Received: from rnnvmail205.nvidia.com (10.129.68.10) by mail.nvidia.com (10.129.200.67) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.2562.20; Thu, 22 Jan 2026 12:42:54 -0800 Received: from rnnvmail205.nvidia.com (10.129.68.10) by rnnvmail205.nvidia.com (10.129.68.10) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.2562.20; Thu, 22 Jan 2026 12:42:54 -0800 Received: from inno-vm-xubuntu (10.127.8.13) by mail.nvidia.com (10.129.68.10) with Microsoft SMTP Server id 15.2.2562.20 via Frontend Transport; Thu, 22 Jan 2026 12:42:47 -0800 From: Zhi Wang To: , , CC: , , , , , , , , , , , , , , , , , , , , , , , , , Zhi Wang , Jason Gunthorpe Subject: [PATCH v2 2/2] samples: rust: fwctl: add sample code for fwctl Date: Thu, 22 Jan 2026 22:42:31 +0200 Message-ID: <20260122204232.15988-3-zhiw@nvidia.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260122204232.15988-1-zhiw@nvidia.com> References: <20260122204232.15988-1-zhiw@nvidia.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-NV-OnPremToCloud: ExternallySecured X-EOPAttributedMessage: 0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: SJ5PEPF000001D4:EE_|MN2PR12MB4471:EE_ X-MS-Office365-Filtering-Correlation-Id: 5c008297-4ead-4391-11c4-08de59f93c9a X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|7416014|1800799024|376014|82310400026|36860700013; X-Microsoft-Antispam-Message-Info: =?us-ascii?Q?/AiToN5WXUoUjU5y4C/FZvAtAYnssCZ3IAFcr5xCSRMSGCr+ySutaF7pCYq2?= =?us-ascii?Q?JMo0RFlDlO+kHanf01HWtie/vGcKt3uESY6Wa6x2B+GlkP9VQMBFEyTbh3qr?= =?us-ascii?Q?RFt5gLeXmFHU+4H0YRNvCVD3XvC758mQIbjCxfGZRD/snzNyUicnFXWPzUSf?= =?us-ascii?Q?7duVQiVEDcPB9L18yNh9GbUNKy0DB17rAC3S91iNkNnywodQdtvlHqMbE0N1?= =?us-ascii?Q?F6yvx8S68LRigqIxjtn4Ev6tKPCvZSJKYl2d2yPBVsG72NS1+8fKxNmskJ79?= =?us-ascii?Q?FW2Nqf8XX8jJMx4H0NbusHYBgfTEkGE3Y5PvHPrN7btYTBMZUKrwvxFmPJt3?= =?us-ascii?Q?HVUycqxpUl9NNThCGnLu1Bd1hRm154t1bmBiVTa1kNKtwJC5T+HH3mGPf9nM?= =?us-ascii?Q?wAwoBAp1s810tvRB5sBC+uUhg1Vnk48OfqtX6aD0nyW1Lb6sNvILUnGeP3RW?= =?us-ascii?Q?ZIArG7kVrrRgapqZ9mkoBwY/uCeKHL7oA5DdumM7F/vLTev0ZjPH6zU4e0cv?= =?us-ascii?Q?B7cT2BVb7AdRtY2HJwuM82ORgH7tzCq6WkVecO5Q4OyYwBZUaj2GoodhjGI5?= =?us-ascii?Q?MgwSG106hcjj8Gqd580z4qPjxFI9p8/jLAxZ2lHgBIB8TRGaHvwqKReKBNPW?= =?us-ascii?Q?PJ9KnqY4J7HnMQKeGlRmW+3vMz05pJ+ZMA+pbymIE4o0MRlOFU+j9WPd85bi?= =?us-ascii?Q?Lk4BqVfA4TOIJ3hWW6ZuEiXmmvyUD+RPq7Zu5SVSkM0lJ4gmtZqqSkm0vhdo?= =?us-ascii?Q?HGb1/uOfqoOcWuEuTmFIultQvj4dt/ort4+ArANXzyavK2Rxtxsi0YX8EI95?= =?us-ascii?Q?TDXEq3LwdVE/IRWibF9FQmpnlKO1qilhern9QDa0XOh67l8M6ebu8Rk+HFhJ?= =?us-ascii?Q?IEz1A6Rz57PfgUzrIpENFS+WY2UlHC4HS++e10/k90t0G+b0C79V2bOQHJ0h?= =?us-ascii?Q?u5nfAwwzyl1TbFVB8mTEdlwVxjUhsEzdEVeRQCXhDr4mcE61qxhkQ9x3f0C4?= =?us-ascii?Q?kOOqhp/r5VtsjUxdtLSUyEJYKwTykfrfdpAanttx62bUnhm/mZbc4IeiouJa?= =?us-ascii?Q?sr1QsFg0O23k+QMvO/zsClLg+ZY+Hdi7NBRnfTmDiipnm/VHLIfdHCgG9qn0?= =?us-ascii?Q?YkP06foCdqm83xEFWL0+GQnswGZxI6Oh+lJRnbKfbngRW/mjvnCyVeOl4gjP?= =?us-ascii?Q?kGwQk4YMxpj4ar94wnWjZ07uctub14WEyreN13OsHYEL2Pp9wn+sNTW6afc3?= =?us-ascii?Q?gUGgtW9j7eRn9B6WtdAej5bH12ZxnulctU0UMplALobUYf7KERNEvXIykM0r?= =?us-ascii?Q?Q9Z49iaotwQrDQmLGNpetnT+gQc8N1IEeLa02mkhcHJ+Bsay9BfTAH2MqpHS?= =?us-ascii?Q?Yn5smXELXO1tnf3blJBAbd6NY+rh6Ea13Rylsr5TXKSll3sFwielTYo3gTvx?= =?us-ascii?Q?580Fi5Sr1LOPHyEzeCQruTl4ohT/eIAfRLDQIws4v76FUsTZwfqdJUmJcw6a?= =?us-ascii?Q?OEyd5gZhViLtZVWoaveQDDgovKIlIydNg7ZP0w0U3k0V9WYnW9Lk8FkCma2m?= =?us-ascii?Q?mRFw1fNrN4tryxnI64Ke73vID8bxQaMCccc8sf64CNZxtUWgLLFSUPrvlfWE?= =?us-ascii?Q?1R2BmI+QdT9kqS65eSKEis/Faw7XJHa1UCHDLn4VjuKck78pLHyQxzjdvQSp?= =?us-ascii?Q?i3KWrQ=3D=3D?= X-Forefront-Antispam-Report: CIP:216.228.117.161;CTRY:US;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:mail.nvidia.com;PTR:dc6edge2.nvidia.com;CAT:NONE;SFS:(13230040)(7416014)(1800799024)(376014)(82310400026)(36860700013);DIR:OUT;SFP:1101; X-OriginatorOrg: Nvidia.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 22 Jan 2026 21:00:13.0348 (UTC) X-MS-Exchange-CrossTenant-Network-Message-Id: 5c008297-4ead-4391-11c4-08de59f93c9a X-MS-Exchange-CrossTenant-Id: 43083d15-7273-40c1-b7db-39efd9ccc17a X-MS-Exchange-CrossTenant-OriginalAttributedTenantConnectingIp: TenantId=43083d15-7273-40c1-b7db-39efd9ccc17a;Ip=[216.228.117.161];Helo=[mail.nvidia.com] X-MS-Exchange-CrossTenant-AuthSource: SJ5PEPF000001D4.namprd05.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Anonymous X-MS-Exchange-CrossTenant-FromEntityHeader: HybridOnPrem X-MS-Exchange-Transport-CrossTenantHeadersStamped: MN2PR12MB4471 Content-Type: text/plain; charset="utf-8" This patch adds a sample Rust driver demonstrating the usage of the fwctl Rust abstractions. Add sample code for creating a FwCtl device, getting device info and issuing an RPC. Cc: Danilo Krummrich Cc: Jason Gunthorpe Signed-off-by: Zhi Wang --- samples/rust/Kconfig | 11 +++ samples/rust/Makefile | 1 + samples/rust/rust_driver_fwctl.rs | 136 ++++++++++++++++++++++++++++++ 3 files changed, 148 insertions(+) create mode 100644 samples/rust/rust_driver_fwctl.rs diff --git a/samples/rust/Kconfig b/samples/rust/Kconfig index c49ab9106345..3d0b223caa89 100644 --- a/samples/rust/Kconfig +++ b/samples/rust/Kconfig @@ -172,6 +172,17 @@ config SAMPLE_RUST_SOC =20 If unsure, say N. =20 +config SAMPLE_RUST_DRIVER_FWCTL + tristate "Fwctl Driver" + depends on FWCTL + help + This option builds the Rust Fwctl driver sample. + + To compile this as a module, choose M here: + the module will be called rust_driver_fwctl. + + If unsure, say N. + config SAMPLE_RUST_HOSTPROGS bool "Host programs" help diff --git a/samples/rust/Makefile b/samples/rust/Makefile index 6c0aaa58cccc..6f6030e64727 100644 --- a/samples/rust/Makefile +++ b/samples/rust/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_SAMPLE_RUST_DRIVER_PLATFORM) +=3D rust_drive= r_platform.o obj-$(CONFIG_SAMPLE_RUST_DRIVER_USB) +=3D rust_driver_usb.o obj-$(CONFIG_SAMPLE_RUST_DRIVER_FAUX) +=3D rust_driver_faux.o obj-$(CONFIG_SAMPLE_RUST_DRIVER_AUXILIARY) +=3D rust_driver_auxiliary.o +obj-$(CONFIG_SAMPLE_RUST_DRIVER_FWCTL) +=3D rust_driver_fwctl.o obj-$(CONFIG_SAMPLE_RUST_CONFIGFS) +=3D rust_configfs.o obj-$(CONFIG_SAMPLE_RUST_SOC) +=3D rust_soc.o =20 diff --git a/samples/rust/rust_driver_fwctl.rs b/samples/rust/rust_driver_f= wctl.rs new file mode 100644 index 000000000000..ac5f979fd73b --- /dev/null +++ b/samples/rust/rust_driver_fwctl.rs @@ -0,0 +1,136 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Rust fwctl API test (based on QEMU's `pci-testdev`). +//! +//! To make this driver probe, QEMU must be run with `-device pci-testdev`. + +use kernel::{ + bindings, + device, + device::Core, + devres::Devres, + fwctl, + pci, + prelude::*, + sync::aref::ARef, + types, +}; + +struct FwctlSampleUserCtx { + _drvdata: u32, +} + +struct FwctlSampleOps; + +impl fwctl::Operations for FwctlSampleOps { + type UserCtx =3D FwctlSampleUserCtx; + + const DEVICE_TYPE: fwctl::DeviceType =3D fwctl::DeviceType::RustFwctlT= est; + + fn open( + fwctl_uctx: &types::Opaque + ) -> Result, Error> { + let dev =3D fwctl::UserCtx::::parent_device_from_ra= w(fwctl_uctx); + + dev_info!(dev, "fwctl test driver: open_uctx()"); + + // Return an initializer for the user context. + // The framework will initialize this in-place in the C-allocated = memory. + Ok(try_init!(FwctlSampleUserCtx { + _drvdata: 0, + })) + } + + fn close(uctx: &mut fwctl::UserCtx) { + let dev =3D uctx.get_parent_device(); + + dev_info!(dev, "fwctl test driver: close_uctx()"); + } + + fn info(uctx: &mut fwctl::UserCtx) -> Result, Error> { + let dev =3D uctx.get_parent_device(); + + dev_info!(dev, "fwctl test driver: info()"); + + let mut infobuf =3D KVec::::new(); + infobuf.push(0xef, GFP_KERNEL)?; + infobuf.push(0xbe, GFP_KERNEL)?; + infobuf.push(0xad, GFP_KERNEL)?; + infobuf.push(0xde, GFP_KERNEL)?; + + Ok(infobuf) + } + + fn fw_rpc( + uctx: &mut fwctl::UserCtx, + scope: u32, + rpc_in: &mut [u8], + _out_len: *mut usize, + ) -> Result>, Error> { + let dev =3D uctx.get_parent_device(); + + dev_info!(dev, "fwctl test driver: fw_rpc() scope {}", scope); + + if rpc_in.len() !=3D 4 { + return Err(EINVAL); + } + + dev_info!( + dev, + "fwctl test driver: inbuf len{} bytes[0-3] {:x} {:x} {:x} {:x}= ", + rpc_in.len(), + rpc_in[0], + rpc_in[1], + rpc_in[2], + rpc_in[3] + ); + + let mut outbuf =3D KVec::::new(); + outbuf.push(0xef, GFP_KERNEL)?; + outbuf.push(0xbe, GFP_KERNEL)?; + outbuf.push(0xad, GFP_KERNEL)?; + outbuf.push(0xde, GFP_KERNEL)?; + + Ok(Some(outbuf)) + } +} + +#[pin_data] +struct FwctlSampleDriver { + pdev: ARef, + #[pin] + fwctl: Devres>, +} + +kernel::pci_device_table!( + PCI_TABLE, + MODULE_PCI_TABLE, + ::IdInfo, + [(pci::DeviceId::from_id(pci::Vendor::REDHAT, 0x5), ())] +); + +impl pci::Driver for FwctlSampleDriver { + type IdInfo =3D (); + const ID_TABLE: pci::IdTable =3D &PCI_TABLE; + + fn probe(pdev: &pci::Device, _info: &Self::IdInfo) -> impl PinIn= it { + dev_info!(pdev.as_ref(), "Probe fwctl test driver"); + + // `pdev` is `Device`, which derefs to `Device` durin= g probe. + let pdev_bound: &pci::Device =3D pdev; + + try_pin_init!(Self { + pdev: pdev.into(), + fwctl <- fwctl::Registration::::new(pdev_bound= .as_ref()), + }) + } +} + +kernel::module_pci_driver! { + type: FwctlSampleDriver, + name: "rust_driver_fwctl", + authors: ["Zhi Wang"], + description: "Rust fwctl test", + license: "GPL v2", + imports_ns: ["FWCTL"], +} --=20 2.51.0