VirtualBox

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

Last change on this file since 35853 was 35353, checked in by vboxsync, 14 years ago

Move the misc files the in src/VBox/Devices/ directory into a build/ subdirectory, changing their names to match the target module.

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