VirtualBox

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

最後變更 在這個檔案從62966是 62890,由 vboxsync 提交於 9 年 前

Devices: warnings

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

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