VirtualBox

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

Last change on this file since 27275 was 27126, checked in by vboxsync, 15 years ago

PIT,RTC,HPET,PDM: Combined and simplified the two interfaces PDM uses to inform PIT & RTC about HPET legacy mode changes. Moved the code to the right places in the files. Use bool, not uint8_t, when we mean boolean (no saved state difference).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 34.5 KB
Line 
1/* $Id: DevRTC.cpp 27126 2010-03-05 19:40:43Z 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#include <iprt/string.h>
56
57#ifdef IN_RING3
58# include <iprt/alloc.h>
59# include <iprt/uuid.h>
60#endif /* IN_RING3 */
61
62#include "../Builtins.h"
63
64struct RTCState;
65typedef struct RTCState RTCState;
66
67#define RTC_CRC_START 0x10
68#define RTC_CRC_LAST 0x2d
69#define RTC_CRC_HIGH 0x2e
70#define RTC_CRC_LOW 0x2f
71
72
73/*******************************************************************************
74* Internal Functions *
75*******************************************************************************/
76#ifndef VBOX_DEVICE_STRUCT_TESTCASE
77RT_C_DECLS_BEGIN
78PDMBOTHCBDECL(int) rtcIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb);
79PDMBOTHCBDECL(int) rtcIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb);
80PDMBOTHCBDECL(void) rtcTimerPeriodic(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser);
81PDMBOTHCBDECL(void) rtcTimerSecond(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser);
82PDMBOTHCBDECL(void) rtcTimerSecond2(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser);
83RT_C_DECLS_END
84#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
85
86
87/*******************************************************************************
88* Defined Constants And Macros *
89*******************************************************************************/
90/*#define DEBUG_CMOS*/
91
92#define RTC_SECONDS 0
93#define RTC_SECONDS_ALARM 1
94#define RTC_MINUTES 2
95#define RTC_MINUTES_ALARM 3
96#define RTC_HOURS 4
97#define RTC_HOURS_ALARM 5
98#define RTC_ALARM_DONT_CARE 0xC0
99
100#define RTC_DAY_OF_WEEK 6
101#define RTC_DAY_OF_MONTH 7
102#define RTC_MONTH 8
103#define RTC_YEAR 9
104
105#define RTC_REG_A 10
106#define RTC_REG_B 11
107#define RTC_REG_C 12
108#define RTC_REG_D 13
109
110#define REG_A_UIP 0x80
111
112#define REG_B_SET 0x80
113#define REG_B_PIE 0x40
114#define REG_B_AIE 0x20
115#define REG_B_UIE 0x10
116
117
118/** The saved state version. */
119#define RTC_SAVED_STATE_VERSION 3
120/** The saved state version used by VirtualBox 3.1 and earlier.
121 * This does not include disabled by HPET state. */
122#define RTC_SAVED_STATE_VERSION_VBOX_31 2
123/** The saved state version used by VirtualBox 3.0 and earlier.
124 * This does not include the configuration. */
125#define RTC_SAVED_STATE_VERSION_VBOX_30 1
126
127
128/*******************************************************************************
129* Structures and Typedefs *
130*******************************************************************************/
131/** @todo Replace struct my_tm with RTTIME. */
132struct my_tm
133{
134 int32_t tm_sec;
135 int32_t tm_min;
136 int32_t tm_hour;
137 int32_t tm_mday;
138 int32_t tm_mon;
139 int32_t tm_year;
140 int32_t tm_wday;
141 int32_t tm_yday;
142};
143
144
145struct RTCState {
146 uint8_t cmos_data[128];
147 uint8_t cmos_index;
148 uint8_t Alignment0[7];
149 struct my_tm current_tm;
150 /** The configured IRQ. */
151 int32_t irq;
152 /** The configured I/O port base. */
153 RTIOPORT IOPortBase;
154 /** Use UTC or local time initially. */
155 bool fUTC;
156 /** Disabled by HPET legacy mode. */
157 bool fDisabledByHpet;
158 /* periodic timer */
159 int64_t next_periodic_time;
160 /* second update */
161 int64_t next_second_time;
162
163 /** Pointer to the device instance - R3 Ptr. */
164 PPDMDEVINSR3 pDevInsR3;
165 /** The periodic timer (rtcTimerPeriodic) - R3 Ptr. */
166 PTMTIMERR3 pPeriodicTimerR3;
167 /** The second timer (rtcTimerSecond) - R3 Ptr. */
168 PTMTIMERR3 pSecondTimerR3;
169 /** The second second timer (rtcTimerSecond2) - R3 Ptr. */
170 PTMTIMERR3 pSecondTimer2R3;
171
172 /** Pointer to the device instance - R0 Ptr. */
173 PPDMDEVINSR0 pDevInsR0;
174 /** The periodic timer (rtcTimerPeriodic) - R0 Ptr. */
175 PTMTIMERR0 pPeriodicTimerR0;
176 /** The second timer (rtcTimerSecond) - R0 Ptr. */
177 PTMTIMERR0 pSecondTimerR0;
178 /** The second second timer (rtcTimerSecond2) - R0 Ptr. */
179 PTMTIMERR0 pSecondTimer2R0;
180
181 /** Pointer to the device instance - RC Ptr. */
182 PPDMDEVINSRC pDevInsRC;
183 /** The periodic timer (rtcTimerPeriodic) - RC Ptr. */
184 PTMTIMERRC pPeriodicTimerRC;
185 /** The second timer (rtcTimerSecond) - RC Ptr. */
186 PTMTIMERRC pSecondTimerRC;
187 /** The second second timer (rtcTimerSecond2) - RC Ptr. */
188 PTMTIMERRC pSecondTimer2RC;
189
190 /** The RTC registration structure. */
191 PDMRTCREG RtcReg;
192 /** The RTC device helpers. */
193 R3PTRTYPE(PCPDMRTCHLP) pRtcHlpR3;
194 /** Number of release log entries. Used to prevent flooding. */
195 uint32_t cRelLogEntries;
196 /** The current/previous timer period. Used to prevent flooding changes. */
197 int32_t CurPeriod;
198
199 /** HPET legacy mode notification interface. */
200 PDMIHPETLEGACYNOTIFY IHpetLegacyNotify;
201};
202
203#ifndef VBOX_DEVICE_STRUCT_TESTCASE
204static void rtc_set_time(RTCState *s);
205static void rtc_copy_date(RTCState *s);
206
207static void rtc_timer_update(RTCState *s, int64_t current_time)
208{
209 int period_code, period;
210 uint64_t cur_clock, next_irq_clock;
211 uint32_t freq;
212
213 period_code = s->cmos_data[RTC_REG_A] & 0x0f;
214 if (period_code != 0 &&
215 (s->cmos_data[RTC_REG_B] & REG_B_PIE)) {
216 if (period_code <= 2)
217 period_code += 7;
218 /* period in 32 kHz cycles */
219 period = 1 << (period_code - 1);
220 /* compute 32 kHz clock */
221 freq = TMTimerGetFreq(s->CTX_SUFF(pPeriodicTimer));
222
223 cur_clock = ASMMultU64ByU32DivByU32(current_time, 32768, freq);
224 next_irq_clock = (cur_clock & ~(uint64_t)(period - 1)) + period;
225 s->next_periodic_time = ASMMultU64ByU32DivByU32(next_irq_clock, freq, 32768) + 1;
226 TMTimerSet(s->CTX_SUFF(pPeriodicTimer), s->next_periodic_time);
227
228 if (period != s->CurPeriod)
229 {
230 if (s->cRelLogEntries++ < 64)
231 LogRel(("RTC: period=%#x (%d) %u Hz\n", period, period, _32K / period));
232 s->CurPeriod = period;
233 }
234 } else {
235 if (TMTimerIsActive(s->CTX_SUFF(pPeriodicTimer)) && s->cRelLogEntries++ < 64)
236 LogRel(("RTC: stopped the periodic timer\n"));
237 TMTimerStop(s->CTX_SUFF(pPeriodicTimer));
238 }
239}
240
241static void rtc_raise_irq(RTCState* pThis, uint32_t iLevel)
242{
243 if (!pThis->fDisabledByHpet)
244 PDMDevHlpISASetIrq(pThis->CTX_SUFF(pDevIns), pThis->irq, iLevel);
245}
246
247static void rtc_periodic_timer(void *opaque)
248{
249 RTCState *s = (RTCState*)opaque;
250
251 rtc_timer_update(s, s->next_periodic_time);
252 s->cmos_data[RTC_REG_C] |= 0xc0;
253
254 rtc_raise_irq(s, 1);
255}
256
257static void cmos_ioport_write(void *opaque, uint32_t addr, uint32_t data)
258{
259 RTCState *s = (RTCState*)opaque;
260
261 if ((addr & 1) == 0) {
262 s->cmos_index = data & 0x7f;
263 } else {
264 Log(("CMOS: Write idx %#04x: %#04x (old %#04x)\n", s->cmos_index, data, s->cmos_data[s->cmos_index]));
265 switch(s->cmos_index) {
266 case RTC_SECONDS_ALARM:
267 case RTC_MINUTES_ALARM:
268 case RTC_HOURS_ALARM:
269 s->cmos_data[s->cmos_index] = data;
270 break;
271 case RTC_SECONDS:
272 case RTC_MINUTES:
273 case RTC_HOURS:
274 case RTC_DAY_OF_WEEK:
275 case RTC_DAY_OF_MONTH:
276 case RTC_MONTH:
277 case RTC_YEAR:
278 s->cmos_data[s->cmos_index] = data;
279 /* if in set mode, do not update the time */
280 if (!(s->cmos_data[RTC_REG_B] & REG_B_SET)) {
281 rtc_set_time(s);
282 }
283 break;
284 case RTC_REG_A:
285 /* UIP bit is read only */
286 s->cmos_data[RTC_REG_A] = (data & ~REG_A_UIP) |
287 (s->cmos_data[RTC_REG_A] & REG_A_UIP);
288 rtc_timer_update(s, TMTimerGet(s->CTX_SUFF(pPeriodicTimer)));
289 break;
290 case RTC_REG_B:
291 if (data & REG_B_SET) {
292 /* set mode: reset UIP mode */
293 s->cmos_data[RTC_REG_A] &= ~REG_A_UIP;
294#if 0 /* This is probably wrong as it breaks changing the time/date in OS/2. */
295 data &= ~REG_B_UIE;
296#endif
297 } else {
298 /* if disabling set mode, update the time */
299 if (s->cmos_data[RTC_REG_B] & REG_B_SET) {
300 rtc_set_time(s);
301 }
302 }
303 s->cmos_data[RTC_REG_B] = data;
304 rtc_timer_update(s, TMTimerGet(s->CTX_SUFF(pPeriodicTimer)));
305 break;
306 case RTC_REG_C:
307 case RTC_REG_D:
308 /* cannot write to them */
309 break;
310 default:
311 s->cmos_data[s->cmos_index] = data;
312 break;
313 }
314 }
315}
316
317static inline int to_bcd(RTCState *s, int a)
318{
319 if (s->cmos_data[RTC_REG_B] & 0x04) {
320 return a;
321 } else {
322 return ((a / 10) << 4) | (a % 10);
323 }
324}
325
326static inline int from_bcd(RTCState *s, int a)
327{
328 if (s->cmos_data[RTC_REG_B] & 0x04) {
329 return a;
330 } else {
331 return ((a >> 4) * 10) + (a & 0x0f);
332 }
333}
334
335static void rtc_set_time(RTCState *s)
336{
337 struct my_tm *tm = &s->current_tm;
338
339 tm->tm_sec = from_bcd(s, s->cmos_data[RTC_SECONDS]);
340 tm->tm_min = from_bcd(s, s->cmos_data[RTC_MINUTES]);
341 tm->tm_hour = from_bcd(s, s->cmos_data[RTC_HOURS] & 0x7f);
342 if (!(s->cmos_data[RTC_REG_B] & 0x02) &&
343 (s->cmos_data[RTC_HOURS] & 0x80)) {
344 tm->tm_hour += 12;
345 }
346 tm->tm_wday = from_bcd(s, s->cmos_data[RTC_DAY_OF_WEEK]);
347 tm->tm_mday = from_bcd(s, s->cmos_data[RTC_DAY_OF_MONTH]);
348 tm->tm_mon = from_bcd(s, s->cmos_data[RTC_MONTH]) - 1;
349 tm->tm_year = from_bcd(s, s->cmos_data[RTC_YEAR]) + 100;
350}
351
352static void rtc_copy_date(RTCState *s)
353{
354 const struct my_tm *tm = &s->current_tm;
355
356 s->cmos_data[RTC_SECONDS] = to_bcd(s, tm->tm_sec);
357 s->cmos_data[RTC_MINUTES] = to_bcd(s, tm->tm_min);
358 if (s->cmos_data[RTC_REG_B] & 0x02) {
359 /* 24 hour format */
360 s->cmos_data[RTC_HOURS] = to_bcd(s, tm->tm_hour);
361 } else {
362 /* 12 hour format */
363 s->cmos_data[RTC_HOURS] = to_bcd(s, tm->tm_hour % 12);
364 if (tm->tm_hour >= 12)
365 s->cmos_data[RTC_HOURS] |= 0x80;
366 }
367 s->cmos_data[RTC_DAY_OF_WEEK] = to_bcd(s, tm->tm_wday);
368 s->cmos_data[RTC_DAY_OF_MONTH] = to_bcd(s, tm->tm_mday);
369 s->cmos_data[RTC_MONTH] = to_bcd(s, tm->tm_mon + 1);
370 s->cmos_data[RTC_YEAR] = to_bcd(s, tm->tm_year % 100);
371}
372
373/* month is between 0 and 11. */
374static int get_days_in_month(int month, int year)
375{
376 static const int days_tab[12] = {
377 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
378 };
379 int d;
380 if ((unsigned )month >= 12)
381 return 31;
382 d = days_tab[month];
383 if (month == 1) {
384 if ((year % 4) == 0 && ((year % 100) != 0 || (year % 400) == 0))
385 d++;
386 }
387 return d;
388}
389
390/* update 'tm' to the next second */
391static void rtc_next_second(struct my_tm *tm)
392{
393 int days_in_month;
394
395 tm->tm_sec++;
396 if ((unsigned)tm->tm_sec >= 60) {
397 tm->tm_sec = 0;
398 tm->tm_min++;
399 if ((unsigned)tm->tm_min >= 60) {
400 tm->tm_min = 0;
401 tm->tm_hour++;
402 if ((unsigned)tm->tm_hour >= 24) {
403 tm->tm_hour = 0;
404 /* next day */
405 tm->tm_wday++;
406 if ((unsigned)tm->tm_wday >= 7)
407 tm->tm_wday = 0;
408 days_in_month = get_days_in_month(tm->tm_mon,
409 tm->tm_year + 1900);
410 tm->tm_mday++;
411 if (tm->tm_mday < 1) {
412 tm->tm_mday = 1;
413 } else if (tm->tm_mday > days_in_month) {
414 tm->tm_mday = 1;
415 tm->tm_mon++;
416 if (tm->tm_mon >= 12) {
417 tm->tm_mon = 0;
418 tm->tm_year++;
419 }
420 }
421 }
422 }
423 }
424}
425
426
427static void rtc_update_second(void *opaque)
428{
429 RTCState *s = (RTCState*)opaque;
430
431 /* if the oscillator is not in normal operation, we do not update */
432 if ((s->cmos_data[RTC_REG_A] & 0x70) != 0x20) {
433 s->next_second_time += TMTimerGetFreq(s->CTX_SUFF(pSecondTimer));
434 TMTimerSet(s->CTX_SUFF(pSecondTimer), s->next_second_time);
435 } else {
436 rtc_next_second(&s->current_tm);
437
438 if (!(s->cmos_data[RTC_REG_B] & REG_B_SET)) {
439 /* update in progress bit */
440 Log2(("RTC: UIP %x -> 1\n", !!(s->cmos_data[RTC_REG_A] & REG_A_UIP)));
441 s->cmos_data[RTC_REG_A] |= REG_A_UIP;
442 }
443
444 /* 244140 ns = 8 / 32768 seconds */
445 uint64_t delay = TMTimerFromNano(s->CTX_SUFF(pSecondTimer2), 244140);
446 TMTimerSet(s->CTX_SUFF(pSecondTimer2), s->next_second_time + delay);
447 }
448}
449
450static void rtc_update_second2(void *opaque)
451{
452 RTCState *s = (RTCState*)opaque;
453
454 if (!(s->cmos_data[RTC_REG_B] & REG_B_SET)) {
455 rtc_copy_date(s);
456 }
457
458 /* check alarm */
459 if (s->cmos_data[RTC_REG_B] & REG_B_AIE) {
460 if (((s->cmos_data[RTC_SECONDS_ALARM] & 0xc0) == 0xc0 ||
461 from_bcd(s, s->cmos_data[RTC_SECONDS_ALARM]) == s->current_tm.tm_sec) &&
462 ((s->cmos_data[RTC_MINUTES_ALARM] & 0xc0) == 0xc0 ||
463 from_bcd(s, s->cmos_data[RTC_MINUTES_ALARM]) == s->current_tm.tm_min) &&
464 ((s->cmos_data[RTC_HOURS_ALARM] & 0xc0) == 0xc0 ||
465 from_bcd(s, s->cmos_data[RTC_HOURS_ALARM]) == s->current_tm.tm_hour)) {
466
467 s->cmos_data[RTC_REG_C] |= 0xa0;
468 rtc_raise_irq(s, 1);
469 }
470 }
471
472 /* update ended interrupt */
473 if (s->cmos_data[RTC_REG_B] & REG_B_UIE) {
474 s->cmos_data[RTC_REG_C] |= 0x90;
475 rtc_raise_irq(s, 1);
476 }
477
478 /* clear update in progress bit */
479 Log2(("RTC: UIP %x -> 0\n", !!(s->cmos_data[RTC_REG_A] & REG_A_UIP)));
480 s->cmos_data[RTC_REG_A] &= ~REG_A_UIP;
481
482 s->next_second_time += TMTimerGetFreq(s->CTX_SUFF(pSecondTimer));
483 TMTimerSet(s->CTX_SUFF(pSecondTimer), s->next_second_time);
484}
485
486static uint32_t cmos_ioport_read(void *opaque, uint32_t addr)
487{
488 RTCState *s = (RTCState*)opaque;
489 int ret;
490 if ((addr & 1) == 0) {
491 return 0xff;
492 } else {
493 switch(s->cmos_index) {
494 case RTC_SECONDS:
495 case RTC_MINUTES:
496 case RTC_HOURS:
497 case RTC_DAY_OF_WEEK:
498 case RTC_DAY_OF_MONTH:
499 case RTC_MONTH:
500 case RTC_YEAR:
501 ret = s->cmos_data[s->cmos_index];
502 break;
503 case RTC_REG_A:
504 ret = s->cmos_data[s->cmos_index];
505 break;
506 case RTC_REG_C:
507 ret = s->cmos_data[s->cmos_index];
508 rtc_raise_irq(s, 0);
509 s->cmos_data[RTC_REG_C] = 0x00;
510 break;
511 default:
512 ret = s->cmos_data[s->cmos_index];
513 break;
514 }
515 Log(("CMOS: Read idx %#04x: %#04x\n", s->cmos_index, ret));
516 return ret;
517 }
518}
519
520#ifdef IN_RING3
521static void rtc_set_memory(RTCState *s, int addr, int val)
522{
523 if (addr >= 0 && addr <= 127)
524 s->cmos_data[addr] = val;
525}
526
527static void rtc_set_date(RTCState *s, const struct my_tm *tm)
528{
529 s->current_tm = *tm;
530 rtc_copy_date(s);
531}
532
533#endif /* IN_RING3 */
534
535/* -=-=-=-=-=- wrappers / stuff -=-=-=-=-=- */
536
537/**
538 * Port I/O Handler for IN operations.
539 *
540 * @returns VBox status code.
541 *
542 * @param pDevIns The device instance.
543 * @param pvUser User argument - ignored.
544 * @param uPort Port number used for the IN operation.
545 * @param pu32 Where to store the result.
546 * @param cb Number of bytes read.
547 */
548PDMBOTHCBDECL(int) rtcIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
549{
550 NOREF(pvUser);
551 if (cb == 1)
552 {
553 *pu32 = cmos_ioport_read(PDMINS_2_DATA(pDevIns, RTCState *), Port);
554 return VINF_SUCCESS;
555 }
556 return VERR_IOM_IOPORT_UNUSED;
557}
558
559
560/**
561 * Port I/O Handler for OUT operations.
562 *
563 * @returns VBox status code.
564 *
565 * @param pDevIns The device instance.
566 * @param pvUser User argument - ignored.
567 * @param uPort Port number used for the IN operation.
568 * @param u32 The value to output.
569 * @param cb The value size in bytes.
570 */
571PDMBOTHCBDECL(int) rtcIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
572{
573 NOREF(pvUser);
574 if (cb == 1)
575 cmos_ioport_write(PDMINS_2_DATA(pDevIns, RTCState *), Port, u32);
576 return VINF_SUCCESS;
577}
578
579
580/**
581 * Device timer callback function, periodic.
582 *
583 * @param pDevIns Device instance of the device which registered the timer.
584 * @param pTimer The timer handle.
585 * @param pvUser Pointer to the RTC state.
586 */
587PDMBOTHCBDECL(void) rtcTimerPeriodic(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
588{
589 rtc_periodic_timer((RTCState *)pvUser);
590}
591
592
593/**
594 * Device timer callback function, second.
595 *
596 * @param pDevIns Device instance of the device which registered the timer.
597 * @param pTimer The timer handle.
598 * @param pvUser Pointer to the RTC state.
599 */
600PDMBOTHCBDECL(void) rtcTimerSecond(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
601{
602 rtc_update_second((RTCState *)pvUser);
603}
604
605
606/**
607 * Device timer callback function, second2.
608 *
609 * @param pDevIns Device instance of the device which registered the timer.
610 * @param pTimer The timer handle.
611 * @param pvUser Pointer to the RTC state.
612 */
613PDMBOTHCBDECL(void) rtcTimerSecond2(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
614{
615 rtc_update_second2((RTCState *)pvUser);
616}
617
618#ifdef IN_RING3
619
620/**
621 * @copydoc FNSSMDEVLIVEEXEC
622 */
623static DECLCALLBACK(int) rtcLiveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uPass)
624{
625 RTCState *pThis = PDMINS_2_DATA(pDevIns, RTCState *);
626
627 SSMR3PutU8( pSSM, pThis->irq);
628 SSMR3PutIOPort(pSSM, pThis->IOPortBase);
629 SSMR3PutBool( pSSM, pThis->fUTC);
630
631 return VINF_SSM_DONT_CALL_AGAIN;
632}
633
634
635/**
636 * @copydoc FNSSMDEVSAVEEXEC
637 */
638static DECLCALLBACK(int) rtcSaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
639{
640 RTCState *pThis = PDMINS_2_DATA(pDevIns, RTCState *);
641
642 /* The config. */
643 rtcLiveExec(pDevIns, pSSM, SSM_PASS_FINAL);
644
645 /* The state. */
646 SSMR3PutMem(pSSM, pThis->cmos_data, 128);
647 SSMR3PutU8(pSSM, pThis->cmos_index);
648
649 SSMR3PutS32(pSSM, pThis->current_tm.tm_sec);
650 SSMR3PutS32(pSSM, pThis->current_tm.tm_min);
651 SSMR3PutS32(pSSM, pThis->current_tm.tm_hour);
652 SSMR3PutS32(pSSM, pThis->current_tm.tm_wday);
653 SSMR3PutS32(pSSM, pThis->current_tm.tm_mday);
654 SSMR3PutS32(pSSM, pThis->current_tm.tm_mon);
655 SSMR3PutS32(pSSM, pThis->current_tm.tm_year);
656
657 TMR3TimerSave(pThis->CTX_SUFF(pPeriodicTimer), pSSM);
658
659 SSMR3PutS64(pSSM, pThis->next_periodic_time);
660
661 SSMR3PutS64(pSSM, pThis->next_second_time);
662 TMR3TimerSave(pThis->CTX_SUFF(pSecondTimer), pSSM);
663 TMR3TimerSave(pThis->CTX_SUFF(pSecondTimer2), pSSM);
664
665 return SSMR3PutBool(pSSM, pThis->fDisabledByHpet);
666}
667
668
669/**
670 * @copydoc FNSSMDEVLOADEXEC
671 */
672static DECLCALLBACK(int) rtcLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
673{
674 RTCState *pThis = PDMINS_2_DATA(pDevIns, RTCState *);
675 int rc;
676
677 if ( uVersion != RTC_SAVED_STATE_VERSION
678 && uVersion != RTC_SAVED_STATE_VERSION_VBOX_31
679 && uVersion != RTC_SAVED_STATE_VERSION_VBOX_30)
680 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
681
682 /* The config. */
683 if (uVersion > RTC_SAVED_STATE_VERSION_VBOX_30)
684 {
685 uint8_t u8Irq;
686 rc = SSMR3GetU8(pSSM, &u8Irq); AssertRCReturn(rc, rc);
687 if (u8Irq != pThis->irq)
688 return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Config mismatch - u8Irq: saved=%#x config=%#x"), u8Irq, pThis->irq);
689
690 RTIOPORT IOPortBase;
691 rc = SSMR3GetIOPort(pSSM, &IOPortBase); AssertRCReturn(rc, rc);
692 if (IOPortBase != pThis->IOPortBase)
693 return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Config mismatch - IOPortBase: saved=%RTiop config=%RTiop"), IOPortBase, pThis->IOPortBase);
694
695 bool fUTC;
696 rc = SSMR3GetBool(pSSM, &fUTC); AssertRCReturn(rc, rc);
697 if (fUTC != pThis->fUTC)
698 LogRel(("RTC: Config mismatch - fUTC: saved=%RTbool config=%RTbool\n", fUTC, pThis->fUTC));
699 }
700
701 if (uPass != SSM_PASS_FINAL)
702 return VINF_SUCCESS;
703
704 /* The state. */
705 SSMR3GetMem(pSSM, pThis->cmos_data, 128);
706 SSMR3GetU8(pSSM, &pThis->cmos_index);
707
708 SSMR3GetS32(pSSM, &pThis->current_tm.tm_sec);
709 SSMR3GetS32(pSSM, &pThis->current_tm.tm_min);
710 SSMR3GetS32(pSSM, &pThis->current_tm.tm_hour);
711 SSMR3GetS32(pSSM, &pThis->current_tm.tm_wday);
712 SSMR3GetS32(pSSM, &pThis->current_tm.tm_mday);
713 SSMR3GetS32(pSSM, &pThis->current_tm.tm_mon);
714 SSMR3GetS32(pSSM, &pThis->current_tm.tm_year);
715
716 TMR3TimerLoad(pThis->CTX_SUFF(pPeriodicTimer), pSSM);
717
718 SSMR3GetS64(pSSM, &pThis->next_periodic_time);
719
720 SSMR3GetS64(pSSM, &pThis->next_second_time);
721 TMR3TimerLoad(pThis->CTX_SUFF(pSecondTimer), pSSM);
722 TMR3TimerLoad(pThis->CTX_SUFF(pSecondTimer2), pSSM);
723
724 if (uVersion > RTC_SAVED_STATE_VERSION_VBOX_31)
725 SSMR3GetBool(pSSM, &pThis->fDisabledByHpet);
726
727 int period_code = pThis->cmos_data[RTC_REG_A] & 0x0f;
728 if ( period_code != 0
729 && (pThis->cmos_data[RTC_REG_B] & REG_B_PIE)) {
730 if (period_code <= 2)
731 period_code += 7;
732 int period = 1 << (period_code - 1);
733 LogRel(("RTC: period=%#x (%d) %u Hz (restore)\n", period, period, _32K / period));
734 pThis->CurPeriod = period;
735 } else {
736 LogRel(("RTC: stopped the periodic timer (restore)\n"));
737 pThis->CurPeriod = 0;
738 }
739 pThis->cRelLogEntries = 0;
740
741 return VINF_SUCCESS;
742}
743
744
745/* -=-=-=-=-=- PDM Interface provided by the RTC device -=-=-=-=-=- */
746
747/**
748 * Calculate and update the standard CMOS checksum.
749 *
750 * @param pThis Pointer to the RTC state data.
751 */
752static void rtcCalcCRC(RTCState *pThis)
753{
754 uint16_t u16;
755 unsigned i;
756
757 for (i = RTC_CRC_START, u16 = 0; i <= RTC_CRC_LAST; i++)
758 u16 += pThis->cmos_data[i];
759 pThis->cmos_data[RTC_CRC_LOW] = u16 & 0xff;
760 pThis->cmos_data[RTC_CRC_HIGH] = (u16 >> 8) & 0xff;
761}
762
763
764/**
765 * Write to a CMOS register and update the checksum if necessary.
766 *
767 * @returns VBox status code.
768 * @param pDevIns Device instance of the RTC.
769 * @param iReg The CMOS register index.
770 * @param u8Value The CMOS register value.
771 */
772static DECLCALLBACK(int) rtcCMOSWrite(PPDMDEVINS pDevIns, unsigned iReg, uint8_t u8Value)
773{
774 RTCState *pThis = PDMINS_2_DATA(pDevIns, RTCState *);
775 if (iReg < RT_ELEMENTS(pThis->cmos_data))
776 {
777 pThis->cmos_data[iReg] = u8Value;
778
779 /* does it require checksum update? */
780 if ( iReg >= RTC_CRC_START
781 && iReg <= RTC_CRC_LAST)
782 rtcCalcCRC(pThis);
783
784 return VINF_SUCCESS;
785 }
786 AssertMsgFailed(("iReg=%d\n", iReg));
787 return VERR_INVALID_PARAMETER;
788}
789
790
791/**
792 * Read a CMOS register.
793 *
794 * @returns VBox status code.
795 * @param pDevIns Device instance of the RTC.
796 * @param iReg The CMOS register index.
797 * @param pu8Value Where to store the CMOS register value.
798 */
799static DECLCALLBACK(int) rtcCMOSRead(PPDMDEVINS pDevIns, unsigned iReg, uint8_t *pu8Value)
800{
801 RTCState *pThis = PDMINS_2_DATA(pDevIns, RTCState *);
802 if (iReg < RT_ELEMENTS(pThis->cmos_data))
803 {
804 *pu8Value = pThis->cmos_data[iReg];
805 return VINF_SUCCESS;
806 }
807 AssertMsgFailed(("iReg=%d\n", iReg));
808 return VERR_INVALID_PARAMETER;
809}
810
811
812/* -=-=-=-=-=- based on bits from pc.c -=-=-=-=-=- */
813
814/** @copydoc FNPDMDEVINITCOMPLETE */
815static DECLCALLBACK(int) rtcInitComplete(PPDMDEVINS pDevIns)
816{
817 /** @todo this should be (re)done at power on if we didn't load a state... */
818 RTCState *pThis = PDMINS_2_DATA(pDevIns, RTCState *);
819
820 /*
821 * Set the CMOS date/time.
822 */
823 RTTIMESPEC Now;
824 PDMDevHlpTMUtcNow(pDevIns, &Now);
825 RTTIME Time;
826 if (pThis->fUTC)
827 RTTimeExplode(&Time, &Now);
828 else
829 RTTimeLocalExplode(&Time, &Now);
830
831 struct my_tm Tm;
832 memset(&Tm, 0, sizeof(Tm));
833 Tm.tm_year = Time.i32Year - 1900;
834 Tm.tm_mon = Time.u8Month - 1;
835 Tm.tm_mday = Time.u8MonthDay;
836 Tm.tm_wday = (Time.u8WeekDay + 1 + 7) % 7; /* 0 = monday -> sunday */
837 Tm.tm_yday = Time.u16YearDay - 1;
838 Tm.tm_hour = Time.u8Hour;
839 Tm.tm_min = Time.u8Minute;
840 Tm.tm_sec = Time.u8Second;
841
842 rtc_set_date(pThis, &Tm);
843
844 int iYear = to_bcd(pThis, (Tm.tm_year / 100) + 19); /* tm_year is 1900 based */
845 rtc_set_memory(pThis, 0x32, iYear); /* 32h - Century Byte (BCD value for the century */
846 rtc_set_memory(pThis, 0x37, iYear); /* 37h - (IBM PS/2) Date Century Byte */
847
848 /*
849 * Recalculate the checksum just in case.
850 */
851 rtcCalcCRC(pThis);
852
853 Log(("CMOS: \n%16.128Rhxd\n", pThis->cmos_data));
854 return VINF_SUCCESS;
855}
856
857
858/* -=-=-=-=-=- real code -=-=-=-=-=- */
859
860/**
861 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
862 */
863static DECLCALLBACK(void *) rtcQueryInterface(PPDMIBASE pInterface, const char *pszIID)
864{
865 PPDMDEVINS pDevIns = RT_FROM_MEMBER(pInterface, PDMDEVINS, IBase);
866 RTCState *pThis = PDMINS_2_DATA(pDevIns, RTCState *);
867 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDevIns->IBase);
868 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHPETLEGACYNOTIFY, &pThis->IHpetLegacyNotify);
869 return NULL;
870}
871
872
873/**
874 * @interface_method_impl{PDMIHPETLEGACYNOTIFY,pfnModeChanged}
875 */
876static DECLCALLBACK(void) rtcHpetLegacyNotify_ModeChanged(PPDMIHPETLEGACYNOTIFY pInterface, bool fActivated)
877{
878 RTCState *pThis = RT_FROM_MEMBER(pInterface, RTCState, IHpetLegacyNotify);
879 pThis->fDisabledByHpet = fActivated;
880}
881
882
883/**
884 * @copydoc
885 */
886static DECLCALLBACK(void) rtcRelocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
887{
888 RTCState *pThis = PDMINS_2_DATA(pDevIns, RTCState *);
889
890 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
891 pThis->pPeriodicTimerRC = TMTimerRCPtr(pThis->pPeriodicTimerR3);
892 pThis->pSecondTimerRC = TMTimerRCPtr(pThis->pSecondTimerR3);
893 pThis->pSecondTimer2RC = TMTimerRCPtr(pThis->pSecondTimer2R3);
894}
895
896
897/**
898 * @interface_method_impl{PDMDEVREG,pfnConstruct}
899 */
900static DECLCALLBACK(int) rtcConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
901{
902 RTCState *pThis = PDMINS_2_DATA(pDevIns, RTCState *);
903 int rc;
904 Assert(iInstance == 0);
905
906 /*
907 * Validate configuration.
908 */
909 if (!CFGMR3AreValuesValid(pCfg,
910 "Irq\0"
911 "Base\0"
912 "UseUTC\0"
913 "GCEnabled\0"
914 "R0Enabled\0"))
915 return VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES;
916
917 /*
918 * Init the data.
919 */
920 uint8_t u8Irq;
921 rc = CFGMR3QueryU8Def(pCfg, "Irq", &u8Irq, 8);
922 if (RT_FAILURE(rc))
923 return PDMDEV_SET_ERROR(pDevIns, rc,
924 N_("Configuration error: Querying \"Irq\" as a uint8_t failed"));
925 pThis->irq = u8Irq;
926
927 rc = CFGMR3QueryPortDef(pCfg, "Base", &pThis->IOPortBase, 0x70);
928 if (RT_FAILURE(rc))
929 return PDMDEV_SET_ERROR(pDevIns, rc,
930 N_("Configuration error: Querying \"Base\" as a RTIOPORT failed"));
931
932 rc = CFGMR3QueryBoolDef(pCfg, "UseUTC", &pThis->fUTC, false);
933 if (RT_FAILURE(rc))
934 return PDMDEV_SET_ERROR(pDevIns, rc,
935 N_("Configuration error: Querying \"UseUTC\" as a bool failed"));
936
937 bool fGCEnabled;
938 rc = CFGMR3QueryBoolDef(pCfg, "GCEnabled", &fGCEnabled, true);
939 if (RT_FAILURE(rc))
940 return PDMDEV_SET_ERROR(pDevIns, rc,
941 N_("Configuration error: failed to read GCEnabled as boolean"));
942
943 bool fR0Enabled;
944 rc = CFGMR3QueryBoolDef(pCfg, "R0Enabled", &fR0Enabled, true);
945 if (RT_FAILURE(rc))
946 return PDMDEV_SET_ERROR(pDevIns, rc,
947 N_("Configuration error: failed to read R0Enabled as boolean"));
948
949 Log(("RTC: Irq=%#x Base=%#x fGCEnabled=%RTbool fR0Enabled=%RTbool\n",
950 u8Irq, pThis->IOPortBase, fGCEnabled, fR0Enabled));
951
952
953 pThis->pDevInsR3 = pDevIns;
954 pThis->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
955 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
956 pThis->cmos_data[RTC_REG_A] = 0x26;
957 pThis->cmos_data[RTC_REG_B] = 0x02;
958 pThis->cmos_data[RTC_REG_C] = 0x00;
959 pThis->cmos_data[RTC_REG_D] = 0x80;
960 pThis->RtcReg.u32Version = PDM_RTCREG_VERSION;
961 pThis->RtcReg.pfnRead = rtcCMOSRead;
962 pThis->RtcReg.pfnWrite = rtcCMOSWrite;
963 pThis->fDisabledByHpet = false;
964
965 /* IBase */
966 pDevIns->IBase.pfnQueryInterface = rtcQueryInterface;
967 /* IHpetLegacyNotify */
968 pThis->IHpetLegacyNotify.pfnModeChanged = rtcHpetLegacyNotify_ModeChanged;
969
970 /*
971 * Create timers, arm them, register I/O Ports and save state.
972 */
973 rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL_SYNC, rtcTimerPeriodic, pThis,
974 TMTIMER_FLAGS_DEFAULT_CRIT_SECT, "MC146818 RTC/CMOS - Periodic",
975 &pThis->pPeriodicTimerR3);
976 if (RT_FAILURE(rc))
977 return rc;
978 pThis->pPeriodicTimerR0 = TMTimerR0Ptr(pThis->pPeriodicTimerR3);
979 pThis->pPeriodicTimerRC = TMTimerRCPtr(pThis->pPeriodicTimerR3);
980
981 rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL_SYNC, rtcTimerSecond, pThis,
982 TMTIMER_FLAGS_DEFAULT_CRIT_SECT, "MC146818 RTC/CMOS - Second",
983 &pThis->pSecondTimerR3);
984 if (RT_FAILURE(rc))
985 return rc;
986 pThis->pSecondTimerR0 = TMTimerR0Ptr(pThis->pSecondTimerR3);
987 pThis->pSecondTimerRC = TMTimerRCPtr(pThis->pSecondTimerR3);
988
989 rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL_SYNC, rtcTimerSecond2, pThis,
990 TMTIMER_FLAGS_DEFAULT_CRIT_SECT, "MC146818 RTC/CMOS - Second2",
991 &pThis->pSecondTimer2R3);
992 if (RT_FAILURE(rc))
993 return rc;
994 pThis->pSecondTimer2R0 = TMTimerR0Ptr(pThis->pSecondTimer2R3);
995 pThis->pSecondTimer2RC = TMTimerRCPtr(pThis->pSecondTimer2R3);
996 pThis->next_second_time = TMTimerGet(pThis->CTX_SUFF(pSecondTimer2))
997 + (TMTimerGetFreq(pThis->CTX_SUFF(pSecondTimer2)) * 99) / 100;
998 rc = TMTimerSet(pThis->CTX_SUFF(pSecondTimer2), pThis->next_second_time);
999 if (RT_FAILURE(rc))
1000 return rc;
1001
1002 rc = PDMDevHlpIOPortRegister(pDevIns, pThis->IOPortBase, 2, NULL,
1003 rtcIOPortWrite, rtcIOPortRead, NULL, NULL, "MC146818 RTC/CMOS");
1004 if (RT_FAILURE(rc))
1005 return rc;
1006 if (fGCEnabled)
1007 {
1008 rc = PDMDevHlpIOPortRegisterRC(pDevIns, pThis->IOPortBase, 2, 0,
1009 "rtcIOPortWrite", "rtcIOPortRead", NULL, NULL, "MC146818 RTC/CMOS");
1010 if (RT_FAILURE(rc))
1011 return rc;
1012 }
1013 if (fR0Enabled)
1014 {
1015 rc = PDMDevHlpIOPortRegisterR0(pDevIns, pThis->IOPortBase, 2, 0,
1016 "rtcIOPortWrite", "rtcIOPortRead", NULL, NULL, "MC146818 RTC/CMOS");
1017 if (RT_FAILURE(rc))
1018 return rc;
1019 }
1020
1021 rc = PDMDevHlpSSMRegister3(pDevIns, RTC_SAVED_STATE_VERSION, sizeof(*pThis), rtcLiveExec, rtcSaveExec, rtcLoadExec);
1022 if (RT_FAILURE(rc))
1023 return rc;
1024
1025 /*
1026 * Register ourselves as the RTC/CMOS with PDM.
1027 */
1028 rc = PDMDevHlpRTCRegister(pDevIns, &pThis->RtcReg, &pThis->pRtcHlpR3);
1029 if (RT_FAILURE(rc))
1030 return rc;
1031
1032 return VINF_SUCCESS;
1033}
1034
1035
1036/**
1037 * The device registration structure.
1038 */
1039const PDMDEVREG g_DeviceMC146818 =
1040{
1041 /* u32Version */
1042 PDM_DEVREG_VERSION,
1043 /* szName */
1044 "mc146818",
1045 /* szRCMod */
1046 "VBoxDDGC.gc",
1047 /* szR0Mod */
1048 "VBoxDDR0.r0",
1049 /* pszDescription */
1050 "Motorola MC146818 RTC/CMOS Device.",
1051 /* fFlags */
1052 PDM_DEVREG_FLAGS_HOST_BITS_DEFAULT | PDM_DEVREG_FLAGS_GUEST_BITS_32_64 | PDM_DEVREG_FLAGS_PAE36 | PDM_DEVREG_FLAGS_RC | PDM_DEVREG_FLAGS_R0,
1053 /* fClass */
1054 PDM_DEVREG_CLASS_RTC,
1055 /* cMaxInstances */
1056 1,
1057 /* cbInstance */
1058 sizeof(RTCState),
1059 /* pfnConstruct */
1060 rtcConstruct,
1061 /* pfnDestruct */
1062 NULL,
1063 /* pfnRelocate */
1064 rtcRelocate,
1065 /* pfnIOCtl */
1066 NULL,
1067 /* pfnPowerOn */
1068 NULL,
1069 /* pfnReset */
1070 NULL,
1071 /* pfnSuspend */
1072 NULL,
1073 /* pfnResume */
1074 NULL,
1075 /* pfnAttach */
1076 NULL,
1077 /* pfnDetach */
1078 NULL,
1079 /* pfnQueryInterface */
1080 NULL,
1081 /* pfnInitComplete */
1082 rtcInitComplete,
1083 /* pfnPowerOff */
1084 NULL,
1085 /* pfnSoftReset */
1086 NULL,
1087 /* u32VersionEnd */
1088 PDM_DEVREG_VERSION
1089};
1090
1091#endif /* IN_RING3 */
1092#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
1093
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette