VirtualBox

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

Last change on this file since 85194 was 82968, checked in by vboxsync, 5 years ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 44.2 KB
Line 
1/* $Id: DevRTC.cpp 82968 2020-02-04 10:35:17Z 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(pTimer == PDMDevHlpTimerToPtr(pDevIns, pThis->hPeriodicTimer));
591 Assert(PDMDevHlpTimerIsLockOwner(pDevIns, pThis->hPeriodicTimer));
592 Assert(PDMDevHlpCritSectIsOwner(pDevIns, pDevIns->CTX_SUFF(pCritSectRo)));
593
594 rtc_timer_update(pDevIns, pThis, pThis->next_periodic_time);
595 STAM_COUNTER_INC(&pThis->StatRTCTimerCB);
596 pThis->cmos_data[RTC_REG_C] |= 0xc0;
597
598 rtc_raise_irq(pDevIns, pThis, 1);
599}
600
601
602/* month is between 0 and 11. */
603static int get_days_in_month(int month, int year)
604{
605 static const int days_tab[12] =
606 {
607 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
608 };
609 int d;
610
611 if ((unsigned )month >= 12)
612 return 31;
613
614 d = days_tab[month];
615 if (month == 1)
616 {
617 if ((year % 4) == 0 && ((year % 100) != 0 || (year % 400) == 0))
618 d++;
619 }
620 return d;
621}
622
623
624/* update 'tm' to the next second */
625static void rtc_next_second(struct my_tm *tm)
626{
627 int days_in_month;
628
629 tm->tm_sec++;
630 if ((unsigned)tm->tm_sec >= 60)
631 {
632 tm->tm_sec = 0;
633 tm->tm_min++;
634 if ((unsigned)tm->tm_min >= 60)
635 {
636 tm->tm_min = 0;
637 tm->tm_hour++;
638 if ((unsigned)tm->tm_hour >= 24)
639 {
640 tm->tm_hour = 0;
641 /* next day */
642 tm->tm_wday++;
643 if ((unsigned)tm->tm_wday >= 7)
644 tm->tm_wday = 0;
645 days_in_month = get_days_in_month(tm->tm_mon,
646 tm->tm_year + 1900);
647 tm->tm_mday++;
648 if (tm->tm_mday < 1)
649 tm->tm_mday = 1;
650 else if (tm->tm_mday > days_in_month)
651 {
652 tm->tm_mday = 1;
653 tm->tm_mon++;
654 if (tm->tm_mon >= 12)
655 {
656 tm->tm_mon = 0;
657 tm->tm_year++;
658 }
659 }
660 }
661 }
662 }
663}
664
665
666/**
667 * @callback_method_impl{FNTMTIMERDEV, Second timer.}
668 */
669static DECLCALLBACK(void) rtcTimerSecond(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
670{
671 PRTCSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PRTCSTATE);
672
673 Assert(PDMDevHlpTimerIsLockOwner(pDevIns, pThis->hPeriodicTimer));
674 Assert(PDMDevHlpCritSectIsOwner(pDevIns, pDevIns->CTX_SUFF(pCritSectRo)));
675 Assert(pTimer == PDMDevHlpTimerToPtr(pDevIns, pThis->hSecondTimer));
676 RT_NOREF(pvUser, pTimer);
677
678 /* if the oscillator is not in normal operation, we do not update */
679 if ((pThis->cmos_data[RTC_REG_A] & 0x70) != 0x20)
680 {
681 pThis->next_second_time += PDMDevHlpTimerGetFreq(pDevIns, pThis->hSecondTimer);
682 PDMDevHlpTimerSet(pDevIns, pThis->hSecondTimer, pThis->next_second_time);
683 }
684 else
685 {
686 rtc_next_second(&pThis->current_tm);
687
688 if (!(pThis->cmos_data[RTC_REG_B] & REG_B_SET))
689 {
690 /* update in progress bit */
691 Log2(("RTC: UIP %x -> 1\n", !!(pThis->cmos_data[RTC_REG_A] & REG_A_UIP)));
692 pThis->cmos_data[RTC_REG_A] |= REG_A_UIP;
693 }
694
695 /* 244140 ns = 8 / 32768 seconds */
696 uint64_t delay = PDMDevHlpTimerFromNano(pDevIns, pThis->hSecondTimer2, 244140);
697 PDMDevHlpTimerSet(pDevIns, pThis->hSecondTimer2, pThis->next_second_time + delay);
698 }
699}
700
701
702/* Used by rtc_set_date and rtcTimerSecond2. */
703static void rtc_copy_date(PRTCSTATE pThis)
704{
705 const struct my_tm *tm = &pThis->current_tm;
706
707 pThis->cmos_data[RTC_SECONDS] = to_bcd(pThis, tm->tm_sec);
708 pThis->cmos_data[RTC_MINUTES] = to_bcd(pThis, tm->tm_min);
709 if (pThis->cmos_data[RTC_REG_B] & 0x02)
710 {
711 /* 24 hour format */
712 pThis->cmos_data[RTC_HOURS] = to_bcd(pThis, tm->tm_hour);
713 }
714 else
715 {
716 /* 12 hour format */
717 int h = tm->tm_hour % 12;
718 pThis->cmos_data[RTC_HOURS] = to_bcd(pThis, h ? h : 12);
719 if (tm->tm_hour >= 12)
720 pThis->cmos_data[RTC_HOURS] |= 0x80;
721 }
722 pThis->cmos_data[RTC_DAY_OF_WEEK] = to_bcd(pThis, tm->tm_wday);
723 pThis->cmos_data[RTC_DAY_OF_MONTH] = to_bcd(pThis, tm->tm_mday);
724 pThis->cmos_data[RTC_MONTH] = to_bcd(pThis, tm->tm_mon + 1);
725 pThis->cmos_data[RTC_YEAR] = to_bcd(pThis, tm->tm_year % 100);
726}
727
728
729/**
730 * @callback_method_impl{FNTMTIMERDEV, Second2 timer.}
731 */
732static DECLCALLBACK(void) rtcTimerSecond2(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
733{
734 PRTCSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PRTCSTATE);
735
736 Assert(PDMDevHlpTimerIsLockOwner(pDevIns, pThis->hPeriodicTimer));
737 Assert(PDMDevHlpCritSectIsOwner(pDevIns, pDevIns->CTX_SUFF(pCritSectRo)));
738 Assert(pTimer == PDMDevHlpTimerToPtr(pDevIns, pThis->hSecondTimer2));
739 RT_NOREF2(pTimer, pvUser);
740
741 if (!(pThis->cmos_data[RTC_REG_B] & REG_B_SET))
742 rtc_copy_date(pThis);
743
744 /* check alarm */
745 if (pThis->cmos_data[RTC_REG_B] & REG_B_AIE)
746 {
747 if ( ( (pThis->cmos_data[RTC_SECONDS_ALARM] & 0xc0) == 0xc0
748 || from_bcd(pThis, pThis->cmos_data[RTC_SECONDS_ALARM]) == pThis->current_tm.tm_sec)
749 && ( (pThis->cmos_data[RTC_MINUTES_ALARM] & 0xc0) == 0xc0
750 || from_bcd(pThis, pThis->cmos_data[RTC_MINUTES_ALARM]) == pThis->current_tm.tm_min)
751 && ( (pThis->cmos_data[RTC_HOURS_ALARM ] & 0xc0) == 0xc0
752 || from_bcd(pThis, pThis->cmos_data[RTC_HOURS_ALARM ]) == pThis->current_tm.tm_hour)
753 )
754 {
755 pThis->cmos_data[RTC_REG_C] |= 0xa0;
756 rtc_raise_irq(pDevIns, pThis, 1);
757 }
758 }
759
760 /* update ended interrupt */
761 if (pThis->cmos_data[RTC_REG_B] & REG_B_UIE)
762 {
763 pThis->cmos_data[RTC_REG_C] |= 0x90;
764 rtc_raise_irq(pDevIns, pThis, 1);
765 }
766
767 /* clear update in progress bit */
768 Log2(("RTC: UIP %x -> 0\n", !!(pThis->cmos_data[RTC_REG_A] & REG_A_UIP)));
769 pThis->cmos_data[RTC_REG_A] &= ~REG_A_UIP;
770
771 pThis->next_second_time += PDMDevHlpTimerGetFreq(pDevIns, pThis->hSecondTimer);
772 PDMDevHlpTimerSet(pDevIns, pThis->hSecondTimer, pThis->next_second_time);
773}
774
775
776/* -=-=-=-=-=- Saved State -=-=-=-=-=- */
777
778
779/**
780 * @callback_method_impl{FNSSMDEVLIVEEXEC}
781 */
782static DECLCALLBACK(int) rtcLiveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uPass)
783{
784 RT_NOREF1(uPass);
785 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
786 PRTCSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PRTCSTATE);
787
788 pHlp->pfnSSMPutU8( pSSM, pThis->irq);
789 pHlp->pfnSSMPutIOPort(pSSM, pThis->IOPortBase);
790 pHlp->pfnSSMPutBool( pSSM, pThis->fUTC);
791
792 return VINF_SSM_DONT_CALL_AGAIN;
793}
794
795
796/**
797 * @callback_method_impl{FNSSMDEVSAVEEXEC}
798 */
799static DECLCALLBACK(int) rtcSaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
800{
801 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
802 PRTCSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PRTCSTATE);
803
804 /* The config. */
805 rtcLiveExec(pDevIns, pSSM, SSM_PASS_FINAL);
806
807 /* The state. */
808 pHlp->pfnSSMPutMem(pSSM, pThis->cmos_data, CMOS_BANK_SIZE);
809 pHlp->pfnSSMPutU8(pSSM, pThis->cmos_index[0]);
810
811 pHlp->pfnSSMPutS32(pSSM, pThis->current_tm.tm_sec);
812 pHlp->pfnSSMPutS32(pSSM, pThis->current_tm.tm_min);
813 pHlp->pfnSSMPutS32(pSSM, pThis->current_tm.tm_hour);
814 pHlp->pfnSSMPutS32(pSSM, pThis->current_tm.tm_wday);
815 pHlp->pfnSSMPutS32(pSSM, pThis->current_tm.tm_mday);
816 pHlp->pfnSSMPutS32(pSSM, pThis->current_tm.tm_mon);
817 pHlp->pfnSSMPutS32(pSSM, pThis->current_tm.tm_year);
818
819 PDMDevHlpTimerSave(pDevIns, pThis->hPeriodicTimer, pSSM);
820
821 pHlp->pfnSSMPutS64(pSSM, pThis->next_periodic_time);
822
823 pHlp->pfnSSMPutS64(pSSM, pThis->next_second_time);
824 PDMDevHlpTimerSave(pDevIns, pThis->hSecondTimer, pSSM);
825 PDMDevHlpTimerSave(pDevIns, pThis->hSecondTimer2, pSSM);
826
827 pHlp->pfnSSMPutBool(pSSM, pThis->fDisabledByHpet);
828
829 pHlp->pfnSSMPutMem(pSSM, &pThis->cmos_data[CMOS_BANK_SIZE], CMOS_BANK_SIZE);
830 return pHlp->pfnSSMPutU8(pSSM, pThis->cmos_index[1]);
831}
832
833
834/**
835 * @callback_method_impl{FNSSMDEVLOADEXEC}
836 */
837static DECLCALLBACK(int) rtcLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
838{
839 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
840 PRTCSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PRTCSTATE);
841 int rc;
842
843 if ( uVersion != RTC_SAVED_STATE_VERSION
844 && uVersion != RTC_SAVED_STATE_VERSION_VBOX_32PRE
845 && uVersion != RTC_SAVED_STATE_VERSION_VBOX_31
846 && uVersion != RTC_SAVED_STATE_VERSION_VBOX_30)
847 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
848
849 /* The config. */
850 if (uVersion > RTC_SAVED_STATE_VERSION_VBOX_30)
851 {
852 uint8_t u8Irq;
853 rc = pHlp->pfnSSMGetU8(pSSM, &u8Irq);
854 AssertRCReturn(rc, rc);
855 if (u8Irq != pThis->irq)
856 return pHlp->pfnSSMSetCfgError(pSSM, RT_SRC_POS, N_("Config mismatch - u8Irq: saved=%#x config=%#x"), u8Irq, pThis->irq);
857
858 RTIOPORT IOPortBase;
859 rc = pHlp->pfnSSMGetIOPort(pSSM, &IOPortBase);
860 AssertRCReturn(rc, rc);
861 if (IOPortBase != pThis->IOPortBase)
862 return pHlp->pfnSSMSetCfgError(pSSM, RT_SRC_POS, N_("Config mismatch - IOPortBase: saved=%RTiop config=%RTiop"), IOPortBase, pThis->IOPortBase);
863
864 bool fUTC;
865 rc = pHlp->pfnSSMGetBool(pSSM, &fUTC);
866 AssertRCReturn(rc, rc);
867 if (fUTC != pThis->fUTC)
868 LogRel(("RTC: Config mismatch - fUTC: saved=%RTbool config=%RTbool\n", fUTC, pThis->fUTC));
869 }
870
871 if (uPass != SSM_PASS_FINAL)
872 return VINF_SUCCESS;
873
874 /* The state. */
875 pHlp->pfnSSMGetMem(pSSM, pThis->cmos_data, CMOS_BANK_SIZE);
876 pHlp->pfnSSMGetU8(pSSM, &pThis->cmos_index[0]);
877
878 pHlp->pfnSSMGetS32(pSSM, &pThis->current_tm.tm_sec);
879 pHlp->pfnSSMGetS32(pSSM, &pThis->current_tm.tm_min);
880 pHlp->pfnSSMGetS32(pSSM, &pThis->current_tm.tm_hour);
881 pHlp->pfnSSMGetS32(pSSM, &pThis->current_tm.tm_wday);
882 pHlp->pfnSSMGetS32(pSSM, &pThis->current_tm.tm_mday);
883 pHlp->pfnSSMGetS32(pSSM, &pThis->current_tm.tm_mon);
884 pHlp->pfnSSMGetS32(pSSM, &pThis->current_tm.tm_year);
885
886 PDMDevHlpTimerLoad(pDevIns, pThis->hPeriodicTimer, pSSM);
887
888 pHlp->pfnSSMGetS64(pSSM, &pThis->next_periodic_time);
889
890 pHlp->pfnSSMGetS64(pSSM, &pThis->next_second_time);
891 PDMDevHlpTimerLoad(pDevIns, pThis->hSecondTimer, pSSM);
892 PDMDevHlpTimerLoad(pDevIns, pThis->hSecondTimer2, pSSM);
893
894 if (uVersion > RTC_SAVED_STATE_VERSION_VBOX_31)
895 pHlp->pfnSSMGetBool(pSSM, &pThis->fDisabledByHpet);
896
897 if (uVersion > RTC_SAVED_STATE_VERSION_VBOX_32PRE)
898 {
899 /* Second CMOS bank. */
900 pHlp->pfnSSMGetMem(pSSM, &pThis->cmos_data[CMOS_BANK_SIZE], CMOS_BANK_SIZE);
901 pHlp->pfnSSMGetU8(pSSM, &pThis->cmos_index[1]);
902 }
903
904 int period_code = pThis->cmos_data[RTC_REG_A] & 0x0f;
905 if ( period_code != 0
906 && (pThis->cmos_data[RTC_REG_B] & REG_B_PIE))
907 {
908 if (period_code <= 2)
909 period_code += 7;
910 int period = 1 << (period_code - 1);
911 LogRel(("RTC: period=%#x (%d) %u Hz (restore)\n", period, period, _32K / period));
912 PDMDevHlpCritSectEnter(pDevIns, pDevIns->pCritSectRoR3, VINF_SUCCESS);
913 PDMDevHlpTimerSetFrequencyHint(pDevIns, pThis->hPeriodicTimer, _32K / period);
914 PDMDevHlpCritSectLeave(pDevIns, pDevIns->pCritSectRoR3);
915 pThis->CurLogPeriod = period;
916 pThis->CurHintPeriod = period;
917 }
918 else
919 {
920 LogRel(("RTC: Stopped the periodic timer (restore)\n"));
921 pThis->CurLogPeriod = 0;
922 pThis->CurHintPeriod = 0;
923 }
924 pThis->cRelLogEntries = 0;
925
926 return VINF_SUCCESS;
927}
928
929
930/* -=-=-=-=-=- PDM Interface provided by the RTC device -=-=-=-=-=- */
931
932/**
933 * Calculate and update the standard CMOS checksum.
934 *
935 * @param pThis Pointer to the RTC state data.
936 */
937static void rtcCalcCRC(PRTCSTATE pThis)
938{
939 uint16_t u16 = 0;
940 for (unsigned i = RTC_CRC_START; i <= RTC_CRC_LAST; i++)
941 u16 += pThis->cmos_data[i];
942
943 pThis->cmos_data[RTC_CRC_LOW] = u16 & 0xff;
944 pThis->cmos_data[RTC_CRC_HIGH] = (u16 >> 8) & 0xff;
945}
946
947
948/**
949 * @interface_method_impl{PDMRTCREG,pfnWrite}
950 */
951static DECLCALLBACK(int) rtcCMOSWrite(PPDMDEVINS pDevIns, unsigned iReg, uint8_t u8Value)
952{
953 PRTCSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PRTCSTATE);
954 Assert(PDMDevHlpCritSectIsOwner(pDevIns, pDevIns->pCritSectRoR3));
955 if (iReg < RT_ELEMENTS(pThis->cmos_data))
956 {
957 pThis->cmos_data[iReg] = u8Value;
958
959 /* does it require checksum update? */
960 if ( iReg >= RTC_CRC_START
961 && iReg <= RTC_CRC_LAST)
962 rtcCalcCRC(pThis);
963
964 return VINF_SUCCESS;
965 }
966
967 AssertMsgFailed(("iReg=%d\n", iReg));
968 return VERR_INVALID_PARAMETER;
969}
970
971
972/**
973 * @interface_method_impl{PDMRTCREG,pfnRead}
974 */
975static DECLCALLBACK(int) rtcCMOSRead(PPDMDEVINS pDevIns, unsigned iReg, uint8_t *pu8Value)
976{
977 PRTCSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PRTCSTATE);
978 Assert(PDMDevHlpCritSectIsOwner(pDevIns, pDevIns->pCritSectRoR3));
979
980 if (iReg < RT_ELEMENTS(pThis->cmos_data))
981 {
982 *pu8Value = pThis->cmos_data[iReg];
983 return VINF_SUCCESS;
984 }
985 AssertMsgFailed(("iReg=%d\n", iReg));
986 return VERR_INVALID_PARAMETER;
987}
988
989
990/**
991 * @interface_method_impl{PDMIHPETLEGACYNOTIFY,pfnModeChanged}
992 */
993static DECLCALLBACK(void) rtcHpetLegacyNotify_ModeChanged(PPDMIHPETLEGACYNOTIFY pInterface, bool fActivated)
994{
995 PRTCSTATECC pThisCC = RT_FROM_MEMBER(pInterface, RTCSTATER3, IHpetLegacyNotify);
996 PPDMDEVINS pDevIns = pThisCC->pDevInsR3;
997 PDMDevHlpCritSectEnter(pDevIns, pDevIns->pCritSectRoR3, VERR_IGNORED);
998
999 pThisCC->pShared->fDisabledByHpet = fActivated;
1000
1001 PDMDevHlpCritSectLeave(pDevIns, pDevIns->pCritSectRoR3);
1002}
1003
1004
1005
1006/* -=-=-=-=-=- IBase -=-=-=-=-=- */
1007
1008/**
1009 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
1010 */
1011static DECLCALLBACK(void *) rtcQueryInterface(PPDMIBASE pInterface, const char *pszIID)
1012{
1013 PPDMDEVINS pDevIns = RT_FROM_MEMBER(pInterface, PDMDEVINS, IBase);
1014 PRTCSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PRTCSTATECC);
1015 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDevIns->IBase);
1016 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHPETLEGACYNOTIFY, &pThisCC->IHpetLegacyNotify);
1017 return NULL;
1018}
1019
1020
1021/* -=-=-=-=-=- PDMDEVREG -=-=-=-=-=- */
1022
1023static void rtc_set_memory(PRTCSTATE pThis, int addr, int val)
1024{
1025 if (addr >= 0 && addr <= 127)
1026 pThis->cmos_data[addr] = val;
1027}
1028
1029
1030static void rtc_set_date(PRTCSTATE pThis, const struct my_tm *tm)
1031{
1032 pThis->current_tm = *tm;
1033 rtc_copy_date(pThis);
1034}
1035
1036
1037/**
1038 * @interface_method_impl{PDMDEVREG,pfnInitComplete}
1039 *
1040 * Used to set the clock.
1041 */
1042static DECLCALLBACK(int) rtcInitComplete(PPDMDEVINS pDevIns)
1043{
1044 /** @todo this should be (re)done at power on if we didn't load a state... */
1045 PRTCSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PRTCSTATE);
1046
1047 /*
1048 * Set the CMOS date/time.
1049 */
1050 RTTIMESPEC Now;
1051 PDMDevHlpTMUtcNow(pDevIns, &Now);
1052 RTTIME Time;
1053 if (pThis->fUTC)
1054 RTTimeExplode(&Time, &Now);
1055 else
1056 RTTimeLocalExplode(&Time, &Now);
1057
1058 struct my_tm Tm;
1059 memset(&Tm, 0, sizeof(Tm));
1060 Tm.tm_year = Time.i32Year - 1900;
1061 Tm.tm_mon = Time.u8Month - 1;
1062 Tm.tm_mday = Time.u8MonthDay;
1063 Tm.tm_wday = (Time.u8WeekDay + 1 + 7) % 7; /* 0 = Monday -> Sunday */
1064 Tm.tm_yday = Time.u16YearDay - 1;
1065 Tm.tm_hour = Time.u8Hour;
1066 Tm.tm_min = Time.u8Minute;
1067 Tm.tm_sec = Time.u8Second;
1068
1069 rtc_set_date(pThis, &Tm);
1070
1071 int iYear = to_bcd(pThis, (Tm.tm_year / 100) + 19); /* tm_year is 1900 based */
1072 rtc_set_memory(pThis, 0x32, iYear); /* 32h - Century Byte (BCD value for the century */
1073 rtc_set_memory(pThis, 0x37, iYear); /* 37h - (IBM PS/2) Date Century Byte */
1074
1075 /*
1076 * Recalculate the checksum just in case.
1077 */
1078 rtcCalcCRC(pThis);
1079
1080 Log(("CMOS bank 0: \n%16.128Rhxd\n", &pThis->cmos_data[0]));
1081 Log(("CMOS bank 1: \n%16.128Rhxd\n", &pThis->cmos_data[CMOS_BANK_SIZE]));
1082 return VINF_SUCCESS;
1083}
1084
1085
1086/**
1087 * @interface_method_impl{PDMDEVREG,pfnReset}
1088 */
1089static DECLCALLBACK(void) rtcReset(PPDMDEVINS pDevIns)
1090{
1091 PRTCSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PRTCSTATE);
1092
1093 /* Reset index values (important for second bank). */
1094 pThis->cmos_index[0] = 0;
1095 pThis->cmos_index[1] = CMOS_BANK_SIZE; /* Point to start of second bank. */
1096}
1097
1098
1099/**
1100 * @interface_method_impl{PDMDEVREG,pfnConstruct}
1101 */
1102static DECLCALLBACK(int) rtcConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
1103{
1104 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
1105 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
1106 PRTCSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PRTCSTATE);
1107 PRTCSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PRTCSTATECC);
1108 int rc;
1109 Assert(iInstance == 0); RT_NOREF(iInstance);
1110
1111 /*
1112 * Validate configuration.
1113 */
1114 PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, "Irq|Base|UseUTC", "");
1115
1116 /*
1117 * Init the data.
1118 */
1119 uint8_t u8Irq;
1120 rc = pHlp->pfnCFGMQueryU8Def(pCfg, "Irq", &u8Irq, 8);
1121 if (RT_FAILURE(rc))
1122 return PDMDEV_SET_ERROR(pDevIns, rc,
1123 N_("Configuration error: Querying \"Irq\" as a uint8_t failed"));
1124 pThis->irq = u8Irq;
1125
1126 rc = pHlp->pfnCFGMQueryPortDef(pCfg, "Base", &pThis->IOPortBase, 0x70);
1127 if (RT_FAILURE(rc))
1128 return PDMDEV_SET_ERROR(pDevIns, rc,
1129 N_("Configuration error: Querying \"Base\" as a RTIOPORT failed"));
1130
1131 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "UseUTC", &pThis->fUTC, false);
1132 if (RT_FAILURE(rc))
1133 return PDMDEV_SET_ERROR(pDevIns, rc,
1134 N_("Configuration error: Querying \"UseUTC\" as a bool failed"));
1135
1136 Log(("RTC: Irq=%#x Base=%#x fR0Enabled=%RTbool fRCEnabled=%RTbool\n",
1137 u8Irq, pThis->IOPortBase, pDevIns->fR0Enabled, pDevIns->fRCEnabled));
1138
1139
1140 pThis->cmos_data[RTC_REG_A] = 0x26;
1141 pThis->cmos_data[RTC_REG_B] = 0x02;
1142 pThis->cmos_data[RTC_REG_C] = 0x00;
1143 pThis->cmos_data[RTC_REG_D] = 0x80;
1144 pThis->fDisabledByHpet = false;
1145 pThis->cmos_index[1] = CMOS_BANK_SIZE; /* Point to start of second bank. */
1146
1147 pThisCC->pDevInsR3 = pDevIns;
1148 pThisCC->RtcReg.u32Version = PDM_RTCREG_VERSION;
1149 pThisCC->RtcReg.pfnRead = rtcCMOSRead;
1150 pThisCC->RtcReg.pfnWrite = rtcCMOSWrite;
1151 pThisCC->IHpetLegacyNotify.pfnModeChanged = rtcHpetLegacyNotify_ModeChanged;
1152
1153 /* IBase */
1154 pDevIns->IBase.pfnQueryInterface = rtcQueryInterface;
1155
1156 /*
1157 * Create timers.
1158 */
1159 /* Periodic timer. */
1160 rc = PDMDevHlpTimerCreate(pDevIns, TMCLOCK_VIRTUAL_SYNC, rtcTimerPeriodic, pThis,
1161 TMTIMER_FLAGS_DEFAULT_CRIT_SECT, "MC146818 RTC (CMOS) - Periodic", &pThis->hPeriodicTimer);
1162 AssertRCReturn(rc, rc);
1163
1164 /* Seconds timer. */
1165 rc = PDMDevHlpTimerCreate(pDevIns, TMCLOCK_VIRTUAL_SYNC, rtcTimerSecond, pThis,
1166 TMTIMER_FLAGS_DEFAULT_CRIT_SECT, "MC146818 RTC (CMOS) - Second", &pThis->hSecondTimer);
1167 AssertRCReturn(rc, rc);
1168
1169 /* The second2 timer, this is always active. */
1170 rc = PDMDevHlpTimerCreate(pDevIns, TMCLOCK_VIRTUAL_SYNC, rtcTimerSecond2, pThis,
1171 TMTIMER_FLAGS_DEFAULT_CRIT_SECT, "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