VirtualBox

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

Last change on this file since 2627 was 2627, checked in by vboxsync, 18 years ago

Fixed some RTTIME -> tm conversion bugs.

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