xref: /haiku/src/add-ons/kernel/bus_managers/virtio/VirtioDevice.cpp (revision e1c4049fed1047bdb957b0529e1921e97ef94770)
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 {
184 	if (count > VIRTIO_VIRTQUEUES_MAX_COUNT || queues == NULL)
185 		return B_BAD_VALUE;
186 
187 	fQueues = new(std::nothrow) VirtioQueue*[count];
188 	if (fQueues == NULL)
189 		return B_NO_MEMORY;
190 
191 	status_t status = B_OK;
192 	fQueueCount = count;
193 	for (size_t index = 0; index < count; index++) {
194 		uint16 size = fController->get_queue_ring_size(fCookie, index);
195 		fQueues[index] = new(std::nothrow) VirtioQueue(this, index, size);
196 		queues[index] = fQueues[index];
197 		status = B_NO_MEMORY;
198 		if (fQueues[index] != NULL)
199 			status = fQueues[index]->InitCheck();
200 		if (status != B_OK) {
201 			_DestroyQueues(index + 1);
202 			return status;
203 		}
204 	}
205 
206 	return B_OK;
207 }
208 
209 
210 void
211 VirtioDevice::FreeQueues()
212 {
213 	if (fQueues != NULL)
214 		_DestroyQueues(fQueueCount);
215 
216 	fController->set_status(fCookie, VIRTIO_CONFIG_STATUS_RESET);
217 	fController->set_status(fCookie, VIRTIO_CONFIG_STATUS_DRIVER);
218 }
219 
220 
221 status_t
222 VirtioDevice::SetupInterrupt(virtio_intr_func configHandler, void *driverCookie)
223 {
224 	fConfigHandler = configHandler;
225 	fDriverCookie = driverCookie;
226 	status_t status = fController->setup_interrupt(fCookie, fQueueCount);
227 	if (status != B_OK)
228 		return status;
229 
230 	// ready to go
231 	fController->set_status(fCookie, VIRTIO_CONFIG_STATUS_DRIVER_OK);
232 
233 	for (size_t index = 0; index < fQueueCount; index++)
234 		fQueues[index]->EnableInterrupt();
235 	return B_OK;
236 }
237 
238 
239 status_t
240 VirtioDevice::FreeInterrupts()
241 {
242 	for (size_t index = 0; index < fQueueCount; index++)
243 		fQueues[index]->DisableInterrupt();
244 
245 	fController->set_status(fCookie, VIRTIO_CONFIG_STATUS_DRIVER);
246 
247 	return fController->free_interrupt(fCookie);
248 }
249 
250 
251 status_t
252 VirtioDevice::SetupQueue(uint16 queueNumber, phys_addr_t physAddr, phys_addr_t phyAvail,
253 	phys_addr_t phyUsed)
254 {
255 	return fController->setup_queue(fCookie, queueNumber, physAddr, phyAvail, phyUsed);
256 }
257 
258 
259 void
260 VirtioDevice::NotifyQueue(uint16 queueNumber)
261 {
262 	fController->notify_queue(fCookie, queueNumber);
263 }
264 
265 
266 status_t
267 VirtioDevice::QueueInterrupt(uint16 queueNumber)
268 {
269 	if (queueNumber != INT16_MAX) {
270 		if (queueNumber >= fQueueCount)
271 			return B_BAD_VALUE;
272 		return fQueues[queueNumber]->Interrupt();
273 	}
274 
275 	status_t status = B_OK;
276 	for (uint16 i = 0; i < fQueueCount; i++) {
277 		status = fQueues[i]->Interrupt();
278 		if (status != B_OK)
279 			break;
280 	}
281 
282 	return status;
283 }
284 
285 
286 status_t
287 VirtioDevice::ConfigInterrupt()
288 {
289 	if (fConfigHandler != NULL)
290 		fConfigHandler(fDriverCookie);
291 	return B_OK;
292 }
293 
294 
295 void
296 VirtioDevice::_DestroyQueues(size_t count)
297 {
298 	for (size_t i = 0; i < count; i++) {
299 		delete fQueues[i];
300 	}
301 	delete[] fQueues;
302 	fQueues = NULL;
303 }
304 
305 
306 void
307 VirtioDevice::_DumpFeatures(const char* title, uint64 features,
308 	const char* (*get_feature_name)(uint64))
309 {
310 	char features_string[512] = "";
311 	for (uint32 i = 0; i < 64; i++) {
312 		uint64 feature = features & (1ULL << i);
313 		if (feature == 0)
314 			continue;
315 		const char* name = virtio_get_feature_name(feature);
316 		if (name == NULL)
317 			name = get_feature_name(feature);
318 		if (name != NULL) {
319 			strlcat(features_string, "[", sizeof(features_string));
320 			strlcat(features_string, name, sizeof(features_string));
321 			strlcat(features_string, "] ", sizeof(features_string));
322 		}
323 	}
324 	TRACE("%s: %s\n", title, features_string);
325 }
326