[PATCH]rtc emulation for xenU

classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|

[PATCH]rtc emulation for xenU

Ling, Xiaofeng
There's an issue on x86_64 whereby booting an unpriv guest using a
kernel that has the RTC driver compiled in hangs when the rc scripts run
the command hwclock. The patch attached resolves the issue.
The patch adds a simple rtc emulation and also on i386, hwclock can get
correct date when xenU enabled pirq binding, without this patch, hwclock
read will timeout because of lack of update interrupt.

Signed-off-by: Xiaofeng Ling <[hidden email]>
Signed-off-by: Jun Nakajima <[hidden email]>

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 <xen/kernel.h>
 #include <public/io/ioreq.h>
 #include <xen/multicall.h>
+#include <asm/x86_emulate.h>
 
 /* 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 <xen/sched.h>
+#include <xen/xmalloc.h>
+#include <asm/mc146818rtc.h>
+#include <xen/event.h>
+
+#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;
+}
+

_______________________________________________
Xen-devel mailing list
[hidden email]
http://lists.xensource.com/xen-devel