VirtualBox

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

Last change on this file since 49469 was 48947, checked in by vboxsync, 11 years ago

Devices: Whitespace and svn:keyword cleanups by scm.

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