VirtualBox

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

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

made the file description correspond to the device/driver description.

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