DevRTC.cpp revision e51fe4b097229c66ff098ddbfa63c5054e0fa176
/* $Id$ */
/** @file
*/
/*
* Copyright (C) 2006-2007 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* 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
* 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 <iprt/asm-math.h>
#ifdef IN_RING3
#endif /* IN_RING3 */
#include "../Builtins.h"
struct RTCState;
#define RTC_CRC_START 0x10
#define RTC_CRC_LAST 0x2d
#define RTC_CRC_HIGH 0x2e
#define RTC_CRC_LOW 0x2f
/*******************************************************************************
* Internal Functions *
*******************************************************************************/
#ifndef VBOX_DEVICE_STRUCT_TESTCASE
PDMBOTHCBDECL(int) rtcIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb);
PDMBOTHCBDECL(int) rtcIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb);
#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
/*******************************************************************************
* Defined Constants And Macros *
*******************************************************************************/
/*#define DEBUG_CMOS*/
#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
/** 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
{
};
struct RTCState {
struct my_tm current_tm;
/** The configured IRQ. */
/** The configured I/O port base. */
/** Use UTC or local time initially. */
bool fUTC;
/** Disabled by HPET legacy mode. */
bool fDisabledByHpet;
/* periodic timer */
/* second update */
/** Pointer to the device instance - R3 Ptr. */
/** The periodic timer (rtcTimerPeriodic) - R3 Ptr. */
/** The second timer (rtcTimerSecond) - R3 Ptr. */
/** The second second timer (rtcTimerSecond2) - R3 Ptr. */
/** Pointer to the device instance - R0 Ptr. */
/** The periodic timer (rtcTimerPeriodic) - R0 Ptr. */
/** The second timer (rtcTimerSecond) - R0 Ptr. */
/** The second second timer (rtcTimerSecond2) - R0 Ptr. */
/** Pointer to the device instance - RC Ptr. */
/** The periodic timer (rtcTimerPeriodic) - RC Ptr. */
/** The second timer (rtcTimerSecond) - RC Ptr. */
/** The second second timer (rtcTimerSecond2) - RC Ptr. */
/** The RTC registration structure. */
/** The RTC device helpers. */
/** Number of release log entries. Used to prevent flooding. */
/** HPET legacy mode notification interface. */
};
#ifndef VBOX_DEVICE_STRUCT_TESTCASE
static void rtc_set_time(RTCState *s);
static void rtc_copy_date(RTCState *s);
{
int period_code, period;
if (period_code != 0 &&
if (period_code <= 2)
period_code += 7;
/* period in 32 kHz cycles */
/* compute 32 kHz clock */
#ifdef IN_RING3
#else
#endif
{
#ifdef IN_RING3
if (s->cRelLogEntries++ < 64)
s->CurLogPeriod = period;
#endif
s->CurHintPeriod = period;
}
} else {
LogRel(("RTC: stopped the periodic timer\n"));
}
}
{
if (!pThis->fDisabledByHpet)
}
static void rtc_periodic_timer(void *opaque)
{
rtc_timer_update(s, s->next_periodic_time);
rtc_raise_irq(s, 1);
}
{
if ((addr & 1) == 0) {
} else {
switch(s->cmos_index[bank]) {
case RTC_SECONDS_ALARM:
case RTC_MINUTES_ALARM:
case RTC_HOURS_ALARM:
break;
case RTC_SECONDS:
case RTC_MINUTES:
case RTC_HOURS:
case RTC_DAY_OF_WEEK:
case RTC_DAY_OF_MONTH:
case RTC_MONTH:
case RTC_YEAR:
/* if in set mode, do not update the time */
rtc_set_time(s);
}
break;
case RTC_REG_A:
/* UIP bit is read only */
break;
case RTC_REG_B:
/* set mode: reset UIP mode */
#endif
} else {
/* if disabling set mode, update the time */
rtc_set_time(s);
}
}
break;
case RTC_REG_C:
case RTC_REG_D:
/* cannot write to them */
break;
default:
break;
}
}
}
{
return a;
} else {
return ((a / 10) << 4) | (a % 10);
}
}
{
return a;
} else {
return ((a >> 4) * 10) + (a & 0x0f);
}
}
static void rtc_set_time(RTCState *s)
{
}
}
static void rtc_copy_date(RTCState *s)
{
/* 24 hour format */
} else {
/* 12 hour format */
}
}
/* month is between 0 and 11. */
{
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;
if (month == 1) {
d++;
}
return d;
}
/* update 'tm' to the next second */
{
int days_in_month;
/* next day */
}
}
}
}
}
}
static void rtc_update_second(void *opaque)
{
/* if the oscillator is not in normal operation, we do not update */
} else {
rtc_next_second(&s->current_tm);
/* update in progress bit */
}
/* 244140 ns = 8 / 32768 seconds */
}
}
static void rtc_update_second2(void *opaque)
{
rtc_copy_date(s);
}
/* check alarm */
rtc_raise_irq(s, 1);
}
}
/* update ended interrupt */
rtc_raise_irq(s, 1);
}
/* clear update in progress bit */
}
{
int ret;
unsigned bank;
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:
break;
case RTC_REG_A:
break;
case RTC_REG_C:
rtc_raise_irq(s, 0);
break;
default:
break;
}
return ret;
}
}
#ifdef IN_RING3
{
}
{
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)
{
if (cb == 1)
{
return VINF_SUCCESS;
}
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)
{
if (cb == 1)
return VINF_SUCCESS;
}
/**
* 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.
*/
{
}
/**
* 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.
*/
{
}
/**
* 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.
*/
{
}
#ifdef IN_RING3
/**
* @copydoc FNSSMDEVLIVEEXEC
*/
{
return VINF_SSM_DONT_CALL_AGAIN;
}
/**
* @copydoc FNSSMDEVSAVEEXEC
*/
{
/* The config. */
/* The state. */
}
/**
* @copydoc FNSSMDEVLOADEXEC
*/
static DECLCALLBACK(int) rtcLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
{
int rc;
if ( uVersion != RTC_SAVED_STATE_VERSION
/* The config. */
{
return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Config mismatch - u8Irq: saved=%#x config=%#x"), u8Irq, pThis->irq);
return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Config mismatch - IOPortBase: saved=%RTiop config=%RTiop"), IOPortBase, pThis->IOPortBase);
bool fUTC;
}
if (uPass != SSM_PASS_FINAL)
return VINF_SUCCESS;
/* The state. */
{
/* Second CMOS bank. */
}
if ( period_code != 0
if (period_code <= 2)
period_code += 7;
} 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.
*/
{
unsigned i;
}
/**
* Write to a CMOS register and update the checksum if necessary.
*
* @returns VBox status code.
* @param pDevIns Device instance of the RTC.
* @param iReg The CMOS register index; bit 8 determines bank.
* @param u8Value The CMOS register value.
*/
{
{
/* does it require checksum update? */
if ( iReg >= RTC_CRC_START
&& iReg <= RTC_CRC_LAST)
return VINF_SUCCESS;
}
return VERR_INVALID_PARAMETER;
}
/**
* Read a CMOS register.
*
* @returns VBox status code.
* @param pDevIns Device instance of the RTC.
* @param iReg The CMOS register index; bit 8 determines bank.
* @param pu8Value Where to store the CMOS register value.
*/
{
{
return VINF_SUCCESS;
}
return VERR_INVALID_PARAMETER;
}
/* -=-=-=-=-=- based on bits from pc.c -=-=-=-=-=- */
/** @copydoc FNPDMDEVINITCOMPLETE */
{
/** @todo this should be (re)done at power on if we didn't load a state... */
/*
*/
else
/*
* Recalculate the checksum just in case.
*/
return VINF_SUCCESS;
}
/* -=-=-=-=-=- real code -=-=-=-=-=- */
/**
* @interface_method_impl{PDMIBASE,pfnQueryInterface}
*/
{
return NULL;
}
/**
* @interface_method_impl{PDMIHPETLEGACYNOTIFY,pfnModeChanged}
*/
static DECLCALLBACK(void) rtcHpetLegacyNotify_ModeChanged(PPDMIHPETLEGACYNOTIFY pInterface, bool fActivated)
{
}
/**
* @copydoc
*/
{
}
/**
* @interface_method_impl{PDMDEVREG,pfnConstruct}
*/
{
int rc;
/*
* Validate configuration.
*/
if (!CFGMR3AreValuesValid(pCfg,
"Irq\0"
"Base\0"
"UseUTC\0"
"GCEnabled\0"
"R0Enabled\0"))
/*
* Init the data.
*/
if (RT_FAILURE(rc))
N_("Configuration error: Querying \"Irq\" as a uint8_t failed"));
if (RT_FAILURE(rc))
N_("Configuration error: Querying \"Base\" as a RTIOPORT failed"));
if (RT_FAILURE(rc))
N_("Configuration error: Querying \"UseUTC\" as a bool failed"));
bool fGCEnabled;
if (RT_FAILURE(rc))
N_("Configuration error: failed to read GCEnabled as boolean"));
bool fR0Enabled;
if (RT_FAILURE(rc))
N_("Configuration error: failed to read R0Enabled as boolean"));
Log(("RTC: Irq=%#x Base=%#x fGCEnabled=%RTbool fR0Enabled=%RTbool\n",
pThis->fDisabledByHpet = false;
/* IBase */
/* IHpetLegacyNotify */
/*
* Create timers, arm them, register I/O Ports and save state.
*/
TMTIMER_FLAGS_DEFAULT_CRIT_SECT, "MC146818 RTC/CMOS - Periodic",
if (RT_FAILURE(rc))
return rc;
TMTIMER_FLAGS_DEFAULT_CRIT_SECT, "MC146818 RTC/CMOS - Second",
&pThis->pSecondTimerR3);
if (RT_FAILURE(rc))
return rc;
TMTIMER_FLAGS_DEFAULT_CRIT_SECT, "MC146818 RTC/CMOS - Second2",
&pThis->pSecondTimer2R3);
if (RT_FAILURE(rc))
return rc;
if (RT_FAILURE(rc))
return rc;
if (RT_FAILURE(rc))
return rc;
if (fGCEnabled)
{
if (RT_FAILURE(rc))
return rc;
}
if (fR0Enabled)
{
if (RT_FAILURE(rc))
return rc;
}
rc = PDMDevHlpSSMRegister3(pDevIns, RTC_SAVED_STATE_VERSION, sizeof(*pThis), rtcLiveExec, rtcSaveExec, rtcLoadExec);
if (RT_FAILURE(rc))
return rc;
/*
*/
if (RT_FAILURE(rc))
return rc;
return VINF_SUCCESS;
}
/**
* The device registration structure.
*/
const PDMDEVREG g_DeviceMC146818 =
{
/* u32Version */
/* szName */
"mc146818",
/* szRCMod */
"VBoxDDGC.gc",
/* szR0Mod */
"VBoxDDR0.r0",
/* pszDescription */
/* 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 */
/* cMaxInstances */
1,
/* cbInstance */
sizeof(RTCState),
/* pfnConstruct */
/* pfnDestruct */
NULL,
/* pfnRelocate */
/* pfnIOCtl */
NULL,
/* pfnPowerOn */
NULL,
/* pfnReset */
NULL,
/* pfnSuspend */
NULL,
/* pfnResume */
NULL,
/* pfnAttach */
NULL,
/* pfnDetach */
NULL,
/* pfnQueryInterface */
NULL,
/* pfnInitComplete */
/* pfnPowerOff */
NULL,
/* pfnSoftReset */
NULL,
/* u32VersionEnd */
};
#endif /* IN_RING3 */
#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */