summaryrefslogtreecommitdiff
path: root/src/VBox/Devices/PC/DevRTC.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Devices/PC/DevRTC.cpp')
-rw-r--r--src/VBox/Devices/PC/DevRTC.cpp725
1 files changed, 399 insertions, 326 deletions
diff --git a/src/VBox/Devices/PC/DevRTC.cpp b/src/VBox/Devices/PC/DevRTC.cpp
index f894993dc..6a5a9e40d 100644
--- a/src/VBox/Devices/PC/DevRTC.cpp
+++ b/src/VBox/Devices/PC/DevRTC.cpp
@@ -1,10 +1,10 @@
-/* $Id: DevRTC.cpp $ */
+/* $Id: DevRTC.cpp 37526 2011-06-17 10:17:38Z vboxsync $ */
/** @file
* Motorola MC146818 RTC/CMOS Device with PIIX4 extensions.
*/
/*
- * Copyright (C) 2006-2007 Oracle Corporation
+ * Copyright (C) 2006-2011 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
@@ -203,216 +203,328 @@ struct RTCState {
};
#ifndef VBOX_DEVICE_STRUCT_TESTCASE
-static void rtc_set_time(RTCState *s);
-static void rtc_copy_date(RTCState *s);
-static void rtc_timer_update(RTCState *s, int64_t current_time)
+static void rtc_timer_update(RTCState *pThis, int64_t current_time)
{
int period_code, period;
uint64_t cur_clock, next_irq_clock;
uint32_t freq;
- period_code = s->cmos_data[RTC_REG_A] & 0x0f;
- if (period_code != 0 &&
- (s->cmos_data[RTC_REG_B] & REG_B_PIE)) {
+ Assert(TMTimerIsLockOwner(pThis->CTX_SUFF(pPeriodicTimer)));
+ Assert(PDMCritSectIsOwner(pThis->CTX_SUFF(pDevIns)->CTX_SUFF(pCritSectRo)));
+
+ period_code = pThis->cmos_data[RTC_REG_A] & 0x0f;
+ if ( period_code != 0
+ && (pThis->cmos_data[RTC_REG_B] & REG_B_PIE))
+ {
if (period_code <= 2)
period_code += 7;
/* period in 32 kHz cycles */
period = 1 << (period_code - 1);
/* compute 32 kHz clock */
- freq = TMTimerGetFreq(s->CTX_SUFF(pPeriodicTimer));
+ freq = TMTimerGetFreq(pThis->CTX_SUFF(pPeriodicTimer));
cur_clock = ASMMultU64ByU32DivByU32(current_time, 32768, freq);
next_irq_clock = (cur_clock & ~(uint64_t)(period - 1)) + period;
- s->next_periodic_time = ASMMultU64ByU32DivByU32(next_irq_clock, freq, 32768) + 1;
- TMTimerSet(s->CTX_SUFF(pPeriodicTimer), s->next_periodic_time);
+ pThis->next_periodic_time = ASMMultU64ByU32DivByU32(next_irq_clock, freq, 32768) + 1;
+ TMTimerSet(pThis->CTX_SUFF(pPeriodicTimer), pThis->next_periodic_time);
#ifdef IN_RING3
- if (RT_UNLIKELY(period != s->CurLogPeriod))
+ if (RT_UNLIKELY(period != pThis->CurLogPeriod))
#else
- if (RT_UNLIKELY(period != s->CurHintPeriod))
+ if (RT_UNLIKELY(period != pThis->CurHintPeriod))
#endif
{
#ifdef IN_RING3
- if (s->cRelLogEntries++ < 64)
+ if (pThis->cRelLogEntries++ < 64)
LogRel(("RTC: period=%#x (%d) %u Hz\n", period, period, _32K / period));
- s->CurLogPeriod = period;
+ pThis->CurLogPeriod = period;
#endif
- s->CurHintPeriod = period;
- TMTimerSetFrequencyHint(s->CTX_SUFF(pPeriodicTimer), _32K / period);
+ pThis->CurHintPeriod = period;
+ TMTimerSetFrequencyHint(pThis->CTX_SUFF(pPeriodicTimer), _32K / period);
}
- } else {
- if (TMTimerIsActive(s->CTX_SUFF(pPeriodicTimer)) && s->cRelLogEntries++ < 64)
+ }
+ else
+ {
+ if (TMTimerIsActive(pThis->CTX_SUFF(pPeriodicTimer)) && pThis->cRelLogEntries++ < 64)
LogRel(("RTC: stopped the periodic timer\n"));
- TMTimerStop(s->CTX_SUFF(pPeriodicTimer));
+ TMTimerStop(pThis->CTX_SUFF(pPeriodicTimer));
}
}
+
static void rtc_raise_irq(RTCState* pThis, uint32_t iLevel)
{
if (!pThis->fDisabledByHpet)
PDMDevHlpISASetIrq(pThis->CTX_SUFF(pDevIns), pThis->irq, iLevel);
}
-static void rtc_periodic_timer(void *opaque)
+
+DECLINLINE(int) to_bcd(RTCState *pThis, int a)
{
- RTCState *s = (RTCState*)opaque;
+ if (pThis->cmos_data[RTC_REG_B] & 0x04)
+ return a;
+ return ((a / 10) << 4) | (a % 10);
+}
- rtc_timer_update(s, s->next_periodic_time);
- s->cmos_data[RTC_REG_C] |= 0xc0;
- rtc_raise_irq(s, 1);
+DECLINLINE(int) from_bcd(RTCState *pThis, int a)
+{
+ if (pThis->cmos_data[RTC_REG_B] & 0x04)
+ return a;
+ return ((a >> 4) * 10) + (a & 0x0f);
}
-static void cmos_ioport_write(void *opaque, uint32_t addr, uint32_t data)
+
+static void rtc_set_time(RTCState *pThis)
{
- RTCState *s = (RTCState*)opaque;
- uint32_t bank;
+ struct my_tm *tm = &pThis->current_tm;
- bank = (addr >> 1) & 1;
- if ((addr & 1) == 0) {
- s->cmos_index[bank] = (data & 0x7f) + (bank * 128);
- } else {
- Log(("CMOS: Write bank %d idx %#04x: %#04x (old %#04x)\n", bank,
- s->cmos_index[bank], data, s->cmos_data[s->cmos_index[bank]]));
- switch(s->cmos_index[bank]) {
- case RTC_SECONDS_ALARM:
- case RTC_MINUTES_ALARM:
- case RTC_HOURS_ALARM:
- s->cmos_data[s->cmos_index[0]] = data;
- 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->cmos_data[s->cmos_index[0]] = data;
- /* if in set mode, do not update the time */
- if (!(s->cmos_data[RTC_REG_B] & REG_B_SET)) {
- rtc_set_time(s);
- }
- break;
- case RTC_REG_A:
- /* 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, TMTimerGet(s->CTX_SUFF(pPeriodicTimer)));
- break;
- case RTC_REG_B:
- if (data & REG_B_SET) {
- /* set mode: reset UIP mode */
- s->cmos_data[RTC_REG_A] &= ~REG_A_UIP;
-#if 0 /* This is probably wrong as it breaks changing the time/date in OS/2. */
- data &= ~REG_B_UIE;
-#endif
- } else {
- /* if disabling set mode, update the time */
- if (s->cmos_data[RTC_REG_B] & REG_B_SET) {
- rtc_set_time(s);
- }
- }
- s->cmos_data[RTC_REG_B] = data;
- rtc_timer_update(s, TMTimerGet(s->CTX_SUFF(pPeriodicTimer)));
- break;
- case RTC_REG_C:
- case RTC_REG_D:
- /* cannot write to them */
- break;
- default:
- s->cmos_data[s->cmos_index[bank]] = data;
- break;
- }
- }
+ tm->tm_sec = from_bcd(pThis, pThis->cmos_data[RTC_SECONDS]);
+ tm->tm_min = from_bcd(pThis, pThis->cmos_data[RTC_MINUTES]);
+ tm->tm_hour = from_bcd(pThis, pThis->cmos_data[RTC_HOURS] & 0x7f);
+ if ( !(pThis->cmos_data[RTC_REG_B] & 0x02)
+ && (pThis->cmos_data[RTC_HOURS] & 0x80))
+ tm->tm_hour += 12;
+ tm->tm_wday = from_bcd(pThis, pThis->cmos_data[RTC_DAY_OF_WEEK]);
+ tm->tm_mday = from_bcd(pThis, pThis->cmos_data[RTC_DAY_OF_MONTH]);
+ tm->tm_mon = from_bcd(pThis, pThis->cmos_data[RTC_MONTH]) - 1;
+ tm->tm_year = from_bcd(pThis, pThis->cmos_data[RTC_YEAR]) + 100;
}
-static inline int to_bcd(RTCState *s, int a)
+
+/* -=-=-=-=-=- I/O Port Handlers -=-=-=-=-=- */
+
+
+/**
+ * Port I/O Handler for IN operations.
+ *
+ * @returns VBox status code.
+ *
+ * @param pDevIns The device instance.
+ * @param pvUser User argument - ignored.
+ * @param uPort Port number used for the IN operation.
+ * @param pu32 Where to store the result.
+ * @param cb Number of bytes read.
+ */
+PDMBOTHCBDECL(int) rtcIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
{
- if (s->cmos_data[RTC_REG_B] & 0x04) {
- return a;
- } else {
- return ((a / 10) << 4) | (a % 10);
+ NOREF(pvUser);
+ if (cb != 1)
+ return VERR_IOM_IOPORT_UNUSED;
+
+ RTCState *pThis = PDMINS_2_DATA(pDevIns, RTCState *);
+ if ((Port & 1) == 0)
+ *pu32 = 0xff;
+ else
+ {
+ unsigned bank = (Port >> 1) & 1;
+ switch (pThis->cmos_index[bank])
+ {
+ 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:
+ *pu32 = pThis->cmos_data[pThis->cmos_index[0]];
+ break;
+
+ case RTC_REG_A:
+ *pu32 = pThis->cmos_data[pThis->cmos_index[0]];
+ break;
+
+ case RTC_REG_C:
+ *pu32 = pThis->cmos_data[pThis->cmos_index[0]];
+ rtc_raise_irq(pThis, 0);
+ pThis->cmos_data[RTC_REG_C] = 0x00;
+ break;
+
+ default:
+ *pu32 = pThis->cmos_data[pThis->cmos_index[bank]];
+ break;
+ }
+
+ Log(("CMOS: Read bank %d idx %#04x: %#04x\n", bank, pThis->cmos_index[bank], *pu32));
}
+
+ return VINF_SUCCESS;
}
-static inline int from_bcd(RTCState *s, int a)
+
+/**
+ * Port I/O Handler for OUT operations.
+ *
+ * @returns VBox status code.
+ *
+ * @param pDevIns The device instance.
+ * @param pvUser User argument - ignored.
+ * @param uPort Port number used for the IN operation.
+ * @param u32 The value to output.
+ * @param cb The value size in bytes.
+ */
+PDMBOTHCBDECL(int) rtcIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
{
- if (s->cmos_data[RTC_REG_B] & 0x04) {
- return a;
- } else {
- return ((a >> 4) * 10) + (a & 0x0f);
+ NOREF(pvUser);
+ if (cb != 1)
+ return VINF_SUCCESS;
+
+ RTCState *pThis = PDMINS_2_DATA(pDevIns, RTCState *);
+ uint32_t bank = (Port >> 1) & 1;
+ if ((Port & 1) == 0)
+ {
+ pThis->cmos_index[bank] = (u32 & 0x7f) + (bank * 128);
}
-}
+ else
+ {
+ Log(("CMOS: Write bank %d idx %#04x: %#04x (old %#04x)\n", bank,
+ pThis->cmos_index[bank], u32, pThis->cmos_data[pThis->cmos_index[bank]]));
-static void rtc_set_time(RTCState *s)
-{
- struct my_tm *tm = &s->current_tm;
+ int const idx = pThis->cmos_index[bank];
+ switch (idx)
+ {
+ case RTC_SECONDS_ALARM:
+ case RTC_MINUTES_ALARM:
+ case RTC_HOURS_ALARM:
+ pThis->cmos_data[pThis->cmos_index[0]] = u32;
+ 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:
+ pThis->cmos_data[pThis->cmos_index[0]] = u32;
+ /* if in set mode, do not update the time */
+ if (!(pThis->cmos_data[RTC_REG_B] & REG_B_SET))
+ rtc_set_time(pThis);
+ break;
+
+ case RTC_REG_A:
+ case RTC_REG_B:
+ {
+ /* We need to acquire the clock lock, because of lock ordering
+ issues this means having to release the device lock. Since
+ we're letting IOM do the locking, we must not return without
+ holding the device lock.*/
+ PDMCritSectLeave(pThis->CTX_SUFF(pDevIns)->CTX_SUFF(pCritSectRo));
+ int rc1 = TMTimerLock(pThis->CTX_SUFF(pPeriodicTimer), VINF_SUCCESS /* must get it */);
+ int rc2 = PDMCritSectEnter(pThis->CTX_SUFF(pDevIns)->CTX_SUFF(pCritSectRo), VINF_SUCCESS /* must get it */);
+ AssertRCReturn(rc1, rc1);
+ AssertRCReturnStmt(rc2, TMTimerUnlock(pThis->CTX_SUFF(pPeriodicTimer)), rc2);
+
+ if (idx == RTC_REG_A)
+ {
+ /* UIP bit is read only */
+ pThis->cmos_data[RTC_REG_A] = (u32 & ~REG_A_UIP)
+ | (pThis->cmos_data[RTC_REG_A] & REG_A_UIP);
+ }
+ else
+ {
+ if (u32 & REG_B_SET)
+ {
+ /* set mode: reset UIP mode */
+ pThis->cmos_data[RTC_REG_A] &= ~REG_A_UIP;
+#if 0 /* This is probably wrong as it breaks changing the time/date in OS/2. */
+ u32 &= ~REG_B_UIE;
+#endif
+ }
+ else
+ {
+ /* if disabling set mode, update the time */
+ if (pThis->cmos_data[RTC_REG_B] & REG_B_SET)
+ rtc_set_time(pThis);
+ }
+ pThis->cmos_data[RTC_REG_B] = u32;
+ }
- tm->tm_sec = from_bcd(s, s->cmos_data[RTC_SECONDS]);
- tm->tm_min = from_bcd(s, s->cmos_data[RTC_MINUTES]);
- tm->tm_hour = from_bcd(s, s->cmos_data[RTC_HOURS] & 0x7f);
- if (!(s->cmos_data[RTC_REG_B] & 0x02) &&
- (s->cmos_data[RTC_HOURS] & 0x80)) {
- tm->tm_hour += 12;
+ rtc_timer_update(pThis, TMTimerGet(pThis->CTX_SUFF(pPeriodicTimer)));
+
+ TMTimerUnlock(pThis->CTX_SUFF(pPeriodicTimer));
+ /* the caller leaves the other lock. */
+ break;
+ }
+
+ case RTC_REG_C:
+ case RTC_REG_D:
+ /* cannot write to them */
+ break;
+
+ default:
+ pThis->cmos_data[pThis->cmos_index[bank]] = u32;
+ break;
+ }
}
- tm->tm_wday = from_bcd(s, s->cmos_data[RTC_DAY_OF_WEEK]);
- tm->tm_mday = from_bcd(s, s->cmos_data[RTC_DAY_OF_MONTH]);
- tm->tm_mon = from_bcd(s, s->cmos_data[RTC_MONTH]) - 1;
- tm->tm_year = from_bcd(s, s->cmos_data[RTC_YEAR]) + 100;
+
+ return VINF_SUCCESS;
}
-static void rtc_copy_date(RTCState *s)
+#ifdef IN_RING3
+
+/* -=-=-=-=-=- Timers and their support code -=-=-=-=-=- */
+
+
+/**
+ * Device timer callback function, periodic.
+ *
+ * @param pDevIns Device instance of the device which registered the timer.
+ * @param pTimer The timer handle.
+ * @param pvUser Pointer to the RTC state.
+ */
+static DECLCALLBACK(void) rtcTimerPeriodic(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
{
- const struct my_tm *tm = &s->current_tm;
+ RTCState *pThis = PDMINS_2_DATA(pDevIns, RTCState *);
+ Assert(TMTimerIsLockOwner(pThis->CTX_SUFF(pPeriodicTimer)));
+ Assert(PDMCritSectIsOwner(pThis->CTX_SUFF(pDevIns)->CTX_SUFF(pCritSectRo)));
- s->cmos_data[RTC_SECONDS] = to_bcd(s, tm->tm_sec);
- s->cmos_data[RTC_MINUTES] = to_bcd(s, tm->tm_min);
- if (s->cmos_data[RTC_REG_B] & 0x02) {
- /* 24 hour format */
- s->cmos_data[RTC_HOURS] = to_bcd(s, tm->tm_hour);
- } else {
- /* 12 hour format */
- s->cmos_data[RTC_HOURS] = to_bcd(s, tm->tm_hour % 12);
- if (tm->tm_hour >= 12)
- s->cmos_data[RTC_HOURS] |= 0x80;
- }
- s->cmos_data[RTC_DAY_OF_WEEK] = to_bcd(s, tm->tm_wday);
- s->cmos_data[RTC_DAY_OF_MONTH] = to_bcd(s, tm->tm_mday);
- s->cmos_data[RTC_MONTH] = to_bcd(s, tm->tm_mon + 1);
- s->cmos_data[RTC_YEAR] = to_bcd(s, tm->tm_year % 100);
+ rtc_timer_update(pThis, pThis->next_periodic_time);
+ pThis->cmos_data[RTC_REG_C] |= 0xc0;
+
+ rtc_raise_irq(pThis, 1);
}
+
/* month is between 0 and 11. */
static int get_days_in_month(int month, int year)
{
- static const int days_tab[12] = {
+ 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 (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 my_tm *tm)
{
int days_in_month;
tm->tm_sec++;
- if ((unsigned)tm->tm_sec >= 60) {
+ if ((unsigned)tm->tm_sec >= 60)
+ {
tm->tm_sec = 0;
tm->tm_min++;
- if ((unsigned)tm->tm_min >= 60) {
+ if ((unsigned)tm->tm_min >= 60)
+ {
tm->tm_min = 0;
tm->tm_hour++;
- if ((unsigned)tm->tm_hour >= 24) {
+ if ((unsigned)tm->tm_hour >= 24)
+ {
tm->tm_hour = 0;
/* next day */
tm->tm_wday++;
@@ -421,12 +533,14 @@ static void rtc_next_second(struct my_tm *tm)
days_in_month = get_days_in_month(tm->tm_mon,
tm->tm_year + 1900);
tm->tm_mday++;
- if (tm->tm_mday < 1) {
+ if (tm->tm_mday < 1)
tm->tm_mday = 1;
- } else if (tm->tm_mday > days_in_month) {
+ else if (tm->tm_mday > days_in_month)
+ {
tm->tm_mday = 1;
tm->tm_mon++;
- if (tm->tm_mon >= 12) {
+ if (tm->tm_mon >= 12)
+ {
tm->tm_mon = 0;
tm->tm_year++;
}
@@ -437,201 +551,119 @@ static void rtc_next_second(struct my_tm *tm)
}
-static void rtc_update_second(void *opaque)
+/**
+ * Device timer callback function, second.
+ *
+ * @param pDevIns Device instance of the device which registered the timer.
+ * @param pTimer The timer handle.
+ * @param pvUser Pointer to the RTC state.
+ */
+static DECLCALLBACK(void) rtcTimerSecond(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
{
- RTCState *s = (RTCState*)opaque;
+ RTCState *pThis = PDMINS_2_DATA(pDevIns, RTCState *);
+ Assert(TMTimerIsLockOwner(pThis->CTX_SUFF(pPeriodicTimer)));
+ Assert(PDMCritSectIsOwner(pThis->CTX_SUFF(pDevIns)->CTX_SUFF(pCritSectRo)));
/* 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 += TMTimerGetFreq(s->CTX_SUFF(pSecondTimer));
- TMTimerSet(s->CTX_SUFF(pSecondTimer), s->next_second_time);
- } else {
- rtc_next_second(&s->current_tm);
+ if ((pThis->cmos_data[RTC_REG_A] & 0x70) != 0x20)
+ {
+ pThis->next_second_time += TMTimerGetFreq(pThis->CTX_SUFF(pSecondTimer));
+ TMTimerSet(pThis->CTX_SUFF(pSecondTimer), pThis->next_second_time);
+ }
+ else
+ {
+ rtc_next_second(&pThis->current_tm);
- if (!(s->cmos_data[RTC_REG_B] & REG_B_SET)) {
+ if (!(pThis->cmos_data[RTC_REG_B] & REG_B_SET))
+ {
/* update in progress bit */
- Log2(("RTC: UIP %x -> 1\n", !!(s->cmos_data[RTC_REG_A] & REG_A_UIP)));
- s->cmos_data[RTC_REG_A] |= REG_A_UIP;
+ Log2(("RTC: UIP %x -> 1\n", !!(pThis->cmos_data[RTC_REG_A] & REG_A_UIP)));
+ pThis->cmos_data[RTC_REG_A] |= REG_A_UIP;
}
/* 244140 ns = 8 / 32768 seconds */
- uint64_t delay = TMTimerFromNano(s->CTX_SUFF(pSecondTimer2), 244140);
- TMTimerSet(s->CTX_SUFF(pSecondTimer2), s->next_second_time + delay);
+ uint64_t delay = TMTimerFromNano(pThis->CTX_SUFF(pSecondTimer2), 244140);
+ TMTimerSet(pThis->CTX_SUFF(pSecondTimer2), pThis->next_second_time + delay);
}
}
-static void rtc_update_second2(void *opaque)
-{
- RTCState *s = (RTCState*)opaque;
-
- if (!(s->cmos_data[RTC_REG_B] & REG_B_SET)) {
- rtc_copy_date(s);
- }
-
- /* check alarm */
- if (s->cmos_data[RTC_REG_B] & REG_B_AIE) {
- if (((s->cmos_data[RTC_SECONDS_ALARM] & 0xc0) == 0xc0 ||
- from_bcd(s, s->cmos_data[RTC_SECONDS_ALARM]) == s->current_tm.tm_sec) &&
- ((s->cmos_data[RTC_MINUTES_ALARM] & 0xc0) == 0xc0 ||
- from_bcd(s, s->cmos_data[RTC_MINUTES_ALARM]) == s->current_tm.tm_min) &&
- ((s->cmos_data[RTC_HOURS_ALARM] & 0xc0) == 0xc0 ||
- from_bcd(s, s->cmos_data[RTC_HOURS_ALARM]) == s->current_tm.tm_hour)) {
-
- s->cmos_data[RTC_REG_C] |= 0xa0;
- rtc_raise_irq(s, 1);
- }
- }
-
- /* update ended interrupt */
- if (s->cmos_data[RTC_REG_B] & REG_B_UIE) {
- s->cmos_data[RTC_REG_C] |= 0x90;
- rtc_raise_irq(s, 1);
- }
- /* clear update in progress bit */
- Log2(("RTC: UIP %x -> 0\n", !!(s->cmos_data[RTC_REG_A] & REG_A_UIP)));
- s->cmos_data[RTC_REG_A] &= ~REG_A_UIP;
-
- s->next_second_time += TMTimerGetFreq(s->CTX_SUFF(pSecondTimer));
- TMTimerSet(s->CTX_SUFF(pSecondTimer), s->next_second_time);
-}
-
-static uint32_t cmos_ioport_read(void *opaque, uint32_t addr)
+/* Used by rtc_set_date and rtcTimerSecond2. */
+static void rtc_copy_date(RTCState *pThis)
{
- RTCState *s = (RTCState*)opaque;
- int ret;
- unsigned bank;
+ const struct my_tm *tm = &pThis->current_tm;
- bank = (addr >> 1) & 1;
- if ((addr & 1) == 0) {
- return 0xff;
- } else {
- switch(s->cmos_index[bank]) {
- 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:
- ret = s->cmos_data[s->cmos_index[0]];
- break;
- case RTC_REG_A:
- ret = s->cmos_data[s->cmos_index[0]];
- break;
- case RTC_REG_C:
- ret = s->cmos_data[s->cmos_index[0]];
- rtc_raise_irq(s, 0);
- s->cmos_data[RTC_REG_C] = 0x00;
- break;
- default:
- ret = s->cmos_data[s->cmos_index[bank]];
- break;
- }
- Log(("CMOS: Read bank %d idx %#04x: %#04x\n", bank, s->cmos_index[bank], ret));
- return ret;
+ pThis->cmos_data[RTC_SECONDS] = to_bcd(pThis, tm->tm_sec);
+ pThis->cmos_data[RTC_MINUTES] = to_bcd(pThis, tm->tm_min);
+ if (pThis->cmos_data[RTC_REG_B] & 0x02)
+ {
+ /* 24 hour format */
+ pThis->cmos_data[RTC_HOURS] = to_bcd(pThis, tm->tm_hour);
}
-}
-
-#ifdef IN_RING3
-static void rtc_set_memory(RTCState *s, int addr, int val)
-{
- if (addr >= 0 && addr <= 127)
- s->cmos_data[addr] = val;
-}
-
-static void rtc_set_date(RTCState *s, const struct my_tm *tm)
-{
- s->current_tm = *tm;
- rtc_copy_date(s);
-}
-
-#endif /* IN_RING3 */
-
-/* -=-=-=-=-=- wrappers / stuff -=-=-=-=-=- */
-
-/**
- * Port I/O Handler for IN operations.
- *
- * @returns VBox status code.
- *
- * @param pDevIns The device instance.
- * @param pvUser User argument - ignored.
- * @param uPort Port number used for the IN operation.
- * @param pu32 Where to store the result.
- * @param cb Number of bytes read.
- */
-PDMBOTHCBDECL(int) rtcIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
-{
- NOREF(pvUser);
- if (cb == 1)
+ else
{
- *pu32 = cmos_ioport_read(PDMINS_2_DATA(pDevIns, RTCState *), Port);
- return VINF_SUCCESS;
+ /* 12 hour format */
+ pThis->cmos_data[RTC_HOURS] = to_bcd(pThis, tm->tm_hour % 12);
+ if (tm->tm_hour >= 12)
+ pThis->cmos_data[RTC_HOURS] |= 0x80;
}
- return VERR_IOM_IOPORT_UNUSED;
-}
-
-
-/**
- * Port I/O Handler for OUT operations.
- *
- * @returns VBox status code.
- *
- * @param pDevIns The device instance.
- * @param pvUser User argument - ignored.
- * @param uPort Port number used for the IN operation.
- * @param u32 The value to output.
- * @param cb The value size in bytes.
- */
-PDMBOTHCBDECL(int) rtcIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
-{
- NOREF(pvUser);
- if (cb == 1)
- cmos_ioport_write(PDMINS_2_DATA(pDevIns, RTCState *), Port, u32);
- return VINF_SUCCESS;
+ pThis->cmos_data[RTC_DAY_OF_WEEK] = to_bcd(pThis, tm->tm_wday);
+ pThis->cmos_data[RTC_DAY_OF_MONTH] = to_bcd(pThis, tm->tm_mday);
+ pThis->cmos_data[RTC_MONTH] = to_bcd(pThis, tm->tm_mon + 1);
+ pThis->cmos_data[RTC_YEAR] = to_bcd(pThis, tm->tm_year % 100);
}
/**
- * Device timer callback function, periodic.
+ * Device timer callback function, second2.
*
* @param pDevIns Device instance of the device which registered the timer.
* @param pTimer The timer handle.
* @param pvUser Pointer to the RTC state.
*/
-PDMBOTHCBDECL(void) rtcTimerPeriodic(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
+static DECLCALLBACK(void) rtcTimerSecond2(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
{
- rtc_periodic_timer((RTCState *)pvUser);
-}
+ RTCState *pThis = PDMINS_2_DATA(pDevIns, RTCState *);
+ Assert(TMTimerIsLockOwner(pThis->CTX_SUFF(pPeriodicTimer)));
+ Assert(PDMCritSectIsOwner(pThis->CTX_SUFF(pDevIns)->CTX_SUFF(pCritSectRo)));
+ if (!(pThis->cmos_data[RTC_REG_B] & REG_B_SET))
+ rtc_copy_date(pThis);
-/**
- * Device timer callback function, second.
- *
- * @param pDevIns Device instance of the device which registered the timer.
- * @param pTimer The timer handle.
- * @param pvUser Pointer to the RTC state.
- */
-PDMBOTHCBDECL(void) rtcTimerSecond(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
-{
- rtc_update_second((RTCState *)pvUser);
-}
+ /* check alarm */
+ if (pThis->cmos_data[RTC_REG_B] & REG_B_AIE)
+ {
+ if ( ( (pThis->cmos_data[RTC_SECONDS_ALARM] & 0xc0) == 0xc0
+ || from_bcd(pThis, pThis->cmos_data[RTC_SECONDS_ALARM]) == pThis->current_tm.tm_sec)
+ && ( (pThis->cmos_data[RTC_MINUTES_ALARM] & 0xc0) == 0xc0
+ || from_bcd(pThis, pThis->cmos_data[RTC_MINUTES_ALARM]) == pThis->current_tm.tm_min)
+ && ( (pThis->cmos_data[RTC_HOURS_ALARM ] & 0xc0) == 0xc0
+ || from_bcd(pThis, pThis->cmos_data[RTC_HOURS_ALARM ]) == pThis->current_tm.tm_hour)
+ )
+ {
+ pThis->cmos_data[RTC_REG_C] |= 0xa0;
+ rtc_raise_irq(pThis, 1);
+ }
+ }
+ /* update ended interrupt */
+ if (pThis->cmos_data[RTC_REG_B] & REG_B_UIE)
+ {
+ pThis->cmos_data[RTC_REG_C] |= 0x90;
+ rtc_raise_irq(pThis, 1);
+ }
-/**
- * Device timer callback function, second2.
- *
- * @param pDevIns Device instance of the device which registered the timer.
- * @param pTimer The timer handle.
- * @param pvUser Pointer to the RTC state.
- */
-PDMBOTHCBDECL(void) rtcTimerSecond2(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
-{
- rtc_update_second2((RTCState *)pvUser);
+ /* clear update in progress bit */
+ Log2(("RTC: UIP %x -> 0\n", !!(pThis->cmos_data[RTC_REG_A] & REG_A_UIP)));
+ pThis->cmos_data[RTC_REG_A] &= ~REG_A_UIP;
+
+ pThis->next_second_time += TMTimerGetFreq(pThis->CTX_SUFF(pSecondTimer));
+ TMTimerSet(pThis->CTX_SUFF(pSecondTimer), pThis->next_second_time);
}
-#ifdef IN_RING3
+
+/* -=-=-=-=-=- Saved State -=-=-=-=-=- */
+
/**
* @copydoc FNSSMDEVLIVEEXEC
@@ -786,7 +818,7 @@ static void rtcCalcCRC(RTCState *pThis)
for (i = RTC_CRC_START, u16 = 0; i <= RTC_CRC_LAST; i++)
u16 += pThis->cmos_data[i];
- pThis->cmos_data[RTC_CRC_LOW] = u16 & 0xff;
+ pThis->cmos_data[RTC_CRC_LOW] = u16 & 0xff;
pThis->cmos_data[RTC_CRC_HIGH] = (u16 >> 8) & 0xff;
}
@@ -804,6 +836,8 @@ static DECLCALLBACK(int) rtcCMOSWrite(PPDMDEVINS pDevIns, unsigned iReg, uint8_t
RTCState *pThis = PDMINS_2_DATA(pDevIns, RTCState *);
if (iReg < RT_ELEMENTS(pThis->cmos_data))
{
+ PDMCritSectEnter(pDevIns->pCritSectRoR3, VERR_IGNORED);
+
pThis->cmos_data[iReg] = u8Value;
/* does it require checksum update? */
@@ -811,8 +845,10 @@ static DECLCALLBACK(int) rtcCMOSWrite(PPDMDEVINS pDevIns, unsigned iReg, uint8_t
&& iReg <= RTC_CRC_LAST)
rtcCalcCRC(pThis);
+ PDMCritSectLeave(pDevIns->pCritSectRoR3);
return VINF_SUCCESS;
}
+
AssertMsgFailed(("iReg=%d\n", iReg));
return VERR_INVALID_PARAMETER;
}
@@ -831,7 +867,11 @@ static DECLCALLBACK(int) rtcCMOSRead(PPDMDEVINS pDevIns, unsigned iReg, uint8_t
RTCState *pThis = PDMINS_2_DATA(pDevIns, RTCState *);
if (iReg < RT_ELEMENTS(pThis->cmos_data))
{
+ PDMCritSectEnter(pDevIns->pCritSectRoR3, VERR_IGNORED);
+
*pu8Value = pThis->cmos_data[iReg];
+
+ PDMCritSectLeave(pDevIns->pCritSectRoR3);
return VINF_SUCCESS;
}
AssertMsgFailed(("iReg=%d\n", iReg));
@@ -839,8 +879,37 @@ static DECLCALLBACK(int) rtcCMOSRead(PPDMDEVINS pDevIns, unsigned iReg, uint8_t
}
+/**
+ * @interface_method_impl{PDMIHPETLEGACYNOTIFY,pfnModeChanged}
+ */
+static DECLCALLBACK(void) rtcHpetLegacyNotify_ModeChanged(PPDMIHPETLEGACYNOTIFY pInterface, bool fActivated)
+{
+ RTCState *pThis = RT_FROM_MEMBER(pInterface, RTCState, IHpetLegacyNotify);
+ PDMCritSectEnter(pThis->pDevInsR3->pCritSectRoR3, VERR_IGNORED);
+
+ pThis->fDisabledByHpet = fActivated;
+
+ PDMCritSectLeave(pThis->pDevInsR3->pCritSectRoR3);
+}
+
+
/* -=-=-=-=-=- based on bits from pc.c -=-=-=-=-=- */
+
+static void rtc_set_memory(RTCState *pThis, int addr, int val)
+{
+ if (addr >= 0 && addr <= 127)
+ pThis->cmos_data[addr] = val;
+}
+
+
+static void rtc_set_date(RTCState *pThis, const struct my_tm *tm)
+{
+ pThis->current_tm = *tm;
+ rtc_copy_date(pThis);
+}
+
+
/** @copydoc FNPDMDEVINITCOMPLETE */
static DECLCALLBACK(int) rtcInitComplete(PPDMDEVINS pDevIns)
{
@@ -872,8 +941,8 @@ static DECLCALLBACK(int) rtcInitComplete(PPDMDEVINS pDevIns)
rtc_set_date(pThis, &Tm);
int iYear = to_bcd(pThis, (Tm.tm_year / 100) + 19); /* tm_year is 1900 based */
- rtc_set_memory(pThis, 0x32, iYear); /* 32h - Century Byte (BCD value for the century */
- rtc_set_memory(pThis, 0x37, iYear); /* 37h - (IBM PS/2) Date Century Byte */
+ rtc_set_memory(pThis, 0x32, iYear); /* 32h - Century Byte (BCD value for the century */
+ rtc_set_memory(pThis, 0x37, iYear); /* 37h - (IBM PS/2) Date Century Byte */
/*
* Recalculate the checksum just in case.
@@ -900,17 +969,6 @@ static DECLCALLBACK(void *) rtcQueryInterface(PPDMIBASE pInterface, const char *
return NULL;
}
-
-/**
- * @interface_method_impl{PDMIHPETLEGACYNOTIFY,pfnModeChanged}
- */
-static DECLCALLBACK(void) rtcHpetLegacyNotify_ModeChanged(PPDMIHPETLEGACYNOTIFY pInterface, bool fActivated)
-{
- RTCState *pThis = RT_FROM_MEMBER(pInterface, RTCState, IHpetLegacyNotify);
- pThis->fDisabledByHpet = fActivated;
-}
-
-
/**
* @copydoc
*/
@@ -999,56 +1057,71 @@ static DECLCALLBACK(int) rtcConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMN
pThis->IHpetLegacyNotify.pfnModeChanged = rtcHpetLegacyNotify_ModeChanged;
/*
- * Create timers, arm them, register I/O Ports and save state.
+ * Create timers.
*/
+ PTMTIMER pTimer;
+ /* Periodic timer. */
rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL_SYNC, rtcTimerPeriodic, pThis,
TMTIMER_FLAGS_DEFAULT_CRIT_SECT, "MC146818 RTC/CMOS - Periodic",
- &pThis->pPeriodicTimerR3);
+ &pTimer);
if (RT_FAILURE(rc))
return rc;
- pThis->pPeriodicTimerR0 = TMTimerR0Ptr(pThis->pPeriodicTimerR3);
- pThis->pPeriodicTimerRC = TMTimerRCPtr(pThis->pPeriodicTimerR3);
+ pThis->pPeriodicTimerR3 = pTimer;
+ pThis->pPeriodicTimerR0 = TMTimerR0Ptr(pTimer);
+ pThis->pPeriodicTimerRC = TMTimerRCPtr(pTimer);
+ /* Seconds timer. */
rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL_SYNC, rtcTimerSecond, pThis,
TMTIMER_FLAGS_DEFAULT_CRIT_SECT, "MC146818 RTC/CMOS - Second",
- &pThis->pSecondTimerR3);
+ &pTimer);
if (RT_FAILURE(rc))
return rc;
- pThis->pSecondTimerR0 = TMTimerR0Ptr(pThis->pSecondTimerR3);
- pThis->pSecondTimerRC = TMTimerRCPtr(pThis->pSecondTimerR3);
+ pThis->pSecondTimerR3 = pTimer;
+ pThis->pSecondTimerR0 = TMTimerR0Ptr(pTimer);
+ pThis->pSecondTimerRC = TMTimerRCPtr(pTimer);
+ /* The second2 timer, this is always active. */
rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL_SYNC, rtcTimerSecond2, pThis,
TMTIMER_FLAGS_DEFAULT_CRIT_SECT, "MC146818 RTC/CMOS - Second2",
- &pThis->pSecondTimer2R3);
- if (RT_FAILURE(rc))
- return rc;
- pThis->pSecondTimer2R0 = TMTimerR0Ptr(pThis->pSecondTimer2R3);
- pThis->pSecondTimer2RC = TMTimerRCPtr(pThis->pSecondTimer2R3);
- pThis->next_second_time = TMTimerGet(pThis->CTX_SUFF(pSecondTimer2))
- + (TMTimerGetFreq(pThis->CTX_SUFF(pSecondTimer2)) * 99) / 100;
- rc = TMTimerSet(pThis->CTX_SUFF(pSecondTimer2), pThis->next_second_time);
+ &pTimer);
if (RT_FAILURE(rc))
return rc;
+ pThis->pSecondTimer2R3 = pTimer;
+ pThis->pSecondTimer2R0 = TMTimerR0Ptr(pTimer);
+ pThis->pSecondTimer2RC = TMTimerRCPtr(pTimer);
+ pThis->next_second_time = TMTimerGet(pTimer)
+ + (TMTimerGetFreq(pTimer) * 99) / 100;
+ rc = TMTimerLock(pTimer, VERR_IGNORED);
+ AssertRCReturn(rc, rc);
+ rc = TMTimerSet(pTimer, pThis->next_second_time);
+ TMTimerUnlock(pTimer);
+ AssertRCReturn(rc, rc);
+ /*
+ * Register I/O ports.
+ */
rc = PDMDevHlpIOPortRegister(pDevIns, pThis->IOPortBase, 4, NULL,
rtcIOPortWrite, rtcIOPortRead, NULL, NULL, "MC146818 RTC/CMOS");
if (RT_FAILURE(rc))
return rc;
if (fGCEnabled)
{
- rc = PDMDevHlpIOPortRegisterRC(pDevIns, pThis->IOPortBase, 4, 0,
+ rc = PDMDevHlpIOPortRegisterRC(pDevIns, pThis->IOPortBase, 4, NIL_RTRCPTR,
"rtcIOPortWrite", "rtcIOPortRead", NULL, NULL, "MC146818 RTC/CMOS");
if (RT_FAILURE(rc))
return rc;
}
if (fR0Enabled)
{
- rc = PDMDevHlpIOPortRegisterR0(pDevIns, pThis->IOPortBase, 4, 0,
+ rc = PDMDevHlpIOPortRegisterR0(pDevIns, pThis->IOPortBase, 4, NIL_RTR0PTR,
"rtcIOPortWrite", "rtcIOPortRead", NULL, NULL, "MC146818 RTC/CMOS");
if (RT_FAILURE(rc))
return rc;
}
+ /*
+ * Register the saved state.
+ */
rc = PDMDevHlpSSMRegister3(pDevIns, RTC_SAVED_STATE_VERSION, sizeof(*pThis), rtcLiveExec, rtcSaveExec, rtcLoadExec);
if (RT_FAILURE(rc))
return rc;