VirtualBox

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

Last change on this file since 53426 was 53116, checked in by vboxsync, 10 years ago

DevRTC: don't spam the R0 log with "RTC: stopped the periodic timer" messages

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