VirtualBox

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

Last change on this file since 96407 was 96407, checked in by vboxsync, 2 years ago

scm copyright and license note update

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