xref: /haiku/src/add-ons/kernel/bus_managers/virtio/VirtioDevice.cpp (revision 52f7c9389475e19fc21487b38064b4390eeb6fea)
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(uint32 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 	fConfigHandler(NULL),
66 	fDriverCookie(NULL)
67 {
68 	CALLED();
69 	device_node *parent = gDeviceManager->get_parent_node(node);
70 	fStatus = gDeviceManager->get_driver(parent,
71 		(driver_module_info **)&fController, &fCookie);
72 	gDeviceManager->put_node(parent);
73 
74 	if (fStatus != B_OK)
75 		return;
76 
77 	fStatus = gDeviceManager->get_attr_uint16(fNode,
78 		VIRTIO_VRING_ALIGNMENT_ITEM, &fAlignment, true);
79 	if (fStatus != B_OK) {
80 		ERROR("alignment missing\n");
81 		return;
82 	}
83 
84 	fController->set_sim(fCookie, this);
85 
86 	fController->set_status(fCookie, VIRTIO_CONFIG_STATUS_DRIVER);
87 }
88 
89 
90 VirtioDevice::~VirtioDevice()
91 {
92 	if (fQueues != NULL) {
93 		_DestroyQueues(fQueueCount);
94 	}
95 	fController->set_status(fCookie, VIRTIO_CONFIG_STATUS_RESET);
96 }
97 
98 
99 status_t
100 VirtioDevice::InitCheck()
101 {
102 	return fStatus;
103 }
104 
105 
106 status_t
107 VirtioDevice::NegotiateFeatures(uint32 supported, uint32* negotiated,
108 	const char* (*get_feature_name)(uint32))
109 {
110 	fFeatures = 0;
111 	status_t status = fController->read_host_features(fCookie, &fFeatures);
112 	if (status != B_OK)
113 		return status;
114 
115 	_DumpFeatures("read features", fFeatures, get_feature_name);
116 
117 	fFeatures &= supported;
118 
119 	// filter our own features
120 	fFeatures &= (VIRTIO_FEATURE_TRANSPORT_MASK
121 		| VIRTIO_FEATURE_RING_INDIRECT_DESC | VIRTIO_FEATURE_RING_EVENT_IDX);
122 
123 	*negotiated = fFeatures;
124 
125 	_DumpFeatures("negotiated features", fFeatures, get_feature_name);
126 
127 	return fController->write_guest_features(fCookie, fFeatures);
128 }
129 
130 
131 status_t
132 VirtioDevice::ClearFeature(uint32 feature)
133 {
134 	fFeatures &= ~feature;
135 	return fController->write_guest_features(fCookie, fFeatures);
136 }
137 
138 
139 status_t
140 VirtioDevice::ReadDeviceConfig(uint8 offset, void* buffer, size_t bufferSize)
141 {
142 	return fController->read_device_config(fCookie, offset, buffer,
143 		bufferSize);
144 }
145 
146 
147 status_t
148 VirtioDevice::WriteDeviceConfig(uint8 offset, const void* buffer,
149 	size_t bufferSize)
150 {
151 	return fController->write_device_config(fCookie, offset, buffer,
152 		bufferSize);
153 }
154 
155 
156 status_t
157 VirtioDevice::AllocateQueues(size_t count, virtio_queue *queues)
158 {
159 	if (count > VIRTIO_VIRTQUEUES_MAX_COUNT || queues == NULL)
160 		return B_BAD_VALUE;
161 
162 	fQueues = new(std::nothrow) VirtioQueue*[count];
163 	if (fQueues == NULL)
164 		return B_NO_MEMORY;
165 
166 	status_t status = B_OK;
167 	fQueueCount = count;
168 	for (size_t index = 0; index < count; index++) {
169 		uint16 size = fController->get_queue_ring_size(fCookie, index);
170 		fQueues[index] = new(std::nothrow) VirtioQueue(this, index, size);
171 		queues[index] = fQueues[index];
172 		status = B_NO_MEMORY;
173 		if (fQueues[index] != NULL)
174 			status = fQueues[index]->InitCheck();
175 		if (status != B_OK) {
176 			_DestroyQueues(index + 1);
177 			return status;
178 		}
179 	}
180 
181 	return B_OK;
182 }
183 
184 
185 void
186 VirtioDevice::FreeQueues()
187 {
188 	if (fQueues != NULL)
189 		_DestroyQueues(fQueueCount);
190 
191 	fController->set_status(fCookie, VIRTIO_CONFIG_STATUS_RESET);
192 	fController->set_status(fCookie, VIRTIO_CONFIG_STATUS_DRIVER);
193 }
194 
195 
196 status_t
197 VirtioDevice::SetupInterrupt(virtio_intr_func configHandler, void *driverCookie)
198 {
199 	fConfigHandler = configHandler;
200 	fDriverCookie = driverCookie;
201 	status_t status = fController->setup_interrupt(fCookie, fQueueCount);
202 	if (status != B_OK)
203 		return status;
204 
205 	// ready to go
206 	fController->set_status(fCookie, VIRTIO_CONFIG_STATUS_DRIVER_OK);
207 
208 	for (size_t index = 0; index < fQueueCount; index++)
209 		fQueues[index]->EnableInterrupt();
210 	return B_OK;
211 }
212 
213 
214 status_t
215 VirtioDevice::FreeInterrupts()
216 {
217 	for (size_t index = 0; index < fQueueCount; index++)
218 		fQueues[index]->DisableInterrupt();
219 
220 	fController->set_status(fCookie, VIRTIO_CONFIG_STATUS_DRIVER);
221 
222 	return fController->free_interrupt(fCookie);
223 }
224 
225 
226 status_t
227 VirtioDevice::SetupQueue(uint16 queueNumber, phys_addr_t physAddr)
228 {
229 	return fController->setup_queue(fCookie, queueNumber, physAddr);
230 }
231 
232 
233 void
234 VirtioDevice::NotifyQueue(uint16 queueNumber)
235 {
236 	fController->notify_queue(fCookie, queueNumber);
237 }
238 
239 
240 status_t
241 VirtioDevice::QueueInterrupt(uint16 queueNumber)
242 {
243 	if (queueNumber != INT16_MAX) {
244 		if (queueNumber >= fQueueCount)
245 			return B_BAD_VALUE;
246 		return fQueues[queueNumber]->Interrupt();
247 	}
248 
249 	status_t status = B_OK;
250 	for (uint16 i = 0; i < fQueueCount; i++) {
251 		status = fQueues[i]->Interrupt();
252 		if (status != B_OK)
253 			break;
254 	}
255 
256 	return status;
257 }
258 
259 
260 status_t
261 VirtioDevice::ConfigInterrupt()
262 {
263 	if (fConfigHandler != NULL)
264 		fConfigHandler(fDriverCookie);
265 	return B_OK;
266 }
267 
268 
269 void
270 VirtioDevice::_DestroyQueues(size_t count)
271 {
272 	for (size_t i = 0; i < count; i++) {
273 		delete fQueues[i];
274 	}
275 	delete[] fQueues;
276 	fQueues = NULL;
277 }
278 
279 
280 void
281 VirtioDevice::_DumpFeatures(const char* title, uint32 features,
282 	const char* (*get_feature_name)(uint32))
283 {
284 	char features_string[512] = "";
285 	for (uint32 i = 0; i < 32; i++) {
286 		uint32 feature = features & (1 << i);
287 		if (feature == 0)
288 			continue;
289 		const char* name = virtio_get_feature_name(feature);
290 		if (name == NULL)
291 			name = get_feature_name(feature);
292 		if (name != NULL) {
293 			strlcat(features_string, "[", sizeof(features_string));
294 			strlcat(features_string, name, sizeof(features_string));
295 			strlcat(features_string, "] ", sizeof(features_string));
296 		}
297 	}
298 	TRACE("%s: %s\n", title, features_string);
299 }
300