From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from BN1PR04CU002.outbound.protection.outlook.com (mail-eastus2azon11010017.outbound.protection.outlook.com [52.101.56.17]) (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 A39122C15A2; Thu, 6 Nov 2025 10:29:26 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=52.101.56.17 ARC-Seal:i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762424969; cv=fail; b=ervR03+PVTAs5Nsgdyqxz+eFtkBln81fkS+uRBum9LENVsBFeFLkHb605zeRy5ZyWDeIt/BsdHh4sKjGm/ohBbD3YwB6ImIP12s7ej1J4/jjeLaR4aJbREkojfKVykQ1QhDp/8g5wsYp2kJd1A9TUnN0RalggAtPhd9PqlL3h/g= ARC-Message-Signature:i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762424969; c=relaxed/simple; bh=pJU6Dsl2Tt2/lnQwq9Wbd2VjQt8qltIJCeCOJViHvXQ=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=KfH78rwg8NxGXme2s1Kr6zdqlR5hfqO/PX5OzGnul8LsRVAZLR/FZJXAd9c86+m7WaepxZXnkw746acLWH2ib3U6v0pR4BajPQvRD7jH3mGtiQ88sxN3s1Rz+yLTRC43KHFKLQgoQGVGmu61mKQlh7NSUB7xl83/0xO0ffVmDV8= 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=PwtU1aek; arc=fail smtp.client-ip=52.101.56.17 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="PwtU1aek" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=VLSkqedEH7C8/OMFqJACl03OtBfHyKpj6oSeY6bMGW5rwojjcnWl+Y8odLFhvh4ZcB8sdpgaOMZeJQEJk+Qw1fdpDFZvrb+uPjHnKKO0oWY/jCnOovPQXZn85Pvl1yvnCCIsB9ZPhCqzpp4v51SbzYeEzg/1SilqCD/BTabd9Pn8zQvpTGM91SN0Lg9b1BLDUt903CS3RdiLUSHPzddEI5Kdl4oTHYBnzuRJUuHFnYvVl42m9vaX1paYnilnYGhGl499Qq3QO/K/uTDa8it6jmta4AsOGv2ibHpBj9rtm1sQ2uNihqW7LNlGY1vMGV2w1VhaEF0JwNrGJAYaX8bS2w== 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=+0RIm8YSJNe0G9DEDfigAb4Yyr6wgLjX0gnDeeaQ9WU=; b=Scue137egWYA09zxTCalOPTIV75bBl+YrAm6umyUN8tjU9nkWPyGdMDXHuuwFc7c0hI20q0Mlso6zelJWdniDPLUj90RaASbOXC6zdQlV9N90TNc6dQT0wLxsIkoznzh/mzFkB3P2zfUD0QAYajFCwtWMVlaWTk/esPb/7cD2bgHYtFGJPRtY2GV1rdSHmJkCbyLDdqbqST93E4RI12JN0Kg4/J6ZFW5t6UfhHsQ7KQZMn9+Dku8OWaXUsdUWHU2Y4DA2djHuhtqJbP46okerMpJ30mh0KcBACWR4iFLvkjAKZS+9+L5hEcefYTf6iiclYr9u32Qy6LGTGPkQqZ8AA== 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=+0RIm8YSJNe0G9DEDfigAb4Yyr6wgLjX0gnDeeaQ9WU=; b=PwtU1aekOlEOYqedBeWtFJh7HzsJ3MZSH4qr1Gd1yfSLEqNgcjMJPfzMCjgOm6c/vUYHi5OB1oAWomL7qOI4VRBbvVJD9+9/JW8AAuZFsuHc15i6epAu5v3N0mF6ZKxzDLblKmcuQNPRQivtyKNHfnvOiAv8PoWrkeBJoYvQbFv5KEAU2Gf1jAAj72Rtkuet6GVceWM13L+JsSICyyZWbYXQuFBXsErj3g8ZqXo3VmFHygoJ8ap4LelQbB7Qrz4KxKCH3jEt5Bzb44hw5oTIaIC2NufCvbtuYFh8Myur1SYdk7KQH5cFO13iDt1537qTqLBQTdoIy/6XPLb2GTU30g== Received: from MW4PR04CA0388.namprd04.prod.outlook.com (2603:10b6:303:81::33) by MN2PR12MB4221.namprd12.prod.outlook.com (2603:10b6:208:1d2::23) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.9298.12; Thu, 6 Nov 2025 10:29:15 +0000 Received: from SJ1PEPF00002323.namprd03.prod.outlook.com (2603:10b6:303:81:cafe::32) by MW4PR04CA0388.outlook.office365.com (2603:10b6:303:81::33) with Microsoft SMTP Server (version=TLS1_3, cipher=TLS_AES_256_GCM_SHA384) id 15.20.9298.9 via Frontend Transport; Thu, 6 Nov 2025 10:29:04 +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 SJ1PEPF00002323.mail.protection.outlook.com (10.167.242.85) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.9298.6 via Frontend Transport; Thu, 6 Nov 2025 10:29:14 +0000 Received: from rnnvmail203.nvidia.com (10.129.68.9) 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, 6 Nov 2025 02:28:57 -0800 Received: from rnnvmail201.nvidia.com (10.129.68.8) by rnnvmail203.nvidia.com (10.129.68.9) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.2562.20; Thu, 6 Nov 2025 02:28:56 -0800 Received: from inno-vm-xubuntu (10.127.8.13) by mail.nvidia.com (10.129.68.8) with Microsoft SMTP Server id 15.2.2562.20 via Frontend Transport; Thu, 6 Nov 2025 02:28:46 -0800 From: Zhi Wang To: , , CC: , , , , , , , , , , , , , , , , , , , , , , , , "Zhi Wang" Subject: [PATCH v5 4/7] rust: io: factor common I/O helpers into Io trait Date: Thu, 6 Nov 2025 12:27:50 +0200 Message-ID: <20251106102753.2976-5-zhiw@nvidia.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20251106102753.2976-1-zhiw@nvidia.com> References: <20251106102753.2976-1-zhiw@nvidia.com> Precedence: bulk X-Mailing-List: rust-for-linux@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Content-Type: text/plain X-NV-OnPremToCloud: ExternallySecured X-EOPAttributedMessage: 0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: SJ1PEPF00002323:EE_|MN2PR12MB4221:EE_ X-MS-Office365-Filtering-Correlation-Id: f3d8477f-da56-4055-8f7a-08de1d1f551a X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|1800799024|376014|7416014|36860700013|82310400026; X-Microsoft-Antispam-Message-Info: =?us-ascii?Q?9a2xdVmoBG2R8SUrBWbEXTBkAh+VC36DEaoD2XMmulI8w6HndUIy0Uiwfl9o?= =?us-ascii?Q?ZoweH4UhBXTUs7pWqW9vraQLB+vWSeKdMqTyJX+GL8IGCYjCEgpAEp+pSoks?= =?us-ascii?Q?r//73VjVtfAMz8DCs/94IahH6f1TAbca4KDgJP5PC5ybSWMu0pNb/w/NZYNw?= =?us-ascii?Q?sJ+yo1qoLnEjwzg/ypVDP+pvtkjpV3/nL412HVXdkRcFeEL8VfWLo23xnvri?= =?us-ascii?Q?DLsCZXBH6XV3lS86mi6A5MR0vu2vgQY3Qzzy2CEcQ3zvjutm81wyM0fwLUUu?= =?us-ascii?Q?uoiDikNbKrnoML+wKQLacu8VtRfcf8g2teB8aVa+AXAenGCVR+s6BuZiNgxq?= =?us-ascii?Q?fa55HJijzkwzE+hVTYpE70b9OrXP56Ig2m+F+9eLav6pdIh7aSvV8xlqcAvV?= =?us-ascii?Q?/99afvPQGK3xQgJI/nZWiKKhXjxBkYfuXuk9+VUk6siHad8DkWXurjpZJkAs?= =?us-ascii?Q?m1X709VLTe32YGK9+d7qUWRqr9Y+pVYBnHt/C4PiITdJrficrCxPeET3aAmz?= =?us-ascii?Q?eAmlpctw+fRh9SIgQD5iNaqbQB6f2GpAMPRrlYRuvEbiES4MgGa7LU/hq2xH?= =?us-ascii?Q?MZSHPaFadO158eBsQANcH9o+AloI6FBaSr7afft8xVD0GLQHDjmRDz4mMFUW?= =?us-ascii?Q?TRpYYvzvyVD/nbG9KK9cgoFDrgVHVA3/IzGx9kjPcwqAY1dpqUArPzDBC1Cn?= =?us-ascii?Q?TEz69by3uMdyaslvLzqLv0c90caqe7QR73H+khf0ipy6n1Dr9CE2vfZHDTdx?= =?us-ascii?Q?oMTb/3jieIlj32a3++3KkKm9za4sAOmBzyJ/Qm7+celXgQi6uNjCDYxGO7et?= =?us-ascii?Q?RxAJ+zguChHA57bLOX1k9Cyvgrf6RBOWpV08jh168pyytR/irA34+uGhKqq3?= =?us-ascii?Q?YucRzV+DE/rHfDqiE0e7uWqY1E9Q9r0HvinEFhW04TVY0byizx9spGj6V2VF?= =?us-ascii?Q?KfuGcdDr2KaGRtk0cf6JIXQaPW4fkd2NNoGHBauHhEztV7yJp+95zADpE5ax?= =?us-ascii?Q?aSHuZZ+uaONvOoE7XduWKDCyeL+Ng29WVcrQ57grS292Gx3Lako/ynLNKtP1?= =?us-ascii?Q?tiJxqwKtOInXyzgT3SKr4B0wG0q5NvCFmNaZfyeHwJdmDsioJ3dbUF9F/AfM?= =?us-ascii?Q?VTDOFKg8mV4lMF7CkcmWtQe0UN/CRHEGLsd0vdr4QwD6kbvaaa3yHY6gv57x?= =?us-ascii?Q?CZpvzdktGlz6WCoO6slgt0ncgZsW19C10DFMAzSieAi64OIf6+Kun/ebH/mB?= =?us-ascii?Q?GU10ap6gQox7VWletJ3jbhX8rpE74NGoHd4zC7Myy3jppBeZcTT2wCqv5dMc?= =?us-ascii?Q?Zv5Mzzf7a4GtsmaWKwcvQNJQY9q9vlb2DS3u/bh0ZbYbVJk1UmrLmEeXEO2P?= =?us-ascii?Q?QVCzLVIU56ckof2oOhUWdAiiUGjl7szsfiQAxjiSRfGGIkl8+t+BwnAf0DLn?= =?us-ascii?Q?miIDRVMPVXwoTU3AWfRuFvofMsRMgB6CKP7HHsRaoJ2qQbI/69aCmf1CvvIB?= =?us-ascii?Q?dhDKDqml9gGyXNxH7yaPjB1UbD4ULNaYtPibUqpfsuF4RBslYhPjbVQRDenS?= =?us-ascii?Q?gDWVL8UpCParS2h5kyo=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)(1800799024)(376014)(7416014)(36860700013)(82310400026);DIR:OUT;SFP:1101; X-OriginatorOrg: Nvidia.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 06 Nov 2025 10:29:14.1904 (UTC) X-MS-Exchange-CrossTenant-Network-Message-Id: f3d8477f-da56-4055-8f7a-08de1d1f551a 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: SJ1PEPF00002323.namprd03.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Anonymous X-MS-Exchange-CrossTenant-FromEntityHeader: HybridOnPrem X-MS-Exchange-Transport-CrossTenantHeadersStamped: MN2PR12MB4221 The previous Io type combined both the generic I/O access helpers and MMIO implementation details in a single struct. To establish a cleaner layering between the I/O interface and its concrete backends, paving the way for supporting additional I/O mechanisms in the future, Io need to be factored. Factor the common helpers into a new Io trait, and move the MMIO-specific logic into a dedicated Mmio type implementing that trait. Rename the IoRaw to MmioRaw and update the bus MMIO implementations to use MmioRaw. No functional change intended. Cc: Alexandre Courbot Cc: Bjorn Helgaas Cc: Danilo Krummrich Cc: John Hubbard Signed-off-by: Zhi Wang --- drivers/gpu/nova-core/regs/macros.rs | 90 +++++---- drivers/gpu/nova-core/vbios.rs | 1 + rust/kernel/devres.rs | 14 +- rust/kernel/io.rs | 264 ++++++++++++++++++++------- rust/kernel/io/mem.rs | 16 +- rust/kernel/io/poll.rs | 8 +- rust/kernel/pci/io.rs | 12 +- samples/rust/rust_driver_pci.rs | 2 + 8 files changed, 277 insertions(+), 130 deletions(-) diff --git a/drivers/gpu/nova-core/regs/macros.rs b/drivers/gpu/nova-core/regs/macros.rs index 8058e1696df9..39b1069a3429 100644 --- a/drivers/gpu/nova-core/regs/macros.rs +++ b/drivers/gpu/nova-core/regs/macros.rs @@ -608,16 +608,18 @@ impl $name { /// Read the register from its address in `io`. #[inline(always)] - pub(crate) fn read(io: &T) -> Self where - T: ::core::ops::Deref>, + pub(crate) fn read(io: &T) -> Self where + T: ::core::ops::Deref, + I: ::kernel::io::IoInfallible, { Self(io.read32($offset)) } /// Write the value contained in `self` to the register address in `io`. #[inline(always)] - pub(crate) fn write(self, io: &T) where - T: ::core::ops::Deref>, + pub(crate) fn write(self, io: &T) where + T: ::core::ops::Deref, + I: ::kernel::io::IoInfallible, { io.write32(self.0, $offset) } @@ -625,11 +627,12 @@ pub(crate) fn write(self, io: &T) where /// Read the register from its address in `io` and run `f` on its value to obtain a new /// value to write back. #[inline(always)] - pub(crate) fn alter( + pub(crate) fn alter( io: &T, f: F, ) where - T: ::core::ops::Deref>, + T: ::core::ops::Deref, + I: ::kernel::io::IoInfallible, F: ::core::ops::FnOnce(Self) -> Self, { let reg = f(Self::read(io)); @@ -647,12 +650,13 @@ impl $name { /// Read the register from `io`, using the base address provided by `base` and adding /// the register's offset to it. #[inline(always)] - pub(crate) fn read( + pub(crate) fn read( io: &T, #[allow(unused_variables)] base: &B, ) -> Self where - T: ::core::ops::Deref>, + T: ::core::ops::Deref, + I: ::kernel::io::IoInfallible, B: crate::regs::macros::RegisterBase<$base>, { const OFFSET: usize = $name::OFFSET; @@ -667,13 +671,14 @@ pub(crate) fn read( /// Write the value contained in `self` to `io`, using the base address provided by /// `base` and adding the register's offset to it. #[inline(always)] - pub(crate) fn write( + pub(crate) fn write( self, io: &T, #[allow(unused_variables)] base: &B, ) where - T: ::core::ops::Deref>, + T: ::core::ops::Deref, + I: ::kernel::io::IoInfallible, B: crate::regs::macros::RegisterBase<$base>, { const OFFSET: usize = $name::OFFSET; @@ -688,12 +693,13 @@ pub(crate) fn write( /// the register's offset to it, then run `f` on its value to obtain a new value to /// write back. #[inline(always)] - pub(crate) fn alter( + pub(crate) fn alter( io: &T, base: &B, f: F, ) where - T: ::core::ops::Deref>, + T: ::core::ops::Deref, + I: ::kernel::io::IoInfallible, B: crate::regs::macros::RegisterBase<$base>, F: ::core::ops::FnOnce(Self) -> Self, { @@ -713,11 +719,12 @@ impl $name { /// Read the array register at index `idx` from its address in `io`. #[inline(always)] - pub(crate) fn read( + pub(crate) fn read( io: &T, idx: usize, ) -> Self where - T: ::core::ops::Deref>, + T: ::core::ops::Deref, + I: ::kernel::io::IoInfallible, { build_assert!(idx < Self::SIZE); @@ -729,12 +736,13 @@ pub(crate) fn read( /// Write the value contained in `self` to the array register with index `idx` in `io`. #[inline(always)] - pub(crate) fn write( + pub(crate) fn write( self, io: &T, idx: usize ) where - T: ::core::ops::Deref>, + T: ::core::ops::Deref, + I: ::kernel::io::IoInfallible, { build_assert!(idx < Self::SIZE); @@ -746,12 +754,13 @@ pub(crate) fn write( /// Read the array register at index `idx` in `io` and run `f` on its value to obtain a /// new value to write back. #[inline(always)] - pub(crate) fn alter( + pub(crate) fn alter( io: &T, idx: usize, f: F, ) where - T: ::core::ops::Deref>, + T: ::core::ops::Deref, + I: ::kernel::io::IoInfallible, F: ::core::ops::FnOnce(Self) -> Self, { let reg = f(Self::read(io, idx)); @@ -763,11 +772,12 @@ pub(crate) fn alter( /// The validity of `idx` is checked at run-time, and `EINVAL` is returned is the /// access was out-of-bounds. #[inline(always)] - pub(crate) fn try_read( + pub(crate) fn try_read( io: &T, idx: usize, ) -> ::kernel::error::Result where - T: ::core::ops::Deref>, + T: ::core::ops::Deref, + I: ::kernel::io::IoInfallible, { if idx < Self::SIZE { Ok(Self::read(io, idx)) @@ -781,12 +791,13 @@ pub(crate) fn try_read( /// The validity of `idx` is checked at run-time, and `EINVAL` is returned is the /// access was out-of-bounds. #[inline(always)] - pub(crate) fn try_write( + pub(crate) fn try_write( self, io: &T, idx: usize, ) -> ::kernel::error::Result where - T: ::core::ops::Deref>, + T: ::core::ops::Deref, + I: ::kernel::io::IoInfallible, { if idx < Self::SIZE { Ok(self.write(io, idx)) @@ -801,12 +812,13 @@ pub(crate) fn try_write( /// The validity of `idx` is checked at run-time, and `EINVAL` is returned is the /// access was out-of-bounds. #[inline(always)] - pub(crate) fn try_alter( + pub(crate) fn try_alter( io: &T, idx: usize, f: F, ) -> ::kernel::error::Result where - T: ::core::ops::Deref>, + T: ::core::ops::Deref, + I: ::kernel::io::IoInfallible, F: ::core::ops::FnOnce(Self) -> Self, { if idx < Self::SIZE { @@ -832,13 +844,14 @@ impl $name { /// Read the array register at index `idx` from `io`, using the base address provided /// by `base` and adding the register's offset to it. #[inline(always)] - pub(crate) fn read( + pub(crate) fn read( io: &T, #[allow(unused_variables)] base: &B, idx: usize, ) -> Self where - T: ::core::ops::Deref>, + T: ::core::ops::Deref, + I: ::kernel::io::IoInfallible, B: crate::regs::macros::RegisterBase<$base>, { build_assert!(idx < Self::SIZE); @@ -853,14 +866,15 @@ pub(crate) fn read( /// Write the value contained in `self` to `io`, using the base address provided by /// `base` and adding the offset of array register `idx` to it. #[inline(always)] - pub(crate) fn write( + pub(crate) fn write( self, io: &T, #[allow(unused_variables)] base: &B, idx: usize ) where - T: ::core::ops::Deref>, + T: ::core::ops::Deref, + I: ::kernel::io::IoInfallible, B: crate::regs::macros::RegisterBase<$base>, { build_assert!(idx < Self::SIZE); @@ -875,13 +889,14 @@ pub(crate) fn write( /// by `base` and adding the register's offset to it, then run `f` on its value to /// obtain a new value to write back. #[inline(always)] - pub(crate) fn alter( + pub(crate) fn alter( io: &T, base: &B, idx: usize, f: F, ) where - T: ::core::ops::Deref>, + T: ::core::ops::Deref, + I: ::kernel::io::IoInfallible, B: crate::regs::macros::RegisterBase<$base>, F: ::core::ops::FnOnce(Self) -> Self, { @@ -895,12 +910,13 @@ pub(crate) fn alter( /// The validity of `idx` is checked at run-time, and `EINVAL` is returned is the /// access was out-of-bounds. #[inline(always)] - pub(crate) fn try_read( + pub(crate) fn try_read( io: &T, base: &B, idx: usize, ) -> ::kernel::error::Result where - T: ::core::ops::Deref>, + T: ::core::ops::Deref, + I: ::kernel::io::IoInfallible, B: crate::regs::macros::RegisterBase<$base>, { if idx < Self::SIZE { @@ -916,13 +932,14 @@ pub(crate) fn try_read( /// The validity of `idx` is checked at run-time, and `EINVAL` is returned is the /// access was out-of-bounds. #[inline(always)] - pub(crate) fn try_write( + pub(crate) fn try_write( self, io: &T, base: &B, idx: usize, ) -> ::kernel::error::Result where - T: ::core::ops::Deref>, + T: ::core::ops::Deref, + I: ::kernel::io::IoInfallible, B: crate::regs::macros::RegisterBase<$base>, { if idx < Self::SIZE { @@ -939,13 +956,14 @@ pub(crate) fn try_write( /// The validity of `idx` is checked at run-time, and `EINVAL` is returned is the /// access was out-of-bounds. #[inline(always)] - pub(crate) fn try_alter( + pub(crate) fn try_alter( io: &T, base: &B, idx: usize, f: F, ) -> ::kernel::error::Result where - T: ::core::ops::Deref>, + T: ::core::ops::Deref, + I: ::kernel::io::IoInfallible, B: crate::regs::macros::RegisterBase<$base>, F: ::core::ops::FnOnce(Self) -> Self, { diff --git a/drivers/gpu/nova-core/vbios.rs b/drivers/gpu/nova-core/vbios.rs index 71fbe71b84db..7a0121ab9b09 100644 --- a/drivers/gpu/nova-core/vbios.rs +++ b/drivers/gpu/nova-core/vbios.rs @@ -8,6 +8,7 @@ use core::convert::TryFrom; use kernel::device; use kernel::error::Result; +use kernel::io::IoFallible; use kernel::prelude::*; use kernel::ptr::{Alignable, Alignment}; use kernel::types::ARef; diff --git a/rust/kernel/devres.rs b/rust/kernel/devres.rs index d4f3169248df..45739350f7cb 100644 --- a/rust/kernel/devres.rs +++ b/rust/kernel/devres.rs @@ -57,14 +57,15 @@ struct Inner { /// device::{Bound, Device}, /// devres::Devres, /// io::{ -/// Io, -/// IoRaw, // +/// IoInfallible, +/// Mmio, +/// MmioRaw, // /// }, // /// }; /// # use core::ops::Deref; /// /// // See also [`pci::Bar`] for a real example. -/// struct IoMem(IoRaw); +/// struct IoMem(MmioRaw); /// /// impl IoMem { /// /// # Safety @@ -79,7 +80,7 @@ struct Inner { /// return Err(ENOMEM); /// } /// -/// Ok(IoMem(IoRaw::new(addr as usize, SIZE)?)) +/// Ok(IoMem(MmioRaw::new(addr as usize, SIZE)?)) /// } /// } /// @@ -91,11 +92,11 @@ struct Inner { /// } /// /// impl Deref for IoMem { -/// type Target = Io; +/// type Target = Mmio; /// /// fn deref(&self) -> &Self::Target { /// // SAFETY: The memory range stored in `self` has been properly mapped in `Self::new`. -/// unsafe { Io::from_raw(&self.0) } +/// unsafe { Mmio::from_raw(&self.0) } /// } /// } /// # fn no_run(dev: &Device) -> Result<(), Error> { @@ -241,6 +242,7 @@ pub fn device(&self) -> &Device { /// # use kernel::{ /// device::Core, /// devres::Devres, + /// io::IoInfallible, /// pci, // /// }; /// diff --git a/rust/kernel/io.rs b/rust/kernel/io.rs index 6f1e22613f8b..6221dea9c8c3 100644 --- a/rust/kernel/io.rs +++ b/rust/kernel/io.rs @@ -20,16 +20,16 @@ /// By itself, the existence of an instance of this structure does not provide any guarantees that /// the represented MMIO region does exist or is properly mapped. /// -/// Instead, the bus specific MMIO implementation must convert this raw representation into an `Io` -/// instance providing the actual memory accessors. Only by the conversion into an `Io` structure -/// any guarantees are given. -pub struct IoRaw { +/// Instead, the bus specific MMIO implementation must convert this raw representation into an +/// `Mmio` instance providing the actual memory accessors. Only by the conversion into an `Mmio` +/// structure any guarantees are given. +pub struct MmioRaw { addr: usize, maxsize: usize, } -impl IoRaw { - /// Returns a new `IoRaw` instance on success, an error otherwise. +impl MmioRaw { + /// Returns a new `MmioRaw` instance on success, an error otherwise. pub fn new(addr: usize, maxsize: usize) -> Result { if maxsize < SIZE { return Err(EINVAL); @@ -68,14 +68,16 @@ pub fn maxsize(&self) -> usize { /// bindings, /// ffi::c_void, /// io::{ -/// Io, -/// IoRaw, // +/// IoFallible, +/// IoInfallible, +/// Mmio, +/// MmioRaw, // /// }, // /// }; /// # use core::ops::Deref; /// /// // See also [`pci::Bar`] for a real example. -/// struct IoMem(IoRaw); +/// struct IoMem(MmioRaw); /// /// impl IoMem { /// /// # Safety @@ -90,7 +92,7 @@ pub fn maxsize(&self) -> usize { /// return Err(ENOMEM); /// } /// -/// Ok(IoMem(IoRaw::new(addr as usize, SIZE)?)) +/// Ok(IoMem(MmioRaw::new(addr as usize, SIZE)?)) /// } /// } /// @@ -102,11 +104,11 @@ pub fn maxsize(&self) -> usize { /// } /// /// impl Deref for IoMem { -/// type Target = Io; +/// type Target = Mmio; /// /// fn deref(&self) -> &Self::Target { /// // SAFETY: The memory range stored in `self` has been properly mapped in `Self::new`. -/// unsafe { Io::from_raw(&self.0) } +/// unsafe { Mmio::from_raw(&self.0) } /// } /// } /// @@ -120,29 +122,31 @@ pub fn maxsize(&self) -> usize { /// # } /// ``` #[repr(transparent)] -pub struct Io(IoRaw); +pub struct Mmio(MmioRaw); macro_rules! define_read { - ($(#[$attr:meta])* $name:ident, $try_name:ident, $c_fn:ident -> $type_name:ty) => { + (infallible, $(#[$attr:meta])* $vis:vis $name:ident, $c_fn:ident -> $type_name:ty) => { /// Read IO data from a given offset known at compile time. /// /// Bound checks are performed on compile time, hence if the offset is not known at compile /// time, the build will fail. $(#[$attr])* #[inline] - pub fn $name(&self, offset: usize) -> $type_name { + $vis fn $name(&self, offset: usize) -> $type_name { let addr = self.io_addr_assert::<$type_name>(offset); // SAFETY: By the type invariant `addr` is a valid address for MMIO operations. unsafe { bindings::$c_fn(addr as *const c_void) } } + }; + (fallible, $(#[$attr:meta])* $vis:vis $try_name:ident, $c_fn:ident -> $type_name:ty) => { /// Read IO data from a given offset. /// /// Bound checks are performed on runtime, it fails if the offset (plus the type size) is /// out of bounds. $(#[$attr])* - pub fn $try_name(&self, offset: usize) -> Result<$type_name> { + $vis fn $try_name(&self, offset: usize) -> Result<$type_name> { let addr = self.io_addr::<$type_name>(offset)?; // SAFETY: By the type invariant `addr` is a valid address for MMIO operations. @@ -152,26 +156,28 @@ pub fn $try_name(&self, offset: usize) -> Result<$type_name> { } macro_rules! define_write { - ($(#[$attr:meta])* $name:ident, $try_name:ident, $c_fn:ident <- $type_name:ty) => { + (infallible, $(#[$attr:meta])* $vis:vis $name:ident, $c_fn:ident <- $type_name:ty) => { /// Write IO data from a given offset known at compile time. /// /// Bound checks are performed on compile time, hence if the offset is not known at compile /// time, the build will fail. $(#[$attr])* #[inline] - pub fn $name(&self, value: $type_name, offset: usize) { + $vis fn $name(&self, value: $type_name, offset: usize) { let addr = self.io_addr_assert::<$type_name>(offset); // SAFETY: By the type invariant `addr` is a valid address for MMIO operations. unsafe { bindings::$c_fn(value, addr as *mut c_void) } } + }; + (fallible, $(#[$attr:meta])* $vis:vis $try_name:ident, $c_fn:ident <- $type_name:ty) => { /// Write IO data from a given offset. /// /// Bound checks are performed on runtime, it fails if the offset (plus the type size) is /// out of bounds. $(#[$attr])* - pub fn $try_name(&self, value: $type_name, offset: usize) -> Result { + $vis fn $try_name(&self, value: $type_name, offset: usize) -> Result { let addr = self.io_addr::<$type_name>(offset)?; // SAFETY: By the type invariant `addr` is a valid address for MMIO operations. @@ -181,43 +187,38 @@ pub fn $try_name(&self, value: $type_name, offset: usize) -> Result { }; } -impl Io { - /// Converts an `IoRaw` into an `Io` instance, providing the accessors to the MMIO mapping. - /// - /// # Safety - /// - /// Callers must ensure that `addr` is the start of a valid I/O mapped memory region of size - /// `maxsize`. - pub unsafe fn from_raw(raw: &IoRaw) -> &Self { - // SAFETY: `Io` is a transparent wrapper around `IoRaw`. - unsafe { &*core::ptr::from_ref(raw).cast() } +/// Checks whether an access of type `U` at the given `offset` +/// is valid within this region. +#[inline] +const fn offset_valid(offset: usize, size: usize) -> bool { + let type_size = core::mem::size_of::(); + if let Some(end) = offset.checked_add(type_size) { + end <= size && offset % type_size == 0 + } else { + false } +} + +/// Represents a region of I/O space of a fixed size. +/// +/// Provides common helpers for offset validation and address +/// calculation on top of a base address and maximum size. +/// +pub trait Io { + /// Minimum usable size + const MIN_SIZE: usize; /// Returns the base address of this mapping. - #[inline] - pub fn addr(&self) -> usize { - self.0.addr() - } + fn addr(&self) -> usize; /// Returns the maximum size of this mapping. - #[inline] - pub fn maxsize(&self) -> usize { - self.0.maxsize() - } - - #[inline] - const fn offset_valid(offset: usize, size: usize) -> bool { - let type_size = core::mem::size_of::(); - if let Some(end) = offset.checked_add(type_size) { - end <= size && offset % type_size == 0 - } else { - false - } - } + fn maxsize(&self) -> usize; + /// Returns the absolute I/O address for a given `offset`. + /// Performs runtime bounds checks using [`offset_valid`] #[inline] fn io_addr(&self, offset: usize) -> Result { - if !Self::offset_valid::(offset, self.maxsize()) { + if !offset_valid::(offset, self.maxsize()) { return Err(EINVAL); } @@ -226,50 +227,173 @@ fn io_addr(&self, offset: usize) -> Result { self.addr().checked_add(offset).ok_or(EINVAL) } + /// Returns the absolute I/O address for a given `offset`, + /// performing compile-time bound checks. #[inline] fn io_addr_assert(&self, offset: usize) -> usize { - build_assert!(Self::offset_valid::(offset, SIZE)); + build_assert!(offset_valid::(offset, Self::MIN_SIZE)); self.addr() + offset } +} + +/// Types implementing this trait (e.g. MMIO BARs or PCI config +/// regions) can share the same Infallible accessors. +pub trait IoInfallible: Io { + /// Infallible 8-bit read with compile-time bounds check. + fn read8(&self, offset: usize) -> u8; + + /// Infallible 16-bit read with compile-time bounds check. + fn read16(&self, offset: usize) -> u16; + + /// Infallible 32-bit read with compile-time bounds check. + fn read32(&self, offset: usize) -> u32; + + /// Infallible 8-bit write with compile-time bounds check. + fn write8(&self, value: u8, offset: usize); + + /// Infallible 16-bit write with compile-time bounds check. + fn write16(&self, value: u16, offset: usize); + + /// Infallible 32-bit write with compile-time bounds check. + fn write32(&self, value: u32, offset: usize); +} + +/// Types implementing this trait (e.g. MMIO BARs or PCI config +/// regions) can share the same Fallible accessors. +pub trait IoFallible: Io { + /// Fallible 8-bit read with runtime bounds check. + fn try_read8(&self, offset: usize) -> Result; + + /// Fallible 16-bit read with runtime bounds check. + fn try_read16(&self, offset: usize) -> Result; + + /// Fallible 32-bit read with runtime bounds check. + fn try_read32(&self, offset: usize) -> Result; + + /// Fallible 8-bit write with runtime bounds check. + fn try_write8(&self, value: u8, offset: usize) -> Result; + + /// Fallible 16-bit write with runtime bounds check. + fn try_write16(&self, value: u16, offset: usize) -> Result; + + /// Fallible 32-bit write with runtime bounds check. + fn try_write32(&self, value: u32, offset: usize) -> Result; +} + +impl Io for Mmio { + const MIN_SIZE: usize = SIZE; + + /// Returns the base address of this mapping. + #[inline] + fn addr(&self) -> usize { + self.0.addr() + } + + /// Returns the maximum size of this mapping. + #[inline] + fn maxsize(&self) -> usize { + self.0.maxsize() + } +} + +impl IoInfallible for Mmio { + define_read!(infallible, read8, readb -> u8); + define_read!(infallible, read16, readw -> u16); + define_read!(infallible, read32, readl -> u32); + + define_write!(infallible, write8, writeb <- u8); + define_write!(infallible, write16, writew <- u16); + define_write!(infallible, write32, writel <- u32); +} + +impl IoFallible for Mmio { + define_read!(fallible, try_read8, readb -> u8); + define_read!(fallible, try_read16, readw -> u16); + define_read!(fallible, try_read32, readl -> u32); + + define_write!(fallible, try_write8, writeb <- u8); + define_write!(fallible, try_write16, writew <- u16); + define_write!(fallible, try_write32, writel <- u32); +} + +impl Mmio { + /// Converts an `MmioRaw` into an `Mmio` instance, providing the accessors to the MMIO mapping. + /// + /// # Safety + /// + /// Callers must ensure that `addr` is the start of a valid I/O mapped memory region of size + /// `maxsize`. + pub unsafe fn from_raw(raw: &MmioRaw) -> &Self { + // SAFETY: `Mmio` is a transparent wrapper around `MmioRaw`. + unsafe { &*core::ptr::from_ref(raw).cast() } + } - define_read!(read8, try_read8, readb -> u8); - define_read!(read16, try_read16, readw -> u16); - define_read!(read32, try_read32, readl -> u32); define_read!( + infallible, #[cfg(CONFIG_64BIT)] - read64, - try_read64, + pub read64, readq -> u64 ); - define_read!(read8_relaxed, try_read8_relaxed, readb_relaxed -> u8); - define_read!(read16_relaxed, try_read16_relaxed, readw_relaxed -> u16); - define_read!(read32_relaxed, try_read32_relaxed, readl_relaxed -> u32); + define_write!( + infallible, + #[cfg(CONFIG_64BIT)] + pub write64, + writeq <- u64 + ); + define_read!( + fallible, #[cfg(CONFIG_64BIT)] - read64_relaxed, - try_read64_relaxed, - readq_relaxed -> u64 + pub try_read64, + readq -> u64 ); - define_write!(write8, try_write8, writeb <- u8); - define_write!(write16, try_write16, writew <- u16); - define_write!(write32, try_write32, writel <- u32); define_write!( + fallible, #[cfg(CONFIG_64BIT)] - write64, - try_write64, + pub try_write64, writeq <- u64 ); - define_write!(write8_relaxed, try_write8_relaxed, writeb_relaxed <- u8); - define_write!(write16_relaxed, try_write16_relaxed, writew_relaxed <- u16); - define_write!(write32_relaxed, try_write32_relaxed, writel_relaxed <- u32); + define_read!(infallible, pub read8_relaxed, readb_relaxed -> u8); + define_read!(infallible, pub read16_relaxed, readw_relaxed -> u16); + define_read!(infallible, pub read32_relaxed, readl_relaxed -> u32); + define_read!( + infallible, + #[cfg(CONFIG_64BIT)] + pub read64_relaxed, + readq_relaxed -> u64 + ); + + define_read!(fallible, pub try_read8_relaxed, readb_relaxed -> u8); + define_read!(fallible, pub try_read16_relaxed, readw_relaxed -> u16); + define_read!(fallible, pub try_read32_relaxed, readl_relaxed -> u32); + define_read!( + fallible, + #[cfg(CONFIG_64BIT)] + pub try_read64_relaxed, + readq_relaxed -> u64 + ); + + define_write!(infallible, pub write8_relaxed, writeb_relaxed <- u8); + define_write!(infallible, pub write16_relaxed, writew_relaxed <- u16); + define_write!(infallible, pub write32_relaxed, writel_relaxed <- u32); + define_write!( + infallible, + #[cfg(CONFIG_64BIT)] + pub write64_relaxed, + writeq_relaxed <- u64 + ); + + define_write!(fallible, pub try_write8_relaxed, writeb_relaxed <- u8); + define_write!(fallible, pub try_write16_relaxed, writew_relaxed <- u16); + define_write!(fallible, pub try_write32_relaxed, writel_relaxed <- u32); define_write!( + fallible, #[cfg(CONFIG_64BIT)] - write64_relaxed, - try_write64_relaxed, + pub try_write64_relaxed, writeq_relaxed <- u64 ); } diff --git a/rust/kernel/io/mem.rs b/rust/kernel/io/mem.rs index b03b82cd531b..5dcd7c901427 100644 --- a/rust/kernel/io/mem.rs +++ b/rust/kernel/io/mem.rs @@ -17,8 +17,8 @@ Region, Resource, // }, - Io, - IoRaw, // + Mmio, + MmioRaw, // }, prelude::*, }; @@ -203,7 +203,7 @@ pub fn new<'a>(io_request: IoRequest<'a>) -> impl PinInit, Error> + } impl Deref for ExclusiveIoMem { - type Target = Io; + type Target = Mmio; fn deref(&self) -> &Self::Target { &self.iomem @@ -217,10 +217,10 @@ fn deref(&self) -> &Self::Target { /// /// # Invariants /// -/// [`IoMem`] always holds an [`IoRaw`] instance that holds a valid pointer to the +/// [`IoMem`] always holds an [`MmioRaw`] instance that holds a valid pointer to the /// start of the I/O memory mapped region. pub struct IoMem { - io: IoRaw, + io: MmioRaw, } impl IoMem { @@ -255,7 +255,7 @@ fn ioremap(resource: &Resource) -> Result { return Err(ENOMEM); } - let io = IoRaw::new(addr as usize, size)?; + let io = MmioRaw::new(addr as usize, size)?; let io = IoMem { io }; Ok(io) @@ -278,10 +278,10 @@ fn drop(&mut self) { } impl Deref for IoMem { - type Target = Io; + type Target = Mmio; fn deref(&self) -> &Self::Target { // SAFETY: Safe as by the invariant of `IoMem`. - unsafe { Io::from_raw(&self.io) } + unsafe { Mmio::from_raw(&self.io) } } } diff --git a/rust/kernel/io/poll.rs b/rust/kernel/io/poll.rs index b1a2570364f4..543a4b7cea0d 100644 --- a/rust/kernel/io/poll.rs +++ b/rust/kernel/io/poll.rs @@ -45,12 +45,12 @@ /// # Examples /// /// ```no_run -/// use kernel::io::{Io, poll::read_poll_timeout}; +/// use kernel::io::{IoFallible, Mmio, poll::read_poll_timeout}; /// use kernel::time::Delta; /// /// const HW_READY: u16 = 0x01; /// -/// fn wait_for_hardware(io: &Io) -> Result { +/// fn wait_for_hardware(io: &Mmio) -> Result { /// read_poll_timeout( /// // The `op` closure reads the value of a specific status register. /// || io.try_read16(0x1000), @@ -128,12 +128,12 @@ pub fn read_poll_timeout( /// # Examples /// /// ```no_run -/// use kernel::io::{poll::read_poll_timeout_atomic, Io}; +/// use kernel::io::{poll::read_poll_timeout_atomic, IoFallible, Mmio}; /// use kernel::time::Delta; /// /// const HW_READY: u16 = 0x01; /// -/// fn wait_for_hardware(io: &Io) -> Result { +/// fn wait_for_hardware(io: &Mmio) -> Result { /// read_poll_timeout_atomic( /// // The `op` closure reads the value of a specific status register. /// || io.try_read16(0x1000), diff --git a/rust/kernel/pci/io.rs b/rust/kernel/pci/io.rs index 0d55c3139b6f..2bbb3261198d 100644 --- a/rust/kernel/pci/io.rs +++ b/rust/kernel/pci/io.rs @@ -8,8 +8,8 @@ device, devres::Devres, io::{ - Io, - IoRaw, // + Mmio, + MmioRaw, // }, prelude::*, sync::aref::ARef, // @@ -24,7 +24,7 @@ /// memory mapped PCI BAR and its size. pub struct Bar { pdev: ARef, - io: IoRaw, + io: MmioRaw, num: i32, } @@ -60,7 +60,7 @@ pub(super) fn new(pdev: &Device, num: u32, name: &CStr) -> Result { return Err(ENOMEM); } - let io = match IoRaw::new(ioptr, len as usize) { + let io = match MmioRaw::new(ioptr, len as usize) { Ok(io) => io, Err(err) => { // SAFETY: @@ -114,11 +114,11 @@ fn drop(&mut self) { } impl Deref for Bar { - type Target = Io; + type Target = Mmio; fn deref(&self) -> &Self::Target { // SAFETY: By the type invariant of `Self`, the MMIO range in `self.io` is properly mapped. - unsafe { Io::from_raw(&self.io) } + unsafe { Mmio::from_raw(&self.io) } } } diff --git a/samples/rust/rust_driver_pci.rs b/samples/rust/rust_driver_pci.rs index ee6248b8cda5..74b93ca7c338 100644 --- a/samples/rust/rust_driver_pci.rs +++ b/samples/rust/rust_driver_pci.rs @@ -8,6 +8,8 @@ c_str, device::Core, devres::Devres, + io::IoFallible, + io::IoInfallible, pci, prelude::*, sync::aref::ARef, // -- 2.51.0