VirtualBox

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

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

64-bit Alignment.

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