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_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 fConfigHandler(NULL), 66 fDriverCookie(NULL) 67 { 68 CALLED(); 69 device_node *parent = gDeviceManager->get_parent_node(node); 70 fStatus = gDeviceManager->get_driver(parent, 71 (driver_module_info **)&fController, &fCookie); 72 gDeviceManager->put_node(parent); 73 74 if (fStatus != B_OK) 75 return; 76 77 fStatus = gDeviceManager->get_attr_uint16(fNode, 78 VIRTIO_VRING_ALIGNMENT_ITEM, &fAlignment, true); 79 if (fStatus != B_OK) { 80 ERROR("alignment missing\n"); 81 return; 82 } 83 84 fController->set_sim(fCookie, this); 85 86 fController->set_status(fCookie, VIRTIO_CONFIG_STATUS_DRIVER); 87 } 88 89 90 VirtioDevice::~VirtioDevice() 91 { 92 if (fQueues != NULL) { 93 _DestroyQueues(fQueueCount); 94 } 95 fController->set_status(fCookie, VIRTIO_CONFIG_STATUS_RESET); 96 } 97 98 99 status_t 100 VirtioDevice::InitCheck() 101 { 102 return fStatus; 103 } 104 105 106 status_t 107 VirtioDevice::NegotiateFeatures(uint32 supported, uint32* negotiated, 108 const char* (*get_feature_name)(uint32)) 109 { 110 fFeatures = 0; 111 status_t status = fController->read_host_features(fCookie, &fFeatures); 112 if (status != B_OK) 113 return status; 114 115 _DumpFeatures("read features", fFeatures, get_feature_name); 116 117 fFeatures &= supported; 118 119 // filter our own features 120 fFeatures &= (VIRTIO_FEATURE_TRANSPORT_MASK 121 | VIRTIO_FEATURE_RING_INDIRECT_DESC | VIRTIO_FEATURE_RING_EVENT_IDX); 122 123 *negotiated = fFeatures; 124 125 _DumpFeatures("negotiated features", fFeatures, get_feature_name); 126 127 return fController->write_guest_features(fCookie, fFeatures); 128 } 129 130 131 status_t 132 VirtioDevice::ClearFeature(uint32 feature) 133 { 134 fFeatures &= ~feature; 135 return fController->write_guest_features(fCookie, fFeatures); 136 } 137 138 139 status_t 140 VirtioDevice::ReadDeviceConfig(uint8 offset, void* buffer, size_t bufferSize) 141 { 142 return fController->read_device_config(fCookie, offset, buffer, 143 bufferSize); 144 } 145 146 147 status_t 148 VirtioDevice::WriteDeviceConfig(uint8 offset, const void* buffer, 149 size_t bufferSize) 150 { 151 return fController->write_device_config(fCookie, offset, buffer, 152 bufferSize); 153 } 154 155 156 status_t 157 VirtioDevice::AllocateQueues(size_t count, virtio_queue *queues) 158 { 159 if (count > VIRTIO_VIRTQUEUES_MAX_COUNT || queues == NULL) 160 return B_BAD_VALUE; 161 162 fQueues = new(std::nothrow) VirtioQueue*[count]; 163 if (fQueues == NULL) 164 return B_NO_MEMORY; 165 166 status_t status = B_OK; 167 fQueueCount = count; 168 for (size_t index = 0; index < count; index++) { 169 uint16 size = fController->get_queue_ring_size(fCookie, index); 170 fQueues[index] = new(std::nothrow) VirtioQueue(this, index, size); 171 queues[index] = fQueues[index]; 172 status = B_NO_MEMORY; 173 if (fQueues[index] != NULL) 174 status = fQueues[index]->InitCheck(); 175 if (status != B_OK) { 176 _DestroyQueues(index + 1); 177 return status; 178 } 179 } 180 181 return B_OK; 182 } 183 184 185 void 186 VirtioDevice::FreeQueues() 187 { 188 if (fQueues != NULL) 189 _DestroyQueues(fQueueCount); 190 191 fController->set_status(fCookie, VIRTIO_CONFIG_STATUS_RESET); 192 fController->set_status(fCookie, VIRTIO_CONFIG_STATUS_DRIVER); 193 } 194 195 196 status_t 197 VirtioDevice::SetupInterrupt(virtio_intr_func configHandler, void *driverCookie) 198 { 199 fConfigHandler = configHandler; 200 fDriverCookie = driverCookie; 201 status_t status = fController->setup_interrupt(fCookie, fQueueCount); 202 if (status != B_OK) 203 return status; 204 205 // ready to go 206 fController->set_status(fCookie, VIRTIO_CONFIG_STATUS_DRIVER_OK); 207 208 for (size_t index = 0; index < fQueueCount; index++) 209 fQueues[index]->EnableInterrupt(); 210 return B_OK; 211 } 212 213 214 status_t 215 VirtioDevice::FreeInterrupts() 216 { 217 for (size_t index = 0; index < fQueueCount; index++) 218 fQueues[index]->DisableInterrupt(); 219 220 fController->set_status(fCookie, VIRTIO_CONFIG_STATUS_DRIVER); 221 222 return fController->free_interrupt(fCookie); 223 } 224 225 226 status_t 227 VirtioDevice::SetupQueue(uint16 queueNumber, phys_addr_t physAddr) 228 { 229 return fController->setup_queue(fCookie, queueNumber, physAddr); 230 } 231 232 233 void 234 VirtioDevice::NotifyQueue(uint16 queueNumber) 235 { 236 fController->notify_queue(fCookie, queueNumber); 237 } 238 239 240 status_t 241 VirtioDevice::QueueInterrupt(uint16 queueNumber) 242 { 243 if (queueNumber != INT16_MAX) { 244 if (queueNumber >= fQueueCount) 245 return B_BAD_VALUE; 246 return fQueues[queueNumber]->Interrupt(); 247 } 248 249 status_t status = B_OK; 250 for (uint16 i = 0; i < fQueueCount; i++) { 251 status = fQueues[i]->Interrupt(); 252 if (status != B_OK) 253 break; 254 } 255 256 return status; 257 } 258 259 260 status_t 261 VirtioDevice::ConfigInterrupt() 262 { 263 if (fConfigHandler != NULL) 264 fConfigHandler(fDriverCookie); 265 return B_OK; 266 } 267 268 269 void 270 VirtioDevice::_DestroyQueues(size_t count) 271 { 272 for (size_t i = 0; i < count; i++) { 273 delete fQueues[i]; 274 } 275 delete[] fQueues; 276 fQueues = NULL; 277 } 278 279 280 void 281 VirtioDevice::_DumpFeatures(const char* title, uint32 features, 282 const char* (*get_feature_name)(uint32)) 283 { 284 char features_string[512] = ""; 285 for (uint32 i = 0; i < 32; i++) { 286 uint32 feature = features & (1 << i); 287 if (feature == 0) 288 continue; 289 const char* name = virtio_get_feature_name(feature); 290 if (name == NULL) 291 name = get_feature_name(feature); 292 if (name != NULL) { 293 strlcat(features_string, "[", sizeof(features_string)); 294 strlcat(features_string, name, sizeof(features_string)); 295 strlcat(features_string, "] ", sizeof(features_string)); 296 } 297 } 298 TRACE("%s: %s\n", title, features_string); 299 } 300