VirtualBox

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

Last change on this file since 30846 was 30846, checked in by vboxsync, 14 years ago

DevRTC: No point calling LogRel when we are not in R3.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 35.2 KB
Line 
1/* $Id: DevRTC.cpp 30846 2010-07-14 15:39:25Z vboxsync $ */
2/** @file
3 * Motorola MC146818 RTC/CMOS Device with PIIX4 extensions.
4 */
5
6/*
7 * Copyright (C) 2006-2007 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 * --------------------------------------------------------------------
17 *
18 * This code is based on:
19 *
20 * QEMU MC146818 RTC emulation
21 *
22 * Copyright (c) 2003-2004 Fabrice Bellard
23 *
24 * Permission is hereby granted, free of charge, to any person obtaining a copy
25 * of this software and associated documentation files (the "Software"), to deal
26 * in the Software without restriction, including without limitation the rights
27 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
28 * copies of the Software, and to permit persons to whom the Software is
29 * furnished to do so, subject to the following conditions:
30 *
31 * The above copyright notice and this permission notice shall be included in
32 * all copies or substantial portions of the Software.
33 *
34 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
35 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
36 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
37 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
38 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
39 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
40 * THE SOFTWARE.
41 */
42
43/*******************************************************************************
44* Header Files *
45*******************************************************************************/
46#define LOG_GROUP LOG_GROUP_DEV_RTC
47#include <VBox/pdmdev.h>
48#include <VBox/log.h>
49#include <iprt/asm-math.h>
50#include <iprt/assert.h>
51#include <iprt/string.h>
52
53#ifdef IN_RING3
54# include <iprt/alloc.h>
55# include <iprt/uuid.h>
56#endif /* IN_RING3 */
57
58#include "../Builtins.h"
59
60struct RTCState;
61typedef struct RTCState RTCState;
62
63#define RTC_CRC_START 0x10
64#define RTC_CRC_LAST 0x2d
65#define RTC_CRC_HIGH 0x2e
66#define RTC_CRC_LOW 0x2f
67
68
69/*******************************************************************************
70* Internal Functions *
71*******************************************************************************/
72#ifndef VBOX_DEVICE_STRUCT_TESTCASE
73RT_C_DECLS_BEGIN
74PDMBOTHCBDECL(int) rtcIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb);
75PDMBOTHCBDECL(int) rtcIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb);
76PDMBOTHCBDECL(void) rtcTimerPeriodic(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser);
77PDMBOTHCBDECL(void) rtcTimerSecond(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser);
78PDMBOTHCBDECL(void) rtcTimerSecond2(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser);
79RT_C_DECLS_END
80#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
81
82
83/*******************************************************************************
84* Defined Constants And Macros *
85*******************************************************************************/
86/*#define DEBUG_CMOS*/
87
88#define RTC_SECONDS 0
89#define RTC_SECONDS_ALARM 1
90#define RTC_MINUTES 2
91#define RTC_MINUTES_ALARM 3
92#define RTC_HOURS 4
93#define RTC_HOURS_ALARM 5
94#define RTC_ALARM_DONT_CARE 0xC0
95
96#define RTC_DAY_OF_WEEK 6
97#define RTC_DAY_OF_MONTH 7
98#define RTC_MONTH 8
99#define RTC_YEAR 9
100
101#define RTC_REG_A 10
102#define RTC_REG_B 11
103#define RTC_REG_C 12
104#define RTC_REG_D 13
105
106#define REG_A_UIP 0x80
107
108#define REG_B_SET 0x80
109#define REG_B_PIE 0x40
110#define REG_B_AIE 0x20
111#define REG_B_UIE 0x10
112
113
114/** The saved state version. */
115#define RTC_SAVED_STATE_VERSION 4
116/** The saved state version used by VirtualBox pre-3.2.
117 * This does not include the second 128-byte bank. */
118#define RTC_SAVED_STATE_VERSION_VBOX_32PRE 3
119/** The saved state version used by VirtualBox 3.1 and earlier.
120 * This does not include disabled by HPET state. */
121#define RTC_SAVED_STATE_VERSION_VBOX_31 2
122/** The saved state version used by VirtualBox 3.0 and earlier.
123 * This does not include the configuration. */
124#define RTC_SAVED_STATE_VERSION_VBOX_30 1
125
126
127/*******************************************************************************
128* Structures and Typedefs *
129*******************************************************************************/
130/** @todo Replace struct my_tm with RTTIME. */
131struct my_tm
132{
133 int32_t tm_sec;
134 int32_t tm_min;
135 int32_t tm_hour;
136 int32_t tm_mday;
137 int32_t tm_mon;
138 int32_t tm_year;
139 int32_t tm_wday;
140 int32_t tm_yday;
141};
142
143
144struct RTCState {
145 uint8_t cmos_data[256];
146 uint8_t cmos_index[2];
147 uint8_t Alignment0[6];
148 struct my_tm current_tm;
149 /** The configured IRQ. */
150 int32_t irq;
151 /** The configured I/O port base. */
152 RTIOPORT IOPortBase;
153 /** Use UTC or local time initially. */
154 bool fUTC;
155 /** Disabled by HPET legacy mode. */
156 bool fDisabledByHpet;
157 /* periodic timer */
158 int64_t next_periodic_time;
159 /* second update */
160 int64_t next_second_time;
161
162 /** Pointer to the device instance - R3 Ptr. */
163 PPDMDEVINSR3 pDevInsR3;
164 /** The periodic timer (rtcTimerPeriodic) - R3 Ptr. */
165 PTMTIMERR3 pPeriodicTimerR3;
166 /** The second timer (rtcTimerSecond) - R3 Ptr. */
167 PTMTIMERR3 pSecondTimerR3;
168 /** The second second timer (rtcTimerSecond2) - R3 Ptr. */
169 PTMTIMERR3 pSecondTimer2R3;
170
171 /** Pointer to the device instance - R0 Ptr. */
172 PPDMDEVINSR0 pDevInsR0;
173 /** The periodic timer (rtcTimerPeriodic) - R0 Ptr. */
174 PTMTIMERR0 pPeriodicTimerR0;
175 /** The second timer (rtcTimerSecond) - R0 Ptr. */
176 PTMTIMERR0 pSecondTimerR0;
177 /** The second second timer (rtcTimerSecond2) - R0 Ptr. */
178 PTMTIMERR0 pSecondTimer2R0;
179
180 /** Pointer to the device instance - RC Ptr. */
181 PPDMDEVINSRC pDevInsRC;
182 /** The periodic timer (rtcTimerPeriodic) - RC Ptr. */
183 PTMTIMERRC pPeriodicTimerRC;
184 /** The second timer (rtcTimerSecond) - RC Ptr. */
185 PTMTIMERRC pSecondTimerRC;
186 /** The second second timer (rtcTimerSecond2) - RC Ptr. */
187 PTMTIMERRC pSecondTimer2RC;
188
189 /** The RTC registration structure. */
190 PDMRTCREG RtcReg;
191 /** The RTC device helpers. */
192 R3PTRTYPE(PCPDMRTCHLP) pRtcHlpR3;
193 /** Number of release log entries. Used to prevent flooding. */
194 uint32_t cRelLogEntries;
195 /** The current/previous timer period. Used to prevent flooding changes. */
196 int32_t CurPeriod;
197
198 /** HPET legacy mode notification interface. */
199 PDMIHPETLEGACYNOTIFY IHpetLegacyNotify;
200};
201
202#ifndef VBOX_DEVICE_STRUCT_TESTCASE
203static void rtc_set_time(RTCState *s);
204static void rtc_copy_date(RTCState *s);
205
206static void rtc_timer_update(RTCState *s, int64_t current_time)
207{
208 int period_code, period;
209 uint64_t cur_clock, next_irq_clock;
210 uint32_t freq;
211
212 period_code = s->cmos_data[RTC_REG_A] & 0x0f;
213 if (period_code != 0 &&
214 (s->cmos_data[RTC_REG_B] & REG_B_PIE)) {
215 if (period_code <= 2)
216 period_code += 7;
217 /* period in 32 kHz cycles */
218 period = 1 << (period_code - 1);
219 /* compute 32 kHz clock */
220 freq = TMTimerGetFreq(s->CTX_SUFF(pPeriodicTimer));
221
222 cur_clock = ASMMultU64ByU32DivByU32(current_time, 32768, freq);
223 next_irq_clock = (cur_clock & ~(uint64_t)(period - 1)) + period;
224 s->next_periodic_time = ASMMultU64ByU32DivByU32(next_irq_clock, freq, 32768) + 1;
225 TMTimerSet(s->CTX_SUFF(pPeriodicTimer), s->next_periodic_time);
226
227#ifdef IN_RING3
228 if (period != s->CurPeriod)
229 {
230 if (s->cRelLogEntries++ < 64)
231 LogRel(("RTC: period=%#x (%d) %u Hz\n", period, period, _32K / period));
232 s->CurPeriod = period;
233 }
234#endif
235 } else {
236 if (TMTimerIsActive(s->CTX_SUFF(pPeriodicTimer)) && s->cRelLogEntries++ < 64)
237 LogRel(("RTC: stopped the periodic timer\n"));
238 TMTimerStop(s->CTX_SUFF(pPeriodicTimer));
239 }
240}
241
242static void rtc_raise_irq(RTCState* pThis, uint32_t iLevel)
243{
244 if (!pThis->fDisabledByHpet)
245 PDMDevHlpISASetIrq(pThis->CTX_SUFF(pDevIns), pThis->irq, iLevel);
246}
247
248static void rtc_periodic_timer(void *opaque)
249{
250 RTCState *s = (RTCState*)opaque;
251
252 rtc_timer_update(s, s->next_periodic_time);
253 s->cmos_data[RTC_REG_C] |= 0xc0;
254
255 rtc_raise_irq(s, 1);
256}
257
258static void cmos_ioport_write(void *opaque, uint32_t addr, uint32_t data)
259{
260 RTCState *s = (RTCState*)opaque;
261 uint32_t bank;
262
263 bank = (addr >> 1) & 1;
264 if ((addr & 1) == 0) {
265 s->cmos_index[bank] = (data & 0x7f) + (bank * 128);
266 } else {
267 Log(("CMOS: Write bank %d idx %#04x: %#04x (old %#04x)\n", bank,
268 s->cmos_index[bank], data, s->cmos_data[s->cmos_index[bank]]));
269 switch(s->cmos_index[bank]) {
270 case RTC_SECONDS_ALARM:
271 case RTC_MINUTES_ALARM:
272 case RTC_HOURS_ALARM:
273 s->cmos_data[s->cmos_index[0]] = data;
274 break;
275 case RTC_SECONDS:
276 case RTC_MINUTES:
277 case RTC_HOURS:
278 case RTC_DAY_OF_WEEK:
279 case RTC_DAY_OF_MONTH:
280 case RTC_MONTH:
281 case RTC_YEAR:
282 s->cmos_data[s->cmos_index[0]] = data;
283 /* if in set mode, do not update the time */
284 if (!(s->cmos_data[RTC_REG_B] & REG_B_SET)) {
285 rtc_set_time(s);
286 }
287 break;
288 case RTC_REG_A:
289 /* UIP bit is read only */
290 s->cmos_data[RTC_REG_A] = (data & ~REG_A_UIP) |
291 (s->cmos_data[RTC_REG_A] & REG_A_UIP);
292 rtc_timer_update(s, TMTimerGet(s->CTX_SUFF(pPeriodicTimer)));
293 break;
294 case RTC_REG_B:
295 if (data & REG_B_SET) {
296 /* set mode: reset UIP mode */
297 s->cmos_data[RTC_REG_A] &= ~REG_A_UIP;
298#if 0 /* This is probably wrong as it breaks changing the time/date in OS/2. */
299 data &= ~REG_B_UIE;
300#endif
301 } else {
302 /* if disabling set mode, update the time */
303 if (s->cmos_data[RTC_REG_B] & REG_B_SET) {
304 rtc_set_time(s);
305 }
306 }
307 s->cmos_data[RTC_REG_B] = data;
308 rtc_timer_update(s, TMTimerGet(s->CTX_SUFF(pPeriodicTimer)));
309 break;
310 case RTC_REG_C:
311 case RTC_REG_D:
312 /* cannot write to them */
313 break;
314 default:
315 s->cmos_data[s->cmos_index[bank]] = data;
316 break;
317 }
318 }
319}
320
321static inline int to_bcd(RTCState *s, int a)
322{
323 if (s->cmos_data[RTC_REG_B] & 0x04) {
324 return a;
325 } else {
326 return ((a / 10) << 4) | (a % 10);
327 }
328}
329
330static inline int from_bcd(RTCState *s, int a)
331{
332 if (s->cmos_data[RTC_REG_B] & 0x04) {
333 return a;
334 } else {
335 return ((a >> 4) * 10) + (a & 0x0f);
336 }
337}
338
339static void rtc_set_time(RTCState *s)
340{
341 struct my_tm *tm = &s->current_tm;
342
343 tm->tm_sec = from_bcd(s, s->cmos_data[RTC_SECONDS]);
344 tm->tm_min = from_bcd(s, s->cmos_data[RTC_MINUTES]);
345 tm->tm_hour = from_bcd(s, s->cmos_data[RTC_HOURS] & 0x7f);
346 if (!(s->cmos_data[RTC_REG_B] & 0x02) &&
347 (s->cmos_data[RTC_HOURS] & 0x80)) {
348 tm->tm_hour += 12;
349 }
350 tm->tm_wday = from_bcd(s, s->cmos_data[RTC_DAY_OF_WEEK]);
351 tm->tm_mday = from_bcd(s, s->cmos_data[RTC_DAY_OF_MONTH]);
352 tm->tm_mon = from_bcd(s, s->cmos_data[RTC_MONTH]) - 1;
353 tm->tm_year = from_bcd(s, s->cmos_data[RTC_YEAR]) + 100;
354}
355
356static void rtc_copy_date(RTCState *s)
357{
358 const struct my_tm *tm = &s->current_tm;
359
360 s->cmos_data[RTC_SECONDS] = to_bcd(s, tm->tm_sec);
361 s->cmos_data[RTC_MINUTES] = to_bcd(s, tm->tm_min);
362 if (s->cmos_data[RTC_REG_B] & 0x02) {
363 /* 24 hour format */
364 s->cmos_data[RTC_HOURS] = to_bcd(s, tm->tm_hour);
365 } else {
366 /* 12 hour format */
367 s->cmos_data[RTC_HOURS] = to_bcd(s, tm->tm_hour % 12);
368 if (tm->tm_hour >= 12)
369 s->cmos_data[RTC_HOURS] |= 0x80;
370 }
371 s->cmos_data[RTC_DAY_OF_WEEK] = to_bcd(s, tm->tm_wday);
372 s->cmos_data[RTC_DAY_OF_MONTH] = to_bcd(s, tm->tm_mday);
373 s->cmos_data[RTC_MONTH] = to_bcd(s, tm->tm_mon + 1);
374 s->cmos_data[RTC_YEAR] = to_bcd(s, tm->tm_year % 100);
375}
376
377/* month is between 0 and 11. */
378static int get_days_in_month(int month, int year)
379{
380 static const int days_tab[12] = {
381 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
382 };
383 int d;
384 if ((unsigned )month >= 12)
385 return 31;
386 d = days_tab[month];
387 if (month == 1) {
388 if ((year % 4) == 0 && ((year % 100) != 0 || (year % 400) == 0))
389 d++;
390 }
391 return d;
392}
393
394/* update 'tm' to the next second */
395static void rtc_next_second(struct my_tm *tm)
396{
397 int days_in_month;
398
399 tm->tm_sec++;
400 if ((unsigned)tm->tm_sec >= 60) {
401 tm->tm_sec = 0;
402 tm->tm_min++;
403 if ((unsigned)tm->tm_min >= 60) {
404 tm->tm_min = 0;
405 tm->tm_hour++;
406 if ((unsigned)tm->tm_hour >= 24) {
407 tm->tm_hour = 0;
408 /* next day */
409 tm->tm_wday++;
410 if ((unsigned)tm->tm_wday >= 7)
411 tm->tm_wday = 0;
412 days_in_month = get_days_in_month(tm->tm_mon,
413 tm->tm_year + 1900);
414 tm->tm_mday++;
415 if (tm->tm_mday < 1) {
416 tm->tm_mday = 1;
417 } else if (tm->tm_mday > days_in_month) {
418 tm->tm_mday = 1;
419 tm->tm_mon++;
420 if (tm->tm_mon >= 12) {
421 tm->tm_mon = 0;
422 tm->tm_year++;
423 }
424 }
425 }
426 }
427 }
428}
429
430
431static void rtc_update_second(void *opaque)
432{
433 RTCState *s = (RTCState*)opaque;
434
435 /* if the oscillator is not in normal operation, we do not update */
436 if ((s->cmos_data[RTC_REG_A] & 0x70) != 0x20) {
437 s->next_second_time += TMTimerGetFreq(s->CTX_SUFF(pSecondTimer));
438 TMTimerSet(s->CTX_SUFF(pSecondTimer), s->next_second_time);
439 } else {
440 rtc_next_second(&s->current_tm);
441
442 if (!(s->cmos_data[RTC_REG_B] & REG_B_SET)) {
443 /* update in progress bit */
444 Log2(("RTC: UIP %x -> 1\n", !!(s->cmos_data[RTC_REG_A] & REG_A_UIP)));
445 s->cmos_data[RTC_REG_A] |= REG_A_UIP;
446 }
447
448 /* 244140 ns = 8 / 32768 seconds */
449 uint64_t delay = TMTimerFromNano(s->CTX_SUFF(pSecondTimer2), 244140);
450 TMTimerSet(s->CTX_SUFF(pSecondTimer2), s->next_second_time + delay);
451 }
452}
453
454static void rtc_update_second2(void *opaque)
455{
456 RTCState *s = (RTCState*)opaque;
457
458 if (!(s->cmos_data[RTC_REG_B] & REG_B_SET)) {
459 rtc_copy_date(s);
460 }
461
462 /* check alarm */
463 if (s->cmos_data[RTC_REG_B] & REG_B_AIE) {
464 if (((s->cmos_data[RTC_SECONDS_ALARM] & 0xc0) == 0xc0 ||
465 from_bcd(s, s->cmos_data[RTC_SECONDS_ALARM]) == s->current_tm.tm_sec) &&
466 ((s->cmos_data[RTC_MINUTES_ALARM] & 0xc0) == 0xc0 ||
467 from_bcd(s, s->cmos_data[RTC_MINUTES_ALARM]) == s->current_tm.tm_min) &&
468 ((s->cmos_data[RTC_HOURS_ALARM] & 0xc0) == 0xc0 ||
469 from_bcd(s, s->cmos_data[RTC_HOURS_ALARM]) == s->current_tm.tm_hour)) {
470
471 s->cmos_data[RTC_REG_C] |= 0xa0;
472 rtc_raise_irq(s, 1);
473 }
474 }
475
476 /* update ended interrupt */
477 if (s->cmos_data[RTC_REG_B] & REG_B_UIE) {
478 s->cmos_data[RTC_REG_C] |= 0x90;
479 rtc_raise_irq(s, 1);
480 }
481
482 /* clear update in progress bit */
483 Log2(("RTC: UIP %x -> 0\n", !!(s->cmos_data[RTC_REG_A] & REG_A_UIP)));
484 s->cmos_data[RTC_REG_A] &= ~REG_A_UIP;
485
486 s->next_second_time += TMTimerGetFreq(s->CTX_SUFF(pSecondTimer));
487 TMTimerSet(s->CTX_SUFF(pSecondTimer), s->next_second_time);
488}
489
490static uint32_t cmos_ioport_read(void *opaque, uint32_t addr)
491{
492 RTCState *s = (RTCState*)opaque;
493 int ret;
494 unsigned bank;
495
496 bank = (addr >> 1) & 1;
497 if ((addr & 1) == 0) {
498 return 0xff;
499 } else {
500 switch(s->cmos_index[bank]) {
501 case RTC_SECONDS:
502 case RTC_MINUTES:
503 case RTC_HOURS:
504 case RTC_DAY_OF_WEEK:
505 case RTC_DAY_OF_MONTH:
506 case RTC_MONTH:
507 case RTC_YEAR:
508 ret = s->cmos_data[s->cmos_index[0]];
509 break;
510 case RTC_REG_A:
511 ret = s->cmos_data[s->cmos_index[0]];
512 break;
513 case RTC_REG_C:
514 ret = s->cmos_data[s->cmos_index[0]];
515 rtc_raise_irq(s, 0);
516 s->cmos_data[RTC_REG_C] = 0x00;
517 break;
518 default:
519 ret = s->cmos_data[s->cmos_index[bank]];
520 break;
521 }
522 Log(("CMOS: Read bank %d idx %#04x: %#04x\n", bank, s->cmos_index[bank], ret));
523 return ret;
524 }
525}
526
527#ifdef IN_RING3
528static void rtc_set_memory(RTCState *s, int addr, int val)
529{
530 if (addr >= 0 && addr <= 127)
531 s->cmos_data[addr] = val;
532}
533
534static void rtc_set_date(RTCState *s, const struct my_tm *tm)
535{
536 s->current_tm = *tm;
537 rtc_copy_date(s);
538}
539
540#endif /* IN_RING3 */
541
542/* -=-=-=-=-=- wrappers / stuff -=-=-=-=-=- */
543
544/**
545 * Port I/O Handler for IN operations.
546 *
547 * @returns VBox status code.
548 *
549 * @param pDevIns The device instance.
550 * @param pvUser User argument - ignored.
551 * @param uPort Port number used for the IN operation.
552 * @param pu32 Where to store the result.
553 * @param cb Number of bytes read.
554 */
555PDMBOTHCBDECL(int) rtcIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
556{
557 NOREF(pvUser);
558 if (cb == 1)
559 {
560 *pu32 = cmos_ioport_read(PDMINS_2_DATA(pDevIns, RTCState *), Port);
561 return VINF_SUCCESS;
562 }
563 return VERR_IOM_IOPORT_UNUSED;
564}
565
566
567/**
568 * Port I/O Handler for OUT operations.
569 *
570 * @returns VBox status code.
571 *
572 * @param pDevIns The device instance.
573 * @param pvUser User argument - ignored.
574 * @param uPort Port number used for the IN operation.
575 * @param u32 The value to output.
576 * @param cb The value size in bytes.
577 */
578PDMBOTHCBDECL(int) rtcIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
579{
580 NOREF(pvUser);
581 if (cb == 1)
582 cmos_ioport_write(PDMINS_2_DATA(pDevIns, RTCState *), Port, u32);
583 return VINF_SUCCESS;
584}
585
586
587/**
588 * Device timer callback function, periodic.
589 *
590 * @param pDevIns Device instance of the device which registered the timer.
591 * @param pTimer The timer handle.
592 * @param pvUser Pointer to the RTC state.
593 */
594PDMBOTHCBDECL(void) rtcTimerPeriodic(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
595{
596 rtc_periodic_timer((RTCState *)pvUser);
597}
598
599
600/**
601 * Device timer callback function, second.
602 *
603 * @param pDevIns Device instance of the device which registered the timer.
604 * @param pTimer The timer handle.
605 * @param pvUser Pointer to the RTC state.
606 */
607PDMBOTHCBDECL(void) rtcTimerSecond(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
608{
609 rtc_update_second((RTCState *)pvUser);
610}
611
612
613/**
614 * Device timer callback function, second2.
615 *
616 * @param pDevIns Device instance of the device which registered the timer.
617 * @param pTimer The timer handle.
618 * @param pvUser Pointer to the RTC state.
619 */
620PDMBOTHCBDECL(void) rtcTimerSecond2(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
621{
622 rtc_update_second2((RTCState *)pvUser);
623}
624
625#ifdef IN_RING3
626
627/**
628 * @copydoc FNSSMDEVLIVEEXEC
629 */
630static DECLCALLBACK(int) rtcLiveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uPass)
631{
632 RTCState *pThis = PDMINS_2_DATA(pDevIns, RTCState *);
633
634 SSMR3PutU8( pSSM, pThis->irq);
635 SSMR3PutIOPort(pSSM, pThis->IOPortBase);
636 SSMR3PutBool( pSSM, pThis->fUTC);
637
638 return VINF_SSM_DONT_CALL_AGAIN;
639}
640
641
642/**
643 * @copydoc FNSSMDEVSAVEEXEC
644 */
645static DECLCALLBACK(int) rtcSaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
646{
647 RTCState *pThis = PDMINS_2_DATA(pDevIns, RTCState *);
648
649 /* The config. */
650 rtcLiveExec(pDevIns, pSSM, SSM_PASS_FINAL);
651
652 /* The state. */
653 SSMR3PutMem(pSSM, pThis->cmos_data, 128);
654 SSMR3PutU8(pSSM, pThis->cmos_index[0]);
655
656 SSMR3PutS32(pSSM, pThis->current_tm.tm_sec);
657 SSMR3PutS32(pSSM, pThis->current_tm.tm_min);
658 SSMR3PutS32(pSSM, pThis->current_tm.tm_hour);
659 SSMR3PutS32(pSSM, pThis->current_tm.tm_wday);
660 SSMR3PutS32(pSSM, pThis->current_tm.tm_mday);
661 SSMR3PutS32(pSSM, pThis->current_tm.tm_mon);
662 SSMR3PutS32(pSSM, pThis->current_tm.tm_year);
663
664 TMR3TimerSave(pThis->CTX_SUFF(pPeriodicTimer), pSSM);
665
666 SSMR3PutS64(pSSM, pThis->next_periodic_time);
667
668 SSMR3PutS64(pSSM, pThis->next_second_time);
669 TMR3TimerSave(pThis->CTX_SUFF(pSecondTimer), pSSM);
670 TMR3TimerSave(pThis->CTX_SUFF(pSecondTimer2), pSSM);
671
672 SSMR3PutBool(pSSM, pThis->fDisabledByHpet);
673
674 SSMR3PutMem(pSSM, &pThis->cmos_data[128], 128);
675 return SSMR3PutU8(pSSM, pThis->cmos_index[1]);
676}
677
678
679/**
680 * @copydoc FNSSMDEVLOADEXEC
681 */
682static DECLCALLBACK(int) rtcLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
683{
684 RTCState *pThis = PDMINS_2_DATA(pDevIns, RTCState *);
685 int rc;
686
687 if ( uVersion != RTC_SAVED_STATE_VERSION
688 && uVersion != RTC_SAVED_STATE_VERSION_VBOX_32PRE
689 && uVersion != RTC_SAVED_STATE_VERSION_VBOX_31
690 && uVersion != RTC_SAVED_STATE_VERSION_VBOX_30)
691 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
692
693 /* The config. */
694 if (uVersion > RTC_SAVED_STATE_VERSION_VBOX_30)
695 {
696 uint8_t u8Irq;
697 rc = SSMR3GetU8(pSSM, &u8Irq); AssertRCReturn(rc, rc);
698 if (u8Irq != pThis->irq)
699 return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Config mismatch - u8Irq: saved=%#x config=%#x"), u8Irq, pThis->irq);
700
701 RTIOPORT IOPortBase;
702 rc = SSMR3GetIOPort(pSSM, &IOPortBase); AssertRCReturn(rc, rc);
703 if (IOPortBase != pThis->IOPortBase)
704 return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Config mismatch - IOPortBase: saved=%RTiop config=%RTiop"), IOPortBase, pThis->IOPortBase);
705
706 bool fUTC;
707 rc = SSMR3GetBool(pSSM, &fUTC); AssertRCReturn(rc, rc);
708 if (fUTC != pThis->fUTC)
709 LogRel(("RTC: Config mismatch - fUTC: saved=%RTbool config=%RTbool\n", fUTC, pThis->fUTC));
710 }
711
712 if (uPass != SSM_PASS_FINAL)
713 return VINF_SUCCESS;
714
715 /* The state. */
716 SSMR3GetMem(pSSM, pThis->cmos_data, 128);
717 SSMR3GetU8(pSSM, &pThis->cmos_index[0]);
718
719 SSMR3GetS32(pSSM, &pThis->current_tm.tm_sec);
720 SSMR3GetS32(pSSM, &pThis->current_tm.tm_min);
721 SSMR3GetS32(pSSM, &pThis->current_tm.tm_hour);
722 SSMR3GetS32(pSSM, &pThis->current_tm.tm_wday);
723 SSMR3GetS32(pSSM, &pThis->current_tm.tm_mday);
724 SSMR3GetS32(pSSM, &pThis->current_tm.tm_mon);
725 SSMR3GetS32(pSSM, &pThis->current_tm.tm_year);
726
727 TMR3TimerLoad(pThis->CTX_SUFF(pPeriodicTimer), pSSM);
728
729 SSMR3GetS64(pSSM, &pThis->next_periodic_time);
730
731 SSMR3GetS64(pSSM, &pThis->next_second_time);
732 TMR3TimerLoad(pThis->CTX_SUFF(pSecondTimer), pSSM);
733 TMR3TimerLoad(pThis->CTX_SUFF(pSecondTimer2), pSSM);
734
735 if (uVersion > RTC_SAVED_STATE_VERSION_VBOX_31)
736 SSMR3GetBool(pSSM, &pThis->fDisabledByHpet);
737
738 if (uVersion > RTC_SAVED_STATE_VERSION_VBOX_32PRE)
739 {
740 /* Second CMOS bank. */
741 SSMR3GetMem(pSSM, &pThis->cmos_data[128], 128);
742 SSMR3GetU8(pSSM, &pThis->cmos_index[1]);
743 }
744
745 int period_code = pThis->cmos_data[RTC_REG_A] & 0x0f;
746 if ( period_code != 0
747 && (pThis->cmos_data[RTC_REG_B] & REG_B_PIE)) {
748 if (period_code <= 2)
749 period_code += 7;
750 int period = 1 << (period_code - 1);
751 LogRel(("RTC: period=%#x (%d) %u Hz (restore)\n", period, period, _32K / period));
752 pThis->CurPeriod = period;
753 } else {
754 LogRel(("RTC: stopped the periodic timer (restore)\n"));
755 pThis->CurPeriod = 0;
756 }
757 pThis->cRelLogEntries = 0;
758
759 return VINF_SUCCESS;
760}
761
762
763/* -=-=-=-=-=- PDM Interface provided by the RTC device -=-=-=-=-=- */
764
765/**
766 * Calculate and update the standard CMOS checksum.
767 *
768 * @param pThis Pointer to the RTC state data.
769 */
770static void rtcCalcCRC(RTCState *pThis)
771{
772 uint16_t u16;
773 unsigned i;
774
775 for (i = RTC_CRC_START, u16 = 0; i <= RTC_CRC_LAST; i++)
776 u16 += pThis->cmos_data[i];
777 pThis->cmos_data[RTC_CRC_LOW] = u16 & 0xff;
778 pThis->cmos_data[RTC_CRC_HIGH] = (u16 >> 8) & 0xff;
779}
780
781
782/**
783 * Write to a CMOS register and update the checksum if necessary.
784 *
785 * @returns VBox status code.
786 * @param pDevIns Device instance of the RTC.
787 * @param iReg The CMOS register index; bit 8 determines bank.
788 * @param u8Value The CMOS register value.
789 */
790static DECLCALLBACK(int) rtcCMOSWrite(PPDMDEVINS pDevIns, unsigned iReg, uint8_t u8Value)
791{
792 RTCState *pThis = PDMINS_2_DATA(pDevIns, RTCState *);
793 if (iReg < RT_ELEMENTS(pThis->cmos_data))
794 {
795 pThis->cmos_data[iReg] = u8Value;
796
797 /* does it require checksum update? */
798 if ( iReg >= RTC_CRC_START
799 && iReg <= RTC_CRC_LAST)
800 rtcCalcCRC(pThis);
801
802 return VINF_SUCCESS;
803 }
804 AssertMsgFailed(("iReg=%d\n", iReg));
805 return VERR_INVALID_PARAMETER;
806}
807
808
809/**
810 * Read a CMOS register.
811 *
812 * @returns VBox status code.
813 * @param pDevIns Device instance of the RTC.
814 * @param iReg The CMOS register index; bit 8 determines bank.
815 * @param pu8Value Where to store the CMOS register value.
816 */
817static DECLCALLBACK(int) rtcCMOSRead(PPDMDEVINS pDevIns, unsigned iReg, uint8_t *pu8Value)
818{
819 RTCState *pThis = PDMINS_2_DATA(pDevIns, RTCState *);
820 if (iReg < RT_ELEMENTS(pThis->cmos_data))
821 {
822 *pu8Value = pThis->cmos_data[iReg];
823 return VINF_SUCCESS;
824 }
825 AssertMsgFailed(("iReg=%d\n", iReg));
826 return VERR_INVALID_PARAMETER;
827}
828
829
830/* -=-=-=-=-=- based on bits from pc.c -=-=-=-=-=- */
831
832/** @copydoc FNPDMDEVINITCOMPLETE */
833static DECLCALLBACK(int) rtcInitComplete(PPDMDEVINS pDevIns)
834{
835 /** @todo this should be (re)done at power on if we didn't load a state... */
836 RTCState *pThis = PDMINS_2_DATA(pDevIns, RTCState *);
837
838 /*
839 * Set the CMOS date/time.
840 */
841 RTTIMESPEC Now;
842 PDMDevHlpTMUtcNow(pDevIns, &Now);
843 RTTIME Time;
844 if (pThis->fUTC)
845 RTTimeExplode(&Time, &Now);
846 else
847 RTTimeLocalExplode(&Time, &Now);
848
849 struct my_tm Tm;
850 memset(&Tm, 0, sizeof(Tm));
851 Tm.tm_year = Time.i32Year - 1900;
852 Tm.tm_mon = Time.u8Month - 1;
853 Tm.tm_mday = Time.u8MonthDay;
854 Tm.tm_wday = (Time.u8WeekDay + 1 + 7) % 7; /* 0 = monday -> sunday */
855 Tm.tm_yday = Time.u16YearDay - 1;
856 Tm.tm_hour = Time.u8Hour;
857 Tm.tm_min = Time.u8Minute;
858 Tm.tm_sec = Time.u8Second;
859
860 rtc_set_date(pThis, &Tm);
861
862 int iYear = to_bcd(pThis, (Tm.tm_year / 100) + 19); /* tm_year is 1900 based */
863 rtc_set_memory(pThis, 0x32, iYear); /* 32h - Century Byte (BCD value for the century */
864 rtc_set_memory(pThis, 0x37, iYear); /* 37h - (IBM PS/2) Date Century Byte */
865
866 /*
867 * Recalculate the checksum just in case.
868 */
869 rtcCalcCRC(pThis);
870
871 Log(("CMOS bank 0: \n%16.128Rhxd\n", &pThis->cmos_data[0]));
872 Log(("CMOS bank 1: \n%16.128Rhxd\n", &pThis->cmos_data[128]));
873 return VINF_SUCCESS;
874}
875
876
877/* -=-=-=-=-=- real code -=-=-=-=-=- */
878
879/**
880 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
881 */
882static DECLCALLBACK(void *) rtcQueryInterface(PPDMIBASE pInterface, const char *pszIID)
883{
884 PPDMDEVINS pDevIns = RT_FROM_MEMBER(pInterface, PDMDEVINS, IBase);
885 RTCState *pThis = PDMINS_2_DATA(pDevIns, RTCState *);
886 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDevIns->IBase);
887 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHPETLEGACYNOTIFY, &pThis->IHpetLegacyNotify);
888 return NULL;
889}
890
891
892/**
893 * @interface_method_impl{PDMIHPETLEGACYNOTIFY,pfnModeChanged}
894 */
895static DECLCALLBACK(void) rtcHpetLegacyNotify_ModeChanged(PPDMIHPETLEGACYNOTIFY pInterface, bool fActivated)
896{
897 RTCState *pThis = RT_FROM_MEMBER(pInterface, RTCState, IHpetLegacyNotify);
898 pThis->fDisabledByHpet = fActivated;
899}
900
901
902/**
903 * @copydoc
904 */
905static DECLCALLBACK(void) rtcRelocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
906{
907 RTCState *pThis = PDMINS_2_DATA(pDevIns, RTCState *);
908
909 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
910 pThis->pPeriodicTimerRC = TMTimerRCPtr(pThis->pPeriodicTimerR3);
911 pThis->pSecondTimerRC = TMTimerRCPtr(pThis->pSecondTimerR3);
912 pThis->pSecondTimer2RC = TMTimerRCPtr(pThis->pSecondTimer2R3);
913}
914
915
916/**
917 * @interface_method_impl{PDMDEVREG,pfnConstruct}
918 */
919static DECLCALLBACK(int) rtcConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
920{
921 RTCState *pThis = PDMINS_2_DATA(pDevIns, RTCState *);
922 int rc;
923 Assert(iInstance == 0);
924
925 /*
926 * Validate configuration.
927 */
928 if (!CFGMR3AreValuesValid(pCfg,
929 "Irq\0"
930 "Base\0"
931 "UseUTC\0"
932 "GCEnabled\0"
933 "R0Enabled\0"))
934 return VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES;
935
936 /*
937 * Init the data.
938 */
939 uint8_t u8Irq;
940 rc = CFGMR3QueryU8Def(pCfg, "Irq", &u8Irq, 8);
941 if (RT_FAILURE(rc))
942 return PDMDEV_SET_ERROR(pDevIns, rc,
943 N_("Configuration error: Querying \"Irq\" as a uint8_t failed"));
944 pThis->irq = u8Irq;
945
946 rc = CFGMR3QueryPortDef(pCfg, "Base", &pThis->IOPortBase, 0x70);
947 if (RT_FAILURE(rc))
948 return PDMDEV_SET_ERROR(pDevIns, rc,
949 N_("Configuration error: Querying \"Base\" as a RTIOPORT failed"));
950
951 rc = CFGMR3QueryBoolDef(pCfg, "UseUTC", &pThis->fUTC, false);
952 if (RT_FAILURE(rc))
953 return PDMDEV_SET_ERROR(pDevIns, rc,
954 N_("Configuration error: Querying \"UseUTC\" as a bool failed"));
955
956 bool fGCEnabled;
957 rc = CFGMR3QueryBoolDef(pCfg, "GCEnabled", &fGCEnabled, true);
958 if (RT_FAILURE(rc))
959 return PDMDEV_SET_ERROR(pDevIns, rc,
960 N_("Configuration error: failed to read GCEnabled as boolean"));
961
962 bool fR0Enabled;
963 rc = CFGMR3QueryBoolDef(pCfg, "R0Enabled", &fR0Enabled, true);
964 if (RT_FAILURE(rc))
965 return PDMDEV_SET_ERROR(pDevIns, rc,
966 N_("Configuration error: failed to read R0Enabled as boolean"));
967
968 Log(("RTC: Irq=%#x Base=%#x fGCEnabled=%RTbool fR0Enabled=%RTbool\n",
969 u8Irq, pThis->IOPortBase, fGCEnabled, fR0Enabled));
970
971
972 pThis->pDevInsR3 = pDevIns;
973 pThis->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
974 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
975 pThis->cmos_data[RTC_REG_A] = 0x26;
976 pThis->cmos_data[RTC_REG_B] = 0x02;
977 pThis->cmos_data[RTC_REG_C] = 0x00;
978 pThis->cmos_data[RTC_REG_D] = 0x80;
979 pThis->RtcReg.u32Version = PDM_RTCREG_VERSION;
980 pThis->RtcReg.pfnRead = rtcCMOSRead;
981 pThis->RtcReg.pfnWrite = rtcCMOSWrite;
982 pThis->fDisabledByHpet = false;
983
984 /* IBase */
985 pDevIns->IBase.pfnQueryInterface = rtcQueryInterface;
986 /* IHpetLegacyNotify */
987 pThis->IHpetLegacyNotify.pfnModeChanged = rtcHpetLegacyNotify_ModeChanged;
988
989 /*
990 * Create timers, arm them, register I/O Ports and save state.
991 */
992 rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL_SYNC, rtcTimerPeriodic, pThis,
993 TMTIMER_FLAGS_DEFAULT_CRIT_SECT, "MC146818 RTC/CMOS - Periodic",
994 &pThis->pPeriodicTimerR3);
995 if (RT_FAILURE(rc))
996 return rc;
997 pThis->pPeriodicTimerR0 = TMTimerR0Ptr(pThis->pPeriodicTimerR3);
998 pThis->pPeriodicTimerRC = TMTimerRCPtr(pThis->pPeriodicTimerR3);
999
1000 rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL_SYNC, rtcTimerSecond, pThis,
1001 TMTIMER_FLAGS_DEFAULT_CRIT_SECT, "MC146818 RTC/CMOS - Second",
1002 &pThis->pSecondTimerR3);
1003 if (RT_FAILURE(rc))
1004 return rc;
1005 pThis->pSecondTimerR0 = TMTimerR0Ptr(pThis->pSecondTimerR3);
1006 pThis->pSecondTimerRC = TMTimerRCPtr(pThis->pSecondTimerR3);
1007
1008 rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL_SYNC, rtcTimerSecond2, pThis,
1009 TMTIMER_FLAGS_DEFAULT_CRIT_SECT, "MC146818 RTC/CMOS - Second2",
1010 &pThis->pSecondTimer2R3);
1011 if (RT_FAILURE(rc))
1012 return rc;
1013 pThis->pSecondTimer2R0 = TMTimerR0Ptr(pThis->pSecondTimer2R3);
1014 pThis->pSecondTimer2RC = TMTimerRCPtr(pThis->pSecondTimer2R3);
1015 pThis->next_second_time = TMTimerGet(pThis->CTX_SUFF(pSecondTimer2))
1016 + (TMTimerGetFreq(pThis->CTX_SUFF(pSecondTimer2)) * 99) / 100;
1017 rc = TMTimerSet(pThis->CTX_SUFF(pSecondTimer2), pThis->next_second_time);
1018 if (RT_FAILURE(rc))
1019 return rc;
1020
1021 rc = PDMDevHlpIOPortRegister(pDevIns, pThis->IOPortBase, 4, NULL,
1022 rtcIOPortWrite, rtcIOPortRead, NULL, NULL, "MC146818 RTC/CMOS");
1023 if (RT_FAILURE(rc))
1024 return rc;
1025 if (fGCEnabled)
1026 {
1027 rc = PDMDevHlpIOPortRegisterRC(pDevIns, pThis->IOPortBase, 4, 0,
1028 "rtcIOPortWrite", "rtcIOPortRead", NULL, NULL, "MC146818 RTC/CMOS");
1029 if (RT_FAILURE(rc))
1030 return rc;
1031 }
1032 if (fR0Enabled)
1033 {
1034 rc = PDMDevHlpIOPortRegisterR0(pDevIns, pThis->IOPortBase, 4, 0,
1035 "rtcIOPortWrite", "rtcIOPortRead", NULL, NULL, "MC146818 RTC/CMOS");
1036 if (RT_FAILURE(rc))
1037 return rc;
1038 }
1039
1040 rc = PDMDevHlpSSMRegister3(pDevIns, RTC_SAVED_STATE_VERSION, sizeof(*pThis), rtcLiveExec, rtcSaveExec, rtcLoadExec);
1041 if (RT_FAILURE(rc))
1042 return rc;
1043
1044 /*
1045 * Register ourselves as the RTC/CMOS with PDM.
1046 */
1047 rc = PDMDevHlpRTCRegister(pDevIns, &pThis->RtcReg, &pThis->pRtcHlpR3);
1048 if (RT_FAILURE(rc))
1049 return rc;
1050
1051 return VINF_SUCCESS;
1052}
1053
1054
1055/**
1056 * The device registration structure.
1057 */
1058const PDMDEVREG g_DeviceMC146818 =
1059{
1060 /* u32Version */
1061 PDM_DEVREG_VERSION,
1062 /* szName */
1063 "mc146818",
1064 /* szRCMod */
1065 "VBoxDDGC.gc",
1066 /* szR0Mod */
1067 "VBoxDDR0.r0",
1068 /* pszDescription */
1069 "Motorola MC146818 RTC/CMOS Device.",
1070 /* fFlags */
1071 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,
1072 /* fClass */
1073 PDM_DEVREG_CLASS_RTC,
1074 /* cMaxInstances */
1075 1,
1076 /* cbInstance */
1077 sizeof(RTCState),
1078 /* pfnConstruct */
1079 rtcConstruct,
1080 /* pfnDestruct */
1081 NULL,
1082 /* pfnRelocate */
1083 rtcRelocate,
1084 /* pfnIOCtl */
1085 NULL,
1086 /* pfnPowerOn */
1087 NULL,
1088 /* pfnReset */
1089 NULL,
1090 /* pfnSuspend */
1091 NULL,
1092 /* pfnResume */
1093 NULL,
1094 /* pfnAttach */
1095 NULL,
1096 /* pfnDetach */
1097 NULL,
1098 /* pfnQueryInterface */
1099 NULL,
1100 /* pfnInitComplete */
1101 rtcInitComplete,
1102 /* pfnPowerOff */
1103 NULL,
1104 /* pfnSoftReset */
1105 NULL,
1106 /* u32VersionEnd */
1107 PDM_DEVREG_VERSION
1108};
1109
1110#endif /* IN_RING3 */
1111#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
1112
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