# HG changeset patch
# User kfraser@xxxxxxxxxxxxxxxxxxxxx
# Node ID 71e2a165aa7f81602c569430b18ba1ea705f0b70
# Parent da66691687dfd90c55420cfdf27f55d18cca7810
[HVM] Move RTC emulation into the hypervisor.
Signed-off-by: Xiaowei Yang <xiaowei.yang@xxxxxxxxx>
---
tools/ioemu/Makefile.target | 8
tools/ioemu/target-i386-dm/rtc-dm.c | 107 +++++++++
xen/arch/x86/hvm/Makefile | 1
xen/arch/x86/hvm/hvm.c | 11 -
xen/arch/x86/hvm/i8254.c | 16 -
xen/arch/x86/hvm/i8259.c | 48 +++-
xen/arch/x86/hvm/intercept.c | 16 -
xen/arch/x86/hvm/io.c | 7
xen/arch/x86/hvm/rtc.c | 393 ++++++++++++++++++++++++++++++++++++
xen/arch/x86/hvm/svm/intr.c | 4
xen/arch/x86/hvm/svm/svm.c | 4
xen/arch/x86/hvm/vmx/vmx.c | 4
xen/arch/x86/time.c | 7
xen/common/Makefile | 3
xen/common/time.c | 77 +++++++
xen/include/asm-x86/hvm/vpic.h | 2
xen/include/asm-x86/hvm/vpit.h | 56 ++++-
xen/include/xen/time.h | 14 +
18 files changed, 722 insertions(+), 56 deletions(-)
diff -r da66691687df -r 71e2a165aa7f tools/ioemu/Makefile.target
--- a/tools/ioemu/Makefile.target Wed Oct 18 18:13:57 2006 +0100
+++ b/tools/ioemu/Makefile.target Wed Oct 18 18:35:21 2006 +0100
@@ -294,7 +294,11 @@ endif
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 @@ ifeq ($(TARGET_BASE_ARCH), i386)
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
diff -r da66691687df -r 71e2a165aa7f xen/arch/x86/hvm/Makefile
--- a/xen/arch/x86/hvm/Makefile Wed Oct 18 18:13:57 2006 +0100
+++ b/xen/arch/x86/hvm/Makefile Wed Oct 18 18:35:21 2006 +0100
@@ -4,6 +4,7 @@ obj-y += hvm.o
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
diff -r da66691687df -r 71e2a165aa7f xen/arch/x86/hvm/hvm.c
--- a/xen/arch/x86/hvm/hvm.c Wed Oct 18 18:13:57 2006 +0100
+++ b/xen/arch/x86/hvm/hvm.c Wed Oct 18 18:35:21 2006 +0100
@@ -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);
diff -r da66691687df -r 71e2a165aa7f xen/arch/x86/hvm/i8254.c
--- a/xen/arch/x86/hvm/i8254.c Wed Oct 18 18:13:57 2006 +0100
+++ b/xen/arch/x86/hvm/i8254.c Wed Oct 18 18:35:21 2006 +0100
@@ -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
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(PITCha
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
diff -r da66691687df -r 71e2a165aa7f xen/arch/x86/hvm/i8259.c
--- a/xen/arch/x86/hvm/i8259.c Wed Oct 18 18:13:57 2006 +0100
+++ b/xen/arch/x86/hvm/i8259.c Wed Oct 18 18:35:21 2006 +0100
@@ -598,23 +598,47 @@ int cpu_get_pic_interrupt(struct vcpu *v
return intno;
}
-int is_pit_irq(struct vcpu *v, int irq, int type)
-{
- int pit_vec;
-
- 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;
-
- return (irq == pit_vec);
+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);
+ struct RTCState *vrtc =
+ &(v->domain->arch.hvm_domain.pl_time.vrtc);
+
+ 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;
+
+ 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);
}
diff -r da66691687df -r 71e2a165aa7f xen/arch/x86/hvm/intercept.c
--- a/xen/arch/x86/hvm/intercept.c Wed Oct 18 18:13:57 2006 +0100
+++ b/xen/arch/x86/hvm/intercept.c Wed Oct 18 18:35:21 2006 +0100
@@ -315,17 +315,14 @@ void pickup_deactive_ticks(struct period
* period: fire frequency in ns.
*/
struct periodic_time * create_periodic_time(
- PITChannelState *s,
u32 period,
char irq,
- char one_shot)
-{
- struct vcpu *v = s->vcpu;
- struct periodic_time *pt =
&(v->domain->arch.hvm_domain.pl_time.periodic_tm);
+ char one_shot,
+ time_cb *cb,
+ void *data)
+{
+ 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_t
pt->scheduled = NOW() + period;
set_timer (&pt->timer,pt->scheduled);
pt->enabled = 1;
- pt->priv = s;
+ pt->cb = cb;
+ pt->priv = data;
return pt;
}
diff -r da66691687df -r 71e2a165aa7f xen/arch/x86/hvm/io.c
--- a/xen/arch/x86/hvm/io.c Wed Oct 18 18:13:57 2006 +0100
+++ b/xen/arch/x86/hvm/io.c Wed Oct 18 18:35:21 2006 +0100
@@ -683,7 +683,7 @@ void hvm_interrupt_post(struct vcpu *v,
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,
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) {
diff -r da66691687df -r 71e2a165aa7f xen/arch/x86/hvm/svm/intr.c
--- a/xen/arch/x86/hvm/svm/intr.c Wed Oct 18 18:13:57 2006 +0100
+++ b/xen/arch/x86/hvm/svm/intr.c Wed Oct 18 18:35:21 2006 +0100
@@ -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 */
diff -r da66691687df -r 71e2a165aa7f xen/arch/x86/hvm/svm/svm.c
--- a/xen/arch/x86/hvm/svm/svm.c Wed Oct 18 18:13:57 2006 +0100
+++ b/xen/arch/x86/hvm/svm/svm.c Wed Oct 18 18:35:21 2006 +0100
@@ -921,6 +921,7 @@ static void svm_relinquish_guest_resourc
}
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 vc
{
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 vc
}
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);
}
diff -r da66691687df -r 71e2a165aa7f xen/arch/x86/hvm/vmx/vmx.c
--- a/xen/arch/x86/hvm/vmx/vmx.c Wed Oct 18 18:13:57 2006 +0100
+++ b/xen/arch/x86/hvm/vmx/vmx.c Wed Oct 18 18:35:21 2006 +0100
@@ -146,6 +146,7 @@ static void vmx_relinquish_guest_resourc
}
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 @@ void vmx_migrate_timers(struct vcpu *v)
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(
diff -r da66691687df -r 71e2a165aa7f xen/arch/x86/time.c
--- a/xen/arch/x86/time.c Wed Oct 18 18:13:57 2006 +0100
+++ b/xen/arch/x86/time.c Wed Oct 18 18:35:21 2006 +0100
@@ -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
diff -r da66691687df -r 71e2a165aa7f xen/common/Makefile
--- a/xen/common/Makefile Wed Oct 18 18:13:57 2006 +0100
+++ b/xen/common/Makefile Wed Oct 18 18:35:21 2006 +0100
@@ -20,8 +20,9 @@ obj-y += string.o
obj-y += string.o
obj-y += symbols.o
obj-y += sysctl.o
+obj-y += time.o
+obj-y += timer.o
obj-y += trace.o
-obj-y += timer.o
obj-y += version.o
obj-y += vsprintf.o
obj-y += xmalloc.o
diff -r da66691687df -r 71e2a165aa7f xen/include/asm-x86/hvm/vpic.h
--- a/xen/include/asm-x86/hvm/vpic.h Wed Oct 18 18:13:57 2006 +0100
+++ b/xen/include/asm-x86/hvm/vpic.h Wed Oct 18 18:35:21 2006 +0100
@@ -75,7 +75,7 @@ uint32_t pic_intack_read(struct hvm_virp
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);
diff -r da66691687df -r 71e2a165aa7f xen/include/asm-x86/hvm/vpit.h
--- a/xen/include/asm-x86/hvm/vpit.h Wed Oct 18 18:13:57 2006 +0100
+++ b/xen/include/asm-x86/hvm/vpit.h Wed Oct 18 18:35:21 2006 +0100
@@ -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 */
@@ -49,10 +50,32 @@ typedef struct PITChannelState {
struct vcpu *vcpu;
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);
diff -r da66691687df -r 71e2a165aa7f xen/include/xen/time.h
--- a/xen/include/xen/time.h Wed Oct 18 18:13:57 2006 +0100
+++ b/xen/include/xen/time.h Wed Oct 18 18:35:21 2006 +0100
@@ -49,6 +49,20 @@ typedef s64 s_time_t;
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))
diff -r da66691687df -r 71e2a165aa7f tools/ioemu/target-i386-dm/rtc-dm.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/ioemu/target-i386-dm/rtc-dm.c Wed Oct 18 18:35:21 2006 +0100
@@ -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) {}
diff -r da66691687df -r 71e2a165aa7f xen/arch/x86/hvm/rtc.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/xen/arch/x86/hvm/rtc.c Wed Oct 18 18:35:21 2006 +0100
@@ -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);
+}
diff -r da66691687df -r 71e2a165aa7f xen/common/time.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/xen/common/time.c Wed Oct 18 18:35:21 2006 +0100
@@ -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;
+}
_______________________________________________
Xen-changelog mailing list
Xen-changelog@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-changelog
|