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