rust-for-linux.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH WIP 0/5] qcom-socinfo Rust Implementation
@ 2025-08-19 23:12 Matthew Maurer
  2025-08-19 23:12 ` [PATCH WIP 1/5] rust: Add soc_device support Matthew Maurer
                   ` (4 more replies)
  0 siblings, 5 replies; 6+ messages in thread
From: Matthew Maurer @ 2025-08-19 23:12 UTC (permalink / raw)
  To: Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
	Björn Roy Baron, Andreas Hindborg, Alice Ryhl, Trevor Gross,
	Danilo Krummrich, Greg Kroah-Hartman, Rafael J. Wysocki,
	Sami Tolvanen, Timur Tabi, Benno Lossin, Dirk Beheme
  Cc: linux-kernel, rust-for-linux, Matthew Maurer

THIS IS NOT A PROPOSAL TO LAND THIS SERIES

This is a demonstration of implementing a real driver that uses DebugFS,
primarily uploaded to serve as an example for API discussions on the
DebugFS series [1].

There are a number of rough edges (transmute, intra-module bindings) and
un-landed abstractions (SoC registration, randomness), but the main
purpose of this series is to look at sample usage of DebugFS in

* driver/soc/qcom/socinfo_rust/socinfo_rust_scoped.rs
* driver/soc/qcom/socinfo_rust/socinfo_rust_file.rs

to show how this would look in the two different APIs.

The example outputs byte-identical (as checked by recursive diff on
debugfs mounts) DebugFS files to the existing C driver. A real rewrite
would likely be simpler as it would be willing to not precisely mimic
newline quirks, etc.

[1] https://lore.kernel.org/all/20250819-debugfs-rust-v10-0-86e20f3cf3bb@google.com/

Signed-off-by: Matthew Maurer <mmaurer@google.com>
---
Matthew Maurer (5):
      rust: Add soc_device support
      rust: transmute: Cleanup + Fixes
      rust: Add support for feeding entropy to randomness pool
      soc: qcom: socinfo: `File`-based example
      soc: qcom: socinfo: `Scoped`-based example

 MAINTAINERS                                        |   1 +
 drivers/soc/qcom/Kconfig                           |  12 +
 drivers/soc/qcom/Makefile                          |   2 +
 drivers/soc/qcom/smem.c                            |   9 +
 drivers/soc/qcom/socinfo_rust/bindings.rs          | 126 +++++
 drivers/soc/qcom/socinfo_rust/data.rs              | 387 +++++++++++++++
 drivers/soc/qcom/socinfo_rust/socinfo_rust_file.rs | 538 +++++++++++++++++++++
 .../soc/qcom/socinfo_rust/socinfo_rust_scoped.rs   | 367 ++++++++++++++
 rust/bindings/bindings_helper.h                    |   8 +
 rust/kernel/lib.rs                                 |   4 +
 rust/kernel/rand.rs                                |  14 +
 rust/kernel/soc.rs                                 | 137 ++++++
 rust/kernel/transmute.rs                           | 126 +++--
 13 files changed, 1681 insertions(+), 50 deletions(-)
---
base-commit: f3c5631f70e434e318c44001e2417d4770f06cd0
change-id: 20250818-qcom-socinfo-c58407a81ac6
prerequisite-message-id: <20250811213851.65644-1-christiansantoslima21@gmail.com>
prerequisite-patch-id: 9448855f52cb137ad246ae5fde9eab12eac5da94
prerequisite-change-id: 20250428-debugfs-rust-3cd5c97eb7d1:v10
prerequisite-patch-id: 78c729bc8c164ce7a7c3ff841e720518a7f28c54
prerequisite-patch-id: ca7c06b342af0d1f5b03f8907206f8b69a189c9b
prerequisite-patch-id: f3e7541c4e907d874c8f303c2da14dd3aafe05f6
prerequisite-patch-id: e892f83de14710bea11f042ed874c05ccb3bc972
prerequisite-patch-id: ce8a5988c32a6491bc36e87fb33b2e9adf254db8
prerequisite-patch-id: 0762df7763820dd608da15dfbd8b5309d7692aa9
prerequisite-patch-id: 23da818045894ee8c6f2a7d8ade002ccfc96cc3a
prerequisite-patch-id: 2bfdaa7c29838c44d80003c623067051617f2b5e

Best regards,
-- 
Matthew Maurer <mmaurer@google.com>


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH WIP 1/5] rust: Add soc_device support
  2025-08-19 23:12 [PATCH WIP 0/5] qcom-socinfo Rust Implementation Matthew Maurer
@ 2025-08-19 23:12 ` Matthew Maurer
  2025-08-19 23:12 ` [PATCH WIP 2/5] rust: transmute: Cleanup + Fixes Matthew Maurer
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Matthew Maurer @ 2025-08-19 23:12 UTC (permalink / raw)
  To: Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
	Björn Roy Baron, Andreas Hindborg, Alice Ryhl, Trevor Gross,
	Danilo Krummrich, Greg Kroah-Hartman, Rafael J. Wysocki,
	Sami Tolvanen, Timur Tabi, Benno Lossin, Dirk Beheme
  Cc: linux-kernel, rust-for-linux, Matthew Maurer

Adds the ability to register SoC devices.

(This will be sent upstream in a separate request, it's uploaded now as
a dependency of the example driver.)

Signed-off-by: Matthew Maurer <mmaurer@google.com>
---
 MAINTAINERS                     |   1 +
 rust/bindings/bindings_helper.h |   1 +
 rust/kernel/lib.rs              |   2 +
 rust/kernel/soc.rs              | 137 ++++++++++++++++++++++++++++++++++++++++
 4 files changed, 141 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 2cbe890085dbb6a652623b38dd0eadeeaa127a94..e0ff2731f1c2ae4bb01d361e99c1f4517fbd45d5 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7481,6 +7481,7 @@ F:	rust/kernel/devres.rs
 F:	rust/kernel/driver.rs
 F:	rust/kernel/faux.rs
 F:	rust/kernel/platform.rs
+F:	rust/kernel/soc.rs
 F:	samples/rust/rust_debugfs.rs
 F:	samples/rust/rust_scoped_debugfs.rs
 F:	samples/rust/rust_driver_platform.rs
diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
index e847820dc807fdda2d682d496a3c6361bb944c10..140e2f4e60c0b745ac5d5c7456d60af28e21f55a 100644
--- a/rust/bindings/bindings_helper.h
+++ b/rust/bindings/bindings_helper.h
@@ -72,6 +72,7 @@
 #include <linux/sched.h>
 #include <linux/security.h>
 #include <linux/slab.h>
+#include <linux/sys_soc.h>
 #include <linux/tracepoint.h>
 #include <linux/wait.h>
 #include <linux/workqueue.h>
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index 828620c8441566a638f31d03633fc1bf4c1bda85..045f1088938cf646519edea2102439402fb27660 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -117,6 +117,8 @@
 pub mod security;
 pub mod seq_file;
 pub mod sizes;
+#[cfg(CONFIG_SOC_BUS)]
+pub mod soc;
 mod static_assert;
 #[doc(hidden)]
 pub mod std_vendor;
diff --git a/rust/kernel/soc.rs b/rust/kernel/soc.rs
new file mode 100644
index 0000000000000000000000000000000000000000..b8412751a5ca8839e588cf5bd52f2e6a7f33d457
--- /dev/null
+++ b/rust/kernel/soc.rs
@@ -0,0 +1,137 @@
+// SPDX-License-Identifier: GPL-2.0
+
+// Copyright (C) 2025 Google LLC.
+
+//! SoC Driver Abstraction
+//!
+//! C header: [`include/linux/sys_soc.h`](srctree/include/linux/sys_soc.h)
+
+use crate::bindings;
+use crate::error;
+use crate::prelude::*;
+use crate::str::CString;
+use core::marker::PhantomPinned;
+use core::ptr::addr_of;
+
+/// Attributes for a SoC device
+pub struct DeviceAttribute {
+    /// Machine
+    pub machine: Option<CString>,
+    /// Family
+    pub family: Option<CString>,
+    /// Revision
+    pub revision: Option<CString>,
+    /// Serial Number
+    pub serial_number: Option<CString>,
+    /// SoC ID
+    pub soc_id: Option<CString>,
+}
+
+// SAFETY: We provide no operations through `&BuiltDeviceAttribute`
+unsafe impl Sync for BuiltDeviceAttribute {}
+
+// SAFETY: All pointers are normal allocations, not thread-specific
+unsafe impl Send for BuiltDeviceAttribute {}
+
+#[pin_data]
+struct BuiltDeviceAttribute {
+    #[pin]
+    backing: DeviceAttribute,
+    inner: bindings::soc_device_attribute,
+    // Since `inner` has pointers to `backing`, we are !Unpin
+    #[pin]
+    _pin: PhantomPinned,
+}
+
+fn cstring_to_c(mcs: &Option<CString>) -> *const kernel::ffi::c_char {
+    mcs.as_ref()
+        .map(|cs| cs.as_char_ptr())
+        .unwrap_or(core::ptr::null())
+}
+
+impl BuiltDeviceAttribute {
+    fn as_mut_ptr(&self) -> *mut bindings::soc_device_attribute {
+        core::ptr::from_ref(&self.inner).cast_mut()
+    }
+}
+
+impl DeviceAttribute {
+    fn build(self) -> impl PinInit<BuiltDeviceAttribute> {
+        pin_init!(BuiltDeviceAttribute {
+            inner: bindings::soc_device_attribute {
+                machine: cstring_to_c(&self.machine),
+                family: cstring_to_c(&self.family),
+                revision: cstring_to_c(&self.revision),
+                serial_number: cstring_to_c(&self.serial_number),
+                soc_id: cstring_to_c(&self.soc_id),
+                data: core::ptr::null(),
+                custom_attr_group: core::ptr::null(),
+            },
+            backing: self,
+            _pin: PhantomPinned,
+        })
+    }
+}
+
+// SAFETY: We provide no operations through &Device
+unsafe impl Sync for Device {}
+
+// SAFETY: Device holds a pointer to a `soc_device`, which may be sent to any thread.
+unsafe impl Send for Device {}
+
+/// A registered soc device
+#[repr(transparent)]
+pub struct Device(*mut bindings::soc_device);
+
+impl Device {
+    /// # Safety
+    /// * `attr` must be pinned
+    /// * `attr` must be valid for reads during the function call
+    /// * If a device is returned (e.g. no error), `attr` must remain valid for reads until the
+    ///   returned `Device` is dropped.
+    unsafe fn register(attr: *const BuiltDeviceAttribute) -> Result<Device> {
+        let raw_soc =
+            // SAFETY: The struct provided through attr is backed by pinned data next to it, so as
+            // long as attr lives, the strings pointed to by the struct will too. By caller
+            // invariant, `attr` is pinned, so the pinned data won't move. By caller invariant,
+            // `attr` is valid during this call. If it returns a device, and so others may try to
+            // read this data, by caller invariant, `attr` won't be released until the device is.
+            error::from_err_ptr(unsafe { bindings::soc_device_register((*attr).as_mut_ptr()) })?;
+        Ok(Device(raw_soc))
+    }
+}
+
+#[pin_data(PinnedDrop)]
+/// Registration handle for your soc_dev. If you let it go out of scope, your soc_dev will be
+/// unregistered.
+pub struct DeviceRegistration {
+    #[pin]
+    attr: BuiltDeviceAttribute,
+    soc_dev: Device,
+    // Since Device transitively points to the contents of attr, we are !Unpin
+    #[pin]
+    _pin: PhantomPinned,
+}
+
+#[pinned_drop]
+impl PinnedDrop for DeviceRegistration {
+    fn drop(self: Pin<&mut Self>) {
+        // SAFETY: Device always contains a live pointer to a soc_device that can be unregistered
+        unsafe { bindings::soc_device_unregister(self.soc_dev.0) }
+    }
+}
+
+impl DeviceRegistration {
+    /// Register a new SoC device
+    pub fn register(attr: DeviceAttribute) -> impl PinInit<Self, Error> {
+        try_pin_init!(&this in Self {
+                    attr <- attr.build(),
+                    // SAFETY: We have already initialized attr, and we are inside PinInit and Self
+                    // is !Unpin, so attr won't be moved and is valid. If it returns success, attr
+                    // will not be dropped until after our `PinnedDrop` implementation runs, so the
+                    // device will be unregistered first.
+                    soc_dev: unsafe { Device::register(addr_of!((*this.as_ptr()).attr))? },
+                    _pin: PhantomPinned,
+        }? Error)
+    }
+}

