VirtualBox

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

Last change on this file since 44810 was 44810, checked in by vboxsync, 12 years ago

DevRTC.cpp: cleanups.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 40.8 KB
Line 
1/* $Id: DevRTC.cpp 44810 2013-02-24 20:22:31Z vboxsync $ */
2/** @file
3 * Motorola MC146818 RTC/CMOS Device with PIIX4 extensions.
4 */
5
6/*
7 * Copyright (C) 2006-2013 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* Header Files *
45*******************************************************************************/
46#define LOG_GROUP LOG_GROUP_DEV_RTC
47#include <VBox/vmm/pdmdev.h>
48#include <VBox/log.h>
49#include <iprt/asm-math.h>
50#include <iprt/assert.h>
51#include <iprt/string.h>
52
53#ifdef IN_RING3
54# include <iprt/alloc.h>
55# include <iprt/uuid.h>
56#endif /* IN_RING3 */
57
58#include "VBoxDD.h"
59
60
61/*******************************************************************************
62* Defined Constants And Macros *
63*******************************************************************************/
64/*#define DEBUG_CMOS*/
65#define RTC_CRC_START 0x10
66#define RTC_CRC_LAST 0x2d
67#define RTC_CRC_HIGH 0x2e
68#define RTC_CRC_LOW 0x2f
69
70#define RTC_SECONDS 0
71#define RTC_SECONDS_ALARM 1
72#define RTC_MINUTES 2
73#define RTC_MINUTES_ALARM 3
74#define RTC_HOURS 4
75#define RTC_HOURS_ALARM 5
76#define RTC_ALARM_DONT_CARE 0xC0
77
78#define RTC_DAY_OF_WEEK 6
79#define RTC_DAY_OF_MONTH 7
80#define RTC_MONTH 8
81#define RTC_YEAR 9
82
83#define RTC_REG_A 10
84#define RTC_REG_B 11
85#define RTC_REG_C 12
86#define RTC_REG_D 13
87
88#define REG_A_UIP 0x80
89
90#define REG_B_SET 0x80
91#define REG_B_PIE 0x40
92#define REG_B_AIE 0x20
93#define REG_B_UIE 0x10
94
95#define CMOS_BANK_LOWER_LIMIT 0x0E
96#define CMOS_BANK_UPPER_LIMIT 0x7F
97#define CMOS_BANK2_LOWER_LIMIT 0x80
98#define CMOS_BANK2_UPPER_LIMIT 0xFF
99#define CMOS_BANK_SIZE 0x80
100
101/** The saved state version. */
102#define RTC_SAVED_STATE_VERSION 4
103/** The saved state version used by VirtualBox pre-3.2.
104 * This does not include the second 128-byte bank. */
105#define RTC_SAVED_STATE_VERSION_VBOX_32PRE 3
106/** The saved state version used by VirtualBox 3.1 and earlier.
107 * This does not include disabled by HPET state. */
108#define RTC_SAVED_STATE_VERSION_VBOX_31 2
109/** The saved state version used by VirtualBox 3.0 and earlier.
110 * This does not include the configuration. */
111#define RTC_SAVED_STATE_VERSION_VBOX_30 1
112
113
114/*******************************************************************************
115* Structures and Typedefs *
116*******************************************************************************/
117/** @todo Replace struct my_tm with RTTIME. */
118struct my_tm
119{
120 int32_t tm_sec;
121 int32_t tm_min;
122 int32_t tm_hour;
123 int32_t tm_mday;
124 int32_t tm_mon;
125 int32_t tm_year;
126 int32_t tm_wday;
127 int32_t tm_yday;
128};
129
130
131typedef struct RTCSTATE
132{
133 uint8_t cmos_data[256];
134 uint8_t cmos_index[2];
135 uint8_t Alignment0[6];
136 struct my_tm current_tm;
137 /** The configured IRQ. */
138 int32_t irq;
139 /** The configured I/O port base. */
140 RTIOPORT IOPortBase;
141 /** Use UTC or local time initially. */
142 bool fUTC;
143 /** Disabled by HPET legacy mode. */
144 bool fDisabledByHpet;
145 /* periodic timer */
146 int64_t next_periodic_time;
147 /* second update */
148 int64_t next_second_time;
149
150 /** Pointer to the device instance - R3 Ptr. */
151 PPDMDEVINSR3 pDevInsR3;
152 /** The periodic timer (rtcTimerPeriodic) - R3 Ptr. */
153 PTMTIMERR3 pPeriodicTimerR3;
154 /** The second timer (rtcTimerSecond) - R3 Ptr. */
155 PTMTIMERR3 pSecondTimerR3;
156 /** The second second timer (rtcTimerSecond2) - R3 Ptr. */
157 PTMTIMERR3 pSecondTimer2R3;
158
159 /** Pointer to the device instance - R0 Ptr. */
160 PPDMDEVINSR0 pDevInsR0;
161 /** The periodic timer (rtcTimerPeriodic) - R0 Ptr. */
162 PTMTIMERR0 pPeriodicTimerR0;
163 /** The second timer (rtcTimerSecond) - R0 Ptr. */
164 PTMTIMERR0 pSecondTimerR0;
165 /** The second second timer (rtcTimerSecond2) - R0 Ptr. */
166 PTMTIMERR0 pSecondTimer2R0;
167
168 /** Pointer to the device instance - RC Ptr. */
169 PPDMDEVINSRC pDevInsRC;
170 /** The periodic timer (rtcTimerPeriodic) - RC Ptr. */
171 PTMTIMERRC pPeriodicTimerRC;
172 /** The second timer (rtcTimerSecond) - RC Ptr. */
173 PTMTIMERRC pSecondTimerRC;
174 /** The second second timer (rtcTimerSecond2) - RC Ptr. */
175 PTMTIMERRC pSecondTimer2RC;
176
177 /** The RTC registration structure. */
178 PDMRTCREG RtcReg;
179 /** The RTC device helpers. */
180 R3PTRTYPE(PCPDMRTCHLP) pRtcHlpR3;
181 /** Number of release log entries. Used to prevent flooding. */
182 uint32_t cRelLogEntries;
183 /** The current/previous logged timer period. */
184 int32_t CurLogPeriod;
185 /** The current/previous hinted timer period. */
186 int32_t CurHintPeriod;
187 uint32_t u32AlignmentPadding;
188
189 /** HPET legacy mode notification interface. */
190 PDMIHPETLEGACYNOTIFY IHpetLegacyNotify;
191} RTCSTATE;
192/** Pointer to the RTC device state. */
193typedef RTCSTATE *PRTCSTATE;
194
195#ifndef VBOX_DEVICE_STRUCT_TESTCASE
196
197static void rtc_timer_update(PRTCSTATE pThis, int64_t current_time)
198{
199 int period_code, period;
200 uint64_t cur_clock, next_irq_clock;
201 uint32_t freq;
202
203 Assert(TMTimerIsLockOwner(pThis->CTX_SUFF(pPeriodicTimer)));
204 Assert(PDMCritSectIsOwner(pThis->CTX_SUFF(pDevIns)->CTX_SUFF(pCritSectRo)));
205
206 period_code = pThis->cmos_data[RTC_REG_A] & 0x0f;
207 if ( period_code != 0
208 && (pThis->cmos_data[RTC_REG_B] & REG_B_PIE))
209 {
210 if (period_code <= 2)
211 period_code += 7;
212 /* period in 32 kHz cycles */
213 period = 1 << (period_code - 1);
214 /* compute 32 kHz clock */
215 freq = TMTimerGetFreq(pThis->CTX_SUFF(pPeriodicTimer));
216
217 cur_clock = ASMMultU64ByU32DivByU32(current_time, 32768, freq);
218 next_irq_clock = (cur_clock & ~(uint64_t)(period - 1)) + period;
219 pThis->next_periodic_time = ASMMultU64ByU32DivByU32(next_irq_clock, freq, 32768) + 1;
220 TMTimerSet(pThis->CTX_SUFF(pPeriodicTimer), pThis->next_periodic_time);
221
222#ifdef IN_RING3
223 if (RT_UNLIKELY(period != pThis->CurLogPeriod))
224#else
225 if (RT_UNLIKELY(period != pThis->CurHintPeriod))
226#endif
227 {
228#ifdef IN_RING3
229 if (pThis->cRelLogEntries++ < 64)
230 LogRel(("RTC: period=%#x (%d) %u Hz\n", period, period, _32K / period));
231 pThis->CurLogPeriod = period;
232#endif
233 pThis->CurHintPeriod = period;
234 TMTimerSetFrequencyHint(pThis->CTX_SUFF(pPeriodicTimer), _32K / period);
235 }
236 }
237 else
238 {
239 if (TMTimerIsActive(pThis->CTX_SUFF(pPeriodicTimer)) && pThis->cRelLogEntries++ < 64)
240 LogRel(("RTC: stopped the periodic timer\n"));
241 TMTimerStop(pThis->CTX_SUFF(pPeriodicTimer));
242 }
243}
244
245
246static void rtc_raise_irq(PRTCSTATE pThis, uint32_t iLevel)
247{
248 if (!pThis->fDisabledByHpet)
249 PDMDevHlpISASetIrq(pThis->CTX_SUFF(pDevIns), pThis->irq, iLevel);
250}
251
252
253DECLINLINE(int) to_bcd(PRTCSTATE pThis, int a)
254{
255 if (pThis->cmos_data[RTC_REG_B] & 0x04)
256 return a;
257 return ((a / 10) << 4) | (a % 10);
258}
259
260
261DECLINLINE(int) from_bcd(PRTCSTATE pThis, int a)
262{
263 if (pThis->cmos_data[RTC_REG_B] & 0x04)
264 return a;
265 return ((a >> 4) * 10) + (a & 0x0f);
266}
267
268
269static void rtc_set_time(PRTCSTATE pThis)
270{
271 struct my_tm *tm = &pThis->current_tm;
272
273 tm->tm_sec = from_bcd(pThis, pThis->cmos_data[RTC_SECONDS]);
274 tm->tm_min = from_bcd(pThis, pThis->cmos_data[RTC_MINUTES]);
275 tm->tm_hour = from_bcd(pThis, pThis->cmos_data[RTC_HOURS] & 0x7f);
276 if ( !(pThis->cmos_data[RTC_REG_B] & 0x02)
277 && (pThis->cmos_data[RTC_HOURS] & 0x80))
278 tm->tm_hour += 12;
279 tm->tm_wday = from_bcd(pThis, pThis->cmos_data[RTC_DAY_OF_WEEK]);
280 tm->tm_mday = from_bcd(pThis, pThis->cmos_data[RTC_DAY_OF_MONTH]);
281 tm->tm_mon = from_bcd(pThis, pThis->cmos_data[RTC_MONTH]) - 1;
282 tm->tm_year = from_bcd(pThis, pThis->cmos_data[RTC_YEAR]) + 100;
283}
284
285
286/* -=-=-=-=-=- I/O Port Handlers -=-=-=-=-=- */
287
288
289/**
290 * @callback_method_impl{FNIOMIOPORTIN}
291 */
292PDMBOTHCBDECL(int) rtcIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
293{
294 NOREF(pvUser);
295 if (cb != 1)
296 return VERR_IOM_IOPORT_UNUSED;
297
298 PRTCSTATE pThis = PDMINS_2_DATA(pDevIns, PRTCSTATE);
299 if ((Port & 1) == 0)
300 *pu32 = 0xff;
301 else
302 {
303 unsigned bank = (Port >> 1) & 1;
304 switch (pThis->cmos_index[bank])
305 {
306 case RTC_SECONDS:
307 case RTC_MINUTES:
308 case RTC_HOURS:
309 case RTC_DAY_OF_WEEK:
310 case RTC_DAY_OF_MONTH:
311 case RTC_MONTH:
312 case RTC_YEAR:
313 *pu32 = pThis->cmos_data[pThis->cmos_index[0]];
314 break;
315
316 case RTC_REG_A:
317 *pu32 = pThis->cmos_data[pThis->cmos_index[0]];
318 break;
319
320 case RTC_REG_C:
321 *pu32 = pThis->cmos_data[pThis->cmos_index[0]];
322 rtc_raise_irq(pThis, 0);
323 pThis->cmos_data[RTC_REG_C] = 0x00;
324 break;
325
326 default:
327 *pu32 = pThis->cmos_data[pThis->cmos_index[bank]];
328 break;
329 }
330
331 Log(("CMOS: Read bank %d idx %#04x: %#04x\n", bank, pThis->cmos_index[bank], *pu32));
332 }
333
334 return VINF_SUCCESS;
335}
336
337
338/**
339 * @callback_method_impl{FNIOMIOPORTOUT}
340 */
341PDMBOTHCBDECL(int) rtcIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
342{
343 NOREF(pvUser);
344 if (cb != 1)
345 return VINF_SUCCESS;
346
347 PRTCSTATE pThis = PDMINS_2_DATA(pDevIns, PRTCSTATE);
348 uint32_t bank = (Port >> 1) & 1;
349 if ((Port & 1) == 0)
350 {
351 pThis->cmos_index[bank] = (u32 & 0x7f) + (bank * CMOS_BANK_SIZE);
352 }
353 else
354 {
355 Log(("CMOS: Write bank %d idx %#04x: %#04x (old %#04x)\n", bank,
356 pThis->cmos_index[bank], u32, pThis->cmos_data[pThis->cmos_index[bank]]));
357
358 int const idx = pThis->cmos_index[bank];
359 switch (idx)
360 {
361 case RTC_SECONDS_ALARM:
362 case RTC_MINUTES_ALARM:
363 case RTC_HOURS_ALARM:
364 pThis->cmos_data[pThis->cmos_index[0]] = u32;
365 break;
366
367 case RTC_SECONDS:
368 case RTC_MINUTES:
369 case RTC_HOURS:
370 case RTC_DAY_OF_WEEK:
371 case RTC_DAY_OF_MONTH:
372 case RTC_MONTH:
373 case RTC_YEAR:
374 pThis->cmos_data[pThis->cmos_index[0]] = u32;
375 /* if in set mode, do not update the time */
376 if (!(pThis->cmos_data[RTC_REG_B] & REG_B_SET))
377 rtc_set_time(pThis);
378 break;
379
380 case RTC_REG_A:
381 case RTC_REG_B:
382 {
383 /* We need to acquire the clock lock, because of lock ordering
384 issues this means having to release the device lock. Since
385 we're letting IOM do the locking, we must not return without
386 holding the device lock.*/
387 PDMCritSectLeave(pThis->CTX_SUFF(pDevIns)->CTX_SUFF(pCritSectRo));
388 int rc1 = TMTimerLock(pThis->CTX_SUFF(pPeriodicTimer), VINF_SUCCESS /* must get it */);
389 int rc2 = PDMCritSectEnter(pThis->CTX_SUFF(pDevIns)->CTX_SUFF(pCritSectRo), VINF_SUCCESS /* must get it */);
390 AssertRCReturn(rc1, rc1);
391 AssertRCReturnStmt(rc2, TMTimerUnlock(pThis->CTX_SUFF(pPeriodicTimer)), rc2);
392
393 if (idx == RTC_REG_A)
394 {
395 /* UIP bit is read only */
396 pThis->cmos_data[RTC_REG_A] = (u32 & ~REG_A_UIP)
397 | (pThis->cmos_data[RTC_REG_A] & REG_A_UIP);
398 }
399 else
400 {
401 if (u32 & REG_B_SET)
402 {
403 /* set mode: reset UIP mode */
404 pThis->cmos_data[RTC_REG_A] &= ~REG_A_UIP;
405#if 0 /* This is probably wrong as it breaks changing the time/date in OS/2. */
406 u32 &= ~REG_B_UIE;
407#endif
408 }
409 else
410 {
411 /* if disabling set mode, update the time */
412 if (pThis->cmos_data[RTC_REG_B] & REG_B_SET)
413 rtc_set_time(pThis);
414 }
415 pThis->cmos_data[RTC_REG_B] = u32;
416 }
417
418 rtc_timer_update(pThis, TMTimerGet(pThis->CTX_SUFF(pPeriodicTimer)));
419
420 TMTimerUnlock(pThis->CTX_SUFF(pPeriodicTimer));
421 /* the caller leaves the other lock. */
422 break;
423 }
424
425 case RTC_REG_C:
426 case RTC_REG_D:
427 /* cannot write to them */
428 break;
429
430 default:
431 pThis->cmos_data[pThis->cmos_index[bank]] = u32;
432 break;
433 }
434 }
435
436 return VINF_SUCCESS;
437}
438
439#ifdef IN_RING3
440
441/* -=-=-=-=-=- Debug Info Handlers -=-=-=-=-=- */
442
443/**
444 * @callback_method_impl{FNDBGFHANDLERDEV,
445 * Dumps the cmos Bank Info.}
446 */
447static DECLCALLBACK(void) rtcCmosBankInfo(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
448{
449 PRTCSTATE pThis = PDMINS_2_DATA(pDevIns, PRTCSTATE);
450
451 pHlp->pfnPrintf(pHlp,
452 "First CMOS bank, offsets 0x0E - 0x7F\n"
453 "Offset %02x : --- use 'info rtc' to show CMOS clock ---", 0);
454 for (unsigned iCmos = CMOS_BANK_LOWER_LIMIT; iCmos <= CMOS_BANK_UPPER_LIMIT; iCmos++)
455 {
456 if ((iCmos & 15) == 0)
457 pHlp->pfnPrintf(pHlp, "Offset %02x : %02x", iCmos, pThis->cmos_data[iCmos]);
458 else if ((iCmos & 15) == 8)
459 pHlp->pfnPrintf(pHlp, "-%02x", pThis->cmos_data[iCmos]);
460 else if ((iCmos & 15) == 15)
461 pHlp->pfnPrintf(pHlp, " %02x\n", pThis->cmos_data[iCmos]);
462 else
463 pHlp->pfnPrintf(pHlp, " %02x", pThis->cmos_data[iCmos]);
464 }
465}
466
467/**
468 * @callback_method_impl{FNDBGFHANDLERDEV,
469 * Dumps the cmos Bank2 Info.}
470 */
471static DECLCALLBACK(void) rtcCmosBank2Info(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
472{
473 PRTCSTATE pThis = PDMINS_2_DATA(pDevIns, PRTCSTATE);
474
475 pHlp->pfnPrintf(pHlp, "Second CMOS bank, offsets 0x80 - 0xFF\n");
476 for (uint16_t iCmos = CMOS_BANK2_LOWER_LIMIT; iCmos <= CMOS_BANK2_UPPER_LIMIT; iCmos++)
477 {
478 if ((iCmos & 15) == 0)
479 pHlp->pfnPrintf(pHlp, "Offset %02x : %02x", iCmos, pThis->cmos_data[iCmos]);
480 else if ((iCmos & 15) == 8)
481 pHlp->pfnPrintf(pHlp, "-%02x", pThis->cmos_data[iCmos]);
482 else if ((iCmos & 15) == 15)
483 pHlp->pfnPrintf(pHlp, " %02x\n", pThis->cmos_data[iCmos]);
484 else
485 pHlp->pfnPrintf(pHlp, " %02x", pThis->cmos_data[iCmos]);
486 }
487}
488
489/**
490 * @callback_method_impl{FNDBGFHANDLERDEV,
491 * Dumps the cmos RTC Info.}
492 */
493static DECLCALLBACK(void) rtcCmosClockInfo(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
494{
495 PRTCSTATE pThis = PDMINS_2_DATA(pDevIns, PRTCSTATE);
496 uint8_t u8Sec = from_bcd(pThis, pThis->cmos_data[RTC_SECONDS]);
497 uint8_t u8Min = from_bcd(pThis, pThis->cmos_data[RTC_MINUTES]);
498 uint8_t u8Hr = from_bcd(pThis, pThis->cmos_data[RTC_HOURS] & 0x7f);
499 if ( !(pThis->cmos_data[RTC_REG_B] & 0x02)
500 && (pThis->cmos_data[RTC_HOURS] & 0x80))
501 u8Hr += 12;
502 uint8_t u8Day = from_bcd(pThis, pThis->cmos_data[RTC_DAY_OF_MONTH]);
503 uint8_t u8Month = from_bcd(pThis, pThis->cmos_data[RTC_MONTH]) ;
504 uint8_t u8Year = from_bcd(pThis, pThis->cmos_data[RTC_YEAR]);
505 pHlp->pfnPrintf(pHlp, "Time: %02u:%02u:%02u Date: %02u-%02u-%02u\n",
506 u8Hr, u8Min, u8Sec, u8Year, u8Month, u8Day);
507 pHlp->pfnPrintf(pHlp, "REG A=%02x B=%02x C=%02x D=%02x\n",
508 pThis->cmos_data[RTC_REG_A], pThis->cmos_data[RTC_REG_B],
509 pThis->cmos_data[RTC_REG_C], pThis->cmos_data[RTC_REG_D]);
510}
511
512
513
514/* -=-=-=-=-=- Timers and their support code -=-=-=-=-=- */
515
516
517/**
518 * @callback_method_impl{FNTMTIMERDEV, periodic}
519 */
520static DECLCALLBACK(void) rtcTimerPeriodic(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
521{
522 PRTCSTATE pThis = PDMINS_2_DATA(pDevIns, PRTCSTATE);
523 Assert(TMTimerIsLockOwner(pThis->CTX_SUFF(pPeriodicTimer)));
524 Assert(PDMCritSectIsOwner(pThis->CTX_SUFF(pDevIns)->CTX_SUFF(pCritSectRo)));
525
526 rtc_timer_update(pThis, pThis->next_periodic_time);
527 pThis->cmos_data[RTC_REG_C] |= 0xc0;
528
529 rtc_raise_irq(pThis, 1);
530}
531
532
533/* month is between 0 and 11. */
534static int get_days_in_month(int month, int year)
535{
536 static const int days_tab[12] =
537 {
538 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
539 };
540 int d;
541
542 if ((unsigned )month >= 12)
543 return 31;
544
545 d = days_tab[month];
546 if (month == 1)
547 {
548 if ((year % 4) == 0 && ((year % 100) != 0 || (year % 400) == 0))
549 d++;
550 }
551 return d;
552}
553
554
555/* update 'tm' to the next second */
556static void rtc_next_second(struct my_tm *tm)
557{
558 int days_in_month;
559
560 tm->tm_sec++;
561 if ((unsigned)tm->tm_sec >= 60)
562 {
563 tm->tm_sec = 0;
564 tm->tm_min++;
565 if ((unsigned)tm->tm_min >= 60)
566 {
567 tm->tm_min = 0;
568 tm->tm_hour++;
569 if ((unsigned)tm->tm_hour >= 24)
570 {
571 tm->tm_hour = 0;
572 /* next day */
573 tm->tm_wday++;
574 if ((unsigned)tm->tm_wday >= 7)
575 tm->tm_wday = 0;
576 days_in_month = get_days_in_month(tm->tm_mon,
577 tm->tm_year + 1900);
578 tm->tm_mday++;
579 if (tm->tm_mday < 1)
580 tm->tm_mday = 1;
581 else if (tm->tm_mday > days_in_month)
582 {
583 tm->tm_mday = 1;
584 tm->tm_mon++;
585 if (tm->tm_mon >= 12)
586 {
587 tm->tm_mon = 0;
588 tm->tm_year++;
589 }
590 }
591 }
592 }
593 }
594}
595
596
597/**
598 * @callback_method_impl{FNTMTIMERDEV, Second timer.}
599 */
600static DECLCALLBACK(void) rtcTimerSecond(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
601{
602 PRTCSTATE pThis = PDMINS_2_DATA(pDevIns, PRTCSTATE);
603 Assert(TMTimerIsLockOwner(pThis->CTX_SUFF(pPeriodicTimer)));
604 Assert(PDMCritSectIsOwner(pThis->CTX_SUFF(pDevIns)->CTX_SUFF(pCritSectRo)));
605
606 /* if the oscillator is not in normal operation, we do not update */
607 if ((pThis->cmos_data[RTC_REG_A] & 0x70) != 0x20)
608 {
609 pThis->next_second_time += TMTimerGetFreq(pThis->CTX_SUFF(pSecondTimer));
610 TMTimerSet(pThis->CTX_SUFF(pSecondTimer), pThis->next_second_time);
611 }
612 else
613 {
614 rtc_next_second(&pThis->current_tm);
615
616 if (!(pThis->cmos_data[RTC_REG_B] & REG_B_SET))
617 {
618 /* update in progress bit */
619 Log2(("RTC: UIP %x -> 1\n", !!(pThis->cmos_data[RTC_REG_A] & REG_A_UIP)));
620 pThis->cmos_data[RTC_REG_A] |= REG_A_UIP;
621 }
622
623 /* 244140 ns = 8 / 32768 seconds */
624 uint64_t delay = TMTimerFromNano(pThis->CTX_SUFF(pSecondTimer2), 244140);
625 TMTimerSet(pThis->CTX_SUFF(pSecondTimer2), pThis->next_second_time + delay);
626 }
627}
628
629
630/* Used by rtc_set_date and rtcTimerSecond2. */
631static void rtc_copy_date(PRTCSTATE pThis)
632{
633 const struct my_tm *tm = &pThis->current_tm;
634
635 pThis->cmos_data[RTC_SECONDS] = to_bcd(pThis, tm->tm_sec);
636 pThis->cmos_data[RTC_MINUTES] = to_bcd(pThis, tm->tm_min);
637 if (pThis->cmos_data[RTC_REG_B] & 0x02)
638 {
639 /* 24 hour format */
640 pThis->cmos_data[RTC_HOURS] = to_bcd(pThis, tm->tm_hour);
641 }
642 else
643 {
644 /* 12 hour format */
645 pThis->cmos_data[RTC_HOURS] = to_bcd(pThis, tm->tm_hour % 12);
646 if (tm->tm_hour >= 12)
647 pThis->cmos_data[RTC_HOURS] |= 0x80;
648 }
649 pThis->cmos_data[RTC_DAY_OF_WEEK] = to_bcd(pThis, tm->tm_wday);
650 pThis->cmos_data[RTC_DAY_OF_MONTH] = to_bcd(pThis, tm->tm_mday);
651 pThis->cmos_data[RTC_MONTH] = to_bcd(pThis, tm->tm_mon + 1);
652 pThis->cmos_data[RTC_YEAR] = to_bcd(pThis, tm->tm_year % 100);
653}
654
655
656/**
657 * @callback_method_impl{FNTMTIMERDEV, Second2 timer.}
658 */
659static DECLCALLBACK(void) rtcTimerSecond2(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
660{
661 PRTCSTATE pThis = PDMINS_2_DATA(pDevIns, PRTCSTATE);
662 Assert(TMTimerIsLockOwner(pThis->CTX_SUFF(pPeriodicTimer)));
663 Assert(PDMCritSectIsOwner(pThis->CTX_SUFF(pDevIns)->CTX_SUFF(pCritSectRo)));
664
665 if (!(pThis->cmos_data[RTC_REG_B] & REG_B_SET))
666 rtc_copy_date(pThis);
667
668 /* check alarm */
669 if (pThis->cmos_data[RTC_REG_B] & REG_B_AIE)
670 {
671 if ( ( (pThis->cmos_data[RTC_SECONDS_ALARM] & 0xc0) == 0xc0
672 || from_bcd(pThis, pThis->cmos_data[RTC_SECONDS_ALARM]) == pThis->current_tm.tm_sec)
673 && ( (pThis->cmos_data[RTC_MINUTES_ALARM] & 0xc0) == 0xc0
674 || from_bcd(pThis, pThis->cmos_data[RTC_MINUTES_ALARM]) == pThis->current_tm.tm_min)
675 && ( (pThis->cmos_data[RTC_HOURS_ALARM ] & 0xc0) == 0xc0
676 || from_bcd(pThis, pThis->cmos_data[RTC_HOURS_ALARM ]) == pThis->current_tm.tm_hour)
677 )
678 {
679 pThis->cmos_data[RTC_REG_C] |= 0xa0;
680 rtc_raise_irq(pThis, 1);
681 }
682 }
683
684 /* update ended interrupt */
685 if (pThis->cmos_data[RTC_REG_B] & REG_B_UIE)
686 {
687 pThis->cmos_data[RTC_REG_C] |= 0x90;
688 rtc_raise_irq(pThis, 1);
689 }
690
691 /* clear update in progress bit */
692 Log2(("RTC: UIP %x -> 0\n", !!(pThis->cmos_data[RTC_REG_A] & REG_A_UIP)));
693 pThis->cmos_data[RTC_REG_A] &= ~REG_A_UIP;
694
695 pThis->next_second_time += TMTimerGetFreq(pThis->CTX_SUFF(pSecondTimer));
696 TMTimerSet(pThis->CTX_SUFF(pSecondTimer), pThis->next_second_time);
697}
698
699
700/* -=-=-=-=-=- Saved State -=-=-=-=-=- */
701
702
703/**
704 * @callback_method_impl{FNSSMDEVLIVEEXEC}
705 */
706static DECLCALLBACK(int) rtcLiveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uPass)
707{
708 PRTCSTATE pThis = PDMINS_2_DATA(pDevIns, PRTCSTATE);
709
710 SSMR3PutU8( pSSM, pThis->irq);
711 SSMR3PutIOPort(pSSM, pThis->IOPortBase);
712 SSMR3PutBool( pSSM, pThis->fUTC);
713
714 return VINF_SSM_DONT_CALL_AGAIN;
715}
716
717
718/**
719 * @callback_method_impl{FNSSMDEVSAVEEXEC}
720 */
721static DECLCALLBACK(int) rtcSaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
722{
723 PRTCSTATE pThis = PDMINS_2_DATA(pDevIns, PRTCSTATE);
724
725 /* The config. */
726 rtcLiveExec(pDevIns, pSSM, SSM_PASS_FINAL);
727
728 /* The state. */
729 SSMR3PutMem(pSSM, pThis->cmos_data, CMOS_BANK_SIZE);
730 SSMR3PutU8(pSSM, pThis->cmos_index[0]);
731
732 SSMR3PutS32(pSSM, pThis->current_tm.tm_sec);
733 SSMR3PutS32(pSSM, pThis->current_tm.tm_min);
734 SSMR3PutS32(pSSM, pThis->current_tm.tm_hour);
735 SSMR3PutS32(pSSM, pThis->current_tm.tm_wday);
736 SSMR3PutS32(pSSM, pThis->current_tm.tm_mday);
737 SSMR3PutS32(pSSM, pThis->current_tm.tm_mon);
738 SSMR3PutS32(pSSM, pThis->current_tm.tm_year);
739
740 TMR3TimerSave(pThis->CTX_SUFF(pPeriodicTimer), pSSM);
741
742 SSMR3PutS64(pSSM, pThis->next_periodic_time);
743
744 SSMR3PutS64(pSSM, pThis->next_second_time);
745 TMR3TimerSave(pThis->CTX_SUFF(pSecondTimer), pSSM);
746 TMR3TimerSave(pThis->CTX_SUFF(pSecondTimer2), pSSM);
747
748 SSMR3PutBool(pSSM, pThis->fDisabledByHpet);
749
750 SSMR3PutMem(pSSM, &pThis->cmos_data[CMOS_BANK_SIZE], CMOS_BANK_SIZE);
751 return SSMR3PutU8(pSSM, pThis->cmos_index[1]);
752}
753
754
755/**
756 * @callback_method_impl{FNSSMDEVLOADEXEC}
757 */
758static DECLCALLBACK(int) rtcLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
759{
760 PRTCSTATE pThis = PDMINS_2_DATA(pDevIns, PRTCSTATE);
761 int rc;
762
763 if ( uVersion != RTC_SAVED_STATE_VERSION
764 && uVersion != RTC_SAVED_STATE_VERSION_VBOX_32PRE
765 && uVersion != RTC_SAVED_STATE_VERSION_VBOX_31
766 && uVersion != RTC_SAVED_STATE_VERSION_VBOX_30)
767 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
768
769 /* The config. */
770 if (uVersion > RTC_SAVED_STATE_VERSION_VBOX_30)
771 {
772 uint8_t u8Irq;
773 rc = SSMR3GetU8(pSSM, &u8Irq); AssertRCReturn(rc, rc);
774 if (u8Irq != pThis->irq)
775 return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Config mismatch - u8Irq: saved=%#x config=%#x"), u8Irq, pThis->irq);
776
777 RTIOPORT IOPortBase;
778 rc = SSMR3GetIOPort(pSSM, &IOPortBase); AssertRCReturn(rc, rc);
779 if (IOPortBase != pThis->IOPortBase)
780 return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Config mismatch - IOPortBase: saved=%RTiop config=%RTiop"), IOPortBase, pThis->IOPortBase);
781
782 bool fUTC;
783 rc = SSMR3GetBool(pSSM, &fUTC); AssertRCReturn(rc, rc);
784 if (fUTC != pThis->fUTC)
785 LogRel(("RTC: Config mismatch - fUTC: saved=%RTbool config=%RTbool\n", fUTC, pThis->fUTC));
786 }
787
788 if (uPass != SSM_PASS_FINAL)
789 return VINF_SUCCESS;
790
791 /* The state. */
792 SSMR3GetMem(pSSM, pThis->cmos_data, CMOS_BANK_SIZE);
793 SSMR3GetU8(pSSM, &pThis->cmos_index[0]);
794
795 SSMR3GetS32(pSSM, &pThis->current_tm.tm_sec);
796 SSMR3GetS32(pSSM, &pThis->current_tm.tm_min);
797 SSMR3GetS32(pSSM, &pThis->current_tm.tm_hour);
798 SSMR3GetS32(pSSM, &pThis->current_tm.tm_wday);
799 SSMR3GetS32(pSSM, &pThis->current_tm.tm_mday);
800 SSMR3GetS32(pSSM, &pThis->current_tm.tm_mon);
801 SSMR3GetS32(pSSM, &pThis->current_tm.tm_year);
802
803 TMR3TimerLoad(pThis->CTX_SUFF(pPeriodicTimer), pSSM);
804
805 SSMR3GetS64(pSSM, &pThis->next_periodic_time);
806
807 SSMR3GetS64(pSSM, &pThis->next_second_time);
808 TMR3TimerLoad(pThis->CTX_SUFF(pSecondTimer), pSSM);
809 TMR3TimerLoad(pThis->CTX_SUFF(pSecondTimer2), pSSM);
810
811 if (uVersion > RTC_SAVED_STATE_VERSION_VBOX_31)
812 SSMR3GetBool(pSSM, &pThis->fDisabledByHpet);
813
814 if (uVersion > RTC_SAVED_STATE_VERSION_VBOX_32PRE)
815 {
816 /* Second CMOS bank. */
817 SSMR3GetMem(pSSM, &pThis->cmos_data[CMOS_BANK_SIZE], CMOS_BANK_SIZE);
818 SSMR3GetU8(pSSM, &pThis->cmos_index[1]);
819 }
820
821 int period_code = pThis->cmos_data[RTC_REG_A] & 0x0f;
822 if ( period_code != 0
823 && (pThis->cmos_data[RTC_REG_B] & REG_B_PIE))
824 {
825 if (period_code <= 2)
826 period_code += 7;
827 int period = 1 << (period_code - 1);
828 LogRel(("RTC: period=%#x (%d) %u Hz (restore)\n", period, period, _32K / period));
829 PDMCritSectEnter(pThis->pDevInsR3->pCritSectRoR3, VINF_SUCCESS);
830 TMTimerSetFrequencyHint(pThis->CTX_SUFF(pPeriodicTimer), _32K / period);
831 PDMCritSectLeave(pThis->pDevInsR3->pCritSectRoR3);
832 pThis->CurLogPeriod = period;
833 pThis->CurHintPeriod = period;
834 }
835 else
836 {
837 LogRel(("RTC: stopped the periodic timer (restore)\n"));
838 pThis->CurLogPeriod = 0;
839 pThis->CurHintPeriod = 0;
840 }
841 pThis->cRelLogEntries = 0;
842
843 return VINF_SUCCESS;
844}
845
846
847/* -=-=-=-=-=- PDM Interface provided by the RTC device -=-=-=-=-=- */
848
849/**
850 * Calculate and update the standard CMOS checksum.
851 *
852 * @param pThis Pointer to the RTC state data.
853 */
854static void rtcCalcCRC(PRTCSTATE pThis)
855{
856 uint16_t u16 = 0;
857 for (unsigned i = RTC_CRC_START; i <= RTC_CRC_LAST; i++)
858 u16 += pThis->cmos_data[i];
859
860 pThis->cmos_data[RTC_CRC_LOW] = u16 & 0xff;
861 pThis->cmos_data[RTC_CRC_HIGH] = (u16 >> 8) & 0xff;
862}
863
864
865/**
866 * @interface_method_impl{PDMRTCREG,pfnWrite}
867 */
868static DECLCALLBACK(int) rtcCMOSWrite(PPDMDEVINS pDevIns, unsigned iReg, uint8_t u8Value)
869{
870 PRTCSTATE pThis = PDMINS_2_DATA(pDevIns, PRTCSTATE);
871 Assert(PDMCritSectIsOwner(pDevIns->pCritSectRoR3));
872 if (iReg < RT_ELEMENTS(pThis->cmos_data))
873 {
874 pThis->cmos_data[iReg] = u8Value;
875
876 /* does it require checksum update? */
877 if ( iReg >= RTC_CRC_START
878 && iReg <= RTC_CRC_LAST)
879 rtcCalcCRC(pThis);
880
881 return VINF_SUCCESS;
882 }
883
884 AssertMsgFailed(("iReg=%d\n", iReg));
885 return VERR_INVALID_PARAMETER;
886}
887
888
889/**
890 * @interface_method_impl{PDMRTCREG,pfnRead}
891 */
892static DECLCALLBACK(int) rtcCMOSRead(PPDMDEVINS pDevIns, unsigned iReg, uint8_t *pu8Value)
893{
894 PRTCSTATE pThis = PDMINS_2_DATA(pDevIns, PRTCSTATE);
895 Assert(PDMCritSectIsOwner(pDevIns->pCritSectRoR3));
896
897 if (iReg < RT_ELEMENTS(pThis->cmos_data))
898 {
899 *pu8Value = pThis->cmos_data[iReg];
900 return VINF_SUCCESS;
901 }
902 AssertMsgFailed(("iReg=%d\n", iReg));
903 return VERR_INVALID_PARAMETER;
904}
905
906
907/**
908 * @interface_method_impl{PDMIHPETLEGACYNOTIFY,pfnModeChanged}
909 */
910static DECLCALLBACK(void) rtcHpetLegacyNotify_ModeChanged(PPDMIHPETLEGACYNOTIFY pInterface, bool fActivated)
911{
912 PRTCSTATE pThis = RT_FROM_MEMBER(pInterface, RTCSTATE, IHpetLegacyNotify);
913 PDMCritSectEnter(pThis->pDevInsR3->pCritSectRoR3, VERR_IGNORED);
914
915 pThis->fDisabledByHpet = fActivated;
916
917 PDMCritSectLeave(pThis->pDevInsR3->pCritSectRoR3);
918}
919
920
921
922/* -=-=-=-=-=- IBase -=-=-=-=-=- */
923
924/**
925 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
926 */
927static DECLCALLBACK(void *) rtcQueryInterface(PPDMIBASE pInterface, const char *pszIID)
928{
929 PPDMDEVINS pDevIns = RT_FROM_MEMBER(pInterface, PDMDEVINS, IBase);
930 PRTCSTATE pThis = PDMINS_2_DATA(pDevIns, PRTCSTATE);
931 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDevIns->IBase);
932 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHPETLEGACYNOTIFY, &pThis->IHpetLegacyNotify);
933 return NULL;
934}
935
936
937/* -=-=-=-=-=- PDMDEVREG -=-=-=-=-=- */
938
939static void rtc_set_memory(PRTCSTATE pThis, int addr, int val)
940{
941 if (addr >= 0 && addr <= 127)
942 pThis->cmos_data[addr] = val;
943}
944
945
946static void rtc_set_date(PRTCSTATE pThis, const struct my_tm *tm)
947{
948 pThis->current_tm = *tm;
949 rtc_copy_date(pThis);
950}
951
952
953/**
954 * @interface_method_impl{PDMDEVREG,pfnInitComplete}
955 *
956 * Used to set the clock.
957 */
958static DECLCALLBACK(int) rtcInitComplete(PPDMDEVINS pDevIns)
959{
960 /** @todo this should be (re)done at power on if we didn't load a state... */
961 PRTCSTATE pThis = PDMINS_2_DATA(pDevIns, PRTCSTATE);
962
963 /*
964 * Set the CMOS date/time.
965 */
966 RTTIMESPEC Now;
967 PDMDevHlpTMUtcNow(pDevIns, &Now);
968 RTTIME Time;
969 if (pThis->fUTC)
970 RTTimeExplode(&Time, &Now);
971 else
972 RTTimeLocalExplode(&Time, &Now);
973
974 struct my_tm Tm;
975 memset(&Tm, 0, sizeof(Tm));
976 Tm.tm_year = Time.i32Year - 1900;
977 Tm.tm_mon = Time.u8Month - 1;
978 Tm.tm_mday = Time.u8MonthDay;
979 Tm.tm_wday = (Time.u8WeekDay + 1 + 7) % 7; /* 0 = Monday -> Sunday */
980 Tm.tm_yday = Time.u16YearDay - 1;
981 Tm.tm_hour = Time.u8Hour;
982 Tm.tm_min = Time.u8Minute;
983 Tm.tm_sec = Time.u8Second;
984
985 rtc_set_date(pThis, &Tm);
986
987 int iYear = to_bcd(pThis, (Tm.tm_year / 100) + 19); /* tm_year is 1900 based */
988 rtc_set_memory(pThis, 0x32, iYear); /* 32h - Century Byte (BCD value for the century */
989 rtc_set_memory(pThis, 0x37, iYear); /* 37h - (IBM PS/2) Date Century Byte */
990
991 /*
992 * Recalculate the checksum just in case.
993 */
994 rtcCalcCRC(pThis);
995
996 Log(("CMOS bank 0: \n%16.128Rhxd\n", &pThis->cmos_data[0]));
997 Log(("CMOS bank 1: \n%16.128Rhxd\n", &pThis->cmos_data[CMOS_BANK_SIZE]));
998 return VINF_SUCCESS;
999}
1000
1001
1002/**
1003 * @interface_method_impl{PDMDEVREG,pfnRelocate}
1004 */
1005static DECLCALLBACK(void) rtcRelocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
1006{
1007 PRTCSTATE pThis = PDMINS_2_DATA(pDevIns, PRTCSTATE);
1008
1009 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
1010 pThis->pPeriodicTimerRC = TMTimerRCPtr(pThis->pPeriodicTimerR3);
1011 pThis->pSecondTimerRC = TMTimerRCPtr(pThis->pSecondTimerR3);
1012 pThis->pSecondTimer2RC = TMTimerRCPtr(pThis->pSecondTimer2R3);
1013}
1014
1015
1016/**
1017 * @interface_method_impl{PDMDEVREG,pfnReset}
1018 */
1019static DECLCALLBACK(void) rtcReset(PPDMDEVINS pDevIns)
1020{
1021 PRTCSTATE pThis = PDMINS_2_DATA(pDevIns, PRTCSTATE);
1022
1023 /* If shutdown status is non-zero, log its value. */
1024 if (pThis->cmos_data[0xF])
1025 {
1026 LogRel(("CMOS shutdown status byte is %02X\n", pThis->cmos_data[0xF]));
1027
1028#if 0 /* It would be nice to log the warm reboot vector but alas, we already trashed it. */
1029 uint32_t u32WarmVector;
1030 int rc;
1031 rc = PDMDevHlpPhysRead(pDevIns, 0x467, &u32WarmVector, sizeof(u32WarmVector));
1032 AssertRC(rc);
1033 LogRel((", 40:67 contains %04X:%04X\n", u32WarmVector >> 16, u32WarmVector & 0xFFFF));
1034#endif
1035 /* If we're going to trash the VM's memory, we also have to clear this. */
1036 pThis->cmos_data[0xF] = 0;
1037 }
1038
1039 /* Reset index values (important for second bank). */
1040 pThis->cmos_index[0] = 0;
1041 pThis->cmos_index[1] = CMOS_BANK_SIZE; /* Point to start of second bank. */
1042}
1043
1044
1045/**
1046 * @interface_method_impl{PDMDEVREG,pfnConstruct}
1047 */
1048static DECLCALLBACK(int) rtcConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
1049{
1050 PRTCSTATE pThis = PDMINS_2_DATA(pDevIns, PRTCSTATE);
1051 int rc;
1052 Assert(iInstance == 0);
1053
1054 /*
1055 * Validate configuration.
1056 */
1057 if (!CFGMR3AreValuesValid(pCfg,
1058 "Irq\0"
1059 "Base\0"
1060 "UseUTC\0"
1061 "GCEnabled\0"
1062 "R0Enabled\0"))
1063 return VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES;
1064
1065 /*
1066 * Init the data.
1067 */
1068 uint8_t u8Irq;
1069 rc = CFGMR3QueryU8Def(pCfg, "Irq", &u8Irq, 8);
1070 if (RT_FAILURE(rc))
1071 return PDMDEV_SET_ERROR(pDevIns, rc,
1072 N_("Configuration error: Querying \"Irq\" as a uint8_t failed"));
1073 pThis->irq = u8Irq;
1074
1075 rc = CFGMR3QueryPortDef(pCfg, "Base", &pThis->IOPortBase, 0x70);
1076 if (RT_FAILURE(rc))
1077 return PDMDEV_SET_ERROR(pDevIns, rc,
1078 N_("Configuration error: Querying \"Base\" as a RTIOPORT failed"));
1079
1080 rc = CFGMR3QueryBoolDef(pCfg, "UseUTC", &pThis->fUTC, false);
1081 if (RT_FAILURE(rc))
1082 return PDMDEV_SET_ERROR(pDevIns, rc,
1083 N_("Configuration error: Querying \"UseUTC\" as a bool failed"));
1084
1085 bool fGCEnabled;
1086 rc = CFGMR3QueryBoolDef(pCfg, "GCEnabled", &fGCEnabled, true);
1087 if (RT_FAILURE(rc))
1088 return PDMDEV_SET_ERROR(pDevIns, rc,
1089 N_("Configuration error: failed to read GCEnabled as boolean"));
1090
1091 bool fR0Enabled;
1092 rc = CFGMR3QueryBoolDef(pCfg, "R0Enabled", &fR0Enabled, true);
1093 if (RT_FAILURE(rc))
1094 return PDMDEV_SET_ERROR(pDevIns, rc,
1095 N_("Configuration error: failed to read R0Enabled as boolean"));
1096
1097 Log(("RTC: Irq=%#x Base=%#x fGCEnabled=%RTbool fR0Enabled=%RTbool\n",
1098 u8Irq, pThis->IOPortBase, fGCEnabled, fR0Enabled));
1099
1100
1101 pThis->pDevInsR3 = pDevIns;
1102 pThis->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
1103 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
1104 pThis->cmos_data[RTC_REG_A] = 0x26;
1105 pThis->cmos_data[RTC_REG_B] = 0x02;
1106 pThis->cmos_data[RTC_REG_C] = 0x00;
1107 pThis->cmos_data[RTC_REG_D] = 0x80;
1108 pThis->RtcReg.u32Version = PDM_RTCREG_VERSION;
1109 pThis->RtcReg.pfnRead = rtcCMOSRead;
1110 pThis->RtcReg.pfnWrite = rtcCMOSWrite;
1111 pThis->fDisabledByHpet = false;
1112 pThis->cmos_index[1] = CMOS_BANK_SIZE; /* Point to start of second bank. */
1113
1114
1115 /* IBase */
1116 pDevIns->IBase.pfnQueryInterface = rtcQueryInterface;
1117 /* IHpetLegacyNotify */
1118 pThis->IHpetLegacyNotify.pfnModeChanged = rtcHpetLegacyNotify_ModeChanged;
1119
1120 /*
1121 * Create timers.
1122 */
1123 PTMTIMER pTimer;
1124 /* Periodic timer. */
1125 rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL_SYNC, rtcTimerPeriodic, pThis,
1126 TMTIMER_FLAGS_DEFAULT_CRIT_SECT, "MC146818 RTC/CMOS - Periodic",
1127 &pTimer);
1128 if (RT_FAILURE(rc))
1129 return rc;
1130 pThis->pPeriodicTimerR3 = pTimer;
1131 pThis->pPeriodicTimerR0 = TMTimerR0Ptr(pTimer);
1132 pThis->pPeriodicTimerRC = TMTimerRCPtr(pTimer);
1133
1134 /* Seconds timer. */
1135 rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL_SYNC, rtcTimerSecond, pThis,
1136 TMTIMER_FLAGS_DEFAULT_CRIT_SECT, "MC146818 RTC/CMOS - Second",
1137 &pTimer);
1138 if (RT_FAILURE(rc))
1139 return rc;
1140 pThis->pSecondTimerR3 = pTimer;
1141 pThis->pSecondTimerR0 = TMTimerR0Ptr(pTimer);
1142 pThis->pSecondTimerRC = TMTimerRCPtr(pTimer);
1143
1144 /* The second2 timer, this is always active. */
1145 rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL_SYNC, rtcTimerSecond2, pThis,
1146 TMTIMER_FLAGS_DEFAULT_CRIT_SECT, "MC146818 RTC/CMOS - Second2",
1147 &pTimer);
1148 if (RT_FAILURE(rc))
1149 return rc;
1150 pThis->pSecondTimer2R3 = pTimer;
1151 pThis->pSecondTimer2R0 = TMTimerR0Ptr(pTimer);
1152 pThis->pSecondTimer2RC = TMTimerRCPtr(pTimer);
1153 pThis->next_second_time = TMTimerGet(pTimer)
1154 + (TMTimerGetFreq(pTimer) * 99) / 100;
1155 rc = TMTimerLock(pTimer, VERR_IGNORED);
1156 AssertRCReturn(rc, rc);
1157 rc = TMTimerSet(pTimer, pThis->next_second_time);
1158 TMTimerUnlock(pTimer);
1159 AssertRCReturn(rc, rc);
1160
1161 /*
1162 * Register I/O ports.
1163 */
1164 rc = PDMDevHlpIOPortRegister(pDevIns, pThis->IOPortBase, 4, NULL,
1165 rtcIOPortWrite, rtcIOPortRead, NULL, NULL, "MC146818 RTC/CMOS");
1166 if (RT_FAILURE(rc))
1167 return rc;
1168 if (fGCEnabled)
1169 {
1170 rc = PDMDevHlpIOPortRegisterRC(pDevIns, pThis->IOPortBase, 4, NIL_RTRCPTR,
1171 "rtcIOPortWrite", "rtcIOPortRead", NULL, NULL, "MC146818 RTC/CMOS");
1172 if (RT_FAILURE(rc))
1173 return rc;
1174 }
1175 if (fR0Enabled)
1176 {
1177 rc = PDMDevHlpIOPortRegisterR0(pDevIns, pThis->IOPortBase, 4, NIL_RTR0PTR,
1178 "rtcIOPortWrite", "rtcIOPortRead", NULL, NULL, "MC146818 RTC/CMOS");
1179 if (RT_FAILURE(rc))
1180 return rc;
1181 }
1182
1183 /*
1184 * Register the saved state.
1185 */
1186 rc = PDMDevHlpSSMRegister3(pDevIns, RTC_SAVED_STATE_VERSION, sizeof(*pThis), rtcLiveExec, rtcSaveExec, rtcLoadExec);
1187 if (RT_FAILURE(rc))
1188 return rc;
1189
1190 /*
1191 * Register ourselves as the RTC/CMOS with PDM.
1192 */
1193 rc = PDMDevHlpRTCRegister(pDevIns, &pThis->RtcReg, &pThis->pRtcHlpR3);
1194 if (RT_FAILURE(rc))
1195 return rc;
1196
1197 /*
1198 * Register debugger info callback.
1199 */
1200 PDMDevHlpDBGFInfoRegister(pDevIns, "cmos1", "Display CMOS Bank 1 Info (0x0e-0x7f). No arguments. See also rtc.", rtcCmosBankInfo);
1201 PDMDevHlpDBGFInfoRegister(pDevIns, "cmos2", "Display CMOS Bank 2 Info (0x0e-0x7f). No arguments.", rtcCmosBank2Info);
1202 PDMDevHlpDBGFInfoRegister(pDevIns, "rtc", "Display CMOS RTC (0x00-0x0d). No arguments. See also cmos1 & cmos2", rtcCmosClockInfo);
1203 return VINF_SUCCESS;
1204}
1205
1206
1207/**
1208 * The device registration structure.
1209 */
1210const PDMDEVREG g_DeviceMC146818 =
1211{
1212 /* u32Version */
1213 PDM_DEVREG_VERSION,
1214 /* szName */
1215 "mc146818",
1216 /* szRCMod */
1217 "VBoxDDGC.gc",
1218 /* szR0Mod */
1219 "VBoxDDR0.r0",
1220 /* pszDescription */
1221 "Motorola MC146818 RTC/CMOS Device.",
1222 /* fFlags */
1223 PDM_DEVREG_FLAGS_HOST_BITS_DEFAULT | PDM_DEVREG_FLAGS_GUEST_BITS_32_64 | PDM_DEVREG_FLAGS_PAE36 | PDM_DEVREG_FLAGS_RC | PDM_DEVREG_FLAGS_R0,
1224 /* fClass */
1225 PDM_DEVREG_CLASS_RTC,
1226 /* cMaxInstances */
1227 1,
1228 /* cbInstance */
1229 sizeof(RTCSTATE),
1230 /* pfnConstruct */
1231 rtcConstruct,
1232 /* pfnDestruct */
1233 NULL,
1234 /* pfnRelocate */
1235 rtcRelocate,
1236 /* pfnIOCtl */
1237 NULL,
1238 /* pfnPowerOn */
1239 NULL,
1240 /* pfnReset */
1241 rtcReset,
1242 /* pfnSuspend */
1243 NULL,
1244 /* pfnResume */
1245 NULL,
1246 /* pfnAttach */
1247 NULL,
1248 /* pfnDetach */
1249 NULL,
1250 /* pfnQueryInterface */
1251 NULL,
1252 /* pfnInitComplete */
1253 rtcInitComplete,
1254 /* pfnPowerOff */
1255 NULL,
1256 /* pfnSoftReset */
1257 NULL,
1258 /* u32VersionEnd */
1259 PDM_DEVREG_VERSION
1260};
1261
1262#endif /* IN_RING3 */
1263#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
1264
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