VirtualBox

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

Last change on this file since 3254 was 2981, checked in by vboxsync, 17 years ago

InnoTek -> innotek: all the headers and comments.

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