1 /* 2 * Copyright 2021, Haiku, Inc. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7 #include "VirtioDevice.h" 8 9 #include <malloc.h> 10 #include <string.h> 11 #include <new> 12 13 #include <KernelExport.h> 14 #include <kernel.h> 15 #include <debug.h> 16 17 18 static inline void 19 SetLowHi(vuint32 &low, vuint32 &hi, uint64 val) 20 { 21 low = (uint32)val; 22 hi = (uint32)(val >> 32); 23 } 24 25 26 // #pragma mark - VirtioQueue 27 28 29 VirtioQueue::VirtioQueue(VirtioDevice *dev, int32 id) 30 : 31 fDev(dev), 32 fId(id), 33 fQueueHandler(NULL), 34 fQueueHandlerCookie(NULL) 35 { 36 } 37 38 39 VirtioQueue::~VirtioQueue() 40 { 41 } 42 43 44 status_t 45 VirtioQueue::Init() 46 { 47 fDev->fRegs->queueSel = fId; 48 TRACE("queueNumMax: %d\n", fDev->fRegs->queueNumMax); 49 fQueueLen = fDev->fRegs->queueNumMax; 50 fDev->fRegs->queueNum = fQueueLen; 51 fLastUsed = 0; 52 53 size_t queueMemSize = 0; 54 fDescs = (VirtioDesc*)queueMemSize; 55 queueMemSize += ROUNDUP(sizeof(VirtioDesc) 56 * fQueueLen, B_PAGE_SIZE); 57 58 fAvail = (VirtioAvail*)queueMemSize; 59 queueMemSize += ROUNDUP(sizeof(VirtioAvail) 60 + sizeof(uint16) * fQueueLen, B_PAGE_SIZE); 61 62 fUsed = (VirtioUsed*)queueMemSize; 63 queueMemSize += ROUNDUP(sizeof(VirtioUsed) 64 + sizeof(VirtioUsedItem)*fQueueLen, B_PAGE_SIZE); 65 66 uint8* queueMem = NULL; 67 fArea.SetTo(create_area("VirtIO Queue", (void**)&queueMem, 68 B_ANY_KERNEL_ADDRESS, queueMemSize, B_CONTIGUOUS, 69 B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA)); 70 71 if (!fArea.IsSet()) { 72 ERROR("can't create area: %08" B_PRIx32, fArea.Get()); 73 return fArea.Get(); 74 } 75 76 physical_entry pe; 77 if (status_t res = get_memory_map(queueMem, queueMemSize, &pe, 1) < B_OK) { 78 ERROR("get_memory_map failed"); 79 return res; 80 } 81 82 TRACE("queueMem: %p\n", queueMem); 83 84 memset(queueMem, 0, queueMemSize); 85 86 fDescs = (VirtioDesc*)((uint8*)fDescs + (addr_t)queueMem); 87 fAvail = (VirtioAvail*)((uint8*)fAvail + (addr_t)queueMem); 88 fUsed = (VirtioUsed*)((uint8*)fUsed + (addr_t)queueMem); 89 90 phys_addr_t descsPhys = (addr_t)fDescs - (addr_t)queueMem + pe.address; 91 phys_addr_t availPhys = (addr_t)fAvail - (addr_t)queueMem + pe.address; 92 phys_addr_t usedPhys = (addr_t)fUsed - (addr_t)queueMem + pe.address; 93 94 SetLowHi(fDev->fRegs->queueDescLow, fDev->fRegs->queueDescHi, descsPhys); 95 SetLowHi(fDev->fRegs->queueAvailLow, fDev->fRegs->queueAvailHi, availPhys); 96 SetLowHi(fDev->fRegs->queueUsedLow, fDev->fRegs->queueUsedHi, usedPhys); 97 98 fFreeDescs.SetTo(new(std::nothrow) uint32[(fQueueLen + 31) / 32]); 99 if (!fFreeDescs.IsSet()) 100 return B_NO_MEMORY; 101 102 memset(fFreeDescs.Get(), 0xff, sizeof(uint32) * ((fQueueLen + 31) / 32)); 103 fCookies.SetTo(new(std::nothrow) void*[fQueueLen]); 104 if (!fCookies.IsSet()) 105 return B_NO_MEMORY; 106 107 fDev->fRegs->queueReady = 1; 108 109 return B_OK; 110 } 111 112 113 int32 114 VirtioQueue::AllocDesc() 115 { 116 for (size_t i = 0; i < fQueueLen; i++) { 117 if ((fFreeDescs[i / 32] & (1 << (i % 32))) != 0) { 118 fFreeDescs[i / 32] &= ~((uint32)1 << (i % 32)); 119 return i; 120 } 121 } 122 return -1; 123 } 124 125 126 void 127 VirtioQueue::FreeDesc(int32 idx) 128 { 129 fFreeDescs[idx / 32] |= (uint32)1 << (idx % 32); 130 } 131 132 133 status_t 134 VirtioQueue::Enqueue(const physical_entry* vector, 135 size_t readVectorCount, size_t writtenVectorCount, 136 void* cookie) 137 { 138 int32 firstDesc = -1, lastDesc = -1; 139 size_t count = readVectorCount + writtenVectorCount; 140 141 if (count == 0) 142 return B_OK; 143 144 for (size_t i = 0; i < count; i++) { 145 int32 desc = AllocDesc(); 146 147 if (desc < 0) { 148 ERROR("no free virtio descs, queue: %p\n", this); 149 150 if (firstDesc >= 0) { 151 desc = firstDesc; 152 while (kVringDescFlagsNext & fDescs[desc].flags) { 153 int32_t nextDesc = fDescs[desc].next; 154 FreeDesc(desc); 155 desc = nextDesc; 156 } 157 FreeDesc(desc); 158 } 159 160 return B_WOULD_BLOCK; 161 } 162 163 if (i == 0) { 164 firstDesc = desc; 165 } else { 166 fDescs[lastDesc].flags |= kVringDescFlagsNext; 167 fDescs[lastDesc].next = desc; 168 } 169 fDescs[desc].addr = vector[i].address; 170 fDescs[desc].len = vector[i].size; 171 fDescs[desc].flags = 0; 172 fDescs[desc].next = 0; 173 if (i >= readVectorCount) 174 fDescs[desc].flags |= kVringDescFlagsWrite; 175 176 lastDesc = desc; 177 } 178 179 int32_t idx = fAvail->idx % fQueueLen; 180 fCookies[idx] = cookie; 181 fAvail->ring[idx] = firstDesc; 182 fAvail->idx++; 183 fDev->fRegs->queueNotify = fId; 184 185 return B_OK; 186 } 187 188 189 bool 190 VirtioQueue::Dequeue(void** _cookie, uint32* _usedLength) 191 { 192 fDev->fRegs->queueSel = fId; 193 194 if (fUsed->idx == fLastUsed) 195 return false; 196 197 if (_cookie != NULL) 198 *_cookie = fCookies[fLastUsed % fQueueLen]; 199 fCookies[fLastUsed % fQueueLen] = NULL; 200 201 if (_usedLength != NULL) 202 *_usedLength = fUsed->ring[fLastUsed % fQueueLen].len; 203 204 int32_t desc = fUsed->ring[fLastUsed % fQueueLen].id; 205 while (kVringDescFlagsNext & fDescs[desc].flags) { 206 int32_t nextDesc = fDescs[desc].next; 207 FreeDesc(desc); 208 desc = nextDesc; 209 } 210 FreeDesc(desc); 211 fLastUsed++; 212 213 return true; 214 } 215 216 217 // #pragma mark - VirtioIrqHandler 218 219 220 VirtioIrqHandler::VirtioIrqHandler(VirtioDevice* dev) 221 : 222 fDev(dev) 223 { 224 fReferenceCount = 0; 225 } 226 227 228 void 229 VirtioIrqHandler::FirstReferenceAcquired() 230 { 231 install_io_interrupt_handler(fDev->fIrq, Handle, fDev, 0); 232 } 233 234 235 void 236 VirtioIrqHandler::LastReferenceReleased() 237 { 238 remove_io_interrupt_handler(fDev->fIrq, Handle, fDev); 239 } 240 241 242 int32 243 VirtioIrqHandler::Handle(void* data) 244 { 245 // TRACE("VirtioIrqHandler::Handle(%p)\n", data); 246 VirtioDevice* dev = (VirtioDevice*)data; 247 248 if ((kVirtioIntQueue & dev->fRegs->interruptStatus) != 0) { 249 for (int32 i = 0; i < dev->fQueueCnt; i++) { 250 VirtioQueue* queue = dev->fQueues[i].Get(); 251 if (queue->fUsed->idx != queue->fLastUsed 252 && queue->fQueueHandler != NULL) { 253 queue->fQueueHandler(dev->fConfigHandlerCookie, 254 queue->fQueueHandlerCookie); 255 } 256 } 257 dev->fRegs->interruptAck = kVirtioIntQueue; 258 } 259 260 if ((kVirtioIntConfig & dev->fRegs->interruptStatus) != 0) { 261 if (dev->fConfigHandler != NULL) 262 dev->fConfigHandler(dev->fConfigHandlerCookie); 263 264 dev->fRegs->interruptAck = kVirtioIntConfig; 265 } 266 267 return B_HANDLED_INTERRUPT; 268 } 269 270 271 // #pragma mark - VirtioDevice 272 273 274 VirtioDevice::VirtioDevice() 275 : 276 fRegs(NULL), 277 fQueueCnt(0), 278 fIrqHandler(this), 279 fConfigHandler(NULL), 280 fConfigHandlerCookie(NULL) 281 { 282 } 283 284 285 status_t 286 VirtioDevice::Init(phys_addr_t regs, size_t regsLen, int32 irq, int32 queueCnt) 287 { 288 fRegsArea.SetTo(map_physical_memory("Virtio MMIO", 289 regs, regsLen, B_ANY_KERNEL_ADDRESS, 290 B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA, 291 (void**)&fRegs)); 292 293 if (!fRegsArea.IsSet()) 294 return fRegsArea.Get(); 295 296 fIrq = irq; 297 298 // Reset 299 fRegs->status = 0; 300 301 return B_OK; 302 } 303