VirtualBox

source: vbox/trunk/src/VBox/Devices/PC/DevRTC.cpp@ 69496

Last change on this file since 69496 was 69496, checked in by vboxsync, 8 years ago

*: scm --update-copyright-year

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette