A guest-host hardware device intermediary (vdev ser8250)

This vdev emulates an Intel 8250 UART, and acts as an intermediary between the guest and an actual physical device.

Like the watchdog vdevs described in this chapter, the ser8250 vdev emulates a device that actually exists as a hardware device. For a guest in a hypervisor VM, it is indistinguishable from an Intel 8250 UART available on many ARM and x86 boards.

The guest should be able to work with this vdev without modifying the driver it already has for this hardware device. For example, a QNX guest can use the devc-ser8250 driver (see devc-ser8250 in the QNX SDP Utilities Reference.

Since this vdev emulates a hardware device, it includes the qvm/8250.h public header file, which has the definitions it needs to function as though it were an actual 8250 UART.

The overall organization of the ser8250 vdev is like that of the trace vdev and the watchdog vdevs discussed earlier (e.g., option parsing, factory structure, and *_control(), *_vread() and *_vwrite() functions). It also differs from these vdevs, chiefly in that it that presents the guest with the equivalent of a hardware device, but also interacts with an actual hardware device in the host.

For information on how to configure and use this vdev, see vdev ser8250 in the User's Guide “Virtual Device Reference” chapter.

Defining the vdev

Like other vdevs, the ser8250 vdev defines itself as a structure (ser8250_state), populates its factory structure, and calls vdev_register_factory to register itself in the VM framework.

The first member of the ser8250_state structure is defined as a cdev_state structure, a generic structure for character device vdevs that includes information for both input and output. Note that this structure includes members with pointers to file descriptors, input and output bufffers, timeouts for flushes, and other values needed by a character device, so they don't need to be defined in the code for the ser8250 vdev proper.

The rest of the ser8250_state structure comprises values (e.g., registers, baud rate) particular to this type of device. These will be set during the vdev's startup, or updated when it is running.

On startup, the ser8250 vdev initializes some default values; this includes setting the location of this vdev at address in guest-physical memory space (type is QST_DEFAULT_TYPE_IO; see qvm_state_block in the Virtual Device Developer's API Reference “types.h” chapter), and a default baud rate in the register where a driver would expect to find it.

case VDEV_CTRL_OPTIONS_START:
	vdp->v_block.type = QST_DEFAULT_TYPE_IO;
	vdp->v_block.location = 0x3f8u;
	vdp->v_block.length = 8u;
	state->s_dlr = 1;	// 115200 bauds
	break;

When the qvm process finishes parsing the vdev's options, it issues the VDEV_CTRL_OPTIONS_END callback, and the vdev can call cdev_setup() to complete its setup; that is, fill in the remaining values needed in its definition of itself. Notice that the last argument for cdev_setup() is the size of the vdev's input and output buffers, defined in the vdev.

Notice also that this vdev uses the cdev_control() function. This function looks after options common to the ser8250 vdev and other character vdevs, such as the pl011 and virtio-console vdevs. These options include:

Note: For information about the cdev_*() functions, cdev_state and other structures and definitions available for use by character device vdevs, see the cdev.hchapter of the Virtual Device Developer's API Reference.

Intermediary between the guest and a hardware device

Like other vdevs, after it has completed its startup tasks the ser8250 vdev uses its vread() and vwrite() functions (vd8250_vwrite() and vd8250_vwrite()) to support the guest reading and writing as it would from and to a physical device, setting the actions theses functions take according the register values the guest passes in. For example, for REG_TX, which indicates that the guest wants to transmit data, vd8250_vwrite() calls:

  1. cdev_output_full() to check if there is room in the output buffer
  2. cdev_write() to write the output to the physical device in the hypervisor host
  3. cdev_intr_signal() to assert an interrupt to the guest

Notice that this vdev's factory structure has a pulse entry that references the vd8250_pulse() function. This function uses the pulse code passed to it to determine if it should read input from the physical device in the host, or write the contents of its buffer to that device (see Pulse code in the Virtual Device Developer's API Reference, and _pulse in the QNX SDP C Library Reference).