VirtualBox

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

Last change on this file since 3670 was 3666, checked in by vboxsync, 17 years ago

replace underscore symbols in Devices/

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