Commit 6f227d21 authored by Matthew Maurer's avatar Matthew Maurer Committed by Danilo Krummrich
Browse files

samples: rust: Add debugfs sample driver



Adds a new sample driver that demonstrates the debugfs APIs.

The driver creates a directory in debugfs and populates it with a few
files:
- A read-only file that displays a fwnode property.
- A read-write file that exposes an atomic counter.
- A read-write file that exposes a custom struct.

This sample serves as a basic example of how to use the `debugfs::Dir`
and `debugfs::File` APIs to create and manage debugfs entries.

Signed-off-by: default avatarMatthew Maurer <mmaurer@google.com>
Tested-by: default avatarDirk Behme <dirk.behme@de.bosch.com>
Acked-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Link: https://lore.kernel.org/r/20250904-debugfs-rust-v11-5-7d12a165685a@google.com


[ Change ACPI ID "LNUXDEBF" to "LNUXBEEF". - Danilo ]
Signed-off-by: default avatarDanilo Krummrich <dakr@kernel.org>
parent 40ecc494
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -7496,6 +7496,7 @@ F: rust/kernel/devres.rs
F:	rust/kernel/driver.rs
F:	rust/kernel/faux.rs
F:	rust/kernel/platform.rs
F:	samples/rust/rust_debugfs.rs
F:	samples/rust/rust_driver_platform.rs
F:	samples/rust/rust_driver_faux.rs
+11 −0
Original line number Diff line number Diff line
@@ -62,6 +62,17 @@ config SAMPLE_RUST_DMA

	  If unsure, say N.

config SAMPLE_RUST_DEBUGFS
	tristate "DebugFS Test Module"
	depends on DEBUG_FS
	help
	  This option builds the Rust DebugFS Test module sample.

	  To compile this as a module, choose M here:
	  the module will be called rust_debugfs.

	  If unsure, say N.

config SAMPLE_RUST_DRIVER_PCI
	tristate "PCI Driver"
	depends on PCI
+1 −0
Original line number Diff line number Diff line
@@ -4,6 +4,7 @@ ccflags-y += -I$(src) # needed for trace events
obj-$(CONFIG_SAMPLE_RUST_MINIMAL)		+= rust_minimal.o
obj-$(CONFIG_SAMPLE_RUST_MISC_DEVICE)		+= rust_misc_device.o
obj-$(CONFIG_SAMPLE_RUST_PRINT)			+= rust_print.o
obj-$(CONFIG_SAMPLE_RUST_DEBUGFS)		+= rust_debugfs.o
obj-$(CONFIG_SAMPLE_RUST_DMA)			+= rust_dma.o
obj-$(CONFIG_SAMPLE_RUST_DRIVER_PCI)		+= rust_driver_pci.o
obj-$(CONFIG_SAMPLE_RUST_DRIVER_PLATFORM)	+= rust_driver_platform.o
+151 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0

// Copyright (C) 2025 Google LLC.

//! Sample DebugFS exporting platform driver
//!
//! To successfully probe this driver with ACPI, use an ssdt that looks like
//!
//! ```dsl
//! DefinitionBlock ("", "SSDT", 2, "TEST", "VIRTACPI", 0x00000001)
//! {
//!    Scope (\_SB)
//!    {
//!        Device (T432)
//!        {
//!            Name (_HID, "LNUXBEEF")  // ACPI hardware ID to match
//!            Name (_UID, 1)
//!            Name (_STA, 0x0F)        // Device present, enabled
//!            Name (_DSD, Package () { // Sample attribute
//!                ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
//!                Package() {
//!                    Package(2) {"compatible", "sample-debugfs"}
//!                }
//!            })
//!            Name (_CRS, ResourceTemplate ()
//!            {
//!                Memory32Fixed (ReadWrite, 0xFED00000, 0x1000)
//!            })
//!        }
//!    }
//! }
//! ```

