Skip to content

Add Samsung Exynos true random number generator driver #554

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

Draft
wants to merge 8 commits into
base: rust
Choose a base branch
from
Draft
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
13 changes: 13 additions & 0 deletions drivers/char/hw_random/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -479,6 +479,19 @@ config HW_RANDOM_EXYNOS

If unsure, say Y.

config HW_RANDOM_EXYNOS_RUST
tristate "Rust version of Samsung Exynos True Random Number Generator support"
depends on HAS_RUST && ARCH_EXYNOS
default HW_RANDOM
help
This driver provides support for the True Random Number
Generator available in Exynos SoCs.

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

If unsure, say Y.

config HW_RANDOM_OPTEE
tristate "OP-TEE based Random Number Generator support"
depends on OPTEE
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 @@ -15,6 +15,7 @@ obj-$(CONFIG_HW_RANDOM_N2RNG) += n2-rng.o
n2-rng-y := n2-drv.o n2-asm.o
obj-$(CONFIG_HW_RANDOM_VIA) += via-rng.o
obj-$(CONFIG_HW_RANDOM_EXYNOS) += exynos-trng.o
obj-$(CONFIG_HW_RANDOM_EXYNOS_RUST) += exynos_trng_rust.o
obj-$(CONFIG_HW_RANDOM_IXP4XX) += ixp4xx-rng.o
obj-$(CONFIG_HW_RANDOM_OMAP) += omap-rng.o
obj-$(CONFIG_HW_RANDOM_OMAP3_ROM) += omap3-rom-rng.o
Expand Down
8 changes: 4 additions & 4 deletions drivers/char/hw_random/bcm2835_rng_rust.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,15 +52,15 @@ struct RngDriver;
impl PlatformDriver for RngDriver {
type DrvData = Pin<Box<miscdev::Registration<()>>>;

fn probe(device_id: i32) -> Result<Self::DrvData> {
pr_info!("probing discovered hwrng with id {}\n", device_id);
fn probe(pdev: &mut PlatformDevice) -> Result<Self::DrvData> {
pr_info!("probing discovered pdev with name {}\n", pdev.name());
let drv_data =
miscdev::Registration::new_pinned::<RngDevice>(c_str!("rust_hwrng"), None, ())?;
Ok(drv_data)
}

fn remove(device_id: i32, _drv_data: Self::DrvData) -> Result {
pr_info!("removing hwrng with id {}\n", device_id);
fn remove(pdev: &mut PlatformDevice, _drv_data: Self::DrvData) -> Result {
pr_info!("removing pdev with name {}\n", pdev.name());
Ok(())
}
}
Expand Down
150 changes: 150 additions & 0 deletions drivers/char/hw_random/exynos_trng_rust.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
// SPDX-License-Identifier: GPL-2.0

//! RNG driver for Exynos TRNGs
//!
//! Author: Maciej Falkowski <[email protected]>
//!
//! Copyright 2021 (c) Samsung Electronics Software, Inc.
//!
//! Based on the Exynos TRNG driver drivers/char/hw_random/exynos-rng by
//! Łukasz Stelmach <[email protected]>

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

use core::{cmp::min, convert::TryInto, str};

use kernel::{
c_str,
clk::{self, Clk},
declare_hwrng_operations,
device::RawDevice,
hw_random::{self, HwrngOperations},
io_mem::{self, IoMem},
of::ConstOfMatchTable,
platdev::{self, PlatformDevice, PlatformDriver},
prelude::*,
};

module! {
type: ExynosTrngDriverModule,
name: b"exynos_trng_rust",
author: b"Maciej Falkowski <[email protected]>",
description: b"RNG driver for Exynos TRNGs",
license: b"GPL v2",
}

const EXYNOS_TRNG_CLKDIV: usize = 0x0;

const EXYNOS_TRNG_CTRL: usize = 0x20;
const EXYNOS_TRNG_CTRL_RNGEN: u32 = 0x1 << 31; // BIT(31)

const EXYNOS_REG_SIZE: usize = 0x100;
const EXYNOS_TRNG_POST_CTRL: usize = 0x30;
const _EXYNOS_TRNG_ONLINE_CTRL: usize = 0x40;
const _EXYNOS_TRNG_ONLINE_STAT: usize = 0x44;
const _EXYNOS_TRNG_ONLINE_MAXCHI2: usize = 0x48;
const EXYNOS_TRNG_FIFO_CTRL: usize = 0x50;
const EXYNOS_TRNG_FIFO_0: usize = 0x80;
const _EXYNOS_TRNG_FIFO_1: usize = 0x84;
const _EXYNOS_TRNG_FIFO_2: usize = 0x88;
const _EXYNOS_TRNG_FIFO_3: usize = 0x8c;
const _EXYNOS_TRNG_FIFO_4: usize = 0x90;
const _EXYNOS_TRNG_FIFO_5: usize = 0x94;
const _EXYNOS_TRNG_FIFO_6: usize = 0x98;
const _EXYNOS_TRNG_FIFO_7: usize = 0x9c;
const EXYNOS_TRNG_FIFO_LEN: u32 = 8;
const EXYNOS_TRNG_CLOCK_RATE: u32 = 500000;

struct ExynosTrngDevice {
clk: Clk,
mem: IoMem<EXYNOS_REG_SIZE>,
}

impl HwrngOperations for ExynosTrngDevice {
declare_hwrng_operations!(init);

type Data = Box<ExynosTrngDevice>;

fn init(trng: &Self) -> Result {
let sss_rate = trng.clk.get_rate();

// For most TRNG circuits the clock frequency of under 500 kHz
// is safe.
let mut val = sss_rate / (EXYNOS_TRNG_CLOCK_RATE * 2);
if val > 0x7fff {
// dev_err(trng->dev, "clock divider too large: %d", val);
pr_info!("rust_hwrng_init(): clock divider too large: {}\n", val);
return Err(Error::ERANGE);
}

val <<= 1;
trng.mem.writel_relaxed(val, EXYNOS_TRNG_CLKDIV);

// Enable the generator.
val = EXYNOS_TRNG_CTRL_RNGEN;
trng.mem.writel_relaxed(val, EXYNOS_TRNG_CTRL);

// Disable post-processing. /dev/hwrng is supposed to deliver
// unprocessed data.
trng.mem.writel_relaxed(0, EXYNOS_TRNG_POST_CTRL);

Ok(())
}

fn read(trng: &Self, data: &mut [i8], _wait: bool) -> Result<i32> {
let max: u32 = min(data.len().try_into()?, EXYNOS_TRNG_FIFO_LEN * 4);

trng.mem.writel_relaxed(max * 8, EXYNOS_TRNG_FIFO_CTRL);

let _ =
trng.mem
.readl_poll_timeout(EXYNOS_TRNG_FIFO_CTRL, |val| *val == 0, 200, 1000000)?;

io_mem::try_memcpy_fromio(&trng.mem, data, EXYNOS_TRNG_FIFO_0, max.try_into()?)?;

Ok(max as i32)
}
}

struct ExynosTrngDriver;

impl PlatformDriver for ExynosTrngDriver {
type DrvData = Pin<Box<hw_random::Registration<ExynosTrngDevice>>>;

fn probe(pdev: &mut PlatformDevice) -> Result<Self::DrvData> {
let mut clk = clk::devm_clk_get(pdev, c_str!("secss"))?;
clk.prepare_enable()?;

let mem = IoMem::<EXYNOS_REG_SIZE>::try_platform_ioremap_resource(pdev, 0)?;

let exynos_trng = Box::try_new(ExynosTrngDevice { clk, mem })?;

let drv_data = hw_random::Registration::new_pinned(pdev.name(), 0, exynos_trng)?;

Ok(drv_data)
}

fn remove(_pdev: &mut PlatformDevice, _drv_data: Self::DrvData) -> Result {
Ok(())
}
}

struct ExynosTrngDriverModule {
_pdev: Pin<Box<platdev::Registration>>,
}

impl KernelModule for ExynosTrngDriverModule {
fn init(name: &'static CStr, module: &'static ThisModule) -> Result<Self> {
const OF_MATCH_TBL: ConstOfMatchTable<1> =
ConstOfMatchTable::new_const([c_str!("samsung,exynos5250-trng")]);

let pdev = platdev::Registration::new_pinned::<ExynosTrngDriver>(
name,
Some(&OF_MATCH_TBL),
module,
)?;

Ok(ExynosTrngDriverModule { _pdev: pdev })
}
}
87 changes: 87 additions & 0 deletions rust/helpers.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

#include <linux/bug.h>
#include <linux/build_bug.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/ktime.h>
#include <linux/kernel.h>
#include <linux/uaccess.h>
#include <linux/sched/signal.h>
#include <linux/gfp.h>
Expand All @@ -22,6 +26,32 @@ __noreturn void rust_helper_BUG(void)
BUG();
}

void rust_helper_usleep_range(unsigned long min, unsigned long max)
{
usleep_range(min, max);
}

void rust_helper_might_sleep(void)
{
might_sleep();
}

int rust_helper_ktime_compare(const ktime_t cmp1, const ktime_t cmp2)
{
return ktime_compare(cmp1, cmp2);
}

ktime_t rust_helper_ktime_add_us(const ktime_t kt, const u64 usec)
{
return ktime_add_us(kt, usec);
}

int rust_helper_clk_prepare_enable(struct clk *clk)
{
return clk_prepare_enable(clk);
}
EXPORT_SYMBOL_GPL(rust_helper_clk_prepare_enable);

unsigned long rust_helper_copy_from_user(void *to, const void __user *from, unsigned long n)
{
return copy_from_user(to, from, n);
Expand Down Expand Up @@ -95,6 +125,63 @@ void rust_helper_writeq(u64 value, volatile void __iomem *addr)
EXPORT_SYMBOL_GPL(rust_helper_writeq);
#endif

u8 rust_helper_readb_relaxed(const volatile void __iomem *addr)
{
return readb_relaxed(addr);
}
EXPORT_SYMBOL_GPL(rust_helper_readb_relaxed);

u16 rust_helper_readw_relaxed(const volatile void __iomem *addr)
{
return readw_relaxed(addr);
}
EXPORT_SYMBOL_GPL(rust_helper_readw_relaxed);

u32 rust_helper_readl_relaxed(const volatile void __iomem *addr)
{
return readl_relaxed(addr);
}
EXPORT_SYMBOL_GPL(rust_helper_readl_relaxed);

#ifdef CONFIG_64BIT
u64 rust_helper_readq_relaxed(const volatile void __iomem *addr)
{
return readq_relaxed(addr);
}
EXPORT_SYMBOL_GPL(rust_helper_readq_relaxed);
#endif

void rust_helper_writeb_relaxed(u8 value, volatile void __iomem *addr)
{
writeb_relaxed(value, addr);
}
EXPORT_SYMBOL_GPL(rust_helper_writeb_relaxed);

void rust_helper_writew_relaxed(u16 value, volatile void __iomem *addr)
{
writew_relaxed(value, addr);
}
EXPORT_SYMBOL_GPL(rust_helper_writew_relaxed);

void rust_helper_writel_relaxed(u32 value, volatile void __iomem *addr)
{
writel_relaxed(value, addr);
}
EXPORT_SYMBOL_GPL(rust_helper_writel_relaxed);

#ifdef CONFIG_64BIT
void rust_helper_writeq_relaxed(u64 value, volatile void __iomem *addr)
{
writeq_relaxed(value, addr);
}
EXPORT_SYMBOL_GPL(rust_helper_writeq_relaxed);
#endif

void rust_helper_memcpy_fromio(void *to, const volatile void __iomem *from, long count)
{
memcpy_fromio(to, from, count);
}

void rust_helper___spin_lock_init(spinlock_t *lock, const char *name,
struct lock_class_key *key)
{
Expand Down
2 changes: 2 additions & 0 deletions rust/kernel/bindings_helper.h
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
/* SPDX-License-Identifier: GPL-2.0 */

#include <linux/cdev.h>
#include <linux/clk.h>
#include <linux/errname.h>
#include <linux/fs.h>
#include <linux/hw_random.h>
#include <linux/module.h>
#include <linux/random.h>
#include <linux/slab.h>
Expand Down
51 changes: 51 additions & 0 deletions rust/kernel/clk.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// SPDX-License-Identifier: GPL-2.0

//! Clock subsystem
//!
//! C header: [`include/linux/clk.h`](../../../../include/linux/clk.h)

use crate::{
bindings, device,
error::{from_kernel_err_ptr, Error, Result},
str::CStr,
};

/// Represents `struct clk *`
///
/// # Invariants
///
/// The pointer is valid.
pub struct Clk(*mut bindings::clk);

impl Clk {
/// Returns value of rate field of `struct clk`.
pub fn get_rate(&self) -> u32 {
// SAFETY: the pointer is valid by the type invariant.
unsafe { bindings::clk_get_rate((*self).0) }
}

/// Do not use in atomic context.
pub fn prepare_enable(&mut self) -> Result {
// SAFETY: the pointer is valid by the type invariant.
let ret = unsafe { bindings::clk_prepare_enable((*self).0) };
if ret < 0 {
return Err(Error::from_kernel_errno(ret));
}

Ok(())
}
}

/// Lookup and obtain a managed reference to a clock producer.
///
/// * `dev` - device for clock "consumer".
/// * `id` - clock consumer ID.
pub fn devm_clk_get(dev: &impl device::RawDevice, id: &'static CStr) -> Result<Clk> {
// SAFETY:
// - dev is valid by the safety contract.
// - id is valid by the type invariant.
let clk_ptr =
unsafe { from_kernel_err_ptr(bindings::devm_clk_get(dev.raw_device(), id.as_char_ptr())) }?;

Ok(Clk(clk_ptr))
}
11 changes: 3 additions & 8 deletions rust/kernel/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -488,17 +488,12 @@ pub(crate) use from_kernel_result;
/// # use kernel::from_kernel_err_ptr;
/// # use kernel::c_types;
/// # use kernel::bindings;
/// fn devm_platform_ioremap_resource(
/// pub fn devm_platform_ioremap_resource(
/// pdev: &mut PlatformDevice,
/// index: u32,
/// ) -> Result<*mut c_types::c_void> {
/// // SAFETY: FFI call.
/// unsafe {
/// from_kernel_err_ptr(bindings::devm_platform_ioremap_resource(
/// pdev.to_ptr(),
/// index,
/// ))
/// }
/// // SAFETY: `pdev` is valid by the type invariant.
/// unsafe { from_kernel_err_ptr(bindings::devm_platform_ioremap_resource(pdev.0, index)) }
/// }
/// ```
// TODO: remove `dead_code` marker once an in-kernel client is available.
Expand Down
Loading