Reading and writing in a vdev

When a guest reads from or writes to a vdev, the vdev must perform actions analogous to the actions hardware performs in response to reads and writes.

*_vread() and *_vwrite()

When you write a vdev it is important to remember that you are creating a device that, from the perspective of the guest, is indistinguishable from hardware. Thus, when the guest reads, your vdev must write what it is trying to read; when the guest writes, you must read what it has written.

The diagram below illustrates how *_vread() and *_vwrite() functions call the appropriate corresponding guest_cpu_read() and guest_cpu_write() functions.

Figure 1. Reading and writing in a a vdev

Remember that if the vdev acts as an intermediary between the guest and a hardware device (e.g., vdev ser8250), to the guest the vdev is indistinguishable from hardware, but in the hypervisor host (where vdevs run) the vdev is just another application interacting with a hardware driver.

Note: VirtIO devices use virtqueues, so you don't need to look after writing to the vCPU when the guest reads, or reading the vCPU when the guest writes (see Para-virtualized vdevs (virtio-entropy) in the “Advanced Topics” chapter).

vopnd and oopnd

The *_vread() and *_vwrite() functions in the source code examples that call guest_cpu_*() functions (see the guest.h chapter in the Virtual Device Developer's API Reference) use vopnd and oopnd in their arguments. For example, in the trace vdev's vdtrace_vwrite() function, you will find the following:

...
if (vopnd->location == vdp->v_block.location) {
  const int err = guest_cpu_read(gcp, GXF_NONE, oopnd, 1, &counter, vopnd->length);
  if (err != EOK) {
    return guest_cpu_to_vrs(err);
  }
...

The vopnd and the oopnd arguments both point to arrays of qvm_state_block data structures:

vopnd
Virtual device operand. The operand that caused the guest exit. Typically this will be a guest-physical memory location, though it might be something else (e.g., on x86, it could be a guest IO port location).
oopnd
Other operand. Refers to another data element; the data type and its location (e.g., PCI, CPU registers) depends on the value of the qvm_state_block type member (see Data elements and their types in this chapter).