VirtualBox

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

Last change on this file since 41603 was 41599, checked in by vboxsync, 13 years ago

CMOS Debuging: As suggested by Michal, moved the debug information functions from DevPcBios.cpp to DevRTC.cpp.
(Debug Functions: info cmos (CMOSBankInfo), info cmos2(CMOSBank2Info), info rtc (CMOSRTCInfo)

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

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