DevRTC.cpp revision 6a0fec50190b900a6fc83656f5d5b55da8c5597e
/* $Id$ */
/** @file
*/
/*
* Copyright (C) 2006-2012 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 "VBoxDD.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
#define CMOS_BANK_LOWER_LIMIT 0x0E
#define CMOS_BANK_UPPER_LIMIT 0x7F
#define CMOS_BANK2_LOWER_LIMIT 0x80
#define CMOS_BANK2_UPPER_LIMIT 0xFF
/** 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
{
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
#endif
}
}
else
{
LogRel(("RTC: stopped the periodic timer\n"));
}
}
{
if (!pThis->fDisabledByHpet)
}
{
return a;
return ((a / 10) << 4) | (a % 10);
}
{
return a;
return ((a >> 4) * 10) + (a & 0x0f);
}
{
}
/* -=-=-=-=-=- I/O Port Handlers -=-=-=-=-=- */
/**
* Port I/O Handler for IN operations.
*
* @returns VBox status code.
*
* @param pDevIns The device instance.
* @param pvUser User argument - ignored.
* @param uPort Port number used for the IN operation.
* @param pu32 Where to store the result.
* @param cb Number of bytes read.
*/
PDMBOTHCBDECL(int) rtcIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
{
if (cb != 1)
return VERR_IOM_IOPORT_UNUSED;
if ((Port & 1) == 0)
*pu32 = 0xff;
else
{
{
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(pThis, 0);
break;
default:
break;
}
}
return VINF_SUCCESS;
}
/**
* 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;
if ((Port & 1) == 0)
{
}
else
{
switch (idx)
{
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 */
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.*/
int rc2 = PDMCritSectEnter(pThis->CTX_SUFF(pDevIns)->CTX_SUFF(pCritSectRo), VINF_SUCCESS /* must get it */);
{
/* UIP bit is read only */
}
else
{
{
/* set mode: reset UIP mode */
#endif
}
else
{
/* if disabling set mode, update the time */
}
}
/* the caller leaves the other lock. */
break;
}
case RTC_REG_C:
case RTC_REG_D:
/* cannot write to them */
break;
default:
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)
{
"First CMOS bank, offsets 0x0E - 0x7F\n"
"Offset %02x : --- use 'info rtc' to show CMOS clock ---", 0);
{
if ((iCmos & 15) == 0)
else
}
}
/**
* @callback_method_impl{FNDBGFHANDLERDEV,
* Dumps the cmos Bank2 Info.}
*/
static DECLCALLBACK(void) rtcCmosBank2Info(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
{
{
if ((iCmos & 15) == 0)
else
}
}
/**
* @callback_method_impl{FNDBGFHANDLERDEV,
* Dumps the cmos RTC Info.}
*/
static DECLCALLBACK(void) rtcCmosClockInfo(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
{
u8Hr += 12;
}
/* -=-=-=-=-=- Timers and their support code -=-=-=-=-=- */
/**
* Device timer callback function, periodic.
*
* @param pDevIns Device instance of the device which registered the timer.
* @param pTimer The timer handle.
* @param pvUser Pointer to the RTC state.
*/
{
}
/* 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 */
{
{
}
}
}
}
}
}
/**
* 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.
*/
{
/* if the oscillator is not in normal operation, we do not update */
{
}
else
{
{
/* update in progress bit */
}
/* 244140 ns = 8 / 32768 seconds */
}
}
/* Used by rtc_set_date and rtcTimerSecond2. */
{
{
/* 24 hour format */
}
else
{
/* 12 hour format */
}
}
/**
* 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.
*/
{
/* check alarm */
{
)
{
}
}
/* update ended interrupt */
{
}
/* clear update in progress bit */
}
/* -=-=-=-=-=- Saved State -=-=-=-=-=- */
/**
* @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;
}
/**
* @interface_method_impl{PDMIHPETLEGACYNOTIFY,pfnModeChanged}
*/
static DECLCALLBACK(void) rtcHpetLegacyNotify_ModeChanged(PPDMIHPETLEGACYNOTIFY pInterface, bool fActivated)
{
}
/* -=-=-=-=-=- 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;
}
/**
* @copydoc
*/
{
}
/**
* @copydoc
*/
{
/* If shutdown status is non-zero, log its value. */
{
#if 0 /* It would be nice to log the warm reboot vector but alas, we already trashed it. */
int rc;
#endif
/* If we're going to trash the VM's memory, we also have to clear this. */
}
}
/**
* @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.
*/
/* Periodic timer. */
TMTIMER_FLAGS_DEFAULT_CRIT_SECT, "MC146818 RTC/CMOS - Periodic",
&pTimer);
if (RT_FAILURE(rc))
return rc;
/* Seconds timer. */
TMTIMER_FLAGS_DEFAULT_CRIT_SECT, "MC146818 RTC/CMOS - Second",
&pTimer);
if (RT_FAILURE(rc))
return rc;
/* The second2 timer, this is always active. */
TMTIMER_FLAGS_DEFAULT_CRIT_SECT, "MC146818 RTC/CMOS - Second2",
&pTimer);
if (RT_FAILURE(rc))
return rc;
/*
* Register I/O ports.
*/
if (RT_FAILURE(rc))
return rc;
if (fGCEnabled)
{
if (RT_FAILURE(rc))
return rc;
}
if (fR0Enabled)
{
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;
/*
*/
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);
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 */
/* pfnSuspend */
NULL,
/* pfnResume */
NULL,
/* pfnAttach */
NULL,
/* pfnDetach */
NULL,
/* pfnQueryInterface */
NULL,
/* pfnInitComplete */
/* pfnPowerOff */
NULL,
/* pfnSoftReset */
NULL,
/* u32VersionEnd */
};
#endif /* IN_RING3 */
#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */