static void rtc_set_time(RTCState *s) { struct tm *tm = &s->current_tm; struct domain *d = vrtc_domain(s); unsigned long before, after; /* XXX s_time_t */ ASSERT(spin_is_locked(&s->lock)); before = mktime(get_year(tm->tm_year), tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); tm->tm_sec = from_bcd(s, s->hw.cmos_data[RTC_SECONDS]); tm->tm_min = from_bcd(s, s->hw.cmos_data[RTC_MINUTES]); tm->tm_hour = convert_hour(s, s->hw.cmos_data[RTC_HOURS]); tm->tm_wday = from_bcd(s, s->hw.cmos_data[RTC_DAY_OF_WEEK]); tm->tm_mday = from_bcd(s, s->hw.cmos_data[RTC_DAY_OF_MONTH]); tm->tm_mon = from_bcd(s, s->hw.cmos_data[RTC_MONTH]) - 1; tm->tm_year = from_bcd(s, s->hw.cmos_data[RTC_YEAR]) + 100; after = mktime(get_year(tm->tm_year), tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); /* We use the guest's setting of the RTC to define the local-time * offset for this domain. */ d->time_offset_seconds += (after - before); update_domain_wallclock_time(d); /* Also tell qemu-dm about it so it will be remembered for next boot. */ send_timeoffset_req(after - before); }
static uint32_t rtc_ioport_read(RTCState *s, uint32_t addr) { int ret; struct domain *d = vrtc_domain(s); if ( (addr & 1) == 0 ) return 0xff; spin_lock(&s->lock); switch ( s->hw.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 not in set mode, adjust cmos before reading*/ if (!(s->hw.cmos_data[RTC_REG_B] & RTC_SET)) { s->current_tm = gmtime(get_localtime(d)); rtc_copy_date(s); } ret = s->hw.cmos_data[s->hw.cmos_index]; break; case RTC_REG_A: ret = s->hw.cmos_data[s->hw.cmos_index]; if ((s->use_timer == 0) && update_in_progress(s)) ret |= RTC_UIP; break; case RTC_REG_C: ret = s->hw.cmos_data[s->hw.cmos_index]; hvm_isa_irq_deassert(vrtc_domain(s), RTC_IRQ); s->hw.cmos_data[RTC_REG_C] = 0x00; check_update_timer(s); break; default: ret = s->hw.cmos_data[s->hw.cmos_index]; break; } spin_unlock(&s->lock); return ret; }
static void rtc_update_irq(RTCState *s) { ASSERT(spin_is_locked(&s->lock)); if ( rtc_mode_is(s, strict) && (s->hw.cmos_data[RTC_REG_C] & RTC_IRQF) ) return; /* IRQ is raised if any source is both raised & enabled */ if ( !(s->hw.cmos_data[RTC_REG_B] & s->hw.cmos_data[RTC_REG_C] & (RTC_PF | RTC_AF | RTC_UF)) ) return; s->hw.cmos_data[RTC_REG_C] |= RTC_IRQF; if ( rtc_mode_is(s, no_ack) ) hvm_isa_irq_deassert(vrtc_domain(s), RTC_IRQ); hvm_isa_irq_assert(vrtc_domain(s), RTC_IRQ); }
static int update_in_progress(RTCState *s) { uint64_t guest_usec; struct domain *d = vrtc_domain(s); if (s->hw.cmos_data[RTC_REG_B] & RTC_SET) return 0; guest_usec = get_localtime_us(d); /* UIP bit will be set at last 244us of every second. */ if ((guest_usec % USEC_PER_SEC) >= (USEC_PER_SEC - 244)) return 1; return 0; }
/* handle update-ended timer */ static void check_update_timer(RTCState *s) { uint64_t next_update_time, expire_time; uint64_t guest_usec; struct domain *d = vrtc_domain(s); stop_timer(&s->update_timer); stop_timer(&s->update_timer2); ASSERT(spin_is_locked(&s->lock)); if (!(s->hw.cmos_data[RTC_REG_C] & RTC_UF) && !(s->hw.cmos_data[RTC_REG_B] & RTC_SET)) { s->use_timer = 1; guest_usec = get_localtime_us(d) % USEC_PER_SEC; if (guest_usec >= (USEC_PER_SEC - 244)) { /* RTC is in update cycle */ s->hw.cmos_data[RTC_REG_A] |= RTC_UIP; next_update_time = (USEC_PER_SEC - guest_usec) * NS_PER_USEC; expire_time = NOW() + next_update_time; /* release lock before set timer */ spin_unlock(&s->lock); set_timer(&s->update_timer2, expire_time); /* fetch lock again */ spin_lock(&s->lock); } else { next_update_time = (USEC_PER_SEC - guest_usec - 244) * NS_PER_USEC; expire_time = NOW() + next_update_time; s->next_update_time = expire_time; /* release lock before set timer */ spin_unlock(&s->lock); set_timer(&s->update_timer, expire_time); /* fetch lock again */ spin_lock(&s->lock); } } else s->use_timer = 0; }
static void rtc_update_irq(RTCState *s) { struct domain *d = vrtc_domain(s); uint8_t irqf; ASSERT(spin_is_locked(&s->lock)); /* IRQ is raised if any source is both raised & enabled */ irqf = (s->hw.cmos_data[RTC_REG_B] & s->hw.cmos_data[RTC_REG_C] & (RTC_PF|RTC_AF|RTC_UF)) ? RTC_IRQF : 0; s->hw.cmos_data[RTC_REG_C] &= ~RTC_IRQF; s->hw.cmos_data[RTC_REG_C] |= irqf; hvm_isa_irq_deassert(d, RTC_IRQ); if ( irqf ) hvm_isa_irq_assert(d, RTC_IRQ); }
static void rtc_alarm_cb(void *opaque) { RTCState *s = opaque; struct domain *d = vrtc_domain(s); spin_lock(&s->lock); if (!(s->hw.cmos_data[RTC_REG_B] & RTC_SET)) { s->hw.cmos_data[RTC_REG_C] |= RTC_AF; /* alarm interrupt */ if (s->hw.cmos_data[RTC_REG_B] & RTC_AIE) { s->hw.cmos_data[RTC_REG_C] |= RTC_IRQF; hvm_isa_irq_deassert(d, RTC_IRQ); hvm_isa_irq_assert(d, RTC_IRQ); } alarm_timer_update(s); } spin_unlock(&s->lock); }
static void rtc_update_timer2(void *opaque) { RTCState *s = opaque; struct domain *d = vrtc_domain(s); spin_lock(&s->lock); if (!(s->hw.cmos_data[RTC_REG_B] & RTC_SET)) { s->hw.cmos_data[RTC_REG_C] |= RTC_UF; s->hw.cmos_data[RTC_REG_A] &= ~RTC_UIP; if ((s->hw.cmos_data[RTC_REG_B] & RTC_UIE)) { s->hw.cmos_data[RTC_REG_C] |= RTC_IRQF; hvm_isa_irq_deassert(d, RTC_IRQ); hvm_isa_irq_assert(d, RTC_IRQ); } check_update_timer(s); } spin_unlock(&s->lock); }
static int rtc_ioport_write(void *opaque, uint32_t addr, uint32_t data) { RTCState *s = opaque; struct domain *d = vrtc_domain(s); uint32_t orig; spin_lock(&s->lock); if ( (addr & 1) == 0 ) { data &= 0x7f; s->hw.cmos_index = data; spin_unlock(&s->lock); return (data < RTC_CMOS_SIZE); } if ( s->hw.cmos_index >= RTC_CMOS_SIZE ) { spin_unlock(&s->lock); return 0; } orig = s->hw.cmos_data[s->hw.cmos_index]; switch ( s->hw.cmos_index ) { case RTC_SECONDS_ALARM: case RTC_MINUTES_ALARM: case RTC_HOURS_ALARM: s->hw.cmos_data[s->hw.cmos_index] = data; alarm_timer_update(s); break; 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 in set mode, just write the register */ if ( (s->hw.cmos_data[RTC_REG_B] & RTC_SET) ) s->hw.cmos_data[s->hw.cmos_index] = data; else { /* Fetch the current time and update just this field. */ s->current_tm = gmtime(get_localtime(d)); rtc_copy_date(s); s->hw.cmos_data[s->hw.cmos_index] = data; rtc_set_time(s); } alarm_timer_update(s); break; case RTC_REG_A: /* UIP bit is read only */ s->hw.cmos_data[RTC_REG_A] = (data & ~RTC_UIP) | (orig & RTC_UIP); if ( (data ^ orig) & ~RTC_UIP ) rtc_timer_update(s); break; case RTC_REG_B: if ( data & RTC_SET ) { /* set mode: reset UIP mode */ s->hw.cmos_data[RTC_REG_A] &= ~RTC_UIP; /* adjust cmos before stopping */ if (!(orig & RTC_SET)) { s->current_tm = gmtime(get_localtime(d)); rtc_copy_date(s); } } else { /* if disabling set mode, update the time */ if ( orig & RTC_SET ) rtc_set_time(s); } check_for_pf_ticks(s); s->hw.cmos_data[RTC_REG_B] = data; /* * If the interrupt is already set when the interrupt becomes * enabled, raise an interrupt immediately. */ rtc_update_irq(s); if ( (data ^ orig) & RTC_PIE ) { TRACE_0D(TRC_HVM_EMUL_RTC_STOP_TIMER); destroy_periodic_time(&s->pt); s->period = 0; rtc_timer_update(s); } if ( (data ^ orig) & RTC_SET ) check_update_timer(s); if ( (data ^ orig) & (RTC_24H | RTC_DM_BINARY | RTC_SET) ) alarm_timer_update(s); break; case RTC_REG_C: case RTC_REG_D: /* cannot write to them */ break; } spin_unlock(&s->lock); return 1; }
/* handle alarm timer */ static void alarm_timer_update(RTCState *s) { uint64_t next_update_time, next_alarm_sec; uint64_t expire_time; int32_t alarm_sec, alarm_min, alarm_hour, cur_hour, cur_min, cur_sec; int32_t hour, min; struct domain *d = vrtc_domain(s); ASSERT(spin_is_locked(&s->lock)); stop_timer(&s->alarm_timer); if (!(s->hw.cmos_data[RTC_REG_C] & RTC_AF) && !(s->hw.cmos_data[RTC_REG_B] & RTC_SET)) { s->current_tm = gmtime(get_localtime(d)); rtc_copy_date(s); alarm_sec = from_bcd(s, s->hw.cmos_data[RTC_SECONDS_ALARM]); alarm_min = from_bcd(s, s->hw.cmos_data[RTC_MINUTES_ALARM]); alarm_hour = convert_hour(s, s->hw.cmos_data[RTC_HOURS_ALARM]); cur_sec = from_bcd(s, s->hw.cmos_data[RTC_SECONDS]); cur_min = from_bcd(s, s->hw.cmos_data[RTC_MINUTES]); cur_hour = convert_hour(s, s->hw.cmos_data[RTC_HOURS]); next_update_time = USEC_PER_SEC - (get_localtime_us(d) % USEC_PER_SEC); next_update_time = next_update_time * NS_PER_USEC + NOW(); if ((s->hw.cmos_data[RTC_HOURS_ALARM] & 0xc0) == 0xc0) { if ((s->hw.cmos_data[RTC_MINUTES_ALARM] & 0xc0) == 0xc0) { if ((s->hw.cmos_data[RTC_SECONDS_ALARM] & 0xc0) == 0xc0) next_alarm_sec = 1; else if (cur_sec < alarm_sec) next_alarm_sec = alarm_sec - cur_sec; else next_alarm_sec = alarm_sec + SEC_PER_MIN - cur_sec; } else { if (cur_min < alarm_min) { min = alarm_min - cur_min; next_alarm_sec = min * SEC_PER_MIN - cur_sec; if ((s->hw.cmos_data[RTC_SECONDS_ALARM] & 0xc0) == 0xc0) next_alarm_sec += 0; else next_alarm_sec += alarm_sec; } else if (cur_min == alarm_min) { if ((s->hw.cmos_data[RTC_SECONDS_ALARM] & 0xc0) == 0xc0) next_alarm_sec = 1; else if (cur_sec < alarm_sec) next_alarm_sec = alarm_sec - cur_sec; else { min = alarm_min + MIN_PER_HOUR - cur_min; next_alarm_sec = alarm_sec + min * SEC_PER_MIN - cur_sec; } } else { min = alarm_min + MIN_PER_HOUR - cur_min; next_alarm_sec = min * SEC_PER_MIN - cur_sec; if ((s->hw.cmos_data[RTC_SECONDS_ALARM] & 0xc0) == 0xc0) next_alarm_sec += 0; else next_alarm_sec += alarm_sec; } } } else { if (cur_hour < alarm_hour) { hour = alarm_hour - cur_hour; next_alarm_sec = hour * SEC_PER_HOUR - cur_min * SEC_PER_MIN - cur_sec; if ((s->hw.cmos_data[RTC_MINUTES_ALARM] & 0xc0) == 0xc0) { if ((s->hw.cmos_data[RTC_SECONDS_ALARM] & 0xc0) == 0xc0) next_alarm_sec += 0; else next_alarm_sec += alarm_sec; } else { next_alarm_sec += alarm_min * SEC_PER_MIN; if ((s->hw.cmos_data[RTC_SECONDS_ALARM] & 0xc0) == 0xc0) next_alarm_sec += 0; else next_alarm_sec += alarm_sec; } } else if (cur_hour == alarm_hour) { if ((s->hw.cmos_data[RTC_MINUTES_ALARM] & 0xc0) == 0xc0) { if ((s->hw.cmos_data[RTC_SECONDS_ALARM] & 0xc0) == 0xc0) next_alarm_sec = 1; else if (cur_sec < alarm_sec) next_alarm_sec = alarm_sec - cur_sec; else next_alarm_sec = alarm_sec + SEC_PER_MIN - cur_sec; } else if (cur_min < alarm_min) { min = alarm_min - cur_min; next_alarm_sec = min * SEC_PER_MIN - cur_sec; if ((s->hw.cmos_data[RTC_SECONDS_ALARM] & 0xc0) == 0xc0) next_alarm_sec += 0; else next_alarm_sec += alarm_sec; } else if (cur_min == alarm_min) { if ((s->hw.cmos_data[RTC_SECONDS_ALARM] & 0xc0) == 0xc0) next_alarm_sec = 1; else if (cur_sec < alarm_sec) next_alarm_sec = alarm_sec - cur_sec; else { hour = alarm_hour + HOUR_PER_DAY - cur_hour; next_alarm_sec = hour * SEC_PER_HOUR - cur_min * SEC_PER_MIN - cur_sec; next_alarm_sec += alarm_min * SEC_PER_MIN + alarm_sec; } } else { hour = alarm_hour + HOUR_PER_DAY - cur_hour; next_alarm_sec = hour * SEC_PER_HOUR - cur_min * SEC_PER_MIN - cur_sec; next_alarm_sec += alarm_min * SEC_PER_MIN; if ((s->hw.cmos_data[RTC_SECONDS_ALARM] & 0xc0) == 0xc0) next_alarm_sec += 0; else next_alarm_sec += alarm_sec; } } else { hour = alarm_hour + HOUR_PER_DAY - cur_hour; next_alarm_sec = hour * SEC_PER_HOUR - cur_min * SEC_PER_MIN - cur_sec; if ((s->hw.cmos_data[RTC_MINUTES_ALARM] & 0xc0) == 0xc0) { if ((s->hw.cmos_data[RTC_SECONDS_ALARM] & 0xc0) == 0xc0) next_alarm_sec += 0; else next_alarm_sec += alarm_sec; } else { next_alarm_sec += alarm_min * SEC_PER_MIN; if ((s->hw.cmos_data[RTC_SECONDS_ALARM] & 0xc0) == 0xc0) next_alarm_sec += 0; else next_alarm_sec += alarm_sec; } } } expire_time = (next_alarm_sec - 1) * NS_PER_SEC + next_update_time; /* release lock before set timer */ spin_unlock(&s->lock); set_timer(&s->alarm_timer, expire_time); /* fetch lock again */ spin_lock(&s->lock); } }
static int rtc_ioport_write(void *opaque, uint32_t addr, uint32_t data) { RTCState *s = opaque; struct domain *d = vrtc_domain(s); uint32_t orig; spin_lock(&s->lock); if ( (addr & 1) == 0 ) { data &= 0x7f; s->hw.cmos_index = data; spin_unlock(&s->lock); return (data < RTC_CMOS_SIZE); } if ( s->hw.cmos_index >= RTC_CMOS_SIZE ) { spin_unlock(&s->lock); return 0; } orig = s->hw.cmos_data[s->hw.cmos_index]; switch ( s->hw.cmos_index ) { case RTC_SECONDS_ALARM: case RTC_MINUTES_ALARM: case RTC_HOURS_ALARM: s->hw.cmos_data[s->hw.cmos_index] = data; alarm_timer_update(s); break; 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: s->hw.cmos_data[s->hw.cmos_index] = data; /* if in set mode, do not update the time */ if ( !(s->hw.cmos_data[RTC_REG_B] & RTC_SET) ) rtc_set_time(s); alarm_timer_update(s); break; case RTC_REG_A: /* UIP bit is read only */ s->hw.cmos_data[RTC_REG_A] = (data & ~RTC_UIP) | (orig & RTC_UIP); if ( (data ^ orig) & ~RTC_UIP ) rtc_timer_update(s); break; case RTC_REG_B: if ( data & RTC_SET ) { /* set mode: reset UIP mode */ s->hw.cmos_data[RTC_REG_A] &= ~RTC_UIP; /* adjust cmos before stopping */ if (!(s->hw.cmos_data[RTC_REG_B] & RTC_SET)) { s->current_tm = gmtime(get_localtime(d)); rtc_copy_date(s); } } else { /* if disabling set mode, update the time */ if ( s->hw.cmos_data[RTC_REG_B] & RTC_SET ) rtc_set_time(s); } /* if the interrupt is already set when the interrupt become * enabled, raise an interrupt immediately*/ if ((data & RTC_UIE) && !(s->hw.cmos_data[RTC_REG_B] & RTC_UIE)) if (s->hw.cmos_data[RTC_REG_C] & RTC_UF) { hvm_isa_irq_deassert(d, RTC_IRQ); hvm_isa_irq_assert(d, RTC_IRQ); } s->hw.cmos_data[RTC_REG_B] = data; if ( (data ^ orig) & RTC_PIE ) rtc_timer_update(s); check_update_timer(s); alarm_timer_update(s); break; case RTC_REG_C: case RTC_REG_D: /* cannot write to them */ break; } spin_unlock(&s->lock); return 1; }