VirtualBox

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

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

Device/Driver: 5986: Applying patch from qemu-devel related to "Hours in 12-hour mode are in the 1-12 range, not 0-11"

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