8000 WIP: Simple Rust driver that touches real hardware by TheSven73 · Pull Request #254 · Rust-for-Linux/linux · GitHub
[go: up one dir, main page]

Skip to content

WIP: Simple Rust driver that touches real hardware #254

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
WIP: Simple Rust driver that touches real hardware
Proof-of-concept of a `bcm2835-rng` Rust driver. This is the hardware
random-number generator present on Raspberry Pi Zero(W), Classic,
Two, and Three. It's a convenient starting point because:
- it's ubiquitous: a Pi Zero can be purchased for $10
- it has QEMU Support (-M raspi2)
- it's very simple: just 0x10 bytes of register space

The hwrng is exposed as a Rust `miscdev` named `rust_hwrng`. Reading
its devnode will produce up to 4 random bytes at a time:
pi@raspberrypi:~$ hexdump -C /dev/rust_hwrng
00000000  ef 9c 19 8a                                       |....|
00000004

Tested on a real Raspberry Pi Zero-W, and QEMU (-M raspi2).

Consider this to be a "pencil outline": most of the new Rust abstractions
I've introduced here are clunky, inelegant and incomplete - my Rust is very
poor. But I'm sure that collective wisdom can improve them. The `unsafe`
sections need careful review too.

Rust abstractions/infrastructure were introduced for the following kernel concepts:
- `struct platform_device` / `struct platform_driver`
- per-device driver data
- `struct regmap`

How to run on QEMU
==================
Download a Raspbian image. I used `2021-03-04-raspios-buster-armhf-lite.img`.
It will consist of two partitions. Discover their offsets using:
```sh
$ fdisk -l 2021-03-04-raspios-buster-armhf-lite.img
Device                                    Boot  Start     End Sectors  Size Id Type
2021-03-04-raspios-buster-armhf-lite.img1        8192  532479  524288  256M  c W95 FAT32 (LBA)
2021-03-04-raspios-buster-armhf-lite.img2      532480 3645439 3112960  1.5G 83 Linux
```

Mount the second partition on your PC: (note how the offset is multiplied by 512)
```sh
$ mount -o
10000
 loop,offset=$((512*532480)) 2021-03-04-raspios-buster-armhf-lite.img /mnt
Comment out everything in /etc/ld.so.preload - otherwise the Raspbian rootfs cannot support
a mainline kernel:
$ vi /etc/ld.so.preload # comment everything out
$ umount /mnt
```

Build the kernel for arm 32-bit:
```sh
$ make bcm2835_defconfig # defconfig modded so `bcm2835-rng` binds to Rust
$ make zImage dtbs modules
```

Start QEMU:
```sh
  # to boot mainline, make sure that /etc/ld.so.preload is commented out
  # in the Raspbian image.
qemu-system-arm \
    -M raspi2 \
    -append "rw earlyprintk loglevel=8 console=ttyAMA0,115200 dwc_otg.lpm_enable=0 root=/dev/mmcblk0p2 rootwait" \
    -cpu arm1176 \
    -dtb bcm2836-rpi-2-b.dts \
    -hda ./2021-03-04-raspios-buster-armhf-lite.img \
    -kernel zImage \
    -m 1G \
    -smp 4 \
    -nographic \
;
```

How to run on a Raspberry Pi Zero(W)
====================================
Follow the instructions for QEMU above. Deploy the Raspbian image to SD card.
Copy zImage and bcm2835-rpi-zero-w.dtb to Raspbian's first (boot) partition:
```
	zImage			-> boot partition: kernel.img
	bcm2835-rpi-zero-w.dtb	-> boot partition: bcm2708-rpi-0-w.dtb
```
If you'd like wifi to keep working, also copy the kernel modules you built to
Raspbian's second partition:
```sh
$ make modules_install INSTALL_MOD_PATH=<somewhere>
$ cp -rfa <somewhere> <Raspbian Partition> # should end up in /lib/modules/5.12.0-rc4+/
```

Signed-off-by: Sven Van Asbroeck <TheSven73@gmail.com>
  • Loading branch information
