VirtualBox

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

Last change on this file since 38188 was 37526, checked in by vboxsync, 13 years ago

Dev*: Timer locking fixes.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 38.3 KB
Line 
1/* $Id: DevRTC.cpp 37526 2011-06-17 10:17:38Z 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 if (period_code <= 2)
790 period_code += 7;
791 int period = 1 << (period_code - 1);
792 LogRel(("RTC: period=%#x (%d) %u Hz (restore)\n", period, period, _32K / period));
793 TMTimerSetFrequencyHint(pThis->CTX_SUFF(pPeriodicTimer), _32K / period);
794 pThis->CurLogPeriod = period;
795 pThis->CurHintPeriod = period;
796 } else {
797 LogRel(("RTC: stopped the periodic timer (restore)\n"));
798 pThis->CurLogPeriod = 0;
799 pThis->CurHintPeriod = 0;
800 }
801 pThis->cRelLogEntries = 0;
802
803 return VINF_SUCCESS;
804}
805
806
807/* -=-=-=-=-=- PDM Interface provided by the RTC device -=-=-=-=-=- */
808
809/**
810 * Calculate and update the standard CMOS checksum.
811 *
812 * @param pThis Pointer to the RTC state data.
813 */
814static void rtcCalcCRC(RTCState *pThis)
815{
816 uint16_t u16;
817 unsigned i;
818
819 for (i = RTC_CRC_START, u16 = 0; i <= RTC_CRC_LAST; i++)
820 u16 += pThis->cmos_data[i];
821 pThis->cmos_data[RTC_CRC_LOW] = u16 & 0xff;
822 pThis->cmos_data[RTC_CRC_HIGH] = (u16 >> 8) & 0xff;
823}
824
825
826/**
827 * Write to a CMOS register and update the checksum if necessary.
828 *
829 * @returns VBox status code.
830 * @param pDevIns Device instance of the RTC.
831 * @param iReg The CMOS register index; bit 8 determines bank.
832 * @param u8Value The CMOS register value.
833 */
834static DECLCALLBACK(int) rtcCMOSWrite(PPDMDEVINS pDevIns, unsigned iReg, uint8_t u8Value)
835{
836 RTCState *pThis = PDMINS_2_DATA(pDevIns, RTCState *);
837 if (iReg < RT_ELEMENTS(pThis->cmos_data))
838 {
839 PDMCritSectEnter(pDevIns->pCritSectRoR3, VERR_IGNORED);
840
841 pThis->cmos_data[iReg] = u8Value;
842
843 /* does it require checksum update? */
844 if ( iReg >= RTC_CRC_START
845 && iReg <= RTC_CRC_LAST)
846 rtcCalcCRC(pThis);
847
848 PDMCritSectLeave(pDevIns->pCritSectRoR3);
849 return VINF_SUCCESS;
850 }
851
852 AssertMsgFailed(("iReg=%d\n", iReg));
853 return VERR_INVALID_PARAMETER;
854}
855
856
857/**
858 * Read a CMOS register.
859 *
860 * @returns VBox status code.
861 * @param pDevIns Device instance of the RTC.
862 * @param iReg The CMOS register index; bit 8 determines bank.
863 * @param pu8Value Where to store the CMOS register value.
864 */
865static DECLCALLBACK(int) rtcCMOSRead(PPDMDEVINS pDevIns, unsigned iReg, uint8_t *pu8Value)
866{
867 RTCState *pThis = PDMINS_2_DATA(pDevIns, RTCState *);
868 if (iReg < RT_ELEMENTS(pThis->cmos_data))
869 {
870 PDMCritSectEnter(pDevIns->pCritSectRoR3, VERR_IGNORED);
871
872 *pu8Value = pThis->cmos_data[iReg];
873
874 PDMCritSectLeave(pDevIns->pCritSectRoR3);
875 return VINF_SUCCESS;
876 }
877 AssertMsgFailed(("iReg=%d\n", iReg));
878 return VERR_INVALID_PARAMETER;
879}
880
881
882/**
883 * @interface_method_impl{PDMIHPETLEGACYNOTIFY,pfnModeChanged}
884 */
885static DECLCALLBACK(void) rtcHpetLegacyNotify_ModeChanged(PPDMIHPETLEGACYNOTIFY pInterface, bool fActivated)
886{
887 RTCState *pThis = RT_FROM_MEMBER(pInterface, RTCState, IHpetLegacyNotify);
888 PDMCritSectEnter(pThis->pDevInsR3->pCritSectRoR3, VERR_IGNORED);
889
890 pThis->fDisabledByHpet = fActivated;
891
892 PDMCritSectLeave(pThis->pDevInsR3->pCritSectRoR3);
893}
894
895
896/* -=-=-=-=-=- based on bits from pc.c -=-=-=-=-=- */
897
898
899static void rtc_set_memory(RTCState *pThis, int addr, int val)
900{
901 if (addr >= 0 && addr <= 127)
902 pThis->cmos_data[addr] = val;
903}
904
905
906static void rtc_set_date(RTCState *pThis, const struct my_tm *tm)
907{
908 pThis->current_tm = *tm;
909 rtc_copy_date(pThis);
910}
911
912
913/** @copydoc FNPDMDEVINITCOMPLETE */
914static DECLCALLBACK(int) rtcInitComplete(PPDMDEVINS pDevIns)
915{
916 /** @todo this should be (re)done at power on if we didn't load a state... */
917 RTCState *pThis = PDMINS_2_DATA(pDevIns, RTCState *);
918
919 /*
920 * Set the CMOS date/time.
921 */
922 RTTIMESPEC Now;
923 PDMDevHlpTMUtcNow(pDevIns, &Now);
924 RTTIME Time;
925 if (pThis->fUTC)
926 RTTimeExplode(&Time, &Now);
927 else
928 RTTimeLocalExplode(&Time, &Now);
929
930 struct my_tm Tm;
931 memset(&Tm, 0, sizeof(Tm));
932 Tm.tm_year = Time.i32Year - 1900;
933 Tm.tm_mon = Time.u8Month - 1;
934 Tm.tm_mday = Time.u8MonthDay;
935 Tm.tm_wday = (Time.u8WeekDay + 1 + 7) % 7; /* 0 = Monday -> Sunday */
936 Tm.tm_yday = Time.u16YearDay - 1;
937 Tm.tm_hour = Time.u8Hour;
938 Tm.tm_min = Time.u8Minute;
939 Tm.tm_sec = Time.u8Second;
940
941 rtc_set_date(pThis, &Tm);
942
943 int iYear = to_bcd(pThis, (Tm.tm_year / 100) + 19); /* tm_year is 1900 based */
944 rtc_set_memory(pThis, 0x32, iYear); /* 32h - Century Byte (BCD value for the century */
945 rtc_set_memory(pThis, 0x37, iYear); /* 37h - (IBM PS/2) Date Century Byte */
946
947 /*
948 * Recalculate the checksum just in case.
949 */
950 rtcCalcCRC(pThis);
951
952 Log(("CMOS bank 0: \n%16.128Rhxd\n", &pThis->cmos_data[0]));
953 Log(("CMOS bank 1: \n%16.128Rhxd\n", &pThis->cmos_data[128]));
954 return VINF_SUCCESS;
955}
956
957
958/* -=-=-=-=-=- real code -=-=-=-=-=- */
959
960/**
961 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
962 */
963static DECLCALLBACK(void *) rtcQueryInterface(PPDMIBASE pInterface, const char *pszIID)
964{
965 PPDMDEVINS pDevIns = RT_FROM_MEMBER(pInterface, PDMDEVINS, IBase);
966 RTCState *pThis = PDMINS_2_DATA(pDevIns, RTCState *);
967 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDevIns->IBase);
968 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHPETLEGACYNOTIFY, &pThis->IHpetLegacyNotify);
969 return NULL;
970}
971
972/**
973 * @copydoc
974 */
975static DECLCALLBACK(void) rtcRelocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
976{
977 RTCState *pThis = PDMINS_2_DATA(pDevIns, RTCState *);
978
979 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
980 pThis->pPeriodicTimerRC = TMTimerRCPtr(pThis->pPeriodicTimerR3);
981 pThis->pSecondTimerRC = TMTimerRCPtr(pThis->pSecondTimerR3);
982 pThis->pSecondTimer2RC = TMTimerRCPtr(pThis->pSecondTimer2R3);
983}
984
985
986/**
987 * @interface_method_impl{PDMDEVREG,pfnConstruct}
988 */
989static DECLCALLBACK(int) rtcConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
990{
991 RTCState *pThis = PDMINS_2_DATA(pDevIns, RTCState *);
992 int rc;
993 Assert(iInstance == 0);
994
995 /*
996 * Validate configuration.
997 */
998 if (!CFGMR3AreValuesValid(pCfg,
999 "Irq\0"
1000 "Base\0"
1001 "UseUTC\0"
1002 "GCEnabled\0"
1003 "R0Enabled\0"))
1004 return VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES;
1005
1006 /*
1007 * Init the data.
1008 */
1009 uint8_t u8Irq;
1010 rc = CFGMR3QueryU8Def(pCfg, "Irq", &u8Irq, 8);
1011 if (RT_FAILURE(rc))
1012 return PDMDEV_SET_ERROR(pDevIns, rc,
1013 N_("Configuration error: Querying \"Irq\" as a uint8_t failed"));
1014 pThis->irq = u8Irq;
1015
1016 rc = CFGMR3QueryPortDef(pCfg, "Base", &pThis->IOPortBase, 0x70);
1017 if (RT_FAILURE(rc))
1018 return PDMDEV_SET_ERROR(pDevIns, rc,
1019 N_("Configuration error: Querying \"Base\" as a RTIOPORT failed"));
1020
1021 rc = CFGMR3QueryBoolDef(pCfg, "UseUTC", &pThis->fUTC, false);
1022 if (RT_FAILURE(rc))
1023 return PDMDEV_SET_ERROR(pDevIns, rc,
1024 N_("Configuration error: Querying \"UseUTC\" as a bool failed"));
1025
1026 bool fGCEnabled;
1027 rc = CFGMR3QueryBoolDef(pCfg, "GCEnabled", &fGCEnabled, true);
1028 if (RT_FAILURE(rc))
1029 return PDMDEV_SET_ERROR(pDevIns, rc,
1030 N_("Configuration error: failed to read GCEnabled as boolean"));
1031
1032 bool fR0Enabled;
1033 rc = CFGMR3QueryBoolDef(pCfg, "R0Enabled", &fR0Enabled, true);
1034 if (RT_FAILURE(rc))
1035 return PDMDEV_SET_ERROR(pDevIns, rc,
1036 N_("Configuration error: failed to read R0Enabled as boolean"));
1037
1038 Log(("RTC: Irq=%#x Base=%#x fGCEnabled=%RTbool fR0Enabled=%RTbool\n",
1039 u8Irq, pThis->IOPortBase, fGCEnabled, fR0Enabled));
1040
1041
1042 pThis->pDevInsR3 = pDevIns;
1043 pThis->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
1044 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
1045 pThis->cmos_data[RTC_REG_A] = 0x26;
1046 pThis->cmos_data[RTC_REG_B] = 0x02;
1047 pThis->cmos_data[RTC_REG_C] = 0x00;
1048 pThis->cmos_data[RTC_REG_D] = 0x80;
1049 pThis->RtcReg.u32Version = PDM_RTCREG_VERSION;
1050 pThis->RtcReg.pfnRead = rtcCMOSRead;
1051 pThis->RtcReg.pfnWrite = rtcCMOSWrite;
1052 pThis->fDisabledByHpet = false;
1053
1054 /* IBase */
1055 pDevIns->IBase.pfnQueryInterface = rtcQueryInterface;
1056 /* IHpetLegacyNotify */
1057 pThis->IHpetLegacyNotify.pfnModeChanged = rtcHpetLegacyNotify_ModeChanged;
1058
1059 /*
1060 * Create timers.
1061 */
1062 PTMTIMER pTimer;
1063 /* Periodic timer. */
1064 rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL_SYNC, rtcTimerPeriodic, pThis,
1065 TMTIMER_FLAGS_DEFAULT_CRIT_SECT, "MC146818 RTC/CMOS - Periodic",
1066 &pTimer);
1067 if (RT_FAILURE(rc))
1068 return rc;
1069 pThis->pPeriodicTimerR3 = pTimer;
1070 pThis->pPeriodicTimerR0 = TMTimerR0Ptr(pTimer);
1071 pThis->pPeriodicTimerRC = TMTimerRCPtr(pTimer);
1072
1073 /* Seconds timer. */
1074 rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL_SYNC, rtcTimerSecond, pThis,
1075 TMTIMER_FLAGS_DEFAULT_CRIT_SECT, "MC146818 RTC/CMOS - Second",
1076 &pTimer);
1077 if (RT_FAILURE(rc))
1078 return rc;
1079 pThis->pSecondTimerR3 = pTimer;
1080 pThis->pSecondTimerR0 = TMTimerR0Ptr(pTimer);
1081 pThis->pSecondTimerRC = TMTimerRCPtr(pTimer);
1082
1083 /* The second2 timer, this is always active. */
1084 rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL_SYNC, rtcTimerSecond2, pThis,
1085 TMTIMER_FLAGS_DEFAULT_CRIT_SECT, "MC146818 RTC/CMOS - Second2",
1086 &pTimer);
1087 if (RT_FAILURE(rc))
1088 return rc;
1089 pThis->pSecondTimer2R3 = pTimer;
1090 pThis->pSecondTimer2R0 = TMTimerR0Ptr(pTimer);
1091 pThis->pSecondTimer2RC = TMTimerRCPtr(pTimer);
1092 pThis->next_second_time = TMTimerGet(pTimer)
1093 + (TMTimerGetFreq(pTimer) * 99) / 100;
1094 rc = TMTimerLock(pTimer, VERR_IGNORED);
1095 AssertRCReturn(rc, rc);
1096 rc = TMTimerSet(pTimer, pThis->next_second_time);
1097 TMTimerUnlock(pTimer);
1098 AssertRCReturn(rc, rc);
1099
1100 /*
1101 * Register I/O ports.
1102 */
1103 rc = PDMDevHlpIOPortRegister(pDevIns, pThis->IOPortBase, 4, NULL,
1104 rtcIOPortWrite, rtcIOPortRead, NULL, NULL, "MC146818 RTC/CMOS");
1105 if (RT_FAILURE(rc))
1106 return rc;
1107 if (fGCEnabled)
1108 {
1109 rc = PDMDevHlpIOPortRegisterRC(pDevIns, pThis->IOPortBase, 4, NIL_RTRCPTR,
1110 "rtcIOPortWrite", "rtcIOPortRead", NULL, NULL, "MC146818 RTC/CMOS");
1111 if (RT_FAILURE(rc))
1112 return rc;
1113 }
1114 if (fR0Enabled)
1115 {
1116 rc = PDMDevHlpIOPortRegisterR0(pDevIns, pThis->IOPortBase, 4, NIL_RTR0PTR,
1117 "rtcIOPortWrite", "rtcIOPortRead", NULL, NULL, "MC146818 RTC/CMOS");
1118 if (RT_FAILURE(rc))
1119 return rc;
1120 }
1121
1122 /*
1123 * Register the saved state.
1124 */
1125 rc = PDMDevHlpSSMRegister3(pDevIns, RTC_SAVED_STATE_VERSION, sizeof(*pThis), rtcLiveExec, rtcSaveExec, rtcLoadExec);
1126 if (RT_FAILURE(rc))
1127 return rc;
1128
1129 /*
1130 * Register ourselves as the RTC/CMOS with PDM.
1131 */
1132 rc = PDMDevHlpRTCRegister(pDevIns, &pThis->RtcReg, &pThis->pRtcHlpR3);
1133 if (RT_FAILURE(rc))
1134 return rc;
1135
1136 return VINF_SUCCESS;
1137}
1138
1139
1140/**
1141 * The device registration structure.
1142 */
1143const PDMDEVREG g_DeviceMC146818 =
1144{
1145 /* u32Version */
1146 PDM_DEVREG_VERSION,
1147 /* szName */
1148 "mc146818",
1149 /* szRCMod */
1150 "VBoxDDGC.gc",
1151 /* szR0Mod */
1152 "VBoxDDR0.r0",
1153 /* pszDescription */
1154 "Motorola MC146818 RTC/CMOS Device.",
1155 /* fFlags */
1156 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,
1157 /* fClass */
1158 PDM_DEVREG_CLASS_RTC,
1159 /* cMaxInstances */
1160 1,
1161 /* cbInstance */
1162 sizeof(RTCState),
1163 /* pfnConstruct */
1164 rtcConstruct,
1165 /* pfnDestruct */
1166 NULL,
1167 /* pfnRelocate */
1168 rtcRelocate,
1169 /* pfnIOCtl */
1170 NULL,
1171 /* pfnPowerOn */
1172 NULL,
1173 /* pfnReset */
1174 NULL,
1175 /* pfnSuspend */
1176 NULL,
1177 /* pfnResume */
1178 NULL,
1179 /* pfnAttach */
1180 NULL,
1181 /* pfnDetach */
1182 NULL,
1183 /* pfnQueryInterface */
1184 NULL,
1185 /* pfnInitComplete */
1186 rtcInitComplete,
1187 /* pfnPowerOff */
1188 NULL,
1189 /* pfnSoftReset */
1190 NULL,
1191 /* u32VersionEnd */
1192 PDM_DEVREG_VERSION
1193};
1194
1195#endif /* IN_RING3 */
1196#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
1197
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