VirtualBox

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

Last change on this file since 100658 was 98103, checked in by vboxsync, 22 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 71.9 KB
Line 
1/* $Id: DevHPET.cpp 98103 2023-01-17 14:15:46Z vboxsync $ */
2/** @file
3 * HPET virtual device - High Precision Event Timer emulation.
4 *
5 * This implementation is based on the (generic) Intel IA-PC HPET specification
6 * and the Intel ICH9 datasheet.
7 *
8 * Typical windows 1809 usage (efi, smp) is to do repated one-shots and
9 * a variable rate. The reprogramming sequence is as follows (all accesses
10 * are 32-bit):
11 * -# counter register read.
12 * -# timer 0: config register read.
13 * -# timer 0: write 0x134 to config register.
14 * -# timer 0: write comparator register.
15 * -# timer 0: write 0x134 to config register.
16 * -# timer 0: read comparator register.
17 * -# counter register read.
18 *
19 * Typical linux will configure the timer at Hz but not necessarily enable
20 * interrupts (HPET_TN_ENABLE not set). It would be nice to emulate this
21 * mode without using timers.
22 *
23 */
24
25/*
26 * Copyright (C) 2009-2023 Oracle and/or its affiliates.
27 *
28 * This file is part of VirtualBox base platform packages, as
29 * available from https://www.virtualbox.org.
30 *
31 * This program is free software; you can redistribute it and/or
32 * modify it under the terms of the GNU General Public License
33 * as published by the Free Software Foundation, in version 3 of the
34 * License.
35 *
36 * This program is distributed in the hope that it will be useful, but
37 * WITHOUT ANY WARRANTY; without even the implied warranty of
38 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
39 * General Public License for more details.
40 *
41 * You should have received a copy of the GNU General Public License
42 * along with this program; if not, see <https://www.gnu.org/licenses>.
43 *
44 * SPDX-License-Identifier: GPL-3.0-only
45 */
46
47
48/*********************************************************************************************************************************
49* Header Files *
50*********************************************************************************************************************************/
51#define LOG_GROUP LOG_GROUP_DEV_HPET
52#include <VBox/vmm/pdmdev.h>
53#include <VBox/vmm/stam.h>
54#include <VBox/log.h>
55#include <VBox/AssertGuest.h>
56#include <iprt/asm-math.h>
57#include <iprt/string.h>
58
59#include "VBoxDD.h"
60
61
62/*********************************************************************************************************************************
63* Defined Constants And Macros *
64*********************************************************************************************************************************/
65/*
66 * Current limitations:
67 * - not entirely correct time of interrupt, i.e. never
68 * schedule interrupt earlier than in 1ms
69 * - statistics not implemented
70 * - level-triggered mode not implemented
71 */
72
73/** Base address for MMIO.
74 * On ICH9, it is 0xFED0x000 where 'x' is 0-3, default 0. We do not support
75 * relocation as the platform firmware is responsible for configuring the
76 * HPET base address and the OS isn't expected to move it.
77 * WARNING: This has to match the ACPI tables! */
78#define HPET_BASE 0xfed00000
79
80/** HPET reserves a 1K range. */
81#define HPET_BAR_SIZE 0x1000
82
83/** The number of timers for PIIX4 / PIIX3. */
84#define HPET_NUM_TIMERS_PIIX 3 /* Minimal implementation. */
85/** The number of timers for ICH9. */
86#define HPET_NUM_TIMERS_ICH9 4
87
88/** HPET clock period for PIIX4 / PIIX3.
89 * 10000000 femtoseconds == 10ns.
90 */
91#define HPET_CLK_PERIOD_PIIX UINT32_C(10000000)
92
93/** HPET clock period for ICH9.
94 * 69841279 femtoseconds == 69.84 ns (1 / 14.31818MHz).
95 */
96#define HPET_CLK_PERIOD_ICH9 UINT32_C(69841279)
97
98/**
99 * Femtosecods in a nanosecond
100 */
101#define FS_PER_NS UINT32_C(1000000)
102
103/** Number of HPET ticks per second (Hz), ICH9 frequency. */
104#define HPET_TICKS_PER_SEC_ICH9 UINT32_C(14318180)
105AssertCompile(HPET_TICKS_PER_SEC_ICH9 == (RT_NS_1SEC_64 * FS_PER_NS + HPET_CLK_PERIOD_ICH9 / 2) / HPET_CLK_PERIOD_ICH9);
106
107/** Number of HPET ticks per second (Hz), made-up PIIX frequency. */
108#define HPET_TICKS_PER_SEC_PIIX UINT32_C(100000000)
109AssertCompile(HPET_TICKS_PER_SEC_PIIX == (RT_NS_1SEC_64 * FS_PER_NS + HPET_CLK_PERIOD_PIIX / 2) / HPET_CLK_PERIOD_PIIX);
110
111/** Number of HPET ticks in 100 years (approximate), ICH9 frequency.
112 * Value: 45153812448000000 (0x00A06B27'3737B800) */
113#define HPET_TICKS_IN_100YR_ICH9 (HPET_TICKS_PER_SEC_ICH9 * RT_SEC_1DAY_64 * 365 * 100)
114AssertCompile(HPET_TICKS_IN_100YR_ICH9 >= UINT64_C(45153812448000000));
115
116/** Number of HPET ticks in 100 years, made-up PIIX frequency.
117 * Value: 315360000000000000 (0x0460623F'C85E0000) */
118#define HPET_TICKS_IN_100YR_PIIX (HPET_TICKS_PER_SEC_PIIX * RT_SEC_1DAY_64 * 365 * 100)
119AssertCompile(HPET_TICKS_IN_100YR_PIIX >= UINT64_C(315360000000000000));
120
121/** @name Interrupt type
122 * @{ */
123#define HPET_TIMER_TYPE_LEVEL (1 << 1)
124#define HPET_TIMER_TYPE_EDGE (0 << 1)
125/** @} */
126
127/** @name Delivery mode
128 * @{ */
129#define HPET_TIMER_DELIVERY_APIC 0 /**< Delivery through APIC. */
130#define HPET_TIMER_DELIVERY_FSB 1 /**< Delivery through FSB. */
131/** @} */
132
133#define HPET_TIMER_CAP_FSB_INT_DEL (1 << 15)
134#define HPET_TIMER_CAP_PER_INT (1 << 4)
135
136#define HPET_CFG_ENABLE 0x001 /**< ENABLE_CNF */
137#define HPET_CFG_LEGACY 0x002 /**< LEG_RT_CNF */
138
139/** @name Register offsets in HPET space.
140 * @{ */
141#define HPET_ID 0x000 /**< Device ID. */
142#define HPET_PERIOD 0x004 /**< Clock period in femtoseconds. */
143#define HPET_CFG 0x010 /**< Configuration register. */
144#define HPET_STATUS 0x020 /**< Status register. */
145#define HPET_COUNTER 0x0f0 /**< Main HPET counter. */
146/** @} */
147
148/** @name Timer N offsets (within each timer's space).
149 * @{ */
150#define HPET_TN_CFG 0x000 /**< Timer N configuration. */
151#define HPET_TN_CMP 0x008 /**< Timer N comparator. */
152#define HPET_TN_ROUTE 0x010 /**< Timer N interrupt route. */
153/** @} */
154
155#define HPET_CFG_WRITE_MASK 0x3
156
157#define HPET_TN_INT_TYPE RT_BIT_64(1)
158#define HPET_TN_ENABLE RT_BIT_64(2)
159#define HPET_TN_PERIODIC RT_BIT_64(3)
160#define HPET_TN_PERIODIC_CAP RT_BIT_64(4)
161#define HPET_TN_SIZE_CAP RT_BIT_64(5)
162#define HPET_TN_SETVAL RT_BIT_64(6) /**< Periodic timers only: Change COMPARATOR as well as ACCUMULATOR. */
163#define HPET_TN_32BIT RT_BIT_64(8)
164#define HPET_TN_INT_ROUTE_MASK UINT64_C(0x3e00)
165#define HPET_TN_CFG_WRITE_MASK UINT64_C(0x3e46)
166#define HPET_TN_INT_ROUTE_SHIFT 9
167#define HPET_TN_INT_ROUTE_CAP_SHIFT 32
168
169#define HPET_TN_CFG_BITS_READONLY_OR_RESERVED 0xffff80b1U
170
171/** Extract the timer count from the capabilities. */
172#define HPET_CAP_GET_TIMERS(a_u32) ((((a_u32) >> 8) + 1) & 0x1f)
173/** Revision ID. */
174#define HPET_CAP_GET_REV_ID(a_u32) ((a_u32) & 0xff)
175/** Counter size. */
176#define HPET_CAP_HAS_64BIT_COUNT_SIZE(a_u32) RT_BOOL((a_u32) & RT_BIT(13))
177/** Legacy Replacement Route. */
178#define HPET_CAP_HAS_LEG_RT(a_u32) RT_BOOL((a_u32) & RT_BIT(15))
179
180
181/** The version of the saved state. */
182#define HPET_SAVED_STATE_VERSION 3
183/** The version of the saved state prior to the off-by-1 timer count fix. */
184#define HPET_SAVED_STATE_VERSION_PRE_TIMER 2
185/** Empty saved state */
186#define HPET_SAVED_STATE_VERSION_EMPTY 1
187
188
189/**
190 * Acquires the HPET lock or returns.
191 */
192#define DEVHPET_LOCK_RETURN(a_pDevIns, a_pThis, a_rcBusy) \
193 do { \
194 int const rcLock = PDMDevHlpCritSectEnter((a_pDevIns), &(a_pThis)->CritSect, (a_rcBusy)); \
195 if (RT_LIKELY(rcLock == VINF_SUCCESS)) \
196 { /* likely */ } \
197 else \
198 return rcLock; \
199 } while (0)
200
201/**
202 * Releases the HPET lock.
203 */
204#define DEVHPET_UNLOCK(a_pDevIns, a_pThis) \
205 do { PDMDevHlpCritSectLeave((a_pDevIns), &(a_pThis)->CritSect); } while (0)
206
207
208/**
209 * Acquires the TM lock and HPET lock, returns on failure.
210 * @todo r=bird: Aren't the timers using the same critsect?!?
211 */
212#define DEVHPET_LOCK_BOTH_RETURN(a_pDevIns, a_pThis, a_rcBusy) \
213 do { \
214 VBOXSTRICTRC rcLock = PDMDevHlpTimerLockClock2((a_pDevIns), (a_pThis)->aTimers[0].hTimer, &(a_pThis)->CritSect, (a_rcBusy)); \
215 if (RT_LIKELY(rcLock == VINF_SUCCESS)) \
216 { /* likely */ } \
217 else \
218 return rcLock; \
219 } while (0)
220
221
222/**
223 * Releases the HPET lock and TM lock.
224 */
225#define DEVHPET_UNLOCK_BOTH(a_pDevIns, a_pThis) \
226 PDMDevHlpTimerUnlockClock2((a_pDevIns), (a_pThis)->aTimers[0].hTimer, &(a_pThis)->CritSect)
227
228
229/*********************************************************************************************************************************
230* Structures and Typedefs *
231*********************************************************************************************************************************/
232/**
233 * A HPET timer.
234 *
235 * @note To avoid excessive locking, we many of the updates atomically.
236 */
237typedef struct HPETTIMER
238{
239 /** The HPET timer. */
240 TMTIMERHANDLE hTimer;
241
242 /** Timer index. */
243 uint8_t idxTimer;
244 /** Wrap. */
245 uint8_t u8Wrap;
246 /** Explicit padding. */
247 uint8_t abPadding[6];
248
249 /** @name Memory-mapped, software visible timer registers.
250 * @{ */
251 /** Configuration/capabilities. */
252 uint64_t u64Config;
253 /** Comparator. */
254 uint64_t u64Cmp;
255 /** FSB route, not supported now. */
256 uint64_t u64Fsb;
257 /** @} */
258
259 /** @name Hidden register state.
260 * @{ */
261 /** Accumulator / Last value written to comparator. */
262 uint64_t u64Period;
263 /** @} */
264
265 STAMCOUNTER StatSetIrq;
266 STAMCOUNTER StatSetTimer;
267} HPETTIMER;
268AssertCompileMemberAlignment(HPETTIMER, u64Config, sizeof(uint64_t));
269AssertCompileSizeAlignment(HPETTIMER, 64);
270/** Pointer to the shared state of an HPET timer. */
271typedef HPETTIMER *PHPETTIMER;
272/** Const pointer to the shared state of an HPET timer. */
273typedef HPETTIMER const *PCHPETTIMER;
274
275
276/**
277 * The shared HPET device state.
278 */
279typedef struct HPET
280{
281 /** Timer structures. */
282 HPETTIMER aTimers[RT_MAX(HPET_NUM_TIMERS_PIIX, HPET_NUM_TIMERS_ICH9)];
283
284 /** Offset realtive to the virtual sync clock. */
285 uint64_t u64HpetOffset;
286
287 /** @name Memory-mapped, software visible registers
288 * @{ */
289 /** Capabilities. */
290 uint32_t u32Capabilities;
291 /** Used to be u32Period. We only implement two period values depending on
292 * fIch9, and since we usually would have to RT_MIN(u32Period,1) we could
293 * just as well select between HPET_CLK_PERIOD_ICH9 and HPET_CLK_PERIOD_PIIX. */
294 uint32_t u32Padding;
295 /** Configuration. */
296 uint64_t u64HpetConfig;
297 /** Interrupt status register. */
298 uint64_t u64Isr;
299 /** Main counter. */
300 uint64_t u64HpetCounter;
301 /** @} */
302
303 /** Whether we emulate ICH9 HPET (different frequency & timer count). */
304 bool fIch9;
305 /** Size alignment padding. */
306 uint8_t abPadding0[7+8];
307
308 /** The handle of the MMIO region. */
309 IOMMMIOHANDLE hMmio;
310
311 /** Global device lock. */
312 PDMCRITSECT CritSect;
313
314 STAMCOUNTER StatCounterRead4Byte;
315 STAMCOUNTER StatCounterRead8Byte;
316 STAMCOUNTER StatCounterWriteLow;
317 STAMCOUNTER StatCounterWriteHigh;
318 STAMCOUNTER StatZeroDeltaHack;
319} HPET;
320AssertCompileMemberAlignment(HPET, aTimers, 64);
321AssertCompileMemberAlignment(HPET, CritSect, 64);
322/** Pointer to the shared HPET device state. */
323typedef HPET *PHPET;
324/** Const pointer to the shared HPET device state. */
325typedef const HPET *PCHPET;
326
327
328/**
329 * The ring-3 specific HPET device state.
330 */
331typedef struct HPETR3
332{
333 /** The HPET helpers. */
334 PCPDMHPETHLPR3 pHpetHlp;
335} HPETR3;
336/** Pointer to the ring-3 specific HPET device state. */
337typedef HPETR3 *PHPETR3;
338
339
340/**
341 * The ring-0 specific HPET device state.
342 */
343typedef struct HPETR0
344{
345 /** The HPET helpers. */
346 PCPDMHPETHLPR0 pHpetHlp;
347} HPETR0;
348/** Pointer to the ring-0 specific HPET device state. */
349typedef HPETR0 *PHPETR0;
350
351
352/**
353 * The raw-mode specific HPET device state.
354 */
355typedef struct HPETRC
356{
357 /** The HPET helpers. */
358 PCPDMHPETHLPRC pHpetHlp;
359} HPETRC;
360/** Pointer to the raw-mode specific HPET device state. */
361typedef HPETRC *PHPETRC;
362
363
364/** The HPET device state specific to the current context. */
365typedef CTX_SUFF(HPET) HPETCC;
366/** Pointer to the HPET device state specific to the current context. */
367typedef CTX_SUFF(PHPET) PHPETCC;
368
369
370#ifndef VBOX_DEVICE_STRUCT_TESTCASE
371
372DECLINLINE(bool) hpet32bitTimerEx(uint64_t fConfig)
373{
374 return !(fConfig & HPET_TN_SIZE_CAP)
375 || (fConfig & HPET_TN_32BIT);
376}
377
378
379DECLINLINE(bool) hpet32bitTimer(PHPETTIMER pHpetTimer)
380{
381 return hpet32bitTimerEx(ASMAtomicUoReadU64(&pHpetTimer->u64Config));
382}
383
384
385DECLINLINE(uint64_t) hpetInvalidValue(PHPETTIMER pHpetTimer)
386{
387 return hpet32bitTimer(pHpetTimer) ? UINT32_MAX : UINT64_MAX;
388}
389
390
391/**
392 * @note The caller shall do overflow checks! See @bugref{10301}.
393 */
394DECLINLINE(uint64_t) hpetTicksToNs(PHPET pThis, uint64_t value)
395{
396 return ASMMultU64ByU32DivByU32(value, pThis->fIch9 ? HPET_CLK_PERIOD_ICH9 : HPET_CLK_PERIOD_PIIX, FS_PER_NS);
397}
398
399DECLINLINE(uint64_t) nsToHpetTicks(PCHPET pThis, uint64_t u64Value)
400{
401 return ASMMultU64ByU32DivByU32(u64Value, FS_PER_NS, pThis->fIch9 ? HPET_CLK_PERIOD_ICH9 : HPET_CLK_PERIOD_PIIX);
402}
403
404DECLINLINE(uint64_t) hpetGetTicksEx(PCHPET pThis, uint64_t tsNow)
405{
406 return nsToHpetTicks(pThis, tsNow + pThis->u64HpetOffset);
407}
408
409DECLINLINE(uint64_t) hpetUpdateMasked(uint64_t u64NewValue, uint64_t u64OldValue, uint64_t u64Mask)
410{
411 u64NewValue &= u64Mask;
412 u64NewValue |= (u64OldValue & ~u64Mask);
413 return u64NewValue;
414}
415
416DECLINLINE(bool) hpetBitJustSet(uint64_t u64OldValue, uint64_t u64NewValue, uint64_t u64Mask)
417{
418 return !(u64OldValue & u64Mask)
419 && !!(u64NewValue & u64Mask);
420}
421
422DECLINLINE(bool) hpetBitJustCleared(uint64_t u64OldValue, uint64_t u64NewValue, uint64_t u64Mask)
423{
424 return !!(u64OldValue & u64Mask)
425 && !(u64NewValue & u64Mask);
426}
427
428DECLINLINE(uint64_t) hpetComputeDiff(uint64_t fConfig, uint64_t uCmp, uint64_t uHpetNow)
429{
430 if (hpet32bitTimerEx(fConfig))
431 {
432 uint32_t u32Diff = (uint32_t)uCmp - (uint32_t)uHpetNow;
433 if ((int32_t)u32Diff > 0)
434 return u32Diff;
435 }
436 else
437 {
438 uint64_t u64Diff = uCmp - uHpetNow;
439 if ((int64_t)u64Diff > 0)
440 return u64Diff;
441 }
442 return 0;
443}
444
445
446DECLINLINE(uint64_t) hpetAdjustComparator(PHPETTIMER pHpetTimer, uint64_t fConfig, uint64_t uCmp,
447 uint64_t uPeriod, uint64_t uHpetNow)
448{
449 if (fConfig & HPET_TN_PERIODIC)
450 {
451 if (uPeriod)
452 {
453 uint64_t cPeriods = (uHpetNow - uCmp) / uPeriod;
454 uCmp += (cPeriods + 1) * uPeriod;
455 ASMAtomicWriteU64(&pHpetTimer->u64Cmp, uCmp);
456 }
457 }
458 return uCmp;
459}
460
461
462/**
463 * Sets the frequency hint if it's a periodic timer.
464 *
465 * @param pDevIns The device instance.
466 * @param pThis The shared HPET state.
467 * @param pHpetTimer The timer.
468 * @param fConfig Already read config value.
469 * @param uPeriod Already read period value.
470 */
471DECLINLINE(void) hpetTimerSetFrequencyHint(PPDMDEVINS pDevIns, PHPET pThis, PHPETTIMER pHpetTimer,
472 uint64_t fConfig, uint64_t uPeriod)
473{
474 if ( (fConfig & HPET_TN_PERIODIC)
475 && uPeriod > 0
476 && uPeriod < (pThis->fIch9 ? HPET_TICKS_PER_SEC_ICH9 : HPET_TICKS_PER_SEC_PIIX) / 10 /* 100 ns */)
477 {
478 uint64_t const nsPeriod = hpetTicksToNs(pThis, uPeriod);
479 PDMDevHlpTimerSetFrequencyHint(pDevIns, pHpetTimer->hTimer, RT_NS_1SEC / (uint32_t)nsPeriod);
480 }
481}
482
483
484/**
485 * Programs an HPET timer, arming hTimer for the next IRQ.
486 *
487 * @param pDevIns The device instance.
488 * @param pThis The HPET instance data.
489 * @param pHpetTimer The HPET timer to program. The wrap-around indicator is
490 * updates, and for periodic timer the comparator.
491 * @param tsNow The current virtual sync clock time.
492 * @note Caller must both the virtual sync (timer) and HPET locks.
493 */
494static void hpetProgramTimer(PPDMDEVINS pDevIns, PHPET pThis, PHPETTIMER pHpetTimer, uint64_t const tsNow)
495{
496 /*
497 * Calculate the number of HPET ticks to the next timer IRQ, but
498 * first updating comparator if periodic timer.
499 */
500 uint64_t const fConfig = pHpetTimer->u64Config;
501 uint64_t const uPeriod = pHpetTimer->u64Period;
502 uint64_t uCmp = pHpetTimer->u64Cmp;
503 uint64_t const uHpetNow = hpetGetTicksEx(pThis, tsNow);
504 uCmp = hpetAdjustComparator(pHpetTimer, fConfig, uCmp, uPeriod, uHpetNow);
505 uint64_t uHpetDelta = hpetComputeDiff(fConfig, uCmp, uHpetNow);
506
507 /*
508 * HPET spec says in one-shot 32-bit mode, generate an interrupt when
509 * counter wraps in addition to an interrupt with comparator match.
510 */
511 bool fWrap = false;
512 if ( hpet32bitTimerEx(fConfig)
513 && !(fConfig & HPET_TN_PERIODIC))
514 {
515 uint32_t cHpetTicksTillWrap = UINT32_MAX - (uint32_t)uHpetNow + 1;
516 if (cHpetTicksTillWrap < (uint32_t)uHpetDelta)
517 {
518 Log(("HPET[%u]: wrap: till=%u ticks=%lld diff64=%lld\n",
519 pHpetTimer->idxTimer, cHpetTicksTillWrap, uHpetNow, uHpetDelta));
520 uHpetDelta = cHpetTicksTillWrap;
521 fWrap = true;
522 }
523 }
524 pHpetTimer->u8Wrap = fWrap;
525
526 /*
527 * HACK ALERT! Avoid killing VM with interrupts.
528 */
529#if 1 /** @todo HACK, rethink, may have negative impact on the guest */
530 if (uHpetDelta != 0)
531 { /* likely? */ }
532 else
533 {
534 Log(("HPET[%u]: Applying zero delta hack!\n", pHpetTimer->idxTimer));
535 STAM_REL_COUNTER_INC(&pThis->StatZeroDeltaHack);
536/** @todo lower this. */
537 uHpetDelta = pThis->fIch9 ? 14318 : 100000; /* 1 millisecond */
538 }
539#endif
540
541 /*
542 * Arm the timer.
543 */
544 uint64_t u64TickLimit = pThis->fIch9 ? HPET_TICKS_IN_100YR_ICH9 : HPET_TICKS_IN_100YR_PIIX;
545 if (uHpetDelta <= u64TickLimit)
546 {
547 uint64_t const cTicksDelta = hpetTicksToNs(pThis, uHpetDelta);
548 uint64_t const tsDeadline = tsNow + cTicksDelta;
549 Log4(("HPET[%u]: next IRQ in %lld hpet ticks (TM %lld ticks, at %llu)\n",
550 pHpetTimer->idxTimer, uHpetDelta, cTicksDelta, tsDeadline));
551 PDMDevHlpTimerSet(pDevIns, pHpetTimer->hTimer, tsDeadline);
552 hpetTimerSetFrequencyHint(pDevIns, pThis, pHpetTimer, fConfig, uPeriod);
553 STAM_REL_COUNTER_INC(&pHpetTimer->StatSetTimer);
554 }
555 else
556 LogRelMax(10, ("HPET[%u]: Not scheduling an interrupt more than 100 years in the future.\n", pHpetTimer->idxTimer));
557}
558
559
560/* -=-=-=-=-=- Timer register accesses -=-=-=-=-=- */
561
562
563/**
564 * Reads a HPET timer register.
565 *
566 * @returns The register value.
567 * @param pThis The HPET instance.
568 * @param iTimerNo The timer index.
569 * @param iTimerReg The index of the timer register to read.
570 *
571 * @note No locking required.
572 */
573static uint32_t hpetTimerRegRead32(PHPET pThis, uint32_t iTimerNo, uint32_t iTimerReg)
574{
575 uint32_t u32Value;
576 if ( iTimerNo < HPET_CAP_GET_TIMERS(pThis->u32Capabilities)
577 && iTimerNo < RT_ELEMENTS(pThis->aTimers) )
578 {
579 PHPETTIMER pHpetTimer = &pThis->aTimers[iTimerNo];
580 switch (iTimerReg)
581 {
582 case HPET_TN_CFG:
583 u32Value = (uint32_t)ASMAtomicReadU64(&pHpetTimer->u64Config);
584 Log(("HPET[%u]: read32 HPET_TN_CFG: %#x\n", iTimerNo, u32Value));
585 break;
586
587 case HPET_TN_CFG + 4:
588 u32Value = (uint32_t)(ASMAtomicReadU64(&pHpetTimer->u64Config) >> 32);
589 Log(("HPET[%u]: read32 HPET_TN_CFG+4: %#x\n", iTimerNo, u32Value));
590 break;
591
592 case HPET_TN_CMP:
593 {
594 uint64_t uCmp = ASMAtomicReadU64(&pHpetTimer->u64Cmp);
595 u32Value = (uint32_t)uCmp;
596 Log(("HPET[%u]: read32 HPET_TN_CMP: %#x (%#RX64)\n", pHpetTimer->idxTimer, u32Value, uCmp));
597 break;
598 }
599
600 case HPET_TN_CMP + 4:
601 {
602 uint64_t uCmp = ASMAtomicReadU64(&pHpetTimer->u64Cmp);
603 u32Value = (uint32_t)(uCmp >> 32);
604 Log(("HPET[%u]: read32 HPET_TN_CMP+4: %#x (%#RX64)\n", pHpetTimer->idxTimer, u32Value, uCmp));
605 break;
606 }
607
608 case HPET_TN_ROUTE:
609 u32Value = (uint32_t)(pHpetTimer->u64Fsb >> 32); /** @todo Looks wrong, but since it's not supported, who cares. */
610 Log(("HPET[%u]: read32 HPET_TN_ROUTE: %#x\n", iTimerNo, u32Value));
611 break;
612
613 default:
614 LogRelMax(10, ("HPET[%u]: Invalid HPET register read: %d\n", iTimerNo, iTimerReg));
615 u32Value = 0;
616 break;
617 }
618 }
619 else
620 {
621 LogRelMax(10, ("HPET: Using timer above configured range: %d\n", iTimerNo));
622 u32Value = 0;
623 }
624 return u32Value;
625}
626
627
628/**
629 * Reads a HPET timer register, 64-bit access.
630 *
631 * @returns The register value.
632 * @param pThis The HPET instance.
633 * @param iTimerNo The timer index.
634 * @param iTimerReg The index of the timer register to read.
635 */
636static uint64_t hpetTimerRegRead64(PHPET pThis, uint32_t iTimerNo, uint32_t iTimerReg)
637{
638 uint64_t u64Value;
639 if ( iTimerNo < HPET_CAP_GET_TIMERS(pThis->u32Capabilities)
640 && iTimerNo < RT_ELEMENTS(pThis->aTimers) )
641 {
642 PHPETTIMER pHpetTimer = &pThis->aTimers[iTimerNo];
643 switch (iTimerReg)
644 {
645 case HPET_TN_CFG:
646 u64Value = ASMAtomicReadU64(&pHpetTimer->u64Config);
647 Log(("HPET[%u]: read64 HPET_TN_CFG: %#RX64\n", iTimerNo, u64Value));
648 break;
649
650 case HPET_TN_CMP:
651 u64Value = ASMAtomicReadU64(&pHpetTimer->u64Cmp);
652 Log(("HPET[%u]: read64 HPET_TN_CMP: %#RX64\n", iTimerNo, u64Value));
653 break;
654
655 case HPET_TN_ROUTE:
656 u64Value = (uint32_t)(pHpetTimer->u64Fsb >> 32); /** @todo Looks wrong, but since it's not supported, who cares. */
657 Log(("HPET[%u]: read64 HPET_TN_ROUTE: %#RX64\n", iTimerNo, u64Value));
658 break;
659
660 default:
661 LogRelMax(10, ("HPET[%u]: Invalid 64-bit HPET register read64: %d\n", iTimerNo, iTimerReg));
662 u64Value = 0;
663 break;
664 }
665 }
666 else
667 {
668 LogRelMax(10, ("HPET: Using timer above configured range: %d\n", iTimerNo));
669 u64Value = 0;
670 }
671 return u64Value;
672}
673
674
675/**
676 * 32-bit write to a HPET timer register.
677 *
678 * @returns Strict VBox status code.
679 *
680 * @param pDevIns The device instance.
681 * @param pThis The shared HPET state.
682 * @param iTimerNo The timer being written to.
683 * @param iTimerReg The register being written to.
684 * @param u32NewValue The value being written.
685 *
686 * @remarks The caller should not hold any locks.
687 */
688static VBOXSTRICTRC hpetTimerRegWrite32(PPDMDEVINS pDevIns, PHPET pThis, uint32_t iTimerNo,
689 uint32_t iTimerReg, uint32_t u32NewValue)
690{
691 Assert(!PDMDevHlpCritSectIsOwner(pDevIns, &pThis->CritSect));
692 Assert(!PDMDevHlpTimerIsLockOwner(pDevIns, pThis->aTimers[0].hTimer));
693
694 if ( iTimerNo < HPET_CAP_GET_TIMERS(pThis->u32Capabilities)
695 && iTimerNo < RT_ELEMENTS(pThis->aTimers) ) /* Parfait - see above. */
696 {
697 PHPETTIMER pHpetTimer = &pThis->aTimers[iTimerNo];
698
699 switch (iTimerReg)
700 {
701 case HPET_TN_CFG:
702 {
703 /*
704 * Calculate the writable mask and see if anything actually changed
705 * before doing any locking. Windows 10 (1809) does two CFG writes
706 * with the same value (0x134) when reprogramming the HPET#0 timer.
707 */
708 uint64_t fConfig = ASMAtomicUoReadU64(&pHpetTimer->u64Config);
709 uint64_t const fMask = HPET_TN_CFG_WRITE_MASK
710 | (fConfig & HPET_TN_PERIODIC_CAP ? HPET_TN_PERIODIC : 0)
711 | (fConfig & HPET_TN_SIZE_CAP ? HPET_TN_32BIT : 0);
712 if ((u32NewValue & fMask) == (fConfig & fMask))
713 Log(("HPET[%u]: write32 HPET_TN_CFG: %#x - no change (%#RX64)\n", iTimerNo, u32NewValue, fConfig));
714 else
715 {
716#ifndef IN_RING3
717 /* Return to ring-3 (where LogRel works) to complain about level-triggered interrupts. */
718 if ((u32NewValue & HPET_TN_INT_TYPE) == HPET_TIMER_TYPE_LEVEL)
719 return VINF_IOM_R3_MMIO_WRITE;
720#endif
721 DEVHPET_LOCK_RETURN(pDevIns, pThis, VINF_IOM_R3_MMIO_WRITE);
722
723 fConfig = ASMAtomicUoReadU64(&pHpetTimer->u64Config);
724 uint64_t const fConfigNew = hpetUpdateMasked(u32NewValue, fConfig, fMask);
725 Log(("HPET[%u]: write HPET_TN_CFG: %#RX64 -> %#RX64\n", iTimerNo, fConfig, fConfigNew));
726
727 if ((fConfigNew & HPET_TN_32BIT) == (fConfig & HPET_TN_32BIT))
728 { /* likely it stays the same */ }
729 else if (fConfigNew & HPET_TN_32BIT)
730 {
731 Log(("HPET[%u]: Changing timer to 32-bit mode.\n", iTimerNo));
732 /* Clear the top bits of the comparator and period to be on the safe side. */
733 ASMAtomicUoWriteU64(&pHpetTimer->u64Cmp, (uint32_t)pHpetTimer->u64Cmp);
734 ASMAtomicUoWriteU64(&pHpetTimer->u64Period, (uint32_t)pHpetTimer->u64Period);
735 }
736 else
737 Log(("HPET[%u]: Changing timer to 64-bit mode.\n", iTimerNo));
738 ASMAtomicWriteU64(&pHpetTimer->u64Config, fConfigNew);
739
740 DEVHPET_UNLOCK(pDevIns, pThis);
741
742 if (RT_LIKELY((fConfigNew & HPET_TN_INT_TYPE) != HPET_TIMER_TYPE_LEVEL))
743 { /* likely */ }
744 else
745 {
746 LogRelMax(10, ("HPET[%u]: Level-triggered config not yet supported\n", iTimerNo));
747 ASSERT_GUEST_MSG_FAILED(("Level-triggered config not yet supported"));
748 }
749 }
750 break;
751 }
752
753 case HPET_TN_CFG + 4: /* Interrupt capabilities - read only. */
754 Log(("HPET[%u]: write32 HPET_TN_CFG + 4 (ignored)\n", iTimerNo));
755 break;
756
757 case HPET_TN_CMP: /* lower bits of comparator register */
758 {
759 DEVHPET_LOCK_BOTH_RETURN(pDevIns, pThis, VINF_IOM_R3_MMIO_WRITE);
760 uint64_t fConfig = ASMAtomicUoReadU64(&pHpetTimer->u64Config);
761 Log(("HPET[%u]: write32 HPET_TN_CMP: %#x (fCfg=%#RX32)\n", iTimerNo, u32NewValue, (uint32_t)fConfig));
762
763 if (fConfig & HPET_TN_PERIODIC)
764 ASMAtomicUoWriteU64(&pHpetTimer->u64Period, RT_MAKE_U64(u32NewValue, RT_HI_U32(pHpetTimer->u64Period)));
765
766 if (!(fConfig & HPET_TN_PERIODIC) || (fConfig & HPET_TN_SETVAL))
767 ASMAtomicUoWriteU64(&pHpetTimer->u64Cmp, RT_MAKE_U64(u32NewValue, RT_HI_U32(pHpetTimer->u64Cmp)));
768
769 ASMAtomicAndU64(&pHpetTimer->u64Config, ~HPET_TN_SETVAL);
770 Log2(("HPET[%u]: after32 HPET_TN_CMP cmp=%#llx per=%#llx\n", iTimerNo, pHpetTimer->u64Cmp, pHpetTimer->u64Period));
771
772 if (pThis->u64HpetConfig & HPET_CFG_ENABLE)
773 hpetProgramTimer(pDevIns, pThis, pHpetTimer, PDMDevHlpTimerGet(pDevIns, pHpetTimer->hTimer));
774 DEVHPET_UNLOCK_BOTH(pDevIns, pThis);
775 break;
776 }
777
778 /** @todo figure out how exactly it behaves wrt to HPET_TN_SETVAL */
779 case HPET_TN_CMP + 4: /* upper bits of comparator register */
780 {
781 DEVHPET_LOCK_BOTH_RETURN(pDevIns, pThis, VINF_IOM_R3_MMIO_WRITE);
782 uint64_t fConfig = ASMAtomicUoReadU64(&pHpetTimer->u64Config);
783
784 if (!hpet32bitTimerEx(fConfig))
785 {
786 Log(("HPET[%u]: write32 HPET_TN_CMP + 4: %#x (fCfg=%#RX32)\n", iTimerNo, u32NewValue, (uint32_t)fConfig));
787 if (fConfig & HPET_TN_PERIODIC)
788 ASMAtomicUoWriteU64(&pHpetTimer->u64Period, RT_MAKE_U64(RT_LO_U32(pHpetTimer->u64Period), u32NewValue));
789
790 if (!(fConfig & HPET_TN_PERIODIC) || (fConfig & HPET_TN_SETVAL))
791 ASMAtomicUoWriteU64(&pHpetTimer->u64Cmp, RT_MAKE_U64(RT_LO_U32(pHpetTimer->u64Cmp), u32NewValue));
792
793 ASMAtomicAndU64(&pHpetTimer->u64Config, ~HPET_TN_SETVAL);
794 Log2(("HPET[%u]: after32 HPET_TN_CMP+4: cmp=%#llx per=%#llx\n", iTimerNo, pHpetTimer->u64Cmp, pHpetTimer->u64Period));
795
796 if (pThis->u64HpetConfig & HPET_CFG_ENABLE)
797 hpetProgramTimer(pDevIns, pThis, pHpetTimer, PDMDevHlpTimerGet(pDevIns, pHpetTimer->hTimer));
798 }
799 else
800 Log(("HPET[%u]: write32 HPET_TN_CMP + 4: %#x - but timer is 32-bit!! (fCfg=%#RX32)\n", iTimerNo, u32NewValue, (uint32_t)fConfig));
801 DEVHPET_UNLOCK_BOTH(pDevIns, pThis);
802 break;
803 }
804
805 case HPET_TN_ROUTE:
806 Log(("HPET[%u]: write32 HPET_TN_ROUTE (ignored)\n", iTimerNo));
807 break;
808
809 case HPET_TN_ROUTE + 4:
810 Log(("HPET[%u]: write32 HPET_TN_ROUTE + 4 (ignored)\n", iTimerNo));
811 break;
812
813 default:
814 LogRelMax(10, ("HPET[%u]: Invalid timer register write: %d\n", iTimerNo, iTimerReg));
815 break;
816 }
817 }
818 else
819 LogRelMax(10, ("HPET: Using timer above configured range: %d (reg %#x)\n", iTimerNo, iTimerReg));
820 return VINF_SUCCESS;
821}
822
823
824/**
825 * 64-bit write to a HPET timer register.
826 *
827 * @returns Strict VBox status code.
828 *
829 * @param pDevIns The device instance.
830 * @param pThis The shared HPET state.
831 * @param iTimerNo The timer being written to.
832 * @param iTimerReg The register being written to.
833 * @param u64NewValue The value being written.
834 *
835 * @remarks The caller should not hold any locks.
836 */
837static VBOXSTRICTRC hpetTimerRegWrite64(PPDMDEVINS pDevIns, PHPET pThis, uint32_t iTimerNo,
838 uint32_t iTimerReg, uint64_t u64NewValue)
839{
840 Assert(!PDMDevHlpCritSectIsOwner(pDevIns, &pThis->CritSect));
841 Assert(!PDMDevHlpTimerIsLockOwner(pDevIns, pThis->aTimers[0].hTimer));
842 Assert(!(iTimerReg & 7));
843
844 if ( iTimerNo < HPET_CAP_GET_TIMERS(pThis->u32Capabilities)
845 && iTimerNo < RT_ELEMENTS(pThis->aTimers) ) /* Parfait - see above. */
846 {
847 PHPETTIMER pHpetTimer = &pThis->aTimers[iTimerNo];
848
849 switch (iTimerReg)
850 {
851 case HPET_TN_CFG:
852 /* The upper 32 bits are not writable, so join paths with the 32-bit version. */
853 return hpetTimerRegWrite32(pDevIns, pThis, iTimerNo, iTimerReg, (uint32_t)u64NewValue);
854
855 case HPET_TN_CMP:
856 {
857 DEVHPET_LOCK_BOTH_RETURN(pDevIns, pThis, VINF_IOM_R3_MMIO_WRITE);
858 uint64_t fConfig = ASMAtomicUoReadU64(&pHpetTimer->u64Config);
859 Log(("HPET[%u]: write64 HPET_TN_CMP: %#RX64 (fCfg=%#RX64)\n", iTimerNo, u64NewValue, (uint32_t)fConfig));
860
861 /** @todo not sure if this is right, but it is consistent with the 32-bit config
862 * change behaviour and defensive wrt mixups. */
863 if (!hpet32bitTimerEx(fConfig))
864 { /* likely */ }
865 else
866 u64NewValue = (uint32_t)u64NewValue;
867
868 if (fConfig & HPET_TN_PERIODIC)
869 ASMAtomicUoWriteU64(&pHpetTimer->u64Period, u64NewValue);
870
871 if (!(fConfig & HPET_TN_PERIODIC) || (fConfig & HPET_TN_SETVAL))
872 ASMAtomicUoWriteU64(&pHpetTimer->u64Cmp, u64NewValue);
873
874 ASMAtomicAndU64(&pHpetTimer->u64Config, ~HPET_TN_SETVAL);
875 Log2(("HPET[%u]: after64 HPET_TN_CMP cmp=%#llx per=%#llx\n", iTimerNo, pHpetTimer->u64Cmp, pHpetTimer->u64Period));
876
877 if (pThis->u64HpetConfig & HPET_CFG_ENABLE)
878 hpetProgramTimer(pDevIns, pThis, pHpetTimer, PDMDevHlpTimerGet(pDevIns, pHpetTimer->hTimer));
879 DEVHPET_UNLOCK_BOTH(pDevIns, pThis);
880 break;
881 }
882
883 case HPET_TN_ROUTE:
884 Log(("HPET[%u]: write64 HPET_TN_ROUTE (ignored)\n", iTimerNo));
885 break;
886
887 default:
888 LogRelMax(10, ("HPET[%u]: Invalid timer register write: %d\n", iTimerNo, iTimerReg));
889 break;
890 }
891 }
892 else
893 LogRelMax(10, ("HPET: Using timer above configured range: %d (reg %#x)\n", iTimerNo, iTimerReg));
894 return VINF_SUCCESS;
895}
896
897
898/* -=-=-=-=-=- Non-timer register accesses -=-=-=-=-=- */
899
900
901/**
902 * Read a 32-bit HPET register.
903 *
904 * @returns Strict VBox status code.
905 * @param pDevIns The device instance.
906 * @param pThis The shared HPET state.
907 * @param idxReg The register to read.
908 * @param pu32Value Where to return the register value.
909 *
910 * @remarks The caller must not own the device lock if HPET_COUNTER is read.
911 */
912static VBOXSTRICTRC hpetConfigRegRead32(PPDMDEVINS pDevIns, PHPET pThis, uint32_t idxReg, uint32_t *pu32Value)
913{
914 Assert(!PDMDevHlpCritSectIsOwner(pDevIns, &pThis->CritSect) || (idxReg != HPET_COUNTER && idxReg != HPET_COUNTER + 4));
915
916 uint32_t u32Value;
917 switch (idxReg)
918 {
919 case HPET_ID:
920 DEVHPET_LOCK_RETURN(pDevIns, pThis, VINF_IOM_R3_MMIO_READ);
921 u32Value = pThis->u32Capabilities;
922 DEVHPET_UNLOCK(pDevIns, pThis);
923 Log(("read HPET_ID: %#x\n", u32Value));
924 break;
925
926 case HPET_PERIOD:
927 u32Value = pThis->fIch9 ? HPET_CLK_PERIOD_ICH9 : HPET_CLK_PERIOD_PIIX;
928 Log(("read HPET_PERIOD: %#x\n", u32Value));
929 break;
930
931 case HPET_CFG:
932 DEVHPET_LOCK_RETURN(pDevIns, pThis, VINF_IOM_R3_MMIO_READ);
933 u32Value = (uint32_t)pThis->u64HpetConfig;
934 DEVHPET_UNLOCK(pDevIns, pThis);
935 Log(("read HPET_CFG: %#x\n", u32Value));
936 break;
937
938 case HPET_CFG + 4:
939 DEVHPET_LOCK_RETURN(pDevIns, pThis, VINF_IOM_R3_MMIO_READ);
940 u32Value = (uint32_t)(pThis->u64HpetConfig >> 32);
941 DEVHPET_UNLOCK(pDevIns, pThis);
942 Log(("read of HPET_CFG + 4: %#x\n", u32Value));
943 break;
944
945 case HPET_COUNTER:
946 case HPET_COUNTER + 4:
947 {
948 /** @todo We don't technically need to sit on the virtualsync lock here to
949 * read it, but it helps wrt quality... */
950 DEVHPET_LOCK_BOTH_RETURN(pDevIns, pThis, VINF_IOM_R3_MMIO_READ);
951
952 uint64_t u64Ticks;
953 if (pThis->u64HpetConfig & HPET_CFG_ENABLE)
954 {
955 uint64_t const tsNow = PDMDevHlpTimerGet(pDevIns, pThis->aTimers[0].hTimer);
956 PDMDevHlpTimerUnlockClock(pDevIns, pThis->aTimers[0].hTimer);
957 u64Ticks = hpetGetTicksEx(pThis, tsNow);
958 }
959 else
960 {
961 PDMDevHlpTimerUnlockClock(pDevIns, pThis->aTimers[0].hTimer);
962 u64Ticks = pThis->u64HpetCounter;
963 }
964
965 STAM_REL_COUNTER_INC(&pThis->StatCounterRead4Byte);
966 DEVHPET_UNLOCK(pDevIns, pThis);
967
968 /** @todo is it correct? */
969 u32Value = idxReg == HPET_COUNTER ? (uint32_t)u64Ticks : (uint32_t)(u64Ticks >> 32);
970 Log(("read HPET_COUNTER: %s part value %x (%#llx)\n", (idxReg == HPET_COUNTER) ? "low" : "high", u32Value, u64Ticks));
971 break;
972 }
973
974 case HPET_STATUS:
975 DEVHPET_LOCK_RETURN(pDevIns, pThis, VINF_IOM_R3_MMIO_READ);
976 u32Value = (uint32_t)pThis->u64Isr;
977 DEVHPET_UNLOCK(pDevIns, pThis);
978 Log(("read HPET_STATUS: %#x\n", u32Value));
979 break;
980
981 default:
982 Log(("invalid HPET register read: %x\n", idxReg));
983 u32Value = 0;
984 break;
985 }
986
987 *pu32Value = u32Value;
988 return VINF_SUCCESS;
989}
990
991
992/**
993 * 32-bit write to a config register.
994 *
995 * @returns Strict VBox status code.
996 *
997 * @param pDevIns The device instance.
998 * @param pThis The shared HPET state.
999 * @param idxReg The register being written to.
1000 * @param u32NewValue The value being written.
1001 *
1002 * @remarks The caller should not hold the device lock, unless it also holds
1003 * the TM lock.
1004 */
1005static VBOXSTRICTRC hpetConfigRegWrite32(PPDMDEVINS pDevIns, PHPET pThis, uint32_t idxReg, uint32_t u32NewValue)
1006{
1007 Assert(!PDMDevHlpCritSectIsOwner(pDevIns, &pThis->CritSect) || PDMDevHlpTimerIsLockOwner(pDevIns, pThis->aTimers[0].hTimer));
1008
1009 VBOXSTRICTRC rc = VINF_SUCCESS;
1010 switch (idxReg)
1011 {
1012 case HPET_ID:
1013 case HPET_ID + 4:
1014 {
1015 Log(("write HPET_ID, useless\n"));
1016 break;
1017 }
1018
1019 case HPET_CFG:
1020 {
1021 DEVHPET_LOCK_BOTH_RETURN(pDevIns, pThis, VINF_IOM_R3_MMIO_WRITE);
1022 uint32_t const iOldValue = (uint32_t)(pThis->u64HpetConfig);
1023 Log(("write HPET_CFG: %x (old %x)\n", u32NewValue, iOldValue));
1024
1025 /*
1026 * This check must be here, before actual update, as hpetLegacyMode
1027 * may request retry in R3 - so we must keep state intact.
1028 */
1029 if ((iOldValue ^ u32NewValue) & HPET_CFG_LEGACY)
1030 {
1031#ifdef IN_RING3
1032 PHPETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PHPETCC);
1033 if (pThisCC->pHpetHlp != NULL)
1034 {
1035 rc = pThisCC->pHpetHlp->pfnSetLegacyMode(pDevIns, RT_BOOL(u32NewValue & HPET_CFG_LEGACY));
1036 if (rc != VINF_SUCCESS)
1037 {
1038 DEVHPET_UNLOCK_BOTH(pDevIns, pThis);
1039 break;
1040 }
1041 }
1042#else
1043 rc = VINF_IOM_R3_MMIO_WRITE;
1044 DEVHPET_UNLOCK_BOTH(pDevIns, pThis);
1045 break;
1046#endif
1047 }
1048
1049 /* Updating it using an atomic write just to be on the safe side. */
1050 ASMAtomicWriteU64(&pThis->u64HpetConfig, hpetUpdateMasked(u32NewValue, iOldValue, HPET_CFG_WRITE_MASK));
1051
1052 uint32_t const cTimers = RT_MIN(HPET_CAP_GET_TIMERS(pThis->u32Capabilities), RT_ELEMENTS(pThis->aTimers));
1053 if (hpetBitJustSet(iOldValue, u32NewValue, HPET_CFG_ENABLE))
1054 {
1055 /*
1056 * Enable main counter and interrupt generation.
1057 */
1058 uint64_t u64TickLimit = pThis->fIch9 ? HPET_TICKS_IN_100YR_ICH9 : HPET_TICKS_IN_100YR_PIIX;
1059 if (pThis->u64HpetCounter <= u64TickLimit)
1060 pThis->u64HpetOffset = hpetTicksToNs(pThis, pThis->u64HpetCounter);
1061 else
1062 {
1063 LogRelMax(10, ("HPET: Counter set more than 100 years in the future, reducing.\n"));
1064 pThis->u64HpetOffset = 1000000LL * 60 * 60 * 24 * 365 * 100;
1065 }
1066
1067 uint64_t const tsNow = PDMDevHlpTimerGet(pDevIns, pThis->aTimers[0].hTimer);
1068 pThis->u64HpetOffset -= tsNow;
1069
1070 for (uint32_t i = 0; i < cTimers; i++)
1071 if (pThis->aTimers[i].u64Cmp != hpetInvalidValue(&pThis->aTimers[i]))
1072 hpetProgramTimer(pDevIns, pThis, &pThis->aTimers[i], tsNow);
1073 }
1074 else if (hpetBitJustCleared(iOldValue, u32NewValue, HPET_CFG_ENABLE))
1075 {
1076 /*
1077 * Halt main counter and disable interrupt generation.
1078 */
1079 pThis->u64HpetCounter = hpetGetTicksEx(pThis, PDMDevHlpTimerGet(pDevIns, pThis->aTimers[0].hTimer));
1080 for (uint32_t i = 0; i < cTimers; i++)
1081 PDMDevHlpTimerStop(pDevIns, pThis->aTimers[i].hTimer);
1082 }
1083
1084 DEVHPET_UNLOCK_BOTH(pDevIns, pThis);
1085 break;
1086 }
1087
1088 case HPET_CFG + 4:
1089 {
1090/** @todo r=bird: Is the whole upper part of the config register really
1091 * writable? Only 2 bits are writable in the lower part... */
1092 DEVHPET_LOCK_RETURN(pDevIns, pThis, VINF_IOM_R3_MMIO_WRITE);
1093 pThis->u64HpetConfig = hpetUpdateMasked((uint64_t)u32NewValue << 32,
1094 pThis->u64HpetConfig,
1095 UINT64_C(0xffffffff00000000));
1096 Log(("write HPET_CFG + 4: %x -> %#llx\n", u32NewValue, pThis->u64HpetConfig));
1097 DEVHPET_UNLOCK(pDevIns, pThis);
1098 break;
1099 }
1100
1101 case HPET_STATUS:
1102 {
1103 DEVHPET_LOCK_RETURN(pDevIns, pThis, VINF_IOM_R3_MMIO_WRITE);
1104 /* Clear ISR for all set bits in u32NewValue, see p. 14 of the HPET spec. */
1105 pThis->u64Isr &= ~((uint64_t)u32NewValue);
1106 Log(("write HPET_STATUS: %x -> ISR=%#llx\n", u32NewValue, pThis->u64Isr));
1107 DEVHPET_UNLOCK(pDevIns, pThis);
1108 break;
1109 }
1110
1111 case HPET_STATUS + 4:
1112 {
1113 Log(("write HPET_STATUS + 4: %x\n", u32NewValue));
1114 if (u32NewValue != 0)
1115 LogRelMax(10, ("HPET: Writing HPET_STATUS + 4 with non-zero, ignored\n"));
1116 break;
1117 }
1118
1119 case HPET_COUNTER:
1120 {
1121 STAM_REL_COUNTER_INC(&pThis->StatCounterWriteLow);
1122 DEVHPET_LOCK_RETURN(pDevIns, pThis, VINF_IOM_R3_MMIO_WRITE);
1123 pThis->u64HpetCounter = RT_MAKE_U64(u32NewValue, RT_HI_U32(pThis->u64HpetCounter));
1124/** @todo how is this supposed to work if the HPET is enabled? */
1125 Log(("write HPET_COUNTER: %#x -> %llx\n", u32NewValue, pThis->u64HpetCounter));
1126 DEVHPET_UNLOCK(pDevIns, pThis);
1127 break;
1128 }
1129
1130 case HPET_COUNTER + 4:
1131 {
1132 STAM_REL_COUNTER_INC(&pThis->StatCounterWriteHigh);
1133 DEVHPET_LOCK_RETURN(pDevIns, pThis, VINF_IOM_R3_MMIO_WRITE);
1134 pThis->u64HpetCounter = RT_MAKE_U64(RT_LO_U32(pThis->u64HpetCounter), u32NewValue);
1135 Log(("write HPET_COUNTER + 4: %#x -> %llx\n", u32NewValue, pThis->u64HpetCounter));
1136 DEVHPET_UNLOCK(pDevIns, pThis);
1137 break;
1138 }
1139
1140 default:
1141 LogRelMax(10, ("HPET: Invalid HPET config write: %x\n", idxReg));
1142 break;
1143 }
1144
1145 return rc;
1146}
1147
1148
1149/* -=-=-=-=-=- MMIO callbacks -=-=-=-=-=- */
1150
1151
1152/**
1153 * @callback_method_impl{FNIOMMMIONEWREAD}
1154 */
1155static DECLCALLBACK(VBOXSTRICTRC) hpetMMIORead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void *pv, unsigned cb)
1156{
1157 HPET *pThis = PDMDEVINS_2_DATA(pDevIns, HPET*);
1158 NOREF(pvUser);
1159 Assert(cb == 4 || cb == 8);
1160 Assert(!(off & (cb - 1)));
1161
1162 LogFlow(("hpetMMIORead (%d): %RGp\n", cb, off));
1163
1164 VBOXSTRICTRC rc;
1165 if (cb == 4)
1166 {
1167 /*
1168 * 4-byte access.
1169 */
1170 if (off >= 0x100 && off < 0x400)
1171 {
1172 *(uint32_t *)pv = hpetTimerRegRead32(pThis,
1173 (uint32_t)(off - 0x100) / 0x20,
1174 (uint32_t)(off - 0x100) % 0x20);
1175 rc = VINF_SUCCESS;
1176 }
1177 else
1178 rc = hpetConfigRegRead32(pDevIns, pThis, off, (uint32_t *)pv);
1179 }
1180 else
1181 {
1182 /*
1183 * 8-byte access - Split the access except for timing sensitive registers.
1184 * The others assume the protection of the lock.
1185 */
1186 PRTUINT64U pValue = (PRTUINT64U)pv;
1187 if (off == HPET_COUNTER)
1188 {
1189 /** @todo We don't technically need to sit on the virtualsync lock here to
1190 * read it, but it helps wrt quality... */
1191 /* When reading HPET counter we must read it in a single read,
1192 to avoid unexpected time jumps on 32-bit overflow. */
1193 DEVHPET_LOCK_BOTH_RETURN(pDevIns, pThis, VINF_IOM_R3_MMIO_READ);
1194
1195 if (pThis->u64HpetConfig & HPET_CFG_ENABLE)
1196 {
1197 uint64_t const tsNow = PDMDevHlpTimerGet(pDevIns, pThis->aTimers[0].hTimer);
1198 PDMDevHlpTimerUnlockClock(pDevIns, pThis->aTimers[0].hTimer);
1199 pValue->u = hpetGetTicksEx(pThis, tsNow);
1200 }
1201 else
1202 {
1203 PDMDevHlpTimerUnlockClock(pDevIns, pThis->aTimers[0].hTimer);
1204 pValue->u = pThis->u64HpetCounter;
1205 }
1206
1207 STAM_REL_COUNTER_INC(&pThis->StatCounterRead8Byte);
1208 DEVHPET_UNLOCK(pDevIns, pThis);
1209 rc = VINF_SUCCESS;
1210 }
1211 else
1212 {
1213 if (off >= 0x100 && off < 0x400)
1214 {
1215 uint32_t iTimer = (uint32_t)(off - 0x100) / 0x20;
1216 uint32_t iTimerReg = (uint32_t)(off - 0x100) % 0x20;
1217 Assert(!(iTimerReg & 7));
1218 pValue->u = hpetTimerRegRead64(pThis, iTimer, iTimerReg);
1219 rc = VINF_SUCCESS;
1220 }
1221 else
1222 {
1223 /* for most 8-byte accesses we just split them, happens under lock anyway. */
1224 DEVHPET_LOCK_RETURN(pDevIns, pThis, VINF_IOM_R3_MMIO_READ);
1225 rc = hpetConfigRegRead32(pDevIns, pThis, off, &pValue->s.Lo);
1226 if (rc == VINF_SUCCESS)
1227 rc = hpetConfigRegRead32(pDevIns, pThis, off + 4, &pValue->s.Hi);
1228 DEVHPET_UNLOCK(pDevIns, pThis);
1229 }
1230 }
1231 }
1232 return rc;
1233}
1234
1235
1236/**
1237 * @callback_method_impl{FNIOMMMIONEWWRITE}
1238 */
1239static DECLCALLBACK(VBOXSTRICTRC) hpetMMIOWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void const *pv, unsigned cb)
1240{
1241 HPET *pThis = PDMDEVINS_2_DATA(pDevIns, HPET*);
1242 LogFlow(("hpetMMIOWrite: cb=%u reg=%RGp val=%llx\n",
1243 cb, off, cb == 4 ? *(uint32_t *)pv : cb == 8 ? *(uint64_t *)pv : 0xdeadbeef));
1244 NOREF(pvUser);
1245 Assert(cb == 4 || cb == 8);
1246 Assert(!(off & (cb - 1)));
1247
1248 VBOXSTRICTRC rc;
1249 if (cb == 4)
1250 {
1251 if (off >= 0x100 && off < 0x400)
1252 rc = hpetTimerRegWrite32(pDevIns, pThis,
1253 (uint32_t)(off - 0x100) / 0x20,
1254 (uint32_t)(off - 0x100) % 0x20,
1255 *(uint32_t const *)pv);
1256 else
1257 rc = hpetConfigRegWrite32(pDevIns, pThis, off, *(uint32_t const *)pv);
1258 }
1259 else
1260 {
1261 /*
1262 * 8-byte access.
1263 */
1264 if (off >= 0x100 && off < 0x400)
1265 rc = hpetTimerRegWrite64(pDevIns, pThis,
1266 (uint32_t)(off - 0x100) / 0x20,
1267 (uint32_t)(off - 0x100) % 0x20,
1268 *(uint64_t const *)pv);
1269 else
1270 {
1271 /* Split the access and rely on the locking to prevent trouble. */
1272 RTUINT64U uValue;
1273 uValue.u = *(uint64_t const *)pv;
1274 DEVHPET_LOCK_BOTH_RETURN(pDevIns, pThis, VINF_IOM_R3_MMIO_WRITE);
1275 rc = hpetConfigRegWrite32(pDevIns, pThis, off, uValue.s.Lo);
1276 if (RT_LIKELY(rc == VINF_SUCCESS))
1277 rc = hpetConfigRegWrite32(pDevIns, pThis, off + 4, uValue.s.Hi);
1278 DEVHPET_UNLOCK_BOTH(pDevIns, pThis);
1279 }
1280 }
1281
1282 return rc;
1283}
1284
1285#ifdef IN_RING3
1286
1287/* -=-=-=-=-=- Timer Callback Processing -=-=-=-=-=- */
1288
1289/**
1290 * Gets the IRQ of an HPET timer.
1291 *
1292 * @returns IRQ number.
1293 * @param pThis The shared HPET state.
1294 * @param pHpetTimer The HPET timer.
1295 * @param fConfig The HPET timer config value.
1296 */
1297DECLINLINE(uint32_t) hpetR3TimerGetIrq(PHPET pThis, PCHPETTIMER pHpetTimer, uint64_t fConfig)
1298{
1299 /*
1300 * Per spec, in legacy mode the HPET timers are wired as follows:
1301 * timer 0: IRQ0 for PIC and IRQ2 for APIC
1302 * timer 1: IRQ8 for both PIC and APIC
1303 *
1304 * ISA IRQ delivery logic will take care of correct delivery
1305 * to the different ICs.
1306 */
1307 if ( pHpetTimer->idxTimer <= 1
1308 && (pThis->u64HpetConfig & HPET_CFG_LEGACY))
1309 return pHpetTimer->idxTimer == 0 ? 0 : 8;
1310
1311 return (fConfig & HPET_TN_INT_ROUTE_MASK) >> HPET_TN_INT_ROUTE_SHIFT;
1312}
1313
1314
1315/**
1316 * @callback_method_impl{FNTMTIMERDEV, Device timer callback function.}
1317 *
1318 * @note Only the virtual sync lock is held when called.
1319 */
1320static DECLCALLBACK(void) hpetR3Timer(PPDMDEVINS pDevIns, TMTIMERHANDLE hTimer, void *pvUser)
1321{
1322 PHPET pThis = PDMDEVINS_2_DATA(pDevIns, PHPET);
1323 PHPETTIMER pHpetTimer = (HPETTIMER *)pvUser;
1324
1325 /*
1326 * Read the timer configuration values we need first.
1327 *
1328 * The comparator and period are only written while owning the virtual sync
1329 * lock, so we don't run any risk there. The configuration register is
1330 * written with only the device lock, so must be a bit more careful with it.
1331 */
1332 uint64_t uCmp = ASMAtomicUoReadU64(&pHpetTimer->u64Cmp);
1333 uint64_t const uPeriod = ASMAtomicUoReadU64(&pHpetTimer->u64Period);
1334 uint64_t const fConfig = ASMAtomicUoReadU64(&pHpetTimer->u64Config);
1335 Assert(hTimer == pHpetTimer->hTimer);
1336
1337 if (fConfig & HPET_TN_PERIODIC)
1338 {
1339 if (uPeriod)
1340 {
1341 uint64_t const tsNow = PDMDevHlpTimerGet(pDevIns, pHpetTimer->hTimer);
1342 uint64_t const uHpetNow = hpetGetTicksEx(pThis, tsNow);
1343 uCmp = hpetAdjustComparator(pHpetTimer, fConfig, uCmp, uPeriod, uHpetNow);
1344 uint64_t const cTicksDiff = hpetComputeDiff(fConfig, uCmp, uHpetNow);
1345 uint64_t const u64TickLimit = pThis->fIch9 ? HPET_TICKS_IN_100YR_ICH9 : HPET_TICKS_IN_100YR_PIIX;
1346 if (cTicksDiff <= u64TickLimit)
1347 {
1348 uint64_t const tsDeadline = tsNow + hpetTicksToNs(pThis, cTicksDiff);
1349 Log4(("HPET[%u]: periodic: next in %llu\n", pHpetTimer->idxTimer, tsDeadline));
1350 PDMDevHlpTimerSet(pDevIns, hTimer, tsDeadline);
1351 STAM_REL_COUNTER_INC(&pHpetTimer->StatSetTimer);
1352 }
1353 else
1354 LogRelMax(10, ("HPET[%u]: Not scheduling periodic interrupt more than 100 years in the future.\n",
1355 pHpetTimer->idxTimer));
1356 }
1357 }
1358 /* For 32-bit non-periodic timers, generate wrap-around interrupts. */
1359 else if (pHpetTimer->u8Wrap && hpet32bitTimerEx(fConfig))
1360 {
1361 pHpetTimer->u8Wrap = 0; /* (only modified while owning the virtual sync lock) */
1362 uint64_t const tsNow = PDMDevHlpTimerGet(pDevIns, hTimer);
1363 uint64_t const uHpetNow = nsToHpetTicks(pThis, tsNow + pThis->u64HpetOffset);
1364 uint64_t const cTicksDiff = hpetComputeDiff(fConfig, uCmp, uHpetNow);
1365 uint64_t const tsDeadline = tsNow + hpetTicksToNs(pThis, cTicksDiff);
1366 Log4(("HPET[%u]: post-wrap deadline: %llu\n", pHpetTimer->idxTimer, tsDeadline));
1367 PDMDevHlpTimerSet(pDevIns, pHpetTimer->hTimer, tsDeadline);
1368 }
1369
1370 /*
1371 * IRQ update.
1372 */
1373 if ( (fConfig & HPET_TN_ENABLE)
1374 && (pThis->u64HpetConfig & HPET_CFG_ENABLE))
1375 {
1376 AssertCompile(HPET_TN_INT_TYPE == 2);
1377
1378 /* We trigger flip/flop in edge-triggered mode and do nothing in
1379 level-triggered mode yet. */
1380 if ((fConfig & HPET_TN_INT_TYPE) == HPET_TIMER_TYPE_EDGE)
1381 {
1382 PHPETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PHPETCC);
1383 AssertReturnVoid(pThisCC);
1384
1385 uint32_t const uIrq = hpetR3TimerGetIrq(pThis, pHpetTimer, fConfig);
1386 Log4(("HPET[%u]: raising IRQ %u\n", pHpetTimer->idxTimer, uIrq));
1387
1388 pThisCC->pHpetHlp->pfnSetIrq(pDevIns, uIrq, PDM_IRQ_LEVEL_FLIP_FLOP);
1389 STAM_REL_COUNTER_INC(&pHpetTimer->StatSetIrq);
1390 }
1391 /* ISR bits are only set in level-triggered mode. */
1392 else
1393 {
1394 Assert((fConfig & HPET_TN_INT_TYPE) == HPET_TIMER_TYPE_LEVEL);
1395 ASMAtomicOrU64(&pThis->u64Isr, RT_BIT_64(pHpetTimer->idxTimer));
1396 /** @todo implement IRQs in level-triggered mode */
1397 }
1398 }
1399
1400}
1401
1402
1403/* -=-=-=-=-=- DBGF Info Handlers -=-=-=-=-=- */
1404
1405
1406/**
1407 * @callback_method_impl{FNDBGFHANDLERDEV}
1408 */
1409static DECLCALLBACK(void) hpetR3Info(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
1410{
1411 PHPET pThis = PDMDEVINS_2_DATA(pDevIns, PHPET);
1412 NOREF(pszArgs);
1413
1414 pHlp->pfnPrintf(pHlp,
1415 "HPET status:\n"
1416 " config=%016RX64 isr=%016RX64\n"
1417 " offset=%016RX64 counter=%016RX64 frequency=%u fs\n"
1418 " legacy-mode=%s timer-count=%u\n",
1419 pThis->u64HpetConfig, pThis->u64Isr,
1420 pThis->u64HpetOffset, pThis->u64HpetCounter, pThis->fIch9 ? HPET_CLK_PERIOD_ICH9 : HPET_CLK_PERIOD_PIIX,
1421 !!(pThis->u64HpetConfig & HPET_CFG_LEGACY) ? "on " : "off",
1422 HPET_CAP_GET_TIMERS(pThis->u32Capabilities));
1423 pHlp->pfnPrintf(pHlp,
1424 "Timers:\n");
1425 for (unsigned i = 0; i < RT_ELEMENTS(pThis->aTimers); i++)
1426 {
1427 static const struct
1428 {
1429 const char *psz;
1430 uint32_t cch;
1431 uint32_t fFlags;
1432 } s_aFlags[] =
1433 {
1434 { RT_STR_TUPLE(" lvl"), HPET_TN_INT_TYPE },
1435 { RT_STR_TUPLE(" en"), HPET_TN_ENABLE },
1436 { RT_STR_TUPLE(" per"), HPET_TN_PERIODIC },
1437 { RT_STR_TUPLE(" cap_per"), HPET_TN_PERIODIC_CAP },
1438 { RT_STR_TUPLE(" cap_64"), HPET_TN_SIZE_CAP },
1439 { RT_STR_TUPLE(" setval"), HPET_TN_SETVAL },
1440 { RT_STR_TUPLE(" 32b"), HPET_TN_32BIT },
1441 };
1442 char szTmp[64];
1443 uint64_t fCfg = pThis->aTimers[i].u64Config;
1444 size_t off = 0;
1445 for (unsigned j = 0; j < RT_ELEMENTS(s_aFlags); j++)
1446 if (fCfg & s_aFlags[j].fFlags)
1447 {
1448 memcpy(&szTmp[off], s_aFlags[j].psz, s_aFlags[j].cch);
1449 off += s_aFlags[j].cch;
1450 fCfg &= ~(uint64_t)s_aFlags[j].fFlags;
1451 }
1452 szTmp[off] = '\0';
1453 Assert(off < sizeof(szTmp));
1454
1455 pHlp->pfnPrintf(pHlp,
1456 " %d: comparator=%016RX64 accumulator=%016RX64 (%RU64 ns)\n"
1457 " config=%016RX64 irq=%d%s\n",
1458 pThis->aTimers[i].idxTimer,
1459 pThis->aTimers[i].u64Cmp,
1460 pThis->aTimers[i].u64Period,
1461 pThis->aTimers[i].u64Period < (pThis->fIch9 ? HPET_TICKS_IN_100YR_ICH9 : HPET_TICKS_IN_100YR_PIIX)
1462 ? hpetTicksToNs(pThis, pThis->aTimers[i].u64Period) : UINT64_MAX,
1463 pThis->aTimers[i].u64Config,
1464 hpetR3TimerGetIrq(pThis, &pThis->aTimers[i], pThis->aTimers[i].u64Config),
1465 szTmp);
1466 }
1467}
1468
1469
1470/* -=-=-=-=-=- Saved State -=-=-=-=-=- */
1471
1472
1473/**
1474 * @callback_method_impl{FNSSMDEVLIVEEXEC}
1475 */
1476static DECLCALLBACK(int) hpetR3LiveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uPass)
1477{
1478 PHPET pThis = PDMDEVINS_2_DATA(pDevIns, PHPET);
1479 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
1480 NOREF(uPass);
1481
1482 pHlp->pfnSSMPutU8(pSSM, HPET_CAP_GET_TIMERS(pThis->u32Capabilities));
1483
1484 return VINF_SSM_DONT_CALL_AGAIN;
1485}
1486
1487
1488/**
1489 * @callback_method_impl{FNSSMDEVSAVEEXEC}
1490 */
1491static DECLCALLBACK(int) hpetR3SaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
1492{
1493 PHPET pThis = PDMDEVINS_2_DATA(pDevIns, PHPET);
1494 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
1495
1496 /*
1497 * The config.
1498 */
1499 hpetR3LiveExec(pDevIns, pSSM, SSM_PASS_FINAL);
1500
1501 /*
1502 * The state.
1503 */
1504 uint32_t const cTimers = HPET_CAP_GET_TIMERS(pThis->u32Capabilities);
1505 AssertReturn(cTimers <= RT_ELEMENTS(pThis->aTimers), VERR_OUT_OF_RANGE);
1506 for (uint32_t iTimer = 0; iTimer < cTimers; iTimer++)
1507 {
1508 PHPETTIMER pHpetTimer = &pThis->aTimers[iTimer];
1509 PDMDevHlpTimerSave(pDevIns, pHpetTimer->hTimer, pSSM);
1510 pHlp->pfnSSMPutU8(pSSM, pHpetTimer->u8Wrap);
1511 pHlp->pfnSSMPutU64(pSSM, pHpetTimer->u64Config);
1512 pHlp->pfnSSMPutU64(pSSM, pHpetTimer->u64Cmp);
1513 pHlp->pfnSSMPutU64(pSSM, pHpetTimer->u64Fsb);
1514 pHlp->pfnSSMPutU64(pSSM, pHpetTimer->u64Period);
1515 }
1516
1517 pHlp->pfnSSMPutU64(pSSM, pThis->u64HpetOffset);
1518 uint64_t u64CapPer = RT_MAKE_U64(pThis->u32Capabilities, pThis->fIch9 ? HPET_CLK_PERIOD_ICH9 : HPET_CLK_PERIOD_PIIX);
1519 pHlp->pfnSSMPutU64(pSSM, u64CapPer);
1520 pHlp->pfnSSMPutU64(pSSM, pThis->u64HpetConfig);
1521 pHlp->pfnSSMPutU64(pSSM, pThis->u64Isr);
1522 return pHlp->pfnSSMPutU64(pSSM, pThis->u64HpetCounter);
1523}
1524
1525
1526/**
1527 * @callback_method_impl{FNSSMDEVLOADEXEC}
1528 */
1529static DECLCALLBACK(int) hpetR3LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
1530{
1531 PHPET pThis = PDMDEVINS_2_DATA(pDevIns, PHPET);
1532 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
1533
1534 /*
1535 * Version checks.
1536 */
1537 if (uVersion == HPET_SAVED_STATE_VERSION_EMPTY)
1538 return VINF_SUCCESS;
1539 if ( uVersion != HPET_SAVED_STATE_VERSION
1540 && uVersion != HPET_SAVED_STATE_VERSION_PRE_TIMER)
1541 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
1542
1543 /*
1544 * The config.
1545 */
1546 uint8_t cTimers;
1547 int rc = pHlp->pfnSSMGetU8(pSSM, &cTimers);
1548 AssertRCReturn(rc, rc);
1549 if (cTimers > RT_ELEMENTS(pThis->aTimers))
1550 return pHlp->pfnSSMSetCfgError(pSSM, RT_SRC_POS, N_("Config mismatch - too many timers: saved=%#x config=%#x"),
1551 cTimers, RT_ELEMENTS(pThis->aTimers));
1552
1553 if (uPass != SSM_PASS_FINAL)
1554 return VINF_SUCCESS;
1555
1556 /*
1557 * The state.
1558 */
1559 for (uint32_t iTimer = 0; iTimer < cTimers; iTimer++)
1560 {
1561 PHPETTIMER pHpetTimer = &pThis->aTimers[iTimer];
1562 PDMDevHlpTimerLoad(pDevIns, pHpetTimer->hTimer, pSSM);
1563 pHlp->pfnSSMGetU8(pSSM, &pHpetTimer->u8Wrap);
1564 pHlp->pfnSSMGetU64(pSSM, &pHpetTimer->u64Config);
1565 pHlp->pfnSSMGetU64(pSSM, &pHpetTimer->u64Cmp);
1566 pHlp->pfnSSMGetU64(pSSM, &pHpetTimer->u64Fsb);
1567 pHlp->pfnSSMGetU64(pSSM, &pHpetTimer->u64Period);
1568 }
1569
1570 pHlp->pfnSSMGetU64(pSSM, &pThis->u64HpetOffset);
1571 uint64_t u64CapPer;
1572 pHlp->pfnSSMGetU64(pSSM, &u64CapPer);
1573 pHlp->pfnSSMGetU64(pSSM, &pThis->u64HpetConfig);
1574 pHlp->pfnSSMGetU64(pSSM, &pThis->u64Isr);
1575 rc = pHlp->pfnSSMGetU64(pSSM, &pThis->u64HpetCounter);
1576 if (RT_FAILURE(rc))
1577 return rc;
1578
1579 /* Older saved state have an off-by-1 timer count bug. */
1580 uint8_t cCapTimers = HPET_CAP_GET_TIMERS(RT_LO_U32(u64CapPer));
1581 if ( uVersion <= HPET_SAVED_STATE_VERSION_PRE_TIMER
1582 && cCapTimers > 0 /* Paranoia */)
1583 --cCapTimers;
1584
1585 /* Verify capability reported timer count matches timer count in the saved state field. */
1586 if (cCapTimers != cTimers)
1587 return pHlp->pfnSSMSetCfgError(pSSM, RT_SRC_POS, N_("Capabilities does not match timer count: cTimers=%#x caps=%#x"),
1588 cTimers, cCapTimers);
1589 if (HPET_CAP_GET_TIMERS(RT_LO_U32(u64CapPer)) > RT_ELEMENTS(pThis->aTimers))
1590 return pHlp->pfnSSMSetCfgError(pSSM, RT_SRC_POS, N_("Config mismatch - too many timers in capability register: CAP=%#x => %u times, max %u"),
1591 RT_LO_U32(u64CapPer), (unsigned)HPET_CAP_GET_TIMERS(RT_LO_U32(u64CapPer)), RT_ELEMENTS(pThis->aTimers));
1592
1593 pThis->u32Capabilities = RT_LO_U32(u64CapPer);
1594 uint32_t const uExpectedPeriod = pThis->fIch9 ? HPET_CLK_PERIOD_ICH9 : HPET_CLK_PERIOD_PIIX;
1595 if (RT_HI_U32(u64CapPer) != uExpectedPeriod)
1596 return pHlp->pfnSSMSetCfgError(pSSM, RT_SRC_POS, N_("Config mismatch - Expected period %RU32 fs, loaded %RU32 fs"),
1597 uExpectedPeriod, RT_HI_U32(u64CapPer));
1598
1599 /*
1600 * Set the timer frequency hints.
1601 */
1602 rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_IGNORED);
1603 AssertRCReturn(rc, rc);
1604
1605 for (uint32_t iTimer = 0; iTimer < cTimers; iTimer++)
1606 {
1607 PHPETTIMER pHpetTimer = &pThis->aTimers[iTimer];
1608 if (PDMDevHlpTimerIsActive(pDevIns, pHpetTimer->hTimer))
1609 hpetTimerSetFrequencyHint(pDevIns, pThis, pHpetTimer, pHpetTimer->u64Config, pHpetTimer->u64Period);
1610 }
1611
1612 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
1613 return VINF_SUCCESS;
1614}
1615
1616
1617/* -=-=-=-=-=- PDMDEVREG -=-=-=-=-=- */
1618
1619
1620/**
1621 * @interface_method_impl{PDMDEVREG,pfnRelocate}
1622 */
1623static DECLCALLBACK(void) hpetR3Relocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
1624{
1625 PHPETRC pThisRC = PDMINS_2_DATA_RC(pDevIns, PHPETRC);
1626 LogFlow(("hpetR3Relocate:\n"));
1627
1628 pThisRC->pHpetHlp += offDelta;
1629}
1630
1631
1632/**
1633 * @interface_method_impl{PDMDEVREG,pfnReset}
1634 */
1635static DECLCALLBACK(void) hpetR3Reset(PPDMDEVINS pDevIns)
1636{
1637 PHPET pThis = PDMDEVINS_2_DATA(pDevIns, PHPET);
1638 PHPETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PHPETCC);
1639 LogFlow(("hpetR3Reset:\n"));
1640
1641 /*
1642 * The timers first.
1643 */
1644 PDMDevHlpTimerLockClock(pDevIns, pThis->aTimers[0].hTimer, VERR_IGNORED);
1645 for (unsigned i = 0; i < RT_ELEMENTS(pThis->aTimers); i++)
1646 {
1647 PHPETTIMER pHpetTimer = &pThis->aTimers[i];
1648 Assert(pHpetTimer->idxTimer == i);
1649 PDMDevHlpTimerStop(pDevIns, pHpetTimer->hTimer);
1650
1651 /* capable of periodic operations and 64-bits */
1652 uint64_t fConfig;
1653 if (pThis->fIch9)
1654 fConfig = i == 0 ? HPET_TN_PERIODIC_CAP | HPET_TN_SIZE_CAP : 0;
1655 else
1656 fConfig = HPET_TN_PERIODIC_CAP | HPET_TN_SIZE_CAP;
1657
1658 /* We can do all IRQs */
1659 uint32_t u32RoutingCap = 0xffffffff;
1660 fConfig |= ((uint64_t)u32RoutingCap) << HPET_TN_INT_ROUTE_CAP_SHIFT;
1661 ASMAtomicWriteU64(&pHpetTimer->u64Config, fConfig);
1662 pHpetTimer->u64Period = 0;
1663 pHpetTimer->u8Wrap = 0;
1664 pHpetTimer->u64Cmp = hpetInvalidValue(pHpetTimer);
1665 }
1666 PDMDevHlpTimerUnlockClock(pDevIns, pThis->aTimers[0].hTimer);
1667
1668 /*
1669 * The shared HPET state.
1670 */
1671 pThis->u64HpetConfig = 0;
1672 pThis->u64HpetCounter = 0;
1673 pThis->u64HpetOffset = 0;
1674
1675 /* 64-bit main counter; 3 timers supported; LegacyReplacementRoute. */
1676 pThis->u32Capabilities = (1 << 15) /* LEG_RT_CAP - LegacyReplacementRoute capable. */
1677 | (1 << 13) /* COUNTER_SIZE_CAP - Main counter is 64-bit capable. */
1678 | 1; /* REV_ID - Revision, must not be 0 */
1679 if (pThis->fIch9) /* NUM_TIM_CAP - Number of timers -1. */
1680 pThis->u32Capabilities |= (HPET_NUM_TIMERS_ICH9 - 1) << 8;
1681 else
1682 pThis->u32Capabilities |= (HPET_NUM_TIMERS_PIIX - 1) << 8;
1683 pThis->u32Capabilities |= UINT32_C(0x80860000); /* VENDOR */
1684 AssertCompile(HPET_NUM_TIMERS_ICH9 <= RT_ELEMENTS(pThis->aTimers));
1685 AssertCompile(HPET_NUM_TIMERS_PIIX <= RT_ELEMENTS(pThis->aTimers));
1686
1687
1688 /*
1689 * Notify the PIT/RTC devices.
1690 */
1691 if (pThisCC->pHpetHlp)
1692 pThisCC->pHpetHlp->pfnSetLegacyMode(pDevIns, false /*fActive*/);
1693}
1694
1695
1696/**
1697 * @interface_method_impl{PDMDEVREG,pfnConstruct}
1698 */
1699static DECLCALLBACK(int) hpetR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
1700{
1701 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
1702 PHPET pThis = PDMDEVINS_2_DATA(pDevIns, PHPET);
1703 PHPETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PHPETCC);
1704 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
1705
1706 /* Only one HPET device now, as we use fixed MMIO region. */
1707 Assert(iInstance == 0); RT_NOREF(iInstance);
1708
1709 /*
1710 * Initialize the device state.
1711 */
1712
1713 /* Init the HPET timers (init all regardless of how many we expose). */
1714 for (unsigned i = 0; i < RT_ELEMENTS(pThis->aTimers); i++)
1715 {
1716 PHPETTIMER pHpetTimer = &pThis->aTimers[i];
1717 pHpetTimer->idxTimer = i;
1718 pHpetTimer->hTimer = NIL_TMTIMERHANDLE;
1719 }
1720
1721 /*
1722 * Validate and read the configuration.
1723 */
1724 PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, "ICH9", "");
1725
1726 int rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "ICH9", &pThis->fIch9, false);
1727 if (RT_FAILURE(rc))
1728 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: failed to read ICH9 as boolean"));
1729
1730
1731 /*
1732 * Create critsect and timers.
1733 * Note! We don't use the default critical section of the device, but our own.
1734 */
1735 rc = PDMDevHlpCritSectInit(pDevIns, &pThis->CritSect, RT_SRC_POS, "HPET");
1736 AssertRCReturn(rc, rc);
1737
1738 rc = PDMDevHlpSetDeviceCritSect(pDevIns, PDMDevHlpCritSectGetNop(pDevIns));
1739 AssertRCReturn(rc, rc);
1740
1741 /* Init the HPET timers (init all regardless of how many we expose). */
1742 static const char * const s_apszTimerNames[] =
1743 { "HPET Timer 0", "HPET Timer 1", "HPET Timer 2", "HPET Timer 3" };
1744 AssertCompile(RT_ELEMENTS(pThis->aTimers) == RT_ELEMENTS(s_apszTimerNames));
1745 for (unsigned i = 0; i < RT_ELEMENTS(pThis->aTimers); i++)
1746 {
1747 PHPETTIMER pHpetTimer = &pThis->aTimers[i];
1748 rc = PDMDevHlpTimerCreate(pDevIns, TMCLOCK_VIRTUAL_SYNC, hpetR3Timer, pHpetTimer,
1749 TMTIMER_FLAGS_NO_CRIT_SECT | TMTIMER_FLAGS_RING0,
1750 s_apszTimerNames[i], &pThis->aTimers[i].hTimer);
1751 AssertRCReturn(rc, rc);
1752 uint64_t const cTicksPerSec = PDMDevHlpTimerGetFreq(pDevIns, pThis->aTimers[i].hTimer);
1753 if (cTicksPerSec != RT_NS_1SEC)
1754 return PDMDevHlpVMSetError(pDevIns, VERR_INTERNAL_ERROR_2, RT_SRC_POS,
1755 "Unexpected timer resolution %RU64, code assumes nanonsecond resolution!", cTicksPerSec);
1756 }
1757
1758 /*
1759 * This must be done prior to registering the HPET, right?
1760 */
1761 hpetR3Reset(pDevIns);
1762
1763 uint32_t const fCaps = pThis->u32Capabilities;
1764 LogRel(("HPET: Capabilities=%#RX32 (LegacyRt=%RTbool CounterSize=%s Timers=%u Revision=%#x)\n",
1765 fCaps, HPET_CAP_HAS_LEG_RT(fCaps), HPET_CAP_HAS_64BIT_COUNT_SIZE(fCaps) ? "64-bit" : "32-bit",
1766 HPET_CAP_GET_TIMERS(fCaps), HPET_CAP_GET_REV_ID(fCaps)));
1767
1768 /*
1769 * Register the HPET and get helpers.
1770 */
1771 PDMHPETREG HpetReg;
1772 HpetReg.u32Version = PDM_HPETREG_VERSION;
1773 rc = PDMDevHlpHpetRegister(pDevIns, &HpetReg, &pThisCC->pHpetHlp);
1774 AssertRCReturn(rc, rc);
1775
1776 /*
1777 * Register the MMIO range, PDM API requests page aligned
1778 * addresses and sizes.
1779 */
1780 rc = PDMDevHlpMmioCreateAndMap(pDevIns, HPET_BASE, HPET_BAR_SIZE, hpetMMIOWrite, hpetMMIORead,
1781 IOMMMIO_FLAGS_READ_DWORD_QWORD | IOMMMIO_FLAGS_WRITE_ONLY_DWORD_QWORD,
1782 "HPET Memory", &pThis->hMmio);
1783 AssertRCReturn(rc, rc);
1784
1785 /*
1786 * Register SSM state, info item and statistics.
1787 */
1788 rc = PDMDevHlpSSMRegister3(pDevIns, HPET_SAVED_STATE_VERSION, sizeof(*pThis), hpetR3LiveExec, hpetR3SaveExec, hpetR3LoadExec);
1789 AssertRCReturn(rc, rc);
1790
1791 PDMDevHlpDBGFInfoRegister(pDevIns, "hpet", "Display HPET status. (no arguments)", hpetR3Info);
1792
1793 /* Statistics: */
1794 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatCounterRead4Byte, STAMTYPE_COUNTER,
1795 "ReadCounter32bit", STAMUNIT_OCCURENCES, "HPET_COUNTER 32-bit reads");
1796 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatCounterRead8Byte, STAMTYPE_COUNTER,
1797 "ReadCounter64bit", STAMUNIT_OCCURENCES, "HPET_COUNTER 64-bit reads");
1798 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatCounterWriteLow, STAMTYPE_COUNTER,
1799 "WriteCounterLow", STAMUNIT_OCCURENCES, "Low HPET_COUNTER writes");
1800 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatCounterWriteHigh, STAMTYPE_COUNTER,
1801 "WriteCounterHigh", STAMUNIT_OCCURENCES, "High HPET_COUNTER writes");
1802 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatZeroDeltaHack, STAMTYPE_COUNTER,
1803 "ZeroDeltaHacks", STAMUNIT_OCCURENCES, "High HPET_COUNTER writes");
1804
1805 for (unsigned i = 0; i < RT_ELEMENTS(pThis->aTimers); i++)
1806 {
1807 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->aTimers[i].StatSetIrq, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS,
1808 STAMUNIT_OCCURENCES, "Number of times the IRQ has been set.", "timer%u/SetIrq", i);
1809 PDMDevHlpSTAMRegisterF(pDevIns, &pThis->aTimers[i].StatSetTimer, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS,
1810 STAMUNIT_OCCURENCES, "Number of times the timer has be programmed.", "timer%u/SetTimer", i);
1811 }
1812
1813 return VINF_SUCCESS;
1814}
1815
1816#else /* !IN_RING3 */
1817
1818/**
1819 * @callback_method_impl{PDMDEVREGR0,pfnConstruct}
1820 */
1821static DECLCALLBACK(int) hpetRZConstruct(PPDMDEVINS pDevIns)
1822{
1823 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
1824 PHPET pThis = PDMDEVINS_2_DATA(pDevIns, PHPET);
1825 PHPETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PHPETCC);
1826
1827 int rc = PDMDevHlpSetDeviceCritSect(pDevIns, PDMDevHlpCritSectGetNop(pDevIns));
1828 AssertRCReturn(rc, rc);
1829
1830 PDMHPETREG HpetReg;
1831 HpetReg.u32Version = PDM_HPETREG_VERSION;
1832 rc = PDMDevHlpHpetSetUpContext(pDevIns, &HpetReg, &pThisCC->pHpetHlp);
1833 AssertRCReturn(rc, rc);
1834
1835 rc = PDMDevHlpMmioSetUpContext(pDevIns, pThis->hMmio, hpetMMIOWrite, hpetMMIORead, NULL /*pvUser*/);
1836 AssertRCReturn(rc, rc);
1837
1838 return VINF_SUCCESS;
1839}
1840
1841#endif /* !IN_RING3 */
1842
1843/**
1844 * The device registration structure.
1845 */
1846const PDMDEVREG g_DeviceHPET =
1847{
1848 /* .u32Version = */ PDM_DEVREG_VERSION,
1849 /* .uReserved0 = */ 0,
1850 /* .szName = */ "hpet",
1851 /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RZ | PDM_DEVREG_FLAGS_NEW_STYLE,
1852 /* .fClass = */ PDM_DEVREG_CLASS_PIT,
1853 /* .cMaxInstances = */ 1,
1854 /* .uSharedVersion = */ 42,
1855 /* .cbInstanceShared = */ sizeof(HPET),
1856 /* .cbInstanceCC = */ sizeof(HPETCC),
1857 /* .cbInstanceRC = */ sizeof(HPETRC),
1858 /* .cMaxPciDevices = */ 0,
1859 /* .cMaxMsixVectors = */ 0,
1860 /* .pszDescription = */ "High Precision Event Timer (HPET) Device",
1861#if defined(IN_RING3)
1862 /* .pszRCMod = */ "VBoxDDRC.rc",
1863 /* .pszR0Mod = */ "VBoxDDR0.r0",
1864 /* .pfnConstruct = */ hpetR3Construct,
1865 /* .pfnDestruct = */ NULL,
1866 /* .pfnRelocate = */ hpetR3Relocate,
1867 /* .pfnMemSetup = */ NULL,
1868 /* .pfnPowerOn = */ NULL,
1869 /* .pfnReset = */ hpetR3Reset,
1870 /* .pfnSuspend = */ NULL,
1871 /* .pfnResume = */ NULL,
1872 /* .pfnAttach = */ NULL,
1873 /* .pfnDetach = */ NULL,
1874 /* .pfnQueryInterface = */ NULL,
1875 /* .pfnInitComplete = */ NULL,
1876 /* .pfnPowerOff = */ NULL,
1877 /* .pfnSoftReset = */ NULL,
1878 /* .pfnReserved0 = */ NULL,
1879 /* .pfnReserved1 = */ NULL,
1880 /* .pfnReserved2 = */ NULL,
1881 /* .pfnReserved3 = */ NULL,
1882 /* .pfnReserved4 = */ NULL,
1883 /* .pfnReserved5 = */ NULL,
1884 /* .pfnReserved6 = */ NULL,
1885 /* .pfnReserved7 = */ NULL,
1886#elif defined(IN_RING0)
1887 /* .pfnEarlyConstruct = */ NULL,
1888 /* .pfnConstruct = */ hpetRZConstruct,
1889 /* .pfnDestruct = */ NULL,
1890 /* .pfnFinalDestruct = */ NULL,
1891 /* .pfnRequest = */ NULL,
1892 /* .pfnReserved0 = */ NULL,
1893 /* .pfnReserved1 = */ NULL,
1894 /* .pfnReserved2 = */ NULL,
1895 /* .pfnReserved3 = */ NULL,
1896 /* .pfnReserved4 = */ NULL,
1897 /* .pfnReserved5 = */ NULL,
1898 /* .pfnReserved6 = */ NULL,
1899 /* .pfnReserved7 = */ NULL,
1900#elif defined(IN_RC)
1901 /* .pfnConstruct = */ hpetRZConstruct,
1902 /* .pfnReserved0 = */ NULL,
1903 /* .pfnReserved1 = */ NULL,
1904 /* .pfnReserved2 = */ NULL,
1905 /* .pfnReserved3 = */ NULL,
1906 /* .pfnReserved4 = */ NULL,
1907 /* .pfnReserved5 = */ NULL,
1908 /* .pfnReserved6 = */ NULL,
1909 /* .pfnReserved7 = */ NULL,
1910#else
1911# error "Not in IN_RING3, IN_RING0 or IN_RC!"
1912#endif
1913 /* .u32VersionEnd = */ PDM_DEVREG_VERSION
1914};
1915
1916#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
1917
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