VirtualBox

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

Last change on this file since 25776 was 25127, checked in by vboxsync, 15 years ago

DevRTC: Fixed incorrect weekday calculation (two days off).

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