mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 02:01:24 +01:00
[HVM] Move RTC emulation into the hypervisor.
Signed-off-by: Xiaowei Yang <xiaowei.yang@intel.com>
This commit is contained in:
1 parent
2b30fc3c70
commit
4ca1612143
18 files changed
+716
-50
No files matched your search
@@ -294,7 +294,11 @@ OBJS+=gdbstub.o
|
||||
endif
|
||||
|
||||
# qemu-dm objects
|
||||
ifeq ($(ARCH),ia64)
|
||||
LIBOBJS=helper2.o exec-dm.o i8259-dm.o
|
||||
else
|
||||
LIBOBJS=helper2.o exec-dm.o i8259-dm.o rtc-dm.o
|
||||
endif
|
||||
|
||||
all: $(PROGS)
|
||||
|
||||
@@ -354,7 +358,11 @@ VL_OBJS+= ne2000.o rtl8139.o pcnet.o
|
||||
ifeq ($(TARGET_BASE_ARCH), i386)
|
||||
# Hardware support
|
||||
VL_OBJS+= ide.o pckbd.o ps2.o vga.o $(SOUND_HW) dma.o $(AUDIODRV)
|
||||
ifeq ($(ARCH),ia64)
|
||||
VL_OBJS+= fdc.o mc146818rtc.o serial.o pc.o
|
||||
else
|
||||
VL_OBJS+= fdc.o serial.o pc.o
|
||||
endif
|
||||
VL_OBJS+= cirrus_vga.o mixeng.o parallel.o acpi.o piix_pci.o
|
||||
VL_OBJS+= usb-uhci.o
|
||||
VL_OBJS+= piix4acpi.o
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* QEMU MC146818 RTC emulation
|
||||
*
|
||||
* Copyright (c) 2003-2004 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "vl.h"
|
||||
|
||||
//#define DEBUG_CMOS
|
||||
|
||||
struct RTCState {
|
||||
uint8_t cmos_data[128];
|
||||
uint8_t cmos_index;
|
||||
};
|
||||
|
||||
void rtc_set_memory(RTCState *s, int addr, int val)
|
||||
{
|
||||
if (addr >= 0 && addr <= 127)
|
||||
s->cmos_data[addr] = val;
|
||||
}
|
||||
|
||||
static void cmos_ioport_write(void *opaque, uint32_t addr, uint32_t data)
|
||||
{
|
||||
RTCState *s = opaque;
|
||||
|
||||
if ((addr & 1) == 0) {
|
||||
s->cmos_index = data & 0x7f;
|
||||
} else {
|
||||
#ifdef DEBUG_CMOS
|
||||
printf("cmos: write index=0x%02x val=0x%02x\n",
|
||||
s->cmos_index, data);
|
||||
#endif
|
||||
s->cmos_data[s->cmos_index] = data;
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t cmos_ioport_read(void *opaque, uint32_t addr)
|
||||
{
|
||||
RTCState *s = opaque;
|
||||
int ret;
|
||||
if ((addr & 1) == 0) {
|
||||
return 0xff;
|
||||
} else {
|
||||
ret = s->cmos_data[s->cmos_index];
|
||||
#ifdef DEBUG_CMOS
|
||||
printf("cmos: read index=0x%02x val=0x%02x\n",
|
||||
s->cmos_index, ret);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
static void rtc_save(QEMUFile *f, void *opaque)
|
||||
{
|
||||
RTCState *s = opaque;
|
||||
|
||||
qemu_put_buffer(f, s->cmos_data, 128);
|
||||
qemu_put_8s(f, &s->cmos_index);
|
||||
}
|
||||
|
||||
static int rtc_load(QEMUFile *f, void *opaque, int version_id)
|
||||
{
|
||||
RTCState *s = opaque;
|
||||
|
||||
if (version_id != 1)
|
||||
return -EINVAL;
|
||||
|
||||
qemu_get_buffer(f, s->cmos_data, 128);
|
||||
qemu_get_8s(f, &s->cmos_index);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
RTCState *rtc_init(int base, int irq)
|
||||
{
|
||||
RTCState *s;
|
||||
|
||||
s = qemu_mallocz(sizeof(RTCState));
|
||||
if (!s)
|
||||
return NULL;
|
||||
|
||||
register_ioport_write(base, 2, 1, cmos_ioport_write, s);
|
||||
register_ioport_read(base, 2, 1, cmos_ioport_read, s);
|
||||
|
||||
register_savevm("mc146818rtc", base, 1, rtc_save, rtc_load, s);
|
||||
return s;
|
||||
}
|
||||
|
||||
void rtc_set_date(RTCState *s, const struct tm *tm) {}
|
||||
@@ -4,6 +4,7 @@ subdir-y += vmx
|
||||
obj-y += hvm.o
|
||||
obj-y += i8254.o
|
||||
obj-y += i8259.o
|
||||
obj-y += rtc.o
|
||||
obj-y += instrlen.o
|
||||
obj-y += intercept.o
|
||||
obj-y += io.o
|
||||
|
||||
@@ -40,8 +40,10 @@
|
||||
#include <asm/processor.h>
|
||||
#include <asm/types.h>
|
||||
#include <asm/msr.h>
|
||||
#include <asm/mc146818rtc.h>
|
||||
#include <asm/spinlock.h>
|
||||
#include <asm/hvm/hvm.h>
|
||||
#include <asm/hvm/vpit.h>
|
||||
#include <asm/hvm/support.h>
|
||||
#include <public/sched.h>
|
||||
#include <public/hvm/ioreq.h>
|
||||
@@ -277,6 +279,7 @@ void hvm_setup_platform(struct domain* d)
|
||||
init_timer(&platform->pl_time.periodic_tm.timer,
|
||||
pt_timer_fn, v, v->processor);
|
||||
pit_init(v, cpu_khz);
|
||||
rtc_init(v, RTC_PORT(0), RTC_IRQ);
|
||||
}
|
||||
|
||||
void pic_irq_request(void *data, int level)
|
||||
@@ -368,7 +371,7 @@ void hvm_hlt(unsigned long rflags)
|
||||
{
|
||||
struct vcpu *v = current;
|
||||
struct periodic_time *pt = &v->domain->arch.hvm_domain.pl_time.periodic_tm;
|
||||
s_time_t next_pit = -1, next_wakeup;
|
||||
s_time_t next_pt = -1, next_wakeup;
|
||||
|
||||
/*
|
||||
* If we halt with interrupts disabled, that's a pretty sure sign that we
|
||||
@@ -379,10 +382,10 @@ void hvm_hlt(unsigned long rflags)
|
||||
return hvm_vcpu_down();
|
||||
|
||||
if ( !v->vcpu_id )
|
||||
next_pit = get_scheduled(v, pt->irq, pt);
|
||||
next_pt = 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;
|
||||
if ( (next_pt != -1 && next_pt < next_wakeup) || next_wakeup == -1 )
|
||||
next_wakeup = next_pt;
|
||||
if ( next_wakeup != - 1 )
|
||||
set_timer(¤t->arch.hvm_vcpu.hlt_timer, next_wakeup);
|
||||
do_sched_op_compat(SCHEDOP_block, 0);
|
||||
|
||||
@@ -49,7 +49,6 @@
|
||||
#define RW_STATE_WORD0 3
|
||||
#define RW_STATE_WORD1 4
|
||||
|
||||
#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);
|
||||
|
||||
@@ -77,17 +76,6 @@ uint64_t muldiv64(uint64_t a, uint32_t b, uint32_t 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)
|
||||
{
|
||||
uint64_t d;
|
||||
@@ -215,11 +203,11 @@ static inline void pit_load_count(PITChannelState *s, int val)
|
||||
switch (s->mode) {
|
||||
case 2:
|
||||
/* create periodic time */
|
||||
s->pt = create_periodic_time (s, period, 0, 0);
|
||||
s->pt = create_periodic_time (period, 0, 0, pit_time_fired, s);
|
||||
break;
|
||||
case 1:
|
||||
/* create one shot time */
|
||||
s->pt = create_periodic_time (s, period, 0, 1);
|
||||
s->pt = create_periodic_time (period, 0, 1, pit_time_fired, s);
|
||||
#ifdef DEBUG_PIT
|
||||
printk("HVM_PIT: create one shot time.\n");
|
||||
#endif
|
||||
|
||||
@@ -598,23 +598,47 @@ int cpu_get_pic_interrupt(struct vcpu *v, int *type)
|
||||
return intno;
|
||||
}
|
||||
|
||||
int is_pit_irq(struct vcpu *v, int irq, int type)
|
||||
int is_periodic_irq(struct vcpu *v, int irq, int type)
|
||||
{
|
||||
int pit_vec;
|
||||
int vec;
|
||||
struct periodic_time *pt =
|
||||
&(v->domain->arch.hvm_domain.pl_time.periodic_tm);
|
||||
struct RTCState *vrtc =
|
||||
&(v->domain->arch.hvm_domain.pl_time.vrtc);
|
||||
|
||||
if (type == APIC_DM_EXTINT)
|
||||
pit_vec = v->domain->arch.hvm_domain.vpic.pics[0].irq_base;
|
||||
else
|
||||
pit_vec =
|
||||
v->domain->arch.hvm_domain.vioapic.redirtbl[0].RedirForm.vector;
|
||||
if (pt->irq == 0) { /* Is it pit irq? */
|
||||
if (type == APIC_DM_EXTINT)
|
||||
vec = v->domain->arch.hvm_domain.vpic.pics[0].irq_base;
|
||||
else
|
||||
vec =
|
||||
v->domain->arch.hvm_domain.vioapic.redirtbl[0].RedirForm.vector;
|
||||
|
||||
return (irq == pit_vec);
|
||||
if (irq == vec)
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (pt->irq == 8) { /* Or rtc irq? */
|
||||
if (type == APIC_DM_EXTINT)
|
||||
vec = v->domain->arch.hvm_domain.vpic.pics[1].irq_base;
|
||||
else
|
||||
vec =
|
||||
v->domain->arch.hvm_domain.vioapic.redirtbl[8].RedirForm.vector;
|
||||
|
||||
if (irq == vec)
|
||||
return is_rtc_periodic_irq(vrtc);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int is_irq_enabled(struct vcpu *v, int irq)
|
||||
{
|
||||
struct hvm_vioapic *vioapic = &v->domain->arch.hvm_domain.vioapic;
|
||||
struct hvm_virpic *vpic=&v->domain->arch.hvm_domain.vpic;
|
||||
|
||||
if (vioapic->redirtbl[irq].RedirForm.mask == 0)
|
||||
return 1;
|
||||
|
||||
if ( irq & 8 ) {
|
||||
return !( (1 << (irq&7)) & vpic->pics[1].imr);
|
||||
}
|
||||
|
||||
@@ -315,17 +315,14 @@ void pickup_deactive_ticks(struct periodic_time *pt)
|
||||
* period: fire frequency in ns.
|
||||
*/
|
||||
struct periodic_time * create_periodic_time(
|
||||
PITChannelState *s,
|
||||
u32 period,
|
||||
char irq,
|
||||
char one_shot)
|
||||
char one_shot,
|
||||
time_cb *cb,
|
||||
void *data)
|
||||
{
|
||||
struct vcpu *v = s->vcpu;
|
||||
struct periodic_time *pt = &(v->domain->arch.hvm_domain.pl_time.periodic_tm);
|
||||
struct periodic_time *pt = &(current->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;
|
||||
}
|
||||
@@ -345,7 +342,8 @@ struct periodic_time * create_periodic_time(
|
||||
pt->scheduled = NOW() + period;
|
||||
set_timer (&pt->timer,pt->scheduled);
|
||||
pt->enabled = 1;
|
||||
pt->priv = s;
|
||||
pt->cb = cb;
|
||||
pt->priv = data;
|
||||
return pt;
|
||||
}
|
||||
|
||||
|
||||
@@ -683,7 +683,7 @@ 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 ( is_pit_irq(v, vector, type) ) {
|
||||
if ( pt->enabled && is_periodic_irq(v, vector, type) ) {
|
||||
if ( !pt->first_injected ) {
|
||||
pt->pending_intr_nr = 0;
|
||||
pt->last_plt_gtime = hvm_get_guest_time(v);
|
||||
@@ -694,8 +694,9 @@ void hvm_interrupt_post(struct vcpu *v, int vector, int type)
|
||||
pt->pending_intr_nr--;
|
||||
pt->last_plt_gtime += pt->period_cycles;
|
||||
hvm_set_guest_time(v, pt->last_plt_gtime);
|
||||
pit_time_fired(v, pt->priv);
|
||||
}
|
||||
if (pt->cb)
|
||||
pt->cb(v, pt->priv);
|
||||
}
|
||||
|
||||
switch(type) {
|
||||
|
||||
@@ -0,0 +1,393 @@
|
||||
/*
|
||||
* QEMU MC146818 RTC emulation
|
||||
*
|
||||
* Copyright (c) 2003-2004 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <asm/mc146818rtc.h>
|
||||
#include <asm/hvm/vpit.h>
|
||||
#include <asm/hvm/io.h>
|
||||
#include <asm/hvm/support.h>
|
||||
#include <asm/current.h>
|
||||
|
||||
/* #define DEBUG_RTC */
|
||||
|
||||
void rtc_periodic_cb(struct vcpu *v, void *opaque)
|
||||
{
|
||||
RTCState *s = opaque;
|
||||
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);
|
||||
}
|
||||
|
||||
static void rtc_timer_update(RTCState *s, int64_t current_time)
|
||||
{
|
||||
int period_code;
|
||||
int period;
|
||||
|
||||
period_code = s->cmos_data[RTC_REG_A] & 0x0f;
|
||||
if (period_code != 0 && (s->cmos_data[RTC_REG_B] & RTC_PIE)) {
|
||||
if (period_code <= 2)
|
||||
period_code += 7;
|
||||
|
||||
period = 1 << (period_code - 1); /* period in 32 Khz cycles */
|
||||
period = DIV_ROUND((period * 1000000000ULL), 32768); /* period in ns */
|
||||
|
||||
#ifdef DEBUG_RTC
|
||||
printk("HVM_RTC: period = %uns\n", period);
|
||||
#endif
|
||||
|
||||
s->pt = create_periodic_time(period, RTC_IRQ, 0, rtc_periodic_cb, s);
|
||||
} else if (s->pt) {
|
||||
destroy_periodic_time(s->pt);
|
||||
s->pt = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void rtc_set_time(RTCState *s);
|
||||
|
||||
static int rtc_ioport_write(void *opaque, uint32_t addr, uint32_t data)
|
||||
{
|
||||
RTCState *s = opaque;
|
||||
|
||||
if ((addr & 1) == 0) {
|
||||
s->cmos_index = data & 0x7f;
|
||||
if (s->cmos_index < RTC_SIZE)
|
||||
return 1;
|
||||
} else if (s->cmos_index < RTC_SIZE) {
|
||||
#ifdef DEBUG_RTC
|
||||
printk("HVM_RTC: write index=0x%02x val=0x%02x\n",
|
||||
s->cmos_index, data);
|
||||
#endif
|
||||
switch(s->cmos_index) {
|
||||
case RTC_SECONDS_ALARM:
|
||||
case RTC_MINUTES_ALARM:
|
||||
case RTC_HOURS_ALARM:
|
||||
s->cmos_data[s->cmos_index] = data;
|
||||
break;
|
||||
case RTC_SECONDS:
|
||||
case RTC_MINUTES:
|
||||
case RTC_HOURS:
|
||||
case RTC_DAY_OF_WEEK:
|
||||
case RTC_DAY_OF_MONTH:
|
||||
case RTC_MONTH:
|
||||
case RTC_YEAR:
|
||||
s->cmos_data[s->cmos_index] = data;
|
||||
/* if in set mode, do not update the time */
|
||||
if (!(s->cmos_data[RTC_REG_B] & RTC_SET)) {
|
||||
rtc_set_time(s);
|
||||
}
|
||||
break;
|
||||
case RTC_REG_A:
|
||||
/* UIP bit is read only */
|
||||
s->cmos_data[RTC_REG_A] = (data & ~RTC_UIP) |
|
||||
(s->cmos_data[RTC_REG_A] & RTC_UIP);
|
||||
rtc_timer_update(s, hvm_get_clock(s->vcpu));
|
||||
break;
|
||||
case RTC_REG_B:
|
||||
if (data & RTC_SET) {
|
||||
/* set mode: reset UIP mode */
|
||||
s->cmos_data[RTC_REG_A] &= ~RTC_UIP;
|
||||
data &= ~RTC_UIE;
|
||||
} else {
|
||||
/* if disabling set mode, update the time */
|
||||
if (s->cmos_data[RTC_REG_B] & RTC_SET) {
|
||||
rtc_set_time(s);
|
||||
}
|
||||
}
|
||||
s->cmos_data[RTC_REG_B] = data;
|
||||
rtc_timer_update(s, hvm_get_clock(s->vcpu));
|
||||
break;
|
||||
case RTC_REG_C:
|
||||
case RTC_REG_D:
|
||||
/* cannot write to them */
|
||||
break;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int to_bcd(RTCState *s, int a)
|
||||
{
|
||||
if (s->cmos_data[RTC_REG_B] & 0x04) {
|
||||
return a;
|
||||
} else {
|
||||
return ((a / 10) << 4) | (a % 10);
|
||||
}
|
||||
}
|
||||
|
||||
static inline int from_bcd(RTCState *s, int a)
|
||||
{
|
||||
if (s->cmos_data[RTC_REG_B] & 0x04) {
|
||||
return a;
|
||||
} else {
|
||||
return ((a >> 4) * 10) + (a & 0x0f);
|
||||
}
|
||||
}
|
||||
|
||||
static void rtc_set_time(RTCState *s)
|
||||
{
|
||||
struct tm *tm = &s->current_tm;
|
||||
|
||||
tm->tm_sec = from_bcd(s, s->cmos_data[RTC_SECONDS]);
|
||||
tm->tm_min = from_bcd(s, s->cmos_data[RTC_MINUTES]);
|
||||
tm->tm_hour = from_bcd(s, s->cmos_data[RTC_HOURS] & 0x7f);
|
||||
if (!(s->cmos_data[RTC_REG_B] & 0x02) &&
|
||||
(s->cmos_data[RTC_HOURS] & 0x80)) {
|
||||
tm->tm_hour += 12;
|
||||
}
|
||||
tm->tm_wday = from_bcd(s, s->cmos_data[RTC_DAY_OF_WEEK]);
|
||||
tm->tm_mday = from_bcd(s, s->cmos_data[RTC_DAY_OF_MONTH]);
|
||||
tm->tm_mon = from_bcd(s, s->cmos_data[RTC_MONTH]) - 1;
|
||||
tm->tm_year = from_bcd(s, s->cmos_data[RTC_YEAR]) + 100;
|
||||
}
|
||||
|
||||
static void rtc_copy_date(RTCState *s)
|
||||
{
|
||||
const struct tm *tm = &s->current_tm;
|
||||
|
||||
s->cmos_data[RTC_SECONDS] = to_bcd(s, tm->tm_sec);
|
||||
s->cmos_data[RTC_MINUTES] = to_bcd(s, tm->tm_min);
|
||||
if (s->cmos_data[RTC_REG_B] & 0x02) {
|
||||
/* 24 hour format */
|
||||
s->cmos_data[RTC_HOURS] = to_bcd(s, tm->tm_hour);
|
||||
} else {
|
||||
/* 12 hour format */
|
||||
s->cmos_data[RTC_HOURS] = to_bcd(s, tm->tm_hour % 12);
|
||||
if (tm->tm_hour >= 12)
|
||||
s->cmos_data[RTC_HOURS] |= 0x80;
|
||||
}
|
||||
s->cmos_data[RTC_DAY_OF_WEEK] = to_bcd(s, tm->tm_wday);
|
||||
s->cmos_data[RTC_DAY_OF_MONTH] = to_bcd(s, tm->tm_mday);
|
||||
s->cmos_data[RTC_MONTH] = to_bcd(s, tm->tm_mon + 1);
|
||||
s->cmos_data[RTC_YEAR] = to_bcd(s, tm->tm_year % 100);
|
||||
}
|
||||
|
||||
/* month is between 0 and 11. */
|
||||
static int get_days_in_month(int month, int year)
|
||||
{
|
||||
static const int days_tab[12] = {
|
||||
31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
|
||||
};
|
||||
int d;
|
||||
if ((unsigned )month >= 12)
|
||||
return 31;
|
||||
d = days_tab[month];
|
||||
if (month == 1) {
|
||||
if ((year % 4) == 0 && ((year % 100) != 0 || (year % 400) == 0))
|
||||
d++;
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
/* update 'tm' to the next second */
|
||||
static void rtc_next_second(struct tm *tm)
|
||||
{
|
||||
int days_in_month;
|
||||
|
||||
tm->tm_sec++;
|
||||
if ((unsigned)tm->tm_sec >= 60) {
|
||||
tm->tm_sec = 0;
|
||||
tm->tm_min++;
|
||||
if ((unsigned)tm->tm_min >= 60) {
|
||||
tm->tm_min = 0;
|
||||
tm->tm_hour++;
|
||||
if ((unsigned)tm->tm_hour >= 24) {
|
||||
tm->tm_hour = 0;
|
||||
/* next day */
|
||||
tm->tm_wday++;
|
||||
if ((unsigned)tm->tm_wday >= 7)
|
||||
tm->tm_wday = 0;
|
||||
days_in_month = get_days_in_month(tm->tm_mon,
|
||||
tm->tm_year + 1900);
|
||||
tm->tm_mday++;
|
||||
if (tm->tm_mday < 1) {
|
||||
tm->tm_mday = 1;
|
||||
} else if (tm->tm_mday > days_in_month) {
|
||||
tm->tm_mday = 1;
|
||||
tm->tm_mon++;
|
||||
if (tm->tm_mon >= 12) {
|
||||
tm->tm_mon = 0;
|
||||
tm->tm_year++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void rtc_update_second(void *opaque)
|
||||
{
|
||||
RTCState *s = opaque;
|
||||
|
||||
/* if the oscillator is not in normal operation, we do not update */
|
||||
if ((s->cmos_data[RTC_REG_A] & 0x70) != 0x20) {
|
||||
s->next_second_time += 1000000000ULL;
|
||||
set_timer(&s->second_timer, s->next_second_time);
|
||||
} else {
|
||||
rtc_next_second(&s->current_tm);
|
||||
|
||||
if (!(s->cmos_data[RTC_REG_B] & RTC_SET)) {
|
||||
/* update in progress bit */
|
||||
s->cmos_data[RTC_REG_A] |= RTC_UIP;
|
||||
}
|
||||
/* Delay time before update cycle */
|
||||
set_timer(&s->second_timer2, s->next_second_time + 244000);
|
||||
}
|
||||
}
|
||||
|
||||
static void rtc_update_second2(void *opaque)
|
||||
{
|
||||
RTCState *s = opaque;
|
||||
struct hvm_domain *plat=&s->vcpu->domain->arch.hvm_domain;
|
||||
struct hvm_virpic *pic= &plat->vpic;
|
||||
|
||||
if (!(s->cmos_data[RTC_REG_B] & RTC_SET)) {
|
||||
rtc_copy_date(s);
|
||||
}
|
||||
|
||||
/* check alarm */
|
||||
if (s->cmos_data[RTC_REG_B] & RTC_AIE) {
|
||||
if (((s->cmos_data[RTC_SECONDS_ALARM] & 0xc0) == 0xc0 ||
|
||||
s->cmos_data[RTC_SECONDS_ALARM] == s->current_tm.tm_sec) &&
|
||||
((s->cmos_data[RTC_MINUTES_ALARM] & 0xc0) == 0xc0 ||
|
||||
s->cmos_data[RTC_MINUTES_ALARM] == s->current_tm.tm_mon) &&
|
||||
((s->cmos_data[RTC_HOURS_ALARM] & 0xc0) == 0xc0 ||
|
||||
s->cmos_data[RTC_HOURS_ALARM] == s->current_tm.tm_hour)) {
|
||||
|
||||
s->cmos_data[RTC_REG_C] |= 0xa0;
|
||||
pic_set_irq(pic, s->irq, 0);
|
||||
pic_set_irq(pic, s->irq, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/* update ended interrupt */
|
||||
if (s->cmos_data[RTC_REG_B] & RTC_UIE) {
|
||||
s->cmos_data[RTC_REG_C] |= 0x90;
|
||||
pic_set_irq(pic, s->irq, 0);
|
||||
pic_set_irq(pic, s->irq, 1);
|
||||
}
|
||||
|
||||
/* clear update in progress bit */
|
||||
s->cmos_data[RTC_REG_A] &= ~RTC_UIP;
|
||||
|
||||
s->next_second_time += 1000000000ULL;
|
||||
set_timer(&s->second_timer, s->next_second_time);
|
||||
}
|
||||
|
||||
static uint32_t rtc_ioport_read(void *opaque, uint32_t addr)
|
||||
{
|
||||
RTCState *s = opaque;
|
||||
struct hvm_domain *plat=&s->vcpu->domain->arch.hvm_domain;
|
||||
struct hvm_virpic *pic= &plat->vpic;
|
||||
int ret;
|
||||
|
||||
if ((addr & 1) == 0) {
|
||||
return 0xff;
|
||||
} else {
|
||||
switch(s->cmos_index) {
|
||||
case RTC_SECONDS:
|
||||
case RTC_MINUTES:
|
||||
case RTC_HOURS:
|
||||
case RTC_DAY_OF_WEEK:
|
||||
case RTC_DAY_OF_MONTH:
|
||||
case RTC_MONTH:
|
||||
case RTC_YEAR:
|
||||
ret = s->cmos_data[s->cmos_index];
|
||||
break;
|
||||
case RTC_REG_A:
|
||||
ret = s->cmos_data[s->cmos_index];
|
||||
break;
|
||||
case RTC_REG_C:
|
||||
ret = s->cmos_data[s->cmos_index];
|
||||
pic_set_irq(pic, s->irq, 0);
|
||||
s->cmos_data[RTC_REG_C] = 0x00;
|
||||
break;
|
||||
default:
|
||||
ret = s->cmos_data[s->cmos_index];
|
||||
break;
|
||||
}
|
||||
#ifdef DEBUG_RTC
|
||||
printk("HVM_RTC: read index=0x%02x val=0x%02x\n",
|
||||
s->cmos_index, ret);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
static int handle_rtc_io(ioreq_t *p)
|
||||
{
|
||||
struct vcpu *v = current;
|
||||
struct RTCState *vrtc = &v->domain->arch.hvm_domain.pl_time.vrtc;
|
||||
|
||||
if (p->size != 1 ||
|
||||
p->pdata_valid ||
|
||||
p->type != IOREQ_TYPE_PIO){
|
||||
printk("HVM_RTC: wrong RTC IO!\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (p->dir == 0) { /* write */
|
||||
if (rtc_ioport_write(vrtc, p->addr, p->u.data & 0xFF))
|
||||
return 1;
|
||||
} else if (p->dir == 1 && vrtc->cmos_index < RTC_SIZE) { /* read */
|
||||
p->u.data = rtc_ioport_read(vrtc, p->addr);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void rtc_init(struct vcpu *v, int base, int irq)
|
||||
{
|
||||
RTCState *s = &v->domain->arch.hvm_domain.pl_time.vrtc;
|
||||
|
||||
s->vcpu = v;
|
||||
s->irq = irq;
|
||||
s->cmos_data[RTC_REG_A] = 0x26;
|
||||
s->cmos_data[RTC_REG_B] = 0x02;
|
||||
s->cmos_data[RTC_REG_C] = 0x00;
|
||||
s->cmos_data[RTC_REG_D] = 0x80;
|
||||
|
||||
s->current_tm = gmtime(get_localtime(v->domain));
|
||||
rtc_copy_date(s);
|
||||
|
||||
init_timer(&s->second_timer, rtc_update_second, s, v->processor);
|
||||
init_timer(&s->second_timer2, rtc_update_second2, s, v->processor);
|
||||
|
||||
s->next_second_time = NOW() + 1000000000ULL;
|
||||
set_timer(&s->second_timer2, s->next_second_time);
|
||||
|
||||
register_portio_handler(base, 2, handle_rtc_io);
|
||||
}
|
||||
|
||||
void rtc_deinit(struct domain *d)
|
||||
{
|
||||
RTCState *s = &d->arch.hvm_domain.pl_time.vrtc;
|
||||
|
||||
kill_timer(&s->second_timer);
|
||||
kill_timer(&s->second_timer2);
|
||||
}
|
||||
@@ -140,8 +140,8 @@ asmlinkage void svm_intr_assist(void)
|
||||
case APIC_DM_FIXED:
|
||||
case APIC_DM_LOWEST:
|
||||
/* Re-injecting a PIT interruptt? */
|
||||
if (re_injecting &&
|
||||
is_pit_irq(v, intr_vector, intr_type)) {
|
||||
if (re_injecting && pt->enabled &&
|
||||
is_periodic_irq(v, intr_vector, intr_type)) {
|
||||
++pt->pending_intr_nr;
|
||||
}
|
||||
/* let's inject this interrupt */
|
||||
|
||||
@@ -921,6 +921,7 @@ static void svm_relinquish_guest_resources(struct domain *d)
|
||||
}
|
||||
|
||||
kill_timer(&d->arch.hvm_domain.pl_time.periodic_tm.timer);
|
||||
rtc_deinit(d);
|
||||
|
||||
if ( d->arch.hvm_domain.shared_page_va )
|
||||
unmap_domain_page_global(
|
||||
@@ -935,6 +936,7 @@ static void svm_migrate_timers(struct vcpu *v)
|
||||
{
|
||||
struct periodic_time *pt =
|
||||
&(v->domain->arch.hvm_domain.pl_time.periodic_tm);
|
||||
struct RTCState *vrtc = &v->domain->arch.hvm_domain.pl_time.vrtc;
|
||||
|
||||
if ( pt->enabled )
|
||||
{
|
||||
@@ -943,6 +945,8 @@ static void svm_migrate_timers(struct vcpu *v)
|
||||
}
|
||||
if ( VLAPIC(v) != NULL )
|
||||
migrate_timer(&VLAPIC(v)->vlapic_timer, v->processor);
|
||||
migrate_timer(&vrtc->second_timer, v->processor);
|
||||
migrate_timer(&vrtc->second_timer2, v->processor);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -146,6 +146,7 @@ static void vmx_relinquish_guest_resources(struct domain *d)
|
||||
}
|
||||
|
||||
kill_timer(&d->arch.hvm_domain.pl_time.periodic_tm.timer);
|
||||
rtc_deinit(d);
|
||||
|
||||
if ( d->arch.hvm_domain.shared_page_va )
|
||||
unmap_domain_page_global(
|
||||
@@ -487,6 +488,7 @@ static void stop_vmx(void)
|
||||
void vmx_migrate_timers(struct vcpu *v)
|
||||
{
|
||||
struct periodic_time *pt = &(v->domain->arch.hvm_domain.pl_time.periodic_tm);
|
||||
struct RTCState *vrtc = &v->domain->arch.hvm_domain.pl_time.vrtc;
|
||||
|
||||
if ( pt->enabled )
|
||||
{
|
||||
@@ -495,6 +497,8 @@ void vmx_migrate_timers(struct vcpu *v)
|
||||
}
|
||||
if ( VLAPIC(v) != NULL )
|
||||
migrate_timer(&VLAPIC(v)->vlapic_timer, v->processor);
|
||||
migrate_timer(&vrtc->second_timer, v->processor);
|
||||
migrate_timer(&vrtc->second_timer2, v->processor);
|
||||
}
|
||||
|
||||
static void vmx_store_cpu_guest_regs(
|
||||
|
||||
@@ -919,6 +919,13 @@ void send_timer_event(struct vcpu *v)
|
||||
send_guest_vcpu_virq(v, VIRQ_TIMER);
|
||||
}
|
||||
|
||||
/* Return secs after 00:00:00 localtime, 1 January, 1970. */
|
||||
unsigned long get_localtime(struct domain *d)
|
||||
{
|
||||
return wc_sec + (wc_nsec + NOW()) / 1000000000ULL
|
||||
+ d->time_offset_seconds;
|
||||
}
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* mode: C
|
||||
|
||||
+2
-1
@@ -20,8 +20,9 @@ obj-y += softirq.o
|
||||
obj-y += string.o
|
||||
obj-y += symbols.o
|
||||
obj-y += sysctl.o
|
||||
obj-y += trace.o
|
||||
obj-y += time.o
|
||||
obj-y += timer.o
|
||||
obj-y += trace.o
|
||||
obj-y += version.o
|
||||
obj-y += vsprintf.o
|
||||
obj-y += xmalloc.o
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
/******************************************************************************
|
||||
* time.c
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <xen/config.h>
|
||||
#include <xen/time.h>
|
||||
|
||||
/* Nonzero if YEAR is a leap year (every 4 years,
|
||||
except every 100th isn't, and every 400th is). */
|
||||
#define __isleap(year) \
|
||||
((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
|
||||
|
||||
/* How many days are in each month. */
|
||||
const unsigned short int __mon_lengths[2][12] = {
|
||||
/* Normal years. */
|
||||
{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
|
||||
/* Leap years. */
|
||||
{31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
|
||||
};
|
||||
|
||||
#define SECS_PER_HOUR (60 * 60)
|
||||
#define SECS_PER_DAY (SECS_PER_HOUR * 24)
|
||||
|
||||
struct tm gmtime(unsigned long t)
|
||||
{
|
||||
struct tm tbuf;
|
||||
long days, rem;
|
||||
int y;
|
||||
unsigned short int *ip;
|
||||
|
||||
days = t / SECS_PER_DAY;
|
||||
rem = t % SECS_PER_DAY;
|
||||
|
||||
tbuf.tm_hour = rem / SECS_PER_HOUR;
|
||||
rem %= SECS_PER_HOUR;
|
||||
tbuf.tm_min = rem / 60;
|
||||
tbuf.tm_sec = rem % 60;
|
||||
/* January 1, 1970 was a Thursday. */
|
||||
tbuf.tm_wday = (4 + days) % 7;
|
||||
if ( tbuf.tm_wday < 0 )
|
||||
tbuf.tm_wday += 7;
|
||||
y = 1970;
|
||||
while ( days >= (rem = __isleap(y) ? 366 : 365) )
|
||||
{
|
||||
++y;
|
||||
days -= rem;
|
||||
}
|
||||
while ( days < 0 )
|
||||
{
|
||||
--y;
|
||||
days += __isleap(y) ? 366 : 365;
|
||||
}
|
||||
tbuf.tm_year = y - 1900;
|
||||
tbuf.tm_yday = days;
|
||||
ip = (unsigned short int *)__mon_lengths[__isleap(y)];
|
||||
for ( y = 0; days >= ip[y]; ++y )
|
||||
days -= ip[y];
|
||||
tbuf.tm_mon = y;
|
||||
tbuf.tm_mday = days + 1;
|
||||
tbuf.tm_isdst = -1;
|
||||
|
||||
return tbuf;
|
||||
}
|
||||
@@ -75,7 +75,7 @@ void pic_update_irq(struct hvm_virpic *s); /* Caller must hold s->lock */
|
||||
uint32_t pic_intack_read(struct hvm_virpic *s);
|
||||
void register_pic_io_hook (void);
|
||||
int cpu_get_pic_interrupt(struct vcpu *v, int *type);
|
||||
int is_pit_irq(struct vcpu *v, int irq, int type);
|
||||
int is_periodic_irq(struct vcpu *v, int irq, int type);
|
||||
int is_irq_enabled(struct vcpu *v, int irq);
|
||||
void do_pic_irqs (struct hvm_virpic *s, uint16_t irqs);
|
||||
void do_pic_irqs_clear (struct hvm_virpic *s, uint16_t irqs);
|
||||
|
||||
@@ -25,11 +25,12 @@
|
||||
#include <xen/lib.h>
|
||||
#include <xen/time.h>
|
||||
#include <xen/errno.h>
|
||||
#include <xen/time.h>
|
||||
#include <xen/timer.h>
|
||||
#include <asm/hvm/vpic.h>
|
||||
|
||||
#define PIT_FREQ 1193181
|
||||
#define PIT_BASE 0x40
|
||||
#define PIT_BASE 0x40
|
||||
|
||||
typedef struct PITChannelState {
|
||||
int count; /* can be 65536 */
|
||||
@@ -50,9 +51,31 @@ typedef struct PITChannelState {
|
||||
struct periodic_time *pt;
|
||||
} PITChannelState;
|
||||
|
||||
typedef struct PITState {
|
||||
PITChannelState channels[3];
|
||||
int speaker_data_on;
|
||||
int dummy_refresh_clock;
|
||||
} PITState;
|
||||
|
||||
#define RTC_SIZE 14
|
||||
typedef struct RTCState {
|
||||
uint8_t cmos_data[RTC_SIZE]; /* Only handle time/interrupt part in HV */
|
||||
uint8_t cmos_index;
|
||||
struct tm current_tm;
|
||||
int irq;
|
||||
/* second update */
|
||||
int64_t next_second_time;
|
||||
struct timer second_timer;
|
||||
struct timer second_timer2;
|
||||
struct vcpu *vcpu;
|
||||
struct periodic_time *pt;
|
||||
} RTCState;
|
||||
|
||||
/*
|
||||
* 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 */
|
||||
@@ -64,19 +87,15 @@ struct periodic_time {
|
||||
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 PITState {
|
||||
PITChannelState channels[3];
|
||||
int speaker_data_on;
|
||||
int dummy_refresh_clock;
|
||||
} PITState;
|
||||
|
||||
struct pl_time { /* platform time */
|
||||
struct periodic_time periodic_tm;
|
||||
struct PITState vpit;
|
||||
/* TODO: RTC/ACPI time */
|
||||
struct RTCState vrtc;
|
||||
/* TODO: ACPI time */
|
||||
};
|
||||
|
||||
static __inline__ s_time_t get_scheduled(
|
||||
@@ -90,13 +109,30 @@ static __inline__ s_time_t get_scheduled(
|
||||
return -1;
|
||||
}
|
||||
|
||||
extern u64 hvm_get_guest_time(struct vcpu *v);
|
||||
/*
|
||||
* get processor time.
|
||||
* unit: TSC
|
||||
*/
|
||||
static __inline__ int64_t hvm_get_clock(struct vcpu *v)
|
||||
{
|
||||
uint64_t gtsc;
|
||||
|
||||
gtsc = hvm_get_guest_time(v);
|
||||
return gtsc;
|
||||
}
|
||||
|
||||
#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 u64 hvm_get_guest_time(struct vcpu *v);
|
||||
extern struct periodic_time *create_periodic_time(PITChannelState *v, u32 period, char irq, char one_shot);
|
||||
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 pit_init(struct vcpu *v, unsigned long cpu_khz);
|
||||
void rtc_init(struct vcpu *v, int base, int irq);
|
||||
void rtc_deinit(struct domain *d);
|
||||
int is_rtc_periodic_irq(void *opaque);
|
||||
void pt_timer_fn(void *data);
|
||||
void pit_time_fired(struct vcpu *v, void *priv);
|
||||
|
||||
|
||||
@@ -49,6 +49,20 @@ struct domain;
|
||||
typedef s64 s_time_t;
|
||||
|
||||
s_time_t get_s_time(void);
|
||||
unsigned long get_localtime(struct domain *d);
|
||||
|
||||
struct tm {
|
||||
int tm_sec; /* seconds */
|
||||
int tm_min; /* minutes */
|
||||
int tm_hour; /* hours */
|
||||
int tm_mday; /* day of the month */
|
||||
int tm_mon; /* month */
|
||||
int tm_year; /* year */
|
||||
int tm_wday; /* day of the week */
|
||||
int tm_yday; /* day in the year */
|
||||
int tm_isdst; /* daylight saving time */
|
||||
};
|
||||
struct tm gmtime(unsigned long t);
|
||||
|
||||
#define NOW() ((s_time_t)get_s_time())
|
||||
#define SECONDS(_s) ((s_time_t)((_s) * 1000000000ULL))
|
||||
|
||||