VirtualBox

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

Last change on this file since 82064 was 81966, checked in by vboxsync, 5 years ago

DevHPET: Nits. bugref:9218

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