VirtualBox

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

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

Fixed RTC alarm (problem seen in Norton Diagnostics). We need to a) compare tm_min instead
of tm_mon(!!), and b) properly convert from BCD if necessary.

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