1 /*
2 * Copyright 2013, 2018, Jérôme Duval, jerome.duval@gmail.com.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7 #include "VirtioPrivate.h"
8
9
10 const char *
virtio_get_feature_name(uint64 feature)11 virtio_get_feature_name(uint64 feature)
12 {
13 switch (feature) {
14 case VIRTIO_FEATURE_NOTIFY_ON_EMPTY:
15 return "notify on empty";
16 case VIRTIO_FEATURE_ANY_LAYOUT:
17 return "any layout";
18 case VIRTIO_FEATURE_RING_INDIRECT_DESC:
19 return "ring indirect";
20 case VIRTIO_FEATURE_RING_EVENT_IDX:
21 return "ring event index";
22 case VIRTIO_FEATURE_BAD_FEATURE:
23 return "bad feature";
24 }
25 return NULL;
26 }
27
28
29 const char *
virtio_get_device_type_name(uint16 type)30 virtio_get_device_type_name(uint16 type)
31 {
32 switch (type) {
33 case VIRTIO_DEVICE_ID_NETWORK:
34 return "network";
35 case VIRTIO_DEVICE_ID_BLOCK:
36 return "block";
37 case VIRTIO_DEVICE_ID_CONSOLE:
38 return "console";
39 case VIRTIO_DEVICE_ID_ENTROPY:
40 return "entropy";
41 case VIRTIO_DEVICE_ID_BALLOON:
42 return "balloon";
43 case VIRTIO_DEVICE_ID_IOMEMORY:
44 return "io_memory";
45 case VIRTIO_DEVICE_ID_SCSI:
46 return "scsi";
47 case VIRTIO_DEVICE_ID_9P:
48 return "9p transport";
49 default:
50 return "unknown";
51 }
52 }
53
54
VirtioDevice(device_node * node)55 VirtioDevice::VirtioDevice(device_node *node)
56 :
57 fNode(node),
58 fID(0),
59 fController(NULL),
60 fCookie(NULL),
61 fStatus(B_NO_INIT),
62 fQueues(NULL),
63 fFeatures(0),
64 fAlignment(0),
65 fVirtio1(false),
66 fConfigHandler(NULL),
67 fDriverCookie(NULL)
68 {
69 CALLED();
70 device_node *parent = gDeviceManager->get_parent_node(node);
71 fStatus = gDeviceManager->get_driver(parent,
72 (driver_module_info **)&fController, &fCookie);
73 gDeviceManager->put_node(parent);
74
75 if (fStatus != B_OK)
76 return;
77
78 fStatus = gDeviceManager->get_attr_uint16(fNode,
79 VIRTIO_VRING_ALIGNMENT_ITEM, &fAlignment, true);
80 if (fStatus != B_OK) {
81 ERROR("alignment missing\n");
82 return;
83 }
84 uint8 version = 0;
85 if (gDeviceManager->get_attr_uint8(fNode, VIRTIO_VERSION_ITEM, &version, true) == B_OK)
86 fVirtio1 = version == 1;
87
88 fController->set_sim(fCookie, this);
89
90 fController->set_status(fCookie, VIRTIO_CONFIG_STATUS_DRIVER);
91 }
92
93
~VirtioDevice()94 VirtioDevice::~VirtioDevice()
95 {
96 if (fQueues != NULL) {
97 _DestroyQueues(fQueueCount);
98 }
99 fController->set_status(fCookie, VIRTIO_CONFIG_STATUS_RESET);
100 }
101
102
103 status_t
InitCheck()104 VirtioDevice::InitCheck()
105 {
106 return fStatus;
107 }
108
109
110 status_t
NegotiateFeatures(uint64 supported,uint64 * negotiated,const char * (* get_feature_name)(uint64))111 VirtioDevice::NegotiateFeatures(uint64 supported, uint64* negotiated,
112 const char* (*get_feature_name)(uint64))
113 {
114 fFeatures = 0;
115 status_t status = fController->read_host_features(fCookie, &fFeatures);
116 if (status != B_OK)
117 return status;
118
119 _DumpFeatures("read features", fFeatures, get_feature_name);
120
121 if (fVirtio1) {
122 supported |= VIRTIO_FEATURE_VERSION_1;
123 supported &= ~VIRTIO_FEATURE_NOTIFY_ON_EMPTY;
124 }
125
126 fFeatures &= supported;
127
128 // filter our own features
129 fFeatures &= (VIRTIO_FEATURE_TRANSPORT_MASK
130 | VIRTIO_FEATURE_RING_INDIRECT_DESC | VIRTIO_FEATURE_RING_EVENT_IDX
131 | VIRTIO_FEATURE_VERSION_1);
132
133 _DumpFeatures("negotiated features", fFeatures, get_feature_name);
134
135 status = fController->write_guest_features(fCookie, fFeatures);
136 if (status != B_OK)
137 return status;
138
139 if (fVirtio1) {
140 fController->set_status(fCookie, VIRTIO_CONFIG_STATUS_FEATURES_OK);
141 if ((fController->get_status(fCookie) & VIRTIO_CONFIG_STATUS_FEATURES_OK) == 0) {
142 fController->set_status(fCookie, VIRTIO_CONFIG_STATUS_FAILED);
143 return B_BAD_VALUE;
144 }
145 if ((fFeatures & VIRTIO_FEATURE_VERSION_1) == 0) {
146 fController->set_status(fCookie, VIRTIO_CONFIG_STATUS_FAILED);
147 return B_BAD_VALUE;
148 }
149 }
150 *negotiated = fFeatures;
151
152 return B_OK;
153 }
154
155
156 status_t
ClearFeature(uint64 feature)157 VirtioDevice::ClearFeature(uint64 feature)
158 {
159 fFeatures &= ~feature;
160 return fController->write_guest_features(fCookie, fFeatures);
161 }
162
163
164 status_t
ReadDeviceConfig(uint8 offset,void * buffer,size_t bufferSize)165 VirtioDevice::ReadDeviceConfig(uint8 offset, void* buffer, size_t bufferSize)
166 {
167 return fController->read_device_config(fCookie, offset, buffer,
168 bufferSize);
169 }
170
171
172 status_t
WriteDeviceConfig(uint8 offset,const void * buffer,size_t bufferSize)173 VirtioDevice::WriteDeviceConfig(uint8 offset, const void* buffer,
174 size_t bufferSize)
175 {
176 return fController->write_device_config(fCookie, offset, buffer,
177 bufferSize);
178 }
179
180
181 status_t
AllocateQueues(size_t count,virtio_queue * queues,uint16 * requestedSizes)182 VirtioDevice::AllocateQueues(size_t count, virtio_queue *queues,
183 uint16 *requestedSizes)
184 {
185 if (count > VIRTIO_VIRTQUEUES_MAX_COUNT || queues == NULL)
186 return B_BAD_VALUE;
187
188 fQueues = new(std::nothrow) VirtioQueue*[count];
189 if (fQueues == NULL)
190 return B_NO_MEMORY;
191
192 status_t status = B_OK;
193 fQueueCount = count;
194 for (size_t index = 0; index < count; index++) {
195 uint16 size = fController->get_queue_ring_size(fCookie, index);
196
197 uint16 requestedSize
198 = requestedSizes != NULL ? requestedSizes[index] : 0;
199 if (requestedSize != 0) {
200 if (requestedSize > size)
201 status = B_BUFFER_OVERFLOW;
202 else
203 size = requestedSize;
204 }
205
206 if (status == B_OK) {
207 fQueues[index] = new(std::nothrow) VirtioQueue(this, index, size);
208 queues[index] = fQueues[index];
209 status = B_NO_MEMORY;
210 if (fQueues[index] != NULL)
211 status = fQueues[index]->InitCheck();
212 }
213
214 if (status != B_OK) {
215 _DestroyQueues(index + 1);
216 return status;
217 }
218 }
219
220 return B_OK;
221 }
222
223
224 void
FreeQueues()225 VirtioDevice::FreeQueues()
226 {
227 if (fQueues != NULL)
228 _DestroyQueues(fQueueCount);
229
230 fController->set_status(fCookie, VIRTIO_CONFIG_STATUS_RESET);
231 fController->set_status(fCookie, VIRTIO_CONFIG_STATUS_DRIVER);
232 }
233
234
235 status_t
SetupInterrupt(virtio_intr_func configHandler,void * driverCookie)236 VirtioDevice::SetupInterrupt(virtio_intr_func configHandler, void *driverCookie)
237 {
238 fConfigHandler = configHandler;
239 fDriverCookie = driverCookie;
240 status_t status = fController->setup_interrupt(fCookie, fQueueCount);
241 if (status != B_OK)
242 return status;
243
244 // ready to go
245 fController->set_status(fCookie, VIRTIO_CONFIG_STATUS_DRIVER_OK);
246
247 for (size_t index = 0; index < fQueueCount; index++)
248 fQueues[index]->EnableInterrupt();
249 return B_OK;
250 }
251
252
253 status_t
FreeInterrupts()254 VirtioDevice::FreeInterrupts()
255 {
256 for (size_t index = 0; index < fQueueCount; index++)
257 fQueues[index]->DisableInterrupt();
258
259 fController->set_status(fCookie, VIRTIO_CONFIG_STATUS_DRIVER);
260
261 return fController->free_interrupt(fCookie);
262 }
263
264
265 status_t
SetupQueue(uint16 queueNumber,phys_addr_t physAddr,phys_addr_t phyAvail,phys_addr_t phyUsed)266 VirtioDevice::SetupQueue(uint16 queueNumber, phys_addr_t physAddr, phys_addr_t phyAvail,
267 phys_addr_t phyUsed)
268 {
269 return fController->setup_queue(fCookie, queueNumber, physAddr, phyAvail, phyUsed);
270 }
271
272
273 void
NotifyQueue(uint16 queueNumber)274 VirtioDevice::NotifyQueue(uint16 queueNumber)
275 {
276 fController->notify_queue(fCookie, queueNumber);
277 }
278
279
280 status_t
QueueInterrupt(uint16 queueNumber)281 VirtioDevice::QueueInterrupt(uint16 queueNumber)
282 {
283 if (queueNumber != INT16_MAX) {
284 if (queueNumber >= fQueueCount)
285 return B_BAD_VALUE;
286 return fQueues[queueNumber]->Interrupt();
287 }
288
289 status_t status = B_OK;
290 for (uint16 i = 0; i < fQueueCount; i++) {
291 status = fQueues[i]->Interrupt();
292 if (status != B_OK)
293 break;
294 }
295
296 return status;
297 }
298
299
300 status_t
ConfigInterrupt()301 VirtioDevice::ConfigInterrupt()
302 {
303 if (fConfigHandler != NULL)
304 fConfigHandler(fDriverCookie);
305 return B_OK;
306 }
307
308
309 void
_DestroyQueues(size_t count)310 VirtioDevice::_DestroyQueues(size_t count)
311 {
312 for (size_t i = 0; i < count; i++) {
313 delete fQueues[i];
314 }
315 delete[] fQueues;
316 fQueues = NULL;
317 }
318
319
320 void
_DumpFeatures(const char * title,uint64 features,const char * (* get_feature_name)(uint64))321 VirtioDevice::_DumpFeatures(const char* title, uint64 features,
322 const char* (*get_feature_name)(uint64))
323 {
324 char features_string[512] = "";
325 for (uint32 i = 0; i < 64; i++) {
326 uint64 feature = features & (1ULL << i);
327 if (feature == 0)
328 continue;
329 const char* name = virtio_get_feature_name(feature);
330 if (name == NULL)
331 name = get_feature_name(feature);
332 if (name != NULL) {
333 strlcat(features_string, "[", sizeof(features_string));
334 strlcat(features_string, name, sizeof(features_string));
335 strlcat(features_string, "] ", sizeof(features_string));
336 }
337 }
338 TRACE("%s: %s\n", title, features_string);
339 }
340