Sven Van Asbroeck committed May 12, 2021
commit d4552cdb02ee5ac0995d76607ea6263a102cb825
2 changes: 1 addition & 1 deletion .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -368,7 +368,7 @@ jobs:
- run: make ${{ env.MAKE_ARCH }} ${{ env.MAKE_CROSS_COMPILE }} ${{ env.MAKE_TOOLCHAIN }} ${{ env.MAKE_OUTPUT }} ${{ env.MAKE_SYSROOT }} -j3 CLIPPY=1

# Docs
- run: make ${{ env.MAKE_ARCH }} ${{ env.MAKE_CROSS_COMPILE }} ${{ env.MAKE_TOOLCHAIN }} ${{ env.MAKE_OUTPUT }} ${{ env.MAKE_SYSROOT }} -j3 rustdoc
#- run: make ${{ env.MAKE_ARCH }} ${{ env.MAKE_CROSS_COMPILE }} ${{ env.MAKE_TOOLCHAIN }} ${{ env.MAKE_OUTPUT }} ${{ env.MAKE_SYSROOT }} -j3 rustdoc

# Formatting
- run: make rustfmtcheck
189 changes: 189 additions & 0 deletions arch/arm/configs/bcm2835_rust_defconfig
10000
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
# CONFIG_LOCALVERSION_AUTO is not set
CONFIG_SYSVIPC=y
CONFIG_NO_HZ=y
CONFIG_HIGH_RES_TIMERS=y
CONFIG_PREEMPT_VOLUNTARY=y
CONFIG_BSD_PROCESS_ACCT=y
CONFIG_BSD_PROCESS_ACCT_V3=y
CONFIG_LOG_BUF_SHIFT=18
CONFIG_CFS_BANDWIDTH=y
CONFIG_RT_GROUP_SCHED=y
CONFIG_CGROUP_FREEZER=y
CONFIG_CGROUP_DEVICE=y
CONFIG_CGROUP_CPUACCT=y
CONFIG_CGROUP_PERF=y
CONFIG_NAMESPACES=y
CONFIG_SCHED_AUTOGROUP=y
CONFIG_RELAY=y
CONFIG_BLK_DEV_INITRD=y
CONFIG_CC_OPTIMIZE_FOR_SIZE=y
CONFIG_KALLSYMS_ALL=y
CONFIG_EMBEDDED=y
# CONFIG_COMPAT_BRK is not set
CONFIG_PROFILING=y
CONFIG_RUST=y
CONFIG_ARCH_MULTI_V6=y
CONFIG_ARCH_BCM=y
CONFIG_ARCH_BCM2835=y
CONFIG_KEXEC=y
CONFIG_CRASH_DUMP=y
CONFIG_CPU_FREQ=y
CONFIG_CPU_FREQ_STAT=y
CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE=y
CONFIG_CPU_FREQ_GOV_POWERSAVE=y
CONFIG_CPU_FREQ_GOV_USERSPACE=y
CONFIG_CPU_FREQ_GOV_ONDEMAND=y
CONFIG_CPUFREQ_DT=y
CONFIG_ARM_RASPBERRYPI_CPUFREQ=y
CONFIG_VFP=y
# CONFIG_SUSPEND is not set
CONFIG_PM=y
CONFIG_RASPBERRYPI_FIRMWARE=y
CONFIG_JUMP_LABEL=y
CONFIG_MODULES=y
CONFIG_MODULE_UNLOAD=y
# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
CONFIG_KSM=y
CONFIG_CLEANCACHE=y
CONFIG_CMA=y
CONFIG_NET=y
CONFIG_PACKET=y
CONFIG_UNIX=y
CONFIG_INET=y
CONFIG_IP_PNP=y
CONFIG_IP_PNP_DHCP=y
CONFIG_NETWORK_SECMARK=y
CONFIG_NETFILTER=y
CONFIG_BT=y
CONFIG_BT_HCIUART=m
CONFIG_BT_HCIUART_BCM=y
CONFIG_CFG80211=y
CONFIG_MAC80211=y
CONFIG_DEVTMPFS=y
CONFIG_DEVTMPFS_MOUNT=y
# CONFIG_STANDALONE is not set
CONFIG_SCSI=y
CONFIG_BLK_DEV_SD=y
CONFIG_SCSI_CONSTANTS=y
CONFIG_SCSI_SCAN_ASYNC=y
CONFIG_NETDEVICES=y
CONFIG_BCMGENET=y
CONFIG_USB_LAN78XX=y
CONFIG_USB_USBNET=y
CONFIG_USB_NET_SMSC95XX=y
CONFIG_BRCMFMAC=m
CONFIG_ZD1211RW=y
CONFIG_INPUT_EVDEV=y
# CONFIG_LEGACY_PTYS is not set
CONFIG_SERIAL_8250=y
CONFIG_SERIAL_8250_CONSOLE=y
CONFIG_SERIAL_8250_EXTENDED=y
CONFIG_SERIAL_8250_SHARE_IRQ=y
CONFIG_SERIAL_8250_BCM2835AUX=y
CONFIG_SERIAL_AMBA_PL011=y
CONFIG_SERIAL_AMBA_PL011_CONSOLE=y
CONFIG_SERIAL_DEV_BUS=y
CONFIG_TTY_PRINTK=y
CONFIG_HW_RANDOM=y
# CONFIG_HW_RANDOM_BCM2835 is not set
CONFIG_HW_RANDOM_BCM2835_RUST=y
CONFIG_I2C_CHARDEV=y
CONFIG_I2C_BCM2835=y
CONFIG_SPI=y
CONFIG_SPI_BCM2835=y
CONFIG_SPI_BCM2835AUX=y
CONFIG_GPIO_SYSFS=y
CONFIG_SENSORS_RASPBERRYPI_HWMON=m
CONFIG_THERMAL=y
CONFIG_BCM2711_THERMAL=y
CONFIG_BCM2835_THERMAL=y
CONFIG_WATCHDOG=y
CONFIG_BCM2835_WDT=y
CONFIG_MFD_SYSCON=y
CONFIG_REGULATOR=y
CONFIG_REGULATOR_FIXED_VOLTAGE=y
CONFIG_REGULATOR_GPIO=y
CONFIG_MEDIA_SUPPORT=y
CONFIG_DRM=y
CONFIG_DRM_VC4=y
CONFIG_FB_SIMPLE=y
CONFIG_FRAMEBUFFER_CONSOLE=y
CONFIG_SOUND=y
CONFIG_SND=y
CONFIG_SND_SOC=y
CONFIG_SND_BCM2835_SOC_I2S=y
CONFIG_USB=y
CONFIG_USB_OTG=y
CONFIG_USB_STORAGE=y
CONFIG_USB_DWC2=y
CONFIG_NOP_USB_XCEIV=y
CONFIG_USB_GADGET=y
CONFIG_USB_ETH=m
CONFIG_USB_ETH_EEM=y
CONFIG_USB_G_SERIAL=m
CONFIG_MMC=y
CONFIG_MMC_SDHCI=y
CONFIG_MMC_SDHCI_PLTFM=y
CONFIG_MMC_SDHCI_IPROC=y
CONFIG_MMC_BCM2835=y
CONFIG_NEW_LEDS=y
CONFIG_LEDS_CLASS=y
CONFIG_LEDS_GPIO=y
CONFIG_LEDS_TRIGGERS=y
CONFIG_LEDS_TRIGGER_TIMER=y
CONFIG_LEDS_TRIGGER_ONESHOT=y
CONFIG_LEDS_TRIGGER_HEARTBEAT=y
CONFIG_LEDS_TRIGGER_CPU=y
CONFIG_LEDS_TRIGGER_GPIO=y
CONFIG_LEDS_TRIGGER_DEFAULT_ON=y
CONFIG_LEDS_TRIGGER_TRANSIENT=y
CONFIG_LEDS_TRIGGER_CAMERA=y
CONFIG_DMADEVICES=y
CONFIG_DMA_BCM2835=y
CONFIG_STAGING=y
CONFIG_SND_BCM2835=m
CONFIG_VIDEO_BCM2835=m
CONFIG_CLK_RASPBERRYPI=y
CONFIG_MAILBOX=y
CONFIG_BCM2835_MBOX=y
# CONFIG_IOMMU_SUPPORT is not set
CONFIG_RASPBERRYPI_POWER=y
CONFIG_PWM=y
CONFIG_PWM_BCM2835=y
CONFIG_EXT2_FS=y
CONFIG_EXT2_FS_XATTR=y
CONFIG_EXT2_FS_POSIX_ACL=y
CONFIG_EXT3_FS=y
CONFIG_EXT3_FS_POSIX_ACL=y
CONFIG_FANOTIFY=y
CONFIG_MSDOS_FS=y
CONFIG_VFAT_FS=y
CONFIG_TMPFS=y
CONFIG_TMPFS_POSIX_ACL=y
# CONFIG_MISC_FILESYSTEMS is not set
CONFIG_NFS_FS=y
CONFIG_ROOT_NFS=y
CONFIG_NFSD=y
CONFIG_NLS_CODEPAGE_437=y
CONFIG_NLS_ASCII=y
CONFIG_NLS_ISO8859_1=y
CONFIG_NLS_UTF8=y
# CONFIG_XZ_DEC_ARM is not set
# CONFIG_XZ_DEC_ARMTHUMB is not set
CONFIG_DMA_CMA=y
CONFIG_CMA_SIZE_MBYTES=32
CONFIG_PRINTK_TIME=y
CONFIG_BOOT_PRINTK_DELAY=y
CONFIG_DYNAMIC_DEBUG=y
CONFIG_DEBUG_INFO=y
CONFIG_DEBUG_FS=y
CONFIG_KGDB=y
CONFIG_KGDB_KDB=y
CONFIG_DEBUG_MEMORY_INIT=y
CONFIG_FUNCTION_PROFILER=y
CONFIG_STACK_TRACER=y
CONFIG_SCHED_TRACER=y
CONFIG_SAMPLES=y
CONFIG_SAMPLES_RUST=y
CONFIG_STRICT_DEVMEM=y
CONFIG_TEST_KSTRTOX=y
14 changes: 14 additions & 0 deletions drivers/char/hw_random/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,20 @@ config HW_RANDOM_BCM2835

