DevRTC.cpp revision a644d8184ef2f759eee8e55e4073317252e57e48
/** @file
*
* VBox basic PC devices:
*/
/*
* Copyright (C) 2006 InnoTek Systemberatung GmbH
*
* 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 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.
*
* If you received this file as part of a commercial VirtualBox
* distribution, then only the terms of your commercial VirtualBox
* license agreement apply instead of the previous paragraph.
*
* --------------------------------------------------------------------
*
* 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 "vl_vbox.h"
#include <time.h>
struct RTCState;
#define RTC_CRC_START 0x10
#define RTC_CRC_LAST 0x2d
#define RTC_CRC_HIGH 0x2e
#define RTC_CRC_LOW 0x2f
#ifndef VBOX_DEVICE_STRUCT_TESTCASE
/*******************************************************************************
* Internal Functions *
*******************************************************************************/
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 */
/*#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
struct RTCState {
struct tm current_tm;
# if !defined(__WIN__)
# endif
#endif
/* periodic timer */
/* second update */
/** Pointer to the device instance - HC Ptr. */
/** Pointer to the device instance - GC Ptr. */
/** Use UCT or local time initially. */
bool fUCT;
/** The RTC registration structure. */
/** The RTC device helpers. */
};
#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 */
} else {
}
}
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 */
} 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 */
}
/* should be 244 us = 8 / 32768 seconds, but currently the
timers do not have the necessary resolution. */
if (delay < 1)
delay = 1;
}
}
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);
}
{
qemu_put_8s(f, &s->cmos_index);
qemu_put_be64s(f, &s->next_periodic_time);
qemu_put_be64s(f, &s->next_second_time);
}
{
if (version_id != 1)
qemu_get_8s(f, &s->cmos_index);
return 0;
}
#endif /* IN_RING3 */
/* -=-=-=-=-=- wrappers -=-=-=-=-=- */
/**
* 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.
*/
{
}
/**
* Device timer callback function, second.
*
* @param pDevIns Device instance of the device which registered the timer.
* @param pTimer The timer handle.
*/
{
}
/**
* Device timer callback function, second2.
*
* @param pDevIns Device instance of the device which registered the timer.
* @param pTimer The timer handle.
*/
{
}
#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)
{
}
/* -=-=-=-=-=- PDM Interface provided by the RTC device -=-=-=-=-=- */
/**
* Calculate and update the standard CMOS checksum.
*
* @param pData 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 */
{
int iYear;
/*
*/
#if 0 /* later */
else
#else
#ifndef __WIN__
else
#else
/* Win32 doesn't have thread safe stuff, let's just hope this works out fine :/ */
else
#endif
#endif
/*
* 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 (rc == VERR_CFGM_VALUE_NOT_FOUND)
u8Irq = 8;
else if (VBOX_FAILURE(rc))
N_("Configuration error: Querying \"Irq\" as a uint8_t failed"));
if (rc == VERR_CFGM_VALUE_NOT_FOUND)
u16Base = 0x70;
else if (VBOX_FAILURE(rc))
N_("Configuration error: Querying \"Base\" as a uint16_t failed"));
if (rc == VERR_CFGM_VALUE_NOT_FOUND)
else if (VBOX_FAILURE(rc))
N_("Configuration error: failed to read GCEnabled as boolean"));
if (rc == VERR_CFGM_VALUE_NOT_FOUND)
fR0Enabled = true;
else if (VBOX_FAILURE(rc))
N_("Configuration error: failed to read R0Enabled as boolean"));
/*
* Create timers, arm them, register I/O Ports and save state.
*/
rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL_SYNC, rtcTimerPeriodic, "MC146818 RTC/CMOS - Periodic", &pData->pPeriodicTimerHC);
#else
rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL, rtcTimerPeriodic, "MC146818 RTC/CMOS - Periodic", &pData->pPeriodicTimerHC);
#endif
if (VBOX_FAILURE(rc))
{
return rc;
}
rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL_SYNC, rtcTimerSecond, "MC146818 RTC/CMOS - Second", &pData->pSecondTimerHC);
#else
rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL, rtcTimerSecond, "MC146818 RTC/CMOS - Second", &pData->pSecondTimerHC);
#endif
if (VBOX_FAILURE(rc))
{
return rc;
}
rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL_SYNC, rtcTimerSecond2, "MC146818 RTC/CMOS - Second2", &pData->pSecondTimer2HC);
#else
rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL, rtcTimerSecond2, "MC146818 RTC/CMOS - Second2", &pData->pSecondTimer2HC);
#endif
if (VBOX_FAILURE(rc))
{
return rc;
}
pData->next_second_time = TMTimerGet(pData->CTXSUFF(pSecondTimer2)) + (TMTimerGetFreq(pData->CTXSUFF(pSecondTimer2)) * 99) / 100;
rc = PDMDevHlpIOPortRegister(pDevIns, u16Base, 2, NULL, rtcIOPortWrite, rtcIOPortRead, NULL, NULL, "MC146818 RTC/CMOS");
if (VBOX_FAILURE(rc))
return rc;
if (fGCEnabled)
{
rc = PDMDevHlpIOPortRegisterGC(pDevIns, u16Base, 2, 0, "rtcIOPortWrite", "rtcIOPortRead", NULL, NULL, "MC146818 RTC/CMOS");
if (VBOX_FAILURE(rc))
return rc;
}
if (fR0Enabled)
{
rc = PDMDevHlpIOPortRegisterR0(pDevIns, u16Base, 2, 0, "rtcIOPortWrite", "rtcIOPortRead", NULL, NULL, "MC146818 RTC/CMOS");
if (VBOX_FAILURE(rc))
return rc;
}
rc = PDMDevHlpSSMRegister(pDevIns, pDevIns->pDevReg->szDeviceName, iInstance, 1 /* version */, sizeof(*pData),
if (VBOX_FAILURE(rc))
return rc;
/*
* Register ourselves as the RTC with PDM.
*/
if (VBOX_FAILURE(rc))
return rc;
return VINF_SUCCESS;
}
/**
* The device registration structure.
*/
const PDMDEVREG g_DeviceMC146818 =
{
/* u32Version */
/* szDeviceName */
"mc146818",
/* szGCMod */
"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_GC | 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 */
};
#endif /* IN_RING3 */
#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */