VirtualBox

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

Last change on this file since 91881 was 90447, checked in by vboxsync, 3 years ago

Dev*: Checked up all the PDMDevHlpCritSectEnter calls to make sure the status code is checked. bugref:6695

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