Skip to content

Commit acf78f8

Browse files
author
Sven Van Asbroeck
committed
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 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 <[email protected]>
1 parent 56e7e87 commit acf78f8

File tree

11 files changed

+724
-1
lines changed

11 files changed

+724
-1
lines changed

Diff for: .github/workflows/ci.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -368,7 +368,7 @@ jobs:
368368
- run: make ${{ env.MAKE_ARCH }} ${{ env.MAKE_CROSS_COMPILE }} ${{ env.MAKE_TOOLCHAIN }} ${{ env.MAKE_OUTPUT }} ${{ env.MAKE_SYSROOT }} -j3 CLIPPY=1
369369

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

373373
# Formatting
374374
- run: make rustfmtcheck

Diff for: arch/arm/configs/bcm2835_rust_defconfig

+189
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
# CONFIG_LOCALVERSION_AUTO is not set
2+
CONFIG_SYSVIPC=y
3+
CONFIG_NO_HZ=y
4+
CONFIG_HIGH_RES_TIMERS=y
5+
CONFIG_PREEMPT_VOLUNTARY=y
6+
CONFIG_BSD_PROCESS_ACCT=y
7+
CONFIG_BSD_PROCESS_ACCT_V3=y
8+
CONFIG_LOG_BUF_SHIFT=18
9+
CONFIG_CFS_BANDWIDTH=y
10+
CONFIG_RT_GROUP_SCHED=y
11+
CONFIG_CGROUP_FREEZER=y
12+
CONFIG_CGROUP_DEVICE=y
13+
CONFIG_CGROUP_CPUACCT=y
14+
CONFIG_CGROUP_PERF=y
15+
CONFIG_NAMESPACES=y
16+
CONFIG_SCHED_AUTOGROUP=y
17+
CONFIG_RELAY=y
18+
CONFIG_BLK_DEV_INITRD=y
19+
CONFIG_CC_OPTIMIZE_FOR_SIZE=y
20+
CONFIG_KALLSYMS_ALL=y
21+
CONFIG_EMBEDDED=y
22+
# CONFIG_COMPAT_BRK is not set
23+
CONFIG_PROFILING=y
24+
CONFIG_RUST=y
25+
CONFIG_ARCH_MULTI_V6=y
26+
CONFIG_ARCH_BCM=y
27+
CONFIG_ARCH_BCM2835=y
28+
CONFIG_KEXEC=y
29+
CONFIG_CRASH_DUMP=y
30+
CONFIG_CPU_FREQ=y
31+
CONFIG_CPU_FREQ_STAT=y
32+
CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE=y
33+
CONFIG_CPU_FREQ_GOV_POWERSAVE=y
34+
CONFIG_CPU_FREQ_GOV_USERSPACE=y
35+
CONFIG_CPU_FREQ_GOV_ONDEMAND=y
36+
CONFIG_CPUFREQ_DT=y
37+
CONFIG_ARM_RASPBERRYPI_CPUFREQ=y
38+
CONFIG_VFP=y
39+
# CONFIG_SUSPEND is not set
40+
CONFIG_PM=y
41+
CONFIG_RASPBERRYPI_FIRMWARE=y
42+
CONFIG_JUMP_LABEL=y
43+
CONFIG_MODULES=y
44+
CONFIG_MODULE_UNLOAD=y
45+
# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
46+
CONFIG_KSM=y
47+
CONFIG_CLEANCACHE=y
48+
CONFIG_CMA=y
49+
CONFIG_NET=y
50+
CONFIG_PACKET=y
51+
CONFIG_UNIX=y
52+
CONFIG_INET=y
53+
CONFIG_IP_PNP=y
54+
CONFIG_IP_PNP_DHCP=y
55+
CONFIG_NETWORK_SECMARK=y
56+
CONFIG_NETFILTER=y
57+
CONFIG_BT=y
58+
CONFIG_BT_HCIUART=m
59+
CONFIG_BT_HCIUART_BCM=y
60+
CONFIG_CFG80211=y
61+
CONFIG_MAC80211=y
62+
CONFIG_DEVTMPFS=y
63+
CONFIG_DEVTMPFS_MOUNT=y
64+
# CONFIG_STANDALONE is not set
65+
CONFIG_SCSI=y
66+
CONFIG_BLK_DEV_SD=y
67+
CONFIG_SCSI_CONSTANTS=y
68+
CONFIG_SCSI_SCAN_ASYNC=y
69+
CONFIG_NETDEVICES=y
70+
CONFIG_BCMGENET=y
71+
CONFIG_USB_LAN78XX=y
72+
CONFIG_USB_USBNET=y
73+
CONFIG_USB_NET_SMSC95XX=y
74+
CONFIG_BRCMFMAC=m
75+
CONFIG_ZD1211RW=y
76+
CONFIG_INPUT_EVDEV=y
77+
# CONFIG_LEGACY_PTYS is not set
78+
CONFIG_SERIAL_8250=y
79+
CONFIG_SERIAL_8250_CONSOLE=y
80+
CONFIG_SERIAL_8250_EXTENDED=y
81+
CONFIG_SERIAL_8250_SHARE_IRQ=y
82+
CONFIG_SERIAL_8250_BCM2835AUX=y
83+
CONFIG_SERIAL_AMBA_PL011=y
84+
CONFIG_SERIAL_AMBA_PL011_CONSOLE=y
85+
CONFIG_SERIAL_DEV_BUS=y
86+
CONFIG_TTY_PRINTK=y
87+
CONFIG_HW_RANDOM=y
88+
# CONFIG_HW_RANDOM_BCM2835 is not set
89+
CONFIG_HW_RANDOM_BCM2835_RUST=y
90+
CONFIG_I2C_CHARDEV=y
91+
CONFIG_I2C_BCM2835=y
92+
CONFIG_SPI=y
93+
CONFIG_SPI_BCM2835=y
94+
CONFIG_SPI_BCM2835AUX=y
95+
CONFIG_GPIO_SYSFS=y
96+
CONFIG_SENSORS_RASPBERRYPI_HWMON=m
97+
CONFIG_THERMAL=y
98+
CONFIG_BCM2711_THERMAL=y
99+
CONFIG_BCM2835_THERMAL=y
100+
CONFIG_WATCHDOG=y
101+
CONFIG_BCM2835_WDT=y
102+
CONFIG_MFD_SYSCON=y
103+
CONFIG_REGULATOR=y
104+
CONFIG_REGULATOR_FIXED_VOLTAGE=y
105+
CONFIG_REGULATOR_GPIO=y
106+
CONFIG_MEDIA_SUPPORT=y
107+
CONFIG_DRM=y
108+
CONFIG_DRM_VC4=y
109+
CONFIG_FB_SIMPLE=y
110+
CONFIG_FRAMEBUFFER_CONSOLE=y
111+
CONFIG_SOUND=y
112+
CONFIG_SND=y
113+
CONFIG_SND_SOC=y
114+
CONFIG_SND_BCM2835_SOC_I2S=y
115+
CONFIG_USB=y
116+
CONFIG_USB_OTG=y
117+
CONFIG_USB_STORAGE=y
118+
CONFIG_USB_DWC2=y
119+
CONFIG_NOP_USB_XCEIV=y
120+
CONFIG_USB_GADGET=y
121+
CONFIG_USB_ETH=m
122+
CONFIG_USB_ETH_EEM=y
123+
CONFIG_USB_G_SERIAL=m
124+
CONFIG_MMC=y
125+
CONFIG_MMC_SDHCI=y
126+
CONFIG_MMC_SDHCI_PLTFM=y
127+
CONFIG_MMC_SDHCI_IPROC=y
128+
CONFIG_MMC_BCM2835=y
129+
CONFIG_NEW_LEDS=y
130+
CONFIG_LEDS_CLASS=y
131+
CONFIG_LEDS_GPIO=y
132+
CONFIG_LEDS_TRIGGERS=y
133+
CONFIG_LEDS_TRIGGER_TIMER=y
134+
CONFIG_LEDS_TRIGGER_ONESHOT=y
135+
CONFIG_LEDS_TRIGGER_HEARTBEAT=y
136+
CONFIG_LEDS_TRIGGER_CPU=y
137+
CONFIG_LEDS_TRIGGER_GPIO=y
138+
CONFIG_LEDS_TRIGGER_DEFAULT_ON=y
139+
CONFIG_LEDS_TRIGGER_TRANSIENT=y
140+
CONFIG_LEDS_TRIGGER_CAMERA=y
141+
CONFIG_DMADEVICES=y
142+
CONFIG_DMA_BCM2835=y
143+
CONFIG_STAGING=y
144+
CONFIG_SND_BCM2835=m
145+
CONFIG_VIDEO_BCM2835=m
146+
CONFIG_CLK_RASPBERRYPI=y
147+
CONFIG_MAILBOX=y
148+
CONFIG_BCM2835_MBOX=y
149+
# CONFIG_IOMMU_SUPPORT is not set
150+
CONFIG_RASPBERRYPI_POWER=y
151+
CONFIG_PWM=y
152+
CONFIG_PWM_BCM2835=y
153+
CONFIG_EXT2_FS=y
154+
CONFIG_EXT2_FS_XATTR=y
155+
CONFIG_EXT2_FS_POSIX_ACL=y
156+
CONFIG_EXT3_FS=y
157+
CONFIG_EXT3_FS_POSIX_ACL=y
158+
CONFIG_FANOTIFY=y
159+
CONFIG_MSDOS_FS=y
160+
CONFIG_VFAT_FS=y
161+
CONFIG_TMPFS=y
162+
CONFIG_TMPFS_POSIX_ACL=y
163+
# CONFIG_MISC_FILESYSTEMS is not set
164+
CONFIG_NFS_FS=y
165+
CONFIG_ROOT_NFS=y
166+
CONFIG_NFSD=y
167+
CONFIG_NLS_CODEPAGE_437=y
168+
CONFIG_NLS_ASCII=y
169+
CONFIG_NLS_ISO8859_1=y
170+
CONFIG_NLS_UTF8=y
171+
# CONFIG_XZ_DEC_ARM is not set
172+
# CONFIG_XZ_DEC_ARMTHUMB is not set
173+
CONFIG_DMA_CMA=y
174+
CONFIG_CMA_SIZE_MBYTES=32
175+
CONFIG_PRINTK_TIME=y
176+
CONFIG_BOOT_PRINTK_DELAY=y
177+
CONFIG_DYNAMIC_DEBUG=y
178+
CONFIG_DEBUG_INFO=y
179+
CONFIG_DEBUG_FS=y
180+
CONFIG_KGDB=y
181+
CONFIG_KGDB_KDB=y
182+
CONFIG_DEBUG_MEMORY_INIT=y
183+
CONFIG_FUNCTION_PROFILER=y
184+
CONFIG_STACK_TRACER=y
185+
CONFIG_SCHED_TRACER=y
186+
CONFIG_SAMPLES=y
187+
CONFIG_SAMPLES_RUST=y
188+
CONFIG_STRICT_DEVMEM=y
189+
CONFIG_TEST_KSTRTOX=y

