VirtualBox

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

Last change on this file since 61 was 1, checked in by vboxsync, 55 years ago

import

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