VirtualBox

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

Last change on this file since 56292 was 56292, checked in by vboxsync, 10 years ago

Devices: Updated (C) year.

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