diff mbox

[5/5] Enable host-clock-based RTC

Message ID 20090909151112.26816.52077.stgit@mchn012c.ww002.siemens.net
State Superseded
Headers show

Commit Message

Jan Kiszka Sept. 9, 2009, 3:11 p.m. UTC
Allow RTC emulations to use the new host_clock instead of vm_clock. This
has the advantage that the emulated RTC will follow automatically the
host time while it might be tuned via NTP.

Note that some RTC emulations (at least M48T59) already use the host
time unconditionally while others (namely MC146818) do not. This patch
introduces the required infrastructure for selecting the base clock and
only converts MC146818 for now.

Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
---

 hw/mc146818rtc.c |   35 ++++++++++++++++-------------------
 qemu-options.hx  |   14 ++++++++++----
 sysemu.h         |    1 +
 vl.c             |   15 ++++++++++++++-
 4 files changed, 41 insertions(+), 24 deletions(-)
diff mbox

Patch

diff --git a/hw/mc146818rtc.c b/hw/mc146818rtc.c
index 5c8676e..d339b7a 100644
--- a/hw/mc146818rtc.c
+++ b/hw/mc146818rtc.c
@@ -107,8 +107,8 @@  static void rtc_coalesced_timer_update(RTCState *s)
     } else {
         /* divide each RTC interval to 2 - 8 smaller intervals */
         int c = MIN(s->irq_coalesced, 7) + 1; 
-        int64_t next_clock = qemu_get_clock(vm_clock) +
-		muldiv64(s->period / c, ticks_per_sec, 32768);
+        int64_t next_clock = qemu_get_clock(rtc_clock) +
+            muldiv64(s->period / c, ticks_per_sec, 32768);
         qemu_mod_timer(s->coalesced_timer, next_clock);
     }
 }
@@ -231,7 +231,7 @@  static void cmos_ioport_write(void *opaque, uint32_t addr, uint32_t data)
             /* UIP bit is read only */
             s->cmos_data[RTC_REG_A] = (data & ~REG_A_UIP) |
                 (s->cmos_data[RTC_REG_A] & REG_A_UIP);
-            rtc_timer_update(s, qemu_get_clock(vm_clock));
+            rtc_timer_update(s, qemu_get_clock(rtc_clock));
             break;
         case RTC_REG_B:
             if (data & REG_B_SET) {
@@ -245,7 +245,7 @@  static void cmos_ioport_write(void *opaque, uint32_t addr, uint32_t data)
                 }
             }
             s->cmos_data[RTC_REG_B] = data;
-            rtc_timer_update(s, qemu_get_clock(vm_clock));
+            rtc_timer_update(s, qemu_get_clock(rtc_clock));
             break;
         case RTC_REG_C:
         case RTC_REG_D:
@@ -604,18 +604,17 @@  RTCState *rtc_init_sqw(int base, qemu_irq irq, qemu_irq sqw_irq, int base_year)
     s->base_year = base_year;
     rtc_set_date_from_host(s);
 
-    s->periodic_timer = qemu_new_timer(vm_clock,
-                                       rtc_periodic_timer, s);
+    s->periodic_timer = qemu_new_timer(rtc_clock, rtc_periodic_timer, s);
 #ifdef TARGET_I386
     if (rtc_td_hack)
-        s->coalesced_timer = qemu_new_timer(vm_clock, rtc_coalesced_timer, s);
+        s->coalesced_timer =
+            qemu_new_timer(rtc_clock, rtc_coalesced_timer, s);
 #endif
-    s->second_timer = qemu_new_timer(vm_clock,
-                                     rtc_update_second, s);
-    s->second_timer2 = qemu_new_timer(vm_clock,
-                                      rtc_update_second2, s);
+    s->second_timer = qemu_new_timer(rtc_clock, rtc_update_second, s);
+    s->second_timer2 = qemu_new_timer(rtc_clock, rtc_update_second2, s);
 
-    s->next_second_time = qemu_get_clock(vm_clock) + (ticks_per_sec * 99) / 100;
+    s->next_second_time =
+        qemu_get_clock(rtc_clock) + (ticks_per_sec * 99) / 100;
     qemu_mod_timer(s->second_timer2, s->next_second_time);
 
     register_ioport_write(base, 2, 1, cmos_ioport_write, s);
