VirtualBox

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

最後變更 在這個檔案從81458是 81408,由 vboxsync 提交於 5 年 前

DevRTC: nits. bugref:9218

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Id Revision
檔案大小: 45.2 KB
 
1/* $Id: DevRTC.cpp 81408 2019-10-21 13:10:03Z vboxsync $ */
2/** @file
3 * Motorola MC146818 RTC/CMOS Device with PIIX4 extensions.
4 */
5
6/*
7 * Copyright (C) 2006-2019 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.alldomusa.eu.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/*********************************************************************************************************************************
45* Header Files *
46*********************************************************************************************************************************/
47#define LOG_GROUP LOG_GROUP_DEV_RTC
48#include <VBox/vmm/pdmdev.h>
49#include <VBox/log.h>
50#include <iprt/asm-math.h>
51#include <iprt/assert.h>
52#include <iprt/string.h>
53
54#ifdef IN_RING3
55# include <iprt/alloc.h>
56# include <iprt/uuid.h>
57#endif /* IN_RING3 */
58
59#include "VBoxDD.h"
60
61
62/*********************************************************************************************************************************
63* Defined Constants And Macros *
64*********************************************************************************************************************************/
65/*#define DEBUG_CMOS*/
66#define RTC_CRC_START 0x10
67#define RTC_CRC_LAST 0x2d
68#define RTC_CRC_HIGH 0x2e
69#define RTC_CRC_LOW 0x2f
70
71#define RTC_SECONDS 0
72#define RTC_SECONDS_ALARM 1
73#define RTC_MINUTES 2
74#define RTC_MINUTES_ALARM 3
75#define RTC_HOURS 4
76#define RTC_HOURS_ALARM 5
77#define RTC_ALARM_DONT_CARE 0xC0
78
79#define RTC_DAY_OF_WEEK 6
80#define RTC_DAY_OF_MONTH 7
81#define RTC_MONTH 8
82#define RTC_YEAR 9
83
84#define RTC_REG_A 10
85#define RTC_REG_B 11
86#define RTC_REG_C 12
87#define RTC_REG_D 13
88
89#define REG_A_UIP 0x80
90
91#define REG_B_SET 0x80
92#define REG_B_PIE 0x40
93#define REG_B_AIE 0x20
94#define REG_B_UIE 0x10
95
96#define CMOS_BANK_LOWER_LIMIT 0x0E
97#define CMOS_BANK_UPPER_LIMIT 0x7F
98#define CMOS_BANK2_LOWER_LIMIT 0x80
99#define CMOS_BANK2_UPPER_LIMIT 0xFF
100#define CMOS_BANK_SIZE 0x80
101
102/** The saved state version. */
103#define RTC_SAVED_STATE_VERSION 4
104/** The saved state version used by VirtualBox pre-3.2.
105 * This does not include the second 128-byte bank. */
106#define RTC_SAVED_STATE_VERSION_VBOX_32PRE 3
107/** The saved state version used by VirtualBox 3.1 and earlier.
108 * This does not include disabled by HPET state. */
109#define RTC_SAVED_STATE_VERSION_VBOX_31 2
110/** The saved state version used by VirtualBox 3.0 and earlier.
111 * This does not include the configuration. */
112#define RTC_SAVED_STATE_VERSION_VBOX_30 1
113
114
115/*********************************************************************************************************************************
116* Structures and Typedefs *
117*********************************************************************************************************************************/
118/** @todo Replace struct my_tm with RTTIME. */
119struct my_tm
120{
121 int32_t tm_sec;
122 int32_t tm_min;
123 int32_t tm_hour;
124 int32_t tm_mday;
125 int32_t tm_mon;
126 int32_t tm_year;
127 int32_t tm_wday;
128 int32_t tm_yday;
129};
130
131
132typedef struct RTCSTATE
133{
134 uint8_t cmos_data[256];
135 uint8_t cmos_index[2];
136 uint8_t Alignment0[6];
137 struct my_tm current_tm;
138 /** The configured IRQ. */
139 int32_t irq;
140 /** The configured I/O port base. */
141 RTIOPORT IOPortBase;
142 /** Use UTC or local time initially. */
143 bool fUTC;
144 /** Disabled by HPET legacy mode. */
145 bool fDisabledByHpet;
146 /* periodic timer */
147 int64_t next_periodic_time;
148 /* second update */
149 int64_t next_second_time;
150
151 /** The periodic timer (rtcTimerPeriodic). */
152 TMTIMERHANDLE hPeriodicTimer;
153 /** The second timer (rtcTimerSecond). */
154 TMTIMERHANDLE hSecondTimer;
155 /** The second second timer (rtcTimerSecond2). */
156 TMTIMERHANDLE hSecondTimer2;
157 /** The I/O port range handle. */
158 IOMIOPORTHANDLE hIoPorts;
159
160 /** Number of release log entries. Used to prevent flooding. */
161 uint32_t cRelLogEntries;
162 /** The current/previous logged timer period. */
163 int32_t CurLogPeriod;
164 /** The current/previous hinted timer period. */
165 int32_t CurHintPeriod;
166 /** How many consecutive times the UIP has been seen. */
167 int32_t cUipSeen;
168
169 /** Number of IRQs that's been raised. */
170 STAMCOUNTER StatRTCIrq;
171 /** Number of times the timer callback handler ran. */
172 STAMCOUNTER StatRTCTimerCB;
173} RTCSTATE;
174/** Pointer to the RTC device state. */
175typedef RTCSTATE *PRTCSTATE;
176
177
178/**
179 * RTC ring-3 instance data.
180 */
181typedef struct RTCSTATER3
182{
183 /** Pointer to the device instance. */
184 PPDMDEVINSR3 pDevInsR3;
185
186 /** The RTC registration structure. */
187 PDMRTCREG RtcReg;
188 /** The RTC device helpers. */
189 R3PTRTYPE(PCPDMRTCHLP) pRtcHlpR3;
190
191 /** Pointer to the shared state (for the IHpetLegacyNotify callback). */
192 PRTCSTATE pShared;
193 /** HPET legacy mode notification interface. */
194 PDMIHPETLEGACYNOTIFY IHpetLegacyNotify;
195} RTCSTATER3;
196/** Pointer to the ring-3 RTC device instance data. */
197typedef RTCSTATER3 *PRTCSTATER3;
198
199
200/**
201 * RTC ring-0 instance data.
202 * @note Not needed, but serves as example for testing the new model.
203 */
204typedef struct RTCSTATER0
205{
206 /** Pointer to the device instance. */
207 PPDMDEVINSR0 pDevInsR0;
208} RTCSTATER0;
209/** Pointer to the ring-0 RTC device instance data. */
210typedef RTCSTATER0 *PRTCSTATER0;
211
212
213/**
214 * RTC raw-mode instance data.
215 * @note Not needed, but serves as example for testing the new model.
216 */
217typedef struct RTCSTATERC
218{
219 /** Pointer to the device instance. */
220 PPDMDEVINSRC pDevInsRC;
221} RTCSTATERC;
222/** Pointer to the raw-mode RTC device instance data. */
223typedef RTCSTATERC *PRTCSTATERC;
224
225
226/** @def PRTCSTATECC
227 * Pointer to the instance data for the current context. */
228#ifdef IN_RING3
229typedef RTCSTATER3 RTCSTATECC;
230typedef PRTCSTATER3 PRTCSTATECC;
231#elif defined(IN_RING0)
232typedef RTCSTATER0 RTCSTATECC;
233typedef PRTCSTATER0 PRTCSTATECC;
234#elif defined(IN_RC)
235typedef RTCSTATERC RTCSTATECC;
236typedef PRTCSTATERC PRTCSTATECC;
237#else
238# error "Not IN_RING3, IN_RING0 or IN_RC"
239#endif
240
241
242#ifndef VBOX_DEVICE_STRUCT_TESTCASE
243
244static void rtc_timer_update(PPDMDEVINS pDevIns, PRTCSTATE pThis, int64_t current_time)
245{
246 int period_code, period;
247 uint64_t cur_clock, next_irq_clock;
248 uint32_t freq;
249
250 Assert(PDMDevHlpTimerIsLockOwner(pDevIns, pThis->hPeriodicTimer));
251 Assert(PDMDevHlpCritSectIsOwner(pDevIns, pDevIns->CTX_SUFF(pCritSectRo)));
252
253 period_code = pThis->cmos_data[RTC_REG_A] & 0x0f;
254 if ( period_code != 0
255 && (pThis->cmos_data[RTC_REG_B] & REG_B_PIE))
256 {
257 if (period_code <= 2)
258 period_code += 7;
259 /* period in 32 kHz cycles */
260 period = 1 << (period_code - 1);
261 /* compute 32 kHz clock */
262 freq = PDMDevHlpTimerGetFreq(pDevIns, pThis->hPeriodicTimer);
263
264 cur_clock = ASMMultU64ByU32DivByU32(current_time, 32768, freq);
265 next_irq_clock = (cur_clock & ~(uint64_t)(period - 1)) + period;
266 pThis->next_periodic_time = ASMMultU64ByU32DivByU32(next_irq_clock, freq, 32768) + 1;
267 PDMDevHlpTimerSet(pDevIns, pThis->hPeriodicTimer, pThis->next_periodic_time);
268
269#ifdef IN_RING3
270 if (RT_UNLIKELY(period != pThis->CurLogPeriod))
271#else
272 if (RT_UNLIKELY(period != pThis->CurHintPeriod))
273#endif
274 {
275#ifdef IN_RING3
276 if (pThis->cRelLogEntries++ < 64)
277 LogRel(("RTC: period=%#x (%d) %u Hz\n", period, period, _32K / period));
278 pThis->CurLogPeriod = period;
279#endif
280 pThis->CurHintPeriod = period;
281 PDMDevHlpTimerSetFrequencyHint(pDevIns, pThis->hPeriodicTimer, _32K / period);
282 }
283 }
284 else
285 {
286#ifdef IN_RING3
287 if (PDMDevHlpTimerIsActive(pDevIns, pThis->hPeriodicTimer) && pThis->cRelLogEntries++ < 64)
288 LogRel(("RTC: Stopped the periodic timer\n"));
289#endif
290 PDMDevHlpTimerStop(pDevIns, pThis->hPeriodicTimer);
291 }
292 RT_NOREF(pDevIns);
293}
294
295
296static void rtc_raise_irq(PPDMDEVINS pDevIns, PRTCSTATE pThis, uint32_t iLevel)
297{
298 if (!pThis->fDisabledByHpet)
299 {
300 PDMDevHlpISASetIrq(pDevIns, pThis->irq, iLevel);
301 if (iLevel)
302 STAM_COUNTER_INC(&pThis->StatRTCIrq);
303 }
304}
305
306
307#ifdef IN_RING3
308DECLINLINE(int) to_bcd(PRTCSTATE pThis, int a)
309{
310 if (pThis->cmos_data[RTC_REG_B] & 0x04)
311 return a;
312 return ((a / 10) << 4) | (a % 10);
313}
314#endif
315
316
317DECLINLINE(int) from_bcd(PRTCSTATE pThis, int a)
318{
319 if (pThis->cmos_data[RTC_REG_B] & 0x04)
320 return a;
321 return ((a >> 4) * 10) + (a & 0x0f);
322}
323
324
325static void rtc_set_time(PRTCSTATE pThis)
326{
327 struct my_tm *tm = &pThis->current_tm;
328
329 tm->tm_sec = from_bcd(pThis, pThis->cmos_data[RTC_SECONDS]);
330 tm->tm_min = from_bcd(pThis, pThis->cmos_data[RTC_MINUTES]);
331 tm->tm_hour = from_bcd(pThis, pThis->cmos_data[RTC_HOURS] & 0x7f);
332 if (!(pThis->cmos_data[RTC_REG_B] & 0x02))
333 {
334 tm->tm_hour %= 12;
335 if (pThis->cmos_data[RTC_HOURS] & 0x80)
336 tm->tm_hour += 12;
337 }
338 tm->tm_wday = from_bcd(pThis, pThis->cmos_data[RTC_DAY_OF_WEEK]);
339 tm->tm_mday = from_bcd(pThis, pThis->cmos_data[RTC_DAY_OF_MONTH]);
340 tm->tm_mon = from_bcd(pThis, pThis->cmos_data[RTC_MONTH]) - 1;
341 tm->tm_year = from_bcd(pThis, pThis->cmos_data[RTC_YEAR]) + 100;
342}
343
344
345/* -=-=-=-=-=- I/O Port Handlers -=-=-=-=-=- */
346
347
348/**
349 * @callback_method_impl{FNIOMIOPORTNEWIN}
350 */
351PDMBOTHCBDECL(VBOXSTRICTRC) rtcIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
352{
353 NOREF(pvUser);
354 Assert(offPort < 4);
355
356 if (cb != 1)
357 return VERR_IOM_IOPORT_UNUSED;
358
359 PRTCSTATE pThis = PDMINS_2_DATA(pDevIns, PRTCSTATE);
360 if ((offPort & 1) == 0)
361 *pu32 = 0xff;
362 else
363 {
364 unsigned bank = (offPort >> 1) & 1;
365 switch (pThis->cmos_index[bank])
366 {
367 case RTC_SECONDS:
368 case RTC_MINUTES:
369 case RTC_HOURS:
370 case RTC_DAY_OF_WEEK:
371 case RTC_DAY_OF_MONTH:
372 case RTC_MONTH:
373 case RTC_YEAR:
374 *pu32 = pThis->cmos_data[pThis->cmos_index[0]];
375 break;
376
377 case RTC_REG_A:
378 if (pThis->cmos_data[RTC_REG_A] & REG_A_UIP)
379 ++pThis->cUipSeen;
380 else
381 pThis->cUipSeen = 0;
382 if (pThis->cUipSeen >= 250)
383 {
384 pThis->cmos_data[pThis->cmos_index[0]] &= ~REG_A_UIP;
385 pThis->cUipSeen = 0;
386 }
387 *pu32 = pThis->cmos_data[pThis->cmos_index[0]];
388 break;
389
390 case RTC_REG_C:
391 *pu32 = pThis->cmos_data[pThis->cmos_index[0]];
392 rtc_raise_irq(pDevIns, pThis, 0);
393 pThis->cmos_data[RTC_REG_C] = 0x00;
394 break;
395
396 default:
397 *pu32 = pThis->cmos_data[pThis->cmos_index[bank]];
398 break;
399 }
400
401 Log(("CMOS: Read bank %d idx %#04x: %#04x\n", bank, pThis->cmos_index[bank], *pu32));
402 }
403
404 return VINF_SUCCESS;
405}
406
407
408/**
409 * @callback_method_impl{FNIOMIOPORTNEWOUT}
410 */
411PDMBOTHCBDECL(VBOXSTRICTRC) rtcIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
412{
413 NOREF(pvUser);
414 Assert(offPort < 4);
415
416 if (cb != 1)
417 return VINF_SUCCESS;
418
419 PRTCSTATE pThis = PDMINS_2_DATA(pDevIns, PRTCSTATE);
420 uint32_t bank = (offPort >> 1) & 1;
421 if ((offPort & 1) == 0)
422 {
423 pThis->cmos_index[bank] = (u32 & 0x7f) + (bank * CMOS_BANK_SIZE);
424
425 /* HACK ALERT! Attempt to trigger VM_FF_TIMER and/or VM_FF_TM_VIRTUAL_SYNC
426 for forcing the pSecondTimer2 timer to run be run and clear UIP in
427 a timely fashion. */
428 if (u32 == RTC_REG_A)
429 PDMDevHlpTimerGet(pDevIns, pThis->hSecondTimer);
430 }
431 else
432 {
433 Log(("CMOS: Write bank %d idx %#04x: %#04x (old %#04x)\n", bank,
434 pThis->cmos_index[bank], u32, pThis->cmos_data[pThis->cmos_index[bank]]));
435
436 int const idx = pThis->cmos_index[bank];
437 switch (idx)
438 {
439 case RTC_SECONDS_ALARM:
440 case RTC_MINUTES_ALARM:
441 case RTC_HOURS_ALARM:
442 pThis->cmos_data[pThis->cmos_index[0]] = u32;
443 break;
444
445 case RTC_SECONDS:
446 case RTC_MINUTES:
447 case RTC_HOURS:
448 case RTC_DAY_OF_WEEK:
449 case RTC_DAY_OF_MONTH:
450 case RTC_MONTH:
451 case RTC_YEAR:
452 pThis->cmos_data[pThis->cmos_index[0]] = u32;
453 /* if in set mode, do not update the time */
454 if (!(pThis->cmos_data[RTC_REG_B] & REG_B_SET))
455 rtc_set_time(pThis);
456 break;
457
458 case RTC_REG_A:
459 case RTC_REG_B:
460 {
461 /* We need to acquire the clock lock, because of lock ordering
462 issues this means having to release the device lock. Since
463 we're letting IOM do the locking, we must not return without
464 holding the device lock.*/
465 PDMDevHlpCritSectLeave(pDevIns, pDevIns->CTX_SUFF(pCritSectRo));
466 int rc1 = PDMDevHlpTimerLock(pDevIns, pThis->hPeriodicTimer, VINF_SUCCESS /* must get it */);
467 int rc2 = PDMDevHlpCritSectEnter(pDevIns, pDevIns->CTX_SUFF(pCritSectRo), VINF_SUCCESS /* must get it */);
468 AssertRCReturn(rc1, rc1);
469 AssertRCReturnStmt(rc2, PDMDevHlpTimerUnlock(pDevIns, pThis->hPeriodicTimer), rc2);
470
471 if (idx == RTC_REG_A)
472 {
473 /* UIP bit is read only */
474 pThis->cmos_data[RTC_REG_A] = (u32 & ~REG_A_UIP)
475 | (pThis->cmos_data[RTC_REG_A] & REG_A_UIP);
476 }
477 else
478 {
479 if (u32 & REG_B_SET)
480 {
481 /* set mode: reset UIP mode */
482 pThis->cmos_data[RTC_REG_A] &= ~REG_A_UIP;
483#if 0 /* This is probably wrong as it breaks changing the time/date in OS/2. */
484 u32 &= ~REG_B_UIE;
485#endif
486 }
487 else
488 {
489 /* if disabling set mode, update the time */
490 if (pThis->cmos_data[RTC_REG_B] & REG_B_SET)
491 rtc_set_time(pThis);
492 }
493 pThis->cmos_data[RTC_REG_B] = u32;
494 }
495
496 rtc_timer_update(pDevIns, pThis, PDMDevHlpTimerGet(pDevIns, pThis->hPeriodicTimer));
497
498 PDMDevHlpTimerUnlock(pDevIns, pThis->hPeriodicTimer);
499 /* the caller leaves the other lock. */
500 break;
501 }
502
503 case RTC_REG_C:
504 case RTC_REG_D:
505 /* cannot write to them */
506 break;
507
508 default:
509 pThis->cmos_data[pThis->cmos_index[bank]] = u32;
510 break;
511 }
512 }
513
514 return VINF_SUCCESS;
515}
516
517#ifdef IN_RING3
518
519/* -=-=-=-=-=- Debug Info Handlers -=-=-=-=-=- */
520
521/**
522 * @callback_method_impl{FNDBGFHANDLERDEV,
523 * Dumps the cmos Bank Info.}
524 */
525static DECLCALLBACK(void) rtcCmosBankInfo(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
526{
527 RT_NOREF1(pszArgs);
528 PRTCSTATE pThis = PDMINS_2_DATA(pDevIns, PRTCSTATE);
529
530 pHlp->pfnPrintf(pHlp,
531 "First CMOS bank, offsets 0x0E - 0x7F\n"
532 "Offset %02x : --- use 'info rtc' to show CMOS clock ---", 0);
533 for (unsigned iCmos = CMOS_BANK_LOWER_LIMIT; iCmos <= CMOS_BANK_UPPER_LIMIT; iCmos++)
534 {
535 if ((iCmos & 15) == 0)
536 pHlp->pfnPrintf(pHlp, "Offset %02x : %02x", iCmos, pThis->cmos_data[iCmos]);
537 else if ((iCmos & 15) == 8)
538 pHlp->pfnPrintf(pHlp, "-%02x", pThis->cmos_data[iCmos]);
539 else if ((iCmos & 15) == 15)
540 pHlp->pfnPrintf(pHlp, " %02x\n", pThis->cmos_data[iCmos]);
541 else
542 pHlp->pfnPrintf(pHlp, " %02x", pThis->cmos_data[iCmos]);
543 }
544}
545
546/**
547 * @callback_method_impl{FNDBGFHANDLERDEV,
548 * Dumps the cmos Bank2 Info.}
549 */
550static DECLCALLBACK(void) rtcCmosBank2Info(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
551{
552 RT_NOREF1(pszArgs);
553 PRTCSTATE pThis = PDMINS_2_DATA(pDevIns, PRTCSTATE);
554
555 pHlp->pfnPrintf(pHlp, "Second CMOS bank, offsets 0x80 - 0xFF\n");
556 for (uint16_t iCmos = CMOS_BANK2_LOWER_LIMIT; iCmos <= CMOS_BANK2_UPPER_LIMIT; iCmos++)
557 {
558 if ((iCmos & 15) == 0)
559 pHlp->pfnPrintf(pHlp, "Offset %02x : %02x", iCmos, pThis->cmos_data[iCmos]);
560 else if ((iCmos & 15) == 8)
561 pHlp->pfnPrintf(pHlp, "-%02x", pThis->cmos_data[iCmos]);
562 else if ((iCmos & 15) == 15)
563 pHlp->pfnPrintf(pHlp, " %02x\n", pThis->cmos_data[iCmos]);
564 else
565 pHlp->pfnPrintf(pHlp, " %02x", pThis->cmos_data[iCmos]);
566 }
567}
568
569/**
570 * @callback_method_impl{FNDBGFHANDLERDEV,
571 * Dumps the cmos RTC Info.}
572 */
573static DECLCALLBACK(void) rtcCmosClockInfo(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
574{
575 RT_NOREF1(pszArgs);
576 PRTCSTATE pThis = PDMINS_2_DATA(pDevIns, PRTCSTATE);
577 uint8_t u8Sec = from_bcd(pThis, pThis->cmos_data[RTC_SECONDS]);
578 uint8_t u8Min = from_bcd(pThis, pThis->cmos_data[RTC_MINUTES]);
579 uint8_t u8Hr = from_bcd(pThis, pThis->cmos_data[RTC_HOURS] & 0x7f);
580 if ( !(pThis->cmos_data[RTC_REG_B] & 0x02)
581 && (pThis->cmos_data[RTC_HOURS] & 0x80))
582 u8Hr += 12;
583 uint8_t u8Day = from_bcd(pThis, pThis->cmos_data[RTC_DAY_OF_MONTH]);
584 uint8_t u8Month = from_bcd(pThis, pThis->cmos_data[RTC_MONTH]) ;
585 uint8_t u8Year = from_bcd(pThis, pThis->cmos_data[RTC_YEAR]);
586 pHlp->pfnPrintf(pHlp, "Time: %02u:%02u:%02u Date: %02u-%02u-%02u\n",
587 u8Hr, u8Min, u8Sec, u8Year, u8Month, u8Day);
588 pHlp->pfnPrintf(pHlp, "REG A=%02x B=%02x C=%02x D=%02x\n",
589 pThis->cmos_data[RTC_REG_A], pThis->cmos_data[RTC_REG_B],
590 pThis->cmos_data[RTC_REG_C], pThis->cmos_data[RTC_REG_D]);
591}
592
593
594
595/* -=-=-=-=-=- Timers and their support code -=-=-=-=-=- */
596
597
598/**
599 * @callback_method_impl{FNTMTIMERDEV, periodic}
600 */
601static DECLCALLBACK(void) rtcTimerPeriodic(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
602{
603 RT_NOREF2(pTimer, pvUser);
604 PRTCSTATE pThis = PDMINS_2_DATA(pDevIns, PRTCSTATE);
605 Assert(pTimer == PDMDevHlpTimerToPtr(pDevIns, pThis->hPeriodicTimer));
606 Assert(PDMDevHlpTimerIsLockOwner(pDevIns, pThis->hPeriodicTimer));
607 Assert(PDMDevHlpCritSectIsOwner(pDevIns, pDevIns->CTX_SUFF(pCritSectRo)));
608
609 rtc_timer_update(pDevIns, pThis, pThis->next_periodic_time);
610 STAM_COUNTER_INC(&pThis->StatRTCTimerCB);
611 pThis->cmos_data[RTC_REG_C] |= 0xc0;
612
613 rtc_raise_irq(pDevIns, pThis, 1);
614}
615
616
617/* month is between 0 and 11. */
618static int get_days_in_month(int month, int year)
619{
620 static const int days_tab[12] =
621 {
622 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
623 };
624 int d;
625
626 if ((unsigned )month >= 12)
627 return 31;
628
629 d = days_tab[month];
630 if (month == 1)
631 {
632 if ((year % 4) == 0 && ((year % 100) != 0 || (year % 400) == 0))
633 d++;
634 }
635 return d;
636}
637
638
639/* update 'tm' to the next second */
640static void rtc_next_second(struct my_tm *tm)
641{
642 int days_in_month;
643
644 tm->tm_sec++;
645 if ((unsigned)tm->tm_sec >= 60)
646 {
647 tm->tm_sec = 0;
648 tm->tm_min++;
649 if ((unsigned)tm->tm_min >= 60)
650 {
651 tm->tm_min = 0;
652 tm->tm_hour++;
653 if ((unsigned)tm->tm_hour >= 24)
654 {
655 tm->tm_hour = 0;
656 /* next day */
657 tm->tm_wday++;
658 if ((unsigned)tm->tm_wday >= 7)
659 tm->tm_wday = 0;
660 days_in_month = get_days_in_month(tm->tm_mon,
661 tm->tm_year + 1900);
662 tm->tm_mday++;
663 if (tm->tm_mday < 1)
664 tm->tm_mday = 1;
665 else if (tm->tm_mday > days_in_month)
666 {
667 tm->tm_mday = 1;
668 tm->tm_mon++;
669 if (tm->tm_mon >= 12)
670 {
671 tm->tm_mon = 0;
672 tm->tm_year++;
673 }
674 }
675 }
676 }
677 }
678}
679
680
681/**
682 * @callback_method_impl{FNTMTIMERDEV, Second timer.}
683 */
684static DECLCALLBACK(void) rtcTimerSecond(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
685{
686 PRTCSTATE pThis = PDMINS_2_DATA(pDevIns, PRTCSTATE);
687
688 Assert(PDMDevHlpTimerIsLockOwner(pDevIns, pThis->hPeriodicTimer));
689 Assert(PDMDevHlpCritSectIsOwner(pDevIns, pDevIns->CTX_SUFF(pCritSectRo)));
690 Assert(pTimer == PDMDevHlpTimerToPtr(pDevIns, pThis->hSecondTimer));
691 RT_NOREF(pvUser, pTimer);
692
693 /* if the oscillator is not in normal operation, we do not update */
694 if ((pThis->cmos_data[RTC_REG_A] & 0x70) != 0x20)
695 {
696 pThis->next_second_time += PDMDevHlpTimerGetFreq(pDevIns, pThis->hSecondTimer);
697 PDMDevHlpTimerSet(pDevIns, pThis->hSecondTimer, pThis->next_second_time);
698 }
699 else
700 {
701 rtc_next_second(&pThis->current_tm);
702
703 if (!(pThis->cmos_data[RTC_REG_B] & REG_B_SET))
704 {
705 /* update in progress bit */
706 Log2(("RTC: UIP %x -> 1\n", !!(pThis->cmos_data[RTC_REG_A] & REG_A_UIP)));
707 pThis->cmos_data[RTC_REG_A] |= REG_A_UIP;
708 }
709
710 /* 244140 ns = 8 / 32768 seconds */
711 uint64_t delay = PDMDevHlpTimerFromNano(pDevIns, pThis->hSecondTimer2, 244140);
712 PDMDevHlpTimerSet(pDevIns, pThis->hSecondTimer2, pThis->next_second_time + delay);
713 }
714}
715
716
717/* Used by rtc_set_date and rtcTimerSecond2. */
718static void rtc_copy_date(PRTCSTATE pThis)
719{
720 const struct my_tm *tm = &pThis->current_tm;
721
722 pThis->cmos_data[RTC_SECONDS] = to_bcd(pThis, tm->tm_sec);
723 pThis->cmos_data[RTC_MINUTES] = to_bcd(pThis, tm->tm_min);
724 if (pThis->cmos_data[RTC_REG_B] & 0x02)
725 {
726 /* 24 hour format */
727 pThis->cmos_data[RTC_HOURS] = to_bcd(pThis, tm->tm_hour);
728 }
729 else
730 {
731 /* 12 hour format */
732 int h = tm->tm_hour % 12;
733 pThis->cmos_data[RTC_HOURS] = to_bcd(pThis, h ? h : 12);
734 if (tm->tm_hour >= 12)
735 pThis->cmos_data[RTC_HOURS] |= 0x80;
736 }
737 pThis->cmos_data[RTC_DAY_OF_WEEK] = to_bcd(pThis, tm->tm_wday);
738 pThis->cmos_data[RTC_DAY_OF_MONTH] = to_bcd(pThis, tm->tm_mday);
739 pThis->cmos_data[RTC_MONTH] = to_bcd(pThis, tm->tm_mon + 1);
740 pThis->cmos_data[RTC_YEAR] = to_bcd(pThis, tm->tm_year % 100);
741}
742
743
744/**
745 * @callback_method_impl{FNTMTIMERDEV, Second2 timer.}
746 */
747static DECLCALLBACK(void) rtcTimerSecond2(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
748{
749 PRTCSTATE pThis = PDMINS_2_DATA(pDevIns, PRTCSTATE);
750
751 Assert(PDMDevHlpTimerIsLockOwner(pDevIns, pThis->hPeriodicTimer));
752 Assert(PDMDevHlpCritSectIsOwner(pDevIns, pDevIns->CTX_SUFF(pCritSectRo)));
753 Assert(pTimer == PDMDevHlpTimerToPtr(pDevIns, pThis->hSecondTimer2));
754 RT_NOREF2(pTimer, pvUser);
755
756 if (!(pThis->cmos_data[RTC_REG_B] & REG_B_SET))
757 rtc_copy_date(pThis);
758
759 /* check alarm */
760 if (pThis->cmos_data[RTC_REG_B] & REG_B_AIE)
761 {
762 if ( ( (pThis->cmos_data[RTC_SECONDS_ALARM] & 0xc0) == 0xc0
763 || from_bcd(pThis, pThis->cmos_data[RTC_SECONDS_ALARM]) == pThis->current_tm.tm_sec)
764 && ( (pThis->cmos_data[RTC_MINUTES_ALARM] & 0xc0) == 0xc0
765 || from_bcd(pThis, pThis->cmos_data[RTC_MINUTES_ALARM]) == pThis->current_tm.tm_min)
766 && ( (pThis->cmos_data[RTC_HOURS_ALARM ] & 0xc0) == 0xc0
767 || from_bcd(pThis, pThis->cmos_data[RTC_HOURS_ALARM ]) == pThis->current_tm.tm_hour)
768 )
769 {
770 pThis->cmos_data[RTC_REG_C] |= 0xa0;
771 rtc_raise_irq(pDevIns, pThis, 1);
772 }
773 }
774
775 /* update ended interrupt */
776 if (pThis->cmos_data[RTC_REG_B] & REG_B_UIE)
777 {
778 pThis->cmos_data[RTC_REG_C] |= 0x90;
779 rtc_raise_irq(pDevIns, pThis, 1);
780 }
781
782 /* clear update in progress bit */
783 Log2(("RTC: UIP %x -> 0\n", !!(pThis->cmos_data[RTC_REG_A] & REG_A_UIP)));
784 pThis->cmos_data[RTC_REG_A] &= ~REG_A_UIP;
785
786 pThis->next_second_time += PDMDevHlpTimerGetFreq(pDevIns, pThis->hSecondTimer);
787 PDMDevHlpTimerSet(pDevIns, pThis->hSecondTimer, pThis->next_second_time);
788}
789
790
791/* -=-=-=-=-=- Saved State -=-=-=-=-=- */
792
793
794/**
795 * @callback_method_impl{FNSSMDEVLIVEEXEC}
796 */
797static DECLCALLBACK(int) rtcLiveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uPass)
798{
799 RT_NOREF1(uPass);
800 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
801 PRTCSTATE pThis = PDMINS_2_DATA(pDevIns, PRTCSTATE);
802
803 pHlp->pfnSSMPutU8( pSSM, pThis->irq);
804 pHlp->pfnSSMPutIOPort(pSSM, pThis->IOPortBase);
805 pHlp->pfnSSMPutBool( pSSM, pThis->fUTC);
806
807 return VINF_SSM_DONT_CALL_AGAIN;
808}
809
810
811/**
812 * @callback_method_impl{FNSSMDEVSAVEEXEC}
813 */
814static DECLCALLBACK(int) rtcSaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
815{
816 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
817 PRTCSTATE pThis = PDMINS_2_DATA(pDevIns, PRTCSTATE);
818
819 /* The config. */
820 rtcLiveExec(pDevIns, pSSM, SSM_PASS_FINAL);
821
822 /* The state. */
823 pHlp->pfnSSMPutMem(pSSM, pThis->cmos_data, CMOS_BANK_SIZE);
824 pHlp->pfnSSMPutU8(pSSM, pThis->cmos_index[0]);
825
826 pHlp->pfnSSMPutS32(pSSM, pThis->current_tm.tm_sec);
827 pHlp->pfnSSMPutS32(pSSM, pThis->current_tm.tm_min);
828 pHlp->pfnSSMPutS32(pSSM, pThis->current_tm.tm_hour);
829 pHlp->pfnSSMPutS32(pSSM, pThis->current_tm.tm_wday);
830 pHlp->pfnSSMPutS32(pSSM, pThis->current_tm.tm_mday);
831 pHlp->pfnSSMPutS32(pSSM, pThis->current_tm.tm_mon);
832 pHlp->pfnSSMPutS32(pSSM, pThis->current_tm.tm_year);
833
834 PDMDevHlpTimerSave(pDevIns, pThis->hPeriodicTimer, pSSM);
835
836 pHlp->pfnSSMPutS64(pSSM, pThis->next_periodic_time);
837
838 pHlp->pfnSSMPutS64(pSSM, pThis->next_second_time);
839 PDMDevHlpTimerSave(pDevIns, pThis->hSecondTimer, pSSM);
840 PDMDevHlpTimerSave(pDevIns, pThis->hSecondTimer2, pSSM);
841
842 pHlp->pfnSSMPutBool(pSSM, pThis->fDisabledByHpet);
843
844 pHlp->pfnSSMPutMem(pSSM, &pThis->cmos_data[CMOS_BANK_SIZE], CMOS_BANK_SIZE);
845 return pHlp->pfnSSMPutU8(pSSM, pThis->cmos_index[1]);
846}
847
848
849/**
850 * @callback_method_impl{FNSSMDEVLOADEXEC}
851 */
852static DECLCALLBACK(int) rtcLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
853{
854 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
855 PRTCSTATE pThis = PDMINS_2_DATA(pDevIns, PRTCSTATE);
856 int rc;
857
858 if ( uVersion != RTC_SAVED_STATE_VERSION
859 && uVersion != RTC_SAVED_STATE_VERSION_VBOX_32PRE
860 && uVersion != RTC_SAVED_STATE_VERSION_VBOX_31
861 && uVersion != RTC_SAVED_STATE_VERSION_VBOX_30)
862 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
863
864 /* The config. */
865 if (uVersion > RTC_SAVED_STATE_VERSION_VBOX_30)
866 {
867 uint8_t u8Irq;
868 rc = pHlp->pfnSSMGetU8(pSSM, &u8Irq);
869 AssertRCReturn(rc, rc);
870 if (u8Irq != pThis->irq)
871 return pHlp->pfnSSMSetCfgError(pSSM, RT_SRC_POS, N_("Config mismatch - u8Irq: saved=%#x config=%#x"), u8Irq, pThis->irq);
872
873 RTIOPORT IOPortBase;
874 rc = pHlp->pfnSSMGetIOPort(pSSM, &IOPortBase);
875 AssertRCReturn(rc, rc);
876 if (IOPortBase != pThis->IOPortBase)
877 return pHlp->pfnSSMSetCfgError(pSSM, RT_SRC_POS, N_("Config mismatch - IOPortBase: saved=%RTiop config=%RTiop"), IOPortBase, pThis->IOPortBase);
878
879 bool fUTC;
880 rc = pHlp->pfnSSMGetBool(pSSM, &fUTC);
881 AssertRCReturn(rc, rc);
882 if (fUTC != pThis->fUTC)
883 LogRel(("RTC: Config mismatch - fUTC: saved=%RTbool config=%RTbool\n", fUTC, pThis->fUTC));
884 }
885
886 if (uPass != SSM_PASS_FINAL)
887 return VINF_SUCCESS;
888
889 /* The state. */
890 pHlp->pfnSSMGetMem(pSSM, pThis->cmos_data, CMOS_BANK_SIZE);
891 pHlp->pfnSSMGetU8(pSSM, &pThis->cmos_index[0]);
892
893 pHlp->pfnSSMGetS32(pSSM, &pThis->current_tm.tm_sec);
894 pHlp->pfnSSMGetS32(pSSM, &pThis->current_tm.tm_min);
895 pHlp->pfnSSMGetS32(pSSM, &pThis->current_tm.tm_hour);
896 pHlp->pfnSSMGetS32(pSSM, &pThis->current_tm.tm_wday);
897 pHlp->pfnSSMGetS32(pSSM, &pThis->current_tm.tm_mday);
898 pHlp->pfnSSMGetS32(pSSM, &pThis->current_tm.tm_mon);
899 pHlp->pfnSSMGetS32(pSSM, &pThis->current_tm.tm_year);
900
901 PDMDevHlpTimerLoad(pDevIns, pThis->hPeriodicTimer, pSSM);
902
903 pHlp->pfnSSMGetS64(pSSM, &pThis->next_periodic_time);
904
905 pHlp->pfnSSMGetS64(pSSM, &pThis->next_second_time);
906 PDMDevHlpTimerLoad(pDevIns, pThis->hSecondTimer, pSSM);
907 PDMDevHlpTimerLoad(pDevIns, pThis->hSecondTimer2, pSSM);
908
909 if (uVersion > RTC_SAVED_STATE_VERSION_VBOX_31)
910 pHlp->pfnSSMGetBool(pSSM, &pThis->fDisabledByHpet);
911
912 if (uVersion > RTC_SAVED_STATE_VERSION_VBOX_32PRE)
913 {
914 /* Second CMOS bank. */
915 pHlp->pfnSSMGetMem(pSSM, &pThis->cmos_data[CMOS_BANK_SIZE], CMOS_BANK_SIZE);
916 pHlp->pfnSSMGetU8(pSSM, &pThis->cmos_index[1]);
917 }
918
919 int period_code = pThis->cmos_data[RTC_REG_A] & 0x0f;
920 if ( period_code != 0
921 && (pThis->cmos_data[RTC_REG_B] & REG_B_PIE))
922 {
923 if (period_code <= 2)
924 period_code += 7;
925 int period = 1 << (period_code - 1);
926 LogRel(("RTC: period=%#x (%d) %u Hz (restore)\n", period, period, _32K / period));
927 PDMDevHlpCritSectEnter(pDevIns, pDevIns->pCritSectRoR3, VINF_SUCCESS);
928 PDMDevHlpTimerSetFrequencyHint(pDevIns, pThis->hPeriodicTimer, _32K / period);
929 PDMDevHlpCritSectLeave(pDevIns, pDevIns->pCritSectRoR3);
930 pThis->CurLogPeriod = period;
931 pThis->CurHintPeriod = period;
932 }
933 else
934 {
935 LogRel(("RTC: Stopped the periodic timer (restore)\n"));
936 pThis->CurLogPeriod = 0;
937 pThis->CurHintPeriod = 0;
938 }
939 pThis->cRelLogEntries = 0;
940
941 return VINF_SUCCESS;
942}
943
944
945/* -=-=-=-=-=- PDM Interface provided by the RTC device -=-=-=-=-=- */
946
947/**
948 * Calculate and update the standard CMOS checksum.
949 *
950 * @param pThis Pointer to the RTC state data.
951 */
952static void rtcCalcCRC(PRTCSTATE pThis)
953{
954 uint16_t u16 = 0;
955 for (unsigned i = RTC_CRC_START; i <= RTC_CRC_LAST; i++)
956 u16 += pThis->cmos_data[i];
957
958 pThis->cmos_data[RTC_CRC_LOW] = u16 & 0xff;
959 pThis->cmos_data[RTC_CRC_HIGH] = (u16 >> 8) & 0xff;
960}
961
962
963/**
964 * @interface_method_impl{PDMRTCREG,pfnWrite}
965 */
966static DECLCALLBACK(int) rtcCMOSWrite(PPDMDEVINS pDevIns, unsigned iReg, uint8_t u8Value)
967{
968 PRTCSTATE pThis = PDMINS_2_DATA(pDevIns, PRTCSTATE);
969 Assert(PDMDevHlpCritSectIsOwner(pDevIns, pDevIns->pCritSectRoR3));
970 if (iReg < RT_ELEMENTS(pThis->cmos_data))
971 {
972 pThis->cmos_data[iReg] = u8Value;
973
974 /* does it require checksum update? */
975 if ( iReg >= RTC_CRC_START
976 && iReg <= RTC_CRC_LAST)
977 rtcCalcCRC(pThis);
978
979 return VINF_SUCCESS;
980 }
981
982 AssertMsgFailed(("iReg=%d\n", iReg));
983 return VERR_INVALID_PARAMETER;
984}
985
986
987/**
988 * @interface_method_impl{PDMRTCREG,pfnRead}
989 */
990static DECLCALLBACK(int) rtcCMOSRead(PPDMDEVINS pDevIns, unsigned iReg, uint8_t *pu8Value)
991{
992 PRTCSTATE pThis = PDMINS_2_DATA(pDevIns, PRTCSTATE);
993 Assert(PDMDevHlpCritSectIsOwner(pDevIns, pDevIns->pCritSectRoR3));
994
995 if (iReg < RT_ELEMENTS(pThis->cmos_data))
996 {
997 *pu8Value = pThis->cmos_data[iReg];
998 return VINF_SUCCESS;
999 }
1000 AssertMsgFailed(("iReg=%d\n", iReg));
1001 return VERR_INVALID_PARAMETER;
1002}
1003
1004
1005/**
1006 * @interface_method_impl{PDMIHPETLEGACYNOTIFY,pfnModeChanged}
1007 */
1008static DECLCALLBACK(void) rtcHpetLegacyNotify_ModeChanged(PPDMIHPETLEGACYNOTIFY pInterface, bool fActivated)
1009{
1010 PRTCSTATECC pThisCC = RT_FROM_MEMBER(pInterface, RTCSTATER3, IHpetLegacyNotify);
1011 PPDMDEVINS pDevIns = pThisCC->pDevInsR3;
1012 PDMDevHlpCritSectEnter(pDevIns, pDevIns->pCritSectRoR3, VERR_IGNORED);
1013
1014 pThisCC->pShared->fDisabledByHpet = fActivated;
1015
1016 PDMDevHlpCritSectLeave(pDevIns, pDevIns->pCritSectRoR3);
1017}
1018
1019
1020
1021/* -=-=-=-=-=- IBase -=-=-=-=-=- */
1022
1023/**
1024 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
1025 */
1026static DECLCALLBACK(void *) rtcQueryInterface(PPDMIBASE pInterface, const char *pszIID)
1027{
1028 PPDMDEVINS pDevIns = RT_FROM_MEMBER(pInterface, PDMDEVINS, IBase);
1029 PRTCSTATECC pThisCC = PDMINS_2_DATA_CC(pDevIns, PRTCSTATECC);
1030 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDevIns->IBase);
1031 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHPETLEGACYNOTIFY, &pThisCC->IHpetLegacyNotify);
1032 return NULL;
1033}
1034
1035
1036/* -=-=-=-=-=- PDMDEVREG -=-=-=-=-=- */
1037
1038static void rtc_set_memory(PRTCSTATE pThis, int addr, int val)
1039{
1040 if (addr >= 0 && addr <= 127)
1041 pThis->cmos_data[addr] = val;
1042}
1043
1044
1045static void rtc_set_date(PRTCSTATE pThis, const struct my_tm *tm)
1046{
1047 pThis->current_tm = *tm;
1048 rtc_copy_date(pThis);
1049}
1050
1051
1052/**
1053 * @interface_method_impl{PDMDEVREG,pfnInitComplete}
1054 *
1055 * Used to set the clock.
1056 */
1057static DECLCALLBACK(int) rtcInitComplete(PPDMDEVINS pDevIns)
1058{
1059 /** @todo this should be (re)done at power on if we didn't load a state... */
1060 PRTCSTATE pThis = PDMINS_2_DATA(pDevIns, PRTCSTATE);
1061
1062 /*
1063 * Set the CMOS date/time.
1064 */
1065 RTTIMESPEC Now;
1066 PDMDevHlpTMUtcNow(pDevIns, &Now);
1067 RTTIME Time;
1068 if (pThis->fUTC)
1069 RTTimeExplode(&Time, &Now);
1070 else
1071 RTTimeLocalExplode(&Time, &Now);
1072
1073 struct my_tm Tm;
1074 memset(&Tm, 0, sizeof(Tm));
1075 Tm.tm_year = Time.i32Year - 1900;
1076 Tm.tm_mon = Time.u8Month - 1;
1077 Tm.tm_mday = Time.u8MonthDay;
1078 Tm.tm_wday = (Time.u8WeekDay + 1 + 7) % 7; /* 0 = Monday -> Sunday */
1079 Tm.tm_yday = Time.u16YearDay - 1;
1080 Tm.tm_hour = Time.u8Hour;
1081 Tm.tm_min = Time.u8Minute;
1082 Tm.tm_sec = Time.u8Second;
1083
1084 rtc_set_date(pThis, &Tm);
1085
1086 int iYear = to_bcd(pThis, (Tm.tm_year / 100) + 19); /* tm_year is 1900 based */
1087 rtc_set_memory(pThis, 0x32, iYear); /* 32h - Century Byte (BCD value for the century */
1088 rtc_set_memory(pThis, 0x37, iYear); /* 37h - (IBM PS/2) Date Century Byte */
1089
1090 /*
1091 * Recalculate the checksum just in case.
1092 */
1093 rtcCalcCRC(pThis);
1094
1095 Log(("CMOS bank 0: \n%16.128Rhxd\n", &pThis->cmos_data[0]));
1096 Log(("CMOS bank 1: \n%16.128Rhxd\n", &pThis->cmos_data[CMOS_BANK_SIZE]));
1097 return VINF_SUCCESS;
1098}
1099
1100
1101/**
1102 * @interface_method_impl{PDMDEVREG,pfnRelocate}
1103 */
1104static DECLCALLBACK(void) rtcRelocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
1105{
1106 RT_NOREF1(offDelta);
1107 PRTCSTATERC pThisRC = PDMINS_2_DATA_RC(pDevIns, PRTCSTATERC);
1108 pThisRC->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
1109}
1110
1111
1112/**
1113 * @interface_method_impl{PDMDEVREG,pfnReset}
1114 */
1115static DECLCALLBACK(void) rtcReset(PPDMDEVINS pDevIns)
1116{
1117 PRTCSTATE pThis = PDMINS_2_DATA(pDevIns, PRTCSTATE);
1118
1119 /* Reset index values (important for second bank). */
1120 pThis->cmos_index[0] = 0;
1121 pThis->cmos_index[1] = CMOS_BANK_SIZE; /* Point to start of second bank. */
1122}
1123
1124
1125/**
1126 * @interface_method_impl{PDMDEVREG,pfnConstruct}
1127 */
1128static DECLCALLBACK(int) rtcConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
1129{
1130 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
1131 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
1132 PRTCSTATE pThis = PDMINS_2_DATA(pDevIns, PRTCSTATE);
1133 PRTCSTATECC pThisCC = PDMINS_2_DATA_CC(pDevIns, PRTCSTATECC);
1134 int rc;
1135 Assert(iInstance == 0); RT_NOREF(iInstance);
1136
1137 /*
1138 * Validate configuration.
1139 */
1140 PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, "Irq|Base|UseUTC", "");
1141
1142 /*
1143 * Init the data.
1144 */
1145 uint8_t u8Irq;
1146 rc = pHlp->pfnCFGMQueryU8Def(pCfg, "Irq", &u8Irq, 8);
1147 if (RT_FAILURE(rc))
1148 return PDMDEV_SET_ERROR(pDevIns, rc,
1149 N_("Configuration error: Querying \"Irq\" as a uint8_t failed"));
1150 pThis->irq = u8Irq;
1151
1152 rc = pHlp->pfnCFGMQueryPortDef(pCfg, "Base", &pThis->IOPortBase, 0x70);
1153 if (RT_FAILURE(rc))
1154 return PDMDEV_SET_ERROR(pDevIns, rc,
1155 N_("Configuration error: Querying \"Base\" as a RTIOPORT failed"));
1156
1157 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "UseUTC", &pThis->fUTC, false);
1158 if (RT_FAILURE(rc))
1159 return PDMDEV_SET_ERROR(pDevIns, rc,
1160 N_("Configuration error: Querying \"UseUTC\" as a bool failed"));
1161
1162 Log(("RTC: Irq=%#x Base=%#x fR0Enabled=%RTbool fRCEnabled=%RTbool\n",
1163 u8Irq, pThis->IOPortBase, pDevIns->fR0Enabled, pDevIns->fRCEnabled));
1164
1165
1166 pThis->cmos_data[RTC_REG_A] = 0x26;
1167 pThis->cmos_data[RTC_REG_B] = 0x02;
1168 pThis->cmos_data[RTC_REG_C] = 0x00;
1169 pThis->cmos_data[RTC_REG_D] = 0x80;
1170 pThis->fDisabledByHpet = false;
1171 pThis->cmos_index[1] = CMOS_BANK_SIZE; /* Point to start of second bank. */
1172
1173 pThisCC->pDevInsR3 = pDevIns;
1174 pThisCC->RtcReg.u32Version = PDM_RTCREG_VERSION;
1175 pThisCC->RtcReg.pfnRead = rtcCMOSRead;
1176 pThisCC->RtcReg.pfnWrite = rtcCMOSWrite;
1177 pThisCC->IHpetLegacyNotify.pfnModeChanged = rtcHpetLegacyNotify_ModeChanged;
1178
1179 /* IBase */
1180 pDevIns->IBase.pfnQueryInterface = rtcQueryInterface;
1181
1182 /*
1183 * Create timers.
1184 */
1185 /* Periodic timer. */
1186 rc = PDMDevHlpTimerCreate(pDevIns, TMCLOCK_VIRTUAL_SYNC, rtcTimerPeriodic, pThis,
1187 TMTIMER_FLAGS_DEFAULT_CRIT_SECT, "MC146818 RTC/CMOS - Periodic", &pThis->hPeriodicTimer);
1188 AssertRCReturn(rc, rc);
1189
1190 /* Seconds timer. */
1191 rc = PDMDevHlpTimerCreate(pDevIns, TMCLOCK_VIRTUAL_SYNC, rtcTimerSecond, pThis,
1192 TMTIMER_FLAGS_DEFAULT_CRIT_SECT, "MC146818 RTC/CMOS - Second", &pThis->hSecondTimer);
1193 AssertRCReturn(rc, rc);
1194
1195 /* The second2 timer, this is always active. */
1196 rc = PDMDevHlpTimerCreate(pDevIns, TMCLOCK_VIRTUAL_SYNC, rtcTimerSecond2, pThis,
1197 TMTIMER_FLAGS_DEFAULT_CRIT_SECT, "MC146818 RTC/CMOS - Second2", &pThis->hSecondTimer2);
1198 AssertRCReturn(rc, rc);
1199
1200 pThis->next_second_time = PDMDevHlpTimerGet(pDevIns, pThis->hSecondTimer2)
1201 + (PDMDevHlpTimerGetFreq(pDevIns, pThis->hSecondTimer2) * 99) / 100;
1202 rc = PDMDevHlpTimerLock(pDevIns, pThis->hSecondTimer2, VERR_IGNORED);
1203 AssertRCReturn(rc, rc);
1204 rc = PDMDevHlpTimerSet(pDevIns, pThis->hSecondTimer2, pThis->next_second_time);
1205 PDMDevHlpTimerUnlock(pDevIns, pThis->hSecondTimer2);
1206 AssertRCReturn(rc, rc);
1207
1208 /*
1209 * Register I/O ports.
1210 */
1211 static const IOMIOPORTDESC g_aIoPortDescs[] =
1212 {
1213 { NULL, "ADDR - CMOS Bank #1", NULL, NULL },
1214 { "DATA - CMOS Bank #1", "DATA - CMOS Bank #1", NULL, NULL },
1215 { NULL, "ADDR - CMOS Bank #2", NULL, NULL },
1216 { "DATA - CMOS Bank #2", "DATA - CMOS Bank #2", NULL, NULL },
1217 { NULL, NULL, NULL, NULL }
1218 };
1219 rc = PDMDevHlpIoPortCreateAndMap(pDevIns, pThis->IOPortBase, 4, rtcIOPortWrite, rtcIOPortRead,
1220 "MC146818 RTC/CMOS", g_aIoPortDescs, &pThis->hIoPorts);
1221 AssertRCReturn(rc, rc);
1222
1223 /*
1224 * Register the saved state.
1225 */
1226 rc = PDMDevHlpSSMRegister3(pDevIns, RTC_SAVED_STATE_VERSION, sizeof(*pThis), rtcLiveExec, rtcSaveExec, rtcLoadExec);
1227 AssertRCReturn(rc, rc);
1228
1229 /*
1230 * Register ourselves as the RTC/CMOS with PDM.
1231 */
1232 rc = PDMDevHlpRTCRegister(pDevIns, &pThisCC->RtcReg, &pThisCC->pRtcHlpR3);
1233 AssertRCReturn(rc, rc);
1234
1235 /*
1236 * Register debugger info callback.
1237 */
1238 PDMDevHlpDBGFInfoRegister(pDevIns, "cmos1", "Display CMOS Bank 1 Info (0x0e-0x7f). No arguments. See also rtc.", rtcCmosBankInfo);
1239 PDMDevHlpDBGFInfoRegister(pDevIns, "cmos2", "Display CMOS Bank 2 Info (0x0e-0x7f). No arguments.", rtcCmosBank2Info);
1240 PDMDevHlpDBGFInfoRegister(pDevIns, "rtc", "Display CMOS RTC (0x00-0x0d). No arguments. See also cmos1 & cmos2", rtcCmosClockInfo);
1241
1242 /*
1243 * Register statistics.
1244 */
1245 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatRTCIrq, STAMTYPE_COUNTER, "/TM/RTC/Irq", STAMUNIT_OCCURENCES, "The number of times a RTC interrupt was triggered.");
1246 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatRTCTimerCB, STAMTYPE_COUNTER, "/TM/RTC/TimerCB", STAMUNIT_OCCURENCES, "The number of times the RTC timer callback ran.");
1247
1248 return VINF_SUCCESS;
1249}
1250
1251#else /* !IN_RING3 */
1252
1253/**
1254 * @callback_method_impl{PDMDEVREGR0,pfnConstruct}
1255 */
1256static DECLCALLBACK(int) rtcRZConstruct(PPDMDEVINS pDevIns)
1257{
1258 PRTCSTATE pThis = PDMINS_2_DATA(pDevIns, PRTCSTATE);
1259 PRTCSTATER0 pThisCC = PDMINS_2_DATA_CC(pDevIns, PRTCSTATECC);
1260 pThisCC->CTX_SUFF(pDevIns) = pDevIns;
1261
1262 int rc = PDMDevHlpIoPortSetUpContext(pDevIns, pThis->hIoPorts, rtcIOPortWrite, rtcIOPortRead, NULL /*pvUser*/);
1263 AssertRCReturn(rc, rc);
1264
1265 return VINF_SUCCESS;
1266}
1267
1268#endif /* !IN_RING3 */
1269
1270/**
1271 * The device registration structure.
1272 */
1273const PDMDEVREG g_DeviceMC146818 =
1274{
1275 /* .u32Version = */ PDM_DEVREG_VERSION,
1276 /* .uReserved0 = */ 0,
1277 /* .szName = */ "mc146818",
1278 /* .fFlags = */ PDM_DEVREG_FLAGS_RC | PDM_DEVREG_FLAGS_R0 | PDM_DEVREG_FLAGS_NEW_STYLE
1279 | PDM_DEVREG_FLAGS_HOST_BITS_DEFAULT | PDM_DEVREG_FLAGS_GUEST_BITS_DEFAULT,
1280 /* .fClass = */ PDM_DEVREG_CLASS_RTC,
1281 /* .cMaxInstances = */ 1,
1282 /* .uSharedVersion = */ 1,
1283 /* .cbInstanceShared = */ sizeof(RTCSTATE),
1284 /* .cbInstanceCC = */ sizeof(RTCSTATECC),
1285 /* .cbInstanceRC = */ sizeof(RTCSTATERC),
1286 /* .cMaxPciDevices = */ 0,
1287 /* .cMaxMsixVectors = */ 0,
1288 /* .pszDescription = */ "Motorola MC146818 RTC/CMOS Device.",
1289#ifdef IN_RING3
1290 /* .pszRCMod = */ "VBoxDDRC.rc",
1291 /* .pszR0Mod = */ "VBoxDDR0.r0",
1292 /* .pfnConstruct = */ rtcConstruct,
1293 /* .pfnDestruct = */ NULL,
1294 /* .pfnRelocate = */ rtcRelocate,
1295 /* .pfnMemSetup = */ NULL,
1296 /* .pfnPowerOn = */ NULL,
1297 /* .pfnReset = */ rtcReset,
1298 /* .pfnSuspend = */ NULL,
1299 /* .pfnResume = */ NULL,
1300 /* .pfnAttach = */ NULL,
1301 /* .pfnDetach = */ NULL,
1302 /* .pfnQueryInterface = */ NULL,
1303 /* .pfnInitComplete = */ rtcInitComplete,
1304 /* .pfnPowerOff = */ NULL,
1305 /* .pfnSoftReset = */ NULL,
1306 /* .pfnReserved0 = */ NULL,
1307 /* .pfnReserved1 = */ NULL,
1308 /* .pfnReserved2 = */ NULL,
1309 /* .pfnReserved3 = */ NULL,
1310 /* .pfnReserved4 = */ NULL,
1311 /* .pfnReserved5 = */ NULL,
1312 /* .pfnReserved6 = */ NULL,
1313 /* .pfnReserved7 = */ NULL,
1314#elif defined(IN_RING0)
1315 /* .pfnEarlyConstruct = */ NULL,
1316 /* .pfnConstruct = */ rtcRZConstruct,
1317 /* .pfnDestruct = */ NULL,
1318 /* .pfnFinalDestruct = */ NULL,
1319 /* .pfnRequest = */ NULL,
1320 /* .pfnReserved0 = */ NULL,
1321 /* .pfnReserved1 = */ NULL,
1322 /* .pfnReserved2 = */ NULL,
1323 /* .pfnReserved3 = */ NULL,
1324 /* .pfnReserved4 = */ NULL,
1325 /* .pfnReserved5 = */ NULL,
1326 /* .pfnReserved6 = */ NULL,
1327 /* .pfnReserved7 = */ NULL,
1328#elif defined(IN_RC)
1329 /* .pfnConstruct = */ rtcRZConstruct,
1330 /* .pfnReserved0 = */ NULL,
1331 /* .pfnReserved1 = */ NULL,
1332 /* .pfnReserved2 = */ NULL,
1333 /* .pfnReserved3 = */ NULL,
1334 /* .pfnReserved4 = */ NULL,
1335 /* .pfnReserved5 = */ NULL,
1336 /* .pfnReserved6 = */ NULL,
1337 /* .pfnReserved7 = */ NULL,
1338#else
1339# error "Not IN_RING3, IN_RING0 nor IN_RC!"
1340#endif
1341 /* .u32VersionEnd = */ PDM_DEVREG_VERSION
1342};
1343
1344#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

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