VirtualBox

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

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

Dropping the VBOX_WITH_VIRTUAL_SYNC_TIMERS #ifdefs, making it default.

  • Property svn:eol-style set to native
File size: 29.6 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 Implement time/localtime/gmtime replacements in Runtime! */
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 UCT or local time initially. */
137 bool fUCT;
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 RTCState *pData = PDMINS2DATA(pDevIns, RTCState *);
695 time_t Ti;
696 struct tm Tm;
697 struct tm *pTm;
698 int iYear;
699
700 /*
701 * Set the CMOS date/time.
702 */
703#if 0 /* later */
704 RTTIMESPEC Now;
705 RTTIME Time;
706 RTTimeNow(&Now);
707 if (pData->fUCT)
708 RTTimeExplode(&Time, &Now);
709 else
710 RTTimeLocalExplode(&Time, &Now);
711 pTm = RTTimeToPosixTm(&Tm, &Time);
712#else
713 time(&Ti);
714#ifndef __WIN__
715 if (pData->fUCT)
716 pTm = gmtime_r(&Ti, &Tm);
717 else
718 pTm = localtime_r(&Ti, &Tm);
719 Assert(pTm);
720#else
721 /* Win32 doesn't have thread safe stuff, let's just hope this works out fine :/ */
722 if (pData->fUCT)
723 pTm = gmtime(&Ti);
724 else
725 pTm = localtime(&Ti);
726 Assert(pTm);
727 Tm = *pTm;
728 pTm = &Tm;
729#endif
730#endif
731 rtc_set_date(pData, pTm);
732
733 iYear = to_bcd(pData, (Tm.tm_year / 100) + 19); /* tm_year is 1900 based (stupid) */
734 rtc_set_memory(pData, 0x32, iYear); /* 32h - Century Byte (BCD value for the century */
735 rtc_set_memory(pData, 0x37, iYear); /* 37h - (IBM PS/2) Date Century Byte */
736
737 /*
738 * Recalculate the checksum just in case.
739 */
740 rtcCalcCRC(pData);
741
742 Log(("CMOS: \n%16.128Vhxd\n", pData->cmos_data));
743 return VINF_SUCCESS;
744}
745
746
747/* -=-=-=-=-=- real code -=-=-=-=-=- */
748
749/**
750 * @copydoc
751 */
752static DECLCALLBACK(void) rtcRelocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
753{
754 RTCState *pThis = PDMINS2DATA(pDevIns, RTCState *);
755
756 pThis->pDevInsGC = PDMDEVINS_2_GCPTR(pDevIns);
757 pThis->pPeriodicTimerGC = TMTimerGCPtr(pThis->pPeriodicTimerHC);
758 pThis->pSecondTimerGC = TMTimerGCPtr(pThis->pSecondTimerHC);
759 pThis->pSecondTimer2GC = TMTimerGCPtr(pThis->pSecondTimer2HC);
760}
761
762
763/**
764 * Construct a device instance for a VM.
765 *
766 * @returns VBox status.
767 * @param pDevIns The device instance data.
768 * If the registration structure is needed, pDevIns->pDevReg points to it.
769 * @param iInstance Instance number. Use this to figure out which registers and such to use.
770 * The device number is also found in pDevIns->iInstance, but since it's
771 * likely to be freqently used PDM passes it as parameter.
772 * @param pCfgHandle Configuration node handle for the device. Use this to obtain the configuration
773 * of the device instance. It's also found in pDevIns->pCfgHandle, but like
774 * iInstance it's expected to be used a bit in this function.
775 */
776static DECLCALLBACK(int) rtcConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfgHandle)
777{
778 RTCState *pData = PDMINS2DATA(pDevIns, RTCState *);
779 int rc;
780 uint8_t u8Irq;
781 uint16_t u16Base;
782 bool fGCEnabled;
783 bool fR0Enabled;
784 Assert(iInstance == 0);
785
786 /*
787 * Validate configuration.
788 */
789 if (!CFGMR3AreValuesValid(pCfgHandle, "Irq\0Base\0GCEnabled\0fR0Enabled\0"))
790 return VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES;
791
792 /*
793 * Init the data.
794 */
795 rc = CFGMR3QueryU8(pCfgHandle, "Irq", &u8Irq);
796 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
797 u8Irq = 8;
798 else if (VBOX_FAILURE(rc))
799 return PDMDEV_SET_ERROR(pDevIns, rc,
800 N_("Configuration error: Querying \"Irq\" as a uint8_t failed"));
801
802 rc = CFGMR3QueryU16(pCfgHandle, "Base", &u16Base);
803 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
804 u16Base = 0x70;
805 else if (VBOX_FAILURE(rc))
806 return PDMDEV_SET_ERROR(pDevIns, rc,
807 N_("Configuration error: Querying \"Base\" as a uint16_t failed"));
808
809 rc = CFGMR3QueryBool(pCfgHandle, "GCEnabled", &fGCEnabled);
810 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
811 fGCEnabled = false/*true*/; /** @todo later when we've got more than 15-30 switches to save. */
812 else if (VBOX_FAILURE(rc))
813 return PDMDEV_SET_ERROR(pDevIns, rc,
814 N_("Configuration error: failed to read GCEnabled as boolean"));
815
816 rc = CFGMR3QueryBool(pCfgHandle, "R0Enabled", &fR0Enabled);
817 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
818 fR0Enabled = true;
819 else if (VBOX_FAILURE(rc))
820 return PDMDEV_SET_ERROR(pDevIns, rc,
821 N_("Configuration error: failed to read R0Enabled as boolean"));
822
823 Log(("CMOS: fGCEnabled=%d fR0Enabled=%d\n", fGCEnabled, fR0Enabled));
824
825
826 pData->pDevInsHC = pDevIns;
827 pData->irq = u8Irq;
828 pData->cmos_data[RTC_REG_A] = 0x26;
829 pData->cmos_data[RTC_REG_B] = 0x02;
830 pData->cmos_data[RTC_REG_C] = 0x00;
831 pData->cmos_data[RTC_REG_D] = 0x80;
832 pData->RtcReg.u32Version = PDM_RTCREG_VERSION;
833 pData->RtcReg.pfnRead = rtcCMOSRead;
834 pData->RtcReg.pfnWrite = rtcCMOSWrite;
835
836 /*
837 * Create timers, arm them, register I/O Ports and save state.
838 */
839#ifdef VBOX_WITH_VIRTUAL_SYNC_TIMERS
840 rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL_SYNC, rtcTimerPeriodic, "MC146818 RTC/CMOS - Periodic", &pData->pPeriodicTimerHC);
841#else
842 rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL, rtcTimerPeriodic, "MC146818 RTC/CMOS - Periodic", &pData->pPeriodicTimerHC);
843#endif
844 if (VBOX_FAILURE(rc))
845 {
846 AssertMsgFailed(("pfnTMTimerCreate -> %Vrc\n", rc));
847 return rc;
848 }
849#ifdef VBOX_WITH_VIRTUAL_SYNC_TIMERS
850 rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL_SYNC, rtcTimerSecond, "MC146818 RTC/CMOS - Second", &pData->pSecondTimerHC);
851#else
852 rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL, rtcTimerSecond, "MC146818 RTC/CMOS - Second", &pData->pSecondTimerHC);
853#endif
854 if (VBOX_FAILURE(rc))
855 {
856 AssertMsgFailed(("pfnTMTimerCreate -> %Vrc\n", rc));
857 return rc;
858 }
859#ifdef VBOX_WITH_VIRTUAL_SYNC_TIMERS
860 rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL_SYNC, rtcTimerSecond2, "MC146818 RTC/CMOS - Second2", &pData->pSecondTimer2HC);
861#else
862 rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL, rtcTimerSecond2, "MC146818 RTC/CMOS - Second2", &pData->pSecondTimer2HC);
863#endif
864 if (VBOX_FAILURE(rc))
865 {
866 AssertMsgFailed(("pfnTMTimerCreate -> %Vrc\n", rc));
867 return rc;
868 }
869 pData->next_second_time = TMTimerGet(pData->CTXSUFF(pSecondTimer2)) + (TMTimerGetFreq(pData->CTXSUFF(pSecondTimer2)) * 99) / 100;
870 TMTimerSet(pData->CTXSUFF(pSecondTimer2), pData->next_second_time);
871
872 rc = PDMDevHlpIOPortRegister(pDevIns, u16Base, 2, NULL, rtcIOPortWrite, rtcIOPortRead, NULL, NULL, "MC146818 RTC/CMOS");
873 if (VBOX_FAILURE(rc))
874 return rc;
875 if (fGCEnabled)
876 {
877 rc = PDMDevHlpIOPortRegisterGC(pDevIns, u16Base, 2, 0, "rtcIOPortWrite", "rtcIOPortRead", NULL, NULL, "MC146818 RTC/CMOS");
878 if (VBOX_FAILURE(rc))
879 return rc;
880 }
881 if (fR0Enabled)
882 {
883 rc = PDMDevHlpIOPortRegisterR0(pDevIns, u16Base, 2, 0, "rtcIOPortWrite", "rtcIOPortRead", NULL, NULL, "MC146818 RTC/CMOS");
884 if (VBOX_FAILURE(rc))
885 return rc;
886 }
887
888 rc = PDMDevHlpSSMRegister(pDevIns, pDevIns->pDevReg->szDeviceName, iInstance, 1 /* version */, sizeof(*pData),
889 NULL, rtcSaveExec, NULL,
890 NULL, rtcLoadExec, NULL);
891 if (VBOX_FAILURE(rc))
892 return rc;
893
894 /*
895 * Register ourselves as the RTC with PDM.
896 */
897 rc = pDevIns->pDevHlp->pfnRTCRegister(pDevIns, &pData->RtcReg, &pData->pRtcHlpHC);
898 if (VBOX_FAILURE(rc))
899 return rc;
900
901 return VINF_SUCCESS;
902}
903
904
905/**
906 * The device registration structure.
907 */
908const PDMDEVREG g_DeviceMC146818 =
909{
910 /* u32Version */
911 PDM_DEVREG_VERSION,
912 /* szDeviceName */
913 "mc146818",
914 /* szGCMod */
915 "VBoxDDGC.gc",
916 /* szR0Mod */
917 "VBoxDDR0.r0",
918 /* pszDescription */
919 "Motorola MC146818 RTC/CMOS Device.",
920 /* fFlags */
921 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,
922 /* fClass */
923 PDM_DEVREG_CLASS_RTC,
924 /* cMaxInstances */
925 1,
926 /* cbInstance */
927 sizeof(RTCState),
928 /* pfnConstruct */
929 rtcConstruct,
930 /* pfnDestruct */
931 NULL,
932 /* pfnRelocate */
933 rtcRelocate,
934 /* pfnIOCtl */
935 NULL,
936 /* pfnPowerOn */
937 NULL,
938 /* pfnReset */
939 NULL,
940 /* pfnSuspend */
941 NULL,
942 /* pfnResume */
943 NULL,
944 /* pfnAttach */
945 NULL,
946 /* pfnDetach */
947 NULL,
948 /* pfnQueryInterface */
949 NULL,
950 /* pfnInitComplete */
951 rtcInitComplete
952};
953#endif /* IN_RING3 */
954#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
955
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