mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 10:11:27 +01:00
[HVM] Enable more than one platform timer (PIT/RTC/HPET)
programmed as periodic timer and adds them to abstract layer, which keeps track of pending_intr_nr to avoid time interrupt lost and sync'ed timer with TSC. It also makes some cleanup to the time related code. Signed-off-by: Xiaowei Yang <xiaowei.yang@intel.com> Signed-off-by: Yunhong Jiang <yunhong.jiang@intel.com>
This commit is contained in:
1 parent
625141e5d7
commit
ddc35d1cc9
21 files changed
+191
-353
No files matched your search
@@ -726,6 +726,9 @@ void context_switch(struct vcpu *prev, struct vcpu *next)
|
||||
|
||||
local_irq_disable();
|
||||
|
||||
if ( is_hvm_vcpu(prev) )
|
||||
pt_freeze_time(prev);
|
||||
|
||||
set_current(next);
|
||||
|
||||
if ( (per_cpu(curr_vcpu, cpu) == next) || is_idle_vcpu(next) )
|
||||
|
||||
@@ -10,6 +10,7 @@ obj-y += irq.o
|
||||
obj-y += platform.o
|
||||
obj-y += pmtimer.o
|
||||
obj-y += rtc.o
|
||||
obj-y += vpt.o
|
||||
obj-y += vioapic.o
|
||||
obj-y += vlapic.o
|
||||
obj-y += vpic.o
|
||||
+7
-60
@@ -82,56 +82,21 @@ u64 hvm_get_guest_time(struct vcpu *v)
|
||||
return host_tsc + v->arch.hvm_vcpu.cache_tsc_offset;
|
||||
}
|
||||
|
||||
void hvm_freeze_time(struct vcpu *v)
|
||||
{
|
||||
struct periodic_time *pt=&v->domain->arch.hvm_domain.pl_time.periodic_tm;
|
||||
|
||||
if ( pt->enabled && pt->first_injected
|
||||
&& (v->vcpu_id == pt->bind_vcpu)
|
||||
&& !v->arch.hvm_vcpu.guest_time ) {
|
||||
v->arch.hvm_vcpu.guest_time = hvm_get_guest_time(v);
|
||||
if ( !test_bit(_VCPUF_blocked, &v->vcpu_flags) )
|
||||
{
|
||||
stop_timer(&pt->timer);
|
||||
rtc_freeze(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void hvm_migrate_timers(struct vcpu *v)
|
||||
{
|
||||
struct periodic_time *pt = &v->domain->arch.hvm_domain.pl_time.periodic_tm;
|
||||
struct PMTState *vpmt = &v->domain->arch.hvm_domain.pl_time.vpmt;
|
||||
|
||||
if ( pt->enabled )
|
||||
{
|
||||
migrate_timer(&pt->timer, v->processor);
|
||||
}
|
||||
migrate_timer(&vcpu_vlapic(v)->vlapic_timer, v->processor);
|
||||
migrate_timer(&vpmt->timer, v->processor);
|
||||
pit_migrate_timers(v);
|
||||
rtc_migrate_timers(v);
|
||||
pmtimer_migrate_timers(v);
|
||||
migrate_timer(&vcpu_vlapic(v)->vlapic_timer, v->processor);
|
||||
}
|
||||
|
||||
void hvm_do_resume(struct vcpu *v)
|
||||
{
|
||||
ioreq_t *p;
|
||||
struct periodic_time *pt = &v->domain->arch.hvm_domain.pl_time.periodic_tm;
|
||||
|
||||
hvm_stts(v);
|
||||
|
||||
/* Pick up the elapsed PIT ticks and re-enable pit_timer. */
|
||||
if ( pt->enabled && (v->vcpu_id == pt->bind_vcpu) && pt->first_injected )
|
||||
{
|
||||
if ( v->arch.hvm_vcpu.guest_time )
|
||||
{
|
||||
hvm_set_guest_time(v, v->arch.hvm_vcpu.guest_time);
|
||||
v->arch.hvm_vcpu.guest_time = 0;
|
||||
}
|
||||
pickup_deactive_ticks(pt);
|
||||
}
|
||||
|
||||
/* Re-enable the RTC timer if needed */
|
||||
rtc_thaw(v);
|
||||
pt_thaw_time(v);
|
||||
|
||||
/* NB. Optimised for common case (p->state == STATE_IOREQ_NONE). */
|
||||
p = &get_vio(v->domain, v->vcpu_id)->vp_ioreq;
|
||||
@@ -182,7 +147,7 @@ int hvm_domain_initialise(struct domain *d)
|
||||
|
||||
void hvm_domain_destroy(struct domain *d)
|
||||
{
|
||||
kill_timer(&d->arch.hvm_domain.pl_time.periodic_tm.timer);
|
||||
pit_deinit(d);
|
||||
rtc_deinit(d);
|
||||
pmtimer_deinit(d);
|
||||
|
||||
@@ -196,7 +161,6 @@ void hvm_domain_destroy(struct domain *d)
|
||||
|
||||
int hvm_vcpu_initialise(struct vcpu *v)
|
||||
{
|
||||
struct hvm_domain *platform;
|
||||
int rc;
|
||||
|
||||
if ( (rc = vlapic_init(v)) != 0 )
|
||||
@@ -214,14 +178,11 @@ int hvm_vcpu_initialise(struct vcpu *v)
|
||||
get_vio(v->domain, v->vcpu_id)->vp_eport =
|
||||
v->arch.hvm_vcpu.xen_port;
|
||||
|
||||
INIT_LIST_HEAD(&v->arch.hvm_vcpu.tm_list);
|
||||
|
||||
if ( v->vcpu_id != 0 )
|
||||
return 0;
|
||||
|
||||
/* XXX Below should happen in hvm_domain_initialise(). */
|
||||
platform = &v->domain->arch.hvm_domain;
|
||||
|
||||
init_timer(&platform->pl_time.periodic_tm.timer,
|
||||
pt_timer_fn, v, v->processor);
|
||||
rtc_init(v, RTC_PORT(0), RTC_IRQ);
|
||||
pmtimer_init(v, ACPI_PM_TMR_BLK_ADDRESS);
|
||||
|
||||
@@ -240,20 +201,6 @@ void hvm_vcpu_destroy(struct vcpu *v)
|
||||
/*free_xen_event_channel(v, v->arch.hvm_vcpu.xen_port);*/
|
||||
}
|
||||
|
||||
int cpu_get_interrupt(struct vcpu *v, int *type)
|
||||
{
|
||||
int vector;
|
||||
|
||||
if ( (vector = cpu_get_apic_interrupt(v, type)) != -1 )
|
||||
return vector;
|
||||
|
||||
if ( (v->vcpu_id == 0) &&
|
||||
((vector = cpu_get_pic_interrupt(v, type)) != -1) )
|
||||
return vector;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void hvm_vcpu_down(void)
|
||||
{
|
||||
struct vcpu *v = current;
|
||||
|
||||
+36
-17
@@ -81,7 +81,7 @@ static int pit_get_count(PITChannelState *s)
|
||||
uint64_t d;
|
||||
int counter;
|
||||
|
||||
d = muldiv64(hvm_get_clock(s->vcpu) - s->count_load_time, PIT_FREQ, ticks_per_sec(s->vcpu));
|
||||
d = muldiv64(hvm_get_guest_time(s->pt.vcpu) - s->count_load_time, PIT_FREQ, ticks_per_sec(s->pt.vcpu));
|
||||
switch(s->mode) {
|
||||
case 0:
|
||||
case 1:
|
||||
@@ -106,7 +106,7 @@ static int pit_get_out1(PITChannelState *s, int64_t current_time)
|
||||
uint64_t d;
|
||||
int out;
|
||||
|
||||
d = muldiv64(current_time - s->count_load_time, PIT_FREQ, ticks_per_sec(s->vcpu));
|
||||
d = muldiv64(current_time - s->count_load_time, PIT_FREQ, ticks_per_sec(s->pt.vcpu));
|
||||
switch(s->mode) {
|
||||
default:
|
||||
case 0:
|
||||
@@ -153,7 +153,7 @@ void pit_set_gate(PITState *pit, int channel, int val)
|
||||
case 5:
|
||||
if (s->gate < val) {
|
||||
/* restart counting on rising edge */
|
||||
s->count_load_time = hvm_get_clock(s->vcpu);
|
||||
s->count_load_time = hvm_get_guest_time(s->pt.vcpu);
|
||||
// pit_irq_timer_update(s, s->count_load_time);
|
||||
}
|
||||
break;
|
||||
@@ -161,7 +161,7 @@ void pit_set_gate(PITState *pit, int channel, int val)
|
||||
case 3:
|
||||
if (s->gate < val) {
|
||||
/* restart counting on rising edge */
|
||||
s->count_load_time = hvm_get_clock(s->vcpu);
|
||||
s->count_load_time = hvm_get_guest_time(s->pt.vcpu);
|
||||
// pit_irq_timer_update(s, s->count_load_time);
|
||||
}
|
||||
/* XXX: disable/enable counting */
|
||||
@@ -179,7 +179,7 @@ int pit_get_gate(PITState *pit, int channel)
|
||||
void pit_time_fired(struct vcpu *v, void *priv)
|
||||
{
|
||||
PITChannelState *s = priv;
|
||||
s->count_load_time = hvm_get_clock(v);
|
||||
s->count_load_time = hvm_get_guest_time(v);
|
||||
}
|
||||
|
||||
static inline void pit_load_count(PITChannelState *s, int val)
|
||||
@@ -190,7 +190,7 @@ static inline void pit_load_count(PITChannelState *s, int val)
|
||||
|
||||
if (val == 0)
|
||||
val = 0x10000;
|
||||
s->count_load_time = hvm_get_clock(s->vcpu);
|
||||
s->count_load_time = hvm_get_guest_time(s->pt.vcpu);
|
||||
s->count = val;
|
||||
period = DIV_ROUND((val * 1000000000ULL), PIT_FREQ);
|
||||
|
||||
@@ -209,16 +209,17 @@ static inline void pit_load_count(PITChannelState *s, int val)
|
||||
switch (s->mode) {
|
||||
case 2:
|
||||
/* create periodic time */
|
||||
s->pt = create_periodic_time (period, 0, 0, pit_time_fired, s);
|
||||
create_periodic_time(&s->pt, period, 0, 0, pit_time_fired, s);
|
||||
break;
|
||||
case 1:
|
||||
/* create one shot time */
|
||||
s->pt = create_periodic_time (period, 0, 1, pit_time_fired, s);
|
||||
create_periodic_time(&s->pt, period, 0, 1, pit_time_fired, s);
|
||||
#ifdef DEBUG_PIT
|
||||
printk("HVM_PIT: create one shot time.\n");
|
||||
#endif
|
||||
break;
|
||||
default:
|
||||
destroy_periodic_time(&s->pt);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -253,7 +254,7 @@ static void pit_ioport_write(void *opaque, uint32_t addr, uint32_t val)
|
||||
if (!(val & 0x10) && !s->status_latched) {
|
||||
/* status latch */
|
||||
/* XXX: add BCD and null count */
|
||||
s->status = (pit_get_out1(s, hvm_get_clock(s->vcpu)) << 7) |
|
||||
s->status = (pit_get_out1(s, hvm_get_guest_time(s->pt.vcpu)) << 7) |
|
||||
(s->rw_mode << 4) |
|
||||
(s->mode << 1) |
|
||||
s->bcd;
|
||||
@@ -359,10 +360,7 @@ static void pit_reset(void *opaque)
|
||||
|
||||
for(i = 0;i < 3; i++) {
|
||||
s = &pit->channels[i];
|
||||
if ( s -> pt ) {
|
||||
destroy_periodic_time (s->pt);
|
||||
s->pt = NULL;
|
||||
}
|
||||
destroy_periodic_time(&s->pt);
|
||||
s->mode = 0xff; /* the init mode */
|
||||
s->gate = (i != 2);
|
||||
pit_load_count(s, 0);
|
||||
@@ -375,10 +373,11 @@ void pit_init(struct vcpu *v, unsigned long cpu_khz)
|
||||
PITChannelState *s;
|
||||
|
||||
s = &pit->channels[0];
|
||||
s->pt.vcpu = v;
|
||||
/* the timer 0 is connected to an IRQ */
|
||||
s->vcpu = v;
|
||||
s++; s->vcpu = v;
|
||||
s++; s->vcpu = v;
|
||||
init_timer(&s->pt.timer, pt_timer_fn, &s->pt, v->processor);
|
||||
s++; s->pt.vcpu = v;
|
||||
s++; s->pt.vcpu = v;
|
||||
|
||||
register_portio_handler(v->domain, PIT_BASE, 4, handle_pit_io);
|
||||
/* register the speaker port */
|
||||
@@ -391,6 +390,25 @@ void pit_init(struct vcpu *v, unsigned long cpu_khz)
|
||||
return;
|
||||
}
|
||||
|
||||
void pit_migrate_timers(struct vcpu *v)
|
||||
{
|
||||
PITState *pit = &v->domain->arch.hvm_domain.pl_time.vpit;
|
||||
PITChannelState *s;
|
||||
|
||||
s = &pit->channels[0];
|
||||
if ( s->pt.vcpu == v && s->pt.enabled )
|
||||
migrate_timer(&s->pt.timer, v->processor);
|
||||
}
|
||||
|
||||
void pit_deinit(struct domain *d)
|
||||
{
|
||||
PITState *pit = &d->arch.hvm_domain.pl_time.vpit;
|
||||
PITChannelState *s;
|
||||
|
||||
s = &pit->channels[0];
|
||||
kill_timer(&s->pt.timer);
|
||||
}
|
||||
|
||||
/* the intercept action for PIT DM retval:0--not handled; 1--handled */
|
||||
static int handle_pit_io(ioreq_t *p)
|
||||
{
|
||||
@@ -426,7 +444,8 @@ static void speaker_ioport_write(void *opaque, uint32_t addr, uint32_t val)
|
||||
static uint32_t speaker_ioport_read(void *opaque, uint32_t addr)
|
||||
{
|
||||
PITState *pit = opaque;
|
||||
int out = pit_get_out(pit, 2, hvm_get_clock(pit->channels[2].vcpu));
|
||||
int out = pit_get_out(pit, 2,
|
||||
hvm_get_guest_time(pit->channels[2].pt.vcpu));
|
||||
/* Refresh clock toggles at about 15us. We approximate as 2^14ns. */
|
||||
unsigned int refresh_clock = ((unsigned int)NOW() >> 14) & 1;
|
||||
return ((pit->speaker_data_on << 1) | pit_get_gate(pit, 2) |
|
||||
|
||||
@@ -263,98 +263,6 @@ int register_io_handler(
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static __inline__ void missed_ticks(struct periodic_time *pt)
|
||||
{
|
||||
s_time_t missed_ticks;
|
||||
|
||||
missed_ticks = NOW() - pt->scheduled;
|
||||
if ( missed_ticks > 0 ) {
|
||||
missed_ticks = missed_ticks / (s_time_t) pt->period + 1;
|
||||
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;
|
||||
|
||||
pt->pending_intr_nr++;
|
||||
pt->scheduled += pt->period;
|
||||
|
||||
/* Pick up missed timer ticks. */
|
||||
missed_ticks(pt);
|
||||
|
||||
/* No need to run the timer while a VCPU is descheduled. */
|
||||
if ( test_bit(_VCPUF_running, &v->vcpu_flags) )
|
||||
set_timer(&pt->timer, pt->scheduled);
|
||||
|
||||
vcpu_kick(v);
|
||||
}
|
||||
|
||||
/* 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(
|
||||
u32 period,
|
||||
char irq,
|
||||
char one_shot,
|
||||
time_cb *cb,
|
||||
void *data)
|
||||
{
|
||||
struct periodic_time *pt = &(current->domain->arch.hvm_domain.pl_time.periodic_tm);
|
||||
if ( pt->enabled ) {
|
||||
stop_timer (&pt->timer);
|
||||
pt->enabled = 0;
|
||||
}
|
||||
pt->bind_vcpu = 0; /* timer interrupt delivered to BSP by default */
|
||||
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;
|
||||
pt->cb = cb;
|
||||
pt->priv = data;
|
||||
return pt;
|
||||
}
|
||||
|
||||
void destroy_periodic_time(struct periodic_time *pt)
|
||||
{
|
||||
if ( pt->enabled ) {
|
||||
stop_timer(&pt->timer);
|
||||
pt->enabled = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* mode: C
|
||||
|
||||
+1
-19
@@ -691,25 +691,7 @@ static void hvm_mmio_assist(struct cpu_user_regs *regs, ioreq_t *p,
|
||||
|
||||
void hvm_interrupt_post(struct vcpu *v, int vector, int type)
|
||||
{
|
||||
struct periodic_time *pt =
|
||||
&(v->domain->arch.hvm_domain.pl_time.periodic_tm);
|
||||
|
||||
if ( pt->enabled && v->vcpu_id == pt->bind_vcpu
|
||||
&& is_periodic_irq(v, vector, type) ) {
|
||||
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 {
|
||||
pt->pending_intr_nr--;
|
||||
pt->last_plt_gtime += pt->period_cycles;
|
||||
hvm_set_guest_time(v, pt->last_plt_gtime);
|
||||
}
|
||||
if (pt->cb)
|
||||
pt->cb(v, pt->priv);
|
||||
}
|
||||
pt_intr_post(v, vector, type);
|
||||
|
||||
switch(type) {
|
||||
case APIC_DM_EXTINT:
|
||||
|
||||
@@ -225,3 +225,51 @@ void hvm_set_callback_gsi(struct domain *d, unsigned int gsi)
|
||||
dprintk(XENLOG_G_INFO, "Dom%u callback GSI changed %u -> %u\n",
|
||||
d->domain_id, old_gsi, gsi);
|
||||
}
|
||||
|
||||
int cpu_has_pending_irq(struct vcpu *v)
|
||||
{
|
||||
struct hvm_domain *plat = &v->domain->arch.hvm_domain;
|
||||
int dummy;
|
||||
|
||||
/* APIC */
|
||||
if ( cpu_get_apic_interrupt(v, &dummy) != -1 )
|
||||
return 1;
|
||||
|
||||
/* PIC */
|
||||
if ( !vlapic_accept_pic_intr(v) )
|
||||
return 0;
|
||||
|
||||
return plat->irq.vpic[0].int_output;
|
||||
}
|
||||
|
||||
int cpu_get_interrupt(struct vcpu *v, int *type)
|
||||
{
|
||||
int vector;
|
||||
|
||||
if ( (vector = cpu_get_apic_interrupt(v, type)) != -1 )
|
||||
return vector;
|
||||
|
||||
if ( (v->vcpu_id == 0) &&
|
||||
((vector = cpu_get_pic_interrupt(v, type)) != -1) )
|
||||
return vector;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int get_intr_vector(struct vcpu* v, int irq, int type)
|
||||
{
|
||||
if ( type == APIC_DM_EXTINT )
|
||||
return v->domain->arch.hvm_domain.irq.vpic[irq >> 3].irq_base
|
||||
+ (irq & 0x7);
|
||||
|
||||
return domain_vioapic(v->domain)->redirtbl[irq].fields.vector;
|
||||
}
|
||||
|
||||
int is_irq_masked(struct vcpu *v, int irq)
|
||||
{
|
||||
if ( v->domain->arch.hvm_domain.irq.vpic[irq >> 3].imr & (1 << (irq & 7))
|
||||
&& domain_vioapic(v->domain)->redirtbl[irq].fields.mask )
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -55,6 +55,14 @@ void pmtimer_init(struct vcpu *v, int base)
|
||||
register_portio_handler(v->domain, base, 4, handle_pmt_io);
|
||||
}
|
||||
|
||||
void pmtimer_migrate_timers(struct vcpu *v)
|
||||
{
|
||||
struct PMTState *vpmt = &v->domain->arch.hvm_domain.pl_time.vpmt;
|
||||
|
||||
if (vpmt->vcpu == v)
|
||||
migrate_timer(&vpmt->timer, v->processor);
|
||||
}
|
||||
|
||||
void pmtimer_deinit(struct domain *d)
|
||||
{
|
||||
PMTState *s = &d->arch.hvm_domain.pl_time.vpmt;
|
||||
|
||||
+29
-43
@@ -30,17 +30,18 @@
|
||||
|
||||
/* #define DEBUG_RTC */
|
||||
|
||||
/* Callback that fires the RTC's periodic interrupt */
|
||||
void rtc_pie_callback(void *opaque)
|
||||
void rtc_periodic_cb(struct vcpu *v, void *opaque)
|
||||
{
|
||||
RTCState *s = opaque;
|
||||
/* Record that we have fired */
|
||||
s->cmos_data[RTC_REG_C] |= (RTC_IRQF|RTC_PF); /* 0xc0 */
|
||||
/* Fire */
|
||||
hvm_isa_irq_assert(s->vcpu->domain, s->irq);
|
||||
/* Remember to fire again */
|
||||
s->next_pie = NOW() + s->period;
|
||||
set_timer(&s->pie_timer, s->next_pie);
|
||||
s->cmos_data[RTC_REG_C] |= 0xc0;
|
||||
}
|
||||
|
||||
int is_rtc_periodic_irq(void *opaque)
|
||||
{
|
||||
RTCState *s = opaque;
|
||||
|
||||
return !(s->cmos_data[RTC_REG_C] & RTC_AF ||
|
||||
s->cmos_data[RTC_REG_C] & RTC_UF);
|
||||
}
|
||||
|
||||
/* Enable/configure/disable the periodic timer based on the RTC_PIE and
|
||||
@@ -58,17 +59,13 @@ static void rtc_timer_update(RTCState *s)
|
||||
|
||||
period = 1 << (period_code - 1); /* period in 32 Khz cycles */
|
||||
period = DIV_ROUND((period * 1000000000ULL), 32768); /* period in ns */
|
||||
s->period = period;
|
||||
#ifdef DEBUG_RTC
|
||||
printk("HVM_RTC: period = %uns\n", period);
|
||||
#endif
|
||||
s->next_pie = NOW() + s->period;
|
||||
set_timer(&s->pie_timer, s->next_pie);
|
||||
}
|
||||
create_periodic_time(&s->pt, period, RTC_IRQ, 0, rtc_periodic_cb, s);
|
||||
}
|
||||
else
|
||||
{
|
||||
stop_timer(&s->pie_timer);
|
||||
}
|
||||
destroy_periodic_time(&s->pt);
|
||||
}
|
||||
|
||||
static void rtc_set_time(RTCState *s);
|
||||
@@ -292,8 +289,8 @@ static void rtc_update_second2(void *opaque)
|
||||
s->current_tm.tm_hour) )
|
||||
{
|
||||
s->cmos_data[RTC_REG_C] |= 0xa0;
|
||||
hvm_isa_irq_deassert(s->vcpu->domain, s->irq);
|
||||
hvm_isa_irq_assert(s->vcpu->domain, s->irq);
|
||||
hvm_isa_irq_deassert(s->pt.vcpu->domain, s->irq);
|
||||
hvm_isa_irq_assert(s->pt.vcpu->domain, s->irq);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -301,8 +298,8 @@ static void rtc_update_second2(void *opaque)
|
||||
if ( s->cmos_data[RTC_REG_B] & RTC_UIE )
|
||||
{
|
||||
s->cmos_data[RTC_REG_C] |= 0x90;
|
||||
hvm_isa_irq_deassert(s->vcpu->domain, s->irq);
|
||||
hvm_isa_irq_assert(s->vcpu->domain, s->irq);
|
||||
hvm_isa_irq_deassert(s->pt.vcpu->domain, s->irq);
|
||||
hvm_isa_irq_assert(s->pt.vcpu->domain, s->irq);
|
||||
}
|
||||
|
||||
/* clear update in progress bit */
|
||||
@@ -336,7 +333,7 @@ static uint32_t rtc_ioport_read(void *opaque, uint32_t addr)
|
||||
break;
|
||||
case RTC_REG_C:
|
||||
ret = s->cmos_data[s->cmos_index];
|
||||
hvm_isa_irq_deassert(s->vcpu->domain, s->irq);
|
||||
hvm_isa_irq_deassert(s->pt.vcpu->domain, s->irq);
|
||||
s->cmos_data[RTC_REG_C] = 0x00;
|
||||
break;
|
||||
default:
|
||||
@@ -377,36 +374,25 @@ static int handle_rtc_io(ioreq_t *p)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Stop the periodic interrupts from this RTC */
|
||||
void rtc_freeze(struct vcpu *v)
|
||||
{
|
||||
RTCState *s = &v->domain->arch.hvm_domain.pl_time.vrtc;
|
||||
stop_timer(&s->pie_timer);
|
||||
}
|
||||
|
||||
/* Start them again */
|
||||
void rtc_thaw(struct vcpu *v)
|
||||
{
|
||||
RTCState *s = &v->domain->arch.hvm_domain.pl_time.vrtc;
|
||||
if ( (s->cmos_data[RTC_REG_A] & RTC_RATE_SELECT) /* Period is not zero */
|
||||
&& (s->cmos_data[RTC_REG_B] & RTC_PIE) )
|
||||
set_timer(&s->pie_timer, s->next_pie);
|
||||
}
|
||||
|
||||
/* Move the RTC timers on to this vcpu's current cpu */
|
||||
void rtc_migrate_timers(struct vcpu *v)
|
||||
{
|
||||
RTCState *s = &v->domain->arch.hvm_domain.pl_time.vrtc;
|
||||
migrate_timer(&s->second_timer, v->processor);
|
||||
migrate_timer(&s->second_timer2, v->processor);
|
||||
migrate_timer(&s->pie_timer, v->processor);
|
||||
|
||||
if ( s->pt.vcpu == v )
|
||||
{
|
||||
if ( s->pt.enabled )
|
||||
migrate_timer(&s->pt.timer, v->processor);
|
||||
migrate_timer(&s->second_timer, v->processor);
|
||||
migrate_timer(&s->second_timer2, v->processor);
|
||||
}
|
||||
}
|
||||
|
||||
void rtc_init(struct vcpu *v, int base, int irq)
|
||||
{
|
||||
RTCState *s = &v->domain->arch.hvm_domain.pl_time.vrtc;
|
||||
|
||||
s->vcpu = v;
|
||||
s->pt.vcpu = v;
|
||||
s->irq = irq;
|
||||
s->cmos_data[RTC_REG_A] = RTC_REF_CLCK_32KHZ | 6; /* ~1kHz */
|
||||
s->cmos_data[RTC_REG_B] = RTC_24H;
|
||||
@@ -416,9 +402,9 @@ void rtc_init(struct vcpu *v, int base, int irq)
|
||||
s->current_tm = gmtime(get_localtime(v->domain));
|
||||
rtc_copy_date(s);
|
||||
|
||||
init_timer(&s->pt.timer, pt_timer_fn, &s->pt, v->processor);
|
||||
init_timer(&s->second_timer, rtc_update_second, s, v->processor);
|
||||
init_timer(&s->second_timer2, rtc_update_second2, s, v->processor);
|
||||
init_timer(&s->pie_timer, rtc_pie_callback, s, v->processor);
|
||||
|
||||
s->next_second_time = NOW() + 1000000000ULL;
|
||||
set_timer(&s->second_timer2, s->next_second_time);
|
||||
@@ -430,7 +416,7 @@ void rtc_deinit(struct domain *d)
|
||||
{
|
||||
RTCState *s = &d->arch.hvm_domain.pl_time.vrtc;
|
||||
|
||||
kill_timer(&s->pt.timer);
|
||||
kill_timer(&s->second_timer);
|
||||
kill_timer(&s->second_timer2);
|
||||
kill_timer(&s->pie_timer);
|
||||
}
|
||||
@@ -63,8 +63,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 periodic_time *pt = &plat->pl_time.periodic_tm;
|
||||
struct periodic_time *pt;
|
||||
int intr_type = APIC_DM_EXTINT;
|
||||
int intr_vector = -1;
|
||||
int re_injecting = 0;
|
||||
@@ -95,11 +94,7 @@ asmlinkage void svm_intr_assist(void)
|
||||
/* Now let's check for newer interrrupts */
|
||||
else
|
||||
{
|
||||
if ( (v->vcpu_id == 0) && pt->enabled && pt->pending_intr_nr )
|
||||
{
|
||||
hvm_isa_irq_deassert(current->domain, pt->irq);
|
||||
hvm_isa_irq_assert(current->domain, pt->irq);
|
||||
}
|
||||
pt_update_irq(v);
|
||||
|
||||
hvm_set_callback_irq_level();
|
||||
|
||||
@@ -130,8 +125,7 @@ asmlinkage void svm_intr_assist(void)
|
||||
case APIC_DM_FIXED:
|
||||
case APIC_DM_LOWEST:
|
||||
/* Re-injecting a PIT interruptt? */
|
||||
if ( re_injecting && pt->enabled &&
|
||||
is_periodic_irq(v, intr_vector, intr_type) )
|
||||
if ( re_injecting && (pt = is_pt_irq(v, intr_vector, intr_type)) )
|
||||
++pt->pending_intr_nr;
|
||||
/* let's inject this interrupt */
|
||||
TRACE_3D(TRC_VMX_INTR, v->domain->domain_id, intr_vector, 0);
|
||||
|
||||
@@ -46,6 +46,7 @@
|
||||
#include <asm/hvm/svm/intr.h>
|
||||
#include <asm/x86_emulate.h>
|
||||
#include <public/sched.h>
|
||||
#include <asm/hvm/vpt.h>
|
||||
|
||||
#define SVM_EXTRA_DEBUG
|
||||
|
||||
@@ -770,7 +771,6 @@ static void arch_svm_do_launch(struct vcpu *v)
|
||||
|
||||
static void svm_ctxt_switch_from(struct vcpu *v)
|
||||
{
|
||||
hvm_freeze_time(v);
|
||||
svm_save_dr(v);
|
||||
}
|
||||
|
||||
@@ -1994,6 +1994,7 @@ static inline void svm_do_msr_access(
|
||||
switch (ecx)
|
||||
{
|
||||
case MSR_IA32_TIME_STAMP_COUNTER:
|
||||
pt_reset(v);
|
||||
hvm_set_guest_time(v, msr_content);
|
||||
break;
|
||||
case MSR_IA32_SYSENTER_CS:
|
||||
|
||||
@@ -154,16 +154,6 @@ int vlapic_set_irq(struct vlapic *vlapic, uint8_t vec, uint8_t trig)
|
||||
return ret;
|
||||
}
|
||||
|
||||
s_time_t get_apictime_scheduled(struct vcpu *v)
|
||||
{
|
||||
struct vlapic *vlapic = vcpu_vlapic(v);
|
||||
|
||||
if ( !vlapic_lvt_enabled(vlapic, APIC_LVTT) )
|
||||
return -1;
|
||||
|
||||
return vlapic->vlapic_timer.expires;
|
||||
}
|
||||
|
||||
int vlapic_find_highest_isr(struct vlapic *vlapic)
|
||||
{
|
||||
int result;
|
||||
@@ -836,23 +826,6 @@ int cpu_get_apic_interrupt(struct vcpu *v, int *mode)
|
||||
return highest_irr;
|
||||
}
|
||||
|
||||
/* check to see if there is pending interrupt */
|
||||
int cpu_has_pending_irq(struct vcpu *v)
|
||||
{
|
||||
struct hvm_domain *plat = &v->domain->arch.hvm_domain;
|
||||
int dummy;
|
||||
|
||||
/* APIC */
|
||||
if ( cpu_get_apic_interrupt(v, &dummy) != -1 )
|
||||
return 1;
|
||||
|
||||
/* PIC */
|
||||
if ( !vlapic_accept_pic_intr(v) )
|
||||
return 0;
|
||||
|
||||
return plat->irq.vpic[0].int_output;
|
||||
}
|
||||
|
||||
void vlapic_post_injection(struct vcpu *v, int vector, int deliver_mode)
|
||||
{
|
||||
struct vlapic *vlapic = vcpu_vlapic(v);
|
||||
|
||||
@@ -91,17 +91,11 @@ asmlinkage void vmx_intr_assist(void)
|
||||
int highest_vector;
|
||||
unsigned long eflags;
|
||||
struct vcpu *v = current;
|
||||
struct hvm_domain *plat=&v->domain->arch.hvm_domain;
|
||||
struct periodic_time *pt = &plat->pl_time.periodic_tm;
|
||||
unsigned int idtv_info_field;
|
||||
unsigned long inst_len;
|
||||
int has_ext_irq;
|
||||
|
||||
if ( (v->vcpu_id == 0) && pt->enabled && pt->pending_intr_nr )
|
||||
{
|
||||
hvm_isa_irq_deassert(current->domain, pt->irq);
|
||||
hvm_isa_irq_assert(current->domain, pt->irq);
|
||||
}
|
||||
pt_update_irq(v);
|
||||
|
||||
hvm_set_callback_irq_level();
|
||||
|
||||
|
||||
@@ -46,6 +46,7 @@
|
||||
#include <asm/hvm/vpic.h>
|
||||
#include <asm/hvm/vlapic.h>
|
||||
#include <asm/x86_emulate.h>
|
||||
#include <asm/hvm/vpt.h>
|
||||
|
||||
static void vmx_ctxt_switch_from(struct vcpu *v);
|
||||
static void vmx_ctxt_switch_to(struct vcpu *v);
|
||||
@@ -372,8 +373,6 @@ static inline void vmx_restore_dr(struct vcpu *v)
|
||||
|
||||
static void vmx_ctxt_switch_from(struct vcpu *v)
|
||||
{
|
||||
hvm_freeze_time(v);
|
||||
|
||||
/* NB. MSR_SHADOW_GS_BASE may be changed by swapgs instrucion in guest,
|
||||
* so we must save it. */
|
||||
rdmsrl(MSR_SHADOW_GS_BASE, v->arch.hvm_vmx.msr_state.shadow_gs);
|
||||
@@ -2072,13 +2071,7 @@ static inline int vmx_do_msr_write(struct cpu_user_regs *regs)
|
||||
|
||||
switch (ecx) {
|
||||
case MSR_IA32_TIME_STAMP_COUNTER:
|
||||
{
|
||||
struct periodic_time *pt =
|
||||
&(v->domain->arch.hvm_domain.pl_time.periodic_tm);
|
||||
if ( pt->enabled && pt->first_injected
|
||||
&& v->vcpu_id == pt->bind_vcpu )
|
||||
pt->first_injected = 0;
|
||||
}
|
||||
pt_reset(v);
|
||||
hvm_set_guest_time(v, msr_content);
|
||||
break;
|
||||
case MSR_IA32_SYSENTER_CS:
|
||||
|
||||
@@ -445,19 +445,3 @@ int cpu_get_pic_interrupt(struct vcpu *v, int *type)
|
||||
*type = APIC_DM_EXTINT;
|
||||
return vector;
|
||||
}
|
||||
|
||||
int is_periodic_irq(struct vcpu *v, int irq, int type)
|
||||
{
|
||||
int vec;
|
||||
struct periodic_time *pt = &v->domain->arch.hvm_domain.pl_time.periodic_tm;
|
||||
|
||||
if ( pt->irq != 0 )
|
||||
return 0;
|
||||
|
||||
if ( type == APIC_DM_EXTINT )
|
||||
vec = v->domain->arch.hvm_domain.irq.vpic[0].irq_base;
|
||||
else
|
||||
vec = domain_vioapic(v->domain)->redirtbl[0].fields.vector;
|
||||
|
||||
return (irq == vec);
|
||||
}
|
||||
@@ -223,7 +223,7 @@ void hvm_cpuid(unsigned int input, unsigned int *eax, unsigned int *ebx,
|
||||
unsigned int *ecx, unsigned int *edx);
|
||||
void hvm_stts(struct vcpu *v);
|
||||
void hvm_set_guest_time(struct vcpu *v, u64 gtime);
|
||||
void hvm_freeze_time(struct vcpu *v);
|
||||
u64 hvm_get_guest_time(struct vcpu *v);
|
||||
void hvm_migrate_timers(struct vcpu *v);
|
||||
void hvm_do_resume(struct vcpu *v);
|
||||
|
||||
|
||||
@@ -147,8 +147,6 @@ extern void send_pio_req(unsigned long port, unsigned long count, int size,
|
||||
extern void handle_mmio(unsigned long gpa);
|
||||
extern void hvm_interrupt_post(struct vcpu *v, int vector, int type);
|
||||
extern void hvm_io_assist(struct vcpu *v);
|
||||
extern int cpu_get_interrupt(struct vcpu *v, int *type);
|
||||
extern int cpu_has_pending_irq(struct vcpu *v);
|
||||
|
||||
#endif /* __ASM_X86_HVM_IO_H__ */
|
||||
|
||||
@@ -104,4 +104,9 @@ void hvm_set_pci_link_route(struct domain *d, u8 link, u8 isa_irq);
|
||||
void hvm_set_callback_irq_level(void);
|
||||
void hvm_set_callback_gsi(struct domain *d, unsigned int gsi);
|
||||
|
||||
int cpu_get_interrupt(struct vcpu *v, int *type);
|
||||
int cpu_has_pending_irq(struct vcpu *v);
|
||||
int get_intr_vector(struct vcpu* vcpu, int irq, int type);
|
||||
int is_irq_masked(struct vcpu *v, int irq);
|
||||
|
||||
#endif /* __ASM_X86_HVM_IRQ_H__ */
|
||||
@@ -35,6 +35,7 @@ struct hvm_vcpu {
|
||||
struct vlapic vlapic;
|
||||
s64 cache_tsc_offset;
|
||||
u64 guest_time;
|
||||
struct list_head tm_list;
|
||||
|
||||
/* For AP startup */
|
||||
unsigned long init_sipi_sipi_state;
|
||||
|
||||
@@ -89,8 +89,6 @@ int vlapic_accept_pic_intr(struct vcpu *v);
|
||||
struct vlapic *apic_round_robin(
|
||||
struct domain *d, uint8_t vector, uint32_t bitmap);
|
||||
|
||||
s_time_t get_apictime_scheduled(struct vcpu *v);
|
||||
|
||||
int vlapic_match_logical_addr(struct vlapic *vlapic, uint8_t mda);
|
||||
|
||||
#endif /* __ASM_X86_HVM_VLAPIC_H__ */
|
||||
@@ -27,11 +27,33 @@
|
||||
#include <xen/errno.h>
|
||||
#include <xen/time.h>
|
||||
#include <xen/timer.h>
|
||||
#include <xen/list.h>
|
||||
#include <asm/hvm/vpic.h>
|
||||
|
||||
#define PIT_FREQ 1193181
|
||||
#define PIT_BASE 0x40
|
||||
|
||||
/*
|
||||
* Abstract layer of periodic time, one short time.
|
||||
*/
|
||||
typedef void time_cb(struct vcpu *v, void *opaque);
|
||||
|
||||
struct periodic_time {
|
||||
struct list_head list;
|
||||
char enabled;
|
||||
char one_shot; /* one shot time */
|
||||
int irq;
|
||||
struct vcpu *vcpu; /* vcpu timer interrupt delivers to */
|
||||
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 */
|
||||
time_cb *cb;
|
||||
void *priv; /* ponit back to platform time source */
|
||||
};
|
||||
|
||||
typedef struct PITChannelState {
|
||||
int count; /* can be 65536 */
|
||||
u16 latched_count;
|
||||
@@ -47,8 +69,7 @@ typedef struct PITChannelState {
|
||||
u8 gate; /* timer start */
|
||||
s64 count_load_time;
|
||||
/* irq handling */
|
||||
struct vcpu *vcpu;
|
||||
struct periodic_time *pt;
|
||||
struct periodic_time pt;
|
||||
} PITChannelState;
|
||||
|
||||
typedef struct PITState {
|
||||
@@ -66,10 +87,7 @@ typedef struct RTCState {
|
||||
int64_t next_second_time;
|
||||
struct timer second_timer;
|
||||
struct timer second_timer2;
|
||||
struct timer pie_timer;
|
||||
int period;
|
||||
s_time_t next_pie;
|
||||
struct vcpu *vcpu;
|
||||
struct periodic_time pt;
|
||||
} RTCState;
|
||||
|
||||
#define FREQUENCE_PMTIMER 3579545
|
||||
@@ -82,58 +100,35 @@ typedef struct PMTState {
|
||||
struct vcpu *vcpu;
|
||||
} PMTState;
|
||||
|
||||
/*
|
||||
* Abstract layer of periodic time, one short time.
|
||||
*/
|
||||
typedef void time_cb(struct vcpu *v, void *opaque);
|
||||
|
||||
struct periodic_time {
|
||||
char enabled; /* enabled */
|
||||
char one_shot; /* one shot time */
|
||||
char irq;
|
||||
char first_injected; /* flag to prevent shadow window */
|
||||
u32 bind_vcpu; /* vcpu timer interrupt delivers to */
|
||||
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 */
|
||||
time_cb *cb;
|
||||
void *priv; /* ponit back to platform time source */
|
||||
};
|
||||
|
||||
struct pl_time { /* platform time */
|
||||
struct periodic_time periodic_tm;
|
||||
struct PITState vpit;
|
||||
struct RTCState vrtc;
|
||||
struct PMTState vpmt;
|
||||
struct PITState vpit;
|
||||
struct RTCState vrtc;
|
||||
struct PMTState vpmt;
|
||||
};
|
||||
|
||||
extern u64 hvm_get_guest_time(struct vcpu *v);
|
||||
static inline int64_t hvm_get_clock(struct vcpu *v)
|
||||
{
|
||||
return hvm_get_guest_time(v);
|
||||
}
|
||||
|
||||
#define ticks_per_sec(v) (v->domain->arch.hvm_domain.tsc_frequency)
|
||||
|
||||
/* to hook the ioreq packet to get the PIT initialization info */
|
||||
extern void hvm_hooks_assist(struct vcpu *v);
|
||||
extern void pickup_deactive_ticks(struct periodic_time *vpit);
|
||||
extern struct periodic_time *create_periodic_time(
|
||||
u32 period, char irq, char one_shot, time_cb *cb, void *data);
|
||||
extern void destroy_periodic_time(struct periodic_time *pt);
|
||||
void pt_freeze_time(struct vcpu *v);
|
||||
void pt_thaw_time(struct vcpu *v);
|
||||
void pt_timer_fn(void *data);
|
||||
void pt_update_irq(struct vcpu *v);
|
||||
struct periodic_time *is_pt_irq(struct vcpu *v, int vector, int type);
|
||||
void pt_intr_post(struct vcpu *v, int vector, int type);
|
||||
void pt_reset(struct vcpu *v);
|
||||
void create_periodic_time(struct periodic_time *pt, u32 period, char irq,
|
||||
char one_shot, time_cb *cb, void *data);
|
||||
void destroy_periodic_time(struct periodic_time *pt);
|
||||
|
||||
int pv_pit_handler(int port, int data, int write);
|
||||
void pit_init(struct vcpu *v, unsigned long cpu_khz);
|
||||
void pit_migrate_timers(struct vcpu *v);
|
||||
void pit_deinit(struct domain *d);
|
||||
void rtc_init(struct vcpu *v, int base, int irq);
|
||||
void rtc_deinit(struct domain *d);
|
||||
void rtc_freeze(struct vcpu *v);
|
||||
void rtc_thaw(struct vcpu *v);
|
||||
void rtc_migrate_timers(struct vcpu *v);
|
||||
void rtc_deinit(struct domain *d);
|
||||
int is_rtc_periodic_irq(void *opaque);
|
||||
void pmtimer_init(struct vcpu *v, int base);
|
||||
void pmtimer_migrate_timers(struct vcpu *v);
|
||||
void pmtimer_deinit(struct domain *d);
|
||||
void pt_timer_fn(void *data);
|
||||
void pit_time_fired(struct vcpu *v, void *priv);
|
||||
|
||||
#endif /* __ASM_X86_HVM_VPT_H__ */
|
||||