xref: /haiku/src/add-ons/kernel/bus_managers/virtio/VirtioDevice.cpp (revision 9a6a20d4689307142a7ed26a1437ba47e244e73f)
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 *
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 *
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 
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 
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
104 VirtioDevice::InitCheck()
105 {
106 	return fStatus;
107 }
108 
109 
110 status_t
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
157 VirtioDevice::ClearFeature(uint64 feature)
158 {
159 	fFeatures &= ~feature;
160 	return fController->write_guest_features(fCookie, fFeatures);
161 }
162 
163 
164 status_t
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
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
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
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
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
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
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
274 VirtioDevice::NotifyQueue(uint16 queueNumber)
275 {
276 	fController->notify_queue(fCookie, queueNumber);
277 }
278 
279 
280 status_t
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
301 VirtioDevice::ConfigInterrupt()
302 {
303 	if (fConfigHandler != NULL)
304 		fConfigHandler(fDriverCookie);
305 	return B_OK;
306 }
307 
308 
309 void
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
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