Emulating hardware (vdev wdt-sp805)

When it constructs and configures itself, the wdt-sp805 vdev defines its virtual hardware, and specifies default values such as clock frequency.

The wdt-sp805 vdev emulates a hardware watchdog for ARM platforms. Since it emulates an actual physical device, this vdev:

For information about how to configure and use the wdt-sp805 vdev, see vdev wdt-sp805 in the User's Guide “Virtual Device Reference” chapter.

Note:

The wdt-sp805 vdev emulates the ARM Watchdog Module (SP805); for more information about this hardware device, see the ARM Information Centre Documentation at http://infocenter.arm.com/help/topic/com.arm.doc.ddi0270b/index.html

This vdev doesn't support controller interrupts or multiple clock modes (i.e., WDOGCLK as a distinct sub-multiple of the PCLK interface clock).

Defining defaults

Before it parses the options passed to it through the VM configuration, the wdt-sp805 vdev must provide default values for some options. Specifically, it must provide default values for options that are in fact actually optional; that is, options that can be omitted from the VM configuration without causing the VM startup to fail.

For example, if the user doesn't specify the frequency or the loc, the wdt-sp805 vdev will use the values defined by, respectively, DEFAULT_FREQ and SP805_ARMFM_BASE_ADDR. When it begins processing its configuration options, the vdev loads these default values into the structure that defines it (see Initializing the vdev state below).

Hardware emulation

Since the wdt-sp805 vdev emulates hardware and to a driver in the guest must be indistinguishable from an actual physical device, it must present the guest with the same interfaces that the physical device presents.

Below is a portion of the values the vdev defines so that it can emulate the SP805 watchdog as specified for the ARMv8 foundation model: the base address for the watchdog device on the hardware, the offsets from the base address for the relevant device registers, the clock frequency, and some flags:

#define SP805_ARMFM_BASE_ADDR   0x1C0F0000  ///< Base address
#define DEFAULT_FREQ            25000000UL  ///< Interface clock frequency
#define WDOG_LOAD               0x0000      ///< Load register
#define WDOG_VALUE              0x0004      ///< Value register
#define WDOG_CONTROL            0x0008      ///< Control register
  #define WDOG_CONTROL_INTEN    (1 << 0)    ///< Enable the counter & the interrupt
  #define WDOG_CONTROL_RESEN    (1 << 1)    ///< Unmask module reset output
...
#define PCELLID2                0x0FF8      ///< Prime Cell ID 2
#define PCELLID3                0x0FFC      ///< Prime Cell ID 3
Note: Note that vdev trace vdev doesn't do any virtual hardware set up of the sort that vdev wdt-sp805 does; it doesn't need to because it isn't emulating a hardware device.

Defining the device

The vdev defines itself as a data structure. This structure is initialized by the startup, and updated as the state of the vdev changes:

/// One instance of a SP805 watchdog timer
struct sp805_state {
    struct wdog_state   s_wdog;                ///<    State of the generic watchdog device
    struct guest_timer  *s_gtimer;             ///<    Virtual timer used to fire events
    uint64_t            s_resolution;          ///<    Timer resolution
    uint64_t            s_freq;                ///<    Interface clock frequency
    uint64_t            s_scale;               ///<    Amount to scale counter to convert to gtimer resolution
    uint64_t            s_scaled_reload;       ///<    Reload value, prescaled to gtimer resolution
    uint32_t            wdog_load;             ///<    Load register (R/W)
    uint32_t            wdog_value;            ///<    Value register (RO)
    uint32_t            wdog_control;          ///<    Control register (RW)
    uint32_t            wdog_intclr;           ///<    Interrupt clear register (WO)
    uint32_t            wdog_ris;              ///<    Raw interrupt status register (RO)
    uint32_t            wdog_mis;              ///<    Masked interrupt status register (RO)
    uint32_t            wdog_lock;             ///<    Lock register (RW)
    enum sp805_states   timer_state;           ///<    Current timer state
};

The vdev also specifies its possible states, which are the same as the states of the hardware device it is emulating:

enum sp805_states {
    WDT_DISABLED,
    WDT_RUNNING,
    WDT_EXPIRED
};

Defining the options

The code snippets below show how vdev wdt-sp805 defines its options:

enum {
    OPT_FREQUENCY_IDX = WDOG_OPT_NUM_OPTS,
    OPT_NUM_OPTS
};
...
sp805_register(void)
{
    static const char *const sp805_options[OPT_NUM_OPTS+1] = {
        WDOG_OPTIONS,
        [OPT_FREQUENCY_IDX] =   "frequency",
        [OPT_NUM_OPTS] =        NULL,
    };

This is exactly like the trace vdev defines its options (see Defining the options in the The Basics: vdev trace chapter).

Like other vdevs the wdt-sp805 vdev needs to define only those options that are specific to itself. Common options, such as the loc option, are defined by the qvm process, which looks after parsing them (see Common vdev options in the User's Guide).

Unlike many vdevs, however, this control function for the watchdog vdevs call a common function wdog_control(); this function , which looks after parsing the action option, which is common to both watchdogs. Thus, for these vdevs the code looking after parsing their options is in three places: the qvm process for common options, wdog_control() defined in the qvm/wdog.h header file for options common to both vdevs, and the *_control() function for each vdev.

Creating a timer

Notice that during at the end of its startup stage this vdev calls guest_timer_create(), adjusting the timer resolution if necessary:

case VDEV_CTRL_OPTIONS_END: ;
    state->s_gtimer = guest_timer_create(NULL, vdp, NULL,
                                             &state->s_resolution);
    if (state->s_gtimer == NULL) {
    // If the timer creation failed, the resolution variable has the reason.
     return (int)state->s_resolution;
    }
    if (state->s_resolution > state->s_freq) {
    // The qvm timer resolution is greater than the watchdog module's interface clock.
    state->s_scale = state->s_resolution / state->s_freq;
    state->s_scaled_reload = state->wdog_load * state->s_scale;
    } else {
        state->s_scale = state->s_freq / state->s_resolution;
        state->s_scaled_reload = state->wdog_load / state->s_scale;
    }
    break;

Populating the vdev factory and registering the vdev

Likevdev trace, vdev wdt-sp805 populates a factory page for itself (sp805_factory), and calls vdev_register_factory() to register itself.

The sp805_factory factory page is not very different from the vdtrace_factory data structure used by trace vdev (see Defining the vdev in the factory structure in the The Basics: vdev trace chapter). Note, however, the sp805_timer member, which references the sp805_timer() function (see Timer management functions in this chapter).

Initializing the vdev state

When it is starts, this vdev must initialize some values, such as its location in memory and the register base address. Note that the location of the device is set in guest-physical memory (QST_MEMORY; see the qvm_state_types in the Virtual Device Developer's API Reference); and that the location of the vdev is set to the ARMv8 foundation model location (SP805_ARMFM_BASE_ADDR):

    case VDEV_CTRL_OPTIONS_START:
        vdp->v_block.type = QST_MEMORY;
        vdp->v_block.location = SP805_ARMFM_BASE_ADDR;
        vdp->v_block.length = 0x1000;
        // Initialize non-zero registers to reset values
        state->wdog_load = 0xFFFFFFFF;
        state->wdog_value = 0xFFFFFFFF;
        state->s_freq = DEFAULT_FREQ;
        state->s_wdog.ws_vdev = vdp;
        break;