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