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*
aligned_malloc(size_t required_bytes,size_t alignment)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
aligned_free(void * p)43 aligned_free(void* p)
44 {
45 free(((void**)p)[-1]);
46 }
47
48
49 VirtioResources*
ThisVirtioDev(uint32 deviceId,int n)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
AllocDesc()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
FreeDesc(int32_t idx)79 VirtioDevice::FreeDesc(int32_t idx)
80 {
81 fFreeDescs[idx/32] |= (uint32_t)1 << (idx % 32);
82 }
83
84
VirtioDevice(const VirtioResources & devRes)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
~VirtioDevice()136 VirtioDevice::~VirtioDevice()
137 {
138 gVirtioDevices.Remove(this);
139 fRegs->status = 0; // reset
140 }
141
142
143 void
ScheduleIO(IORequest ** reqs,uint32 cnt)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
ScheduleIO(IORequest * req)178 VirtioDevice::ScheduleIO(IORequest* req)
179 {
180 ScheduleIO(&req, 1);
181 }
182
183
184 IORequest*
ConsumeIO()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*
WaitIO()206 VirtioDevice::WaitIO()
207 {
208 while (fUsed->idx == fLastUsed) {}
209 return ConsumeIO();
210 }
211
212
213 //#pragma mark -
214
215 void
virtio_register(addr_t base,size_t len,uint32 irq)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
virtio_init()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
virtio_fini()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
virtio_input_get_key()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
virtio_input_wait_for_key()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