diff -r 0845f66aa5d5 xen/arch/x86/domain.c --- a/xen/arch/x86/domain.c Thu Oct 13 15:47:46 2005 +0800 +++ b/xen/arch/x86/domain.c Fri Oct 14 10:42:03 2005 +0800 @@ -41,6 +41,7 @@ #include #include #include +#include /* opt_noreboot: If true, machine will need manual reset on error. */ static int opt_noreboot = 0; @@ -286,7 +287,7 @@ #endif (void)ptwr_init(d); - + (void)rtc_emu_init(d); shadow_lock_init(d); INIT_LIST_HEAD(&d->arch.free_shadow_frames); } @@ -995,6 +996,7 @@ physdev_destroy_state(d); ptwr_destroy(d); + rtc_emu_destroy(d); /* Release device mappings of other domains */ gnttab_release_dev_mappings(d->grant_table); diff -r 0845f66aa5d5 xen/arch/x86/time.c --- a/xen/arch/x86/time.c Thu Oct 13 15:47:46 2005 +0800 +++ b/xen/arch/x86/time.c Fri Oct 14 10:42:03 2005 +0800 @@ -592,6 +592,79 @@ )*60 + sec; /* finally seconds */ } +#define SECS_PER_HOUR (60 * 60) +#define SECS_PER_DAY (SECS_PER_HOUR * 24) +# define __isleap(year) \ + ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0)) +const unsigned short int __mon_yday[2][13] = +{ + /* Normal years. */ + { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }, + /* Leap years. */ + { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 } +}; + +/* Compute the `struct tm' representation of *T, + and store year, yday, mon, mday, wday, hour, min, sec into *TP. + Return nonzero if successful. */ +int +gmtime (unsigned int time, struct tm *tp) +{ + long int days, rem, y; + const unsigned short int *ip; + + days = time / SECS_PER_DAY; + rem = time % SECS_PER_DAY; + while (rem < 0) + { + rem += SECS_PER_DAY; + --days; + } + while (rem >= SECS_PER_DAY) + { + rem -= SECS_PER_DAY; + ++days; + } + tp->tm_hour = rem / SECS_PER_HOUR; + rem %= SECS_PER_HOUR; + tp->tm_min = rem / 60; + tp->tm_sec = rem % 60; + /* January 1, 1970 was a Thursday. */ + tp->tm_wday = (4 + days) % 7; + if (tp->tm_wday < 0) + tp->tm_wday += 7; + y = 1970; + +#define DIV(a, b) ((a) / (b) - ((a) % (b) < 0)) +#define LEAPS_THRU_END_OF(y) (DIV (y, 4) - DIV (y, 100) + DIV (y, 400)) + + while (days < 0 || days >= (__isleap (y) ? 366 : 365)) + { + /* Guess a corrected year, assuming 365 days per year. */ + long int yg = y + days / 365 - (days % 365 < 0); + + /* Adjust DAYS and Y to match the guessed year. */ + days -= ((yg - y) * 365 + + LEAPS_THRU_END_OF (yg - 1) + - LEAPS_THRU_END_OF (y - 1)); + y = yg; + } + tp->tm_year = y - 1900; + if (tp->tm_year != y - 1900) + { + /* The year cannot be represented due to overflow. */ + return 0; + } + tp->tm_yday = days; + ip = __mon_yday[__isleap(y)]; + for (y = 11; days < (long int) ip[y]; --y) + continue; + days -= ip[y]; + tp->tm_mon = y; + tp->tm_mday = days + 1; + return 1; +} + static unsigned long __get_cmos_time(void) { unsigned int year, mon, day, hour, min, sec; diff -r 0845f66aa5d5 xen/arch/x86/traps.c --- a/xen/arch/x86/traps.c Thu Oct 13 15:47:46 2005 +0800 +++ b/xen/arch/x86/traps.c Fri Oct 14 10:42:03 2005 +0800 @@ -785,8 +785,15 @@ case 0xe5: /* IN imm8,%eax */ port = insn_fetch(u8, 1, eip); exec_in: + if(v->domain->domain_id != 0) + { + if(emulate_io_read(port, op_bytes, v, regs)) + goto done; + } if ( !guest_io_okay(port, op_bytes, v, regs) ) + { goto fail; + } switch ( op_bytes ) { case 1: @@ -814,8 +821,15 @@ case 0xe7: /* OUT %eax,imm8 */ port = insn_fetch(u8, 1, eip); exec_out: + if(v->domain->domain_id != 0) + { + if(emulate_io_write(port, op_bytes, v, regs)) + goto done; + } if ( !guest_io_okay(port, op_bytes, v, regs) ) + { goto fail; + } switch ( op_bytes ) { case 1: diff -r 0845f66aa5d5 xen/include/asm-x86/domain.h --- a/xen/include/asm-x86/domain.h Thu Oct 13 15:47:46 2005 +0800 +++ b/xen/include/asm-x86/domain.h Fri Oct 14 10:42:03 2005 +0800 @@ -65,6 +65,7 @@ pagetable_t phys_table; /* guest 1:1 pagetable */ struct virtual_platform_def vmx_platform; + void *cmos; /* RTC emulation state*/ } __cacheline_aligned; struct arch_vcpu diff -r 0845f66aa5d5 xen/include/asm-x86/time.h --- a/xen/include/asm-x86/time.h Thu Oct 13 15:47:46 2005 +0800 +++ b/xen/include/asm-x86/time.h Fri Oct 14 10:42:03 2005 +0800 @@ -10,4 +10,19 @@ struct domain; extern void init_domain_time(struct domain *d); +struct tm +{ + int tm_sec; /* Seconds. [0-60] (1 leap second) */ + int tm_min; /* Minutes. [0-59] */ + int tm_hour; /* Hours. [0-23] */ + int tm_mday; /* Day. [1-31] */ + int tm_mon; /* Month. [0-11] */ + int tm_year; /* Year - 1900. */ + int tm_wday; /* Day of week. [0-6] */ + int tm_yday; /* Days in year.[0-365] */ + int tm_isdst; /* DST. [-1/0/1]*/ +}; + +int gmtime (unsigned int time, struct tm *tp); + #endif /* __X86_TIME_H__ */ diff -r 0845f66aa5d5 xen/include/asm-x86/x86_emulate.h --- a/xen/include/asm-x86/x86_emulate.h Thu Oct 13 15:47:46 2005 +0800 +++ b/xen/include/asm-x86/x86_emulate.h Fri Oct 14 10:42:03 2005 +0800 @@ -166,4 +166,14 @@ decode_register( u8 modrm_reg, struct cpu_user_regs *regs, int highbyte_regs); + +int emulate_io_write(unsigned int port, unsigned int bytes, + struct vcpu *v, struct cpu_user_regs *regs ); + +int emulate_io_read(unsigned int port, unsigned int bytes, + struct vcpu *v, struct cpu_user_regs *regs ); + +int rtc_emu_init(struct domain *d); +void rtc_emu_destroy(struct domain *d); + #endif /* __X86_EMULATE_H__ */ diff -r 0845f66aa5d5 xen/arch/x86/rtc_emu.c --- /dev/null Thu Oct 13 15:47:46 2005 +0800 +++ b/xen/arch/x86/rtc_emu.c Fri Oct 14 10:42:03 2005 +0800 @@ -0,0 +1,276 @@ +/* + * MC146818 RTC emulation for xenlinux + * + * Copyright (c) 2003-2004 Fabrice Bellard + * Copyright (c) 2005 Intel Corporation + * + * 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 +#include +#include +#include + +#define DEBUG_CMOS 0 +#define USE_CMOS_TIME 0 + + +typedef struct tagRTCState { + unsigned char cmos_data[128]; + unsigned char cmos_index; + struct ac_timer timer; + struct domain *domain; + int irq; + s_time_t update_time; + struct tm current_tm; + +} RTCState; + +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 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); +} + +void update_cmos_date(void *opaque) +{ + RTCState *s = opaque; + struct domain *d = s->domain; + unsigned int wtime; + + wtime = d->shared_info->vcpu_time[0].system_time/1000000000LL; + wtime += d->shared_info->wc_sec; + + gmtime(wtime, &s->current_tm); +#if DEBUG_CMOS + printk("%x ", d->shared_info->wc_sec); + printk("%d-%d-%d %d:%d:%d\n", 1900+tm->tm_year, tm->tm_mon, tm->tm_mday, + tm->tm_hour, tm->tm_min, tm->tm_sec); +#endif + + rtc_copy_date(s); +} + +/* + * This is simple emulation, and only update interrupt is emulated + */ +static void rtc_timer_fn(void *opaque) +{ + RTCState *s = opaque; + struct domain *d = s->domain; + + if (!(s->cmos_data[RTC_REG_B] & RTC_SET)) { + update_cmos_date(s); + } + + /* update ended interrupt */ + if (s->cmos_data[RTC_REG_B] & RTC_UIE) { + s->cmos_data[RTC_REG_C] |= 0x90; +#if DEBUG_CMOS + printk("send rtc update interrupt, irq:%d\n", s->irq); +#endif + send_guest_pirq(d, s->irq); + } + + /* clear update in progress bit */ + s->cmos_data[RTC_REG_A] &= ~RTC_UIP; + + set_ac_timer(&s->timer, NOW() + s->update_time); +} + +int rtc_emu_init(struct domain *d) +{ + RTCState *s; + + if(d->domain_id == 0) + return 1; + s = xmalloc(RTCState); + if (!s) + { + printk("xmalloc error for rtc emulation\n"); + return 0; + } + memset(s, 0, sizeof(RTCState)); + d->arch.cmos = s; + 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->domain = d; + s->irq = RTC_IRQ; + s->update_time = SECONDS(1); + init_ac_timer(&s->timer, rtc_timer_fn, s, d->vcpu[0]->processor); + set_ac_timer(&s->timer, NOW() + s->update_time); + update_cmos_date(s); + return 1; +} + +void rtc_emu_destroy(struct domain *d) +{ + if(d->arch.cmos) + xfree(d->arch.cmos); + return; +} + +static uint32_t cmos_ioport_read(void *opaque, uint32_t addr) +{ + RTCState *s = opaque; + 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: +#if USE_CMOS_TIME + ret = CMOS_READ(s->cmos_index); +#else + ret = s->cmos_data[s->cmos_index]; +#endif + break; + default: + ret = s->cmos_data[s->cmos_index]; + break; + } +#if DEBUG_CMOS + printf("cmos: read index=0x%02x val=0x%02x\n", + s->cmos_index, ret); +#endif + return ret; + } +} + +/* all write are ignored except the control register for UIE open/close*/ +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 + { + switch(s->cmos_index) { + case RTC_CONTROL: + s->cmos_data[s->cmos_index] = data; + break; + default: + break; + } +#if DEBUG_CMOS + printf("cmos: write index=0x%02x val=0x%02x\n", + s->cmos_index, data); +#endif + + } +} + +/* + * if more device need to emulate, The following can be put into another file + * and use a more generic and effective register/handle framework + */ +int emulate_io_read(unsigned int port, unsigned int bytes, + struct vcpu *v, struct cpu_user_regs *regs ) +{ + struct domain *d = v->domain; + switch(port) + { + case RTC_PORT(0): + case RTC_PORT(1): + switch ( bytes ) + { + case 1: + regs->eax &= ~0xffUL; + regs->eax |= (u8)cmos_ioport_read(d->arch.cmos, port); + break; + case 2: + regs->eax &= ~0xffffUL; + regs->eax |= (u16)cmos_ioport_read(d->arch.cmos, port); + break; + case 4: + regs->eax = (u32)cmos_ioport_read(d->arch.cmos, port); + break; + default: + printk("strange op bytes!\n"); + break; + } + return 1; + } + return 0; +} + +int emulate_io_write(unsigned int port, unsigned int bytes, + struct vcpu *v, struct cpu_user_regs *regs ) +{ + + struct domain *d = v->domain; + switch(port) + { + case RTC_PORT(0): + case RTC_PORT(1): + switch ( bytes ) + { + case 1: + cmos_ioport_write(d->arch.cmos, port, (u8)regs->eax); + break; + case 2: + cmos_ioport_write(d->arch.cmos, port, (u16)regs->eax); + break; + case 4: + cmos_ioport_write(d->arch.cmos, port, (u32)regs->eax); + break; + default: + printk("strange op bytes!\n"); + break; + } + return 1; + } + return 0; +} +