1 /*
2 * Copyright 2013, Jérôme Duval, korli@users.berlios.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7 #include "VirtioRNGPrivate.h"
8
9 #include <new>
10 #include <stdlib.h>
11 #include <string.h>
12
13 #include <util/AutoLock.h>
14
15
16 const char *
get_feature_name(uint64 feature)17 get_feature_name(uint64 feature)
18 {
19 switch (feature) {
20 }
21 return NULL;
22 }
23
24
VirtioRNGDevice(device_node * node)25 VirtioRNGDevice::VirtioRNGDevice(device_node *node)
26 :
27 fVirtio(NULL),
28 fVirtioDevice(NULL),
29 fStatus(B_NO_INIT),
30 fExpectsInterrupt(false),
31 fDPCHandle(NULL)
32 {
33 CALLED();
34
35 B_INITIALIZE_SPINLOCK(&fInterruptLock);
36 fInterruptCondition.Init(this, "virtio rng transfer");
37
38 // get the Virtio device from our parent's parent
39 device_node *virtioParent = gDeviceManager->get_parent_node(node);
40
41 gDeviceManager->get_driver(virtioParent, (driver_module_info **)&fVirtio,
42 (void **)&fVirtioDevice);
43 gDeviceManager->put_node(virtioParent);
44
45 fVirtio->negotiate_features(fVirtioDevice,
46 0, &fFeatures, &get_feature_name);
47
48 fStatus = fVirtio->alloc_queues(fVirtioDevice, 1, &fVirtioQueue, NULL);
49 if (fStatus != B_OK) {
50 ERROR("queue allocation failed (%s)\n", strerror(fStatus));
51 return;
52 }
53
54 fStatus = fVirtio->setup_interrupt(fVirtioDevice, NULL, this);
55 if (fStatus != B_OK) {
56 ERROR("interrupt setup failed (%s)\n", strerror(fStatus));
57 return;
58 }
59
60 fStatus = fVirtio->queue_setup_interrupt(fVirtioQueue, _RequestCallback,
61 this);
62 if (fStatus != B_OK) {
63 ERROR("queue interrupt setup failed (%s)\n", strerror(fStatus));
64 return;
65 }
66
67 fStatus = gDPC->new_dpc_queue(&fDPCHandle, "virtio_rng timer",
68 B_LOW_PRIORITY);
69 if (fStatus != B_OK) {
70 ERROR("dpc setup failed (%s)\n", strerror(fStatus));
71 return;
72 }
73
74 if (fStatus == B_OK) {
75 fTimer.user_data = this;
76 fStatus = add_timer(&fTimer, &HandleTimerHook, 300 * 1000 * 1000, B_PERIODIC_TIMER);
77 if (fStatus != B_OK)
78 ERROR("timer setup failed (%s)\n", strerror(fStatus));
79 }
80
81 // trigger also now
82 gDPC->queue_dpc(fDPCHandle, HandleDPC, this);
83 }
84
85
~VirtioRNGDevice()86 VirtioRNGDevice::~VirtioRNGDevice()
87 {
88 cancel_timer(&fTimer);
89 gDPC->delete_dpc_queue(fDPCHandle);
90
91 }
92
93
94 status_t
InitCheck()95 VirtioRNGDevice::InitCheck()
96 {
97 return fStatus;
98 }
99
100
101 status_t
_Enqueue()102 VirtioRNGDevice::_Enqueue()
103 {
104 CALLED();
105
106 uint64 value = 0;
107 physical_entry entry;
108 get_memory_map(&value, sizeof(value), &entry, 1);
109
110 ConditionVariableEntry conditionVariableEntry;
111 {
112 InterruptsSpinLocker locker(fInterruptLock);
113 fExpectsInterrupt = true;
114 fInterruptCondition.Add(&conditionVariableEntry);
115 }
116
117 status_t result = fVirtio->queue_request(fVirtioQueue, NULL, &entry, NULL);
118 if (result != B_OK) {
119 ERROR("queueing failed (%s)\n", strerror(result));
120 return result;
121 }
122
123 result = conditionVariableEntry.Wait(B_CAN_INTERRUPT);
124
125 {
126 InterruptsSpinLocker locker(fInterruptLock);
127 fExpectsInterrupt = false;
128 }
129
130 if (result == B_OK) {
131 if (value != 0) {
132 gRandom->queue_randomness(value);
133 TRACE("enqueue %" B_PRIx64 "\n", value);
134 }
135 } else if (result != B_OK && result != B_INTERRUPTED) {
136 ERROR("request failed (%s)\n", strerror(result));
137 }
138
139 return result;
140 }
141
142
143 void
_RequestCallback(void * driverCookie,void * cookie)144 VirtioRNGDevice::_RequestCallback(void* driverCookie, void* cookie)
145 {
146 VirtioRNGDevice* device = (VirtioRNGDevice*)driverCookie;
147
148 while (device->fVirtio->queue_dequeue(device->fVirtioQueue, NULL, NULL))
149 ;
150
151 device->_RequestInterrupt();
152 }
153
154
155 void
_RequestInterrupt()156 VirtioRNGDevice::_RequestInterrupt()
157 {
158 SpinLocker locker(fInterruptLock);
159 fInterruptCondition.NotifyAll();
160 }
161
162
163 /*static*/ int32
HandleTimerHook(struct timer * timer)164 VirtioRNGDevice::HandleTimerHook(struct timer* timer)
165 {
166 VirtioRNGDevice* device = reinterpret_cast<VirtioRNGDevice*>(timer->user_data);
167
168 gDPC->queue_dpc(device->fDPCHandle, HandleDPC, device);
169 return B_HANDLED_INTERRUPT;
170 }
171
172
173 /*static*/ void
HandleDPC(void * arg)174 VirtioRNGDevice::HandleDPC(void *arg)
175 {
176 VirtioRNGDevice* device = reinterpret_cast<VirtioRNGDevice*>(arg);
177 device->_Enqueue();
178 }
179
180