VirtualBox

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

Last change on this file since 81077 was 80960, checked in by vboxsync, 5 years ago

IOM,PDM,DevPCI,DevPciIch9,RTC: Fixes to the I/O port lookup table insertion code. Converted (mostly) the two PCI buses to the new PDM device style. The ICH9 variant wasn't actually dropping the default critsect, it turned out. Changed the new I/O port callbacks to return VBOXSTRICTRC rather than plain int. bugref:9218

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