VirtualBox

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

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

scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 46.3 KB
Line 
1/* $Id: DevRTC.cpp 93115 2022-01-01 11:31:46Z vboxsync $ */
2/** @file
3 * Motorola MC146818 RTC/CMOS Device with PIIX4 extensions.
4 */
5
6/*
7 * Copyright (C) 2006-2022 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 rc = PDMDevHlpTimerLoad(pDevIns, pThis->hSecondTimer2, pSSM);
927 AssertRCReturn(rc, rc);
928
929 if (uVersion > RTC_SAVED_STATE_VERSION_VBOX_31)
930 {
931 rc = pHlp->pfnSSMGetBool(pSSM, &pThis->fDisabledByHpet);
932 AssertRCReturn(rc, rc);
933 }
934
935 if (uVersion > RTC_SAVED_STATE_VERSION_VBOX_32PRE)
936 {
937 /* Second CMOS bank. */
938 pHlp->pfnSSMGetMem(pSSM, &pThis->cmos_data[CMOS_BANK_SIZE], CMOS_BANK_SIZE);
939 rc = pHlp->pfnSSMGetU8(pSSM, &pThis->cmos_index[1]);
940 AssertRCReturn(rc, rc);
941 }
942
943 int period_code = pThis->cmos_data[RTC_REG_A] & 0x0f;
944 if ( period_code != 0
945 && (pThis->cmos_data[RTC_REG_B] & REG_B_PIE))
946 {
947 if (period_code <= 2)
948 period_code += 7;
949 int period = 1 << (period_code - 1);
950 LogRel(("RTC: period=%#x (%d) %u Hz (restore)\n", period, period, _32K / period));
951 rc = PDMDevHlpCritSectEnter(pDevIns, pDevIns->pCritSectRoR3, VINF_SUCCESS);
952 AssertRCReturn(rc, rc);
953 PDMDevHlpTimerSetFrequencyHint(pDevIns, pThis->hPeriodicTimer, _32K / period);
954 PDMDevHlpCritSectLeave(pDevIns, pDevIns->pCritSectRoR3);
955 pThis->CurLogPeriod = period;
956 pThis->CurHintPeriod = period;
957 }
958 else
959 {
960 LogRel(("RTC: Stopped the periodic timer (restore)\n"));
961 pThis->CurLogPeriod = 0;
962 pThis->CurHintPeriod = 0;
963 }
964 pThis->cRelLogEntries = 0;
965
966 return VINF_SUCCESS;
967}
968
969
970/* -=-=-=-=-=- PDM Interface provided by the RTC device -=-=-=-=-=- */
971
972/**
973 * Calculate and update the standard CMOS checksum.
974 *
975 * @param pThis Pointer to the RTC state data.
976 */
977static void rtcCalcCRC(PRTCSTATE pThis)
978{
979 uint16_t u16 = 0;
980 for (unsigned i = RTC_CRC_START; i <= RTC_CRC_LAST; i++)
981 u16 += pThis->cmos_data[i];
982
983 pThis->cmos_data[RTC_CRC_LOW] = u16 & 0xff;
984 pThis->cmos_data[RTC_CRC_HIGH] = (u16 >> 8) & 0xff;
985}
986
987
988/**
989 * @interface_method_impl{PDMRTCREG,pfnWrite}
990 */
991static DECLCALLBACK(int) rtcCMOSWrite(PPDMDEVINS pDevIns, unsigned iReg, uint8_t u8Value)
992{
993 PRTCSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PRTCSTATE);
994 Assert(PDMDevHlpCritSectIsOwner(pDevIns, pDevIns->pCritSectRoR3));
995 if (iReg < RT_ELEMENTS(pThis->cmos_data))
996 {
997 pThis->cmos_data[iReg] = u8Value;
998
999 /* does it require checksum update? */
1000 if ( iReg >= RTC_CRC_START
1001 && iReg <= RTC_CRC_LAST)
1002 rtcCalcCRC(pThis);
1003
1004 return VINF_SUCCESS;
1005 }
1006
1007 AssertMsgFailed(("iReg=%d\n", iReg));
1008 return VERR_INVALID_PARAMETER;
1009}
1010
1011
1012/**
1013 * @interface_method_impl{PDMRTCREG,pfnRead}
1014 */
1015static DECLCALLBACK(int) rtcCMOSRead(PPDMDEVINS pDevIns, unsigned iReg, uint8_t *pu8Value)
1016{
1017 PRTCSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PRTCSTATE);
1018 Assert(PDMDevHlpCritSectIsOwner(pDevIns, pDevIns->pCritSectRoR3));
1019
1020 if (iReg < RT_ELEMENTS(pThis->cmos_data))
1021 {
1022 *pu8Value = pThis->cmos_data[iReg];
1023 return VINF_SUCCESS;
1024 }
1025 AssertMsgFailed(("iReg=%d\n", iReg));
1026 return VERR_INVALID_PARAMETER;
1027}
1028
1029
1030/**
1031 * @interface_method_impl{PDMIHPETLEGACYNOTIFY,pfnModeChanged}
1032 */
1033static DECLCALLBACK(void) rtcHpetLegacyNotify_ModeChanged(PPDMIHPETLEGACYNOTIFY pInterface, bool fActivated)
1034{
1035 PRTCSTATECC pThisCC = RT_FROM_MEMBER(pInterface, RTCSTATER3, IHpetLegacyNotify);
1036 PPDMDEVINS pDevIns = pThisCC->pDevInsR3;
1037 int const rcLock = PDMDevHlpCritSectEnter(pDevIns, pDevIns->pCritSectRoR3, VERR_IGNORED);
1038 PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, pDevIns->pCritSectRoR3, rcLock);
1039
1040 pThisCC->pShared->fDisabledByHpet = fActivated;
1041
1042 PDMDevHlpCritSectLeave(pDevIns, pDevIns->pCritSectRoR3);
1043}
1044
1045
1046
1047/* -=-=-=-=-=- IBase -=-=-=-=-=- */
1048
1049/**
1050 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
1051 */
1052static DECLCALLBACK(void *) rtcQueryInterface(PPDMIBASE pInterface, const char *pszIID)
1053{
1054 PPDMDEVINS pDevIns = RT_FROM_MEMBER(pInterface, PDMDEVINS, IBase);
1055 PRTCSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PRTCSTATECC);
1056 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDevIns->IBase);
1057 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHPETLEGACYNOTIFY, &pThisCC->IHpetLegacyNotify);
1058 return NULL;
1059}
1060
1061
1062/* -=-=-=-=-=- PDMDEVREG -=-=-=-=-=- */
1063
1064static void rtc_set_memory(PRTCSTATE pThis, int addr, int val)
1065{
1066 if (addr >= 0 && addr <= 127)
1067 pThis->cmos_data[addr] = val;
1068}
1069
1070
1071static void rtc_set_date(PRTCSTATE pThis, const struct my_tm *tm)
1072{
1073 pThis->current_tm = *tm;
1074 rtc_copy_date(pThis);
1075}
1076
1077
1078/**
1079 * @interface_method_impl{PDMDEVREG,pfnInitComplete}
1080 *
1081 * Used to set the clock.
1082 */
1083static DECLCALLBACK(int) rtcInitComplete(PPDMDEVINS pDevIns)
1084{
1085 /** @todo this should be (re)done at power on if we didn't load a state... */
1086 PRTCSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PRTCSTATE);
1087
1088 /*
1089 * Set the CMOS date/time.
1090 */
1091 RTTIMESPEC Now;
1092 PDMDevHlpTMUtcNow(pDevIns, &Now);
1093 RTTIME Time;
1094 if (pThis->fUTC)
1095 RTTimeExplode(&Time, &Now);
1096 else
1097 RTTimeLocalExplode(&Time, &Now);
1098
1099 struct my_tm Tm;
1100 memset(&Tm, 0, sizeof(Tm));
1101 Tm.tm_year = Time.i32Year - 1900;
1102 Tm.tm_mon = Time.u8Month - 1;
1103 Tm.tm_mday = Time.u8MonthDay;
1104 Tm.tm_wday = (Time.u8WeekDay + 1 + 7) % 7; /* 0 = Monday -> Sunday */
1105 Tm.tm_yday = Time.u16YearDay - 1;
1106 Tm.tm_hour = Time.u8Hour;
1107 Tm.tm_min = Time.u8Minute;
1108 Tm.tm_sec = Time.u8Second;
1109
1110 rtc_set_date(pThis, &Tm);
1111
1112 int iYear = to_bcd(pThis, (Tm.tm_year / 100) + 19); /* tm_year is 1900 based */
1113 rtc_set_memory(pThis, 0x32, iYear); /* 32h - Century Byte (BCD value for the century */
1114 rtc_set_memory(pThis, 0x37, iYear); /* 37h - (IBM PS/2) Date Century Byte */
1115
1116 /*
1117 * Recalculate the checksum just in case.
1118 */
1119 rtcCalcCRC(pThis);
1120
1121 Log(("CMOS bank 0: \n%16.128Rhxd\n", &pThis->cmos_data[0]));
1122 Log(("CMOS bank 1: \n%16.128Rhxd\n", &pThis->cmos_data[CMOS_BANK_SIZE]));
1123 return VINF_SUCCESS;
1124}
1125
1126
1127/**
1128 * @interface_method_impl{PDMDEVREG,pfnReset}
1129 */
1130static DECLCALLBACK(void) rtcReset(PPDMDEVINS pDevIns)
1131{
1132 PRTCSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PRTCSTATE);
1133
1134 /* Reset index values (important for second bank). */
1135 pThis->cmos_index[0] = 0;
1136 pThis->cmos_index[1] = CMOS_BANK_SIZE; /* Point to start of second bank. */
1137}
1138
1139
1140/**
1141 * @interface_method_impl{PDMDEVREG,pfnConstruct}
1142 */
1143static DECLCALLBACK(int) rtcConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
1144{
1145 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
1146 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
1147 PRTCSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PRTCSTATE);
1148 PRTCSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PRTCSTATECC);
1149 int rc;
1150 Assert(iInstance == 0); RT_NOREF(iInstance);
1151
1152 /*
1153 * Validate configuration.
1154 */
1155 PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, "Irq|Base|UseUTC", "");
1156
1157 /*
1158 * Init the data.
1159 */
1160 uint8_t u8Irq;
1161 rc = pHlp->pfnCFGMQueryU8Def(pCfg, "Irq", &u8Irq, 8);
1162 if (RT_FAILURE(rc))
1163 return PDMDEV_SET_ERROR(pDevIns, rc,
1164 N_("Configuration error: Querying \"Irq\" as a uint8_t failed"));
1165 pThis->irq = u8Irq;
1166
1167 rc = pHlp->pfnCFGMQueryPortDef(pCfg, "Base", &pThis->IOPortBase, 0x70);
1168 if (RT_FAILURE(rc))
1169 return PDMDEV_SET_ERROR(pDevIns, rc,
1170 N_("Configuration error: Querying \"Base\" as a RTIOPORT failed"));
1171
1172 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "UseUTC", &pThis->fUTC, false);
1173 if (RT_FAILURE(rc))
1174 return PDMDEV_SET_ERROR(pDevIns, rc,
1175 N_("Configuration error: Querying \"UseUTC\" as a bool failed"));
1176
1177 Log(("RTC: Irq=%#x Base=%#x fR0Enabled=%RTbool fRCEnabled=%RTbool\n",
1178 u8Irq, pThis->IOPortBase, pDevIns->fR0Enabled, pDevIns->fRCEnabled));
1179
1180
1181 pThis->cmos_data[RTC_REG_A] = 0x26;
1182 pThis->cmos_data[RTC_REG_B] = 0x02;
1183 pThis->cmos_data[RTC_REG_C] = 0x00;
1184 pThis->cmos_data[RTC_REG_D] = 0x80;
1185 pThis->fDisabledByHpet = false;
1186 pThis->cmos_index[1] = CMOS_BANK_SIZE; /* Point to start of second bank. */
1187
1188 pThisCC->pDevInsR3 = pDevIns;
1189 pThisCC->RtcReg.u32Version = PDM_RTCREG_VERSION;
1190 pThisCC->RtcReg.pfnRead = rtcCMOSRead;
1191 pThisCC->RtcReg.pfnWrite = rtcCMOSWrite;
1192 pThisCC->IHpetLegacyNotify.pfnModeChanged = rtcHpetLegacyNotify_ModeChanged;
1193
1194 /* IBase */
1195 pDevIns->IBase.pfnQueryInterface = rtcQueryInterface;
1196
1197 /*
1198 * Create timers.
1199 */
1200 /* Periodic timer. */
1201 rc = PDMDevHlpTimerCreate(pDevIns, TMCLOCK_VIRTUAL_SYNC, rtcTimerPeriodic, pThis,
1202 TMTIMER_FLAGS_DEFAULT_CRIT_SECT | TMTIMER_FLAGS_RING0,
1203 "MC146818 RTC Periodic", &pThis->hPeriodicTimer);
1204 AssertRCReturn(rc, rc);
1205
1206 /* Seconds timer. */
1207 rc = PDMDevHlpTimerCreate(pDevIns, TMCLOCK_VIRTUAL_SYNC, rtcR3TimerSecond, pThis,
1208 TMTIMER_FLAGS_DEFAULT_CRIT_SECT | TMTIMER_FLAGS_RING0,
1209 "MC146818 RTC Second", &pThis->hSecondTimer);
1210 AssertRCReturn(rc, rc);
1211
1212 /* The second2 timer, this is always active. */
1213 rc = PDMDevHlpTimerCreate(pDevIns, TMCLOCK_VIRTUAL_SYNC, rtcR3TimerSecond2, pThis,
1214 TMTIMER_FLAGS_DEFAULT_CRIT_SECT | TMTIMER_FLAGS_NO_RING0,
1215 "MC146818 RTC Second2", &pThis->hSecondTimer2);
1216 AssertRCReturn(rc, rc);
1217
1218 pThis->next_second_time = PDMDevHlpTimerGet(pDevIns, pThis->hSecondTimer2)
1219 + (PDMDevHlpTimerGetFreq(pDevIns, pThis->hSecondTimer2) * 99) / 100;
1220 PDMDevHlpTimerLockClock(pDevIns, pThis->hSecondTimer2, VERR_IGNORED);
1221 rc = PDMDevHlpTimerSet(pDevIns, pThis->hSecondTimer2, pThis->next_second_time);
1222 PDMDevHlpTimerUnlockClock(pDevIns, pThis->hSecondTimer2);
1223 AssertRCReturn(rc, rc);
1224
1225 /*
1226 * Register I/O ports.
1227 */
1228 static const IOMIOPORTDESC g_aIoPortDescs[] =
1229 {
1230 { NULL, "ADDR - CMOS Bank #1", NULL, NULL },
1231 { "DATA - CMOS Bank #1", "DATA - CMOS Bank #1", NULL, NULL },
1232 { NULL, "ADDR - CMOS Bank #2", NULL, NULL },
1233 { "DATA - CMOS Bank #2", "DATA - CMOS Bank #2", NULL, NULL },
1234 { NULL, NULL, NULL, NULL }
1235 };
1236 rc = PDMDevHlpIoPortCreateAndMap(pDevIns, pThis->IOPortBase, 4, rtcIOPortWrite, rtcIOPortRead,
1237 "MC146818 RTC/CMOS", g_aIoPortDescs, &pThis->hIoPorts);
1238 AssertRCReturn(rc, rc);
1239
1240 /*
1241 * Register the saved state.
1242 */
1243 rc = PDMDevHlpSSMRegister3(pDevIns, RTC_SAVED_STATE_VERSION, sizeof(*pThis), rtcLiveExec, rtcSaveExec, rtcLoadExec);
1244 AssertRCReturn(rc, rc);
1245
1246 /*
1247 * Register ourselves as the RTC/CMOS with PDM.
1248 */
1249 rc = PDMDevHlpRTCRegister(pDevIns, &pThisCC->RtcReg, &pThisCC->pRtcHlpR3);
1250 AssertRCReturn(rc, rc);
1251
1252 /*
1253 * Register debugger info callback.
1254 */
1255 PDMDevHlpDBGFInfoRegister(pDevIns, "cmos1", "Display CMOS Bank 1 Info (0x0e-0x7f). No arguments. See also rtc.", rtcCmosBankInfo);
1256 PDMDevHlpDBGFInfoRegister(pDevIns, "cmos2", "Display CMOS Bank 2 Info (0x0e-0x7f). No arguments.", rtcCmosBank2Info);
1257 PDMDevHlpDBGFInfoRegister(pDevIns, "rtc", "Display CMOS RTC (0x00-0x0d). No arguments. See also cmos1 & cmos2", rtcCmosClockInfo);
1258
1259 /*
1260 * Register statistics.
1261 */
1262 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatRTCIrq, STAMTYPE_COUNTER, "Irq", STAMUNIT_OCCURENCES, "The number of times a RTC interrupt was triggered.");
1263 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatRTCTimerCB, STAMTYPE_COUNTER, "TimerCB", STAMUNIT_OCCURENCES, "The number of times the RTC timer callback ran.");
1264 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatRTCPieFlip, STAMTYPE_COUNTER, "PieFlip", STAMUNIT_OCCURENCES, "The number of times Periodic Interrupt Enable changed.");
1265 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatRTCIrqClear, STAMTYPE_COUNTER, "IrqClear", STAMUNIT_OCCURENCES, "The number of times an active interrupt was cleared.");
1266 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatPIrqPending, STAMTYPE_PROFILE, "PiActive", STAMUNIT_TICKS_PER_CALL, "How long periodic interrupt stays active (pending).");
1267
1268 return VINF_SUCCESS;
1269}
1270
1271#else /* !IN_RING3 */
1272
1273/**
1274 * @callback_method_impl{PDMDEVREGR0,pfnConstruct}
1275 */
1276static DECLCALLBACK(int) rtcRZConstruct(PPDMDEVINS pDevIns)
1277{
1278 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
1279 PRTCSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PRTCSTATE);
1280
1281 int rc = PDMDevHlpIoPortSetUpContext(pDevIns, pThis->hIoPorts, rtcIOPortWrite, rtcIOPortRead, NULL /*pvUser*/);
1282 AssertRCReturn(rc, rc);
1283
1284 return VINF_SUCCESS;
1285}
1286
1287#endif /* !IN_RING3 */
1288
1289/**
1290 * The device registration structure.
1291 */
1292const PDMDEVREG g_DeviceMC146818 =
1293{
1294 /* .u32Version = */ PDM_DEVREG_VERSION,
1295 /* .uReserved0 = */ 0,
1296 /* .szName = */ "mc146818",
1297 /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RZ | PDM_DEVREG_FLAGS_NEW_STYLE,
1298 /* .fClass = */ PDM_DEVREG_CLASS_RTC,
1299 /* .cMaxInstances = */ 1,
1300 /* .uSharedVersion = */ 1,
1301 /* .cbInstanceShared = */ sizeof(RTCSTATE),
1302 /* .cbInstanceCC = */ sizeof(RTCSTATECC),
1303 /* .cbInstanceRC = */ sizeof(RTCSTATERC),
1304 /* .cMaxPciDevices = */ 0,
1305 /* .cMaxMsixVectors = */ 0,
1306 /* .pszDescription = */ "Motorola MC146818 RTC/CMOS Device.",
1307#ifdef IN_RING3
1308 /* .pszRCMod = */ "VBoxDDRC.rc",
1309 /* .pszR0Mod = */ "VBoxDDR0.r0",
1310 /* .pfnConstruct = */ rtcConstruct,
1311 /* .pfnDestruct = */ NULL,
1312 /* .pfnRelocate = */ NULL,
1313 /* .pfnMemSetup = */ NULL,
1314 /* .pfnPowerOn = */ NULL,
1315 /* .pfnReset = */ rtcReset,
1316 /* .pfnSuspend = */ NULL,
1317 /* .pfnResume = */ NULL,
1318 /* .pfnAttach = */ NULL,
1319 /* .pfnDetach = */ NULL,
1320 /* .pfnQueryInterface = */ NULL,
1321 /* .pfnInitComplete = */ rtcInitComplete,
1322 /* .pfnPowerOff = */ NULL,
1323 /* .pfnSoftReset = */ NULL,
1324 /* .pfnReserved0 = */ NULL,
1325 /* .pfnReserved1 = */ NULL,
1326 /* .pfnReserved2 = */ NULL,
1327 /* .pfnReserved3 = */ NULL,
1328 /* .pfnReserved4 = */ NULL,
1329 /* .pfnReserved5 = */ NULL,
1330 /* .pfnReserved6 = */ NULL,
1331 /* .pfnReserved7 = */ NULL,
1332#elif defined(IN_RING0)
1333 /* .pfnEarlyConstruct = */ NULL,
1334 /* .pfnConstruct = */ rtcRZConstruct,
1335 /* .pfnDestruct = */ NULL,
1336 /* .pfnFinalDestruct = */ NULL,
1337 /* .pfnRequest = */ NULL,
1338 /* .pfnReserved0 = */ NULL,
1339 /* .pfnReserved1 = */ NULL,
1340 /* .pfnReserved2 = */ NULL,
1341 /* .pfnReserved3 = */ NULL,
1342 /* .pfnReserved4 = */ NULL,
1343 /* .pfnReserved5 = */ NULL,
1344 /* .pfnReserved6 = */ NULL,
1345 /* .pfnReserved7 = */ NULL,
1346#elif defined(IN_RC)
1347 /* .pfnConstruct = */ rtcRZConstruct,
1348 /* .pfnReserved0 = */ NULL,
1349 /* .pfnReserved1 = */ NULL,
1350 /* .pfnReserved2 = */ NULL,
1351 /* .pfnReserved3 = */ NULL,
1352 /* .pfnReserved4 = */ NULL,
1353 /* .pfnReserved5 = */ NULL,
1354 /* .pfnReserved6 = */ NULL,
1355 /* .pfnReserved7 = */ NULL,
1356#else
1357# error "Not IN_RING3, IN_RING0 nor IN_RC!"
1358#endif
1359 /* .u32VersionEnd = */ PDM_DEVREG_VERSION
1360};
1361
1362#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