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 { 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 210 void 211 VirtioDevice::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 221 status_t 222 VirtioDevice::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 239 status_t 240 VirtioDevice::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 251 status_t 252 VirtioDevice::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 259 void 260 VirtioDevice::NotifyQueue(uint16 queueNumber) 261 { 262 fController->notify_queue(fCookie, queueNumber); 263 } 264 265 266 status_t 267 VirtioDevice::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 286 status_t 287 VirtioDevice::ConfigInterrupt() 288 { 289 if (fConfigHandler != NULL) 290 fConfigHandler(fDriverCookie); 291 return B_OK; 292 } 293 294 295 void 296 VirtioDevice::_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 306 void 307 VirtioDevice::_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