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