Diff for: drivers/char/hw_random/Kconfig

+14
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,20 @@ config HW_RANDOM_BCM2835
9898

9999
If unsure, say Y.
100100

101+
config HW_RANDOM_BCM2835_RUST
102+
tristate "Rust implementation of Broadcom BCM2835 Random Number Generator"
103+
depends on HAS_RUST && ARCH_BCM2835
104+
select REGMAP_MMIO
105+
help
106+
This driver provides alternative Rust-based kernel-side support
107+
for the Random Number Generator hardware found on the Broadcom
108+
BCM2835 SoC.
109+
110+
To compile this driver as a module, choose M here: the
111+
module will be called bcm2835-rng
112+
113+
If unsure, say N.
114+
101115
config HW_RANDOM_IPROC_RNG200
102116
tristate "Broadcom iProc/STB RNG200 support"
103117
depends on ARCH_BCM_IPROC || ARCH_BCM2835 || ARCH_BRCMSTB

Diff for: drivers/char/hw_random/Makefile

+1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ obj-$(CONFIG_HW_RANDOM_PSERIES) += pseries-rng.o
3131
obj-$(CONFIG_HW_RANDOM_POWERNV) += powernv-rng.o
3232
obj-$(CONFIG_HW_RANDOM_HISI) += hisi-rng.o
3333
obj-$(CONFIG_HW_RANDOM_BCM2835) += bcm2835-rng.o
34+
obj-$(CONFIG_HW_RANDOM_BCM2835_RUST) += bcm2835_rng_rust.o
3435
obj-$(CONFIG_HW_RANDOM_IPROC_RNG200) += iproc-rng200.o
3536
obj-$(CONFIG_HW_RANDOM_ST) += st-rng.o
3637
obj-$(CONFIG_HW_RANDOM_XGENE) += xgene-rng.o

