mirror of
https://github.com/projectacrn/acrn-hypervisor.git
synced 2025-08-15 23:05:15 +00:00
dm: vhpet: add vHPET support
vHPET is used as a source of system timer by UEFI (e.g. OVMF). This provides an alternative to using vPIT, which OVMF assumes is always connected to vPIC. This is ported from Bhyve, with a few changes: - move to user space, using acrn_timer - enable timers only when necessary Origin: FreeBSD License: BSD-3-Clause URL: https://svnweb.freebsd.org/ commit: 326257 Purpose: Adding vHPET support. Maintained-by: External Tracked-On: #2319 Signed-off-by: Peter Fang <peter.fang@intel.com> Acked-by: Anthony Xu <anthony.xu@intel.com>
This commit is contained in:
parent
0343da8c70
commit
4642269248
@ -73,6 +73,7 @@ SRCS += hw/platform/atkbdc.c
|
||||
SRCS += hw/platform/ps2mouse.c
|
||||
SRCS += hw/platform/rtc.c
|
||||
SRCS += hw/platform/pit.c
|
||||
SRCS += hw/platform/hpet.c
|
||||
SRCS += hw/platform/ps2kbd.c
|
||||
SRCS += hw/platform/ioapic.c
|
||||
SRCS += hw/platform/cmos_io.c
|
||||
|
@ -56,6 +56,7 @@
|
||||
#include "smbiostbl.h"
|
||||
#include "rtc.h"
|
||||
#include "pit.h"
|
||||
#include "hpet.h"
|
||||
#include "version.h"
|
||||
#include "sw_load.h"
|
||||
#include "monitor.h"
|
||||
@ -458,6 +459,10 @@ vm_init_vdevs(struct vmctx *ctx)
|
||||
if (ret < 0)
|
||||
goto vpit_fail;
|
||||
|
||||
ret = vhpet_init(ctx);
|
||||
if (ret < 0)
|
||||
goto vhpet_fail;
|
||||
|
||||
sci_init(ctx);
|
||||
|
||||
if (debugexit_enabled)
|
||||
@ -481,6 +486,8 @@ monitor_fail:
|
||||
if (debugexit_enabled)
|
||||
deinit_debugexit();
|
||||
|
||||
vhpet_deinit(ctx);
|
||||
vhpet_fail:
|
||||
vpit_deinit(ctx);
|
||||
vpit_fail:
|
||||
vrtc_deinit(ctx);
|
||||
@ -501,6 +508,7 @@ vm_deinit_vdevs(struct vmctx *ctx)
|
||||
if (debugexit_enabled)
|
||||
deinit_debugexit();
|
||||
|
||||
vhpet_deinit(ctx);
|
||||
vpit_deinit(ctx);
|
||||
vrtc_deinit(ctx);
|
||||
ioc_deinit(ctx);
|
||||
@ -529,6 +537,7 @@ vm_reset_vdevs(struct vmctx *ctx)
|
||||
if (debugexit_enabled)
|
||||
deinit_debugexit();
|
||||
|
||||
vhpet_deinit(ctx);
|
||||
vpit_deinit(ctx);
|
||||
vrtc_deinit(ctx);
|
||||
|
||||
@ -540,6 +549,7 @@ vm_reset_vdevs(struct vmctx *ctx)
|
||||
atkbdc_init(ctx);
|
||||
vrtc_init(ctx);
|
||||
vpit_init(ctx);
|
||||
vhpet_init(ctx);
|
||||
|
||||
if (debugexit_enabled)
|
||||
init_debugexit();
|
||||
|
@ -93,7 +93,6 @@ mmio_rb_lookup(struct mmio_rb_tree *rbt, uint64_t addr,
|
||||
return -1;
|
||||
}
|
||||
|
||||
__attribute__((unused))
|
||||
static int
|
||||
mmio_rb_add(struct mmio_rb_tree *rbt, struct mmio_rb_range *new)
|
||||
{
|
||||
@ -131,7 +130,6 @@ mmio_rb_dump(struct mmio_rb_tree *rbt)
|
||||
|
||||
RB_GENERATE(mmio_rb_tree, mmio_rb_range, mr_link, mmio_rb_range_compare);
|
||||
|
||||
__attribute__((unused))
|
||||
static int
|
||||
mem_read(void *ctx, int vcpu, uint64_t gpa, uint64_t *rval, int size, void *arg)
|
||||
{
|
||||
@ -143,7 +141,6 @@ mem_read(void *ctx, int vcpu, uint64_t gpa, uint64_t *rval, int size, void *arg)
|
||||
return error;
|
||||
}
|
||||
|
||||
__attribute__((unused))
|
||||
static int
|
||||
mem_write(void *ctx, int vcpu, uint64_t gpa, uint64_t wval, int size, void *arg)
|
||||
{
|
||||
|
@ -556,7 +556,8 @@ basl_fwrite_hpet(FILE *fp, struct vmctx *ctx)
|
||||
EFPRINTF(fp, "[0004]\t\tAsl Compiler Revision : 00000000\n");
|
||||
EFPRINTF(fp, "\n");
|
||||
|
||||
EFPRINTF(fp, "[0004]\t\tTimer Block ID : 00000000\n");
|
||||
EFPRINTF(fp, "[0004]\t\tTimer Block ID : %08X\n",
|
||||
(uint32_t)vhpet_capabilities());
|
||||
EFPRINTF(fp,
|
||||
"[0012]\t\tTimer Block Register : [Generic Address Structure]\n");
|
||||
EFPRINTF(fp, "[0001]\t\tSpace ID : 00 [SystemMemory]\n");
|
||||
|
997
devicemodel/hw/platform/hpet.c
Normal file
997
devicemodel/hw/platform/hpet.c
Normal file
@ -0,0 +1,997 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
||||
*
|
||||
* Copyright (c) 2019 Intel Corporation
|
||||
* Copyright (c) 2013 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com>
|
||||
* Copyright (c) 2013 Neel Natu <neel@freebsd.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#include <pthread.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "vmmapi.h"
|
||||
#include "mem.h"
|
||||
#include "timer.h"
|
||||
#include "hpet.h"
|
||||
#include "acpi_hpet.h"
|
||||
|
||||
#define HPET_FREQ (16777216) /* 16.7 (2^24) Mhz */
|
||||
#define FS_PER_S (1000000000000000UL)
|
||||
|
||||
/* Timer N Configuration and Capabilities Register */
|
||||
#define HPET_TCAP_RO_MASK (HPET_TCAP_INT_ROUTE | \
|
||||
HPET_TCAP_FSB_INT_DEL | \
|
||||
HPET_TCAP_SIZE | \
|
||||
HPET_TCAP_PER_INT)
|
||||
|
||||
/*
|
||||
* HPET requires at least 3 timers and up to 32 timers per block.
|
||||
*/
|
||||
#define VHPET_NUM_TIMERS (8)
|
||||
|
||||
#define VHPET_LOCK() \
|
||||
do { \
|
||||
int err; \
|
||||
err = pthread_mutex_lock(&vhpet_mtx); \
|
||||
assert(err == 0); \
|
||||
} while (0)
|
||||
|
||||
#define VHPET_UNLOCK() \
|
||||
do { \
|
||||
int err; \
|
||||
err = pthread_mutex_unlock(&vhpet_mtx); \
|
||||
assert(err == 0); \
|
||||
} while (0)
|
||||
|
||||
#define vhpet_ts_to_ticks(ts) ts_to_ticks(HPET_FREQ, ts)
|
||||
/* won't overflow since the max value of ticks is 2^32 */
|
||||
#define vhpet_ticks_to_ts(tk, ts) ticks_to_ts(HPET_FREQ, tk, ts)
|
||||
|
||||
#define vhpet_tmr(v, n) (&(v)->timer[n].tmrlst[(v)->timer[n].tmridx].t)
|
||||
#define vhpet_tmrarg(v, n) (&(v)->timer[n].tmrlst[(v)->timer[n].tmridx].a)
|
||||
|
||||
#define ts_is_zero(ts) timespeccmp(ts, &zero_ts.it_value, ==)
|
||||
#define ts_set_zero(ts) do { *(ts) = zero_ts.it_value; } while (0)
|
||||
|
||||
/*
|
||||
* Debug printf
|
||||
*/
|
||||
static int hpet_debug;
|
||||
#define DPRINTF(params) do { if (hpet_debug) printf params; } while (0)
|
||||
#define WPRINTF(params) (printf params)
|
||||
|
||||
|
||||
struct vhpet_timer_arg {
|
||||
struct vhpet *vhpet;
|
||||
int timer_num;
|
||||
bool running;
|
||||
};
|
||||
|
||||
static struct vhpet {
|
||||
struct vmctx *vm;
|
||||
bool inited;
|
||||
|
||||
uint64_t config; /* Configuration */
|
||||
uint64_t isr; /* Interrupt Status */
|
||||
uint32_t countbase; /* HPET counter base value */
|
||||
struct timespec countbase_ts; /* uptime corresponding to base value */
|
||||
|
||||
struct {
|
||||
uint64_t cap_config; /* Configuration */
|
||||
uint64_t msireg; /* FSB interrupt routing */
|
||||
uint32_t compval; /* Comparator */
|
||||
uint32_t comprate;
|
||||
struct timespec expts; /* time when counter==compval */
|
||||
struct {
|
||||
struct acrn_timer t;
|
||||
struct vhpet_timer_arg a;
|
||||
} tmrlst[3];
|
||||
int tmridx;
|
||||
} timer[VHPET_NUM_TIMERS];
|
||||
} __vhpet; /* one vHPET per VM */
|
||||
|
||||
|
||||
/* one vHPET per VM */
|
||||
static pthread_mutex_t vhpet_mtx = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
static const struct itimerspec zero_ts = { 0 };
|
||||
|
||||
|
||||
static inline struct vhpet *
|
||||
vhpet_instance(void)
|
||||
{
|
||||
return &__vhpet;
|
||||
}
|
||||
|
||||
const uint64_t
|
||||
vhpet_capabilities(void)
|
||||
{
|
||||
static uint64_t cap = 0;
|
||||
|
||||
if (cap == 0) {
|
||||
cap |= 0x8086 << 16; /* vendor id */
|
||||
cap |= (VHPET_NUM_TIMERS - 1) << 8; /* number of timers */
|
||||
cap |= 1; /* revision */
|
||||
cap &= ~HPET_CAP_COUNT_SIZE; /* 32-bit timer */
|
||||
|
||||
cap &= 0xffffffff;
|
||||
cap |= (FS_PER_S / HPET_FREQ) << 32; /* tick period in fs */
|
||||
}
|
||||
|
||||
return cap;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
vhpet_counter_enabled(struct vhpet *vhpet)
|
||||
{
|
||||
return ((vhpet->config & HPET_CNF_ENABLE) != 0);
|
||||
}
|
||||
|
||||
static inline bool
|
||||
vhpet_timer_msi_enabled(struct vhpet *vhpet, int n)
|
||||
{
|
||||
const uint64_t msi_enable = HPET_TCAP_FSB_INT_DEL | HPET_TCNF_FSB_EN;
|
||||
|
||||
return ((vhpet->timer[n].cap_config & msi_enable) == msi_enable);
|
||||
}
|
||||
|
||||
static inline int
|
||||
vhpet_timer_ioapic_pin(struct vhpet *vhpet, int n)
|
||||
{
|
||||
/*
|
||||
* If the timer is configured to use MSI then treat it as if the
|
||||
* timer is not connected to the ioapic.
|
||||
*/
|
||||
if (vhpet_timer_msi_enabled(vhpet, n))
|
||||
return 0;
|
||||
|
||||
return ((vhpet->timer[n].cap_config & HPET_TCNF_INT_ROUTE) >> 9);
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
vhpet_counter(struct vhpet *vhpet, struct timespec *nowptr)
|
||||
{
|
||||
uint32_t val;
|
||||
struct timespec now, delta;
|
||||
|
||||
val = vhpet->countbase;
|
||||
|
||||
if (vhpet_counter_enabled(vhpet)) {
|
||||
if (clock_gettime(CLOCK_REALTIME, &now)) {
|
||||
perror("clock_gettime failed");
|
||||
assert(0);
|
||||
}
|
||||
|
||||
/* delta = now - countbase_ts */
|
||||
assert(timespeccmp(&now, &vhpet->countbase_ts, >=));
|
||||
delta = now;
|
||||
timespecsub(&delta, &vhpet->countbase_ts);
|
||||
val += vhpet_ts_to_ticks(&delta);
|
||||
|
||||
if (nowptr != NULL)
|
||||
*nowptr = now;
|
||||
} else {
|
||||
/*
|
||||
* The timespec corresponding to the 'countbase' is
|
||||
* meaningless when the counter is disabled. Make sure
|
||||
* that the caller doesn't want to use it.
|
||||
*/
|
||||
assert(nowptr == NULL);
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static void
|
||||
vhpet_timer_clear_isr(struct vhpet *vhpet, int n)
|
||||
{
|
||||
int pin;
|
||||
|
||||
if (vhpet->isr & (1 << n)) {
|
||||
pin = vhpet_timer_ioapic_pin(vhpet, n);
|
||||
assert(pin != 0);
|
||||
vm_set_gsi_irq(vhpet->vm, pin, GSI_SET_LOW);
|
||||
vhpet->isr &= ~(1 << n);
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool
|
||||
vhpet_periodic_timer(struct vhpet *vhpet, int n)
|
||||
{
|
||||
return ((vhpet->timer[n].cap_config & HPET_TCNF_TYPE) != 0);
|
||||
}
|
||||
|
||||
static inline bool
|
||||
vhpet_timer_interrupt_enabled(struct vhpet *vhpet, int n)
|
||||
{
|
||||
return ((vhpet->timer[n].cap_config & HPET_TCNF_INT_ENB) != 0);
|
||||
}
|
||||
|
||||
static inline bool
|
||||
vhpet_timer_enabled(struct vhpet *vhpet, int n)
|
||||
{
|
||||
/* The timer is enabled when at least one of the two bits is set */
|
||||
return vhpet_timer_interrupt_enabled(vhpet, n) ||
|
||||
vhpet_periodic_timer(vhpet, n);
|
||||
}
|
||||
|
||||
static inline bool
|
||||
vhpet_timer_running(struct vhpet *vhpet, int n)
|
||||
{
|
||||
return vhpet_tmrarg(vhpet, n)->running;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
vhpet_timer_edge_trig(struct vhpet *vhpet, int n)
|
||||
{
|
||||
assert(!vhpet_timer_msi_enabled(vhpet, n));
|
||||
return ((vhpet->timer[n].cap_config & HPET_TCNF_INT_TYPE) == 0);
|
||||
}
|
||||
|
||||
static void
|
||||
vhpet_timer_interrupt(struct vhpet *vhpet, int n)
|
||||
{
|
||||
int pin;
|
||||
|
||||
/* If interrupts are not enabled for this timer then just return. */
|
||||
if (!vhpet_timer_interrupt_enabled(vhpet, n))
|
||||
return;
|
||||
|
||||
/*
|
||||
* If a level triggered interrupt is already asserted then just return.
|
||||
*/
|
||||
if ((vhpet->isr & (1 << n)) != 0) {
|
||||
assert(!vhpet_timer_edge_trig(vhpet, n));
|
||||
DPRINTF(("hpet t%d intr is already asserted\n", n));
|
||||
return;
|
||||
}
|
||||
|
||||
if (vhpet_timer_msi_enabled(vhpet, n)) {
|
||||
vm_lapic_msi(vhpet->vm, vhpet->timer[n].msireg >> 32,
|
||||
vhpet->timer[n].msireg & 0xffffffff);
|
||||
return;
|
||||
}
|
||||
|
||||
pin = vhpet_timer_ioapic_pin(vhpet, n);
|
||||
|
||||
if (pin == 0) {
|
||||
DPRINTF(("hpet t%d intr is not routed to ioapic\n", n));
|
||||
return;
|
||||
}
|
||||
|
||||
if (vhpet_timer_edge_trig(vhpet, n)) {
|
||||
vm_set_gsi_irq(vhpet->vm, pin, GSI_RAISING_PULSE);
|
||||
} else {
|
||||
vhpet->isr |= 1 << n;
|
||||
vm_set_gsi_irq(vhpet->vm, pin, GSI_SET_HIGH);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
vhpet_timer_handler(void *a, uint64_t nexp)
|
||||
{
|
||||
int n;
|
||||
struct vhpet *vhpet;
|
||||
struct vhpet_timer_arg *arg;
|
||||
struct timespec now;
|
||||
struct itimerspec tmrts;
|
||||
uint64_t newexp;
|
||||
|
||||
arg = a;
|
||||
vhpet = arg->vhpet;
|
||||
n = arg->timer_num;
|
||||
|
||||
DPRINTF(("hpet t%d(%p) fired\n", n, arg));
|
||||
|
||||
VHPET_LOCK();
|
||||
|
||||
/* Bail if timer was destroyed */
|
||||
if (!vhpet->inited)
|
||||
goto done;
|
||||
|
||||
/* Bail if timer was stopped */
|
||||
if (!arg->running) {
|
||||
DPRINTF(("hpet t%d(%p) already stopped\n", n, arg));
|
||||
assert(ts_is_zero(&vhpet->timer[n].expts));
|
||||
goto done;
|
||||
} else
|
||||
assert(arg == vhpet_tmrarg(vhpet, n));
|
||||
|
||||
vhpet_timer_interrupt(vhpet, n);
|
||||
|
||||
if (clock_gettime(CLOCK_REALTIME, &now)) {
|
||||
perror("clock_gettime failed");
|
||||
assert(0);
|
||||
}
|
||||
|
||||
if (acrn_timer_gettime(vhpet_tmr(vhpet, n), &tmrts))
|
||||
assert(0);
|
||||
|
||||
/* One-shot mode has a periodicity of 2^32 ticks */
|
||||
assert(!ts_is_zero(&tmrts.it_interval));
|
||||
|
||||
/*
|
||||
* The actual expiration time will be slightly later than expts.
|
||||
*
|
||||
* This may cause spurious interrupts when stopping a timer but
|
||||
* at least interrupts won't be completely lost.
|
||||
*/
|
||||
timespecadd(&tmrts.it_value, &now);
|
||||
vhpet->timer[n].expts = tmrts.it_value;
|
||||
|
||||
/*
|
||||
* Catch any remaining expirations that happened after being
|
||||
* last consumed by the mevent_dispatch thread.
|
||||
*/
|
||||
if (read(vhpet_tmr(vhpet, n)->fd, &newexp, sizeof(newexp)) > 0)
|
||||
nexp += newexp;
|
||||
|
||||
/*
|
||||
* Periodic timer updates 'compval' upon expiration.
|
||||
* Try to keep 'compval' as up-to-date as possible.
|
||||
*/
|
||||
vhpet->timer[n].compval += nexp * vhpet->timer[n].comprate;
|
||||
|
||||
done:
|
||||
VHPET_UNLOCK();
|
||||
return;
|
||||
}
|
||||
|
||||
static void
|
||||
vhpet_adjust_compval(struct vhpet *vhpet, int n, const struct timespec *now)
|
||||
{
|
||||
uint32_t compval, comprate, compnext;
|
||||
struct timespec delta;
|
||||
uint64_t delta_ticks;
|
||||
|
||||
compval = vhpet->timer[n].compval;
|
||||
comprate = vhpet->timer[n].comprate;
|
||||
|
||||
assert(comprate != 0);
|
||||
assert(timespeccmp(&vhpet->timer[n].expts, now, <));
|
||||
|
||||
/* delta = now - expts */
|
||||
delta = *now;
|
||||
timespecsub(&delta, &vhpet->timer[n].expts);
|
||||
delta_ticks = vhpet_ts_to_ticks(&delta);
|
||||
|
||||
/*
|
||||
* Calculate the comparator value to be used for the next periodic
|
||||
* interrupt.
|
||||
*
|
||||
* In this scenario 'counter' is ahead of 'compval' by at least
|
||||
* 'comprate'. To find the next value to program into the
|
||||
* accumulator we divide 'delta_ticks', the number space between
|
||||
* 'compval + comprate' and 'counter', into 'comprate' sized units.
|
||||
* The 'compval' is rounded up such that it stays "ahead" of
|
||||
* 'counter'.
|
||||
*
|
||||
* There's a slight chance read() in vhpet_timer_handler() had
|
||||
* already accomplished some of this just prior to calling this
|
||||
* function.
|
||||
*/
|
||||
compnext = compval + (delta_ticks / comprate + 1) * comprate;
|
||||
|
||||
vhpet->timer[n].compval = compnext;
|
||||
}
|
||||
|
||||
static void
|
||||
vhpet_stop_timer(struct vhpet *vhpet, int n, const struct timespec *now,
|
||||
bool adj_compval)
|
||||
{
|
||||
struct vhpet_timer_arg *arg;
|
||||
|
||||
assert(vhpet_timer_running(vhpet, n));
|
||||
assert(!ts_is_zero(&vhpet->timer[n].expts));
|
||||
|
||||
DPRINTF(("hpet t%d stopped\n", n));
|
||||
|
||||
arg = vhpet_tmrarg(vhpet, n);
|
||||
arg->running = false;
|
||||
|
||||
/* Cancel the existing timer */
|
||||
if (acrn_timer_settime(vhpet_tmr(vhpet, n), &zero_ts))
|
||||
assert(0);
|
||||
|
||||
if (++vhpet->timer[n].tmridx == nitems(vhpet->timer[n].tmrlst))
|
||||
vhpet->timer[n].tmridx = 0;
|
||||
|
||||
assert(!vhpet_timer_running(vhpet, n));
|
||||
|
||||
/*
|
||||
* If the timer was scheduled to expire in the past but hasn't
|
||||
* had a chance to execute yet then trigger the timer interrupt
|
||||
* here. Failing to do so will result in a missed timer interrupt
|
||||
* in the guest. This is especially bad in one-shot mode because
|
||||
* the next interrupt has to wait for the counter to wrap around.
|
||||
*/
|
||||
if (timespeccmp(&vhpet->timer[n].expts, now, <)) {
|
||||
DPRINTF(("hpet t%d interrupt triggered after "
|
||||
"stopping timer\n", n));
|
||||
if (adj_compval && vhpet->timer[n].comprate != 0)
|
||||
vhpet_adjust_compval(vhpet, n, now);
|
||||
vhpet_timer_interrupt(vhpet, n);
|
||||
}
|
||||
|
||||
ts_set_zero(&vhpet->timer[n].expts);
|
||||
}
|
||||
|
||||
static void
|
||||
vhpet_start_timer(struct vhpet *vhpet, int n, uint32_t counter,
|
||||
const struct timespec *now, bool adj_compval)
|
||||
{
|
||||
struct itimerspec ts;
|
||||
uint32_t delta;
|
||||
struct vhpet_timer_arg *arg;
|
||||
|
||||
if (vhpet_timer_running(vhpet, n))
|
||||
vhpet_stop_timer(vhpet, n, now, adj_compval);
|
||||
|
||||
DPRINTF(("hpet t%d started\n", n));
|
||||
|
||||
/*
|
||||
* It is the guest's responsibility to make sure that the
|
||||
* comparator value is not in the "past". The hardware
|
||||
* doesn't have any belt-and-suspenders to deal with this
|
||||
* so we don't either.
|
||||
*/
|
||||
delta = vhpet->timer[n].compval - counter;
|
||||
vhpet_ticks_to_ts(delta, &ts.it_value);
|
||||
timespecadd(&ts.it_value, now);
|
||||
|
||||
if (vhpet->timer[n].comprate != 0)
|
||||
vhpet_ticks_to_ts(vhpet->timer[n].comprate, &ts.it_interval);
|
||||
else /* It takes 2^32 ticks to wrap around */
|
||||
vhpet_ticks_to_ts(1ULL << 32, &ts.it_interval);
|
||||
|
||||
arg = vhpet_tmrarg(vhpet, n);
|
||||
assert(!arg->running);
|
||||
arg->running = true;
|
||||
|
||||
/* Arm the new timer */
|
||||
if (acrn_timer_settime_abs(vhpet_tmr(vhpet, n), &ts))
|
||||
assert(0);
|
||||
|
||||
assert(ts_is_zero(&vhpet->timer[n].expts));
|
||||
vhpet->timer[n].expts = ts.it_value;
|
||||
}
|
||||
|
||||
static void
|
||||
vhpet_restart_timer(struct vhpet *vhpet, int n, bool adj_compval)
|
||||
{
|
||||
uint32_t counter;
|
||||
struct timespec now;
|
||||
|
||||
/*
|
||||
* Restart the specified timer based on the current value of
|
||||
* the main counter.
|
||||
*/
|
||||
counter = vhpet_counter(vhpet, &now);
|
||||
vhpet_start_timer(vhpet, n, counter, &now, adj_compval);
|
||||
}
|
||||
|
||||
static void
|
||||
vhpet_start_counting(struct vhpet *vhpet)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (clock_gettime(CLOCK_REALTIME, &vhpet->countbase_ts)) {
|
||||
perror("clock_gettime failed");
|
||||
assert(0);
|
||||
}
|
||||
|
||||
/* Restart the timers based on the main counter base value */
|
||||
for (i = 0; i < VHPET_NUM_TIMERS; i++) {
|
||||
if (vhpet_timer_enabled(vhpet, i))
|
||||
vhpet_start_timer(vhpet, i, vhpet->countbase,
|
||||
&vhpet->countbase_ts, true);
|
||||
else
|
||||
assert(!vhpet_timer_running(vhpet, i));
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
vhpet_stop_counting(struct vhpet *vhpet, uint32_t counter,
|
||||
const struct timespec *now)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* Update the main counter base value */
|
||||
vhpet->countbase = counter;
|
||||
|
||||
for (i = 0; i < VHPET_NUM_TIMERS; i++) {
|
||||
if (vhpet_timer_enabled(vhpet, i))
|
||||
vhpet_stop_timer(vhpet, i, now, true);
|
||||
else
|
||||
assert(!vhpet_timer_running(vhpet, i));
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
update_register(uint64_t *const regptr, const uint64_t data,
|
||||
const uint64_t mask)
|
||||
{
|
||||
*regptr &= ~mask;
|
||||
*regptr |= (data & mask);
|
||||
}
|
||||
|
||||
static void
|
||||
vhpet_timer_update_config(struct vhpet *vhpet, int n, uint64_t data,
|
||||
uint64_t mask)
|
||||
{
|
||||
int old_pin, new_pin;
|
||||
uint32_t allowed_irqs;
|
||||
uint64_t oldval, newval;
|
||||
struct timespec now;
|
||||
|
||||
if (vhpet_timer_msi_enabled(vhpet, n) ||
|
||||
vhpet_timer_edge_trig(vhpet, n))
|
||||
assert(!(vhpet->isr & (1 << n)));
|
||||
|
||||
old_pin = vhpet_timer_ioapic_pin(vhpet, n);
|
||||
oldval = vhpet->timer[n].cap_config;
|
||||
|
||||
newval = oldval;
|
||||
update_register(&newval, data, mask);
|
||||
newval &= ~(HPET_TCAP_RO_MASK | HPET_TCNF_32MODE);
|
||||
newval |= oldval & HPET_TCAP_RO_MASK;
|
||||
|
||||
if (newval == oldval)
|
||||
return;
|
||||
|
||||
vhpet->timer[n].cap_config = newval;
|
||||
DPRINTF(("hpet t%d cap_config set to 0x%016lx\n", n, newval));
|
||||
|
||||
if ((oldval ^ newval) & (HPET_TCNF_TYPE | HPET_TCNF_INT_ENB)) {
|
||||
if (!vhpet_periodic_timer(vhpet, n))
|
||||
vhpet->timer[n].comprate = 0;
|
||||
|
||||
if (vhpet_counter_enabled(vhpet)) {
|
||||
/*
|
||||
* Stop the timer if both bits are now cleared
|
||||
*
|
||||
* Else, restart the timer if:
|
||||
* - The timer was stopped, or
|
||||
* - HPET_TCNF_TYPE is being toggled
|
||||
*
|
||||
* Else, no-op
|
||||
* - Timer remains in periodic mode
|
||||
*/
|
||||
if (!vhpet_timer_enabled(vhpet, n)) {
|
||||
if (clock_gettime(CLOCK_REALTIME, &now)) {
|
||||
perror("clock_gettime failed");
|
||||
assert(0);
|
||||
}
|
||||
vhpet_stop_timer(vhpet, n, &now, true);
|
||||
} else if (!(oldval & (HPET_TCNF_TYPE | HPET_TCNF_INT_ENB)) ||
|
||||
((oldval ^ newval) & HPET_TCNF_TYPE))
|
||||
vhpet_restart_timer(vhpet, n, true);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Validate the interrupt routing in the HPET_TCNF_INT_ROUTE field.
|
||||
* If it does not match the bits set in HPET_TCAP_INT_ROUTE then set
|
||||
* it to the default value of 0.
|
||||
*/
|
||||
allowed_irqs = vhpet->timer[n].cap_config >> 32;
|
||||
new_pin = vhpet_timer_ioapic_pin(vhpet, n);
|
||||
|
||||
if (new_pin != 0 && (allowed_irqs & (1 << new_pin)) == 0) {
|
||||
WPRINTF(("hpet t%d configured invalid irq %d, "
|
||||
"allowed_irqs 0x%08x\n", n, new_pin, allowed_irqs));
|
||||
new_pin = 0;
|
||||
vhpet->timer[n].cap_config &= ~HPET_TCNF_INT_ROUTE;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the timer's ISR bit is set then clear it in the following cases:
|
||||
* - interrupt is disabled
|
||||
* - interrupt type is changed from level to edge or fsb.
|
||||
* - interrupt routing is changed
|
||||
*
|
||||
* This is to ensure that this timer's level triggered interrupt does
|
||||
* not remain asserted forever.
|
||||
*/
|
||||
if (vhpet->isr & (1 << n)) {
|
||||
assert(old_pin != 0);
|
||||
if (!vhpet_timer_interrupt_enabled(vhpet, n) ||
|
||||
vhpet_timer_msi_enabled(vhpet, n) ||
|
||||
vhpet_timer_edge_trig(vhpet, n) ||
|
||||
new_pin != old_pin) {
|
||||
DPRINTF(("hpet t%d isr cleared due to "
|
||||
"configuration change\n", n));
|
||||
vm_set_gsi_irq(vhpet->vm, old_pin, GSI_SET_LOW);
|
||||
vhpet->isr &= ~(1 << n);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
vhpet_mmio_write(struct vhpet *vhpet, int vcpuid, uint64_t gpa, uint64_t *wval,
|
||||
int size)
|
||||
{
|
||||
uint64_t data, mask, oldval, val64;
|
||||
uint32_t isr_clear_mask, old_compval, old_comprate, counter;
|
||||
struct timespec now, *nowptr;
|
||||
int offset, i;
|
||||
|
||||
offset = gpa - VHPET_BASE;
|
||||
|
||||
/* Accesses to the HPET should be 4 or 8 bytes wide */
|
||||
switch (size) {
|
||||
case 8:
|
||||
mask = 0xffffffffffffffff;
|
||||
data = *wval;
|
||||
break;
|
||||
case 4:
|
||||
mask = 0xffffffff;
|
||||
data = *wval;
|
||||
if ((offset & 0x4) != 0) {
|
||||
mask <<= 32;
|
||||
data <<= 32;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
WPRINTF(("hpet invalid mmio write: "
|
||||
"offset 0x%08x, size %d\n", offset, size));
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Access to the HPET should be naturally aligned to its width */
|
||||
if (offset & (size - 1)) {
|
||||
WPRINTF(("hpet invalid mmio write: "
|
||||
"offset 0x%08x, size %d\n", offset, size));
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (offset == HPET_CONFIG || offset == HPET_CONFIG + 4) {
|
||||
/*
|
||||
* Get the most recent value of the counter before updating
|
||||
* the 'config' register. If the HPET is going to be disabled
|
||||
* then we need to update 'countbase' with the value right
|
||||
* before it is disabled.
|
||||
*/
|
||||
nowptr = vhpet_counter_enabled(vhpet) ? &now : NULL;
|
||||
counter = vhpet_counter(vhpet, nowptr);
|
||||
oldval = vhpet->config;
|
||||
update_register(&vhpet->config, data, mask);
|
||||
|
||||
/*
|
||||
* LegacyReplacement Routing is not supported so clear the
|
||||
* bit along with the reserved bits explicitly.
|
||||
*/
|
||||
vhpet->config &= HPET_CNF_ENABLE;
|
||||
|
||||
if ((oldval ^ vhpet->config) & HPET_CNF_ENABLE) {
|
||||
if (vhpet_counter_enabled(vhpet)) {
|
||||
vhpet_start_counting(vhpet);
|
||||
DPRINTF(("hpet enabled\n"));
|
||||
} else {
|
||||
vhpet_stop_counting(vhpet, counter, &now);
|
||||
DPRINTF(("hpet disabled\n"));
|
||||
}
|
||||
}
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (offset == HPET_ISR || offset == HPET_ISR + 4) {
|
||||
/* Top 32 bits are reserved */
|
||||
isr_clear_mask = vhpet->isr & data;
|
||||
for (i = 0; i < VHPET_NUM_TIMERS; i++) {
|
||||
if ((isr_clear_mask & (1 << i)) != 0) {
|
||||
DPRINTF(("hpet t%d isr cleared\n", i));
|
||||
vhpet_timer_clear_isr(vhpet, i);
|
||||
}
|
||||
}
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (offset == HPET_MAIN_COUNTER || offset == HPET_MAIN_COUNTER + 4) {
|
||||
/* Zero-extend the counter to 64-bits before updating it */
|
||||
val64 = vhpet_counter(vhpet, NULL);
|
||||
update_register(&val64, data, mask);
|
||||
vhpet->countbase = val64;
|
||||
if (vhpet_counter_enabled(vhpet))
|
||||
vhpet_start_counting(vhpet);
|
||||
goto done;
|
||||
}
|
||||
|
||||
for (i = 0; i < VHPET_NUM_TIMERS; i++) {
|
||||
if (offset == HPET_TIMER_CAP_CNF(i) ||
|
||||
offset == HPET_TIMER_CAP_CNF(i) + 4) {
|
||||
vhpet_timer_update_config(vhpet, i, data, mask);
|
||||
break;
|
||||
}
|
||||
|
||||
if (offset == HPET_TIMER_COMPARATOR(i) ||
|
||||
offset == HPET_TIMER_COMPARATOR(i) + 4) {
|
||||
old_compval = vhpet->timer[i].compval;
|
||||
old_comprate = vhpet->timer[i].comprate;
|
||||
|
||||
if (vhpet_periodic_timer(vhpet, i)) {
|
||||
/*
|
||||
* In periodic mode, writes to the comparator
|
||||
* change the 'compval' register only if the
|
||||
* HPET_TCNF_VAL_SET bit is set in the config
|
||||
* register.
|
||||
*/
|
||||
val64 = vhpet->timer[i].comprate;
|
||||
update_register(&val64, data, mask);
|
||||
vhpet->timer[i].comprate = val64;
|
||||
|
||||
if ((vhpet->timer[i].cap_config &
|
||||
HPET_TCNF_VAL_SET) != 0)
|
||||
vhpet->timer[i].compval = val64;
|
||||
} else {
|
||||
assert(vhpet->timer[i].comprate == 0);
|
||||
val64 = vhpet->timer[i].compval;
|
||||
update_register(&val64, data, mask);
|
||||
vhpet->timer[i].compval = val64;
|
||||
}
|
||||
|
||||
vhpet->timer[i].cap_config &= ~HPET_TCNF_VAL_SET;
|
||||
|
||||
if (vhpet->timer[i].compval != old_compval ||
|
||||
vhpet->timer[i].comprate != old_comprate) {
|
||||
if (vhpet_counter_enabled(vhpet) &&
|
||||
vhpet_timer_enabled(vhpet, i))
|
||||
vhpet_restart_timer(vhpet, i, false);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (offset == HPET_TIMER_FSB_VAL(i) ||
|
||||
offset == HPET_TIMER_FSB_ADDR(i)) {
|
||||
update_register(&vhpet->timer[i].msireg, data, mask);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i >= VHPET_NUM_TIMERS)
|
||||
WPRINTF(("hpet invalid mmio write: "
|
||||
"offset 0x%08x, size %d\n", offset, size));
|
||||
|
||||
done:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
vhpet_mmio_read(struct vhpet *vhpet, int vcpuid, uint64_t gpa, uint64_t *rval,
|
||||
int size)
|
||||
{
|
||||
int offset, i;
|
||||
uint64_t data;
|
||||
|
||||
offset = gpa - VHPET_BASE;
|
||||
|
||||
/*
|
||||
* Accesses to the HPET should be:
|
||||
* - 4 or 8 bytes wide
|
||||
* - naturally aligned to its width
|
||||
*/
|
||||
if ((size != 4 && size != 8) || (offset & (size - 1))) {
|
||||
WPRINTF(("hpet invalid mmio read: "
|
||||
"offset 0x%08x, size %d\n", offset, size));
|
||||
data = 0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (offset == HPET_CAPABILITIES || offset == HPET_CAPABILITIES + 4) {
|
||||
data = vhpet_capabilities();
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (offset == HPET_CONFIG || offset == HPET_CONFIG + 4) {
|
||||
data = vhpet->config;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (offset == HPET_ISR || offset == HPET_ISR + 4) {
|
||||
data = vhpet->isr;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (offset == HPET_MAIN_COUNTER || offset == HPET_MAIN_COUNTER + 4) {
|
||||
data = vhpet_counter(vhpet, NULL);
|
||||
goto done;
|
||||
}
|
||||
|
||||
for (i = 0; i < VHPET_NUM_TIMERS; i++) {
|
||||
if (offset == HPET_TIMER_CAP_CNF(i) ||
|
||||
offset == HPET_TIMER_CAP_CNF(i) + 4) {
|
||||
data = vhpet->timer[i].cap_config;
|
||||
break;
|
||||
}
|
||||
|
||||
if (offset == HPET_TIMER_COMPARATOR(i) ||
|
||||
offset == HPET_TIMER_COMPARATOR(i) + 4) {
|
||||
data = vhpet->timer[i].compval;
|
||||
break;
|
||||
}
|
||||
|
||||
if (offset == HPET_TIMER_FSB_VAL(i) ||
|
||||
offset == HPET_TIMER_FSB_ADDR(i)) {
|
||||
data = vhpet->timer[i].msireg;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i >= VHPET_NUM_TIMERS) {
|
||||
WPRINTF(("hpet invalid mmio read: "
|
||||
"offset 0x%08x, size %d\n", offset, size));
|
||||
data = 0;
|
||||
}
|
||||
|
||||
done:
|
||||
if (size == 4) {
|
||||
if (offset & 0x4)
|
||||
data >>= 32;
|
||||
}
|
||||
|
||||
*rval = data;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
vhpet_handler(struct vmctx *ctx, int vcpu, int dir, uint64_t addr,
|
||||
int size, uint64_t *val, void *arg1, long arg2)
|
||||
{
|
||||
struct vhpet *vhpet = arg1;
|
||||
int error;
|
||||
|
||||
VHPET_LOCK();
|
||||
|
||||
if (!vhpet->inited) {
|
||||
error = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
error = ((dir == MEM_F_READ) ? vhpet_mmio_read : vhpet_mmio_write)(
|
||||
vhpet, vcpu, addr, val, size);
|
||||
|
||||
done:
|
||||
VHPET_UNLOCK();
|
||||
return error;
|
||||
}
|
||||
|
||||
static void
|
||||
vhpet_deinit_timers(struct vhpet *vhpet)
|
||||
{
|
||||
int i, j;
|
||||
struct acrn_timer *tmr;
|
||||
|
||||
for (i = 0; i < VHPET_NUM_TIMERS; i++) {
|
||||
for (j = 0; j < nitems(vhpet->timer[i].tmrlst); j++) {
|
||||
tmr = &vhpet->timer[i].tmrlst[j].t;
|
||||
acrn_timer_deinit(tmr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
vhpet_init(struct vmctx *ctx)
|
||||
{
|
||||
int error = 0, pincount, i, j;
|
||||
struct vhpet *vhpet;
|
||||
uint64_t allowed_irqs;
|
||||
struct vhpet_timer_arg *arg;
|
||||
struct acrn_timer *tmr;
|
||||
struct mem_range mr;
|
||||
|
||||
vhpet = vhpet_instance();
|
||||
|
||||
VHPET_LOCK();
|
||||
|
||||
if (vhpet->inited) {
|
||||
WPRINTF(("hpet already initialized!\n"));
|
||||
error = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
memset(vhpet, 0, sizeof(*vhpet));
|
||||
vhpet->vm = ctx;
|
||||
|
||||
pincount = VIOAPIC_RTE_NUM;
|
||||
|
||||
if (pincount >= 32)
|
||||
allowed_irqs = 0xff000000; /* irqs 24-31 */
|
||||
else if (pincount >= 20)
|
||||
allowed_irqs = 0xf << (pincount - 4); /* 4 upper irqs */
|
||||
else
|
||||
allowed_irqs = 0;
|
||||
|
||||
/*
|
||||
* Initialize HPET timer hardware state.
|
||||
*/
|
||||
for (i = 0; i < VHPET_NUM_TIMERS; i++) {
|
||||
vhpet->timer[i].cap_config = allowed_irqs << 32;
|
||||
vhpet->timer[i].cap_config |= HPET_TCAP_PER_INT;
|
||||
vhpet->timer[i].cap_config |= HPET_TCAP_FSB_INT_DEL;
|
||||
vhpet->timer[i].compval = 0xffffffff;
|
||||
|
||||
for (j = 0; j < nitems(vhpet->timer[i].tmrlst); j++) {
|
||||
arg = &vhpet->timer[i].tmrlst[j].a;
|
||||
arg->vhpet = vhpet;
|
||||
arg->timer_num = i;
|
||||
|
||||
tmr = &vhpet->timer[i].tmrlst[j].t;
|
||||
tmr->clockid = CLOCK_REALTIME;
|
||||
error = acrn_timer_init(tmr, vhpet_timer_handler, arg);
|
||||
|
||||
if (error) {
|
||||
vhpet_deinit_timers(vhpet);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mr.name = "vhpet";
|
||||
mr.base = VHPET_BASE;
|
||||
mr.size = VHPET_SIZE;
|
||||
mr.flags = MEM_F_RW;
|
||||
mr.handler = vhpet_handler;
|
||||
mr.arg1 = vhpet;
|
||||
mr.arg2 = 0;
|
||||
|
||||
error = register_mem(&mr);
|
||||
|
||||
if (error) {
|
||||
vhpet_deinit_timers(vhpet);
|
||||
goto done;
|
||||
}
|
||||
|
||||
vhpet->inited = true;
|
||||
|
||||
done:
|
||||
VHPET_UNLOCK();
|
||||
return error;
|
||||
}
|
||||
|
||||
void
|
||||
vhpet_deinit(struct vmctx *ctx)
|
||||
{
|
||||
struct vhpet *vhpet;
|
||||
|
||||
vhpet = vhpet_instance();
|
||||
|
||||
VHPET_LOCK();
|
||||
|
||||
if (!vhpet->inited)
|
||||
goto done;
|
||||
|
||||
vhpet_deinit_timers(vhpet);
|
||||
vhpet->inited = false;
|
||||
|
||||
done:
|
||||
VHPET_UNLOCK();
|
||||
}
|
@ -32,10 +32,9 @@
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "vmmapi.h"
|
||||
#include "timer.h"
|
||||
#include "inout.h"
|
||||
#include "pit.h"
|
||||
|
||||
@ -43,7 +42,6 @@
|
||||
|
||||
#define PIT_8254_FREQ (1193182)
|
||||
#define PIT_HZ_TO_TICKS(hz) ((PIT_8254_FREQ + (hz) / 2) / (hz))
|
||||
#define NS_PER_SEC (1000000000ULL)
|
||||
|
||||
#define PERIODIC_MODE(mode) \
|
||||
((mode) == TIMER_RATEGEN || (mode) == TIMER_SQWAVE)
|
||||
@ -62,6 +60,10 @@
|
||||
assert(err == 0); \
|
||||
} while (0)
|
||||
|
||||
#define vpit_ts_to_ticks(ts) ts_to_ticks(PIT_8254_FREQ, ts)
|
||||
/* won't overflow since the max value of ticks is 65536 */
|
||||
#define vpit_ticks_to_ts(tk, ts) ticks_to_ts(PIT_8254_FREQ, tk, ts)
|
||||
|
||||
|
||||
struct vpit_timer_arg {
|
||||
struct vpit *vpit;
|
||||
@ -92,32 +94,9 @@ struct vpit {
|
||||
|
||||
|
||||
/* one vPIT per VM */
|
||||
pthread_mutex_t vpit_mtx = PTHREAD_MUTEX_INITIALIZER;
|
||||
static pthread_mutex_t vpit_mtx = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
|
||||
static inline uint64_t
|
||||
ts_to_ticks(const struct timespec *ts)
|
||||
{
|
||||
uint64_t tv_sec_ticks, tv_nsec_ticks;
|
||||
|
||||
tv_sec_ticks = ts->tv_sec * PIT_8254_FREQ;
|
||||
tv_nsec_ticks = howmany(ts->tv_nsec * PIT_8254_FREQ, NS_PER_SEC);
|
||||
|
||||
return tv_sec_ticks + tv_nsec_ticks;
|
||||
}
|
||||
|
||||
static inline void
|
||||
ticks_to_ts(uint64_t ticks, struct timespec *ts)
|
||||
{
|
||||
uint64_t ns;
|
||||
|
||||
/* won't overflow since the max value of ticks is 65536 */
|
||||
ns = howmany(ticks * NS_PER_SEC, PIT_8254_FREQ);
|
||||
|
||||
ts->tv_sec = ns / NS_PER_SEC;
|
||||
ts->tv_nsec = ns % NS_PER_SEC;
|
||||
}
|
||||
|
||||
static uint64_t
|
||||
ticks_elapsed_since(const struct timespec *since)
|
||||
{
|
||||
@ -132,7 +111,7 @@ ticks_elapsed_since(const struct timespec *since)
|
||||
}
|
||||
|
||||
timespecsub(&ts, since);
|
||||
return ts_to_ticks(&ts);
|
||||
return vpit_ts_to_ticks(&ts);
|
||||
}
|
||||
|
||||
static bool
|
||||
@ -313,7 +292,7 @@ pit_timer_start_cntr0(struct vpit *vpit)
|
||||
assert(timespecisset(&ts.it_interval));
|
||||
|
||||
/* ts.it_value contains the remaining time until expiration */
|
||||
ticks_to_ts(pit_cr_val(c->cr), &ts.it_interval);
|
||||
vpit_ticks_to_ts(pit_cr_val(c->cr), &ts.it_interval);
|
||||
} else {
|
||||
/*
|
||||
* Aperiodic mode or no running periodic counter.
|
||||
@ -325,7 +304,7 @@ pit_timer_start_cntr0(struct vpit *vpit)
|
||||
|
||||
timer_ticks = (c->mode == TIMER_SWSTROBE) ?
|
||||
c->initial + 1 : c->initial;
|
||||
ticks_to_ts(timer_ticks, &ts.it_value);
|
||||
vpit_ticks_to_ts(timer_ticks, &ts.it_value);
|
||||
|
||||
/* make it periodic if required */
|
||||
if (PERIODIC_MODE(c->mode)) {
|
||||
|
@ -6,6 +6,8 @@
|
||||
#ifndef _TIMER_H_
|
||||
#define _TIMER_H_
|
||||
|
||||
#include <sys/param.h>
|
||||
|
||||
struct acrn_timer {
|
||||
int32_t fd;
|
||||
int32_t clockid;
|
||||
@ -26,4 +28,28 @@ acrn_timer_settime_abs(struct acrn_timer *timer,
|
||||
int32_t
|
||||
acrn_timer_gettime(struct acrn_timer *timer, struct itimerspec *cur_value);
|
||||
|
||||
#define NS_PER_SEC (1000000000ULL)
|
||||
|
||||
static inline uint64_t
|
||||
ts_to_ticks(const uint32_t freq, const struct timespec *const ts)
|
||||
{
|
||||
uint64_t tv_sec_ticks, tv_nsec_ticks;
|
||||
|
||||
tv_sec_ticks = ts->tv_sec * freq;
|
||||
tv_nsec_ticks = (ts->tv_nsec * freq) / NS_PER_SEC;
|
||||
|
||||
return tv_sec_ticks + tv_nsec_ticks;
|
||||
}
|
||||
|
||||
static inline void
|
||||
ticks_to_ts(const uint32_t freq, const uint64_t ticks,
|
||||
struct timespec *const ts)
|
||||
{
|
||||
uint64_t ns;
|
||||
|
||||
ns = howmany(ticks * NS_PER_SEC, freq);
|
||||
|
||||
ts->tv_sec = ns / NS_PER_SEC;
|
||||
ts->tv_nsec = ns % NS_PER_SEC;
|
||||
}
|
||||
#endif /* _VTIMER_ */
|
||||
|
Loading…
Reference in New Issue
Block a user