VirtualBox

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

Last change on this file since 43712 was 43241, checked in by vboxsync, 12 years ago

RTC: Do not corrupt CMOS bank 1 by writing to bank 2 without writing bank 2 index first.

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