Emulating hardware (vdev wdt-sp805)
The wdt-sp805 vdev emulates a hardware watchdog for ARM platforms.
When it constructs and configures itself, the wdt-sp805 vdev defines its virtual hardware and specifies default values such as clock frequency.
Since it emulates an actual physical device, this vdev:
- is architecture-specific
- doesn't require a custom driver in the guest; the guest's driver for the equivalent hardware device should suffice
For information about how to configure and use the vdev, see the vdev wdt-sp805 reference
in the User's Guide Virtual Device Reference
chapter.
The wdt-sp805 vdev emulates the ARM Watchdog Module (SP805); for more information about this hardware device, see the ARM Information Center 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 options, the vdev will use the
values defined by 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.
#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 3Defining the device
/// One instance of an 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
};enum sp805_states {
WDT_DISABLED,
WDT_RUNNING,
WDT_EXPIRED
};Defining the 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 how 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 calls a common function, wdog_control(); this function 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
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
Like vdev 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 the 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
Definitions in types.hsection 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;