VirtualBox

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

Last change on this file since 40754 was 38195, checked in by vboxsync, 13 years ago

rtcLoadExec: Avoid asserting in TMTimerSetFrequencyHint.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 38.4 KB
Line 
1/* $Id: DevRTC.cpp 38195 2011-07-27 11:21:13Z 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 * @interface_method_impl{PDMDEVREG,pfnConstruct}
993 */
994static DECLCALLBACK(int) rtcConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
995{
996 RTCState *pThis = PDMINS_2_DATA(pDevIns, RTCState *);
997 int rc;
998 Assert(iInstance == 0);
999
1000 /*
1001 * Validate configuration.
1002 */
1003 if (!CFGMR3AreValuesValid(pCfg,
1004 "Irq\0"
1005 "Base\0"
1006 "UseUTC\0"
1007 "GCEnabled\0"
1008 "R0Enabled\0"))
1009 return VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES;
1010
1011 /*
1012 * Init the data.
1013 */
1014 uint8_t u8Irq;
1015 rc = CFGMR3QueryU8Def(pCfg, "Irq", &u8Irq, 8);
1016 if (RT_FAILURE(rc))
1017 return PDMDEV_SET_ERROR(pDevIns, rc,
1018 N_("Configuration error: Querying \"Irq\" as a uint8_t failed"));
1019 pThis->irq = u8Irq;
1020
1021 rc = CFGMR3QueryPortDef(pCfg, "Base", &pThis->IOPortBase, 0x70);
1022 if (RT_FAILURE(rc))
1023 return PDMDEV_SET_ERROR(pDevIns, rc,
1024 N_("Configuration error: Querying \"Base\" as a RTIOPORT failed"));
1025
1026 rc = CFGMR3QueryBoolDef(pCfg, "UseUTC", &pThis->fUTC, false);
1027 if (RT_FAILURE(rc))
1028 return PDMDEV_SET_ERROR(pDevIns, rc,
1029 N_("Configuration error: Querying \"UseUTC\" as a bool failed"));
1030
1031 bool fGCEnabled;
1032 rc = CFGMR3QueryBoolDef(pCfg, "GCEnabled", &fGCEnabled, true);
1033 if (RT_FAILURE(rc))
1034 return PDMDEV_SET_ERROR(pDevIns, rc,
1035 N_("Configuration error: failed to read GCEnabled as boolean"));
1036
1037 bool fR0Enabled;
1038 rc = CFGMR3QueryBoolDef(pCfg, "R0Enabled", &fR0Enabled, true);
1039 if (RT_FAILURE(rc))
1040 return PDMDEV_SET_ERROR(pDevIns, rc,
1041 N_("Configuration error: failed to read R0Enabled as boolean"));
1042
1043 Log(("RTC: Irq=%#x Base=%#x fGCEnabled=%RTbool fR0Enabled=%RTbool\n",
1044 u8Irq, pThis->IOPortBase, fGCEnabled, fR0Enabled));
1045
1046
1047 pThis->pDevInsR3 = pDevIns;
1048 pThis->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
1049 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
1050 pThis->cmos_data[RTC_REG_A] = 0x26;
1051 pThis->cmos_data[RTC_REG_B] = 0x02;
1052 pThis->cmos_data[RTC_REG_C] = 0x00;
1053 pThis->cmos_data[RTC_REG_D] = 0x80;
1054 pThis->RtcReg.u32Version = PDM_RTCREG_VERSION;
1055 pThis->RtcReg.pfnRead = rtcCMOSRead;
1056 pThis->RtcReg.pfnWrite = rtcCMOSWrite;
1057 pThis->fDisabledByHpet = false;
1058
1059 /* IBase */
1060 pDevIns->IBase.pfnQueryInterface = rtcQueryInterface;
1061 /* IHpetLegacyNotify */
1062 pThis->IHpetLegacyNotify.pfnModeChanged = rtcHpetLegacyNotify_ModeChanged;
1063
1064 /*
1065 * Create timers.
1066 */
1067 PTMTIMER pTimer;
1068 /* Periodic timer. */
1069 rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL_SYNC, rtcTimerPeriodic, pThis,
1070 TMTIMER_FLAGS_DEFAULT_CRIT_SECT, "MC146818 RTC/CMOS - Periodic",
1071 &pTimer);
1072 if (RT_FAILURE(rc))
1073 return rc;
1074 pThis->pPeriodicTimerR3 = pTimer;
1075 pThis->pPeriodicTimerR0 = TMTimerR0Ptr(pTimer);
1076 pThis->pPeriodicTimerRC = TMTimerRCPtr(pTimer);
1077
1078 /* Seconds timer. */
1079 rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL_SYNC, rtcTimerSecond, pThis,
1080 TMTIMER_FLAGS_DEFAULT_CRIT_SECT, "MC146818 RTC/CMOS - Second",
1081 &pTimer);
1082 if (RT_FAILURE(rc))
1083 return rc;
1084 pThis->pSecondTimerR3 = pTimer;
1085 pThis->pSecondTimerR0 = TMTimerR0Ptr(pTimer);
1086 pThis->pSecondTimerRC = TMTimerRCPtr(pTimer);
1087
1088 /* The second2 timer, this is always active. */
1089 rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL_SYNC, rtcTimerSecond2, pThis,
1090 TMTIMER_FLAGS_DEFAULT_CRIT_SECT, "MC146818 RTC/CMOS - Second2",
1091 &pTimer);
1092 if (RT_FAILURE(rc))
1093 return rc;
1094 pThis->pSecondTimer2R3 = pTimer;
1095 pThis->pSecondTimer2R0 = TMTimerR0Ptr(pTimer);
1096 pThis->pSecondTimer2RC = TMTimerRCPtr(pTimer);
1097 pThis->next_second_time = TMTimerGet(pTimer)
1098 + (TMTimerGetFreq(pTimer) * 99) / 100;
1099 rc = TMTimerLock(pTimer, VERR_IGNORED);
1100 AssertRCReturn(rc, rc);
1101 rc = TMTimerSet(pTimer, pThis->next_second_time);
1102 TMTimerUnlock(pTimer);
1103 AssertRCReturn(rc, rc);
1104
1105 /*
1106 * Register I/O ports.
1107 */
1108 rc = PDMDevHlpIOPortRegister(pDevIns, pThis->IOPortBase, 4, NULL,
1109 rtcIOPortWrite, rtcIOPortRead, NULL, NULL, "MC146818 RTC/CMOS");
1110 if (RT_FAILURE(rc))
1111 return rc;
1112 if (fGCEnabled)
1113 {
1114 rc = PDMDevHlpIOPortRegisterRC(pDevIns, pThis->IOPortBase, 4, NIL_RTRCPTR,
1115 "rtcIOPortWrite", "rtcIOPortRead", NULL, NULL, "MC146818 RTC/CMOS");
1116 if (RT_FAILURE(rc))
1117 return rc;
1118 }
1119 if (fR0Enabled)
1120 {
1121 rc = PDMDevHlpIOPortRegisterR0(pDevIns, pThis->IOPortBase, 4, NIL_RTR0PTR,
1122 "rtcIOPortWrite", "rtcIOPortRead", NULL, NULL, "MC146818 RTC/CMOS");
1123 if (RT_FAILURE(rc))
1124 return rc;
1125 }
1126
1127 /*
1128 * Register the saved state.
1129 */
1130 rc = PDMDevHlpSSMRegister3(pDevIns, RTC_SAVED_STATE_VERSION, sizeof(*pThis), rtcLiveExec, rtcSaveExec, rtcLoadExec);
1131 if (RT_FAILURE(rc))
1132 return rc;
1133
1134 /*
1135 * Register ourselves as the RTC/CMOS with PDM.
1136 */
1137 rc = PDMDevHlpRTCRegister(pDevIns, &pThis->RtcReg, &pThis->pRtcHlpR3);
1138 if (RT_FAILURE(rc))
1139 return rc;
1140
1141 return VINF_SUCCESS;
1142}
1143
1144
1145/**
1146 * The device registration structure.
1147 */
1148const PDMDEVREG g_DeviceMC146818 =
1149{
1150 /* u32Version */
1151 PDM_DEVREG_VERSION,
1152 /* szName */
1153 "mc146818",
1154 /* szRCMod */
1155 "VBoxDDGC.gc",
1156 /* szR0Mod */
1157 "VBoxDDR0.r0",
1158 /* pszDescription */
1159 "Motorola MC146818 RTC/CMOS Device.",
1160 /* fFlags */
1161 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,
1162 /* fClass */
1163 PDM_DEVREG_CLASS_RTC,
1164 /* cMaxInstances */
1165 1,
1166 /* cbInstance */
1167 sizeof(RTCState),
1168 /* pfnConstruct */
1169 rtcConstruct,
1170 /* pfnDestruct */
1171 NULL,
1172 /* pfnRelocate */
1173 rtcRelocate,
1174 /* pfnIOCtl */
1175 NULL,
1176 /* pfnPowerOn */
1177 NULL,
1178 /* pfnReset */
1179 NULL,
1180 /* pfnSuspend */
1181 NULL,
1182 /* pfnResume */
1183 NULL,
1184 /* pfnAttach */
1185 NULL,
1186 /* pfnDetach */
1187 NULL,
1188 /* pfnQueryInterface */
1189 NULL,
1190 /* pfnInitComplete */
1191 rtcInitComplete,
1192 /* pfnPowerOff */
1193 NULL,
1194 /* pfnSoftReset */
1195 NULL,
1196 /* u32VersionEnd */
1197 PDM_DEVREG_VERSION
1198};
1199
1200#endif /* IN_RING3 */
1201#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
1202
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use