VirtualBox

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

Last change on this file since 105745 was 98103, checked in by vboxsync, 23 months ago

Copyright year updates by scm.

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