From patchwork Wed Sep 11 14:27:18 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ayush Singh X-Patchwork-Id: 13800690 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 bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id C7900EE49B8 for ; Wed, 11 Sep 2024 14:34:01 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender:List-Subscribe:List-Help :List-Post:List-Archive:List-Unsubscribe:List-Id:Cc:To:In-Reply-To:References :Message-Id:Content-Transfer-Encoding:Content-Type:MIME-Version:Subject:Date: From:Reply-To:Content-ID:Content-Description:Resent-Date:Resent-From: Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Owner; bh=YOaH7wUDUVg3jrdLcJDpY4x/GyOxkGNywptNXtNYkIM=; b=IJxOX0ZCzZBKOLzOVgYIdNQMcY NTF9OcUTsujNWkAotP9kZifJbHAlyez+1Bl0+lZh/rWaYsiNvl6c1i+VSiLKlIvo4+De58XL5lQ/Z A/4wouJaqt03qMSlFo0c96/nTiT6si3JvmSG/Ca4oftEs+oVFeA57HoBSGK0rtsZEGzEI9bt31DbV vdPh0ke0xZAE29V3+ShJuNamfYZRJ10zglXIXUevQL/qLoZw321QT7FJS2eC9m/T9jBCDB/p316Mf sVGykvSFy1tudSXElArCBBS1t+l0saodpAR4uL0MLOUQUYkI0OsTLaHTLdCLZA4Pw0htwCtem4uqA S9YqM8uQ==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.97.1 #2 (Red Hat Linux)) id 1soOPl-00000009vdq-0zoj; Wed, 11 Sep 2024 14:33:49 +0000 Received: from mail-pj1-x1041.google.com ([2607:f8b0:4864:20::1041]) by bombadil.infradead.org with esmtps (Exim 4.97.1 #2 (Red Hat Linux)) id 1soOK3-00000009tr8-3D0O for linux-arm-kernel@lists.infradead.org; Wed, 11 Sep 2024 14:27:57 +0000 Received: by mail-pj1-x1041.google.com with SMTP id 98e67ed59e1d1-2d88c0f8e79so5376085a91.3 for ; Wed, 11 Sep 2024 07:27:55 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=beagleboard-org.20230601.gappssmtp.com; s=20230601; t=1726064875; x=1726669675; darn=lists.infradead.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=YOaH7wUDUVg3jrdLcJDpY4x/GyOxkGNywptNXtNYkIM=; b=oKP1zEL5NAapy2a2deOLhTiCP4ReUw0u1FFfj8qqp0JLdgZXQQePc36A9SeDM42fT2 OULBiFNsiGiffPr30yRH5shelmQESsLPJwm67sd+klx/6aZpL9LlndjTpm7ArvMfLutP CsoZ+LudUEhU9N2tGsSRpjCcAZnDulH+kbMmI8pRO/JnWM+LjXvafTDksoX46elPL5dP 7f7blPV9BQtqpM6LFjulKhw/Y4t3IdKHxYCop9IdDbUWjyfrxzVGcIDhFOi5rm8MzaRx ruyDD4DLlelgWTOk2WZxsjkjCn+tK5Cp3cpsQ0GPQIV0Wbd3FvdOxdcixrASGw9+++Kj qhQQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1726064875; x=1726669675; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=YOaH7wUDUVg3jrdLcJDpY4x/GyOxkGNywptNXtNYkIM=; b=j1FogGxXXtnTYeUN2RYvH0o3ZuTeaapHKeKwvBclhtEqa1VyA0hRFKd0t9mwiymY/T mAwznhmuYDRZKVOhfSDfhQvC5c/sQ6P7eL5u+RRN5SEfCiK8NNH3UhReOMI+xPy+MkkW R8EUvysKoV2s+TKCFSiy6Ex96Ee2AEWJmQVXcs5BDSPXdS8Tpos/CPk2V/bWZwcEm3J0 K/sv/tn9ucAT1oHh8cvjWfW0Y4mwi/Kp+zZeyjWTCBtm0cypbP/I2Rdc03aQNwuzfZ2a nxt7P4IrdK+/6F5o5ysTcoNGlFq9ms424YETMR+ij59JEHvbOtF59zA83Q/P3hAl3PA1 qZfg== X-Forwarded-Encrypted: i=1; AJvYcCXUj6dUpIn288J2TDAjfSb9hTDxOUvBqRs5QgmZ/foPZ71I9eakqAHQobMdAK1HoYf5zITggEOLOGc25WfEhy7I@lists.infradead.org X-Gm-Message-State: AOJu0YypQpSxaQaG6GkC4WbsI+pLzFo8+V1hafm0wP2oGDYx7yz9nr5f PEnvAufgFztbjvJMRHvJOESlFDW+H0FmZq9NvP/r6m7ZEMgJ2D4TCGu1ATpaJg== X-Google-Smtp-Source: AGHT+IETD8NcWXEwrzRI7PfUQkX1aQcbeJI3Z+n8OukCsjjngyEPaWbX125rUDAAT6JvWnPjmRzDnw== X-Received: by 2002:a17:90a:8a15:b0:2d8:99c4:3cd9 with SMTP id 98e67ed59e1d1-2dad4de1392mr20248004a91.3.1726064874446; Wed, 11 Sep 2024 07:27:54 -0700 (PDT) Received: from [172.16.118.100] ([103.15.228.94]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-2dadbfe46c9sm10639116a91.11.2024.09.11.07.27.44 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 11 Sep 2024 07:27:54 -0700 (PDT) From: Ayush Singh Date: Wed, 11 Sep 2024 19:57:18 +0530 Subject: [PATCH 1/8] rust: kernel: Add Platform device and driver abstractions MIME-Version: 1.0 Message-Id: <20240911-mikrobus-dt-v1-1-3ded4dc879e7@beagleboard.org> References: <20240911-mikrobus-dt-v1-0-3ded4dc879e7@beagleboard.org> In-Reply-To: <20240911-mikrobus-dt-v1-0-3ded4dc879e7@beagleboard.org> To: fabien.parent@linaro.org, d-gole@ti.com, lorforlinux@beagleboard.org, jkridner@beagleboard.org, robertcnelson@beagleboard.org, Andrew Davis , Miguel Ojeda , Alex Gaynor , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Derek Kiernan , Dragan Cvetic , Arnd Bergmann , Greg Kroah-Hartman , Nishanth Menon , Vignesh Raghavendra , Tero Kristo Cc: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org, Ayush Singh X-Mailer: b4 0.14.1 X-Developer-Signature: v=1; a=openpgp-sha256; l=14880; i=ayush@beagleboard.org; h=from:subject:message-id; bh=zkzVPkQfS9LodBPXtJMr2bHYTVhX/BFEg3r5ta8DpxE=; b=owEBbQKS/ZANAwAIAQXO9ceJ5Vp0AcsmYgBm4ajV037slacrW67oDDENCopdqoaSn9E7y3hl3 pM9wPh0T8KJAjMEAAEIAB0WIQTfzBMe8k8tZW+lBNYFzvXHieVadAUCZuGo1QAKCRAFzvXHieVa dOYHD/wN0sTYbOCHvDxME4ZMoLGPRMqCTb20jASqA7kv2/98kyksK6fRt57d8xDjMebjJEMTP2o zSUbg2hJ+S1vJVaMOMR23MciQnzUZ46EBrYvobIUEsjnmXp2luajua1SbR99DuYEI7Gh99byLhX CUE4z6eqjEVsjp9LPaJjFlRKPm1dG77PJQMGBWh1RRUT6xEwDN5kcPjjfPQCVV2mo+KAMPXeLDp bHYVYpcmt+Vk89jH0Qq9hD4/knDp9Eoid/hmz6/0lsP1Ia6IVScDsJP1CQNb9x9yPHG7BNkxef6 0tdPDnETf636G4efv0UuuoFpfq4lm7txZBWJgnqdvmQmYLQjmVrdr0TRA22sNX6htvIB2jhPLn1 niVYpkJjvpRlHw1F8oWzrVExalYu5tbVwlQdo4JVjnQO7Lywl2pDrL3CDxr06C2Sw4ZOXMnscdc SP9jBNLq2fHQUxGSuK6lXEdNfuHQ3nWc3T6/MYIL6NOviYOMZjPc+kHRl8nv48DpYha/Uy/ISBx A2Y92b5KeXXXtOcU4ElbvLOTLxWU7PoKRka6xzF/0OVmIj2sNPZSkIIajHjEAcYmel6/XjDW3PR ovh7Qj2JLU05teSMqCNe8yb2W7H4zmlTf31BvjMfjJ4mwcNX8zfRf3XQ86XGNbM+wdP3eEhIWww re7PSfNtMph7Esw== X-Developer-Key: i=ayush@beagleboard.org; a=openpgp; fpr=DFCC131EF24F2D656FA504D605CEF5C789E55A74 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20240911_072755_867791_17B47771 X-CRM114-Status: GOOD ( 25.39 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+linux-arm-kernel=archiver.kernel.org@lists.infradead.org From: Fabien Parent Ports Platform device and driver abstractions from Fabien's tree [0]. These abstractions do not depend on any generic driver registration and id table. Instead, the minimal abstractions have been implemented specifically for platform subsystem taking heavy inspiration from the existing phy device and driver abstractions. [0]: https://github.com/Fabo/linux/commits/fparent/rust-platform Signed-off-by: Fabien Parent Signed-off-by: Ayush Singh --- rust/bindings/bindings_helper.h | 1 + rust/kernel/lib.rs | 1 + rust/kernel/platform.rs | 380 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 382 insertions(+) diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h index ae82e9c941af..10cbcdd74089 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index b5f4b3ce6b48..b3a318fde46c 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -42,6 +42,7 @@ #[cfg(CONFIG_NET)] pub mod net; pub mod page; +pub mod platform; pub mod prelude; pub mod print; pub mod rbtree; diff --git a/rust/kernel/platform.rs b/rust/kernel/platform.rs new file mode 100644 index 000000000000..de28429f5551 --- /dev/null +++ b/rust/kernel/platform.rs @@ -0,0 +1,380 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Platform devices and drivers. +//! +//! Also called `platformdev`, `pdev`. +//! +//! C header: [`include/linux/platform_device.h`](../../../../include/linux/platform_device.h) + +use core::{marker::PhantomData, pin::Pin, ptr::addr_of_mut}; + +use macros::vtable; + +use crate::{ + bindings, device, + error::{from_result, Result}, + str::CStr, + types::Opaque, +}; + +/// A platform device. +/// +/// # Invariants +/// +/// The field `ptr` is non-null and valid for the lifetime of the object. +#[repr(transparent)] +pub struct Device(Opaque); + +impl Device { + /// Creates a new [`Device`] instance from a raw pointer. + /// + /// # Safety + /// + /// For the duration of `'a`, + /// - the pointer must point at a valid `platform_device`, and the caller + /// must be in a context where all methods defined on this struct + /// are safe to call. + unsafe fn from_raw<'a>(ptr: *mut bindings::platform_device) -> &'a mut Self { + // CAST: `Self` is a `repr(transparent)` wrapper around `bindings::platform_device`. + let ptr = ptr.cast::(); + // SAFETY: by the function requirements the pointer is valid and we have unique access for + // the duration of `'a`. + unsafe { &mut *ptr } + } + + /// Returns id of the platform device. + pub fn id(&self) -> i32 { + let platformdev = self.0.get(); + // SAFETY: By the type invariants, we know that `self.ptr` is non-null and valid. + unsafe { (*platformdev).id } + } +} + +impl AsRef for Device { + fn as_ref(&self) -> &device::Device { + let platformdev = self.0.get(); + // SAFETY: By the type invariants, we know that `self.ptr` is non-null and valid. + unsafe { device::Device::as_ref(addr_of_mut!((*platformdev).dev)) } + } +} + +/// An adapter for the registration of a Platform driver. +struct Adapter { + _p: PhantomData, +} + +impl Adapter { + /// # Safety + /// + /// `pdev` must be passed by the corresponding callback in `platform_driver`. + unsafe extern "C" fn probe_callback(pdev: *mut bindings::platform_device) -> core::ffi::c_int { + from_result(|| { + // SAFETY: This callback is called only in contexts + // where we can exclusively access `platform_device` because + // it's not published yet, so the accessors on `Device` are okay + // to call. + let dev = unsafe { Device::from_raw(pdev) }; + T::probe(dev)?; + Ok(0) + }) + } + + /// # Safety + /// + /// `pdev` must be passed by the corresponding callback in `platform_driver`. + unsafe extern "C" fn remove_callback(pdev: *mut bindings::platform_device) { + // SAFETY: This callback is called only in contexts + // where we can exclusively access `platform_device` because + // it's not published yet, so the accessors on `Device` are okay + // to call. + let dev = unsafe { Device::from_raw(pdev) }; + T::remove(dev); + } +} + +/// Driver structure for a particular Platform driver. +/// +/// Wraps the kernel's [`struct platform_driver`]. +/// This is used to register a driver for a particular PHY type with the kernel. +/// +/// # Invariants +/// +/// `self.0` is always in a valid state. +/// +/// [`struct platform_driver`]: srctree/include/linux/platform.h +#[repr(transparent)] +pub struct DriverVTable(Opaque); + +// SAFETY: `DriverVTable` doesn't expose any &self method to access internal data, so it's safe to +// share `&DriverVTable` across execution context boundaries. +unsafe impl Sync for DriverVTable {} + +impl DriverVTable { + /// Creates a [`DriverVTable`] instance from [`Driver`]. + /// + /// This is used by [`module_platform_driver`] macro to create a static array of `phy_driver`. + /// + /// [`module_platform_driver`]: crate::module_platform_driver + pub const fn new(match_tbl: &'static DeviceIdTable) -> Self { + let drv = Opaque::new(bindings::platform_driver { + probe: if T::HAS_PROBE { + Some(Adapter::::probe_callback) + } else { + None + }, + __bindgen_anon_1: bindings::platform_driver__bindgen_ty_1 { + remove: if T::HAS_REMOVE { + Some(Adapter::::remove_callback) + } else { + None + }, + }, + driver: create_device_driver::(match_tbl), + // SAFETY: The rest is zeroed out to initialize `struct platform_driver`. + ..unsafe { core::mem::MaybeUninit::::zeroed().assume_init() } + }); + + DriverVTable(drv) + } +} + +const fn create_device_driver( + match_tbl: &'static DeviceIdTable, +) -> bindings::device_driver { + bindings::device_driver { + name: T::NAME.as_char_ptr(), + of_match_table: match_tbl.get(), + // SAFETY: The rest is zeroed out to initialize `struct device_driver`. + ..unsafe { core::mem::MaybeUninit::::zeroed().assume_init() } + } +} + +/// A platform driver. +#[vtable] +pub trait Driver { + /// The friendly name + const NAME: &'static CStr; + + /// Sets up device-specific structures during discovery. + fn probe(_dev: &mut Device) -> Result; + + /// Clean up device-specific structures during removal. + fn remove(_dev: &mut Device); +} + +/// Registration structure for Platform driver. +/// +/// Registers [`DriverVTable`] instance with the kernel. It will be unregistered when dropped. +/// +/// # Invariants +/// +/// The `driver` is currently registered to the kernel via `__platform_driver_register`. +pub struct Registration(Pin<&'static DriverVTable>); + +// SAFETY: The only action allowed in a `Registration` instance is dropping it, which is safe to do +// from any thread because `platform_drivers_unregister` can be called from any thread context. +unsafe impl Send for Registration {} + +impl Registration { + /// Registers a Platform driver. + pub fn new(drv: Pin<&'static DriverVTable>, m: &'static crate::ThisModule) -> Registration { + unsafe { + bindings::__platform_driver_register(drv.0.get(), m.0); + } + + Self(drv) + } +} + +impl Drop for Registration { + fn drop(&mut self) { + unsafe { bindings::platform_driver_unregister(self.0 .0.get()) } + } +} + +/// An identifier for Platform devices. +/// +/// Represents the kernel's [`struct of_device_id`]. This is used to find an appropriate +/// Platform driver. +/// +/// [`struct of_device_id`]: srctree/include/linux/mod_devicetable.h +pub struct DeviceId(&'static CStr); + +impl DeviceId { + /// A zeroed [`struct of_device_id`] used to signify end of of_device_id array. + /// + /// [`struct of_device_id`]: srctree/include/linux/mod_devicetable.h + pub const ZERO: bindings::of_device_id = bindings::of_device_id { + // SAFETY: The rest is zeroed out to initialize `struct of_device_id`. + ..unsafe { core::mem::MaybeUninit::::zeroed().assume_init() } + }; + + /// Create new instance + pub const fn new(s: &'static CStr) -> Self { + Self(s) + } + + const fn compatible(&self) -> [i8; 128] { + let compatible = self.0.as_bytes_with_nul(); + let mut comp = [0i8; 128]; + let mut i = 0; + + while i < compatible.len() { + comp[i] = compatible[i] as _; + i += 1; + } + + comp + } + + // macro use only + #[doc(hidden)] + pub const fn to_rawid(&self) -> bindings::of_device_id { + let comp = self.compatible(); + + bindings::of_device_id { + compatible: comp, + // SAFETY: The rest is zeroed out to initialize `struct of_device_id`. + ..unsafe { core::mem::MaybeUninit::::zeroed().assume_init() } + } + } +} + +/// An array of identifiers for platform driver +#[repr(transparent)] +pub struct DeviceIdTable([bindings::of_device_id; C]); + +impl DeviceIdTable { + /// Create a new instance + pub const fn new(ids: [bindings::of_device_id; C]) -> Self { + Self(ids) + } + + /// Returns a raw pointer to static table. + pub const fn get(&'static self) -> *const bindings::of_device_id { + self.0.as_ptr() + } +} + +// SAFETY: `DeviceIdTable` is only used in C side behind a *const pointer, and thus remains +// immutable and thus can be shared across execution context boundaries. +unsafe impl Sync for DeviceIdTable {} + +/// Declares a kernel module for Platform drivers. +/// +/// This creates a static [`struct platform_driver`] and registers it. It also creates an array of +/// [`struct of_device_id`] for matching the driver to devicetree device. +/// +/// [`struct platform_driver`]: srctree/include/linux/platform.h +/// [`struct of_device_id`]: srctree/include/linux/mod_devicetable.h +/// +/// # Examples +/// +/// ``` +/// # mod module_platform_driver_sample { +/// use kernel::c_str; +/// use kernel::platform::{self, DeviceId}; +/// use kernel::prelude::*; +/// +/// kernel::module_platform_driver! { +/// driver: PlatformSimple, +/// of_table: [DeviceId::new(c_str!("platform-simple"))], +/// name: "rust_sample_platform", +/// author: "Rust for Linux Contributors", +/// description: "Rust sample Platform driver", +/// license: "GPL", +/// } +/// +/// struct PlatformSimple; +/// +/// #[vtable] +/// impl platform::Driver for PlatformSimple { +/// const NAME: &'static CStr = c_str!("PlatformSimple"); +/// } +/// # } +/// ``` +/// +/// This expands to the following code: +/// +/// ```ignore +/// use kernel::c_str; +/// use kernel::platform::{self, DeviceId}; +/// use kernel::prelude::*; +/// +/// +/// struct Module { +/// _reg: $crate::platform::Registration, +/// } +/// +/// module! { +/// type: Module, +/// name: "rust_sample_platform", +/// author: "Rust for Linux Contributors", +/// description: "Rust sample Platform driver", +/// license: "GPL", +/// } +/// +/// const _: () = { +/// static OF_TABLE: $crate::platform::DeviceIdTable = $crate::platform::DeviceIdTable<2>([ +/// (DeviceId::new(c_str!("platform-simple"))).to_rawid(), +/// $crate::platform::DeviceId::ZERO, +/// ]); +/// static DRIVER: $crate::platform::DriverVTable = +/// $crate::platform::DriverVTable::new::(&OF_TABLE); +/// impl $crate::Module for Module { +/// fn init(module: &'static ThisModule) -> Result { +/// let reg = +/// $crate::platform::Registration::new( +/// ::core::pin::Pin::static_ref(&DRIVER), module); +/// Ok(Module { _reg: reg }) +/// } +/// } +/// } +/// +/// struct PlatformSimple; +/// +/// #[vtable] +/// impl platform::Driver for PlatformSimple { +/// const NAME: &'static CStr = c_str!("PlatformSimple"); +/// } +/// ``` +#[macro_export] +macro_rules! module_platform_driver { + (@replace_expr $_t:tt $sub:expr) => {$sub}; + + (@count_devices $($x:expr),*) => { + 0usize $(+ $crate::module_platform_driver!(@replace_expr $x 1usize))* + }; + + (driver: $driver:ident, of_table: [$($of_id:expr),+ $(,)?], $($f:tt)*) => { + struct Module { + _reg: $crate::platform::Registration, + } + + $crate::prelude::module! { + type: Module, + $($f)* + } + + const _: () = { + // SAFETY: C will not read off the end of this constant since the last element is zero. + static OF_TABLE: $crate::platform::DeviceIdTable< + {$crate::module_platform_driver!(@count_devices $($of_id),+) + 1} > = + $crate::platform::DeviceIdTable::new( + [$($of_id.to_rawid()),*, $crate::platform::DeviceId::ZERO]); + + static DRIVER: $crate::platform::DriverVTable = + $crate::platform::DriverVTable::new::< + $driver, {$crate::module_platform_driver!(@count_devices $($of_id),+) + 1} + >(&OF_TABLE); + + impl $crate::Module for Module { + fn init(module: &'static ThisModule) -> Result { + let reg = $crate::platform::Registration::new( + ::core::pin::Pin::static_ref(&DRIVER), module); + Ok(Module { _reg: reg }) + } + } + }; + }; +}