VirtualBox

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

Last change on this file since 89735 was 89735, checked in by vboxsync, 3 years ago

DevRTC: Added more statistics to the RTC device, tracking interrupt behavior. Also a few more symbolic constants instead of hardcoded numbers.

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