use core::str::FromStr;
use core::sync::atomic::AtomicUsize;
use core::sync::atomic::Ordering;
use kernel::c_str;
use kernel::debugfs::{Dir, File};
use kernel::new_mutex;
use kernel::prelude::*;
use kernel::sync::Mutex;

use kernel::{acpi, device::Core, of, platform, str::CString, types::ARef};

kernel::module_platform_driver! {
    type: RustDebugFs,
    name: "rust_debugfs",
    authors: ["Matthew Maurer"],
    description: "Rust DebugFS usage sample",
    license: "GPL",
}

#[pin_data]
struct RustDebugFs {
    pdev: ARef<platform::Device>,
    // As we only hold these for drop effect (to remove the directory/files) we have a leading
    // underscore to indicate to the compiler that we don't expect to use this field directly.
    _debugfs: Dir,
    #[pin]
    _compatible: File<CString>,
    #[pin]
    counter: File<AtomicUsize>,
    #[pin]
    inner: File<Mutex<Inner>>,
}

#[derive(Debug)]
struct Inner {
    x: u32,
    y: u32,
}

impl FromStr for Inner {
    type Err = Error;
    fn from_str(s: &str) -> Result<Self> {
        let mut parts = s.split_whitespace();
        let x = parts
            .next()
            .ok_or(EINVAL)?
            .parse::<u32>()
            .map_err(|_| EINVAL)?;
        let y = parts
            .next()
            .ok_or(EINVAL)?
            .parse::<u32>()
            .map_err(|_| EINVAL)?;
        if parts.next().is_some() {
            return Err(EINVAL);
        }
        Ok(Inner { x, y })
    }
}

kernel::acpi_device_table!(
    ACPI_TABLE,
    MODULE_ACPI_TABLE,
    <RustDebugFs as platform::Driver>::IdInfo,
    [(acpi::DeviceId::new(c_str!("LNUXBEEF")), ())]
);

impl platform::Driver for RustDebugFs {
    type IdInfo = ();
    const OF_ID_TABLE: Option<of::IdTable<Self::IdInfo>> = None;
    const ACPI_ID_TABLE: Option<acpi::IdTable<Self::IdInfo>> = Some(&ACPI_TABLE);

    fn probe(
        pdev: &platform::Device<Core>,
        _info: Option<&Self::IdInfo>,
    ) -> Result<Pin<KBox<Self>>> {
        let result = KBox::try_pin_init(RustDebugFs::new(pdev), GFP_KERNEL)?;
        // We can still mutate fields through the files which are atomic or mutexed:
        result.counter.store(91, Ordering::Relaxed);
        {
            let mut guard = result.inner.lock();
            guard.x = guard.y;
            guard.y = 42;
        }
        Ok(result)
    }
}

impl RustDebugFs {
    fn build_counter(dir: &Dir) -> impl PinInit<File<AtomicUsize>> + '_ {
        dir.read_write_file(c_str!("counter"), AtomicUsize::new(0))
    }

    fn build_inner(dir: &Dir) -> impl PinInit<File<Mutex<Inner>>> + '_ {
        dir.read_write_file(c_str!("pair"), new_mutex!(Inner { x: 3, y: 10 }))
    }

    fn new(pdev: &platform::Device<Core>) -> impl PinInit<Self, Error> + '_ {
        let debugfs = Dir::new(c_str!("sample_debugfs"));
        let dev = pdev.as_ref();

        try_pin_init! {
            Self {
                _compatible <- debugfs.read_only_file(
                    c_str!("compatible"),
                    dev.fwnode()
                        .ok_or(ENOENT)?
                        .property_read::<CString>(c_str!("compatible"))
                        .required_by(dev)?,
                ),
                counter <- Self::build_counter(&debugfs),
                inner <- Self::build_inner(&debugfs),
                _debugfs: debugfs,
                pdev: pdev.into(),
            }
        }
    }
}