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::ReadDeviceConfig(uint8 offset, void* buffer, size_t bufferSize) 131 { 132 return fController->read_device_config(fCookie, offset, buffer, 133 bufferSize); 134 } 135 136 137 status_t 138 VirtioDevice::WriteDeviceConfig(uint8 offset, const void* buffer, 139 size_t bufferSize) 140 { 141 return fController->write_device_config(fCookie, offset, buffer, 142 bufferSize); 143 } 144 145 146 status_t 147 VirtioDevice::AllocateQueues(size_t count, virtio_queue *queues) 148 { 149 if (count > VIRTIO_VIRTQUEUES_MAX_COUNT || queues == NULL) 150 return B_BAD_VALUE; 151 152 fQueues = new(std::nothrow) VirtioQueue*[count]; 153 if (fQueues == NULL) 154 return B_NO_MEMORY; 155 156 status_t status = B_OK; 157 fQueueCount = count; 158 for (size_t index = 0; index < count; index++) { 159 uint16 size = fController->get_queue_ring_size(fCookie, index); 160 fQueues[index] = new(std::nothrow) VirtioQueue(this, index, size); 161 queues[index] = fQueues[index]; 162 status = B_NO_MEMORY; 163 if (fQueues[index] != NULL) 164 status = fQueues[index]->InitCheck(); 165 if (status != B_OK) { 166 _DestroyQueues(index + 1); 167 return status; 168 } 169 } 170 171 return B_OK; 172 } 173 174 175 void 176 VirtioDevice::FreeQueues() 177 { 178 if (fQueues != NULL) 179 _DestroyQueues(fQueueCount); 180 181 fController->set_status(fCookie, VIRTIO_CONFIG_STATUS_RESET); 182 fController->set_status(fCookie, VIRTIO_CONFIG_STATUS_DRIVER); 183 } 184 185 186 status_t 187 VirtioDevice::SetupInterrupt(virtio_intr_func configHandler, void *driverCookie) 188 { 189 fConfigHandler = configHandler; 190 fDriverCookie = driverCookie; 191 status_t status = fController->setup_interrupt(fCookie, fQueueCount); 192 if (status != B_OK) 193 return status; 194 195 // ready to go 196 fController->set_status(fCookie, VIRTIO_CONFIG_STATUS_DRIVER_OK); 197 198 for (size_t index = 0; index < fQueueCount; index++) 199 fQueues[index]->EnableInterrupt(); 200 return B_OK; 201 } 202 203 204 status_t 205 VirtioDevice::FreeInterrupts() 206 { 207 for (size_t index = 0; index < fQueueCount; index++) 208 fQueues[index]->DisableInterrupt(); 209 210 fController->set_status(fCookie, VIRTIO_CONFIG_STATUS_DRIVER); 211 212 return fController->free_interrupt(fCookie); 213 } 214 215 216 status_t 217 VirtioDevice::SetupQueue(uint16 queueNumber, phys_addr_t physAddr) 218 { 219 return fController->setup_queue(fCookie, queueNumber, physAddr); 220 } 221 222 223 void 224 VirtioDevice::NotifyQueue(uint16 queueNumber) 225 { 226 fController->notify_queue(fCookie, queueNumber); 227 } 228 229 230 status_t 231 VirtioDevice::QueueInterrupt(uint16 queueNumber) 232 { 233 if (queueNumber != INT16_MAX) { 234 if (queueNumber >= fQueueCount) 235 return B_BAD_VALUE; 236 return fQueues[queueNumber]->Interrupt(); 237 } 238 239 status_t status = B_OK; 240 for (uint16 i = 0; i < fQueueCount; i++) { 241 status = fQueues[i]->Interrupt(); 242 if (status != B_OK) 243 break; 244 } 245 246 return status; 247 } 248 249 250 status_t 251 VirtioDevice::ConfigInterrupt() 252 { 253 if (fConfigHandler != NULL) 254 fConfigHandler(fDriverCookie); 255 return B_OK; 256 } 257 258 259 void 260 VirtioDevice::_DestroyQueues(size_t count) 261 { 262 for (size_t i = 0; i < count; i++) { 263 delete fQueues[i]; 264 } 265 delete[] fQueues; 266 fQueues = NULL; 267 } 268 269 270 void 271 VirtioDevice::_DumpFeatures(const char* title, uint32 features, 272 const char* (*get_feature_name)(uint32)) 273 { 274 char features_string[512] = ""; 275 for (uint32 i = 0; i < 32; i++) { 276 uint32 feature = features & (1 << i); 277 if (feature == 0) 278 continue; 279 const char* name = virtio_get_feature_name(feature); 280 if (name == NULL) 281 name = get_feature_name(feature); 282 if (name != NULL) { 283 snprintf(features_string, sizeof(features_string), "%s[%s] ", 284 features_string, name); 285 } 286 } 287 TRACE("%s: %s\n", title, features_string); 288 } 289