VirtualBox

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

Last change on this file since 10719 was 9212, checked in by vboxsync, 17 years ago

Major changes for sizeof(RTGCPTR) == uint64_t.
Introduced RCPTRTYPE for pointers valid in raw mode only (RTGCPTR32).

Disabled by default. Enable by adding VBOX_WITH_64_BITS_GUESTS to your LocalConfig.kmk.

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