@@ -44,6 +44,8 @@
# define DPRINTF_C(format, ...) do { } while (0)
#endif
+#define USEC_PER_SEC 1000000
+
#define RTC_REINJECT_ON_ACK_COUNT 20
#define RTC_SECONDS 0
@@ -85,6 +87,8 @@ typedef struct RTCState {
uint8_t cmos_data[128];
uint8_t cmos_index;
struct tm current_tm;
+ int64_t offset_sec;
+ int32_t offset_usec;
int32_t base_year;
qemu_irq irq;
qemu_irq sqw_irq;
@@ -92,20 +96,15 @@ typedef struct RTCState {
/* periodic timer */
QEMUTimer *periodic_timer;
int64_t next_periodic_time;
- /* second update */
- int64_t next_second_time;
uint16_t irq_reinject_on_ack_count;
uint32_t irq_coalesced;
uint32_t period;
QEMUTimer *coalesced_timer;
- QEMUTimer *second_timer;
- QEMUTimer *second_timer2;
Notifier clock_reset_notifier;
LostTickPolicy lost_tick_policy;
} RTCState;
static void rtc_set_time(RTCState *s);
-static void rtc_copy_date(RTCState *s);
#ifdef TARGET_I386
static void rtc_coalesced_timer_update(RTCState *s)
@@ -206,6 +205,24 @@ static void rtc_periodic_timer(void *opaque)
}
}
+static void rtc_set_offset(RTCState *s)
+{
+ struct tm *tm = &s->current_tm;
+ struct timeval tv_now;
+ int64_t host_usec, guest_sec, guest_usec;
+
+ gettimeofday(&tv_now, NULL);
+ host_usec = tv_now.tv_sec * USEC_PER_SEC + tv_now.tv_usec;
+
+ guest_sec = mktimegm(tm);
+ /* after setting the clock, the next second tick
+ * will occur in exactly 500 ms*/
+ guest_usec = guest_sec * USEC_PER_SEC + 500000;
+
+ s->offset_sec = (guest_usec - host_usec) / USEC_PER_SEC;
+ s->offset_usec = (guest_usec - host_usec) % USEC_PER_SEC;
+}
+
static void cmos_ioport_write(void *opaque, uint32_t addr, uint32_t data)
{
RTCState *s = opaque;
@@ -232,6 +249,7 @@ static void cmos_ioport_write(void *opaque, uint32_t addr, uint32_t data)
/* if in set mode, do not update the time */
if (!(s->cmos_data[RTC_REG_B] & REG_B_SET)) {
rtc_set_time(s);
+ rtc_set_offset(s);
}
break;
case RTC_REG_A:
@@ -249,6 +267,7 @@ static void cmos_ioport_write(void *opaque, uint32_t addr, uint32_t data)
/* if disabling set mode, update the time */
if (s->cmos_data[RTC_REG_B] & REG_B_SET) {
rtc_set_time(s);
+ rtc_set_offset(s);
}
}
if (((s->cmos_data[RTC_REG_B] ^ data) & (REG_B_DM | REG_B_24H)) &&
@@ -256,7 +275,7 @@ static void cmos_ioport_write(void *opaque, uint32_t addr, uint32_t data)
/* If the time format has changed and not in set mode,
update the registers immediately. */
s->cmos_data[RTC_REG_B] = data;
- rtc_copy_date(s);
+ rtc_set_offset(s);
} else {
s->cmos_data[RTC_REG_B] = data;
}
@@ -312,10 +331,10 @@ static void rtc_set_time(RTCState *s)
rtc_change_mon_event(tm);
}
-static void rtc_copy_date(RTCState *s)
+static void rtc_set_cmos(RTCState *s)
{
- const struct tm *tm = &s->current_tm;
int year;
+ struct tm *tm = &s->current_tm;
s->cmos_data[RTC_SECONDS] = rtc_to_bcd(s, tm->tm_sec);
s->cmos_data[RTC_MINUTES] = rtc_to_bcd(s, tm->tm_min);
@@ -338,121 +357,22 @@ static void rtc_copy_date(RTCState *s)
s->cmos_data[RTC_YEAR] = rtc_to_bcd(s, year);
}
-/* 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;
- int64_t delay;
-
- /* 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 += get_ticks_per_sec();
- qemu_mod_timer(s->second_timer, s->next_second_time);
- } else {
- rtc_next_second(&s->current_tm);
-
- if (!(s->cmos_data[RTC_REG_B] & REG_B_SET)) {
- /* update in progress bit */
- s->cmos_data[RTC_REG_A] |= REG_A_UIP;
- }
- /* should be 244 us = 8 / 32768 seconds, but currently the
- timers do not have the necessary resolution. */
- delay = (get_ticks_per_sec() * 1) / 100;
- if (delay < 1)
- delay = 1;
- qemu_mod_timer(s->second_timer2,
- s->next_second_time + delay);
- }
-}
-
-static void rtc_update_second2(void *opaque)
+static void rtc_calibrate_time(RTCState *s)
{
- RTCState *s = opaque;
+ struct timeval tv_now;
+ struct tm *ret;
+ time_t guest_sec;
+ int64_t host_usec, offset_usec, guest_usec;
- if (!(s->cmos_data[RTC_REG_B] & REG_B_SET)) {
- rtc_copy_date(s);
- }
+ gettimeofday(&tv_now, NULL);
+ host_usec = tv_now.tv_sec * USEC_PER_SEC + tv_now.tv_usec;
+ offset_usec = s->offset_sec * USEC_PER_SEC + s->offset_usec;
+ guest_usec = host_usec + offset_usec;
- /* check alarm */
- if (((s->cmos_data[RTC_SECONDS_ALARM] & 0xc0) == 0xc0 ||
- rtc_from_bcd(s, s->cmos_data[RTC_SECONDS_ALARM]) == s->current_tm.tm_sec) &&
- ((s->cmos_data[RTC_MINUTES_ALARM] & 0xc0) == 0xc0 ||
- rtc_from_bcd(s, s->cmos_data[RTC_MINUTES_ALARM]) == s->current_tm.tm_min) &&
- ((s->cmos_data[RTC_HOURS_ALARM] & 0xc0) == 0xc0 ||
- rtc_from_bcd(s, s->cmos_data[RTC_HOURS_ALARM]) == s->current_tm.tm_hour)) {
+ guest_sec = guest_usec / USEC_PER_SEC;
+ ret = gmtime(&guest_sec);
- s->cmos_data[RTC_REG_C] |= REG_C_AF;
- if (s->cmos_data[RTC_REG_B] & REG_B_AIE) {
- qemu_irq_raise(s->irq);
- s->cmos_data[RTC_REG_C] |= REG_C_IRQF;
- }
- }
-
- /* update ended interrupt */
- s->cmos_data[RTC_REG_C] |= REG_C_UF;
- if (s->cmos_data[RTC_REG_B] & REG_B_UIE) {
- s->cmos_data[RTC_REG_C] |= REG_C_IRQF;
- qemu_irq_raise(s->irq);
- }
-
- /* clear update in progress bit */
- s->cmos_data[RTC_REG_A] &= ~REG_A_UIP;
-
- s->next_second_time += get_ticks_per_sec();
- qemu_mod_timer(s->second_timer, s->next_second_time);
+ s->current_tm = *ret;
}
static uint32_t cmos_ioport_read(void *opaque, uint32_t addr)
@@ -470,6 +390,8 @@ static uint32_t cmos_ioport_read(void *opaque, uint32_t addr)
case RTC_DAY_OF_MONTH:
case RTC_MONTH:
case RTC_YEAR:
+ rtc_calibrate_time(s);
+ rtc_set_cmos(s);
ret = s->cmos_data[s->cmos_index];
break;
case RTC_REG_A:
@@ -513,13 +435,6 @@ void rtc_set_memory(ISADevice *dev, int addr, int val)
s->cmos_data[addr] = val;
}
-void rtc_set_date(ISADevice *dev, const struct tm *tm)
-{
- RTCState *s = DO_UPCAST(RTCState, dev, dev);
- s->current_tm = *tm;
- rtc_copy_date(s);
-}
-
/* PC cmos mappings */
#define REG_IBM_CENTURY_BYTE 0x32
#define REG_IBM_PS2_CENTURY_BYTE 0x37
@@ -530,9 +445,14 @@ static void rtc_set_date_from_host(ISADevice *dev)
struct tm tm;
int val;
- /* set the CMOS date */
qemu_get_timedate(&tm, 0);
- rtc_set_date(dev, &tm);
+
+ s->offset_sec = mktimegm(&tm) - time(NULL);
+ s->offset_usec = 0;
+
+ /* set the CMOS date */
+ s->current_tm = tm;
+ rtc_set_cmos(s);
val = rtc_to_bcd(s, (tm.tm_year / 100) + 19);
rtc_set_memory(dev, REG_IBM_CENTURY_BYTE, val);
@@ -555,9 +475,9 @@ static int rtc_post_load(void *opaque, int version_id)
static const VMStateDescription vmstate_rtc = {
.name = "mc146818rtc",
- .version_id = 2,
- .minimum_version_id = 1,
- .minimum_version_id_old = 1,
+ .version_id = 3,
+ .minimum_version_id = 3,
+ .minimum_version_id_old = 3,
.post_load = rtc_post_load,
.fields = (VMStateField []) {
VMSTATE_BUFFER(cmos_data, RTCState),
@@ -569,11 +489,10 @@ static const VMStateDescription vmstate_rtc = {
VMSTATE_INT32(current_tm.tm_mday, RTCState),
VMSTATE_INT32(current_tm.tm_mon, RTCState),
VMSTATE_INT32(current_tm.tm_year, RTCState),
+ VMSTATE_INT64(offset_sec, RTCState),
+ VMSTATE_INT32(offset_usec, RTCState),
VMSTATE_TIMER(periodic_timer, RTCState),
VMSTATE_INT64(next_periodic_time, RTCState),
- VMSTATE_INT64(next_second_time, RTCState),
- VMSTATE_TIMER(second_timer, RTCState),
- VMSTATE_TIMER(second_timer2, RTCState),
VMSTATE_UINT32_V(irq_coalesced, RTCState, 2),
VMSTATE_UINT32_V(period, RTCState, 2),
VMSTATE_END_OF_LIST()
@@ -586,8 +505,6 @@ static void rtc_notify_clock_reset(Notifier *notifier, void *data)
int64_t now = *(int64_t *)data;
rtc_set_date_from_host(&s->dev);
- s->next_second_time = now + (get_ticks_per_sec() * 99) / 100;
- qemu_mod_timer(s->second_timer2, s->next_second_time);
rtc_timer_update(s, now);
#ifdef TARGET_I386
if (s->lost_tick_policy == LOST_TICK_SLEW) {
@@ -634,6 +551,8 @@ static void rtc_get_date(Object *obj, Visitor *v, void *opaque,
ISADevice *isa = ISA_DEVICE(obj);
RTCState *s = DO_UPCAST(RTCState, dev, isa);
+ rtc_calibrate_time(s);
+ rtc_set_cmos(s);
visit_start_struct(v, NULL, "struct tm", name, 0, errp);
visit_type_int32(v, &s->current_tm.tm_year, "tm_year", errp);
visit_type_int32(v, &s->current_tm.tm_mon, "tm_mon", errp);
@@ -670,20 +589,14 @@ static int rtc_initfn(ISADevice *dev)
#endif
s->periodic_timer = qemu_new_timer_ns(rtc_clock, rtc_periodic_timer, s);
- s->second_timer = qemu_new_timer_ns(rtc_clock, rtc_update_second, s);
- s->second_timer2 = qemu_new_timer_ns(rtc_clock, rtc_update_second2, s);
s->clock_reset_notifier.notify = rtc_notify_clock_reset;
qemu_register_clock_reset_notifier(rtc_clock, &s->clock_reset_notifier);
- s->next_second_time =
- qemu_get_clock_ns(rtc_clock) + (get_ticks_per_sec() * 99) / 100;
- qemu_mod_timer(s->second_timer2, s->next_second_time);
-
memory_region_init_io(&s->io, &cmos_ops, s, "rtc", 2);
isa_register_ioport(dev, &s->io, base);
- qdev_set_legacy_instance_id(&dev->qdev, base, 2);
+ qdev_set_legacy_instance_id(&dev->qdev, base, 3);
qemu_register_reset(rtc_reset, s);
object_property_add(OBJECT(s), "date", "struct tm",
There has no need to use two periodic timer to update RTC time. In this patch, we only update it when guest reading it. Signed-off-by: Yang Zhang <yang.z.zhang@intel.com> --- hw/mc146818rtc.c | 199 +++++++++++++++--------------------------------------- 1 files changed, 56 insertions(+), 143 deletions(-) -- 1.7.1