VirtualBox

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

Last change on this file since 82064 was 81896, checked in by vboxsync, 5 years ago

DevRTC: Some cleanup. bugref:9218

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