summaryrefslogtreecommitdiffstats
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.cpp1299
1 files changed, 1299 insertions, 0 deletions
diff --git a/src/VBox/Devices/PC/DevRTC.cpp b/src/VBox/Devices/PC/DevRTC.cpp
new file mode 100644
index 00000000..4416aa99
--- /dev/null
+++ b/src/VBox/Devices/PC/DevRTC.cpp
@@ -0,0 +1,1299 @@
+/* $Id: DevRTC.cpp $ */
+/** @file
+ * Motorola MC146818 RTC/CMOS Device with PIIX4 extensions.
+ */
+
+/*
+ * Copyright (C) 2006-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ * --------------------------------------------------------------------
+ *
+ * This code is based on:
+ *
+ * QEMU MC146818 RTC emulation
+ *
+ * Copyright (c) 2003-2004 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_DEV_RTC
+#include <VBox/vmm/pdmdev.h>
+#include <VBox/log.h>
+#include <iprt/asm-math.h>
+#include <iprt/assert.h>
+#include <iprt/string.h>
+
+#ifdef IN_RING3
+# include <iprt/alloc.h>
+# include <iprt/uuid.h>
+#endif /* IN_RING3 */
+
+#include "VBoxDD.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/*#define DEBUG_CMOS*/
+#define RTC_CRC_START 0x10
+#define RTC_CRC_LAST 0x2d
+#define RTC_CRC_HIGH 0x2e
+#define RTC_CRC_LOW 0x2f
+
+#define RTC_SECONDS 0
+#define RTC_SECONDS_ALARM 1
+#define RTC_MINUTES 2
+#define RTC_MINUTES_ALARM 3
+#define RTC_HOURS 4
+#define RTC_HOURS_ALARM 5
+#define RTC_ALARM_DONT_CARE 0xC0
+
+#define RTC_DAY_OF_WEEK 6
+#define RTC_DAY_OF_MONTH 7
+#define RTC_MONTH 8
+#define RTC_YEAR 9
+
+#define RTC_REG_A 10
+#define RTC_REG_B 11
+#define RTC_REG_C 12
+#define RTC_REG_D 13
+
+#define REG_A_UIP 0x80
+
+#define REG_B_SET 0x80
+#define REG_B_PIE 0x40
+#define REG_B_AIE 0x20
+#define REG_B_UIE 0x10
+
+#define CMOS_BANK_LOWER_LIMIT 0x0E
+#define CMOS_BANK_UPPER_LIMIT 0x7F
+#define CMOS_BANK2_LOWER_LIMIT 0x80
+#define CMOS_BANK2_UPPER_LIMIT 0xFF
+#define CMOS_BANK_SIZE 0x80
+
+/** The saved state version. */
+#define RTC_SAVED_STATE_VERSION 4
+/** The saved state version used by VirtualBox pre-3.2.
+ * This does not include the second 128-byte bank. */
+#define RTC_SAVED_STATE_VERSION_VBOX_32PRE 3
+/** The saved state version used by VirtualBox 3.1 and earlier.
+ * This does not include disabled by HPET state. */
+#define RTC_SAVED_STATE_VERSION_VBOX_31 2
+/** The saved state version used by VirtualBox 3.0 and earlier.
+ * This does not include the configuration. */
+#define RTC_SAVED_STATE_VERSION_VBOX_30 1
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/** @todo Replace struct my_tm with RTTIME. */
+struct my_tm
+{
+ int32_t tm_sec;
+ int32_t tm_min;
+ int32_t tm_hour;
+ int32_t tm_mday;
+ int32_t tm_mon;
+ int32_t tm_year;
+ int32_t tm_wday;
+ int32_t tm_yday;
+};
+
+
+typedef struct RTCSTATE
+{
+ uint8_t cmos_data[256];
+ uint8_t cmos_index[2];
+ uint8_t Alignment0[6];
+ struct my_tm current_tm;
+ /** The configured IRQ. */
+ int32_t irq;
+ /** The configured I/O port base. */
+ RTIOPORT IOPortBase;
+ /** Use UTC or local time initially. */
+ bool fUTC;
+ /** Disabled by HPET legacy mode. */
+ bool fDisabledByHpet;
+ /* periodic timer */
+ int64_t next_periodic_time;
+ /* second update */
+ int64_t next_second_time;
+
+ /** Pointer to the device instance - R3 Ptr. */
+ PPDMDEVINSR3 pDevInsR3;
+ /** The periodic timer (rtcTimerPeriodic) - R3 Ptr. */
+ PTMTIMERR3 pPeriodicTimerR3;
+ /** The second timer (rtcTimerSecond) - R3 Ptr. */
+ PTMTIMERR3 pSecondTimerR3;
+ /** The second second timer (rtcTimerSecond2) - R3 Ptr. */
+ PTMTIMERR3 pSecondTimer2R3;
+
+ /** Pointer to the device instance - R0 Ptr. */
+ PPDMDEVINSR0 pDevInsR0;
+ /** The periodic timer (rtcTimerPeriodic) - R0 Ptr. */
+ PTMTIMERR0 pPeriodicTimerR0;
+ /** The second timer (rtcTimerSecond) - R0 Ptr. */
+ PTMTIMERR0 pSecondTimerR0;
+ /** The second second timer (rtcTimerSecond2) - R0 Ptr. */
+ PTMTIMERR0 pSecondTimer2R0;
+
+ /** Pointer to the device instance - RC Ptr. */
+ PPDMDEVINSRC pDevInsRC;
+ /** The periodic timer (rtcTimerPeriodic) - RC Ptr. */
+ PTMTIMERRC pPeriodicTimerRC;
+ /** The second timer (rtcTimerSecond) - RC Ptr. */
+ PTMTIMERRC pSecondTimerRC;
+ /** The second second timer (rtcTimerSecond2) - RC Ptr. */
+ PTMTIMERRC pSecondTimer2RC;
+
+ /** The RTC registration structure. */
+ PDMRTCREG RtcReg;
+ /** The RTC device helpers. */
+ R3PTRTYPE(PCPDMRTCHLP) pRtcHlpR3;
+ /** Number of release log entries. Used to prevent flooding. */
+ uint32_t cRelLogEntries;
+ /** The current/previous logged timer period. */
+ int32_t CurLogPeriod;
+ /** The current/previous hinted timer period. */
+ int32_t CurHintPeriod;
+ /** How many consecutive times the UIP has been seen. */
+ int32_t cUipSeen;
+
+ /** HPET legacy mode notification interface. */
+ PDMIHPETLEGACYNOTIFY IHpetLegacyNotify;
+
+ /** Number of IRQs that's been raised. */
+ STAMCOUNTER StatRTCIrq;
+ /** Number of times the timer callback handler ran. */
+ STAMCOUNTER StatRTCTimerCB;
+} RTCSTATE;
+/** Pointer to the RTC device state. */
+typedef RTCSTATE *PRTCSTATE;
+
+#ifndef VBOX_DEVICE_STRUCT_TESTCASE
+
+static void rtc_timer_update(PRTCSTATE pThis, int64_t current_time)
+{
+ int period_code, period;
+ uint64_t cur_clock, next_irq_clock;
+ uint32_t freq;
+
+ 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(pThis->CTX_SUFF(pPeriodicTimer));
+
+ cur_clock = ASMMultU64ByU32DivByU32(current_time, 32768, freq);
+ next_irq_clock = (cur_clock & ~(uint64_t)(period - 1)) + period;
+ 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 != pThis->CurLogPeriod))
+#else
+ if (RT_UNLIKELY(period != pThis->CurHintPeriod))
+#endif
+ {
+#ifdef IN_RING3
+ if (pThis->cRelLogEntries++ < 64)
+ LogRel(("RTC: period=%#x (%d) %u Hz\n", period, period, _32K / period));
+ pThis->CurLogPeriod = period;
+#endif
+ pThis->CurHintPeriod = period;
+ TMTimerSetFrequencyHint(pThis->CTX_SUFF(pPeriodicTimer), _32K / period);
+ }
+ }
+ else
+ {
+#ifdef IN_RING3
+ if (TMTimerIsActive(pThis->CTX_SUFF(pPeriodicTimer)) && pThis->cRelLogEntries++ < 64)
+ LogRel(("RTC: Stopped the periodic timer\n"));
+#endif
+ TMTimerStop(pThis->CTX_SUFF(pPeriodicTimer));
+ }
+}
+
+
+static void rtc_raise_irq(PRTCSTATE pThis, uint32_t iLevel)
+{
+ if (!pThis->fDisabledByHpet)
+ {
+ PDMDevHlpISASetIrq(pThis->CTX_SUFF(pDevIns), pThis->irq, iLevel);
+ if (iLevel)
+ STAM_COUNTER_INC(&pThis->StatRTCIrq);
+ }
+}
+
+
+#ifdef IN_RING3
+DECLINLINE(int) to_bcd(PRTCSTATE pThis, int a)
+{
+ if (pThis->cmos_data[RTC_REG_B] & 0x04)
+ return a;
+ return ((a / 10) << 4) | (a % 10);
+}
+#endif
+
+
+DECLINLINE(int) from_bcd(PRTCSTATE pThis, int a)
+{
+ if (pThis->cmos_data[RTC_REG_B] & 0x04)
+ return a;
+ return ((a >> 4) * 10) + (a & 0x0f);
+}
+
+
+static void rtc_set_time(PRTCSTATE pThis)
+{
+ struct my_tm *tm = &pThis->current_tm;
+
+ 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))
+ {
+ tm->tm_hour %= 12;
+ if (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;
+}
+
+
+/* -=-=-=-=-=- I/O Port Handlers -=-=-=-=-=- */
+
+
+/**
+ * @callback_method_impl{FNIOMIOPORTIN}
+ */
+PDMBOTHCBDECL(int) rtcIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT uPort, uint32_t *pu32, unsigned cb)
+{
+ NOREF(pvUser);
+ if (cb != 1)
+ return VERR_IOM_IOPORT_UNUSED;
+
+ PRTCSTATE pThis = PDMINS_2_DATA(pDevIns, PRTCSTATE);
+ if ((uPort & 1) == 0)
+ *pu32 = 0xff;
+ else
+ {
+ unsigned bank = (uPort >> 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:
+ if (pThis->cmos_data[RTC_REG_A] & REG_A_UIP)
+ ++pThis->cUipSeen;
+ else
+ pThis->cUipSeen = 0;
+ if (pThis->cUipSeen >= 250)
+ {
+ pThis->cmos_data[pThis->cmos_index[0]] &= ~REG_A_UIP;
+ pThis->cUipSeen = 0;
+ }
+ *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;
+}
+
+
+/**
+ * @callback_method_impl{FNIOMIOPORTOUT}
+ */
+PDMBOTHCBDECL(int) rtcIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT uPort, uint32_t u32, unsigned cb)
+{
+ NOREF(pvUser);
+ if (cb != 1)
+ return VINF_SUCCESS;
+
+ PRTCSTATE pThis = PDMINS_2_DATA(pDevIns, PRTCSTATE);
+ uint32_t bank = (uPort >> 1) & 1;
+ if ((uPort & 1) == 0)
+ {
+ pThis->cmos_index[bank] = (u32 & 0x7f) + (bank * CMOS_BANK_SIZE);
+
+ /* HACK ALERT! Attempt to trigger VM_FF_TIMER and/or VM_FF_TM_VIRTUAL_SYNC
+ for forcing the pSecondTimer2 timer to run be run and clear UIP in
+ a timely fashion. */
+ if (u32 == RTC_REG_A)
+ TMTimerGet(pThis->CTX_SUFF(pSecondTimer));
+ }
+ 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]]));
+
+ 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;
+ }
+
+ 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;
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+
+#ifdef IN_RING3
+
+/* -=-=-=-=-=- Debug Info Handlers -=-=-=-=-=- */
+
+/**
+ * @callback_method_impl{FNDBGFHANDLERDEV,
+ * Dumps the cmos Bank Info.}
+ */
+static DECLCALLBACK(void) rtcCmosBankInfo(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
+{
+ RT_NOREF1(pszArgs);
+ PRTCSTATE pThis = PDMINS_2_DATA(pDevIns, PRTCSTATE);
+
+ pHlp->pfnPrintf(pHlp,
+ "First CMOS bank, offsets 0x0E - 0x7F\n"
+ "Offset %02x : --- use 'info rtc' to show CMOS clock ---", 0);
+ for (unsigned iCmos = CMOS_BANK_LOWER_LIMIT; iCmos <= CMOS_BANK_UPPER_LIMIT; iCmos++)
+ {
+ if ((iCmos & 15) == 0)
+ pHlp->pfnPrintf(pHlp, "Offset %02x : %02x", iCmos, pThis->cmos_data[iCmos]);
+ else if ((iCmos & 15) == 8)
+ pHlp->pfnPrintf(pHlp, "-%02x", pThis->cmos_data[iCmos]);
+ else if ((iCmos & 15) == 15)
+ pHlp->pfnPrintf(pHlp, " %02x\n", pThis->cmos_data[iCmos]);
+ else
+ pHlp->pfnPrintf(pHlp, " %02x", pThis->cmos_data[iCmos]);
+ }
+}
+
+/**
+ * @callback_method_impl{FNDBGFHANDLERDEV,
+ * Dumps the cmos Bank2 Info.}
+ */
+static DECLCALLBACK(void) rtcCmosBank2Info(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
+{
+ RT_NOREF1(pszArgs);
+ PRTCSTATE pThis = PDMINS_2_DATA(pDevIns, PRTCSTATE);
+
+ pHlp->pfnPrintf(pHlp, "Second CMOS bank, offsets 0x80 - 0xFF\n");
+ for (uint16_t iCmos = CMOS_BANK2_LOWER_LIMIT; iCmos <= CMOS_BANK2_UPPER_LIMIT; iCmos++)
+ {
+ if ((iCmos & 15) == 0)
+ pHlp->pfnPrintf(pHlp, "Offset %02x : %02x", iCmos, pThis->cmos_data[iCmos]);
+ else if ((iCmos & 15) == 8)
+ pHlp->pfnPrintf(pHlp, "-%02x", pThis->cmos_data[iCmos]);
+ else if ((iCmos & 15) == 15)
+ pHlp->pfnPrintf(pHlp, " %02x\n", pThis->cmos_data[iCmos]);
+ else
+ pHlp->pfnPrintf(pHlp, " %02x", pThis->cmos_data[iCmos]);
+ }
+}
+
+/**
+ * @callback_method_impl{FNDBGFHANDLERDEV,
+ * Dumps the cmos RTC Info.}
+ */
+static DECLCALLBACK(void) rtcCmosClockInfo(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
+{
+ RT_NOREF1(pszArgs);
+ PRTCSTATE pThis = PDMINS_2_DATA(pDevIns, PRTCSTATE);
+ uint8_t u8Sec = from_bcd(pThis, pThis->cmos_data[RTC_SECONDS]);
+ uint8_t u8Min = from_bcd(pThis, pThis->cmos_data[RTC_MINUTES]);
+ uint8_t u8Hr = from_bcd(pThis, pThis->cmos_data[RTC_HOURS] & 0x7f);
+ if ( !(pThis->cmos_data[RTC_REG_B] & 0x02)
+ && (pThis->cmos_data[RTC_HOURS] & 0x80))
+ u8Hr += 12;
+ uint8_t u8Day = from_bcd(pThis, pThis->cmos_data[RTC_DAY_OF_MONTH]);
+ uint8_t u8Month = from_bcd(pThis, pThis->cmos_data[RTC_MONTH]) ;
+ uint8_t u8Year = from_bcd(pThis, pThis->cmos_data[RTC_YEAR]);
+ pHlp->pfnPrintf(pHlp, "Time: %02u:%02u:%02u Date: %02u-%02u-%02u\n",
+ u8Hr, u8Min, u8Sec, u8Year, u8Month, u8Day);
+ pHlp->pfnPrintf(pHlp, "REG A=%02x B=%02x C=%02x D=%02x\n",
+ pThis->cmos_data[RTC_REG_A], pThis->cmos_data[RTC_REG_B],
+ pThis->cmos_data[RTC_REG_C], pThis->cmos_data[RTC_REG_D]);
+}
+
+
+
+/* -=-=-=-=-=- Timers and their support code -=-=-=-=-=- */
+
+
+/**
+ * @callback_method_impl{FNTMTIMERDEV, periodic}
+ */
+static DECLCALLBACK(void) rtcTimerPeriodic(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
+{
+ RT_NOREF2(pTimer, pvUser);
+ PRTCSTATE pThis = PDMINS_2_DATA(pDevIns, PRTCSTATE);
+ Assert(TMTimerIsLockOwner(pThis->CTX_SUFF(pPeriodicTimer)));
+ Assert(PDMCritSectIsOwner(pThis->CTX_SUFF(pDevIns)->CTX_SUFF(pCritSectRo)));
+
+ rtc_timer_update(pThis, pThis->next_periodic_time);
+ STAM_COUNTER_INC(&pThis->StatRTCTimerCB);
+ 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] =
+ {
+ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+ };
+ int d;
+
+ if ((unsigned )month >= 12)
+ return 31;
+
+ d = days_tab[month];
+ if (month == 1)
+ {
+ if ((year % 4) == 0 && ((year % 100) != 0 || (year % 400) == 0))
+ d++;
+ }
+ return d;
+}
+
+
+/* update 'tm' to the next second */
+static void rtc_next_second(struct my_tm *tm)
+{
+ int days_in_month;
+
+ tm->tm_sec++;
+ if ((unsigned)tm->tm_sec >= 60)
+ {
+ tm->tm_sec = 0;
+ tm->tm_min++;
+ if ((unsigned)tm->tm_min >= 60)
+ {
+ tm->tm_min = 0;
+ tm->tm_hour++;
+ if ((unsigned)tm->tm_hour >= 24)
+ {
+ tm->tm_hour = 0;
+ /* next day */
+ tm->tm_wday++;
+ if ((unsigned)tm->tm_wday >= 7)
+ tm->tm_wday = 0;
+ days_in_month = get_days_in_month(tm->tm_mon,
+ tm->tm_year + 1900);
+ tm->tm_mday++;
+ if (tm->tm_mday < 1)
+ tm->tm_mday = 1;
+ else if (tm->tm_mday > days_in_month)
+ {
+ tm->tm_mday = 1;
+ tm->tm_mon++;
+ if (tm->tm_mon >= 12)
+ {
+ tm->tm_mon = 0;
+ tm->tm_year++;
+ }
+ }
+ }
+ }
+ }
+}
+
+
+/**
+ * @callback_method_impl{FNTMTIMERDEV, Second timer.}
+ */
+static DECLCALLBACK(void) rtcTimerSecond(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
+{
+ RT_NOREF2(pTimer, pvUser);
+ PRTCSTATE pThis = PDMINS_2_DATA(pDevIns, PRTCSTATE);
+ 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 ((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 (!(pThis->cmos_data[RTC_REG_B] & REG_B_SET))
+ {
+ /* update in progress bit */
+ 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(pThis->CTX_SUFF(pSecondTimer2), 244140);
+ TMTimerSet(pThis->CTX_SUFF(pSecondTimer2), pThis->next_second_time + delay);
+ }
+}
+
+
+/* Used by rtc_set_date and rtcTimerSecond2. */
+static void rtc_copy_date(PRTCSTATE pThis)
+{
+ const struct my_tm *tm = &pThis->current_tm;
+
+ 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);
+ }
+ else
+ {
+ /* 12 hour format */
+ int h = tm->tm_hour % 12;
+ pThis->cmos_data[RTC_HOURS] = to_bcd(pThis, h ? h : 12);
+ if (tm->tm_hour >= 12)
+ pThis->cmos_data[RTC_HOURS] |= 0x80;
+ }
+ 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);
+}
+
+
+/**
+ * @callback_method_impl{FNTMTIMERDEV, Second2 timer.}
+ */
+static DECLCALLBACK(void) rtcTimerSecond2(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
+{
+ RT_NOREF2(pTimer, pvUser);
+ PRTCSTATE pThis = PDMINS_2_DATA(pDevIns, PRTCSTATE);
+ 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);
+
+ /* 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);
+ }
+
+ /* 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);
+}
+
+
+/* -=-=-=-=-=- Saved State -=-=-=-=-=- */
+
+
+/**
+ * @callback_method_impl{FNSSMDEVLIVEEXEC}
+ */
+static DECLCALLBACK(int) rtcLiveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uPass)
+{
+ RT_NOREF1(uPass);
+ PRTCSTATE pThis = PDMINS_2_DATA(pDevIns, PRTCSTATE);
+
+ SSMR3PutU8( pSSM, pThis->irq);
+ SSMR3PutIOPort(pSSM, pThis->IOPortBase);
+ SSMR3PutBool( pSSM, pThis->fUTC);
+
+ return VINF_SSM_DONT_CALL_AGAIN;
+}
+
+
+/**
+ * @callback_method_impl{FNSSMDEVSAVEEXEC}
+ */
+static DECLCALLBACK(int) rtcSaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
+{
+ PRTCSTATE pThis = PDMINS_2_DATA(pDevIns, PRTCSTATE);
+
+ /* The config. */
+ rtcLiveExec(pDevIns, pSSM, SSM_PASS_FINAL);
+
+ /* The state. */
+ SSMR3PutMem(pSSM, pThis->cmos_data, CMOS_BANK_SIZE);
+ SSMR3PutU8(pSSM, pThis->cmos_index[0]);
+
+ SSMR3PutS32(pSSM, pThis->current_tm.tm_sec);
+ SSMR3PutS32(pSSM, pThis->current_tm.tm_min);
+ SSMR3PutS32(pSSM, pThis->current_tm.tm_hour);
+ SSMR3PutS32(pSSM, pThis->current_tm.tm_wday);
+ SSMR3PutS32(pSSM, pThis->current_tm.tm_mday);
+ SSMR3PutS32(pSSM, pThis->current_tm.tm_mon);
+ SSMR3PutS32(pSSM, pThis->current_tm.tm_year);
+
+ TMR3TimerSave(pThis->CTX_SUFF(pPeriodicTimer), pSSM);
+
+ SSMR3PutS64(pSSM, pThis->next_periodic_time);
+
+ SSMR3PutS64(pSSM, pThis->next_second_time);
+ TMR3TimerSave(pThis->CTX_SUFF(pSecondTimer), pSSM);
+ TMR3TimerSave(pThis->CTX_SUFF(pSecondTimer2), pSSM);
+
+ SSMR3PutBool(pSSM, pThis->fDisabledByHpet);
+
+ SSMR3PutMem(pSSM, &pThis->cmos_data[CMOS_BANK_SIZE], CMOS_BANK_SIZE);
+ return SSMR3PutU8(pSSM, pThis->cmos_index[1]);
+}
+
+
+/**
+ * @callback_method_impl{FNSSMDEVLOADEXEC}
+ */
+static DECLCALLBACK(int) rtcLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
+{
+ PRTCSTATE pThis = PDMINS_2_DATA(pDevIns, PRTCSTATE);
+ int rc;
+
+ if ( uVersion != RTC_SAVED_STATE_VERSION
+ && uVersion != RTC_SAVED_STATE_VERSION_VBOX_32PRE
+ && uVersion != RTC_SAVED_STATE_VERSION_VBOX_31
+ && uVersion != RTC_SAVED_STATE_VERSION_VBOX_30)
+ return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
+
+ /* The config. */
+ if (uVersion > RTC_SAVED_STATE_VERSION_VBOX_30)
+ {
+ uint8_t u8Irq;
+ rc = SSMR3GetU8(pSSM, &u8Irq); AssertRCReturn(rc, rc);
+ if (u8Irq != pThis->irq)
+ return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Config mismatch - u8Irq: saved=%#x config=%#x"), u8Irq, pThis->irq);
+
+ RTIOPORT IOPortBase;
+ rc = SSMR3GetIOPort(pSSM, &IOPortBase); AssertRCReturn(rc, rc);
+ if (IOPortBase != pThis->IOPortBase)
+ return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Config mismatch - IOPortBase: saved=%RTiop config=%RTiop"), IOPortBase, pThis->IOPortBase);
+
+ bool fUTC;
+ rc = SSMR3GetBool(pSSM, &fUTC); AssertRCReturn(rc, rc);
+ if (fUTC != pThis->fUTC)
+ LogRel(("RTC: Config mismatch - fUTC: saved=%RTbool config=%RTbool\n", fUTC, pThis->fUTC));
+ }
+
+ if (uPass != SSM_PASS_FINAL)
+ return VINF_SUCCESS;
+
+ /* The state. */
+ SSMR3GetMem(pSSM, pThis->cmos_data, CMOS_BANK_SIZE);
+ SSMR3GetU8(pSSM, &pThis->cmos_index[0]);
+
+ SSMR3GetS32(pSSM, &pThis->current_tm.tm_sec);
+ SSMR3GetS32(pSSM, &pThis->current_tm.tm_min);
+ SSMR3GetS32(pSSM, &pThis->current_tm.tm_hour);
+ SSMR3GetS32(pSSM, &pThis->current_tm.tm_wday);
+ SSMR3GetS32(pSSM, &pThis->current_tm.tm_mday);
+ SSMR3GetS32(pSSM, &pThis->current_tm.tm_mon);
+ SSMR3GetS32(pSSM, &pThis->current_tm.tm_year);
+
+ TMR3TimerLoad(pThis->CTX_SUFF(pPeriodicTimer), pSSM);
+
+ SSMR3GetS64(pSSM, &pThis->next_periodic_time);
+
+ SSMR3GetS64(pSSM, &pThis->next_second_time);
+ TMR3TimerLoad(pThis->CTX_SUFF(pSecondTimer), pSSM);
+ TMR3TimerLoad(pThis->CTX_SUFF(pSecondTimer2), pSSM);
+
+ if (uVersion > RTC_SAVED_STATE_VERSION_VBOX_31)
+ SSMR3GetBool(pSSM, &pThis->fDisabledByHpet);
+
+ if (uVersion > RTC_SAVED_STATE_VERSION_VBOX_32PRE)
+ {
+ /* Second CMOS bank. */
+ SSMR3GetMem(pSSM, &pThis->cmos_data[CMOS_BANK_SIZE], CMOS_BANK_SIZE);
+ SSMR3GetU8(pSSM, &pThis->cmos_index[1]);
+ }
+
+ int 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;
+ int period = 1 << (period_code - 1);
+ LogRel(("RTC: period=%#x (%d) %u Hz (restore)\n", period, period, _32K / period));
+ PDMCritSectEnter(pThis->pDevInsR3->pCritSectRoR3, VINF_SUCCESS);
+ TMTimerSetFrequencyHint(pThis->CTX_SUFF(pPeriodicTimer), _32K / period);
+ PDMCritSectLeave(pThis->pDevInsR3->pCritSectRoR3);
+ pThis->CurLogPeriod = period;
+ pThis->CurHintPeriod = period;
+ }
+ else
+ {
+ LogRel(("RTC: Stopped the periodic timer (restore)\n"));
+ pThis->CurLogPeriod = 0;
+ pThis->CurHintPeriod = 0;
+ }
+ pThis->cRelLogEntries = 0;
+
+ return VINF_SUCCESS;
+}
+
+
+/* -=-=-=-=-=- PDM Interface provided by the RTC device -=-=-=-=-=- */
+
+/**
+ * Calculate and update the standard CMOS checksum.
+ *
+ * @param pThis Pointer to the RTC state data.
+ */
+static void rtcCalcCRC(PRTCSTATE pThis)
+{
+ uint16_t u16 = 0;
+ for (unsigned i = RTC_CRC_START; i <= RTC_CRC_LAST; i++)
+ u16 += pThis->cmos_data[i];
+
+ pThis->cmos_data[RTC_CRC_LOW] = u16 & 0xff;
+ pThis->cmos_data[RTC_CRC_HIGH] = (u16 >> 8) & 0xff;
+}
+
+
+/**
+ * @interface_method_impl{PDMRTCREG,pfnWrite}
+ */
+static DECLCALLBACK(int) rtcCMOSWrite(PPDMDEVINS pDevIns, unsigned iReg, uint8_t u8Value)
+{
+ PRTCSTATE pThis = PDMINS_2_DATA(pDevIns, PRTCSTATE);
+ Assert(PDMCritSectIsOwner(pDevIns->pCritSectRoR3));
+ if (iReg < RT_ELEMENTS(pThis->cmos_data))
+ {
+ pThis->cmos_data[iReg] = u8Value;
+
+ /* does it require checksum update? */
+ if ( iReg >= RTC_CRC_START
+ && iReg <= RTC_CRC_LAST)
+ rtcCalcCRC(pThis);
+
+ return VINF_SUCCESS;
+ }
+
+ AssertMsgFailed(("iReg=%d\n", iReg));
+ return VERR_INVALID_PARAMETER;
+}
+
+
+/**
+ * @interface_method_impl{PDMRTCREG,pfnRead}
+ */
+static DECLCALLBACK(int) rtcCMOSRead(PPDMDEVINS pDevIns, unsigned iReg, uint8_t *pu8Value)
+{
+ PRTCSTATE pThis = PDMINS_2_DATA(pDevIns, PRTCSTATE);
+ Assert(PDMCritSectIsOwner(pDevIns->pCritSectRoR3));
+
+ if (iReg < RT_ELEMENTS(pThis->cmos_data))
+ {
+ *pu8Value = pThis->cmos_data[iReg];
+ return VINF_SUCCESS;
+ }
+ AssertMsgFailed(("iReg=%d\n", iReg));
+ return VERR_INVALID_PARAMETER;
+}
+
+
+/**
+ * @interface_method_impl{PDMIHPETLEGACYNOTIFY,pfnModeChanged}
+ */
+static DECLCALLBACK(void) rtcHpetLegacyNotify_ModeChanged(PPDMIHPETLEGACYNOTIFY pInterface, bool fActivated)
+{
+ PRTCSTATE pThis = RT_FROM_MEMBER(pInterface, RTCSTATE, IHpetLegacyNotify);
+ PDMCritSectEnter(pThis->pDevInsR3->pCritSectRoR3, VERR_IGNORED);
+
+ pThis->fDisabledByHpet = fActivated;
+
+ PDMCritSectLeave(pThis->pDevInsR3->pCritSectRoR3);
+}
+
+
+
+/* -=-=-=-=-=- IBase -=-=-=-=-=- */
+
+/**
+ * @interface_method_impl{PDMIBASE,pfnQueryInterface}
+ */
+static DECLCALLBACK(void *) rtcQueryInterface(PPDMIBASE pInterface, const char *pszIID)
+{
+ PPDMDEVINS pDevIns = RT_FROM_MEMBER(pInterface, PDMDEVINS, IBase);
+ PRTCSTATE pThis = PDMINS_2_DATA(pDevIns, PRTCSTATE);
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDevIns->IBase);
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHPETLEGACYNOTIFY, &pThis->IHpetLegacyNotify);
+ return NULL;
+}
+
+
+/* -=-=-=-=-=- PDMDEVREG -=-=-=-=-=- */
+
+static void rtc_set_memory(PRTCSTATE pThis, int addr, int val)
+{
+ if (addr >= 0 && addr <= 127)
+ pThis->cmos_data[addr] = val;
+}
+
+
+static void rtc_set_date(PRTCSTATE pThis, const struct my_tm *tm)
+{
+ pThis->current_tm = *tm;
+ rtc_copy_date(pThis);
+}
+
+
+/**
+ * @interface_method_impl{PDMDEVREG,pfnInitComplete}
+ *
+ * Used to set the clock.
+ */
+static DECLCALLBACK(int) rtcInitComplete(PPDMDEVINS pDevIns)
+{
+ /** @todo this should be (re)done at power on if we didn't load a state... */
+ PRTCSTATE pThis = PDMINS_2_DATA(pDevIns, PRTCSTATE);
+
+ /*
+ * Set the CMOS date/time.
+ */
+ RTTIMESPEC Now;
+ PDMDevHlpTMUtcNow(pDevIns, &Now);
+ RTTIME Time;
+ if (pThis->fUTC)
+ RTTimeExplode(&Time, &Now);
+ else
+ RTTimeLocalExplode(&Time, &Now);
+
+ struct my_tm Tm;
+ memset(&Tm, 0, sizeof(Tm));
+ Tm.tm_year = Time.i32Year - 1900;
+ Tm.tm_mon = Time.u8Month - 1;
+ Tm.tm_mday = Time.u8MonthDay;
+ Tm.tm_wday = (Time.u8WeekDay + 1 + 7) % 7; /* 0 = Monday -> Sunday */
+ Tm.tm_yday = Time.u16YearDay - 1;
+ Tm.tm_hour = Time.u8Hour;
+ Tm.tm_min = Time.u8Minute;
+ Tm.tm_sec = Time.u8Second;
+
+ 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 */
+
+ /*
+ * Recalculate the checksum just in case.
+ */
+ rtcCalcCRC(pThis);
+
+ Log(("CMOS bank 0: \n%16.128Rhxd\n", &pThis->cmos_data[0]));
+ Log(("CMOS bank 1: \n%16.128Rhxd\n", &pThis->cmos_data[CMOS_BANK_SIZE]));
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{PDMDEVREG,pfnRelocate}
+ */
+static DECLCALLBACK(void) rtcRelocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
+{
+ RT_NOREF1(offDelta);
+ PRTCSTATE pThis = PDMINS_2_DATA(pDevIns, PRTCSTATE);
+
+ pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
+ pThis->pPeriodicTimerRC = TMTimerRCPtr(pThis->pPeriodicTimerR3);
+ pThis->pSecondTimerRC = TMTimerRCPtr(pThis->pSecondTimerR3);
+ pThis->pSecondTimer2RC = TMTimerRCPtr(pThis->pSecondTimer2R3);
+}
+
+
+/**
+ * @interface_method_impl{PDMDEVREG,pfnReset}
+ */
+static DECLCALLBACK(void) rtcReset(PPDMDEVINS pDevIns)
+{
+ PRTCSTATE pThis = PDMINS_2_DATA(pDevIns, PRTCSTATE);
+
+ /* Reset index values (important for second bank). */
+ pThis->cmos_index[0] = 0;
+ pThis->cmos_index[1] = CMOS_BANK_SIZE; /* Point to start of second bank. */
+}
+
+
+/**
+ * @interface_method_impl{PDMDEVREG,pfnConstruct}
+ */
+static DECLCALLBACK(int) rtcConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
+{
+ PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
+ PRTCSTATE pThis = PDMINS_2_DATA(pDevIns, PRTCSTATE);
+ int rc;
+ Assert(iInstance == 0); RT_NOREF(iInstance);
+
+ /*
+ * Validate configuration.
+ */
+ if (!CFGMR3AreValuesValid(pCfg,
+ "Irq\0"
+ "Base\0"
+ "UseUTC\0"
+ "RCEnabled\0"
+ "R0Enabled\0"))
+ return VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES;
+
+ /*
+ * Init the data.
+ */
+ uint8_t u8Irq;
+ rc = CFGMR3QueryU8Def(pCfg, "Irq", &u8Irq, 8);
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc,
+ N_("Configuration error: Querying \"Irq\" as a uint8_t failed"));
+ pThis->irq = u8Irq;
+
+ rc = CFGMR3QueryPortDef(pCfg, "Base", &pThis->IOPortBase, 0x70);
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc,
+ N_("Configuration error: Querying \"Base\" as a RTIOPORT failed"));
+
+ rc = CFGMR3QueryBoolDef(pCfg, "UseUTC", &pThis->fUTC, false);
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc,
+ N_("Configuration error: Querying \"UseUTC\" as a bool failed"));
+
+ bool fRCEnabled;
+ rc = CFGMR3QueryBoolDef(pCfg, "RCEnabled", &fRCEnabled, true);
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc,
+ N_("Configuration error: failed to read GCEnabled as boolean"));
+
+ bool fR0Enabled;
+ rc = CFGMR3QueryBoolDef(pCfg, "R0Enabled", &fR0Enabled, true);
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc,
+ N_("Configuration error: failed to read R0Enabled as boolean"));
+
+ Log(("RTC: Irq=%#x Base=%#x fRCEnabled=%RTbool fR0Enabled=%RTbool\n",
+ u8Irq, pThis->IOPortBase, fRCEnabled, fR0Enabled));
+
+
+ pThis->pDevInsR3 = pDevIns;
+ pThis->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
+ pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
+ pThis->cmos_data[RTC_REG_A] = 0x26;
+ pThis->cmos_data[RTC_REG_B] = 0x02;
+ pThis->cmos_data[RTC_REG_C] = 0x00;
+ pThis->cmos_data[RTC_REG_D] = 0x80;
+ pThis->RtcReg.u32Version = PDM_RTCREG_VERSION;
+ pThis->RtcReg.pfnRead = rtcCMOSRead;
+ pThis->RtcReg.pfnWrite = rtcCMOSWrite;
+ pThis->fDisabledByHpet = false;
+ pThis->cmos_index[1] = CMOS_BANK_SIZE; /* Point to start of second bank. */
+
+
+ /* IBase */
+ pDevIns->IBase.pfnQueryInterface = rtcQueryInterface;
+ /* IHpetLegacyNotify */
+ pThis->IHpetLegacyNotify.pfnModeChanged = rtcHpetLegacyNotify_ModeChanged;
+
+ /*
+ * Create timers.
+ */
+ PTMTIMER pTimer;
+ /* Periodic timer. */
+ rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL_SYNC, rtcTimerPeriodic, pThis,
+ TMTIMER_FLAGS_DEFAULT_CRIT_SECT, "MC146818 RTC/CMOS - Periodic",
+ &pTimer);
+ if (RT_FAILURE(rc))
+ return rc;
+ 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",
+ &pTimer);
+ if (RT_FAILURE(rc))
+ return rc;
+ 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",
+ &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 (fRCEnabled)
+ {
+ 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, 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;
+
+ /*
+ * Register ourselves as the RTC/CMOS with PDM.
+ */
+ rc = PDMDevHlpRTCRegister(pDevIns, &pThis->RtcReg, &pThis->pRtcHlpR3);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Register debugger info callback.
+ */
+ PDMDevHlpDBGFInfoRegister(pDevIns, "cmos1", "Display CMOS Bank 1 Info (0x0e-0x7f). No arguments. See also rtc.", rtcCmosBankInfo);
+ PDMDevHlpDBGFInfoRegister(pDevIns, "cmos2", "Display CMOS Bank 2 Info (0x0e-0x7f). No arguments.", rtcCmosBank2Info);
+ PDMDevHlpDBGFInfoRegister(pDevIns, "rtc", "Display CMOS RTC (0x00-0x0d). No arguments. See also cmos1 & cmos2", rtcCmosClockInfo);
+
+ /*
+ * Register statistics.
+ */
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->StatRTCIrq, STAMTYPE_COUNTER, "/TM/RTC/Irq", STAMUNIT_OCCURENCES, "The number of times a RTC interrupt was triggered.");
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->StatRTCTimerCB, STAMTYPE_COUNTER, "/TM/RTC/TimerCB", STAMUNIT_OCCURENCES, "The number of times the RTC timer callback ran.");
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * The device registration structure.
+ */
+const PDMDEVREG g_DeviceMC146818 =
+{
+ /* u32Version */
+ PDM_DEVREG_VERSION,
+ /* szName */
+ "mc146818",
+ /* szRCMod */
+ "VBoxDDRC.rc",
+ /* szR0Mod */
+ "VBoxDDR0.r0",
+ /* pszDescription */
+ "Motorola MC146818 RTC/CMOS Device.",
+ /* fFlags */
+ PDM_DEVREG_FLAGS_HOST_BITS_DEFAULT | PDM_DEVREG_FLAGS_GUEST_BITS_32_64 | PDM_DEVREG_FLAGS_PAE36 | PDM_DEVREG_FLAGS_RC | PDM_DEVREG_FLAGS_R0,
+ /* fClass */
+ PDM_DEVREG_CLASS_RTC,
+ /* cMaxInstances */
+ 1,
+ /* cbInstance */
+ sizeof(RTCSTATE),
+ /* pfnConstruct */
+ rtcConstruct,
+ /* pfnDestruct */
+ NULL,
+ /* pfnRelocate */
+ rtcRelocate,
+ /* pfnMemSetup */
+ NULL,
+ /* pfnPowerOn */
+ NULL,
+ /* pfnReset */
+ rtcReset,
+ /* pfnSuspend */
+ NULL,
+ /* pfnResume */
+ NULL,
+ /* pfnAttach */
+ NULL,
+ /* pfnDetach */
+ NULL,
+ /* pfnQueryInterface */
+ NULL,
+ /* pfnInitComplete */
+ rtcInitComplete,
+ /* pfnPowerOff */
+ NULL,
+ /* pfnSoftReset */
+ NULL,
+ /* u32VersionEnd */
+ PDM_DEVREG_VERSION
+};
+
+#endif /* IN_RING3 */
+#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
+