summaryrefslogtreecommitdiff
path: root/samples/rust
diff options
context:
space:
mode:
Diffstat (limited to 'samples/rust')
-rw-r--r--samples/rust/Kconfig41
-rw-r--r--samples/rust/Makefile4
-rw-r--r--samples/rust/rust_driver_faux.rs29
-rw-r--r--samples/rust/rust_driver_pci.rs110
-rw-r--r--samples/rust/rust_driver_platform.rs49
-rw-r--r--samples/rust/rust_misc_device.rs238
-rw-r--r--samples/rust/rust_print_main.rs20
7 files changed, 490 insertions, 1 deletions
diff --git a/samples/rust/Kconfig b/samples/rust/Kconfig
index b0f74a81c8f9..3b6eae84b297 100644
--- a/samples/rust/Kconfig
+++ b/samples/rust/Kconfig
@@ -20,6 +20,16 @@ config SAMPLE_RUST_MINIMAL
If unsure, say N.
+config SAMPLE_RUST_MISC_DEVICE
+ tristate "Misc device"
+ help
+ This option builds the Rust misc device.
+
+ To compile this as a module, choose M here:
+ the module will be called rust_misc_device.
+
+ If unsure, say N.
+
config SAMPLE_RUST_PRINT
tristate "Printing macros"
help
@@ -30,6 +40,37 @@ config SAMPLE_RUST_PRINT
If unsure, say N.
+config SAMPLE_RUST_DRIVER_PCI
+ tristate "PCI Driver"
+ depends on PCI
+ help
+ This option builds the Rust PCI driver sample.
+
+ To compile this as a module, choose M here:
+ the module will be called driver_pci.
+
+ If unsure, say N.
+
+config SAMPLE_RUST_DRIVER_PLATFORM
+ tristate "Platform Driver"
+ help
+ This option builds the Rust Platform driver sample.
+
+ To compile this as a module, choose M here:
+ the module will be called rust_driver_platform.
+
+ If unsure, say N.
+
+config SAMPLE_RUST_DRIVER_FAUX
+ tristate "Faux Driver"
+ help
+ This option builds the Rust Faux driver sample.
+
+ To compile this as a module, choose M here:
+ the module will be called rust_driver_faux.
+
+ If unsure, say N.
+
config SAMPLE_RUST_HOSTPROGS
bool "Host programs"
help
diff --git a/samples/rust/Makefile b/samples/rust/Makefile
index c1a5c1655395..0dbc6d90f1ef 100644
--- a/samples/rust/Makefile
+++ b/samples/rust/Makefile
@@ -2,7 +2,11 @@
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_DRIVER_PCI) += rust_driver_pci.o
+obj-$(CONFIG_SAMPLE_RUST_DRIVER_PLATFORM) += rust_driver_platform.o
+obj-$(CONFIG_SAMPLE_RUST_DRIVER_FAUX) += rust_driver_faux.o
rust_print-y := rust_print_main.o rust_print_events.o
diff --git a/samples/rust/rust_driver_faux.rs b/samples/rust/rust_driver_faux.rs
new file mode 100644
index 000000000000..048c6cb98b29
--- /dev/null
+++ b/samples/rust/rust_driver_faux.rs
@@ -0,0 +1,29 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+//! Rust faux device sample.
+
+use kernel::{c_str, faux, prelude::*, Module};
+
+module! {
+ type: SampleModule,
+ name: "rust_faux_driver",
+ author: "Lyude Paul",
+ description: "Rust faux device sample",
+ license: "GPL",
+}
+
+struct SampleModule {
+ _reg: faux::Registration,
+}
+
+impl Module for SampleModule {
+ fn init(_module: &'static ThisModule) -> Result<Self> {
+ pr_info!("Initialising Rust Faux Device Sample\n");
+
+ let reg = faux::Registration::new(c_str!("rust-faux-sample-device"))?;
+
+ dev_info!(reg.as_ref(), "Hello from faux device!\n");
+
+ Ok(Self { _reg: reg })
+ }
+}
diff --git a/samples/rust/rust_driver_pci.rs b/samples/rust/rust_driver_pci.rs
new file mode 100644
index 000000000000..1fb6e44f3395
--- /dev/null
+++ b/samples/rust/rust_driver_pci.rs
@@ -0,0 +1,110 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Rust PCI driver sample (based on QEMU's `pci-testdev`).
+//!
+//! To make this driver probe, QEMU must be run with `-device pci-testdev`.
+
+use kernel::{bindings, c_str, devres::Devres, pci, prelude::*};
+
+struct Regs;
+
+impl Regs {
+ const TEST: usize = 0x0;
+ const OFFSET: usize = 0x4;
+ const DATA: usize = 0x8;
+ const COUNT: usize = 0xC;
+ const END: usize = 0x10;
+}
+
+type Bar0 = pci::Bar<{ Regs::END }>;
+
+#[derive(Debug)]
+struct TestIndex(u8);
+
+impl TestIndex {
+ const NO_EVENTFD: Self = Self(0);
+}
+
+struct SampleDriver {
+ pdev: pci::Device,
+ bar: Devres<Bar0>,
+}
+
+kernel::pci_device_table!(
+ PCI_TABLE,
+ MODULE_PCI_TABLE,
+ <SampleDriver as pci::Driver>::IdInfo,
+ [(
+ pci::DeviceId::from_id(bindings::PCI_VENDOR_ID_REDHAT, 0x5),
+ TestIndex::NO_EVENTFD
+ )]
+);
+
+impl SampleDriver {
+ fn testdev(index: &TestIndex, bar: &Bar0) -> Result<u32> {
+ // Select the test.
+ bar.writeb(index.0, Regs::TEST);
+
+ let offset = u32::from_le(bar.readl(Regs::OFFSET)) as usize;
+ let data = bar.readb(Regs::DATA);
+
+ // Write `data` to `offset` to increase `count` by one.
+ //
+ // Note that we need `try_writeb`, since `offset` can't be checked at compile-time.
+ bar.try_writeb(data, offset)?;
+
+ Ok(bar.readl(Regs::COUNT))
+ }
+}
+
+impl pci::Driver for SampleDriver {
+ type IdInfo = TestIndex;
+
+ const ID_TABLE: pci::IdTable<Self::IdInfo> = &PCI_TABLE;
+
+ fn probe(pdev: &mut pci::Device, info: &Self::IdInfo) -> Result<Pin<KBox<Self>>> {
+ dev_dbg!(
+ pdev.as_ref(),
+ "Probe Rust PCI driver sample (PCI ID: 0x{:x}, 0x{:x}).\n",
+ pdev.vendor_id(),
+ pdev.device_id()
+ );
+
+ pdev.enable_device_mem()?;
+ pdev.set_master();
+
+ let bar = pdev.iomap_region_sized::<{ Regs::END }>(0, c_str!("rust_driver_pci"))?;
+
+ let drvdata = KBox::new(
+ Self {
+ pdev: pdev.clone(),
+ bar,
+ },
+ GFP_KERNEL,
+ )?;
+
+ let bar = drvdata.bar.try_access().ok_or(ENXIO)?;
+
+ dev_info!(
+ pdev.as_ref(),
+ "pci-testdev data-match count: {}\n",
+ Self::testdev(info, &bar)?
+ );
+
+ Ok(drvdata.into())
+ }
+}
+
+impl Drop for SampleDriver {
+ fn drop(&mut self) {
+ dev_dbg!(self.pdev.as_ref(), "Remove Rust PCI driver sample.\n");
+ }
+}
+
+kernel::module_pci_driver! {
+ type: SampleDriver,
+ name: "rust_driver_pci",
+ author: "Danilo Krummrich",
+ description: "Rust PCI driver",
+ license: "GPL v2",
+}
diff --git a/samples/rust/rust_driver_platform.rs b/samples/rust/rust_driver_platform.rs
new file mode 100644
index 000000000000..8120609e2940
--- /dev/null
+++ b/samples/rust/rust_driver_platform.rs
@@ -0,0 +1,49 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Rust Platform driver sample.
+
+use kernel::{c_str, of, platform, prelude::*};
+
+struct SampleDriver {
+ pdev: platform::Device,
+}
+
+struct Info(u32);
+
+kernel::of_device_table!(
+ OF_TABLE,
+ MODULE_OF_TABLE,
+ <SampleDriver as platform::Driver>::IdInfo,
+ [(of::DeviceId::new(c_str!("test,rust-device")), Info(42))]
+);
+
+impl platform::Driver for SampleDriver {
+ type IdInfo = Info;
+ const OF_ID_TABLE: Option<of::IdTable<Self::IdInfo>> = Some(&OF_TABLE);
+
+ fn probe(pdev: &mut platform::Device, info: Option<&Self::IdInfo>) -> Result<Pin<KBox<Self>>> {
+ dev_dbg!(pdev.as_ref(), "Probe Rust Platform driver sample.\n");
+
+ if let Some(info) = info {
+ dev_info!(pdev.as_ref(), "Probed with info: '{}'.\n", info.0);
+ }
+
+ let drvdata = KBox::new(Self { pdev: pdev.clone() }, GFP_KERNEL)?;
+
+ Ok(drvdata.into())
+ }
+}
+
+impl Drop for SampleDriver {
+ fn drop(&mut self) {
+ dev_dbg!(self.pdev.as_ref(), "Remove Rust Platform driver sample.\n");
+ }
+}
+
+kernel::module_platform_driver! {
+ type: SampleDriver,
+ name: "rust_driver_platform",
+ author: "Danilo Krummrich",
+ description: "Rust Platform driver",
+ license: "GPL v2",
+}
diff --git a/samples/rust/rust_misc_device.rs b/samples/rust/rust_misc_device.rs
new file mode 100644
index 000000000000..40ad7266c225
--- /dev/null
+++ b/samples/rust/rust_misc_device.rs
@@ -0,0 +1,238 @@
+// SPDX-License-Identifier: GPL-2.0
+
+// Copyright (C) 2024 Google LLC.
+
+//! Rust misc device sample.
+
+/// Below is an example userspace C program that exercises this sample's functionality.
+///
+/// ```c
+/// #include <stdio.h>
+/// #include <stdlib.h>
+/// #include <errno.h>
+/// #include <fcntl.h>
+/// #include <unistd.h>
+/// #include <sys/ioctl.h>
+///
+/// #define RUST_MISC_DEV_FAIL _IO('|', 0)
+/// #define RUST_MISC_DEV_HELLO _IO('|', 0x80)
+/// #define RUST_MISC_DEV_GET_VALUE _IOR('|', 0x81, int)
+/// #define RUST_MISC_DEV_SET_VALUE _IOW('|', 0x82, int)
+///
+/// int main() {
+/// int value, new_value;
+/// int fd, ret;
+///
+/// // Open the device file
+/// printf("Opening /dev/rust-misc-device for reading and writing\n");
+/// fd = open("/dev/rust-misc-device", O_RDWR);
+/// if (fd < 0) {
+/// perror("open");
+/// return errno;
+/// }
+///
+/// // Make call into driver to say "hello"
+/// printf("Calling Hello\n");
+/// ret = ioctl(fd, RUST_MISC_DEV_HELLO, NULL);
+/// if (ret < 0) {
+/// perror("ioctl: Failed to call into Hello");
+/// close(fd);
+/// return errno;
+/// }
+///
+/// // Get initial value
+/// printf("Fetching initial value\n");
+/// ret = ioctl(fd, RUST_MISC_DEV_GET_VALUE, &value);
+/// if (ret < 0) {
+/// perror("ioctl: Failed to fetch the initial value");
+/// close(fd);
+/// return errno;
+/// }
+///
+/// value++;
+///
+/// // Set value to something different
+/// printf("Submitting new value (%d)\n", value);
+/// ret = ioctl(fd, RUST_MISC_DEV_SET_VALUE, &value);
+/// if (ret < 0) {
+/// perror("ioctl: Failed to submit new value");
+/// close(fd);
+/// return errno;
+/// }
+///
+/// // Ensure new value was applied
+/// printf("Fetching new value\n");
+/// ret = ioctl(fd, RUST_MISC_DEV_GET_VALUE, &new_value);
+/// if (ret < 0) {
+/// perror("ioctl: Failed to fetch the new value");
+/// close(fd);
+/// return errno;
+/// }
+///
+/// if (value != new_value) {
+/// printf("Failed: Committed and retrieved values are different (%d - %d)\n", value, new_value);
+/// close(fd);
+/// return -1;
+/// }
+///
+/// // Call the unsuccessful ioctl
+/// printf("Attempting to call in to an non-existent IOCTL\n");
+/// ret = ioctl(fd, RUST_MISC_DEV_FAIL, NULL);
+/// if (ret < 0) {
+/// perror("ioctl: Succeeded to fail - this was expected");
+/// } else {
+/// printf("ioctl: Failed to fail\n");
+/// close(fd);
+/// return -1;
+/// }
+///
+/// // Close the device file
+/// printf("Closing /dev/rust-misc-device\n");
+/// close(fd);
+///
+/// printf("Success\n");
+/// return 0;
+/// }
+/// ```
+use core::pin::Pin;
+
+use kernel::{
+ c_str,
+ device::Device,
+ fs::File,
+ ioctl::{_IO, _IOC_SIZE, _IOR, _IOW},
+ miscdevice::{MiscDevice, MiscDeviceOptions, MiscDeviceRegistration},
+ new_mutex,
+ prelude::*,
+ sync::Mutex,
+ types::ARef,
+ uaccess::{UserSlice, UserSliceReader, UserSliceWriter},
+};
+
+const RUST_MISC_DEV_HELLO: u32 = _IO('|' as u32, 0x80);
+const RUST_MISC_DEV_GET_VALUE: u32 = _IOR::<i32>('|' as u32, 0x81);
+const RUST_MISC_DEV_SET_VALUE: u32 = _IOW::<i32>('|' as u32, 0x82);
+
+module! {
+ type: RustMiscDeviceModule,
+ name: "rust_misc_device",
+ author: "Lee Jones",
+ description: "Rust misc device sample",
+ license: "GPL",
+}
+
+#[pin_data]
+struct RustMiscDeviceModule {
+ #[pin]
+ _miscdev: MiscDeviceRegistration<RustMiscDevice>,
+}
+
+impl kernel::InPlaceModule for RustMiscDeviceModule {
+ fn init(_module: &'static ThisModule) -> impl PinInit<Self, Error> {
+ pr_info!("Initialising Rust Misc Device Sample\n");
+
+ let options = MiscDeviceOptions {
+ name: c_str!("rust-misc-device"),
+ };
+
+ try_pin_init!(Self {
+ _miscdev <- MiscDeviceRegistration::register(options),
+ })
+ }
+}
+
+struct Inner {
+ value: i32,
+}
+
+#[pin_data(PinnedDrop)]
+struct RustMiscDevice {
+ #[pin]
+ inner: Mutex<Inner>,
+ dev: ARef<Device>,
+}
+
+#[vtable]
+impl MiscDevice for RustMiscDevice {
+ type Ptr = Pin<KBox<Self>>;
+
+ fn open(_file: &File, misc: &MiscDeviceRegistration<Self>) -> Result<Pin<KBox<Self>>> {
+ let dev = ARef::from(misc.device());
+
+ dev_info!(dev, "Opening Rust Misc Device Sample\n");
+
+ KBox::try_pin_init(
+ try_pin_init! {
+ RustMiscDevice {
+ inner <- new_mutex!( Inner{ value: 0_i32 } ),
+ dev: dev,
+ }
+ },
+ GFP_KERNEL,
+ )
+ }
+
+ fn ioctl(me: Pin<&RustMiscDevice>, _file: &File, cmd: u32, arg: usize) -> Result<isize> {
+ dev_info!(me.dev, "IOCTLing Rust Misc Device Sample\n");
+
+ let size = _IOC_SIZE(cmd);
+
+ match cmd {
+ RUST_MISC_DEV_GET_VALUE => me.get_value(UserSlice::new(arg, size).writer())?,
+ RUST_MISC_DEV_SET_VALUE => me.set_value(UserSlice::new(arg, size).reader())?,
+ RUST_MISC_DEV_HELLO => me.hello()?,
+ _ => {
+ dev_err!(me.dev, "-> IOCTL not recognised: {}\n", cmd);
+ return Err(ENOTTY);
+ }
+ };
+
+ Ok(0)
+ }
+}
+
+#[pinned_drop]
+impl PinnedDrop for RustMiscDevice {
+ fn drop(self: Pin<&mut Self>) {
+ dev_info!(self.dev, "Exiting the Rust Misc Device Sample\n");
+ }
+}
+
+impl RustMiscDevice {
+ fn set_value(&self, mut reader: UserSliceReader) -> Result<isize> {
+ let new_value = reader.read::<i32>()?;
+ let mut guard = self.inner.lock();
+
+ dev_info!(
+ self.dev,
+ "-> Copying data from userspace (value: {})\n",
+ new_value
+ );
+
+ guard.value = new_value;
+ Ok(0)
+ }
+
+ fn get_value(&self, mut writer: UserSliceWriter) -> Result<isize> {
+ let guard = self.inner.lock();
+ let value = guard.value;
+
+ // Free-up the lock and use our locally cached instance from here
+ drop(guard);
+
+ dev_info!(
+ self.dev,
+ "-> Copying data to userspace (value: {})\n",
+ &value
+ );
+
+ writer.write::<i32>(&value)?;
+ Ok(0)
+ }
+
+ fn hello(&self) -> Result<isize> {
+ dev_info!(self.dev, "-> Hello from the Rust Misc Device\n");
+
+ Ok(0)
+ }
+}
diff --git a/samples/rust/rust_print_main.rs b/samples/rust/rust_print_main.rs
index aed90a6feecf..7e8af5f176a3 100644
--- a/samples/rust/rust_print_main.rs
+++ b/samples/rust/rust_print_main.rs
@@ -34,6 +34,24 @@ fn arc_print() -> Result {
// Uses `dbg` to print, will move `c` (for temporary debugging purposes).
dbg!(c);
+ {
+ // `Arc` can be used to delegate dynamic dispatch and the following is an example.
+ // Both `i32` and `&str` implement `Display`. This enables us to express a unified
+ // behaviour, contract or protocol on both `i32` and `&str` into a single `Arc` of
+ // type `Arc<dyn Display>`.
+
+ use core::fmt::Display;
+ fn arc_dyn_print(arc: &Arc<dyn Display>) {
+ pr_info!("Arc<dyn Display> says {arc}");
+ }
+
+ let a_i32_display: Arc<dyn Display> = Arc::new(42i32, GFP_KERNEL)?;
+ let a_str_display: Arc<dyn Display> = a.clone();
+
+ arc_dyn_print(&a_i32_display);
+ arc_dyn_print(&a_str_display);
+ }
+
// Pretty-prints the debug formatting with lower-case hexadecimal integers.
pr_info!("{:#x?}", a);
@@ -83,7 +101,7 @@ impl Drop for RustPrint {
}
mod trace {
- use core::ffi::c_int;
+ use kernel::ffi::c_int;
kernel::declare_trace! {
/// # Safety