1 /* 2 * Copyright 2021, Haiku, Inc. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7 #include "virtio.h" 8 9 #include <new> 10 #include <string.h> 11 #include <malloc.h> 12 #include <KernelExport.h> 13 14 15 enum { 16 maxVirtioDevices = 32, 17 }; 18 19 20 VirtioResources gVirtioDevList[maxVirtioDevices]; 21 int32_t gVirtioDevListLen = 0; 22 23 DoublyLinkedList<VirtioDevice> gVirtioDevices; 24 VirtioDevice* gKeyboardDev = NULL; 25 26 27 void* 28 aligned_malloc(size_t required_bytes, size_t alignment) 29 { 30 void* p1; // original block 31 void** p2; // aligned block 32 int offset = alignment - 1 + sizeof(void*); 33 if ((p1 = (void*)malloc(required_bytes + offset)) == NULL) { 34 return NULL; 35 } 36 p2 = (void**)(((size_t)(p1) + offset) & ~(alignment - 1)); 37 p2[-1] = p1; 38 return p2; 39 } 40 41 42 void 43 aligned_free(void* p) 44 { 45 free(((void**)p)[-1]); 46 } 47 48 49 VirtioResources* 50 ThisVirtioDev(uint32 deviceId, int n) 51 { 52 for (int i = 0; i < gVirtioDevListLen; i++) { 53 VirtioRegs* volatile regs = gVirtioDevList[i].regs; 54 if (regs->signature != kVirtioSignature) continue; 55 if (regs->deviceId == deviceId) { 56 if (n == 0) return &gVirtioDevList[i]; else n--; 57 } 58 } 59 return NULL; 60 } 61 62 63 //#pragma mark VirtioDevice 64 65 int32_t 66 VirtioDevice::AllocDesc() 67 { 68 for (size_t i = 0; i < fQueueLen; i++) { 69 if ((fFreeDescs[i/32] & (1 << (i % 32))) != 0) { 70 fFreeDescs[i/32] &= ~((uint32_t)1 << (i % 32)); 71 return i; 72 } 73 } 74 return -1; 75 } 76 77 78 void 79 VirtioDevice::FreeDesc(int32_t idx) 80 { 81 fFreeDescs[idx/32] |= (uint32_t)1 << (idx % 32); 82 } 83 84 85 VirtioDevice::VirtioDevice(const VirtioResources& devRes): fRegs(devRes.regs) 86 { 87 gVirtioDevices.Insert(this); 88 89 dprintf("+VirtioDevice\n"); 90 91 fRegs->status = 0; // reset 92 93 fRegs->status |= kVirtioConfigSAcknowledge; 94 fRegs->status |= kVirtioConfigSDriver; 95 dprintf("features: %08x\n", fRegs->deviceFeatures); 96 fRegs->status |= kVirtioConfigSFeaturesOk; 97 fRegs->status |= kVirtioConfigSDriverOk; 98 99 fRegs->queueSel = 0; 100 // dprintf("queueNumMax: %d\n", fRegs->queueNumMax); 101 fQueueLen = fRegs->queueNumMax; 102 fRegs->queueNum = fQueueLen; 103 fLastUsed = 0; 104 105 fDescs = (VirtioDesc*)aligned_malloc(sizeof(VirtioDesc) * fQueueLen, 4096); 106 memset(fDescs, 0, sizeof(VirtioDesc) * fQueueLen); 107 fAvail = (VirtioAvail*)aligned_malloc(sizeof(VirtioAvail) 108 + sizeof(uint16_t) * fQueueLen, 4096); 109 memset(fAvail, 0, sizeof(VirtioAvail) + sizeof(uint16_t) * fQueueLen); 110 fUsed = (VirtioUsed*)aligned_malloc(sizeof(VirtioUsed) 111 + sizeof(VirtioUsedItem) * fQueueLen, 4096); 112 memset(fUsed, 0, sizeof(VirtioUsed) + sizeof(VirtioUsedItem) * fQueueLen); 113 fFreeDescs = new(std::nothrow) uint32_t[(fQueueLen + 31)/32]; 114 memset(fFreeDescs, 0xff, sizeof(uint32_t) * ((fQueueLen + 31)/32)); 115 116 fReqs = new(std::nothrow) IORequest*[fQueueLen]; 117 118 fRegs->queueDescLow = (uint32_t)(uint64_t)fDescs; 119 fRegs->queueDescHi = (uint32_t)((uint64_t)fDescs >> 32); 120 fRegs->queueAvailLow = (uint32_t)(uint64_t)fAvail; 121 fRegs->queueAvailHi = (uint32_t)((uint64_t)fAvail >> 32); 122 fRegs->queueUsedLow = (uint32_t)(uint64_t)fUsed; 123 fRegs->queueUsedHi = (uint32_t)((uint64_t)fUsed >> 32); 124 /* 125 dprintf("fDescs: %p\n", fDescs); 126 dprintf("fAvail: %p\n", fAvail); 127 dprintf("fUsed: %p\n", fUsed); 128 */ 129 fRegs->queueReady = 1; 130 131 fRegs->config[0] = kVirtioInputCfgIdName; 132 // dprintf("name: %s\n", (const char*)(&fRegs->config[8])); 133 } 134 135 136 VirtioDevice::~VirtioDevice() 137 { 138 gVirtioDevices.Remove(this); 139 fRegs->status = 0; // reset 140 } 141 142 143 void 144 VirtioDevice::ScheduleIO(IORequest** reqs, uint32 cnt) 145 { 146 if (cnt < 1) return; 147 int32_t firstDesc, lastDesc; 148 for (uint32 i = 0; i < cnt; i++) { 149 int32_t desc = AllocDesc(); 150 if (desc < 0) {panic("virtio: no more descs"); return;} 151 if (i == 0) { 152 firstDesc = desc; 153 } else { 154 fDescs[lastDesc].flags |= kVringDescFlagsNext; 155 fDescs[lastDesc].next = desc; 156 reqs[i - 1]->next = reqs[i]; 157 } 158 fDescs[desc].addr = (uint64_t)(reqs[i]->buf); 159 fDescs[desc].len = reqs[i]->len; 160 fDescs[desc].flags = 0; 161 fDescs[desc].next = 0; 162 switch (reqs[i]->op) { 163 case ioOpRead: break; 164 case ioOpWrite: fDescs[desc].flags |= kVringDescFlagsWrite; break; 165 } 166 reqs[i]->state = ioStatePending; 167 lastDesc = desc; 168 } 169 int32_t idx = fAvail->idx % fQueueLen; 170 fReqs[idx] = reqs[0]; 171 fAvail->ring[idx] = firstDesc; 172 fAvail->idx++; 173 fRegs->queueNotify = 0; 174 } 175 176 177 void 178 VirtioDevice::ScheduleIO(IORequest* req) 179 { 180 ScheduleIO(&req, 1); 181 } 182 183 184 IORequest* 185 VirtioDevice::ConsumeIO() 186 { 187 if (fUsed->idx == fLastUsed) 188 return NULL; 189 190 IORequest* req = fReqs[fLastUsed % fQueueLen]; 191 fReqs[fLastUsed % fQueueLen] = NULL; 192 req->state = ioStateDone; 193 int32 desc = fUsed->ring[fLastUsed % fQueueLen].id; 194 while (kVringDescFlagsNext & fDescs[desc].flags) { 195 int32 nextDesc = fDescs[desc].next; 196 FreeDesc(desc); 197 desc = nextDesc; 198 } 199 FreeDesc(desc); 200 fLastUsed++; 201 return req; 202 } 203 204 205 IORequest* 206 VirtioDevice::WaitIO() 207 { 208 while (fUsed->idx == fLastUsed) {} 209 return ConsumeIO(); 210 } 211 212 213 //#pragma mark - 214 215 void 216 virtio_register(addr_t base, size_t len, uint32 irq) 217 { 218 VirtioRegs* volatile regs = (VirtioRegs* volatile)base; 219 220 dprintf("virtio_register(0x%" B_PRIxADDR ", 0x%" B_PRIxSIZE ", " 221 "%" B_PRIu32 ")\n", base, len, irq); 222 dprintf(" signature: 0x%" B_PRIx32 "\n", regs->signature); 223 dprintf(" version: %" B_PRIu32 "\n", regs->version); 224 dprintf(" device id: %" B_PRIu32 "\n", regs->deviceId); 225 226 if (!(gVirtioDevListLen < maxVirtioDevices)) { 227 dprintf("too many VirtIO devices\n"); 228 return; 229 } 230 gVirtioDevList[gVirtioDevListLen].regs = regs; 231 gVirtioDevList[gVirtioDevListLen].regsSize = len; 232 gVirtioDevList[gVirtioDevListLen].irq = irq; 233 gVirtioDevListLen++; 234 } 235 236 237 void 238 virtio_init() 239 { 240 dprintf("virtio_init()\n"); 241 242 int i = 0; 243 for (; ; i++) { 244 VirtioResources* devRes = ThisVirtioDev(kVirtioDevInput, i); 245 if (devRes == NULL) break; 246 VirtioRegs* volatile regs = devRes->regs; 247 regs->config[0] = kVirtioInputCfgIdName; 248 dprintf("virtio_input[%d]: %s\n", i, (const char*)(®s->config[8])); 249 if (i == 0) 250 gKeyboardDev = new(std::nothrow) VirtioDevice(*devRes); 251 } 252 dprintf("virtio_input count: %d\n", i); 253 if (gKeyboardDev != NULL) { 254 for (int i = 0; i < 4; i++) { 255 gKeyboardDev->ScheduleIO(new(std::nothrow) IORequest(ioOpWrite, 256 malloc(sizeof(VirtioInputPacket)), sizeof(VirtioInputPacket))); 257 } 258 } 259 } 260 261 262 void 263 virtio_fini() 264 { 265 auto it = gVirtioDevices.GetIterator(); 266 while (VirtioDevice* dev = it.Next()) { 267 dev->Regs()->status = 0; // reset 268 } 269 } 270 271 272 int 273 virtio_input_get_key() 274 { 275 if (gKeyboardDev == NULL) 276 return 0; 277 278 IORequest* req = gKeyboardDev->ConsumeIO(); 279 if (req == NULL) 280 return 0; 281 282 VirtioInputPacket &pkt = *(VirtioInputPacket*)req->buf; 283 284 int key = 0; 285 if (pkt.type == 1 && pkt.value == 1) 286 key = pkt.code; 287 288 free(req->buf); req->buf = NULL; delete req; 289 gKeyboardDev->ScheduleIO(new(std::nothrow) IORequest(ioOpWrite, 290 malloc(sizeof(VirtioInputPacket)), sizeof(VirtioInputPacket))); 291 292 return key; 293 } 294 295 296 int 297 virtio_input_wait_for_key() 298 { 299 int key = 0; 300 301 do { 302 key = virtio_input_get_key(); 303 } while (key == 0); 304 305 return key; 306 } 307