DevRTC.cpp revision 2244ff160a626ebdb3164949d5f4da0d6f709486
715N/A/* $Id$ */
715N/A/** @file
715N/A * Motorola MC146818 RTC/CMOS Device with PIIX4 extensions.
715N/A */
715N/A
715N/A/*
715N/A * Copyright (C) 2006-2011 Oracle Corporation
715N/A *
715N/A * This file is part of VirtualBox Open Source Edition (OSE), as
715N/A * available from http://www.virtualbox.org. This file is free software;
715N/A * you can redistribute it and/or modify it under the terms of the GNU
715N/A * General Public License (GPL) as published by the Free Software
715N/A * Foundation, in version 2 as it comes in the "COPYING" file of the
715N/A * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
715N/A * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
715N/A * --------------------------------------------------------------------
715N/A *
715N/A * This code is based on:
715N/A *
715N/A * QEMU MC146818 RTC emulation
1338N/A *
715N/A * Copyright (c) 2003-2004 Fabrice Bellard
715N/A *
715N/A * Permission is hereby granted, free of charge, to any person obtaining a copy
715N/A * of this software and associated documentation files (the "Software"), to deal
715N/A * in the Software without restriction, including without limitation the rights
858N/A * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
857N/A * copies of the Software, and to permit persons to whom the Software is
715N/A * furnished to do so, subject to the following conditions:
715N/A *
715N/A * The above copyright notice and this permission notice shall be included in
715N/A * all copies or substantial portions of the Software.
1338N/A *
715N/A * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
715N/A * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1402N/A * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
715N/A * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1403N/A * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
715N/A * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
844N/A * THE SOFTWARE.
761N/A */
761N/A
715N/A/*******************************************************************************
803N/A* Header Files *
844N/A*******************************************************************************/
851N/A#define LOG_GROUP LOG_GROUP_DEV_RTC
788N/A#include <VBox/vmm/pdmdev.h>
788N/A#include <VBox/log.h>
788N/A#include <iprt/asm-math.h>
760N/A#include <iprt/assert.h>
715N/A#include <iprt/string.h>
726N/A
726N/A#ifdef IN_RING3
726N/A# include <iprt/alloc.h>
1195N/A# include <iprt/uuid.h>
715N/A#endif /* IN_RING3 */
715N/A
715N/A#include "VBoxDD.h"
899N/A
899N/Astruct RTCState;
717N/Atypedef struct RTCState RTCState;
747N/A
747N/A#define RTC_CRC_START 0x10
1421N/A#define RTC_CRC_LAST 0x2d
844N/A#define RTC_CRC_HIGH 0x2e
747N/A#define RTC_CRC_LOW 0x2f
747N/A
788N/A
788N/A/*******************************************************************************
788N/A* Internal Functions *
788N/A*******************************************************************************/
788N/A#ifndef VBOX_DEVICE_STRUCT_TESTCASE
788N/ART_C_DECLS_BEGIN
856N/APDMBOTHCBDECL(int) rtcIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb);
856N/APDMBOTHCBDECL(int) rtcIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb);
856N/APDMBOTHCBDECL(void) rtcTimerPeriodic(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser);
856N/APDMBOTHCBDECL(void) rtcTimerSecond(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser);
856N/APDMBOTHCBDECL(void) rtcTimerSecond2(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser);
856N/ART_C_DECLS_END
715N/A#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
715N/A
760N/A
760N/A/*******************************************************************************
760N/A* Defined Constants And Macros *
851N/A*******************************************************************************/
851N/A/*#define DEBUG_CMOS*/
851N/A
851N/A#define RTC_SECONDS 0
851N/A#define RTC_SECONDS_ALARM 1
851N/A#define RTC_MINUTES 2
851N/A#define RTC_MINUTES_ALARM 3
851N/A#define RTC_HOURS 4
851N/A#define RTC_HOURS_ALARM 5
898N/A#define RTC_ALARM_DONT_CARE 0xC0
898N/A
898N/A#define RTC_DAY_OF_WEEK 6
1421N/A#define RTC_DAY_OF_MONTH 7
1421N/A#define RTC_MONTH 8
1421N/A#define RTC_YEAR 9
864N/A
864N/A#define RTC_REG_A 10
864N/A#define RTC_REG_B 11
803N/A#define RTC_REG_C 12
1238N/A#define RTC_REG_D 13
803N/A
1182N/A#define REG_A_UIP 0x80
857N/A
803N/A#define REG_B_SET 0x80
857N/A#define REG_B_PIE 0x40
857N/A#define REG_B_AIE 0x20
857N/A#define REG_B_UIE 0x10
803N/A
810N/A
860N/A/** The saved state version. */
1195N/A#define RTC_SAVED_STATE_VERSION 4
803N/A/** The saved state version used by VirtualBox pre-3.2.
803N/A * This does not include the second 128-byte bank. */
803N/A#define RTC_SAVED_STATE_VERSION_VBOX_32PRE 3
760N/A/** The saved state version used by VirtualBox 3.1 and earlier.
760N/A * This does not include disabled by HPET state. */
760N/A#define RTC_SAVED_STATE_VERSION_VBOX_31 2
760N/A/** The saved state version used by VirtualBox 3.0 and earlier.
833N/A * This does not include the configuration. */
833N/A#define RTC_SAVED_STATE_VERSION_VBOX_30 1
760N/A
760N/A
760N/A/*******************************************************************************
760N/A* Structures and Typedefs *
760N/A*******************************************************************************/
760N/A/** @todo Replace struct my_tm with RTTIME. */
760N/Astruct my_tm
760N/A{
760N/A int32_t tm_sec;
760N/A int32_t tm_min;
760N/A int32_t tm_hour;
760N/A int32_t tm_mday;
760N/A int32_t tm_mon;
788N/A int32_t tm_year;
887N/A int32_t tm_wday;
887N/A int32_t tm_yday;
887N/A};
887N/A
887N/A
887N/Astruct RTCState {
887N/A uint8_t cmos_data[256];
887N/A uint8_t cmos_index[2];
887N/A uint8_t Alignment0[6];
887N/A struct my_tm current_tm;
788N/A /** The configured IRQ. */
788N/A int32_t irq;
788N/A /** The configured I/O port base. */
788N/A RTIOPORT IOPortBase;
788N/A /** Use UTC or local time initially. */
788N/A bool fUTC;
788N/A /** Disabled by HPET legacy mode. */
788N/A bool fDisabledByHpet;
788N/A /* periodic timer */
788N/A int64_t next_periodic_time;
788N/A /* second update */
793N/A int64_t next_second_time;
1338N/A
1338N/A /** Pointer to the device instance - R3 Ptr. */
1338N/A PPDMDEVINSR3 pDevInsR3;
1338N/A /** The periodic timer (rtcTimerPeriodic) - R3 Ptr. */
793N/A PTMTIMERR3 pPeriodicTimerR3;
1338N/A /** The second timer (rtcTimerSecond) - R3 Ptr. */
793N/A PTMTIMERR3 pSecondTimerR3;
793N/A /** The second second timer (rtcTimerSecond2) - R3 Ptr. */
788N/A PTMTIMERR3 pSecondTimer2R3;
788N/A
788N/A /** Pointer to the device instance - R0 Ptr. */
793N/A PPDMDEVINSR0 pDevInsR0;
793N/A /** The periodic timer (rtcTimerPeriodic) - R0 Ptr. */
788N/A PTMTIMERR0 pPeriodicTimerR0;
788N/A /** The second timer (rtcTimerSecond) - R0 Ptr. */
788N/A PTMTIMERR0 pSecondTimerR0;
803N/A /** The second second timer (rtcTimerSecond2) - R0 Ptr. */
803N/A PTMTIMERR0 pSecondTimer2R0;
803N/A
803N/A /** Pointer to the device instance - RC Ptr. */
803N/A PPDMDEVINSRC pDevInsRC;
803N/A /** The periodic timer (rtcTimerPeriodic) - RC Ptr. */
803N/A PTMTIMERRC pPeriodicTimerRC;
803N/A /** The second timer (rtcTimerSecond) - RC Ptr. */
803N/A PTMTIMERRC pSecondTimerRC;
851N/A /** The second second timer (rtcTimerSecond2) - RC Ptr. */
715N/A PTMTIMERRC pSecondTimer2RC;
715N/A
1402N/A /** The RTC registration structure. */
1402N/A PDMRTCREG RtcReg;
715N/A /** The RTC device helpers. */
726N/A R3PTRTYPE(PCPDMRTCHLP) pRtcHlpR3;
715N/A /** Number of release log entries. Used to prevent flooding. */
717N/A uint32_t cRelLogEntries;
803N/A /** The current/previous logged timer period. */
715N/A int32_t CurLogPeriod;
715N/A /** The current/previous hinted timer period. */
844N/A int32_t CurHintPeriod;
844N/A uint32_t u32AlignmentPadding;
844N/A
844N/A /** HPET legacy mode notification interface. */
1402N/A PDMIHPETLEGACYNOTIFY IHpetLegacyNotify;
1402N/A};
1402N/A
1402N/A#ifndef VBOX_DEVICE_STRUCT_TESTCASE
1402N/A
1402N/Astatic void rtc_timer_update(RTCState *pThis, int64_t current_time)
1402N/A{
1402N/A int period_code, period;
1402N/A uint64_t cur_clock, next_irq_clock;
717N/A uint32_t freq;
803N/A
715N/A Assert(TMTimerIsLockOwner(pThis->CTX_SUFF(pPeriodicTimer)));
715N/A Assert(PDMCritSectIsOwner(pThis->CTX_SUFF(pDevIns)->CTX_SUFF(pCritSectRo)));
717N/A
803N/A period_code = pThis->cmos_data[RTC_REG_A] & 0x0f;
715N/A if ( period_code != 0
715N/A && (pThis->cmos_data[RTC_REG_B] & REG_B_PIE))
1421N/A {
1421N/A if (period_code <= 2)
1421N/A period_code += 7;
1421N/A /* period in 32 kHz cycles */
717N/A period = 1 << (period_code - 1);
803N/A /* compute 32 kHz clock */
841N/A freq = TMTimerGetFreq(pThis->CTX_SUFF(pPeriodicTimer));
841N/A
841N/A cur_clock = ASMMultU64ByU32DivByU32(current_time, 32768, freq);
841N/A next_irq_clock = (cur_clock & ~(uint64_t)(period - 1)) + period;
715N/A pThis->next_periodic_time = ASMMultU64ByU32DivByU32(next_irq_clock, freq, 32768) + 1;
715N/A TMTimerSet(pThis->CTX_SUFF(pPeriodicTimer), pThis->next_periodic_time);
844N/A
844N/A#ifdef IN_RING3
844N/A if (RT_UNLIKELY(period != pThis->CurLogPeriod))
844N/A#else
717N/A if (RT_UNLIKELY(period != pThis->CurHintPeriod))
803N/A#endif
715N/A {
851N/A#ifdef IN_RING3
898N/A if (pThis->cRelLogEntries++ < 64)
898N/A LogRel(("RTC: period=%#x (%d) %u Hz\n", period, period, _32K / period));
898N/A pThis->CurLogPeriod = period;
898N/A#endif
898N/A pThis->CurHintPeriod = period;
898N/A TMTimerSetFrequencyHint(pThis->CTX_SUFF(pPeriodicTimer), _32K / period);
851N/A }
851N/A }
851N/A else
898N/A {
1421N/A if (TMTimerIsActive(pThis->CTX_SUFF(pPeriodicTimer)) && pThis->cRelLogEntries++ < 64)
864N/A LogRel(("RTC: stopped the periodic timer\n"));
864N/A TMTimerStop(pThis->CTX_SUFF(pPeriodicTimer));
864N/A }
864N/A}
864N/A
864N/A
864N/Astatic void rtc_raise_irq(RTCState* pThis, uint32_t iLevel)
864N/A{
864N/A if (!pThis->fDisabledByHpet)
864N/A PDMDevHlpISASetIrq(pThis->CTX_SUFF(pDevIns), pThis->irq, iLevel);
715N/A}
715N/A
1402N/A
1402N/ADECLINLINE(int) to_bcd(RTCState *pThis, int a)
1402N/A{
1402N/A if (pThis->cmos_data[RTC_REG_B] & 0x04)
1402N/A return a;
1402N/A return ((a / 10) << 4) | (a % 10);
1402N/A}
1402N/A
1402N/A
1402N/ADECLINLINE(int) from_bcd(RTCState *pThis, int a)
1402N/A{
1402N/A if (pThis->cmos_data[RTC_REG_B] & 0x04)
1402N/A return a;
1402N/A return ((a >> 4) * 10) + (a & 0x0f);
1402N/A}
1402N/A
1402N/A
1402N/Astatic void rtc_set_time(RTCState *pThis)
1402N/A{
1402N/A struct my_tm *tm = &pThis->current_tm;
1402N/A
1402N/A tm->tm_sec = from_bcd(pThis, pThis->cmos_data[RTC_SECONDS]);
1402N/A tm->tm_min = from_bcd(pThis, pThis->cmos_data[RTC_MINUTES]);
1402N/A tm->tm_hour = from_bcd(pThis, pThis->cmos_data[RTC_HOURS] & 0x7f);
1402N/A if ( !(pThis->cmos_data[RTC_REG_B] & 0x02)
1402N/A && (pThis->cmos_data[RTC_HOURS] & 0x80))
715N/A tm->tm_hour += 12;
715N/A tm->tm_wday = from_bcd(pThis, pThis->cmos_data[RTC_DAY_OF_WEEK]);
715N/A tm->tm_mday = from_bcd(pThis, pThis->cmos_data[RTC_DAY_OF_MONTH]);
715N/A tm->tm_mon = from_bcd(pThis, pThis->cmos_data[RTC_MONTH]) - 1;
715N/A tm->tm_year = from_bcd(pThis, pThis->cmos_data[RTC_YEAR]) + 100;
720N/A}
720N/A
720N/A
720N/A/* -=-=-=-=-=- I/O Port Handlers -=-=-=-=-=- */
720N/A
715N/A
715N/A/**
1402N/A * Port I/O Handler for IN operations.
1402N/A *
1402N/A * @returns VBox status code.
1402N/A *
1402N/A * @param pDevIns The device instance.
1402N/A * @param pvUser User argument - ignored.
1402N/A * @param uPort Port number used for the IN operation.
1402N/A * @param pu32 Where to store the result.
1402N/A * @param cb Number of bytes read.
1402N/A */
1402N/APDMBOTHCBDECL(int) rtcIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
851N/A{
851N/A NOREF(pvUser);
851N/A if (cb != 1)
851N/A return VERR_IOM_IOPORT_UNUSED;
851N/A
851N/A RTCState *pThis = PDMINS_2_DATA(pDevIns, RTCState *);
851N/A if ((Port & 1) == 0)
851N/A *pu32 = 0xff;
851N/A else
851N/A {
851N/A unsigned bank = (Port >> 1) & 1;
851N/A switch (pThis->cmos_index[bank])
851N/A {
851N/A case RTC_SECONDS:
851N/A case RTC_MINUTES:
851N/A case RTC_HOURS:
851N/A case RTC_DAY_OF_WEEK:
851N/A case RTC_DAY_OF_MONTH:
851N/A case RTC_MONTH:
851N/A case RTC_YEAR:
851N/A *pu32 = pThis->cmos_data[pThis->cmos_index[0]];
851N/A break;
851N/A
851N/A case RTC_REG_A:
851N/A *pu32 = pThis->cmos_data[pThis->cmos_index[0]];
851N/A break;
851N/A
815N/A case RTC_REG_C:
715N/A *pu32 = pThis->cmos_data[pThis->cmos_index[0]];
715N/A rtc_raise_irq(pThis, 0);
760N/A pThis->cmos_data[RTC_REG_C] = 0x00;
760N/A break;
788N/A
788N/A default:
788N/A *pu32 = pThis->cmos_data[pThis->cmos_index[bank]];
720N/A break;
788N/A }
788N/A
788N/A Log(("CMOS: Read bank %d idx %#04x: %#04x\n", bank, pThis->cmos_index[bank], *pu32));
788N/A }
788N/A
788N/A return VINF_SUCCESS;
788N/A}
788N/A
788N/A
788N/A/**
788N/A * Port I/O Handler for OUT operations.
720N/A *
788N/A * @returns VBox status code.
720N/A *
715N/A * @param pDevIns The device instance.
715N/A * @param pvUser User argument - ignored.
715N/A * @param uPort Port number used for the IN operation.
715N/A * @param u32 The value to output.
715N/A * @param cb The value size in bytes.
715N/A */
803N/APDMBOTHCBDECL(int) rtcIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
803N/A{
726N/A NOREF(pvUser);
717N/A if (cb != 1)
717N/A return VINF_SUCCESS;
717N/A
815N/A RTCState *pThis = PDMINS_2_DATA(pDevIns, RTCState *);
764N/A uint32_t bank = (Port >> 1) & 1;
715N/A if ((Port & 1) == 0)
715N/A {
715N/A pThis->cmos_index[bank] = (u32 & 0x7f) + (bank * 128);
788N/A }
788N/A else
788N/A {
720N/A Log(("CMOS: Write bank %d idx %#04x: %#04x (old %#04x)\n", bank,
788N/A pThis->cmos_index[bank], u32, pThis->cmos_data[pThis->cmos_index[bank]]));
788N/A
1407N/A int const idx = pThis->cmos_index[bank];
788N/A switch (idx)
788N/A {
788N/A case RTC_SECONDS_ALARM:
788N/A case RTC_MINUTES_ALARM:
788N/A case RTC_HOURS_ALARM:
788N/A pThis->cmos_data[pThis->cmos_index[0]] = u32;
788N/A break;
788N/A
720N/A case RTC_SECONDS:
788N/A case RTC_MINUTES:
720N/A case RTC_HOURS:
715N/A case RTC_DAY_OF_WEEK:
715N/A case RTC_DAY_OF_MONTH:
715N/A case RTC_MONTH:
715N/A case RTC_YEAR:
715N/A pThis->cmos_data[pThis->cmos_index[0]] = u32;
715N/A /* if in set mode, do not update the time */
715N/A if (!(pThis->cmos_data[RTC_REG_B] & REG_B_SET))
715N/A rtc_set_time(pThis);
715N/A break;
715N/A
715N/A case RTC_REG_A:
715N/A case RTC_REG_B:
715N/A {
715N/A /* We need to acquire the clock lock, because of lock ordering
715N/A issues this means having to release the device lock. Since
715N/A we're letting IOM do the locking, we must not return without
715N/A holding the device lock.*/
715N/A PDMCritSectLeave(pThis->CTX_SUFF(pDevIns)->CTX_SUFF(pCritSectRo));
715N/A int rc1 = TMTimerLock(pThis->CTX_SUFF(pPeriodicTimer), VINF_SUCCESS /* must get it */);
715N/A int rc2 = PDMCritSectEnter(pThis->CTX_SUFF(pDevIns)->CTX_SUFF(pCritSectRo), VINF_SUCCESS /* must get it */);
715N/A AssertRCReturn(rc1, rc1);
715N/A AssertRCReturnStmt(rc2, TMTimerUnlock(pThis->CTX_SUFF(pPeriodicTimer)), rc2);
715N/A
715N/A if (idx == RTC_REG_A)
715N/A {
763N/A /* UIP bit is read only */
763N/A pThis->cmos_data[RTC_REG_A] = (u32 & ~REG_A_UIP)
763N/A | (pThis->cmos_data[RTC_REG_A] & REG_A_UIP);
763N/A }
763N/A else
763N/A {
763N/A if (u32 & REG_B_SET)
763N/A {
763N/A /* set mode: reset UIP mode */
763N/A pThis->cmos_data[RTC_REG_A] &= ~REG_A_UIP;
763N/A#if 0 /* This is probably wrong as it breaks changing the time/date in OS/2. */
763N/A u32 &= ~REG_B_UIE;
763N/A#endif
763N/A }
763N/A else
763N/A {
763N/A /* if disabling set mode, update the time */
763N/A if (pThis->cmos_data[RTC_REG_B] & REG_B_SET)
763N/A rtc_set_time(pThis);
763N/A }
763N/A pThis->cmos_data[RTC_REG_B] = u32;
715N/A }
715N/A
761N/A rtc_timer_update(pThis, TMTimerGet(pThis->CTX_SUFF(pPeriodicTimer)));
844N/A
844N/A TMTimerUnlock(pThis->CTX_SUFF(pPeriodicTimer));
844N/A /* the caller leaves the other lock. */
844N/A break;
844N/A }
844N/A
844N/A case RTC_REG_C:
844N/A case RTC_REG_D:
844N/A /* cannot write to them */
844N/A break;
844N/A
844N/A default:
844N/A pThis->cmos_data[pThis->cmos_index[bank]] = u32;
844N/A break;
844N/A }
844N/A }
844N/A
844N/A return VINF_SUCCESS;
844N/A}
844N/A
844N/A#ifdef IN_RING3
844N/A
844N/A/* -=-=-=-=-=- Timers and their support code -=-=-=-=-=- */
844N/A
844N/A
844N/A/**
844N/A * Device timer callback function, periodic.
844N/A *
844N/A * @param pDevIns Device instance of the device which registered the timer.
844N/A * @param pTimer The timer handle.
844N/A * @param pvUser Pointer to the RTC state.
844N/A */
844N/Astatic DECLCALLBACK(void) rtcTimerPeriodic(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
844N/A{
844N/A RTCState *pThis = PDMINS_2_DATA(pDevIns, RTCState *);
844N/A Assert(TMTimerIsLockOwner(pThis->CTX_SUFF(pPeriodicTimer)));
844N/A Assert(PDMCritSectIsOwner(pThis->CTX_SUFF(pDevIns)->CTX_SUFF(pCritSectRo)));
844N/A
844N/A rtc_timer_update(pThis, pThis->next_periodic_time);
844N/A pThis->cmos_data[RTC_REG_C] |= 0xc0;
844N/A
844N/A rtc_raise_irq(pThis, 1);
844N/A}
844N/A
844N/A
844N/A/* month is between 0 and 11. */
844N/Astatic int get_days_in_month(int month, int year)
844N/A{
844N/A static const int days_tab[12] =
844N/A {
844N/A 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
844N/A };
844N/A int d;
856N/A
856N/A if ((unsigned )month >= 12)
856N/A return 31;
856N/A
856N/A d = days_tab[month];
856N/A if (month == 1)
856N/A {
856N/A if ((year % 4) == 0 && ((year % 100) != 0 || (year % 400) == 0))
856N/A d++;
856N/A }
856N/A return d;
856N/A}
856N/A
856N/A
856N/A/* update 'tm' to the next second */
856N/Astatic void rtc_next_second(struct my_tm *tm)
856N/A{
856N/A int days_in_month;
856N/A
761N/A tm->tm_sec++;
761N/A if ((unsigned)tm->tm_sec >= 60)
761N/A {
761N/A tm->tm_sec = 0;
779N/A tm->tm_min++;
803N/A if ((unsigned)tm->tm_min >= 60)
779N/A {
779N/A tm->tm_min = 0;
779N/A tm->tm_hour++;
779N/A if ((unsigned)tm->tm_hour >= 24)
779N/A {
779N/A tm->tm_hour = 0;
779N/A /* next day */
779N/A tm->tm_wday++;
803N/A if ((unsigned)tm->tm_wday >= 7)
726N/A tm->tm_wday = 0;
826N/A days_in_month = get_days_in_month(tm->tm_mon,
826N/A tm->tm_year + 1900);
826N/A tm->tm_mday++;
826N/A if (tm->tm_mday < 1)
815N/A tm->tm_mday = 1;
839N/A else if (tm->tm_mday > days_in_month)
715N/A {
788N/A tm->tm_mday = 1;
788N/A tm->tm_mon++;
788N/A if (tm->tm_mon >= 12)
839N/A {
788N/A tm->tm_mon = 0;
788N/A tm->tm_year++;
788N/A }
788N/A }
788N/A }
788N/A }
788N/A }
788N/A}
788N/A
788N/A
788N/A/**
788N/A * Device timer callback function, second.
839N/A *
839N/A * @param pDevIns Device instance of the device which registered the timer.
788N/A * @param pTimer The timer handle.
763N/A * @param pvUser Pointer to the RTC state.
715N/A */
715N/Astatic DECLCALLBACK(void) rtcTimerSecond(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
788N/A{
788N/A RTCState *pThis = PDMINS_2_DATA(pDevIns, RTCState *);
715N/A Assert(TMTimerIsLockOwner(pThis->CTX_SUFF(pPeriodicTimer)));
788N/A Assert(PDMCritSectIsOwner(pThis->CTX_SUFF(pDevIns)->CTX_SUFF(pCritSectRo)));
788N/A
844N/A /* if the oscillator is not in normal operation, we do not update */
788N/A if ((pThis->cmos_data[RTC_REG_A] & 0x70) != 0x20)
844N/A {
788N/A pThis->next_second_time += TMTimerGetFreq(pThis->CTX_SUFF(pSecondTimer));
788N/A TMTimerSet(pThis->CTX_SUFF(pSecondTimer), pThis->next_second_time);
788N/A }
844N/A else
844N/A {
788N/A rtc_next_second(&pThis->current_tm);
788N/A
826N/A if (!(pThis->cmos_data[RTC_REG_B] & REG_B_SET))
839N/A {
839N/A /* update in progress bit */
826N/A Log2(("RTC: UIP %x -> 1\n", !!(pThis->cmos_data[RTC_REG_A] & REG_A_UIP)));
788N/A pThis->cmos_data[RTC_REG_A] |= REG_A_UIP;
715N/A }
788N/A
826N/A /* 244140 ns = 8 / 32768 seconds */
788N/A uint64_t delay = TMTimerFromNano(pThis->CTX_SUFF(pSecondTimer2), 244140);
826N/A TMTimerSet(pThis->CTX_SUFF(pSecondTimer2), pThis->next_second_time + delay);
1421N/A }
1421N/A}
1421N/A
826N/A
1421N/A/* Used by rtc_set_date and rtcTimerSecond2. */
826N/Astatic void rtc_copy_date(RTCState *pThis)
826N/A{
839N/A const struct my_tm *tm = &pThis->current_tm;
839N/A
839N/A pThis->cmos_data[RTC_SECONDS] = to_bcd(pThis, tm->tm_sec);
1421N/A pThis->cmos_data[RTC_MINUTES] = to_bcd(pThis, tm->tm_min);
839N/A if (pThis->cmos_data[RTC_REG_B] & 0x02)
839N/A {
839N/A /* 24 hour format */
839N/A pThis->cmos_data[RTC_HOURS] = to_bcd(pThis, tm->tm_hour);
839N/A }
839N/A else
839N/A {
839N/A /* 12 hour format */
839N/A pThis->cmos_data[RTC_HOURS] = to_bcd(pThis, tm->tm_hour % 12);
839N/A if (tm->tm_hour >= 12)
826N/A pThis->cmos_data[RTC_HOURS] |= 0x80;
720N/A }
715N/A pThis->cmos_data[RTC_DAY_OF_WEEK] = to_bcd(pThis, tm->tm_wday);
715N/A pThis->cmos_data[RTC_DAY_OF_MONTH] = to_bcd(pThis, tm->tm_mday);
788N/A pThis->cmos_data[RTC_MONTH] = to_bcd(pThis, tm->tm_mon + 1);
715N/A pThis->cmos_data[RTC_YEAR] = to_bcd(pThis, tm->tm_year % 100);
788N/A}
788N/A
715N/A
715N/A/**
715N/A * Device timer callback function, second2.
715N/A *
715N/A * @param pDevIns Device instance of the device which registered the timer.
715N/A * @param pTimer The timer handle.
715N/A * @param pvUser Pointer to the RTC state.
1230N/A */
803N/Astatic DECLCALLBACK(void) rtcTimerSecond2(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
726N/A{
1230N/A RTCState *pThis = PDMINS_2_DATA(pDevIns, RTCState *);
803N/A Assert(TMTimerIsLockOwner(pThis->CTX_SUFF(pPeriodicTimer)));
726N/A Assert(PDMCritSectIsOwner(pThis->CTX_SUFF(pDevIns)->CTX_SUFF(pCritSectRo)));
815N/A
743N/A if (!(pThis->cmos_data[RTC_REG_B] & REG_B_SET))
743N/A rtc_copy_date(pThis);
715N/A
726N/A /* check alarm */
726N/A if (pThis->cmos_data[RTC_REG_B] & REG_B_AIE)
715N/A {
745N/A if ( ( (pThis->cmos_data[RTC_SECONDS_ALARM] & 0xc0) == 0xc0
715N/A || from_bcd(pThis, pThis->cmos_data[RTC_SECONDS_ALARM]) == pThis->current_tm.tm_sec)
715N/A && ( (pThis->cmos_data[RTC_MINUTES_ALARM] & 0xc0) == 0xc0
715N/A || from_bcd(pThis, pThis->cmos_data[RTC_MINUTES_ALARM]) == pThis->current_tm.tm_min)
715N/A && ( (pThis->cmos_data[RTC_HOURS_ALARM ] & 0xc0) == 0xc0
715N/A || from_bcd(pThis, pThis->cmos_data[RTC_HOURS_ALARM ]) == pThis->current_tm.tm_hour)
715N/A )
715N/A {
715N/A pThis->cmos_data[RTC_REG_C] |= 0xa0;
1230N/A rtc_raise_irq(pThis, 1);
803N/A }
745N/A }
1230N/A
844N/A /* update ended interrupt */
844N/A if (pThis->cmos_data[RTC_REG_B] & REG_B_UIE)
1230N/A {
803N/A pThis->cmos_data[RTC_REG_C] |= 0x90;
745N/A rtc_raise_irq(pThis, 1);
745N/A }
745N/A
745N/A /* clear update in progress bit */
788N/A Log2(("RTC: UIP %x -> 0\n", !!(pThis->cmos_data[RTC_REG_A] & REG_A_UIP)));
788N/A pThis->cmos_data[RTC_REG_A] &= ~REG_A_UIP;
1421N/A
788N/A pThis->next_second_time += TMTimerGetFreq(pThis->CTX_SUFF(pSecondTimer));
844N/A TMTimerSet(pThis->CTX_SUFF(pSecondTimer), pThis->next_second_time);
788N/A}
844N/A
788N/A
745N/A/* -=-=-=-=-=- Saved State -=-=-=-=-=- */
788N/A
788N/A
788N/A/**
788N/A * @copydoc FNSSMDEVLIVEEXEC
788N/A */
788N/Astatic DECLCALLBACK(int) rtcLiveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uPass)
788N/A{
788N/A RTCState *pThis = PDMINS_2_DATA(pDevIns, RTCState *);
788N/A
788N/A SSMR3PutU8( pSSM, pThis->irq);
788N/A SSMR3PutIOPort(pSSM, pThis->IOPortBase);
1421N/A SSMR3PutBool( pSSM, pThis->fUTC);
1421N/A
1421N/A return VINF_SSM_DONT_CALL_AGAIN;
1421N/A}
1421N/A
745N/A
844N/A/**
844N/A * @copydoc FNSSMDEVSAVEEXEC
844N/A */
844N/Astatic DECLCALLBACK(int) rtcSaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
788N/A{
844N/A RTCState *pThis = PDMINS_2_DATA(pDevIns, RTCState *);
844N/A
788N/A /* The config. */
788N/A rtcLiveExec(pDevIns, pSSM, SSM_PASS_FINAL);
788N/A
788N/A /* The state. */
788N/A SSMR3PutMem(pSSM, pThis->cmos_data, 128);
745N/A SSMR3PutU8(pSSM, pThis->cmos_index[0]);
844N/A
844N/A SSMR3PutS32(pSSM, pThis->current_tm.tm_sec);
844N/A SSMR3PutS32(pSSM, pThis->current_tm.tm_min);
844N/A SSMR3PutS32(pSSM, pThis->current_tm.tm_hour);
788N/A SSMR3PutS32(pSSM, pThis->current_tm.tm_wday);
788N/A SSMR3PutS32(pSSM, pThis->current_tm.tm_mday);
788N/A SSMR3PutS32(pSSM, pThis->current_tm.tm_mon);
788N/A SSMR3PutS32(pSSM, pThis->current_tm.tm_year);
788N/A
788N/A TMR3TimerSave(pThis->CTX_SUFF(pPeriodicTimer), pSSM);
788N/A
788N/A SSMR3PutS64(pSSM, pThis->next_periodic_time);
788N/A
788N/A SSMR3PutS64(pSSM, pThis->next_second_time);
788N/A TMR3TimerSave(pThis->CTX_SUFF(pSecondTimer), pSSM);
788N/A TMR3TimerSave(pThis->CTX_SUFF(pSecondTimer2), pSSM);
788N/A
745N/A SSMR3PutBool(pSSM, pThis->fDisabledByHpet);
761N/A
761N/A SSMR3PutMem(pSSM, &pThis->cmos_data[128], 128);
761N/A return SSMR3PutU8(pSSM, pThis->cmos_index[1]);
761N/A}
761N/A
761N/A
761N/A/**
761N/A * @copydoc FNSSMDEVLOADEXEC
761N/A */
761N/Astatic DECLCALLBACK(int) rtcLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
761N/A{
794N/A RTCState *pThis = PDMINS_2_DATA(pDevIns, RTCState *);
788N/A int rc;
788N/A
788N/A if ( uVersion != RTC_SAVED_STATE_VERSION
788N/A && uVersion != RTC_SAVED_STATE_VERSION_VBOX_32PRE
788N/A && uVersion != RTC_SAVED_STATE_VERSION_VBOX_31
1421N/A && uVersion != RTC_SAVED_STATE_VERSION_VBOX_30)
1421N/A return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
1421N/A
1421N/A /* The config. */
1421N/A if (uVersion > RTC_SAVED_STATE_VERSION_VBOX_30)
1421N/A {
788N/A uint8_t u8Irq;
856N/A rc = SSMR3GetU8(pSSM, &u8Irq); AssertRCReturn(rc, rc);
856N/A if (u8Irq != pThis->irq)
856N/A return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Config mismatch - u8Irq: saved=%#x config=%#x"), u8Irq, pThis->irq);
856N/A
856N/A RTIOPORT IOPortBase;
856N/A rc = SSMR3GetIOPort(pSSM, &IOPortBase); AssertRCReturn(rc, rc);
1421N/A if (IOPortBase != pThis->IOPortBase)
851N/A return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Config mismatch - IOPortBase: saved=%RTiop config=%RTiop"), IOPortBase, pThis->IOPortBase);
1421N/A
788N/A bool fUTC;
745N/A rc = SSMR3GetBool(pSSM, &fUTC); AssertRCReturn(rc, rc);
844N/A if (fUTC != pThis->fUTC)
844N/A LogRel(("RTC: Config mismatch - fUTC: saved=%RTbool config=%RTbool\n", fUTC, pThis->fUTC));
844N/A }
844N/A
788N/A if (uPass != SSM_PASS_FINAL)
788N/A return VINF_SUCCESS;
844N/A
844N/A /* The state. */
788N/A SSMR3GetMem(pSSM, pThis->cmos_data, 128);
788N/A SSMR3GetU8(pSSM, &pThis->cmos_index[0]);
844N/A
844N/A SSMR3GetS32(pSSM, &pThis->current_tm.tm_sec);
844N/A SSMR3GetS32(pSSM, &pThis->current_tm.tm_min);
844N/A SSMR3GetS32(pSSM, &pThis->current_tm.tm_hour);
844N/A SSMR3GetS32(pSSM, &pThis->current_tm.tm_wday);
844N/A SSMR3GetS32(pSSM, &pThis->current_tm.tm_mday);
844N/A SSMR3GetS32(pSSM, &pThis->current_tm.tm_mon);
844N/A SSMR3GetS32(pSSM, &pThis->current_tm.tm_year);
844N/A
844N/A TMR3TimerLoad(pThis->CTX_SUFF(pPeriodicTimer), pSSM);
844N/A
788N/A SSMR3GetS64(pSSM, &pThis->next_periodic_time);
788N/A
788N/A SSMR3GetS64(pSSM, &pThis->next_second_time);
788N/A TMR3TimerLoad(pThis->CTX_SUFF(pSecondTimer), pSSM);
788N/A TMR3TimerLoad(pThis->CTX_SUFF(pSecondTimer2), pSSM);
788N/A
788N/A if (uVersion > RTC_SAVED_STATE_VERSION_VBOX_31)
788N/A SSMR3GetBool(pSSM, &pThis->fDisabledByHpet);
788N/A
788N/A if (uVersion > RTC_SAVED_STATE_VERSION_VBOX_32PRE)
788N/A {
745N/A /* Second CMOS bank. */
745N/A SSMR3GetMem(pSSM, &pThis->cmos_data[128], 128);
745N/A SSMR3GetU8(pSSM, &pThis->cmos_index[1]);
745N/A }
745N/A
795N/A int period_code = pThis->cmos_data[RTC_REG_A] & 0x0f;
795N/A if ( period_code != 0
795N/A && (pThis->cmos_data[RTC_REG_B] & REG_B_PIE)) {
795N/A if (period_code <= 2)
795N/A period_code += 7;
795N/A int period = 1 << (period_code - 1);
795N/A LogRel(("RTC: period=%#x (%d) %u Hz (restore)\n", period, period, _32K / period));
815N/A TMTimerSetFrequencyHint(pThis->CTX_SUFF(pPeriodicTimer), _32K / period);
795N/A pThis->CurLogPeriod = period;
795N/A pThis->CurHintPeriod = period;
795N/A } else {
795N/A LogRel(("RTC: stopped the periodic timer (restore)\n"));
795N/A pThis->CurLogPeriod = 0;
795N/A pThis->CurHintPeriod = 0;
880N/A }
795N/A pThis->cRelLogEntries = 0;
795N/A
795N/A return VINF_SUCCESS;
795N/A}
795N/A
795N/A
795N/A/* -=-=-=-=-=- PDM Interface provided by the RTC device -=-=-=-=-=- */
795N/A
795N/A/**
747N/A * Calculate and update the standard CMOS checksum.
747N/A *
747N/A * @param pThis Pointer to the RTC state data.
747N/A */
747N/Astatic void rtcCalcCRC(RTCState *pThis)
747N/A{
747N/A uint16_t u16;
747N/A unsigned i;
747N/A
747N/A for (i = RTC_CRC_START, u16 = 0; i <= RTC_CRC_LAST; i++)
747N/A u16 += pThis->cmos_data[i];
747N/A pThis->cmos_data[RTC_CRC_LOW] = u16 & 0xff;
747N/A pThis->cmos_data[RTC_CRC_HIGH] = (u16 >> 8) & 0xff;
747N/A}
747N/A
747N/A
795N/A/**
795N/A * Write to a CMOS register and update the checksum if necessary.
795N/A *
795N/A * @returns VBox status code.
795N/A * @param pDevIns Device instance of the RTC.
747N/A * @param iReg The CMOS register index; bit 8 determines bank.
880N/A * @param u8Value The CMOS register value.
747N/A */
747N/Astatic DECLCALLBACK(int) rtcCMOSWrite(PPDMDEVINS pDevIns, unsigned iReg, uint8_t u8Value)
747N/A{
747N/A RTCState *pThis = PDMINS_2_DATA(pDevIns, RTCState *);
747N/A if (iReg < RT_ELEMENTS(pThis->cmos_data))
880N/A {
747N/A PDMCritSectEnter(pDevIns->pCritSectRoR3, VERR_IGNORED);
747N/A
747N/A pThis->cmos_data[iReg] = u8Value;
747N/A
747N/A /* does it require checksum update? */
747N/A if ( iReg >= RTC_CRC_START
788N/A && iReg <= RTC_CRC_LAST)
788N/A rtcCalcCRC(pThis);
788N/A
788N/A PDMCritSectLeave(pDevIns->pCritSectRoR3);
788N/A return VINF_SUCCESS;
788N/A }
788N/A
788N/A AssertMsgFailed(("iReg=%d\n", iReg));
788N/A return VERR_INVALID_PARAMETER;
788N/A}
788N/A
788N/A
788N/A/**
747N/A * Read a CMOS register.
747N/A *
747N/A * @returns VBox status code.
747N/A * @param pDevIns Device instance of the RTC.
747N/A * @param iReg The CMOS register index; bit 8 determines bank.
747N/A * @param pu8Value Where to store the CMOS register value.
747N/A */
747N/Astatic DECLCALLBACK(int) rtcCMOSRead(PPDMDEVINS pDevIns, unsigned iReg, uint8_t *pu8Value)
880N/A{
880N/A RTCState *pThis = PDMINS_2_DATA(pDevIns, RTCState *);
880N/A if (iReg < RT_ELEMENTS(pThis->cmos_data))
880N/A {
880N/A PDMCritSectEnter(pDevIns->pCritSectRoR3, VERR_IGNORED);
880N/A
880N/A *pu8Value = pThis->cmos_data[iReg];
880N/A
880N/A PDMCritSectLeave(pDevIns->pCritSectRoR3);
880N/A return VINF_SUCCESS;
880N/A }
880N/A AssertMsgFailed(("iReg=%d\n", iReg));
880N/A return VERR_INVALID_PARAMETER;
880N/A}
880N/A
880N/A
880N/A/**
880N/A * @interface_method_impl{PDMIHPETLEGACYNOTIFY,pfnModeChanged}
880N/A */
880N/Astatic DECLCALLBACK(void) rtcHpetLegacyNotify_ModeChanged(PPDMIHPETLEGACYNOTIFY pInterface, bool fActivated)
880N/A{
880N/A RTCState *pThis = RT_FROM_MEMBER(pInterface, RTCState, IHpetLegacyNotify);
880N/A PDMCritSectEnter(pThis->pDevInsR3->pCritSectRoR3, VERR_IGNORED);
880N/A
880N/A pThis->fDisabledByHpet = fActivated;
880N/A
880N/A PDMCritSectLeave(pThis->pDevInsR3->pCritSectRoR3);
880N/A}
880N/A
880N/A
880N/A/* -=-=-=-=-=- based on bits from pc.c -=-=-=-=-=- */
880N/A
880N/A
880N/Astatic void rtc_set_memory(RTCState *pThis, int addr, int val)
880N/A{
880N/A if (addr >= 0 && addr <= 127)
880N/A pThis->cmos_data[addr] = val;
880N/A}
880N/A
745N/A
745N/Astatic void rtc_set_date(RTCState *pThis, const struct my_tm *tm)
745N/A{
745N/A pThis->current_tm = *tm;
745N/A rtc_copy_date(pThis);
745N/A}
745N/A
745N/A
745N/A/** @copydoc FNPDMDEVINITCOMPLETE */
745N/Astatic DECLCALLBACK(int) rtcInitComplete(PPDMDEVINS pDevIns)
745N/A{
745N/A /** @todo this should be (re)done at power on if we didn't load a state... */
745N/A RTCState *pThis = PDMINS_2_DATA(pDevIns, RTCState *);
745N/A
745N/A /*
745N/A * Set the CMOS date/time.
745N/A */
745N/A RTTIMESPEC Now;
745N/A PDMDevHlpTMUtcNow(pDevIns, &Now);
745N/A RTTIME Time;
745N/A if (pThis->fUTC)
745N/A RTTimeExplode(&Time, &Now);
745N/A else
745N/A RTTimeLocalExplode(&Time, &Now);
745N/A
745N/A struct my_tm Tm;
745N/A memset(&Tm, 0, sizeof(Tm));
745N/A Tm.tm_year = Time.i32Year - 1900;
745N/A Tm.tm_mon = Time.u8Month - 1;
1230N/A Tm.tm_mday = Time.u8MonthDay;
803N/A Tm.tm_wday = (Time.u8WeekDay + 1 + 7) % 7; /* 0 = Monday -> Sunday */
726N/A Tm.tm_yday = Time.u16YearDay - 1;
1230N/A Tm.tm_hour = Time.u8Hour;
803N/A Tm.tm_min = Time.u8Minute;
726N/A Tm.tm_sec = Time.u8Second;
1421N/A
1421N/A rtc_set_date(pThis, &Tm);
1421N/A
1421N/A int iYear = to_bcd(pThis, (Tm.tm_year / 100) + 19); /* tm_year is 1900 based */
1421N/A rtc_set_memory(pThis, 0x32, iYear); /* 32h - Century Byte (BCD value for the century */
1421N/A rtc_set_memory(pThis, 0x37, iYear); /* 37h - (IBM PS/2) Date Century Byte */
715N/A
745N/A /*
745N/A * Recalculate the checksum just in case.
745N/A */
715N/A rtcCalcCRC(pThis);
715N/A
715N/A Log(("CMOS bank 0: \n%16.128Rhxd\n", &pThis->cmos_data[0]));
715N/A Log(("CMOS bank 1: \n%16.128Rhxd\n", &pThis->cmos_data[128]));
715N/A return VINF_SUCCESS;
715N/A}
726N/A
715N/A
715N/A/* -=-=-=-=-=- real code -=-=-=-=-=- */
745N/A
745N/A/**
745N/A * @interface_method_impl{PDMIBASE,pfnQueryInterface}
745N/A */
745N/Astatic DECLCALLBACK(void *) rtcQueryInterface(PPDMIBASE pInterface, const char *pszIID)
745N/A{
745N/A PPDMDEVINS pDevIns = RT_FROM_MEMBER(pInterface, PDMDEVINS, IBase);
745N/A RTCState *pThis = PDMINS_2_DATA(pDevIns, RTCState *);
745N/A PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDevIns->IBase);
745N/A PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHPETLEGACYNOTIFY, &pThis->IHpetLegacyNotify);
715N/A return NULL;
745N/A}
745N/A
726N/A/**
726N/A * @copydoc
745N/A */
898N/Astatic DECLCALLBACK(void) rtcRelocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
726N/A{
898N/A RTCState *pThis = PDMINS_2_DATA(pDevIns, RTCState *);
726N/A
745N/A pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
846N/A pThis->pPeriodicTimerRC = TMTimerRCPtr(pThis->pPeriodicTimerR3);
721N/A pThis->pSecondTimerRC = TMTimerRCPtr(pThis->pSecondTimerR3);
715N/A pThis->pSecondTimer2RC = TMTimerRCPtr(pThis->pSecondTimer2R3);
715N/A}
715N/A
715N/A
1421N/A/**
1421N/A * @interface_method_impl{PDMDEVREG,pfnConstruct}
1421N/A */
1421N/Astatic DECLCALLBACK(int) rtcConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
1421N/A{
1421N/A RTCState *pThis = PDMINS_2_DATA(pDevIns, RTCState *);
1421N/A int rc;
1421N/A Assert(iInstance == 0);
1421N/A
1421N/A /*
1421N/A * Validate configuration.
1421N/A */
1421N/A if (!CFGMR3AreValuesValid(pCfg,
1421N/A "Irq\0"
1421N/A "Base\0"
1421N/A "UseUTC\0"
1421N/A "GCEnabled\0"
1421N/A "R0Enabled\0"))
1421N/A return VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES;
1421N/A
1421N/A /*
1421N/A * Init the data.
1421N/A */
1421N/A uint8_t u8Irq;
1421N/A rc = CFGMR3QueryU8Def(pCfg, "Irq", &u8Irq, 8);
1421N/A if (RT_FAILURE(rc))
1421N/A return PDMDEV_SET_ERROR(pDevIns, rc,
1421N/A N_("Configuration error: Querying \"Irq\" as a uint8_t failed"));
1421N/A pThis->irq = u8Irq;
1421N/A
1421N/A rc = CFGMR3QueryPortDef(pCfg, "Base", &pThis->IOPortBase, 0x70);
1421N/A if (RT_FAILURE(rc))
715N/A return PDMDEV_SET_ERROR(pDevIns, rc,
1230N/A N_("Configuration error: Querying \"Base\" as a RTIOPORT failed"));
844N/A
844N/A rc = CFGMR3QueryBoolDef(pCfg, "UseUTC", &pThis->fUTC, false);
1230N/A if (RT_FAILURE(rc))
803N/A return PDMDEV_SET_ERROR(pDevIns, rc,
745N/A N_("Configuration error: Querying \"UseUTC\" as a bool failed"));
1230N/A
844N/A bool fGCEnabled;
844N/A rc = CFGMR3QueryBoolDef(pCfg, "GCEnabled", &fGCEnabled, true);
1230N/A if (RT_FAILURE(rc))
803N/A return PDMDEV_SET_ERROR(pDevIns, rc,
745N/A N_("Configuration error: failed to read GCEnabled as boolean"));
715N/A
844N/A bool fR0Enabled;
844N/A rc = CFGMR3QueryBoolDef(pCfg, "R0Enabled", &fR0Enabled, true);
844N/A if (RT_FAILURE(rc))
715N/A return PDMDEV_SET_ERROR(pDevIns, rc,
745N/A N_("Configuration error: failed to read R0Enabled as boolean"));
844N/A
715N/A Log(("RTC: Irq=%#x Base=%#x fGCEnabled=%RTbool fR0Enabled=%RTbool\n",
844N/A u8Irq, pThis->IOPortBase, fGCEnabled, fR0Enabled));
844N/A
715N/A
844N/A pThis->pDevInsR3 = pDevIns;
844N/A pThis->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
844N/A pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
745N/A pThis->cmos_data[RTC_REG_A] = 0x26;
844N/A pThis->cmos_data[RTC_REG_B] = 0x02;
844N/A pThis->cmos_data[RTC_REG_C] = 0x00;
844N/A pThis->cmos_data[RTC_REG_D] = 0x80;
844N/A pThis->RtcReg.u32Version = PDM_RTCREG_VERSION;
852N/A pThis->RtcReg.pfnRead = rtcCMOSRead;
852N/A pThis->RtcReg.pfnWrite = rtcCMOSWrite;
844N/A pThis->fDisabledByHpet = false;
844N/A
844N/A /* IBase */
844N/A pDevIns->IBase.pfnQueryInterface = rtcQueryInterface;
844N/A /* IHpetLegacyNotify */
844N/A pThis->IHpetLegacyNotify.pfnModeChanged = rtcHpetLegacyNotify_ModeChanged;
844N/A
844N/A /*
844N/A * Create timers, arm them, register I/O Ports and save state.
844N/A */
844N/A rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL_SYNC, rtcTimerPeriodic, pThis,
844N/A TMTIMER_FLAGS_DEFAULT_CRIT_SECT, "MC146818 RTC/CMOS - Periodic",
851N/A &pThis->pPeriodicTimerR3);
844N/A if (RT_FAILURE(rc))
844N/A return rc;
851N/A pThis->pPeriodicTimerR0 = TMTimerR0Ptr(pThis->pPeriodicTimerR3);
844N/A pThis->pPeriodicTimerRC = TMTimerRCPtr(pThis->pPeriodicTimerR3);
851N/A
852N/A rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL_SYNC, rtcTimerSecond, pThis,
852N/A TMTIMER_FLAGS_DEFAULT_CRIT_SECT, "MC146818 RTC/CMOS - Second",
852N/A &pThis->pSecondTimerR3);
852N/A if (RT_FAILURE(rc))
852N/A return rc;
852N/A pThis->pSecondTimerR0 = TMTimerR0Ptr(pThis->pSecondTimerR3);
852N/A pThis->pSecondTimerRC = TMTimerRCPtr(pThis->pSecondTimerR3);
852N/A
852N/A rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL_SYNC, rtcTimerSecond2, pThis,
852N/A TMTIMER_FLAGS_DEFAULT_CRIT_SECT, "MC146818 RTC/CMOS - Second2",
852N/A &pThis->pSecondTimer2R3);
844N/A if (RT_FAILURE(rc))
844N/A return rc;
844N/A pThis->pSecondTimer2R0 = TMTimerR0Ptr(pThis->pSecondTimer2R3);
844N/A pThis->pSecondTimer2RC = TMTimerRCPtr(pThis->pSecondTimer2R3);
844N/A pThis->next_second_time = TMTimerGet(pThis->CTX_SUFF(pSecondTimer2))
844N/A + (TMTimerGetFreq(pThis->CTX_SUFF(pSecondTimer2)) * 99) / 100;
844N/A rc = TMTimerSet(pThis->CTX_SUFF(pSecondTimer2), pThis->next_second_time);
844N/A if (RT_FAILURE(rc))
844N/A return rc;
844N/A
844N/A
844N/A rc = PDMDevHlpIOPortRegister(pDevIns, pThis->IOPortBase, 4, NULL,
844N/A rtcIOPortWrite, rtcIOPortRead, NULL, NULL, "MC146818 RTC/CMOS");
844N/A if (RT_FAILURE(rc))
844N/A return rc;
844N/A if (fGCEnabled)
844N/A {
745N/A rc = PDMDevHlpIOPortRegisterRC(pDevIns, pThis->IOPortBase, 4, NIL_RTRCPTR,
745N/A "rtcIOPortWrite", "rtcIOPortRead", NULL, NULL, "MC146818 RTC/CMOS");
720N/A if (RT_FAILURE(rc))
745N/A return rc;
745N/A }
720N/A if (fR0Enabled)
720N/A {
745N/A rc = PDMDevHlpIOPortRegisterR0(pDevIns, pThis->IOPortBase, 4, NIL_RTR0PTR,
715N/A "rtcIOPortWrite", "rtcIOPortRead", NULL, NULL, "MC146818 RTC/CMOS");
844N/A if (RT_FAILURE(rc))
715N/A return rc;
844N/A }
844N/A
844N/A rc = PDMDevHlpSSMRegister3(pDevIns, RTC_SAVED_STATE_VERSION, sizeof(*pThis), rtcLiveExec, rtcSaveExec, rtcLoadExec);
844N/A if (RT_FAILURE(rc))
844N/A return rc;
844N/A
851N/A /*
851N/A * Register ourselves as the RTC/CMOS with PDM.
844N/A */
844N/A rc = PDMDevHlpRTCRegister(pDevIns, &pThis->RtcReg, &pThis->pRtcHlpR3);
844N/A if (RT_FAILURE(rc))
844N/A return rc;
844N/A
844N/A return VINF_SUCCESS;
844N/A}
844N/A
844N/A
844N/A/**
844N/A * The device registration structure.
844N/A */
844N/Aconst PDMDEVREG g_DeviceMC146818 =
844N/A{
1402N/A /* u32Version */
1402N/A PDM_DEVREG_VERSION,
844N/A /* szName */
1402N/A "mc146818",
1402N/A /* szRCMod */
1402N/A "VBoxDDGC.gc",
1402N/A /* szR0Mod */
1402N/A "VBoxDDR0.r0",
1402N/A /* pszDescription */
1402N/A "Motorola MC146818 RTC/CMOS Device.",
1402N/A /* fFlags */
1402N/A 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,
745N/A /* fClass */
745N/A PDM_DEVREG_CLASS_RTC,
715N/A /* cMaxInstances */
844N/A 1,
715N/A /* cbInstance */
719N/A sizeof(RTCState),
719N/A /* pfnConstruct */
719N/A rtcConstruct,
719N/A /* pfnDestruct */
719N/A NULL,
719N/A /* pfnRelocate */
719N/A rtcRelocate,
719N/A /* pfnIOCtl */
719N/A NULL,
719N/A /* pfnPowerOn */
719N/A NULL,
719N/A /* pfnReset */
1183N/A NULL,
719N/A /* pfnSuspend */
719N/A NULL,
719N/A /* pfnResume */
719N/A NULL,
761N/A /* pfnAttach */
1230N/A NULL,
803N/A /* pfnDetach */
761N/A NULL,
815N/A /* pfnQueryInterface */
761N/A NULL,
761N/A /* pfnInitComplete */
761N/A rtcInitComplete,
788N/A /* pfnPowerOff */
761N/A NULL,
788N/A /* pfnSoftReset */
788N/A NULL,
788N/A /* u32VersionEnd */
761N/A PDM_DEVREG_VERSION
761N/A};
761N/A
761N/A#endif /* IN_RING3 */
761N/A#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
761N/A
788N/A