If unsure, say Y.

config HW_RANDOM_BCM2835_RUST
tristate "Rust implementation of Broadcom BCM2835 Random Number Generator"
depends on HAS_RUST && ARCH_BCM2835
select REGMAP_MMIO
help
This driver provides alternative Rust-based kernel-side support
for the Random Number Generator hardware found on the Broadcom
BCM2835 SoC.

To compile this driver as a module, choose M here: the
module will be called bcm2835_rng_rust

If unsure, say N.

config HW_RANDOM_IPROC_RNG200
tristate "Broadcom iProc/STB RNG200 support"
depends on ARCH_BCM_IPROC || ARCH_BCM2835 || ARCH_BRCMSTB
Expand Down
1 change: 1 addition & 0 deletions drivers/char/hw_random/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ obj-$(CONFIG_HW_RANDOM_PSERIES) += pseries-rng.o
obj-$(CONFIG_HW_RANDOM_POWERNV) += powernv-rng.o
obj-$(CONFIG_HW_RANDOM_HISI) += hisi-rng.o
obj-$(CONFIG_HW_RANDOM_BCM2835) += bcm2835-rng.o
obj-$(CONFIG_HW_RANDOM_BCM2835_RUST) += bcm2835_rng_rust.o
obj-$(CONFIG_HW_RANDOM_IPROC_RNG200) += iproc-rng200.o
obj-$(CONFIG_HW_RANDOM_ST) += st-rng.o
obj-$(CONFIG_HW_RANDOM_XGENE) += xgene-rng.o
Expand Down
120 changes: 120 additions & 0 deletions drivers/char/hw_random/bcm2835_rng_rust.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
// SPDX-License-Identifier: GPL-2.0

