Patch Detail
get:
Show a patch.
patch:
Update a patch.
put:
Update a patch.
GET /api/1.0/patches/2197380/?format=api
{ "id": 2197380, "url": "http://patchwork.ozlabs.org/api/1.0/patches/2197380/?format=api", "project": { "id": 28, "url": "http://patchwork.ozlabs.org/api/1.0/projects/28/?format=api", "name": "Linux PCI development", "link_name": "linux-pci", "list_id": "linux-pci.vger.kernel.org", "list_email": "linux-pci@vger.kernel.org", "web_url": null, "scm_url": null, "webscm_url": null }, "msgid": "<20260217204909.211793-2-zhiw@nvidia.com>", "date": "2026-02-17T20:49:06", "name": "[v3,1/1] rust: introduce abstractions for fwctl", "commit_ref": null, "pull_url": null, "state": "new", "archived": false, "hash": "e31277e6607923a9e0699f4561c3c79f95c8e303", "submitter": { "id": 88076, "url": "http://patchwork.ozlabs.org/api/1.0/people/88076/?format=api", "name": "Zhi Wang", "email": "zhiw@nvidia.com" }, "delegate": null, "mbox": "http://patchwork.ozlabs.org/project/linux-pci/patch/20260217204909.211793-2-zhiw@nvidia.com/mbox/", "series": [ { "id": 492476, "url": "http://patchwork.ozlabs.org/api/1.0/series/492476/?format=api", "date": "2026-02-17T20:49:05", "name": "rust: introduce abstractions for fwctl", "version": 3, "mbox": "http://patchwork.ozlabs.org/series/492476/mbox/" } ], "check": "pending", "checks": "http://patchwork.ozlabs.org/api/patches/2197380/checks/", "tags": {}, "headers": { "Return-Path": "\n <linux-pci+bounces-47505-incoming=patchwork.ozlabs.org@vger.kernel.org>", "X-Original-To": [ "incoming@patchwork.ozlabs.org", "linux-pci@vger.kernel.org" ], "Delivered-To": "patchwork-incoming@legolas.ozlabs.org", "Authentication-Results": [ "legolas.ozlabs.org;\n\tdkim=pass (2048-bit key;\n unprotected) header.d=Nvidia.com header.i=@Nvidia.com header.a=rsa-sha256\n header.s=selector2 header.b=prVxL1Ql;\n\tdkim-atps=neutral", "legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=vger.kernel.org\n (client-ip=2600:3c09:e001:a7::12fc:5321; helo=sto.lore.kernel.org;\n envelope-from=linux-pci+bounces-47505-incoming=patchwork.ozlabs.org@vger.kernel.org;\n receiver=patchwork.ozlabs.org)", "smtp.subspace.kernel.org;\n\tdkim=pass (2048-bit key) header.d=Nvidia.com header.i=@Nvidia.com\n header.b=\"prVxL1Ql\"", "smtp.subspace.kernel.org;\n arc=fail smtp.client-ip=52.101.48.14", "smtp.subspace.kernel.org;\n dmarc=pass (p=reject dis=none) header.from=nvidia.com", "smtp.subspace.kernel.org;\n spf=fail smtp.mailfrom=nvidia.com" ], "Received": [ "from sto.lore.kernel.org (sto.lore.kernel.org\n [IPv6:2600:3c09:e001:a7::12fc:5321])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange x25519 server-signature ECDSA (secp384r1) server-digest SHA384)\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4fFsGT6BKCz1xpl\n\tfor <incoming@patchwork.ozlabs.org>; Wed, 18 Feb 2026 07:50:09 +1100 (AEDT)", "from smtp.subspace.kernel.org (conduit.subspace.kernel.org\n [100.90.174.1])\n\tby sto.lore.kernel.org (Postfix) with ESMTP id 565B63017DEF\n\tfor <incoming@patchwork.ozlabs.org>; Tue, 17 Feb 2026 20:50:04 +0000 (UTC)", "from localhost.localdomain (localhost.localdomain [127.0.0.1])\n\tby smtp.subspace.kernel.org (Postfix) with ESMTP id 3CF8C36F430;\n\tTue, 17 Feb 2026 20:50:00 +0000 (UTC)", "from MW6PR02CU001.outbound.protection.outlook.com\n (mail-westus2azon11012014.outbound.protection.outlook.com [52.101.48.14])\n\t(using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits))\n\t(No client certificate requested)\n\tby smtp.subspace.kernel.org (Postfix) with ESMTPS id 471A1259CBD;\n\tTue, 17 Feb 2026 20:49:58 +0000 (UTC)", "from PH7P220CA0022.NAMP220.PROD.OUTLOOK.COM (2603:10b6:510:326::23)\n by SA3PR12MB8440.namprd12.prod.outlook.com (2603:10b6:806:2f8::8) with\n Microsoft SMTP Server (version=TLS1_2,\n cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.9632.13; Tue, 17 Feb\n 2026 20:49:49 +0000", "from CO1PEPF000044FD.namprd21.prod.outlook.com\n (2603:10b6:510:326:cafe::78) by PH7P220CA0022.outlook.office365.com\n (2603:10b6:510:326::23) with Microsoft SMTP Server (version=TLS1_3,\n cipher=TLS_AES_256_GCM_SHA384) id 15.20.9611.16 via Frontend Transport; Tue,\n 17 Feb 2026 20:49:49 +0000", "from mail.nvidia.com (216.228.117.160) by\n CO1PEPF000044FD.mail.protection.outlook.com (10.167.241.203) with Microsoft\n SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id\n 15.20.9654.0 via Frontend Transport; Tue, 17 Feb 2026 20:49:49 +0000", "from rnnvmail204.nvidia.com (10.129.68.6) by mail.nvidia.com\n (10.129.200.66) with Microsoft SMTP Server (version=TLS1_2,\n cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.2562.20; Tue, 17 Feb\n 2026 12:49:28 -0800", "from rnnvmail203.nvidia.com (10.129.68.9) by rnnvmail204.nvidia.com\n (10.129.68.6) with Microsoft SMTP Server (version=TLS1_2,\n cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.2562.20; Tue, 17 Feb\n 2026 12:49:27 -0800", "from inno-thin-ubuntu.nvidia.com (10.127.8.9) by mail.nvidia.com\n (10.129.68.9) with Microsoft SMTP Server id 15.2.2562.20 via Frontend\n Transport; Tue, 17 Feb 2026 12:49:20 -0800" ], "ARC-Seal": [ "i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116;\n\tt=1771361400; cv=fail;\n b=j9UZzPlTVkbP7ybmF+vf8ps94UTmDT/WMS+6pTlecI3Khr6e7URe/vAN3bOmVNLgH7OAztzgvamXhbffffN0gWQomQTABg5XA2pSt1LDIlZYMfdqpWOH0J5lKAk/3AL99dipBhzSPFM8/IB/zMdDNFVcTffyTtFfyCpJ+6Dcm6s=", "i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none;\n b=wUP1R+uae/bMbUw7HLlAjKTIjPQlPb5XJvXzY3LRh5gBAw+L7pUqbsraA0j7c3ZAR+LpZWtZJKuuyI/TynCPVgtlA4urKGDLCMa35V2pzm9K9BcElvqgm2gcK9pM5Xy8CG1ht2W8vqDhxu5cUqnrl5U4KgaIEyPQZCiX/J3eMxdBJrTmR0d/syEYL4nw1hH2f+ShWK+DHQEDj+aH3gA6tySFiIwnN6IF2Oa4OiKLk81Fu+oIlNqDEGKswuwd8De0i/yai94p+GbQTUgKqYBDgK8JpYcljduxMSwIFdFKTccGYnBq6h0T/o9IEOKUl+VXCn7wtDUq+q36Iix+YfJKmw==" ], "ARC-Message-Signature": [ "i=2; a=rsa-sha256; d=subspace.kernel.org;\n\ts=arc-20240116; t=1771361400; c=relaxed/simple;\n\tbh=ubbnnhPXIfPRsBEglCZxBk4l6d+zYdc3A9GkiIHfogg=;\n\th=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References:\n\t MIME-Version:Content-Type;\n b=nxkz3ZEaw8rqlXvuxljlJXxVH/C2Fi7MRjJ7KTphYaf/HxrKAugJzu4eP5rFHMUfh3iHdWgwdQRvUw030C4hH44oW806zvzfTy0TkW+6pb1o4HgkbzYAy3VqMWNtaVCDewUjRAmc9wqSQbyJ+CC50eFtP98sOw3JPcA5Q7eN8To=", "i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com;\n s=arcselector10001;\n 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;\n bh=WlBiRKaZ8SSuXBodxLOQpqwBMMwIS1MSqb/PlHGGNIw=;\n b=GDMUNqaBvgtz9N5L14/vvUsLOyBNKEYdAO33QfN3YSElI7v7FDBxbaS9C1afm9zdjbrKhX7o17YF0fOMBG6oKH7G+X+sCFTja+ypJLw5sod6vcN7wkKr1ZFB7h7+fG9LS3CF5x5DmZtW2ko4YLSnoLc4eKTYTT8ios9RN3ZCBzZUXJZUtf216erFv/bQaxPde0FTF1wSgWuUoxobjyqvQTv8io1WtC2HNa+5W4hFDoWD8xXdo/wXu8+FBSOtRY5YiIyRnnQkf351aPRbhDuOLWizZILRHYK53JPdgNWoHsmLmT7SHbqNx8fqU1vS0tZDDGSKQIwACsWQuYYdx9bRHA==" ], "ARC-Authentication-Results": [ "i=2; smtp.subspace.kernel.org;\n dmarc=pass (p=reject dis=none) header.from=nvidia.com;\n spf=fail smtp.mailfrom=nvidia.com;\n dkim=pass (2048-bit key) header.d=Nvidia.com header.i=@Nvidia.com\n header.b=prVxL1Ql; arc=fail smtp.client-ip=52.101.48.14", "i=1; mx.microsoft.com 1; spf=pass (sender ip is\n 216.228.117.160) smtp.rcpttodomain=vger.kernel.org smtp.mailfrom=nvidia.com;\n dmarc=pass (p=reject sp=reject pct=100) action=none header.from=nvidia.com;\n dkim=none (message not signed); arc=none (0)" ], "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed; d=Nvidia.com;\n s=selector2;\n h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck;\n bh=WlBiRKaZ8SSuXBodxLOQpqwBMMwIS1MSqb/PlHGGNIw=;\n b=prVxL1QlX8rw6I8Bu2XmSSieqLfSEjDhQ6gfHP6sMc12MZ6OP2O7j4baj37q/eVLXcO98p/VUNq93Z6LqYh3Zhv1QhUyjrYfc9dX2js67OuUTFuXCJ9V/Qy5Sjhvi56IxorDHx1fqKS+AkcpAOqnuE5wxABHM3oel4Scx03KZQHbRMIiZn10JILqHfzV9DQwiEoap+KRdBb7SC+HNCuWE+g/Wz2fIPNPrnBQtV/6cV34/AaDjWCm7P4ouEZPceJczM3f6ZLyfsqu7py4vcP3qA9GoFGJVPCgckoF6JPg7ma3YNCoJN+lwWflLs0hCR8f00r5gConRrQEzYBjiYE4gQ==", "X-MS-Exchange-Authentication-Results": "spf=pass (sender IP is 216.228.117.160)\n smtp.mailfrom=nvidia.com; dkim=none (message not signed)\n header.d=none;dmarc=pass action=none header.from=nvidia.com;", "Received-SPF": "Pass (protection.outlook.com: domain of nvidia.com designates\n 216.228.117.160 as permitted sender) receiver=protection.outlook.com;\n client-ip=216.228.117.160; helo=mail.nvidia.com; pr=C", "From": "Zhi Wang <zhiw@nvidia.com>", "To": "<rust-for-linux@vger.kernel.org>, <linux-pci@vger.kernel.org>,\n\t<linux-kernel@vger.kernel.org>", "CC": "<dakr@kernel.org>, <jgg@nvidia.com>, <gary@garyguo.net>,\n\t<joelagnelf@nvidia.com>, <aliceryhl@google.com>, <bhelgaas@google.com>,\n\t<kwilczynski@kernel.org>, <ojeda@kernel.org>, <alex.gaynor@gmail.com>,\n\t<boqun.feng@gmail.com>, <bjorn3_gh@protonmail.com>, <lossin@kernel.org>,\n\t<a.hindborg@kernel.org>, <tmgross@umich.edu>, <markus.probst@posteo.de>,\n\t<helgaas@kernel.org>, <cjia@nvidia.com>, <smitra@nvidia.com>,\n\t<ankita@nvidia.com>, <aniketa@nvidia.com>, <kwankhede@nvidia.com>,\n\t<targupta@nvidia.com>, <acourbot@nvidia.com>, <jhubbard@nvidia.com>,\n\t<zhiwang@kernel.org>, <daniel.almeida@collabora.com>, Zhi Wang\n\t<zhiw@nvidia.com>", "Subject": "[PATCH v3 1/1] rust: introduce abstractions for fwctl", "Date": "Tue, 17 Feb 2026 22:49:06 +0200", "Message-ID": "<20260217204909.211793-2-zhiw@nvidia.com>", "X-Mailer": "git-send-email 2.43.0", "In-Reply-To": "<20260217204909.211793-1-zhiw@nvidia.com>", "References": "<20260217204909.211793-1-zhiw@nvidia.com>", "Precedence": "bulk", "X-Mailing-List": "linux-pci@vger.kernel.org", "List-Id": "<linux-pci.vger.kernel.org>", "List-Subscribe": "<mailto:linux-pci+subscribe@vger.kernel.org>", "List-Unsubscribe": "<mailto:linux-pci+unsubscribe@vger.kernel.org>", "MIME-Version": "1.0", "Content-Type": "text/plain; charset=\"UTF-8\"", "Content-Transfer-Encoding": "8bit", "X-NV-OnPremToCloud": "ExternallySecured", "X-EOPAttributedMessage": "0", "X-MS-PublicTrafficType": "Email", "X-MS-TrafficTypeDiagnostic": "CO1PEPF000044FD:EE_|SA3PR12MB8440:EE_", "X-MS-Office365-Filtering-Correlation-Id": "56a73de9-5233-459a-1990-08de6e661769", "X-MS-Exchange-SenderADCheck": "1", "X-MS-Exchange-AntiSpam-Relay": "0", "X-Microsoft-Antispam": "\n\tBCL:0;ARA:13230040|36860700013|82310400026|376014|7416014|1800799024;", "X-Microsoft-Antispam-Message-Info": "=?utf-8?q?gz88ERuUaNQZ8KW70jsrb0NeQDM05X/?=\n\t=?utf-8?q?nRZR5RSHLkRffWuJzmV8qGq1WuzUVzfWWjuib1DJQkAGUGzJ3/0XV+CXELqN4iQ3w?=\n\t=?utf-8?q?iZo1yo6W/eu/t3ZuW4uo+hKQ4uCvq7o0QoYVsXm0OumokcyOZAzHbqQU1uzUp7pPZ?=\n\t=?utf-8?q?oXoTRx0fpJJiD2kjTqpaucf9OT8BYQhe24AVbRABpqorsVutct9mlomY3e5H1CxxR?=\n\t=?utf-8?q?SiBhrGf0lTIYwAdZmRrGOWO1MvKaAjkgIFxVLWdqOhFiaAHhgSTv1cU34M7IOk8sG?=\n\t=?utf-8?q?n3xVKfBh9XUUG+2a9mJLUtagtJzCALfgnOpzlX2erAgEfnj5EYXtEX68lwJL1qju/?=\n\t=?utf-8?q?R/WUSCQutYkLG5O84H5OH/PBxnfAgQ99ebHAA5Q2AxfdYAQWuv9ltqRfeJwQGfiIy?=\n\t=?utf-8?q?SLVCYg3iUOrIWg4pfpB69xZGQdjfxtm72tkwQdYfdY0sh1NtDMsYAPHov3wPrvo68?=\n\t=?utf-8?q?CNDt5Ld3DaGnhTvWQCNzFz+VZcs1Pb3kQX0bP9hhRqBSsE3JSxsEqw47BIdIRJxb7?=\n\t=?utf-8?q?/fyfLVWWRl21eB9Trn7FLVTufnFO2M2SFN4C7p364akO35tsMk05U6migsBD3i1O8?=\n\t=?utf-8?q?n+2LCcN6HkJ/+cEtzA1kLnA5mkun4A1+6guRBYtC4/6119NUgEsohSEDo9ybB6rVq?=\n\t=?utf-8?q?h1pCpZiSXHmwGY1d4aVAfZFEFonpm2dDjYqH6uolM2I1Zw3XsQursfzUOaw+1voNG?=\n\t=?utf-8?q?ZXzFmXzN7LDC7rr3b231ARBfUFX/s4l9vMEHIOCWvEceNOQK5xxbL1jb52fCJWZwC?=\n\t=?utf-8?q?lH2cTWklz599V1EbuVJeTeG7/IzegU/ptfWCbpnFmU4JSmV7sToJNCduYsLlbrDIo?=\n\t=?utf-8?q?iPWu4YWPasgef3rsCvYzEIKXKUAErGZAFg2hSP60er1cUgH4a41QRupeM19/nQ5kV?=\n\t=?utf-8?q?gCLcXUdeFiM2dHVk0+pc2WUQSKpDtCgXUgurGoUSoLgptZAz8K3zxi35qucEQWUpf?=\n\t=?utf-8?q?tJ+dmVAKoNrxZHVC/caM73nudd67RQcTR8wdQe4beuHgOVq/5nJtT01jEE5/EyUuD?=\n\t=?utf-8?q?kYWDIzstIbjf67TkxU6wV8EF5jmu4E2KPjEfb3xHbIfsiZYgQ2GxrSoOl+HQX9hf6?=\n\t=?utf-8?q?sf0/+7dm80bZistMDHEtAK2s4sGQV9mwg+R8R9VWJ8AhOKi6Elsm6AYpydUmillDE?=\n\t=?utf-8?q?insiIMUdgglQaOwoh4EoOT/TJk2A0UqeRS8FHkjMiakd8IkRIzC8AIa8YiMekti/P?=\n\t=?utf-8?q?Br42RDnPDsgV1NL8/3n5BhMWA/EYKL40zRAEgu3u0XzqmDzkAI8aPNbvlQdwOHDNJ?=\n\t=?utf-8?q?qCLFbPc98elKeWMs8Rdz7OAxywjAFrhrF4JBFZhkbSKbsH8RWcf7qI+0AymorWLpJ?=\n\t=?utf-8?q?rUZxuI99RCJmwzblsdRXtRJ0J3doW+6KJewGJi70zxvL9g2uFiMVj22EQLL6qMPTp?=\n\t=?utf-8?q?3UMQLTgQwIpOu6wZRQBFqfSFVE3c0RmuBDOBcoJMIYAH83Ry2xO+7cNbZKzM15Mvg?=\n\t=?utf-8?q?B7XzhJiRwScD+BIjaByUdl4k7v2210ztdRMOUF0IBMlZaj9TgntJVas3UTiPj8LIe?=\n\t=?utf-8?q?7ySl6obqdtyfGihtMxQg/WK+oTsBZsfZs5+ORsRh/xg1DwHxfywIcwNGnQVCaCySI?=\n\t=?utf-8?q?+NH+x2/ihgD3vlsPCG3MoxZxnWmDJg=3D=3D?=", "X-Forefront-Antispam-Report": "\n\tCIP: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)(376014)(7416014)(1800799024);DIR:OUT;SFP:1101;", "X-MS-Exchange-AntiSpam-MessageData-ChunkCount": "1", "X-MS-Exchange-AntiSpam-MessageData-0": "\n\tgLAJ7Gi9QVOWsDxWZCVq4dRhSOVHBBZ3X7PgkdniOkpXbKoOiarfikYWyuewLVWsD10Q7+he3P8jlOKeQhSIO7pfuxgtvuqRjYoQnwq+akwoDbvDj2hTzhmHRD6P8YL/iDhPMZ/ZEQDuHyY6zw70wLlDAzTWeCZpJlr01DuV+KAFulZ8HBvnCC2w5SSkjBFSNRc1Sdj9LYRuswCkf6vv0ae3LQ/5YZmJxjo/Lkj4CNWxkDSQXI0UZDSQltct8v/OeEd/fjt0DbeVLvi0OlqsY8dhYZ6Zz3J5++uQZuTfcT3KiHACrfI99+SNAQ7zSsz+HqVpCi8NWF6t2LQvkCF+O+KgIZ0jCtNTg8PcWbGtmO+qPgK7IIm1+GjJWpb6uUsgK5gIq5vF73mhLokhTEH9TLxsfGP4fQrHeENQhrT+2TinY+5sHHtGVL0eLtDXDYgY", "X-OriginatorOrg": "Nvidia.com", "X-MS-Exchange-CrossTenant-OriginalArrivalTime": "17 Feb 2026 20:49:49.0029\n (UTC)", "X-MS-Exchange-CrossTenant-Network-Message-Id": "\n 56a73de9-5233-459a-1990-08de6e661769", "X-MS-Exchange-CrossTenant-Id": "43083d15-7273-40c1-b7db-39efd9ccc17a", "X-MS-Exchange-CrossTenant-OriginalAttributedTenantConnectingIp": "\n TenantId=43083d15-7273-40c1-b7db-39efd9ccc17a;Ip=[216.228.117.160];Helo=[mail.nvidia.com]", "X-MS-Exchange-CrossTenant-AuthSource": "\n\tCO1PEPF000044FD.namprd21.prod.outlook.com", "X-MS-Exchange-CrossTenant-AuthAs": "Anonymous", "X-MS-Exchange-CrossTenant-FromEntityHeader": "HybridOnPrem", "X-MS-Exchange-Transport-CrossTenantHeadersStamped": "SA3PR12MB8440" }, "content": "Introduce safe wrappers around `struct fwctl_device` and\n`struct fwctl_uctx`, allowing rust drivers to register fwctl devices and\nimplement their control and RPC logic in safe rust.\n\nCc: Danilo Krummrich <dakr@kernel.org>\nCc: Gary Guo <gary@garyguo.net>\nCc: Jason Gunthorpe <jgg@nvidia.com>\nCc: Joel Fernandes <joelagnelf@nvidia.com>\nSigned-off-by: Zhi Wang <zhiw@nvidia.com>\n---\n drivers/fwctl/Kconfig | 12 +\n rust/bindings/bindings_helper.h | 1 +\n rust/helpers/fwctl.c | 17 ++\n rust/helpers/helpers.c | 3 +-\n rust/kernel/fwctl.rs | 449 ++++++++++++++++++++++++++++++++\n rust/kernel/lib.rs | 2 +\n 6 files changed, 483 insertions(+), 1 deletion(-)\n create mode 100644 rust/helpers/fwctl.c\n create mode 100644 rust/kernel/fwctl.rs", "diff": "diff --git a/drivers/fwctl/Kconfig b/drivers/fwctl/Kconfig\nindex b5583b12a011..ce42c0f52d6d 100644\n--- a/drivers/fwctl/Kconfig\n+++ b/drivers/fwctl/Kconfig\n@@ -9,6 +9,18 @@ menuconfig FWCTL\n \t fit neatly into an existing subsystem.\n \n if FWCTL\n+\n+config RUST_FWCTL_ABSTRACTIONS\n+\tbool \"Rust fwctl abstractions\"\n+\tdepends on RUST\n+\thelp\n+\t This enables the Rust abstractions for the fwctl device firmware\n+\t access framework. It provides safe wrappers around struct fwctl_device\n+\t and struct fwctl_uctx, allowing Rust drivers to register fwctl devices\n+\t and implement their control and RPC logic in safe Rust.\n+\n+\t If unsure, say N.\n+\n config FWCTL_MLX5\n \ttristate \"mlx5 ConnectX control fwctl driver\"\n \tdepends on MLX5_CORE\ndiff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h\nindex 083cc44aa952..c0a7248989f7 100644\n--- a/rust/bindings/bindings_helper.h\n+++ b/rust/bindings/bindings_helper.h\n@@ -56,6 +56,7 @@\n #include <linux/fdtable.h>\n #include <linux/file.h>\n #include <linux/firmware.h>\n+#include <linux/fwctl.h>\n #include <linux/fs.h>\n #include <linux/i2c.h>\n #include <linux/interrupt.h>\ndiff --git a/rust/helpers/fwctl.c b/rust/helpers/fwctl.c\nnew file mode 100644\nindex 000000000000..c7eecd4336a7\n--- /dev/null\n+++ b/rust/helpers/fwctl.c\n@@ -0,0 +1,17 @@\n+// SPDX-License-Identifier: GPL-2.0\n+\n+#include <linux/fwctl.h>\n+\n+#if IS_ENABLED(CONFIG_RUST_FWCTL_ABSTRACTIONS)\n+\n+__rust_helper struct fwctl_device *rust_helper_fwctl_get(struct fwctl_device *fwctl)\n+{\n+\treturn fwctl_get(fwctl);\n+}\n+\n+__rust_helper void rust_helper_fwctl_put(struct fwctl_device *fwctl)\n+{\n+\tfwctl_put(fwctl);\n+}\n+\n+#endif\ndiff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c\nindex a3c42e51f00a..38fbae6880a0 100644\n--- a/rust/helpers/helpers.c\n+++ b/rust/helpers/helpers.c\n@@ -30,8 +30,9 @@\n #include \"dma.c\"\n #include \"drm.c\"\n #include \"err.c\"\n-#include \"irq.c\"\n #include \"fs.c\"\n+#include \"fwctl.c\"\n+#include \"irq.c\"\n #include \"io.c\"\n #include \"jump_label.c\"\n #include \"kunit.c\"\ndiff --git a/rust/kernel/fwctl.rs b/rust/kernel/fwctl.rs\nnew file mode 100644\nindex 000000000000..38bb6f9ebca5\n--- /dev/null\n+++ b/rust/kernel/fwctl.rs\n@@ -0,0 +1,449 @@\n+// SPDX-License-Identifier: GPL-2.0-only\n+\n+//! Abstractions for the fwctl subsystem.\n+//!\n+//! C header: [`include/linux/fwctl.h`]\n+\n+use crate::{\n+ bindings,\n+ container_of,\n+ device,\n+ devres::Devres,\n+ prelude::*,\n+ sync::aref::{\n+ ARef,\n+ AlwaysRefCounted, //\n+ },\n+ types::Opaque, //\n+};\n+use core::{\n+ marker::PhantomData,\n+ ptr::NonNull,\n+ slice, //\n+};\n+\n+/// Represents a fwctl device type.\n+///\n+/// Corresponds to the C `enum fwctl_device_type`.\n+#[repr(u32)]\n+#[derive(Copy, Clone, Debug, Eq, PartialEq)]\n+pub enum DeviceType {\n+ /// Mellanox ConnectX (mlx5) device.\n+ Mlx5 = bindings::fwctl_device_type_FWCTL_DEVICE_TYPE_MLX5,\n+ /// CXL (Compute Express Link) device.\n+ Cxl = bindings::fwctl_device_type_FWCTL_DEVICE_TYPE_CXL,\n+ /// AMD/Pensando PDS device.\n+ Pds = bindings::fwctl_device_type_FWCTL_DEVICE_TYPE_PDS,\n+}\n+\n+impl From<DeviceType> for u32 {\n+ fn from(device_type: DeviceType) -> Self {\n+ device_type as u32\n+ }\n+}\n+\n+/// Scope of access for an RPC request.\n+///\n+/// Corresponds to the C `enum fwctl_rpc_scope`.\n+#[repr(u32)]\n+#[derive(Copy, Clone, Debug, Eq, PartialEq)]\n+pub enum RpcScope {\n+ /// Read/write access to device configuration.\n+ Configuration = bindings::fwctl_rpc_scope_FWCTL_RPC_CONFIGURATION,\n+ /// Read-only access to debug information.\n+ DebugReadOnly = bindings::fwctl_rpc_scope_FWCTL_RPC_DEBUG_READ_ONLY,\n+ /// Write access to lockdown-compatible debug information.\n+ DebugWrite = bindings::fwctl_rpc_scope_FWCTL_RPC_DEBUG_WRITE,\n+ /// Full read/write access to all debug information (requires `CAP_SYS_RAWIO`).\n+ DebugWriteFull = bindings::fwctl_rpc_scope_FWCTL_RPC_DEBUG_WRITE_FULL,\n+}\n+\n+impl TryFrom<u32> for RpcScope {\n+ type Error = Error;\n+\n+ fn try_from(value: u32) -> Result<Self, Error> {\n+ match value {\n+ v if v == Self::Configuration as u32 => Ok(Self::Configuration),\n+ v if v == Self::DebugReadOnly as u32 => Ok(Self::DebugReadOnly),\n+ v if v == Self::DebugWrite as u32 => Ok(Self::DebugWrite),\n+ v if v == Self::DebugWriteFull as u32 => Ok(Self::DebugWriteFull),\n+ _ => Err(ENOTSUPP),\n+ }\n+ }\n+}\n+\n+/// Response from a [`Operations::fw_rpc`] call.\n+pub enum FwRpcResponse {\n+ /// Reuse the input buffer as the output, with the given output length.\n+ InPlace(usize),\n+ /// Return a newly allocated buffer as the output.\n+ NewBuffer(KVec<u8>),\n+}\n+\n+/// Trait implemented by each Rust driver that integrates with the fwctl subsystem.\n+///\n+/// The implementing type **is** the per-FD user context: one instance is\n+/// created for each `open()` call and dropped when the FD is closed.\n+///\n+/// Each implementation corresponds to a specific device type and provides the\n+/// vtable used by the core `fwctl` layer to manage per-FD user contexts and\n+/// handle RPC requests.\n+pub trait Operations: Sized {\n+ /// Driver data embedded alongside the `fwctl_device` allocation.\n+ type DeviceData;\n+\n+ /// fwctl device type identifier.\n+ const DEVICE_TYPE: DeviceType;\n+\n+ /// Called when a new user context is opened.\n+ ///\n+ /// Returns a [`PinInit`] initializer for `Self`. The instance is dropped\n+ /// automatically when the FD is closed (after [`close`](Self::close)).\n+ fn open(device: &Device<Self>) -> Result<impl PinInit<Self, Error>, Error>;\n+\n+ /// Called when the user context is closed.\n+ ///\n+ /// The driver may perform additional cleanup here that requires access\n+ /// to the owning [`Device`]. `Self` is dropped automatically after this\n+ /// returns.\n+ fn close(_this: Pin<&mut Self>, _device: &Device<Self>) {}\n+\n+ /// Return device information to userspace.\n+ ///\n+ /// The default implementation returns no device-specific data.\n+ fn info(_this: &Self, _device: &Device<Self>) -> Result<KVec<u8>, Error> {\n+ Ok(KVec::new())\n+ }\n+\n+ /// Handle a userspace RPC request.\n+ fn fw_rpc(\n+ this: &Self,\n+ device: &Device<Self>,\n+ scope: RpcScope,\n+ rpc_in: &mut [u8],\n+ ) -> Result<FwRpcResponse, Error>;\n+}\n+\n+/// A fwctl device with embedded driver data.\n+///\n+/// `#[repr(C)]` with the `fwctl_device` at offset 0, matching the C\n+/// `fwctl_alloc_device()` layout convention.\n+///\n+/// # Invariants\n+///\n+/// The `fwctl_device` portion is initialised by the C subsystem during\n+/// [`Device::new()`]. The `data` field is initialised in-place via\n+/// [`PinInit`] before the struct is exposed.\n+#[repr(C)]\n+pub struct Device<T: Operations> {\n+ dev: Opaque<bindings::fwctl_device>,\n+ data: T::DeviceData,\n+}\n+\n+impl<T: Operations> Device<T> {\n+ /// Allocate a new fwctl device with embedded driver data.\n+ ///\n+ /// Returns an [`ARef`] that can be passed to [`Registration::new()`]\n+ /// to make the device visible to userspace. The caller may inspect or\n+ /// configure the device between allocation and registration.\n+ pub fn new(\n+ parent: &device::Device<device::Bound>,\n+ data: impl PinInit<T::DeviceData, Error>,\n+ ) -> Result<ARef<Self>> {\n+ let ops = core::ptr::from_ref::<bindings::fwctl_ops>(&VTable::<T>::VTABLE).cast_mut();\n+\n+ // SAFETY: `_fwctl_alloc_device` allocates `size` bytes via kzalloc and\n+ // initialises the embedded fwctl_device. `ops` points to a static vtable\n+ // that outlives the device. `parent` is bound.\n+ let raw = unsafe {\n+ bindings::_fwctl_alloc_device(parent.as_raw(), ops, core::mem::size_of::<Self>())\n+ };\n+\n+ if raw.is_null() {\n+ return Err(ENOMEM);\n+ }\n+\n+ // CAST: Device<T> is repr(C) with fwctl_device at offset 0.\n+ let this = raw as *mut Self;\n+\n+ // SAFETY: `data` field is within the kzalloc'd allocation, uninitialised.\n+ let data_ptr = unsafe { core::ptr::addr_of_mut!((*this).data) };\n+ unsafe { data.__pinned_init(data_ptr) }.inspect_err(|_| {\n+ // SAFETY: Init failed; release the allocation.\n+ unsafe { bindings::fwctl_put(raw) };\n+ })?;\n+\n+ // SAFETY: `_fwctl_alloc_device` returned a valid pointer with refcount 1\n+ // and DeviceData is fully initialised.\n+ Ok(unsafe { ARef::from_raw(NonNull::new_unchecked(raw as *mut Self)) })\n+ }\n+\n+ /// Returns a reference to the embedded driver data.\n+ pub fn data(&self) -> &T::DeviceData {\n+ &self.data\n+ }\n+\n+ fn as_raw(&self) -> *mut bindings::fwctl_device {\n+ self.dev.get()\n+ }\n+\n+ /// # Safety\n+ ///\n+ /// `ptr` must point to a valid `fwctl_device` embedded in a `Device<T>`.\n+ unsafe fn from_raw<'a>(ptr: *mut bindings::fwctl_device) -> &'a Self {\n+ unsafe { &*ptr.cast() }\n+ }\n+\n+ /// Returns the parent device.\n+ ///\n+ /// The parent is guaranteed to be bound while any fwctl callback is\n+ /// running (ensured by the `registration_lock` read lock on the ioctl\n+ /// path and by `Devres` on the teardown path).\n+ pub fn parent(&self) -> &device::Device<device::Bound> {\n+ // SAFETY: fwctl_device always has a valid parent.\n+ let parent_dev = unsafe { (*self.as_raw()).dev.parent };\n+ let dev: &device::Device = unsafe { device::Device::from_raw(parent_dev) };\n+ // SAFETY: The parent is guaranteed to be bound while fwctl ops are active.\n+ unsafe { dev.as_bound() }\n+ }\n+}\n+\n+impl<T: Operations> AsRef<device::Device> for Device<T> {\n+ fn as_ref(&self) -> &device::Device {\n+ // SAFETY: self.as_raw() is a valid fwctl_device.\n+ let dev = unsafe { core::ptr::addr_of_mut!((*self.as_raw()).dev) };\n+ // SAFETY: dev points to a valid struct device.\n+ unsafe { device::Device::from_raw(dev) }\n+ }\n+}\n+\n+// SAFETY: `fwctl_get` increments the refcount of a valid fwctl_device.\n+// `fwctl_put` decrements it and frees the device when it reaches zero.\n+unsafe impl<T: Operations> AlwaysRefCounted for Device<T> {\n+ fn inc_ref(&self) {\n+ // SAFETY: The existence of a shared reference guarantees that the refcount is non-zero.\n+ unsafe { bindings::fwctl_get(self.as_raw()) };\n+ }\n+\n+ unsafe fn dec_ref(obj: NonNull<Self>) {\n+ // SAFETY: The safety requirements guarantee that the refcount is non-zero.\n+ unsafe { bindings::fwctl_put(obj.cast().as_ptr()) };\n+ }\n+}\n+\n+/// A registered fwctl device.\n+///\n+/// Must live inside a [`Devres`] to guarantee that [`fwctl_unregister`] runs\n+/// before the parent driver unbinds. `Devres` prevents the `Registration`\n+/// from being moved to a context that could outlive the parent device.\n+///\n+/// On drop the device is unregistered (all user contexts are closed and\n+/// `ops` is set to `NULL`) and the [`ARef`] is released.\n+///\n+/// [`fwctl_unregister`]: srctree/drivers/fwctl/main.c\n+pub struct Registration<T: Operations> {\n+ dev: ARef<Device<T>>,\n+}\n+\n+impl<T: Operations> Registration<T> {\n+ /// Register a previously allocated fwctl device.\n+ pub fn new<'a>(\n+ parent: &'a device::Device<device::Bound>,\n+ dev: &'a Device<T>,\n+ ) -> impl PinInit<Devres<Self>, Error> + 'a {\n+ pin_init::pin_init_scope(move || {\n+ // SAFETY: `dev` is a valid fwctl_device backed by an ARef.\n+ let ret = unsafe { bindings::fwctl_register(dev.as_raw()) };\n+ if ret != 0 {\n+ return Err(Error::from_errno(ret));\n+ }\n+\n+ Ok(Devres::new(parent, Self { dev: dev.into() }))\n+ })\n+ }\n+}\n+\n+impl<T: Operations> Drop for Registration<T> {\n+ fn drop(&mut self) {\n+ // SAFETY: `Registration` lives inside a `Devres`, which guarantees\n+ // that drop runs while the parent device is still bound.\n+ unsafe { bindings::fwctl_unregister(self.dev.as_raw()) };\n+ // ARef<Device<T>> is dropped after this, calling fwctl_put.\n+ }\n+}\n+\n+// SAFETY: `Registration` can be sent between threads; the underlying\n+// fwctl_device uses internal locking.\n+unsafe impl<T: Operations> Send for Registration<T> {}\n+\n+// SAFETY: `Registration` provides no mutable access; the underlying\n+// fwctl_device is protected by internal locking.\n+unsafe impl<T: Operations> Sync for Registration<T> {}\n+\n+/// Internal per-FD user context wrapping `struct fwctl_uctx` and `T`.\n+///\n+/// Not exposed to drivers — they work with `&T` / `Pin<&mut T>` directly.\n+#[repr(C)]\n+#[pin_data]\n+struct UserCtx<T: Operations> {\n+ #[pin]\n+ fwctl_uctx: Opaque<bindings::fwctl_uctx>,\n+ #[pin]\n+ uctx: T,\n+}\n+\n+impl<T: Operations> UserCtx<T> {\n+ /// # Safety\n+ ///\n+ /// `ptr` must point to a `fwctl_uctx` embedded in a live `UserCtx<T>`.\n+ unsafe fn from_raw<'a>(ptr: *mut bindings::fwctl_uctx) -> &'a Self {\n+ unsafe { &*container_of!(Opaque::cast_from(ptr), Self, fwctl_uctx) }\n+ }\n+\n+ /// # Safety\n+ ///\n+ /// `ptr` must point to a `fwctl_uctx` embedded in a live `UserCtx<T>`.\n+ /// The caller must ensure exclusive access to the `UserCtx<T>`.\n+ unsafe fn from_raw_mut<'a>(ptr: *mut bindings::fwctl_uctx) -> &'a mut Self {\n+ unsafe { &mut *container_of!(Opaque::cast_from(ptr), Self, fwctl_uctx).cast_mut() }\n+ }\n+\n+ /// Returns a reference to the fwctl [`Device`] that owns this context.\n+ fn device(&self) -> &Device<T> {\n+ // SAFETY: fwctl_uctx.fwctl is set by the subsystem and always valid.\n+ let raw_fwctl = unsafe { (*self.fwctl_uctx.get()).fwctl };\n+ // SAFETY: The fwctl_device is embedded in a Device<T>.\n+ unsafe { Device::from_raw(raw_fwctl) }\n+ }\n+}\n+\n+/// Static vtable mapping Rust trait methods to C callbacks.\n+pub struct VTable<T: Operations>(PhantomData<T>);\n+\n+impl<T: Operations> VTable<T> {\n+ /// The fwctl operations vtable for this driver type.\n+ pub const VTABLE: bindings::fwctl_ops = bindings::fwctl_ops {\n+ device_type: T::DEVICE_TYPE as u32,\n+ uctx_size: core::mem::size_of::<UserCtx<T>>(),\n+ open_uctx: Some(Self::open_uctx_callback),\n+ close_uctx: Some(Self::close_uctx_callback),\n+ info: Some(Self::info_callback),\n+ fw_rpc: Some(Self::fw_rpc_callback),\n+ };\n+\n+ /// # Safety\n+ ///\n+ /// `uctx` must be a valid `fwctl_uctx` embedded in a `UserCtx<T>` with\n+ /// sufficient allocated space for the uctx field.\n+ unsafe extern \"C\" fn open_uctx_callback(uctx: *mut bindings::fwctl_uctx) -> ffi::c_int {\n+ // SAFETY: `fwctl_uctx.fwctl` is set by the subsystem before calling open.\n+ let raw_fwctl = unsafe { (*uctx).fwctl };\n+ // SAFETY: `raw_fwctl` points to a valid fwctl_device embedded in a Device<T>.\n+ let device = unsafe { Device::<T>::from_raw(raw_fwctl) };\n+\n+ let initializer = match T::open(device) {\n+ Ok(init) => init,\n+ Err(e) => return e.to_errno(),\n+ };\n+\n+ let uctx_offset = core::mem::offset_of!(UserCtx<T>, uctx);\n+ // SAFETY: The C side allocated space for the entire UserCtx<T>.\n+ let uctx_ptr: *mut T = unsafe { uctx.cast::<u8>().add(uctx_offset).cast() };\n+\n+ // SAFETY: uctx_ptr points to uninitialised, properly aligned memory.\n+ match unsafe { initializer.__pinned_init(uctx_ptr.cast()) } {\n+ Ok(()) => 0,\n+ Err(e) => e.to_errno(),\n+ }\n+ }\n+\n+ /// # Safety\n+ ///\n+ /// `uctx` must point to a fully initialised `UserCtx<T>`.\n+ unsafe extern \"C\" fn close_uctx_callback(uctx: *mut bindings::fwctl_uctx) {\n+ // SAFETY: `fwctl_uctx.fwctl` is set by the subsystem and always valid.\n+ let device = unsafe { Device::<T>::from_raw((*uctx).fwctl) };\n+\n+ // SAFETY: uctx is a valid pointer promised by C side.\n+ let ctx = unsafe { UserCtx::<T>::from_raw_mut(uctx) };\n+\n+ {\n+ // SAFETY: driver uctx is pinned in place by the C allocation.\n+ let pinned = unsafe { Pin::new_unchecked(&mut ctx.uctx) };\n+ T::close(pinned, device);\n+ }\n+\n+ // SAFETY: After close returns, no further callbacks will access UserCtx.\n+ // Drop T before the C side kfree's the allocation.\n+ unsafe { core::ptr::drop_in_place(&mut ctx.uctx) };\n+ }\n+\n+ /// # Safety\n+ ///\n+ /// `uctx` must point to a fully initialised `UserCtx<T>`.\n+ /// `length` must be a valid pointer.\n+ unsafe extern \"C\" fn info_callback(\n+ uctx: *mut bindings::fwctl_uctx,\n+ length: *mut usize,\n+ ) -> *mut ffi::c_void {\n+ // SAFETY: uctx is a valid pointer promised by C side.\n+ let ctx = unsafe { UserCtx::<T>::from_raw(uctx) };\n+ let device = ctx.device();\n+\n+ match T::info(&ctx.uctx, device) {\n+ Ok(kvec) if kvec.is_empty() => {\n+ // SAFETY: `length` is a valid out-parameter.\n+ unsafe { *length = 0 };\n+ // Return NULL for empty data; kfree(NULL) is safe.\n+ core::ptr::null_mut()\n+ }\n+ Ok(kvec) => {\n+ let (ptr, len, _cap) = kvec.into_raw_parts();\n+ // SAFETY: `length` is a valid out-parameter.\n+ unsafe { *length = len };\n+ ptr.cast::<ffi::c_void>()\n+ }\n+ Err(e) => Error::to_ptr(e),\n+ }\n+ }\n+\n+ /// # Safety\n+ ///\n+ /// `uctx` must point to a fully initialised `UserCtx<T>`.\n+ /// `rpc_in` must be valid for `in_len` bytes. `out_len` must be valid.\n+ unsafe extern \"C\" fn fw_rpc_callback(\n+ uctx: *mut bindings::fwctl_uctx,\n+ scope: u32,\n+ rpc_in: *mut ffi::c_void,\n+ in_len: usize,\n+ out_len: *mut usize,\n+ ) -> *mut ffi::c_void {\n+ let scope = match RpcScope::try_from(scope) {\n+ Ok(s) => s,\n+ Err(e) => return Error::to_ptr(e),\n+ };\n+\n+ // SAFETY: `uctx` is fully initialised; shared access is sufficient.\n+ let ctx = unsafe { UserCtx::<T>::from_raw(uctx) };\n+ let device = ctx.device();\n+\n+ // SAFETY: `rpc_in` / `in_len` are guaranteed valid by the fwctl subsystem.\n+ let rpc_in_slice: &mut [u8] =\n+ unsafe { slice::from_raw_parts_mut(rpc_in.cast::<u8>(), in_len) };\n+\n+ match T::fw_rpc(&ctx.uctx, device, scope, rpc_in_slice) {\n+ Ok(FwRpcResponse::InPlace(len)) => {\n+ // SAFETY: `out_len` is valid.\n+ unsafe { *out_len = len };\n+ rpc_in\n+ }\n+ Ok(FwRpcResponse::NewBuffer(kvec)) => {\n+ let (ptr, len, _cap) = kvec.into_raw_parts();\n+ // SAFETY: `out_len` is valid.\n+ unsafe { *out_len = len };\n+ ptr.cast::<ffi::c_void>()\n+ }\n+ Err(e) => Error::to_ptr(e),\n+ }\n+ }\n+}\ndiff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs\nindex 3da92f18f4ee..d658720276f2 100644\n--- a/rust/kernel/lib.rs\n+++ b/rust/kernel/lib.rs\n@@ -97,6 +97,8 @@\n pub mod firmware;\n pub mod fmt;\n pub mod fs;\n+#[cfg(CONFIG_RUST_FWCTL_ABSTRACTIONS)]\n+pub mod fwctl;\n #[cfg(CONFIG_I2C = \"y\")]\n pub mod i2c;\n pub mod id_pool;\n", "prefixes": [ "v3", "1/1" ] }