DevRTC.cpp revision 7657321bb3311dcc786e86421c6f8bbea52cb23b
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync/* $Id$ */
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync/** @file
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync * Motorola MC146818 RTC/CMOS Device.
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync */
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync/*
c98fb3e16fcd571a790eab772c0c66173d225205vboxsync * Copyright (C) 2006-2007 Sun Microsystems, Inc.
c98fb3e16fcd571a790eab772c0c66173d225205vboxsync *
c98fb3e16fcd571a790eab772c0c66173d225205vboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
c98fb3e16fcd571a790eab772c0c66173d225205vboxsync * available from http://www.virtualbox.org. This file is free software;
c98fb3e16fcd571a790eab772c0c66173d225205vboxsync * you can redistribute it and/or modify it under the terms of the GNU
c98fb3e16fcd571a790eab772c0c66173d225205vboxsync * General Public License (GPL) as published by the Free Software
c98fb3e16fcd571a790eab772c0c66173d225205vboxsync * Foundation, in version 2 as it comes in the "COPYING" file of the
c98fb3e16fcd571a790eab772c0c66173d225205vboxsync * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
c98fb3e16fcd571a790eab772c0c66173d225205vboxsync * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync *
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync * Clara, CA 95054 USA or visit http://www.sun.com if you need
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync * additional information or have any questions.
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync * --------------------------------------------------------------------
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync *
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync * This code is based on:
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync *
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync * QEMU MC146818 RTC emulation
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync *
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync * Copyright (c) 2003-2004 Fabrice Bellard
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync *
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync * Permission is hereby granted, free of charge, to any person obtaining a copy
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync * of this software and associated documentation files (the "Software"), to deal
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync * in the Software without restriction, including without limitation the rights
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync * copies of the Software, and to permit persons to whom the Software is
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync * furnished to do so, subject to the following conditions:
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync *
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync * The above copyright notice and this permission notice shall be included in
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync * all copies or substantial portions of the Software.
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync *
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync * THE SOFTWARE.
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync */
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync/*******************************************************************************
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync* Header Files *
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync*******************************************************************************/
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync#define LOG_GROUP LOG_GROUP_DEV_RTC
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync#include <VBox/pdmdev.h>
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync#include <VBox/log.h>
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync#include <iprt/asm.h>
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync#include <iprt/assert.h>
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync#include <iprt/string.h>
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync
cacc4e75dbbff469c10a505168208f064c6c385cvboxsyncstruct RTCState;
cacc4e75dbbff469c10a505168208f064c6c385cvboxsynctypedef struct RTCState RTCState;
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync#define RTC_CRC_START 0x10
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync#define RTC_CRC_LAST 0x2d
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync#define RTC_CRC_HIGH 0x2e
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync#define RTC_CRC_LOW 0x2f
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync/*******************************************************************************
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync* Internal Functions *
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync*******************************************************************************/
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync#ifndef VBOX_DEVICE_STRUCT_TESTCASE
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync__BEGIN_DECLS
cacc4e75dbbff469c10a505168208f064c6c385cvboxsyncPDMBOTHCBDECL(int) rtcIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb);
cacc4e75dbbff469c10a505168208f064c6c385cvboxsyncPDMBOTHCBDECL(int) rtcIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb);
cacc4e75dbbff469c10a505168208f064c6c385cvboxsyncPDMBOTHCBDECL(void) rtcTimerPeriodic(PPDMDEVINS pDevIns, PTMTIMER pTimer);
cacc4e75dbbff469c10a505168208f064c6c385cvboxsyncPDMBOTHCBDECL(void) rtcTimerSecond(PPDMDEVINS pDevIns, PTMTIMER pTimer);
cacc4e75dbbff469c10a505168208f064c6c385cvboxsyncPDMBOTHCBDECL(void) rtcTimerSecond2(PPDMDEVINS pDevIns, PTMTIMER pTimer);
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync__END_DECLS
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync/*******************************************************************************
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync* Defined Constants And Macros *
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync*******************************************************************************/
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync/*#define DEBUG_CMOS*/
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync#define RTC_SECONDS 0
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync#define RTC_SECONDS_ALARM 1
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync#define RTC_MINUTES 2
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync#define RTC_MINUTES_ALARM 3
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync#define RTC_HOURS 4
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync#define RTC_HOURS_ALARM 5
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync#define RTC_ALARM_DONT_CARE 0xC0
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync#define RTC_DAY_OF_WEEK 6
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync#define RTC_DAY_OF_MONTH 7
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync#define RTC_MONTH 8
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync#define RTC_YEAR 9
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync#define RTC_REG_A 10
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync#define RTC_REG_B 11
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync#define RTC_REG_C 12
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync#define RTC_REG_D 13
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync#define REG_A_UIP 0x80
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync#define REG_B_SET 0x80
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync#define REG_B_PIE 0x40
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync#define REG_B_AIE 0x20
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync#define REG_B_UIE 0x10
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync/*******************************************************************************
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync* Structures and Typedefs *
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync*******************************************************************************/
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync/** @todo Replace struct my_tm with RTTIME. */
cacc4e75dbbff469c10a505168208f064c6c385cvboxsyncstruct my_tm
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync{
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync int32_t tm_sec;
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync int32_t tm_min;
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync int32_t tm_hour;
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync int32_t tm_mday;
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync int32_t tm_mon;
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync int32_t tm_year;
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync int32_t tm_wday;
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync int32_t tm_yday;
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync};
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync
cacc4e75dbbff469c10a505168208f064c6c385cvboxsyncstruct RTCState {
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync uint8_t cmos_data[128];
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync uint8_t cmos_index;
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync uint8_t Alignment0[7];
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync struct my_tm current_tm;
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync int32_t irq;
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync /* periodic timer */
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync int64_t next_periodic_time;
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync /* second update */
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync int64_t next_second_time;
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync /** Pointer to the device instance - R3 Ptr. */
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync PPDMDEVINSR3 pDevInsR3;
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync /** The periodic timer (rtcTimerPeriodic) - R3 Ptr. */
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync PTMTIMERR3 pPeriodicTimerR3;
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync /** The second timer (rtcTimerSecond) - R3 Ptr. */
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync PTMTIMERR3 pSecondTimerR3;
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync /** The second second timer (rtcTimerSecond2) - R3 Ptr. */
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync PTMTIMERR3 pSecondTimer2R3;
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync /** Pointer to the device instance - R0 Ptr. */
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync PPDMDEVINSR0 pDevInsR0;
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync /** The periodic timer (rtcTimerPeriodic) - R0 Ptr. */
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync PTMTIMERR0 pSecondTimerR0;
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync /** The second timer (rtcTimerSecond) - R0 Ptr. */
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync PTMTIMERR0 pPeriodicTimerR0;
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync /** The second second timer (rtcTimerSecond2) - R0 Ptr. */
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync PTMTIMERR0 pSecondTimer2R0;
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync /** Pointer to the device instance - RC Ptr. */
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync PPDMDEVINSRC pDevInsRC;
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync /** The periodic timer (rtcTimerPeriodic) - RC Ptr. */
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync PTMTIMERRC pPeriodicTimerRC;
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync /** The second timer (rtcTimerSecond) - RC Ptr. */
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync PTMTIMERRC pSecondTimerRC;
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync /** The second second timer (rtcTimerSecond2) - RC Ptr. */
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync PTMTIMERRC pSecondTimer2RC;
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync /** The RTC registration structure. */
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync PDMRTCREG RtcReg;
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync /** The RTC device helpers. */
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync R3PTRTYPE(PCPDMRTCHLP) pRtcHlpR3;
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync /** Use UTC or local time initially. */
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync bool fUTC;
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync /** Number of release log entries. Used to prevent flooding. */
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync uint32_t cRelLogEntries;
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync /** The current/previous timer period. Used to prevent flooding changes. */
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync int32_t CurPeriod;
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync};
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync#ifndef VBOX_DEVICE_STRUCT_TESTCASE
cacc4e75dbbff469c10a505168208f064c6c385cvboxsyncstatic void rtc_set_time(RTCState *s);
cacc4e75dbbff469c10a505168208f064c6c385cvboxsyncstatic void rtc_copy_date(RTCState *s);
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync
cacc4e75dbbff469c10a505168208f064c6c385cvboxsyncstatic void rtc_timer_update(RTCState *s, int64_t current_time)
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync{
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync int period_code, period;
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync uint64_t cur_clock, next_irq_clock;
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync uint32_t freq;
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync period_code = s->cmos_data[RTC_REG_A] & 0x0f;
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync if (period_code != 0 &&
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync (s->cmos_data[RTC_REG_B] & REG_B_PIE)) {
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync if (period_code <= 2)
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync period_code += 7;
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync /* period in 32 kHz cycles */
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync period = 1 << (period_code - 1);
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync /* compute 32 kHz clock */
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync freq = TMTimerGetFreq(s->CTX_SUFF(pPeriodicTimer));
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync cur_clock = ASMMultU64ByU32DivByU32(current_time, 32768, freq);
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync next_irq_clock = (cur_clock & ~(uint64_t)(period - 1)) + period;
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync s->next_periodic_time = ASMMultU64ByU32DivByU32(next_irq_clock, freq, 32768) + 1;
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync TMTimerSet(s->CTX_SUFF(pPeriodicTimer), s->next_periodic_time);
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync if (period != s->CurPeriod)
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync {
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync if (s->cRelLogEntries++ < 64)
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync LogRel(("RTC: period=%#x (%d) %u Hz\n", period, period, _32K / period));
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync s->CurPeriod = period;
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync }
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync } else {
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync if (TMTimerIsActive(s->CTX_SUFF(pPeriodicTimer)) && s->cRelLogEntries++ < 64)
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync LogRel(("RTC: stopped the periodic timer\n"));
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync TMTimerStop(s->CTX_SUFF(pPeriodicTimer));
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync }
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync}
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync
cacc4e75dbbff469c10a505168208f064c6c385cvboxsyncstatic void rtc_periodic_timer(void *opaque)
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync{
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync RTCState *s = (RTCState*)opaque;
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync rtc_timer_update(s, s->next_periodic_time);
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync s->cmos_data[RTC_REG_C] |= 0xc0;
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync PDMDevHlpISASetIrq(s->CTX_SUFF(pDevIns), s->irq, 1);
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync}
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync
cacc4e75dbbff469c10a505168208f064c6c385cvboxsyncstatic void cmos_ioport_write(void *opaque, uint32_t addr, uint32_t data)
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync{
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync RTCState *s = (RTCState*)opaque;
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync if ((addr & 1) == 0) {
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync s->cmos_index = data & 0x7f;
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync } else {
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync Log(("CMOS: Write idx %#04x: %#04x (old %#04x)\n", s->cmos_index, data, s->cmos_data[s->cmos_index]));
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync switch(s->cmos_index) {
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync case RTC_SECONDS_ALARM:
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync case RTC_MINUTES_ALARM:
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync case RTC_HOURS_ALARM:
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync s->cmos_data[s->cmos_index] = data;
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync break;
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync case RTC_SECONDS:
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync case RTC_MINUTES:
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync case RTC_HOURS:
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync case RTC_DAY_OF_WEEK:
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync case RTC_DAY_OF_MONTH:
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync case RTC_MONTH:
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync case RTC_YEAR:
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync s->cmos_data[s->cmos_index] = data;
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync /* if in set mode, do not update the time */
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync if (!(s->cmos_data[RTC_REG_B] & REG_B_SET)) {
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync rtc_set_time(s);
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync }
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync break;
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync case RTC_REG_A:
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync /* UIP bit is read only */
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync s->cmos_data[RTC_REG_A] = (data & ~REG_A_UIP) |
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync (s->cmos_data[RTC_REG_A] & REG_A_UIP);
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync rtc_timer_update(s, TMTimerGet(s->CTX_SUFF(pPeriodicTimer)));
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync break;
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync case RTC_REG_B:
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync if (data & REG_B_SET) {
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync /* set mode: reset UIP mode */
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync s->cmos_data[RTC_REG_A] &= ~REG_A_UIP;
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync#if 0 /* This is probably wrong as it breaks changing the time/date in OS/2. */
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync data &= ~REG_B_UIE;
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync#endif
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync } else {
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync /* if disabling set mode, update the time */
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync if (s->cmos_data[RTC_REG_B] & REG_B_SET) {
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync rtc_set_time(s);
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync }
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync }
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync s->cmos_data[RTC_REG_B] = data;
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync rtc_timer_update(s, TMTimerGet(s->CTX_SUFF(pPeriodicTimer)));
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync break;
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync case RTC_REG_C:
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync case RTC_REG_D:
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync /* cannot write to them */
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync break;
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync default:
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync s->cmos_data[s->cmos_index] = data;
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync break;
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync }
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync }
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync}
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync
cacc4e75dbbff469c10a505168208f064c6c385cvboxsyncstatic inline int to_bcd(RTCState *s, int a)
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync{
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync if (s->cmos_data[RTC_REG_B] & 0x04) {
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync return a;
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync } else {
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync return ((a / 10) << 4) | (a % 10);
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync }
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync}
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync
cacc4e75dbbff469c10a505168208f064c6c385cvboxsyncstatic inline int from_bcd(RTCState *s, int a)
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync{
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync if (s->cmos_data[RTC_REG_B] & 0x04) {
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync return a;
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync } else {
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync return ((a >> 4) * 10) + (a & 0x0f);
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync }
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync}
cacc4e75dbbff469c10a505168208f064c6c385cvboxsync
static void rtc_set_time(RTCState *s)
{
struct my_tm *tm = &s->current_tm;
tm->tm_sec = from_bcd(s, s->cmos_data[RTC_SECONDS]);
tm->tm_min = from_bcd(s, s->cmos_data[RTC_MINUTES]);
tm->tm_hour = from_bcd(s, s->cmos_data[RTC_HOURS] & 0x7f);
if (!(s->cmos_data[RTC_REG_B] & 0x02) &&
(s->cmos_data[RTC_HOURS] & 0x80)) {
tm->tm_hour += 12;
}
tm->tm_wday = from_bcd(s, s->cmos_data[RTC_DAY_OF_WEEK]);
tm->tm_mday = from_bcd(s, s->cmos_data[RTC_DAY_OF_MONTH]);
tm->tm_mon = from_bcd(s, s->cmos_data[RTC_MONTH]) - 1;
tm->tm_year = from_bcd(s, s->cmos_data[RTC_YEAR]) + 100;
}
static void rtc_copy_date(RTCState *s)
{
const struct my_tm *tm = &s->current_tm;
s->cmos_data[RTC_SECONDS] = to_bcd(s, tm->tm_sec);
s->cmos_data[RTC_MINUTES] = to_bcd(s, tm->tm_min);
if (s->cmos_data[RTC_REG_B] & 0x02) {
/* 24 hour format */
s->cmos_data[RTC_HOURS] = to_bcd(s, tm->tm_hour);
} else {
/* 12 hour format */
s->cmos_data[RTC_HOURS] = to_bcd(s, tm->tm_hour % 12);
if (tm->tm_hour >= 12)
s->cmos_data[RTC_HOURS] |= 0x80;
}
s->cmos_data[RTC_DAY_OF_WEEK] = to_bcd(s, tm->tm_wday);
s->cmos_data[RTC_DAY_OF_MONTH] = to_bcd(s, tm->tm_mday);
s->cmos_data[RTC_MONTH] = to_bcd(s, tm->tm_mon + 1);
s->cmos_data[RTC_YEAR] = to_bcd(s, tm->tm_year % 100);
}
/* month is between 0 and 11. */
static int get_days_in_month(int month, int year)
{
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;
d = days_tab[month];
if (month == 1) {
if ((year % 4) == 0 && ((year % 100) != 0 || (year % 400) == 0))
d++;
}
return d;
}
/* update 'tm' to the next second */
static void rtc_next_second(struct my_tm *tm)
{
int days_in_month;
tm->tm_sec++;
if ((unsigned)tm->tm_sec >= 60) {
tm->tm_sec = 0;
tm->tm_min++;
if ((unsigned)tm->tm_min >= 60) {
tm->tm_min = 0;
tm->tm_hour++;
if ((unsigned)tm->tm_hour >= 24) {
tm->tm_hour = 0;
/* next day */
tm->tm_wday++;
if ((unsigned)tm->tm_wday >= 7)
tm->tm_wday = 0;
days_in_month = get_days_in_month(tm->tm_mon,
tm->tm_year + 1900);
tm->tm_mday++;
if (tm->tm_mday < 1) {
tm->tm_mday = 1;
} else if (tm->tm_mday > days_in_month) {
tm->tm_mday = 1;
tm->tm_mon++;
if (tm->tm_mon >= 12) {
tm->tm_mon = 0;
tm->tm_year++;
}
}
}
}
}
}
static void rtc_update_second(void *opaque)
{
RTCState *s = (RTCState*)opaque;
/* if the oscillator is not in normal operation, we do not update */
if ((s->cmos_data[RTC_REG_A] & 0x70) != 0x20) {
s->next_second_time += TMTimerGetFreq(s->CTX_SUFF(pSecondTimer));
TMTimerSet(s->CTX_SUFF(pSecondTimer), s->next_second_time);
} else {
rtc_next_second(&s->current_tm);
if (!(s->cmos_data[RTC_REG_B] & REG_B_SET)) {
/* update in progress bit */
s->cmos_data[RTC_REG_A] |= REG_A_UIP;
}
/* 244140 ns = 8 / 32768 seconds */
uint64_t delay = TMTimerFromNano(s->CTX_SUFF(pSecondTimer2), 244140);
TMTimerSet(s->CTX_SUFF(pSecondTimer2), s->next_second_time + delay);
}
}
static void rtc_update_second2(void *opaque)
{
RTCState *s = (RTCState*)opaque;
if (!(s->cmos_data[RTC_REG_B] & REG_B_SET)) {
rtc_copy_date(s);
}
/* check alarm */
if (s->cmos_data[RTC_REG_B] & REG_B_AIE) {
if (((s->cmos_data[RTC_SECONDS_ALARM] & 0xc0) == 0xc0 ||
from_bcd(s, s->cmos_data[RTC_SECONDS_ALARM]) == s->current_tm.tm_sec) &&
((s->cmos_data[RTC_MINUTES_ALARM] & 0xc0) == 0xc0 ||
from_bcd(s, s->cmos_data[RTC_MINUTES_ALARM]) == s->current_tm.tm_min) &&
((s->cmos_data[RTC_HOURS_ALARM] & 0xc0) == 0xc0 ||
from_bcd(s, s->cmos_data[RTC_HOURS_ALARM]) == s->current_tm.tm_hour)) {
s->cmos_data[RTC_REG_C] |= 0xa0;
PDMDevHlpISASetIrq(s->CTX_SUFF(pDevIns), s->irq, 1);
}
}
/* update ended interrupt */
if (s->cmos_data[RTC_REG_B] & REG_B_UIE) {
s->cmos_data[RTC_REG_C] |= 0x90;
PDMDevHlpISASetIrq(s->CTX_SUFF(pDevIns), s->irq, 1);
}
/* clear update in progress bit */
s->cmos_data[RTC_REG_A] &= ~REG_A_UIP;
s->next_second_time += TMTimerGetFreq(s->CTX_SUFF(pSecondTimer));
TMTimerSet(s->CTX_SUFF(pSecondTimer), s->next_second_time);
}
static uint32_t cmos_ioport_read(void *opaque, uint32_t addr)
{
RTCState *s = (RTCState*)opaque;
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:
ret = s->cmos_data[s->cmos_index];
break;
case RTC_REG_A:
ret = s->cmos_data[s->cmos_index];
break;
case RTC_REG_C:
ret = s->cmos_data[s->cmos_index];
PDMDevHlpISASetIrq(s->CTX_SUFF(pDevIns), s->irq, 0);
s->cmos_data[RTC_REG_C] = 0x00;
break;
default:
ret = s->cmos_data[s->cmos_index];
break;
}
Log(("CMOS: Read idx %#04x: %#04x\n", s->cmos_index, ret));
return ret;
}
}
#ifdef IN_RING3
static void rtc_set_memory(RTCState *s, int addr, int val)
{
if (addr >= 0 && addr <= 127)
s->cmos_data[addr] = val;
}
static void rtc_set_date(RTCState *s, const struct my_tm *tm)
{
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)
{
NOREF(pvUser);
if (cb == 1)
{
*pu32 = cmos_ioport_read(PDMINS_2_DATA(pDevIns, RTCState *), Port);
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)
{
NOREF(pvUser);
if (cb == 1)
cmos_ioport_write(PDMINS_2_DATA(pDevIns, RTCState *), Port, u32);
return VINF_SUCCESS;
}
/**
* Device timer callback function, periodic.
*
* @param pDevIns Device instance of the device which registered the timer.
* @param pTimer The timer handle.
*/
PDMBOTHCBDECL(void) rtcTimerPeriodic(PPDMDEVINS pDevIns, PTMTIMER pTimer)
{
rtc_periodic_timer(PDMINS_2_DATA(pDevIns, RTCState *));
}
/**
* Device timer callback function, second.
*
* @param pDevIns Device instance of the device which registered the timer.
* @param pTimer The timer handle.
*/
PDMBOTHCBDECL(void) rtcTimerSecond(PPDMDEVINS pDevIns, PTMTIMER pTimer)
{
rtc_update_second(PDMINS_2_DATA(pDevIns, RTCState *));
}
/**
* Device timer callback function, second2.
*
* @param pDevIns Device instance of the device which registered the timer.
* @param pTimer The timer handle.
*/
PDMBOTHCBDECL(void) rtcTimerSecond2(PPDMDEVINS pDevIns, PTMTIMER pTimer)
{
rtc_update_second2(PDMINS_2_DATA(pDevIns, RTCState *));
}
#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.
*/
static DECLCALLBACK(int) rtcSaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSMHandle)
{
RTCState *pThis = PDMINS_2_DATA(pDevIns, RTCState *);
SSMR3PutMem(pSSMHandle, pThis->cmos_data, 128);
SSMR3PutU8(pSSMHandle, pThis->cmos_index);
SSMR3PutS32(pSSMHandle, pThis->current_tm.tm_sec);
SSMR3PutS32(pSSMHandle, pThis->current_tm.tm_min);
SSMR3PutS32(pSSMHandle, pThis->current_tm.tm_hour);
SSMR3PutS32(pSSMHandle, pThis->current_tm.tm_wday);
SSMR3PutS32(pSSMHandle, pThis->current_tm.tm_mday);
SSMR3PutS32(pSSMHandle, pThis->current_tm.tm_mon);
SSMR3PutS32(pSSMHandle, pThis->current_tm.tm_year);
TMR3TimerSave(pThis->CTX_SUFF(pPeriodicTimer), pSSMHandle);
SSMR3PutS64(pSSMHandle, pThis->next_periodic_time);
SSMR3PutS64(pSSMHandle, pThis->next_second_time);
TMR3TimerSave(pThis->CTX_SUFF(pSecondTimer), pSSMHandle);
TMR3TimerSave(pThis->CTX_SUFF(pSecondTimer2), pSSMHandle);
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)
{
RTCState *pThis = PDMINS_2_DATA(pDevIns, RTCState *);
if (u32Version != 1)
return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
SSMR3GetMem(pSSMHandle, pThis->cmos_data, 128);
SSMR3GetU8(pSSMHandle, &pThis->cmos_index);
SSMR3GetS32(pSSMHandle, &pThis->current_tm.tm_sec);
SSMR3GetS32(pSSMHandle, &pThis->current_tm.tm_min);
SSMR3GetS32(pSSMHandle, &pThis->current_tm.tm_hour);
SSMR3GetS32(pSSMHandle, &pThis->current_tm.tm_wday);
SSMR3GetS32(pSSMHandle, &pThis->current_tm.tm_mday);
SSMR3GetS32(pSSMHandle, &pThis->current_tm.tm_mon);
SSMR3GetS32(pSSMHandle, &pThis->current_tm.tm_year);
TMR3TimerLoad(pThis->CTX_SUFF(pPeriodicTimer), pSSMHandle);
SSMR3GetS64(pSSMHandle, &pThis->next_periodic_time);
SSMR3GetS64(pSSMHandle, &pThis->next_second_time);
TMR3TimerLoad(pThis->CTX_SUFF(pSecondTimer), pSSMHandle);
TMR3TimerLoad(pThis->CTX_SUFF(pSecondTimer2), pSSMHandle);
int period_code = pThis->cmos_data[RTC_REG_A] & 0x0f;
if ( period_code != 0
&& (pThis->cmos_data[RTC_REG_B] & REG_B_PIE)) {
if (period_code <= 2)
period_code += 7;
int period = 1 << (period_code - 1);
LogRel(("RTC: period=%#x (%d) %u Hz (restore)\n", period, period, _32K / period));
pThis->CurPeriod = period;
} else {
LogRel(("RTC: stopped the periodic timer (restore)\n"));
pThis->CurPeriod = 0;
}
pThis->cRelLogEntries = 0;
return VINF_SUCCESS;
}
/* -=-=-=-=-=- PDM Interface provided by the RTC device -=-=-=-=-=- */
/**
* Calculate and update the standard CMOS checksum.
*
* @param pData Pointer to the RTC state data.
*/
static void rtcCalcCRC(RTCState *pData)
{
uint16_t u16;
unsigned i;
for (i = RTC_CRC_START, u16 = 0; i <= RTC_CRC_LAST; i++)
u16 += pData->cmos_data[i];
pData->cmos_data[RTC_CRC_LOW] = u16 & 0xff;
pData->cmos_data[RTC_CRC_HIGH] = (u16 >> 8) & 0xff;
}
/**
* 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.
*/
static DECLCALLBACK(int) rtcCMOSWrite(PPDMDEVINS pDevIns, unsigned iReg, uint8_t u8Value)
{
RTCState *pData = PDMINS_2_DATA(pDevIns, RTCState *);
if (iReg < RT_ELEMENTS(pData->cmos_data))
{
pData->cmos_data[iReg] = u8Value;
/* does it require checksum update? */
if ( iReg >= RTC_CRC_START
&& iReg <= RTC_CRC_LAST)
rtcCalcCRC(pData);
return VINF_SUCCESS;
}
AssertMsgFailed(("iReg=%d\n", iReg));
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.
*/
static DECLCALLBACK(int) rtcCMOSRead(PPDMDEVINS pDevIns, unsigned iReg, uint8_t *pu8Value)
{
RTCState *pData = PDMINS_2_DATA(pDevIns, RTCState *);
if (iReg < RT_ELEMENTS(pData->cmos_data))
{
*pu8Value = pData->cmos_data[iReg];
return VINF_SUCCESS;
}
AssertMsgFailed(("iReg=%d\n", iReg));
return VERR_INVALID_PARAMETER;
}
/* -=-=-=-=-=- based on bits from pc.c -=-=-=-=-=- */
/** @copydoc FNPDMDEVINITCOMPLETE */
static DECLCALLBACK(int) rtcInitComplete(PPDMDEVINS pDevIns)
{
/** @todo this should be (re)done at power on if we didn't load a state... */
RTCState *pData = PDMINS_2_DATA(pDevIns, RTCState *);
/*
* Set the CMOS date/time.
*/
RTTIMESPEC Now;
PDMDevHlpUTCNow(pDevIns, &Now);
RTTIME Time;
if (pData->fUTC)
RTTimeExplode(&Time, &Now);
else
RTTimeLocalExplode(&Time, &Now);
struct my_tm Tm;
memset(&Tm, 0, sizeof(Tm));
Tm.tm_year = Time.i32Year - 1900;
Tm.tm_mon = Time.u8Month - 1;
Tm.tm_mday = Time.u8MonthDay;
Tm.tm_wday = (Time.u8WeekDay - 1 + 7) % 7; /* 0 = monday -> sunday */
Tm.tm_yday = Time.u16YearDay - 1;
Tm.tm_hour = Time.u8Hour;
Tm.tm_min = Time.u8Minute;
Tm.tm_sec = Time.u8Second;
rtc_set_date(pData, &Tm);
int iYear = to_bcd(pData, (Tm.tm_year / 100) + 19); /* tm_year is 1900 based */
rtc_set_memory(pData, 0x32, iYear); /* 32h - Century Byte (BCD value for the century */
rtc_set_memory(pData, 0x37, iYear); /* 37h - (IBM PS/2) Date Century Byte */
/*
* Recalculate the checksum just in case.
*/
rtcCalcCRC(pData);
Log(("CMOS: \n%16.128Vhxd\n", pData->cmos_data));
return VINF_SUCCESS;
}
/* -=-=-=-=-=- real code -=-=-=-=-=- */
/**
* @copydoc
*/
static DECLCALLBACK(void) rtcRelocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
{
RTCState *pThis = PDMINS_2_DATA(pDevIns, RTCState *);
pThis->pDevInsRC = PDMDEVINS_2_GCPTR(pDevIns);
pThis->pPeriodicTimerRC = TMTimerRCPtr(pThis->pPeriodicTimerR3);
pThis->pSecondTimerRC = TMTimerRCPtr(pThis->pSecondTimerR3);
pThis->pSecondTimer2RC = TMTimerRCPtr(pThis->pSecondTimer2R3);
}
/**
* 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.
*/
static DECLCALLBACK(int) rtcConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfgHandle)
{
RTCState *pData = PDMINS_2_DATA(pDevIns, RTCState *);
int rc;
uint8_t u8Irq;
uint16_t u16Base;
bool fGCEnabled;
bool fR0Enabled;
Assert(iInstance == 0);
/*
* Validate configuration.
*/
if (!CFGMR3AreValuesValid(pCfgHandle, "Irq\0Base\0GCEnabled\0fR0Enabled\0"))
return VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES;
/*
* Init the data.
*/
rc = CFGMR3QueryU8Def(pCfgHandle, "Irq", &u8Irq, 8);
if (RT_FAILURE(rc))
return PDMDEV_SET_ERROR(pDevIns, rc,
N_("Configuration error: Querying \"Irq\" as a uint8_t failed"));
rc = CFGMR3QueryU16Def(pCfgHandle, "Base", &u16Base, 0x70);
if (RT_FAILURE(rc))
return PDMDEV_SET_ERROR(pDevIns, rc,
N_("Configuration error: Querying \"Base\" as a uint16_t failed"));
rc = CFGMR3QueryBoolDef(pCfgHandle, "GCEnabled", &fGCEnabled, true);
if (RT_FAILURE(rc))
return PDMDEV_SET_ERROR(pDevIns, rc,
N_("Configuration error: failed to read GCEnabled as boolean"));
rc = CFGMR3QueryBoolDef(pCfgHandle, "R0Enabled", &fR0Enabled, true);
if (RT_FAILURE(rc))
return PDMDEV_SET_ERROR(pDevIns, 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));
pData->pDevInsR3 = pDevIns;
pData->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
pData->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
pData->irq = u8Irq;
pData->cmos_data[RTC_REG_A] = 0x26;
pData->cmos_data[RTC_REG_B] = 0x02;
pData->cmos_data[RTC_REG_C] = 0x00;
pData->cmos_data[RTC_REG_D] = 0x80;
pData->RtcReg.u32Version = PDM_RTCREG_VERSION;
pData->RtcReg.pfnRead = rtcCMOSRead;
pData->RtcReg.pfnWrite = rtcCMOSWrite;
/*
* Create timers, arm them, register I/O Ports and save state.
*/
rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL_SYNC, rtcTimerPeriodic, "MC146818 RTC/CMOS - Periodic", &pData->pPeriodicTimerR3);
if (RT_FAILURE(rc))
return rc;
pData->pPeriodicTimerR0 = TMTimerR0Ptr(pData->pPeriodicTimerR3);
pData->pPeriodicTimerRC = TMTimerRCPtr(pData->pPeriodicTimerR3);
rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL_SYNC, rtcTimerSecond, "MC146818 RTC/CMOS - Second", &pData->pSecondTimerR3);
if (RT_FAILURE(rc))
return rc;
pData->pSecondTimerR0 = TMTimerR0Ptr(pData->pSecondTimerR3);
pData->pSecondTimerRC = TMTimerRCPtr(pData->pSecondTimerR3);
rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL_SYNC, rtcTimerSecond2, "MC146818 RTC/CMOS - Second2", &pData->pSecondTimer2R3);
if (RT_FAILURE(rc))
return rc;
pData->pSecondTimer2R0 = TMTimerR0Ptr(pData->pSecondTimer2R3);
pData->pSecondTimer2RC = TMTimerRCPtr(pData->pSecondTimer2R3);
pData->next_second_time = TMTimerGet(pData->CTX_SUFF(pSecondTimer2)) + (TMTimerGetFreq(pData->CTX_SUFF(pSecondTimer2)) * 99) / 100;
rc = TMTimerSet(pData->CTX_SUFF(pSecondTimer2), pData->next_second_time);
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(*pData),
NULL, rtcSaveExec, NULL,
NULL, rtcLoadExec, NULL);
if (RT_FAILURE(rc))
return rc;
/*
* Register ourselves as the RTC/CMOS with PDM.
*/
rc = pDevIns->pDevHlp->pfnRTCRegister(pDevIns, &pData->RtcReg, &pData->pRtcHlpR3);
if (RT_FAILURE(rc))
return rc;
return VINF_SUCCESS;
}
/**
* The device registration structure.
*/
const PDMDEVREG g_DeviceMC146818 =
{
/* u32Version */
PDM_DEVREG_VERSION,
/* szDeviceName */
"mc146818",
/* szGCMod */
"VBoxDDGC.gc",
/* szR0Mod */
"VBoxDDR0.r0",
/* pszDescription */
"Motorola MC146818 RTC/CMOS Device.",
/* 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 */
PDM_DEVREG_CLASS_RTC,
/* cMaxInstances */
1,
/* cbInstance */
sizeof(RTCState),
/* pfnConstruct */
rtcConstruct,
/* pfnDestruct */
NULL,
/* pfnRelocate */
rtcRelocate,
/* pfnIOCtl */
NULL,
/* pfnPowerOn */
NULL,
/* pfnReset */
NULL,
/* pfnSuspend */
NULL,
/* pfnResume */
NULL,
/* pfnAttach */
NULL,
/* pfnDetach */
NULL,
/* pfnQueryInterface */
NULL,
/* pfnInitComplete */
rtcInitComplete
};
#endif /* IN_RING3 */
#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */