@ -45,8 +45,11 @@
# define DPRINTF_C(format, ...) do { } while (0)
# endif
# define NSEC_PER_SEC 1000000000LL
# define RTC_REINJECT_ON_ACK_COUNT 20
# define RTC_CLOCK_RATE 32768
# define UIP_HOLD_LENGTH (8 * NSEC_PER_SEC / 32768)
typedef struct RTCState {
ISADevice dev ;
@ -55,27 +58,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 )
@ -111,6 +127,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 ;
@ -176,6 +193,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 ;
@ -190,6 +301,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 :
@ -202,6 +314,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 :
@ -209,15 +322,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 ) ;
}
}
@ -232,6 +351,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 :
@ -280,10 +400,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 ;
@ -309,122 +432,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 ;
gmtime_r ( & guest_sec , & ret ) ;
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 ) ;
int64_t guest_nsec ;
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 / RTC_CLOCK_RATE 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 ) ;
if ( s - > cmos_data [ RTC_REG_B ] & REG_B_SET ) {
return 0 ;
}
}
static void rtc_update_second2 ( void * opaque )
{
RTCState * s = opaque ;
if ( ! ( s - > cmos_data [ RTC_REG_B ] & REG_B_SET ) ) {
rtc_copy_date ( s ) ;
}
/* 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 - UIP_HOLD_LENGTH ) ) {
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 - UIP_HOLD_LENGTH ) ) {
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 )
@ -442,15 +484,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 ) & &
@ -485,13 +540,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
@ -502,9 +550,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 ) ;
@ -513,9 +567,15 @@ static void rtc_set_date_from_host(ISADevice *dev)
static int rtc_post_load ( void * opaque , int version_id )
{
# ifdef TARGET_I386
RTCState * s = opaque ;
if ( version_id < = 2 ) {
rtc_set_time ( s ) ;
s - > offset = 0 ;
check_update_timer ( s ) ;
}
# ifdef TARGET_I386
if ( version_id > = 2 ) {
if ( s - > lost_tick_policy = = LOST_TICK_SLEW ) {
rtc_coalesced_timer_update ( s ) ;
@ -527,7 +587,7 @@ static int rtc_post_load(void *opaque, int version_id)
static const VMStateDescription vmstate_rtc = {
. name = " mc146818rtc " ,
. version_id = 2 ,
. version_id = 3 ,
. minimum_version_id = 1 ,
. minimum_version_id_old = 1 ,
. post_load = rtc_post_load ,
@ -543,11 +603,13 @@ static const VMStateDescription vmstate_rtc = {
VMSTATE_INT32 ( current_tm . tm_year , 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_UNUSED ( 3 * 8 ) ,
VMSTATE_UINT32_V ( irq_coalesced , RTCState , 2 ) ,
VMSTATE_UINT32_V ( period , RTCState , 2 ) ,
VMSTATE_UINT64_V ( base_rtc , RTCState , 3 ) ,
VMSTATE_UINT64_V ( last_update , RTCState , 3 ) ,
VMSTATE_INT64_V ( offset , RTCState , 3 ) ,
VMSTATE_TIMER_V ( update_timer , RTCState , 3 ) ,
VMSTATE_END_OF_LIST ( )
}
} ;
@ -558,9 +620,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 ) ;
@ -582,6 +643,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 ) ;
@ -607,6 +669,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 ) ;
@ -643,8 +706,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 ) ;
@ -652,14 +715,10 @@ 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 ) ;
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 " ,