-- 
2.51.0.rc1.167.g924127e9c0-goog


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH WIP 2/5] rust: transmute: Cleanup + Fixes
  2025-08-19 23:12 [PATCH WIP 0/5] qcom-socinfo Rust Implementation Matthew Maurer
  2025-08-19 23:12 ` [PATCH WIP 1/5] rust: Add soc_device support Matthew Maurer
@ 2025-08-19 23:12 ` Matthew Maurer
  2025-08-19 23:12 ` [PATCH WIP 3/5] rust: Add support for feeding entropy to randomness pool Matthew Maurer
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Matthew Maurer @ 2025-08-19 23:12 UTC (permalink / raw)
  To: Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
	Björn Roy Baron, Andreas Hindborg, Alice Ryhl, Trevor Gross,
	Danilo Krummrich, Greg Kroah-Hartman, Rafael J. Wysocki,
	Sami Tolvanen, Timur Tabi, Benno Lossin, Dirk Beheme
  Cc: linux-kernel, rust-for-linux, Matthew Maurer

This change is not intended to go upstream as-is, the original
`FromBytes`/`AsBytes` is being litigated on the list. This just fixes it
up so that I can use it for this example.

Signed-off-by: Matthew Maurer <mmaurer@google.com>
---
 rust/kernel/lib.rs       |   1 +
 rust/kernel/transmute.rs | 126 ++++++++++++++++++++++++++++-------------------
 2 files changed, 77 insertions(+), 50 deletions(-)

diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index 045f1088938cf646519edea2102439402fb27660..0461f25cb5aee797d25153a2004d63b6b41f4ae3 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -18,6 +18,7 @@
 //
 // Stable since Rust 1.79.0.
 #![feature(inline_const)]
+#![feature(pointer_is_aligned)]
 //
 // Stable since Rust 1.81.0.
 #![feature(lint_reasons)]
diff --git a/rust/kernel/transmute.rs b/rust/kernel/transmute.rs
index ba21fe49e4f07808c0a43f16461b535fadc033f1..452b1cfb1dbecfdddec7bb59204f7290ae5040af 100644
--- a/rust/kernel/transmute.rs
+++ b/rust/kernel/transmute.rs
@@ -46,63 +46,71 @@ fn from_bytes_mut(bytes: &mut [u8]) -> Option<&mut Self>
         Self: AsBytes;
 }
 
-/// Provide an auto-implementation of FromBytes's methods for all
-/// sized types, if you need an implementation for your type use this instead.
-///
-/// # Safety
-///
-/// All bit-patterns must be valid for this type. This type must not have interior mutability.
-pub unsafe trait FromBytesSized: Sized {}
+/// Helper for implementing `from_bytes` for sized types.
+pub fn sized_from_bytes<T: FromBytes>(bytes: &[u8]) -> Option<&T> {
+    if bytes.len() == core::mem::size_of::<T>() {
+        let slice_ptr = bytes.as_ptr().cast::<T>();
+        if !slice_ptr.is_aligned() {
+            None
+        } else {
+            // SAFETY:
+            // * T is FromBytes, so anything in the bytes array is a valid bit pattern
+            // * The pointer is aligned
+            // * The pointer points to a region of the appropriate size
+            unsafe { Some(&*slice_ptr) }
+        }
+    } else {
+        None
+    }
+}
 
-macro_rules! impl_frombytessized {
+/// Helper for implementing `from_bytes_mut` for sized types.
+pub fn sized_from_bytes_mut<T: FromBytes + AsBytes>(bytes: &mut [u8]) -> Option<&mut T> {
+    if bytes.len() == core::mem::size_of::<T>() {
+        let slice_ptr = bytes.as_mut_ptr().cast::<T>();
+        if !slice_ptr.is_aligned() {
+            None
+        } else {
+            // SAFETY:
+            // * T is FromBytes, so anything in the bytes array is a valid bit pattern
+            // * T is AsBytes, so mutating T will not expose padding to the byte array
+            // * The pointer is aligned
+            // * The pointer points to a region of the appropriate size
+            unsafe { Some(&mut *slice_ptr) }
+        }
+    } else {
+        None
+    }
+}
+
+macro_rules! impl_from_bytes{
     ($($({$($generics:tt)*})? $t:ty, )*) => {
         // SAFETY: Safety comments written in the macro invocation.
-        $(unsafe impl$($($generics)*)? FromBytesSized for $t {})*
+        $(unsafe impl$($($generics)*)? FromBytes for $t {
+            fn from_bytes(bytes: &[u8]) -> Option<&$t> {
+                sized_from_bytes(bytes)
+            }
+
+            fn from_bytes_mut(bytes: &mut [u8]) -> Option<&mut $t>
+            where
+            Self: AsBytes,
+            {
+                sized_from_bytes_mut(bytes)
+            }
+        })*
     };
 }
 
-impl_frombytessized! {
+impl_from_bytes! {
     // SAFETY: All bit patterns are acceptable values of the types below.
+    // Checking the pointer size makes this operation safe and it's necessary
+    // to dereference to get the value and return it as a reference to `Self`.
     u8, u16, u32, u64, usize,
     i8, i16, i32, i64, isize,
 
     // SAFETY: If all bit patterns are acceptable for individual values in an array, then all bit
     // patterns are also acceptable for arrays of that type.
-    {<T: FromBytesSized, const N: usize>} [T; N],
-}
-
-// SAFETY: The `FromBytesSized` implementation guarantees that all bit
-// patterns are acceptable values of the types and in array case if
-// all bit patterns are acceptable for individual values in an array,
-// then all bit patterns are also acceptable for arrays of that type.
-unsafe impl<T> FromBytes for T
-where
-    T: FromBytesSized,
-{
-    fn from_bytes(bytes: &[u8]) -> Option<&Self> {
-        let slice_ptr = bytes.as_ptr().cast::<T>();
-        let size = ::core::mem::size_of::<T>();
-        if bytes.len() == size && slice_ptr.is_aligned() {
-            // SAFETY: Since the code checks the size and alignment, the slice is valid.
-            unsafe { Some(&*slice_ptr) }
-        } else {
-            None
-        }
-    }
-
-    fn from_bytes_mut(bytes: &mut [u8]) -> Option<&mut Self>
-    where
-        Self: AsBytes,
-    {
-        let slice_ptr = bytes.as_mut_ptr().cast::<T>();
-        let size = ::core::mem::size_of::<T>();
-        if bytes.len() == size && slice_ptr.is_aligned() {
-            // SAFETY: Since the code checks the size and alignment, the slice is valid.
-            unsafe { Some(&mut *slice_ptr) }
-        } else {
-            None
-        }
-    }
+    {<T: FromBytes, const N: usize>} [T; N],
 }
 
 // SAFETY: If all bit patterns are acceptable for individual values in an array, then all bit
@@ -110,7 +118,7 @@ fn from_bytes_mut(bytes: &mut [u8]) -> Option<&mut Self>
 unsafe impl<T: FromBytes> FromBytes for [T] {
     fn from_bytes(bytes: &[u8]) -> Option<&Self> {
         let size = ::core::mem::size_of::<T>();
-        build_assert!(size == 0, "Can't create a slice with zero elements");
+        build_assert!(size != 0, "Can't create a slice with zero-sized elements");
         let slice_ptr = bytes.as_ptr().cast::<T>();
         if bytes.len() % size == 0 && slice_ptr.is_aligned() {
             // SAFETY: Since the number of elements is different from
@@ -126,7 +134,7 @@ fn from_bytes_mut(bytes: &mut [u8]) -> Option<&mut Self>
         Self: AsBytes,
     {
         let size = ::core::mem::size_of::<T>();
-        build_assert!(size == 0, "Can't create a slice with zero elements");
+        build_assert!(size != 0, "Can't create a slice with zero-sized elements");
         let slice_ptr = bytes.as_mut_ptr().cast::<T>();
         if bytes.len() % size == 0 && slice_ptr.is_aligned() {
             // SAFETY: Since the number of elements is different from
@@ -158,16 +166,34 @@ fn from_bytes_mut(bytes: &mut [u8]) -> Option<&mut Self>
 ///
 /// Values of this type may not contain any uninitialized bytes. This type must not have interior
 /// mutability.
-pub unsafe trait AsBytes {}
+pub unsafe trait AsBytes {
+    /// View data structure as a buffer
+    fn as_bytes(&self) -> &[u8] {
+        let len = core::mem::size_of_val(self);
+        // SAFETY: By unsafe trait impl precondition, there's no interior mutability and no
+        // uninitialized bytes.
+        unsafe { core::slice::from_raw_parts(core::ptr::from_ref(self).cast::<u8>(), len) }
+    }
+    /// View data structure as a mutable buffer
+    fn as_mut_bytes(&mut self) -> &mut [u8]
+    where
+        Self: FromBytes,
+    {
+        let len = core::mem::size_of_val(self);
+        // SAFETY: By unsafe trait impl precondition, there's no interior mutability, and no
+        // unititialized bytes. By FromBytes trait impl precondition, all bit patterns are valid.
+        unsafe { core::slice::from_raw_parts_mut(core::ptr::from_mut(self).cast::<u8>(), len) }
+    }
+}
 
-macro_rules! impl_asbytes {
+macro_rules! impl_as_bytes {
     ($($({$($generics:tt)*})? $t:ty, )*) => {
         // SAFETY: Safety comments written in the macro invocation.
         $(unsafe impl$($($generics)*)? AsBytes for $t {})*
     };
 }
 
-impl_asbytes! {
+impl_as_bytes! {
     // SAFETY: Instances of the following types have no uninitialized portions.
     u8, u16, u32, u64, usize,
     i8, i16, i32, i64, isize,

-- 
2.51.0.rc1.167.g924127e9c0-goog


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH WIP 3/5] rust: Add support for feeding entropy to randomness pool
  2025-08-19 23:12 [PATCH WIP 0/5] qcom-socinfo Rust Implementation Matthew Maurer
  2025-08-19 23:12 ` [PATCH WIP 1/5] rust: Add soc_device support Matthew Maurer
  2025-08-19 23:12 ` [PATCH WIP 2/5] rust: transmute: Cleanup + Fixes Matthew Maurer
@ 2025-08-19 23:12 ` Matthew Maurer
  2025-08-19 23:12 ` [PATCH WIP 4/5] soc: qcom: socinfo: `File`-based example Matthew Maurer
  2025-08-19 23:12 ` [PATCH WIP 5/5] soc: qcom: socinfo: `Scoped`-based example Matthew Maurer
  4 siblings, 0 replies; 6+ messages in thread
From: Matthew Maurer @ 2025-08-19 23:12 UTC (permalink / raw)
  To: Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
	Björn Roy Baron, Andreas Hindborg, Alice Ryhl, Trevor Gross,
	Danilo Krummrich, Greg Kroah-Hartman, Rafael J. Wysocki,
	Sami Tolvanen, Timur Tabi, Benno Lossin, Dirk Beheme
  Cc: linux-kernel, rust-for-linux, Matthew Maurer

(This will be separately sent to the list later.)

