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:
@@ -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
|
||||
};
|
||||
|
||||
@@ -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 ;
|
||||
|
||||
@@ -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
|
||||
};
|
||||
Reference in New Issue
Block a user