mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 10:11:27 +01:00
[HVM] Support multiple HVM time device models coming soon.
1: Adopt an abstract layer for periodic time on top of different
HV time device models like PIT, RTC and ACPI time.
2: Extract VMM knowledge from PIT DM code and keep close with
qemu for easy maintain in future.
3: Use guest time (TSC) to drive PIT CLK that helps us to remove
a lot of extra logic previously introduced to convert from host time to
guest time.
4: Some cleanup for SMP code like move cache_tsc_offset to per VP.
Signed-off-by: Eddie Dong <eddie.dong@intel.com>
This commit is contained in:
1 parent
e4813816b2
commit
86d5da49a6
14 files changed
+329
-408
No files matched your search
+12
-2
@@ -185,8 +185,9 @@ static void hvm_get_info(struct domain *d)
|
||||
void hvm_setup_platform(struct domain* d)
|
||||
{
|
||||
struct hvm_domain *platform;
|
||||
struct vcpu *v=current;
|
||||
|
||||
if ( !hvm_guest(current) || (current->vcpu_id != 0) )
|
||||
if ( !hvm_guest(v) || (v->vcpu_id != 0) )
|
||||
return;
|
||||
|
||||
if ( shadow_direct_map_init(d) == 0 )
|
||||
@@ -208,7 +209,8 @@ void hvm_setup_platform(struct domain* d)
|
||||
hvm_vioapic_init(d);
|
||||
}
|
||||
|
||||
pit_init(&platform->vpit, current);
|
||||
init_timer(&platform->pl_time.periodic_tm.timer, pt_timer_fn, v, v->processor);
|
||||
pit_init(v, cpu_khz);
|
||||
}
|
||||
|
||||
void pic_irq_request(void *data, int level)
|
||||
@@ -240,6 +242,14 @@ void hvm_pic_assist(struct vcpu *v)
|
||||
}
|
||||
}
|
||||
|
||||
u64 hvm_get_guest_time(struct vcpu *v)
|
||||
{
|
||||
u64 host_tsc;
|
||||
|
||||
rdtscll(host_tsc);
|
||||
return host_tsc + v->arch.hvm_vcpu.cache_tsc_offset;
|
||||
}
|
||||
|
||||
int cpu_get_interrupt(struct vcpu *v, int *type)
|
||||
{
|
||||
int intno;
|
||||
|
||||
+121
-260
@@ -22,11 +22,10 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
/* Edwin Zhai <edwin.zhai@intel.com>
|
||||
/* Edwin Zhai <edwin.zhai@intel.com>, Eddie Dong <eddie.dong@intel.com>
|
||||
* Ported to xen:
|
||||
* use actimer for intr generation;
|
||||
* Add a new layer of periodic time on top of PIT;
|
||||
* move speaker io access to hypervisor;
|
||||
* use new method for counter/intrs calculation
|
||||
*/
|
||||
|
||||
#include <xen/config.h>
|
||||
@@ -42,184 +41,117 @@
|
||||
#include <asm/hvm/vpit.h>
|
||||
#include <asm/current.h>
|
||||
|
||||
/*#define DEBUG_PIT*/
|
||||
/* Enable DEBUG_PIT may cause guest calibration inaccuracy */
|
||||
/* #define DEBUG_PIT */
|
||||
|
||||
#define RW_STATE_LSB 1
|
||||
#define RW_STATE_MSB 2
|
||||
#define RW_STATE_WORD0 3
|
||||
#define RW_STATE_WORD1 4
|
||||
|
||||
#ifndef NSEC_PER_SEC
|
||||
#define NSEC_PER_SEC (1000000000ULL)
|
||||
#endif
|
||||
#define ticks_per_sec(v) (v->domain->arch.hvm_domain.tsc_frequency)
|
||||
static int handle_pit_io(ioreq_t *p);
|
||||
static int handle_speaker_io(ioreq_t *p);
|
||||
|
||||
#ifndef TIMER_SLOP
|
||||
#define TIMER_SLOP (50*1000) /* ns */
|
||||
#endif
|
||||
|
||||
static void pit_irq_timer_update(PITChannelState *s, s64 current_time);
|
||||
|
||||
s_time_t hvm_get_clock(void)
|
||||
/* compute with 96 bit intermediate result: (a*b)/c */
|
||||
uint64_t muldiv64(uint64_t a, uint32_t b, uint32_t c)
|
||||
{
|
||||
/* TODO: add pause/unpause support */
|
||||
return NOW();
|
||||
union {
|
||||
uint64_t ll;
|
||||
struct {
|
||||
#ifdef WORDS_BIGENDIAN
|
||||
uint32_t high, low;
|
||||
#else
|
||||
uint32_t low, high;
|
||||
#endif
|
||||
} l;
|
||||
} u, res;
|
||||
uint64_t rl, rh;
|
||||
|
||||
u.ll = a;
|
||||
rl = (uint64_t)u.l.low * (uint64_t)b;
|
||||
rh = (uint64_t)u.l.high * (uint64_t)b;
|
||||
rh += (rl >> 32);
|
||||
res.l.high = rh / c;
|
||||
res.l.low = (((rh % c) << 32) + (rl & 0xffffffff)) / c;
|
||||
return res.ll;
|
||||
}
|
||||
|
||||
/*
|
||||
* get processor time.
|
||||
* unit: TSC
|
||||
*/
|
||||
int64_t hvm_get_clock(struct vcpu *v)
|
||||
{
|
||||
uint64_t gtsc;
|
||||
gtsc = hvm_get_guest_time(v);
|
||||
return gtsc;
|
||||
}
|
||||
|
||||
static int pit_get_count(PITChannelState *s)
|
||||
{
|
||||
u64 d;
|
||||
u64 counter;
|
||||
uint64_t d;
|
||||
int counter;
|
||||
|
||||
d = hvm_get_clock() - s->count_load_time;
|
||||
d = muldiv64(hvm_get_clock(s->vcpu) - s->count_load_time, PIT_FREQ, ticks_per_sec(s->vcpu));
|
||||
switch(s->mode) {
|
||||
case 0:
|
||||
case 1:
|
||||
case 4:
|
||||
case 5:
|
||||
counter = (s->period - d) & 0xffff;
|
||||
counter = (s->count - d) & 0xffff;
|
||||
break;
|
||||
case 3:
|
||||
/* XXX: may be incorrect for odd counts */
|
||||
counter = s->period - ((2 * d) % s->period);
|
||||
counter = s->count - ((2 * d) % s->count);
|
||||
break;
|
||||
default:
|
||||
/* mod 2 counter handle */
|
||||
d = hvm_get_clock() - s->hvm_time->count_point;
|
||||
d += s->hvm_time->count_advance;
|
||||
counter = s->period - (d % s->period);
|
||||
counter = s->count - (d % s->count);
|
||||
break;
|
||||
}
|
||||
/* change from ns to pit counter */
|
||||
counter = DIV_ROUND( (counter * PIT_FREQ), NSEC_PER_SEC);
|
||||
return counter;
|
||||
}
|
||||
|
||||
/* get pit output bit */
|
||||
static int pit_get_out1(PITChannelState *s, s64 current_time)
|
||||
static int pit_get_out1(PITChannelState *s, int64_t current_time)
|
||||
{
|
||||
u64 d;
|
||||
uint64_t d;
|
||||
int out;
|
||||
|
||||
d = current_time - s->count_load_time;
|
||||
d = muldiv64(current_time - s->count_load_time, PIT_FREQ, ticks_per_sec(s->vcpu));
|
||||
switch(s->mode) {
|
||||
default:
|
||||
case 0:
|
||||
out = (d >= s->period);
|
||||
out = (d >= s->count);
|
||||
break;
|
||||
case 1:
|
||||
out = (d < s->period);
|
||||
out = (d < s->count);
|
||||
break;
|
||||
case 2:
|
||||
/* mod2 out is no meaning, since intr are generated in background */
|
||||
if ((d % s->period) == 0 && d != 0)
|
||||
if ((d % s->count) == 0 && d != 0)
|
||||
out = 1;
|
||||
else
|
||||
out = 0;
|
||||
break;
|
||||
case 3:
|
||||
out = (d % s->period) < ((s->period + 1) >> 1);
|
||||
out = (d % s->count) < ((s->count + 1) >> 1);
|
||||
break;
|
||||
case 4:
|
||||
case 5:
|
||||
out = (d == s->period);
|
||||
out = (d == s->count);
|
||||
break;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
int pit_get_out(hvm_virpit *pit, int channel, s64 current_time)
|
||||
int pit_get_out(PITState *pit, int channel, int64_t current_time)
|
||||
{
|
||||
PITChannelState *s = &pit->channels[channel];
|
||||
return pit_get_out1(s, current_time);
|
||||
}
|
||||
|
||||
static __inline__ s64 missed_ticks(PITChannelState *s, s64 current_time)
|
||||
{
|
||||
struct hvm_time_info *hvm_time = s->hvm_time;
|
||||
struct domain *d = (void *) s -
|
||||
offsetof(struct domain, arch.hvm_domain.vpit.channels[0]);
|
||||
|
||||
/* ticks from current time(expected time) to NOW */
|
||||
int missed_ticks;
|
||||
/* current_time is expected time for next intr, check if it's true
|
||||
* (actimer has a TIMER_SLOP in advance)
|
||||
*/
|
||||
s64 missed_time = hvm_get_clock() + TIMER_SLOP - current_time;
|
||||
|
||||
if (missed_time >= 0) {
|
||||
missed_ticks = missed_time/(s_time_t)s->period + 1;
|
||||
if (test_bit(_DOMF_debugging, &d->domain_flags)) {
|
||||
hvm_time->pending_intr_nr++;
|
||||
} else {
|
||||
hvm_time->pending_intr_nr += missed_ticks;
|
||||
}
|
||||
s->next_transition_time = current_time + (missed_ticks ) * s->period;
|
||||
}
|
||||
|
||||
return s->next_transition_time;
|
||||
}
|
||||
|
||||
/* only rearm the actimer when return value > 0
|
||||
* -2: init state
|
||||
* -1: the mode has expired
|
||||
* 0: current VCPU is not running
|
||||
* >0: the next fired time
|
||||
*/
|
||||
s64 pit_get_next_transition_time(PITChannelState *s,
|
||||
s64 current_time)
|
||||
{
|
||||
s64 d, next_time, base;
|
||||
int period2;
|
||||
struct hvm_time_info *hvm_time = s->hvm_time;
|
||||
|
||||
d = current_time - s->count_load_time;
|
||||
switch(s->mode) {
|
||||
default:
|
||||
case 0:
|
||||
case 1:
|
||||
if (d < s->period)
|
||||
next_time = s->period;
|
||||
else
|
||||
return -1;
|
||||
break;
|
||||
case 2:
|
||||
next_time = missed_ticks(s, current_time);
|
||||
if ( !test_bit(_VCPUF_running, &(hvm_time->vcpu->vcpu_flags)) )
|
||||
return 0;
|
||||
break;
|
||||
case 3:
|
||||
base = (d / s->period) * s->period;
|
||||
period2 = ((s->period + 1) >> 1);
|
||||
if ((d - base) < period2)
|
||||
next_time = base + period2;
|
||||
else
|
||||
next_time = base + s->period;
|
||||
break;
|
||||
case 4:
|
||||
case 5:
|
||||
if (d < s->period)
|
||||
next_time = s->period;
|
||||
else if (d == s->period)
|
||||
next_time = s->period + 1;
|
||||
else
|
||||
return -1;
|
||||
break;
|
||||
case 0xff:
|
||||
return -2; /* for init state */
|
||||
break;
|
||||
}
|
||||
/* XXX: better solution: use a clock at PIT_FREQ Hz */
|
||||
if (next_time <= current_time){
|
||||
#ifdef DEBUG_PIT
|
||||
printk("HVM_PIT:next_time <= current_time. next=0x%llx, current=0x%llx!\n",next_time, current_time);
|
||||
#endif
|
||||
next_time = current_time + 1;
|
||||
}
|
||||
return next_time;
|
||||
}
|
||||
|
||||
/* val must be 0 or 1 */
|
||||
void pit_set_gate(hvm_virpit *pit, int channel, int val)
|
||||
void pit_set_gate(PITState *pit, int channel, int val)
|
||||
{
|
||||
PITChannelState *s = &pit->channels[channel];
|
||||
|
||||
@@ -233,16 +165,16 @@ void pit_set_gate(hvm_virpit *pit, int channel, int val)
|
||||
case 5:
|
||||
if (s->gate < val) {
|
||||
/* restart counting on rising edge */
|
||||
s->count_load_time = hvm_get_clock();
|
||||
pit_irq_timer_update(s, s->count_load_time);
|
||||
s->count_load_time = hvm_get_clock(s->vcpu);
|
||||
// pit_irq_timer_update(s, s->count_load_time);
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
case 3:
|
||||
if (s->gate < val) {
|
||||
/* restart counting on rising edge */
|
||||
s->count_load_time = hvm_get_clock();
|
||||
pit_irq_timer_update(s, s->count_load_time);
|
||||
s->count_load_time = hvm_get_clock(s->vcpu);
|
||||
// pit_irq_timer_update(s, s->count_load_time);
|
||||
}
|
||||
/* XXX: disable/enable counting */
|
||||
break;
|
||||
@@ -250,7 +182,7 @@ void pit_set_gate(hvm_virpit *pit, int channel, int val)
|
||||
s->gate = val;
|
||||
}
|
||||
|
||||
int pit_get_gate(hvm_virpit *pit, int channel)
|
||||
int pit_get_gate(PITState *pit, int channel)
|
||||
{
|
||||
PITChannelState *s = &pit->channels[channel];
|
||||
return s->gate;
|
||||
@@ -258,37 +190,37 @@ int pit_get_gate(hvm_virpit *pit, int channel)
|
||||
|
||||
static inline void pit_load_count(PITChannelState *s, int val)
|
||||
{
|
||||
u32 period;
|
||||
if (val == 0)
|
||||
val = 0x10000;
|
||||
|
||||
s->count_load_time = hvm_get_clock();
|
||||
s->count_load_time = hvm_get_clock(s->vcpu);
|
||||
s->count = val;
|
||||
s->period = DIV_ROUND(((s->count) * NSEC_PER_SEC), PIT_FREQ);
|
||||
period = DIV_ROUND((val * 1000000000ULL), PIT_FREQ);
|
||||
|
||||
#ifdef DEBUG_PIT
|
||||
printk("HVM_PIT: pit-load-counter, count=0x%x,period=0x%u us,mode=%d, load_time=%lld\n",
|
||||
printk("HVM_PIT: pit-load-counter(%p), count=0x%x, period=%uns mode=%d, load_time=%lld\n",
|
||||
s,
|
||||
val,
|
||||
s->period / 1000,
|
||||
period,
|
||||
s->mode,
|
||||
s->count_load_time);
|
||||
(long long)s->count_load_time);
|
||||
#endif
|
||||
|
||||
if (s->mode == HVM_PIT_ACCEL_MODE) {
|
||||
if (!s->hvm_time) {
|
||||
printk("HVM_PIT:guest should only set mod 2 on channel 0!\n");
|
||||
return;
|
||||
}
|
||||
s->hvm_time->period_cycles = (u64)s->period * cpu_khz / 1000000L;
|
||||
s->hvm_time->first_injected = 0;
|
||||
|
||||
if (s->period < 900000) { /* < 0.9 ms */
|
||||
printk("HVM_PIT: guest programmed too small an count: %x\n",
|
||||
s->count);
|
||||
s->period = 1000000;
|
||||
}
|
||||
switch (s->mode) {
|
||||
case 2:
|
||||
/* create periodic time */
|
||||
s->pt = create_periodic_time (s->vcpu, period, 0, 0);
|
||||
break;
|
||||
case 1:
|
||||
/* create one shot time */
|
||||
s->pt = create_periodic_time (s->vcpu, period, 0, 1);
|
||||
#ifdef DEBUG_PIT
|
||||
printk("HVM_PIT: create one shot time.\n");
|
||||
#endif
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
pit_irq_timer_update(s, s->count_load_time);
|
||||
}
|
||||
|
||||
/* if already latched, do not latch again */
|
||||
@@ -300,9 +232,9 @@ static void pit_latch_count(PITChannelState *s)
|
||||
}
|
||||
}
|
||||
|
||||
static void pit_ioport_write(void *opaque, u32 addr, u32 val)
|
||||
static void pit_ioport_write(void *opaque, uint32_t addr, uint32_t val)
|
||||
{
|
||||
hvm_virpit *pit = opaque;
|
||||
PITState *pit = opaque;
|
||||
int channel, access;
|
||||
PITChannelState *s;
|
||||
val &= 0xff;
|
||||
@@ -321,7 +253,7 @@ static void pit_ioport_write(void *opaque, u32 addr, u32 val)
|
||||
if (!(val & 0x10) && !s->status_latched) {
|
||||
/* status latch */
|
||||
/* XXX: add BCD and null count */
|
||||
s->status = (pit_get_out1(s, hvm_get_clock()) << 7) |
|
||||
s->status = (pit_get_out1(s, hvm_get_clock(s->vcpu)) << 7) |
|
||||
(s->rw_mode << 4) |
|
||||
(s->mode << 1) |
|
||||
s->bcd;
|
||||
@@ -366,9 +298,9 @@ static void pit_ioport_write(void *opaque, u32 addr, u32 val)
|
||||
}
|
||||
}
|
||||
|
||||
static u32 pit_ioport_read(void *opaque, u32 addr)
|
||||
static uint32_t pit_ioport_read(void *opaque, uint32_t addr)
|
||||
{
|
||||
hvm_virpit *pit = opaque;
|
||||
PITState *pit = opaque;
|
||||
int ret, count;
|
||||
PITChannelState *s;
|
||||
|
||||
@@ -419,84 +351,51 @@ static u32 pit_ioport_read(void *opaque, u32 addr)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void pit_irq_timer_update(PITChannelState *s, s64 current_time)
|
||||
{
|
||||
s64 expire_time;
|
||||
int irq_level;
|
||||
struct vcpu *v = current;
|
||||
struct hvm_virpic *pic= &v->domain->arch.hvm_domain.vpic;
|
||||
|
||||
if (!s->hvm_time || s->mode == 0xff)
|
||||
return;
|
||||
|
||||
expire_time = pit_get_next_transition_time(s, current_time);
|
||||
/* not generate intr by direct pic_set_irq in mod 2
|
||||
* XXX:mod 3 should be same as mod 2
|
||||
*/
|
||||
if (s->mode != HVM_PIT_ACCEL_MODE) {
|
||||
irq_level = pit_get_out1(s, current_time);
|
||||
pic_set_irq(pic, s->irq, irq_level);
|
||||
s->next_transition_time = expire_time;
|
||||
#ifdef DEBUG_PIT
|
||||
printk("HVM_PIT:irq_level=%d next_delay=%l ns\n",
|
||||
irq_level,
|
||||
(expire_time - current_time));
|
||||
#endif
|
||||
}
|
||||
|
||||
if (expire_time > 0)
|
||||
set_timer(&(s->hvm_time->pit_timer), s->next_transition_time);
|
||||
|
||||
}
|
||||
|
||||
static void pit_irq_timer(void *data)
|
||||
{
|
||||
PITChannelState *s = data;
|
||||
|
||||
pit_irq_timer_update(s, s->next_transition_time);
|
||||
}
|
||||
|
||||
static void pit_reset(void *opaque)
|
||||
{
|
||||
hvm_virpit *pit = opaque;
|
||||
PITState *pit = opaque;
|
||||
PITChannelState *s;
|
||||
int i;
|
||||
|
||||
for(i = 0;i < 3; i++) {
|
||||
s = &pit->channels[i];
|
||||
if ( s -> pt ) {
|
||||
destroy_periodic_time (s->pt);
|
||||
s->pt = NULL;
|
||||
}
|
||||
s->mode = 0xff; /* the init mode */
|
||||
s->gate = (i != 2);
|
||||
pit_load_count(s, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* hvm_io_assist light-weight version, specific to PIT DM */
|
||||
static void resume_pit_io(ioreq_t *p)
|
||||
void pit_init(struct vcpu *v, unsigned long cpu_khz)
|
||||
{
|
||||
struct cpu_user_regs *regs = guest_cpu_user_regs();
|
||||
unsigned long old_eax = regs->eax;
|
||||
p->state = STATE_INVALID;
|
||||
PITState *pit = &v->domain->arch.hvm_domain.pl_time.vpit;
|
||||
PITChannelState *s;
|
||||
|
||||
switch(p->size) {
|
||||
case 1:
|
||||
regs->eax = (old_eax & 0xffffff00) | (p->u.data & 0xff);
|
||||
break;
|
||||
case 2:
|
||||
regs->eax = (old_eax & 0xffff0000) | (p->u.data & 0xffff);
|
||||
break;
|
||||
case 4:
|
||||
regs->eax = (p->u.data & 0xffffffff);
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
s = &pit->channels[0];
|
||||
/* the timer 0 is connected to an IRQ */
|
||||
s->vcpu = v;
|
||||
s++; s->vcpu = v;
|
||||
s++; s->vcpu = v;
|
||||
|
||||
register_portio_handler(PIT_BASE, 4, handle_pit_io);
|
||||
/* register the speaker port */
|
||||
register_portio_handler(0x61, 1, handle_speaker_io);
|
||||
ticks_per_sec(v) = cpu_khz * (int64_t)1000;
|
||||
#ifdef DEBUG_PIT
|
||||
printk("HVM_PIT: guest frequency =%lld\n", (long long)ticks_per_sec(v));
|
||||
#endif
|
||||
pit_reset(pit);
|
||||
return;
|
||||
}
|
||||
|
||||
/* the intercept action for PIT DM retval:0--not handled; 1--handled */
|
||||
int handle_pit_io(ioreq_t *p)
|
||||
static int handle_pit_io(ioreq_t *p)
|
||||
{
|
||||
struct vcpu *v = current;
|
||||
struct hvm_virpit *vpit = &(v->domain->arch.hvm_domain.vpit);
|
||||
struct PITState *vpit = &(v->domain->arch.hvm_domain.pl_time.vpit);
|
||||
|
||||
if (p->size != 1 ||
|
||||
p->pdata_valid ||
|
||||
@@ -508,18 +407,18 @@ int handle_pit_io(ioreq_t *p)
|
||||
if (p->dir == 0) {/* write */
|
||||
pit_ioport_write(vpit, p->addr, p->u.data);
|
||||
} else if (p->dir == 1) { /* read */
|
||||
p->u.data = pit_ioport_read(vpit, p->addr);
|
||||
resume_pit_io(p);
|
||||
if ( (p->addr & 3) != 3 ) {
|
||||
p->u.data = pit_ioport_read(vpit, p->addr);
|
||||
} else {
|
||||
printk("HVM_PIT: read A1:A0=3!\n");
|
||||
}
|
||||
}
|
||||
|
||||
/* always return 1, since PIT sit in HV now */
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void speaker_ioport_write(void *opaque, uint32_t addr, uint32_t val)
|
||||
{
|
||||
hvm_virpit *pit = opaque;
|
||||
val &= 0xff;
|
||||
PITState *pit = opaque;
|
||||
pit->speaker_data_on = (val >> 1) & 1;
|
||||
pit_set_gate(pit, 2, val & 1);
|
||||
}
|
||||
@@ -527,18 +426,18 @@ static void speaker_ioport_write(void *opaque, uint32_t addr, uint32_t val)
|
||||
static uint32_t speaker_ioport_read(void *opaque, uint32_t addr)
|
||||
{
|
||||
int out;
|
||||
hvm_virpit *pit = opaque;
|
||||
out = pit_get_out(pit, 2, hvm_get_clock());
|
||||
PITState *pit = opaque;
|
||||
out = pit_get_out(pit, 2, hvm_get_clock(pit->channels[2].vcpu));
|
||||
pit->dummy_refresh_clock ^= 1;
|
||||
|
||||
return (pit->speaker_data_on << 1) | pit_get_gate(pit, 2) | (out << 5) |
|
||||
(pit->dummy_refresh_clock << 4);
|
||||
}
|
||||
|
||||
int handle_speaker_io(ioreq_t *p)
|
||||
static int handle_speaker_io(ioreq_t *p)
|
||||
{
|
||||
struct vcpu *v = current;
|
||||
struct hvm_virpit *vpit = &(v->domain->arch.hvm_domain.vpit);
|
||||
struct PITState *vpit = &(v->domain->arch.hvm_domain.pl_time.vpit);
|
||||
|
||||
if (p->size != 1 ||
|
||||
p->pdata_valid ||
|
||||
@@ -551,45 +450,7 @@ int handle_speaker_io(ioreq_t *p)
|
||||
speaker_ioport_write(vpit, p->addr, p->u.data);
|
||||
} else if (p->dir == 1) {/* read */
|
||||
p->u.data = speaker_ioport_read(vpit, p->addr);
|
||||
resume_pit_io(p);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* pick up missed timer ticks at deactive time */
|
||||
void pickup_deactive_ticks(struct hvm_virpit *vpit)
|
||||
{
|
||||
s64 next_time;
|
||||
PITChannelState *s = &(vpit->channels[0]);
|
||||
if ( !active_timer(&(vpit->time_info.pit_timer)) ) {
|
||||
next_time = pit_get_next_transition_time(s, s->next_transition_time);
|
||||
if (next_time >= 0)
|
||||
set_timer(&(s->hvm_time->pit_timer), s->next_transition_time);
|
||||
}
|
||||
}
|
||||
|
||||
void pit_init(struct hvm_virpit *pit, struct vcpu *v)
|
||||
{
|
||||
PITChannelState *s;
|
||||
struct hvm_time_info *hvm_time;
|
||||
|
||||
s = &pit->channels[0];
|
||||
/* the timer 0 is connected to an IRQ */
|
||||
s->irq = 0;
|
||||
/* channel 0 need access the related time info for intr injection */
|
||||
hvm_time = s->hvm_time = &pit->time_info;
|
||||
hvm_time->vcpu = v;
|
||||
|
||||
init_timer(&(hvm_time->pit_timer), pit_irq_timer, s, v->processor);
|
||||
|
||||
register_portio_handler(PIT_BASE, 4, handle_pit_io);
|
||||
|
||||
/* register the speaker port */
|
||||
register_portio_handler(0x61, 1, handle_speaker_io);
|
||||
|
||||
pit_reset(pit);
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
@@ -214,6 +214,88 @@ void hlt_timer_fn(void *data)
|
||||
evtchn_set_pending(v, iopacket_port(v));
|
||||
}
|
||||
|
||||
static __inline__ void missed_ticks(struct periodic_time *pt)
|
||||
{
|
||||
int missed_ticks;
|
||||
|
||||
missed_ticks = (NOW() - pt->scheduled)/(s_time_t) pt->period;
|
||||
if ( missed_ticks++ >= 0 ) {
|
||||
if ( missed_ticks > 1000 ) {
|
||||
/* TODO: Adjust guest time togther */
|
||||
pt->pending_intr_nr ++;
|
||||
}
|
||||
else {
|
||||
pt->pending_intr_nr += missed_ticks;
|
||||
}
|
||||
pt->scheduled += missed_ticks * pt->period;
|
||||
}
|
||||
}
|
||||
|
||||
/* hook function for the platform periodic time */
|
||||
void pt_timer_fn(void *data)
|
||||
{
|
||||
struct vcpu *v = data;
|
||||
struct periodic_time *pt = &(v->domain->arch.hvm_domain.pl_time.periodic_tm);
|
||||
|
||||
/* pick up missed timer tick */
|
||||
missed_ticks(pt);
|
||||
if ( test_bit(_VCPUF_running, &v->vcpu_flags) ) {
|
||||
set_timer(&pt->timer, pt->scheduled);
|
||||
}
|
||||
}
|
||||
|
||||
/* pick up missed timer ticks at deactive time */
|
||||
void pickup_deactive_ticks(struct periodic_time *pt)
|
||||
{
|
||||
if ( !active_timer(&(pt->timer)) ) {
|
||||
missed_ticks(pt);
|
||||
set_timer(&pt->timer, pt->scheduled);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* period: fire frequency in ns.
|
||||
*/
|
||||
struct periodic_time * create_periodic_time(
|
||||
struct vcpu *v,
|
||||
u32 period,
|
||||
char irq,
|
||||
char one_shot)
|
||||
{
|
||||
struct periodic_time *pt = &(v->domain->arch.hvm_domain.pl_time.periodic_tm);
|
||||
if ( pt->enabled ) {
|
||||
if ( v->vcpu_id != 0 ) {
|
||||
printk("HVM_PIT: start 2nd periodic time on non BSP!\n");
|
||||
}
|
||||
stop_timer (&pt->timer);
|
||||
pt->enabled = 0;
|
||||
}
|
||||
pt->pending_intr_nr = 0;
|
||||
pt->first_injected = 0;
|
||||
if (period < 900000) { /* < 0.9 ms */
|
||||
printk("HVM_PlatformTime: program too small period %u\n",period);
|
||||
period = 900000; /* force to 0.9ms */
|
||||
}
|
||||
pt->period = period;
|
||||
pt->irq = irq;
|
||||
pt->period_cycles = (u64)period * cpu_khz / 1000000L;
|
||||
pt->one_shot = one_shot;
|
||||
if ( one_shot ) {
|
||||
printk("HVM_PL: No support for one shot platform time yet\n");
|
||||
}
|
||||
pt->scheduled = NOW() + period;
|
||||
set_timer (&pt->timer,pt->scheduled);
|
||||
pt->enabled = 1;
|
||||
return pt;
|
||||
}
|
||||
|
||||
void destroy_periodic_time(struct periodic_time *pt)
|
||||
{
|
||||
if ( pt->enabled ) {
|
||||
stop_timer(&pt->timer);
|
||||
pt->enabled = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
|
||||
+17
-30
@@ -44,45 +44,33 @@
|
||||
*/
|
||||
#define BSP_CPU(v) (!(v->vcpu_id))
|
||||
|
||||
u64 svm_get_guest_time(struct vcpu *v)
|
||||
{
|
||||
struct hvm_time_info *time_info = &(v->domain->arch.hvm_domain.vpit.time_info);
|
||||
u64 host_tsc;
|
||||
|
||||
rdtscll(host_tsc);
|
||||
return host_tsc + time_info->cache_tsc_offset;
|
||||
}
|
||||
|
||||
void svm_set_guest_time(struct vcpu *v, u64 gtime)
|
||||
{
|
||||
struct hvm_time_info *time_info = &(v->domain->arch.hvm_domain.vpit.time_info);
|
||||
u64 host_tsc;
|
||||
|
||||
rdtscll(host_tsc);
|
||||
|
||||
time_info->cache_tsc_offset = gtime - host_tsc;
|
||||
v->arch.hvm_svm.vmcb->tsc_offset = time_info->cache_tsc_offset;
|
||||
v->arch.hvm_vcpu.cache_tsc_offset = gtime - host_tsc;
|
||||
v->arch.hvm_svm.vmcb->tsc_offset = v->arch.hvm_vcpu.cache_tsc_offset;
|
||||
}
|
||||
|
||||
static inline void
|
||||
interrupt_post_injection(struct vcpu * v, int vector, int type)
|
||||
{
|
||||
struct hvm_virpit *vpit = &(v->domain->arch.hvm_domain.vpit);
|
||||
struct hvm_time_info *time_info = &vpit->time_info;
|
||||
struct periodic_time *pt = &(v->domain->arch.hvm_domain.pl_time.periodic_tm);
|
||||
|
||||
if ( is_pit_irq(v, vector, type) ) {
|
||||
if ( !time_info->first_injected ) {
|
||||
time_info->pending_intr_nr = 0;
|
||||
time_info->last_pit_gtime = svm_get_guest_time(v);
|
||||
time_info->first_injected = 1;
|
||||
if ( !pt->first_injected ) {
|
||||
pt->pending_intr_nr = 0;
|
||||
pt->last_plt_gtime = hvm_get_guest_time(v);
|
||||
pt->scheduled = NOW() + pt->period;
|
||||
set_timer(&pt->timer, pt->scheduled);
|
||||
pt->first_injected = 1;
|
||||
} else {
|
||||
time_info->pending_intr_nr--;
|
||||
pt->pending_intr_nr--;
|
||||
pt->last_plt_gtime += pt->period_cycles;
|
||||
svm_set_guest_time(v, pt->last_plt_gtime);
|
||||
}
|
||||
time_info->count_advance = 0;
|
||||
time_info->count_point = NOW();
|
||||
|
||||
time_info->last_pit_gtime += time_info->period_cycles;
|
||||
svm_set_guest_time(v, time_info->last_pit_gtime);
|
||||
}
|
||||
|
||||
switch(type)
|
||||
@@ -121,8 +109,7 @@ asmlinkage void svm_intr_assist(void)
|
||||
struct vcpu *v = current;
|
||||
struct vmcb_struct *vmcb = v->arch.hvm_svm.vmcb;
|
||||
struct hvm_domain *plat=&v->domain->arch.hvm_domain;
|
||||
struct hvm_virpit *vpit = &plat->vpit;
|
||||
struct hvm_time_info *time_info = &vpit->time_info;
|
||||
struct periodic_time *pt = &plat->pl_time.periodic_tm;
|
||||
struct hvm_virpic *pic= &plat->vpic;
|
||||
int intr_type = VLAPIC_DELIV_MODE_EXT;
|
||||
int intr_vector = -1;
|
||||
@@ -174,9 +161,9 @@ asmlinkage void svm_intr_assist(void)
|
||||
if ( cpu_has_pending_irq(v) ) {
|
||||
intr_vector = cpu_get_interrupt(v, &intr_type);
|
||||
}
|
||||
else if ( (v->vcpu_id == 0) && time_info->pending_intr_nr ) {
|
||||
pic_set_irq(pic, 0, 0);
|
||||
pic_set_irq(pic, 0, 1);
|
||||
else if ( (v->vcpu_id == 0) && pt->enabled && pt->pending_intr_nr ) {
|
||||
pic_set_irq(pic, pt->irq, 0);
|
||||
pic_set_irq(pic, pt->irq, 1);
|
||||
intr_vector = cpu_get_interrupt(v, &intr_type);
|
||||
}
|
||||
}
|
||||
@@ -190,7 +177,7 @@ asmlinkage void svm_intr_assist(void)
|
||||
/* Re-injecting a PIT interruptt? */
|
||||
if (re_injecting &&
|
||||
is_pit_irq(v, intr_vector, intr_type)) {
|
||||
++time_info->pending_intr_nr;
|
||||
++pt->pending_intr_nr;
|
||||
}
|
||||
/* let's inject this interrupt */
|
||||
TRACE_3D(TRC_VMX_INT, v->domain->domain_id, intr_vector, 0);
|
||||
|
||||
+13
-18
@@ -672,12 +672,11 @@ static void arch_svm_do_launch(struct vcpu *v)
|
||||
|
||||
static void svm_freeze_time(struct vcpu *v)
|
||||
{
|
||||
struct hvm_time_info *time_info = &v->domain->arch.hvm_domain.vpit.time_info;
|
||||
struct periodic_time *pt=&v->domain->arch.hvm_domain.pl_time.periodic_tm;
|
||||
|
||||
if ( time_info->first_injected && !v->domain->arch.hvm_domain.guest_time ) {
|
||||
v->domain->arch.hvm_domain.guest_time = svm_get_guest_time(v);
|
||||
time_info->count_advance += (NOW() - time_info->count_point);
|
||||
stop_timer(&(time_info->pit_timer));
|
||||
if ( pt->enabled && pt->first_injected && !v->arch.hvm_vcpu.guest_time ) {
|
||||
v->arch.hvm_vcpu.guest_time = hvm_get_guest_time(v);
|
||||
stop_timer(&(pt->timer));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -754,7 +753,7 @@ static void svm_relinquish_guest_resources(struct domain *d)
|
||||
}
|
||||
}
|
||||
|
||||
kill_timer(&d->arch.hvm_domain.vpit.time_info.pit_timer);
|
||||
kill_timer(&d->arch.hvm_domain.pl_time.periodic_tm.timer);
|
||||
|
||||
if ( d->arch.hvm_domain.shared_page_va )
|
||||
unmap_domain_page_global(
|
||||
@@ -784,10 +783,12 @@ void arch_svm_do_resume(struct vcpu *v)
|
||||
|
||||
void svm_migrate_timers(struct vcpu *v)
|
||||
{
|
||||
struct hvm_time_info *time_info = &v->domain->arch.hvm_domain.vpit.time_info;
|
||||
struct periodic_time *pt = &(v->domain->arch.hvm_domain.pl_time.periodic_tm);
|
||||
|
||||
migrate_timer(&time_info->pit_timer, v->processor);
|
||||
migrate_timer(&v->arch.hvm_svm.hlt_timer, v->processor);
|
||||
if ( pt->enabled ) {
|
||||
migrate_timer( &pt->timer, v->processor );
|
||||
migrate_timer( &v->arch.hvm_svm.hlt_timer, v->processor );
|
||||
}
|
||||
if ( hvm_apic_support(v->domain) && VLAPIC( v ))
|
||||
migrate_timer( &(VLAPIC(v)->vlapic_timer ), v->processor );
|
||||
}
|
||||
@@ -1901,14 +1902,8 @@ static inline void svm_do_msr_access(struct vcpu *v, struct cpu_user_regs *regs)
|
||||
regs->edx = 0;
|
||||
switch (regs->ecx) {
|
||||
case MSR_IA32_TIME_STAMP_COUNTER:
|
||||
{
|
||||
struct hvm_time_info *time_info;
|
||||
|
||||
rdtscll(msr_content);
|
||||
time_info = &v->domain->arch.hvm_domain.vpit.time_info;
|
||||
msr_content += time_info->cache_tsc_offset;
|
||||
msr_content = hvm_get_guest_time(v);
|
||||
break;
|
||||
}
|
||||
case MSR_IA32_SYSENTER_CS:
|
||||
msr_content = vmcb->sysenter_cs;
|
||||
break;
|
||||
@@ -1975,7 +1970,7 @@ done:
|
||||
static inline void svm_vmexit_do_hlt(struct vmcb_struct *vmcb)
|
||||
{
|
||||
struct vcpu *v = current;
|
||||
struct hvm_virpit *vpit = &v->domain->arch.hvm_domain.vpit;
|
||||
struct periodic_time *pt=&v->domain->arch.hvm_domain.pl_time.periodic_tm;
|
||||
s_time_t next_pit = -1, next_wakeup;
|
||||
|
||||
__update_guest_eip(vmcb, 1);
|
||||
@@ -1985,7 +1980,7 @@ static inline void svm_vmexit_do_hlt(struct vmcb_struct *vmcb)
|
||||
return;
|
||||
|
||||
if ( !v->vcpu_id )
|
||||
next_pit = get_pit_scheduled(v, vpit);
|
||||
next_pit = get_scheduled(v, pt->irq, pt);
|
||||
next_wakeup = get_apictime_scheduled(v);
|
||||
if ( (next_pit != -1 && next_pit < next_wakeup) || next_wakeup == -1 )
|
||||
next_wakeup = next_pit;
|
||||
|
||||
@@ -442,19 +442,17 @@ void set_hsa_to_guest( struct arch_svm_struct *arch_svm )
|
||||
void svm_do_resume(struct vcpu *v)
|
||||
{
|
||||
struct domain *d = v->domain;
|
||||
struct hvm_virpit *vpit = &d->arch.hvm_domain.vpit;
|
||||
struct hvm_time_info *time_info = &vpit->time_info;
|
||||
struct periodic_time *pt = &d->arch.hvm_domain.pl_time.periodic_tm;
|
||||
|
||||
svm_stts(v);
|
||||
|
||||
/* pick up the elapsed PIT ticks and re-enable pit_timer */
|
||||
if ( time_info->first_injected ) {
|
||||
if ( v->domain->arch.hvm_domain.guest_time ) {
|
||||
svm_set_guest_time(v, v->domain->arch.hvm_domain.guest_time);
|
||||
time_info->count_point = NOW();
|
||||
v->domain->arch.hvm_domain.guest_time = 0;
|
||||
if ( pt->enabled && pt->first_injected ) {
|
||||
if ( v->arch.hvm_vcpu.guest_time ) {
|
||||
svm_set_guest_time(v, v->arch.hvm_vcpu.guest_time);
|
||||
v->arch.hvm_vcpu.guest_time = 0;
|
||||
}
|
||||
pickup_deactive_ticks(vpit);
|
||||
pickup_deactive_ticks(pt);
|
||||
}
|
||||
|
||||
if ( test_bit(iopacket_port(v), &d->shared_info->evtchn_pending[0]) ||
|
||||
|
||||
+22
-36
@@ -49,45 +49,33 @@ void __set_tsc_offset(u64 offset)
|
||||
#endif
|
||||
}
|
||||
|
||||
u64 get_guest_time(struct vcpu *v)
|
||||
{
|
||||
struct hvm_time_info *time_info = &(v->domain->arch.hvm_domain.vpit.time_info);
|
||||
u64 host_tsc;
|
||||
|
||||
rdtscll(host_tsc);
|
||||
return host_tsc + time_info->cache_tsc_offset;
|
||||
}
|
||||
|
||||
void set_guest_time(struct vcpu *v, u64 gtime)
|
||||
{
|
||||
struct hvm_time_info *time_info = &(v->domain->arch.hvm_domain.vpit.time_info);
|
||||
u64 host_tsc;
|
||||
|
||||
rdtscll(host_tsc);
|
||||
|
||||
time_info->cache_tsc_offset = gtime - host_tsc;
|
||||
__set_tsc_offset(time_info->cache_tsc_offset);
|
||||
v->arch.hvm_vcpu.cache_tsc_offset = gtime - host_tsc;
|
||||
__set_tsc_offset(v->arch.hvm_vcpu.cache_tsc_offset);
|
||||
}
|
||||
|
||||
static inline void
|
||||
interrupt_post_injection(struct vcpu * v, int vector, int type)
|
||||
{
|
||||
struct hvm_virpit *vpit = &(v->domain->arch.hvm_domain.vpit);
|
||||
struct hvm_time_info *time_info = &vpit->time_info;
|
||||
struct periodic_time *pt = &(v->domain->arch.hvm_domain.pl_time.periodic_tm);
|
||||
|
||||
if ( is_pit_irq(v, vector, type) ) {
|
||||
if ( !time_info->first_injected ) {
|
||||
time_info->pending_intr_nr = 0;
|
||||
time_info->last_pit_gtime = get_guest_time(v);
|
||||
time_info->first_injected = 1;
|
||||
if ( !pt->first_injected ) {
|
||||
pt->pending_intr_nr = 0;
|
||||
pt->last_plt_gtime = hvm_get_guest_time(v);
|
||||
pt->scheduled = NOW() + pt->period;
|
||||
set_timer(&pt->timer, pt->scheduled);
|
||||
pt->first_injected = 1;
|
||||
} else {
|
||||
time_info->pending_intr_nr--;
|
||||
pt->pending_intr_nr--;
|
||||
pt->last_plt_gtime += pt->period_cycles;
|
||||
set_guest_time(v, pt->last_plt_gtime);
|
||||
}
|
||||
time_info->count_advance = 0;
|
||||
time_info->count_point = NOW();
|
||||
|
||||
time_info->last_pit_gtime += time_info->period_cycles;
|
||||
set_guest_time(v, time_info->last_pit_gtime);
|
||||
}
|
||||
|
||||
switch(type)
|
||||
@@ -151,7 +139,7 @@ asmlinkage void vmx_intr_assist(void)
|
||||
unsigned long eflags;
|
||||
struct vcpu *v = current;
|
||||
struct hvm_domain *plat=&v->domain->arch.hvm_domain;
|
||||
struct hvm_time_info *time_info = &plat->vpit.time_info;
|
||||
struct periodic_time *pt = &plat->pl_time.periodic_tm;
|
||||
struct hvm_virpic *pic= &plat->vpic;
|
||||
unsigned int idtv_info_field;
|
||||
unsigned long inst_len;
|
||||
@@ -160,9 +148,9 @@ asmlinkage void vmx_intr_assist(void)
|
||||
if ( v->vcpu_id == 0 )
|
||||
hvm_pic_assist(v);
|
||||
|
||||
if ( (v->vcpu_id == 0) && time_info->pending_intr_nr ) {
|
||||
pic_set_irq(pic, 0, 0);
|
||||
pic_set_irq(pic, 0, 1);
|
||||
if ( (v->vcpu_id == 0) && pt->enabled && pt->pending_intr_nr ) {
|
||||
pic_set_irq(pic, pt->irq, 0);
|
||||
pic_set_irq(pic, pt->irq, 1);
|
||||
}
|
||||
|
||||
has_ext_irq = cpu_has_pending_irq(v);
|
||||
@@ -232,19 +220,17 @@ asmlinkage void vmx_intr_assist(void)
|
||||
void vmx_do_resume(struct vcpu *v)
|
||||
{
|
||||
struct domain *d = v->domain;
|
||||
struct hvm_virpit *vpit = &v->domain->arch.hvm_domain.vpit;
|
||||
struct hvm_time_info *time_info = &vpit->time_info;
|
||||
struct periodic_time *pt = &v->domain->arch.hvm_domain.pl_time.periodic_tm;
|
||||
|
||||
vmx_stts();
|
||||
|
||||
/* pick up the elapsed PIT ticks and re-enable pit_timer */
|
||||
if ( time_info->first_injected ) {
|
||||
if ( v->domain->arch.hvm_domain.guest_time ) {
|
||||
time_info->count_point = NOW();
|
||||
set_guest_time(v, v->domain->arch.hvm_domain.guest_time);
|
||||
v->domain->arch.hvm_domain.guest_time = 0;
|
||||
if ( pt->enabled && pt->first_injected ) {
|
||||
if ( v->arch.hvm_vcpu.guest_time ) {
|
||||
set_guest_time(v, v->arch.hvm_vcpu.guest_time);
|
||||
v->arch.hvm_vcpu.guest_time = 0;
|
||||
}
|
||||
pickup_deactive_ticks(vpit);
|
||||
pickup_deactive_ticks(pt);
|
||||
}
|
||||
|
||||
if ( test_bit(iopacket_port(v), &d->shared_info->evtchn_pending[0]) ||
|
||||
|
||||
+13
-18
@@ -102,7 +102,7 @@ static void vmx_relinquish_guest_resources(struct domain *d)
|
||||
}
|
||||
}
|
||||
|
||||
kill_timer(&d->arch.hvm_domain.vpit.time_info.pit_timer);
|
||||
kill_timer(&d->arch.hvm_domain.pl_time.periodic_tm.timer);
|
||||
|
||||
if ( d->arch.hvm_domain.shared_page_va )
|
||||
unmap_domain_page_global(
|
||||
@@ -358,12 +358,11 @@ static inline int long_mode_do_msr_write(struct cpu_user_regs *regs)
|
||||
|
||||
static void vmx_freeze_time(struct vcpu *v)
|
||||
{
|
||||
struct hvm_time_info *time_info = &(v->domain->arch.hvm_domain.vpit.time_info);
|
||||
struct periodic_time *pt=&v->domain->arch.hvm_domain.pl_time.periodic_tm;
|
||||
|
||||
if ( time_info->first_injected && !v->domain->arch.hvm_domain.guest_time ) {
|
||||
v->domain->arch.hvm_domain.guest_time = get_guest_time(v);
|
||||
time_info->count_advance += (NOW() - time_info->count_point);
|
||||
stop_timer(&(time_info->pit_timer));
|
||||
if ( pt->enabled && pt->first_injected && !v->arch.hvm_vcpu.guest_time ) {
|
||||
v->arch.hvm_vcpu.guest_time = hvm_get_guest_time(v);
|
||||
stop_timer(&(pt->timer));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -393,10 +392,12 @@ int vmx_initialize_guest_resources(struct vcpu *v)
|
||||
|
||||
void vmx_migrate_timers(struct vcpu *v)
|
||||
{
|
||||
struct hvm_time_info *time_info = &v->domain->arch.hvm_domain.vpit.time_info;
|
||||
struct periodic_time *pt = &(v->domain->arch.hvm_domain.pl_time.periodic_tm);
|
||||
|
||||
migrate_timer(&time_info->pit_timer, v->processor);
|
||||
migrate_timer(&v->arch.hvm_vmx.hlt_timer, v->processor);
|
||||
if ( pt->enabled ) {
|
||||
migrate_timer(&pt->timer, v->processor);
|
||||
migrate_timer(&v->arch.hvm_vmx.hlt_timer, v->processor);
|
||||
}
|
||||
if ( hvm_apic_support(v->domain) && VLAPIC(v))
|
||||
migrate_timer(&(VLAPIC(v)->vlapic_timer), v->processor);
|
||||
}
|
||||
@@ -1861,14 +1862,8 @@ static inline void vmx_do_msr_read(struct cpu_user_regs *regs)
|
||||
(unsigned long)regs->edx);
|
||||
switch (regs->ecx) {
|
||||
case MSR_IA32_TIME_STAMP_COUNTER:
|
||||
{
|
||||
struct hvm_time_info *time_info;
|
||||
|
||||
rdtscll(msr_content);
|
||||
time_info = &(v->domain->arch.hvm_domain.vpit.time_info);
|
||||
msr_content += time_info->cache_tsc_offset;
|
||||
msr_content = hvm_get_guest_time(v);
|
||||
break;
|
||||
}
|
||||
case MSR_IA32_SYSENTER_CS:
|
||||
__vmread(GUEST_SYSENTER_CS, (u32 *)&msr_content);
|
||||
break;
|
||||
@@ -1941,11 +1936,11 @@ static inline void vmx_do_msr_write(struct cpu_user_regs *regs)
|
||||
void vmx_vmexit_do_hlt(void)
|
||||
{
|
||||
struct vcpu *v=current;
|
||||
struct hvm_virpit *vpit = &(v->domain->arch.hvm_domain.vpit);
|
||||
struct periodic_time *pt = &(v->domain->arch.hvm_domain.pl_time.periodic_tm);
|
||||
s_time_t next_pit=-1,next_wakeup;
|
||||
|
||||
if ( !v->vcpu_id )
|
||||
next_pit = get_pit_scheduled(v,vpit);
|
||||
next_pit = get_scheduled(v, pt->irq, pt);
|
||||
next_wakeup = get_apictime_scheduled(v);
|
||||
if ( (next_pit != -1 && next_pit < next_wakeup) || next_wakeup == -1 )
|
||||
next_wakeup = next_pit;
|
||||
|
||||
@@ -35,9 +35,9 @@ struct hvm_domain {
|
||||
unsigned int nr_vcpus;
|
||||
unsigned int apic_enabled;
|
||||
unsigned int pae_enabled;
|
||||
|
||||
struct hvm_virpit vpit;
|
||||
u64 guest_time;
|
||||
s64 tsc_frequency;
|
||||
struct pl_time pl_time;
|
||||
|
||||
struct hvm_virpic vpic;
|
||||
struct hvm_vioapic vioapic;
|
||||
struct hvm_io_handler io_handler;
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
#ifndef __ASM_X86_HVM_SVM_INTR_H__
|
||||
#define __ASM_X86_HVM_SVM_INTR_H__
|
||||
|
||||
extern void svm_set_tsc_shift(struct vcpu *v, struct hvm_virpit *vpit);
|
||||
extern void svm_intr_assist(void);
|
||||
extern void svm_intr_assist_update(struct vcpu *v, int highest_vector);
|
||||
extern void svm_intr_assist_test_valid(struct vcpu *v,
|
||||
|
||||
@@ -48,7 +48,6 @@ extern void svm_stts(struct vcpu *v);
|
||||
extern void svm_do_launch(struct vcpu *v);
|
||||
extern void svm_do_resume(struct vcpu *v);
|
||||
extern void svm_set_guest_time(struct vcpu *v, u64 gtime);
|
||||
extern u64 svm_get_guest_time(struct vcpu *v);
|
||||
extern void arch_svm_do_resume(struct vcpu *v);
|
||||
extern int load_vmcb(struct arch_svm_struct *arch_svm, u64 phys_hsa);
|
||||
/* For debugging. Remove when no longer needed. */
|
||||
|
||||
@@ -32,6 +32,9 @@ struct hvm_vcpu {
|
||||
unsigned long ioflags;
|
||||
struct mmio_op mmio_op;
|
||||
struct vlapic *vlapic;
|
||||
s64 cache_tsc_offset;
|
||||
u64 guest_time;
|
||||
|
||||
/* For AP startup */
|
||||
unsigned long init_sipi_sipi_state;
|
||||
|
||||
|
||||
@@ -34,7 +34,6 @@ extern void vmx_migrate_timers(struct vcpu *v);
|
||||
extern void arch_vmx_do_launch(struct vcpu *);
|
||||
extern void arch_vmx_do_resume(struct vcpu *);
|
||||
extern void set_guest_time(struct vcpu *v, u64 gtime);
|
||||
extern u64 get_guest_time(struct vcpu *v);
|
||||
|
||||
extern unsigned int cpu_rev;
|
||||
|
||||
|
||||
@@ -29,9 +29,7 @@
|
||||
#include <asm/hvm/vpic.h>
|
||||
|
||||
#define PIT_FREQ 1193181
|
||||
|
||||
#define PIT_BASE 0x40
|
||||
#define HVM_PIT_ACCEL_MODE 2
|
||||
#define PIT_BASE 0x40
|
||||
|
||||
typedef struct PITChannelState {
|
||||
int count; /* can be 65536 */
|
||||
@@ -48,47 +46,56 @@ typedef struct PITChannelState {
|
||||
u8 gate; /* timer start */
|
||||
s64 count_load_time;
|
||||
/* irq handling */
|
||||
s64 next_transition_time;
|
||||
int irq;
|
||||
struct hvm_time_info *hvm_time;
|
||||
u32 period; /* period(ns) based on count */
|
||||
struct vcpu *vcpu;
|
||||
struct periodic_time *pt;
|
||||
} PITChannelState;
|
||||
|
||||
struct hvm_time_info {
|
||||
/* extra info for the mode 2 channel */
|
||||
struct timer pit_timer;
|
||||
struct vcpu *vcpu; /* which vcpu the ac_timer bound to */
|
||||
u64 period_cycles; /* pit frequency in cpu cycles */
|
||||
s_time_t count_advance; /* accumulated count advance since last fire */
|
||||
s_time_t count_point; /* last point accumulating count advance */
|
||||
unsigned int pending_intr_nr; /* the couner for pending timer interrupts */
|
||||
int first_injected; /* flag to prevent shadow window */
|
||||
s64 cache_tsc_offset; /* cache of VMCS TSC_OFFSET offset */
|
||||
u64 last_pit_gtime; /* guest time when last pit is injected */
|
||||
|
||||
/*
|
||||
* Abstract layer of periodic time, one short time.
|
||||
*/
|
||||
struct periodic_time {
|
||||
char enabled; /* enabled */
|
||||
char one_shot; /* one shot time */
|
||||
char irq;
|
||||
char first_injected; /* flag to prevent shadow window */
|
||||
u32 pending_intr_nr; /* the couner for pending timer interrupts */
|
||||
u32 period; /* frequency in ns */
|
||||
u64 period_cycles; /* frequency in cpu cycles */
|
||||
s_time_t scheduled; /* scheduled timer interrupt */
|
||||
u64 last_plt_gtime; /* platform time when last IRQ is injected */
|
||||
struct timer timer; /* ac_timer */
|
||||
};
|
||||
|
||||
typedef struct hvm_virpit {
|
||||
typedef struct PITState {
|
||||
PITChannelState channels[3];
|
||||
struct hvm_time_info time_info;
|
||||
int speaker_data_on;
|
||||
int dummy_refresh_clock;
|
||||
}hvm_virpit;
|
||||
} PITState;
|
||||
|
||||
struct pl_time { /* platform time */
|
||||
struct periodic_time periodic_tm;
|
||||
struct PITState vpit;
|
||||
/* TODO: RTC/ACPI time */
|
||||
};
|
||||
|
||||
static __inline__ s_time_t get_pit_scheduled(
|
||||
struct vcpu *v,
|
||||
struct hvm_virpit *vpit)
|
||||
static __inline__ s_time_t get_scheduled(
|
||||
struct vcpu *v, int irq,
|
||||
struct periodic_time *pt)
|
||||
{
|
||||
struct PITChannelState *s = &(vpit->channels[0]);
|
||||
if ( is_irq_enabled(v, 0) ) {
|
||||
return s->next_transition_time;
|
||||
if ( is_irq_enabled(v, irq) ) {
|
||||
return pt->scheduled;
|
||||
}
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* to hook the ioreq packet to get the PIT initialization info */
|
||||
extern void pit_init(struct hvm_virpit *pit, struct vcpu *v);
|
||||
extern void pickup_deactive_ticks(struct hvm_virpit *vpit);
|
||||
extern void hvm_hooks_assist(struct vcpu *v);
|
||||
extern void pickup_deactive_ticks(struct periodic_time *vpit);
|
||||
extern u64 hvm_get_guest_time(struct vcpu *v);
|
||||
extern struct periodic_time *create_periodic_time(struct vcpu *v, u32 period, char irq, char one_shot);
|
||||
extern void destroy_periodic_time(struct periodic_time *pt);
|
||||
void pit_init(struct vcpu *v, unsigned long cpu_khz);
|
||||
void pt_timer_fn(void *data);
|
||||
|
||||
#endif /* __ASM_X86_HVM_VPIT_H__ */
|
||||