minix/drivers/random/main.c
Lionel Sambuc 6a309697f4 From Ben Gras, add select support on /dev/random.
Change-Id: I47bf4c45285458ecc224df0564edf98afdb2c9d6
2014-07-28 17:05:23 +02:00

273 lines
8.3 KiB
C

/* This file contains the device dependent part of the drivers for the
* following special files:
* /dev/random - random number generator
*/
#include <minix/drivers.h>
#include <minix/chardriver.h>
#include <minix/type.h>
#include "assert.h"
#include "random.h"
#define NR_DEVS 1 /* number of minor devices */
# define RANDOM_DEV 0 /* minor device for /dev/random */
#define KRANDOM_PERIOD 1 /* ticks between krandom calls */
static struct device m_geom[NR_DEVS]; /* base and size of each device */
static dev_t m_device; /* current device */
extern int errno; /* error number for PM calls */
static struct device *r_prepare(dev_t device);
static ssize_t r_read(devminor_t minor, u64_t position, endpoint_t endpt,
cp_grant_id_t grant, size_t size, int flags, cdev_id_t id);
static ssize_t r_write(devminor_t minor, u64_t position, endpoint_t endpt,
cp_grant_id_t grant, size_t size, int flags, cdev_id_t id);
static int r_open(devminor_t minor, int access, endpoint_t user_endpt);
static void r_random(clock_t stamp);
static void r_updatebin(int source, struct k_randomness_bin *rb);
static int r_select(devminor_t, unsigned int, endpoint_t);
/* Entry points to this driver. */
static struct chardriver r_dtab = {
.cdr_open = r_open, /* open device */
.cdr_read = r_read, /* read from device */
.cdr_write = r_write, /* write to device (seeding it) */
.cdr_select = r_select, /* select hook */
.cdr_alarm = r_random /* get randomness from kernel (alarm) */
};
/* select requestor */
static endpoint_t random_select = NONE;
/* Buffer for the /dev/random number generator. */
#define RANDOM_BUF_SIZE 1024
static char random_buf[RANDOM_BUF_SIZE];
/* SEF functions and variables. */
static void sef_local_startup(void);
static int sef_cb_init_fresh(int type, sef_init_info_t *info);
/*===========================================================================*
* main *
*===========================================================================*/
int main(void)
{
/* SEF local startup. */
sef_local_startup();
/* Call the generic receive loop. */
chardriver_task(&r_dtab);
return(OK);
}
/*===========================================================================*
* sef_local_startup *
*===========================================================================*/
static void sef_local_startup()
{
/* Register init callbacks. */
sef_setcb_init_fresh(sef_cb_init_fresh);
sef_setcb_init_lu(sef_cb_init_fresh);
sef_setcb_init_restart(sef_cb_init_fresh);
/* Register live update callbacks. */
sef_setcb_lu_prepare(sef_cb_lu_prepare_always_ready);
sef_setcb_lu_state_isvalid(sef_cb_lu_state_isvalid_standard);
/* Let SEF perform startup. */
sef_startup();
}
/*===========================================================================*
* sef_cb_init_fresh *
*===========================================================================*/
static int sef_cb_init_fresh(int UNUSED(type), sef_init_info_t *UNUSED(info))
{
/* Initialize the random driver. */
static struct k_randomness krandom;
int i, s;
random_init();
r_random(0); /* also set periodic timer */
/* Retrieve first randomness buffer with parameters. */
if (OK != (s=sys_getrandomness(&krandom))) {
printf("RANDOM: sys_getrandomness failed: %d\n", s);
exit(1);
}
/* Do sanity check on parameters. */
if(krandom.random_sources != RANDOM_SOURCES ||
krandom.random_elements != RANDOM_ELEMENTS) {
printf("random: parameters (%d, %d) don't match kernel's (%d, %d)\n",
RANDOM_SOURCES, RANDOM_ELEMENTS,
krandom.random_sources, krandom.random_elements);
exit(1);
}
/* Feed initial batch. */
for(i = 0; i < RANDOM_SOURCES; i++)
r_updatebin(i, &krandom.bin[i]);
/* Announce we are up! */
chardriver_announce();
return(OK);
}
/*===========================================================================*
* r_read *
*===========================================================================*/
static ssize_t r_read(devminor_t minor, u64_t UNUSED(position),
endpoint_t endpt, cp_grant_id_t grant, size_t size, int UNUSED(flags),
cdev_id_t UNUSED(id))
{
/* Read from one of the driver's minor devices. */
size_t offset, chunk;
int r;
if (minor != RANDOM_DEV) return(EIO);
if (!random_isseeded()) return(EAGAIN);
for (offset = 0; offset < size; offset += chunk) {
chunk = MIN(size - offset, RANDOM_BUF_SIZE);
random_getbytes(random_buf, chunk);
r = sys_safecopyto(endpt, grant, offset, (vir_bytes)random_buf, chunk);
if (r != OK) {
printf("random: sys_safecopyto failed for proc %d, grant %d\n",
endpt, grant);
return r;
}
}
return size;
}
/*===========================================================================*
* r_write *
*===========================================================================*/
static ssize_t r_write(devminor_t minor, u64_t UNUSED(position),
endpoint_t endpt, cp_grant_id_t grant, size_t size, int UNUSED(flags),
cdev_id_t UNUSED(id))
{
/* Write to one of the driver's minor devices. */
size_t offset, chunk;
int r;
if (minor != RANDOM_DEV) return(EIO);
for (offset = 0; offset < size; offset += chunk) {
chunk = MIN(size - offset, RANDOM_BUF_SIZE);
r = sys_safecopyfrom(endpt, grant, offset, (vir_bytes)random_buf,
chunk);
if (r != OK) {
printf("random: sys_safecopyfrom failed for proc %d,"
" grant %d\n", endpt, grant);
return r;
}
random_putbytes(random_buf, chunk);
}
return size;
}
/*===========================================================================*
* r_open *
*===========================================================================*/
static int r_open(devminor_t minor, int access, endpoint_t UNUSED(user_endpt))
{
/* Check device number on open.
*/
if (minor < 0 || minor >= NR_DEVS) return(ENXIO);
return(OK);
}
#define UPDATE(binnumber, bp, startitem, elems) { \
rand_t *r; \
int n = elems, item = startitem;\
int high; \
assert(binnumber >= 0 && binnumber < RANDOM_SOURCES); \
assert(item >= 0 && item < RANDOM_ELEMENTS); \
if(n > 0) { \
high = item+n-1; \
assert(high >= item); \
assert(high >= 0 && high < RANDOM_ELEMENTS); \
r = &bp->r_buf[item]; \
random_update(binnumber, r, n); \
} \
}
/*===========================================================================*
* r_updatebin *
*===========================================================================*/
static void r_updatebin(int source, struct k_randomness_bin *rb)
{
int r_next, r_size, r_high;
r_next= rb->r_next;
r_size= rb->r_size;
assert(r_next >= 0 && r_next < RANDOM_ELEMENTS);
assert(r_size >= 0 && r_size <= RANDOM_ELEMENTS);
r_high= r_next+r_size;
if (r_high <= RANDOM_ELEMENTS) {
UPDATE(source, rb, r_next, r_size);
} else {
assert(r_next < RANDOM_ELEMENTS);
UPDATE(source, rb, r_next, RANDOM_ELEMENTS-r_next);
UPDATE(source, rb, 0, r_high-RANDOM_ELEMENTS);
}
return;
}
/*===========================================================================*
* r_random *
*===========================================================================*/
static void r_random(clock_t UNUSED(stamp))
{
/* Fetch random information from the kernel to update /dev/random. */
int s;
static int bin = 0;
static struct k_randomness_bin krandom_bin;
u32_t hi, lo;
rand_t r;
int nextperiod = random_isseeded() ? KRANDOM_PERIOD*500 : KRANDOM_PERIOD;
bin = (bin+1) % RANDOM_SOURCES;
if(sys_getrandom_bin(&krandom_bin, bin) == OK)
r_updatebin(bin, &krandom_bin);
/* Add our own timing source. */
read_tsc(&hi, &lo);
r = lo;
random_update(RND_TIMING, &r, 1);
/* Schedule new alarm for next m_random call. */
if (OK != (s=sys_setalarm(nextperiod, 0)))
printf("RANDOM: sys_setalarm failed: %d\n", s);
}
/*===========================================================================*
* r_select *
*===========================================================================*/
static int r_select(devminor_t minor, unsigned int ops, endpoint_t ep)
{
/* random device is always writable; it's infinitely readable
* once seeded, and doesn't block when it's not, so all operations
* are instantly possible. we ignore CDEV_OP_ERR.
*/
int ready_ops = 0;
if (minor != RANDOM_DEV) return(EIO);
return ops & (CDEV_OP_RD | CDEV_OP_WR);
}