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