VirtualBox

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

Last change on this file since 89694 was 88836, checked in by vboxsync, 4 years ago

DevRTC: Since we expose the stats in release builds, we might as well increment them too.

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