diff --git a/src/add-ons/kernel/bus_managers/random/random.h b/headers/private/drivers/random.h similarity index 75% rename from src/add-ons/kernel/bus_managers/random/random.h rename to headers/private/drivers/random.h index 23c9d104e3..cc4d1d5174 100644 --- a/src/add-ons/kernel/bus_managers/random/random.h +++ b/headers/private/drivers/random.h @@ -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 */ diff --git a/src/add-ons/kernel/bus_managers/random/Jamfile b/src/add-ons/kernel/bus_managers/random/Jamfile index f5a33f928e..8772580c54 100644 --- a/src/add-ons/kernel/bus_managers/random/Jamfile +++ b/src/add-ons/kernel/bus_managers/random/Jamfile @@ -1,5 +1,6 @@ SubDir HAIKU_TOP src add-ons kernel bus_managers random ; +UsePrivateHeaders drivers ; UsePrivateKernelHeaders ; KernelAddon random : diff --git a/src/add-ons/kernel/bus_managers/random/driver.cpp b/src/add-ons/kernel/bus_managers/random/driver.cpp index 715f169174..deb937a85d 100644 --- a/src/add-ons/kernel/bus_managers/random/driver.cpp +++ b/src/add-ons/kernel/bus_managers/random/driver.cpp @@ -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; } diff --git a/src/add-ons/kernel/bus_managers/random/yarrow_rng.cpp b/src/add-ons/kernel/bus_managers/random/yarrow_rng.cpp index 6be1acb301..bab1123d12 100644 --- a/src/add-ons/kernel/bus_managers/random/yarrow_rng.cpp +++ b/src/add-ons/kernel/bus_managers/random/yarrow_rng.cpp @@ -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 }; diff --git a/src/add-ons/kernel/busses/Jamfile b/src/add-ons/kernel/busses/Jamfile index 2c9ee48853..74adc1b652 100644 --- a/src/add-ons/kernel/busses/Jamfile +++ b/src/add-ons/kernel/busses/Jamfile @@ -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 ; diff --git a/src/add-ons/kernel/busses/random/Jamfile b/src/add-ons/kernel/busses/random/Jamfile new file mode 100644 index 0000000000..d1d610b9a6 --- /dev/null +++ b/src/add-ons/kernel/busses/random/Jamfile @@ -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 +; diff --git a/src/add-ons/kernel/busses/random/VirtioRNGDevice.cpp b/src/add-ons/kernel/busses/random/VirtioRNGDevice.cpp new file mode 100644 index 0000000000..7aa9ae0838 --- /dev/null +++ b/src/add-ons/kernel/busses/random/VirtioRNGDevice.cpp @@ -0,0 +1,134 @@ +/* + * Copyright 2013, Jérôme Duval, korli@users.berlios.de. + * Distributed under the terms of the MIT License. + */ + + +#include "VirtioRNGPrivate.h" + +#include +#include +#include + +#include + + +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(); +} diff --git a/src/add-ons/kernel/busses/random/VirtioRNGPrivate.h b/src/add-ons/kernel/busses/random/VirtioRNGPrivate.h new file mode 100644 index 0000000000..60a4921ee8 --- /dev/null +++ b/src/add-ons/kernel/busses/random/VirtioRNGPrivate.h @@ -0,0 +1,67 @@ +/* + * Copyright 2013, Jérôme Duval, korli@users.berlios.de. + * Distributed under the terms of the MIT License. + */ +#ifndef VIRTIO_RNG_PRIVATE_H +#define VIRTIO_RNG_PRIVATE_H + + +#include +#include +#include "random.h" +#include + + +//#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 diff --git a/src/add-ons/kernel/busses/random/virtio_rng.cpp b/src/add-ons/kernel/busses/random/virtio_rng.cpp new file mode 100644 index 0000000000..951c4eb149 --- /dev/null +++ b/src/add-ons/kernel/busses/random/virtio_rng.cpp @@ -0,0 +1,197 @@ +/* + * Copyright 2013, Jérôme Duval, korli@users.berlios.de. + * Distributed under the terms of the MIT License. + */ + + +#include "VirtioRNGPrivate.h" + +#include +#include +#include + + +#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 +};