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.
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).
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).
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
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 };
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.
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;
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).
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;