forked from torvalds/linux
-
Notifications
You must be signed in to change notification settings - Fork 438
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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 <TheSven73@gmail.com>
- Loading branch information
Sven Van Asbroeck
committed
May 12, 2021
1 parent
56e7e87
commit d4552cd
Showing
11 changed files
with
724 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(®map.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 }) | ||
} | ||
} |
Oops, something went wrong.