@@ -725,14 +724,12 @@  RTCState *rtc_mm_init(target_phys_addr_t base, int it_shift, qemu_irq irq,
     s->base_year = base_year;
     rtc_set_date_from_host(s);
 
-    s->periodic_timer = qemu_new_timer(vm_clock,
-                                       rtc_periodic_timer, s);
-    s->second_timer = qemu_new_timer(vm_clock,
-                                     rtc_update_second, s);
-    s->second_timer2 = qemu_new_timer(vm_clock,
-                                      rtc_update_second2, s);
+    s->periodic_timer = qemu_new_timer(rtc_clock, rtc_periodic_timer, s);
+    s->second_timer = qemu_new_timer(rtc_clock, rtc_update_second, s);
+    s->second_timer2 = qemu_new_timer(rtc_clock, rtc_update_second2, s);
 
-    s->next_second_time = qemu_get_clock(vm_clock) + (ticks_per_sec * 99) / 100;
+    s->next_second_time =
+        qemu_get_clock(rtc_clock) + (ticks_per_sec * 99) / 100;
     qemu_mod_timer(s->second_timer2, s->next_second_time);
 
     io_memory = cpu_register_io_memory(rtc_mm_read, rtc_mm_write, s);
diff --git a/qemu-options.hx b/qemu-options.hx
index ea672ee..253180b 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -1498,22 +1498,28 @@  DEF("startdate", HAS_ARG, QEMU_OPTION_startdate, "")
 
 #ifdef TARGET_I386
 DEF("rtc", HAS_ARG, QEMU_OPTION_rtc, \
-    "-rtc [base=utc|localtime|date][,drift-fix=on|off]\n" \
-    "                Set the RTC base, enable Windows time drift fix\n")
+    "-rtc [base=utc|localtime|date][,clock=vm|host][,drift-fix=on|off]\n" \
+    "                Set the RTC base and clock, enable Windows time drift fix\n")
 #else
 DEF("rtc", HAS_ARG, QEMU_OPTION_rtc, \
-    "-rtc [base=utc|localtime|date]\n" \
+    "-rtc [base=utc|localtime|date][,clock=vm|host]\n" \
     "                Set the RTC base and clock\n")
 #endif
 
 STEXI
 
-@item -rtc [base=utc|localtime|@var{date}][,drift-fix=on|off]
+@item -rtc [base=utc|localtime|@var{date}][,clock=vm|rt][,drift-fix=on|off]
 Specify @option{base} as @code{utc} or @code{localtime} to let the RTC start at the current
 UTC or local time, respectively. @code{localtime} is required for correct date in
 MS-DOS or Windows. To start at a specific point in time, provide @var{date} in the
 format @code{2006-06-17T16:01:21} or @code{2006-06-17}. The default base is UTC.
 
+By default the RTC is driven by a virtual clock (@code{vm}) which is not subject
+to host time adjustments and does not jump when the guest is supended and
+resumed or migrated. To use the host's system time instead, set @option{clock} to
+@code{host}. This is useful if the host time is smoothly following an accurate
+reference clock, e.g. via NTP, and wants to propagate this into the guest.
+
 Enable @option{drift-fix} (i386 targets only) if you experience time drift problems
 in Windows with ACPI HAL. This option will try to figure out how many timer
 interrupts were not processed by the Windows guest and will re-inject them.
diff --git a/sysemu.h b/sysemu.h
index a018b47..fe4eb53 100644
--- a/sysemu.h
+++ b/sysemu.h
@@ -135,6 +135,7 @@  extern int no_quit;
 extern int semihosting_enabled;
 extern int old_param;
 extern int boot_menu;
+extern QEMUClock *rtc_clock;
 
 #define MAX_NODES 64
 extern int nb_numa_nodes;
diff --git a/vl.c b/vl.c
index 8437d2c..52bfefe 100644
--- a/vl.c
+++ b/vl.c
@@ -194,6 +194,7 @@  int vm_running;
 int autostart;
 static int rtc_utc = 1;
 static int rtc_date_offset = -1; /* -1 means no change */
+QEMUClock *rtc_clock;
 int vga_interface_type = VGA_CIRRUS;
 #ifdef TARGET_SPARC
 int graphic_width = 1024;
@@ -1051,6 +1052,8 @@  static void init_clocks(void)
     rt_clock = qemu_new_clock(QEMU_CLOCK_REALTIME);
     vm_clock = qemu_new_clock(QEMU_CLOCK_VIRTUAL);
     host_clock = qemu_new_clock(QEMU_CLOCK_HOST);
+
+    rtc_clock = vm_clock;
 }
 
 /* save a timer */
@@ -1630,7 +1633,7 @@  static void configure_rtc_date_offset(const char *startdate, int legacy)
 static void configure_rtc(const char *options)
 {
     static const char * const params[] = {
-        "base",
+        "base", "clock",
 #ifdef CONFIG_TARGET_I386
         "td-hack",
 #endif
@@ -1652,6 +1655,16 @@  static void configure_rtc(const char *options)
             configure_rtc_date_offset(buf, 0);
         }
     }
+    if (get_param_value(buf, sizeof(buf), "clock", options)) {
+        if (!strcmp(buf, "vm")) {
+            rtc_clock = vm_clock;
+        } else if (!strcmp(buf, "host")) {
+            rtc_clock = host_clock;
+        } else {
+            fprintf(stderr, "qemu: invalid option value '%s'\n", buf);
+            exit(1);
+        }
+    }
 #ifdef CONFIG_TARGET_I386
     if (get_param_value(buf, sizeof(buf), "drift-hack", options)) {
         if (!strcmp(buf, "on")) {