VirtualBox

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

Last change on this file since 6738 was 5999, checked in by vboxsync, 17 years ago

The Giant CDDL Dual-License Header Change.

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