From fb3fdcbabdf679cca0006b23fcbcf03c9a610f40 Mon Sep 17 00:00:00 2001 From: Sven Van Asbroeck Date: Tue, 18 May 2021 13:04:52 -0400 Subject: [PATCH 1/3] rust-core/platdev: add of/devicetree matching support Implement the bare minimum required so a Rust `platdev` can match with devices described in the devicetree. Driver and device will match if their `compatible` strings match. The Linux kernel will instantiate one device per match. Ideally, the of table should be const, and created at build time, but we haven't figured out how to accomplish that yet. Signed-off-by: Sven Van Asbroeck --- rust/kernel/bindings_helper.h | 1 + rust/kernel/lib.rs | 1 + rust/kernel/of.rs | 74 +++++++++++++++++++++++++++++++++++ rust/kernel/platdev.rs | 24 +++++++++++- 4 files changed, 98 insertions(+), 2 deletions(-) create mode 100644 rust/kernel/of.rs diff --git a/rust/kernel/bindings_helper.h b/rust/kernel/bindings_helper.h index b66a326fc48ae8..d2cbed8566dea1 100644 --- a/rust/kernel/bindings_helper.h +++ b/rust/kernel/bindings_helper.h @@ -14,6 +14,7 @@ #include #include #include +#include // `bindgen` gets confused at certain things const gfp_t BINDINGS_GFP_KERNEL = GFP_KERNEL; diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 0989ea601314dc..b32db1ce0de182 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -62,6 +62,7 @@ pub mod sysctl; pub mod io_buffer; pub mod iov_iter; +pub mod of; pub mod platdev; mod types; pub mod user_ptr; diff --git a/rust/kernel/of.rs b/rust/kernel/of.rs new file mode 100644 index 00000000000000..10372458af2ab1 --- /dev/null +++ b/rust/kernel/of.rs @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Devicetree and Open Firmware abstractions. +//! +//! C header: [`include/linux/of_*.h`](../../../../include/linux/of_*.h) + +use alloc::boxed::Box; + +use crate::{ + bindings, c_types, + error::{Error, Result}, + types::PointerWrapper, + CStr, +}; + +use core::mem::transmute; + +type InnerTable = Box<[bindings::of_device_id; 2]>; + +/// Wraps a kernel Open Firmware / devicetree match table. +/// +/// Rust drivers may create this structure to match against devices +/// described in the devicetree. +/// +/// The ['PointerWrapper'] trait provides conversion to/from a raw pointer, +/// suitable to be assigned to a `bindings::device_driver::of_match_table`. +/// +/// # Invariants +/// +/// The final array element is always filled with zeros (the default). +pub struct OfMatchTable(InnerTable); + +impl OfMatchTable { + /// Creates a [`OfMatchTable`] from a single `compatible` string. + pub fn new(compatible: &CStr<'static>) -> Result { + let tbl: InnerTable = Box::try_new([ + Self::new_of_device_id(compatible)?, + bindings::of_device_id::default(), + ])?; + // INVARIANTS: we allocated an array with `default()` as its final + // element, therefore that final element will be filled with zeros, + // and the invariant above will hold. + Ok(Self(tbl)) + } + + fn new_of_device_id(compatible: &CStr<'static>) -> Result { + let mut buf = [0_u8; 128]; + if compatible.len() > buf.len() { + return Err(Error::EINVAL); + } + buf.get_mut(..compatible.len()) + .ok_or(Error::EINVAL)? + .copy_from_slice(compatible.as_bytes()); + Ok(bindings::of_device_id { + // SAFETY: re-interpretation from [u8] to [c_types::c_char] of same length is always safe. + compatible: unsafe { transmute::<[u8; 128], [c_types::c_char; 128]>(buf) }, + ..Default::default() + }) + } +} + +impl PointerWrapper for OfMatchTable { + fn into_pointer(self) -> *const c_types::c_void { + // Per the invariant above, the generated pointer points to an + // array of `bindings::of_device_id`, where the final element is + // filled with zeros (the sentinel). Therefore, it's suitable to + // be assigned to `bindings::device_driver::of_match_table`. + self.0.into_pointer() + } + + unsafe fn from_pointer(p: *const c_types::c_void) -> Self { + Self(InnerTable::from_pointer(p)) + } +} diff --git a/rust/kernel/platdev.rs b/rust/kernel/platdev.rs index 7cf35657227426..ff95300a1ae127 100644 --- a/rust/kernel/platdev.rs +++ b/rust/kernel/platdev.rs @@ -9,7 +9,10 @@ use crate::{ bindings, c_types, error::{Error, Result}, - pr_info, CStr, + of::OfMatchTable, + pr_info, + types::PointerWrapper, + CStr, }; use alloc::boxed::Box; use core::{marker::PhantomPinned, pin::Pin}; @@ -18,6 +21,7 @@ use core::{marker::PhantomPinned, pin::Pin}; #[derive(Default)] pub struct Registration { registered: bool, + of_table: Option<*const c_types::c_void>, pdrv: bindings::platform_driver, _pin: PhantomPinned, } @@ -40,6 +44,7 @@ impl Registration { fn register( self: Pin<&mut Self>, name: CStr<'static>, + of_match_table: Option, module: &'static crate::ThisModule, ) -> Result { // SAFETY: We must ensure that we never move out of `this`. @@ -49,6 +54,11 @@ impl Registration { return Err(Error::EINVAL); } this.pdrv.driver.name = name.as_ptr() as *const c_types::c_char; + if let Some(tbl) = of_match_table { + let ptr = tbl.into_pointer(); + this.of_table = Some(ptr); + this.pdrv.driver.of_match_table = ptr.cast(); + } this.pdrv.probe = Some(probe_callback); this.pdrv.remove = Some(remove_callback); // SAFETY: @@ -56,6 +66,10 @@ impl Registration { // - `name` pointer has static lifetime. // - `module.0` lives at least as long as the module. // - `probe()` and `remove()` are static functions. + // - `of_match_table` is either: + // - a raw pointer which lives until after the call to + // `bindings::platform_driver_unregister()`, or + // - null. let ret = unsafe { bindings::__platform_driver_register(&mut this.pdrv, module.0) }; if ret < 0 { return Err(Error::from_kernel_errno(ret)); @@ -69,10 +83,11 @@ impl Registration { /// Returns a pinned heap-allocated representation of the registration. pub fn new_pinned( name: CStr<'static>, + of_match_tbl: Option, module: &'static crate::ThisModule, ) -> Result>> { let mut r = Pin::from(Box::try_new(Self::default())?); - r.as_mut().register(name, module)?; + r.as_mut().register(name, of_match_tbl, module)?; Ok(r) } } @@ -85,5 +100,10 @@ impl Drop for Registration { // safe to call. unsafe { bindings::platform_driver_unregister(&mut self.pdrv) } } + if let Some(ptr) = self.of_table { + // SAFETY: `ptr` came from an `OfMatchTable`. + let tbl = unsafe { OfMatchTable::from_pointer(ptr) }; + drop(tbl); + } } } From 6e4043a7a147491838d2ae8b7e2b045cd315a51e Mon Sep 17 00:00:00 2001 From: Sven Van Asbroeck Date: Tue, 18 May 2021 13:19:45 -0400 Subject: [PATCH 2/3] bcm2835_rng_rust: add devicetree matching Devices described in the devicetree with the following `compatible` string will now match: `brcm,bcm2835-rng` Example devicetree .dts: ```dts &some_bus { rng@7e104000 { compatible = "brcm,bcm2835-rng"; reg = <0x7e104000 0x10>; }; rng@7e104040 { compatible = "brcm,bcm2835-rng"; reg = <0x7e104040 0x10>; }; }; ``` This will instantiate two hardware random-number generator devices. Signed-off-by: Sven Van Asbroeck --- drivers/char/hw_random/bcm2835_rng_rust.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/char/hw_random/bcm2835_rng_rust.rs b/drivers/char/hw_random/bcm2835_rng_rust.rs index 0a89dc0ba87828..8b9e83b013b58a 100644 --- a/drivers/char/hw_random/bcm2835_rng_rust.rs +++ b/drivers/char/hw_random/bcm2835_rng_rust.rs @@ -7,6 +7,7 @@ use alloc::boxed::Box; use core::pin::Pin; +use kernel::of::OfMatchTable; use kernel::prelude::*; use kernel::{cstr, platdev}; @@ -24,7 +25,13 @@ struct RngModule { impl KernelModule for RngModule { fn init() -> Result { - let pdev = platdev::Registration::new_pinned(cstr!("bcm2835-rng-rust"), &THIS_MODULE)?; + let of_match_tbl = OfMatchTable::new(&cstr!("brcm,bcm2835-rng"))?; + + let pdev = platdev::Registration::new_pinned( + cstr!("bcm2835-rng-rust"), + Some(of_match_tbl), + &THIS_MODULE, + )?; Ok(RngModule { _pdev: pdev }) } From c02591f2b011e6544db51b47906a4f3e339735ac Mon Sep 17 00:00:00 2001 From: Sven Van Asbroeck Date: Tue, 18 May 2021 13:26:38 -0400 Subject: [PATCH 3/3] platdev testing: remove devicetrees we forked previously Now that Rust `platdev` drivers can match with devicetree devices using a `compatible` string, we no longer require the forked devicetrees. The standard Raspberry Pi devicetrees will work fine. Signed-off-by: Sven Van Asbroeck --- arch/arm/boot/dts/bcm2835-rpi-zero-w-rust.dts | 156 ------------------ arch/arm/boot/dts/bcm2836-rpi-2-b-rust.dts | 135 --------------- 2 files changed, 291 deletions(-) delete mode 100644 arch/arm/boot/dts/bcm2835-rpi-zero-w-rust.dts delete mode 100644 arch/arm/boot/dts/bcm2836-rpi-2-b-rust.dts diff --git a/arch/arm/boot/dts/bcm2835-rpi-zero-w-rust.dts b/arch/arm/boot/dts/bcm2835-rpi-zero-w-rust.dts deleted file mode 100644 index 18298115c1f5ce..00000000000000 --- a/arch/arm/boot/dts/bcm2835-rpi-zero-w-rust.dts +++ /dev/null @@ -1,156 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (C) 2017 Stefan Wahren - */ - -/dts-v1/; -#include "bcm2835.dtsi" -#include "bcm2835-rpi.dtsi" -#include "bcm283x-rpi-usb-otg.dtsi" - -/ { - compatible = "raspberrypi,model-zero-w", "brcm,bcm2835"; - model = "Raspberry Pi Zero W"; - - bcm2835-rng-rust { - compatible = "brcm,bcm2835-rng"; - status = "okay"; - }; - - memory@0 { - device_type = "memory"; - reg = <0 0x20000000>; - }; - - chosen { - /* 8250 auxiliary UART instead of pl011 */ - stdout-path = "serial1:115200n8"; - }; - - leds { - act { - gpios = <&gpio 47 GPIO_ACTIVE_LOW>; - }; - }; - - wifi_pwrseq: wifi-pwrseq { - compatible = "mmc-pwrseq-simple"; - reset-gpios = <&gpio 41 GPIO_ACTIVE_LOW>; - }; -}; - -&gpio { - /* - * This is based on the official GPU firmware DT blob. - * - * Legend: - * "NC" = not connected (no rail from the SoC) - * "FOO" = GPIO line named "FOO" on the schematic - * "FOO_N" = GPIO line named "FOO" on schematic, active low - */ - gpio-line-names = "ID_SDA", - "ID_SCL", - "SDA1", - "SCL1", - "GPIO_GCLK", - "GPIO5", - "GPIO6", - "SPI_CE1_N", - "SPI_CE0_N", - "SPI_MISO", - "SPI_MOSI", - "SPI_SCLK", - "GPIO12", - "GPIO13", - /* Serial port */ - "TXD0", - "RXD0", - "GPIO16", - "GPIO17", - "GPIO18", - "GPIO19", - "GPIO20", - "GPIO21", - "GPIO22", - "GPIO23", - "GPIO24", - "GPIO25", - "GPIO26", - "GPIO27", - "SDA0", - "SCL0", - "NC", /* GPIO30 */ - "NC", /* GPIO31 */ - "NC", /* GPIO32 */ - "NC", /* GPIO33 */ - "NC", /* GPIO34 */ - "NC", /* GPIO35 */ - "NC", /* GPIO36 */ - "NC", /* GPIO37 */ - "NC", /* GPIO38 */ - "NC", /* GPIO39 */ - "CAM_GPIO1", /* GPIO40 */ - "WL_ON", /* GPIO41 */ - "NC", /* GPIO42 */ - "WIFI_CLK", /* GPIO43 */ - "CAM_GPIO0", /* GPIO44 */ - "BT_ON", /* GPIO45 */ - "HDMI_HPD_N", - "STATUS_LED_N", - /* Used by SD Card */ - "SD_CLK_R", - "SD_CMD_R", - "SD_DATA0_R", - "SD_DATA1_R", - "SD_DATA2_R", - "SD_DATA3_R"; - - pinctrl-0 = <&gpioout &alt0>; -}; - -&hdmi { - hpd-gpios = <&gpio 46 GPIO_ACTIVE_LOW>; - power-domains = <&power RPI_POWER_DOMAIN_HDMI>; - status = "okay"; -}; - -&sdhci { - #address-cells = <1>; - #size-cells = <0>; - pinctrl-names = "default"; - pinctrl-0 = <&emmc_gpio34 &gpclk2_gpio43>; - bus-width = <4>; - mmc-pwrseq = <&wifi_pwrseq>; - non-removable; - status = "okay"; - - brcmf: wifi@1 { - reg = <1>; - compatible = "brcm,bcm4329-fmac"; - }; -}; - -&sdhost { - pinctrl-names = "default"; - pinctrl-0 = <&sdhost_gpio48>; - bus-width = <4>; - status = "okay"; -}; - -&uart0 { - pinctrl-names = "default"; - pinctrl-0 = <&uart0_gpio32 &uart0_ctsrts_gpio30>; - status = "okay"; - - bluetooth { - compatible = "brcm,bcm43438-bt"; - max-speed = <2000000>; - shutdown-gpios = <&gpio 45 GPIO_ACTIVE_HIGH>; - }; -}; - -&uart1 { - pinctrl-names = "default"; - pinctrl-0 = <&uart1_gpio14>; - status = "okay"; -}; diff --git a/arch/arm/boot/dts/bcm2836-rpi-2-b-rust.dts b/arch/arm/boot/dts/bcm2836-rpi-2-b-rust.dts deleted file mode 100644 index 1e3340097ec761..00000000000000 --- a/arch/arm/boot/dts/bcm2836-rpi-2-b-rust.dts +++ /dev/null @@ -1,135 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/dts-v1/; -#include "bcm2836.dtsi" -#include "bcm2836-rpi.dtsi" -#include "bcm283x-rpi-smsc9514.dtsi" -#include "bcm283x-rpi-usb-host.dtsi" - -/ { - compatible = "raspberrypi,2-model-b", "brcm,bcm2836"; - model = "Raspberry Pi 2 Model B"; - - memory@0 { - device_type = "memory"; - reg = <0 0x40000000>; - }; - - bcm2835-rng-rust { - compatible = "brcm,bcm2835-rng"; - status = "okay"; - }; - - leds { - act { - gpios = <&gpio 47 GPIO_ACTIVE_HIGH>; - }; - - pwr { - label = "PWR"; - gpios = <&gpio 35 GPIO_ACTIVE_HIGH>; - default-state = "keep"; - linux,default-trigger = "default-on"; - }; - }; -}; - -&gpio { - /* - * Taken from rpi_SCH_2b_1p2_reduced.pdf and - * the official GPU firmware DT blob. - * - * Legend: - * "NC" = not connected (no rail from the SoC) - * "FOO" = GPIO line named "FOO" on the schematic - * "FOO_N" = GPIO line named "FOO" on schematic, active low - */ - gpio-line-names = "ID_SDA", - "ID_SCL", - "SDA1", - "SCL1", - "GPIO_GCLK", - "GPIO5", - "GPIO6", - "SPI_CE1_N", - "SPI_CE0_N", - "SPI_MISO", - "SPI_MOSI", - "SPI_SCLK", - "GPIO12", - "GPIO13", - /* Serial port */ - "TXD0", - "RXD0", - "GPIO16", - "GPIO17", - "GPIO18", - "GPIO19", - "GPIO20", - "GPIO21", - "GPIO22", - "GPIO23", - "GPIO24", - "GPIO25", - "GPIO26", - "GPIO27", - "SDA0", - "SCL0", - "", /* GPIO30 */ - "LAN_RUN", - "CAM_GPIO1", - "", /* GPIO33 */ - "", /* GPIO34 */ - "PWR_LOW_N", - "", /* GPIO36 */ - "", /* GPIO37 */ - "USB_LIMIT", - "", /* GPIO39 */ - "PWM0_OUT", - "CAM_GPIO0", - "SMPS_SCL", - "SMPS_SDA", - "ETHCLK", - "PWM1_OUT", - "HDMI_HPD_N", - "STATUS_LED", - /* Used by SD Card */ - "SD_CLK_R", - "SD_CMD_R", - "SD_DATA0_R", - "SD_DATA1_R", - "SD_DATA2_R", - "SD_DATA3_R"; - - pinctrl-0 = <&gpioout &alt0 &i2s_alt0>; - - /* I2S interface */ - i2s_alt0: i2s_alt0 { - brcm,pins = <18 19 20 21>; - brcm,function = ; - }; -}; - -&hdmi { - hpd-gpios = <&gpio 46 GPIO_ACTIVE_LOW>; - power-domains = <&power RPI_POWER_DOMAIN_HDMI>; - status = "okay"; -}; - -&pwm { - pinctrl-names = "default"; - pinctrl-0 = <&pwm0_gpio40 &pwm1_gpio45>; - status = "okay"; -}; - -&sdhost { - pinctrl-names = "default"; - pinctrl-0 = <&sdhost_gpio48>; - bus-width = <4>; - status = "okay"; -}; - -&uart0 { - pinctrl-names = "default"; - pinctrl-0 = <&uart0_gpio14>; - status = "okay"; -};