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