VirtualBox

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

Last change on this file since 81385 was 81377, checked in by vboxsync, 5 years ago

DevRTC: Missing dox.

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