VirtualBox

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

最後變更 在這個檔案從41709是 41709,由 vboxsync 提交於 13 年 前

Moved the code to the right placed.

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

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