xref: /haiku/src/add-ons/kernel/bus_managers/virtio/VirtioDevice.cpp (revision b84574958d92055d2e33c26e1f2d43d48c9ed50c)
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::ReadDeviceConfig(uint8 offset, void* buffer, size_t bufferSize)
131 {
132 	return fController->read_device_config(fCookie, offset, buffer,
133 		bufferSize);
134 }
135 
136 
137 status_t
138 VirtioDevice::WriteDeviceConfig(uint8 offset, const void* buffer,
139 	size_t bufferSize)
140 {
141 	return fController->write_device_config(fCookie, offset, buffer,
142 		bufferSize);
143 }
144 
145 
146 status_t
147 VirtioDevice::AllocateQueues(size_t count, virtio_queue *queues)
148 {
149 	if (count > VIRTIO_VIRTQUEUES_MAX_COUNT || queues == NULL)
150 		return B_BAD_VALUE;
151 
152 	fQueues = new(std::nothrow) VirtioQueue*[count];
153 	if (fQueues == NULL)
154 		return B_NO_MEMORY;
155 
156 	status_t status = B_OK;
157 	fQueueCount = count;
158 	for (size_t index = 0; index < count; index++) {
159 		uint16 size = fController->get_queue_ring_size(fCookie, index);
160 		fQueues[index] = new(std::nothrow) VirtioQueue(this, index, size);
161 		queues[index] = fQueues[index];
162 		status = B_NO_MEMORY;
163 		if (fQueues[index] != NULL)
164 			status = fQueues[index]->InitCheck();
165 		if (status != B_OK) {
166 			_DestroyQueues(index + 1);
167 			return status;
168 		}
169 	}
170 
171 	return B_OK;
172 }
173 
174 
175 void
176 VirtioDevice::FreeQueues()
177 {
178 	if (fQueues != NULL)
179 		_DestroyQueues(fQueueCount);
180 
181 	fController->set_status(fCookie, VIRTIO_CONFIG_STATUS_RESET);
182 	fController->set_status(fCookie, VIRTIO_CONFIG_STATUS_DRIVER);
183 }
184 
185 
186 status_t
187 VirtioDevice::SetupInterrupt(virtio_intr_func configHandler, void *driverCookie)
188 {
189 	fConfigHandler = configHandler;
190 	fDriverCookie = driverCookie;
191 	status_t status = fController->setup_interrupt(fCookie, fQueueCount);
192 	if (status != B_OK)
193 		return status;
194 
195 	// ready to go
196 	fController->set_status(fCookie, VIRTIO_CONFIG_STATUS_DRIVER_OK);
197 
198 	for (size_t index = 0; index < fQueueCount; index++)
199 		fQueues[index]->EnableInterrupt();
200 	return B_OK;
201 }
202 
203 
204 status_t
205 VirtioDevice::FreeInterrupts()
206 {
207 	for (size_t index = 0; index < fQueueCount; index++)
208 		fQueues[index]->DisableInterrupt();
209 
210 	fController->set_status(fCookie, VIRTIO_CONFIG_STATUS_DRIVER);
211 
212 	return fController->free_interrupt(fCookie);
213 }
214 
215 
216 status_t
217 VirtioDevice::SetupQueue(uint16 queueNumber, phys_addr_t physAddr)
218 {
219 	return fController->setup_queue(fCookie, queueNumber, physAddr);
220 }
221 
222 
223 void
224 VirtioDevice::NotifyQueue(uint16 queueNumber)
225 {
226 	fController->notify_queue(fCookie, queueNumber);
227 }
228 
229 
230 status_t
231 VirtioDevice::QueueInterrupt(uint16 queueNumber)
232 {
233 	if (queueNumber != INT16_MAX) {
234 		if (queueNumber >= fQueueCount)
235 			return B_BAD_VALUE;
236 		return fQueues[queueNumber]->Interrupt();
237 	}
238 
239 	status_t status = B_OK;
240 	for (uint16 i = 0; i < fQueueCount; i++) {
241 		status = fQueues[i]->Interrupt();
242 		if (status != B_OK)
243 			break;
244 	}
245 
246 	return status;
247 }
248 
249 
250 status_t
251 VirtioDevice::ConfigInterrupt()
252 {
253 	if (fConfigHandler != NULL)
254 		fConfigHandler(fDriverCookie);
255 	return B_OK;
256 }
257 
258 
259 void
260 VirtioDevice::_DestroyQueues(size_t count)
261 {
262 	for (size_t i = 0; i < count; i++) {
263 		delete fQueues[i];
264 	}
265 	delete[] fQueues;
266 	fQueues = NULL;
267 }
268 
269 
270 void
271 VirtioDevice::_DumpFeatures(const char* title, uint32 features,
272 	const char* (*get_feature_name)(uint32))
273 {
274 	char features_string[512] = "";
275 	for (uint32 i = 0; i < 32; i++) {
276 		uint32 feature = features & (1 << i);
277 		if (feature == 0)
278 			continue;
279 		const char* name = virtio_get_feature_name(feature);
280 		if (name == NULL)
281 			name = get_feature_name(feature);
282 		if (name != NULL) {
283 			strlcat(features_string, "[", sizeof(features_string));
284 			strlcat(features_string, name, sizeof(features_string));
285 			strlcat(features_string, "] ", sizeof(features_string));
286 		}
287 	}
288 	TRACE("%s: %s\n", title, features_string);
289 }
290