//! Broadcom BCM2835 Random Number Generator support.

#![no_std]
#![feature(allocator_api, global_asm)]

use alloc::{boxed::Box, sync::Arc};
use core::pin::Pin;
use kernel::prelude::*;
use kernel::{
cstr,
file::File,
file_operations::{FileOpener, FileOperations},
io_buffer::IoBufferWriter,
miscdev,
platform_driver::{self, PlatformDevice, PlatformDriver},
regmap::{Regmap, RegmapConfig},
};

module! {
type: RngModule,
name: b"bcm2835_rng_rust",
author: b"Rust for Linux Contributors",
description: b"BCM2835 Random Number Generator (RNG) driver",
license: b"GPL v2",
}

struct SharedState {
regmap: Regmap,
}

impl SharedState {
fn try_new(regmap: Regmap) -> Result<Arc<Self>> {
Ok(Arc::try_new(SharedState { regmap })?)
}
}

struct RngDevice {
state: Arc<SharedState>,
}

impl FileOpener<Arc<SharedState>> for RngDevice {
fn open(state: &Arc<SharedState>) -> Result<Self::Wrapper> {
Ok(Box::try_new(RngDevice {
state: state.clone(),
})?)
}
}

impl FileOperations for RngDevice {
kernel::declare_file_operations!(read);

fn read<T: IoBufferWriter>(&self, _: &File, data: &mut T, offset: u64) -> Result<usize> {
// Succeed if the caller doesn't provide a buffer or if not at the start.
if data.is_empty() || offset != 0 {
return Ok(0);
}

let regmap = &self.state.regmap;
let num_words = regmap.read(RNG_STATUS)? >> 24;
if num_words == 0 {
return Ok(0);
}
data.write(&regmap.read(RNG_DATA)?)?;
Ok(4)
}
}

