VirtualBox

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

Last change on this file since 21205 was 20374, checked in by vboxsync, 15 years ago

*: s/RT_\(BEGIN|END\)_DECLS/RT_C_DECLS_\1/g

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

© 2023 Oracle
ContactPrivacy policyTerms of Use