From nobody Tue Sep 9 12:18:18 2025 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id A3685C64ED8 for ; Fri, 24 Feb 2023 10:53:54 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230049AbjBXKxw (ORCPT ); Fri, 24 Feb 2023 05:53:52 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:54220 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229709AbjBXKxq (ORCPT ); Fri, 24 Feb 2023 05:53:46 -0500 Received: from mail.marcansoft.com (marcansoft.com [212.63.210.85]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id A8B2D30E0; Fri, 24 Feb 2023 02:53:45 -0800 (PST) Received: from [127.0.0.1] (localhost [127.0.0.1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) (Authenticated sender: linasend@asahilina.net) by mail.marcansoft.com (Postfix) with ESMTPSA id 44C634206F; Fri, 24 Feb 2023 10:53:39 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=asahilina.net; s=default; t=1677236024; bh=sJXKzM9ye5UpmrgbyuuthZgT+KWJTloP+4kPhG+KQ/k=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=oa1/MEiiXrg9mbqpHfbEKIp8g2n5RxkQB42j1wGBzAU51cFkRJi7ueQBGd/e59Brl 7GoMcsyCOveOh/QgTpe9MahflHSAX3d67TWE2Fkll+LVpC7TmLb2WlFNCThlREjzLD 6GUpv6nT76QR2+qiaVjO8JIWMCYT5kO15bKsbjxwjSY0wh0Cev51ebn/OijewbwjpB /2qDSDR9e03ZKD8m6EG42hw0Jst6DA4Y7tLPtc2WHipqK3rY5QbcvsFPcLPsQM58bK yhVVjgcSEvXz0Lox/JZZk/8axll9MDYOPzV8hitLUmGKRjGCHDuyNKJzDhgwlbFUDR G8/MIhnVT3HKA== From: Asahi Lina Date: Fri, 24 Feb 2023 19:53:13 +0900 Subject: [PATCH 1/5] rust: Add a Sealed trait MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20230224-rust-iopt-rtkit-v1-1-49ced3391295@asahilina.net> References: <20230224-rust-iopt-rtkit-v1-0-49ced3391295@asahilina.net> In-Reply-To: <20230224-rust-iopt-rtkit-v1-0-49ced3391295@asahilina.net> To: Miguel Ojeda , Alex Gaynor , Wedson Almeida Filho , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Will Deacon , Robin Murphy , Joerg Roedel , Hector Martin , Sven Peter , Arnd Bergmann , Greg Kroah-Hartman Cc: "Rafael J. Wysocki" , Alyssa Rosenzweig , Neal Gompa , rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, asahi@lists.linux.dev, Asahi Lina X-Mailer: b4 0.12.0 X-Developer-Signature: v=1; a=ed25519-sha256; t=1677236013; l=780; i=lina@asahilina.net; s=20230221; h=from:subject:message-id; bh=sJXKzM9ye5UpmrgbyuuthZgT+KWJTloP+4kPhG+KQ/k=; b=2N7EOf9lvtQl1KnhDXXm0FkvG4Na7dWKbQBTLsMk8jqk7B7Y4WPfN/hlCBMv3CBOdYjE0fIKU E+XrQbtCkZPD7FsAJAXaDP50Tbk5LoTqUkrE+GslcgajYZk1801LH+S X-Developer-Key: i=lina@asahilina.net; a=ed25519; pk=Qn8jZuOtR1m5GaiDfTrAoQ4NE1XoYVZ/wmt5YtXWFC4= Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Some traits exposed by the kernel crate may not be intended to be implemented by downstream modules. Add a Sealed trait to allow avoiding this using the sealed trait pattern. Signed-off-by: Asahi Lina --- rust/kernel/lib.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 223564f9f0cc..82dff6f4cf60 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -46,6 +46,11 @@ pub use macros; #[doc(hidden)] pub use build_error::build_error; =20 +pub(crate) mod private { + #[allow(unreachable_pub)] + pub trait Sealed {} +} + /// Prefix to appear before log messages printed from within the `kernel` = crate. const __LOG_PREFIX: &[u8] =3D b"rust_kernel\0"; =20 --=20 2.35.1 From nobody Tue Sep 9 12:18:18 2025 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id E3670C61DA3 for ; Fri, 24 Feb 2023 10:54:06 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230156AbjBXKyF (ORCPT ); Fri, 24 Feb 2023 05:54:05 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:54928 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230073AbjBXKyA (ORCPT ); Fri, 24 Feb 2023 05:54:00 -0500 Received: from mail.marcansoft.com (marcansoft.com [212.63.210.85]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 3E7DD149A5; Fri, 24 Feb 2023 02:53:51 -0800 (PST) Received: from [127.0.0.1] (localhost [127.0.0.1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) (Authenticated sender: linasend@asahilina.net) by mail.marcansoft.com (Postfix) with ESMTPSA id C3BF64248B; Fri, 24 Feb 2023 10:53:44 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=asahilina.net; s=default; t=1677236029; bh=XcpgOxW9xbW5RI+qtCoIK9qZZvy3Hmqang8OrPgwtSc=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=S5GfRRLtjTEa1GDauhqm4LwpVNGK9hwlbYZMDG1ymiXWS62XPbSGKGYfVdiESMhsi oUWZbjIsVtLtdpjnPlNLc77tt5cg+DOJTJpoB3SVHHaCuQoH8yutbwrBHCwUAni2W1 mqtw2lyg9Mt87jqcgmsl1HYvkyr9NBfRbfvZgjxMbTcr393YYR5lLtfwsxo3q3n7pq ++U2vR8szdn9AXLuHQV+Y5Rvt+zp2PfwNt4/xhZjI/W2vjqvDSw+Fg+khrXomc4NsQ 0TGF7QjLUZgLXGpMl/IEJlXXz2yC5DOQ17LEM+bGey75KDq1KQM6dPemGRxock3Wkv rB9HwMDHJCxtg== From: Asahi Lina Date: Fri, 24 Feb 2023 19:53:14 +0900 Subject: [PATCH 2/5] rust: device: Add a minimal RawDevice trait MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20230224-rust-iopt-rtkit-v1-2-49ced3391295@asahilina.net> References: <20230224-rust-iopt-rtkit-v1-0-49ced3391295@asahilina.net> In-Reply-To: <20230224-rust-iopt-rtkit-v1-0-49ced3391295@asahilina.net> To: Miguel Ojeda , Alex Gaynor , Wedson Almeida Filho , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Will Deacon , Robin Murphy , Joerg Roedel , Hector Martin , Sven Peter , Arnd Bergmann , Greg Kroah-Hartman Cc: "Rafael J. Wysocki" , Alyssa Rosenzweig , Neal Gompa , rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, asahi@lists.linux.dev, Asahi Lina X-Mailer: b4 0.12.0 X-Developer-Signature: v=1; a=ed25519-sha256; t=1677236013; l=2542; i=lina@asahilina.net; s=20230221; h=from:subject:message-id; bh=soR466tRTXjasiXIM/nVS1quyj2/l4EZ0DnmL76tJBs=; b=lgy1PZ0uyKdd/tM/G8noK7EaD5kUIHfT1uSUmIXZ1IA3ri0x3dNuPzq/TelJO3hm34TrgUqE9 bNEhHLGfh08CnZPIzu2dvrfFa3L9h2u7tHdI6Yy4lBJWvKiNRcLpm1x X-Developer-Key: i=lina@asahilina.net; a=ed25519; pk=Qn8jZuOtR1m5GaiDfTrAoQ4NE1XoYVZ/wmt5YtXWFC4= Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: Wedson Almeida Filho Add a RawDevice trait which can be implemented by any type representing a device class (such as a PlatformDevice). This is the minimum amount of Device support code required to unblock abstractions that need to take device pointers. Lina: Rewrote commit message, and dropped everything except RawDevice. Co-developed-by: Miguel Ojeda Signed-off-by: Miguel Ojeda Signed-off-by: Wedson Almeida Filho Signed-off-by: Asahi Lina --- rust/bindings/bindings_helper.h | 1 + rust/kernel/device.rs | 23 +++++++++++++++++++++++ rust/kernel/lib.rs | 1 + 3 files changed, 25 insertions(+) diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helpe= r.h index 75d85bd6c592..3632a39a28a6 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -6,6 +6,7 @@ * Sorted alphabetically. */ =20 +#include #include #include =20 diff --git a/rust/kernel/device.rs b/rust/kernel/device.rs new file mode 100644 index 000000000000..9be021e393ca --- /dev/null +++ b/rust/kernel/device.rs @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Generic devices that are part of the kernel's driver model. +//! +//! C header: [`include/linux/device.h`](../../../../include/linux/device.= h) + +use crate::bindings; + +/// A raw device. +/// +/// # Safety +/// +/// Implementers must ensure that the `*mut device` returned by [`RawDevic= e::raw_device`] is +/// related to `self`, that is, actions on it will affect `self`. For exam= ple, if one calls +/// `get_device`, then the refcount on the device represented by `self` wi= ll be incremented. +/// +/// Additionally, implementers must ensure that the device is never rename= d. Commit a5462516aa99 +/// ("driver-core: document restrictions on device_rename()") has details = on why `device_rename` +/// should not be used. +pub unsafe trait RawDevice { + /// Returns the raw `struct device` related to `self`. + fn raw_device(&self) -> *mut bindings::device; +} diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 82dff6f4cf60..de44092718f8 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -29,6 +29,7 @@ compile_error!("Missing kernel configuration for conditio= nal compilation"); #[cfg(not(testlib))] mod allocator; mod build_assert; +pub mod device; pub mod error; pub mod prelude; pub mod print; --=20 2.35.1 From nobody Tue Sep 9 12:18:18 2025 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 0109AC678DB for ; Fri, 24 Feb 2023 10:54:23 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229992AbjBXKyW (ORCPT ); Fri, 24 Feb 2023 05:54:22 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:55358 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229804AbjBXKyK (ORCPT ); Fri, 24 Feb 2023 05:54:10 -0500 Received: from mail.marcansoft.com (marcansoft.com [212.63.210.85]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 0449B60D5E; Fri, 24 Feb 2023 02:53:56 -0800 (PST) Received: from [127.0.0.1] (localhost [127.0.0.1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) (Authenticated sender: linasend@asahilina.net) by mail.marcansoft.com (Postfix) with ESMTPSA id 6C3BA424B9; Fri, 24 Feb 2023 10:53:50 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=asahilina.net; s=default; t=1677236035; bh=NrVqIiH3X9XgjWiOPXyb08yMiHXA6hAq3CWoGWegOsU=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=z4ncFNu82ujl536rci+ZwNDWUFh+JpOeYiio+n69Y/NI3cGdn8gq9c4GuZ7EP8q1I fCBLp8gKeCGt/Ct4n1TYKG3e6vlFlVOg1uDBXKV0GdEkW66azHhCxeYqsK8Y6+bCdl DhxNz2rI1SMRMUju5sZ3mDOr/I7w8HeEDd5O74wraZZjc901j++oKL0iZdGG2QFo66 Bx2ZTiqEOE8oJaPUC6z6ESMDqG5EoE4OyLG6lxdjJFjaJPg8xfpreQq95VSjWD1usy LaIasHjZ9vUE1y6eX5ptPIozRvnzOZTCdY1AOXbAc8yLCLBYKbFXScR0wDMhp2wUpR Yq+vIzcKu/9SQ== From: Asahi Lina Date: Fri, 24 Feb 2023 19:53:15 +0900 Subject: [PATCH 3/5] rust: io_pgtable: Add io_pgtable abstraction MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20230224-rust-iopt-rtkit-v1-3-49ced3391295@asahilina.net> References: <20230224-rust-iopt-rtkit-v1-0-49ced3391295@asahilina.net> In-Reply-To: <20230224-rust-iopt-rtkit-v1-0-49ced3391295@asahilina.net> To: Miguel Ojeda , Alex Gaynor , Wedson Almeida Filho , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Will Deacon , Robin Murphy , Joerg Roedel , Hector Martin , Sven Peter , Arnd Bergmann , Greg Kroah-Hartman Cc: "Rafael J. Wysocki" , Alyssa Rosenzweig , Neal Gompa , rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, asahi@lists.linux.dev, Asahi Lina X-Mailer: b4 0.12.0 X-Developer-Signature: v=1; a=ed25519-sha256; t=1677236013; l=13923; i=lina@asahilina.net; s=20230221; h=from:subject:message-id; bh=NrVqIiH3X9XgjWiOPXyb08yMiHXA6hAq3CWoGWegOsU=; b=P9OJjQ7AsUwVWHlKKVuH9K+Qw3x4Twvm2E2HX6B8xsfUWQmjTKpiewnwJRmHi+HwymNy1/Yy2 tw0/4bjOLl6BFLJbQJeWSGJXE4wU2Dxn/nVewsuULTqRppTxiCZDFF7 X-Developer-Key: i=lina@asahilina.net; a=ed25519; pk=Qn8jZuOtR1m5GaiDfTrAoQ4NE1XoYVZ/wmt5YtXWFC4= Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org The io_pgtable subsystem implements page table management for various IOMMU page table formats. This abstraction allows Rust drivers for devices with an embedded MMU to use this shared code directly. Gather structures are not implemented yet, since we don't have a consumer for that functionality. That can be added and refactored when someone needs it. It's worth noting that although this abstraction is nominally used to manage page tables and mapping physical memory addresses, the abstraction API itself is not unsafe. This is because, by itself, it can only be used to manage page tables in isolation, which have no effect on the system. In Rust, we typically use `unsafe` to mark operations that actually introduce the safety requirements (that is, where the responsibilities are created). Here, that would be actually installing the page table root pointer into a device register (the downstream iomem abstractions are unsafe for this reason, because devices can do DMA). Signed-off-by: Asahi Lina --- rust/bindings/bindings_helper.h | 1 + rust/kernel/io_pgtable.rs | 351 ++++++++++++++++++++++++++++++++++++= ++++ rust/kernel/lib.rs | 2 + 3 files changed, 354 insertions(+) diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helpe= r.h index 3632a39a28a6..88c65431d3ad 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -7,6 +7,7 @@ */ =20 #include +#include #include #include =20 diff --git a/rust/kernel/io_pgtable.rs b/rust/kernel/io_pgtable.rs new file mode 100644 index 000000000000..19029b1fdfd8 --- /dev/null +++ b/rust/kernel/io_pgtable.rs @@ -0,0 +1,351 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! IOMMU page table management +//! +//! C header: [`include/io-pgtable.h`](../../../../include/io-pgtable.h) + +use crate::{ + bindings, device, + error::{code::*, to_result, Result}, + types::{ForeignOwnable, ScopeGuard}, +}; + +use core::marker::PhantomData; +use core::mem; +use core::num::NonZeroU64; + +/// Protection flags used with IOMMU mappings. +pub mod prot { + /// Read access. + pub const READ: u32 =3D bindings::IOMMU_READ; + /// Write access. + pub const WRITE: u32 =3D bindings::IOMMU_WRITE; + /// Request cache coherency. + pub const CACHE: u32 =3D bindings::IOMMU_CACHE; + /// Request no-execute permission. + pub const NOEXEC: u32 =3D bindings::IOMMU_NOEXEC; + /// MMIO peripheral mapping. + pub const MMIO: u32 =3D bindings::IOMMU_MMIO; + /// Privileged mapping. + pub const PRIV: u32 =3D bindings::IOMMU_PRIV; +} + +/// Represents a requested io_pgtable configuration. +pub struct Config { + /// Quirk bitmask (type-specific). + pub quirks: usize, + /// Valid page sizes, as a bitmask of powers of two. + pub pgsize_bitmap: usize, + /// Input address space size in bits. + pub ias: usize, + /// Output address space size in bits. + pub oas: usize, + /// IOMMU uses coherent accesses for page table walks. + pub coherent_walk: bool, +} + +/// IOMMU callbacks for TLB and page table management. +/// +/// Users must implement this trait to perform the TLB flush actions for t= his IOMMU, if +/// required. +pub trait FlushOps { + /// User-specified type owned by the IOPagetable that will be passed t= o TLB operations. + type Data: ForeignOwnable + Send + Sync; + + /// Synchronously invalidate the entire TLB context. + fn tlb_flush_all(data: ::Borrowed<'_>); + + /// Synchronously invalidate all intermediate TLB state (sometimes ref= erred to as the "walk + /// cache") for a virtual address range. + fn tlb_flush_walk( + data: ::Borrowed<'_>, + iova: usize, + size: usize, + granule: usize, + ); + + /// Optional callback to queue up leaf TLB invalidation for a single p= age. + /// + /// IOMMUs that cannot batch TLB invalidation operations efficiently w= ill typically issue + /// them here, but others may decide to update the iommu_iotlb_gather = structure and defer + /// the invalidation until iommu_iotlb_sync() instead. + /// + /// TODO: Implement the gather argument for batching. + fn tlb_add_page( + data: ::Borrowed<'_>, + iova: usize, + granule: usize, + ); +} + +/// Inner page table info shared across all table types. +/// # Invariants +/// +/// - [`self.ops`] is valid and non-null. +/// - [`self.cfg`] is valid and non-null. +#[doc(hidden)] +pub struct IoPageTableInner { + ops: *mut bindings::io_pgtable_ops, + cfg: bindings::io_pgtable_cfg, + data: *mut core::ffi::c_void, +} + +/// Helper trait to get the config type for a single page table type from = the union. +pub trait GetConfig { + /// Returns the specific output configuration for this page table type. + fn cfg(iopt: &impl IoPageTable) -> &Self + where + Self: Sized; +} + +/// A generic IOMMU page table +pub trait IoPageTable: crate::private::Sealed { + #[doc(hidden)] + const FLUSH_OPS: bindings::iommu_flush_ops; + + #[doc(hidden)] + fn new_fmt( + dev: &dyn device::RawDevice, + format: u32, + config: Config, + data: T::Data, + ) -> Result { + let ptr =3D data.into_foreign() as *mut _; + let guard =3D ScopeGuard::new(|| { + // SAFETY: `ptr` came from a previous call to `into_foreign`. + unsafe { T::Data::from_foreign(ptr) }; + }); + + let mut raw_cfg =3D bindings::io_pgtable_cfg { + quirks: config.quirks.try_into()?, + pgsize_bitmap: config.pgsize_bitmap.try_into()?, + ias: config.ias.try_into()?, + oas: config.oas.try_into()?, + coherent_walk: config.coherent_walk, + tlb: &Self::FLUSH_OPS, + iommu_dev: dev.raw_device(), + // SAFETY: This is an output field which is fine to zero-init. + __bindgen_anon_1: unsafe { mem::zeroed() }, + }; + + // SAFETY: FFI call, all input pointers are valid. + let ops =3D unsafe { + bindings::alloc_io_pgtable_ops(format as bindings::io_pgtable_= fmt, &mut raw_cfg, ptr) + }; + + if ops.is_null() { + return Err(EINVAL); + } + + guard.dismiss(); + Ok(IoPageTableInner { + ops, + cfg: raw_cfg, + data: ptr, + }) + } + + /// Map a range of pages. + fn map_pages( + &mut self, + iova: usize, + paddr: usize, + pgsize: usize, + pgcount: usize, + prot: u32, + ) -> Result { + let mut mapped: usize =3D 0; + + // SAFETY: FFI call, ops is valid per the type invariant. + to_result(unsafe { + (*self.inner().ops).map_pages.unwrap()( + self.inner().ops, + iova as u64, + paddr as u64, + pgsize, + pgcount, + prot as i32, + bindings::GFP_KERNEL, + &mut mapped, + ) + })?; + + Ok(mapped) + } + + /// Unmap a range of pages. + fn unmap_pages( + &mut self, + iova: usize, + pgsize: usize, + pgcount: usize, + // TODO: gather: *mut iommu_iotlb_gather, + ) -> usize { + // SAFETY: FFI call, ops is valid per the type invariant. + unsafe { + (*self.inner().ops).unmap_pages.unwrap()( + self.inner().ops, + iova as u64, + pgsize, + pgcount, + core::ptr::null_mut(), + ) + } + } + + /// Translate an IOVA to the corresponding physical address, if mapped. + fn iova_to_phys(&self, iova: usize) -> Option { + // SAFETY: FFI call, ops is valid per the type invariant. + NonZeroU64::new(unsafe { + (*self.inner().ops).iova_to_phys.unwrap()(self.inner().ops, io= va as u64) + }) + } + + #[doc(hidden)] + fn inner(&self) -> &IoPageTableInner; + + #[doc(hidden)] + fn raw_cfg(&self) -> &bindings::io_pgtable_cfg { + &self.inner().cfg + } +} + +// SAFETY: All abstraction operations either require mutable references or= are thread-safe, +// and io_pgtable_ops objects can be passed between threads without issue. +unsafe impl Send for IoPageTableInner {} +unsafe impl Sync for IoPageTableInner {} + +unsafe extern "C" fn tlb_flush_all_callback(cookie: *mut core= ::ffi::c_void) { + // SAFETY: The cookie is always a ForeignOwnable of the right type, pe= r new_fmt(). + T::tlb_flush_all(unsafe { T::Data::borrow(cookie) }); +} + +unsafe extern "C" fn tlb_flush_walk_callback( + iova: core::ffi::c_ulong, + size: usize, + granule: usize, + cookie: *mut core::ffi::c_void, +) { + // SAFETY: The cookie is always a ForeignOwnable of the right type, pe= r new_fmt(). + T::tlb_flush_walk( + unsafe { T::Data::borrow(cookie) }, + iova as usize, + size, + granule, + ); +} + +unsafe extern "C" fn tlb_add_page_callback( + _gather: *mut bindings::iommu_iotlb_gather, + iova: core::ffi::c_ulong, + granule: usize, + cookie: *mut core::ffi::c_void, +) { + // SAFETY: The cookie is always a ForeignOwnable of the right type, pe= r new_fmt(). + T::tlb_add_page(unsafe { T::Data::borrow(cookie) }, iova as usize, gra= nule); +} + +macro_rules! iopt_cfg { + ($name:ident, $field:ident, $type:ident) =3D> { + /// An IOMMU page table configuration for a specific kind of paget= able. + pub type $name =3D bindings::$type; + + impl GetConfig for $name { + fn cfg(iopt: &impl IoPageTable) -> &$name { + // SAFETY: The type system ensures we are accessing the ri= ght union field. + unsafe { &iopt.raw_cfg().__bindgen_anon_1.$field } + } + } + }; +} + +impl GetConfig for () { + fn cfg(_iopt: &impl IoPageTable) -> &() { + &() + } +} + +macro_rules! iopt_type { + ($type:ident, $cfg:ty, $fmt:ident) =3D> { + /// Represents an IOPagetable of this type. + pub struct $type(IoPageTableInner, PhantomData); + + impl $type { + /// Creates a new IOPagetable implementation of this type. + pub fn new(dev: &dyn device::RawDevice, config: Config, data: = T::Data) -> Result { + Ok(Self( + ::new_fmt::(dev, bindings::$fm= t, config, data)?, + PhantomData, + )) + } + + /// Get the configuration for this IOPagetable. + pub fn cfg(&self) -> &$cfg { + <$cfg as GetConfig>::cfg(self) + } + } + + impl crate::private::Sealed for $type {} + + impl IoPageTable for $type { + const FLUSH_OPS: bindings::iommu_flush_ops =3D bindings::iommu= _flush_ops { + tlb_flush_all: Some(tlb_flush_all_callback::), + tlb_flush_walk: Some(tlb_flush_walk_callback::), + tlb_add_page: Some(tlb_add_page_callback::), + }; + + fn inner(&self) -> &IoPageTableInner { + &self.0 + } + } + + impl Drop for $type { + fn drop(&mut self) { + // SAFETY: The pointer is valid by the type invariant. + unsafe { bindings::free_io_pgtable_ops(self.0.ops) }; + + // Free context data. + // + // SAFETY: This matches the call to `into_foreign` from `n= ew_fmt`. + unsafe { T::Data::from_foreign(self.0.data) }; + } + } + }; +} + +// Ew, bindgen unions really are quite messy... +iopt_cfg!( + ARMLPAES1Cfg, + arm_lpae_s1_cfg, + io_pgtable_cfg__bindgen_ty_1__bindgen_ty_1 +); +iopt_cfg!( + ARMLPAES2Cfg, + arm_lpae_s2_cfg, + io_pgtable_cfg__bindgen_ty_1__bindgen_ty_2 +); +iopt_cfg!( + ARMv7SCfg, + arm_v7s_cfg, + io_pgtable_cfg__bindgen_ty_1__bindgen_ty_3 +); +iopt_cfg!( + ARMMaliLPAECfg, + arm_mali_lpae_cfg, + io_pgtable_cfg__bindgen_ty_1__bindgen_ty_4 +); +iopt_cfg!( + AppleDARTCfg, + apple_dart_cfg, + io_pgtable_cfg__bindgen_ty_1__bindgen_ty_5 +); + +iopt_type!(ARM32LPAES1, ARMLPAES1Cfg, io_pgtable_fmt_ARM_32_LPAE_S1); +iopt_type!(ARM32LPAES2, ARMLPAES2Cfg, io_pgtable_fmt_ARM_32_LPAE_S2); +iopt_type!(ARM64LPAES1, ARMLPAES1Cfg, io_pgtable_fmt_ARM_64_LPAE_S1); +iopt_type!(ARM64LPAES2, ARMLPAES2Cfg, io_pgtable_fmt_ARM_64_LPAE_S2); +iopt_type!(ARMv7S, ARMv7SCfg, io_pgtable_fmt_ARM_V7S); +iopt_type!(ARMMaliLPAE, ARMMaliLPAECfg, io_pgtable_fmt_ARM_MALI_LPAE); +iopt_type!(AMDIOMMUV1, (), io_pgtable_fmt_AMD_IOMMU_V1); +iopt_type!(AppleDART, AppleDARTCfg, io_pgtable_fmt_APPLE_DART); +iopt_type!(AppleDART2, AppleDARTCfg, io_pgtable_fmt_APPLE_DART2); diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index de44092718f8..9944086d7e09 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -31,6 +31,8 @@ mod allocator; mod build_assert; pub mod device; pub mod error; +#[cfg(CONFIG_IOMMU_IO_PGTABLE)] +pub mod io_pgtable; pub mod prelude; pub mod print; mod static_assert; --=20 2.35.1 From nobody Tue Sep 9 12:18:18 2025 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id A3E94C678DB for ; Fri, 24 Feb 2023 10:54:50 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230170AbjBXKyt (ORCPT ); Fri, 24 Feb 2023 05:54:49 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:55358 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230247AbjBXKyV (ORCPT ); Fri, 24 Feb 2023 05:54:21 -0500 Received: from mail.marcansoft.com (marcansoft.com [212.63.210.85]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 8418164E16; Fri, 24 Feb 2023 02:54:02 -0800 (PST) Received: from [127.0.0.1] (localhost [127.0.0.1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) (Authenticated sender: linasend@asahilina.net) by mail.marcansoft.com (Postfix) with ESMTPSA id E917342627; Fri, 24 Feb 2023 10:53:55 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=asahilina.net; s=default; t=1677236040; bh=s8yg5Qe3Cf1DPHC1UIkVpTHEHGDsFbQ4/QodiHzArtM=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=A6Ru0K8ekFVsvGDAELnpxa6tTrj8c587gyepAco4OyLOpVCnGZFAAC8pzTvEnr8BK ERgbLZBGIJZOiyYEOPzwWb7HiM6Gnb+/buqxYEmBZCTjAyUKHAL3jljF9FMyC1EvdD Q795JgevAi5UnOrlku3tg1TYdXWnX6kb5468++fDi4o0VbLkfBEN4nPCX34XnnvcO9 QX7UOjo47PKDFa4+uFUN4bf4lwt7021J0XN8PI57gXxiUDMt2LhCypHcqSw9oUHMwK ZwIz4ZOANwzmOk3L6j4WCbNzqVfi1XeGxtWC2lLies3au6RfD7cxjEXS9kW/WMZbel Qz4VyAEbABKEw== From: Asahi Lina Date: Fri, 24 Feb 2023 19:53:16 +0900 Subject: [PATCH 4/5] rust: soc: apple: rtkit: Add Apple RTKit abstraction MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20230224-rust-iopt-rtkit-v1-4-49ced3391295@asahilina.net> References: <20230224-rust-iopt-rtkit-v1-0-49ced3391295@asahilina.net> In-Reply-To: <20230224-rust-iopt-rtkit-v1-0-49ced3391295@asahilina.net> To: Miguel Ojeda , Alex Gaynor , Wedson Almeida Filho , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Will Deacon , Robin Murphy , Joerg Roedel , Hector Martin , Sven Peter , Arnd Bergmann , Greg Kroah-Hartman Cc: "Rafael J. Wysocki" , Alyssa Rosenzweig , Neal Gompa , rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, asahi@lists.linux.dev, Asahi Lina X-Mailer: b4 0.12.0 X-Developer-Signature: v=1; a=ed25519-sha256; t=1677236013; l=11585; i=lina@asahilina.net; s=20230221; h=from:subject:message-id; bh=s8yg5Qe3Cf1DPHC1UIkVpTHEHGDsFbQ4/QodiHzArtM=; b=czVp61paD5NoMTz4vwcTr9lDdsqWjE73xnF7YjvYkIghtw6eScQB6OZFQccW+TpLk15O7XiLU mYioNvRS1+RBDOqpGHh4UMxmwD3NLQvmeW/kCm575MbOzlXnNhTeCpU X-Developer-Key: i=lina@asahilina.net; a=ed25519; pk=Qn8jZuOtR1m5GaiDfTrAoQ4NE1XoYVZ/wmt5YtXWFC4= Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org RTKit is Apple's proprietary real-time operating system framework, used across many subdevices on Apple Silicon platforms including NVMe, system management, GPU, etc. Add Rust abstractions for this subsystem, so that it can be used by upcoming Rust drivers. This API is safe under the expectation that all RTKit coprocessors either have no unfiltered access to system memory (SMC), are behind a DART IOMMU (DCP, etc.), or require other unsafe operations to be granted access to arbitrary system memory (GFX, where the coprocessor page tables need to be mutated in bootloader-allocated memory blocks in order to map additional memory, which is a requirement to get it to do anything interesting beyond basic startup.) Note: Although ARM64 support is not yet merged, this can be built on amd64 with CONFIG_COMPILE_TEST=3Dy. Signed-off-by: Asahi Lina --- rust/bindings/bindings_helper.h | 1 + rust/kernel/lib.rs | 1 + rust/kernel/soc/apple/mod.rs | 6 + rust/kernel/soc/apple/rtkit.rs | 259 ++++++++++++++++++++++++++++++++++++= ++++ rust/kernel/soc/mod.rs | 5 + 5 files changed, 272 insertions(+) diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helpe= r.h index 88c65431d3ad..c920d6242e3a 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -10,6 +10,7 @@ #include #include #include +#include =20 /* `bindgen` gets confused at certain things. */ const gfp_t BINDINGS_GFP_KERNEL =3D GFP_KERNEL; diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 9944086d7e09..78108cbbf814 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -35,6 +35,7 @@ pub mod error; pub mod io_pgtable; pub mod prelude; pub mod print; +pub mod soc; mod static_assert; #[doc(hidden)] pub mod std_vendor; diff --git a/rust/kernel/soc/apple/mod.rs b/rust/kernel/soc/apple/mod.rs new file mode 100644 index 000000000000..dd69db63677d --- /dev/null +++ b/rust/kernel/soc/apple/mod.rs @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT + +//! Apple SoC drivers + +#[cfg(CONFIG_APPLE_RTKIT =3D "y")] +pub mod rtkit; diff --git a/rust/kernel/soc/apple/rtkit.rs b/rust/kernel/soc/apple/rtkit.rs new file mode 100644 index 000000000000..595b9b3dda96 --- /dev/null +++ b/rust/kernel/soc/apple/rtkit.rs @@ -0,0 +1,259 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT + +//! Support for Apple RTKit coprocessors. +//! +//! C header: [`include/linux/soc/apple/rtkit.h`](../../../../include/linu= x/gpio/driver.h) + +use crate::{ + bindings, device, + error::{code::*, from_kernel_err_ptr, from_kernel_result, to_result, R= esult}, + str::CStr, + types::{ForeignOwnable, ScopeGuard}, +}; + +use alloc::boxed::Box; +use core::marker::PhantomData; +use core::ptr; +use macros::vtable; + +/// Trait to represent allocatable buffers for the RTKit core. +/// +/// Users must implement this trait for their own representation of those = allocations. +pub trait Buffer { + /// Returns the IOVA (virtual address) of the buffer from RTKit's poin= t of view, or an error if + /// unavailable. + fn iova(&self) -> Result; + + /// Returns a mutable byte slice of the buffer contents, or an + /// error if unavailable. + fn buf(&mut self) -> Result<&mut [u8]>; +} + +/// Callback operations for an RTKit client. +#[vtable] +pub trait Operations { + /// Arbitrary user context type. + type Data: ForeignOwnable + Send + Sync; + + /// Type representing an allocated buffer for RTKit. + type Buffer: Buffer; + + /// Called when RTKit crashes. + fn crashed(_data: ::Borrowed<'_>) {} + + /// Called when a message was received on a non-system endpoint. Calle= d in non-IRQ context. + fn recv_message( + _data: ::Borrowed<'_>, + _endpoint: u8, + _message: u64, + ) { + } + + /// Called in IRQ context when a message was received on a non-system = endpoint. + /// + /// Must return `true` if the message is handled, or `false` to proces= s it in + /// the handling thread. + fn recv_message_early( + _data: ::Borrowed<'_>, + _endpoint: u8, + _message: u64, + ) -> bool { + false + } + + /// Allocate a buffer for use by RTKit. + fn shmem_alloc( + _data: ::Borrowed<'_>, + _size: usize, + ) -> Result { + Err(EINVAL) + } + + /// Map an existing buffer used by RTKit at a device-specified virtual= address. + fn shmem_map( + _data: ::Borrowed<'_>, + _iova: usize, + _size: usize, + ) -> Result { + Err(EINVAL) + } +} + +/// Represents `struct apple_rtkit *`. +/// +/// # Invariants +/// +/// The rtk pointer is valid. +/// The data pointer is a valid pointer from T::Data::into_foreign(). +pub struct RtKit { + rtk: *mut bindings::apple_rtkit, + data: *mut core::ffi::c_void, + _p: PhantomData, +} + +unsafe extern "C" fn crashed_callback(cookie: *mut core::ff= i::c_void) { + // SAFETY: cookie is always a PointerWrapper of the right type, passed= in new(). + T::crashed(unsafe { T::Data::borrow(cookie) }); +} + +unsafe extern "C" fn recv_message_callback( + cookie: *mut core::ffi::c_void, + endpoint: u8, + message: u64, +) { + // SAFETY: cookie is always a PointerWrapper of the right type, passed= in new(). + T::recv_message(unsafe { T::Data::borrow(cookie) }, endpoint, message); +} + +unsafe extern "C" fn recv_message_early_callback( + cookie: *mut core::ffi::c_void, + endpoint: u8, + message: u64, +) -> bool { + // SAFETY: cookie is always a PointerWrapper of the right type, passed= in new(). + T::recv_message_early(unsafe { T::Data::borrow(cookie) }, endpoint, me= ssage) +} + +unsafe extern "C" fn shmem_setup_callback( + cookie: *mut core::ffi::c_void, + bfr: *mut bindings::apple_rtkit_shmem, +) -> core::ffi::c_int { + // SAFETY: `bfr` is a valid buffer. + let bfr_mut =3D unsafe { &mut *bfr }; + + let buf =3D if bfr_mut.iova !=3D 0 { + bfr_mut.is_mapped =3D true; + T::shmem_map( + // SAFETY: `cookie` came from a previous call to `into_foreign= `. + unsafe { T::Data::borrow(cookie) }, + bfr_mut.iova as usize, + bfr_mut.size, + ) + } else { + bfr_mut.is_mapped =3D false; + // SAFETY: `cookie` came from a previous call to `into_foreign`. + T::shmem_alloc(unsafe { T::Data::borrow(cookie) }, bfr_mut.size) + }; + + from_kernel_result! { + let mut buf =3D buf?; + let iova =3D buf.iova()?; + let slice =3D buf.buf()?; + + if slice.len() < bfr_mut.size { + return Err(ENOMEM); + } + + bfr_mut.iova =3D iova as u64; + bfr_mut.buffer =3D slice.as_mut_ptr() as *mut _; + + // Now box the returned buffer type and stash it in the private po= inter of the + // `apple_rtkit_shmem` struct for safekeeping. + bfr_mut.private =3D Box::into_raw(Box::try_new(buf)?) as *mut _; + Ok(0) + } +} + +unsafe extern "C" fn shmem_destroy_callback( + _cookie: *mut core::ffi::c_void, + bfr: *mut bindings::apple_rtkit_shmem, +) { + // SAFETY: `bfr` is a valid buffer. + let bfr_mut =3D unsafe { &mut *bfr }; + if !bfr_mut.private.is_null() { + // SAFETY: Per shmem_setup_callback, this has to be a pointer to a= Buffer if it is set. + unsafe { + core::mem::drop(Box::from_raw(bfr_mut.private as *mut T::Buffe= r)); + } + bfr_mut.private =3D core::ptr::null_mut(); + } +} + +impl RtKit { + const VTABLE: bindings::apple_rtkit_ops =3D bindings::apple_rtkit_ops { + crashed: Some(crashed_callback::), + recv_message: Some(recv_message_callback::), + recv_message_early: Some(recv_message_early_callback::), + shmem_setup: if T::HAS_SHMEM_ALLOC || T::HAS_SHMEM_MAP { + Some(shmem_setup_callback::) + } else { + None + }, + shmem_destroy: if T::HAS_SHMEM_ALLOC || T::HAS_SHMEM_MAP { + Some(shmem_destroy_callback::) + } else { + None + }, + }; + + /// Creates a new RTKit client for a given device and optional mailbox= name or index. + pub fn new( + dev: &dyn device::RawDevice, + mbox_name: Option<&'static CStr>, + mbox_idx: usize, + data: T::Data, + ) -> Result { + let ptr =3D data.into_foreign() as *mut _; + let guard =3D ScopeGuard::new(|| { + // SAFETY: `ptr` came from a previous call to `into_foreign`. + unsafe { T::Data::from_foreign(ptr) }; + }); + // SAFETY: This just calls the C init function. + let rtk =3D unsafe { + from_kernel_err_ptr(bindings::apple_rtkit_init( + dev.raw_device(), + ptr, + match mbox_name { + Some(s) =3D> s.as_char_ptr(), + None =3D> ptr::null(), + }, + mbox_idx.try_into()?, + &Self::VTABLE, + )) + }?; + + guard.dismiss(); + // INVARIANT: `rtk` and `data` are valid here. + Ok(Self { + rtk, + data: ptr, + _p: PhantomData, + }) + } + + /// Boots (wakes up) the RTKit coprocessor. + pub fn boot(&mut self) -> Result { + // SAFETY: `rtk` is valid per the type invariant. + to_result(unsafe { bindings::apple_rtkit_boot(self.rtk) }) + } + + /// Starts a non-system endpoint. + pub fn start_endpoint(&mut self, endpoint: u8) -> Result { + // SAFETY: `rtk` is valid per the type invariant. + to_result(unsafe { bindings::apple_rtkit_start_ep(self.rtk, endpoi= nt) }) + } + + /// Sends a message to a given endpoint. + pub fn send_message(&mut self, endpoint: u8, message: u64) -> Result { + // SAFETY: `rtk` is valid per the type invariant. + to_result(unsafe { + bindings::apple_rtkit_send_message(self.rtk, endpoint, message= , ptr::null_mut(), false) + }) + } +} + +// SAFETY: `RtKit` operations require a mutable reference. +unsafe impl Sync for RtKit {} +unsafe impl Send for RtKit {} + +impl Drop for RtKit { + fn drop(&mut self) { + // SAFETY: The pointer is valid by the type invariant. + unsafe { bindings::apple_rtkit_free(self.rtk) }; + + // Free context data. + // + // SAFETY: This matches the call to `into_foreign` from `new` in t= he success case. + unsafe { T::Data::from_foreign(self.data) }; + } +} diff --git a/rust/kernel/soc/mod.rs b/rust/kernel/soc/mod.rs new file mode 100644 index 000000000000..e3024042e74f --- /dev/null +++ b/rust/kernel/soc/mod.rs @@ -0,0 +1,5 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! SoC drivers + +pub mod apple; --=20 2.35.1 From nobody Tue Sep 9 12:18:18 2025 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id EE577C61DA3 for ; Fri, 24 Feb 2023 10:55:03 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230234AbjBXKzC (ORCPT ); Fri, 24 Feb 2023 05:55:02 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:54928 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230197AbjBXKyf (ORCPT ); Fri, 24 Feb 2023 05:54:35 -0500 Received: from mail.marcansoft.com (marcansoft.com [212.63.210.85]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 3ADB665CFA; Fri, 24 Feb 2023 02:54:08 -0800 (PST) Received: from [127.0.0.1] (localhost [127.0.0.1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) (Authenticated sender: linasend@asahilina.net) by mail.marcansoft.com (Postfix) with ESMTPSA id 70EE74206F; Fri, 24 Feb 2023 10:54:01 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=asahilina.net; s=default; t=1677236046; bh=EdCqFOCkFmNCQy+An4xXL1lM8eljicqRMAsCfNlFpXc=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=QQ+STDuF5KmAnOI1efDmHlNdV4hDOgItX2AUUxdagG3FUFVa90uZxAGL7b8dUB7Pt 5QjG7nWPiB/Iq8Ltdn2HxdrrDHWPOJb3c0rtMWBaq+tDdwz4TYBgFKoe9oQDJFMyYN LQFz/vE/drpBjenLI8bhQyU/zpctXEnwUdi3m22lq8s0Sai1gZvvwXgMtsCjXAVlm0 7DayCYMhW3AuA7oiut0NtQNjrGvsadhl7fZI5KOY/UHYi42N1QGIR1ieMKtv0PLF1z JEbeIJBkLLKjIgRTi5TFHJpEFpZRr4qxYw16W2LiDqglXiENJ78ZjNj0jDto5vxOt8 hRpzEfbVxlyRg== From: Asahi Lina Date: Fri, 24 Feb 2023 19:53:17 +0900 Subject: [PATCH 5/5] rust: device: Add a stub abstraction for devices MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20230224-rust-iopt-rtkit-v1-5-49ced3391295@asahilina.net> References: <20230224-rust-iopt-rtkit-v1-0-49ced3391295@asahilina.net> In-Reply-To: <20230224-rust-iopt-rtkit-v1-0-49ced3391295@asahilina.net> To: Miguel Ojeda , Alex Gaynor , Wedson Almeida Filho , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Will Deacon , Robin Murphy , Joerg Roedel , Hector Martin , Sven Peter , Arnd Bergmann , Greg Kroah-Hartman Cc: "Rafael J. Wysocki" , Alyssa Rosenzweig , Neal Gompa , rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, asahi@lists.linux.dev, Asahi Lina X-Mailer: b4 0.12.0 X-Developer-Signature: v=1; a=ed25519-sha256; t=1677236013; l=5017; i=lina@asahilina.net; s=20230221; h=from:subject:message-id; bh=cdKIkyLwKJzj4pWUi4LyfvGZS5F3kfJKVHncm9kraaI=; b=OEIdscGIoIcKDDmhOPjbLGxA2i8Nhrg9rQMhaRWYJa7GQDqVwWSu3E/ZXAwXPBLNGXyvChxkV TnzkvIyCmpxAoIODcjIWil4i3mShoVRVROu+yHknTma9STuRcYVcEqq X-Developer-Key: i=lina@asahilina.net; a=ed25519; pk=Qn8jZuOtR1m5GaiDfTrAoQ4NE1XoYVZ/wmt5YtXWFC4= Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: Wedson Almeida Filho Add a Device type which represents an owned reference to a generic struct device. This minimal implementation just handles reference counting and allows the user to get the device name. Lina: Rewrote commit message, dropped the Amba bits, and squashed in simple changes to the core Device code from latter commits in rust-for-linux/rust. Also include the rust_helper_dev_get_drvdata helper which will be needed by consumers later on anyway. Co-developed-by: Miguel Ojeda Signed-off-by: Miguel Ojeda Signed-off-by: Wedson Almeida Filho Signed-off-by: Asahi Lina --- rust/helpers.c | 13 +++++++++ rust/kernel/device.rs | 76 +++++++++++++++++++++++++++++++++++++++++++++++= +++- 2 files changed, 88 insertions(+), 1 deletion(-) diff --git a/rust/helpers.c b/rust/helpers.c index 04b9be46e887..54954fd80c77 100644 --- a/rust/helpers.c +++ b/rust/helpers.c @@ -20,6 +20,7 @@ =20 #include #include +#include #include #include =20 @@ -65,6 +66,18 @@ long rust_helper_PTR_ERR(__force const void *ptr) } EXPORT_SYMBOL_GPL(rust_helper_PTR_ERR); =20 +void *rust_helper_dev_get_drvdata(struct device *dev) +{ + return dev_get_drvdata(dev); +} +EXPORT_SYMBOL_GPL(rust_helper_dev_get_drvdata); + +const char *rust_helper_dev_name(const struct device *dev) +{ + return dev_name(dev); +} +EXPORT_SYMBOL_GPL(rust_helper_dev_name); + /* * We use `bindgen`'s `--size_t-is-usize` option to bind the C `size_t` ty= pe * as the Rust `usize` type, so we can use it in contexts where Rust diff --git a/rust/kernel/device.rs b/rust/kernel/device.rs index 9be021e393ca..e57da622d817 100644 --- a/rust/kernel/device.rs +++ b/rust/kernel/device.rs @@ -4,7 +4,7 @@ //! //! C header: [`include/linux/device.h`](../../../../include/linux/device.= h) =20 -use crate::bindings; +use crate::{bindings, str::CStr}; =20 /// A raw device. /// @@ -20,4 +20,78 @@ use crate::bindings; pub unsafe trait RawDevice { /// Returns the raw `struct device` related to `self`. fn raw_device(&self) -> *mut bindings::device; + + /// Returns the name of the device. + fn name(&self) -> &CStr { + let ptr =3D self.raw_device(); + + // SAFETY: `ptr` is valid because `self` keeps it alive. + let name =3D unsafe { bindings::dev_name(ptr) }; + + // SAFETY: The name of the device remains valid while it is alive = (because the device is + // never renamed, per the safety requirement of this trait). This = is guaranteed to be the + // case because the reference to `self` outlives the one of the re= turned `CStr` (enforced + // by the compiler because of their lifetimes). + unsafe { CStr::from_char_ptr(name) } + } +} + +/// A ref-counted device. +/// +/// # Invariants +/// +/// `ptr` is valid, non-null, and has a non-zero reference count. One of t= he references is owned by +/// `self`, and will be decremented when `self` is dropped. +pub struct Device { + pub(crate) ptr: *mut bindings::device, +} + +// SAFETY: `Device` only holds a pointer to a C device, which is safe to b= e used from any thread. +unsafe impl Send for Device {} + +// SAFETY: `Device` only holds a pointer to a C device, references to whic= h are safe to be used +// from any thread. +unsafe impl Sync for Device {} + +impl Device { + /// Creates a new device instance. + /// + /// # Safety + /// + /// Callers must ensure that `ptr` is valid, non-null, and has a non-z= ero reference count. + pub unsafe fn new(ptr: *mut bindings::device) -> Self { + // SAFETY: By the safety requirements, ptr is valid and its refcou= nted will be incremented. + unsafe { bindings::get_device(ptr) }; + // INVARIANT: The safety requirements satisfy all but one invarian= t, which is that `self` + // owns a reference. This is satisfied by the call to `get_device`= above. + Self { ptr } + } + + /// Creates a new device instance from an existing [`RawDevice`] insta= nce. + pub fn from_dev(dev: &dyn RawDevice) -> Self { + // SAFETY: The requirements are satisfied by the existence of `Raw= Device` and its safety + // requirements. + unsafe { Self::new(dev.raw_device()) } + } +} + +// SAFETY: The device returned by `raw_device` is the one for which we hol= d a reference. +unsafe impl RawDevice for Device { + fn raw_device(&self) -> *mut bindings::device { + self.ptr + } +} + +impl Drop for Device { + fn drop(&mut self) { + // SAFETY: By the type invariants, we know that `self` owns a refe= rence, so it is safe to + // relinquish it now. + unsafe { bindings::put_device(self.ptr) }; + } +} + +impl Clone for Device { + fn clone(&self) -> Self { + Device::from_dev(self) + } } --=20 2.35.1