VirtualBox

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

Last change on this file since 42813 was 41710, checked in by vboxsync, 13 years ago

DevRTC: Extended the 'rtc' info with register A thru D.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 42.7 KB
Line 
1/* $Id: DevRTC.cpp 41710 2012-06-14 13:53:21Z 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.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 * --------------------------------------------------------------------
17 *
18 * This code is based on:
19 *
20 * QEMU MC146818 RTC emulation
21 *
22 * Copyright (c) 2003-2004 Fabrice Bellard
23 *
24 * Permission is hereby granted, free of charge, to any person obtaining a copy
25 * of this software and associated documentation files (the "Software"), to deal
26 * in the Software without restriction, including without limitation the rights
27 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
28 * copies of the Software, and to permit persons to whom the Software is
29 * furnished to do so, subject to the following conditions:
30 *
31 * The above copyright notice and this permission notice shall be included in
32 * all copies or substantial portions of the Software.
33 *
34 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
35 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
36 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
37 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
38 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
39 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
40 * THE SOFTWARE.
41 */
42
43/*******************************************************************************
44* Header Files *
45*******************************************************************************/
46#define LOG_GROUP LOG_GROUP_DEV_RTC
47#include <VBox/vmm/pdmdev.h>
48#include <VBox/log.h>
49#include <iprt/asm-math.h>
50#include <iprt/assert.h>
51#include <iprt/string.h>
52
53#ifdef IN_RING3
54# include <iprt/alloc.h>
55# include <iprt/uuid.h>
56#endif /* IN_RING3 */
57
58#include "VBoxDD.h"
59
60struct RTCState;
61typedef struct RTCState RTCState;
62
63#define RTC_CRC_START 0x10
64#define RTC_CRC_LAST 0x2d
65#define RTC_CRC_HIGH 0x2e
66#define RTC_CRC_LOW 0x2f
67
68
69/*******************************************************************************
70* Internal Functions *
71*******************************************************************************/
72#ifndef VBOX_DEVICE_STRUCT_TESTCASE
73RT_C_DECLS_BEGIN
74PDMBOTHCBDECL(int) rtcIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb);
75PDMBOTHCBDECL(int) rtcIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb);
76PDMBOTHCBDECL(void) rtcTimerPeriodic(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser);
77PDMBOTHCBDECL(void) rtcTimerSecond(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser);
78PDMBOTHCBDECL(void) rtcTimerSecond2(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser);
79RT_C_DECLS_END
80#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
81
82
83/*******************************************************************************
84* Defined Constants And Macros *
85*******************************************************************************/
86/*#define DEBUG_CMOS*/
87
88#define RTC_SECONDS 0
89#define RTC_SECONDS_ALARM 1
90#define RTC_MINUTES 2
91#define RTC_MINUTES_ALARM 3
92#define RTC_HOURS 4
93#define RTC_HOURS_ALARM 5
94#define RTC_ALARM_DONT_CARE 0xC0
95
96#define RTC_DAY_OF_WEEK 6
97#define RTC_DAY_OF_MONTH 7
98#define RTC_MONTH 8
99#define RTC_YEAR 9
100
101#define RTC_REG_A 10
102#define RTC_REG_B 11
103#define RTC_REG_C 12
104#define RTC_REG_D 13
105
106#define REG_A_UIP 0x80
107
108#define REG_B_SET 0x80
109#define REG_B_PIE 0x40
110#define REG_B_AIE 0x20
111#define REG_B_UIE 0x10
112
113#define CMOS_BANK_LOWER_LIMIT 0x0E
114#define CMOS_BANK_UPPER_LIMIT 0x7F
115#define CMOS_BANK2_LOWER_LIMIT 0x80
116#define CMOS_BANK2_UPPER_LIMIT 0xFF
117
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 pHlp->pfnPrintf(pHlp, "REG A=%02x B=%02x C=%02x D=%02x\n",
538 pThis->cmos_data[RTC_REG_A], pThis->cmos_data[RTC_REG_B],
539 pThis->cmos_data[RTC_REG_C], pThis->cmos_data[RTC_REG_D]);
540}
541
542
543
544/* -=-=-=-=-=- Timers and their support code -=-=-=-=-=- */
545
546
547/**
548 * Device timer callback function, periodic.
549 *
550 * @param pDevIns Device instance of the device which registered the timer.
551 * @param pTimer The timer handle.
552 * @param pvUser Pointer to the RTC state.
553 */
554static DECLCALLBACK(void) rtcTimerPeriodic(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
555{
556 RTCState *pThis = PDMINS_2_DATA(pDevIns, RTCState *);
557 Assert(TMTimerIsLockOwner(pThis->CTX_SUFF(pPeriodicTimer)));
558 Assert(PDMCritSectIsOwner(pThis->CTX_SUFF(pDevIns)->CTX_SUFF(pCritSectRo)));
559
560 rtc_timer_update(pThis, pThis->next_periodic_time);
561 pThis->cmos_data[RTC_REG_C] |= 0xc0;
562
563 rtc_raise_irq(pThis, 1);
564}
565
566
567/* month is between 0 and 11. */
568static int get_days_in_month(int month, int year)
569{
570 static const int days_tab[12] =
571 {
572 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
573 };
574 int d;
575
576 if ((unsigned )month >= 12)
577 return 31;
578
579 d = days_tab[month];
580 if (month == 1)
581 {
582 if ((year % 4) == 0 && ((year % 100) != 0 || (year % 400) == 0))
583 d++;
584 }
585 return d;
586}
587
588
589/* update 'tm' to the next second */
590static void rtc_next_second(struct my_tm *tm)
591{
592 int days_in_month;
593
594 tm->tm_sec++;
595 if ((unsigned)tm->tm_sec >= 60)
596 {
597 tm->tm_sec = 0;
598 tm->tm_min++;
599 if ((unsigned)tm->tm_min >= 60)
600 {
601 tm->tm_min = 0;
602 tm->tm_hour++;
603 if ((unsigned)tm->tm_hour >= 24)
604 {
605 tm->tm_hour = 0;
606 /* next day */
607 tm->tm_wday++;
608 if ((unsigned)tm->tm_wday >= 7)
609 tm->tm_wday = 0;
610 days_in_month = get_days_in_month(tm->tm_mon,
611 tm->tm_year + 1900);
612 tm->tm_mday++;
613 if (tm->tm_mday < 1)
614 tm->tm_mday = 1;
615 else if (tm->tm_mday > days_in_month)
616 {
617 tm->tm_mday = 1;
618 tm->tm_mon++;
619 if (tm->tm_mon >= 12)
620 {
621 tm->tm_mon = 0;
622 tm->tm_year++;
623 }
624 }
625 }
626 }
627 }
628}
629
630
631/**
632 * Device timer callback function, second.
633 *
634 * @param pDevIns Device instance of the device which registered the timer.
635 * @param pTimer The timer handle.
636 * @param pvUser Pointer to the RTC state.
637 */
638static DECLCALLBACK(void) rtcTimerSecond(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
639{
640 RTCState *pThis = PDMINS_2_DATA(pDevIns, RTCState *);
641 Assert(TMTimerIsLockOwner(pThis->CTX_SUFF(pPeriodicTimer)));
642 Assert(PDMCritSectIsOwner(pThis->CTX_SUFF(pDevIns)->CTX_SUFF(pCritSectRo)));
643
644 /* if the oscillator is not in normal operation, we do not update */
645 if ((pThis->cmos_data[RTC_REG_A] & 0x70) != 0x20)
646 {
647 pThis->next_second_time += TMTimerGetFreq(pThis->CTX_SUFF(pSecondTimer));
648 TMTimerSet(pThis->CTX_SUFF(pSecondTimer), pThis->next_second_time);
649 }
650 else
651 {
652 rtc_next_second(&pThis->current_tm);
653
654 if (!(pThis->cmos_data[RTC_REG_B] & REG_B_SET))
655 {
656 /* update in progress bit */
657 Log2(("RTC: UIP %x -> 1\n", !!(pThis->cmos_data[RTC_REG_A] & REG_A_UIP)));
658 pThis->cmos_data[RTC_REG_A] |= REG_A_UIP;
659 }
660
661 /* 244140 ns = 8 / 32768 seconds */
662 uint64_t delay = TMTimerFromNano(pThis->CTX_SUFF(pSecondTimer2), 244140);
663 TMTimerSet(pThis->CTX_SUFF(pSecondTimer2), pThis->next_second_time + delay);
664 }
665}
666
667
668/* Used by rtc_set_date and rtcTimerSecond2. */
669static void rtc_copy_date(RTCState *pThis)
670{
671 const struct my_tm *tm = &pThis->current_tm;
672
673 pThis->cmos_data[RTC_SECONDS] = to_bcd(pThis, tm->tm_sec);
674 pThis->cmos_data[RTC_MINUTES] = to_bcd(pThis, tm->tm_min);
675 if (pThis->cmos_data[RTC_REG_B] & 0x02)
676 {
677 /* 24 hour format */
678 pThis->cmos_data[RTC_HOURS] = to_bcd(pThis, tm->tm_hour);
679 }
680 else
681 {
682 /* 12 hour format */
683 pThis->cmos_data[RTC_HOURS] = to_bcd(pThis, tm->tm_hour % 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 * Device timer callback function, second2.
696 *
697 * @param pDevIns Device instance of the device which registered the timer.
698 * @param pTimer The timer handle.
699 * @param pvUser Pointer to the RTC state.
700 */
701static DECLCALLBACK(void) rtcTimerSecond2(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
702{
703 RTCState *pThis = PDMINS_2_DATA(pDevIns, RTCState *);
704 Assert(TMTimerIsLockOwner(pThis->CTX_SUFF(pPeriodicTimer)));
705 Assert(PDMCritSectIsOwner(pThis->CTX_SUFF(pDevIns)->CTX_SUFF(pCritSectRo)));
706
707 if (!(pThis->cmos_data[RTC_REG_B] & REG_B_SET))
708 rtc_copy_date(pThis);
709
710 /* check alarm */
711 if (pThis->cmos_data[RTC_REG_B] & REG_B_AIE)
712 {
713 if ( ( (pThis->cmos_data[RTC_SECONDS_ALARM] & 0xc0) == 0xc0
714 || from_bcd(pThis, pThis->cmos_data[RTC_SECONDS_ALARM]) == pThis->current_tm.tm_sec)
715 && ( (pThis->cmos_data[RTC_MINUTES_ALARM] & 0xc0) == 0xc0
716 || from_bcd(pThis, pThis->cmos_data[RTC_MINUTES_ALARM]) == pThis->current_tm.tm_min)
717 && ( (pThis->cmos_data[RTC_HOURS_ALARM ] & 0xc0) == 0xc0
718 || from_bcd(pThis, pThis->cmos_data[RTC_HOURS_ALARM ]) == pThis->current_tm.tm_hour)
719 )
720 {
721 pThis->cmos_data[RTC_REG_C] |= 0xa0;
722 rtc_raise_irq(pThis, 1);
723 }
724 }
725
726 /* update ended interrupt */
727 if (pThis->cmos_data[RTC_REG_B] & REG_B_UIE)
728 {
729 pThis->cmos_data[RTC_REG_C] |= 0x90;
730 rtc_raise_irq(pThis, 1);
731 }
732
733 /* clear update in progress bit */
734 Log2(("RTC: UIP %x -> 0\n", !!(pThis->cmos_data[RTC_REG_A] & REG_A_UIP)));
735 pThis->cmos_data[RTC_REG_A] &= ~REG_A_UIP;
736
737 pThis->next_second_time += TMTimerGetFreq(pThis->CTX_SUFF(pSecondTimer));
738 TMTimerSet(pThis->CTX_SUFF(pSecondTimer), pThis->next_second_time);
739}
740
741
742/* -=-=-=-=-=- Saved State -=-=-=-=-=- */
743
744
745/**
746 * @copydoc FNSSMDEVLIVEEXEC
747 */
748static DECLCALLBACK(int) rtcLiveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uPass)
749{
750 RTCState *pThis = PDMINS_2_DATA(pDevIns, RTCState *);
751
752 SSMR3PutU8( pSSM, pThis->irq);
753 SSMR3PutIOPort(pSSM, pThis->IOPortBase);
754 SSMR3PutBool( pSSM, pThis->fUTC);
755
756 return VINF_SSM_DONT_CALL_AGAIN;
757}
758
759
760/**
761 * @copydoc FNSSMDEVSAVEEXEC
762 */
763static DECLCALLBACK(int) rtcSaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
764{
765 RTCState *pThis = PDMINS_2_DATA(pDevIns, RTCState *);
766
767 /* The config. */
768 rtcLiveExec(pDevIns, pSSM, SSM_PASS_FINAL);
769
770 /* The state. */
771 SSMR3PutMem(pSSM, pThis->cmos_data, 128);
772 SSMR3PutU8(pSSM, pThis->cmos_index[0]);
773
774 SSMR3PutS32(pSSM, pThis->current_tm.tm_sec);
775 SSMR3PutS32(pSSM, pThis->current_tm.tm_min);
776 SSMR3PutS32(pSSM, pThis->current_tm.tm_hour);
777 SSMR3PutS32(pSSM, pThis->current_tm.tm_wday);
778 SSMR3PutS32(pSSM, pThis->current_tm.tm_mday);
779 SSMR3PutS32(pSSM, pThis->current_tm.tm_mon);
780 SSMR3PutS32(pSSM, pThis->current_tm.tm_year);
781
782 TMR3TimerSave(pThis->CTX_SUFF(pPeriodicTimer), pSSM);
783
784 SSMR3PutS64(pSSM, pThis->next_periodic_time);
785
786 SSMR3PutS64(pSSM, pThis->next_second_time);
787 TMR3TimerSave(pThis->CTX_SUFF(pSecondTimer), pSSM);
788 TMR3TimerSave(pThis->CTX_SUFF(pSecondTimer2), pSSM);
789
790 SSMR3PutBool(pSSM, pThis->fDisabledByHpet);
791
792 SSMR3PutMem(pSSM, &pThis->cmos_data[128], 128);
793 return SSMR3PutU8(pSSM, pThis->cmos_index[1]);
794}
795
796
797/**
798 * @copydoc FNSSMDEVLOADEXEC
799 */
800static DECLCALLBACK(int) rtcLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
801{
802 RTCState *pThis = PDMINS_2_DATA(pDevIns, RTCState *);
803 int rc;
804
805 if ( uVersion != RTC_SAVED_STATE_VERSION
806 && uVersion != RTC_SAVED_STATE_VERSION_VBOX_32PRE
807 && uVersion != RTC_SAVED_STATE_VERSION_VBOX_31
808 && uVersion != RTC_SAVED_STATE_VERSION_VBOX_30)
809 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
810
811 /* The config. */
812 if (uVersion > RTC_SAVED_STATE_VERSION_VBOX_30)
813 {
814 uint8_t u8Irq;
815 rc = SSMR3GetU8(pSSM, &u8Irq); AssertRCReturn(rc, rc);
816 if (u8Irq != pThis->irq)
817 return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Config mismatch - u8Irq: saved=%#x config=%#x"), u8Irq, pThis->irq);
818
819 RTIOPORT IOPortBase;
820 rc = SSMR3GetIOPort(pSSM, &IOPortBase); AssertRCReturn(rc, rc);
821 if (IOPortBase != pThis->IOPortBase)
822 return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Config mismatch - IOPortBase: saved=%RTiop config=%RTiop"), IOPortBase, pThis->IOPortBase);
823
824 bool fUTC;
825 rc = SSMR3GetBool(pSSM, &fUTC); AssertRCReturn(rc, rc);
826 if (fUTC != pThis->fUTC)
827 LogRel(("RTC: Config mismatch - fUTC: saved=%RTbool config=%RTbool\n", fUTC, pThis->fUTC));
828 }
829
830 if (uPass != SSM_PASS_FINAL)
831 return VINF_SUCCESS;
832
833 /* The state. */
834 SSMR3GetMem(pSSM, pThis->cmos_data, 128);
835 SSMR3GetU8(pSSM, &pThis->cmos_index[0]);
836
837 SSMR3GetS32(pSSM, &pThis->current_tm.tm_sec);
838 SSMR3GetS32(pSSM, &pThis->current_tm.tm_min);
839 SSMR3GetS32(pSSM, &pThis->current_tm.tm_hour);
840 SSMR3GetS32(pSSM, &pThis->current_tm.tm_wday);
841 SSMR3GetS32(pSSM, &pThis->current_tm.tm_mday);
842 SSMR3GetS32(pSSM, &pThis->current_tm.tm_mon);
843 SSMR3GetS32(pSSM, &pThis->current_tm.tm_year);
844
845 TMR3TimerLoad(pThis->CTX_SUFF(pPeriodicTimer), pSSM);
846
847 SSMR3GetS64(pSSM, &pThis->next_periodic_time);
848
849 SSMR3GetS64(pSSM, &pThis->next_second_time);
850 TMR3TimerLoad(pThis->CTX_SUFF(pSecondTimer), pSSM);
851 TMR3TimerLoad(pThis->CTX_SUFF(pSecondTimer2), pSSM);
852
853 if (uVersion > RTC_SAVED_STATE_VERSION_VBOX_31)
854 SSMR3GetBool(pSSM, &pThis->fDisabledByHpet);
855
856 if (uVersion > RTC_SAVED_STATE_VERSION_VBOX_32PRE)
857 {
858 /* Second CMOS bank. */
859 SSMR3GetMem(pSSM, &pThis->cmos_data[128], 128);
860 SSMR3GetU8(pSSM, &pThis->cmos_index[1]);
861 }
862
863 int period_code = pThis->cmos_data[RTC_REG_A] & 0x0f;
864 if ( period_code != 0
865 && (pThis->cmos_data[RTC_REG_B] & REG_B_PIE))
866 {
867 if (period_code <= 2)
868 period_code += 7;
869 int period = 1 << (period_code - 1);
870 LogRel(("RTC: period=%#x (%d) %u Hz (restore)\n", period, period, _32K / period));
871 PDMCritSectEnter(pThis->pDevInsR3->pCritSectRoR3, VINF_SUCCESS);
872 TMTimerSetFrequencyHint(pThis->CTX_SUFF(pPeriodicTimer), _32K / period);
873 PDMCritSectLeave(pThis->pDevInsR3->pCritSectRoR3);
874 pThis->CurLogPeriod = period;
875 pThis->CurHintPeriod = period;
876 }
877 else
878 {
879 LogRel(("RTC: stopped the periodic timer (restore)\n"));
880 pThis->CurLogPeriod = 0;
881 pThis->CurHintPeriod = 0;
882 }
883 pThis->cRelLogEntries = 0;
884
885 return VINF_SUCCESS;
886}
887
888
889/* -=-=-=-=-=- PDM Interface provided by the RTC device -=-=-=-=-=- */
890
891/**
892 * Calculate and update the standard CMOS checksum.
893 *
894 * @param pThis Pointer to the RTC state data.
895 */
896static void rtcCalcCRC(RTCState *pThis)
897{
898 uint16_t u16;
899 unsigned i;
900
901 for (i = RTC_CRC_START, u16 = 0; i <= RTC_CRC_LAST; i++)
902 u16 += pThis->cmos_data[i];
903 pThis->cmos_data[RTC_CRC_LOW] = u16 & 0xff;
904 pThis->cmos_data[RTC_CRC_HIGH] = (u16 >> 8) & 0xff;
905}
906
907
908/**
909 * Write to a CMOS register and update the checksum if necessary.
910 *
911 * @returns VBox status code.
912 * @param pDevIns Device instance of the RTC.
913 * @param iReg The CMOS register index; bit 8 determines bank.
914 * @param u8Value The CMOS register value.
915 */
916static DECLCALLBACK(int) rtcCMOSWrite(PPDMDEVINS pDevIns, unsigned iReg, uint8_t u8Value)
917{
918 RTCState *pThis = PDMINS_2_DATA(pDevIns, RTCState *);
919 if (iReg < RT_ELEMENTS(pThis->cmos_data))
920 {
921 PDMCritSectEnter(pDevIns->pCritSectRoR3, VERR_IGNORED);
922
923 pThis->cmos_data[iReg] = u8Value;
924
925 /* does it require checksum update? */
926 if ( iReg >= RTC_CRC_START
927 && iReg <= RTC_CRC_LAST)
928 rtcCalcCRC(pThis);
929
930 PDMCritSectLeave(pDevIns->pCritSectRoR3);
931 return VINF_SUCCESS;
932 }
933
934 AssertMsgFailed(("iReg=%d\n", iReg));
935 return VERR_INVALID_PARAMETER;
936}
937
938
939/**
940 * Read a CMOS register.
941 *
942 * @returns VBox status code.
943 * @param pDevIns Device instance of the RTC.
944 * @param iReg The CMOS register index; bit 8 determines bank.
945 * @param pu8Value Where to store the CMOS register value.
946 */
947static DECLCALLBACK(int) rtcCMOSRead(PPDMDEVINS pDevIns, unsigned iReg, uint8_t *pu8Value)
948{
949 RTCState *pThis = PDMINS_2_DATA(pDevIns, RTCState *);
950 if (iReg < RT_ELEMENTS(pThis->cmos_data))
951 {
952 PDMCritSectEnter(pDevIns->pCritSectRoR3, VERR_IGNORED);
953
954 *pu8Value = pThis->cmos_data[iReg];
955
956 PDMCritSectLeave(pDevIns->pCritSectRoR3);
957 return VINF_SUCCESS;
958 }
959 AssertMsgFailed(("iReg=%d\n", iReg));
960 return VERR_INVALID_PARAMETER;
961}
962
963
964/**
965 * @interface_method_impl{PDMIHPETLEGACYNOTIFY,pfnModeChanged}
966 */
967static DECLCALLBACK(void) rtcHpetLegacyNotify_ModeChanged(PPDMIHPETLEGACYNOTIFY pInterface, bool fActivated)
968{
969 RTCState *pThis = RT_FROM_MEMBER(pInterface, RTCState, IHpetLegacyNotify);
970 PDMCritSectEnter(pThis->pDevInsR3->pCritSectRoR3, VERR_IGNORED);
971
972 pThis->fDisabledByHpet = fActivated;
973
974 PDMCritSectLeave(pThis->pDevInsR3->pCritSectRoR3);
975}
976
977
978/* -=-=-=-=-=- based on bits from pc.c -=-=-=-=-=- */
979
980
981static void rtc_set_memory(RTCState *pThis, int addr, int val)
982{
983 if (addr >= 0 && addr <= 127)
984 pThis->cmos_data[addr] = val;
985}
986
987
988static void rtc_set_date(RTCState *pThis, const struct my_tm *tm)
989{
990 pThis->current_tm = *tm;
991 rtc_copy_date(pThis);
992}
993
994
995/** @copydoc FNPDMDEVINITCOMPLETE */
996static DECLCALLBACK(int) rtcInitComplete(PPDMDEVINS pDevIns)
997{
998 /** @todo this should be (re)done at power on if we didn't load a state... */
999 RTCState *pThis = PDMINS_2_DATA(pDevIns, RTCState *);
1000
1001 /*
1002 * Set the CMOS date/time.
1003 */
1004 RTTIMESPEC Now;
1005 PDMDevHlpTMUtcNow(pDevIns, &Now);
1006 RTTIME Time;
1007 if (pThis->fUTC)
1008 RTTimeExplode(&Time, &Now);
1009 else
1010 RTTimeLocalExplode(&Time, &Now);
1011
1012 struct my_tm Tm;
1013 memset(&Tm, 0, sizeof(Tm));
1014 Tm.tm_year = Time.i32Year - 1900;
1015 Tm.tm_mon = Time.u8Month - 1;
1016 Tm.tm_mday = Time.u8MonthDay;
1017 Tm.tm_wday = (Time.u8WeekDay + 1 + 7) % 7; /* 0 = Monday -> Sunday */
1018 Tm.tm_yday = Time.u16YearDay - 1;
1019 Tm.tm_hour = Time.u8Hour;
1020 Tm.tm_min = Time.u8Minute;
1021 Tm.tm_sec = Time.u8Second;
1022
1023 rtc_set_date(pThis, &Tm);
1024
1025 int iYear = to_bcd(pThis, (Tm.tm_year / 100) + 19); /* tm_year is 1900 based */
1026 rtc_set_memory(pThis, 0x32, iYear); /* 32h - Century Byte (BCD value for the century */
1027 rtc_set_memory(pThis, 0x37, iYear); /* 37h - (IBM PS/2) Date Century Byte */
1028
1029 /*
1030 * Recalculate the checksum just in case.
1031 */
1032 rtcCalcCRC(pThis);
1033
1034 Log(("CMOS bank 0: \n%16.128Rhxd\n", &pThis->cmos_data[0]));
1035 Log(("CMOS bank 1: \n%16.128Rhxd\n", &pThis->cmos_data[128]));
1036 return VINF_SUCCESS;
1037}
1038
1039
1040/* -=-=-=-=-=- real code -=-=-=-=-=- */
1041
1042/**
1043 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
1044 */
1045static DECLCALLBACK(void *) rtcQueryInterface(PPDMIBASE pInterface, const char *pszIID)
1046{
1047 PPDMDEVINS pDevIns = RT_FROM_MEMBER(pInterface, PDMDEVINS, IBase);
1048 RTCState *pThis = PDMINS_2_DATA(pDevIns, RTCState *);
1049 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDevIns->IBase);
1050 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHPETLEGACYNOTIFY, &pThis->IHpetLegacyNotify);
1051 return NULL;
1052}
1053
1054/**
1055 * @copydoc
1056 */
1057static DECLCALLBACK(void) rtcRelocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
1058{
1059 RTCState *pThis = PDMINS_2_DATA(pDevIns, RTCState *);
1060
1061 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
1062 pThis->pPeriodicTimerRC = TMTimerRCPtr(pThis->pPeriodicTimerR3);
1063 pThis->pSecondTimerRC = TMTimerRCPtr(pThis->pSecondTimerR3);
1064 pThis->pSecondTimer2RC = TMTimerRCPtr(pThis->pSecondTimer2R3);
1065}
1066
1067
1068/**
1069 * @copydoc
1070 */
1071static DECLCALLBACK(void) rtcReset(PPDMDEVINS pDevIns)
1072{
1073 RTCState *pThis = PDMINS_2_DATA(pDevIns, RTCState *);
1074
1075 /* If shutdown status is non-zero, log its value. */
1076 if (pThis->cmos_data[0xF])
1077 {
1078 LogRel(("CMOS shutdown status byte is %02X\n", pThis->cmos_data[0xF]));
1079
1080#if 0 /* It would be nice to log the warm reboot vector but alas, we already trashed it. */
1081 uint32_t u32WarmVector;
1082 int rc;
1083 rc = PDMDevHlpPhysRead(pDevIns, 0x467, &u32WarmVector, sizeof(u32WarmVector));
1084 AssertRC(rc);
1085 LogRel((", 40:67 contains %04X:%04X\n", u32WarmVector >> 16, u32WarmVector & 0xFFFF));
1086#endif
1087 /* If we're going to trash the VM's memory, we also have to clear this. */
1088 pThis->cmos_data[0xF] = 0;
1089 }
1090}
1091
1092
1093/**
1094 * @interface_method_impl{PDMDEVREG,pfnConstruct}
1095 */
1096static DECLCALLBACK(int) rtcConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
1097{
1098 RTCState *pThis = PDMINS_2_DATA(pDevIns, RTCState *);
1099 int rc;
1100 Assert(iInstance == 0);
1101
1102 /*
1103 * Validate configuration.
1104 */
1105 if (!CFGMR3AreValuesValid(pCfg,
1106 "Irq\0"
1107 "Base\0"
1108 "UseUTC\0"
1109 "GCEnabled\0"
1110 "R0Enabled\0"))
1111 return VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES;
1112
1113 /*
1114 * Init the data.
1115 */
1116 uint8_t u8Irq;
1117 rc = CFGMR3QueryU8Def(pCfg, "Irq", &u8Irq, 8);
1118 if (RT_FAILURE(rc))
1119 return PDMDEV_SET_ERROR(pDevIns, rc,
1120 N_("Configuration error: Querying \"Irq\" as a uint8_t failed"));
1121 pThis->irq = u8Irq;
1122
1123 rc = CFGMR3QueryPortDef(pCfg, "Base", &pThis->IOPortBase, 0x70);
1124 if (RT_FAILURE(rc))
1125 return PDMDEV_SET_ERROR(pDevIns, rc,
1126 N_("Configuration error: Querying \"Base\" as a RTIOPORT failed"));
1127
1128 rc = CFGMR3QueryBoolDef(pCfg, "UseUTC", &pThis->fUTC, false);
1129 if (RT_FAILURE(rc))
1130 return PDMDEV_SET_ERROR(pDevIns, rc,
1131 N_("Configuration error: Querying \"UseUTC\" as a bool failed"));
1132
1133 bool fGCEnabled;
1134 rc = CFGMR3QueryBoolDef(pCfg, "GCEnabled", &fGCEnabled, true);
1135 if (RT_FAILURE(rc))
1136 return PDMDEV_SET_ERROR(pDevIns, rc,
1137 N_("Configuration error: failed to read GCEnabled as boolean"));
1138
1139 bool fR0Enabled;
1140 rc = CFGMR3QueryBoolDef(pCfg, "R0Enabled", &fR0Enabled, true);
1141 if (RT_FAILURE(rc))
1142 return PDMDEV_SET_ERROR(pDevIns, rc,
1143 N_("Configuration error: failed to read R0Enabled as boolean"));
1144
1145 Log(("RTC: Irq=%#x Base=%#x fGCEnabled=%RTbool fR0Enabled=%RTbool\n",
1146 u8Irq, pThis->IOPortBase, fGCEnabled, fR0Enabled));
1147
1148
1149 pThis->pDevInsR3 = pDevIns;
1150 pThis->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
1151 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
1152 pThis->cmos_data[RTC_REG_A] = 0x26;
1153 pThis->cmos_data[RTC_REG_B] = 0x02;
1154 pThis->cmos_data[RTC_REG_C] = 0x00;
1155 pThis->cmos_data[RTC_REG_D] = 0x80;
1156 pThis->RtcReg.u32Version = PDM_RTCREG_VERSION;
1157 pThis->RtcReg.pfnRead = rtcCMOSRead;
1158 pThis->RtcReg.pfnWrite = rtcCMOSWrite;
1159 pThis->fDisabledByHpet = false;
1160
1161 /* IBase */
1162 pDevIns->IBase.pfnQueryInterface = rtcQueryInterface;
1163 /* IHpetLegacyNotify */
1164 pThis->IHpetLegacyNotify.pfnModeChanged = rtcHpetLegacyNotify_ModeChanged;
1165
1166 /*
1167 * Create timers.
1168 */
1169 PTMTIMER pTimer;
1170 /* Periodic timer. */
1171 rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL_SYNC, rtcTimerPeriodic, pThis,
1172 TMTIMER_FLAGS_DEFAULT_CRIT_SECT, "MC146818 RTC/CMOS - Periodic",
1173 &pTimer);
1174 if (RT_FAILURE(rc))
1175 return rc;
1176 pThis->pPeriodicTimerR3 = pTimer;
1177 pThis->pPeriodicTimerR0 = TMTimerR0Ptr(pTimer);
1178 pThis->pPeriodicTimerRC = TMTimerRCPtr(pTimer);
1179
1180 /* Seconds timer. */
1181 rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL_SYNC, rtcTimerSecond, pThis,
1182 TMTIMER_FLAGS_DEFAULT_CRIT_SECT, "MC146818 RTC/CMOS - Second",
1183 &pTimer);
1184 if (RT_FAILURE(rc))
1185 return rc;
1186 pThis->pSecondTimerR3 = pTimer;
1187 pThis->pSecondTimerR0 = TMTimerR0Ptr(pTimer);
1188 pThis->pSecondTimerRC = TMTimerRCPtr(pTimer);
1189
1190 /* The second2 timer, this is always active. */
1191 rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL_SYNC, rtcTimerSecond2, pThis,
1192 TMTIMER_FLAGS_DEFAULT_CRIT_SECT, "MC146818 RTC/CMOS - Second2",
1193 &pTimer);
1194 if (RT_FAILURE(rc))
1195 return rc;
1196 pThis->pSecondTimer2R3 = pTimer;
1197 pThis->pSecondTimer2R0 = TMTimerR0Ptr(pTimer);
1198 pThis->pSecondTimer2RC = TMTimerRCPtr(pTimer);
1199 pThis->next_second_time = TMTimerGet(pTimer)
1200 + (TMTimerGetFreq(pTimer) * 99) / 100;
1201 rc = TMTimerLock(pTimer, VERR_IGNORED);
1202 AssertRCReturn(rc, rc);
1203 rc = TMTimerSet(pTimer, pThis->next_second_time);
1204 TMTimerUnlock(pTimer);
1205 AssertRCReturn(rc, rc);
1206
1207 /*
1208 * Register I/O ports.
1209 */
1210 rc = PDMDevHlpIOPortRegister(pDevIns, pThis->IOPortBase, 4, NULL,
1211 rtcIOPortWrite, rtcIOPortRead, NULL, NULL, "MC146818 RTC/CMOS");
1212 if (RT_FAILURE(rc))
1213 return rc;
1214 if (fGCEnabled)
1215 {
1216 rc = PDMDevHlpIOPortRegisterRC(pDevIns, pThis->IOPortBase, 4, NIL_RTRCPTR,
1217 "rtcIOPortWrite", "rtcIOPortRead", NULL, NULL, "MC146818 RTC/CMOS");
1218 if (RT_FAILURE(rc))
1219 return rc;
1220 }
1221 if (fR0Enabled)
1222 {
1223 rc = PDMDevHlpIOPortRegisterR0(pDevIns, pThis->IOPortBase, 4, NIL_RTR0PTR,
1224 "rtcIOPortWrite", "rtcIOPortRead", NULL, NULL, "MC146818 RTC/CMOS");
1225 if (RT_FAILURE(rc))
1226 return rc;
1227 }
1228
1229 /*
1230 * Register the saved state.
1231 */
1232 rc = PDMDevHlpSSMRegister3(pDevIns, RTC_SAVED_STATE_VERSION, sizeof(*pThis), rtcLiveExec, rtcSaveExec, rtcLoadExec);
1233 if (RT_FAILURE(rc))
1234 return rc;
1235
1236 /*
1237 * Register ourselves as the RTC/CMOS with PDM.
1238 */
1239 rc = PDMDevHlpRTCRegister(pDevIns, &pThis->RtcReg, &pThis->pRtcHlpR3);
1240 if (RT_FAILURE(rc))
1241 return rc;
1242
1243 /*
1244 * Register debugger info callback.
1245 */
1246 PDMDevHlpDBGFInfoRegister(pDevIns, "cmos1", "Display CMOS Bank 1 Info (0x0e-0x7f). No arguments. See also rtc.", rtcCmosBankInfo);
1247 PDMDevHlpDBGFInfoRegister(pDevIns, "cmos2", "Display CMOS Bank 2 Info (0x0e-0x7f). No arguments.", rtcCmosBank2Info);
1248 PDMDevHlpDBGFInfoRegister(pDevIns, "rtc", "Display CMOS RTC (0x00-0x0d). No arguments. See also cmos1 & cmos2", rtcCmosClockInfo);
1249 return VINF_SUCCESS;
1250}
1251
1252
1253/**
1254 * The device registration structure.
1255 */
1256const PDMDEVREG g_DeviceMC146818 =
1257{
1258 /* u32Version */
1259 PDM_DEVREG_VERSION,
1260 /* szName */
1261 "mc146818",
1262 /* szRCMod */
1263 "VBoxDDGC.gc",
1264 /* szR0Mod */
1265 "VBoxDDR0.r0",
1266 /* pszDescription */
1267 "Motorola MC146818 RTC/CMOS Device.",
1268 /* fFlags */
1269 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,
1270 /* fClass */
1271 PDM_DEVREG_CLASS_RTC,
1272 /* cMaxInstances */
1273 1,
1274 /* cbInstance */
1275 sizeof(RTCState),
1276 /* pfnConstruct */
1277 rtcConstruct,
1278 /* pfnDestruct */
1279 NULL,
1280 /* pfnRelocate */
1281 rtcRelocate,
1282 /* pfnIOCtl */
1283 NULL,
1284 /* pfnPowerOn */
1285 NULL,
1286 /* pfnReset */
1287 rtcReset,
1288 /* pfnSuspend */
1289 NULL,
1290 /* pfnResume */
1291 NULL,
1292 /* pfnAttach */
1293 NULL,
1294 /* pfnDetach */
1295 NULL,
1296 /* pfnQueryInterface */
1297 NULL,
1298 /* pfnInitComplete */
1299 rtcInitComplete,
1300 /* pfnPowerOff */
1301 NULL,
1302 /* pfnSoftReset */
1303 NULL,
1304 /* u32VersionEnd */
1305 PDM_DEVREG_VERSION
1306};
1307
1308#endif /* IN_RING3 */
1309#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
1310
Note: See TracBrowser for help on using the repository browser.

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