VirtualBox

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

Last change on this file since 1939 was 1912, checked in by vboxsync, 18 years ago

muldiv64() => ASMMultU64ByU32DivByU32(); dma.c => DevDMA.cpp

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

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