Diff for: drivers/char/hw_random/bcm2835_rng_rust.rs

+120
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
//! Broadcom BCM2835 Random Number Generator support.
4+
5+
#![no_std]
6+
#![feature(allocator_api, global_asm)]
7+
8+
use alloc::{boxed::Box, sync::Arc};
9+
use core::pin::Pin;
10+
use kernel::prelude::*;
11+
use kernel::{
12+
cstr,
13+
file::File,
14+
file_operations::{FileOpener, FileOperations},
15+
io_buffer::IoBufferWriter,
16+
miscdev,
17+
platform_driver::{self, PlatformDevice, PlatformDriver},
18+
regmap::{Regmap, RegmapConfig},
19+
};
20+
21+
module! {
22+
type: RngModule,
23+
name: b"bcm2835_rng_rust",
24+
author: b"Rust for Linux Contributors",
25+
description: b"BCM2835 Random Number Generator (RNG) driver",
26+
license: b"GPL v2",
27+
}
28+
29+
struct SharedState {
30+
regmap: Regmap,
31+
}
32+
33+
impl SharedState {
34+
fn try_new(regmap: Regmap) -> Result<Arc<Self>> {
35+
Ok(Arc::try_new(SharedState { regmap })?)
36+
}
37+
}
38+
39+
struct RngDevice {
40+
state: Arc<SharedState>,
41+
}
42+
43+
impl FileOpener<Arc<SharedState>> for RngDevice {
44+
fn open(state: &Arc<SharedState>) -> Result<Self::Wrapper> {
45+
Ok(Box::try_new(RngDevice {
46+
state: state.clone(),
47+
})?)
48+
}
49+
}
50+
51+
impl FileOperations for RngDevice {
52+
kernel::declare_file_operations!(read);
53+
54+
fn read<T: IoBufferWriter>(&self, _: &File, data: &mut T, offset: u64) -> Result<usize> {
55+
// Succeed if the caller doesn't provide a buffer or if not at the start.
56+
if data.is_empty() || offset != 0 {
57+
return Ok(0);
58+
}
59+
60+
let regmap = &self.state.regmap;
61+
let num_words = regmap.read(RNG_STATUS)? >> 24;
62+
if num_words == 0 {
63+
return Ok(0);
64+
}
65+
data.write(&regmap.read(RNG_DATA)?)?;
66+
Ok(4)
67+
}
68+
}
69+
70+
#[derive(Default)]
71+
struct RngDriver;
72+
73+
// TODO: Issue #260 ("Use Rust type system to make Regmap API safer").
74+
75+
const RNG_CTRL: u32 = 0x0;
76+
const RNG_STATUS: u32 = 0x4;
77+
const RNG_DATA: u32 = 0x8;
78+
79+
// The initial numbers generated are "less random" so will be discarded.
80+
const RNG_WARMUP_COUNT: u32 = 0x40000;
81+
// Enable rng.
82+
const RNG_RBGEN: u32 = 0x1;
83+
84+
impl PlatformDriver for RngDriver {
85+
type DrvData = Pin<Box<miscdev::Registration<Arc<SharedState>>>>;
86+
87+
fn probe(pdev: &mut PlatformDevice) -> Result<Self::DrvData> {
88+
// Create Regmap which maps device registers.
89+
let cfg = RegmapConfig::new(32, 32)
90+
.reg_stride(4)
91+
.max_register(RNG_DATA);
92+
let regmap = Regmap::init_mmio_platform_resource(pdev, 0, &cfg)?;
93+
// Set warm-up count & enable.
94+
regmap.write(RNG_STATUS, RNG_WARMUP_COUNT)?;
95+
regmap.write(RNG_CTRL, RNG_RBGEN)?;
96+
// Register character device so userspace can read out random data.
97+
// TODO: use a `struct hwrng` instead of a `miscdev`.
98+
let state = SharedState::try_new(regmap)?;
99+
let dev = miscdev::Registration::new_pinned::<RngDevice>(cstr!("rust_hwrng"), None, state)?;
100+
Ok(dev)
101+
}
102+
}
103+
104+
struct RngModule {
105+
_pdev: Pin<Box<platform_driver::Registration>>,
106+
}
107+
108+
impl KernelModule for RngModule {
109+
fn init() -> Result<Self> {
110+
let pdev = platform_driver::Registration::new_pinned::<RngDriver>(
111+
cstr!("bcm2835-rng-rust"),
112+
// TODO: this should be an optional list.
113+
// Perhaps use an enum to specify behavioural differences.
114+
cstr!("brcm,bcm2835-rng"),
115+
&THIS_MODULE,
116+
)?;
117+
118+
Ok(RngModule { _pdev: pdev })
119+
}
120+
}

0 commit comments

Comments
 (0)