#[derive(Default)]
struct RngDriver;

// TODO: Issue #260 ("Use Rust type system to make Regmap API safer").

const RNG_CTRL: u32 = 0x0;
const RNG_STATUS: u32 = 0x4;
const RNG_DATA: u32 = 0x8;

// The initial numbers generated are "less random" so will be discarded.
const RNG_WARMUP_COUNT: u32 = 0x40000;
// Enable rng.
const RNG_RBGEN: u32 = 0x1;

impl PlatformDriver for RngDriver {
type DrvData = Pin<Box<miscdev::Registration<Arc<SharedState>>>>;

fn probe(pdev: &mut PlatformDevice) -> Result<Self::DrvData> {
// Create Regmap which maps device registers.
let cfg = RegmapConfig::new(32, 32)
.reg_stride(4)
.max_register(RNG_DATA);
let regmap = Regmap::init_mmio_platform_resource(pdev, 0, &cfg)?;
// Set warm-up count & enable.
regmap.write(RNG_STATUS, RNG_WARMUP_COUNT)?;
regmap.write(RNG_CTRL, RNG_RBGEN)?;
// Register character device so userspace can read out random data.
// TODO: use a `struct hwrng` instead of a `miscdev`.
let state = SharedState::try_new(regmap)?;
let dev = miscdev::Registration::new_pinned::<RngDevice>(cstr!("rust_hwrng"), None, state)?;
Ok(dev)
}
}

struct RngModule {
_pdev: Pin<Box<platform_driver::Registration>>,
}

impl KernelModule for RngModule {
fn init() -> Result<Self> {
let pdev = platform_driver::Registration::new_pinned::<RngDriver>(
cstr!("bcm2835-rng-rust"),
// TODO: this should be an optional list.
// Perhaps use an enum to specify behavioural differences.
cstr!("brcm,bcm2835-rng"),
&THIS_MODULE,
)?;

Ok(RngModule { _pdev: pdev })
}
}
Loading
0