VirtualBox

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

Last change on this file since 61497 was 60404, checked in by vboxsync, 9 years ago

VMM,Devices,Main: Implemented soft/warm reset for shutdown status codes 05h, 09h and 0Ah.

This is a shot at adjusting our VM reset handling to handle the ancient way of
getting a 286 out of protected mode and back to real mode. Our exiting reset
code (XXXR3Reset, PDMDEVREG::pfnReset, and so on) is doing a cold reset of the
system and then some additional device & memory initialization that the firmware
is usually responsible for doing. When the guest triggers a reset via the
keyboard controller, system control port A, CPU triple fault, and possibly ACPI,
only the CPU is supposed to be reset. The BIOS would then decide whether memory
and devices needed resetting as well, or if the resetter justed wanted to get out
protected mode and resume executing some real mode code pointed to by 467h.

  • New states SOFT_RESETTING and SOFT_RESETTING_LS. The latter returns to RUNNING_LS, not SUSPENDED_LS like for hard reset.
  • Added a firmware interface so the VMM/PDM can ask it whether we're supposed to do a hard reset or a soft(/warm) one.
  • Implemented firmware interface for the PC BIOS (but not EFI). It indicates soft(/warm) reset when CMOS[0xf] is 5, 9 or 10.
  • Moved the CMOS[0xf] resetting from the RTC device to the PC BIOS since it's firmware thing, not RTC.
  • Added a flag parameter to PDMDevHlpVMReset for specifying the source of the reset operation. One class of sources (GIM) will always trigger hard resets, whereas the others will check with the firmware first.
  • Added PDMR3GetResetInfo for query the flags passed to PDMDevHlpVMReset and for asking the firmware whether it's a hard or soft reset. The latter, however, is only done if only CPU 0 is active. Systems with more than one CPU in a state other than EMSTATE_WAIT_SIPI status will always be hard reset.
  • Added internal VMR3ResetFF and VMR3ResetTripleFault APIs for handling the VM_FF_RESET and VINF_EM_TRIPLE_FAULT conditions.
  • Added PMDR3ResetSoft and had it call pfnSoftReset (which is now defined).

Warning! Major PDM_DEVHLPR3_VERSION change, minor PDM_DEVREG_VERSION change.

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