VirtualBox

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

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

header (C) fixes

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