Signed-off-by: Matthew Maurer <mmaurer@google.com>
---
 rust/bindings/bindings_helper.h |  1 +
 rust/kernel/lib.rs              |  1 +
 rust/kernel/rand.rs             | 14 ++++++++++++++
 3 files changed, 16 insertions(+)

diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
index 140e2f4e60c0b745ac5d5c7456d60af28e21f55a..afe8be55a6db1d29706c19a91dc51c1ae3494ed4 100644
--- a/rust/bindings/bindings_helper.h
+++ b/rust/bindings/bindings_helper.h
@@ -67,6 +67,7 @@
 #include <linux/pm_opp.h>
 #include <linux/poll.h>
 #include <linux/property.h>
+#include <linux/random.h>
 #include <linux/refcount.h>
 #include <linux/regulator/consumer.h>
 #include <linux/sched.h>
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index 0461f25cb5aee797d25153a2004d63b6b41f4ae3..39a9088a8d63f0ae54a0101eaeb03b17f82e12e8 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -112,6 +112,7 @@
 pub mod platform;
 pub mod prelude;
 pub mod print;
+pub mod rand;
 pub mod rbtree;
 pub mod regulator;
 pub mod revocable;
diff --git a/rust/kernel/rand.rs b/rust/kernel/rand.rs
new file mode 100644
index 0000000000000000000000000000000000000000..b3fb30f40a8950ac7b47d48129eb89024a1cbd26
--- /dev/null
+++ b/rust/kernel/rand.rs
@@ -0,0 +1,14 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Randomness.
+//!
+//! C header: [`include/linux/random.h`](../../../../include/linux/random.h)
+
+use crate::bindings;
+use crate::ffi::c_void;
+
+/// Adds the given buffer to the entropy pool.
+pub fn add_device_randomness(buf: &[u8]) {
+    // SAFETY: We just need the pointer to be valid for the length, which a slice provides.
+    unsafe { bindings::add_device_randomness(buf.as_ptr().cast::<c_void>(), buf.len()) };
+}

-- 
2.51.0.rc1.167.g924127e9c0-goog


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH WIP 4/5] soc: qcom: socinfo: `File`-based example
  2025-08-19 23:12 [PATCH WIP 0/5] qcom-socinfo Rust Implementation Matthew Maurer
                   ` (2 preceding siblings ...)
  2025-08-19 23:12 ` [PATCH WIP 3/5] rust: Add support for feeding entropy to randomness pool Matthew Maurer
@ 2025-08-19 23:12 ` Matthew Maurer
  2025-08-19 23:12 ` [PATCH WIP 5/5] soc: qcom: socinfo: `Scoped`-based example Matthew Maurer
  4 siblings, 0 replies; 6+ messages in thread
From: Matthew Maurer @ 2025-08-19 23:12 UTC (permalink / raw)
  To: Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
	Björn Roy Baron, Andreas Hindborg, Alice Ryhl, Trevor Gross,
	Danilo Krummrich, Greg Kroah-Hartman, Rafael J. Wysocki,
	Sami Tolvanen, Timur Tabi, Benno Lossin, Dirk Beheme
  Cc: linux-kernel, rust-for-linux, Matthew Maurer

Re-implements qcom-socinfo driver in Rust, using `File`-based DebugFS
bindings.

Signed-off-by: Matthew Maurer <mmaurer@google.com>
---
 drivers/soc/qcom/Kconfig                           |  12 +
 drivers/soc/qcom/Makefile                          |   1 +
 drivers/soc/qcom/smem.c                            |   4 +
 drivers/soc/qcom/socinfo_rust/bindings.rs          | 126 +++++
 drivers/soc/qcom/socinfo_rust/data.rs              | 387 +++++++++++++++
 drivers/soc/qcom/socinfo_rust/socinfo_rust_file.rs | 538 +++++++++++++++++++++
 rust/bindings/bindings_helper.h                    |   6 +
 7 files changed, 1074 insertions(+)

diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
index 2caadbbcf8307ff94f5afbdd1481e5e5e291749f..362ea8c1a18a24a06bb5456e7d1ac8a0cb1003b5 100644
--- a/drivers/soc/qcom/Kconfig
+++ b/drivers/soc/qcom/Kconfig
@@ -227,6 +227,18 @@ config QCOM_SOCINFO
 	 Say yes here to support the Qualcomm socinfo driver, providing
 	 information about the SoC to user space.
 
+config QCOM_SOCINFO_RUST
+	tristate "Qualcomm socinfo driver"
+	depends on QCOM_SMEM && RUST
+	select SOC_BUS
+	help
+	 Say yes here to support the Qualcomm socinfo driver, providing
+	 information about the SoC to user space.
+
+	 This is a draft Rust re-implementation which will enable two
+	 additional copies of the driver to registered alongside the
+	 original.
+
 config QCOM_SPM
 	tristate "Qualcomm Subsystem Power Manager (SPM)"
 	depends on ARCH_QCOM || COMPILE_TEST
diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
index b7f1d2a5736748b8772c090fd24462fa91f321c6..6453a4f4f7da89d4e1677ad8f75257e94cec90f4 100644
--- a/drivers/soc/qcom/Makefile
+++ b/drivers/soc/qcom/Makefile
@@ -40,3 +40,4 @@ qcom_ice-objs			+= ice.o
 obj-$(CONFIG_QCOM_INLINE_CRYPTO_ENGINE)	+= qcom_ice.o
 obj-$(CONFIG_QCOM_PBS) +=	qcom-pbs.o
 obj-$(CONFIG_QCOM_UBWC_CONFIG) += ubwc_config.o
+obj-$(CONFIG_QCOM_SOCINFO_RUST) += socinfo_rust/socinfo_rust_file.o
diff --git a/drivers/soc/qcom/smem.c b/drivers/soc/qcom/smem.c
index cf425930539e406fb81307a17803955371545f32..5279db46a1e0dd7b2994b79d131367175e35290f 100644
--- a/drivers/soc/qcom/smem.c
+++ b/drivers/soc/qcom/smem.c
@@ -1230,7 +1230,11 @@ static int qcom_smem_probe(struct platform_device *pdev)
 
 	__smem = smem;
 
+	// TODO don't double load, this is just to diff the debugfs
 	smem->socinfo = platform_device_register_data(&pdev->dev, "qcom-socinfo",
+						      PLATFORM_DEVID_NONE, NULL, 0);
+	// TODO MODULE_ALIAS
+	smem->socinfo = platform_device_register_data(&pdev->dev, "qcom_socinfo_driver_file_rust",
 						      PLATFORM_DEVID_NONE, NULL,
 						      0);
 	if (IS_ERR(smem->socinfo))
diff --git a/drivers/soc/qcom/socinfo_rust/bindings.rs b/drivers/soc/qcom/socinfo_rust/bindings.rs
new file mode 100644
index 0000000000000000000000000000000000000000..8c2b5cecbfdde05e1a81ad0c78be0e7b6432afc5
--- /dev/null
+++ b/drivers/soc/qcom/socinfo_rust/bindings.rs
@@ -0,0 +1,126 @@
+// SPDX-License-Identifier: GPL-2.0
+
+// Copyright (C) 2025 Google LLC.
+
+use core::ops::Deref;
+use kernel::error::from_err_ptr;
+use kernel::ffi::c_char;
+use kernel::prelude::*;
+use kernel::transmute::{sized_from_bytes, sized_from_bytes_mut, AsBytes, FromBytes};
+
+pub(crate) fn qcom_smem_get(host: i32, item: u32) -> Result<&'static [u8]> {
+    let mut size = 0;
+    // SAFETY: qcom_smem_get only requires that the size pointer be a writable size_t,
+    // host and item are error checked in the qcom_smem module.
+    let err_ptr = unsafe { kernel::bindings::qcom_smem_get(host as u32, item, &mut size) };
+    let ptr = from_err_ptr(err_ptr)?;
+    // SAFETY: If qcom_smem_get does not return an error, the returned pointer points to a readable
+    // byte buffer with its size written into size. Because these buffers are derived from the
+    // static ranges in the DT, this buffer remains accessible even if the qcom_smem module is
+    // unloaded, so 'static is appropriate. The underlying buffer cannot mutate, so upgrading it
+    // to a reference is allowed.
+    Ok(unsafe { core::slice::from_raw_parts(ptr as *const u8, size) })
+}
+
+pub(crate) const SMEM_IMAGE_VERSION_TABLE: u32 = 469;
+
+// bindgen does not know that struct socinfo is AsBytes, so we implement it here
+#[derive(Default, Copy, Clone)]
+#[repr(transparent)]
+pub(crate) struct RawSocInfo(kernel::bindings::socinfo);
+// SAFETY: Transparent wrapper for the socinfo struct, which is a padding-free
+// C-origin type
+unsafe impl AsBytes for RawSocInfo {}
+// SAFETY: Transparent wrapper for the socinfo struct, a C-origin type
+unsafe impl FromBytes for RawSocInfo {
+    fn from_bytes(bytes: &[u8]) -> Option<&Self> {
+        sized_from_bytes(bytes)
+    }
+    fn from_bytes_mut(bytes: &mut [u8]) -> Option<&mut Self> {
+        sized_from_bytes_mut(bytes)
+    }
+}
+
+impl Deref for RawSocInfo {
+    type Target = kernel::bindings::socinfo;
+    fn deref(&self) -> &Self::Target {
+        &self.0
+    }
+}
+
+impl RawSocInfo {
+    /// Produce RawSocInfo by initializing from a byte buffer that may be too small.
+    /// Code using the resulting value is expected to use the info_fmt field to determine
+    /// which fields have meaningful values.
+    pub(crate) fn from_partial_bytes(soc_info_mem: &[u8]) -> Self {
+        let mut soc_info = RawSocInfo::default();
+        let byte_view = soc_info.as_mut_bytes();
+        let len = core::cmp::min(soc_info_mem.len(), byte_view.len());
+        byte_view[..len].copy_from_slice(&soc_info_mem[..len]);
+        soc_info
+    }
+}
+
+#[repr(C)]
+pub(crate) struct PmicEntry {
+    pub(crate) model: u32,
+    pub(crate) die_rev: u32,
+}
+
+// SAFETY: All bit patterns are valid for all fields, and C layout for two u32s
+// does not require padding
+unsafe impl AsBytes for PmicEntry {}
+// SAFETY: All bit patterns are valid for all fields
+unsafe impl FromBytes for PmicEntry {
+    fn from_bytes(bytes: &[u8]) -> Option<&Self> {
+        sized_from_bytes(bytes)
+    }
+    fn from_bytes_mut(bytes: &mut [u8]) -> Option<&mut Self> {
+        sized_from_bytes_mut(bytes)
+    }
+}
+
+#[derive(Default)]
+#[repr(transparent)]
+pub(crate) struct PmicArray(pub(crate) &'static [PmicEntry]);
+
+impl PmicArray {
+    pub(crate) fn from_bytes(bytes: &'static [u8]) -> Option<Self> {
+        Some(Self(FromBytes::from_bytes(bytes)?))
+    }
+}
+
+const SMEM_IMAGE_VERSION_NAME_SIZE: usize = 75;
+const SMEM_IMAGE_VERSION_VARIANT_SIZE: usize = 20;
+const SMEM_IMAGE_VERSION_OEM_SIZE: usize = 32;
+
+#[repr(C)]
+pub(crate) struct ImageVersion {
+    pub(crate) name: [c_char; SMEM_IMAGE_VERSION_NAME_SIZE],
+    pub(crate) variant: [c_char; SMEM_IMAGE_VERSION_VARIANT_SIZE],
+    pub(crate) pad: c_char,
+    pub(crate) oem: [c_char; SMEM_IMAGE_VERSION_OEM_SIZE],
+}
+
+// SAFETY: All bit patterns are valid for all fields, and C layout for two u32s
+// does not require padding
+unsafe impl AsBytes for ImageVersion {}
+// SAFETY: All bit patterns are valid for all fields
+unsafe impl FromBytes for ImageVersion {
+    fn from_bytes(bytes: &[u8]) -> Option<&Self> {
+        sized_from_bytes(bytes)
+    }
+    fn from_bytes_mut(bytes: &mut [u8]) -> Option<&mut Self> {
+        sized_from_bytes_mut(bytes)
+    }
+}
+
+#[derive(Default)]
+#[repr(transparent)]
+pub(crate) struct ImageVersions(pub(crate) &'static [ImageVersion]);
+
+impl ImageVersions {
+    pub(crate) fn from_bytes(bytes: &'static [u8]) -> Option<Self> {
+        Some(Self(FromBytes::from_bytes(bytes)?))
+    }
+}
diff --git a/drivers/soc/qcom/socinfo_rust/data.rs b/drivers/soc/qcom/socinfo_rust/data.rs
new file mode 100644
index 0000000000000000000000000000000000000000..1575d3c5452f801ae27c2c67ea71410e36521846
--- /dev/null
+++ b/drivers/soc/qcom/socinfo_rust/data.rs
@@ -0,0 +1,387 @@
+// SPDX-License-Identifier: GPL-2.0
+
+// Copyright (C) 2025 Google LLC.
+
+//! Data tables for QCom SocInfo driver
+use kernel::c_str;
+use kernel::prelude::*;
+
+macro_rules! id_entry {
+    ($id:ident) => {
+        kernel::macros::paste! {
+            SocId {
+                id: kernel::bindings::[<QCOM_ID_ $id>],
+                name: c_str!(stringify!($id)),
+            }
+        }
+    };
+    ($id:ident, $name:tt) => {
+        SocId {
+            id: kernel::macros::paste!(kernel::bindings::[<QCOM_ID_ $id>]), name: c_str!($name),
+        }
+    }
+}
+
+pub(crate) struct SocId {
+    pub(crate) id: u32,
+    pub(crate) name: &'static CStr,
+}
+
+pub(crate) static SOC_IDS: &[SocId] = &[
+    { id_entry!(MSM8260) },
+    { id_entry!(MSM8660) },
+    { id_entry!(APQ8060) },
+    { id_entry!(MSM8960) },
+    { id_entry!(APQ8064) },
+    { id_entry!(MSM8930) },
+    { id_entry!(MSM8630) },
+    { id_entry!(MSM8230) },
+    { id_entry!(APQ8030) },
+    { id_entry!(MSM8627) },
+    { id_entry!(MSM8227) },
+    { id_entry!(MSM8660A) },
+    { id_entry!(MSM8260A) },
+    { id_entry!(APQ8060A) },
+    { id_entry!(MSM8974) },
+    { id_entry!(MSM8225) },
+    { id_entry!(MSM8625) },
+    { id_entry!(MPQ8064) },
+    { id_entry!(MSM8960AB) },
+    { id_entry!(APQ8060AB) },
+    { id_entry!(MSM8260AB) },
+    { id_entry!(MSM8660AB) },
+    { id_entry!(MSM8930AA) },
+    { id_entry!(MSM8630AA) },
+    { id_entry!(MSM8230AA) },
+    { id_entry!(MSM8626) },
+    { id_entry!(MSM8610) },
+    { id_entry!(APQ8064AB) },
+    { id_entry!(MSM8930AB) },
+    { id_entry!(MSM8630AB) },
+    { id_entry!(MSM8230AB) },
+    { id_entry!(APQ8030AB) },
+    { id_entry!(MSM8226) },
+    { id_entry!(MSM8526) },
+    { id_entry!(APQ8030AA) },
+    { id_entry!(MSM8110) },
+    { id_entry!(MSM8210) },
+    { id_entry!(MSM8810) },
+    { id_entry!(MSM8212) },
+    { id_entry!(MSM8612) },
+    { id_entry!(MSM8112) },
+    { id_entry!(MSM8125) },
+    { id_entry!(MSM8225Q) },
+    { id_entry!(MSM8625Q) },
+    { id_entry!(MSM8125Q) },
+    { id_entry!(APQ8064AA) },
+    { id_entry!(APQ8084) },
+    { id_entry!(MSM8130) },
+    { id_entry!(MSM8130AA) },
+    { id_entry!(MSM8130AB) },
+    { id_entry!(MSM8627AA) },
+    { id_entry!(MSM8227AA) },
+    { id_entry!(APQ8074) },
+    { id_entry!(MSM8274) },
+    { id_entry!(MSM8674) },
+    { id_entry!(MDM9635) },
+    { id_entry!(MSM8974PRO_AC, "MSM8974PRO-AC") },
+    { id_entry!(MSM8126) },
+    { id_entry!(APQ8026) },
+    { id_entry!(MSM8926) },
+    { id_entry!(IPQ8062) },
+    { id_entry!(IPQ8064) },
+    { id_entry!(IPQ8066) },
+    { id_entry!(IPQ8068) },
+    { id_entry!(MSM8326) },
+    { id_entry!(MSM8916) },
+    { id_entry!(MSM8994) },
+    { id_entry!(APQ8074PRO_AA, "APQ8074PRO-AA") },
+    { id_entry!(APQ8074PRO_AB, "APQ8074PRO-AB") },
+    { id_entry!(APQ8074PRO_AC, "APQ8074PRO-AC") },
+    { id_entry!(MSM8274PRO_AA, "MSM8274PRO-AA") },
+    { id_entry!(MSM8274PRO_AB, "MSM8274PRO-AB") },
+    { id_entry!(MSM8274PRO_AC, "MSM8274PRO-AC") },
+    { id_entry!(MSM8674PRO_AA, "MSM8674PRO-AA") },
+    { id_entry!(MSM8674PRO_AB, "MSM8674PRO-AB") },
+    { id_entry!(MSM8674PRO_AC, "MSM8674PRO-AC") },
+    { id_entry!(MSM8974PRO_AA, "MSM8974PRO-AA") },
+    { id_entry!(MSM8974PRO_AB, "MSM8974PRO-AB") },
+    { id_entry!(APQ8028) },
+    { id_entry!(MSM8128) },
+    { id_entry!(MSM8228) },
+    { id_entry!(MSM8528) },
+    { id_entry!(MSM8628) },
+    { id_entry!(MSM8928) },
+    { id_entry!(MSM8510) },
+    { id_entry!(MSM8512) },
+    { id_entry!(MSM8936) },
+    { id_entry!(MDM9640) },
+    { id_entry!(MSM8939) },
+    { id_entry!(APQ8036) },
+    { id_entry!(APQ8039) },
+    { id_entry!(MSM8236) },
+    { id_entry!(MSM8636) },
+    { id_entry!(MSM8909) },
+    { id_entry!(MSM8996) },
+    { id_entry!(APQ8016) },
+    { id_entry!(MSM8216) },
+    { id_entry!(MSM8116) },
+    { id_entry!(MSM8616) },
+    { id_entry!(MSM8992) },
+    { id_entry!(APQ8092) },
+    { id_entry!(APQ8094) },
+    { id_entry!(MSM8209) },
+    { id_entry!(MSM8208) },
+    { id_entry!(MDM9209) },
+    { id_entry!(MDM9309) },
+    { id_entry!(MDM9609) },
+    { id_entry!(MSM8239) },
+    { id_entry!(MSM8952) },
+    { id_entry!(APQ8009) },
+    { id_entry!(MSM8956) },
+    { id_entry!(MSM8929) },
+    { id_entry!(MSM8629) },
+    { id_entry!(MSM8229) },
+    { id_entry!(APQ8029) },
+    { id_entry!(APQ8056) },
+    { id_entry!(MSM8609) },
+    { id_entry!(APQ8076) },
+    { id_entry!(MSM8976) },
+    { id_entry!(IPQ8065) },
+    { id_entry!(IPQ8069) },
+    { id_entry!(MDM9650) },
+    { id_entry!(MDM9655) },
+    { id_entry!(MDM9250) },
+    { id_entry!(MDM9255) },
+    { id_entry!(MDM9350) },
+    { id_entry!(APQ8052) },
+    { id_entry!(MDM9607) },
+    { id_entry!(APQ8096) },
+    { id_entry!(MSM8998) },
+    { id_entry!(MSM8953) },
+    { id_entry!(MSM8937) },
+    { id_entry!(APQ8037) },
+    { id_entry!(MDM8207) },
+    { id_entry!(MDM9207) },
+    { id_entry!(MDM9307) },
+    { id_entry!(MDM9628) },
+    { id_entry!(MSM8909W) },
+    { id_entry!(APQ8009W) },
+    { id_entry!(MSM8996L) },
+    { id_entry!(MSM8917) },
+    { id_entry!(APQ8053) },
+    { id_entry!(MSM8996SG) },
+    { id_entry!(APQ8017) },
+    { id_entry!(MSM8217) },
+    { id_entry!(MSM8617) },
+    { id_entry!(MSM8996AU) },
+    { id_entry!(APQ8096AU) },
+    { id_entry!(APQ8096SG) },
+    { id_entry!(MSM8940) },
+    { id_entry!(SDX201) },
+    { id_entry!(SDM660) },
+    { id_entry!(SDM630) },
+    { id_entry!(APQ8098) },
+    { id_entry!(MSM8920) },
+    { id_entry!(SDM845) },
+    { id_entry!(MDM9206) },
+    { id_entry!(IPQ8074) },
+    { id_entry!(SDA660) },
+    { id_entry!(SDM658) },
+    { id_entry!(SDA658) },
+    { id_entry!(SDA630) },
+    { id_entry!(MSM8905) },
+    { id_entry!(SDX202) },
+    { id_entry!(SDM670) },
+    { id_entry!(SDM450) },
+    { id_entry!(SM8150) },
+    { id_entry!(SDA845) },
+    { id_entry!(IPQ8072) },
+    { id_entry!(IPQ8076) },
+    { id_entry!(IPQ8078) },
+    { id_entry!(SDM636) },
+    { id_entry!(SDA636) },
+    { id_entry!(SDM632) },
+    { id_entry!(SDA632) },
+    { id_entry!(SDA450) },
+    { id_entry!(SDM439) },
+    { id_entry!(SDM429) },
+    { id_entry!(SM8250) },
+    { id_entry!(SA8155) },
+    { id_entry!(SDA439) },
+    { id_entry!(SDA429) },
+    { id_entry!(SM7150) },
+    { id_entry!(SM7150P) },
+    { id_entry!(IPQ8070) },
+    { id_entry!(IPQ8071) },
+    { id_entry!(QM215) },
+    { id_entry!(IPQ8072A) },
+    { id_entry!(IPQ8074A) },
+    { id_entry!(IPQ8076A) },
+    { id_entry!(IPQ8078A) },
+    { id_entry!(SM6125) },
+    { id_entry!(IPQ8070A) },
+    { id_entry!(IPQ8071A) },
+    { id_entry!(IPQ8172) },
+    { id_entry!(IPQ8173) },
+    { id_entry!(IPQ8174) },
+    { id_entry!(IPQ6018) },
+    { id_entry!(IPQ6028) },
+    { id_entry!(SDM429W) },
+    { id_entry!(SM4250) },
+    { id_entry!(IPQ6000) },
+    { id_entry!(IPQ6010) },
+    { id_entry!(SC7180) },
+    { id_entry!(SM6350) },
+    { id_entry!(QCM2150) },
+    { id_entry!(SDA429W) },
+    { id_entry!(SM8350) },
+    { id_entry!(QCM2290) },
+    { id_entry!(SM7125) },
+    { id_entry!(SM6115) },
+    { id_entry!(IPQ5010) },
+    { id_entry!(IPQ5018) },
+    { id_entry!(IPQ5028) },
+    { id_entry!(SC8280XP) },
+    { id_entry!(IPQ6005) },
+    { id_entry!(QRB5165) },
+    { id_entry!(SM8450) },
+    { id_entry!(SM7225) },
+    { id_entry!(SA8295P) },
+    { id_entry!(SA8540P) },
+    { id_entry!(QCM4290) },
+    { id_entry!(QCS4290) },
+    { id_entry!(SM7325) },
+    { id_entry!(SM8450_2, "SM8450") },
+    { id_entry!(SM8450_3, "SM8450") },
+    { id_entry!(SC7280) },
+    { id_entry!(SC7180P) },
+    { id_entry!(QCM6490) },
+    { id_entry!(SM7325P) },
+    { id_entry!(IPQ5000) },
+    { id_entry!(IPQ0509) },
+    { id_entry!(IPQ0518) },
+    { id_entry!(SM6375) },
+    { id_entry!(IPQ9514) },
+    { id_entry!(IPQ9550) },
+    { id_entry!(IPQ9554) },
+    { id_entry!(IPQ9570) },
+    { id_entry!(IPQ9574) },
+    { id_entry!(SM8550) },
+    { id_entry!(IPQ5016) },
+    { id_entry!(IPQ9510) },
+    { id_entry!(QRB4210) },
+    { id_entry!(QRB2210) },
+    { id_entry!(SAR2130P) },
+    { id_entry!(SM8475) },
+    { id_entry!(SM8475P) },
+    { id_entry!(SA8255P) },
+    { id_entry!(SA8775P) },
+    { id_entry!(QRU1000) },
+    { id_entry!(SM8475_2) },
+    { id_entry!(QDU1000) },
+    { id_entry!(X1E80100) },
+    { id_entry!(SM8650) },
+    { id_entry!(SM4450) },
+    { id_entry!(SAR1130P) },
+    { id_entry!(QDU1010) },
+    { id_entry!(QRU1032) },
+    { id_entry!(QRU1052) },
+    { id_entry!(QRU1062) },
+    { id_entry!(IPQ5332) },
+    { id_entry!(IPQ5322) },
+    { id_entry!(IPQ5312) },
+    { id_entry!(IPQ5302) },
+    { id_entry!(QCS8550) },
+    { id_entry!(QCM8550) },
+    { id_entry!(IPQ5300) },
+    { id_entry!(IPQ5321) },
+    { id_entry!(IPQ5424) },
+    { id_entry!(IPQ5404) },
+    { id_entry!(QCS9100) },
+    { id_entry!(QCS8300) },
+    { id_entry!(QCS8275) },
+    { id_entry!(QCS9075) },
+    { id_entry!(QCS615) },
+];
+
+pub(crate) const PMIC_MODELS: [Option<&str>; 84] = {
+    let mut models = [None; 84];
+    models[0] = Some("Unknown PMIC model");
+    models[1] = Some("PM8941");
+    models[2] = Some("PM8841");
+    models[3] = Some("PM8019");
+    models[4] = Some("PM8226");
+    models[5] = Some("PM8110");
+    models[6] = Some("PMA8084");
+    models[7] = Some("PMI8962");
+    models[8] = Some("PMD9635");
+    models[9] = Some("PM8994");
+    models[10] = Some("PMI8994");
+    models[11] = Some("PM8916");
+    models[12] = Some("PM8004");
+    models[13] = Some("PM8909/PM8058");
+    models[14] = Some("PM8028");
+    models[15] = Some("PM8901");
+    models[16] = Some("PM8950/PM8027");
+    models[17] = Some("PMI8950/ISL9519");
+    models[18] = Some("PMK8001/PM8921");
+    models[19] = Some("PMI8996/PM8018");
+    models[20] = Some("PM8998/PM8015");
+    models[21] = Some("PMI8998/PM8014");
+    models[22] = Some("PM8821");
+    models[23] = Some("PM8038");
+    models[24] = Some("PM8005/PM8922");
+    models[25] = Some("PM8917/PM8937");
+    models[26] = Some("PM660L");
+    models[27] = Some("PM660");
+    models[30] = Some("PM8150");
+    models[31] = Some("PM8150L");
+    models[32] = Some("PM8150B");
+    models[33] = Some("PMK8002");
+    models[36] = Some("PM8009");
+    models[37] = Some("PMI632");
+    models[38] = Some("PM8150C");
+    models[40] = Some("PM6150");
+    models[41] = Some("SMB2351");
+    models[44] = Some("PM8008");
+    models[45] = Some("PM6125");
+    models[46] = Some("PM7250B");
+    models[47] = Some("PMK8350");
+    models[48] = Some("PM8350");
+    models[49] = Some("PM8350C");
+    models[50] = Some("PM8350B");
+    models[51] = Some("PMR735A");
+    models[52] = Some("PMR735B");
+    models[54] = Some("PM6350");
+    models[55] = Some("PM4125");
+    models[58] = Some("PM8450");
+    models[65] = Some("PM8010");
+    models[69] = Some("PM8550VS");
+    models[70] = Some("PM8550VE");
+    models[71] = Some("PM8550B");
+    models[72] = Some("PMR735D");
+    models[73] = Some("PM8550");
+    models[74] = Some("PMK8550");
+    models[82] = Some("PMC8380");
+    models[83] = Some("SMB2360");
+    models
+};
+
+pub(crate) const IMAGE_NAMES: &[(&CStr, usize)] = &[
+    (c_str!("adsp"), 12),
+    (c_str!("apps"), 10),
+    (c_str!("appsbl"), 9),
+    (c_str!("boot"), 0),
+    (c_str!("cnss"), 13),
+    (c_str!("mpss"), 11),
+    (c_str!("rpm"), 3),
+    (c_str!("tz"), 1),
+    (c_str!("video"), 14),
+    (c_str!("dsps"), 15),
+    (c_str!("cdsp"), 16),
+    (c_str!("cdsp1"), 19),
+    (c_str!("gpdsp"), 20),
+    (c_str!("gpdsp1"), 21),
+    (c_str!("tme"), 28),
+];
diff --git a/drivers/soc/qcom/socinfo_rust/socinfo_rust_file.rs b/drivers/soc/qcom/socinfo_rust/socinfo_rust_file.rs
new file mode 100644
index 0000000000000000000000000000000000000000..6a7f7af807b53341325dd3e5c5318f36efc52b1d
--- /dev/null
+++ b/drivers/soc/qcom/socinfo_rust/socinfo_rust_file.rs
@@ -0,0 +1,538 @@
+// SPDX-License-Identifier: GPL-2.0
+
+// Copyright (C) 2025 Google LLC.
+
+#![recursion_limit = "256"]
+
+//! Re-implementation of Qualcomm's Socinfo driver in Rust
+use core::convert::From;
+use core::fmt;
+use core::fmt::{Debug, Formatter};
+use core::mem::MaybeUninit;
+use kernel::c_str;
+use kernel::debugfs::{Dir, File, Render};
+use kernel::device::Core;
+use kernel::module_platform_driver;
+use kernel::platform::{self, Device};
+use kernel::prelude::*;
+use kernel::soc;
+use kernel::str::CString;
+
+use pin_init::pin_init_from_closure;
+
+mod bindings;
+mod data;
+
+use bindings::{qcom_smem_get, ImageVersion, ImageVersions, PmicArray, PmicEntry, RawSocInfo};
+use data::{IMAGE_NAMES, PMIC_MODELS, SOC_IDS};
+
+module_platform_driver! {
+    type: QcomSocInfo,
+    name: "qcom_socinfo_driver_file_rust",
+    authors: ["Matthew Maurer"],
+    description: "Rust re-implementation of Qualcomm's Socinfo driver",
+    license: "GPL",
+}
+
+#[pin_data]
+struct QcomSocInfo {
+    #[pin]
+    registration: soc::DeviceRegistration,
+    #[pin]
+    _debugfs: QcomSocInfoDebugFs,
+}
+
+#[derive(Default)]
+#[repr(transparent)]
+struct PmicModel(u32);
+
+impl From<u32> for PmicModel {
+    fn from(x: u32) -> Self {
+        Self(x)
+    }
+}
+
+impl Debug for PmicModel {
+    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+        let model = SocInfo::version_split(self.0).1;
+        if let Some(Some(model)) = PMIC_MODELS.get(model as usize) {
+            write!(f, "{model}")
+        } else {
+            write!(f, "unknown ({})", model)
+        }
+    }
+}
+
+#[derive(Default)]
+#[repr(transparent)]
+struct PmicDieRev(u32);
+
+impl From<u32> for PmicDieRev {
+    fn from(x: u32) -> Self {
+        Self(x)
+    }
+}
+
+impl Debug for PmicDieRev {
+    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+        let (major, minor) = SocInfo::version_split(self.0);
+        write!(f, "{major}.{minor}")
+    }
+}
+
+impl Render for PmicArray {
+    fn render(&self, f: &mut Formatter<'_>) -> fmt::Result {
+        for pmic_entry in self.0 {
+            let (die_rev_major, die_rev_minor) =
+                SocInfo::version_split(u32::from_le(pmic_entry.die_rev));
+            let model_idx = SocInfo::version_split(pmic_entry.model).1 as usize;
+            if let Some(Some(model)) = PMIC_MODELS.get(model_idx) {
+                writeln!(f, "{model} {die_rev_major}.{die_rev_minor}")?
+            } else {
+                writeln!(f, "unknown ({})", pmic_entry.model)?
+            }
+        }
+        Ok(())
+    }
+}
+
+struct Params {
+    info_fmt: u32,
+    build_id: [u8; 32],
+    raw_version: Option<u32>,
+    hardware_platform: Option<u32>,
+    platform_version: Option<u32>,
+    accessory_chip: Option<u32>,
+    hardware_platform_subtype: Option<u32>,
+    pmic_model: Option<PmicModel>,
+    pmic_die_rev: Option<PmicDieRev>,
+    foundry_id: Option<u32>,
+    pmic_model_array: Option<PmicArray>,
+    chip_family: Option<u32>,       // x32
+    raw_device_family: Option<u32>, // x32
+    raw_device_number: Option<u32>, // x32
+    nproduct_id: Option<u32>,
+    chip_id: Option<[u8; 32]>,
+    num_clusters: Option<u32>,
+    ncluster_array_offset: Option<u32>,
+    num_subset_parts: Option<u32>,
+    nsubset_parts_array_offset: Option<u32>,
+    nmodem_supported: Option<u32>,
+    feature_code: Option<u32>,
+    pcode: Option<u32>,
+    oem_variant: Option<u32>,
+    boot_core: Option<u32>,
+    boot_cluster: Option<u32>,
+    num_func_clusters: Option<u32>,
+    versions: Option<ImageVersions>,
+}
+
+#[derive(Copy, Clone)]
+struct SocInfo<'a> {
+    soc_info: RawSocInfo,
+    soc_info_mem: &'a [u8],
+    version_mem: &'a [u8],
+}
+
+impl<'a> SocInfo<'a> {
+    fn from_mem(soc_info_mem: &'a [u8], version_mem: &'a [u8]) -> Self {
+        Self {
+            soc_info: RawSocInfo::from_partial_bytes(soc_info_mem),
+            soc_info_mem,
+            version_mem,
+        }
+    }
+    fn id(&self) -> u32 {
+        u32::from_le(self.soc_info.id)
+    }
+    fn version_split(ver: u32) -> (u16, u16) {
+        let major = (ver >> 16) as u16;
+        let minor = (ver & 0xFFFF) as u16;
+        (major, minor)
+    }
+    fn version_fuse(major: u16, minor: u16) -> u32 {
+        (u32::from(major) << 16) | u32::from(minor)
+    }
+    fn version(&self) -> (u16, u16) {
+        Self::version_split(self.soc_info.ver)
+    }
+    fn serial(&self) -> u32 {
+        u32::from_le(self.soc_info.id)
+    }
+    fn machine(&self) -> Result<Option<CString>> {
+        for soc in SOC_IDS {
+            if soc.id == self.id() {
+                return Ok(Some(soc.name.to_cstring()?));
+            }
+        }
+        Ok(None)
+    }
+    fn device_attribute(&self) -> Result<soc::DeviceAttribute> {
+        Ok(soc::DeviceAttribute {
+            family: Some(c_str!("Snapdragon").to_cstring()?),
+            machine: self.machine()?,
+            revision: Some(CString::try_from_fmt(fmt!(
+                "{}.{}",
+                self.version().0,
+                self.version().1
+            ))?),
+            serial_number: Some(CString::try_from_fmt(fmt!("{}", self.serial()))?),
+            soc_id: Some(CString::try_from_fmt(fmt!("{}", self.id()))?),
+        })
+    }
+}
+
+macro_rules! u32_le_versioned {
+    { $params:expr, $self:ident,
+        [ $( { $major:expr, $minor:expr, { $( $dst:ident: $src:ident ),* } } ),*  ] } => {$(
+        if $params.info_fmt >= SocInfo::version_fuse($major, $minor) {
+            $( $params.$dst = Some(u32::from_le($self.soc_info.$src).into()) );*
+        }
+    )*}
+}
+
+impl SocInfo<'static> {
+    fn build_params(&self) -> Result<Params> {
+        let mut params = Params {
+            build_id: self.soc_info.build_id,
+            info_fmt: u32::from_le(self.soc_info.fmt),
+            raw_version: None,
+            hardware_platform: None,
+            platform_version: None,
+            accessory_chip: None,
+            hardware_platform_subtype: None,
+            pmic_model: None,
+            pmic_die_rev: None,
+            foundry_id: None,
+            pmic_model_array: None,
+            chip_family: None,
+            raw_device_family: None,
+            raw_device_number: None,
+            nproduct_id: None,
+            chip_id: None,
+            num_clusters: None,
+            ncluster_array_offset: None,
+            num_subset_parts: None,
+            nsubset_parts_array_offset: None,
+            nmodem_supported: None,
+            feature_code: None,
+            pcode: None,
+            oem_variant: None,
+            boot_core: None,
+            boot_cluster: None,
+            num_func_clusters: None,
+            versions: None,
+        };
+        u32_le_versioned! { params, self, [
+            {0, 2, { raw_version: raw_ver }},
+            {0, 3, { hardware_platform: hw_plat }},
+            {0, 4, { platform_version: plat_ver }},
+            {0, 5, { accessory_chip: accessory_chip }},
+            {0, 6, { hardware_platform_subtype: hw_plat_subtype }},
+            {0, 7, { pmic_model: pmic_model, pmic_die_rev: pmic_die_rev }},
+            {0, 9, { foundry_id: foundry_id }},
+            {0, 12, {
+                chip_family: chip_family,
+                raw_device_family: raw_device_family,
+                raw_device_number: raw_device_num
+            }},
+            {0, 13, { nproduct_id: nproduct_id }},
+            {0, 14, {
+                num_clusters: num_clusters,
+                ncluster_array_offset: ncluster_array_offset,
+                num_subset_parts: num_subset_parts,
+                nsubset_parts_array_offset: nsubset_parts_array_offset
+            }},
+            {0, 15, { nmodem_supported: nmodem_supported }},
+            {0, 16, { feature_code: feature_code, pcode: pcode }},
+            {0, 17, { oem_variant: oem_variant }},
+            {0, 19, {
+                boot_core: boot_core,
+                boot_cluster: boot_cluster,
+                num_func_clusters: num_func_clusters
+            }}
+        ]};
+        if params.info_fmt >= SocInfo::version_fuse(0, 11) {
+            let offset = u32::from_le(self.soc_info.pmic_array_offset) as usize;
+            let num_pmics = u32::from_le(self.soc_info.num_pmics) as usize;
+            let size = num_pmics * core::mem::size_of::<PmicEntry>();
+            params.pmic_model_array =
+                PmicArray::from_bytes(&self.soc_info_mem[offset..offset + size]);
+        }
+        if params.info_fmt >= SocInfo::version_fuse(0, 13) {
+            params.chip_id = Some(self.soc_info.chip_id);
+        }
+        params.versions = ImageVersions::from_bytes(self.version_mem);
+        Ok(params)
+    }
+}
+
+fn no_quirk<const SIZE: usize>(buf: &[u8; SIZE], f: &mut Formatter<'_>) -> fmt::Result {
+    if buf[0] == 0 {
+        writeln!(f)
+    } else {
+        nul_array(buf, f)
+    }
+}
+
+fn nul_array<const SIZE: usize>(buf: &[u8; SIZE], f: &mut Formatter<'_>) -> fmt::Result {
+    if let Some(end) = buf.iter().position(|x| *x == 0) {
+        if end == 0 {
+            // Match original driver quirk - empty strings don't have a trailing newline
+            return Ok(());
+        }
+        let Ok(c_str) = CStr::from_bytes_with_nul(&buf[0..=end]) else {
+            pr_warn!("Creating CStr from bytes with known first NUL failed?");
+            return Ok(());
+        };
+        writeln!(f, "{c_str}")
+    } else {
+        writeln!(f, "Missing NUL: {buf:?}")
+    }
+}
+
+fn hex(x: &u32, f: &mut Formatter<'_>) -> fmt::Result {
+    writeln!(f, "{x:#010x}")
+}
+
+#[pin_data]
+struct ImageVersionFiles {
+    _dir: Dir,
+    #[pin]
+    name: File<[u8; 75]>,
+    #[pin]
+    variant: File<[u8; 20]>,
+    #[pin]
+    oem: File<[u8; 32]>,
+}
+
+impl ImageVersionFiles {
+    fn create<'a>(
+        parent: &'a Dir,
+        image_name: &'a CStr,
+        version: &'a ImageVersion,
+    ) -> impl PinInit<Self> + 'a {
+        let dir = parent.subdir(image_name);
+        pin_init! {
+            Self {
+                _dir: dir.clone(),
+                name <- dir.read_callback_file(c_str!("name"), version.name, &nul_array),
+                variant <- dir.read_callback_file(c_str!("variant"), version.variant, &nul_array),
+                oem <- dir.read_callback_file(c_str!("oem"), version.oem, &nul_array),
+            }
+        }
+    }
+}
+
+#[pin_data]
+struct QcomSocInfoDebugFs {
+    _dir: Dir,
+    #[pin]
+    info_fmt: File<u32>,
+    #[pin]
+    build_id: File<[u8; 32]>,
+    #[pin]
+    raw_version: Option<File<u32>>,
+    #[pin]
+    hardware_platform: Option<File<u32>>,
+    #[pin]
+    platform_version: Option<File<u32>>,
+    #[pin]
+    accessory_chip: Option<File<u32>>,
+    #[pin]
+    hardware_platform_subtype: Option<File<u32>>,
+    #[pin]
+    pmic_model: Option<File<PmicModel>>,
+    #[pin]
+    pmic_die_rev: Option<File<PmicDieRev>>,
+    #[pin]
+    foundry_id: Option<File<u32>>,
+    #[pin]
+    pmic_model_array: Option<File<PmicArray>>,
+    #[pin]
+    chip_family: Option<File<u32>>,
+    #[pin]
+    raw_device_family: Option<File<u32>>,
+    #[pin]
+    raw_device_number: Option<File<u32>>,
+    #[pin]
+    nproduct_id: Option<File<u32>>,
+    #[pin]
+    chip_id: Option<File<[u8; 32]>>,
+    #[pin]
+    num_clusters: Option<File<u32>>,
+    #[pin]
+    ncluster_array_offset: Option<File<u32>>,
+    #[pin]
+    num_subset_parts: Option<File<u32>>,
+    #[pin]
+    nsubset_parts_array_offset: Option<File<u32>>,
+    #[pin]
+    nmodem_supported: Option<File<u32>>,
+    #[pin]
+    feature_code: Option<File<u32>>,
+    #[pin]
+    pcode: Option<File<u32>>,
+    #[pin]
+    oem_variant: Option<File<u32>>,
+    #[pin]
+    boot_core: Option<File<u32>>,
+    #[pin]
+    boot_cluster: Option<File<u32>>,
+    #[pin]
+    num_func_clusters: Option<File<u32>>,
+    versions: KVec<Pin<KBox<ImageVersionFiles>>>,
+}
+
+// TODO this doesn't seem like we should be defining it here, but without it,
+// it's hard to construct an `Option<File<u32>>`, which is required for this driver.
+fn pin_init_option<T, R, F, E, PI>(val: Option<T>, f: F) -> impl PinInit<Option<R>, E>
+where
+    F: FnOnce(T) -> PI,
+    PI: PinInit<R, E>,
+{
+    let init = move |slot: *mut Option<R>| -> Result<(), E> {
+        let Some(val) = val else {
+            // SAFETY: The constructor guarantees we're called with a writable pointer
+            unsafe { core::ptr::write(slot, None) };
+            return Ok(());
+        };
+
+        let slot_uninit: *mut Option<MaybeUninit<R>> = slot.cast();
+        // SAFETY: The constructor guarantees we're called with a writable pointer
+        // *mut Option<MaybeUninit<R>> is layout compatible with `Option<R>`
+        unsafe { core::ptr::write(slot_uninit, Some(MaybeUninit::uninit())) };
+        // SAFETY: We just initialized it, and we have unique access
+        let inner_slot: *mut R = unsafe { (*slot_uninit).as_mut().unwrap().as_mut_ptr() };
+        // SAFETY: `inner_slot` is a pointer to the uninitialized portion of the option.
+        if let Err(e) = unsafe { f(val).__pinned_init(inner_slot) } {
+            // Wipe out slot with a safe default
+            // SAFETY: The slot is still writable, and the other initializer has cleared out
+            // the payload.
+            unsafe { core::ptr::write(slot, None) };
+            return Err(e);
+        }
+
+        // Since we've initialized the `MaybeUninit` portion, we're fully initialized.
+        Ok(())
+    };
+
+    // SAFETY: If val is None, we always fully initialize it. If we successfully run the
+    // initializer, we fully initialize the Some arm. If we fail to run the initializer, we wipe it
+    // out with None before returning.
+    unsafe { pin_init_from_closure(init) }
+}
+
+macro_rules! create_optional_file {
+    ($dir:expr, $name:expr, $value:expr) => {
+        pin_init_option($value, |v| $dir.read_only_file(c_str!($name), v))
+    };
+    ($dir:expr, $name:expr, $value:expr, $callback:expr) => {
+        pin_init_option($value, |v| {
+            $dir.read_callback_file(c_str!($name), v, $callback)
+        })
+    };
+}
+
+fn create_version_files(
+    parent: &Dir,
+    versions: Option<ImageVersions>,
+) -> Result<KVec<Pin<KBox<ImageVersionFiles>>>> {
+    let mut res = KVec::new();
+    if let Some(v) = versions.as_ref() {
+        for (image_name, idx) in IMAGE_NAMES {
+            if let Some(version) = v.0.get(*idx) {
+                res.push(
+                    KBox::pin_init(
+                        ImageVersionFiles::create(parent, image_name, version),
+                        GFP_KERNEL,
+                    )?,
+                    GFP_KERNEL,
+                )?;
+            }
+        }
+    }
+    Ok(res)
+}
+
+impl QcomSocInfoDebugFs {
+    fn create(mut params: Params) -> impl PinInit<Self, Error> {
+        let dir = Dir::new(c_str!("qcom_socinfo_rs_file"));
+        let versions_data = params.versions.take();
+        try_pin_init! {
+            Self {
+                _dir: dir.clone(),
+                info_fmt <- dir.read_callback_file(c_str!("info_fmt"), params.info_fmt, &hex),
+                build_id <- dir.read_callback_file(c_str!("build_id"), params.build_id, &no_quirk),
+                raw_version <- create_optional_file!(dir, "raw_version", params.raw_version),
+                hardware_platform <- create_optional_file!(dir, "hardware_platform",
+                    params.hardware_platform),
+                platform_version <- create_optional_file!(dir, "platform_version",
+                    params.platform_version),
+                accessory_chip <- create_optional_file!(dir, "accessory_chip",
+                    params.accessory_chip),
+                hardware_platform_subtype <- create_optional_file!(dir,
+                    "hardware_platform_subtype", params.hardware_platform_subtype),
+                raw_device_number <- create_optional_file!(dir, "raw_device_number",
+                    params.raw_device_number, &hex),
+                raw_device_family <- create_optional_file!(dir, "raw_device_family",
+                    params.raw_device_family, &hex),
+                chip_family <- create_optional_file!(dir, "chip_family", params.chip_family, &hex),
+                chip_id <- create_optional_file!(dir, "chip_id", params.chip_id, &nul_array),
+                nproduct_id <- create_optional_file!(dir, "nproduct_id", params.nproduct_id),
+                nsubset_parts_array_offset <- create_optional_file!(dir,
+                    "nsubset_parts_array_offset", params.nsubset_parts_array_offset),
+                num_subset_parts <- create_optional_file!(dir, "num_subset_parts",
+                    params.num_subset_parts),
+                ncluster_array_offset <- create_optional_file!(dir, "ncluster_array_offset",
+                    params.ncluster_array_offset),
+                num_clusters <- create_optional_file!(dir, "num_clusters", params.num_clusters),
+                nmodem_supported <- create_optional_file!(dir, "nmodem_supported",
+                    params.nmodem_supported),
+                pcode <- create_optional_file!(dir, "pcode", params.pcode),
+                feature_code <- create_optional_file!(dir, "feature_code", params.feature_code),
+                oem_variant <- create_optional_file!(dir, "oem_variant", params.oem_variant),
+                boot_core <- create_optional_file!(dir, "boot_core", params.boot_core),
+                boot_cluster <- create_optional_file!(dir, "boot_cluster", params.boot_cluster),
+                num_func_clusters <- create_optional_file!(dir, "num_func_clusters",
+                    params.num_func_clusters),
+                foundry_id <- create_optional_file!(dir, "foundry_id", params.foundry_id),
+                pmic_model <- create_optional_file!(dir, "pmic_model", params.pmic_model),
+                pmic_die_rev <- create_optional_file!(dir, "pmic_die_rev", params.pmic_die_rev),
+                pmic_model_array <- create_optional_file!(dir, "pmic_model_array",
+                    params.pmic_model_array),
+                versions: create_version_files(&dir, versions_data)?,
+            } ? Error
+        }
+    }
+}
+
+impl platform::Driver for QcomSocInfo {
+    type IdInfo = ();
+    const OF_ID_TABLE: Option<kernel::of::IdTable<Self::IdInfo>> = None;
+    fn probe(_dev: &Device<Core>, _id_info: Option<&Self::IdInfo>) -> Result<Pin<KBox<Self>>> {
+        let soc_info_mem = qcom_smem_get(
+            kernel::bindings::QCOM_SMEM_HOST_ANY,
+            kernel::bindings::SMEM_HW_SW_BUILD_ID,
+        )?;
+        let version_mem = qcom_smem_get(
+            kernel::bindings::QCOM_SMEM_HOST_ANY,
+            bindings::SMEM_IMAGE_VERSION_TABLE,
+        )?;
+        let info = SocInfo::from_mem(soc_info_mem, version_mem);
+        let params = info.build_params()?;
+        let soc_info = KBox::pin_init(
+            try_pin_init!(
+                    Self {
+                        registration <- soc::DeviceRegistration::register(info.device_attribute()?),
+                        _debugfs <- QcomSocInfoDebugFs::create(params),
+                    } ? Error
+            ),
+            GFP_KERNEL,
+        )?;
+
+        kernel::rand::add_device_randomness(soc_info_mem);
+
+        Ok(soc_info)
+    }
+}
diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
index afe8be55a6db1d29706c19a91dc51c1ae3494ed4..f22320757e76a77df0e95bdb5bb43afc8cd51616 100644
--- a/rust/bindings/bindings_helper.h
+++ b/rust/bindings/bindings_helper.h
@@ -80,6 +80,12 @@
 #include <linux/xarray.h>
 #include <trace/events/rust_sample.h>
 
