* [Qemu-devel] [PATCH 1/7] RTC: Remove the logic to update time format when DM bit changed
2012-07-20 10:53 [Qemu-devel] [PATCH 0/7] Remove periodic wakeup from RTC timer Paolo Bonzini
@ 2012-07-20 10:53 ` Paolo Bonzini
2012-07-20 10:53 ` [Qemu-devel] [PATCH 2/7] RTC: Rename rtc_timer_update Paolo Bonzini
` (5 subsequent siblings)
6 siblings, 0 replies; 14+ messages in thread
From: Paolo Bonzini @ 2012-07-20 10:53 UTC (permalink / raw)
To: qemu-devel; +Cc: Zhang, Yang Z
From: "Zhang, Yang Z" <yang.z.zhang@intel.com>
Changing the DM (binary/BCD) and 24/12 control bit doesn't affect the internal
registers. It only indicates what format is used for those registers.
Signed-off-by: Yang Zhang <yang.z.zhang@intel.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
hw/mc146818rtc.c | 10 +---------
1 file changed, 1 insertion(+), 9 deletions(-)
diff --git a/hw/mc146818rtc.c b/hw/mc146818rtc.c
index 9c64e0a..1ccfb50 100644
--- a/hw/mc146818rtc.c
+++ b/hw/mc146818rtc.c
@@ -220,15 +220,7 @@ static void cmos_ioport_write(void *opaque, uint32_t addr, uint32_t data)
rtc_set_time(s);
}
}
- if (((s->cmos_data[RTC_REG_B] ^ data) & (REG_B_DM | REG_B_24H)) &&
- !(data & REG_B_SET)) {
- /* 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);
- } else {
- s->cmos_data[RTC_REG_B] = data;
- }
+ s->cmos_data[RTC_REG_B] = data;
rtc_timer_update(s, qemu_get_clock_ns(rtc_clock));
break;
case RTC_REG_C:
--
1.7.10.2
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [Qemu-devel] [PATCH 2/7] RTC: Rename rtc_timer_update
2012-07-20 10:53 [Qemu-devel] [PATCH 0/7] Remove periodic wakeup from RTC timer Paolo Bonzini
2012-07-20 10:53 ` [Qemu-devel] [PATCH 1/7] RTC: Remove the logic to update time format when DM bit changed Paolo Bonzini
@ 2012-07-20 10:53 ` Paolo Bonzini
2012-07-20 10:53 ` [Qemu-devel] [PATCH 3/7] RTC: Update interrupt state when interrupts are masked/unmasked Paolo Bonzini
` (4 subsequent siblings)
6 siblings, 0 replies; 14+ messages in thread
From: Paolo Bonzini @ 2012-07-20 10:53 UTC (permalink / raw)
To: qemu-devel; +Cc: Zhang, Yang Z
From: "Zhang, Yang Z" <yang.z.zhang@intel.com>
Signed-off-by: Yang Zhang <yang.z.zhang@intel.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
hw/mc146818rtc.c | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/hw/mc146818rtc.c b/hw/mc146818rtc.c
index 1ccfb50..6a7dcb8 100644
--- a/hw/mc146818rtc.c
+++ b/hw/mc146818rtc.c
@@ -110,7 +110,7 @@ static void rtc_coalesced_timer(void *opaque)
}
#endif
-static void rtc_timer_update(RTCState *s, int64_t current_time)
+static void periodic_timer_update(RTCState *s, int64_t current_time)
{
int period_code, period;
int64_t cur_clock, next_irq_clock;
@@ -148,7 +148,7 @@ static void rtc_periodic_timer(void *opaque)
{
RTCState *s = opaque;
- rtc_timer_update(s, s->next_periodic_time);
+ periodic_timer_update(s, s->next_periodic_time);
s->cmos_data[RTC_REG_C] |= REG_C_PF;
if (s->cmos_data[RTC_REG_B] & REG_B_PIE) {
s->cmos_data[RTC_REG_C] |= REG_C_IRQF;
@@ -207,7 +207,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_ns(rtc_clock));
+ periodic_timer_update(s, qemu_get_clock_ns(rtc_clock));
break;
case RTC_REG_B:
if (data & REG_B_SET) {
@@ -221,7 +221,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_ns(rtc_clock));
+ periodic_timer_update(s, qemu_get_clock_ns(rtc_clock));
break;
case RTC_REG_C:
case RTC_REG_D:
@@ -550,7 +550,7 @@ static void rtc_notify_clock_reset(Notifier *notifier, void *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);
+ periodic_timer_update(s, now);
#ifdef TARGET_I386
if (s->lost_tick_policy == LOST_TICK_SLEW) {
rtc_coalesced_timer_update(s);
--
1.7.10.2
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [Qemu-devel] [PATCH 3/7] RTC: Update interrupt state when interrupts are masked/unmasked
2012-07-20 10:53 [Qemu-devel] [PATCH 0/7] Remove periodic wakeup from RTC timer Paolo Bonzini
2012-07-20 10:53 ` [Qemu-devel] [PATCH 1/7] RTC: Remove the logic to update time format when DM bit changed Paolo Bonzini
2012-07-20 10:53 ` [Qemu-devel] [PATCH 2/7] RTC: Rename rtc_timer_update Paolo Bonzini
@ 2012-07-20 10:53 ` Paolo Bonzini
2012-07-20 10:53 ` [Qemu-devel] [PATCH 4/7] RTC: Update the RTC clock only when reading it Paolo Bonzini
` (3 subsequent siblings)
6 siblings, 0 replies; 14+ messages in thread
From: Paolo Bonzini @ 2012-07-20 10:53 UTC (permalink / raw)
To: qemu-devel; +Cc: Zhang, Yang Z
From: "Zhang, Yang Z" <yang.z.zhang@intel.com>
If an interrupt flag is already set when the interrupt becomes enabled,
raise an interrupt immediately, and vice versa if interrupts become
disabled.
Signed-off-by: Yang Zhang <yang.z.zhang@intel.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
hw/mc146818rtc.c | 9 +++++++++
hw/mc146818rtc_regs.h | 1 +
2 files changed, 10 insertions(+)
diff --git a/hw/mc146818rtc.c b/hw/mc146818rtc.c
index 6a7dcb8..46f0c10 100644
--- a/hw/mc146818rtc.c
+++ b/hw/mc146818rtc.c
@@ -220,6 +220,15 @@ static void cmos_ioport_write(void *opaque, uint32_t addr, uint32_t data)
rtc_set_time(s);
}
}
+ /* if an interrupt flag is already set when the interrupt
+ * becomes enabled, raise an interrupt immediately. */
+ if (data & s->cmos_data[RTC_REG_C] & REG_C_MASK) {
+ s->cmos_data[RTC_REG_C] |= REG_C_IRQF;
+ qemu_irq_raise(s->irq);
+ } else {
+ s->cmos_data[RTC_REG_C] &= ~REG_C_IRQF;
+ qemu_irq_lower(s->irq);
+ }
s->cmos_data[RTC_REG_B] = data;
periodic_timer_update(s, qemu_get_clock_ns(rtc_clock));
break;
diff --git a/hw/mc146818rtc_regs.h b/hw/mc146818rtc_regs.h
index 3ab3770..fc10076 100644
--- a/hw/mc146818rtc_regs.h
+++ b/hw/mc146818rtc_regs.h
@@ -58,5 +58,6 @@
#define REG_C_IRQF 0x80
#define REG_C_PF 0x40
#define REG_C_AF 0x20
+#define REG_C_MASK 0x70
#endif
--
1.7.10.2
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [Qemu-devel] [PATCH 4/7] RTC: Update the RTC clock only when reading it
2012-07-20 10:53 [Qemu-devel] [PATCH 0/7] Remove periodic wakeup from RTC timer Paolo Bonzini
` (2 preceding siblings ...)
2012-07-20 10:53 ` [Qemu-devel] [PATCH 3/7] RTC: Update interrupt state when interrupts are masked/unmasked Paolo Bonzini
@ 2012-07-20 10:53 ` Paolo Bonzini
2012-07-23 5:17 ` Juan Quintela
2012-07-20 10:53 ` [Qemu-devel] [PATCH 5/7] RTC: Add divider reset support Paolo Bonzini
` (2 subsequent siblings)
6 siblings, 1 reply; 14+ messages in thread
From: Paolo Bonzini @ 2012-07-20 10:53 UTC (permalink / raw)
To: qemu-devel; +Cc: Zhang, Yang Z
From: "Zhang, Yang Z" <yang.z.zhang@intel.com>
Calculate guest RTC based on the time of the last update, instead of
using timers. The formula is
(base_rtc + guest_time_now - guest_time_last_update + offset)
Base_rtc is the RTC value when the RTC was last updated.
Guest_time_now is the guest time when the access happens.
Guest_time_last_update was the guest time when the RTC was last updated.
Offset is used when divider reset happens or the set bit is toggled.
The timer is kept in order to signal interrupts, but it only needs to
run when either UF or AF is cleared. When the bits are both set, the
timer does not run.
UIP is now synthesized when reading register A. If the timer is not set,
or if there is more than one second before it (as is the case at the
end of this series), the leading edge of UIP is computed and the rising
edge occurs 220us later. If the update timer occurs within one second,
however, the rising edge of the AF and UF bits should coincide withe
the falling edge of UIP. We do not know exactly when this will happen
because there could be delays in the servicing of the timer. Hence, in
this case reading register A only computes for the rising edge of UIP,
and latches the bit until the timer is fired and clears it.
Signed-off-by: Yang Zhang <yang.z.zhang@intel.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
hw/mc146818rtc.c | 315 +++++++++++++++++++++++++++++++-----------------------
1 file changed, 183 insertions(+), 132 deletions(-)
diff --git a/hw/mc146818rtc.c b/hw/mc146818rtc.c
index 46f0c10..3a99605 100644
--- a/hw/mc146818rtc.c
+++ b/hw/mc146818rtc.c
@@ -45,6 +45,8 @@
# define DPRINTF_C(format, ...) do { } while (0)
#endif
+#define NSEC_PER_SEC 1000000000LL
+
#define RTC_REINJECT_ON_ACK_COUNT 20
typedef struct RTCState {
@@ -54,27 +56,40 @@ typedef struct RTCState {
uint8_t cmos_index;
struct tm current_tm;
int32_t base_year;
+ uint64_t base_rtc;
+ uint64_t last_update;
+ int64_t offset;
qemu_irq irq;
qemu_irq sqw_irq;
int it_shift;
/* periodic timer */
QEMUTimer *periodic_timer;
int64_t next_periodic_time;
- /* second update */
- int64_t next_second_time;
+ /* update-ended timer */
+ QEMUTimer *update_timer;
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;
Notifier suspend_notifier;
} RTCState;
static void rtc_set_time(RTCState *s);
-static void rtc_copy_date(RTCState *s);
+static void rtc_update_time(RTCState *s);
+static void rtc_set_cmos(RTCState *s);
+static inline int rtc_from_bcd(RTCState *s, int a);
+
+static uint64_t get_guest_rtc_ns(RTCState *s)
+{
+ uint64_t guest_rtc;
+ uint64_t guest_clock = qemu_get_clock_ns(rtc_clock);
+
+ guest_rtc = s->base_rtc * NSEC_PER_SEC
+ + guest_clock - s->last_update + s->offset;
+ return guest_rtc;
+}
#ifdef TARGET_I386
static void rtc_coalesced_timer_update(RTCState *s)
@@ -110,6 +125,7 @@ static void rtc_coalesced_timer(void *opaque)
}
#endif
+/* handle periodic timer */
static void periodic_timer_update(RTCState *s, int64_t current_time)
{
int period_code, period;
@@ -175,6 +191,100 @@ static void rtc_periodic_timer(void *opaque)
}
}
+/* handle update-ended timer */
+static void check_update_timer(RTCState *s)
+{
+ uint64_t next_update_time;
+ uint64_t guest_nsec;
+
+ /* From the data sheet: setting the SET bit does not prevent
+ * interrupts from occurring! However, it will prevent an
+ * alarm interrupt from occurring, because the time of day is
+ * not updated.
+ */
+ if ((s->cmos_data[RTC_REG_C] & REG_C_UF) &&
+ (s->cmos_data[RTC_REG_B] & REG_B_SET)) {
+ qemu_del_timer(s->update_timer);
+ return;
+ }
+ if ((s->cmos_data[RTC_REG_C] & REG_C_UF) &&
+ (s->cmos_data[RTC_REG_C] & REG_C_AF)) {
+ qemu_del_timer(s->update_timer);
+ return;
+ }
+
+ guest_nsec = get_guest_rtc_ns(s) % NSEC_PER_SEC;
+ /* reprogram to next second */
+ next_update_time = qemu_get_clock_ns(rtc_clock)
+ + NSEC_PER_SEC - guest_nsec;
+ if (next_update_time != qemu_timer_expire_time_ns(s->update_timer)) {
+ qemu_mod_timer(s->update_timer, next_update_time);
+ }
+}
+
+static inline uint8_t convert_hour(RTCState *s, uint8_t hour)
+{
+ if (!(s->cmos_data[RTC_REG_B] & REG_B_24H)) {
+ hour %= 12;
+ if (s->cmos_data[RTC_HOURS] & 0x80) {
+ hour += 12;
+ }
+ }
+ return hour;
+}
+
+static uint32_t check_alarm(RTCState *s)
+{
+ uint8_t alarm_hour, alarm_min, alarm_sec;
+ uint8_t cur_hour, cur_min, cur_sec;
+
+ alarm_sec = rtc_from_bcd(s, s->cmos_data[RTC_SECONDS_ALARM]);
+ alarm_min = rtc_from_bcd(s, s->cmos_data[RTC_MINUTES_ALARM]);
+ alarm_hour = rtc_from_bcd(s, s->cmos_data[RTC_HOURS_ALARM]);
+ alarm_hour = convert_hour(s, alarm_hour);
+
+ cur_sec = rtc_from_bcd(s, s->cmos_data[RTC_SECONDS]);
+ cur_min = rtc_from_bcd(s, s->cmos_data[RTC_MINUTES]);
+ cur_hour = rtc_from_bcd(s, s->cmos_data[RTC_HOURS]);
+ cur_hour = convert_hour(s, cur_hour);
+
+ if (((s->cmos_data[RTC_SECONDS_ALARM] & 0xc0) == 0xc0
+ || alarm_sec == cur_sec) &&
+ ((s->cmos_data[RTC_MINUTES_ALARM] & 0xc0) == 0xc0
+ || alarm_min == cur_min) &&
+ ((s->cmos_data[RTC_HOURS_ALARM] & 0xc0) == 0xc0
+ || alarm_hour == cur_hour)) {
+ return 1;
+ }
+ return 0;
+
+}
+
+static void rtc_update_timer(void *opaque)
+{
+ RTCState *s = opaque;
+ int32_t irqs = REG_C_UF;
+ int32_t new_irqs;
+
+ /* UIP might have been latched, update time and clear it. */
+ rtc_update_time(s);
+ s->cmos_data[RTC_REG_A] &= ~REG_A_UIP;
+
+ if (check_alarm(s)) {
+ irqs |= REG_C_AF;
+ if (s->cmos_data[RTC_REG_B] & REG_B_AIE) {
+ qemu_system_wakeup_request(QEMU_WAKEUP_REASON_RTC);
+ }
+ }
+ new_irqs = irqs & ~s->cmos_data[RTC_REG_C];
+ s->cmos_data[RTC_REG_C] |= irqs;
+ if ((new_irqs & s->cmos_data[RTC_REG_B]) != 0) {
+ s->cmos_data[RTC_REG_C] |= REG_C_IRQF;
+ qemu_irq_raise(s->irq);
+ }
+ check_update_timer(s);
+}
+
static void cmos_ioport_write(void *opaque, uint32_t addr, uint32_t data)
{
RTCState *s = opaque;
@@ -189,6 +299,7 @@ static void cmos_ioport_write(void *opaque, uint32_t addr, uint32_t data)
case RTC_MINUTES_ALARM:
case RTC_HOURS_ALARM:
s->cmos_data[s->cmos_index] = data;
+ check_update_timer(s);
break;
case RTC_SECONDS:
case RTC_MINUTES:
@@ -201,6 +312,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);
+ check_update_timer(s);
}
break;
case RTC_REG_A:
@@ -208,15 +320,21 @@ static void cmos_ioport_write(void *opaque, uint32_t addr, uint32_t data)
s->cmos_data[RTC_REG_A] = (data & ~REG_A_UIP) |
(s->cmos_data[RTC_REG_A] & REG_A_UIP);
periodic_timer_update(s, qemu_get_clock_ns(rtc_clock));
+ check_update_timer(s);
break;
case RTC_REG_B:
if (data & REG_B_SET) {
+ /* update cmos to when the rtc was stopping */
+ if (!(s->cmos_data[RTC_REG_B] & REG_B_SET)) {
+ rtc_update_time(s);
+ }
/* set mode: reset UIP mode */
s->cmos_data[RTC_REG_A] &= ~REG_A_UIP;
data &= ~REG_B_UIE;
} else {
/* if disabling set mode, update the time */
if (s->cmos_data[RTC_REG_B] & REG_B_SET) {
+ s->offset = get_guest_rtc_ns(s) % NSEC_PER_SEC;
rtc_set_time(s);
}
}
@@ -231,6 +349,7 @@ static void cmos_ioport_write(void *opaque, uint32_t addr, uint32_t data)
}
s->cmos_data[RTC_REG_B] = data;
periodic_timer_update(s, qemu_get_clock_ns(rtc_clock));
+ check_update_timer(s);
break;
case RTC_REG_C:
case RTC_REG_D:
@@ -279,10 +398,13 @@ static void rtc_set_time(RTCState *s)
tm->tm_mon = rtc_from_bcd(s, s->cmos_data[RTC_MONTH]) - 1;
tm->tm_year = rtc_from_bcd(s, s->cmos_data[RTC_YEAR]) + s->base_year - 1900;
+ s->base_rtc = mktimegm(tm);
+ s->last_update = qemu_get_clock_ns(rtc_clock);
+
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;
@@ -308,122 +430,41 @@ 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 void rtc_update_time(RTCState *s)
{
- 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;
+ struct tm *ret;
+ time_t guest_sec;
+ int64_t guest_nsec;
+
+ guest_nsec = get_guest_rtc_ns(s);
+ guest_sec = guest_nsec / NSEC_PER_SEC;
+ ret = gmtime(&guest_sec);
+ s->current_tm = *ret;
+ rtc_set_cmos(s);
}
-/* update 'tm' to the next second */
-static void rtc_next_second(struct tm *tm)
+static int update_in_progress(RTCState *s)
{
- 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)
-{
- RTCState *s = opaque;
+ int64_t guest_nsec;
- if (!(s->cmos_data[RTC_REG_B] & REG_B_SET)) {
- rtc_copy_date(s);
+ if (s->cmos_data[RTC_REG_B] & REG_B_SET) {
+ return 0;
}
-
- /* 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)) {
-
- s->cmos_data[RTC_REG_C] |= REG_C_AF;
- if (s->cmos_data[RTC_REG_B] & REG_B_AIE) {
- qemu_system_wakeup_request(QEMU_WAKEUP_REASON_RTC);
- qemu_irq_raise(s->irq);
- s->cmos_data[RTC_REG_C] |= REG_C_IRQF;
+ if (qemu_timer_pending(s->update_timer)) {
+ int64_t next_update_time = qemu_timer_expire_time_ns(s->update_timer);
+ /* Latch UIP until the timer expires. */
+ if (qemu_get_clock_ns(rtc_clock) >= (next_update_time - 244000)) {
+ s->cmos_data[RTC_REG_A] |= REG_A_UIP;
+ return 1;
}
}
- /* 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);
+ guest_nsec = get_guest_rtc_ns(s);
+ /* UIP bit will be set at last 244us of every second. */
+ if ((guest_nsec % NSEC_PER_SEC) >= (NSEC_PER_SEC - 244000)) {
+ return 1;
}
-
- /* 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);
+ return 0;
}
static uint32_t cmos_ioport_read(void *opaque, uint32_t addr)
@@ -441,15 +482,28 @@ static uint32_t cmos_ioport_read(void *opaque, uint32_t addr)
case RTC_DAY_OF_MONTH:
case RTC_MONTH:
case RTC_YEAR:
+ /* if not in set mode, calibrate cmos before
+ * reading*/
+ if (!(s->cmos_data[RTC_REG_B] & REG_B_SET)) {
+ rtc_update_time(s);
+ }
ret = s->cmos_data[s->cmos_index];
break;
case RTC_REG_A:
+ if (update_in_progress(s)) {
+ s->cmos_data[s->cmos_index] |= REG_A_UIP;
+ } else {
+ s->cmos_data[s->cmos_index] &= ~REG_A_UIP;
+ }
ret = s->cmos_data[s->cmos_index];
break;
case RTC_REG_C:
ret = s->cmos_data[s->cmos_index];
qemu_irq_lower(s->irq);
s->cmos_data[RTC_REG_C] = 0x00;
+ if (ret & (REG_C_UF | REG_C_AF)) {
+ check_update_timer(s);
+ }
#ifdef TARGET_I386
if(s->irq_coalesced &&
(s->cmos_data[RTC_REG_B] & REG_B_PIE) &&
@@ -484,13 +538,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
@@ -501,9 +548,15 @@ 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->base_rtc = mktimegm(&tm);
+ s->last_update = qemu_get_clock_ns(rtc_clock);
+ s->offset = 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);
@@ -540,11 +593,12 @@ 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_UINT64(base_rtc, RTCState),
+ VMSTATE_UINT64(last_update, RTCState),
+ VMSTATE_INT64(offset, 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_TIMER(update_timer, RTCState),
VMSTATE_UINT32_V(irq_coalesced, RTCState, 2),
VMSTATE_UINT32_V(period, RTCState, 2),
VMSTATE_END_OF_LIST()
@@ -557,9 +611,8 @@ 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);
periodic_timer_update(s, now);
+ check_update_timer(s);
#ifdef TARGET_I386
if (s->lost_tick_policy == LOST_TICK_SLEW) {
rtc_coalesced_timer_update(s);
@@ -581,6 +634,7 @@ static void rtc_reset(void *opaque)
s->cmos_data[RTC_REG_B] &= ~(REG_B_PIE | REG_B_AIE | REG_B_SQWE);
s->cmos_data[RTC_REG_C] &= ~(REG_C_UF | REG_C_IRQF | REG_C_PF | REG_C_AF);
+ check_update_timer(s);
qemu_irq_lower(s->irq);
@@ -613,6 +667,7 @@ static void rtc_get_date(Object *obj, Visitor *v, void *opaque,
ISADevice *isa = ISA_DEVICE(obj);
RTCState *s = DO_UPCAST(RTCState, dev, isa);
+ rtc_update_time(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);
@@ -649,8 +704,8 @@ 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->update_timer = qemu_new_timer_ns(rtc_clock, rtc_update_timer, s);
+ check_update_timer(s);
s->clock_reset_notifier.notify = rtc_notify_clock_reset;
qemu_register_clock_reset_notifier(rtc_clock, &s->clock_reset_notifier);
@@ -658,10 +713,6 @@ static int rtc_initfn(ISADevice *dev)
s->suspend_notifier.notify = rtc_notify_suspend;
qemu_register_suspend_notifier(&s->suspend_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);
--
1.7.10.2
^ permalink raw reply related [flat|nested] 14+ messages in thread
* Re: [Qemu-devel] [PATCH 4/7] RTC: Update the RTC clock only when reading it
2012-07-20 10:53 ` [Qemu-devel] [PATCH 4/7] RTC: Update the RTC clock only when reading it Paolo Bonzini
@ 2012-07-23 5:17 ` Juan Quintela
2012-07-23 7:19 ` Paolo Bonzini
0 siblings, 1 reply; 14+ messages in thread
From: Juan Quintela @ 2012-07-23 5:17 UTC (permalink / raw)
To: Paolo Bonzini; +Cc: Zhang, Yang Z, qemu-devel
Paolo Bonzini <pbonzini@redhat.com> wrote:
> From: "Zhang, Yang Z" <yang.z.zhang@intel.com>
>
> Calculate guest RTC based on the time of the last update, instead of
> using timers. The formula is
>
> (base_rtc + guest_time_now - guest_time_last_update + offset)
>
> Base_rtc is the RTC value when the RTC was last updated.
> Guest_time_now is the guest time when the access happens.
> Guest_time_last_update was the guest time when the RTC was last updated.
> Offset is used when divider reset happens or the set bit is toggled.
>
> The timer is kept in order to signal interrupts, but it only needs to
> run when either UF or AF is cleared. When the bits are both set, the
> timer does not run.
>
> UIP is now synthesized when reading register A. If the timer is not set,
> or if there is more than one second before it (as is the case at the
> end of this series), the leading edge of UIP is computed and the rising
> edge occurs 220us later. If the update timer occurs within one second,
> however, the rising edge of the AF and UF bits should coincide withe
> the falling edge of UIP. We do not know exactly when this will happen
> because there could be delays in the servicing of the timer. Hence, in
> this case reading register A only computes for the rising edge of UIP,
> and latches the bit until the timer is fired and clears it.
>
> Signed-off-by: Yang Zhang <yang.z.zhang@intel.com>
> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
> @@ -540,11 +593,12 @@ 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_UINT64(base_rtc, RTCState),
> + VMSTATE_UINT64(last_update, RTCState),
> + VMSTATE_INT64(offset, RTCState),
VMSTATE_UINT64_V(base_rtc, RTCState, 3)
same ofr the others.
Normally, new fields are added at the end of the structure.
> 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_TIMER(update_timer, RTCState),
I have to read the rest of the patch to know what is the relation of
this 4 fields, to see if there is any way to create this in any sane
way that is compatible.
The new fields can go in a different subsection. The three ones that
are removed, I don't know if that values can be calculated.
> VMSTATE_UINT32_V(irq_coalesced, RTCState, 2),
> VMSTATE_UINT32_V(period, RTCState, 2),
> VMSTATE_END_OF_LIST()
Later, Juan.
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [Qemu-devel] [PATCH 4/7] RTC: Update the RTC clock only when reading it
2012-07-23 5:17 ` Juan Quintela
@ 2012-07-23 7:19 ` Paolo Bonzini
0 siblings, 0 replies; 14+ messages in thread
From: Paolo Bonzini @ 2012-07-23 7:19 UTC (permalink / raw)
To: quintela; +Cc: Zhang, Yang Z, qemu-devel
Il 23/07/2012 07:17, Juan Quintela ha scritto:
> Paolo Bonzini <pbonzini@redhat.com> wrote:
>> From: "Zhang, Yang Z" <yang.z.zhang@intel.com>
>>
>> Calculate guest RTC based on the time of the last update, instead of
>> using timers. The formula is
>>
>> (base_rtc + guest_time_now - guest_time_last_update + offset)
>>
>> Base_rtc is the RTC value when the RTC was last updated.
>> Guest_time_now is the guest time when the access happens.
>> Guest_time_last_update was the guest time when the RTC was last updated.
>> Offset is used when divider reset happens or the set bit is toggled.
>>
>> The timer is kept in order to signal interrupts, but it only needs to
>> run when either UF or AF is cleared. When the bits are both set, the
>> timer does not run.
>>
>> UIP is now synthesized when reading register A. If the timer is not set,
>> or if there is more than one second before it (as is the case at the
>> end of this series), the leading edge of UIP is computed and the rising
>> edge occurs 220us later. If the update timer occurs within one second,
>> however, the rising edge of the AF and UF bits should coincide withe
>> the falling edge of UIP. We do not know exactly when this will happen
>> because there could be delays in the servicing of the timer. Hence, in
>> this case reading register A only computes for the rising edge of UIP,
>> and latches the bit until the timer is fired and clears it.
>>
>> Signed-off-by: Yang Zhang <yang.z.zhang@intel.com>
>> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
>> @@ -540,11 +593,12 @@ 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_UINT64(base_rtc, RTCState),
>> + VMSTATE_UINT64(last_update, RTCState),
>> + VMSTATE_INT64(offset, RTCState),
> VMSTATE_UINT64_V(base_rtc, RTCState, 3)
> same ofr the others.
>
> Normally, new fields are added at the end of the structure.
Doesn't really matter if you bump the minimum version but you're right
that it is more correct.
>
>> 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_TIMER(update_timer, RTCState),
>
> I have to read the rest of the patch to know what is the relation of
> this 4 fields, to see if there is any way to create this in any sane
> way that is compatible.
next_second_time is computed like this (see check_update_timer):
guest_nsec = get_guest_rtc_ns(s) % NSEC_PER_SEC;
next_update_time = qemu_get_clock_ns(rtc_clock)
+ NSEC_PER_SEC - guest_nsec;
One of second_timer and second_timer2 is unset, depending on whether
UIP=1 or UIP=0 respectively.
second_timer if set is next_second_time.
second_timer2 if set is next_second_time + get_ticks_per_sec/100.
> The new fields can go in a different subsection.
But then you would transmit the subsection always, and old QEMU doesn't
know how to skip it. So it doesn't really help.
This is because base_rtc, last_update and offset need to be transmitted
always. next_alarm_time only if no alarm fired already, but the common
case is yes. Only for update_timer we can hack and transmit it in the
slot that was used for next_second_time. Unless you want to transmit
the subsection only for the new machine version, which is never done in
the QEMU tree.
Paolo
^ permalink raw reply [flat|nested] 14+ messages in thread
* [Qemu-devel] [PATCH 5/7] RTC: Add divider reset support
2012-07-20 10:53 [Qemu-devel] [PATCH 0/7] Remove periodic wakeup from RTC timer Paolo Bonzini
` (3 preceding siblings ...)
2012-07-20 10:53 ` [Qemu-devel] [PATCH 4/7] RTC: Update the RTC clock only when reading it Paolo Bonzini
@ 2012-07-20 10:53 ` Paolo Bonzini
2012-07-20 10:53 ` [Qemu-devel] [PATCH 6/7] RTC: Do not fire timer periodically to catch next alarm Paolo Bonzini
2012-07-20 10:53 ` [Qemu-devel] [PATCH 7/7] RTC: Allow to migrate from old QEMU Paolo Bonzini
6 siblings, 0 replies; 14+ messages in thread
From: Paolo Bonzini @ 2012-07-20 10:53 UTC (permalink / raw)
To: qemu-devel; +Cc: Zhang, Yang Z
From: "Zhang, Yang Z" <yang.z.zhang@intel.com>
The first update cycle begins one-half seconds after divider
reset is removed. This feature is useful for testing.
Signed-off-by: Yang Zhang <yang.z.zhang@intel.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
hw/mc146818rtc.c | 50 +++++++++++++++++++++++++++++++++++++++++---------
1 file changed, 41 insertions(+), 9 deletions(-)
diff --git a/hw/mc146818rtc.c b/hw/mc146818rtc.c
index 3a99605..1ab0dc6 100644
--- a/hw/mc146818rtc.c
+++ b/hw/mc146818rtc.c
@@ -81,6 +81,12 @@ static void rtc_update_time(RTCState *s);
static void rtc_set_cmos(RTCState *s);
static inline int rtc_from_bcd(RTCState *s, int a);
+static inline bool rtc_running(RTCState *s)
+{
+ return (!(s->cmos_data[RTC_REG_B] & REG_B_SET) &&
+ (s->cmos_data[RTC_REG_A] & 0x70) <= 0x20);
+}
+
static uint64_t get_guest_rtc_ns(RTCState *s)
{
uint64_t guest_rtc;
@@ -197,11 +203,15 @@ static void check_update_timer(RTCState *s)
uint64_t next_update_time;
uint64_t guest_nsec;
- /* From the data sheet: setting the SET bit does not prevent
- * interrupts from occurring! However, it will prevent an
- * alarm interrupt from occurring, because the time of day is
- * not updated.
+ /* From the data sheet: "Holding the dividers in reset prevents
+ * interrupts from operating, while setting the SET bit allows"
+ * them to occur. However, it will prevent an alarm interrupt
+ * from occurring, because the time of day is not updated.
*/
+ if ((s->cmos_data[RTC_REG_A] & 0x60) == 0x60) {
+ qemu_del_timer(s->update_timer);
+ return;
+ }
if ((s->cmos_data[RTC_REG_C] & REG_C_UF) &&
(s->cmos_data[RTC_REG_B] & REG_B_SET)) {
qemu_del_timer(s->update_timer);
@@ -266,6 +276,8 @@ static void rtc_update_timer(void *opaque)
int32_t irqs = REG_C_UF;
int32_t new_irqs;
+ assert((s->cmos_data[RTC_REG_A] & 0x60) != 0x60);
+
/* UIP might have been latched, update time and clear it. */
rtc_update_time(s);
s->cmos_data[RTC_REG_A] &= ~REG_A_UIP;
@@ -310,12 +322,31 @@ static void cmos_ioport_write(void *opaque, uint32_t addr, uint32_t data)
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] & REG_B_SET)) {
+ if (rtc_running(s)) {
rtc_set_time(s);
check_update_timer(s);
}
break;
case RTC_REG_A:
+ if ((data & 0x60) == 0x60) {
+ if (rtc_running(s)) {
+ rtc_update_time(s);
+ }
+ /* What happens to UIP when divider reset is enabled is
+ * unclear from the datasheet. Shouldn't matter much
+ * though.
+ */
+ s->cmos_data[RTC_REG_A] &= ~REG_A_UIP;
+ } else if (((s->cmos_data[RTC_REG_A] & 0x60) == 0x60) &&
+ (data & 0x70) <= 0x20) {
+ /* when the divider reset is removed, the first update cycle
+ * begins one-half second later*/
+ if (!(s->cmos_data[RTC_REG_B] & REG_B_SET)) {
+ s->offset = 500000000;
+ rtc_set_time(s);
+ }
+ s->cmos_data[RTC_REG_A] &= ~REG_A_UIP;
+ }
/* UIP bit is read only */
s->cmos_data[RTC_REG_A] = (data & ~REG_A_UIP) |
(s->cmos_data[RTC_REG_A] & REG_A_UIP);
@@ -325,7 +356,7 @@ static void cmos_ioport_write(void *opaque, uint32_t addr, uint32_t data)
case RTC_REG_B:
if (data & REG_B_SET) {
/* update cmos to when the rtc was stopping */
- if (!(s->cmos_data[RTC_REG_B] & REG_B_SET)) {
+ if (rtc_running(s)) {
rtc_update_time(s);
}
/* set mode: reset UIP mode */
@@ -333,7 +364,8 @@ static void cmos_ioport_write(void *opaque, uint32_t addr, uint32_t data)
data &= ~REG_B_UIE;
} else {
/* if disabling set mode, update the time */
- if (s->cmos_data[RTC_REG_B] & REG_B_SET) {
+ if ((s->cmos_data[RTC_REG_B] & REG_B_SET) &&
+ (s->cmos_data[RTC_REG_A] & 0x70) <= 0x20) {
s->offset = get_guest_rtc_ns(s) % NSEC_PER_SEC;
rtc_set_time(s);
}
@@ -447,7 +479,7 @@ static int update_in_progress(RTCState *s)
{
int64_t guest_nsec;
- if (s->cmos_data[RTC_REG_B] & REG_B_SET) {
+ if (!rtc_running(s)) {
return 0;
}
if (qemu_timer_pending(s->update_timer)) {
@@ -484,7 +516,7 @@ static uint32_t cmos_ioport_read(void *opaque, uint32_t addr)
case RTC_YEAR:
/* if not in set mode, calibrate cmos before
* reading*/
- if (!(s->cmos_data[RTC_REG_B] & REG_B_SET)) {
+ if (rtc_running(s)) {
rtc_update_time(s);
}
ret = s->cmos_data[s->cmos_index];
--
1.7.10.2
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [Qemu-devel] [PATCH 6/7] RTC: Do not fire timer periodically to catch next alarm
2012-07-20 10:53 [Qemu-devel] [PATCH 0/7] Remove periodic wakeup from RTC timer Paolo Bonzini
` (4 preceding siblings ...)
2012-07-20 10:53 ` [Qemu-devel] [PATCH 5/7] RTC: Add divider reset support Paolo Bonzini
@ 2012-07-20 10:53 ` Paolo Bonzini
2012-07-20 10:53 ` [Qemu-devel] [PATCH 7/7] RTC: Allow to migrate from old QEMU Paolo Bonzini
6 siblings, 0 replies; 14+ messages in thread
From: Paolo Bonzini @ 2012-07-20 10:53 UTC (permalink / raw)
To: qemu-devel; +Cc: Yang Zhang
This patch limits further the usage of a periodic timer. It computes the
time of the next alarm, and uses it to skip all intermediate occurrences
of the timer.
Cc: Yang Zhang <yang.z.zhang@intel.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
The patch from Yang had some problems, for example if the
alarm was 9:XX:XX it would fire also at 10:00:00. So this
one was rewritten by me. Please review with special care.
hw/mc146818rtc.c | 117 +++++++++++++++++++++++++++++++++++++++++++++++-------
1 file changed, 103 insertions(+), 14 deletions(-)
diff --git a/hw/mc146818rtc.c b/hw/mc146818rtc.c
index 1ab0dc6..f0b75cb 100644
--- a/hw/mc146818rtc.c
+++ b/hw/mc146818rtc.c
@@ -46,6 +46,11 @@
#endif
#define NSEC_PER_SEC 1000000000LL
+#define SEC_PER_MIN 60
+#define MIN_PER_HOUR 60
+#define SEC_PER_HOUR 3600
+#define HOUR_PER_DAY 24
+#define SEC_PER_DAY 86400
#define RTC_REINJECT_ON_ACK_COUNT 20
@@ -67,6 +72,7 @@ typedef struct RTCState {
int64_t next_periodic_time;
/* update-ended timer */
QEMUTimer *update_timer;
+ uint64_t next_alarm_time;
uint16_t irq_reinject_on_ack_count;
uint32_t irq_coalesced;
uint32_t period;
@@ -80,6 +86,7 @@ static void rtc_set_time(RTCState *s);
static void rtc_update_time(RTCState *s);
static void rtc_set_cmos(RTCState *s);
static inline int rtc_from_bcd(RTCState *s, int a);
+static uint64_t get_next_alarm(RTCState *s);
static inline bool rtc_running(RTCState *s)
{
@@ -202,6 +209,7 @@ static void check_update_timer(RTCState *s)
{
uint64_t next_update_time;
uint64_t guest_nsec;
+ int next_alarm_sec;
/* From the data sheet: "Holding the dividers in reset prevents
* interrupts from operating, while setting the SET bit allows"
@@ -224,9 +232,21 @@ static void check_update_timer(RTCState *s)
}
guest_nsec = get_guest_rtc_ns(s) % NSEC_PER_SEC;
- /* reprogram to next second */
+ /* if UF is clear, reprogram to next second */
next_update_time = qemu_get_clock_ns(rtc_clock)
+ NSEC_PER_SEC - guest_nsec;
+
+ /* Compute time of next alarm. One second is already accounted
+ * for in next_update_time.
+ */
+ next_alarm_sec = get_next_alarm(s);
+ s->next_alarm_time = next_update_time + (next_alarm_sec - 1) * NSEC_PER_SEC;
+
+ if (s->cmos_data[RTC_REG_C] & REG_C_UF) {
+ /* UF is set, but AF is clear. Program the timer to target
+ * the alarm time. */
+ next_update_time = s->next_alarm_time;
+ }
if (next_update_time != qemu_timer_expire_time_ns(s->update_timer)) {
qemu_mod_timer(s->update_timer, next_update_time);
}
@@ -243,31 +263,95 @@ static inline uint8_t convert_hour(RTCState *s, uint8_t hour)
return hour;
}
-static uint32_t check_alarm(RTCState *s)
+static uint64_t get_next_alarm(RTCState *s)
{
- uint8_t alarm_hour, alarm_min, alarm_sec;
- uint8_t cur_hour, cur_min, cur_sec;
+ int32_t alarm_sec, alarm_min, alarm_hour, cur_hour, cur_min, cur_sec;
+ int32_t hour, min, sec;
+
+ rtc_update_time(s);
alarm_sec = rtc_from_bcd(s, s->cmos_data[RTC_SECONDS_ALARM]);
alarm_min = rtc_from_bcd(s, s->cmos_data[RTC_MINUTES_ALARM]);
alarm_hour = rtc_from_bcd(s, s->cmos_data[RTC_HOURS_ALARM]);
- alarm_hour = convert_hour(s, alarm_hour);
+ alarm_hour = alarm_hour == -1 ? -1 : convert_hour(s, alarm_hour);
cur_sec = rtc_from_bcd(s, s->cmos_data[RTC_SECONDS]);
cur_min = rtc_from_bcd(s, s->cmos_data[RTC_MINUTES]);
cur_hour = rtc_from_bcd(s, s->cmos_data[RTC_HOURS]);
cur_hour = convert_hour(s, cur_hour);
- if (((s->cmos_data[RTC_SECONDS_ALARM] & 0xc0) == 0xc0
- || alarm_sec == cur_sec) &&
- ((s->cmos_data[RTC_MINUTES_ALARM] & 0xc0) == 0xc0
- || alarm_min == cur_min) &&
- ((s->cmos_data[RTC_HOURS_ALARM] & 0xc0) == 0xc0
- || alarm_hour == cur_hour)) {
- return 1;
+ if (alarm_hour == -1) {
+ alarm_hour = cur_hour;
+ if (alarm_min == -1) {
+ alarm_min = cur_min;
+ if (alarm_sec == -1) {
+ alarm_sec = cur_sec + 1;
+ } else if (cur_sec > alarm_sec) {
+ alarm_min++;
+ }
+ } else if (cur_min == alarm_min) {
+ if (alarm_sec == -1) {
+ alarm_sec = cur_sec + 1;
+ } else {
+ if (cur_sec > alarm_sec) {
+ alarm_hour++;
+ }
+ }
+ if (alarm_sec == SEC_PER_MIN) {
+ /* wrap to next hour, minutes is not in don't care mode */
+ alarm_sec = 0;
+ alarm_hour++;
+ }
+ } else if (cur_min > alarm_min) {
+ alarm_hour++;
+ }
+ } else if (cur_hour == alarm_hour) {
+ if (alarm_min == -1) {
+ alarm_min = cur_min;
+ if (alarm_sec == -1) {
+ alarm_sec = cur_sec + 1;
+ } else if (cur_sec > alarm_sec) {
+ alarm_min++;
+ }
+
+ if (alarm_sec == SEC_PER_MIN) {
+ alarm_sec = 0;
+ alarm_min++;
+ }
+ /* wrap to next day, hour is not in don't care mode */
+ alarm_min %= MIN_PER_HOUR;
+ } else if (cur_min == alarm_min) {
+ if (alarm_sec == -1) {
+ alarm_sec = cur_sec + 1;
+ }
+ /* wrap to next day, hours+minutes not in don't care mode */
+ alarm_sec %= SEC_PER_MIN;
+ }
}
- return 0;
+ /* values that are still don't care fire at the next min/sec */
+ if (alarm_min == -1) {
+ alarm_min = 0;
+ }
+ if (alarm_sec == -1) {
+ alarm_sec = 0;
+ }
+
+ /* keep values in range */
+ if (alarm_sec == SEC_PER_MIN) {
+ alarm_sec = 0;
+ alarm_min++;
+ }
+ if (alarm_min == MIN_PER_HOUR) {
+ alarm_min = 0;
+ alarm_hour++;
+ }
+ alarm_hour %= HOUR_PER_DAY;
+
+ hour = alarm_hour - cur_hour;
+ min = hour * MIN_PER_HOUR + alarm_min - cur_min;
+ sec = min * SEC_PER_MIN + alarm_sec - cur_sec;
+ return sec <= 0 ? sec + SEC_PER_DAY : sec;
}
static void rtc_update_timer(void *opaque)
@@ -282,12 +366,13 @@ static void rtc_update_timer(void *opaque)
rtc_update_time(s);
s->cmos_data[RTC_REG_A] &= ~REG_A_UIP;
- if (check_alarm(s)) {
+ if (qemu_get_clock_ns(rtc_clock) >= s->next_alarm_time) {
irqs |= REG_C_AF;
if (s->cmos_data[RTC_REG_B] & REG_B_AIE) {
qemu_system_wakeup_request(QEMU_WAKEUP_REASON_RTC);
}
}
+
new_irqs = irqs & ~s->cmos_data[RTC_REG_C];
s->cmos_data[RTC_REG_C] |= irqs;
if ((new_irqs & s->cmos_data[RTC_REG_B]) != 0) {
@@ -405,6 +490,9 @@ static inline int rtc_to_bcd(RTCState *s, int a)
static inline int rtc_from_bcd(RTCState *s, int a)
{
+ if ((a & 0xc0) == 0xc0) {
+ return -1;
+ }
if (s->cmos_data[RTC_REG_B] & REG_B_DM) {
return a;
} else {
@@ -631,6 +719,7 @@ static const VMStateDescription vmstate_rtc = {
VMSTATE_TIMER(periodic_timer, RTCState),
VMSTATE_INT64(next_periodic_time, RTCState),
VMSTATE_TIMER(update_timer, RTCState),
+ VMSTATE_UINT64(next_alarm_time, RTCState),
VMSTATE_UINT32_V(irq_coalesced, RTCState, 2),
VMSTATE_UINT32_V(period, RTCState, 2),
VMSTATE_END_OF_LIST()
--
1.7.10.2
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [Qemu-devel] [PATCH 7/7] RTC: Allow to migrate from old QEMU
2012-07-20 10:53 [Qemu-devel] [PATCH 0/7] Remove periodic wakeup from RTC timer Paolo Bonzini
` (5 preceding siblings ...)
2012-07-20 10:53 ` [Qemu-devel] [PATCH 6/7] RTC: Do not fire timer periodically to catch next alarm Paolo Bonzini
@ 2012-07-20 10:53 ` Paolo Bonzini
2012-07-20 17:38 ` Michael Roth
2012-07-23 5:12 ` Juan Quintela
6 siblings, 2 replies; 14+ messages in thread
From: Paolo Bonzini @ 2012-07-20 10:53 UTC (permalink / raw)
To: qemu-devel; +Cc: Zhang, Yang Z, Juan Quintela
From: "Zhang, Yang Z" <yang.z.zhang@intel.com>
The new logic is compatible with old, and it should not block migration
from old QEMU. However, the new version cannot migrate to the old one.
Cc: Juan Quintela <quintela@redhat.com>
Signed-off-by: Yang Zhang <yang.z.zhang@intel.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
hw/mc146818rtc.c | 42 +++++++++++++++++++++++++++++++++++++++---
1 file changed, 39 insertions(+), 3 deletions(-)
diff --git a/hw/mc146818rtc.c b/hw/mc146818rtc.c
index f0b75cb..d3871d8 100644
--- a/hw/mc146818rtc.c
+++ b/hw/mc146818rtc.c
@@ -697,11 +697,47 @@ static int rtc_post_load(void *opaque, int version_id)
return 0;
}
+static int rtc_load_old(QEMUFile *f, void *opaque, int version_id)
+{
+ RTCState *s = opaque;
+ uint8_t buf[77];
+
+ if (version_id > 2) {
+ return -EINVAL;
+ }
+
+ qemu_get_buffer(f, s->cmos_data, sizeof(s->cmos_data));
+ qemu_get_8s(f, &s->cmos_index);
+
+ /* Skip loading of s->current_tm, we already have the
+ * information in cmos_data.
+ */
+ qemu_get_buffer(f, buf, 7*4);
+
+ qemu_get_timer(f, s->periodic_timer);
+ s->next_periodic_time = qemu_get_be64(f);
+
+ /* Skip loading of next_second_time, second_timer, second_timer2. */
+ qemu_get_buffer(f, buf, 3*8);
+
+ rtc_set_time(s);
+ s->offset = 0;
+ check_update_timer(s);
+
+ if (version_id >= 2) {
+ s->irq_coalesced = qemu_get_be32(f);
+ s->period = qemu_get_be32(f);
+ }
+
+ return rtc_post_load(opaque, version_id);
+}
+
static const VMStateDescription vmstate_rtc = {
.name = "mc146818rtc",
- .version_id = 2,
- .minimum_version_id = 1,
+ .version_id = 3,
+ .minimum_version_id = 3,
.minimum_version_id_old = 1,
+ .load_state_old = rtc_load_old,
.post_load = rtc_post_load,
.fields = (VMStateField []) {
VMSTATE_BUFFER(cmos_data, RTCState),
@@ -837,7 +873,7 @@ static int rtc_initfn(ISADevice *dev)
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",
--
1.7.10.2
^ permalink raw reply related [flat|nested] 14+ messages in thread
* Re: [Qemu-devel] [PATCH 7/7] RTC: Allow to migrate from old QEMU
2012-07-20 10:53 ` [Qemu-devel] [PATCH 7/7] RTC: Allow to migrate from old QEMU Paolo Bonzini
@ 2012-07-20 17:38 ` Michael Roth
2012-07-20 19:02 ` Paolo Bonzini
2012-07-23 5:12 ` Juan Quintela
1 sibling, 1 reply; 14+ messages in thread
From: Michael Roth @ 2012-07-20 17:38 UTC (permalink / raw)
To: Paolo Bonzini; +Cc: Zhang, Yang Z, qemu-devel, Juan Quintela
On Fri, Jul 20, 2012 at 12:53:53PM +0200, Paolo Bonzini wrote:
> From: "Zhang, Yang Z" <yang.z.zhang@intel.com>
>
> The new logic is compatible with old, and it should not block migration
> from old QEMU. However, the new version cannot migrate to the old one.
Do we not plan to support 1.2 -> 1.1 with -M pc-1.1? I'm not sure what
the "official" stance is, but I think we could do it by making the behavior
configurable and defaulting to the old behavior for -M pc-1.1, and using
VMSTATE_*_TEST(..., rtc_on_demand_enabled) to send the new fields
conditionally.
>
> Cc: Juan Quintela <quintela@redhat.com>
> Signed-off-by: Yang Zhang <yang.z.zhang@intel.com>
> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
> ---
> hw/mc146818rtc.c | 42 +++++++++++++++++++++++++++++++++++++++---
> 1 file changed, 39 insertions(+), 3 deletions(-)
>
> diff --git a/hw/mc146818rtc.c b/hw/mc146818rtc.c
> index f0b75cb..d3871d8 100644
> --- a/hw/mc146818rtc.c
> +++ b/hw/mc146818rtc.c
> @@ -697,11 +697,47 @@ static int rtc_post_load(void *opaque, int version_id)
> return 0;
> }
>
> +static int rtc_load_old(QEMUFile *f, void *opaque, int version_id)
> +{
> + RTCState *s = opaque;
> + uint8_t buf[77];
> +
> + if (version_id > 2) {
> + return -EINVAL;
> + }
> +
> + qemu_get_buffer(f, s->cmos_data, sizeof(s->cmos_data));
> + qemu_get_8s(f, &s->cmos_index);
> +
> + /* Skip loading of s->current_tm, we already have the
> + * information in cmos_data.
> + */
> + qemu_get_buffer(f, buf, 7*4);
> +
> + qemu_get_timer(f, s->periodic_timer);
> + s->next_periodic_time = qemu_get_be64(f);
> +
> + /* Skip loading of next_second_time, second_timer, second_timer2. */
> + qemu_get_buffer(f, buf, 3*8);
> +
> + rtc_set_time(s);
> + s->offset = 0;
> + check_update_timer(s);
> +
> + if (version_id >= 2) {
> + s->irq_coalesced = qemu_get_be32(f);
> + s->period = qemu_get_be32(f);
> + }
> +
> + return rtc_post_load(opaque, version_id);
> +}
> +
> static const VMStateDescription vmstate_rtc = {
> .name = "mc146818rtc",
> - .version_id = 2,
> - .minimum_version_id = 1,
> + .version_id = 3,
> + .minimum_version_id = 3,
> .minimum_version_id_old = 1,
> + .load_state_old = rtc_load_old,
> .post_load = rtc_post_load,
> .fields = (VMStateField []) {
> VMSTATE_BUFFER(cmos_data, RTCState),
> @@ -837,7 +873,7 @@ static int rtc_initfn(ISADevice *dev)
> 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",
> --
> 1.7.10.2
>
>
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [Qemu-devel] [PATCH 7/7] RTC: Allow to migrate from old QEMU
2012-07-20 17:38 ` Michael Roth
@ 2012-07-20 19:02 ` Paolo Bonzini
0 siblings, 0 replies; 14+ messages in thread
From: Paolo Bonzini @ 2012-07-20 19:02 UTC (permalink / raw)
To: Michael Roth; +Cc: Zhang, Yang Z, qemu-devel, Juan Quintela
Il 20/07/2012 19:38, Michael Roth ha scritto:
>> > The new logic is compatible with old, and it should not block migration
>> > from old QEMU. However, the new version cannot migrate to the old one.
> Do we not plan to support 1.2 -> 1.1 with -M pc-1.1?
No, we don't. That's why in general subsections are preferred to
version bumps, as long as the subsection is sent rarely (to fix bugs) or
only for features that are never enabled in the old version. Actually I
think all of our subsections are of the former kind.
Paolo
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [Qemu-devel] [PATCH 7/7] RTC: Allow to migrate from old QEMU
2012-07-20 10:53 ` [Qemu-devel] [PATCH 7/7] RTC: Allow to migrate from old QEMU Paolo Bonzini
2012-07-20 17:38 ` Michael Roth
@ 2012-07-23 5:12 ` Juan Quintela
2012-07-23 7:30 ` Paolo Bonzini
1 sibling, 1 reply; 14+ messages in thread
From: Juan Quintela @ 2012-07-23 5:12 UTC (permalink / raw)
To: Paolo Bonzini; +Cc: Zhang, Yang Z, qemu-devel
Paolo Bonzini <pbonzini@redhat.com> wrote:
> From: "Zhang, Yang Z" <yang.z.zhang@intel.com>
>
> The new logic is compatible with old, and it should not block migration
> from old QEMU. However, the new version cannot migrate to the old one.
>
> Cc: Juan Quintela <quintela@redhat.com>
I guess that you removed the tm structs.
This are the current fields.
.fields = (VMStateField []) {
VMSTATE_BUFFER(cmos_data, RTCState),
VMSTATE_UINT8(cmos_index, RTCState),
VMSTATE_INT32(current_tm.tm_sec, RTCState),
VMSTATE_INT32(current_tm.tm_min, RTCState),
VMSTATE_INT32(current_tm.tm_hour, RTCState),
VMSTATE_INT32(current_tm.tm_wday, RTCState),
VMSTATE_INT32(current_tm.tm_mday, RTCState),
VMSTATE_INT32(current_tm.tm_mon, RTCState),
VMSTATE_INT32(current_tm.tm_year, RTCState),
you can change this to:
VMSTATE_UNUSED(7*4);
Some for the others.
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()
}
> static const VMStateDescription vmstate_rtc = {
> .name = "mc146818rtc",
> - .version_id = 2,
> - .minimum_version_id = 1,
> + .version_id = 3,
> + .minimum_version_id = 3,
> .minimum_version_id_old = 1,
> + .load_state_old = rtc_load_old,
> .post_load = rtc_post_load,
> .fields = (VMStateField []) {
> VMSTATE_BUFFER(cmos_data, RTCState),
> @@ -837,7 +873,7 @@ static int rtc_initfn(ISADevice *dev)
no changes in the other part of vmstate? that is at least strange? I
guess they are on the other patches on the series. Will take a look.
> 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",
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [Qemu-devel] [PATCH 7/7] RTC: Allow to migrate from old QEMU
2012-07-23 5:12 ` Juan Quintela
@ 2012-07-23 7:30 ` Paolo Bonzini
0 siblings, 0 replies; 14+ messages in thread
From: Paolo Bonzini @ 2012-07-23 7:30 UTC (permalink / raw)
To: quintela; +Cc: Zhang, Yang Z, qemu-devel
Il 23/07/2012 07:12, Juan Quintela ha scritto:
> .fields = (VMStateField []) {
> VMSTATE_BUFFER(cmos_data, RTCState),
> VMSTATE_UINT8(cmos_index, RTCState),
> VMSTATE_INT32(current_tm.tm_sec, RTCState),
> VMSTATE_INT32(current_tm.tm_min, RTCState),
> VMSTATE_INT32(current_tm.tm_hour, RTCState),
> VMSTATE_INT32(current_tm.tm_wday, RTCState),
> VMSTATE_INT32(current_tm.tm_mday, RTCState),
> VMSTATE_INT32(current_tm.tm_mon, RTCState),
> VMSTATE_INT32(current_tm.tm_year, RTCState),
>
> you can change this to:
>
> VMSTATE_UNUSED(7*4);
I think this is not safe. In the load_old case, we ignore the struct tm
because we call rtc_set_time and check_update_timer. We need this to
make up some values of base_rtc, last_update and offset.
In the normal case we need both the CMOS data and the timer data.
Though perhaps I can make a further change and remove current_tm
altogether. I'll take a look this week; however that will make it even
harder to produce the old version.
Paolo
^ permalink raw reply [flat|nested] 14+ messages in thread