DevRTC.cpp revision 25747178cb66800d8386c20b8ffd87f78f24f4e5
/* $Id$ */
/** @file
*/
/*
* Copyright (C) 2006-2007 Sun Microsystems, Inc.
*
* 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.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
* Clara, CA 95054 USA or visit http://www.sun.com if you need
* additional information or have any questions.
* --------------------------------------------------------------------
*
* 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 "../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
/*******************************************************************************
* Structures and Typedefs *
*******************************************************************************/
/** @todo Replace struct my_tm with RTTIME. */
struct my_tm
{
};
struct RTCState {
struct my_tm current_tm;
/** Use UTC or local time initially. */
bool fUTC;
/* 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. */
};
#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 */
{
if (s->cRelLogEntries++ < 64)
}
} else {
LogRel(("RTC: stopped the periodic timer\n"));
}
}
static void rtc_periodic_timer(void *opaque)
{
rtc_timer_update(s, s->next_periodic_time);
}
{
if ((addr & 1) == 0) {
} else {
Log(("CMOS: Write idx %#04x: %#04x (old %#04x)\n", s->cmos_index, data, s->cmos_data[s->cmos_index]));
switch(s->cmos_index) {
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 */
}
}
/* update ended interrupt */
}
/* clear update in progress bit */
}
{
int ret;
if ((addr & 1) == 0) {
return 0xff;
} else {
switch(s->cmos_index) {
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:
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
/**
* Saves a state of the programmable interval timer device.
*
* @returns VBox status code.
* @param pDevIns The device instance.
* @param pSSMHandle The handle to save the state to.
*/
{
return VINF_SUCCESS;
}
/**
* Loads a saved programmable interval timer device state.
*
* @returns VBox status code.
* @param pDevIns The device instance.
* @param pSSMHandle The handle to the saved state.
* @param u32Version The data unit version number.
*/
static DECLCALLBACK(int) rtcLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSMHandle, uint32_t u32Version)
{
if (u32Version != 1)
if ( period_code != 0
if (period_code <= 2)
period_code += 7;
} else {
LogRel(("RTC: stopped the periodic timer (restore)\n"));
}
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.
* @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.
* @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 -=-=-=-=-=- */
/**
* @copydoc
*/
{
}
/**
* Construct a device instance for a VM.
*
* @returns VBox status.
* @param pDevIns The device instance data.
* If the registration structure is needed, pDevIns->pDevReg points to it.
* @param iInstance Instance number. Use this to figure out which registers and such to use.
* The device number is also found in pDevIns->iInstance, but since it's
* likely to be freqently used PDM passes it as parameter.
* @param pCfgHandle Configuration node handle for the device. Use this to obtain the configuration
* of the device instance. It's also found in pDevIns->pCfgHandle, but like
* iInstance it's expected to be used a bit in this function.
*/
{
int rc;
bool fGCEnabled;
bool fR0Enabled;
/*
* Validate configuration.
*/
/*
* 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 uint16_t failed"));
if (RT_FAILURE(rc))
N_("Configuration error: failed to read GCEnabled as boolean"));
if (RT_FAILURE(rc))
N_("Configuration error: failed to read R0Enabled as boolean"));
Log(("RTC: Irq=%#x Base=%#x fGCEnabled=%RTbool fR0Enabled=%RTbool\n", u8Irq, u16Base, fGCEnabled, fR0Enabled));
/*
* 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;
pThis->next_second_time = TMTimerGet(pThis->CTX_SUFF(pSecondTimer2)) + (TMTimerGetFreq(pThis->CTX_SUFF(pSecondTimer2)) * 99) / 100;
if (RT_FAILURE(rc))
return rc;
rc = PDMDevHlpIOPortRegister(pDevIns, u16Base, 2, NULL, rtcIOPortWrite, rtcIOPortRead, NULL, NULL, "MC146818 RTC/CMOS");
if (RT_FAILURE(rc))
return rc;
if (fGCEnabled)
{
rc = PDMDevHlpIOPortRegisterGC(pDevIns, u16Base, 2, 0, "rtcIOPortWrite", "rtcIOPortRead", NULL, NULL, "MC146818 RTC/CMOS");
if (RT_FAILURE(rc))
return rc;
}
if (fR0Enabled)
{
rc = PDMDevHlpIOPortRegisterR0(pDevIns, u16Base, 2, 0, "rtcIOPortWrite", "rtcIOPortRead", NULL, NULL, "MC146818 RTC/CMOS");
if (RT_FAILURE(rc))
return rc;
}
rc = PDMDevHlpSSMRegister(pDevIns, pDevIns->pDevReg->szDeviceName, iInstance, 1 /* version */, sizeof(*pThis),
if (RT_FAILURE(rc))
return rc;
/*
*/
if (RT_FAILURE(rc))
return rc;
return VINF_SUCCESS;
}
/**
* The device registration structure.
*/
const PDMDEVREG g_DeviceMC146818 =
{
/* u32Version */
/* szDeviceName */
"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 */