VirtualBox

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

Last change on this file since 87766 was 87760, checked in by vboxsync, 4 years ago

VMM/TM,VMM/DevHlp: Require flag on timers that are to be used in ring-0 (and while refactoring a counte flag to check that all timers have been checked). Removed obsolete timer device helpers. bugref:9943

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

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