[2781] | 1 | /* $Id: DevRTC.cpp 98103 2023-01-17 14:15:46Z vboxsync $ */
|
---|
[1] | 2 | /** @file
|
---|
[27460] | 3 | * Motorola MC146818 RTC/CMOS Device with PIIX4 extensions.
|
---|
[1] | 4 | */
|
---|
| 5 |
|
---|
| 6 | /*
|
---|
[98103] | 7 | * Copyright (C) 2006-2023 Oracle and/or its affiliates.
|
---|
[1] | 8 | *
|
---|
[96407] | 9 | * This file is part of VirtualBox base platform packages, as
|
---|
| 10 | * available from https://www.virtualbox.org.
|
---|
| 11 | *
|
---|
| 12 | * This program is free software; you can redistribute it and/or
|
---|
| 13 | * modify it under the terms of the GNU General Public License
|
---|
| 14 | * as published by the Free Software Foundation, in version 3 of the
|
---|
| 15 | * License.
|
---|
| 16 | *
|
---|
| 17 | * This program is distributed in the hope that it will be useful, but
|
---|
| 18 | * WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
| 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
---|
| 20 | * General Public License for more details.
|
---|
| 21 | *
|
---|
| 22 | * You should have received a copy of the GNU General Public License
|
---|
| 23 | * along with this program; if not, see <https://www.gnu.org/licenses>.
|
---|
| 24 | *
|
---|
| 25 | * SPDX-License-Identifier: GPL-3.0-only
|
---|
[69498] | 26 | * --------------------------------------------------------------------
|
---|
[1] | 27 | *
|
---|
| 28 | * This code is based on:
|
---|
| 29 | *
|
---|
| 30 | * QEMU MC146818 RTC emulation
|
---|
| 31 | *
|
---|
| 32 | * Copyright (c) 2003-2004 Fabrice Bellard
|
---|
| 33 | *
|
---|
| 34 | * Permission is hereby granted, free of charge, to any person obtaining a copy
|
---|
| 35 | * of this software and associated documentation files (the "Software"), to deal
|
---|
| 36 | * in the Software without restriction, including without limitation the rights
|
---|
| 37 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
---|
| 38 | * copies of the Software, and to permit persons to whom the Software is
|
---|
| 39 | * furnished to do so, subject to the following conditions:
|
---|
| 40 | *
|
---|
| 41 | * The above copyright notice and this permission notice shall be included in
|
---|
| 42 | * all copies or substantial portions of the Software.
|
---|
| 43 | *
|
---|
| 44 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
---|
| 45 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
---|
| 46 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
---|
| 47 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
---|
| 48 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
---|
| 49 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
---|
| 50 | * THE SOFTWARE.
|
---|
| 51 | */
|
---|
| 52 |
|
---|
[57358] | 53 |
|
---|
| 54 | /*********************************************************************************************************************************
|
---|
| 55 | * Header Files *
|
---|
| 56 | *********************************************************************************************************************************/
|
---|
[41708] | 57 | #define LOG_GROUP LOG_GROUP_DEV_RTC
|
---|
[35346] | 58 | #include <VBox/vmm/pdmdev.h>
|
---|
[1] | 59 | #include <VBox/log.h>
|
---|
[29250] | 60 | #include <iprt/asm-math.h>
|
---|
[1] | 61 | #include <iprt/assert.h>
|
---|
[11209] | 62 | #include <iprt/string.h>
|
---|
[1] | 63 |
|
---|
[27121] | 64 | #ifdef IN_RING3
|
---|
| 65 | # include <iprt/alloc.h>
|
---|
| 66 | # include <iprt/uuid.h>
|
---|
| 67 | #endif /* IN_RING3 */
|
---|
| 68 |
|
---|
[35353] | 69 | #include "VBoxDD.h"
|
---|
[41708] | 70 |
|
---|
[1] | 71 |
|
---|
[57358] | 72 | /*********************************************************************************************************************************
|
---|
| 73 | * Defined Constants And Macros *
|
---|
| 74 | *********************************************************************************************************************************/
|
---|
[44810] | 75 | /*#define DEBUG_CMOS*/
|
---|
[1] | 76 | #define RTC_CRC_START 0x10
|
---|
| 77 | #define RTC_CRC_LAST 0x2d
|
---|
| 78 | #define RTC_CRC_HIGH 0x2e
|
---|
| 79 | #define RTC_CRC_LOW 0x2f
|
---|
| 80 |
|
---|
| 81 | #define RTC_SECONDS 0
|
---|
| 82 | #define RTC_SECONDS_ALARM 1
|
---|
| 83 | #define RTC_MINUTES 2
|
---|
| 84 | #define RTC_MINUTES_ALARM 3
|
---|
| 85 | #define RTC_HOURS 4
|
---|
| 86 | #define RTC_HOURS_ALARM 5
|
---|
| 87 | #define RTC_ALARM_DONT_CARE 0xC0
|
---|
| 88 |
|
---|
| 89 | #define RTC_DAY_OF_WEEK 6
|
---|
| 90 | #define RTC_DAY_OF_MONTH 7
|
---|
| 91 | #define RTC_MONTH 8
|
---|
| 92 | #define RTC_YEAR 9
|
---|
| 93 |
|
---|
| 94 | #define RTC_REG_A 10
|
---|
| 95 | #define RTC_REG_B 11
|
---|
| 96 | #define RTC_REG_C 12
|
---|
| 97 | #define RTC_REG_D 13
|
---|
| 98 |
|
---|
| 99 | #define REG_A_UIP 0x80
|
---|
| 100 |
|
---|
| 101 | #define REG_B_SET 0x80
|
---|
| 102 | #define REG_B_PIE 0x40
|
---|
| 103 | #define REG_B_AIE 0x20
|
---|
| 104 | #define REG_B_UIE 0x10
|
---|
| 105 |
|
---|
[89735] | 106 | #define REG_C_IRQF 0x80
|
---|
| 107 | #define REG_C_PF 0x40
|
---|
| 108 | #define REG_C_AF 0x20
|
---|
| 109 | #define REG_C_UF 0x10
|
---|
| 110 |
|
---|
[41708] | 111 | #define CMOS_BANK_LOWER_LIMIT 0x0E
|
---|
| 112 | #define CMOS_BANK_UPPER_LIMIT 0x7F
|
---|
| 113 | #define CMOS_BANK2_LOWER_LIMIT 0x80
|
---|
| 114 | #define CMOS_BANK2_UPPER_LIMIT 0xFF
|
---|
[43241] | 115 | #define CMOS_BANK_SIZE 0x80
|
---|
[11209] | 116 |
|
---|
[24089] | 117 | /** The saved state version. */
|
---|
[27460] | 118 | #define RTC_SAVED_STATE_VERSION 4
|
---|
| 119 | /** The saved state version used by VirtualBox pre-3.2.
|
---|
| 120 | * This does not include the second 128-byte bank. */
|
---|
| 121 | #define RTC_SAVED_STATE_VERSION_VBOX_32PRE 3
|
---|
[27121] | 122 | /** The saved state version used by VirtualBox 3.1 and earlier.
|
---|
[27460] | 123 | * This does not include disabled by HPET state. */
|
---|
[27121] | 124 | #define RTC_SAVED_STATE_VERSION_VBOX_31 2
|
---|
[24089] | 125 | /** The saved state version used by VirtualBox 3.0 and earlier.
|
---|
| 126 | * This does not include the configuration. */
|
---|
| 127 | #define RTC_SAVED_STATE_VERSION_VBOX_30 1
|
---|
| 128 |
|
---|
| 129 |
|
---|
[57358] | 130 | /*********************************************************************************************************************************
|
---|
| 131 | * Structures and Typedefs *
|
---|
| 132 | *********************************************************************************************************************************/
|
---|
[5310] | 133 | /** @todo Replace struct my_tm with RTTIME. */
|
---|
[9088] | 134 | struct my_tm
|
---|
[5308] | 135 | {
|
---|
| 136 | int32_t tm_sec;
|
---|
| 137 | int32_t tm_min;
|
---|
| 138 | int32_t tm_hour;
|
---|
| 139 | int32_t tm_mday;
|
---|
| 140 | int32_t tm_mon;
|
---|
| 141 | int32_t tm_year;
|
---|
| 142 | int32_t tm_wday;
|
---|
| 143 | int32_t tm_yday;
|
---|
| 144 | };
|
---|
| 145 |
|
---|
| 146 |
|
---|
[44810] | 147 | typedef struct RTCSTATE
|
---|
| 148 | {
|
---|
[27460] | 149 | uint8_t cmos_data[256];
|
---|
| 150 | uint8_t cmos_index[2];
|
---|
| 151 | uint8_t Alignment0[6];
|
---|
[5308] | 152 | struct my_tm current_tm;
|
---|
[24089] | 153 | /** The configured IRQ. */
|
---|
[80531] | 154 | int32_t irq;
|
---|
| 155 | /** The configured I/O port base. */
|
---|
| 156 | RTIOPORT IOPortBase;
|
---|
| 157 | /** Use UTC or local time initially. */
|
---|
| 158 | bool fUTC;
|
---|
| 159 | /** Disabled by HPET legacy mode. */
|
---|
| 160 | bool fDisabledByHpet;
|
---|
| 161 | /* periodic timer */
|
---|
| 162 | int64_t next_periodic_time;
|
---|
| 163 | /* second update */
|
---|
| 164 | int64_t next_second_time;
|
---|
| 165 |
|
---|
| 166 | /** The periodic timer (rtcTimerPeriodic). */
|
---|
| 167 | TMTIMERHANDLE hPeriodicTimer;
|
---|
| 168 | /** The second timer (rtcTimerSecond). */
|
---|
| 169 | TMTIMERHANDLE hSecondTimer;
|
---|
| 170 | /** The second second timer (rtcTimerSecond2). */
|
---|
| 171 | TMTIMERHANDLE hSecondTimer2;
|
---|
| 172 | /** The I/O port range handle. */
|
---|
| 173 | IOMIOPORTHANDLE hIoPorts;
|
---|
| 174 |
|
---|
| 175 | /** Number of release log entries. Used to prevent flooding. */
|
---|
| 176 | uint32_t cRelLogEntries;
|
---|
| 177 | /** The current/previous logged timer period. */
|
---|
| 178 | int32_t CurLogPeriod;
|
---|
| 179 | /** The current/previous hinted timer period. */
|
---|
| 180 | int32_t CurHintPeriod;
|
---|
| 181 | /** How many consecutive times the UIP has been seen. */
|
---|
| 182 | int32_t cUipSeen;
|
---|
| 183 |
|
---|
| 184 | /** Number of IRQs that's been raised. */
|
---|
| 185 | STAMCOUNTER StatRTCIrq;
|
---|
| 186 | /** Number of times the timer callback handler ran. */
|
---|
| 187 | STAMCOUNTER StatRTCTimerCB;
|
---|
[89735] | 188 | /** Number of times the PIE bit was changed. */
|
---|
| 189 | STAMCOUNTER StatRTCPieFlip;
|
---|
| 190 | /** Number of times an interrupt was cleared. */
|
---|
| 191 | STAMCOUNTER StatRTCIrqClear;
|
---|
| 192 | /** How long the periodic interrupt remains active. */
|
---|
| 193 | STAMPROFILEADV StatPIrqPending;
|
---|
[80531] | 194 | } RTCSTATE;
|
---|
| 195 | /** Pointer to the RTC device state. */
|
---|
| 196 | typedef RTCSTATE *PRTCSTATE;
|
---|
| 197 |
|
---|
| 198 |
|
---|
| 199 | /**
|
---|
| 200 | * RTC ring-3 instance data.
|
---|
| 201 | */
|
---|
| 202 | typedef struct RTCSTATER3
|
---|
| 203 | {
|
---|
| 204 | /** Pointer to the device instance. */
|
---|
| 205 | PPDMDEVINSR3 pDevInsR3;
|
---|
| 206 |
|
---|
| 207 | /** The RTC registration structure. */
|
---|
| 208 | PDMRTCREG RtcReg;
|
---|
| 209 | /** The RTC device helpers. */
|
---|
| 210 | R3PTRTYPE(PCPDMRTCHLP) pRtcHlpR3;
|
---|
| 211 |
|
---|
| 212 | /** Pointer to the shared state (for the IHpetLegacyNotify callback). */
|
---|
| 213 | PRTCSTATE pShared;
|
---|
| 214 | /** HPET legacy mode notification interface. */
|
---|
| 215 | PDMIHPETLEGACYNOTIFY IHpetLegacyNotify;
|
---|
| 216 | } RTCSTATER3;
|
---|
| 217 | /** Pointer to the ring-3 RTC device instance data. */
|
---|
| 218 | typedef RTCSTATER3 *PRTCSTATER3;
|
---|
| 219 |
|
---|
| 220 |
|
---|
| 221 | /**
|
---|
| 222 | * RTC ring-0 instance data.
|
---|
| 223 | */
|
---|
| 224 | typedef struct RTCSTATER0
|
---|
| 225 | {
|
---|
[81896] | 226 | uint64_t uUnused;
|
---|
[80531] | 227 | } RTCSTATER0;
|
---|
| 228 | /** Pointer to the ring-0 RTC device instance data. */
|
---|
| 229 | typedef RTCSTATER0 *PRTCSTATER0;
|
---|
| 230 |
|
---|
| 231 |
|
---|
| 232 | /**
|
---|
| 233 | * RTC raw-mode instance data.
|
---|
| 234 | */
|
---|
| 235 | typedef struct RTCSTATERC
|
---|
| 236 | {
|
---|
[81896] | 237 | uint64_t uUnused;
|
---|
[80531] | 238 | } RTCSTATERC;
|
---|
| 239 | /** Pointer to the raw-mode RTC device instance data. */
|
---|
| 240 | typedef RTCSTATERC *PRTCSTATERC;
|
---|
| 241 |
|
---|
| 242 |
|
---|
[81896] | 243 | /** The instance data for the current context. */
|
---|
| 244 | typedef CTX_SUFF(RTCSTATE) RTCSTATECC;
|
---|
| 245 | /** Pointer to the instance data for the current context. */
|
---|
| 246 | typedef CTX_SUFF(PRTCSTATE) PRTCSTATECC;
|
---|
[80531] | 247 |
|
---|
| 248 |
|
---|
| 249 | #ifndef VBOX_DEVICE_STRUCT_TESTCASE
|
---|
| 250 |
|
---|
| 251 | static void rtc_timer_update(PPDMDEVINS pDevIns, PRTCSTATE pThis, int64_t current_time)
|
---|
| 252 | {
|
---|
| 253 | int period_code, period;
|
---|
| 254 | uint64_t cur_clock, next_irq_clock;
|
---|
| 255 | uint32_t freq;
|
---|
| 256 |
|
---|
| 257 | Assert(PDMDevHlpTimerIsLockOwner(pDevIns, pThis->hPeriodicTimer));
|
---|
| 258 | Assert(PDMDevHlpCritSectIsOwner(pDevIns, pDevIns->CTX_SUFF(pCritSectRo)));
|
---|
| 259 |
|
---|
| 260 | period_code = pThis->cmos_data[RTC_REG_A] & 0x0f;
|
---|
| 261 | if ( period_code != 0
|
---|
| 262 | && (pThis->cmos_data[RTC_REG_B] & REG_B_PIE))
|
---|
| 263 | {
|
---|
| 264 | if (period_code <= 2)
|
---|
| 265 | period_code += 7;
|
---|
| 266 | /* period in 32 kHz cycles */
|
---|
| 267 | period = 1 << (period_code - 1);
|
---|
| 268 | /* compute 32 kHz clock */
|
---|
| 269 | freq = PDMDevHlpTimerGetFreq(pDevIns, pThis->hPeriodicTimer);
|
---|
| 270 |
|
---|
| 271 | cur_clock = ASMMultU64ByU32DivByU32(current_time, 32768, freq);
|
---|
| 272 | next_irq_clock = (cur_clock & ~(uint64_t)(period - 1)) + period;
|
---|
| 273 | pThis->next_periodic_time = ASMMultU64ByU32DivByU32(next_irq_clock, freq, 32768) + 1;
|
---|
| 274 | PDMDevHlpTimerSet(pDevIns, pThis->hPeriodicTimer, pThis->next_periodic_time);
|
---|
| 275 |
|
---|
| 276 | #ifdef IN_RING3
|
---|
[87937] | 277 | if (RT_LIKELY(period == pThis->CurLogPeriod))
|
---|
| 278 | { /* likely */ }
|
---|
| 279 | else
|
---|
[80531] | 280 | {
|
---|
| 281 | if (pThis->cRelLogEntries++ < 64)
|
---|
| 282 | LogRel(("RTC: period=%#x (%d) %u Hz\n", period, period, _32K / period));
|
---|
[87937] | 283 | pThis->CurLogPeriod = period;
|
---|
| 284 | }
|
---|
[80531] | 285 | #endif
|
---|
[87937] | 286 | if (RT_LIKELY(period == pThis->CurHintPeriod))
|
---|
| 287 | { /* likely */ }
|
---|
| 288 | else
|
---|
| 289 | {
|
---|
[80531] | 290 | pThis->CurHintPeriod = period;
|
---|
| 291 | PDMDevHlpTimerSetFrequencyHint(pDevIns, pThis->hPeriodicTimer, _32K / period);
|
---|
| 292 | }
|
---|
| 293 | }
|
---|
| 294 | else
|
---|
| 295 | {
|
---|
| 296 | #ifdef IN_RING3
|
---|
| 297 | if (PDMDevHlpTimerIsActive(pDevIns, pThis->hPeriodicTimer) && pThis->cRelLogEntries++ < 64)
|
---|
| 298 | LogRel(("RTC: Stopped the periodic timer\n"));
|
---|
| 299 | #endif
|
---|
| 300 | PDMDevHlpTimerStop(pDevIns, pThis->hPeriodicTimer);
|
---|
[87937] | 301 | pThis->CurHintPeriod = 0;
|
---|
| 302 | pThis->CurLogPeriod = 0;
|
---|
[80531] | 303 | }
|
---|
| 304 | RT_NOREF(pDevIns);
|
---|
| 305 | }
|
---|
| 306 |
|
---|
| 307 |
|
---|
| 308 | static void rtc_raise_irq(PPDMDEVINS pDevIns, PRTCSTATE pThis, uint32_t iLevel)
|
---|
| 309 | {
|
---|
| 310 | if (!pThis->fDisabledByHpet)
|
---|
| 311 | {
|
---|
| 312 | PDMDevHlpISASetIrq(pDevIns, pThis->irq, iLevel);
|
---|
| 313 | if (iLevel)
|
---|
[88836] | 314 | STAM_REL_COUNTER_INC(&pThis->StatRTCIrq);
|
---|
[80531] | 315 | }
|
---|
| 316 | }
|
---|
| 317 |
|
---|
| 318 |
|
---|
| 319 | #ifdef IN_RING3
|
---|
| 320 | DECLINLINE(int) to_bcd(PRTCSTATE pThis, int a)
|
---|
| 321 | {
|
---|
| 322 | if (pThis->cmos_data[RTC_REG_B] & 0x04)
|
---|
| 323 | return a;
|
---|
| 324 | return ((a / 10) << 4) | (a % 10);
|
---|
| 325 | }
|
---|
| 326 | #endif
|
---|
| 327 |
|
---|
| 328 |
|
---|
| 329 | DECLINLINE(int) from_bcd(PRTCSTATE pThis, int a)
|
---|
| 330 | {
|
---|
| 331 | if (pThis->cmos_data[RTC_REG_B] & 0x04)
|
---|
| 332 | return a;
|
---|
| 333 | return ((a >> 4) * 10) + (a & 0x0f);
|
---|
| 334 | }
|
---|
| 335 |
|
---|
| 336 |
|
---|
| 337 | static void rtc_set_time(PRTCSTATE pThis)
|
---|
| 338 | {
|
---|
| 339 | struct my_tm *tm = &pThis->current_tm;
|
---|
| 340 |
|
---|
| 341 | tm->tm_sec = from_bcd(pThis, pThis->cmos_data[RTC_SECONDS]);
|
---|
| 342 | tm->tm_min = from_bcd(pThis, pThis->cmos_data[RTC_MINUTES]);
|
---|
| 343 | tm->tm_hour = from_bcd(pThis, pThis->cmos_data[RTC_HOURS] & 0x7f);
|
---|
| 344 | if (!(pThis->cmos_data[RTC_REG_B] & 0x02))
|
---|
| 345 | {
|
---|
| 346 | tm->tm_hour %= 12;
|
---|
| 347 | if (pThis->cmos_data[RTC_HOURS] & 0x80)
|
---|
| 348 | tm->tm_hour += 12;
|
---|
| 349 | }
|
---|
| 350 | tm->tm_wday = from_bcd(pThis, pThis->cmos_data[RTC_DAY_OF_WEEK]);
|
---|
| 351 | tm->tm_mday = from_bcd(pThis, pThis->cmos_data[RTC_DAY_OF_MONTH]);
|
---|
| 352 | tm->tm_mon = from_bcd(pThis, pThis->cmos_data[RTC_MONTH]) - 1;
|
---|
| 353 | tm->tm_year = from_bcd(pThis, pThis->cmos_data[RTC_YEAR]) + 100;
|
---|
| 354 | }
|
---|
| 355 |
|
---|
| 356 |
|
---|
| 357 | /* -=-=-=-=-=- I/O Port Handlers -=-=-=-=-=- */
|
---|
| 358 |
|
---|
| 359 |
|
---|
| 360 | /**
|
---|
[81139] | 361 | * @callback_method_impl{FNIOMIOPORTNEWIN}
|
---|
[80531] | 362 | */
|
---|
[95108] | 363 | static DECLCALLBACK(VBOXSTRICTRC) rtcIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
|
---|
[80531] | 364 | {
|
---|
| 365 | NOREF(pvUser);
|
---|
[81136] | 366 | Assert(offPort < 4);
|
---|
| 367 |
|
---|
[80531] | 368 | if (cb != 1)
|
---|
| 369 | return VERR_IOM_IOPORT_UNUSED;
|
---|
| 370 |
|
---|
[81591] | 371 | PRTCSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PRTCSTATE);
|
---|
[81136] | 372 | if ((offPort & 1) == 0)
|
---|
[80531] | 373 | *pu32 = 0xff;
|
---|
| 374 | else
|
---|
| 375 | {
|
---|
[81136] | 376 | unsigned bank = (offPort >> 1) & 1;
|
---|
[80531] | 377 | switch (pThis->cmos_index[bank])
|
---|
| 378 | {
|
---|
| 379 | case RTC_SECONDS:
|
---|
| 380 | case RTC_MINUTES:
|
---|
| 381 | case RTC_HOURS:
|
---|
| 382 | case RTC_DAY_OF_WEEK:
|
---|
| 383 | case RTC_DAY_OF_MONTH:
|
---|
| 384 | case RTC_MONTH:
|
---|
| 385 | case RTC_YEAR:
|
---|
| 386 | *pu32 = pThis->cmos_data[pThis->cmos_index[0]];
|
---|
| 387 | break;
|
---|
| 388 |
|
---|
| 389 | case RTC_REG_A:
|
---|
| 390 | if (pThis->cmos_data[RTC_REG_A] & REG_A_UIP)
|
---|
| 391 | ++pThis->cUipSeen;
|
---|
| 392 | else
|
---|
| 393 | pThis->cUipSeen = 0;
|
---|
| 394 | if (pThis->cUipSeen >= 250)
|
---|
| 395 | {
|
---|
| 396 | pThis->cmos_data[pThis->cmos_index[0]] &= ~REG_A_UIP;
|
---|
| 397 | pThis->cUipSeen = 0;
|
---|
| 398 | }
|
---|
| 399 | *pu32 = pThis->cmos_data[pThis->cmos_index[0]];
|
---|
| 400 | break;
|
---|
| 401 |
|
---|
| 402 | case RTC_REG_C:
|
---|
| 403 | *pu32 = pThis->cmos_data[pThis->cmos_index[0]];
|
---|
[89735] | 404 | if (*pu32) /* If any bits were set, reading will clear them. */
|
---|
| 405 | {
|
---|
| 406 | STAM_REL_COUNTER_INC(&pThis->StatRTCIrqClear);
|
---|
| 407 | if (pThis->cmos_data[RTC_REG_C] & REG_C_PF)
|
---|
| 408 | STAM_REL_PROFILE_ADV_STOP(&pThis->StatPIrqPending, dummy);
|
---|
| 409 | }
|
---|
[80531] | 410 | rtc_raise_irq(pDevIns, pThis, 0);
|
---|
| 411 | pThis->cmos_data[RTC_REG_C] = 0x00;
|
---|
| 412 | break;
|
---|
| 413 |
|
---|
| 414 | default:
|
---|
| 415 | *pu32 = pThis->cmos_data[pThis->cmos_index[bank]];
|
---|
| 416 | break;
|
---|
| 417 | }
|
---|
| 418 |
|
---|
| 419 | Log(("CMOS: Read bank %d idx %#04x: %#04x\n", bank, pThis->cmos_index[bank], *pu32));
|
---|
| 420 | }
|
---|
| 421 |
|
---|
| 422 | return VINF_SUCCESS;
|
---|
| 423 | }
|
---|
| 424 |
|
---|
| 425 |
|
---|
| 426 | /**
|
---|
[81139] | 427 | * @callback_method_impl{FNIOMIOPORTNEWOUT}
|
---|
[80531] | 428 | */
|
---|
[95108] | 429 | static DECLCALLBACK(VBOXSTRICTRC) rtcIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
|
---|
[80531] | 430 | {
|
---|
| 431 | NOREF(pvUser);
|
---|
[81136] | 432 | Assert(offPort < 4);
|
---|
| 433 |
|
---|
[80531] | 434 | if (cb != 1)
|
---|
| 435 | return VINF_SUCCESS;
|
---|
| 436 |
|
---|
[81591] | 437 | PRTCSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PRTCSTATE);
|
---|
[81136] | 438 | uint32_t bank = (offPort >> 1) & 1;
|
---|
| 439 | if ((offPort & 1) == 0)
|
---|
[80531] | 440 | {
|
---|
| 441 | pThis->cmos_index[bank] = (u32 & 0x7f) + (bank * CMOS_BANK_SIZE);
|
---|
| 442 |
|
---|
| 443 | /* HACK ALERT! Attempt to trigger VM_FF_TIMER and/or VM_FF_TM_VIRTUAL_SYNC
|
---|
| 444 | for forcing the pSecondTimer2 timer to run be run and clear UIP in
|
---|
| 445 | a timely fashion. */
|
---|
| 446 | if (u32 == RTC_REG_A)
|
---|
| 447 | PDMDevHlpTimerGet(pDevIns, pThis->hSecondTimer);
|
---|
| 448 | }
|
---|
| 449 | else
|
---|
| 450 | {
|
---|
| 451 | Log(("CMOS: Write bank %d idx %#04x: %#04x (old %#04x)\n", bank,
|
---|
| 452 | pThis->cmos_index[bank], u32, pThis->cmos_data[pThis->cmos_index[bank]]));
|
---|
| 453 |
|
---|
| 454 | int const idx = pThis->cmos_index[bank];
|
---|
| 455 | switch (idx)
|
---|
| 456 | {
|
---|
| 457 | case RTC_SECONDS_ALARM:
|
---|
| 458 | case RTC_MINUTES_ALARM:
|
---|
| 459 | case RTC_HOURS_ALARM:
|
---|
| 460 | pThis->cmos_data[pThis->cmos_index[0]] = u32;
|
---|
| 461 | break;
|
---|
| 462 |
|
---|
| 463 | case RTC_SECONDS:
|
---|
| 464 | case RTC_MINUTES:
|
---|
| 465 | case RTC_HOURS:
|
---|
| 466 | case RTC_DAY_OF_WEEK:
|
---|
| 467 | case RTC_DAY_OF_MONTH:
|
---|
| 468 | case RTC_MONTH:
|
---|
| 469 | case RTC_YEAR:
|
---|
| 470 | pThis->cmos_data[pThis->cmos_index[0]] = u32;
|
---|
| 471 | /* if in set mode, do not update the time */
|
---|
| 472 | if (!(pThis->cmos_data[RTC_REG_B] & REG_B_SET))
|
---|
| 473 | rtc_set_time(pThis);
|
---|
| 474 | break;
|
---|
| 475 |
|
---|
| 476 | case RTC_REG_A:
|
---|
| 477 | case RTC_REG_B:
|
---|
| 478 | {
|
---|
| 479 | /* We need to acquire the clock lock, because of lock ordering
|
---|
| 480 | issues this means having to release the device lock. Since
|
---|
| 481 | we're letting IOM do the locking, we must not return without
|
---|
| 482 | holding the device lock.*/
|
---|
| 483 | PDMDevHlpCritSectLeave(pDevIns, pDevIns->CTX_SUFF(pCritSectRo));
|
---|
[82329] | 484 | VBOXSTRICTRC rc1 = PDMDevHlpTimerLockClock2(pDevIns, pThis->hPeriodicTimer, pDevIns->CTX_SUFF(pCritSectRo),
|
---|
| 485 | VINF_SUCCESS /* must get it */);
|
---|
| 486 | AssertRCReturn(VBOXSTRICTRC_VAL(rc1), rc1);
|
---|
[80531] | 487 |
|
---|
| 488 | if (idx == RTC_REG_A)
|
---|
| 489 | {
|
---|
| 490 | /* UIP bit is read only */
|
---|
| 491 | pThis->cmos_data[RTC_REG_A] = (u32 & ~REG_A_UIP)
|
---|
| 492 | | (pThis->cmos_data[RTC_REG_A] & REG_A_UIP);
|
---|
| 493 | }
|
---|
| 494 | else
|
---|
| 495 | {
|
---|
| 496 | if (u32 & REG_B_SET)
|
---|
| 497 | {
|
---|
| 498 | /* set mode: reset UIP mode */
|
---|
| 499 | pThis->cmos_data[RTC_REG_A] &= ~REG_A_UIP;
|
---|
| 500 | #if 0 /* This is probably wrong as it breaks changing the time/date in OS/2. */
|
---|
| 501 | u32 &= ~REG_B_UIE;
|
---|
| 502 | #endif
|
---|
| 503 | }
|
---|
| 504 | else
|
---|
| 505 | {
|
---|
| 506 | /* if disabling set mode, update the time */
|
---|
| 507 | if (pThis->cmos_data[RTC_REG_B] & REG_B_SET)
|
---|
| 508 | rtc_set_time(pThis);
|
---|
| 509 | }
|
---|
[89735] | 510 |
|
---|
| 511 | if (((uint8_t)u32 & REG_B_PIE) != (pThis->cmos_data[RTC_REG_B] & REG_B_PIE))
|
---|
| 512 | STAM_REL_COUNTER_INC(&pThis->StatRTCPieFlip);
|
---|
| 513 |
|
---|
[80531] | 514 | pThis->cmos_data[RTC_REG_B] = u32;
|
---|
| 515 | }
|
---|
| 516 |
|
---|
| 517 | rtc_timer_update(pDevIns, pThis, PDMDevHlpTimerGet(pDevIns, pThis->hPeriodicTimer));
|
---|
| 518 |
|
---|
[82329] | 519 | PDMDevHlpTimerUnlockClock(pDevIns, pThis->hPeriodicTimer);
|
---|
[80531] | 520 | /* the caller leaves the other lock. */
|
---|
| 521 | break;
|
---|
| 522 | }
|
---|
| 523 |
|
---|
| 524 | case RTC_REG_C:
|
---|
| 525 | case RTC_REG_D:
|
---|
| 526 | /* cannot write to them */
|
---|
| 527 | break;
|
---|
| 528 |
|
---|
| 529 | default:
|
---|
| 530 | pThis->cmos_data[pThis->cmos_index[bank]] = u32;
|
---|
| 531 | break;
|
---|
| 532 | }
|
---|
| 533 | }
|
---|
| 534 |
|
---|
| 535 | return VINF_SUCCESS;
|
---|
| 536 | }
|
---|
| 537 |
|
---|
| 538 | #ifdef IN_RING3
|
---|
| 539 |
|
---|
| 540 | /* -=-=-=-=-=- Debug Info Handlers -=-=-=-=-=- */
|
---|
| 541 |
|
---|
| 542 | /**
|
---|
| 543 | * @callback_method_impl{FNDBGFHANDLERDEV,
|
---|
| 544 | * Dumps the cmos Bank Info.}
|
---|
| 545 | */
|
---|
| 546 | static DECLCALLBACK(void) rtcCmosBankInfo(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
|
---|
| 547 | {
|
---|
| 548 | RT_NOREF1(pszArgs);
|
---|
[81591] | 549 | PRTCSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PRTCSTATE);
|
---|
[80531] | 550 |
|
---|
| 551 | pHlp->pfnPrintf(pHlp,
|
---|
| 552 | "First CMOS bank, offsets 0x0E - 0x7F\n"
|
---|
| 553 | "Offset %02x : --- use 'info rtc' to show CMOS clock ---", 0);
|
---|
| 554 | for (unsigned iCmos = CMOS_BANK_LOWER_LIMIT; iCmos <= CMOS_BANK_UPPER_LIMIT; iCmos++)
|
---|
| 555 | {
|
---|
| 556 | if ((iCmos & 15) == 0)
|
---|
| 557 | pHlp->pfnPrintf(pHlp, "Offset %02x : %02x", iCmos, pThis->cmos_data[iCmos]);
|
---|
| 558 | else if ((iCmos & 15) == 8)
|
---|
| 559 | pHlp->pfnPrintf(pHlp, "-%02x", pThis->cmos_data[iCmos]);
|
---|
| 560 | else if ((iCmos & 15) == 15)
|
---|
| 561 | pHlp->pfnPrintf(pHlp, " %02x\n", pThis->cmos_data[iCmos]);
|
---|
| 562 | else
|
---|
| 563 | pHlp->pfnPrintf(pHlp, " %02x", pThis->cmos_data[iCmos]);
|
---|
| 564 | }
|
---|
| 565 | }
|
---|
| 566 |
|
---|
| 567 | /**
|
---|
| 568 | * @callback_method_impl{FNDBGFHANDLERDEV,
|
---|
| 569 | * Dumps the cmos Bank2 Info.}
|
---|
| 570 | */
|
---|
| 571 | static DECLCALLBACK(void) rtcCmosBank2Info(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
|
---|
| 572 | {
|
---|
| 573 | RT_NOREF1(pszArgs);
|
---|
[81591] | 574 | PRTCSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PRTCSTATE);
|
---|
[80531] | 575 |
|
---|
| 576 | pHlp->pfnPrintf(pHlp, "Second CMOS bank, offsets 0x80 - 0xFF\n");
|
---|
| 577 | for (uint16_t iCmos = CMOS_BANK2_LOWER_LIMIT; iCmos <= CMOS_BANK2_UPPER_LIMIT; iCmos++)
|
---|
| 578 | {
|
---|
| 579 | if ((iCmos & 15) == 0)
|
---|
| 580 | pHlp->pfnPrintf(pHlp, "Offset %02x : %02x", iCmos, pThis->cmos_data[iCmos]);
|
---|
| 581 | else if ((iCmos & 15) == 8)
|
---|
| 582 | pHlp->pfnPrintf(pHlp, "-%02x", pThis->cmos_data[iCmos]);
|
---|
| 583 | else if ((iCmos & 15) == 15)
|
---|
| 584 | pHlp->pfnPrintf(pHlp, " %02x\n", pThis->cmos_data[iCmos]);
|
---|
| 585 | else
|
---|
| 586 | pHlp->pfnPrintf(pHlp, " %02x", pThis->cmos_data[iCmos]);
|
---|
| 587 | }
|
---|
| 588 | }
|
---|
| 589 |
|
---|
| 590 | /**
|
---|
| 591 | * @callback_method_impl{FNDBGFHANDLERDEV,
|
---|
| 592 | * Dumps the cmos RTC Info.}
|
---|
| 593 | */
|
---|
| 594 | static DECLCALLBACK(void) rtcCmosClockInfo(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
|
---|
| 595 | {
|
---|
| 596 | RT_NOREF1(pszArgs);
|
---|
[81591] | 597 | PRTCSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PRTCSTATE);
|
---|
[80531] | 598 | uint8_t u8Sec = from_bcd(pThis, pThis->cmos_data[RTC_SECONDS]);
|
---|
| 599 | uint8_t u8Min = from_bcd(pThis, pThis->cmos_data[RTC_MINUTES]);
|
---|
| 600 | uint8_t u8Hr = from_bcd(pThis, pThis->cmos_data[RTC_HOURS] & 0x7f);
|
---|
| 601 | if ( !(pThis->cmos_data[RTC_REG_B] & 0x02)
|
---|
| 602 | && (pThis->cmos_data[RTC_HOURS] & 0x80))
|
---|
| 603 | u8Hr += 12;
|
---|
| 604 | uint8_t u8Day = from_bcd(pThis, pThis->cmos_data[RTC_DAY_OF_MONTH]);
|
---|
| 605 | uint8_t u8Month = from_bcd(pThis, pThis->cmos_data[RTC_MONTH]) ;
|
---|
| 606 | uint8_t u8Year = from_bcd(pThis, pThis->cmos_data[RTC_YEAR]);
|
---|
| 607 | pHlp->pfnPrintf(pHlp, "Time: %02u:%02u:%02u Date: %02u-%02u-%02u\n",
|
---|
| 608 | u8Hr, u8Min, u8Sec, u8Year, u8Month, u8Day);
|
---|
| 609 | pHlp->pfnPrintf(pHlp, "REG A=%02x B=%02x C=%02x D=%02x\n",
|
---|
| 610 | pThis->cmos_data[RTC_REG_A], pThis->cmos_data[RTC_REG_B],
|
---|
| 611 | pThis->cmos_data[RTC_REG_C], pThis->cmos_data[RTC_REG_D]);
|
---|
[89735] | 612 |
|
---|
| 613 | if (pThis->cmos_data[RTC_REG_B] & REG_B_PIE)
|
---|
| 614 | {
|
---|
| 615 | if (pThis->CurHintPeriod)
|
---|
| 616 | pHlp->pfnPrintf(pHlp, "Periodic Interrupt Enabled: %d Hz\n", _32K / pThis->CurHintPeriod);
|
---|
| 617 | }
|
---|
[80531] | 618 | }
|
---|
| 619 |
|
---|
| 620 |
|
---|
| 621 |
|
---|
| 622 | /* -=-=-=-=-=- Timers and their support code -=-=-=-=-=- */
|
---|
| 623 |
|
---|
| 624 |
|
---|
| 625 | /**
|
---|
| 626 | * @callback_method_impl{FNTMTIMERDEV, periodic}
|
---|
| 627 | */
|
---|
[87767] | 628 | static DECLCALLBACK(void) rtcTimerPeriodic(PPDMDEVINS pDevIns, TMTIMERHANDLE hTimer, void *pvUser)
|
---|
[80531] | 629 | {
|
---|
[81591] | 630 | PRTCSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PRTCSTATE);
|
---|
[87767] | 631 | Assert(hTimer == pThis->hPeriodicTimer);
|
---|
| 632 | Assert(PDMDevHlpTimerIsLockOwner(pDevIns, hTimer));
|
---|
[80531] | 633 | Assert(PDMDevHlpCritSectIsOwner(pDevIns, pDevIns->CTX_SUFF(pCritSectRo)));
|
---|
[87767] | 634 | RT_NOREF2(hTimer, pvUser);
|
---|
[80531] | 635 |
|
---|
| 636 | rtc_timer_update(pDevIns, pThis, pThis->next_periodic_time);
|
---|
[88836] | 637 | STAM_REL_COUNTER_INC(&pThis->StatRTCTimerCB);
|
---|
[80531] | 638 |
|
---|
[89735] | 639 | if (!(pThis->cmos_data[RTC_REG_C] & REG_C_PF))
|
---|
| 640 | STAM_REL_PROFILE_ADV_START(&pThis->StatPIrqPending, dummy);
|
---|
| 641 |
|
---|
| 642 | pThis->cmos_data[RTC_REG_C] |= REG_C_IRQF | REG_C_PF;
|
---|
| 643 |
|
---|
[80531] | 644 | rtc_raise_irq(pDevIns, pThis, 1);
|
---|
| 645 | }
|
---|
| 646 |
|
---|
| 647 |
|
---|
| 648 | /* month is between 0 and 11. */
|
---|
| 649 | static int get_days_in_month(int month, int year)
|
---|
| 650 | {
|
---|
| 651 | static const int days_tab[12] =
|
---|
| 652 | {
|
---|
| 653 | 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
|
---|
| 654 | };
|
---|
| 655 | int d;
|
---|
| 656 |
|
---|
| 657 | if ((unsigned )month >= 12)
|
---|
| 658 | return 31;
|
---|
| 659 |
|
---|
| 660 | d = days_tab[month];
|
---|
| 661 | if (month == 1)
|
---|
| 662 | {
|
---|
| 663 | if ((year % 4) == 0 && ((year % 100) != 0 || (year % 400) == 0))
|
---|
| 664 | d++;
|
---|
| 665 | }
|
---|
| 666 | return d;
|
---|
| 667 | }
|
---|
| 668 |
|
---|
| 669 |
|
---|
| 670 | /* update 'tm' to the next second */
|
---|
| 671 | static void rtc_next_second(struct my_tm *tm)
|
---|
| 672 | {
|
---|
| 673 | int days_in_month;
|
---|
| 674 |
|
---|
| 675 | tm->tm_sec++;
|
---|
| 676 | if ((unsigned)tm->tm_sec >= 60)
|
---|
| 677 | {
|
---|
| 678 | tm->tm_sec = 0;
|
---|
| 679 | tm->tm_min++;
|
---|
| 680 | if ((unsigned)tm->tm_min >= 60)
|
---|
| 681 | {
|
---|
| 682 | tm->tm_min = 0;
|
---|
| 683 | tm->tm_hour++;
|
---|
| 684 | if ((unsigned)tm->tm_hour >= 24)
|
---|
| 685 | {
|
---|
| 686 | tm->tm_hour = 0;
|
---|
| 687 | /* next day */
|
---|
| 688 | tm->tm_wday++;
|
---|
| 689 | if ((unsigned)tm->tm_wday >= 7)
|
---|
| 690 | tm->tm_wday = 0;
|
---|
| 691 | days_in_month = get_days_in_month(tm->tm_mon,
|
---|
| 692 | tm->tm_year + 1900);
|
---|
| 693 | tm->tm_mday++;
|
---|
| 694 | if (tm->tm_mday < 1)
|
---|
| 695 | tm->tm_mday = 1;
|
---|
| 696 | else if (tm->tm_mday > days_in_month)
|
---|
| 697 | {
|
---|
| 698 | tm->tm_mday = 1;
|
---|
| 699 | tm->tm_mon++;
|
---|
| 700 | if (tm->tm_mon >= 12)
|
---|
| 701 | {
|
---|
| 702 | tm->tm_mon = 0;
|
---|
| 703 | tm->tm_year++;
|
---|
| 704 | }
|
---|
| 705 | }
|
---|
| 706 | }
|
---|
| 707 | }
|
---|
| 708 | }
|
---|
| 709 | }
|
---|
| 710 |
|
---|
| 711 |
|
---|
| 712 | /**
|
---|
| 713 | * @callback_method_impl{FNTMTIMERDEV, Second timer.}
|
---|
| 714 | */
|
---|
[87767] | 715 | static DECLCALLBACK(void) rtcR3TimerSecond(PPDMDEVINS pDevIns, TMTIMERHANDLE hTimer, void *pvUser)
|
---|
[80531] | 716 | {
|
---|
[81591] | 717 | PRTCSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PRTCSTATE);
|
---|
[80531] | 718 |
|
---|
| 719 | Assert(PDMDevHlpTimerIsLockOwner(pDevIns, pThis->hPeriodicTimer));
|
---|
| 720 | Assert(PDMDevHlpCritSectIsOwner(pDevIns, pDevIns->CTX_SUFF(pCritSectRo)));
|
---|
[87767] | 721 | RT_NOREF(pvUser, hTimer);
|
---|
[80531] | 722 |
|
---|
| 723 | /* if the oscillator is not in normal operation, we do not update */
|
---|
| 724 | if ((pThis->cmos_data[RTC_REG_A] & 0x70) != 0x20)
|
---|
| 725 | {
|
---|
| 726 | pThis->next_second_time += PDMDevHlpTimerGetFreq(pDevIns, pThis->hSecondTimer);
|
---|
| 727 | PDMDevHlpTimerSet(pDevIns, pThis->hSecondTimer, pThis->next_second_time);
|
---|
| 728 | }
|
---|
| 729 | else
|
---|
| 730 | {
|
---|
| 731 | rtc_next_second(&pThis->current_tm);
|
---|
| 732 |
|
---|
| 733 | if (!(pThis->cmos_data[RTC_REG_B] & REG_B_SET))
|
---|
| 734 | {
|
---|
| 735 | /* update in progress bit */
|
---|
| 736 | Log2(("RTC: UIP %x -> 1\n", !!(pThis->cmos_data[RTC_REG_A] & REG_A_UIP)));
|
---|
| 737 | pThis->cmos_data[RTC_REG_A] |= REG_A_UIP;
|
---|
| 738 | }
|
---|
| 739 |
|
---|
| 740 | /* 244140 ns = 8 / 32768 seconds */
|
---|
| 741 | uint64_t delay = PDMDevHlpTimerFromNano(pDevIns, pThis->hSecondTimer2, 244140);
|
---|
| 742 | PDMDevHlpTimerSet(pDevIns, pThis->hSecondTimer2, pThis->next_second_time + delay);
|
---|
| 743 | }
|
---|
| 744 | }
|
---|
| 745 |
|
---|
| 746 |
|
---|
[87760] | 747 | /* Used by rtc_set_date and rtcR3TimerSecond2. */
|
---|
[80531] | 748 | static void rtc_copy_date(PRTCSTATE pThis)
|
---|
| 749 | {
|
---|
| 750 | const struct my_tm *tm = &pThis->current_tm;
|
---|
| 751 |
|
---|
| 752 | pThis->cmos_data[RTC_SECONDS] = to_bcd(pThis, tm->tm_sec);
|
---|
| 753 | pThis->cmos_data[RTC_MINUTES] = to_bcd(pThis, tm->tm_min);
|
---|
| 754 | if (pThis->cmos_data[RTC_REG_B] & 0x02)
|
---|
| 755 | {
|
---|
| 756 | /* 24 hour format */
|
---|
| 757 | pThis->cmos_data[RTC_HOURS] = to_bcd(pThis, tm->tm_hour);
|
---|
| 758 | }
|
---|
| 759 | else
|
---|
| 760 | {
|
---|
| 761 | /* 12 hour format */
|
---|
| 762 | int h = tm->tm_hour % 12;
|
---|
| 763 | pThis->cmos_data[RTC_HOURS] = to_bcd(pThis, h ? h : 12);
|
---|
| 764 | if (tm->tm_hour >= 12)
|
---|
| 765 | pThis->cmos_data[RTC_HOURS] |= 0x80;
|
---|
| 766 | }
|
---|
| 767 | pThis->cmos_data[RTC_DAY_OF_WEEK] = to_bcd(pThis, tm->tm_wday);
|
---|
| 768 | pThis->cmos_data[RTC_DAY_OF_MONTH] = to_bcd(pThis, tm->tm_mday);
|
---|
| 769 | pThis->cmos_data[RTC_MONTH] = to_bcd(pThis, tm->tm_mon + 1);
|
---|
| 770 | pThis->cmos_data[RTC_YEAR] = to_bcd(pThis, tm->tm_year % 100);
|
---|
| 771 | }
|
---|
| 772 |
|
---|
| 773 |
|
---|
| 774 | /**
|
---|
| 775 | * @callback_method_impl{FNTMTIMERDEV, Second2 timer.}
|
---|
| 776 | */
|
---|
[87767] | 777 | static DECLCALLBACK(void) rtcR3TimerSecond2(PPDMDEVINS pDevIns, TMTIMERHANDLE hTimer, void *pvUser)
|
---|
[80531] | 778 | {
|
---|
[81591] | 779 | PRTCSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PRTCSTATE);
|
---|
[80531] | 780 |
|
---|
| 781 | Assert(PDMDevHlpTimerIsLockOwner(pDevIns, pThis->hPeriodicTimer));
|
---|
| 782 | Assert(PDMDevHlpCritSectIsOwner(pDevIns, pDevIns->CTX_SUFF(pCritSectRo)));
|
---|
[87767] | 783 | RT_NOREF2(hTimer, pvUser);
|
---|
[80531] | 784 |
|
---|
| 785 | if (!(pThis->cmos_data[RTC_REG_B] & REG_B_SET))
|
---|
| 786 | rtc_copy_date(pThis);
|
---|
| 787 |
|
---|
| 788 | /* check alarm */
|
---|
| 789 | if (pThis->cmos_data[RTC_REG_B] & REG_B_AIE)
|
---|
| 790 | {
|
---|
| 791 | if ( ( (pThis->cmos_data[RTC_SECONDS_ALARM] & 0xc0) == 0xc0
|
---|
| 792 | || from_bcd(pThis, pThis->cmos_data[RTC_SECONDS_ALARM]) == pThis->current_tm.tm_sec)
|
---|
| 793 | && ( (pThis->cmos_data[RTC_MINUTES_ALARM] & 0xc0) == 0xc0
|
---|
| 794 | || from_bcd(pThis, pThis->cmos_data[RTC_MINUTES_ALARM]) == pThis->current_tm.tm_min)
|
---|
| 795 | && ( (pThis->cmos_data[RTC_HOURS_ALARM ] & 0xc0) == 0xc0
|
---|
| 796 | || from_bcd(pThis, pThis->cmos_data[RTC_HOURS_ALARM ]) == pThis->current_tm.tm_hour)
|
---|
| 797 | )
|
---|
| 798 | {
|
---|
[89735] | 799 | pThis->cmos_data[RTC_REG_C] |= REG_C_IRQF | REG_C_AF;
|
---|
[80531] | 800 | rtc_raise_irq(pDevIns, pThis, 1);
|
---|
| 801 | }
|
---|
| 802 | }
|
---|
| 803 |
|
---|
| 804 | /* update ended interrupt */
|
---|
| 805 | if (pThis->cmos_data[RTC_REG_B] & REG_B_UIE)
|
---|
| 806 | {
|
---|
[89735] | 807 | pThis->cmos_data[RTC_REG_C] |= REG_C_IRQF | REG_C_UF;
|
---|
[80531] | 808 | rtc_raise_irq(pDevIns, pThis, 1);
|
---|
| 809 | }
|
---|
| 810 |
|
---|
| 811 | /* clear update in progress bit */
|
---|
| 812 | Log2(("RTC: UIP %x -> 0\n", !!(pThis->cmos_data[RTC_REG_A] & REG_A_UIP)));
|
---|
| 813 | pThis->cmos_data[RTC_REG_A] &= ~REG_A_UIP;
|
---|
| 814 |
|
---|
| 815 | pThis->next_second_time += PDMDevHlpTimerGetFreq(pDevIns, pThis->hSecondTimer);
|
---|
| 816 | PDMDevHlpTimerSet(pDevIns, pThis->hSecondTimer, pThis->next_second_time);
|
---|
| 817 | }
|
---|
| 818 |
|
---|
| 819 |
|
---|
| 820 | /* -=-=-=-=-=- Saved State -=-=-=-=-=- */
|
---|
| 821 |
|
---|
| 822 |
|
---|
| 823 | /**
|
---|
| 824 | * @callback_method_impl{FNSSMDEVLIVEEXEC}
|
---|
| 825 | */
|
---|
| 826 | static DECLCALLBACK(int) rtcLiveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uPass)
|
---|
| 827 | {
|
---|
| 828 | RT_NOREF1(uPass);
|
---|
| 829 | PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
|
---|
[81591] | 830 | PRTCSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PRTCSTATE);
|
---|
[80531] | 831 |
|
---|
| 832 | pHlp->pfnSSMPutU8( pSSM, pThis->irq);
|
---|
| 833 | pHlp->pfnSSMPutIOPort(pSSM, pThis->IOPortBase);
|
---|
| 834 | pHlp->pfnSSMPutBool( pSSM, pThis->fUTC);
|
---|
| 835 |
|
---|
| 836 | return VINF_SSM_DONT_CALL_AGAIN;
|
---|
| 837 | }
|
---|
| 838 |
|
---|
| 839 |
|
---|
| 840 | /**
|
---|
| 841 | * @callback_method_impl{FNSSMDEVSAVEEXEC}
|
---|
| 842 | */
|
---|
| 843 | static DECLCALLBACK(int) rtcSaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
|
---|
| 844 | {
|
---|
| 845 | PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
|
---|
[81591] | 846 | PRTCSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PRTCSTATE);
|
---|
[80531] | 847 |
|
---|
| 848 | /* The config. */
|
---|
| 849 | rtcLiveExec(pDevIns, pSSM, SSM_PASS_FINAL);
|
---|
| 850 |
|
---|
| 851 | /* The state. */
|
---|
| 852 | pHlp->pfnSSMPutMem(pSSM, pThis->cmos_data, CMOS_BANK_SIZE);
|
---|
| 853 | pHlp->pfnSSMPutU8(pSSM, pThis->cmos_index[0]);
|
---|
| 854 |
|
---|
| 855 | pHlp->pfnSSMPutS32(pSSM, pThis->current_tm.tm_sec);
|
---|
| 856 | pHlp->pfnSSMPutS32(pSSM, pThis->current_tm.tm_min);
|
---|
| 857 | pHlp->pfnSSMPutS32(pSSM, pThis->current_tm.tm_hour);
|
---|
| 858 | pHlp->pfnSSMPutS32(pSSM, pThis->current_tm.tm_wday);
|
---|
| 859 | pHlp->pfnSSMPutS32(pSSM, pThis->current_tm.tm_mday);
|
---|
| 860 | pHlp->pfnSSMPutS32(pSSM, pThis->current_tm.tm_mon);
|
---|
| 861 | pHlp->pfnSSMPutS32(pSSM, pThis->current_tm.tm_year);
|
---|
| 862 |
|
---|
| 863 | PDMDevHlpTimerSave(pDevIns, pThis->hPeriodicTimer, pSSM);
|
---|
| 864 |
|
---|
| 865 | pHlp->pfnSSMPutS64(pSSM, pThis->next_periodic_time);
|
---|
| 866 |
|
---|
| 867 | pHlp->pfnSSMPutS64(pSSM, pThis->next_second_time);
|
---|
| 868 | PDMDevHlpTimerSave(pDevIns, pThis->hSecondTimer, pSSM);
|
---|
| 869 | PDMDevHlpTimerSave(pDevIns, pThis->hSecondTimer2, pSSM);
|
---|
| 870 |
|
---|
| 871 | pHlp->pfnSSMPutBool(pSSM, pThis->fDisabledByHpet);
|
---|
| 872 |
|
---|
| 873 | pHlp->pfnSSMPutMem(pSSM, &pThis->cmos_data[CMOS_BANK_SIZE], CMOS_BANK_SIZE);
|
---|
| 874 | return pHlp->pfnSSMPutU8(pSSM, pThis->cmos_index[1]);
|
---|
| 875 | }
|
---|
| 876 |
|
---|
| 877 |
|
---|
| 878 | /**
|
---|
| 879 | * @callback_method_impl{FNSSMDEVLOADEXEC}
|
---|
| 880 | */
|
---|
| 881 | static DECLCALLBACK(int) rtcLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
|
---|
| 882 | {
|
---|
| 883 | PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
|
---|
[81591] | 884 | PRTCSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PRTCSTATE);
|
---|
[80531] | 885 | int rc;
|
---|
| 886 |
|
---|
| 887 | if ( uVersion != RTC_SAVED_STATE_VERSION
|
---|
| 888 | && uVersion != RTC_SAVED_STATE_VERSION_VBOX_32PRE
|
---|
| 889 | && uVersion != RTC_SAVED_STATE_VERSION_VBOX_31
|
---|
| 890 | && uVersion != RTC_SAVED_STATE_VERSION_VBOX_30)
|
---|
| 891 | return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
|
---|
| 892 |
|
---|
| 893 | /* The config. */
|
---|
| 894 | if (uVersion > RTC_SAVED_STATE_VERSION_VBOX_30)
|
---|
| 895 | {
|
---|
| 896 | uint8_t u8Irq;
|
---|
| 897 | rc = pHlp->pfnSSMGetU8(pSSM, &u8Irq);
|
---|
| 898 | AssertRCReturn(rc, rc);
|
---|
| 899 | if (u8Irq != pThis->irq)
|
---|
| 900 | return pHlp->pfnSSMSetCfgError(pSSM, RT_SRC_POS, N_("Config mismatch - u8Irq: saved=%#x config=%#x"), u8Irq, pThis->irq);
|
---|
| 901 |
|
---|
| 902 | RTIOPORT IOPortBase;
|
---|
| 903 | rc = pHlp->pfnSSMGetIOPort(pSSM, &IOPortBase);
|
---|
| 904 | AssertRCReturn(rc, rc);
|
---|
| 905 | if (IOPortBase != pThis->IOPortBase)
|
---|
| 906 | return pHlp->pfnSSMSetCfgError(pSSM, RT_SRC_POS, N_("Config mismatch - IOPortBase: saved=%RTiop config=%RTiop"), IOPortBase, pThis->IOPortBase);
|
---|
| 907 |
|
---|
| 908 | bool fUTC;
|
---|
| 909 | rc = pHlp->pfnSSMGetBool(pSSM, &fUTC);
|
---|
| 910 | AssertRCReturn(rc, rc);
|
---|
| 911 | if (fUTC != pThis->fUTC)
|
---|
| 912 | LogRel(("RTC: Config mismatch - fUTC: saved=%RTbool config=%RTbool\n", fUTC, pThis->fUTC));
|
---|
| 913 | }
|
---|
| 914 |
|
---|
| 915 | if (uPass != SSM_PASS_FINAL)
|
---|
| 916 | return VINF_SUCCESS;
|
---|
| 917 |
|
---|
| 918 | /* The state. */
|
---|
| 919 | pHlp->pfnSSMGetMem(pSSM, pThis->cmos_data, CMOS_BANK_SIZE);
|
---|
| 920 | pHlp->pfnSSMGetU8(pSSM, &pThis->cmos_index[0]);
|
---|
| 921 |
|
---|
| 922 | pHlp->pfnSSMGetS32(pSSM, &pThis->current_tm.tm_sec);
|
---|
| 923 | pHlp->pfnSSMGetS32(pSSM, &pThis->current_tm.tm_min);
|
---|
| 924 | pHlp->pfnSSMGetS32(pSSM, &pThis->current_tm.tm_hour);
|
---|
| 925 | pHlp->pfnSSMGetS32(pSSM, &pThis->current_tm.tm_wday);
|
---|
| 926 | pHlp->pfnSSMGetS32(pSSM, &pThis->current_tm.tm_mday);
|
---|
| 927 | pHlp->pfnSSMGetS32(pSSM, &pThis->current_tm.tm_mon);
|
---|
| 928 | pHlp->pfnSSMGetS32(pSSM, &pThis->current_tm.tm_year);
|
---|
| 929 |
|
---|
| 930 | PDMDevHlpTimerLoad(pDevIns, pThis->hPeriodicTimer, pSSM);
|
---|
| 931 |
|
---|
| 932 | pHlp->pfnSSMGetS64(pSSM, &pThis->next_periodic_time);
|
---|
| 933 |
|
---|
| 934 | pHlp->pfnSSMGetS64(pSSM, &pThis->next_second_time);
|
---|
| 935 | PDMDevHlpTimerLoad(pDevIns, pThis->hSecondTimer, pSSM);
|
---|
[90445] | 936 | rc = PDMDevHlpTimerLoad(pDevIns, pThis->hSecondTimer2, pSSM);
|
---|
| 937 | AssertRCReturn(rc, rc);
|
---|
[80531] | 938 |
|
---|
| 939 | if (uVersion > RTC_SAVED_STATE_VERSION_VBOX_31)
|
---|
[90445] | 940 | {
|
---|
| 941 | rc = pHlp->pfnSSMGetBool(pSSM, &pThis->fDisabledByHpet);
|
---|
| 942 | AssertRCReturn(rc, rc);
|
---|
| 943 | }
|
---|
[80531] | 944 |
|
---|
| 945 | if (uVersion > RTC_SAVED_STATE_VERSION_VBOX_32PRE)
|
---|
| 946 | {
|
---|
| 947 | /* Second CMOS bank. */
|
---|
| 948 | pHlp->pfnSSMGetMem(pSSM, &pThis->cmos_data[CMOS_BANK_SIZE], CMOS_BANK_SIZE);
|
---|
[90445] | 949 | rc = pHlp->pfnSSMGetU8(pSSM, &pThis->cmos_index[1]);
|
---|
| 950 | AssertRCReturn(rc, rc);
|
---|
[80531] | 951 | }
|
---|
| 952 |
|
---|
| 953 | int period_code = pThis->cmos_data[RTC_REG_A] & 0x0f;
|
---|
| 954 | if ( period_code != 0
|
---|
| 955 | && (pThis->cmos_data[RTC_REG_B] & REG_B_PIE))
|
---|
| 956 | {
|
---|
| 957 | if (period_code <= 2)
|
---|
| 958 | period_code += 7;
|
---|
| 959 | int period = 1 << (period_code - 1);
|
---|
| 960 | LogRel(("RTC: period=%#x (%d) %u Hz (restore)\n", period, period, _32K / period));
|
---|
[90445] | 961 | rc = PDMDevHlpCritSectEnter(pDevIns, pDevIns->pCritSectRoR3, VINF_SUCCESS);
|
---|
| 962 | AssertRCReturn(rc, rc);
|
---|
[80531] | 963 | PDMDevHlpTimerSetFrequencyHint(pDevIns, pThis->hPeriodicTimer, _32K / period);
|
---|
| 964 | PDMDevHlpCritSectLeave(pDevIns, pDevIns->pCritSectRoR3);
|
---|
| 965 | pThis->CurLogPeriod = period;
|
---|
| 966 | pThis->CurHintPeriod = period;
|
---|
| 967 | }
|
---|
| 968 | else
|
---|
| 969 | {
|
---|
| 970 | LogRel(("RTC: Stopped the periodic timer (restore)\n"));
|
---|
| 971 | pThis->CurLogPeriod = 0;
|
---|
| 972 | pThis->CurHintPeriod = 0;
|
---|
| 973 | }
|
---|
| 974 | pThis->cRelLogEntries = 0;
|
---|
| 975 |
|
---|
| 976 | return VINF_SUCCESS;
|
---|
| 977 | }
|
---|
| 978 |
|
---|
| 979 |
|
---|
| 980 | /* -=-=-=-=-=- PDM Interface provided by the RTC device -=-=-=-=-=- */
|
---|
| 981 |
|
---|
| 982 | /**
|
---|
| 983 | * Calculate and update the standard CMOS checksum.
|
---|
| 984 | *
|
---|
| 985 | * @param pThis Pointer to the RTC state data.
|
---|
| 986 | */
|
---|
| 987 | static void rtcCalcCRC(PRTCSTATE pThis)
|
---|
| 988 | {
|
---|
| 989 | uint16_t u16 = 0;
|
---|
| 990 | for (unsigned i = RTC_CRC_START; i <= RTC_CRC_LAST; i++)
|
---|
| 991 | u16 += pThis->cmos_data[i];
|
---|
| 992 |
|
---|
| 993 | pThis->cmos_data[RTC_CRC_LOW] = u16 & 0xff;
|
---|
| 994 | pThis->cmos_data[RTC_CRC_HIGH] = (u16 >> 8) & 0xff;
|
---|
| 995 | }
|
---|
| 996 |
|
---|
| 997 |
|
---|
| 998 | /**
|
---|
| 999 | * @interface_method_impl{PDMRTCREG,pfnWrite}
|
---|
| 1000 | */
|
---|
| 1001 | static DECLCALLBACK(int) rtcCMOSWrite(PPDMDEVINS pDevIns, unsigned iReg, uint8_t u8Value)
|
---|
| 1002 | {
|
---|
[81591] | 1003 | PRTCSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PRTCSTATE);
|
---|
[80531] | 1004 | Assert(PDMDevHlpCritSectIsOwner(pDevIns, pDevIns->pCritSectRoR3));
|
---|
| 1005 | if (iReg < RT_ELEMENTS(pThis->cmos_data))
|
---|
| 1006 | {
|
---|
| 1007 | pThis->cmos_data[iReg] = u8Value;
|
---|
| 1008 |
|
---|
| 1009 | /* does it require checksum update? */
|
---|
| 1010 | if ( iReg >= RTC_CRC_START
|
---|
| 1011 | && iReg <= RTC_CRC_LAST)
|
---|
| 1012 | rtcCalcCRC(pThis);
|
---|
| 1013 |
|
---|
| 1014 | return VINF_SUCCESS;
|
---|
| 1015 | }
|
---|
| 1016 |
|
---|
| 1017 | AssertMsgFailed(("iReg=%d\n", iReg));
|
---|
| 1018 | return VERR_INVALID_PARAMETER;
|
---|
| 1019 | }
|
---|
| 1020 |
|
---|
| 1021 |
|
---|
| 1022 | /**
|
---|
| 1023 | * @interface_method_impl{PDMRTCREG,pfnRead}
|
---|
| 1024 | */
|
---|
| 1025 | static DECLCALLBACK(int) rtcCMOSRead(PPDMDEVINS pDevIns, unsigned iReg, uint8_t *pu8Value)
|
---|
| 1026 | {
|
---|
[81591] | 1027 | PRTCSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PRTCSTATE);
|
---|
[80531] | 1028 | Assert(PDMDevHlpCritSectIsOwner(pDevIns, pDevIns->pCritSectRoR3));
|
---|
| 1029 |
|
---|
| 1030 | if (iReg < RT_ELEMENTS(pThis->cmos_data))
|
---|
| 1031 | {
|
---|
| 1032 | *pu8Value = pThis->cmos_data[iReg];
|
---|
| 1033 | return VINF_SUCCESS;
|
---|
| 1034 | }
|
---|
| 1035 | AssertMsgFailed(("iReg=%d\n", iReg));
|
---|
| 1036 | return VERR_INVALID_PARAMETER;
|
---|
| 1037 | }
|
---|
| 1038 |
|
---|
| 1039 |
|
---|
| 1040 | /**
|
---|
| 1041 | * @interface_method_impl{PDMIHPETLEGACYNOTIFY,pfnModeChanged}
|
---|
| 1042 | */
|
---|
| 1043 | static DECLCALLBACK(void) rtcHpetLegacyNotify_ModeChanged(PPDMIHPETLEGACYNOTIFY pInterface, bool fActivated)
|
---|
| 1044 | {
|
---|
| 1045 | PRTCSTATECC pThisCC = RT_FROM_MEMBER(pInterface, RTCSTATER3, IHpetLegacyNotify);
|
---|
[90447] | 1046 | PPDMDEVINS pDevIns = pThisCC->pDevInsR3;
|
---|
| 1047 | int const rcLock = PDMDevHlpCritSectEnter(pDevIns, pDevIns->pCritSectRoR3, VERR_IGNORED);
|
---|
| 1048 | PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, pDevIns->pCritSectRoR3, rcLock);
|
---|
[80531] | 1049 |
|
---|
| 1050 | pThisCC->pShared->fDisabledByHpet = fActivated;
|
---|
| 1051 |
|
---|
| 1052 | PDMDevHlpCritSectLeave(pDevIns, pDevIns->pCritSectRoR3);
|
---|
| 1053 | }
|
---|
| 1054 |
|
---|
| 1055 |
|
---|
| 1056 |
|
---|
| 1057 | /* -=-=-=-=-=- IBase -=-=-=-=-=- */
|
---|
| 1058 |
|
---|
| 1059 | /**
|
---|
| 1060 | * @interface_method_impl{PDMIBASE,pfnQueryInterface}
|
---|
| 1061 | */
|
---|
| 1062 | static DECLCALLBACK(void *) rtcQueryInterface(PPDMIBASE pInterface, const char *pszIID)
|
---|
| 1063 | {
|
---|
| 1064 | PPDMDEVINS pDevIns = RT_FROM_MEMBER(pInterface, PDMDEVINS, IBase);
|
---|
[81591] | 1065 | PRTCSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PRTCSTATECC);
|
---|
[80531] | 1066 | PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDevIns->IBase);
|
---|
| 1067 | PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHPETLEGACYNOTIFY, &pThisCC->IHpetLegacyNotify);
|
---|
| 1068 | return NULL;
|
---|
| 1069 | }
|
---|
| 1070 |
|
---|
| 1071 |
|
---|
| 1072 | /* -=-=-=-=-=- PDMDEVREG -=-=-=-=-=- */
|
---|
| 1073 |
|
---|
| 1074 | static void rtc_set_memory(PRTCSTATE pThis, int addr, int val)
|
---|
| 1075 | {
|
---|
| 1076 | if (addr >= 0 && addr <= 127)
|
---|
| 1077 | pThis->cmos_data[addr] = val;
|
---|
| 1078 | }
|
---|
| 1079 |
|
---|
| 1080 |
|
---|
| 1081 | static void rtc_set_date(PRTCSTATE pThis, const struct my_tm *tm)
|
---|
| 1082 | {
|
---|
| 1083 | pThis->current_tm = *tm;
|
---|
| 1084 | rtc_copy_date(pThis);
|
---|
| 1085 | }
|
---|
| 1086 |
|
---|
| 1087 |
|
---|
| 1088 | /**
|
---|
| 1089 | * @interface_method_impl{PDMDEVREG,pfnInitComplete}
|
---|
| 1090 | *
|
---|
| 1091 | * Used to set the clock.
|
---|
| 1092 | */
|
---|
| 1093 | static DECLCALLBACK(int) rtcInitComplete(PPDMDEVINS pDevIns)
|
---|
| 1094 | {
|
---|
| 1095 | /** @todo this should be (re)done at power on if we didn't load a state... */
|
---|
[81591] | 1096 | PRTCSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PRTCSTATE);
|
---|
[80531] | 1097 |
|
---|
| 1098 | /*
|
---|
| 1099 | * Set the CMOS date/time.
|
---|
| 1100 | */
|
---|
| 1101 | RTTIMESPEC Now;
|
---|
| 1102 | PDMDevHlpTMUtcNow(pDevIns, &Now);
|
---|
| 1103 | RTTIME Time;
|
---|
| 1104 | if (pThis->fUTC)
|
---|
| 1105 | RTTimeExplode(&Time, &Now);
|
---|
| 1106 | else
|
---|
| 1107 | RTTimeLocalExplode(&Time, &Now);
|
---|
| 1108 |
|
---|
| 1109 | struct my_tm Tm;
|
---|
| 1110 | memset(&Tm, 0, sizeof(Tm));
|
---|
| 1111 | Tm.tm_year = Time.i32Year - 1900;
|
---|
| 1112 | Tm.tm_mon = Time.u8Month - 1;
|
---|
| 1113 | Tm.tm_mday = Time.u8MonthDay;
|
---|
| 1114 | Tm.tm_wday = (Time.u8WeekDay + 1 + 7) % 7; /* 0 = Monday -> Sunday */
|
---|
| 1115 | Tm.tm_yday = Time.u16YearDay - 1;
|
---|
| 1116 | Tm.tm_hour = Time.u8Hour;
|
---|
| 1117 | Tm.tm_min = Time.u8Minute;
|
---|
| 1118 | Tm.tm_sec = Time.u8Second;
|
---|
| 1119 |
|
---|
| 1120 | rtc_set_date(pThis, &Tm);
|
---|
| 1121 |
|
---|
| 1122 | int iYear = to_bcd(pThis, (Tm.tm_year / 100) + 19); /* tm_year is 1900 based */
|
---|
| 1123 | rtc_set_memory(pThis, 0x32, iYear); /* 32h - Century Byte (BCD value for the century */
|
---|
| 1124 | rtc_set_memory(pThis, 0x37, iYear); /* 37h - (IBM PS/2) Date Century Byte */
|
---|
| 1125 |
|
---|
| 1126 | /*
|
---|
| 1127 | * Recalculate the checksum just in case.
|
---|
| 1128 | */
|
---|
| 1129 | rtcCalcCRC(pThis);
|
---|
| 1130 |
|
---|
| 1131 | Log(("CMOS bank 0: \n%16.128Rhxd\n", &pThis->cmos_data[0]));
|
---|
| 1132 | Log(("CMOS bank 1: \n%16.128Rhxd\n", &pThis->cmos_data[CMOS_BANK_SIZE]));
|
---|
| 1133 | return VINF_SUCCESS;
|
---|
| 1134 | }
|
---|
| 1135 |
|
---|
| 1136 |
|
---|
| 1137 | /**
|
---|
| 1138 | * @interface_method_impl{PDMDEVREG,pfnReset}
|
---|
| 1139 | */
|
---|
| 1140 | static DECLCALLBACK(void) rtcReset(PPDMDEVINS pDevIns)
|
---|
| 1141 | {
|
---|
[81591] | 1142 | PRTCSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PRTCSTATE);
|
---|
[80531] | 1143 |
|
---|
| 1144 | /* Reset index values (important for second bank). */
|
---|
| 1145 | pThis->cmos_index[0] = 0;
|
---|
| 1146 | pThis->cmos_index[1] = CMOS_BANK_SIZE; /* Point to start of second bank. */
|
---|
| 1147 | }
|
---|
| 1148 |
|
---|
| 1149 |
|
---|
| 1150 | /**
|
---|
| 1151 | * @interface_method_impl{PDMDEVREG,pfnConstruct}
|
---|
| 1152 | */
|
---|
| 1153 | static DECLCALLBACK(int) rtcConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
|
---|
| 1154 | {
|
---|
| 1155 | PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
|
---|
| 1156 | PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
|
---|
[81591] | 1157 | PRTCSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PRTCSTATE);
|
---|
| 1158 | PRTCSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PRTCSTATECC);
|
---|
[80531] | 1159 | int rc;
|
---|
| 1160 | Assert(iInstance == 0); RT_NOREF(iInstance);
|
---|
| 1161 |
|
---|
| 1162 | /*
|
---|
| 1163 | * Validate configuration.
|
---|
| 1164 | */
|
---|
| 1165 | PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, "Irq|Base|UseUTC", "");
|
---|
| 1166 |
|
---|
| 1167 | /*
|
---|
| 1168 | * Init the data.
|
---|
| 1169 | */
|
---|
| 1170 | uint8_t u8Irq;
|
---|
| 1171 | rc = pHlp->pfnCFGMQueryU8Def(pCfg, "Irq", &u8Irq, 8);
|
---|
| 1172 | if (RT_FAILURE(rc))
|
---|
| 1173 | return PDMDEV_SET_ERROR(pDevIns, rc,
|
---|
| 1174 | N_("Configuration error: Querying \"Irq\" as a uint8_t failed"));
|
---|
| 1175 | pThis->irq = u8Irq;
|
---|
| 1176 |
|
---|
| 1177 | rc = pHlp->pfnCFGMQueryPortDef(pCfg, "Base", &pThis->IOPortBase, 0x70);
|
---|
| 1178 | if (RT_FAILURE(rc))
|
---|
| 1179 | return PDMDEV_SET_ERROR(pDevIns, rc,
|
---|
| 1180 | N_("Configuration error: Querying \"Base\" as a RTIOPORT failed"));
|
---|
| 1181 |
|
---|
| 1182 | rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "UseUTC", &pThis->fUTC, false);
|
---|
| 1183 | if (RT_FAILURE(rc))
|
---|
| 1184 | return PDMDEV_SET_ERROR(pDevIns, rc,
|
---|
| 1185 | N_("Configuration error: Querying \"UseUTC\" as a bool failed"));
|
---|
| 1186 |
|
---|
| 1187 | Log(("RTC: Irq=%#x Base=%#x fR0Enabled=%RTbool fRCEnabled=%RTbool\n",
|
---|
| 1188 | u8Irq, pThis->IOPortBase, pDevIns->fR0Enabled, pDevIns->fRCEnabled));
|
---|
| 1189 |
|
---|
| 1190 |
|
---|
| 1191 | pThis->cmos_data[RTC_REG_A] = 0x26;
|
---|
| 1192 | pThis->cmos_data[RTC_REG_B] = 0x02;
|
---|
| 1193 | pThis->cmos_data[RTC_REG_C] = 0x00;
|
---|
| 1194 | pThis->cmos_data[RTC_REG_D] = 0x80;
|
---|
| 1195 | pThis->fDisabledByHpet = false;
|
---|
| 1196 | pThis->cmos_index[1] = CMOS_BANK_SIZE; /* Point to start of second bank. */
|
---|
| 1197 |
|
---|
| 1198 | pThisCC->pDevInsR3 = pDevIns;
|
---|
| 1199 | pThisCC->RtcReg.u32Version = PDM_RTCREG_VERSION;
|
---|
| 1200 | pThisCC->RtcReg.pfnRead = rtcCMOSRead;
|
---|
| 1201 | pThisCC->RtcReg.pfnWrite = rtcCMOSWrite;
|
---|
| 1202 | pThisCC->IHpetLegacyNotify.pfnModeChanged = rtcHpetLegacyNotify_ModeChanged;
|
---|
| 1203 |
|
---|
| 1204 | /* IBase */
|
---|
| 1205 | pDevIns->IBase.pfnQueryInterface = rtcQueryInterface;
|
---|
| 1206 |
|
---|
| 1207 | /*
|
---|
| 1208 | * Create timers.
|
---|
| 1209 | */
|
---|
| 1210 | /* Periodic timer. */
|
---|
| 1211 | rc = PDMDevHlpTimerCreate(pDevIns, TMCLOCK_VIRTUAL_SYNC, rtcTimerPeriodic, pThis,
|
---|
[87760] | 1212 | TMTIMER_FLAGS_DEFAULT_CRIT_SECT | TMTIMER_FLAGS_RING0,
|
---|
[87773] | 1213 | "MC146818 RTC Periodic", &pThis->hPeriodicTimer);
|
---|
[80531] | 1214 | AssertRCReturn(rc, rc);
|
---|
| 1215 |
|
---|
| 1216 | /* Seconds timer. */
|
---|
[87760] | 1217 | rc = PDMDevHlpTimerCreate(pDevIns, TMCLOCK_VIRTUAL_SYNC, rtcR3TimerSecond, pThis,
|
---|
| 1218 | TMTIMER_FLAGS_DEFAULT_CRIT_SECT | TMTIMER_FLAGS_RING0,
|
---|
[87773] | 1219 | "MC146818 RTC Second", &pThis->hSecondTimer);
|
---|
[80531] | 1220 | AssertRCReturn(rc, rc);
|
---|
| 1221 |
|
---|
| 1222 | /* The second2 timer, this is always active. */
|
---|
[87760] | 1223 | rc = PDMDevHlpTimerCreate(pDevIns, TMCLOCK_VIRTUAL_SYNC, rtcR3TimerSecond2, pThis,
|
---|
| 1224 | TMTIMER_FLAGS_DEFAULT_CRIT_SECT | TMTIMER_FLAGS_NO_RING0,
|
---|
[87773] | 1225 | "MC146818 RTC Second2", &pThis->hSecondTimer2);
|
---|
[80531] | 1226 | AssertRCReturn(rc, rc);
|
---|
| 1227 |
|
---|
| 1228 | pThis->next_second_time = PDMDevHlpTimerGet(pDevIns, pThis->hSecondTimer2)
|
---|
| 1229 | + (PDMDevHlpTimerGetFreq(pDevIns, pThis->hSecondTimer2) * 99) / 100;
|
---|
[82329] | 1230 | PDMDevHlpTimerLockClock(pDevIns, pThis->hSecondTimer2, VERR_IGNORED);
|
---|
[80531] | 1231 | rc = PDMDevHlpTimerSet(pDevIns, pThis->hSecondTimer2, pThis->next_second_time);
|
---|
[82329] | 1232 | PDMDevHlpTimerUnlockClock(pDevIns, pThis->hSecondTimer2);
|
---|
[80531] | 1233 | AssertRCReturn(rc, rc);
|
---|
| 1234 |
|
---|
| 1235 | /*
|
---|
| 1236 | * Register I/O ports.
|
---|
| 1237 | */
|
---|
[80679] | 1238 | static const IOMIOPORTDESC g_aIoPortDescs[] =
|
---|
| 1239 | {
|
---|
| 1240 | { NULL, "ADDR - CMOS Bank #1", NULL, NULL },
|
---|
| 1241 | { "DATA - CMOS Bank #1", "DATA - CMOS Bank #1", NULL, NULL },
|
---|
| 1242 | { NULL, "ADDR - CMOS Bank #2", NULL, NULL },
|
---|
| 1243 | { "DATA - CMOS Bank #2", "DATA - CMOS Bank #2", NULL, NULL },
|
---|
| 1244 | { NULL, NULL, NULL, NULL }
|
---|
| 1245 | };
|
---|
[80643] | 1246 | rc = PDMDevHlpIoPortCreateAndMap(pDevIns, pThis->IOPortBase, 4, rtcIOPortWrite, rtcIOPortRead,
|
---|
[80679] | 1247 | "MC146818 RTC/CMOS", g_aIoPortDescs, &pThis->hIoPorts);
|
---|
[80531] | 1248 | AssertRCReturn(rc, rc);
|
---|
| 1249 |
|
---|
| 1250 | /*
|
---|
| 1251 | * Register the saved state.
|
---|
| 1252 | */
|
---|
| 1253 | rc = PDMDevHlpSSMRegister3(pDevIns, RTC_SAVED_STATE_VERSION, sizeof(*pThis), rtcLiveExec, rtcSaveExec, rtcLoadExec);
|
---|
| 1254 | AssertRCReturn(rc, rc);
|
---|
| 1255 |
|
---|
| 1256 | /*
|
---|
| 1257 | * Register ourselves as the RTC/CMOS with PDM.
|
---|
| 1258 | */
|
---|
| 1259 | rc = PDMDevHlpRTCRegister(pDevIns, &pThisCC->RtcReg, &pThisCC->pRtcHlpR3);
|
---|
| 1260 | AssertRCReturn(rc, rc);
|
---|
| 1261 |
|
---|
| 1262 | /*
|
---|
| 1263 | * Register debugger info callback.
|
---|
| 1264 | */
|
---|
| 1265 | PDMDevHlpDBGFInfoRegister(pDevIns, "cmos1", "Display CMOS Bank 1 Info (0x0e-0x7f). No arguments. See also rtc.", rtcCmosBankInfo);
|
---|
| 1266 | PDMDevHlpDBGFInfoRegister(pDevIns, "cmos2", "Display CMOS Bank 2 Info (0x0e-0x7f). No arguments.", rtcCmosBank2Info);
|
---|
| 1267 | PDMDevHlpDBGFInfoRegister(pDevIns, "rtc", "Display CMOS RTC (0x00-0x0d). No arguments. See also cmos1 & cmos2", rtcCmosClockInfo);
|
---|
| 1268 |
|
---|
| 1269 | /*
|
---|
| 1270 | * Register statistics.
|
---|
| 1271 | */
|
---|
[89735] | 1272 | PDMDevHlpSTAMRegister(pDevIns, &pThis->StatRTCIrq, STAMTYPE_COUNTER, "Irq", STAMUNIT_OCCURENCES, "The number of times a RTC interrupt was triggered.");
|
---|
| 1273 | PDMDevHlpSTAMRegister(pDevIns, &pThis->StatRTCTimerCB, STAMTYPE_COUNTER, "TimerCB", STAMUNIT_OCCURENCES, "The number of times the RTC timer callback ran.");
|
---|
| 1274 | PDMDevHlpSTAMRegister(pDevIns, &pThis->StatRTCPieFlip, STAMTYPE_COUNTER, "PieFlip", STAMUNIT_OCCURENCES, "The number of times Periodic Interrupt Enable changed.");
|
---|
| 1275 | PDMDevHlpSTAMRegister(pDevIns, &pThis->StatRTCIrqClear, STAMTYPE_COUNTER, "IrqClear", STAMUNIT_OCCURENCES, "The number of times an active interrupt was cleared.");
|
---|
| 1276 | PDMDevHlpSTAMRegister(pDevIns, &pThis->StatPIrqPending, STAMTYPE_PROFILE, "PiActive", STAMUNIT_TICKS_PER_CALL, "How long periodic interrupt stays active (pending).");
|
---|
[80531] | 1277 |
|
---|
| 1278 | return VINF_SUCCESS;
|
---|
| 1279 | }
|
---|
| 1280 |
|
---|
| 1281 | #else /* !IN_RING3 */
|
---|
| 1282 |
|
---|
[81377] | 1283 | /**
|
---|
| 1284 | * @callback_method_impl{PDMDEVREGR0,pfnConstruct}
|
---|
| 1285 | */
|
---|
[80531] | 1286 | static DECLCALLBACK(int) rtcRZConstruct(PPDMDEVINS pDevIns)
|
---|
| 1287 | {
|
---|
[81472] | 1288 | PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
|
---|
[81896] | 1289 | PRTCSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PRTCSTATE);
|
---|
[80531] | 1290 |
|
---|
| 1291 | int rc = PDMDevHlpIoPortSetUpContext(pDevIns, pThis->hIoPorts, rtcIOPortWrite, rtcIOPortRead, NULL /*pvUser*/);
|
---|
| 1292 | AssertRCReturn(rc, rc);
|
---|
| 1293 |
|
---|
| 1294 | return VINF_SUCCESS;
|
---|
| 1295 | }
|
---|
| 1296 |
|
---|
| 1297 | #endif /* !IN_RING3 */
|
---|
| 1298 |
|
---|
| 1299 | /**
|
---|
| 1300 | * The device registration structure.
|
---|
| 1301 | */
|
---|
| 1302 | const PDMDEVREG g_DeviceMC146818 =
|
---|
| 1303 | {
|
---|
| 1304 | /* .u32Version = */ PDM_DEVREG_VERSION,
|
---|
| 1305 | /* .uReserved0 = */ 0,
|
---|
| 1306 | /* .szName = */ "mc146818",
|
---|
[81514] | 1307 | /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RZ | PDM_DEVREG_FLAGS_NEW_STYLE,
|
---|
[80531] | 1308 | /* .fClass = */ PDM_DEVREG_CLASS_RTC,
|
---|
| 1309 | /* .cMaxInstances = */ 1,
|
---|
| 1310 | /* .uSharedVersion = */ 1,
|
---|
| 1311 | /* .cbInstanceShared = */ sizeof(RTCSTATE),
|
---|
| 1312 | /* .cbInstanceCC = */ sizeof(RTCSTATECC),
|
---|
| 1313 | /* .cbInstanceRC = */ sizeof(RTCSTATERC),
|
---|
[80703] | 1314 | /* .cMaxPciDevices = */ 0,
|
---|
[80704] | 1315 | /* .cMaxMsixVectors = */ 0,
|
---|
[80531] | 1316 | /* .pszDescription = */ "Motorola MC146818 RTC/CMOS Device.",
|
---|
| 1317 | #ifdef IN_RING3
|
---|
| 1318 | /* .pszRCMod = */ "VBoxDDRC.rc",
|
---|
| 1319 | /* .pszR0Mod = */ "VBoxDDR0.r0",
|
---|
| 1320 | /* .pfnConstruct = */ rtcConstruct,
|
---|
| 1321 | /* .pfnDestruct = */ NULL,
|
---|
[81896] | 1322 | /* .pfnRelocate = */ NULL,
|
---|
[80531] | 1323 | /* .pfnMemSetup = */ NULL,
|
---|
| 1324 | /* .pfnPowerOn = */ NULL,
|
---|
| 1325 | /* .pfnReset = */ rtcReset,
|
---|
| 1326 | /* .pfnSuspend = */ NULL,
|
---|
| 1327 | /* .pfnResume = */ NULL,
|
---|
| 1328 | /* .pfnAttach = */ NULL,
|
---|
| 1329 | /* .pfnDetach = */ NULL,
|
---|
| 1330 | /* .pfnQueryInterface = */ NULL,
|
---|
| 1331 | /* .pfnInitComplete = */ rtcInitComplete,
|
---|
| 1332 | /* .pfnPowerOff = */ NULL,
|
---|
| 1333 | /* .pfnSoftReset = */ NULL,
|
---|
| 1334 | /* .pfnReserved0 = */ NULL,
|
---|
| 1335 | /* .pfnReserved1 = */ NULL,
|
---|
| 1336 | /* .pfnReserved2 = */ NULL,
|
---|
| 1337 | /* .pfnReserved3 = */ NULL,
|
---|
| 1338 | /* .pfnReserved4 = */ NULL,
|
---|
| 1339 | /* .pfnReserved5 = */ NULL,
|
---|
| 1340 | /* .pfnReserved6 = */ NULL,
|
---|
| 1341 | /* .pfnReserved7 = */ NULL,
|
---|
| 1342 | #elif defined(IN_RING0)
|
---|
| 1343 | /* .pfnEarlyConstruct = */ NULL,
|
---|
| 1344 | /* .pfnConstruct = */ rtcRZConstruct,
|
---|
| 1345 | /* .pfnDestruct = */ NULL,
|
---|
| 1346 | /* .pfnFinalDestruct = */ NULL,
|
---|
| 1347 | /* .pfnRequest = */ NULL,
|
---|
| 1348 | /* .pfnReserved0 = */ NULL,
|
---|
| 1349 | /* .pfnReserved1 = */ NULL,
|
---|
| 1350 | /* .pfnReserved2 = */ NULL,
|
---|
| 1351 | /* .pfnReserved3 = */ NULL,
|
---|
| 1352 | /* .pfnReserved4 = */ NULL,
|
---|
| 1353 | /* .pfnReserved5 = */ NULL,
|
---|
| 1354 | /* .pfnReserved6 = */ NULL,
|
---|
| 1355 | /* .pfnReserved7 = */ NULL,
|
---|
| 1356 | #elif defined(IN_RC)
|
---|
| 1357 | /* .pfnConstruct = */ rtcRZConstruct,
|
---|
| 1358 | /* .pfnReserved0 = */ NULL,
|
---|
| 1359 | /* .pfnReserved1 = */ NULL,
|
---|
| 1360 | /* .pfnReserved2 = */ NULL,
|
---|
| 1361 | /* .pfnReserved3 = */ NULL,
|
---|
| 1362 | /* .pfnReserved4 = */ NULL,
|
---|
| 1363 | /* .pfnReserved5 = */ NULL,
|
---|
| 1364 | /* .pfnReserved6 = */ NULL,
|
---|
| 1365 | /* .pfnReserved7 = */ NULL,
|
---|
| 1366 | #else
|
---|
| 1367 | # error "Not IN_RING3, IN_RING0 nor IN_RC!"
|
---|
| 1368 | #endif
|
---|
| 1369 | /* .u32VersionEnd = */ PDM_DEVREG_VERSION
|
---|
| 1370 | };
|
---|
| 1371 |
|
---|
| 1372 | #endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
|
---|