random: add a Virtio RNG module

* The default module is replaced by the Virtio RNG module when found.
* This can have the undesired effect of rendering /dev/urandom slow.
* Tested with the following QEmu command line option:
-device virtio-rng-pci,rng=rng0 -object rng-random,filename=/dev/random,id=rng0
* moved random.h to private/drivers headers.
This commit is contained in:
Jérôme Duval
2013-08-29 18:47:48 +02:00
parent eb5639c43e
commit 8d2bf6953e
9 changed files with 428 additions and 34 deletions
@@ -23,11 +23,9 @@ typedef struct {
typedef struct random_module_info {
driver_module_info info;
status_t (*init)();
void (*uninit)();
status_t (*read)(void *_buffer, size_t *_numBytes);
status_t (*write)(const void *_buffer, size_t *_numBytes);
} random_sim_interface;
status_t (*read)(void* cookie, void *_buffer, size_t *_numBytes);
status_t (*write)(void* cookie, const void *_buffer, size_t *_numBytes);
} random_module_info;
#endif /* _RANDOM_H */
@@ -1,5 +1,6 @@
SubDir HAIKU_TOP src add-ons kernel bus_managers random ;
UsePrivateHeaders drivers ;
UsePrivateKernelHeaders ;
KernelAddon random :
@@ -32,7 +32,8 @@
static mutex sRandomLock;
static random_sim_interface *sRandomModule;
static random_module_info *sRandomModule;
static void *sRandomCookie;
device_manager_info* gDeviceManager;
@@ -73,7 +74,7 @@ random_read(void *cookie, off_t position, void *_buffer, size_t *_numBytes)
TRACE("read(%Ld,, %ld)\n", position, *_numBytes);
MutexLocker locker(&sRandomLock);
return sRandomModule->read(_buffer, _numBytes);
return sRandomModule->read(sRandomCookie, _buffer, _numBytes);
}
@@ -82,7 +83,11 @@ random_write(void *cookie, off_t position, const void *buffer, size_t *_numBytes
{
TRACE("write(%Ld,, %ld)\n", position, *_numBytes);
MutexLocker locker(&sRandomLock);
return sRandomModule->write(buffer, _numBytes);
if (sRandomModule->write == NULL) {
*_numBytes = 0;
return EINVAL;
}
return sRandomModule->write(sRandomCookie, buffer, _numBytes);
}
@@ -244,10 +249,8 @@ random_added_device(device_node *node)
{
CALLED();
void *cookie;
status_t status = gDeviceManager->get_driver(node,
(driver_module_info **)&sRandomModule, &cookie);
(driver_module_info **)&sRandomModule, &sRandomCookie);
return status;
}
@@ -290,24 +290,7 @@ kill_chrand(ch_randgen *randgen)
static status_t
yarrow_rng_init()
{
sRandomEnv = new_chrand(8);
if (sRandomEnv == NULL)
return B_NO_MEMORY;
return B_OK;
}
static void
yarrow_rng_uninit()
{
kill_chrand(sRandomEnv);
}
static status_t
yarrow_rng_read(void *_buffer, size_t *_numBytes)
yarrow_rng_read(void* cookie, void *_buffer, size_t *_numBytes)
{
sRandomCount += *_numBytes;
@@ -333,7 +316,7 @@ yarrow_rng_read(void *_buffer, size_t *_numBytes)
static status_t
yarrow_rng_write(const void *buffer, size_t *_numBytes)
yarrow_rng_write(void* cookie, const void *buffer, size_t *_numBytes)
{
OCTET* data = (OCTET*)buffer;
for (size_t i = 0; i < *_numBytes / sizeof(OCTET); i++) {
@@ -351,7 +334,10 @@ static status_t
yarrow_init_bus(device_node* node, void** bus_cookie)
{
CALLED();
return yarrow_rng_init();
sRandomEnv = new_chrand(8);
if (sRandomEnv == NULL)
return B_NO_MEMORY;
return B_OK;
}
@@ -359,7 +345,7 @@ static void
yarrow_uninit_bus(void* bus_cookie)
{
CALLED();
yarrow_rng_uninit();
kill_chrand(sRandomEnv);
}
@@ -389,8 +375,6 @@ random_module_info gYarrowRandomModule = {
NULL, // rescan
yarrow_bus_removed,
},
yarrow_rng_init,
yarrow_rng_uninit,
yarrow_rng_read,
yarrow_rng_write
};
+1
View File
@@ -8,6 +8,7 @@ if $(HAIKU_ATA_STACK) = 1 {
}
SubInclude HAIKU_TOP src add-ons kernel busses agp_gart ;
SubInclude HAIKU_TOP src add-ons kernel busses random ;
SubInclude HAIKU_TOP src add-ons kernel busses scsi ;
SubInclude HAIKU_TOP src add-ons kernel busses usb ;
SubInclude HAIKU_TOP src add-ons kernel busses virtio ;
+9
View File
@@ -0,0 +1,9 @@
SubDir HAIKU_TOP src add-ons kernel busses random ;
UsePrivateHeaders drivers virtio ;
UsePrivateKernelHeaders ;
KernelAddon virtio_rng :
virtio_rng.cpp
VirtioRNGDevice.cpp
;
@@ -0,0 +1,134 @@
/*
* Copyright 2013, Jérôme Duval, [email protected].
* Distributed under the terms of the MIT License.
*/
#include "VirtioRNGPrivate.h"
#include <new>
#include <stdlib.h>
#include <string.h>
#include <util/AutoLock.h>
const char *
get_feature_name(uint32 feature)
{
switch (feature) {
}
return NULL;
}
VirtioRNGDevice::VirtioRNGDevice(device_node *node)
:
fNode(node),
fVirtio(NULL),
fVirtioDevice(NULL),
fStatus(B_NO_INIT),
fOffset(BUFFER_SIZE)
{
CALLED();
B_INITIALIZE_SPINLOCK(&fInterruptLock);
fInterruptCondition.Init(this, "virtio rng transfer");
get_memory_map(fBuffer, BUFFER_SIZE, &fEntry, 1);
// get the Virtio device from our parent's parent
device_node *parent = gDeviceManager->get_parent_node(node);
device_node *virtioParent = gDeviceManager->get_parent_node(parent);
gDeviceManager->put_node(parent);
gDeviceManager->get_driver(virtioParent, (driver_module_info **)&fVirtio,
(void **)&fVirtioDevice);
gDeviceManager->put_node(virtioParent);
fVirtio->negociate_features(fVirtioDevice,
0, &fFeatures, &get_feature_name);
fStatus = fVirtio->alloc_queues(fVirtioDevice, 1, &fVirtioQueue);
if (fStatus != B_OK) {
ERROR("queue allocation failed (%s)\n", strerror(fStatus));
return;
}
fStatus = fVirtio->setup_interrupt(fVirtioDevice, NULL, this);
if (fStatus != B_OK) {
ERROR("interrupt setup failed (%s)\n", strerror(fStatus));
return;
}
}
VirtioRNGDevice::~VirtioRNGDevice()
{
}
status_t
VirtioRNGDevice::InitCheck()
{
return fStatus;
}
status_t
VirtioRNGDevice::Read(void* _buffer, size_t* _numBytes)
{
CALLED();
if (fOffset >= BUFFER_SIZE) {
{
InterruptsSpinLocker locker(fInterruptLock);
fExpectsInterrupt = true;
fInterruptCondition.Add(&fInterruptConditionEntry);
}
status_t result = fVirtio->queue_request(fVirtioQueue, NULL, &fEntry,
_RequestCallback, this);
if (result != B_OK) {
ERROR("queueing failed (%s)\n", strerror(result));
return result;
}
result = fInterruptConditionEntry.Wait(B_CAN_INTERRUPT);
{
InterruptsSpinLocker locker(fInterruptLock);
fExpectsInterrupt = false;
}
if (result == B_OK) {
fOffset = 0;
} else if (result != B_INTERRUPTED) {
ERROR("request failed (%s)\n", strerror(result));
}
}
if (fOffset < BUFFER_SIZE) {
size_t size = min_c(BUFFER_SIZE - fOffset, *_numBytes);
memcpy(_buffer, fBuffer + fOffset, size);
fOffset += size;
*_numBytes = size;
} else
*_numBytes = 0;
return B_OK;
}
void
VirtioRNGDevice::_RequestCallback(void* driverCookie, void* cookie)
{
VirtioRNGDevice* device = (VirtioRNGDevice*)driverCookie;
device->_RequestInterrupt();
}
void
VirtioRNGDevice::_RequestInterrupt()
{
SpinLocker locker(fInterruptLock);
fInterruptCondition.NotifyAll();
}
@@ -0,0 +1,67 @@
/*
* Copyright 2013, Jérôme Duval, [email protected].
* Distributed under the terms of the MIT License.
*/
#ifndef VIRTIO_RNG_PRIVATE_H
#define VIRTIO_RNG_PRIVATE_H
#include <condition_variable.h>
#include <lock.h>
#include "random.h"
#include <virtio.h>
//#define TRACE_VIRTIO_RNG
#ifdef TRACE_VIRTIO_RNG
# define TRACE(x...) dprintf("virtio_rng: " x)
#else
# define TRACE(x...) ;
#endif
#define ERROR(x...) dprintf("\33[33mvirtio_rng:\33[0m " x)
#define CALLED() TRACE("CALLED %s\n", __PRETTY_FUNCTION__)
extern device_manager_info* gDeviceManager;
extern random_for_controller_interface *gRandom;
#define BUFFER_SIZE 16
class VirtioRNGDevice {
public:
VirtioRNGDevice(device_node* node);
~VirtioRNGDevice();
status_t InitCheck();
status_t Read(void* buffer, size_t* numBytes);
private:
static void _RequestCallback(void* driverCookie,
void *cookie);
void _RequestInterrupt();
device_node* fNode;
virtio_device_interface* fVirtio;
virtio_device* fVirtioDevice;
status_t fStatus;
uint32 fFeatures;
::virtio_queue fVirtioQueue;
physical_entry fEntry;
uint8 fBuffer[BUFFER_SIZE];
uint32 fOffset;
spinlock fInterruptLock;
ConditionVariable fInterruptCondition;
ConditionVariableEntry fInterruptConditionEntry;
bool fExpectsInterrupt;
};
#endif // VIRTIO_RNG_PRIVATE_H
@@ -0,0 +1,197 @@
/*
* Copyright 2013, Jérôme Duval, [email protected].
* Distributed under the terms of the MIT License.
*/
#include "VirtioRNGPrivate.h"
#include <new>
#include <stdlib.h>
#include <string.h>
#define VIRTIO_RNG_CONTROLLER_PRETTY_NAME "Virtio RNG Device"
#define VIRTIO_RNG_DRIVER_MODULE_NAME "busses/random/virtio_rng/driver_v1"
#define VIRTIO_RNG_DEVICE_MODULE_NAME "busses/random/virtio_rng/device_v1"
device_manager_info *gDeviceManager;
random_for_controller_interface *gRandom;
// #pragma mark - Random module interface
static status_t
sim_init_bus(device_node *node, void **_cookie)
{
CALLED();
VirtioRNGDevice *device = new(std::nothrow)
VirtioRNGDevice(node);
if (device == NULL)
return B_NO_MEMORY;
status_t status = device->InitCheck();
if (status < B_OK) {
delete device;
return status;
}
*_cookie = device;
return B_OK;
}
static void
sim_uninit_bus(void *cookie)
{
CALLED();
VirtioRNGDevice *device = (VirtioRNGDevice*)cookie;
delete device;
}
static status_t
sim_read(void* cookie, void *_buffer, size_t *_numBytes)
{
VirtioRNGDevice *device = (VirtioRNGDevice*)cookie;
return device->Read(_buffer, _numBytes);
}
// #pragma mark - Driver module interface
static float
virtio_rng_supports_device(device_node *parent)
{
const char *bus;
uint16 deviceType;
// make sure parent is really the Virtio bus manager
if (gDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false))
return -1;
if (strcmp(bus, "virtio"))
return 0.0;
// check whether it's really a Virtio Entropy Device
if (gDeviceManager->get_attr_uint16(parent, VIRTIO_DEVICE_TYPE_ITEM,
&deviceType, true) != B_OK || deviceType != VIRTIO_DEVICE_ID_ENTROPY)
return 0.0;
TRACE("Virtio RNG device found!\n");
return 0.6f;
}
static status_t
virtio_rng_register_device(device_node *parent)
{
CALLED();
device_attr attrs[] = {
{ NULL }
};
return gDeviceManager->register_node(parent, VIRTIO_RNG_DRIVER_MODULE_NAME,
attrs, NULL, NULL);
}
static status_t
virtio_rng_init_driver(device_node *node, void **_cookie)
{
CALLED();
*_cookie = node;
return B_OK;
}
static status_t
virtio_rng_register_child_devices(void *cookie)
{
CALLED();
device_node *node = (device_node *)cookie;
device_attr attrs[] = {
{ B_DEVICE_FIXED_CHILD, B_STRING_TYPE,
{ string: RANDOM_FOR_CONTROLLER_MODULE_NAME }},
{ B_DEVICE_PRETTY_NAME, B_STRING_TYPE,
{ string: VIRTIO_RNG_CONTROLLER_PRETTY_NAME }},
{ NULL }
};
return gDeviceManager->register_node(node,
VIRTIO_RNG_DEVICE_MODULE_NAME, attrs, NULL, NULL);
}
static status_t
std_ops(int32 op, ...)
{
switch (op) {
case B_MODULE_INIT:
case B_MODULE_UNINIT:
return B_OK;
default:
return B_ERROR;
}
}
static random_module_info sVirtioRNGDeviceInterface = {
{
{
VIRTIO_RNG_DEVICE_MODULE_NAME,
0,
std_ops
},
NULL, // supported devices
NULL, // register node
sim_init_bus,
sim_uninit_bus,
NULL, // register child devices
NULL, // rescan
NULL // bus_removed
},
sim_read,
NULL // write
};
static driver_module_info sVirtioRNGDriver = {
{
VIRTIO_RNG_DRIVER_MODULE_NAME,
0,
std_ops
},
virtio_rng_supports_device,
virtio_rng_register_device,
virtio_rng_init_driver,
NULL, // uninit_driver,
virtio_rng_register_child_devices,
NULL, // rescan
NULL, // device_removed
};
module_dependency module_dependencies[] = {
{ B_DEVICE_MANAGER_MODULE_NAME, (module_info **)&gDeviceManager },
{ RANDOM_FOR_CONTROLLER_MODULE_NAME, (module_info **)&gRandom },
{}
};
module_info *modules[] = {
(module_info *)&sVirtioRNGDriver,
(module_info *)&sVirtioRNGDeviceInterface,
NULL
};