+#if defined(CONFIG_QCOM_SOCINFO_RUST)
+#include <dt-bindings/arm/qcom,ids.h>
+#include <linux/soc/qcom/smem.h>
+#include <linux/soc/qcom/socinfo.h>
+#endif
+
 #if defined(CONFIG_DRM_PANIC_SCREEN_QR_CODE)
 // Used by `#[export]` in `drivers/gpu/drm/drm_panic_qr.rs`.
 #include <drm/drm_panic.h>

-- 
2.51.0.rc1.167.g924127e9c0-goog


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH WIP 5/5] soc: qcom: socinfo: `Scoped`-based example
  2025-08-19 23:12 [PATCH WIP 0/5] qcom-socinfo Rust Implementation Matthew Maurer
                   ` (3 preceding siblings ...)
  2025-08-19 23:12 ` [PATCH WIP 4/5] soc: qcom: socinfo: `File`-based example Matthew Maurer
@ 2025-08-19 23:12 ` Matthew Maurer
  4 siblings, 0 replies; 6+ messages in thread
From: Matthew Maurer @ 2025-08-19 23:12 UTC (permalink / raw)
  To: Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
	Björn Roy Baron, Andreas Hindborg, Alice Ryhl, Trevor Gross,
	Danilo Krummrich, Greg Kroah-Hartman, Rafael J. Wysocki,
	Sami Tolvanen, Timur Tabi, Benno Lossin, Dirk Beheme
  Cc: linux-kernel, rust-for-linux, Matthew Maurer

Re-implements qcom-socinfo driver in Rust, using `Scoped`-based DebugFS
bindings.

Signed-off-by: Matthew Maurer <mmaurer@google.com>
---
 drivers/soc/qcom/Makefile                          |   1 +
 drivers/soc/qcom/smem.c                            |   7 +-
 .../soc/qcom/socinfo_rust/socinfo_rust_scoped.rs   | 367 +++++++++++++++++++++
 3 files changed, 374 insertions(+), 1 deletion(-)

diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
index 6453a4f4f7da89d4e1677ad8f75257e94cec90f4..ab76fc1d6ae10bef74adbc1489518379b7d5c00b 100644
--- a/drivers/soc/qcom/Makefile
+++ b/drivers/soc/qcom/Makefile
@@ -41,3 +41,4 @@ obj-$(CONFIG_QCOM_INLINE_CRYPTO_ENGINE)	+= qcom_ice.o
 obj-$(CONFIG_QCOM_PBS) +=	qcom-pbs.o
 obj-$(CONFIG_QCOM_UBWC_CONFIG) += ubwc_config.o
 obj-$(CONFIG_QCOM_SOCINFO_RUST) += socinfo_rust/socinfo_rust_file.o
+obj-$(CONFIG_QCOM_SOCINFO_RUST) += socinfo_rust/socinfo_rust_scoped.o
diff --git a/drivers/soc/qcom/smem.c b/drivers/soc/qcom/smem.c
index 5279db46a1e0dd7b2994b79d131367175e35290f..6810f0cdd37d7f393977ffeae5836f47132988c7 100644
--- a/drivers/soc/qcom/smem.c
+++ b/drivers/soc/qcom/smem.c
@@ -1230,13 +1230,18 @@ static int qcom_smem_probe(struct platform_device *pdev)
 
 	__smem = smem;
 
-	// TODO don't double load, this is just to diff the debugfs
+	// TODO don't triple load, this is just to diff the debugfs
 	smem->socinfo = platform_device_register_data(&pdev->dev, "qcom-socinfo",
 						      PLATFORM_DEVID_NONE, NULL, 0);
 	// TODO MODULE_ALIAS
 	smem->socinfo = platform_device_register_data(&pdev->dev, "qcom_socinfo_driver_file_rust",
 						      PLATFORM_DEVID_NONE, NULL,
 						      0);
+	// TODO MODULE_ALIAS
+	smem->socinfo = platform_device_register_data(&pdev->dev, "qcom_socinfo_driver_scoped_rust",
+						      PLATFORM_DEVID_NONE, NULL,
+						      0);
+
 	if (IS_ERR(smem->socinfo))
 		dev_dbg(&pdev->dev, "failed to register socinfo device\n");
 
diff --git a/drivers/soc/qcom/socinfo_rust/socinfo_rust_scoped.rs b/drivers/soc/qcom/socinfo_rust/socinfo_rust_scoped.rs
new file mode 100644
index 0000000000000000000000000000000000000000..845e91560a7879d4cf13c6b62fd9ad8a15c79826
--- /dev/null
+++ b/drivers/soc/qcom/socinfo_rust/socinfo_rust_scoped.rs
@@ -0,0 +1,367 @@
+// SPDX-License-Identifier: GPL-2.0
+
+// Copyright (C) 2025 Google LLC.
+
+//! Re-implementation of Qualcomm's Socinfo driver in Rust
+use core::convert::From;
+use core::fmt;
+use core::fmt::{Debug, Formatter};
+use kernel::c_str;
+use kernel::debugfs::{Render, Scope, ScopedDir};
+use kernel::device::Core;
+use kernel::module_platform_driver;
+use kernel::platform::{self, Device};
+use kernel::prelude::*;
+use kernel::soc;
+use kernel::str::CString;
+
+mod bindings;
+mod data;
+
+use bindings::{qcom_smem_get, ImageVersion, ImageVersions, PmicArray, PmicEntry, RawSocInfo};
+use data::{IMAGE_NAMES, PMIC_MODELS, SOC_IDS};
+
+module_platform_driver! {
+    type: QcomSocInfo,
+    name: "qcom_socinfo_driver_scoped_rust",
+    authors: ["Matthew Maurer"],
+    description: "Rust re-implementation of Qualcomm's Socinfo driver",
+    license: "GPL",
+}
+
+#[pin_data]
+struct QcomSocInfo {
+    #[pin]
+    registration: soc::DeviceRegistration,
+    #[pin]
+    params: Scope<Params>,
+}
+
+#[derive(Default)]
+#[repr(transparent)]
+struct PmicModel(u32);
+
+impl From<u32> for PmicModel {
+    fn from(x: u32) -> Self {
+        Self(x)
+    }
+}
+
+impl Debug for PmicModel {
+    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+        let model = SocInfo::version_split(self.0).1;
+        if let Some(Some(model)) = PMIC_MODELS.get(model as usize) {
+            write!(f, "{model}")
+        } else {
+            write!(f, "unknown ({})", model)
+        }
+    }
+}
+
+#[derive(Default)]
+#[repr(transparent)]
+struct PmicDieRev(u32);
+
+impl From<u32> for PmicDieRev {
+    fn from(x: u32) -> Self {
+        Self(x)
+    }
+}
+
+impl Debug for PmicDieRev {
+    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+        let (major, minor) = SocInfo::version_split(self.0);
+        write!(f, "{major}.{minor}")
+    }
+}
+
+impl Render for PmicArray {
+    fn render(&self, f: &mut Formatter<'_>) -> fmt::Result {
+        for pmic_entry in self.0 {
+            let (die_rev_major, die_rev_minor) =
+                SocInfo::version_split(u32::from_le(pmic_entry.die_rev));
+            let model_idx = SocInfo::version_split(pmic_entry.model).1 as usize;
+            if let Some(Some(model)) = PMIC_MODELS.get(model_idx) {
+                writeln!(f, "{model} {die_rev_major}.{die_rev_minor}")?
+            } else {
+                writeln!(f, "unknown ({})", pmic_entry.model)?
+            }
+        }
+        Ok(())
+    }
+}
+
+#[derive(Default)]
+struct Params {
+    info_fmt: u32,
+    build_id: [u8; 32],
+    raw_version: Option<u32>,
+    hardware_platform: Option<u32>,
+    platform_version: Option<u32>,
+    accessory_chip: Option<u32>,
+    hardware_platform_subtype: Option<u32>,
+    pmic_model: Option<PmicModel>,
+    pmic_die_rev: Option<PmicDieRev>,
+    foundry_id: Option<u32>,
+    pmic_model_array: Option<PmicArray>,
+    chip_family: Option<u32>,       // x32
+    raw_device_family: Option<u32>, // x32
+    raw_device_number: Option<u32>, // x32
+    nproduct_id: Option<u32>,
+    chip_id: Option<[u8; 32]>,
+    num_clusters: Option<u32>,
+    ncluster_array_offset: Option<u32>,
+    num_subset_parts: Option<u32>,
+    nsubset_parts_array_offset: Option<u32>,
+    nmodem_supported: Option<u32>,
+    feature_code: Option<u32>,
+    pcode: Option<u32>,
+    oem_variant: Option<u32>,
+    boot_core: Option<u32>,
+    boot_cluster: Option<u32>,
+    num_func_clusters: Option<u32>,
+    versions: Option<ImageVersions>,
+}
+
+#[derive(Copy, Clone)]
+struct SocInfo<'a> {
+    soc_info: RawSocInfo,
+    soc_info_mem: &'a [u8],
+    version_mem: &'a [u8],
+}
+
+impl<'a> SocInfo<'a> {
+    fn from_mem(soc_info_mem: &'a [u8], version_mem: &'a [u8]) -> Self {
+        Self {
+            soc_info: RawSocInfo::from_partial_bytes(soc_info_mem),
+            soc_info_mem,
+            version_mem,
+        }
+    }
+    fn id(&self) -> u32 {
+        u32::from_le(self.soc_info.id)
+    }
+    fn version_split(ver: u32) -> (u16, u16) {
+        let major = (ver >> 16) as u16;
+        let minor = (ver & 0xFFFF) as u16;
+        (major, minor)
+    }
+    fn version_fuse(major: u16, minor: u16) -> u32 {
+        (u32::from(major) << 16) | u32::from(minor)
+    }
+    fn version(&self) -> (u16, u16) {
+        Self::version_split(self.soc_info.ver)
+    }
+    fn serial(&self) -> u32 {
+        u32::from_le(self.soc_info.id)
+    }
+    fn machine(&self) -> Result<Option<CString>> {
+        for soc in SOC_IDS {
+            if soc.id == self.id() {
+                return Ok(Some(soc.name.to_cstring()?));
+            }
+        }
+        Ok(None)
+    }
+    fn device_attribute(&self) -> Result<soc::DeviceAttribute> {
+        Ok(soc::DeviceAttribute {
+            family: Some(c_str!("Snapdragon").to_cstring()?),
+            machine: self.machine()?,
+            revision: Some(CString::try_from_fmt(fmt!(
+                "{}.{}",
+                self.version().0,
+                self.version().1
+            ))?),
+            serial_number: Some(CString::try_from_fmt(fmt!("{}", self.serial()))?),
+            soc_id: Some(CString::try_from_fmt(fmt!("{}", self.id()))?),
+        })
+    }
+}
+
+macro_rules! u32_le_versioned {
+    { $params:expr, $self:ident,
+        [ $( { $major:expr, $minor:expr, { $( $dst:ident: $src:ident ),* } } ),*  ] } => {$(
+        if $params.info_fmt >= SocInfo::version_fuse($major, $minor) {
+            $( $params.$dst = Some(u32::from_le($self.soc_info.$src).into()) );*
+        }
+    )*}
+}
+
+impl SocInfo<'static> {
+    fn build_params(&self) -> Result<Params> {
+        let mut params = Params {
+            build_id: self.soc_info.build_id,
+            info_fmt: u32::from_le(self.soc_info.fmt),
+            ..Default::default()
+        };
+        u32_le_versioned! { params, self, [
+            {0, 2, { raw_version: raw_ver }},
+            {0, 3, { hardware_platform: hw_plat }},
+            {0, 4, { platform_version: plat_ver }},
+            {0, 5, { accessory_chip: accessory_chip }},
+            {0, 6, { hardware_platform_subtype: hw_plat_subtype }},
+            {0, 7, { pmic_model: pmic_model, pmic_die_rev: pmic_die_rev }},
+            {0, 9, { foundry_id: foundry_id }},
+            {0, 12, {
+                chip_family: chip_family,
+                raw_device_family: raw_device_family,
+                raw_device_number: raw_device_num
+            }},
+            {0, 13, { nproduct_id: nproduct_id }},
+            {0, 14, {
+                num_clusters: num_clusters,
+                ncluster_array_offset: ncluster_array_offset,
+                num_subset_parts: num_subset_parts,
+                nsubset_parts_array_offset: nsubset_parts_array_offset
+            }},
+            {0, 15, { nmodem_supported: nmodem_supported }},
+            {0, 16, { feature_code: feature_code, pcode: pcode }},
+            {0, 17, { oem_variant: oem_variant }},
+            {0, 19, {
+                boot_core: boot_core,
+                boot_cluster: boot_cluster,
+                num_func_clusters: num_func_clusters
+            }}
+        ]};
+        if params.info_fmt >= SocInfo::version_fuse(0, 11) {
+            let offset = u32::from_le(self.soc_info.pmic_array_offset) as usize;
+            let num_pmics = u32::from_le(self.soc_info.num_pmics) as usize;
+            let size = num_pmics * core::mem::size_of::<PmicEntry>();
+            params.pmic_model_array =
+                PmicArray::from_bytes(&self.soc_info_mem[offset..offset + size]);
+        }
+        if params.info_fmt >= SocInfo::version_fuse(0, 13) {
+            params.chip_id = Some(self.soc_info.chip_id);
+        }
+        params.versions = ImageVersions::from_bytes(self.version_mem);
+        Ok(params)
+    }
+}
+
+macro_rules! value_attrs {
+    ($builder:ident, $params:ident, @) => {};
+    ($builder:ident, $params:ident, @ $s:ident $($rest:tt)*) => {
+        if let Some(v) = $params.$s.as_ref() {
+            $builder.read_only_file(c_str!(stringify!($s)), v);
+        }
+        value_attrs!($builder, $params, @ $($rest)*)
+    };
+    ($builder:ident, $params:ident, @ {$s:ident, $cb:expr} $($rest:tt)*) => {
+        if let Some(v) = $params.$s.as_ref() {
+            $builder.read_callback_file(c_str!(stringify!($s)), v, $cb);
+        }
+        value_attrs!($builder, $params, @ $($rest)*)
+    };
+    ($builder:ident, $params:ident, {$($items:tt),*}) => {
+        value_attrs!($builder, $params, @ $($items)*)
+    };
+}
+
+fn no_quirk<const SIZE: usize>(buf: &[u8; SIZE], f: &mut Formatter<'_>) -> fmt::Result {
+    if buf[0] == 0 {
+        writeln!(f)
+    } else {
+        nul_array(buf, f)
+    }
+}
+
+fn nul_array<const SIZE: usize>(buf: &[u8; SIZE], f: &mut Formatter<'_>) -> fmt::Result {
+    if let Some(end) = buf.iter().position(|x| *x == 0) {
+        if end == 0 {
+            // Match original driver quirk - empty strings don't have a trailing newline
+            return Ok(());
+        }
+        let Ok(c_str) = CStr::from_bytes_with_nul(&buf[0..=end]) else {
+            pr_warn!("Creating CStr from bytes with known first NUL failed?");
+            return Ok(());
+        };
+        writeln!(f, "{c_str}")
+    } else {
+        writeln!(f, "Missing NUL: {buf:?}")
+    }
+}
+
+impl ImageVersion {
+    fn build_debugfs<'a>(&'a self, dir: &ScopedDir<'a, '_>, image_name: &CStr) {
+        let subdir = dir.dir(image_name);
+        subdir.read_callback_file(c_str!("name"), &self.name, &nul_array);
+        subdir.read_callback_file(c_str!("variant"), &self.variant, &nul_array);
+        subdir.read_callback_file(c_str!("oem"), &self.oem, &nul_array);
+    }
+}
+
+fn hex(x: &u32, f: &mut Formatter<'_>) -> fmt::Result {
+    writeln!(f, "{x:#010x}")
+}
+
+impl Params {
+    fn build_debugfs<'data>(&'data self, dir: &ScopedDir<'data, '_>) {
+        dir.read_callback_file(c_str!("info_fmt"), &self.info_fmt, &hex);
+        dir.read_callback_file(c_str!("build_id"), &self.build_id, &no_quirk);
+        value_attrs!(dir, self, {
+            raw_version,
+            hardware_platform,
+            platform_version,
+            accessory_chip,
+            hardware_platform_subtype,
+            {raw_device_number, &hex},
+            {raw_device_family, &hex},
+            {chip_family, &hex},
+            {chip_id, &nul_array},
+            nproduct_id,
+            nsubset_parts_array_offset,
+            num_subset_parts,
+            ncluster_array_offset,
+            num_clusters,
+            nmodem_supported,
+            pcode,
+            feature_code,
+            oem_variant,
+            boot_core,
+            boot_cluster,
+            num_func_clusters,
+            foundry_id,
+            pmic_model,
+            pmic_die_rev,
+            pmic_model_array
+        });
+        if let Some(versions) = self.versions.as_ref() {
+            for (image_name, idx) in IMAGE_NAMES {
+                if let Some(version) = versions.0.get(*idx) {
+                    version.build_debugfs(dir, image_name);
+                }
+            }
+        }
+    }
+}
+
+impl platform::Driver for QcomSocInfo {
+    type IdInfo = ();
+    const OF_ID_TABLE: Option<kernel::of::IdTable<Self::IdInfo>> = None;
+    fn probe(_dev: &Device<Core>, _id_info: Option<&Self::IdInfo>) -> Result<Pin<KBox<Self>>> {
+        let soc_info_mem = qcom_smem_get(
+            kernel::bindings::QCOM_SMEM_HOST_ANY,
+            kernel::bindings::SMEM_HW_SW_BUILD_ID,
+        )?;
+        let version_mem = qcom_smem_get(
+            kernel::bindings::QCOM_SMEM_HOST_ANY,
+            bindings::SMEM_IMAGE_VERSION_TABLE,
+        )?;
+        let info = SocInfo::from_mem(soc_info_mem, version_mem);
+        let backing = info.build_params()?;
+        let soc_info = KBox::pin_init(
+            try_pin_init!(
+                    Self {
+                        registration <- soc::DeviceRegistration::register(info.device_attribute()?),
+                        params <- Scope::dir(backing, c_str!("qcom_socinfo_rs_scoped"),
+                                             Params::build_debugfs),
+                    }
+            ),
+            GFP_KERNEL,
+        )?;
+
+        kernel::rand::add_device_randomness(soc_info_mem);
+
+        Ok(soc_info)
+    }
+}

-- 
2.51.0.rc1.167.g924127e9c0-goog


^ permalink raw reply related	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2025-08-19 23:12 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-08-19 23:12 [PATCH WIP 0/5] qcom-socinfo Rust Implementation Matthew Maurer
2025-08-19 23:12 ` [PATCH WIP 1/5] rust: Add soc_device support Matthew Maurer
2025-08-19 23:12 ` [PATCH WIP 2/5] rust: transmute: Cleanup + Fixes Matthew Maurer
2025-08-19 23:12 ` [PATCH WIP 3/5] rust: Add support for feeding entropy to randomness pool Matthew Maurer
2025-08-19 23:12 ` [PATCH WIP 4/5] soc: qcom: socinfo: `File`-based example Matthew Maurer
2025-08-19 23:12 ` [PATCH WIP 5/5] soc: qcom: socinfo: `Scoped`-based example Matthew Maurer

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).