VirtualBox

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

Last change on this file since 9113 was 9088, checked in by vboxsync, 17 years ago

Corrected the delay for the 2nd timer that clears UIP (among others thing) from 10ms to 244140ns. Hopefully this will help with the #2890 issue.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 30.3 KB
Line 
1/* $Id: DevRTC.cpp 9088 2008-05-23 14:13:13Z 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 GCPTRTYPE(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 GCPTRTYPE(PTMTIMER) pSecondTimerGC;
134 GCPTRTYPE(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 int64_t delay;
373
374 /* if the oscillator is not in normal operation, we do not update */
375 if ((s->cmos_data[RTC_REG_A] & 0x70) != 0x20) {
376 s->next_second_time += TMTimerGetFreq(s->CTXSUFF(pSecondTimer));
377 TMTimerSet(s->CTXSUFF(pSecondTimer), s->next_second_time);
378 } else {
379 rtc_next_second(&s->current_tm);
380
381 if (!(s->cmos_data[RTC_REG_B] & REG_B_SET)) {
382 /* update in progress bit */
383 s->cmos_data[RTC_REG_A] |= REG_A_UIP;
384 }
385#if 0 /* old: winds up waiting for 10ms... */
386 /* should be 244 us = 8 / 32768 seconds, but currently the
387 timers do not have the necessary resolution. */
388 delay = (TMTimerGetFreq(s->CTXSUFF(pSecondTimer2)) * 1) / 100;
389 if (delay < 1)
390 delay = 1;
391#else
392 /* 244140 ns = 8 / 32768 seconds */
393 delay = TMTimerFromNano(s->CTXSUFF(pSecondTimer2), 244140);
394#endif
395 TMTimerSet(s->CTXSUFF(pSecondTimer2), s->next_second_time + delay);
396 }
397}
398
399static void rtc_update_second2(void *opaque)
400{
401 RTCState *s = (RTCState*)opaque;
402
403 if (!(s->cmos_data[RTC_REG_B] & REG_B_SET)) {
404 rtc_copy_date(s);
405 }
406
407 /* check alarm */
408 if (s->cmos_data[RTC_REG_B] & REG_B_AIE) {
409 if (((s->cmos_data[RTC_SECONDS_ALARM] & 0xc0) == 0xc0 ||
410 from_bcd(s, s->cmos_data[RTC_SECONDS_ALARM]) == s->current_tm.tm_sec) &&
411 ((s->cmos_data[RTC_MINUTES_ALARM] & 0xc0) == 0xc0 ||
412 from_bcd(s, s->cmos_data[RTC_MINUTES_ALARM]) == s->current_tm.tm_min) &&
413 ((s->cmos_data[RTC_HOURS_ALARM] & 0xc0) == 0xc0 ||
414 from_bcd(s, s->cmos_data[RTC_HOURS_ALARM]) == s->current_tm.tm_hour)) {
415
416 s->cmos_data[RTC_REG_C] |= 0xa0;
417 PDMDevHlpISASetIrq(s->CTXSUFF(pDevIns), s->irq, 1);
418 }
419 }
420
421 /* update ended interrupt */
422 if (s->cmos_data[RTC_REG_B] & REG_B_UIE) {
423 s->cmos_data[RTC_REG_C] |= 0x90;
424 PDMDevHlpISASetIrq(s->CTXSUFF(pDevIns), s->irq, 1);
425 }
426
427 /* clear update in progress bit */
428 s->cmos_data[RTC_REG_A] &= ~REG_A_UIP;
429
430 s->next_second_time += TMTimerGetFreq(s->CTXSUFF(pSecondTimer));
431 TMTimerSet(s->CTXSUFF(pSecondTimer), s->next_second_time);
432}
433
434static uint32_t cmos_ioport_read(void *opaque, uint32_t addr)
435{
436 RTCState *s = (RTCState*)opaque;
437 int ret;
438 if ((addr & 1) == 0) {
439 return 0xff;
440 } else {
441 switch(s->cmos_index) {
442 case RTC_SECONDS:
443 case RTC_MINUTES:
444 case RTC_HOURS:
445 case RTC_DAY_OF_WEEK:
446 case RTC_DAY_OF_MONTH:
447 case RTC_MONTH:
448 case RTC_YEAR:
449 ret = s->cmos_data[s->cmos_index];
450 break;
451 case RTC_REG_A:
452 ret = s->cmos_data[s->cmos_index];
453 break;
454 case RTC_REG_C:
455 ret = s->cmos_data[s->cmos_index];
456 PDMDevHlpISASetIrq(s->CTXSUFF(pDevIns), s->irq, 0);
457 s->cmos_data[RTC_REG_C] = 0x00;
458 break;
459 default:
460 ret = s->cmos_data[s->cmos_index];
461 break;
462 }
463 Log(("CMOS: Read idx %#04x: %#04x\n", s->cmos_index, ret));
464 return ret;
465 }
466}
467
468#ifdef IN_RING3
469static void rtc_set_memory(RTCState *s, int addr, int val)
470{
471 if (addr >= 0 && addr <= 127)
472 s->cmos_data[addr] = val;
473}
474
475static void rtc_set_date(RTCState *s, const struct my_tm *tm)
476{
477 s->current_tm = *tm;
478 rtc_copy_date(s);
479}
480
481static void rtc_save(QEMUFile *f, void *opaque)
482{
483 RTCState *s = (RTCState*)opaque;
484
485 qemu_put_buffer(f, s->cmos_data, 128);
486 qemu_put_8s(f, &s->cmos_index);
487
488 qemu_put_be32s(f, &s->current_tm.tm_sec);
489 qemu_put_be32s(f, &s->current_tm.tm_min);
490 qemu_put_be32s(f, &s->current_tm.tm_hour);
491 qemu_put_be32s(f, &s->current_tm.tm_wday);
492 qemu_put_be32s(f, &s->current_tm.tm_mday);
493 qemu_put_be32s(f, &s->current_tm.tm_mon);
494 qemu_put_be32s(f, &s->current_tm.tm_year);
495
496 qemu_put_timer(f, s->CTXSUFF(pPeriodicTimer));
497 qemu_put_be64s(f, &s->next_periodic_time);
498
499 qemu_put_be64s(f, &s->next_second_time);
500 qemu_put_timer(f, s->CTXSUFF(pSecondTimer));
501 qemu_put_timer(f, s->CTXSUFF(pSecondTimer2));
502
503}
504
505static int rtc_load(QEMUFile *f, void *opaque, int version_id)
506{
507 RTCState *s = (RTCState*)opaque;
508
509 if (version_id != 1)
510 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
511
512 qemu_get_buffer(f, s->cmos_data, 128);
513 qemu_get_8s(f, &s->cmos_index);
514
515 qemu_get_be32s(f, (uint32_t *)&s->current_tm.tm_sec);
516 qemu_get_be32s(f, (uint32_t *)&s->current_tm.tm_min);
517 qemu_get_be32s(f, (uint32_t *)&s->current_tm.tm_hour);
518 qemu_get_be32s(f, (uint32_t *)&s->current_tm.tm_wday);
519 qemu_get_be32s(f, (uint32_t *)&s->current_tm.tm_mday);
520 qemu_get_be32s(f, (uint32_t *)&s->current_tm.tm_mon);
521 qemu_get_be32s(f, (uint32_t *)&s->current_tm.tm_year);
522
523 qemu_get_timer(f, s->CTXSUFF(pPeriodicTimer));
524
525 qemu_get_be64s(f, (uint64_t *)&s->next_periodic_time);
526
527 qemu_get_be64s(f, (uint64_t *)&s->next_second_time);
528 qemu_get_timer(f, s->CTXSUFF(pSecondTimer));
529 qemu_get_timer(f, s->CTXSUFF(pSecondTimer2));
530
531 int period_code = s->cmos_data[RTC_REG_A] & 0x0f;
532 if ( period_code != 0
533 && (s->cmos_data[RTC_REG_B] & REG_B_PIE)) {
534 if (period_code <= 2)
535 period_code += 7;
536 int period = 1 << (period_code - 1);
537 LogRel(("RTC: period=%#x (%d) %u Hz (restore)\n", period, period, _32K / period));
538 s->CurPeriod = period;
539 } else {
540 LogRel(("RTC: stopped the periodic timer (restore)\n"));
541 s->CurPeriod = 0;
542 }
543 s->cRelLogEntries = 0;
544 return 0;
545}
546#endif /* IN_RING3 */
547
548/* -=-=-=-=-=- wrappers -=-=-=-=-=- */
549
550/**
551 * Port I/O Handler for IN operations.
552 *
553 * @returns VBox status code.
554 *
555 * @param pDevIns The device instance.
556 * @param pvUser User argument - ignored.
557 * @param uPort Port number used for the IN operation.
558 * @param pu32 Where to store the result.
559 * @param cb Number of bytes read.
560 */
561PDMBOTHCBDECL(int) rtcIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
562{
563 NOREF(pvUser);
564 if (cb == 1)
565 {
566 *pu32 = cmos_ioport_read(PDMINS2DATA(pDevIns, RTCState *), Port);
567 return VINF_SUCCESS;
568 }
569 return VERR_IOM_IOPORT_UNUSED;
570}
571
572
573/**
574 * Port I/O Handler for OUT operations.
575 *
576 * @returns VBox status code.
577 *
578 * @param pDevIns The device instance.
579 * @param pvUser User argument - ignored.
580 * @param uPort Port number used for the IN operation.
581 * @param u32 The value to output.
582 * @param cb The value size in bytes.
583 */
584PDMBOTHCBDECL(int) rtcIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
585{
586 NOREF(pvUser);
587 if (cb == 1)
588 cmos_ioport_write(PDMINS2DATA(pDevIns, RTCState *), Port, u32);
589 return VINF_SUCCESS;
590}
591
592
593/**
594 * Device timer callback function, periodic.
595 *
596 * @param pDevIns Device instance of the device which registered the timer.
597 * @param pTimer The timer handle.
598 */
599PDMBOTHCBDECL(void) rtcTimerPeriodic(PPDMDEVINS pDevIns, PTMTIMER pTimer)
600{
601 rtc_periodic_timer(PDMINS2DATA(pDevIns, RTCState *));
602}
603
604
605/**
606 * Device timer callback function, second.
607 *
608 * @param pDevIns Device instance of the device which registered the timer.
609 * @param pTimer The timer handle.
610 */
611PDMBOTHCBDECL(void) rtcTimerSecond(PPDMDEVINS pDevIns, PTMTIMER pTimer)
612{
613 rtc_update_second(PDMINS2DATA(pDevIns, RTCState *));
614}
615
616
617/**
618 * Device timer callback function, second2.
619 *
620 * @param pDevIns Device instance of the device which registered the timer.
621 * @param pTimer The timer handle.
622 */
623PDMBOTHCBDECL(void) rtcTimerSecond2(PPDMDEVINS pDevIns, PTMTIMER pTimer)
624{
625 rtc_update_second2(PDMINS2DATA(pDevIns, RTCState *));
626}
627
628
629#ifdef IN_RING3
630/**
631 * Saves a state of the programmable interval timer device.
632 *
633 * @returns VBox status code.
634 * @param pDevIns The device instance.
635 * @param pSSMHandle The handle to save the state to.
636 */
637static DECLCALLBACK(int) rtcSaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSMHandle)
638{
639 RTCState *pData = PDMINS2DATA(pDevIns, RTCState *);
640 rtc_save(pSSMHandle, pData);
641 return VINF_SUCCESS;
642}
643
644
645/**
646 * Loads a saved programmable interval timer device state.
647 *
648 * @returns VBox status code.
649 * @param pDevIns The device instance.
650 * @param pSSMHandle The handle to the saved state.
651 * @param u32Version The data unit version number.
652 */
653static DECLCALLBACK(int) rtcLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSMHandle, uint32_t u32Version)
654{
655 RTCState *pData = PDMINS2DATA(pDevIns, RTCState *);
656 return rtc_load(pSSMHandle, pData, u32Version);
657}
658
659
660/* -=-=-=-=-=- PDM Interface provided by the RTC device -=-=-=-=-=- */
661
662/**
663 * Calculate and update the standard CMOS checksum.
664 *
665 * @param pData Pointer to the RTC state data.
666 */
667static void rtcCalcCRC(RTCState *pData)
668{
669 uint16_t u16;
670 unsigned i;
671
672 for (i = RTC_CRC_START, u16 = 0; i <= RTC_CRC_LAST; i++)
673 u16 += pData->cmos_data[i];
674 pData->cmos_data[RTC_CRC_LOW] = u16 & 0xff;
675 pData->cmos_data[RTC_CRC_HIGH] = (u16 >> 8) & 0xff;
676}
677
678
679/**
680 * Write to a CMOS register and update the checksum if necessary.
681 *
682 * @returns VBox status code.
683 * @param pDevIns Device instance of the RTC.
684 * @param iReg The CMOS register index.
685 * @param u8Value The CMOS register value.
686 */
687static DECLCALLBACK(int) rtcCMOSWrite(PPDMDEVINS pDevIns, unsigned iReg, uint8_t u8Value)
688{
689 RTCState *pData = PDMINS2DATA(pDevIns, RTCState *);
690 if (iReg < ELEMENTS(pData->cmos_data))
691 {
692 pData->cmos_data[iReg] = u8Value;
693
694 /* does it require checksum update? */
695 if ( iReg >= RTC_CRC_START
696 && iReg <= RTC_CRC_LAST)
697 rtcCalcCRC(pData);
698
699 return VINF_SUCCESS;
700 }
701 AssertMsgFailed(("iReg=%d\n", iReg));
702 return VERR_INVALID_PARAMETER;
703}
704
705
706/**
707 * Read a CMOS register.
708 *
709 * @returns VBox status code.
710 * @param pDevIns Device instance of the RTC.
711 * @param iReg The CMOS register index.
712 * @param pu8Value Where to store the CMOS register value.
713 */
714static DECLCALLBACK(int) rtcCMOSRead(PPDMDEVINS pDevIns, unsigned iReg, uint8_t *pu8Value)
715{
716 RTCState *pData = PDMINS2DATA(pDevIns, RTCState *);
717 if (iReg < ELEMENTS(pData->cmos_data))
718 {
719 *pu8Value = pData->cmos_data[iReg];
720 return VINF_SUCCESS;
721 }
722 AssertMsgFailed(("iReg=%d\n", iReg));
723 return VERR_INVALID_PARAMETER;
724}
725
726
727/* -=-=-=-=-=- based on bits from pc.c -=-=-=-=-=- */
728
729/** @copydoc FNPDMDEVINITCOMPLETE */
730static DECLCALLBACK(int) rtcInitComplete(PPDMDEVINS pDevIns)
731{
732 /** @todo this should be (re)done at power on if we didn't load a state... */
733 RTCState *pData = PDMINS2DATA(pDevIns, RTCState *);
734
735 /*
736 * Set the CMOS date/time.
737 */
738 RTTIMESPEC Now;
739 PDMDevHlpUTCNow(pDevIns, &Now);
740 RTTIME Time;
741 if (pData->fUTC)
742 RTTimeExplode(&Time, &Now);
743 else
744 RTTimeLocalExplode(&Time, &Now);
745
746 struct my_tm Tm;
747 memset(&Tm, 0, sizeof(Tm));
748 Tm.tm_year = Time.i32Year - 1900;
749 Tm.tm_mon = Time.u8Month - 1;
750 Tm.tm_mday = Time.u8MonthDay;
751 Tm.tm_wday = (Time.u8WeekDay - 1 + 7) % 7; /* 0 = monday -> sunday */
752 Tm.tm_yday = Time.u16YearDay - 1;
753 Tm.tm_hour = Time.u8Hour;
754 Tm.tm_min = Time.u8Minute;
755 Tm.tm_sec = Time.u8Second;
756
757 rtc_set_date(pData, &Tm);
758
759 int iYear = to_bcd(pData, (Tm.tm_year / 100) + 19); /* tm_year is 1900 based */
760 rtc_set_memory(pData, 0x32, iYear); /* 32h - Century Byte (BCD value for the century */
761 rtc_set_memory(pData, 0x37, iYear); /* 37h - (IBM PS/2) Date Century Byte */
762
763 /*
764 * Recalculate the checksum just in case.
765 */
766 rtcCalcCRC(pData);
767
768 Log(("CMOS: \n%16.128Vhxd\n", pData->cmos_data));
769 return VINF_SUCCESS;
770}
771
772
773/* -=-=-=-=-=- real code -=-=-=-=-=- */
774
775/**
776 * @copydoc
777 */
778static DECLCALLBACK(void) rtcRelocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
779{
780 RTCState *pThis = PDMINS2DATA(pDevIns, RTCState *);
781
782 pThis->pDevInsGC = PDMDEVINS_2_GCPTR(pDevIns);
783 pThis->pPeriodicTimerGC = TMTimerGCPtr(pThis->pPeriodicTimerHC);
784 pThis->pSecondTimerGC = TMTimerGCPtr(pThis->pSecondTimerHC);
785 pThis->pSecondTimer2GC = TMTimerGCPtr(pThis->pSecondTimer2HC);
786}
787
788
789/**
790 * Construct a device instance for a VM.
791 *
792 * @returns VBox status.
793 * @param pDevIns The device instance data.
794 * If the registration structure is needed, pDevIns->pDevReg points to it.
795 * @param iInstance Instance number. Use this to figure out which registers and such to use.
796 * The device number is also found in pDevIns->iInstance, but since it's
797 * likely to be freqently used PDM passes it as parameter.
798 * @param pCfgHandle Configuration node handle for the device. Use this to obtain the configuration
799 * of the device instance. It's also found in pDevIns->pCfgHandle, but like
800 * iInstance it's expected to be used a bit in this function.
801 */
802static DECLCALLBACK(int) rtcConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfgHandle)
803{
804 RTCState *pData = PDMINS2DATA(pDevIns, RTCState *);
805 int rc;
806 uint8_t u8Irq;
807 uint16_t u16Base;
808 bool fGCEnabled;
809 bool fR0Enabled;
810 Assert(iInstance == 0);
811
812 /*
813 * Validate configuration.
814 */
815 if (!CFGMR3AreValuesValid(pCfgHandle, "Irq\0Base\0GCEnabled\0fR0Enabled\0"))
816 return VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES;
817
818 /*
819 * Init the data.
820 */
821 rc = CFGMR3QueryU8(pCfgHandle, "Irq", &u8Irq);
822 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
823 u8Irq = 8;
824 else if (VBOX_FAILURE(rc))
825 return PDMDEV_SET_ERROR(pDevIns, rc,
826 N_("Configuration error: Querying \"Irq\" as a uint8_t failed"));
827
828 rc = CFGMR3QueryU16(pCfgHandle, "Base", &u16Base);
829 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
830 u16Base = 0x70;
831 else if (VBOX_FAILURE(rc))
832 return PDMDEV_SET_ERROR(pDevIns, rc,
833 N_("Configuration error: Querying \"Base\" as a uint16_t failed"));
834
835 rc = CFGMR3QueryBool(pCfgHandle, "GCEnabled", &fGCEnabled);
836 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
837 fGCEnabled = true;
838 else if (VBOX_FAILURE(rc))
839 return PDMDEV_SET_ERROR(pDevIns, rc,
840 N_("Configuration error: failed to read GCEnabled as boolean"));
841
842 rc = CFGMR3QueryBool(pCfgHandle, "R0Enabled", &fR0Enabled);
843 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
844 fR0Enabled = true;
845 else if (VBOX_FAILURE(rc))
846 return PDMDEV_SET_ERROR(pDevIns, rc,
847 N_("Configuration error: failed to read R0Enabled as boolean"));
848
849 Log(("CMOS: fGCEnabled=%d fR0Enabled=%d\n", fGCEnabled, fR0Enabled));
850
851
852 pData->pDevInsHC = pDevIns;
853 pData->irq = u8Irq;
854 pData->cmos_data[RTC_REG_A] = 0x26;
855 pData->cmos_data[RTC_REG_B] = 0x02;
856 pData->cmos_data[RTC_REG_C] = 0x00;
857 pData->cmos_data[RTC_REG_D] = 0x80;
858 pData->RtcReg.u32Version = PDM_RTCREG_VERSION;
859 pData->RtcReg.pfnRead = rtcCMOSRead;
860 pData->RtcReg.pfnWrite = rtcCMOSWrite;
861
862 /*
863 * Create timers, arm them, register I/O Ports and save state.
864 */
865 rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL_SYNC, rtcTimerPeriodic, "MC146818 RTC/CMOS - Periodic", &pData->pPeriodicTimerHC);
866 if (VBOX_FAILURE(rc))
867 {
868 AssertMsgFailed(("pfnTMTimerCreate -> %Vrc\n", rc));
869 return rc;
870 }
871 rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL_SYNC, rtcTimerSecond, "MC146818 RTC/CMOS - Second", &pData->pSecondTimerHC);
872 if (VBOX_FAILURE(rc))
873 {
874 AssertMsgFailed(("pfnTMTimerCreate -> %Vrc\n", rc));
875 return rc;
876 }
877 rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL_SYNC, rtcTimerSecond2, "MC146818 RTC/CMOS - Second2", &pData->pSecondTimer2HC);
878 if (VBOX_FAILURE(rc))
879 {
880 AssertMsgFailed(("pfnTMTimerCreate -> %Vrc\n", rc));
881 return rc;
882 }
883 pData->next_second_time = TMTimerGet(pData->CTXSUFF(pSecondTimer2)) + (TMTimerGetFreq(pData->CTXSUFF(pSecondTimer2)) * 99) / 100;
884 TMTimerSet(pData->CTXSUFF(pSecondTimer2), pData->next_second_time);
885
886 rc = PDMDevHlpIOPortRegister(pDevIns, u16Base, 2, NULL, rtcIOPortWrite, rtcIOPortRead, NULL, NULL, "MC146818 RTC/CMOS");
887 if (VBOX_FAILURE(rc))
888 return rc;
889 if (fGCEnabled)
890 {
891 rc = PDMDevHlpIOPortRegisterGC(pDevIns, u16Base, 2, 0, "rtcIOPortWrite", "rtcIOPortRead", NULL, NULL, "MC146818 RTC/CMOS");
892 if (VBOX_FAILURE(rc))
893 return rc;
894 }
895 if (fR0Enabled)
896 {
897 rc = PDMDevHlpIOPortRegisterR0(pDevIns, u16Base, 2, 0, "rtcIOPortWrite", "rtcIOPortRead", NULL, NULL, "MC146818 RTC/CMOS");
898 if (VBOX_FAILURE(rc))
899 return rc;
900 }
901
902 rc = PDMDevHlpSSMRegister(pDevIns, pDevIns->pDevReg->szDeviceName, iInstance, 1 /* version */, sizeof(*pData),
903 NULL, rtcSaveExec, NULL,
904 NULL, rtcLoadExec, NULL);
905 if (VBOX_FAILURE(rc))
906 return rc;
907
908 /*
909 * Register ourselves as the RTC with PDM.
910 */
911 rc = pDevIns->pDevHlp->pfnRTCRegister(pDevIns, &pData->RtcReg, &pData->pRtcHlpHC);
912 if (VBOX_FAILURE(rc))
913 return rc;
914
915 return VINF_SUCCESS;
916}
917
918
919/**
920 * The device registration structure.
921 */
922const PDMDEVREG g_DeviceMC146818 =
923{
924 /* u32Version */
925 PDM_DEVREG_VERSION,
926 /* szDeviceName */
927 "mc146818",
928 /* szGCMod */
929 "VBoxDDGC.gc",
930 /* szR0Mod */
931 "VBoxDDR0.r0",
932 /* pszDescription */
933 "Motorola MC146818 RTC/CMOS Device.",
934 /* fFlags */
935 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,
936 /* fClass */
937 PDM_DEVREG_CLASS_RTC,
938 /* cMaxInstances */
939 1,
940 /* cbInstance */
941 sizeof(RTCState),
942 /* pfnConstruct */
943 rtcConstruct,
944 /* pfnDestruct */
945 NULL,
946 /* pfnRelocate */
947 rtcRelocate,
948 /* pfnIOCtl */
949 NULL,
950 /* pfnPowerOn */
951 NULL,
952 /* pfnReset */
953 NULL,
954 /* pfnSuspend */
955 NULL,
956 /* pfnResume */
957 NULL,
958 /* pfnAttach */
959 NULL,
960 /* pfnDetach */
961 NULL,
962 /* pfnQueryInterface */
963 NULL,
964 /* pfnInitComplete */
965 rtcInitComplete
966};
967#endif /* IN_RING3 */
968#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
969
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette