VirtualBox

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

Last change on this file since 4394 was 4071, checked in by vboxsync, 17 years ago

Biggest check-in ever. New source code headers for all (C) innotek files.

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