1 /* Virtio MMIO bindings
3 * Copyright (c) 2011 Linaro Limited
4 * Copyright (C) 1991-2016, the Linux Kernel authors
5 * Copyright (c) 2016 Google Inc.
8 * Peter Maydell <peter.maydell@linaro.org>
9 * Rusty Russell <rusty@rustcorp.com.au>
10 * Michael Taufen <mtaufen@gmail.com>
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.
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.
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.
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
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
42 #include <sys/eventfd.h>
43 #include <vmm/virtio_config.h>
44 #include <vmm/virtio_mmio.h>
46 #define VIRT_MAGIC 0x74726976 /* 'virt' */
48 #define VIRT_MMIO_VERSION 0x2
50 #define VIRT_MMIO_VENDOR 0x52414B41 /* 'AKAR' */
52 void virtio_mmio_set_vring_irq(struct virtio_mmio_dev *mmio_dev)
54 mmio_dev->isr |= VIRTIO_MMIO_INT_VRING;
57 void virtio_mmio_set_cfg_irq(struct virtio_mmio_dev *mmio_dev)
59 mmio_dev->isr |= VIRTIO_MMIO_INT_CONFIG;
62 static void virtio_mmio_reset_cfg(struct virtio_mmio_dev *mmio_dev)
64 if (!mmio_dev->vqdev->cfg || mmio_dev->vqdev->cfg_sz == 0)
65 VIRTIO_DEV_WARNX(mmio_dev->vqdev,
66 "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 // If a default device-specific configuration is provided, copy that
69 // into the device-specific configuration space. Otherwise, clear the
70 // device-specific configuration space.
71 if (mmio_dev->vqdev->cfg_d)
72 memcpy(mmio_dev->vqdev->cfg, mmio_dev->vqdev->cfg_d,
73 mmio_dev->vqdev->cfg_sz);
75 memset(mmio_dev->vqdev->cfg, 0x0, mmio_dev->vqdev->cfg_sz);
77 // Increment the ConfigGeneration, since the config space just got reset.
78 // We can't simply set it to 0, because we must ensure that it changes when
79 // the config space changes and it might currently be set to 0.
83 // TODO: virtio_mmio_reset could use a careful audit. We have not yet
84 // encountered a scenario where the driver resets the device
85 // while lots of things are in-flight; thus far we have only seen
86 // device resets prior to the first initialization sequence.
87 static void virtio_mmio_reset(struct virtio_mmio_dev *mmio_dev)
94 fprintf(stderr, "virtio mmio device reset: %s\n", mmio_dev->vqdev->name);
96 // Clear any driver-activated feature bits
97 mmio_dev->vqdev->dri_feat = 0;
99 // virtio-v1.0-cs04 s2.1.2 Device Status Field
100 // The device MUST initialize device status to 0 upon reset
101 mmio_dev->status = 0;
103 // virtio-v1.0-cs04 s4.2.2.1 MMIO Device Register Layout
104 // Upon reset, the device MUST clear all bits in InterruptStatus
107 // virtio-v1.0-cs04 s4.2.2.1 MMIO Device Register Layout
108 // Upon reset, the device MUST clear...ready bits in the QueueReady
109 // register for all queues in the device.
110 for (i = 0; i < mmio_dev->vqdev->num_vqs; ++i) {
111 if (mmio_dev->vqdev->vqs[i].srv_th) {
112 // FIXME! PLEASE, FIXME!
113 // TODO: For now we are going to make device resets an error
114 // once service threads exist on the queues. This is obviously
115 // not sustainable, because the driver needs to be able
116 // to reset the device after certain errors occur.
118 // In the future, when we actually decide how we want
119 // to clean up the threads, the sequence might look
120 // something like this:
121 // 1. Ask the queue's service thread to exit and wait
122 // for it to finish and exit.
123 // 2. Once it has exited, close the queue's eventfd
124 // and set both the eventfd and srv_th fields to 0.
125 VIRTIO_DEV_ERRX(mmio_dev->vqdev,
126 "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.");
129 mmio_dev->vqdev->vqs[i].qready = 0;
130 mmio_dev->vqdev->vqs[i].last_avail = 0;
133 virtio_mmio_reset_cfg(mmio_dev);
136 uint32_t virtio_mmio_rd(struct virtio_mmio_dev *mmio_dev,
137 uint64_t gpa, uint8_t size)
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
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) {
148 case VIRTIO_MMIO_MAGIC_VALUE:
150 case VIRTIO_MMIO_VERSION:
151 return VIRT_MMIO_VERSION;
152 case VIRTIO_MMIO_VENDOR_ID:
153 return VIRT_MMIO_VENDOR;
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");
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);
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);
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");
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;
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.");
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");
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.");
219 target = (uint8_t*)((uint64_t)mmio_dev->vqdev->cfg + offset);
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.
230 return *((uint8_t*)target);
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);
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);
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");
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");
257 // virtio-v1.0-cs04 Table 4.1
260 // 0x74726976 (a Little Endian equivalent of the “virt” string).
261 case VIRTIO_MMIO_MAGIC_VALUE:
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;
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;
275 // Virtio Subsystem Vendor ID
276 case VIRTIO_MMIO_VENDOR_ID:
277 return VIRT_MMIO_VENDOR;
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");
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
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)
300 return mmio_dev->vqdev->vqs[mmio_dev->qsel].qnum_max;
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)
307 return mmio_dev->vqdev->vqs[mmio_dev->qsel].qready;
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;
317 case VIRTIO_MMIO_STATUS:
318 return mmio_dev->status;
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;
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.",
347 // Bad register offset
348 VIRTIO_DRI_ERRX(mmio_dev->vqdev,
349 "Attempt to read invalid device register offset 0x%x.",
357 void virtio_mmio_wr(struct virtio_mmio_dev *mmio_dev, uint64_t gpa,
358 uint8_t size, uint32_t *value)
360 uint64_t offset = gpa - mmio_dev->addr;
361 struct virtio_vq *notified_queue;
362 uint8_t *target; // target of write to device-specific config space
363 void *temp_ptr; // for facilitating bitwise ops on pointers
364 const char *err; // returned err strings
366 if (!mmio_dev->vqdev) {
367 // If there is no vqdev on the mmio_dev,
368 // we just make all registers write-ignored.
372 // virtio-v1.0-cs04 s4.2.3.1.1 Device Initialization (MMIO)
373 if (mmio_dev->vqdev->dev_id == 0)
374 VIRTIO_DRI_ERRX(mmio_dev->vqdev,
375 "Attempt to write to a device whose DeviceID is 0x0.\n"
376 " See virtio-v1.0-cs04 s4.2.3.1.1 Device Initialization");
378 // Warn if FAILED and trying to do something that is definitely not a reset.
379 // virtio-v1.0-cs04 s2.1.1 Device Status Field
380 if (offset != VIRTIO_MMIO_STATUS
381 && (mmio_dev->status & VIRTIO_CONFIG_S_FAILED))
382 VIRTIO_DRI_WARNX(mmio_dev->vqdev,
383 "The FAILED status bit is set. The driver should probably reset the device before continuing.\n"
384 " See virtio-v1.0-cs04 s2.1.1 Device Status Field");
386 // TODO: I could only do a limited amount of testing on the device-
387 // specific config space, because I was limited to seeing what
388 // the guest driver for the console device would do. You may
389 // run into issues when you implement virtio-net, since that
390 // does more with the device-specific config. (In fact, I don't think
391 // the guest driver ever even tried to write the device-specific
392 // config space for the console, so this section is entirely untested)
393 if (offset >= VIRTIO_MMIO_CONFIG) {
394 offset -= VIRTIO_MMIO_CONFIG;
396 if (!mmio_dev->vqdev->cfg || mmio_dev->vqdev->cfg_sz == 0) {
397 VIRTIO_DEV_ERRX(mmio_dev->vqdev,
398 "Driver attempted to write to the device-specific configuration space, but the device failed to provide it.");
401 // virtio-v1.0-cs04 s3.1.1 Device Initialization
402 if (!(mmio_dev->status & VIRTIO_CONFIG_S_FEATURES_OK)) {
403 VIRTIO_DRI_ERRX(mmio_dev->vqdev,
404 "Driver attempted to write the device-specific configuration space before setting the FEATURES_OK status bit.\n"
405 " See virtio-v1.0-cs04 s3.1.1 Device Initialization");
408 if ((offset + size) > mmio_dev->vqdev->cfg_sz
409 || (offset + size) < offset) {
410 VIRTIO_DRI_ERRX(mmio_dev->vqdev,
411 "Attempt to write invalid offset of the device specific configuration space, or (offset + write width) wrapped around.");
414 target = (uint8_t*)((uint64_t)mmio_dev->vqdev->cfg + offset);
416 // TODO: Check that size matches the size of the field at offset
417 // for the given device? i.e. virtio_console_config.rows
418 // should only be accessible via a 16 bit read or write.
419 // I haven't done this yet, it will be a significant
420 // undertaking and maintenance commitment, because you
421 // will have to do it for every virtio device you
422 // want to use in the future.
425 *((uint8_t*)target) = *((uint8_t*)value);
428 if ((uint64_t)target % 2 != 0)
429 VIRTIO_DRI_ERRX(mmio_dev->vqdev,
430 "The driver must use 16 bit aligned writes for writing to 16 bit values in the device-specific configuration space.\n"
431 " See virtio-v1.0-cs04 s4.2.2.2 MMIO Device Register Layout");
432 *((uint16_t*)target) = *((uint16_t*)value);
435 if ((uint64_t)target % 4 != 0)
436 VIRTIO_DRI_ERRX(mmio_dev->vqdev,
437 "The driver must use 32 bit aligned writes for writing to 32 or 64 bit values in the device-specific configuration space.\n"
438 " See virtio-v1.0-cs04 s4.2.2.2 MMIO Device Register Layout");
439 *((uint32_t*)target) = *((uint32_t*)value);
442 VIRTIO_DRI_ERRX(mmio_dev->vqdev,
443 "The driver must use 8, 16, or 32 bit wide and aligned writes for writing to the device-specific configuration space.\n"
444 " See virtio-v1.0-cs04 s4.2.2.2 MMIO Device Register Layout");
447 // Increment cfg_gen because the device-specific config changed
450 // Notify the driver that the device-specific config changed
451 virtio_mmio_set_cfg_irq(mmio_dev);
452 if (mmio_dev->poke_guest)
453 mmio_dev->poke_guest();
458 // virtio-v1.0-cs04 4.2.2.2 MMIO Device Register Layout
459 if (size != 4 || (offset % 4) != 0) {
460 VIRTIO_DRI_ERRX(mmio_dev->vqdev,
461 "The driver must only use 32 bit wide and aligned writes for writing the control registers on the MMIO transport.\n"
462 " See virtio-v1.0-cs04 4.2.2.2 MMIO Device Register Layout");
465 // virtio-v1.0-cs04 Table 4.1
468 // Device (host) features word selection.
469 case VIRTIO_MMIO_DEVICE_FEATURES_SEL:
470 mmio_dev->dev_feat_sel = *value;
473 // Device feature flags activated by the driver
474 case VIRTIO_MMIO_DRIVER_FEATURES:
475 // virtio-v1.0-cs04 s3.1.1 Device Initialization
476 if (mmio_dev->status & VIRTIO_CONFIG_S_FEATURES_OK) {
477 // NOTE: The spec just says the driver isn't allowed to accept
478 // NEW feature bits after setting FEATURES_OK. Although
479 // the language makes it seem like it might be fine to
480 // let the driver un-accept features after it sets
481 // FEATURES_OK, this would require very careful handling,
482 // so for now we just don't allow the driver to write to
483 // the DriverFeatures register after FEATURES_OK is set.
484 VIRTIO_DRI_ERRX(mmio_dev->vqdev,
485 "The driver may not accept (i.e. activate) new feature bits offered by the device after setting FEATURES_OK.\n"
486 " See virtio-v1.0-cs04 s3.1.1 Device Initialization");
487 } else if (mmio_dev->dri_feat_sel) {
488 // clear high 32 bits
489 mmio_dev->vqdev->dri_feat &= 0xffffffff;
490 // write high 32 bits
491 mmio_dev->vqdev->dri_feat |= ((uint64_t)(*value) << 32);
494 mmio_dev->vqdev->dri_feat &= ((uint64_t)0xffffffff << 32);
496 mmio_dev->vqdev->dri_feat |= *value;
500 // Activated (guest) features word selection
501 case VIRTIO_MMIO_DRIVER_FEATURES_SEL:
502 mmio_dev->dri_feat_sel = *value;
505 // Virtual queue index
506 // Selects the virtual queue that QueueNumMax, QueueNum, QueueReady,
507 // QueueDescLow, QueueDescHigh, QueueAvailLow, QueueAvailHigh,
508 // QueueUsedLow and QueueUsedHigh apply to. The index number of the
509 // first queue is zero (0x0).
510 case VIRTIO_MMIO_QUEUE_SEL:
511 // NOTE: We must allow the driver to write whatever they want to
512 // QueueSel, because QueueNumMax contians 0x0 for invalid
514 mmio_dev->qsel = *value;
517 // Virtual queue size
518 // The queue size is the number of elements in the queue, thus in the
519 // Descriptor Table, the Available Ring and the Used Ring. Writes
520 // notify the device what size queue the driver will use.
521 // This applies to the queue selected by writing to QueueSel.
522 case VIRTIO_MMIO_QUEUE_NUM:
523 if (mmio_dev->qsel < mmio_dev->vqdev->num_vqs) {
524 // virtio-v1.0-cs04 4.2.2.2 MMIO Device Register Layout
525 if (*value <= mmio_dev->vqdev->vqs[mmio_dev->qsel].qnum_max)
526 mmio_dev->vqdev->vqs[mmio_dev->qsel].vring.num = *value;
527 else if ((*value != 0) && (*value & ((*value) - 1)))
528 VIRTIO_DRI_ERRX(mmio_dev->vqdev,
529 "The driver may only write powers of 2 to the QueueNum register.\n"
530 " See virtio-v1.0-cs04 s2.4 Virtqueues");
532 VIRTIO_DRI_ERRX(mmio_dev->vqdev,
533 "Attempt to write value greater than QueueNumMax to QueueNum register.");
535 VIRTIO_DRI_WARNX(mmio_dev->vqdev,
536 "Attempt to write QueueNum register for invalid QueueSel. QueueSel was %u, but the number of queues is %u.",
537 mmio_dev->qsel, mmio_dev->vqdev->num_vqs);
541 // Virtual queue ready bit
542 // Writing one (0x1) to this register notifies the device that it can
543 // execute requests from the virtual queue selected by QueueSel.
544 case VIRTIO_MMIO_QUEUE_READY:
545 if (mmio_dev->qsel < mmio_dev->vqdev->num_vqs) {
546 // NOTE: For now, anything that is not a toggle between
547 // 0x1 and 0x0 will bounce with no effect whatsoever.
548 if (mmio_dev->vqdev->vqs[mmio_dev->qsel].qready == 0x0
550 // Driver is trying to write 0x1 QueueReady when the queue
551 // is currently disabled (QueueReady is 0x0). We validate
552 // the vring the driver provided, set up an eventfd for the
553 // queue, set qready on the queue to 0x1, and then launch
554 // the service thread for the queue.
556 // Check that the host actually provided a service function
557 if (!mmio_dev->vqdev->vqs[mmio_dev->qsel].srv_fn) {
558 VIRTIO_DEV_ERRX(mmio_dev->vqdev,
559 "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."
563 virtio_check_vring(&mmio_dev->vqdev->vqs[mmio_dev->qsel]);
565 mmio_dev->vqdev->vqs[mmio_dev->qsel].eventfd = eventfd(0, 0);
566 mmio_dev->vqdev->vqs[mmio_dev->qsel].qready = 0x1;
570 &mmio_dev->vqdev->vqs[mmio_dev->qsel].srv_th,
571 // no special thread attrs
573 // service function that srv_th starts in
574 mmio_dev->vqdev->vqs[mmio_dev->qsel].srv_fn,
575 // arg passed to srv_fn is the vq itself
576 &mmio_dev->vqdev->vqs[mmio_dev->qsel]))
577 VIRTIO_DEV_ERRX(mmio_dev->vqdev,
578 "pthread_create failed when trying to start service thread after driver wrote 0x1 to QueueReady.");
580 } else if (mmio_dev->vqdev->vqs[mmio_dev->qsel].qready == 0x1
582 // Driver is trying to revoke QueueReady while the queue is
583 // currently enabled (QueueReady is 0x1).
584 // TODO: For now we are going to just make this an error.
585 // In the future, when we actually decide how we want
586 // to clean up the threads, the sequence might look
587 // something like this:
588 // 1. Ask the queue's service thread to exit and wait
589 // for it to finish and exit.
590 // 2. Once it has exited, close the queue's eventfd
591 // and set both the eventfd and srv_th fields to 0.
592 // 3. Finally, write 0x0 to QueueReady.
593 VIRTIO_DEV_ERRX(mmio_dev->vqdev,
594 "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.");
598 VIRTIO_DRI_WARNX(mmio_dev->vqdev,
599 "Attempt to write QueueReady register for invalid QueueSel. QueueSel was %u, but the number of queues is %u.",
600 mmio_dev->qsel, mmio_dev->vqdev->num_vqs);
605 // Writing a queue index to this register notifies the device that
606 // there are new buffers to process in that queue.
607 case VIRTIO_MMIO_QUEUE_NOTIFY:
608 if (!(mmio_dev->status & VIRTIO_CONFIG_S_DRIVER_OK))
609 VIRTIO_DRI_ERRX(mmio_dev->vqdev,
610 "Attempt to notify the device before setting the DRIVER_OK status bit.\n"
611 " See virtio-v1.0-cs04 s3.1.1 Device Initialization");
612 else if (*value < mmio_dev->vqdev->num_vqs) {
613 notified_queue = &mmio_dev->vqdev->vqs[*value];
615 // kick the queue's service thread
616 if (notified_queue->eventfd > 0)
617 eventfd_write(notified_queue->eventfd, 1);
619 VIRTIO_DEV_ERRX(mmio_dev->vqdev,
620 "You need to provide a valid eventfd on your virtio_vq so that it can be kicked when the driver writes to QueueNotify.");
624 // Interrupt acknowledge
625 // Writing a value with bits set as defined in InterruptStatus to this
626 // register notifies the device that events causing the interrupt have
628 case VIRTIO_MMIO_INTERRUPT_ACK:
630 VIRTIO_DRI_ERRX(mmio_dev->vqdev,
631 "Attempt to set undefined bits in InterruptACK register.\n"
632 " See virtio-v1.0-cs04 s4.2.2.1 MMIO Device Register Layout");
633 mmio_dev->isr &= ~(*value);
637 // Writing non-zero values to this register sets the status flags.
638 // Writing zero (0x0) to this register triggers a device reset.
639 case VIRTIO_MMIO_STATUS:
641 virtio_mmio_reset(mmio_dev);
642 else if (mmio_dev->status & ~(*value)) {
643 // virtio-v1.0-cs04 s2.1.1. driver must NOT clear a status bit
644 VIRTIO_DRI_ERRX(mmio_dev->vqdev,
645 "The driver must not clear any device status bits, except as a result of resetting the device.\n"
646 " See virtio-v1.0-cs04 s2.1.1 Device Status Field");
647 } else if (mmio_dev->status & VIRTIO_CONFIG_S_FAILED
648 && mmio_dev->status != *value) {
649 // virtio-v1.0-cs04 s2.1.1. MUST reset before re-init if FAILED
650 // NOTE: This fails if the driver tries to *change* the status
651 // after the FAILED bit is set. The driver can set the
652 // same status again all it wants.
653 VIRTIO_DRI_ERRX(mmio_dev->vqdev,
654 "The driver must reset the device after setting the FAILED status bit, before attempting to re-initialize the device.\n"
655 " See virtio-v1.0-cs04 s2.1.1 Device Status Field");
658 // NOTE: If a bit is not set in value, then at this point it
659 // CANNOT be set in status either, because if it were
660 // set in status, we would have just crashed with an
661 // error due to the attempt to clear a status bit.
663 // Now we check that status bits are set in the correct
664 // sequence during device initialization as described
665 // in virtio-v1.0-cs04 s3.1.1 Device Initialization
667 else if ((*value & VIRTIO_CONFIG_S_DRIVER)
668 && !(*value & VIRTIO_CONFIG_S_ACKNOWLEDGE)) {
669 VIRTIO_DRI_ERRX(mmio_dev->vqdev,
670 "Tried to set DRIVER status bit before setting ACKNOWLEDGE feature bit.\n"
671 " See virtio-v1.0-cs04 s3.1.1 Device Initialization");
672 } else if ((*value & VIRTIO_CONFIG_S_FEATURES_OK)
673 && !((*value & VIRTIO_CONFIG_S_ACKNOWLEDGE)
674 && (*value & VIRTIO_CONFIG_S_DRIVER))) {
675 // All those parentheses... Lisp must be making a comeback.
676 VIRTIO_DRI_ERRX(mmio_dev->vqdev,
677 "Tried to set FEATURES_OK status bit before setting both ACKNOWLEDGE and DRIVER status bits.\n"
678 " See virtio-v1.0-cs04 s3.1.1 Device Initialization");
679 } else if ((*value & VIRTIO_CONFIG_S_DRIVER_OK)
680 && !((*value & VIRTIO_CONFIG_S_ACKNOWLEDGE)
681 && (*value & VIRTIO_CONFIG_S_DRIVER)
682 && (*value & VIRTIO_CONFIG_S_FEATURES_OK))) {
683 VIRTIO_DRI_ERRX(mmio_dev->vqdev,
684 "Tried to set DRIVER_OK status bit before setting all of ACKNOWLEDGE, DRIVER, and FEATURES_OK status bits.\n"
685 " See virtio-v1.0-cs04 s3.1.1 Device Initialization");
688 // NOTE: For now, we allow the driver to set all status bits up
689 // through FEATURES_OK in one fell swoop. The driver is,
690 // however, required to re-read FEATURES_OK after setting it
691 // to be sure that the driver-activated features are a subset
692 // of those supported by the device, so it must make an
693 // additional write to set DRIVER_OK.
695 else if ((*value & VIRTIO_CONFIG_S_DRIVER_OK)
696 && !(mmio_dev->status & VIRTIO_CONFIG_S_FEATURES_OK)) {
697 VIRTIO_DRI_ERRX(mmio_dev->vqdev,
698 "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"
699 " See virtio-v1.0-cs04 s3.1.1 Device Initialization");
701 // NOTE: Don't set the FEATURES_OK bit unless the driver
702 // activated a valid subset of the supported features
703 // prior to attempting to set FEATURES_OK.
704 if (!(mmio_dev->status & VIRTIO_CONFIG_S_FEATURES_OK)
705 && (*value & VIRTIO_CONFIG_S_FEATURES_OK)) {
707 err = virtio_validate_feat(mmio_dev->vqdev,
708 mmio_dev->vqdev->dri_feat);
710 if ((mmio_dev->vqdev->dri_feat
711 & ~mmio_dev->vqdev->dev_feat)) {
712 VIRTIO_DRI_WARNX(mmio_dev->vqdev,
713 "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"
714 " See virtio-v1.0-cs04 s3.1.1 Device Initialization");
715 *value &= ~VIRTIO_CONFIG_S_FEATURES_OK;
717 VIRTIO_DRI_WARNX(mmio_dev->vqdev,
718 "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"
719 " See virtio-v1.0-cs04 s3.1.1 Device Initialization\n"
720 " Validation Error: %s", err);
721 *value &= ~VIRTIO_CONFIG_S_FEATURES_OK;
724 // Device status is only a byte wide.
725 mmio_dev->status = *value & 0xff;
729 // Queue's Descriptor Table 64 bit long physical address, low 32
730 case VIRTIO_MMIO_QUEUE_DESC_LOW:
731 if (mmio_dev->qsel < mmio_dev->vqdev->num_vqs) {
732 if (mmio_dev->vqdev->vqs[mmio_dev->qsel].qready != 0)
733 VIRTIO_DRI_ERRX(mmio_dev->vqdev,
734 "Attempt to access QueueDescLow on queue %d, which has nonzero QueueReady.\n"
735 " See virtio-v1.0-cs04 s4.2.2.2 MMIO Device Register Layout"
740 ((uint64_t)mmio_dev->vqdev->vqs[mmio_dev->qsel].vring.desc
741 & ((uint64_t)0xffffffff << 32));
743 temp_ptr = (void *) ((uint64_t)temp_ptr | *value);
745 // virtio-v1.0-cs04 s2.4 Virtqueues
746 if ((uint64_t)temp_ptr % 16)
747 VIRTIO_DRI_ERRX(mmio_dev->vqdev,
748 "Physical address of guest's descriptor table (%p) is misaligned. Address should be a multiple of 16.\n"
749 " See virtio-v1.0-cs04 s2.4 Virtqueues");
751 // assign the new value to the queue desc
752 mmio_dev->vqdev->vqs[mmio_dev->qsel].vring.desc = temp_ptr;
754 VIRTIO_DRI_WARNX(mmio_dev->vqdev,
755 "Attempt to write QueueDescLow register for invalid QueueSel. QueueSel was %u, but the number of queues is %u.",
756 mmio_dev->qsel, mmio_dev->vqdev->num_vqs);
760 // Queue's Descriptor Table 64 bit long physical address, high 32
761 case VIRTIO_MMIO_QUEUE_DESC_HIGH:
762 if (mmio_dev->qsel < mmio_dev->vqdev->num_vqs) {
763 if (mmio_dev->vqdev->vqs[mmio_dev->qsel].qready != 0)
764 VIRTIO_DRI_ERRX(mmio_dev->vqdev,
765 "Attempt to access QueueDescHigh on queue %d, which has nonzero QueueReady.\n"
766 " See virtio-v1.0-cs04 s4.2.2.2 MMIO Device Register Layout"
771 ((uint64_t)mmio_dev->vqdev->vqs[mmio_dev->qsel].vring.desc
772 & ((uint64_t)0xffffffff));
774 temp_ptr = (void *) ((uint64_t)temp_ptr
775 | ((uint64_t)(*value) << 32));
777 // virtio-v1.0-cs04 s2.4 Virtqueues
778 if ((uint64_t)temp_ptr % 16)
779 VIRTIO_DRI_ERRX(mmio_dev->vqdev,
780 "Physical address of guest's descriptor table (%p) is misaligned. Address should be a multiple of 16.\n"
781 " See virtio-v1.0-cs04 s2.4 Virtqueues");
783 // assign the new value to the queue desc
784 mmio_dev->vqdev->vqs[mmio_dev->qsel].vring.desc = temp_ptr;
786 VIRTIO_DRI_WARNX(mmio_dev->vqdev,
787 "Attempt to write QueueDescHigh register for invalid QueueSel. QueueSel was %u, but the number of queues is %u."
788 , mmio_dev->qsel, mmio_dev->vqdev->num_vqs);
792 // Queue's Available Ring 64 bit long physical address, low 32
793 case VIRTIO_MMIO_QUEUE_AVAIL_LOW:
794 if (mmio_dev->qsel < mmio_dev->vqdev->num_vqs) {
795 if (mmio_dev->vqdev->vqs[mmio_dev->qsel].qready != 0)
796 VIRTIO_DRI_ERRX(mmio_dev->vqdev,
797 "Attempt to access QueueAvailLow on queue %d, which has nonzero QueueReady.\n"
798 " See virtio-v1.0-cs04 s4.2.2.2 MMIO Device Register Layout"
803 ((uint64_t)mmio_dev->vqdev->vqs[mmio_dev->qsel].vring.avail
804 & ((uint64_t)0xffffffff << 32));
806 temp_ptr = (void *) ((uint64_t)temp_ptr | *value);
808 // virtio-v1.0-cs04 s2.4 Virtqueues
809 if ((uint64_t)temp_ptr % 2)
810 VIRTIO_DRI_ERRX(mmio_dev->vqdev,
811 "Physical address of guest's available ring (%p) is misaligned. Address should be a multiple of 2.\n"
812 " See virtio-v1.0-cs04 s2.4 Virtqueues");
814 // assign the new value to the queue avail
815 mmio_dev->vqdev->vqs[mmio_dev->qsel].vring.avail = temp_ptr;
817 VIRTIO_DRI_WARNX(mmio_dev->vqdev,
818 "Attempt to write QueueAvailLow register for invalid QueueSel. QueueSel was %u, but the number of queues is %u."
819 , mmio_dev->qsel, mmio_dev->vqdev->num_vqs);
823 // Queue's Available Ring 64 bit long physical address, high 32
824 case VIRTIO_MMIO_QUEUE_AVAIL_HIGH:
825 if (mmio_dev->qsel < mmio_dev->vqdev->num_vqs) {
826 if (mmio_dev->vqdev->vqs[mmio_dev->qsel].qready != 0)
827 VIRTIO_DRI_ERRX(mmio_dev->vqdev,
828 "Attempt to access QueueAvailHigh on queue %d, which has nonzero QueueReady.\n"
829 " See virtio-v1.0-cs04 s4.2.2.2 MMIO Device Register Layout"
834 ((uint64_t)mmio_dev->vqdev->vqs[mmio_dev->qsel].vring.avail
835 & ((uint64_t)0xffffffff));
837 temp_ptr = (void *) ((uint64_t)temp_ptr
838 | ((uint64_t)(*value) << 32));
840 // virtio-v1.0-cs04 s2.4 Virtqueues
841 if ((uint64_t)temp_ptr % 2)
842 VIRTIO_DRI_ERRX(mmio_dev->vqdev,
843 "Physical address of guest's available ring (%p) is misaligned. Address should be a multiple of 2.\n"
844 " See virtio-v1.0-cs04 s2.4 Virtqueues");
846 // assign the new value to the queue avail
847 mmio_dev->vqdev->vqs[mmio_dev->qsel].vring.avail = temp_ptr;
849 VIRTIO_DRI_WARNX(mmio_dev->vqdev,
850 "Attempt to write QueueAvailHigh register for invalid QueueSel. QueueSel was %u, but the number of queues is %u."
851 , mmio_dev->qsel, mmio_dev->vqdev->num_vqs);
855 // Queue's Used Ring 64 bit long physical address, low 32
856 case VIRTIO_MMIO_QUEUE_USED_LOW:
857 if (mmio_dev->qsel < mmio_dev->vqdev->num_vqs) {
858 if (mmio_dev->vqdev->vqs[mmio_dev->qsel].qready != 0)
859 VIRTIO_DRI_ERRX(mmio_dev->vqdev,
860 "Attempt to access QueueUsedLow on queue %d, which has nonzero QueueReady.\n"
861 " See virtio-v1.0-cs04 s4.2.2.2 MMIO Device Register Layout"
866 ((uint64_t)mmio_dev->vqdev->vqs[mmio_dev->qsel].vring.used
867 & ((uint64_t)0xffffffff << 32));
869 temp_ptr = (void *) ((uint64_t)temp_ptr | *value);
871 // virtio-v1.0-cs04 s2.4 Virtqueues
872 if ((uint64_t)temp_ptr % 4)
873 VIRTIO_DRI_ERRX(mmio_dev->vqdev,
874 "Physical address of guest's used ring (%p) is misaligned. Address should be a multiple of 4.\n"
875 " See virtio-v1.0-cs04 s2.4 Virtqueues");
877 // assign the new value to the queue used
878 mmio_dev->vqdev->vqs[mmio_dev->qsel].vring.used = temp_ptr;
880 VIRTIO_DRI_WARNX(mmio_dev->vqdev,
881 "Attempt to write QueueUsedLow register for invalid QueueSel. QueueSel was %u, but the number of queues is %u."
882 , mmio_dev->qsel, mmio_dev->vqdev->num_vqs);
886 // Queue's Used Ring 64 bit long physical address, high 32
887 case VIRTIO_MMIO_QUEUE_USED_HIGH:
888 if (mmio_dev->qsel < mmio_dev->vqdev->num_vqs) {
889 if (mmio_dev->vqdev->vqs[mmio_dev->qsel].qready != 0)
890 VIRTIO_DRI_ERRX(mmio_dev->vqdev,
891 "Attempt to access QueueUsedHigh on queue %d, which has nonzero QueueReady.\n"
892 " See virtio-v1.0-cs04 s4.2.2.2 MMIO Device Register Layout"
897 ((uint64_t)mmio_dev->vqdev->vqs[mmio_dev->qsel].vring.used
898 & ((uint64_t)0xffffffff));
900 temp_ptr = (void *) ((uint64_t)temp_ptr
901 | ((uint64_t)(*value) << 32));
903 // virtio-v1.0-cs04 s2.4 Virtqueues
904 if ((uint64_t)temp_ptr % 4)
905 VIRTIO_DRI_ERRX(mmio_dev->vqdev,
906 "Physical address of guest's used ring (%p) is misaligned. Address should be a multiple of 4.\n"
907 " See virtio-v1.0-cs04 s2.4 Virtqueues");
909 // assign the new value to the queue used
910 mmio_dev->vqdev->vqs[mmio_dev->qsel].vring.used = temp_ptr;
912 VIRTIO_DRI_WARNX(mmio_dev->vqdev,
913 "Attempt to write QueueUsedHigh register for invalid QueueSel. QueueSel was %u, but the number of queues is %u."
914 , mmio_dev->qsel, mmio_dev->vqdev->num_vqs);
918 // Read-only register offsets:
919 case VIRTIO_MMIO_MAGIC_VALUE:
920 case VIRTIO_MMIO_VERSION:
921 case VIRTIO_MMIO_DEVICE_ID:
922 case VIRTIO_MMIO_VENDOR_ID:
923 case VIRTIO_MMIO_DEVICE_FEATURES:
924 case VIRTIO_MMIO_QUEUE_NUM_MAX:
925 case VIRTIO_MMIO_INTERRUPT_STATUS:
926 case VIRTIO_MMIO_CONFIG_GENERATION:
927 // Write to read-only register
928 VIRTIO_DRI_ERRX(mmio_dev->vqdev,
929 "Attempt to write read-only device register offset 0x%x.",
933 // Bad register offset
934 VIRTIO_DRI_ERRX(mmio_dev->vqdev,
935 "Attempt to write invalid device register offset 0x%x.",