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
10const char *
11virtio_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
29const char *
30virtio_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
55VirtioDevice::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
94VirtioDevice::~VirtioDevice()
95{
96	if (fQueues != NULL) {
97		_DestroyQueues(fQueueCount);
98	}
99	fController->set_status(fCookie, VIRTIO_CONFIG_STATUS_RESET);
100}
101
102
103status_t
104VirtioDevice::InitCheck()
105{
106	return fStatus;
107}
108
109
110status_t
111VirtioDevice::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
156status_t
157VirtioDevice::ClearFeature(uint64 feature)
158{
159	fFeatures &= ~feature;
160	return fController->write_guest_features(fCookie, fFeatures);
161}
162
163
164status_t
165VirtioDevice::ReadDeviceConfig(uint8 offset, void* buffer, size_t bufferSize)
166{
167	return fController->read_device_config(fCookie, offset, buffer,
168		bufferSize);
169}
170
171
172status_t
173VirtioDevice::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
181status_t
182VirtioDevice::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
210void
211VirtioDevice::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
221status_t
222VirtioDevice::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
239status_t
240VirtioDevice::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
251status_t
252VirtioDevice::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
259void
260VirtioDevice::NotifyQueue(uint16 queueNumber)
261{
262	fController->notify_queue(fCookie, queueNumber);
263}
264
265
266status_t
267VirtioDevice::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
286status_t
287VirtioDevice::ConfigInterrupt()
288{
289	if (fConfigHandler != NULL)
290		fConfigHandler(fDriverCookie);
291	return B_OK;
292}
293
294
295void
296VirtioDevice::_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
306void
307VirtioDevice::_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