VirtualBox

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

Last change on this file since 41681 was 41667, checked in by vboxsync, 13 years ago

Compacted CMOS debug info output.

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette