VirtualBox

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

Last change on this file since 8465 was 8155, checked in by vboxsync, 17 years ago

The Big Sun Rebranding Header Change

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