[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-changelog] [xen-unstable] [HVM] Move RTC emulation into the hypervisor.
# 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
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |