parlib: have 2LS libraries #include parlib/stdio.h
[akaros.git] / user / vmm / virtio_mmio.c
1 /* Virtio MMIO bindings
2  *
3  * Copyright (c) 2011 Linaro Limited
4  * Copyright (C) 1991-2016, the Linux Kernel authors
5  * Copyright (c) 2016 Google Inc.
6  *
7  * Author:
8  *  Peter Maydell <peter.maydell@linaro.org>
9  *  Rusty Russell <rusty@rustcorp.com.au>
10  *  Michael Taufen <mtaufen@gmail.com>
11  *
12  * This program is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU General Public License as
14  * published by the Free Software Foundation; either version 2 of
15  * the License, or (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * Akaros's virtio_mmio (this file) is inspired by QEMU's virtio-mmio.c
23  * and Linux's lguest.c.  Both of QEMU's virtio-mmio.c and Linux's
24  * lguest.c are released under the GNU General Public License version 2
25  * or later.  Their original files were heavily modified for Akaros.
26  *
27  * Original linux/tools/lguest/lguest.c:
28  *   https://github.com/torvalds/linux/blob/v4.5/tools/lguest/lguest.c
29  *   most recent hash on the file as of v4.5 tag:
30  *     e523caa601f4a7c2fa1ecd040db921baf7453798
31  *
32  * Original virtio-mmio.c:
33  *   https://github.com/qemu/qemu/blob/v2.5.0/hw/virtio/virtio-mmio.c
34  *   most recent hash on the file as of v2.5.0 tag:
35  *     ab223c9518e8c7eb542ef3133de1a34475b69790
36  *
37  * TODO: This needs major refactoring/reformatting.
38  */
39
40 #include <parlib/stdio.h>
41 #include <string.h>
42 #include <unistd.h>
43 #include <sys/eventfd.h>
44 #include <vmm/virtio_config.h>
45 #include <vmm/virtio_mmio.h>
46
47 #define VIRT_MAGIC 0x74726976 /* 'virt' */
48
49 #define VIRT_MMIO_VERSION 0x2
50
51 #define VIRT_MMIO_VENDOR 0x52414B41 /* 'AKAR' */
52
53 void virtio_mmio_set_vring_irq(struct virtio_mmio_dev *mmio_dev)
54 {
55         mmio_dev->isr |= VIRTIO_MMIO_INT_VRING;
56 }
57
58 void virtio_mmio_set_cfg_irq(struct virtio_mmio_dev *mmio_dev)
59 {
60         mmio_dev->isr |= VIRTIO_MMIO_INT_CONFIG;
61 }
62
63 static void virtio_mmio_reset_cfg(struct virtio_mmio_dev *mmio_dev)
64 {
65         if (!mmio_dev->vqdev->cfg || mmio_dev->vqdev->cfg_sz == 0)
66                 VIRTIO_DEV_WARNX(mmio_dev->vqdev,
67                         "Attempt to reset the device-specific configuration space, but the device does not provide it. Generally, this region is required, so you should probably do something about that.");
68
69         // If a default device-specific configuration is provided, copy that
70         // into the device-specific configuration space. Otherwise, clear the
71         // device-specific configuration space.
72         if (mmio_dev->vqdev->cfg_d)
73                 memcpy(mmio_dev->vqdev->cfg, mmio_dev->vqdev->cfg_d,
74                            mmio_dev->vqdev->cfg_sz);
75         else
76                 memset(mmio_dev->vqdev->cfg, 0x0, mmio_dev->vqdev->cfg_sz);
77
78         // Increment the ConfigGeneration, since the config space just got
79         // reset.  We can't simply set it to 0, because we must ensure that it
80         // changes when the config space changes and it might currently be set
81         // to 0.
82         mmio_dev->cfg_gen++;
83 }
84
85 // TODO: virtio_mmio_reset could use a careful audit. We have not yet
86 //       encountered a scenario where the driver resets the device
87 //       while lots of things are in-flight; thus far we have only seen
88 //       device resets prior to the first initialization sequence.
89 static void virtio_mmio_reset(struct virtio_mmio_dev *mmio_dev)
90 {
91         int i;
92
93         if (!mmio_dev->vqdev)
94                 return;
95
96         fprintf(stderr, "virtio mmio device reset: %s\n",
97                 mmio_dev->vqdev->name);
98
99         // Clear any driver-activated feature bits
100         mmio_dev->vqdev->dri_feat = 0;
101
102         // virtio-v1.0-cs04 s2.1.2 Device Status Field
103         // The device MUST initialize device status to 0 upon reset
104         mmio_dev->status = 0;
105
106         // virtio-v1.0-cs04 s4.2.2.1 MMIO Device Register Layout
107         // Upon reset, the device MUST clear all bits in InterruptStatus
108         mmio_dev->isr = 0;
109
110         // virtio-v1.0-cs04 s4.2.2.1 MMIO Device Register Layout
111         // Upon reset, the device MUST clear...ready bits in the QueueReady
112         // register for all queues in the device.
113         for (i = 0; i < mmio_dev->vqdev->num_vqs; ++i) {
114                 if (mmio_dev->vqdev->vqs[i].srv_th) {
115                 // FIXME! PLEASE, FIXME!
116                 // TODO: For now we are going to make device resets an error
117                 // once service threads exist on the queues. This is obviously
118                 // not sustainable, because the driver needs to be able to reset
119                 // the device after certain errors occur.
120                 //
121                 //       In the future, when we actually decide how we want
122                 //       to clean up the threads, the sequence might look
123                 //       something like this:
124                 //       1. Ask the queue's service thread to exit and wait
125                 //          for it to finish and exit.
126                 //       2. Once it has exited, close the queue's eventfd
127                 //          and set both the eventfd and srv_th fields to 0.
128                         VIRTIO_DEV_ERRX(mmio_dev->vqdev,
129                                 "The driver reset the device after queue service threads had started running. This is NOT a restriction imposed by virtio! We just haven't implemented something that will kill service threads yet.");
130                 }
131
132                 mmio_dev->vqdev->vqs[i].qready = 0;
133                 mmio_dev->vqdev->vqs[i].last_avail = 0;
134         }
135
136         virtio_mmio_reset_cfg(mmio_dev);
137 }
138
139 uint32_t virtio_mmio_rd(struct virtual_machine *unused_vm,
140                         struct virtio_mmio_dev *mmio_dev,
141                         uint64_t gpa, uint8_t size)
142 {
143         uint64_t offset = gpa - mmio_dev->addr;
144         uint8_t *target; // target of read from device-specific config space
145         const char *err; // returned err strings
146
147         // Return 0 for all registers except the magic number,
148         // the mmio version, and the device vendor when either
149         // there is no vqdev or no vqs on the vqdev.
150         if (!mmio_dev->vqdev || mmio_dev->vqdev->num_vqs == 0) {
151                 switch (offset) {
152                 case VIRTIO_MMIO_MAGIC_VALUE:
153                         return VIRT_MAGIC;
154                 case VIRTIO_MMIO_VERSION:
155                         return VIRT_MMIO_VERSION;
156                 case VIRTIO_MMIO_VENDOR_ID:
157                         return VIRT_MMIO_VENDOR;
158                 default:
159                         return 0;
160                 }
161         }
162
163         // virtio-v1.0-cs04 s4.2.3.1.1 Device Initialization (MMIO section)
164         if (mmio_dev->vqdev->dev_id == 0
165                 && offset != VIRTIO_MMIO_MAGIC_VALUE
166                 && offset != VIRTIO_MMIO_VERSION
167                 && offset != VIRTIO_MMIO_DEVICE_ID)
168                 VIRTIO_DRI_ERRX(mmio_dev->vqdev,
169                         "Attempt to read from a register not MagicValue, Version, or DeviceID on a device whose DeviceID is 0x0\n"
170                         "  See virtio-v1.0-cs04 s4.2.3.1.1 Device Initialization");
171
172         // Now we know that the host provided a vqdev. As soon as the driver
173         // tries to read the magic number, we know it's considering the device.
174         // This is a great time to validate the features the host is providing.
175         // The host must provide a valid combination of features, or we crash
176         // here until the offered feature combination is made valid.
177         if (offset == VIRTIO_MMIO_MAGIC_VALUE) {
178                 // NOTE: If you ever decide to change this to a warning instead
179                 // of an error, you might want to return an invalid magic value
180                 // here to tell the driver that it is poking at a bad device.
181                 err = virtio_validate_feat(mmio_dev->vqdev,
182                                            mmio_dev->vqdev->dev_feat);
183                 if (err)
184                         VIRTIO_DEV_ERRX(mmio_dev->vqdev,
185                                 "The feature combination offered by the device is not valid. This must be fixed before the device can be used.\n"
186                                 "  Validation Error: %s", err);
187         }
188
189
190         // Warn if FAILED status bit is set.
191         // virtio-v1.0-cs04 s2.1.1 Device Status Field
192         if (mmio_dev->status & VIRTIO_CONFIG_S_FAILED)
193                 VIRTIO_DRI_WARNX(mmio_dev->vqdev,
194                         "The FAILED status bit is set. The driver should probably reset the device before continuing.\n"
195                         "  See virtio-v1.0-cs04 s2.1.1 Device Status Field");
196
197         // TODO: I could only do a limited amount of testing on the device-
198         //       specific config space, because I was limited to seeing what
199         //       the guest driver for the console device would do. You may
200         //       run into issues when you implement virtio-net, since that
201         //       does more with the device-specific config.
202         if (offset >= VIRTIO_MMIO_CONFIG) {
203                 offset -= VIRTIO_MMIO_CONFIG;
204
205                 if (!mmio_dev->vqdev->cfg || mmio_dev->vqdev->cfg_sz == 0) {
206                         VIRTIO_DEV_ERRX(mmio_dev->vqdev,
207                                 "Driver attempted to read the device-specific configuration space, but the device failed to provide it.");
208                 }
209
210                 // virtio-v1.0-cs04 s3.1.1 Device Initialization
211                 if (!(mmio_dev->status & VIRTIO_CONFIG_S_DRIVER)) {
212                         VIRTIO_DRI_ERRX(mmio_dev->vqdev,
213                                 "Driver attempted to read the device-specific configuration space before setting the DRIVER status bit.\n"
214                                 "  See virtio-v1.0-cs04 s3.1.1 Device Initialization");
215                 }
216
217                 if ((offset + size) > mmio_dev->vqdev->cfg_sz
218                         || (offset + size) < offset) {
219                         VIRTIO_DRI_ERRX(mmio_dev->vqdev,
220                                 "Attempt to read invalid offset of the device specific  configuration space, or (offset + read width) wrapped around.");
221                 }
222
223                 target = (uint8_t*)((uint64_t)mmio_dev->vqdev->cfg + offset);
224
225                 // TODO: Check that size matches the size of the field at offset
226                 //       for the given device? i.e. virtio_console_config.rows
227                 //       should only be accessible via a 16 bit read or write.
228                 //       I haven't done this yet, it will be a significant
229                 //       undertaking and maintenance commitment, because you
230                 //       will have to do it for every virtio device you
231                 //       want to use in the future.
232                 switch (size) {
233                 case 1:
234                         return *((uint8_t*)target);
235                 case 2:
236                         if ((uint64_t)target % 2 != 0)
237                                 VIRTIO_DRI_ERRX(mmio_dev->vqdev,
238                                         "The driver must use 16 bit aligned reads for reading from 16 bit values in the device-specific configuration space.\n"
239                                         "  See virtio-v1.0-cs04 s4.2.2.2 MMIO Device Register Layout");
240                         return *((uint16_t*)target);
241                 case 4:
242                         if ((uint64_t)target % 4 != 0)
243                                 VIRTIO_DRI_ERRX(mmio_dev->vqdev,
244                                         "The driver must use 32 bit aligned reads for reading from 32 or 64 bit values in the device-specific configuration space.\n"
245                                         "  See virtio-v1.0-cs04 s4.2.2.2 MMIO Device Register Layout");
246                         return *((uint32_t*)target);
247                 default:
248                         VIRTIO_DRI_ERRX(mmio_dev->vqdev,
249                                 "The driver must use 8, 16, or 32 bit wide and aligned reads for reading from the device-specific configuration space.\n"
250                                 "  See virtio-v1.0-cs04 s4.2.2.2 MMIO Device Register Layout");
251                 }
252         }
253
254         // virtio-v1.0-cs04 4.2.2.2 MMIO Device Register Layout
255         if (size != 4 || (offset % 4) != 0) {
256                 VIRTIO_DRI_ERRX(mmio_dev->vqdev,
257                         "The driver must only use 32 bit wide and aligned reads for reading the control registers on the MMIO transport.\n"
258                         "  See virtio-v1.0-cs04 4.2.2.2 MMIO Device Register Layout");
259         }
260
261         // virtio-v1.0-cs04 Table 4.1
262         switch (offset) {
263         // Magic value
264         // 0x74726976 (a Little Endian equivalent of the “virt” string).
265         case VIRTIO_MMIO_MAGIC_VALUE:
266                 return VIRT_MAGIC;
267
268         // Device version number
269         // 0x2. Note: Legacy devices (see 4.2.4 Legacy interface) used 0x1.
270         case VIRTIO_MMIO_VERSION:
271                 return VIRT_MMIO_VERSION;
272
273         // Virtio Subsystem Device ID (see virtio-v1.0-cs04 sec. 5 for values)
274         // Value 0x0 is used to define a system memory map with placeholder
275         // devices at static, well known addresses.
276         case VIRTIO_MMIO_DEVICE_ID:
277                 return mmio_dev->vqdev->dev_id;
278
279         // Virtio Subsystem Vendor ID
280         case VIRTIO_MMIO_VENDOR_ID:
281                 return VIRT_MMIO_VENDOR;
282
283         // Flags representing features the device supports
284         case VIRTIO_MMIO_DEVICE_FEATURES:
285                 if (!(mmio_dev->status & VIRTIO_CONFIG_S_DRIVER))
286                         VIRTIO_DRI_ERRX(mmio_dev->vqdev,
287                                  "Attempt to read device features before setting the DRIVER status bit.\n"
288                                  "  See virtio-v1.0-cs04 s3.1.1 Device Initialization");
289
290                 // high 32 bits requested
291                 if (mmio_dev->dev_feat_sel)
292                         return mmio_dev->vqdev->dev_feat >> 32;
293                 return mmio_dev->vqdev->dev_feat; // low 32 bits requested
294
295         // Maximum virtual queue size
296         // Returns the maximum size (number of elements) of the queue the device
297         // is ready to process or zero (0x0) if the queue is not available.
298         // Applies to the queue selected by writing to QueueSel.
299         case VIRTIO_MMIO_QUEUE_NUM_MAX:
300         // TODO: Are there other cases that count as "queue not available"?
301         // NOTE: !qready does not count as "queue not available".
302                 if (mmio_dev->qsel >= mmio_dev->vqdev->num_vqs)
303                         return 0;
304                 return mmio_dev->vqdev->vqs[mmio_dev->qsel].qnum_max;
305
306         // Virtual queue ready bit
307         // Applies to the queue selected by writing to QueueSel.
308         case VIRTIO_MMIO_QUEUE_READY:
309                 if (mmio_dev->qsel >= mmio_dev->vqdev->num_vqs)
310                         return 0;
311                 return mmio_dev->vqdev->vqs[mmio_dev->qsel].qready;
312
313         // Interrupt status
314         // Bit mask of events that caused the device interrupt to be asserted.
315         // bit 0: Used Ring Update
316         // bit 1: Configuration Change
317         case VIRTIO_MMIO_INTERRUPT_STATUS:
318                 return mmio_dev->isr;
319
320         // Device status
321         case VIRTIO_MMIO_STATUS:
322                 return mmio_dev->status;
323
324         // Configuration atomicity value
325         // Contains a version for the device-specific configuration space
326         // The driver checks this version before and after accessing the config
327         // space, and if the values don't match it repeats the access.
328         case VIRTIO_MMIO_CONFIG_GENERATION:
329                 return mmio_dev->cfg_gen;
330
331         // Write-only register offsets:
332         case VIRTIO_MMIO_DEVICE_FEATURES_SEL:
333         case VIRTIO_MMIO_DRIVER_FEATURES:
334         case VIRTIO_MMIO_DRIVER_FEATURES_SEL:
335         case VIRTIO_MMIO_QUEUE_SEL:
336         case VIRTIO_MMIO_QUEUE_NUM:
337         case VIRTIO_MMIO_QUEUE_NOTIFY:
338         case VIRTIO_MMIO_INTERRUPT_ACK:
339         case VIRTIO_MMIO_QUEUE_DESC_LOW:
340         case VIRTIO_MMIO_QUEUE_DESC_HIGH:
341         case VIRTIO_MMIO_QUEUE_AVAIL_LOW:
342         case VIRTIO_MMIO_QUEUE_AVAIL_HIGH:
343         case VIRTIO_MMIO_QUEUE_USED_LOW:
344         case VIRTIO_MMIO_QUEUE_USED_HIGH:
345                 // Read of write-only register
346                 VIRTIO_DRI_ERRX(mmio_dev->vqdev,
347                         "Attempt to read write-only device register offset 0x%x.",
348                         offset);
349                 return 0;
350         default:
351                 // Bad register offset
352                 VIRTIO_DRI_ERRX(mmio_dev->vqdev,
353                         "Attempt to read invalid device register offset 0x%x.",
354                         offset);
355                 return 0;
356         }
357
358         return 0;
359 }
360
361 void virtio_mmio_wr(struct virtual_machine *vm,
362                     struct virtio_mmio_dev *mmio_dev, uint64_t gpa,
363                     uint8_t size, uint32_t *value)
364 {
365         uint64_t offset = gpa - mmio_dev->addr;
366         struct virtio_vq *notified_queue;
367         uint8_t *target; // target of write to device-specific config space
368         void *temp_ptr; // for facilitating bitwise ops on pointers
369         const char *err; // returned err strings
370
371         if (!mmio_dev->vqdev) {
372                 // If there is no vqdev on the mmio_dev,
373                 // we just make all registers write-ignored.
374                 return;
375         }
376
377         // virtio-v1.0-cs04 s4.2.3.1.1 Device Initialization (MMIO)
378         if (mmio_dev->vqdev->dev_id == 0)
379                 VIRTIO_DRI_ERRX(mmio_dev->vqdev,
380                         "Attempt to write to a device whose DeviceID is 0x0.\n"
381                         "  See virtio-v1.0-cs04 s4.2.3.1.1 Device Initialization");
382
383         // Warn if FAILED and trying to do something that is definitely not a
384         // reset.  virtio-v1.0-cs04 s2.1.1 Device Status Field
385         if (offset != VIRTIO_MMIO_STATUS
386                 && (mmio_dev->status & VIRTIO_CONFIG_S_FAILED))
387                 VIRTIO_DRI_WARNX(mmio_dev->vqdev,
388                         "The FAILED status bit is set. The driver should probably reset the device before continuing.\n"
389                         "  See virtio-v1.0-cs04 s2.1.1 Device Status Field");
390
391         // TODO: I could only do a limited amount of testing on the device-
392         //       specific config space, because I was limited to seeing what
393         //       the guest driver for the console device would do. You may
394         //       run into issues when you implement virtio-net, since that
395         //       does more with the device-specific config. (In fact, I don't
396         //       think the guest driver ever even tried to write the
397         //       device-specific config space for the console, so this section
398         //       is entirely untested)
399         if (offset >= VIRTIO_MMIO_CONFIG) {
400                 offset -= VIRTIO_MMIO_CONFIG;
401
402                 if (!mmio_dev->vqdev->cfg || mmio_dev->vqdev->cfg_sz == 0) {
403                         VIRTIO_DEV_ERRX(mmio_dev->vqdev,
404                                 "Driver attempted to write to the device-specific configuration space, but the device failed to provide it.");
405                 }
406
407                 // virtio-v1.0-cs04 s3.1.1 Device Initialization
408                 if (!(mmio_dev->status & VIRTIO_CONFIG_S_FEATURES_OK)) {
409                         VIRTIO_DRI_ERRX(mmio_dev->vqdev,
410                                 "Driver attempted to write the device-specific configuration space before setting the FEATURES_OK status bit.\n"
411                                 "  See virtio-v1.0-cs04 s3.1.1 Device Initialization");
412                 }
413
414                 if ((offset + size) > mmio_dev->vqdev->cfg_sz
415                         || (offset + size) < offset) {
416                         VIRTIO_DRI_ERRX(mmio_dev->vqdev,
417                                 "Attempt to write invalid offset of the device specific configuration space, or (offset + write width) wrapped around.");
418                 }
419
420                 target = (uint8_t*)((uint64_t)mmio_dev->vqdev->cfg + offset);
421
422                 // TODO: Check that size matches the size of the field at offset
423                 //       for the given device? i.e. virtio_console_config.rows
424                 //       should only be accessible via a 16 bit read or write.
425                 //       I haven't done this yet, it will be a significant
426                 //       undertaking and maintenance commitment, because you
427                 //       will have to do it for every virtio device you
428                 //       want to use in the future.
429                 switch (size) {
430                 case 1:
431                         *((uint8_t*)target) = *((uint8_t*)value);
432                         break;
433                 case 2:
434                         if ((uint64_t)target % 2 != 0)
435                                 VIRTIO_DRI_ERRX(mmio_dev->vqdev,
436                                         "The driver must use 16 bit aligned writes for writing to 16 bit values in the device-specific configuration space.\n"
437                                         "  See virtio-v1.0-cs04 s4.2.2.2 MMIO Device Register Layout");
438                         *((uint16_t*)target) = *((uint16_t*)value);
439                         break;
440                 case 4:
441                         if ((uint64_t)target % 4 != 0)
442                                 VIRTIO_DRI_ERRX(mmio_dev->vqdev,
443                                         "The driver must use 32 bit aligned writes for writing to 32 or 64 bit values in the device-specific configuration space.\n"
444                                         "  See virtio-v1.0-cs04 s4.2.2.2 MMIO Device Register Layout");
445                         *((uint32_t*)target) = *((uint32_t*)value);
446                         break;
447                 default:
448                         VIRTIO_DRI_ERRX(mmio_dev->vqdev,
449                                 "The driver must use 8, 16, or 32 bit wide and aligned writes for writing to the device-specific configuration space.\n"
450                                 "  See virtio-v1.0-cs04 s4.2.2.2 MMIO Device Register Layout");
451                 }
452
453                 // Increment cfg_gen because the device-specific config changed
454                 mmio_dev->cfg_gen++;
455
456                 // Notify the driver that the device-specific config changed
457                 virtio_mmio_set_cfg_irq(mmio_dev);
458                 if (mmio_dev->poke_guest)
459                         mmio_dev->poke_guest(mmio_dev->vec, mmio_dev->dest);
460
461                 return;
462         }
463
464         // virtio-v1.0-cs04 4.2.2.2 MMIO Device Register Layout
465         if (size != 4 || (offset % 4) != 0) {
466                 VIRTIO_DRI_ERRX(mmio_dev->vqdev,
467                         "The driver must only use 32 bit wide and aligned writes for writing the control registers on the MMIO transport.\n"
468                         "  See virtio-v1.0-cs04 4.2.2.2 MMIO Device Register Layout");
469         }
470
471         // virtio-v1.0-cs04 Table 4.1
472         switch (offset) {
473
474         // Device (host) features word selection.
475         case VIRTIO_MMIO_DEVICE_FEATURES_SEL:
476                 mmio_dev->dev_feat_sel = *value;
477                 break;
478
479         // Device feature flags activated by the driver
480         case VIRTIO_MMIO_DRIVER_FEATURES:
481                 // virtio-v1.0-cs04 s3.1.1 Device Initialization
482                 if (mmio_dev->status & VIRTIO_CONFIG_S_FEATURES_OK) {
483                         // NOTE: The spec just says the driver isn't allowed to
484                         // accept NEW feature bits after setting FEATURES_OK.
485                         // Although the language makes it seem like it might be
486                         // fine to let the driver un-accept features after it
487                         // sets FEATURES_OK, this would require very careful
488                         // handling, so for now we just don't allow the driver
489                         // to write to the DriverFeatures register after
490                         // FEATURES_OK is set.
491                         VIRTIO_DRI_ERRX(mmio_dev->vqdev,
492                                 "The driver may not accept (i.e. activate) new feature bits  offered by the device after setting FEATURES_OK.\n"
493                                 "  See virtio-v1.0-cs04 s3.1.1 Device Initialization");
494                 } else if (mmio_dev->dri_feat_sel) {
495                         // clear high 32 bits
496                         mmio_dev->vqdev->dri_feat &= 0xffffffff;
497                         // write high 32 bits
498                         mmio_dev->vqdev->dri_feat |= ((uint64_t)(*value) << 32);
499                 } else {
500                         // clear low 32 bits
501                         mmio_dev->vqdev->dri_feat &= ((uint64_t)0xffffffff <<
502                                                       32);
503                         // write low 32 bits
504                         mmio_dev->vqdev->dri_feat |= *value;
505                 }
506                 break;
507
508         // Activated (guest) features word selection
509         case VIRTIO_MMIO_DRIVER_FEATURES_SEL:
510                 mmio_dev->dri_feat_sel = *value;
511                 break;
512
513         // Virtual queue index
514         // Selects the virtual queue that QueueNumMax, QueueNum, QueueReady,
515         // QueueDescLow, QueueDescHigh, QueueAvailLow, QueueAvailHigh,
516         // QueueUsedLow and QueueUsedHigh apply to. The index number of the
517         // first queue is zero (0x0).
518         case VIRTIO_MMIO_QUEUE_SEL:
519         // NOTE: We must allow the driver to write whatever they want to
520         //       QueueSel, because QueueNumMax contians 0x0 for invalid
521         //       QueueSel indices.
522                 mmio_dev->qsel = *value;
523                 break;
524
525         // Virtual queue size
526         // The queue size is the number of elements in the queue, thus in the
527         // Descriptor Table, the Available Ring and the Used Ring. Writes
528         // notify the device what size queue the driver will use.
529         // This applies to the queue selected by writing to QueueSel.
530         case VIRTIO_MMIO_QUEUE_NUM:
531                 if (mmio_dev->qsel < mmio_dev->vqdev->num_vqs) {
532                         // virtio-v1.0-cs04 4.2.2.2 MMIO Device Register Layout
533                         if (*value <=
534                             mmio_dev->vqdev->vqs[mmio_dev->qsel].qnum_max)
535                                 mmio_dev->vqdev->vqs[mmio_dev->qsel].vring.num =
536                                         *value;
537                         else if ((*value != 0) && (*value & ((*value) - 1)))
538                                 VIRTIO_DRI_ERRX(mmio_dev->vqdev,
539                                         "The driver may only write powers of 2 to the QueueNum register.\n"
540                                         "  See virtio-v1.0-cs04 s2.4 Virtqueues");
541                         else
542                                 VIRTIO_DRI_ERRX(mmio_dev->vqdev,
543                                         "Attempt to write value greater than QueueNumMax to QueueNum register.");
544                 } else {
545                         VIRTIO_DRI_WARNX(mmio_dev->vqdev,
546                                 "Attempt to write QueueNum register for invalid QueueSel. QueueSel was %u, but the number of queues is %u.",
547                                 mmio_dev->qsel, mmio_dev->vqdev->num_vqs);
548                 }
549                 break;
550
551         // Virtual queue ready bit
552         // Writing one (0x1) to this register notifies the device that it can
553         // execute requests from the virtual queue selected by QueueSel.
554         case VIRTIO_MMIO_QUEUE_READY:
555                 if (mmio_dev->qsel < mmio_dev->vqdev->num_vqs) {
556                         // NOTE: For now, anything that is not a toggle between
557                         // 0x1 and 0x0 will bounce with no effect whatsoever.
558                         if (mmio_dev->vqdev->vqs[mmio_dev->qsel].qready == 0x0
559                                 && *value == 0x1) {
560                                 // Driver is trying to write 0x1 QueueReady when
561                                 // the queue is currently disabled (QueueReady
562                                 // is 0x0). We validate the vring the driver
563                                 // provided, set up an eventfd for the queue,
564                                 // set qready on the queue to 0x1, and then
565                                 // launch the service thread for the queue.
566
567                                 // Check that the host actually provided a
568                                 // service function
569                                 if (!mmio_dev->vqdev->vqs[mmio_dev->qsel].srv_fn) {
570                                         VIRTIO_DEV_ERRX(mmio_dev->vqdev,
571                                                 "The host must provide a service function for each queue on the device before the driver writes 0x1 to QueueReady. No service function found for queue %u."
572                                                 , mmio_dev->qsel);
573                                 }
574
575                                 virtio_check_vring(
576                                         &mmio_dev->vqdev->vqs[mmio_dev->qsel]);
577
578                                 mmio_dev->vqdev->vqs[mmio_dev->qsel].eventfd =
579                                         eventfd(0, 0);
580                                 mmio_dev->vqdev->vqs[mmio_dev->qsel].qready =
581                                         0x1;
582
583                                 mmio_dev->vqdev->vqs[mmio_dev->qsel].srv_th =
584                                                 vmm_run_task(vm,
585                                                                 mmio_dev->vqdev->vqs[mmio_dev->qsel].srv_fn,
586                                                                 &mmio_dev->vqdev->vqs[mmio_dev->qsel]);
587                                 if (!mmio_dev->vqdev->vqs[mmio_dev->qsel].srv_th) {
588                                         VIRTIO_DEV_ERRX(mmio_dev->vqdev,
589                                                 "vm_run_task failed when trying to start service thread after driver wrote 0x1 to QueueReady.");
590                                 }
591                         } else if (mmio_dev->vqdev->vqs[mmio_dev->qsel].qready == 0x1
592                                        && *value == 0x0) {
593                                 // Driver is trying to revoke QueueReady while
594                                 // the queue is currently enabled (QueueReady is
595                                 // 0x1).
596                                 // TODO: For now we are going to just make this
597                                 // an error.  In the future, when we actually
598                                 // decide how we want to clean up the threads,
599                                 // the sequence might look something like this:
600                                 //       1. Ask the queue's service thread to
601                                 //       exit and wait for it to finish and
602                                 //       exit.
603                                 //       2. Once it has exited, close the
604                                 //       queue's eventfd and set both the
605                                 //       eventfd and srv_th fields to 0.
606                                 //       3. Finally, write 0x0 to QueueReady.
607                                 VIRTIO_DEV_ERRX(mmio_dev->vqdev,
608                                         "Our (Akaros) MMIO device does not currently allow the driver to revoke QueueReady (i.e. change QueueReady from 0x1 to 0x0). The driver tried to revoke it, so whatever you are doing might require this ability.");
609                         }
610
611                 } else {
612                         VIRTIO_DRI_WARNX(mmio_dev->vqdev,
613                                 "Attempt to write QueueReady register for invalid QueueSel. QueueSel was %u, but the number of queues is %u.",
614                                 mmio_dev->qsel, mmio_dev->vqdev->num_vqs);
615                 }
616                 break;
617
618         // Queue notifier
619         // Writing a queue index to this register notifies the device that
620         // there are new buffers to process in that queue.
621         case VIRTIO_MMIO_QUEUE_NOTIFY:
622                 if (!(mmio_dev->status & VIRTIO_CONFIG_S_DRIVER_OK))
623                         VIRTIO_DRI_ERRX(mmio_dev->vqdev,
624                                 "Attempt to notify the device before setting the DRIVER_OK status bit.\n"
625                                 "  See virtio-v1.0-cs04 s3.1.1 Device Initialization");
626                 else if (*value < mmio_dev->vqdev->num_vqs) {
627                         notified_queue = &mmio_dev->vqdev->vqs[*value];
628
629                         // kick the queue's service thread
630                         if (notified_queue->eventfd > 0)
631                                 eventfd_write(notified_queue->eventfd, 1);
632                         else
633                                 VIRTIO_DEV_ERRX(mmio_dev->vqdev,
634                                         "You need to provide a valid eventfd on your virtio_vq so that it can be kicked when the driver writes to QueueNotify.");
635                 }
636                 break;
637
638         // Interrupt acknowledge
639         // Writing a value with bits set as defined in InterruptStatus to this
640         // register notifies the device that events causing the interrupt have
641         // been handled.
642         case VIRTIO_MMIO_INTERRUPT_ACK:
643                 if (*value & ~0x3)
644                         VIRTIO_DRI_ERRX(mmio_dev->vqdev,
645                                 "Attempt to set undefined bits in InterruptACK register.\n"
646                                 "  See virtio-v1.0-cs04 s4.2.2.1 MMIO Device Register Layout");
647                 mmio_dev->isr &= ~(*value);
648                 break;
649
650         // Device status
651         // Writing non-zero values to this register sets the status flags.
652         // Writing zero (0x0) to this register triggers a device reset.
653         case VIRTIO_MMIO_STATUS:
654                 if (*value == 0)
655                         virtio_mmio_reset(mmio_dev);
656                 else if (mmio_dev->status & ~(*value)) {
657                         // virtio-v1.0-cs04 s2.1.1. driver must NOT clear a
658                         // status bit
659                         VIRTIO_DRI_ERRX(mmio_dev->vqdev,
660                                 "The driver must not clear any device status bits, except as a result of resetting the device.\n"
661                                 "  See virtio-v1.0-cs04 s2.1.1 Device Status Field");
662                 } else if (mmio_dev->status & VIRTIO_CONFIG_S_FAILED
663                         &&   mmio_dev->status != *value) {
664                         // virtio-v1.0-cs04 s2.1.1. MUST reset before re-init if
665                         // FAILED
666                         // NOTE: This fails if the driver tries to *change* the
667                         // status after the FAILED bit is set. The driver can
668                         // set the same status again all it wants.
669                         VIRTIO_DRI_ERRX(mmio_dev->vqdev,
670                                 "The driver must reset the device after setting the FAILED status bit, before attempting to re-initialize the device.\n"
671                                 "  See virtio-v1.0-cs04 s2.1.1 Device Status Field");
672                 }
673
674                 // NOTE: If a bit is not set in value, then at this point it
675                 //       CANNOT be set in status either, because if it were
676                 //       set in status, we would have just crashed with an
677                 //       error due to the attempt to clear a status bit.
678
679                 // Now we check that status bits are set in the correct
680                 // sequence during device initialization as described
681                 // in virtio-v1.0-cs04 s3.1.1 Device Initialization
682
683                 else if ((*value & VIRTIO_CONFIG_S_DRIVER)
684                           && !(*value & VIRTIO_CONFIG_S_ACKNOWLEDGE)) {
685                         VIRTIO_DRI_ERRX(mmio_dev->vqdev,
686                                 "Tried to set DRIVER status bit before setting ACKNOWLEDGE feature bit.\n"
687                                 "  See virtio-v1.0-cs04 s3.1.1 Device Initialization");
688                 } else if ((*value & VIRTIO_CONFIG_S_FEATURES_OK)
689                          && !((*value & VIRTIO_CONFIG_S_ACKNOWLEDGE)
690                                    && (*value & VIRTIO_CONFIG_S_DRIVER))) {
691                         // All those parentheses... Lisp must be making a
692                         // comeback.
693                         VIRTIO_DRI_ERRX(mmio_dev->vqdev,
694                                 "Tried to set FEATURES_OK status bit before setting both ACKNOWLEDGE and DRIVER status bits.\n"
695                                 "  See virtio-v1.0-cs04 s3.1.1 Device Initialization");
696                 } else if ((*value & VIRTIO_CONFIG_S_DRIVER_OK)
697                          && !((*value & VIRTIO_CONFIG_S_ACKNOWLEDGE)
698                                    && (*value & VIRTIO_CONFIG_S_DRIVER)
699                                    && (*value & VIRTIO_CONFIG_S_FEATURES_OK))) {
700                         VIRTIO_DRI_ERRX(mmio_dev->vqdev,
701                                 "Tried to set DRIVER_OK status bit before setting all of ACKNOWLEDGE, DRIVER, and FEATURES_OK status bits.\n"
702                                 "  See virtio-v1.0-cs04 s3.1.1 Device Initialization");
703                 }
704
705                 // NOTE: For now, we allow the driver to set all status bits up
706                 //       through FEATURES_OK in one fell swoop. The driver is,
707                 //       however, required to re-read FEATURES_OK after setting
708                 //       it to be sure that the driver-activated features are a
709                 //       subset of those supported by the device, so it must
710                 //       make an additional write to set DRIVER_OK.
711
712                 else if ((*value & VIRTIO_CONFIG_S_DRIVER_OK)
713                          && !(mmio_dev->status & VIRTIO_CONFIG_S_FEATURES_OK)) {
714                         VIRTIO_DRI_ERRX(mmio_dev->vqdev,
715                                 "The driver may not set FEATURES_OK and DRIVER_OK status bits simultaneously. It must read back FEATURES_OK after setting it to ensure that its activated features are supported by the device before setting DRIVER_OK.\n"
716                                 "  See virtio-v1.0-cs04 s3.1.1 Device Initialization");
717                 } else {
718                         // NOTE: Don't set the FEATURES_OK bit unless the driver
719                         //       activated a valid subset of the supported
720                         //       features prior to attempting to set
721                         //       FEATURES_OK.
722                         if (!(mmio_dev->status & VIRTIO_CONFIG_S_FEATURES_OK)
723                             && (*value & VIRTIO_CONFIG_S_FEATURES_OK)) {
724
725                                 err = virtio_validate_feat(mmio_dev->vqdev,
726                                                   mmio_dev->vqdev->dri_feat);
727
728                                 if ((mmio_dev->vqdev->dri_feat
729                                         & ~mmio_dev->vqdev->dev_feat)) {
730                                         VIRTIO_DRI_WARNX(mmio_dev->vqdev,
731                                                 "The driver did not accept (e.g. activate) a subset of the features offered by the device prior to attempting to set the FEATURES_OK status bit. The bit will remain unset.\n"
732                                                 "  See virtio-v1.0-cs04 s3.1.1 Device Initialization");
733                                         *value &= ~VIRTIO_CONFIG_S_FEATURES_OK;
734                                 } else if (err) {
735                                         VIRTIO_DRI_WARNX(mmio_dev->vqdev,
736                                                 "The driver did not accept (e.g. activate) a valid combination of the features offered by the device prior to attempting to set the FEATURES_OK status bit. The bit will remain unset.\n"
737                                                 "  See virtio-v1.0-cs04 s3.1.1 Device Initialization\n"
738                                                 "  Validation Error: %s", err);
739                                         *value &= ~VIRTIO_CONFIG_S_FEATURES_OK;
740                                 }
741                         }
742                         // Device status is only a byte wide.
743                         mmio_dev->status = *value & 0xff;
744                 }
745                 break;
746
747         // Queue's Descriptor Table 64 bit long physical address, low 32
748         case VIRTIO_MMIO_QUEUE_DESC_LOW:
749                 if (mmio_dev->qsel < mmio_dev->vqdev->num_vqs) {
750                         if (mmio_dev->vqdev->vqs[mmio_dev->qsel].qready != 0)
751                                 VIRTIO_DRI_ERRX(mmio_dev->vqdev,
752                                         "Attempt to access QueueDescLow on queue %d, which has nonzero QueueReady.\n"
753                                         "  See virtio-v1.0-cs04 s4.2.2.2 MMIO Device Register Layout"
754                                         , mmio_dev->qsel);
755
756                         // clear low bits
757                         temp_ptr = (void *)
758                             ((uint64_t)mmio_dev->vqdev->vqs[mmio_dev->qsel].vring.desc
759                           & ((uint64_t)0xffffffff << 32));
760                         // write low bits
761                         temp_ptr = (void *) ((uint64_t)temp_ptr | *value);
762
763                         // virtio-v1.0-cs04 s2.4 Virtqueues
764                         if ((uint64_t)temp_ptr % 16)
765                                 VIRTIO_DRI_ERRX(mmio_dev->vqdev,
766                                         "Physical address of guest's descriptor table (%p) is misaligned. Address should be a multiple of 16.\n"
767                                         "  See virtio-v1.0-cs04 s2.4 Virtqueues");
768
769                         // assign the new value to the queue desc
770                         mmio_dev->vqdev->vqs[mmio_dev->qsel].vring.desc =
771                                 temp_ptr;
772                 } else {
773                         VIRTIO_DRI_WARNX(mmio_dev->vqdev,
774                                 "Attempt to write QueueDescLow register for invalid QueueSel. QueueSel was %u, but the number of queues is %u.",
775                                 mmio_dev->qsel, mmio_dev->vqdev->num_vqs);
776                 }
777                 break;
778
779         // Queue's Descriptor Table 64 bit long physical address, high 32
780         case VIRTIO_MMIO_QUEUE_DESC_HIGH:
781                 if (mmio_dev->qsel < mmio_dev->vqdev->num_vqs) {
782                         if (mmio_dev->vqdev->vqs[mmio_dev->qsel].qready != 0)
783                                 VIRTIO_DRI_ERRX(mmio_dev->vqdev,
784                                         "Attempt to access QueueDescHigh on queue %d, which has nonzero QueueReady.\n"
785                                         "  See virtio-v1.0-cs04 s4.2.2.2 MMIO Device Register Layout"
786                                         , mmio_dev->qsel);
787
788                         // clear high bits
789                         temp_ptr = (void *)
790                             ((uint64_t)mmio_dev->vqdev->vqs[mmio_dev->qsel].vring.desc
791                           & ((uint64_t)0xffffffff));
792                         // write high bits
793                         temp_ptr = (void *) ((uint64_t)temp_ptr
794                                           | ((uint64_t)(*value) << 32));
795
796                         // virtio-v1.0-cs04 s2.4 Virtqueues
797                         if ((uint64_t)temp_ptr % 16)
798                                 VIRTIO_DRI_ERRX(mmio_dev->vqdev,
799                                         "Physical address of guest's descriptor table (%p) is misaligned. Address should be a multiple of 16.\n"
800                                         "  See virtio-v1.0-cs04 s2.4 Virtqueues");
801
802                         // assign the new value to the queue desc
803                         mmio_dev->vqdev->vqs[mmio_dev->qsel].vring.desc = temp_ptr;
804                 } else {
805                         VIRTIO_DRI_WARNX(mmio_dev->vqdev,
806                                 "Attempt to write QueueDescHigh register for invalid QueueSel. QueueSel was %u, but the number of queues is %u."
807                                 , mmio_dev->qsel, mmio_dev->vqdev->num_vqs);
808                 }
809                 break;
810
811         // Queue's Available Ring 64 bit long physical address, low 32
812         case VIRTIO_MMIO_QUEUE_AVAIL_LOW:
813                 if (mmio_dev->qsel < mmio_dev->vqdev->num_vqs) {
814                         if (mmio_dev->vqdev->vqs[mmio_dev->qsel].qready != 0)
815                                 VIRTIO_DRI_ERRX(mmio_dev->vqdev,
816                                         "Attempt to access QueueAvailLow on queue %d, which has nonzero QueueReady.\n"
817                                         "  See virtio-v1.0-cs04 s4.2.2.2 MMIO Device Register Layout"
818                                         , mmio_dev->qsel);
819
820                         // clear low bits
821                         temp_ptr = (void *)
822                             ((uint64_t)mmio_dev->vqdev->vqs[mmio_dev->qsel].vring.avail
823                           & ((uint64_t)0xffffffff << 32));
824                         // write low bits
825                         temp_ptr = (void *) ((uint64_t)temp_ptr | *value);
826
827                         // virtio-v1.0-cs04 s2.4 Virtqueues
828                         if ((uint64_t)temp_ptr % 2)
829                                 VIRTIO_DRI_ERRX(mmio_dev->vqdev,
830                                         "Physical address of guest's available ring (%p) is misaligned. Address should be a multiple of 2.\n"
831                                         "  See virtio-v1.0-cs04 s2.4 Virtqueues");
832
833                         // assign the new value to the queue avail
834                         mmio_dev->vqdev->vqs[mmio_dev->qsel].vring.avail = temp_ptr;
835                 } else {
836                         VIRTIO_DRI_WARNX(mmio_dev->vqdev,
837                                 "Attempt to write QueueAvailLow register for invalid QueueSel. QueueSel was %u, but the number of queues is %u."
838                                 , mmio_dev->qsel, mmio_dev->vqdev->num_vqs);
839                 }
840                 break;
841
842         // Queue's Available Ring 64 bit long physical address, high 32
843         case VIRTIO_MMIO_QUEUE_AVAIL_HIGH:
844                 if (mmio_dev->qsel < mmio_dev->vqdev->num_vqs) {
845                         if (mmio_dev->vqdev->vqs[mmio_dev->qsel].qready != 0)
846                                 VIRTIO_DRI_ERRX(mmio_dev->vqdev,
847                                         "Attempt to access QueueAvailHigh on queue %d, which has nonzero QueueReady.\n"
848                                         "  See virtio-v1.0-cs04 s4.2.2.2 MMIO Device Register Layout"
849                                         , mmio_dev->qsel);
850
851                         // clear high bits
852                         temp_ptr = (void *)
853                             ((uint64_t)mmio_dev->vqdev->vqs[mmio_dev->qsel].vring.avail
854                          &  ((uint64_t)0xffffffff));
855                         // write high bits
856                         temp_ptr = (void *) ((uint64_t)temp_ptr
857                                           | ((uint64_t)(*value) << 32));
858
859                         // virtio-v1.0-cs04 s2.4 Virtqueues
860                         if ((uint64_t)temp_ptr % 2)
861                                 VIRTIO_DRI_ERRX(mmio_dev->vqdev,
862                                         "Physical address of guest's available ring (%p) is misaligned. Address should be a multiple of 2.\n"
863                                         "  See virtio-v1.0-cs04 s2.4 Virtqueues");
864
865                         // assign the new value to the queue avail
866                         mmio_dev->vqdev->vqs[mmio_dev->qsel].vring.avail = temp_ptr;
867                 } else {
868                         VIRTIO_DRI_WARNX(mmio_dev->vqdev,
869                                 "Attempt to write QueueAvailHigh register for invalid QueueSel. QueueSel was %u, but the number of queues is %u."
870                                 , mmio_dev->qsel, mmio_dev->vqdev->num_vqs);
871                 }
872                 break;
873
874         // Queue's Used Ring 64 bit long physical address, low 32
875         case VIRTIO_MMIO_QUEUE_USED_LOW:
876                 if (mmio_dev->qsel < mmio_dev->vqdev->num_vqs) {
877                         if (mmio_dev->vqdev->vqs[mmio_dev->qsel].qready != 0)
878                                 VIRTIO_DRI_ERRX(mmio_dev->vqdev,
879                                         "Attempt to access QueueUsedLow on queue %d, which has nonzero QueueReady.\n"
880                                         "  See virtio-v1.0-cs04 s4.2.2.2 MMIO Device Register Layout"
881                                         , mmio_dev->qsel);
882
883                         // clear low bits
884                         temp_ptr = (void *)
885                             ((uint64_t)mmio_dev->vqdev->vqs[mmio_dev->qsel].vring.used
886                           & ((uint64_t)0xffffffff << 32));
887                         // write low bits
888                         temp_ptr = (void *) ((uint64_t)temp_ptr | *value);
889
890                         // virtio-v1.0-cs04 s2.4 Virtqueues
891                         if ((uint64_t)temp_ptr % 4)
892                                 VIRTIO_DRI_ERRX(mmio_dev->vqdev,
893                                         "Physical address of guest's used ring (%p) is misaligned. Address should be a multiple of 4.\n"
894                                         "  See virtio-v1.0-cs04 s2.4 Virtqueues");
895
896                         // assign the new value to the queue used
897                         mmio_dev->vqdev->vqs[mmio_dev->qsel].vring.used = temp_ptr;
898                 } else {
899                         VIRTIO_DRI_WARNX(mmio_dev->vqdev,
900                                 "Attempt to write QueueUsedLow register for invalid QueueSel. QueueSel was %u, but the number of queues is %u."
901                                 , mmio_dev->qsel, mmio_dev->vqdev->num_vqs);
902                 }
903                 break;
904
905         // Queue's Used Ring 64 bit long physical address, high 32
906         case VIRTIO_MMIO_QUEUE_USED_HIGH:
907                 if (mmio_dev->qsel < mmio_dev->vqdev->num_vqs) {
908                         if (mmio_dev->vqdev->vqs[mmio_dev->qsel].qready != 0)
909                                 VIRTIO_DRI_ERRX(mmio_dev->vqdev,
910                                         "Attempt to access QueueUsedHigh on queue %d, which has nonzero QueueReady.\n"
911                                         "  See virtio-v1.0-cs04 s4.2.2.2 MMIO Device Register Layout"
912                                         , mmio_dev->qsel);
913
914                         // clear high bits
915                         temp_ptr = (void *)
916                             ((uint64_t)mmio_dev->vqdev->vqs[mmio_dev->qsel].vring.used
917                           & ((uint64_t)0xffffffff));
918                         // write high bits
919                         temp_ptr = (void *) ((uint64_t)temp_ptr
920                                           | ((uint64_t)(*value) << 32));
921
922                         // virtio-v1.0-cs04 s2.4 Virtqueues
923                         if ((uint64_t)temp_ptr % 4)
924                                 VIRTIO_DRI_ERRX(mmio_dev->vqdev,
925                                         "Physical address of guest's used ring (%p) is misaligned. Address should be a multiple of 4.\n"
926                                         "  See virtio-v1.0-cs04 s2.4 Virtqueues");
927
928                         // assign the new value to the queue used
929                         mmio_dev->vqdev->vqs[mmio_dev->qsel].vring.used = temp_ptr;
930                 } else {
931                         VIRTIO_DRI_WARNX(mmio_dev->vqdev,
932                                 "Attempt to write QueueUsedHigh register for invalid QueueSel. QueueSel was %u, but the number of queues is %u."
933                                 , mmio_dev->qsel, mmio_dev->vqdev->num_vqs);
934                 }
935                 break;
936
937         // Read-only register offsets:
938         case VIRTIO_MMIO_MAGIC_VALUE:
939         case VIRTIO_MMIO_VERSION:
940         case VIRTIO_MMIO_DEVICE_ID:
941         case VIRTIO_MMIO_VENDOR_ID:
942         case VIRTIO_MMIO_DEVICE_FEATURES:
943         case VIRTIO_MMIO_QUEUE_NUM_MAX:
944         case VIRTIO_MMIO_INTERRUPT_STATUS:
945         case VIRTIO_MMIO_CONFIG_GENERATION:
946                 // Write to read-only register
947                 VIRTIO_DRI_ERRX(mmio_dev->vqdev,
948                         "Attempt to write read-only device register offset 0x%x.",
949                         offset);
950                 break;
951         default:
952                 // Bad register offset
953                 VIRTIO_DRI_ERRX(mmio_dev->vqdev,
954                         "Attempt to write invalid device register offset 0x%x.",
955                         offset);
956                 break;
957         }
958 }