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(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 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 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 94 VirtioDevice::~VirtioDevice() 95 { 96 if (fQueues != NULL) { 97 _DestroyQueues(fQueueCount); 98 } 99 fController->set_status(fCookie, VIRTIO_CONFIG_STATUS_RESET); 100 } 101 102 103 status_t 104 VirtioDevice::InitCheck() 105 { 106 return fStatus; 107 } 108 109 110 status_t 111 VirtioDevice::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 156 status_t 157 VirtioDevice::ClearFeature(uint64 feature) 158 { 159 fFeatures &= ~feature; 160 return fController->write_guest_features(fCookie, fFeatures); 161 } 162 163 164 status_t 165 VirtioDevice::ReadDeviceConfig(uint8 offset, void* buffer, size_t bufferSize) 166 { 167 return fController->read_device_config(fCookie, offset, buffer, 168 bufferSize); 169 } 170 171 172 status_t 173 VirtioDevice::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 181 status_t 182 VirtioDevice::AllocateQueues(size_t count, virtio_queue *queues, 183 uint16 *requestedSizes) 184 { 185 if (count > VIRTIO_VIRTQUEUES_MAX_COUNT || queues == NULL) 186 return B_BAD_VALUE; 187 188 fQueues = new(std::nothrow) VirtioQueue*[count]; 189 if (fQueues == NULL) 190 return B_NO_MEMORY; 191 192 status_t status = B_OK; 193 fQueueCount = count; 194 for (size_t index = 0; index < count; index++) { 195 uint16 size = fController->get_queue_ring_size(fCookie, index); 196 197 uint16 requestedSize 198 = requestedSizes != NULL ? requestedSizes[index] : 0; 199 if (requestedSize != 0) { 200 if (requestedSize > size) 201 status = B_BUFFER_OVERFLOW; 202 else 203 size = requestedSize; 204 } 205 206 if (status == B_OK) { 207 fQueues[index] = new(std::nothrow) VirtioQueue(this, index, size); 208 queues[index] = fQueues[index]; 209 status = B_NO_MEMORY; 210 if (fQueues[index] != NULL) 211 status = fQueues[index]->InitCheck(); 212 } 213 214 if (status != B_OK) { 215 _DestroyQueues(index + 1); 216 return status; 217 } 218 } 219 220 return B_OK; 221 } 222 223 224 void 225 VirtioDevice::FreeQueues() 226 { 227 if (fQueues != NULL) 228 _DestroyQueues(fQueueCount); 229 230 fController->set_status(fCookie, VIRTIO_CONFIG_STATUS_RESET); 231 fController->set_status(fCookie, VIRTIO_CONFIG_STATUS_DRIVER); 232 } 233 234 235 status_t 236 VirtioDevice::SetupInterrupt(virtio_intr_func configHandler, void *driverCookie) 237 { 238 fConfigHandler = configHandler; 239 fDriverCookie = driverCookie; 240 status_t status = fController->setup_interrupt(fCookie, fQueueCount); 241 if (status != B_OK) 242 return status; 243 244 // ready to go 245 fController->set_status(fCookie, VIRTIO_CONFIG_STATUS_DRIVER_OK); 246 247 for (size_t index = 0; index < fQueueCount; index++) 248 fQueues[index]->EnableInterrupt(); 249 return B_OK; 250 } 251 252 253 status_t 254 VirtioDevice::FreeInterrupts() 255 { 256 for (size_t index = 0; index < fQueueCount; index++) 257 fQueues[index]->DisableInterrupt(); 258 259 fController->set_status(fCookie, VIRTIO_CONFIG_STATUS_DRIVER); 260 261 return fController->free_interrupt(fCookie); 262 } 263 264 265 status_t 266 VirtioDevice::SetupQueue(uint16 queueNumber, phys_addr_t physAddr, phys_addr_t phyAvail, 267 phys_addr_t phyUsed) 268 { 269 return fController->setup_queue(fCookie, queueNumber, physAddr, phyAvail, phyUsed); 270 } 271 272 273 void 274 VirtioDevice::NotifyQueue(uint16 queueNumber) 275 { 276 fController->notify_queue(fCookie, queueNumber); 277 } 278 279 280 status_t 281 VirtioDevice::QueueInterrupt(uint16 queueNumber) 282 { 283 if (queueNumber != INT16_MAX) { 284 if (queueNumber >= fQueueCount) 285 return B_BAD_VALUE; 286 return fQueues[queueNumber]->Interrupt(); 287 } 288 289 status_t status = B_OK; 290 for (uint16 i = 0; i < fQueueCount; i++) { 291 status = fQueues[i]->Interrupt(); 292 if (status != B_OK) 293 break; 294 } 295 296 return status; 297 } 298 299 300 status_t 301 VirtioDevice::ConfigInterrupt() 302 { 303 if (fConfigHandler != NULL) 304 fConfigHandler(fDriverCookie); 305 return B_OK; 306 } 307 308 309 void 310 VirtioDevice::_DestroyQueues(size_t count) 311 { 312 for (size_t i = 0; i < count; i++) { 313 delete fQueues[i]; 314 } 315 delete[] fQueues; 316 fQueues = NULL; 317 } 318 319 320 void 321 VirtioDevice::_DumpFeatures(const char* title, uint64 features, 322 const char* (*get_feature_name)(uint64)) 323 { 324 char features_string[512] = ""; 325 for (uint32 i = 0; i < 64; i++) { 326 uint64 feature = features & (1ULL << i); 327 if (feature == 0) 328 continue; 329 const char* name = virtio_get_feature_name(feature); 330 if (name == NULL) 331 name = get_feature_name(feature); 332 if (name != NULL) { 333 strlcat(features_string, "[", sizeof(features_string)); 334 strlcat(features_string, name, sizeof(features_string)); 335 strlcat(features_string, "] ", sizeof(features_string)); 336 } 337 } 338 TRACE("%s: %s\n", title, features_string); 339 } 340