VirtualBox

source: vbox/trunk/src/VBox/Devices/PC/DevHPET.cpp@ 86367

Last change on this file since 86367 was 82968, checked in by vboxsync, 5 years ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 56.0 KB
Line 
1/* $Id: DevHPET.cpp 82968 2020-02-04 10:35:17Z vboxsync $ */
2/** @file
3 * HPET virtual device - High Precision Event Timer emulation.
4 */
5
6/*
7 * Copyright (C) 2009-2020 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 implementation is based on the (generic) Intel IA-PC HPET specification
19 * and the Intel ICH9 datasheet.
20 */
21
22
23/*********************************************************************************************************************************
24* Header Files *
25*********************************************************************************************************************************/
26#define LOG_GROUP LOG_GROUP_DEV_HPET
27#include <VBox/vmm/pdmdev.h>
28#include <VBox/vmm/stam.h>
29#include <VBox/log.h>
30#include <iprt/assert.h>
31#include <iprt/asm-math.h>
32#include <iprt/string.h>
33
34#include "VBoxDD.h"
35
36
37/*********************************************************************************************************************************
38* Defined Constants And Macros *
39*********************************************************************************************************************************/
40/*
41 * Current limitations:
42 * - not entirely correct time of interrupt, i.e. never
43 * schedule interrupt earlier than in 1ms
44 * - statistics not implemented
45 * - level-triggered mode not implemented
46 */
47
48/** Base address for MMIO.
49 * On ICH9, it is 0xFED0x000 where 'x' is 0-3, default 0. We do not support
50 * relocation as the platform firmware is responsible for configuring the
51 * HPET base address and the OS isn't expected to move it.
52 * WARNING: This has to match the ACPI tables! */
53#define HPET_BASE 0xfed00000
54
55/** HPET reserves a 1K range. */
56#define HPET_BAR_SIZE 0x1000
57
58/** The number of timers for PIIX4 / PIIX3. */
59#define HPET_NUM_TIMERS_PIIX 3 /* Minimal implementation. */
60/** The number of timers for ICH9. */
61#define HPET_NUM_TIMERS_ICH9 4
62
63/** HPET clock period for PIIX4 / PIIX3.
64 * 10000000 femtoseconds == 10ns.
65 */
66#define HPET_CLK_PERIOD_PIIX UINT32_C(10000000)
67
68/** HPET clock period for ICH9.
69 * 69841279 femtoseconds == 69.84 ns (1 / 14.31818MHz).
70 */
71#define HPET_CLK_PERIOD_ICH9 UINT32_C(69841279)
72
73/**
74 * Femtosecods in a nanosecond
75 */
76#define FS_PER_NS 1000000
77
78/**
79 * Femtoseconds in a day. Still fits within int64_t.
80 */
81#define FS_PER_DAY (1000000LL * 60 * 60 * 24 * FS_PER_NS)
82
83/**
84 * Number of HPET ticks in 100 years, ICH9 frequency.
85 */
86#define HPET_TICKS_IN_100YR_ICH9 (FS_PER_DAY / HPET_CLK_PERIOD_ICH9 * 365 * 100)
87
88/**
89 * Number of HPET ticks in 100 years, made-up PIIX frequency.
90 */
91#define HPET_TICKS_IN_100YR_PIIX (FS_PER_DAY / HPET_CLK_PERIOD_PIIX * 365 * 100)
92
93/** @name Interrupt type
94 * @{ */
95#define HPET_TIMER_TYPE_LEVEL (1 << 1)
96#define HPET_TIMER_TYPE_EDGE (0 << 1)
97/** @} */
98
99/** @name Delivery mode
100 * @{ */
101#define HPET_TIMER_DELIVERY_APIC 0 /**< Delivery through APIC. */
102#define HPET_TIMER_DELIVERY_FSB 1 /**< Delivery through FSB. */
103/** @} */
104
105#define HPET_TIMER_CAP_FSB_INT_DEL (1 << 15)
106#define HPET_TIMER_CAP_PER_INT (1 << 4)
107
108#define HPET_CFG_ENABLE 0x001 /**< ENABLE_CNF */
109#define HPET_CFG_LEGACY 0x002 /**< LEG_RT_CNF */
110
111/** @name Register offsets in HPET space.
112 * @{ */
113#define HPET_ID 0x000 /**< Device ID. */
114#define HPET_PERIOD 0x004 /**< Clock period in femtoseconds. */
115#define HPET_CFG 0x010 /**< Configuration register. */
116#define HPET_STATUS 0x020 /**< Status register. */
117#define HPET_COUNTER 0x0f0 /**< Main HPET counter. */
118/** @} */
119
120/** @name Timer N offsets (within each timer's space).
121 * @{ */
122#define HPET_TN_CFG 0x000 /**< Timer N configuration. */
123#define HPET_TN_CMP 0x008 /**< Timer N comparator. */
124#define HPET_TN_ROUTE 0x010 /**< Timer N interrupt route. */
125/** @} */
126
127#define HPET_CFG_WRITE_MASK 0x3
128
129#define HPET_TN_INT_TYPE RT_BIT_64(1)
130#define HPET_TN_ENABLE RT_BIT_64(2)
131#define HPET_TN_PERIODIC RT_BIT_64(3)
132#define HPET_TN_PERIODIC_CAP RT_BIT_64(4)
133#define HPET_TN_SIZE_CAP RT_BIT_64(5)
134#define HPET_TN_SETVAL RT_BIT_64(6)
135#define HPET_TN_32BIT RT_BIT_64(8)
136#define HPET_TN_INT_ROUTE_MASK UINT64_C(0x3e00)
137#define HPET_TN_CFG_WRITE_MASK UINT64_C(0x3e46)
138#define HPET_TN_INT_ROUTE_SHIFT 9
139#define HPET_TN_INT_ROUTE_CAP_SHIFT 32
140
141#define HPET_TN_CFG_BITS_READONLY_OR_RESERVED 0xffff80b1U
142
143/** Extract the timer count from the capabilities. */
144#define HPET_CAP_GET_TIMERS(a_u32) ( ((a_u32) >> 8) & 0x1f )
145
146/** The version of the saved state. */
147#define HPET_SAVED_STATE_VERSION 2
148/** Empty saved state */
149#define HPET_SAVED_STATE_VERSION_EMPTY 1
150
151
152/**
153 * Acquires the HPET lock or returns.
154 */
155#define DEVHPET_LOCK_RETURN(a_pDevIns, a_pThis, a_rcBusy) \
156 do { \
157 int rcLock = PDMDevHlpCritSectEnter((a_pDevIns), &(a_pThis)->CritSect, (a_rcBusy)); \
158 if (RT_LIKELY(rcLock == VINF_SUCCESS)) \
159 { /* likely */ } \
160 else \
161 return rcLock; \
162 } while (0)
163
164/**
165 * Releases the HPET lock.
166 */
167#define DEVHPET_UNLOCK(a_pDevIns, a_pThis) \
168 do { PDMDevHlpCritSectLeave((a_pDevIns), &(a_pThis)->CritSect); } while (0)
169
170
171/**
172 * Acquires the TM lock and HPET lock, returns on failure.
173 * @todo r=bird: Aren't the timers using the same critsect?!?
174 */
175#define DEVHPET_LOCK_BOTH_RETURN(a_pDevIns, a_pThis, a_rcBusy) \
176 do { \
177 VBOXSTRICTRC rcLock = PDMDevHlpTimerLockClock2((a_pDevIns), (a_pThis)->aTimers[0].hTimer, &(a_pThis)->CritSect, (a_rcBusy)); \
178 if (RT_LIKELY(rcLock == VINF_SUCCESS)) \
179 { /* likely */ } \
180 else \
181 return rcLock; \
182 } while (0)
183
184
185/**
186 * Releases the HPET lock and TM lock.
187 */
188#define DEVHPET_UNLOCK_BOTH(a_pDevIns, a_pThis) \
189 PDMDevHlpTimerUnlockClock2((a_pDevIns), (a_pThis)->aTimers[0].hTimer, &(a_pThis)->CritSect)
190
191
192/*********************************************************************************************************************************
193* Structures and Typedefs *
194*********************************************************************************************************************************/
195/**
196 * A HPET timer.
197 */
198typedef struct HPETTIMER
199{
200 /** The HPET timer. */
201 TMTIMERHANDLE hTimer;
202
203 /** Timer index. */
204 uint8_t idxTimer;
205 /** Wrap. */
206 uint8_t u8Wrap;
207 /** Explicit padding. */
208 uint8_t abPadding[6];
209
210 /** @name Memory-mapped, software visible timer registers.
211 * @{ */
212 /** Configuration/capabilities. */
213 uint64_t u64Config;
214 /** Comparator. */
215 uint64_t u64Cmp;
216 /** FSB route, not supported now. */
217 uint64_t u64Fsb;
218 /** @} */
219
220 /** @name Hidden register state.
221 * @{ */
222 /** Last value written to comparator. */
223 uint64_t u64Period;
224 /** @} */
225
226 STAMCOUNTER StatSetIrq;
227 STAMCOUNTER StatSetTimer;
228} HPETTIMER;
229AssertCompileMemberAlignment(HPETTIMER, u64Config, sizeof(uint64_t));
230/** Pointer to the shared state of an HPET timer. */
231typedef HPETTIMER *PHPETTIMER;
232/** Const pointer to the shared state of an HPET timer. */
233typedef HPETTIMER const *PCHPETTIMER;
234
235
236/**
237 * The shared HPET device state.
238 */
239typedef struct HPET
240{
241 /** Timer structures. */
242 HPETTIMER aTimers[RT_MAX(HPET_NUM_TIMERS_PIIX, HPET_NUM_TIMERS_ICH9)];
243
244 /** Offset realtive to the virtual sync clock. */
245 uint64_t u64HpetOffset;
246
247 /** @name Memory-mapped, software visible registers
248 * @{ */
249 /** Capabilities. */
250 uint32_t u32Capabilities;
251 /** HPET_PERIOD - . */
252 uint32_t u32Period;
253 /** Configuration. */
254 uint64_t u64HpetConfig;
255 /** Interrupt status register. */
256 uint64_t u64Isr;
257 /** Main counter. */
258 uint64_t u64HpetCounter;
259 /** @} */
260
261 /** Whether we emulate ICH9 HPET (different frequency & timer count). */
262 bool fIch9;
263 /** Size alignment padding. */
264 uint8_t abPadding0[7];
265
266 /** Global device lock. */
267 PDMCRITSECT CritSect;
268
269 /** The handle of the MMIO region. */
270 IOMMMIOHANDLE hMmio;
271
272 STAMCOUNTER StatCounterRead4Byte;
273 STAMCOUNTER StatCounterRead8Byte;
274 STAMCOUNTER StatCounterWriteLow;
275 STAMCOUNTER StatCounterWriteHigh;
276} HPET;
277/** Pointer to the shared HPET device state. */
278typedef HPET *PHPET;
279/** Const pointer to the shared HPET device state. */
280typedef const HPET *PCHPET;
281
282
283/**
284 * The ring-3 specific HPET device state.
285 */
286typedef struct HPETR3
287{
288 /** The HPET helpers. */
289 PCPDMHPETHLPR3 pHpetHlp;
290} HPETR3;
291/** Pointer to the ring-3 specific HPET device state. */
292typedef HPETR3 *PHPETR3;
293
294
295/**
296 * The ring-0 specific HPET device state.
297 */
298typedef struct HPETR0
299{
300 /** The HPET helpers. */
301 PCPDMHPETHLPR0 pHpetHlp;
302} HPETR0;
303/** Pointer to the ring-0 specific HPET device state. */
304typedef HPETR0 *PHPETR0;
305
306
307/**
308 * The raw-mode specific HPET device state.
309 */
310typedef struct HPETRC
311{
312 /** The HPET helpers. */
313 PCPDMHPETHLPRC pHpetHlp;
314} HPETRC;
315/** Pointer to the raw-mode specific HPET device state. */
316typedef HPETRC *PHPETRC;
317
318
319/** The HPET device state specific to the current context. */
320typedef CTX_SUFF(HPET) HPETCC;
321/** Pointer to the HPET device state specific to the current context. */
322typedef CTX_SUFF(PHPET) PHPETCC;
323
324
325#ifndef VBOX_DEVICE_STRUCT_TESTCASE
326
327
328DECLINLINE(bool) hpet32bitTimer(PHPETTIMER pHpetTimer)
329{
330 uint64_t u64Cfg = pHpetTimer->u64Config;
331 return ((u64Cfg & HPET_TN_SIZE_CAP) == 0) || ((u64Cfg & HPET_TN_32BIT) != 0);
332}
333
334DECLINLINE(uint64_t) hpetInvalidValue(PHPETTIMER pHpetTimer)
335{
336 return hpet32bitTimer(pHpetTimer) ? UINT32_MAX : UINT64_MAX;
337}
338
339DECLINLINE(uint64_t) hpetTicksToNs(PHPET pThis, uint64_t value)
340{
341 return ASMMultU64ByU32DivByU32(value, pThis->u32Period, FS_PER_NS);
342}
343
344DECLINLINE(uint64_t) nsToHpetTicks(PCHPET pThis, uint64_t u64Value)
345{
346 return ASMMultU64ByU32DivByU32(u64Value, FS_PER_NS, RT_MAX(pThis->u32Period, 1 /* no div/zero */));
347}
348
349DECLINLINE(uint64_t) hpetGetTicks(PPDMDEVINS pDevIns, PCHPET pThis)
350{
351 /*
352 * We can use any timer to get current time, they all go with the same speed.
353 */
354 return nsToHpetTicks(pThis, PDMDevHlpTimerGet(pDevIns, pThis->aTimers[0].hTimer) + pThis->u64HpetOffset);
355}
356
357DECLINLINE(uint64_t) hpetUpdateMasked(uint64_t u64NewValue, uint64_t u64OldValue, uint64_t u64Mask)
358{
359 u64NewValue &= u64Mask;
360 u64NewValue |= (u64OldValue & ~u64Mask);
361 return u64NewValue;
362}
363
364DECLINLINE(bool) hpetBitJustSet(uint64_t u64OldValue, uint64_t u64NewValue, uint64_t u64Mask)
365{
366 return !(u64OldValue & u64Mask)
367 && !!(u64NewValue & u64Mask);
368}
369
370DECLINLINE(bool) hpetBitJustCleared(uint64_t u64OldValue, uint64_t u64NewValue, uint64_t u64Mask)
371{
372 return !!(u64OldValue & u64Mask)
373 && !(u64NewValue & u64Mask);
374}
375
376DECLINLINE(uint64_t) hpetComputeDiff(PHPETTIMER pHpetTimer, uint64_t u64Now)
377{
378
379 if (hpet32bitTimer(pHpetTimer))
380 {
381 uint32_t u32Diff;
382
383 u32Diff = (uint32_t)pHpetTimer->u64Cmp - (uint32_t)u64Now;
384 u32Diff = (int32_t)u32Diff > 0 ? u32Diff : (uint32_t)0;
385 return (uint64_t)u32Diff;
386 }
387 uint64_t u64Diff;
388
389 u64Diff = pHpetTimer->u64Cmp - u64Now;
390 u64Diff = (int64_t)u64Diff > 0 ? u64Diff : (uint64_t)0;
391 return u64Diff;
392}
393
394
395static void hpetAdjustComparator(PHPETTIMER pHpetTimer, uint64_t u64Now)
396{
397 if ((pHpetTimer->u64Config & HPET_TN_PERIODIC))
398 {
399 uint64_t u64Period = pHpetTimer->u64Period;
400 if (u64Period)
401 {
402 uint64_t cPeriods = (u64Now - pHpetTimer->u64Cmp) / u64Period;
403 pHpetTimer->u64Cmp += (cPeriods + 1) * u64Period;
404 }
405 }
406}
407
408
409/**
410 * Sets the frequency hint if it's a periodic timer.
411 *
412 * @param pDevIns The device instance.
413 * @param pThis The shared HPET state.
414 * @param pHpetTimer The timer.
415 */
416DECLINLINE(void) hpetTimerSetFrequencyHint(PPDMDEVINS pDevIns, PHPET pThis, PHPETTIMER pHpetTimer)
417{
418 if (pHpetTimer->u64Config & HPET_TN_PERIODIC)
419 {
420 uint64_t const u64Period = pHpetTimer->u64Period;
421 uint32_t const u32Freq = pThis->u32Period;
422 if (u64Period > 0 && u64Period < u32Freq)
423 PDMDevHlpTimerSetFrequencyHint(pDevIns, pHpetTimer->hTimer, u32Freq / (uint32_t)u64Period);
424 }
425}
426
427
428static void hpetProgramTimer(PPDMDEVINS pDevIns, PHPET pThis, PHPETTIMER pHpetTimer)
429{
430 /* no wrapping on new timers */
431 pHpetTimer->u8Wrap = 0;
432
433 uint64_t u64Ticks = hpetGetTicks(pDevIns, pThis);
434 hpetAdjustComparator(pHpetTimer, u64Ticks);
435
436 uint64_t u64Diff = hpetComputeDiff(pHpetTimer, u64Ticks);
437
438 /*
439 * HPET spec says in one-shot 32-bit mode, generate an interrupt when
440 * counter wraps in addition to an interrupt with comparator match.
441 */
442 if ( hpet32bitTimer(pHpetTimer)
443 && !(pHpetTimer->u64Config & HPET_TN_PERIODIC))
444 {
445 uint32_t u32TillWrap = 0xffffffff - (uint32_t)u64Ticks + 1;
446 if (u32TillWrap < (uint32_t)u64Diff)
447 {
448 Log(("wrap on timer %d: till=%u ticks=%lld diff64=%lld\n",
449 pHpetTimer->idxTimer, u32TillWrap, u64Ticks, u64Diff));
450 u64Diff = u32TillWrap;
451 pHpetTimer->u8Wrap = 1;
452 }
453 }
454
455 /*
456 * HACK ALERT! Avoid killing VM with interrupts.
457 */
458#if 1 /** @todo HACK, rethink, may have negative impact on the guest */
459 if (u64Diff == 0)
460 u64Diff = 100000; /* 1 millisecond */
461#endif
462
463 uint64_t u64TickLimit = pThis->fIch9 ? HPET_TICKS_IN_100YR_ICH9 : HPET_TICKS_IN_100YR_PIIX;
464 if (u64Diff <= u64TickLimit)
465 {
466 Log4(("HPET: next IRQ in %lld ticks (%lld ns)\n", u64Diff, hpetTicksToNs(pThis, u64Diff)));
467 STAM_REL_COUNTER_INC(&pHpetTimer->StatSetTimer);
468 PDMDevHlpTimerSetNano(pDevIns, pHpetTimer->hTimer, hpetTicksToNs(pThis, u64Diff));
469 }
470 else
471 {
472 LogRelMax(10, ("HPET: Not scheduling an interrupt more than 100 years in the future.\n"));
473 }
474 hpetTimerSetFrequencyHint(pDevIns, pThis, pHpetTimer);
475}
476
477
478/* -=-=-=-=-=- Timer register accesses -=-=-=-=-=- */
479
480
481/**
482 * Reads a HPET timer register.
483 *
484 * @param pDevIns The device instance.
485 * @param pThis The HPET instance.
486 * @param iTimerNo The timer index.
487 * @param iTimerReg The index of the timer register to read.
488 * @param pu32Value Where to return the register value.
489 *
490 * @remarks ASSUMES the caller holds the HPET lock.
491 */
492static void hpetTimerRegRead32(PPDMDEVINS pDevIns, PCHPET pThis, uint32_t iTimerNo, uint32_t iTimerReg, uint32_t *pu32Value)
493{
494 Assert(PDMDevHlpCritSectIsOwner(pDevIns, &pThis->CritSect));
495 RT_NOREF(pDevIns);
496
497 uint32_t u32Value;
498 if ( iTimerNo < HPET_CAP_GET_TIMERS(pThis->u32Capabilities)
499 && iTimerNo < RT_ELEMENTS(pThis->aTimers) )
500 {
501 PCHPETTIMER pHpetTimer = &pThis->aTimers[iTimerNo];
502 switch (iTimerReg)
503 {
504 case HPET_TN_CFG:
505 u32Value = (uint32_t)pHpetTimer->u64Config;
506 Log(("read HPET_TN_CFG on %d: %#x\n", iTimerNo, u32Value));
507 break;
508
509 case HPET_TN_CFG + 4:
510 u32Value = (uint32_t)(pHpetTimer->u64Config >> 32);
511 Log(("read HPET_TN_CFG+4 on %d: %#x\n", iTimerNo, u32Value));
512 break;
513
514 case HPET_TN_CMP:
515 u32Value = (uint32_t)pHpetTimer->u64Cmp;
516 Log(("read HPET_TN_CMP on %d: %#x (%#llx)\n", pHpetTimer->idxTimer, u32Value, pHpetTimer->u64Cmp));
517 break;
518
519 case HPET_TN_CMP + 4:
520 u32Value = (uint32_t)(pHpetTimer->u64Cmp >> 32);
521 Log(("read HPET_TN_CMP+4 on %d: %#x (%#llx)\n", pHpetTimer->idxTimer, u32Value, pHpetTimer->u64Cmp));
522 break;
523
524 case HPET_TN_ROUTE:
525 u32Value = (uint32_t)(pHpetTimer->u64Fsb >> 32); /** @todo Looks wrong, but since it's not supported, who cares. */
526 Log(("read HPET_TN_ROUTE on %d: %#x\n", iTimerNo, u32Value));
527 break;
528
529 default:
530 {
531 LogRelMax(10, ("HPET: Invalid HPET register read %d on %d\n", iTimerReg, pHpetTimer->idxTimer));
532 u32Value = 0;
533 break;
534 }
535 }
536 }
537 else
538 {
539 LogRelMax(10, ("HPET: Using timer above configured range: %d\n", iTimerNo));
540 u32Value = 0;
541 }
542 *pu32Value = u32Value;
543}
544
545
546/**
547 * 32-bit write to a HPET timer register.
548 *
549 * @returns Strict VBox status code.
550 *
551 * @param pDevIns The device instance.
552 * @param pThis The shared HPET state.
553 * @param iTimerNo The timer being written to.
554 * @param iTimerReg The register being written to.
555 * @param u32NewValue The value being written.
556 *
557 * @remarks The caller should not hold the device lock, unless it also holds
558 * the TM lock.
559 */
560static VBOXSTRICTRC hpetTimerRegWrite32(PPDMDEVINS pDevIns, PHPET pThis, uint32_t iTimerNo,
561 uint32_t iTimerReg, uint32_t u32NewValue)
562{
563 Assert(!PDMDevHlpCritSectIsOwner(pDevIns, &pThis->CritSect) || PDMDevHlpTimerIsLockOwner(pDevIns, pThis->aTimers[0].hTimer));
564
565 if ( iTimerNo >= HPET_CAP_GET_TIMERS(pThis->u32Capabilities)
566 || iTimerNo >= RT_ELEMENTS(pThis->aTimers) ) /* Parfait - see above. */
567 {
568 LogRelMax(10, ("HPET: Using timer above configured range: %d\n", iTimerNo));
569 return VINF_SUCCESS;
570 }
571 PHPETTIMER pHpetTimer = &pThis->aTimers[iTimerNo];
572
573 switch (iTimerReg)
574 {
575 case HPET_TN_CFG:
576 {
577 DEVHPET_LOCK_RETURN(pDevIns, pThis, VINF_IOM_R3_MMIO_WRITE);
578 uint64_t u64Mask = HPET_TN_CFG_WRITE_MASK;
579
580 Log(("write HPET_TN_CFG: %d: %x\n", iTimerNo, u32NewValue));
581 if (pHpetTimer->u64Config & HPET_TN_PERIODIC_CAP)
582 u64Mask |= HPET_TN_PERIODIC;
583
584 if (pHpetTimer->u64Config & HPET_TN_SIZE_CAP)
585 u64Mask |= HPET_TN_32BIT;
586 else
587 u32NewValue &= ~HPET_TN_32BIT;
588
589 if (u32NewValue & HPET_TN_32BIT)
590 {
591 Log(("setting timer %d to 32-bit mode\n", iTimerNo));
592 pHpetTimer->u64Cmp = (uint32_t)pHpetTimer->u64Cmp;
593 pHpetTimer->u64Period = (uint32_t)pHpetTimer->u64Period;
594 }
595 if ((u32NewValue & HPET_TN_INT_TYPE) == HPET_TIMER_TYPE_LEVEL)
596 {
597 LogRelMax(10, ("HPET: Level-triggered config not yet supported\n"));
598 AssertFailed();
599 }
600
601 /* We only care about lower 32-bits so far */
602 pHpetTimer->u64Config = hpetUpdateMasked(u32NewValue, pHpetTimer->u64Config, u64Mask);
603 DEVHPET_UNLOCK(pDevIns, pThis);
604 break;
605 }
606
607 case HPET_TN_CFG + 4: /* Interrupt capabilities - read only. */
608 Log(("write HPET_TN_CFG + 4, useless\n"));
609 break;
610
611 case HPET_TN_CMP: /* lower bits of comparator register */
612 {
613 DEVHPET_LOCK_BOTH_RETURN(pDevIns, pThis, VINF_IOM_R3_MMIO_WRITE);
614 Log(("write HPET_TN_CMP on %d: %#x\n", iTimerNo, u32NewValue));
615
616 if (pHpetTimer->u64Config & HPET_TN_PERIODIC)
617 pHpetTimer->u64Period = RT_MAKE_U64(u32NewValue, RT_HI_U32(pHpetTimer->u64Period));
618 pHpetTimer->u64Cmp = RT_MAKE_U64(u32NewValue, RT_HI_U32(pHpetTimer->u64Cmp));
619 pHpetTimer->u64Config &= ~HPET_TN_SETVAL;
620 Log2(("after HPET_TN_CMP cmp=%#llx per=%#llx\n", pHpetTimer->u64Cmp, pHpetTimer->u64Period));
621
622 if (pThis->u64HpetConfig & HPET_CFG_ENABLE)
623 hpetProgramTimer(pDevIns, pThis, pHpetTimer);
624 DEVHPET_UNLOCK_BOTH(pDevIns, pThis);
625 break;
626 }
627
628 case HPET_TN_CMP + 4: /* upper bits of comparator register */
629 {
630 DEVHPET_LOCK_BOTH_RETURN(pDevIns, pThis, VINF_IOM_R3_MMIO_WRITE);
631 Log(("write HPET_TN_CMP + 4 on %d: %#x\n", iTimerNo, u32NewValue));
632 if (!hpet32bitTimer(pHpetTimer))
633 {
634 if (pHpetTimer->u64Config & HPET_TN_PERIODIC)
635 pHpetTimer->u64Period = RT_MAKE_U64(RT_LO_U32(pHpetTimer->u64Period), u32NewValue);
636 pHpetTimer->u64Cmp = RT_MAKE_U64(RT_LO_U32(pHpetTimer->u64Cmp), u32NewValue);
637
638 Log2(("after HPET_TN_CMP+4 cmp=%llx per=%llx tmr=%d\n", pHpetTimer->u64Cmp, pHpetTimer->u64Period, iTimerNo));
639
640 pHpetTimer->u64Config &= ~HPET_TN_SETVAL;
641
642 if (pThis->u64HpetConfig & HPET_CFG_ENABLE)
643 hpetProgramTimer(pDevIns, pThis, pHpetTimer);
644 }
645 DEVHPET_UNLOCK_BOTH(pDevIns, pThis);
646 break;
647 }
648
649 case HPET_TN_ROUTE:
650 Log(("write HPET_TN_ROUTE\n"));
651 break;
652
653 case HPET_TN_ROUTE + 4:
654 Log(("write HPET_TN_ROUTE + 4\n"));
655 break;
656
657 default:
658 LogRelMax(10, ("HPET: Invalid timer register write: %d\n", iTimerReg));
659 break;
660 }
661
662 return VINF_SUCCESS;
663}
664
665
666/* -=-=-=-=-=- Non-timer register accesses -=-=-=-=-=- */
667
668
669/**
670 * Read a 32-bit HPET register.
671 *
672 * @returns Strict VBox status code.
673 * @param pDevIns The device instance.
674 * @param pThis The shared HPET state.
675 * @param idxReg The register to read.
676 * @param pu32Value Where to return the register value.
677 *
678 * @remarks The caller must not own the device lock if HPET_COUNTER is read.
679 */
680static VBOXSTRICTRC hpetConfigRegRead32(PPDMDEVINS pDevIns, PHPET pThis, uint32_t idxReg, uint32_t *pu32Value)
681{
682 Assert(!PDMDevHlpCritSectIsOwner(pDevIns, &pThis->CritSect) || (idxReg != HPET_COUNTER && idxReg != HPET_COUNTER + 4));
683
684 uint32_t u32Value;
685 switch (idxReg)
686 {
687 case HPET_ID:
688 DEVHPET_LOCK_RETURN(pDevIns, pThis, VINF_IOM_R3_MMIO_READ);
689 u32Value = pThis->u32Capabilities;
690 DEVHPET_UNLOCK(pDevIns, pThis);
691 Log(("read HPET_ID: %#x\n", u32Value));
692 break;
693
694 case HPET_PERIOD:
695 DEVHPET_LOCK_RETURN(pDevIns, pThis, VINF_IOM_R3_MMIO_READ);
696 u32Value = pThis->u32Period;
697 DEVHPET_UNLOCK(pDevIns, pThis);
698 Log(("read HPET_PERIOD: %#x\n", u32Value));
699 break;
700
701 case HPET_CFG:
702 DEVHPET_LOCK_RETURN(pDevIns, pThis, VINF_IOM_R3_MMIO_READ);
703 u32Value = (uint32_t)pThis->u64HpetConfig;
704 DEVHPET_UNLOCK(pDevIns, pThis);
705 Log(("read HPET_CFG: %#x\n", u32Value));
706 break;
707
708 case HPET_CFG + 4:
709 DEVHPET_LOCK_RETURN(pDevIns, pThis, VINF_IOM_R3_MMIO_READ);
710 u32Value = (uint32_t)(pThis->u64HpetConfig >> 32);
711 DEVHPET_UNLOCK(pDevIns, pThis);
712 Log(("read of HPET_CFG + 4: %#x\n", u32Value));
713 break;
714
715 case HPET_COUNTER:
716 case HPET_COUNTER + 4:
717 {
718 STAM_REL_COUNTER_INC(&pThis->StatCounterRead4Byte);
719 DEVHPET_LOCK_BOTH_RETURN(pDevIns, pThis, VINF_IOM_R3_MMIO_READ);
720
721 uint64_t u64Ticks;
722 if (pThis->u64HpetConfig & HPET_CFG_ENABLE)
723 u64Ticks = hpetGetTicks(pDevIns, pThis);
724 else
725 u64Ticks = pThis->u64HpetCounter;
726
727 DEVHPET_UNLOCK_BOTH(pDevIns, pThis);
728
729 /** @todo is it correct? */
730 u32Value = (idxReg == HPET_COUNTER) ? (uint32_t)u64Ticks : (uint32_t)(u64Ticks >> 32);
731 Log(("read HPET_COUNTER: %s part value %x (%#llx)\n", (idxReg == HPET_COUNTER) ? "low" : "high", u32Value, u64Ticks));
732 break;
733 }
734
735 case HPET_STATUS:
736 DEVHPET_LOCK_RETURN(pDevIns, pThis, VINF_IOM_R3_MMIO_READ);
737 u32Value = (uint32_t)pThis->u64Isr;
738 DEVHPET_UNLOCK(pDevIns, pThis);
739 Log(("read HPET_STATUS: %#x\n", u32Value));
740 break;
741
742 default:
743 Log(("invalid HPET register read: %x\n", idxReg));
744 u32Value = 0;
745 break;
746 }
747
748 *pu32Value = u32Value;
749 return VINF_SUCCESS;
750}
751
752
753/**
754 * 32-bit write to a config register.
755 *
756 * @returns Strict VBox status code.
757 *
758 * @param pDevIns The device instance.
759 * @param pThis The shared HPET state.
760 * @param idxReg The register being written to.
761 * @param u32NewValue The value being written.
762 *
763 * @remarks The caller should not hold the device lock, unless it also holds
764 * the TM lock.
765 */
766static VBOXSTRICTRC hpetConfigRegWrite32(PPDMDEVINS pDevIns, PHPET pThis, uint32_t idxReg, uint32_t u32NewValue)
767{
768 Assert(!PDMDevHlpCritSectIsOwner(pDevIns, &pThis->CritSect) || PDMDevHlpTimerIsLockOwner(pDevIns, pThis->aTimers[0].hTimer));
769
770 VBOXSTRICTRC rc = VINF_SUCCESS;
771 switch (idxReg)
772 {
773 case HPET_ID:
774 case HPET_ID + 4:
775 {
776 Log(("write HPET_ID, useless\n"));
777 break;
778 }
779
780 case HPET_CFG:
781 {
782 DEVHPET_LOCK_BOTH_RETURN(pDevIns, pThis, VINF_IOM_R3_MMIO_WRITE);
783 uint32_t const iOldValue = (uint32_t)(pThis->u64HpetConfig);
784 Log(("write HPET_CFG: %x (old %x)\n", u32NewValue, iOldValue));
785
786 /*
787 * This check must be here, before actual update, as hpetLegacyMode
788 * may request retry in R3 - so we must keep state intact.
789 */
790 if ((iOldValue ^ u32NewValue) & HPET_CFG_LEGACY)
791 {
792#ifdef IN_RING3
793 PHPETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PHPETCC);
794 if (pThisCC->pHpetHlp != NULL)
795 {
796 rc = pThisCC->pHpetHlp->pfnSetLegacyMode(pDevIns, RT_BOOL(u32NewValue & HPET_CFG_LEGACY));
797 if (rc != VINF_SUCCESS)
798 {
799 DEVHPET_UNLOCK_BOTH(pDevIns, pThis);
800 break;
801 }
802 }
803#else
804 rc = VINF_IOM_R3_MMIO_WRITE;
805 DEVHPET_UNLOCK_BOTH(pDevIns, pThis);
806 break;
807#endif
808 }
809
810 pThis->u64HpetConfig = hpetUpdateMasked(u32NewValue, iOldValue, HPET_CFG_WRITE_MASK);
811
812 uint32_t const cTimers = HPET_CAP_GET_TIMERS(pThis->u32Capabilities);
813 if (hpetBitJustSet(iOldValue, u32NewValue, HPET_CFG_ENABLE))
814 {
815/** @todo Only get the time stamp once when reprogramming? */
816 /* Enable main counter and interrupt generation. */
817 uint64_t u64TickLimit = pThis->fIch9 ? HPET_TICKS_IN_100YR_ICH9 : HPET_TICKS_IN_100YR_PIIX;
818 if (pThis->u64HpetCounter <= u64TickLimit)
819 {
820 pThis->u64HpetOffset = hpetTicksToNs(pThis, pThis->u64HpetCounter)
821 - PDMDevHlpTimerGet(pDevIns, pThis->aTimers[0].hTimer);
822 }
823 else
824 {
825 LogRelMax(10, ("HPET: Counter set more than 100 years in the future, reducing.\n"));
826 pThis->u64HpetOffset = 1000000LL * 60 * 60 * 24 * 365 * 100
827 - PDMDevHlpTimerGet(pDevIns, pThis->aTimers[0].hTimer);
828 }
829 for (uint32_t i = 0; i < cTimers; i++)
830 if (pThis->aTimers[i].u64Cmp != hpetInvalidValue(&pThis->aTimers[i]))
831 hpetProgramTimer(pDevIns, pThis, &pThis->aTimers[i]);
832 }
833 else if (hpetBitJustCleared(iOldValue, u32NewValue, HPET_CFG_ENABLE))
834 {
835 /* Halt main counter and disable interrupt generation. */
836 pThis->u64HpetCounter = hpetGetTicks(pDevIns, pThis);
837 for (uint32_t i = 0; i < cTimers; i++)
838 PDMDevHlpTimerStop(pDevIns, pThis->aTimers[i].hTimer);
839 }
840
841 DEVHPET_UNLOCK_BOTH(pDevIns, pThis);
842 break;
843 }
844
845 case HPET_CFG + 4:
846 {
847 DEVHPET_LOCK_RETURN(pDevIns, pThis, VINF_IOM_R3_MMIO_WRITE);
848 pThis->u64HpetConfig = hpetUpdateMasked((uint64_t)u32NewValue << 32,
849 pThis->u64HpetConfig,
850 UINT64_C(0xffffffff00000000));
851 Log(("write HPET_CFG + 4: %x -> %#llx\n", u32NewValue, pThis->u64HpetConfig));
852 DEVHPET_UNLOCK(pDevIns, pThis);
853 break;
854 }
855
856 case HPET_STATUS:
857 {
858 DEVHPET_LOCK_RETURN(pDevIns, pThis, VINF_IOM_R3_MMIO_WRITE);
859 /* Clear ISR for all set bits in u32NewValue, see p. 14 of the HPET spec. */
860 pThis->u64Isr &= ~((uint64_t)u32NewValue);
861 Log(("write HPET_STATUS: %x -> ISR=%#llx\n", u32NewValue, pThis->u64Isr));
862 DEVHPET_UNLOCK(pDevIns, pThis);
863 break;
864 }
865
866 case HPET_STATUS + 4:
867 {
868 Log(("write HPET_STATUS + 4: %x\n", u32NewValue));
869 if (u32NewValue != 0)
870 LogRelMax(10, ("HPET: Writing HPET_STATUS + 4 with non-zero, ignored\n"));
871 break;
872 }
873
874 case HPET_COUNTER:
875 {
876 STAM_REL_COUNTER_INC(&pThis->StatCounterWriteLow);
877 DEVHPET_LOCK_RETURN(pDevIns, pThis, VINF_IOM_R3_MMIO_WRITE);
878 pThis->u64HpetCounter = RT_MAKE_U64(u32NewValue, RT_HI_U32(pThis->u64HpetCounter));
879 Log(("write HPET_COUNTER: %#x -> %llx\n", u32NewValue, pThis->u64HpetCounter));
880 DEVHPET_UNLOCK(pDevIns, pThis);
881 break;
882 }
883
884 case HPET_COUNTER + 4:
885 {
886 STAM_REL_COUNTER_INC(&pThis->StatCounterWriteHigh);
887 DEVHPET_LOCK_RETURN(pDevIns, pThis, VINF_IOM_R3_MMIO_WRITE);
888 pThis->u64HpetCounter = RT_MAKE_U64(RT_LO_U32(pThis->u64HpetCounter), u32NewValue);
889 Log(("write HPET_COUNTER + 4: %#x -> %llx\n", u32NewValue, pThis->u64HpetCounter));
890 DEVHPET_UNLOCK(pDevIns, pThis);
891 break;
892 }
893
894 default:
895 LogRelMax(10, ("HPET: Invalid HPET config write: %x\n", idxReg));
896 break;
897 }
898
899 return rc;
900}
901
902
903/* -=-=-=-=-=- MMIO callbacks -=-=-=-=-=- */
904
905
906/**
907 * @callback_method_impl{FNIOMMMIONEWREAD}
908 */
909static DECLCALLBACK(VBOXSTRICTRC) hpetMMIORead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void *pv, unsigned cb)
910{
911 HPET *pThis = PDMDEVINS_2_DATA(pDevIns, HPET*);
912 NOREF(pvUser);
913 Assert(cb == 4 || cb == 8);
914
915 LogFlow(("hpetMMIORead (%d): %RGp\n", cb, off));
916
917 VBOXSTRICTRC rc;
918 if (cb == 4)
919 {
920 /*
921 * 4-byte access.
922 */
923 if (off >= 0x100 && off < 0x400)
924 {
925 DEVHPET_LOCK_RETURN(pDevIns, pThis, VINF_IOM_R3_MMIO_READ);
926 hpetTimerRegRead32(pDevIns, pThis,
927 (uint32_t)(off - 0x100) / 0x20,
928 (uint32_t)(off - 0x100) % 0x20,
929 (uint32_t *)pv);
930 DEVHPET_UNLOCK(pDevIns, pThis);
931 rc = VINF_SUCCESS;
932 }
933 else
934 rc = hpetConfigRegRead32(pDevIns, pThis, off, (uint32_t *)pv);
935 }
936 else
937 {
938 /*
939 * 8-byte access - Split the access except for timing sensitive registers.
940 * The others assume the protection of the lock.
941 */
942 PRTUINT64U pValue = (PRTUINT64U)pv;
943 if (off == HPET_COUNTER)
944 {
945 /* When reading HPET counter we must read it in a single read,
946 to avoid unexpected time jumps on 32-bit overflow. */
947 STAM_REL_COUNTER_INC(&pThis->StatCounterRead8Byte);
948 DEVHPET_LOCK_BOTH_RETURN(pDevIns, pThis, VINF_IOM_R3_MMIO_READ);
949 if (pThis->u64HpetConfig & HPET_CFG_ENABLE)
950 pValue->u = hpetGetTicks(pDevIns, pThis);
951 else
952 pValue->u = pThis->u64HpetCounter;
953 DEVHPET_UNLOCK_BOTH(pDevIns, pThis);
954 rc = VINF_SUCCESS;
955 }
956 else
957 {
958 DEVHPET_LOCK_RETURN(pDevIns, pThis, VINF_IOM_R3_MMIO_READ);
959 if (off >= 0x100 && off < 0x400)
960 {
961 uint32_t iTimer = (uint32_t)(off - 0x100) / 0x20;
962 uint32_t iTimerReg = (uint32_t)(off - 0x100) % 0x20;
963 hpetTimerRegRead32(pDevIns, pThis, iTimer, iTimerReg, &pValue->s.Lo);
964 hpetTimerRegRead32(pDevIns, pThis, iTimer, iTimerReg + 4, &pValue->s.Hi);
965 rc = VINF_SUCCESS;
966 }
967 else
968 {
969 /* for most 8-byte accesses we just split them, happens under lock anyway. */
970 rc = hpetConfigRegRead32(pDevIns, pThis, off, &pValue->s.Lo);
971 if (rc == VINF_SUCCESS)
972 rc = hpetConfigRegRead32(pDevIns, pThis, off + 4, &pValue->s.Hi);
973 }
974 DEVHPET_UNLOCK(pDevIns, pThis);
975 }
976 }
977 return rc;
978}
979
980
981/**
982 * @callback_method_impl{FNIOMMMIONEWWRITE}
983 */
984static DECLCALLBACK(VBOXSTRICTRC) hpetMMIOWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void const *pv, unsigned cb)
985{
986 HPET *pThis = PDMDEVINS_2_DATA(pDevIns, HPET*);
987 LogFlow(("hpetMMIOWrite: cb=%u reg=%RGp val=%llx\n",
988 cb, off, cb == 4 ? *(uint32_t *)pv : cb == 8 ? *(uint64_t *)pv : 0xdeadbeef));
989 NOREF(pvUser);
990 Assert(cb == 4 || cb == 8);
991
992 VBOXSTRICTRC rc;
993 if (cb == 4)
994 {
995 if (off >= 0x100 && off < 0x400)
996 rc = hpetTimerRegWrite32(pDevIns, pThis,
997 (uint32_t)(off - 0x100) / 0x20,
998 (uint32_t)(off - 0x100) % 0x20,
999 *(uint32_t const *)pv);
1000 else
1001 rc = hpetConfigRegWrite32(pDevIns, pThis, off, *(uint32_t const *)pv);
1002 }
1003 else
1004 {
1005 /*
1006 * 8-byte access.
1007 */
1008 /* Split the access and rely on the locking to prevent trouble. */
1009 DEVHPET_LOCK_BOTH_RETURN(pDevIns, pThis, VINF_IOM_R3_MMIO_WRITE);
1010 RTUINT64U uValue;
1011 uValue.u = *(uint64_t const *)pv;
1012 if (off >= 0x100 && off < 0x400)
1013 {
1014 uint32_t iTimer = (uint32_t)(off - 0x100) / 0x20;
1015 uint32_t iTimerReg = (uint32_t)(off - 0x100) % 0x20;
1016 /** @todo Consider handling iTimerReg == HPET_TN_CMP specially here */
1017 rc = hpetTimerRegWrite32(pDevIns, pThis, iTimer, iTimerReg, uValue.s.Lo);
1018 if (RT_LIKELY(rc == VINF_SUCCESS))
1019 rc = hpetTimerRegWrite32(pDevIns, pThis, iTimer, iTimerReg + 4, uValue.s.Hi);
1020 }
1021 else
1022 {
1023 rc = hpetConfigRegWrite32(pDevIns, pThis, off, uValue.s.Lo);
1024 if (RT_LIKELY(rc == VINF_SUCCESS))
1025 rc = hpetConfigRegWrite32(pDevIns, pThis, off + 4, uValue.s.Hi);
1026 }
1027 DEVHPET_UNLOCK_BOTH(pDevIns, pThis);
1028 }
1029
1030 return rc;
1031}
1032
1033#ifdef IN_RING3
1034
1035/* -=-=-=-=-=- Timer Callback Processing -=-=-=-=-=- */
1036
1037/**
1038 * Gets the IRQ of an HPET timer.
1039 *
1040 * @returns IRQ number.
1041 * @param pThis The shared HPET state.
1042 * @param pHpetTimer The HPET timer.
1043 */
1044static uint32_t hpetR3TimerGetIrq(PHPET pThis, PCHPETTIMER pHpetTimer)
1045{
1046 /*
1047 * Per spec, in legacy mode the HPET timers are wired as follows:
1048 * timer 0: IRQ0 for PIC and IRQ2 for APIC
1049 * timer 1: IRQ8 for both PIC and APIC
1050 *
1051 * ISA IRQ delivery logic will take care of correct delivery
1052 * to the different ICs.
1053 */
1054 if ( (pHpetTimer->idxTimer <= 1)
1055 && (pThis->u64HpetConfig & HPET_CFG_LEGACY))
1056 return (pHpetTimer->idxTimer == 0) ? 0 : 8;
1057
1058 return (pHpetTimer->u64Config & HPET_TN_INT_ROUTE_MASK) >> HPET_TN_INT_ROUTE_SHIFT;
1059}
1060
1061
1062/**
1063 * Used by hpetR3Timer to update the IRQ status.
1064 *
1065 * @param pDevIns The device instance.
1066 * @param pThis The shared HPET state.
1067 * @param pHpetTimer The HPET timer.
1068 */
1069static void hpetR3TimerUpdateIrq(PPDMDEVINS pDevIns, PHPET pThis, PHPETTIMER pHpetTimer)
1070{
1071 /** @todo is it correct? */
1072 if ( !!(pHpetTimer->u64Config & HPET_TN_ENABLE)
1073 && !!(pThis->u64HpetConfig & HPET_CFG_ENABLE))
1074 {
1075 uint32_t irq = hpetR3TimerGetIrq(pThis, pHpetTimer);
1076 Log4(("HPET: raising IRQ %d\n", irq));
1077
1078 /* ISR bits are only set in level-triggered mode. */
1079 if ((pHpetTimer->u64Config & HPET_TN_INT_TYPE) == HPET_TIMER_TYPE_LEVEL)
1080 pThis->u64Isr |= UINT64_C(1) << pHpetTimer->idxTimer;
1081
1082 /* We trigger flip/flop in edge-triggered mode and do nothing in
1083 level-triggered mode yet. */
1084 if ((pHpetTimer->u64Config & HPET_TN_INT_TYPE) == HPET_TIMER_TYPE_EDGE)
1085 {
1086 PHPETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PHPETCC);
1087 AssertReturnVoid(pThisCC);
1088 pThisCC->pHpetHlp->pfnSetIrq(pDevIns, irq, PDM_IRQ_LEVEL_FLIP_FLOP);
1089 STAM_REL_COUNTER_INC(&pHpetTimer->StatSetIrq);
1090 }
1091 else
1092 AssertFailed();
1093 /** @todo implement IRQs in level-triggered mode */
1094 }
1095}
1096
1097/**
1098 * Device timer callback function.
1099 *
1100 * @param pDevIns Device instance of the device which registered the timer.
1101 * @param pTimer The timer handle.
1102 * @param pvUser Pointer to the HPET timer state.
1103 */
1104static DECLCALLBACK(void) hpetR3Timer(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
1105{
1106 PHPET pThis = PDMDEVINS_2_DATA(pDevIns, PHPET);
1107 PHPETTIMER pHpetTimer = (HPETTIMER *)pvUser;
1108 uint64_t u64Period = pHpetTimer->u64Period;
1109 uint64_t u64CurTick = hpetGetTicks(pDevIns, pThis);
1110 uint64_t u64Diff;
1111 RT_NOREF(pTimer);
1112
1113 if (pHpetTimer->u64Config & HPET_TN_PERIODIC)
1114 {
1115 if (u64Period)
1116 {
1117 hpetAdjustComparator(pHpetTimer, u64CurTick);
1118
1119 u64Diff = hpetComputeDiff(pHpetTimer, u64CurTick);
1120
1121 uint64_t u64TickLimit = pThis->fIch9 ? HPET_TICKS_IN_100YR_ICH9 : HPET_TICKS_IN_100YR_PIIX;
1122 if (u64Diff <= u64TickLimit)
1123 {
1124 Log4(("HPET: periodic: next in %llu\n", hpetTicksToNs(pThis, u64Diff)));
1125 STAM_REL_COUNTER_INC(&pHpetTimer->StatSetTimer);
1126 PDMDevHlpTimerSetNano(pDevIns, pHpetTimer->hTimer, hpetTicksToNs(pThis, u64Diff));
1127 }
1128 else
1129 {
1130 LogRelMax(10, ("HPET: Not scheduling periodic interrupt more than 100 years in the future.\n"));
1131 }
1132 }
1133 }
1134 else if (hpet32bitTimer(pHpetTimer))
1135 {
1136 /* For 32-bit non-periodic timers, generate wrap-around interrupts. */
1137 if (pHpetTimer->u8Wrap)
1138 {
1139 u64Diff = hpetComputeDiff(pHpetTimer, u64CurTick);
1140 PDMDevHlpTimerSetNano(pDevIns, pHpetTimer->hTimer, hpetTicksToNs(pThis, u64Diff));
1141 pHpetTimer->u8Wrap = 0;
1142 }
1143 }
1144
1145 /* Should it really be under lock, does it really matter? */
1146 hpetR3TimerUpdateIrq(pDevIns, pThis, pHpetTimer);
1147}
1148
1149
1150/* -=-=-=-=-=- DBGF Info Handlers -=-=-=-=-=- */
1151
1152
1153/**
1154 * @callback_method_impl{FNDBGFHANDLERDEV}
1155 */
1156static DECLCALLBACK(void) hpetR3Info(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
1157{
1158 PHPET pThis = PDMDEVINS_2_DATA(pDevIns, PHPET);
1159 NOREF(pszArgs);
1160
1161 pHlp->pfnPrintf(pHlp,
1162 "HPET status:\n"
1163 " config=%016RX64 isr=%016RX64\n"
1164 " offset=%016RX64 counter=%016RX64 frequency=%08x\n"
1165 " legacy-mode=%s timer-count=%u\n",
1166 pThis->u64HpetConfig, pThis->u64Isr,
1167 pThis->u64HpetOffset, pThis->u64HpetCounter, pThis->u32Period,
1168 !!(pThis->u64HpetConfig & HPET_CFG_LEGACY) ? "on " : "off",
1169 HPET_CAP_GET_TIMERS(pThis->u32Capabilities));
1170 pHlp->pfnPrintf(pHlp,
1171 "Timers:\n");
1172 for (unsigned i = 0; i < RT_ELEMENTS(pThis->aTimers); i++)
1173 {
1174 pHlp->pfnPrintf(pHlp, " %d: comparator=%016RX64 period(hidden)=%016RX64 cfg=%016RX64\n",
1175 pThis->aTimers[i].idxTimer,
1176 pThis->aTimers[i].u64Cmp,
1177 pThis->aTimers[i].u64Period,
1178 pThis->aTimers[i].u64Config);
1179 }
1180}
1181
1182
1183/* -=-=-=-=-=- Saved State -=-=-=-=-=- */
1184
1185
1186/**
1187 * @callback_method_impl{FNSSMDEVLIVEEXEC}
1188 */
1189static DECLCALLBACK(int) hpetR3LiveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uPass)
1190{
1191 PHPET pThis = PDMDEVINS_2_DATA(pDevIns, PHPET);
1192 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
1193 NOREF(uPass);
1194
1195 pHlp->pfnSSMPutU8(pSSM, HPET_CAP_GET_TIMERS(pThis->u32Capabilities));
1196
1197 return VINF_SSM_DONT_CALL_AGAIN;
1198}
1199
1200
1201/**
1202 * @callback_method_impl{FNSSMDEVSAVEEXEC}
1203 */
1204static DECLCALLBACK(int) hpetR3SaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
1205{
1206 PHPET pThis = PDMDEVINS_2_DATA(pDevIns, PHPET);
1207 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
1208
1209 /*
1210 * The config.
1211 */
1212 hpetR3LiveExec(pDevIns, pSSM, SSM_PASS_FINAL);
1213
1214 /*
1215 * The state.
1216 */
1217 uint32_t const cTimers = HPET_CAP_GET_TIMERS(pThis->u32Capabilities);
1218 for (uint32_t iTimer = 0; iTimer < cTimers; iTimer++)
1219 {
1220 PHPETTIMER pHpetTimer = &pThis->aTimers[iTimer];
1221 PDMDevHlpTimerSave(pDevIns, pHpetTimer->hTimer, pSSM);
1222 pHlp->pfnSSMPutU8(pSSM, pHpetTimer->u8Wrap);
1223 pHlp->pfnSSMPutU64(pSSM, pHpetTimer->u64Config);
1224 pHlp->pfnSSMPutU64(pSSM, pHpetTimer->u64Cmp);
1225 pHlp->pfnSSMPutU64(pSSM, pHpetTimer->u64Fsb);
1226 pHlp->pfnSSMPutU64(pSSM, pHpetTimer->u64Period);
1227 }
1228
1229 pHlp->pfnSSMPutU64(pSSM, pThis->u64HpetOffset);
1230 uint64_t u64CapPer = RT_MAKE_U64(pThis->u32Capabilities, pThis->u32Period);
1231 pHlp->pfnSSMPutU64(pSSM, u64CapPer);
1232 pHlp->pfnSSMPutU64(pSSM, pThis->u64HpetConfig);
1233 pHlp->pfnSSMPutU64(pSSM, pThis->u64Isr);
1234 return pHlp->pfnSSMPutU64(pSSM, pThis->u64HpetCounter);
1235}
1236
1237
1238/**
1239 * @callback_method_impl{FNSSMDEVLOADEXEC}
1240 */
1241static DECLCALLBACK(int) hpetR3LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
1242{
1243 PHPET pThis = PDMDEVINS_2_DATA(pDevIns, PHPET);
1244 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
1245
1246 /*
1247 * Version checks.
1248 */
1249 if (uVersion == HPET_SAVED_STATE_VERSION_EMPTY)
1250 return VINF_SUCCESS;
1251 if (uVersion != HPET_SAVED_STATE_VERSION)
1252 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
1253
1254 /*
1255 * The config.
1256 */
1257 uint8_t cTimers;
1258 int rc = pHlp->pfnSSMGetU8(pSSM, &cTimers);
1259 AssertRCReturn(rc, rc);
1260 if (cTimers > RT_ELEMENTS(pThis->aTimers))
1261 return pHlp->pfnSSMSetCfgError(pSSM, RT_SRC_POS, N_("Config mismatch - too many timers: saved=%#x config=%#x"),
1262 cTimers, RT_ELEMENTS(pThis->aTimers));
1263
1264 if (uPass != SSM_PASS_FINAL)
1265 return VINF_SUCCESS;
1266
1267 /*
1268 * The state.
1269 */
1270 for (uint32_t iTimer = 0; iTimer < cTimers; iTimer++)
1271 {
1272 PHPETTIMER pHpetTimer = &pThis->aTimers[iTimer];
1273 PDMDevHlpTimerLoad(pDevIns, pHpetTimer->hTimer, pSSM);
1274 pHlp->pfnSSMGetU8(pSSM, &pHpetTimer->u8Wrap);
1275 pHlp->pfnSSMGetU64(pSSM, &pHpetTimer->u64Config);
1276 pHlp->pfnSSMGetU64(pSSM, &pHpetTimer->u64Cmp);
1277 pHlp->pfnSSMGetU64(pSSM, &pHpetTimer->u64Fsb);
1278 pHlp->pfnSSMGetU64(pSSM, &pHpetTimer->u64Period);
1279 }
1280
1281 pHlp->pfnSSMGetU64(pSSM, &pThis->u64HpetOffset);
1282 uint64_t u64CapPer;
1283 pHlp->pfnSSMGetU64(pSSM, &u64CapPer);
1284 pHlp->pfnSSMGetU64(pSSM, &pThis->u64HpetConfig);
1285 pHlp->pfnSSMGetU64(pSSM, &pThis->u64Isr);
1286 rc = pHlp->pfnSSMGetU64(pSSM, &pThis->u64HpetCounter);
1287 if (RT_FAILURE(rc))
1288 return rc;
1289 if (HPET_CAP_GET_TIMERS(RT_LO_U32(u64CapPer)) != cTimers)
1290 return pHlp->pfnSSMSetCfgError(pSSM, RT_SRC_POS, N_("Capabilities does not match timer count: cTimers=%#x caps=%#x"),
1291 cTimers, (unsigned)HPET_CAP_GET_TIMERS(u64CapPer));
1292 pThis->u32Capabilities = RT_LO_U32(u64CapPer);
1293 pThis->u32Period = RT_HI_U32(u64CapPer);
1294
1295 /*
1296 * Set the timer frequency hints.
1297 */
1298 PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_IGNORED);
1299 for (uint32_t iTimer = 0; iTimer < cTimers; iTimer++)
1300 {
1301 PHPETTIMER pHpetTimer = &pThis->aTimers[iTimer];
1302 if (PDMDevHlpTimerIsActive(pDevIns, pHpetTimer->hTimer))
1303 hpetTimerSetFrequencyHint(pDevIns, pThis, pHpetTimer);
1304 }
1305 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
1306 return VINF_SUCCESS;
1307}
1308
1309
1310/* -=-=-=-=-=- PDMDEVREG -=-=-=-=-=- */
1311
1312
1313/**
1314 * @interface_method_impl{PDMDEVREG,pfnRelocate}
1315 */
1316static DECLCALLBACK(void) hpetR3Relocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
1317{
1318 PHPETRC pThisRC = PDMINS_2_DATA_RC(pDevIns, PHPETRC);
1319 LogFlow(("hpetR3Relocate:\n"));
1320
1321 pThisRC->pHpetHlp += offDelta;
1322}
1323
1324
1325/**
1326 * @interface_method_impl{PDMDEVREG,pfnReset}
1327 */
1328static DECLCALLBACK(void) hpetR3Reset(PPDMDEVINS pDevIns)
1329{
1330 PHPET pThis = PDMDEVINS_2_DATA(pDevIns, PHPET);
1331 PHPETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PHPETCC);
1332 LogFlow(("hpetR3Reset:\n"));
1333
1334 /*
1335 * The timers first.
1336 */
1337 PDMDevHlpTimerLockClock(pDevIns, pThis->aTimers[0].hTimer, VERR_IGNORED);
1338 for (unsigned i = 0; i < RT_ELEMENTS(pThis->aTimers); i++)
1339 {
1340 PHPETTIMER pHpetTimer = &pThis->aTimers[i];
1341 Assert(pHpetTimer->idxTimer == i);
1342 PDMDevHlpTimerStop(pDevIns, pHpetTimer->hTimer);
1343
1344 /* capable of periodic operations and 64-bits */
1345 if (pThis->fIch9)
1346 pHpetTimer->u64Config = (i == 0)
1347 ? (HPET_TN_PERIODIC_CAP | HPET_TN_SIZE_CAP)
1348 : 0;
1349 else
1350 pHpetTimer->u64Config = HPET_TN_PERIODIC_CAP | HPET_TN_SIZE_CAP;
1351
1352 /* We can do all IRQs */
1353 uint32_t u32RoutingCap = 0xffffffff;
1354 pHpetTimer->u64Config |= ((uint64_t)u32RoutingCap) << HPET_TN_INT_ROUTE_CAP_SHIFT;
1355 pHpetTimer->u64Period = 0;
1356 pHpetTimer->u8Wrap = 0;
1357 pHpetTimer->u64Cmp = hpetInvalidValue(pHpetTimer);
1358 }
1359 PDMDevHlpTimerUnlockClock(pDevIns, pThis->aTimers[0].hTimer);
1360
1361 /*
1362 * The shared HPET state.
1363 */
1364 pThis->u64HpetConfig = 0;
1365 pThis->u64HpetCounter = 0;
1366 pThis->u64HpetOffset = 0;
1367
1368 /* 64-bit main counter; 3 timers supported; LegacyReplacementRoute. */
1369 pThis->u32Capabilities = (1 << 15) /* LEG_RT_CAP - LegacyReplacementRoute capable. */
1370 | (1 << 13) /* COUNTER_SIZE_CAP - Main counter is 64-bit capable. */
1371 | 1; /* REV_ID - Revision, must not be 0 */
1372 if (pThis->fIch9) /* NUM_TIM_CAP - Number of timers -1. */
1373 pThis->u32Capabilities |= (HPET_NUM_TIMERS_ICH9 - 1) << 8;
1374 else
1375 pThis->u32Capabilities |= (HPET_NUM_TIMERS_PIIX - 1) << 8;
1376 pThis->u32Capabilities |= UINT32_C(0x80860000); /* VENDOR */
1377 AssertCompile(HPET_NUM_TIMERS_ICH9 <= RT_ELEMENTS(pThis->aTimers));
1378 AssertCompile(HPET_NUM_TIMERS_PIIX <= RT_ELEMENTS(pThis->aTimers));
1379
1380 pThis->u32Period = pThis->fIch9 ? HPET_CLK_PERIOD_ICH9 : HPET_CLK_PERIOD_PIIX;
1381
1382 /*
1383 * Notify the PIT/RTC devices.
1384 */
1385 if (pThisCC->pHpetHlp)
1386 pThisCC->pHpetHlp->pfnSetLegacyMode(pDevIns, false /*fActive*/);
1387}
1388
1389
1390/**
1391 * @interface_method_impl{PDMDEVREG,pfnConstruct}
1392 */
1393static DECLCALLBACK(int) hpetR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
1394{
1395 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
1396 PHPET pThis = PDMDEVINS_2_DATA(pDevIns, PHPET);
1397 PHPETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PHPETCC);
1398 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
1399
1400 /* Only one HPET device now, as we use fixed MMIO region. */
1401 Assert(iInstance == 0); RT_NOREF(iInstance);
1402
1403 /*
1404 * Initialize the device state.
1405 */
1406
1407 /* Init the HPET timers (init all regardless of how many we expose). */
1408 for (unsigned i = 0; i < RT_ELEMENTS(pThis->aTimers); i++)
1409 {
1410 PHPETTIMER pHpetTimer = &pThis->aTimers[i];
1411 pHpetTimer->idxTimer = i;
1412 pHpetTimer->hTimer = NIL_TMTIMERHANDLE;
1413 }
1414
1415 /*
1416 * Validate and read the configuration.
1417 */
1418 PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, "ICH9", "");
1419
1420 int rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "ICH9", &pThis->fIch9, false);
1421 if (RT_FAILURE(rc))
1422 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: failed to read ICH9 as boolean"));
1423
1424
1425 /*
1426 * Create critsect and timers.
1427 * Note! We don't use the default critical section of the device, but our own.
1428 */
1429 rc = PDMDevHlpCritSectInit(pDevIns, &pThis->CritSect, RT_SRC_POS, "HPET");
1430 AssertRCReturn(rc, rc);
1431
1432 rc = PDMDevHlpSetDeviceCritSect(pDevIns, PDMDevHlpCritSectGetNop(pDevIns));
1433 AssertRCReturn(rc, rc);
1434
1435 /* Init the HPET timers (init all regardless of how many we expose). */
1436 static const char * const s_apszTimerNames[] =
1437 { "HPET Timer 0", "HPET Timer 1", "HPET Timer 2", "HPET Timer 3" };
1438 AssertCompile(RT_ELEMENTS(pThis->aTimers) == RT_ELEMENTS(s_apszTimerNames));
1439 for (unsigned i = 0; i < RT_ELEMENTS(pThis->aTimers); i++)
1440 {
1441 PHPETTIMER pHpetTimer = &pThis->aTimers[i];
1442
1443 rc = PDMDevHlpTimerCreate(pDevIns, TMCLOCK_VIRTUAL_SYNC, hpetR3Timer, pHpetTimer,
1444 TMTIMER_FLAGS_NO_CRIT_SECT, s_apszTimerNames[i], &pThis->aTimers[i].hTimer);
1445 AssertRCReturn(rc, rc);
1446 /** @todo r=bird: This is TOTALLY MESSED UP! Why do we need
1447 * DEVHPET_LOCK_BOTH_RETURN() when the timers use the same critsect as
1448 * we do?!? */
1449 rc = PDMDevHlpTimerSetCritSect(pDevIns, pThis->aTimers[i].hTimer, &pThis->CritSect);
1450 AssertRCReturn(rc, rc);
1451 }
1452
1453 /*
1454 * This must be done prior to registering the HPET, right?
1455 */
1456 hpetR3Reset(pDevIns);
1457
1458 /*
1459 * Register the HPET and get helpers.
1460 */
1461 PDMHPETREG HpetReg;
1462 HpetReg.u32Version = PDM_HPETREG_VERSION;
1463 rc = PDMDevHlpHpetRegister(pDevIns, &HpetReg, &pThisCC->pHpetHlp);
1464 AssertRCReturn(rc, rc);
1465
1466 /*
1467 * Register the MMIO range, PDM API requests page aligned
1468 * addresses and sizes.
1469 */
1470 rc = PDMDevHlpMmioCreateAndMap(pDevIns, HPET_BASE, HPET_BAR_SIZE, hpetMMIOWrite, hpetMMIORead,
1471 IOMMMIO_FLAGS_READ_DWORD_QWORD | IOMMMIO_FLAGS_WRITE_ONLY_DWORD_QWORD,
1472 "HPET Memory", &pThis->hMmio);
1473 AssertRCReturn(rc, rc);
1474
1475 /*
1476 * Register SSM state, info item and statistics.
1477 */
1478 rc = PDMDevHlpSSMRegister3(pDevIns, HPET_SAVED_STATE_VERSION, sizeof(*pThis), hpetR3LiveExec, hpetR3SaveExec, hpetR3LoadExec);
1479 AssertRCReturn(rc, rc);
1480
1481 PDMDevHlpDBGFInfoRegister(pDevIns, "hpet", "Display HPET status. (no arguments)", hpetR3Info);
1482
1483 /* Statistics: */
1484 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatCounterRead4Byte, STAMTYPE_COUNTER,
1485 "CounterRead4Byte", STAMUNIT_OCCURENCES, "HPET_COUNTER 32-bit reads");
1486 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatCounterRead8Byte, STAMTYPE_COUNTER,
1487 "CounterRead8Byte", STAMUNIT_OCCURENCES, "HPET_COUNTER 64-bit reads");
1488 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatCounterWriteLow, STAMTYPE_COUNTER,
1489 "CounterWriteLow", STAMUNIT_OCCURENCES, "Low HPET_COUNTER writes");
1490 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatCounterWriteHigh, STAMTYPE_COUNTER,
1491 "CounterWriteHigh", STAMUNIT_OCCURENCES, "High HPET_COUNTER writes");
1492 for (unsigned i = 0; i < RT_ELEMENTS(pThis->aTimers); i++)
1493 {
1494 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->aTimers[i].StatSetIrq, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS,
1495 STAMUNIT_OCCURENCES, "Number of times the IRQ has been set.", "timer%u/SetIrq", i);
1496 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->aTimers[i].StatSetTimer, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS,
1497 STAMUNIT_OCCURENCES, "Number of times the timer has be programmed.", "timer%u/SetTimer", i);
1498 }
1499
1500 return VINF_SUCCESS;
1501}
1502
1503#else /* !IN_RING3 */
1504
1505/**
1506 * @callback_method_impl{PDMDEVREGR0,pfnConstruct}
1507 */
1508static DECLCALLBACK(int) hpetRZConstruct(PPDMDEVINS pDevIns)
1509{
1510 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
1511 PHPET pThis = PDMDEVINS_2_DATA(pDevIns, PHPET);
1512 PHPETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PHPETCC);
1513
1514 int rc = PDMDevHlpSetDeviceCritSect(pDevIns, PDMDevHlpCritSectGetNop(pDevIns));
1515 AssertRCReturn(rc, rc);
1516
1517 PDMHPETREG HpetReg;
1518 HpetReg.u32Version = PDM_HPETREG_VERSION;
1519 rc = PDMDevHlpHpetSetUpContext(pDevIns, &HpetReg, &pThisCC->pHpetHlp);
1520 AssertRCReturn(rc, rc);
1521
1522 rc = PDMDevHlpMmioSetUpContext(pDevIns, pThis->hMmio, hpetMMIOWrite, hpetMMIORead, NULL /*pvUser*/);
1523 AssertRCReturn(rc, rc);
1524
1525 return VINF_SUCCESS;
1526}
1527
1528#endif /* !IN_RING3 */
1529
1530/**
1531 * The device registration structure.
1532 */
1533const PDMDEVREG g_DeviceHPET =
1534{
1535 /* .u32Version = */ PDM_DEVREG_VERSION,
1536 /* .uReserved0 = */ 0,
1537 /* .szName = */ "hpet",
1538 /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RZ | PDM_DEVREG_FLAGS_NEW_STYLE,
1539 /* .fClass = */ PDM_DEVREG_CLASS_PIT,
1540 /* .cMaxInstances = */ 1,
1541 /* .uSharedVersion = */ 42,
1542 /* .cbInstanceShared = */ sizeof(HPET),
1543 /* .cbInstanceCC = */ sizeof(HPETCC),
1544 /* .cbInstanceRC = */ sizeof(HPETRC),
1545 /* .cMaxPciDevices = */ 0,
1546 /* .cMaxMsixVectors = */ 0,
1547 /* .pszDescription = */ "High Precision Event Timer (HPET) Device",
1548#if defined(IN_RING3)
1549 /* .pszRCMod = */ "VBoxDDRC.rc",
1550 /* .pszR0Mod = */ "VBoxDDR0.r0",
1551 /* .pfnConstruct = */ hpetR3Construct,
1552 /* .pfnDestruct = */ NULL,
1553 /* .pfnRelocate = */ hpetR3Relocate,
1554 /* .pfnMemSetup = */ NULL,
1555 /* .pfnPowerOn = */ NULL,
1556 /* .pfnReset = */ hpetR3Reset,
1557 /* .pfnSuspend = */ NULL,
1558 /* .pfnResume = */ NULL,
1559 /* .pfnAttach = */ NULL,
1560 /* .pfnDetach = */ NULL,
1561 /* .pfnQueryInterface = */ NULL,
1562 /* .pfnInitComplete = */ NULL,
1563 /* .pfnPowerOff = */ NULL,
1564 /* .pfnSoftReset = */ NULL,
1565 /* .pfnReserved0 = */ NULL,
1566 /* .pfnReserved1 = */ NULL,
1567 /* .pfnReserved2 = */ NULL,
1568 /* .pfnReserved3 = */ NULL,
1569 /* .pfnReserved4 = */ NULL,
1570 /* .pfnReserved5 = */ NULL,
1571 /* .pfnReserved6 = */ NULL,
1572 /* .pfnReserved7 = */ NULL,
1573#elif defined(IN_RING0)
1574 /* .pfnEarlyConstruct = */ NULL,
1575 /* .pfnConstruct = */ hpetRZConstruct,
1576 /* .pfnDestruct = */ NULL,
1577 /* .pfnFinalDestruct = */ NULL,
1578 /* .pfnRequest = */ NULL,
1579 /* .pfnReserved0 = */ NULL,
1580 /* .pfnReserved1 = */ NULL,
1581 /* .pfnReserved2 = */ NULL,
1582 /* .pfnReserved3 = */ NULL,
1583 /* .pfnReserved4 = */ NULL,
1584 /* .pfnReserved5 = */ NULL,
1585 /* .pfnReserved6 = */ NULL,
1586 /* .pfnReserved7 = */ NULL,
1587#elif defined(IN_RC)
1588 /* .pfnConstruct = */ hpetRZConstruct,
1589 /* .pfnReserved0 = */ NULL,
1590 /* .pfnReserved1 = */ NULL,
1591 /* .pfnReserved2 = */ NULL,
1592 /* .pfnReserved3 = */ NULL,
1593 /* .pfnReserved4 = */ NULL,
1594 /* .pfnReserved5 = */ NULL,
1595 /* .pfnReserved6 = */ NULL,
1596 /* .pfnReserved7 = */ NULL,
1597#else
1598# error "Not in IN_RING3, IN_RING0 or IN_RC!"
1599#endif
1600 /* .u32VersionEnd = */ PDM_DEVREG_VERSION
1601};
1602
1603#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
1604
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