VirtualBox

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

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

RTC: Clear CMOS shutdown byte on reset.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use