VirtualBox

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

Last change on this file since 81744 was 81695, checked in by vboxsync, 5 years ago

DevRTC: Use default statistics prefix (was hiding under /TM/RTC).

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