VirtualBox

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

Last change on this file since 50667 was 48947, checked in by vboxsync, 11 years ago

Devices: Whitespace and svn:keyword cleanups by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 49.3 KB
Line 
1/* $Id: DevHPET.cpp 48947 2013-10-07 21:41:00Z vboxsync $ */
2/** @file
3 * HPET virtual device - High Precision Event Timer emulation.
4 */
5
6/*
7 * Copyright (C) 2009-2013 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
80#define HPET_TIMER_TYPE_EDGE 0
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 static unsigned s_cOccurences = 0;
449 if (s_cOccurences++ < 10)
450 LogRel(("HPET: using timer above configured range: %d\n", iTimerNo));
451 *pu32Value = 0;
452 return VINF_SUCCESS;
453 }
454
455 HPETTIMER const *pHpetTimer = &pThis->aTimers[iTimerNo];
456 uint32_t u32Value;
457 switch (iTimerReg)
458 {
459 case HPET_TN_CFG:
460 u32Value = (uint32_t)pHpetTimer->u64Config;
461 Log(("read HPET_TN_CFG on %d: %#x\n", iTimerNo, u32Value));
462 break;
463
464 case HPET_TN_CFG + 4:
465 u32Value = (uint32_t)(pHpetTimer->u64Config >> 32);
466 Log(("read HPET_TN_CFG+4 on %d: %#x\n", iTimerNo, u32Value));
467 break;
468
469 case HPET_TN_CMP:
470 u32Value = (uint32_t)pHpetTimer->u64Cmp;
471 Log(("read HPET_TN_CMP on %d: %#x (%#llx)\n", pHpetTimer->idxTimer, u32Value, pHpetTimer->u64Cmp));
472 break;
473
474 case HPET_TN_CMP + 4:
475 u32Value = (uint32_t)(pHpetTimer->u64Cmp >> 32);
476 Log(("read HPET_TN_CMP+4 on %d: %#x (%#llx)\n", pHpetTimer->idxTimer, u32Value, pHpetTimer->u64Cmp));
477 break;
478
479 case HPET_TN_ROUTE:
480 u32Value = (uint32_t)(pHpetTimer->u64Fsb >> 32); /** @todo Looks wrong, but since it's not supported, who cares. */
481 Log(("read HPET_TN_ROUTE on %d: %#x\n", iTimerNo, u32Value));
482 break;
483
484 default:
485 {
486 static unsigned s_cOccurences = 0;
487 if (s_cOccurences++ < 10)
488 LogRel(("invalid HPET register read %d on %d\n", iTimerReg, pHpetTimer->idxTimer));
489 u32Value = 0;
490 break;
491 }
492 }
493 *pu32Value = u32Value;
494 return VINF_SUCCESS;
495}
496
497
498/**
499 * 32-bit write to a HPET timer register.
500 *
501 * @returns Strict VBox status code.
502 *
503 * @param pThis The HPET state.
504 * @param idxReg The register being written to.
505 * @param u32NewValue The value being written.
506 *
507 * @remarks The caller should not hold the device lock, unless it also holds
508 * the TM lock.
509 */
510static int hpetTimerRegWrite32(HPET *pThis, uint32_t iTimerNo, uint32_t iTimerReg, uint32_t u32NewValue)
511{
512 Assert(!PDMCritSectIsOwner(&pThis->CritSect) || TMTimerIsLockOwner(pThis->aTimers[0].CTX_SUFF(pTimer)));
513
514 if ( iTimerNo >= HPET_CAP_GET_TIMERS(pThis->u32Capabilities)
515 || iTimerNo >= RT_ELEMENTS(pThis->aTimers) ) /* Parfait - see above. */
516 {
517 static unsigned s_cOccurences = 0;
518 if (s_cOccurences++ < 10)
519 LogRel(("HPET: using timer above configured range: %d\n", iTimerNo));
520 return VINF_SUCCESS;
521 }
522 HPETTIMER *pHpetTimer = &pThis->aTimers[iTimerNo];
523
524 switch (iTimerReg)
525 {
526 case HPET_TN_CFG:
527 {
528 DEVHPET_LOCK_RETURN(pThis, VINF_IOM_R3_MMIO_WRITE);
529 uint64_t u64Mask = HPET_TN_CFG_WRITE_MASK;
530
531 Log(("write HPET_TN_CFG: %d: %x\n", iTimerNo, u32NewValue));
532 if (pHpetTimer->u64Config & HPET_TN_PERIODIC_CAP)
533 u64Mask |= HPET_TN_PERIODIC;
534
535 if (pHpetTimer->u64Config & HPET_TN_SIZE_CAP)
536 u64Mask |= HPET_TN_32BIT;
537 else
538 u32NewValue &= ~HPET_TN_32BIT;
539
540 if (u32NewValue & HPET_TN_32BIT)
541 {
542 Log(("setting timer %d to 32-bit mode\n", iTimerNo));
543 pHpetTimer->u64Cmp = (uint32_t)pHpetTimer->u64Cmp;
544 pHpetTimer->u64Period = (uint32_t)pHpetTimer->u64Period;
545 }
546 if ((u32NewValue & HPET_TN_INT_TYPE) == HPET_TIMER_TYPE_LEVEL)
547 {
548 static unsigned s_cOccurences = 0;
549 if (s_cOccurences++ < 10)
550 LogRel(("level-triggered config not yet supported\n"));
551 AssertFailed();
552 }
553
554 /* We only care about lower 32-bits so far */
555 pHpetTimer->u64Config = hpetUpdateMasked(u32NewValue, pHpetTimer->u64Config, u64Mask);
556 DEVHPET_UNLOCK(pThis);
557 break;
558 }
559
560 case HPET_TN_CFG + 4: /* Interrupt capabilities - read only. */
561 {
562 Log(("write HPET_TN_CFG + 4, useless\n"));
563 break;
564 }
565
566 case HPET_TN_CMP: /* lower bits of comparator register */
567 {
568 DEVHPET_LOCK_BOTH_RETURN(pThis, VINF_IOM_R3_MMIO_WRITE);
569 Log(("write HPET_TN_CMP on %d: %#x\n", iTimerNo, u32NewValue));
570
571 if (pHpetTimer->u64Config & HPET_TN_PERIODIC)
572 pHpetTimer->u64Period = RT_MAKE_U64(u32NewValue, RT_HI_U32(pHpetTimer->u64Period));
573 pHpetTimer->u64Cmp = RT_MAKE_U64(u32NewValue, RT_HI_U32(pHpetTimer->u64Cmp));
574 pHpetTimer->u64Config &= ~HPET_TN_SETVAL;
575 Log2(("after HPET_TN_CMP cmp=%#llx per=%#llx\n", pHpetTimer->u64Cmp, pHpetTimer->u64Period));
576
577 if (pThis->u64HpetConfig & HPET_CFG_ENABLE)
578 hpetProgramTimer(pHpetTimer);
579 DEVHPET_UNLOCK_BOTH(pThis);
580 break;
581 }
582
583 case HPET_TN_CMP + 4: /* upper bits of comparator register */
584 {
585 DEVHPET_LOCK_BOTH_RETURN(pThis, VINF_IOM_R3_MMIO_WRITE);
586 Log(("write HPET_TN_CMP + 4 on %d: %#x\n", iTimerNo, u32NewValue));
587 if (!hpet32bitTimer(pHpetTimer))
588 {
589 if (pHpetTimer->u64Config & HPET_TN_PERIODIC)
590 pHpetTimer->u64Period = RT_MAKE_U64(RT_LO_U32(pHpetTimer->u64Period), u32NewValue);
591 pHpetTimer->u64Cmp = RT_MAKE_U64(RT_LO_U32(pHpetTimer->u64Cmp), u32NewValue);
592
593 Log2(("after HPET_TN_CMP+4 cmp=%llx per=%llx tmr=%d\n", pHpetTimer->u64Cmp, pHpetTimer->u64Period, iTimerNo));
594
595 pHpetTimer->u64Config &= ~HPET_TN_SETVAL;
596
597 if (pThis->u64HpetConfig & HPET_CFG_ENABLE)
598 hpetProgramTimer(pHpetTimer);
599 }
600 DEVHPET_UNLOCK_BOTH(pThis);
601 break;
602 }
603
604 case HPET_TN_ROUTE:
605 {
606 Log(("write HPET_TN_ROUTE\n"));
607 break;
608 }
609
610 case HPET_TN_ROUTE + 4:
611 {
612 Log(("write HPET_TN_ROUTE + 4\n"));
613 break;
614 }
615
616 default:
617 {
618 static unsigned s_cOccurences = 0;
619 if (s_cOccurences++ < 10)
620 LogRel(("invalid timer register write: %d\n", iTimerReg));
621 break;
622 }
623 }
624
625 return VINF_SUCCESS;
626}
627
628
629/* -=-=-=-=-=- Non-timer register accesses -=-=-=-=-=- */
630
631
632/**
633 * Read a 32-bit HPET register.
634 *
635 * @returns Strict VBox status code.
636 * @param pThis The HPET state.
637 * @param idxReg The register to read.
638 * @param pu32Value Where to return the register value.
639 *
640 * @remarks The caller must not own the device lock if HPET_COUNTER is read.
641 */
642static int hpetConfigRegRead32(HPET *pThis, uint32_t idxReg, uint32_t *pu32Value)
643{
644 Assert(!PDMCritSectIsOwner(&pThis->CritSect) || (idxReg != HPET_COUNTER && idxReg != HPET_COUNTER + 4));
645
646 uint32_t u32Value;
647 switch (idxReg)
648 {
649 case HPET_ID:
650 DEVHPET_LOCK_RETURN(pThis, VINF_IOM_R3_MMIO_READ);
651 u32Value = pThis->u32Capabilities;
652 DEVHPET_UNLOCK(pThis);
653 Log(("read HPET_ID: %#x\n", u32Value));
654 break;
655
656 case HPET_PERIOD:
657 DEVHPET_LOCK_RETURN(pThis, VINF_IOM_R3_MMIO_READ);
658 u32Value = pThis->u32Period;
659 DEVHPET_UNLOCK(pThis);
660 Log(("read HPET_PERIOD: %#x\n", u32Value));
661 break;
662
663 case HPET_CFG:
664 DEVHPET_LOCK_RETURN(pThis, VINF_IOM_R3_MMIO_READ);
665 u32Value = (uint32_t)pThis->u64HpetConfig;
666 DEVHPET_UNLOCK(pThis);
667 Log(("read HPET_CFG: %#x\n", u32Value));
668 break;
669
670 case HPET_CFG + 4:
671 DEVHPET_LOCK_RETURN(pThis, VINF_IOM_R3_MMIO_READ);
672 u32Value = (uint32_t)(pThis->u64HpetConfig >> 32);
673 DEVHPET_UNLOCK(pThis);
674 Log(("read of HPET_CFG + 4: %#x\n", u32Value));
675 break;
676
677 case HPET_COUNTER:
678 case HPET_COUNTER + 4:
679 {
680 DEVHPET_LOCK_BOTH_RETURN(pThis, VINF_IOM_R3_MMIO_READ);
681
682 uint64_t u64Ticks;
683 if (pThis->u64HpetConfig & HPET_CFG_ENABLE)
684 u64Ticks = hpetGetTicks(pThis);
685 else
686 u64Ticks = pThis->u64HpetCounter;
687
688 DEVHPET_UNLOCK_BOTH(pThis);
689
690 /** @todo is it correct? */
691 u32Value = (idxReg == HPET_COUNTER) ? (uint32_t)u64Ticks : (uint32_t)(u64Ticks >> 32);
692 Log(("read HPET_COUNTER: %s part value %x (%#llx)\n",
693 (idxReg == HPET_COUNTER) ? "low" : "high", u32Value, u64Ticks));
694 break;
695 }
696
697 case HPET_STATUS:
698 DEVHPET_LOCK_RETURN(pThis, VINF_IOM_R3_MMIO_READ);
699 u32Value = (uint32_t)pThis->u64Isr;
700 DEVHPET_UNLOCK(pThis);
701 Log(("read HPET_STATUS: %#x\n", u32Value));
702 break;
703
704 default:
705 Log(("invalid HPET register read: %x\n", idxReg));
706 u32Value = 0;
707 break;
708 }
709
710 *pu32Value = u32Value;
711 return VINF_SUCCESS;
712}
713
714
715/**
716 * 32-bit write to a config register.
717 *
718 * @returns Strict VBox status code.
719 *
720 * @param pThis The HPET state.
721 * @param idxReg The register being written to.
722 * @param u32NewValue The value being written.
723 *
724 * @remarks The caller should not hold the device lock, unless it also holds
725 * the TM lock.
726 */
727static int hpetConfigRegWrite32(HPET *pThis, uint32_t idxReg, uint32_t u32NewValue)
728{
729 Assert(!PDMCritSectIsOwner(&pThis->CritSect) || TMTimerIsLockOwner(pThis->aTimers[0].CTX_SUFF(pTimer)));
730
731 int rc = VINF_SUCCESS;
732 switch (idxReg)
733 {
734 case HPET_ID:
735 case HPET_ID + 4:
736 {
737 Log(("write HPET_ID, useless\n"));
738 break;
739 }
740
741 case HPET_CFG:
742 {
743 DEVHPET_LOCK_BOTH_RETURN(pThis, VINF_IOM_R3_MMIO_WRITE);
744 uint32_t const iOldValue = (uint32_t)(pThis->u64HpetConfig);
745 Log(("write HPET_CFG: %x (old %x)\n", u32NewValue, iOldValue));
746
747 /*
748 * This check must be here, before actual update, as hpetLegacyMode
749 * may request retry in R3 - so we must keep state intact.
750 */
751 if ( ((iOldValue ^ u32NewValue) & HPET_CFG_LEGACY)
752 && pThis->pHpetHlpR3 != NIL_RTR3PTR)
753 {
754#ifdef IN_RING3
755 rc = pThis->pHpetHlpR3->pfnSetLegacyMode(pThis->pDevInsR3, RT_BOOL(u32NewValue & HPET_CFG_LEGACY));
756 if (rc != VINF_SUCCESS)
757#else
758 rc = VINF_IOM_R3_MMIO_WRITE;
759#endif
760 {
761 DEVHPET_UNLOCK_BOTH(pThis);
762 break;
763 }
764 }
765
766 pThis->u64HpetConfig = hpetUpdateMasked(u32NewValue, iOldValue, HPET_CFG_WRITE_MASK);
767
768 uint32_t const cTimers = HPET_CAP_GET_TIMERS(pThis->u32Capabilities);
769 if (hpetBitJustSet(iOldValue, u32NewValue, HPET_CFG_ENABLE))
770 {
771/** @todo Only get the time stamp once when reprogramming? */
772 /* Enable main counter and interrupt generation. */
773 pThis->u64HpetOffset = hpetTicksToNs(pThis, pThis->u64HpetCounter)
774 - TMTimerGet(pThis->aTimers[0].CTX_SUFF(pTimer));
775 for (uint32_t i = 0; i < cTimers; i++)
776 if (pThis->aTimers[i].u64Cmp != hpetInvalidValue(&pThis->aTimers[i]))
777 hpetProgramTimer(&pThis->aTimers[i]);
778 }
779 else if (hpetBitJustCleared(iOldValue, u32NewValue, HPET_CFG_ENABLE))
780 {
781 /* Halt main counter and disable interrupt generation. */
782 pThis->u64HpetCounter = hpetGetTicks(pThis);
783 for (uint32_t i = 0; i < cTimers; i++)
784 TMTimerStop(pThis->aTimers[i].CTX_SUFF(pTimer));
785 }
786
787 DEVHPET_UNLOCK_BOTH(pThis);
788 break;
789 }
790
791 case HPET_CFG + 4:
792 {
793 DEVHPET_LOCK_RETURN(pThis, VINF_IOM_R3_MMIO_WRITE);
794 pThis->u64HpetConfig = hpetUpdateMasked((uint64_t)u32NewValue << 32,
795 pThis->u64HpetConfig,
796 UINT64_C(0xffffffff00000000));
797 Log(("write HPET_CFG + 4: %x -> %#llx\n", u32NewValue, pThis->u64HpetConfig));
798 DEVHPET_UNLOCK(pThis);
799 break;
800 }
801
802 case HPET_STATUS:
803 {
804 DEVHPET_LOCK_RETURN(pThis, VINF_IOM_R3_MMIO_WRITE);
805 /* Clear ISR for all set bits in u32NewValue, see p. 14 of the HPET spec. */
806 pThis->u64Isr &= ~((uint64_t)u32NewValue);
807 Log(("write HPET_STATUS: %x -> ISR=%#llx\n", u32NewValue, pThis->u64Isr));
808 DEVHPET_UNLOCK(pThis);
809 break;
810 }
811
812 case HPET_STATUS + 4:
813 {
814 Log(("write HPET_STATUS + 4: %x\n", u32NewValue));
815 if (u32NewValue != 0)
816 {
817 static unsigned s_cOccurrences = 0;
818 if (s_cOccurrences++ < 10)
819 LogRel(("Writing HPET_STATUS + 4 with non-zero, ignored\n"));
820 }
821 break;
822 }
823
824 case HPET_COUNTER:
825 {
826 DEVHPET_LOCK_RETURN(pThis, VINF_IOM_R3_MMIO_WRITE);
827 pThis->u64HpetCounter = RT_MAKE_U64(u32NewValue, RT_HI_U32(pThis->u64HpetCounter));
828 Log(("write HPET_COUNTER: %#x -> %llx\n", u32NewValue, pThis->u64HpetCounter));
829 DEVHPET_UNLOCK(pThis);
830 break;
831 }
832
833 case HPET_COUNTER + 4:
834 {
835 DEVHPET_LOCK_RETURN(pThis, VINF_IOM_R3_MMIO_WRITE);
836 pThis->u64HpetCounter = RT_MAKE_U64(RT_LO_U32(pThis->u64HpetCounter), u32NewValue);
837 Log(("write HPET_COUNTER + 4: %#x -> %llx\n", u32NewValue, pThis->u64HpetCounter));
838 DEVHPET_UNLOCK(pThis);
839 break;
840 }
841
842 default:
843 {
844 static unsigned s_cOccurences = 0;
845 if (s_cOccurences++ < 10)
846 LogRel(("invalid HPET config write: %x\n", idxReg));
847 break;
848 }
849 }
850
851 return rc;
852}
853
854
855/* -=-=-=-=-=- MMIO callbacks -=-=-=-=-=- */
856
857
858/**
859 * @callback_method_impl{FNIOMMMIOREAD}
860 */
861PDMBOTHCBDECL(int) hpetMMIORead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void *pv, unsigned cb)
862{
863 HPET *pThis = PDMINS_2_DATA(pDevIns, HPET*);
864 uint32_t const idxReg = (uint32_t)(GCPhysAddr - HPET_BASE);
865 NOREF(pvUser);
866 Assert(cb == 4 || cb == 8);
867
868 LogFlow(("hpetMMIORead (%d): %llx (%x)\n", cb, (uint64_t)GCPhysAddr, idxReg));
869
870 int rc;
871 if (cb == 4)
872 {
873 /*
874 * 4-byte access.
875 */
876 if (idxReg >= 0x100 && idxReg < 0x400)
877 {
878 DEVHPET_LOCK_RETURN(pThis, VINF_IOM_R3_MMIO_READ);
879 rc = hpetTimerRegRead32(pThis,
880 (idxReg - 0x100) / 0x20,
881 (idxReg - 0x100) % 0x20,
882 (uint32_t *)pv);
883 DEVHPET_UNLOCK(pThis);
884 }
885 else
886 rc = hpetConfigRegRead32(pThis, idxReg, (uint32_t *)pv);
887 }
888 else
889 {
890 /*
891 * 8-byte access - Split the access except for timing sensitive registers.
892 * The others assume the protection of the lock.
893 */
894 PRTUINT64U pValue = (PRTUINT64U)pv;
895 if (idxReg == HPET_COUNTER)
896 {
897 /* When reading HPET counter we must read it in a single read,
898 to avoid unexpected time jumps on 32-bit overflow. */
899 DEVHPET_LOCK_BOTH_RETURN(pThis, VINF_IOM_R3_MMIO_READ);
900 if (pThis->u64HpetConfig & HPET_CFG_ENABLE)
901 pValue->u = hpetGetTicks(pThis);
902 else
903 pValue->u = pThis->u64HpetCounter;
904 DEVHPET_UNLOCK_BOTH(pThis);
905 rc = VINF_SUCCESS;
906 }
907 else
908 {
909 DEVHPET_LOCK_RETURN(pThis, VINF_IOM_R3_MMIO_READ);
910 if (idxReg >= 0x100 && idxReg < 0x400)
911 {
912 uint32_t iTimer = (idxReg - 0x100) / 0x20;
913 uint32_t iTimerReg = (idxReg - 0x100) % 0x20;
914 rc = hpetTimerRegRead32(pThis, iTimer, iTimerReg, &pValue->s.Lo);
915 if (rc == VINF_SUCCESS)
916 rc = hpetTimerRegRead32(pThis, iTimer, iTimerReg + 4, &pValue->s.Hi);
917 }
918 else
919 {
920 /* for most 8-byte accesses we just split them, happens under lock anyway. */
921 rc = hpetConfigRegRead32(pThis, idxReg, &pValue->s.Lo);
922 if (rc == VINF_SUCCESS)
923 rc = hpetConfigRegRead32(pThis, idxReg + 4, &pValue->s.Hi);
924 }
925 DEVHPET_UNLOCK(pThis);
926 }
927 }
928 return rc;
929}
930
931
932/**
933 * @callback_method_impl{FNIOMMMIOWRITE}
934 */
935PDMBOTHCBDECL(int) hpetMMIOWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void const *pv, unsigned cb)
936{
937 HPET *pThis = PDMINS_2_DATA(pDevIns, HPET*);
938 uint32_t idxReg = (uint32_t)(GCPhysAddr - HPET_BASE);
939 LogFlow(("hpetMMIOWrite: cb=%u reg=%03x (%RGp) val=%llx\n",
940 cb, idxReg, GCPhysAddr, cb == 4 ? *(uint32_t *)pv : cb == 8 ? *(uint64_t *)pv : 0xdeadbeef));
941 NOREF(pvUser);
942 Assert(cb == 4 || cb == 8);
943
944 int rc;
945 if (cb == 4)
946 {
947 if (idxReg >= 0x100 && idxReg < 0x400)
948 rc = hpetTimerRegWrite32(pThis,
949 (idxReg - 0x100) / 0x20,
950 (idxReg - 0x100) % 0x20,
951 *(uint32_t const *)pv);
952 else
953 rc = hpetConfigRegWrite32(pThis, idxReg, *(uint32_t const *)pv);
954 }
955 else
956 {
957 /*
958 * 8-byte access.
959 */
960 /* Split the access and rely on the locking to prevent trouble. */
961 DEVHPET_LOCK_BOTH_RETURN(pThis, VINF_IOM_R3_MMIO_WRITE);
962 RTUINT64U uValue;
963 uValue.u = *(uint64_t const *)pv;
964 if (idxReg >= 0x100 && idxReg < 0x400)
965 {
966 uint32_t iTimer = (idxReg - 0x100) / 0x20;
967 uint32_t iTimerReg = (idxReg - 0x100) % 0x20;
968 /** @todo Consider handling iTimerReg == HPET_TN_CMP specially here */
969 rc = hpetTimerRegWrite32(pThis, iTimer, iTimerReg, uValue.s.Lo);
970 if (RT_LIKELY(rc == VINF_SUCCESS))
971 rc = hpetTimerRegWrite32(pThis, iTimer, iTimerReg + 4, uValue.s.Hi);
972 }
973 else
974 {
975 rc = hpetConfigRegWrite32(pThis, idxReg, uValue.s.Lo);
976 if (RT_LIKELY(rc == VINF_SUCCESS))
977 rc = hpetConfigRegWrite32(pThis, idxReg + 4, uValue.s.Hi);
978 }
979 DEVHPET_UNLOCK_BOTH(pThis);
980 }
981
982 return rc;
983}
984
985#ifdef IN_RING3
986
987/* -=-=-=-=-=- Timer Callback Processing -=-=-=-=-=- */
988
989/**
990 * Gets the IRQ of an HPET timer.
991 *
992 * @returns IRQ number.
993 * @param pHpetTimer The HPET timer.
994 */
995static uint32_t hpetR3TimerGetIrq(struct HPETTIMER const *pHpetTimer)
996{
997 /*
998 * Per spec, in legacy mode the HPET timers are wired as follows:
999 * timer 0: IRQ0 for PIC and IRQ2 for APIC
1000 * timer 1: IRQ8 for both PIC and APIC
1001 *
1002 * ISA IRQ delivery logic will take care of correct delivery
1003 * to the different ICs.
1004 */
1005 if ( (pHpetTimer->idxTimer <= 1)
1006 && (pHpetTimer->CTX_SUFF(pHpet)->u64HpetConfig & HPET_CFG_LEGACY))
1007 return (pHpetTimer->idxTimer == 0) ? 0 : 8;
1008
1009 return (pHpetTimer->u64Config & HPET_TN_INT_ROUTE_MASK) >> HPET_TN_INT_ROUTE_SHIFT;
1010}
1011
1012
1013/**
1014 * Used by hpetR3Timer to update the IRQ status.
1015 *
1016 * @param pThis The HPET device state.
1017 * @param pHpetTimer The HPET timer.
1018 */
1019static void hpetR3TimerUpdateIrq(HPET *pThis, struct HPETTIMER *pHpetTimer)
1020{
1021 /** @todo: is it correct? */
1022 if ( !!(pHpetTimer->u64Config & HPET_TN_ENABLE)
1023 && !!(pThis->u64HpetConfig & HPET_CFG_ENABLE))
1024 {
1025 uint32_t irq = hpetR3TimerGetIrq(pHpetTimer);
1026 Log4(("HPET: raising IRQ %d\n", irq));
1027
1028 /* ISR bits are only set in level-triggered mode. */
1029 if ((pHpetTimer->u64Config & HPET_TN_INT_TYPE) == HPET_TIMER_TYPE_LEVEL)
1030 pThis->u64Isr |= (uint64_t)(1 << pHpetTimer->idxTimer);
1031
1032 /* We trigger flip/flop in edge-triggered mode and do nothing in
1033 level-triggered mode yet. */
1034 if ((pHpetTimer->u64Config & HPET_TN_INT_TYPE) == HPET_TIMER_TYPE_EDGE)
1035 pThis->pHpetHlpR3->pfnSetIrq(pThis->CTX_SUFF(pDevIns), irq, PDM_IRQ_LEVEL_FLIP_FLOP);
1036 else
1037 AssertFailed();
1038 /** @todo: implement IRQs in level-triggered mode */
1039 }
1040}
1041
1042/**
1043 * Device timer callback function.
1044 *
1045 * @param pDevIns Device instance of the device which registered the timer.
1046 * @param pTimer The timer handle.
1047 * @param pvUser Pointer to the HPET timer state.
1048 */
1049static DECLCALLBACK(void) hpetR3Timer(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
1050{
1051 HPET *pThis = PDMINS_2_DATA(pDevIns, HPET *);
1052 HPETTIMER *pHpetTimer = (HPETTIMER *)pvUser;
1053 uint64_t u64Period = pHpetTimer->u64Period;
1054 uint64_t u64CurTick = hpetGetTicks(pThis);
1055 uint64_t u64Diff;
1056
1057 if (pHpetTimer->u64Config & HPET_TN_PERIODIC)
1058 {
1059 if (u64Period) {
1060 hpetAdjustComparator(pHpetTimer, u64CurTick);
1061
1062 u64Diff = hpetComputeDiff(pHpetTimer, u64CurTick);
1063
1064 Log4(("HPET: periodic: next in %llu\n", hpetTicksToNs(pThis, u64Diff)));
1065 TMTimerSetNano(pTimer, hpetTicksToNs(pThis, u64Diff));
1066 }
1067 }
1068 else if (hpet32bitTimer(pHpetTimer))
1069 {
1070 /* For 32-bit non-periodic timers, generate wrap-around interrupts. */
1071 if (pHpetTimer->u8Wrap)
1072 {
1073 u64Diff = hpetComputeDiff(pHpetTimer, u64CurTick);
1074 TMTimerSetNano(pTimer, hpetTicksToNs(pThis, u64Diff));
1075 pHpetTimer->u8Wrap = 0;
1076 }
1077 }
1078
1079 /* Should it really be under lock, does it really matter? */
1080 hpetR3TimerUpdateIrq(pThis, pHpetTimer);
1081}
1082
1083
1084/* -=-=-=-=-=- DBGF Info Handlers -=-=-=-=-=- */
1085
1086
1087/**
1088 * @callback_method_impl{FNDBGFHANDLERDEV}
1089 */
1090static DECLCALLBACK(void) hpetR3Info(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
1091{
1092 HPET *pThis = PDMINS_2_DATA(pDevIns, HPET *);
1093 NOREF(pszArgs);
1094
1095 pHlp->pfnPrintf(pHlp,
1096 "HPET status:\n"
1097 " config=%016RX64 isr=%016RX64\n"
1098 " offset=%016RX64 counter=%016RX64 frequency=%08x\n"
1099 " legacy-mode=%s timer-count=%u\n",
1100 pThis->u64HpetConfig, pThis->u64Isr,
1101 pThis->u64HpetOffset, pThis->u64HpetCounter, pThis->u32Period,
1102 !!(pThis->u64HpetConfig & HPET_CFG_LEGACY) ? "on " : "off",
1103 HPET_CAP_GET_TIMERS(pThis->u32Capabilities));
1104 pHlp->pfnPrintf(pHlp,
1105 "Timers:\n");
1106 for (unsigned i = 0; i < RT_ELEMENTS(pThis->aTimers); i++)
1107 {
1108 pHlp->pfnPrintf(pHlp, " %d: comparator=%016RX64 period(hidden)=%016RX64 cfg=%016RX64\n",
1109 pThis->aTimers[i].idxTimer,
1110 pThis->aTimers[i].u64Cmp,
1111 pThis->aTimers[i].u64Period,
1112 pThis->aTimers[i].u64Config);
1113 }
1114}
1115
1116
1117/* -=-=-=-=-=- Saved State -=-=-=-=-=- */
1118
1119
1120/**
1121 * @callback_method_impl{FNSSMDEVLIVEEXEC}
1122 */
1123static DECLCALLBACK(int) hpetR3LiveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uPass)
1124{
1125 HPET *pThis = PDMINS_2_DATA(pDevIns, HPET *);
1126 NOREF(uPass);
1127
1128 SSMR3PutU8(pSSM, HPET_CAP_GET_TIMERS(pThis->u32Capabilities));
1129
1130 return VINF_SSM_DONT_CALL_AGAIN;
1131}
1132
1133
1134/**
1135 * @callback_method_impl{FNSSMDEVSAVEEXEC}
1136 */
1137static DECLCALLBACK(int) hpetR3SaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
1138{
1139 HPET *pThis = PDMINS_2_DATA(pDevIns, HPET *);
1140
1141 /*
1142 * The config.
1143 */
1144 hpetR3LiveExec(pDevIns, pSSM, SSM_PASS_FINAL);
1145
1146 /*
1147 * The state.
1148 */
1149 uint32_t const cTimers = HPET_CAP_GET_TIMERS(pThis->u32Capabilities);
1150 for (uint32_t iTimer = 0; iTimer < cTimers; iTimer++)
1151 {
1152 HPETTIMER *pHpetTimer = &pThis->aTimers[iTimer];
1153 TMR3TimerSave(pHpetTimer->pTimerR3, pSSM);
1154 SSMR3PutU8(pSSM, pHpetTimer->u8Wrap);
1155 SSMR3PutU64(pSSM, pHpetTimer->u64Config);
1156 SSMR3PutU64(pSSM, pHpetTimer->u64Cmp);
1157 SSMR3PutU64(pSSM, pHpetTimer->u64Fsb);
1158 SSMR3PutU64(pSSM, pHpetTimer->u64Period);
1159 }
1160
1161 SSMR3PutU64(pSSM, pThis->u64HpetOffset);
1162 uint64_t u64CapPer = RT_MAKE_U64(pThis->u32Capabilities, pThis->u32Period);
1163 SSMR3PutU64(pSSM, u64CapPer);
1164 SSMR3PutU64(pSSM, pThis->u64HpetConfig);
1165 SSMR3PutU64(pSSM, pThis->u64Isr);
1166 return SSMR3PutU64(pSSM, pThis->u64HpetCounter);
1167}
1168
1169
1170/**
1171 * @callback_method_impl{FNSSMDEVLOADEXEC}
1172 */
1173static DECLCALLBACK(int) hpetR3LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
1174{
1175 HPET *pThis = PDMINS_2_DATA(pDevIns, HPET *);
1176
1177 /*
1178 * Version checks.
1179 */
1180 if (uVersion == HPET_SAVED_STATE_VERSION_EMPTY)
1181 return VINF_SUCCESS;
1182 if (uVersion != HPET_SAVED_STATE_VERSION)
1183 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
1184
1185 /*
1186 * The config.
1187 */
1188 uint8_t cTimers;
1189 int rc = SSMR3GetU8(pSSM, &cTimers);
1190 AssertRCReturn(rc, rc);
1191 if (cTimers > RT_ELEMENTS(pThis->aTimers))
1192 return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Config mismatch - too many timers: saved=%#x config=%#x"),
1193 cTimers, RT_ELEMENTS(pThis->aTimers));
1194
1195 if (uPass != SSM_PASS_FINAL)
1196 return VINF_SUCCESS;
1197
1198 /*
1199 * The state.
1200 */
1201 for (uint32_t iTimer = 0; iTimer < cTimers; iTimer++)
1202 {
1203 HPETTIMER *pHpetTimer = &pThis->aTimers[iTimer];
1204 TMR3TimerLoad(pHpetTimer->pTimerR3, pSSM);
1205 SSMR3GetU8(pSSM, &pHpetTimer->u8Wrap);
1206 SSMR3GetU64(pSSM, &pHpetTimer->u64Config);
1207 SSMR3GetU64(pSSM, &pHpetTimer->u64Cmp);
1208 SSMR3GetU64(pSSM, &pHpetTimer->u64Fsb);
1209 SSMR3GetU64(pSSM, &pHpetTimer->u64Period);
1210 }
1211
1212 SSMR3GetU64(pSSM, &pThis->u64HpetOffset);
1213 uint64_t u64CapPer;
1214 SSMR3GetU64(pSSM, &u64CapPer);
1215 SSMR3GetU64(pSSM, &pThis->u64HpetConfig);
1216 SSMR3GetU64(pSSM, &pThis->u64Isr);
1217 rc = SSMR3GetU64(pSSM, &pThis->u64HpetCounter);
1218 if (RT_FAILURE(rc))
1219 return rc;
1220 if (HPET_CAP_GET_TIMERS(RT_LO_U32(u64CapPer)) != cTimers)
1221 return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Capabilities does not match timer count: cTimers=%#x caps=%#x"),
1222 cTimers, (unsigned)HPET_CAP_GET_TIMERS(u64CapPer));
1223 pThis->u32Capabilities = RT_LO_U32(u64CapPer);
1224 pThis->u32Period = RT_HI_U32(u64CapPer);
1225
1226 /*
1227 * Set the timer frequency hints.
1228 */
1229 PDMCritSectEnter(&pThis->CritSect, VERR_IGNORED);
1230 for (uint32_t iTimer = 0; iTimer < cTimers; iTimer++)
1231 {
1232 HPETTIMER *pHpetTimer = &pThis->aTimers[iTimer];
1233 if (TMTimerIsActive(pHpetTimer->CTX_SUFF(pTimer)))
1234 hpetTimerSetFrequencyHint(pThis, pHpetTimer);
1235 }
1236 PDMCritSectLeave(&pThis->CritSect);
1237 return VINF_SUCCESS;
1238}
1239
1240
1241/* -=-=-=-=-=- PDMDEVREG -=-=-=-=-=- */
1242
1243
1244/**
1245 * @interface_method_impl{PDMDEVREG,pfnRelocate}
1246 */
1247static DECLCALLBACK(void) hpetR3Relocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
1248{
1249 HPET *pThis = PDMINS_2_DATA(pDevIns, HPET *);
1250 LogFlow(("hpetR3Relocate:\n"));
1251 NOREF(offDelta);
1252
1253 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
1254 pThis->pHpetHlpRC = pThis->pHpetHlpR3->pfnGetRCHelpers(pDevIns);
1255
1256 for (unsigned i = 0; i < RT_ELEMENTS(pThis->aTimers); i++)
1257 {
1258 HPETTIMER *pTm = &pThis->aTimers[i];
1259 if (pTm->pTimerR3)
1260 pTm->pTimerRC = TMTimerRCPtr(pTm->pTimerR3);
1261 pTm->pHpetRC = PDMINS_2_DATA_RCPTR(pDevIns);
1262 }
1263}
1264
1265
1266/**
1267 * @interface_method_impl{PDMDEVREG,pfnReset}
1268 */
1269static DECLCALLBACK(void) hpetR3Reset(PPDMDEVINS pDevIns)
1270{
1271 HPET *pThis = PDMINS_2_DATA(pDevIns, HPET *);
1272 LogFlow(("hpetR3Reset:\n"));
1273
1274 /*
1275 * The timers first.
1276 */
1277 TMTimerLock(pThis->aTimers[0].pTimerR3, VERR_IGNORED);
1278 for (unsigned i = 0; i < RT_ELEMENTS(pThis->aTimers); i++)
1279 {
1280 HPETTIMER *pHpetTimer = &pThis->aTimers[i];
1281 Assert(pHpetTimer->idxTimer == i);
1282 TMTimerStop(pHpetTimer->pTimerR3);
1283
1284 /* capable of periodic operations and 64-bits */
1285 if (pThis->fIch9)
1286 pHpetTimer->u64Config = (i == 0)
1287 ? (HPET_TN_PERIODIC_CAP | HPET_TN_SIZE_CAP)
1288 : 0;
1289 else
1290 pHpetTimer->u64Config = HPET_TN_PERIODIC_CAP | HPET_TN_SIZE_CAP;
1291
1292 /* We can do all IRQs */
1293 uint32_t u32RoutingCap = 0xffffffff;
1294 pHpetTimer->u64Config |= ((uint64_t)u32RoutingCap) << 32;
1295 pHpetTimer->u64Period = 0;
1296 pHpetTimer->u8Wrap = 0;
1297 pHpetTimer->u64Cmp = hpetInvalidValue(pHpetTimer);
1298 }
1299 TMTimerUnlock(pThis->aTimers[0].pTimerR3);
1300
1301 /*
1302 * The HPET state.
1303 */
1304 pThis->u64HpetConfig = 0;
1305 pThis->u64HpetCounter = 0;
1306 pThis->u64HpetOffset = 0;
1307
1308 /* 64-bit main counter; 3 timers supported; LegacyReplacementRoute. */
1309 pThis->u32Capabilities = (1 << 15) /* LEG_RT_CAP - LegacyReplacementRoute capable. */
1310 | (1 << 13) /* COUNTER_SIZE_CAP - Main counter is 64-bit capable. */
1311 | 1; /* REV_ID - Revision, must not be 0 */
1312 if (pThis->fIch9) /* NUM_TIM_CAP - Number of timers -1. */
1313 pThis->u32Capabilities |= (HPET_NUM_TIMERS_ICH9 - 1) << 8;
1314 else
1315 pThis->u32Capabilities |= (HPET_NUM_TIMERS_PIIX - 1) << 8;
1316 pThis->u32Capabilities |= UINT32_C(0x80860000); /* VENDOR */
1317 AssertCompile(HPET_NUM_TIMERS_ICH9 <= RT_ELEMENTS(pThis->aTimers));
1318 AssertCompile(HPET_NUM_TIMERS_PIIX <= RT_ELEMENTS(pThis->aTimers));
1319
1320 pThis->u32Period = pThis->fIch9 ? HPET_CLK_PERIOD_ICH9 : HPET_CLK_PERIOD_PIIX;
1321
1322 /*
1323 * Notify the PIT/RTC devices.
1324 */
1325 if (pThis->pHpetHlpR3)
1326 pThis->pHpetHlpR3->pfnSetLegacyMode(pDevIns, false /*fActive*/);
1327}
1328
1329
1330/**
1331 * @interface_method_impl{PDMDEVREG,pfnConstruct}
1332 */
1333static DECLCALLBACK(int) hpetR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
1334{
1335 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
1336 HPET *pThis = PDMINS_2_DATA(pDevIns, HPET *);
1337
1338 /* Only one HPET device now, as we use fixed MMIO region. */
1339 Assert(iInstance == 0);
1340
1341 /*
1342 * Initialize the device state.
1343 */
1344 pThis->pDevInsR3 = pDevIns;
1345 pThis->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
1346 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
1347
1348 /* Init the HPET timers (init all regardless of how many we expose). */
1349 for (unsigned i = 0; i < RT_ELEMENTS(pThis->aTimers); i++)
1350 {
1351 HPETTIMER *pHpetTimer = &pThis->aTimers[i];
1352
1353 pHpetTimer->idxTimer = i;
1354 pHpetTimer->pHpetR3 = pThis;
1355 pHpetTimer->pHpetR0 = PDMINS_2_DATA_R0PTR(pDevIns);
1356 pHpetTimer->pHpetRC = PDMINS_2_DATA_RCPTR(pDevIns);
1357 }
1358
1359 /*
1360 * Validate and read the configuration.
1361 */
1362 PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, "GCEnabled|R0Enabled|ICH9", "");
1363
1364 bool fRCEnabled;
1365 int rc = CFGMR3QueryBoolDef(pCfg, "GCEnabled", &fRCEnabled, true);
1366 if (RT_FAILURE(rc))
1367 return PDMDEV_SET_ERROR(pDevIns, rc,
1368 N_("Configuration error: Querying \"GCEnabled\" as a bool failed"));
1369
1370 bool fR0Enabled;
1371 rc = CFGMR3QueryBoolDef(pCfg, "R0Enabled", &fR0Enabled, true);
1372 if (RT_FAILURE(rc))
1373 return PDMDEV_SET_ERROR(pDevIns, rc,
1374 N_("Configuration error: failed to read R0Enabled as boolean"));
1375
1376 rc = CFGMR3QueryBoolDef(pCfg, "ICH9", &pThis->fIch9, false);
1377 if (RT_FAILURE(rc))
1378 return PDMDEV_SET_ERROR(pDevIns, rc,
1379 N_("Configuration error: failed to read ICH9 as boolean"));
1380
1381
1382 /*
1383 * Create critsect and timers.
1384 * Note! We don't use the default critical section of the device, but our own.
1385 */
1386 rc = PDMDevHlpCritSectInit(pDevIns, &pThis->CritSect, RT_SRC_POS, "HPET");
1387 AssertRCReturn(rc, rc);
1388
1389 rc = PDMDevHlpSetDeviceCritSect(pDevIns, PDMDevHlpCritSectGetNop(pDevIns));
1390 AssertRCReturn(rc, rc);
1391
1392 /* Init the HPET timers (init all regardless of how many we expose). */
1393 for (unsigned i = 0; i < RT_ELEMENTS(pThis->aTimers); i++)
1394 {
1395 HPETTIMER *pHpetTimer = &pThis->aTimers[i];
1396
1397 rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL_SYNC, hpetR3Timer, pHpetTimer,
1398 TMTIMER_FLAGS_NO_CRIT_SECT, "HPET Timer",
1399 &pThis->aTimers[i].pTimerR3);
1400 AssertRCReturn(rc, rc);
1401 pThis->aTimers[i].pTimerRC = TMTimerRCPtr(pThis->aTimers[i].pTimerR3);
1402 pThis->aTimers[i].pTimerR0 = TMTimerR0Ptr(pThis->aTimers[i].pTimerR3);
1403 rc = TMR3TimerSetCritSect(pThis->aTimers[i].pTimerR3, &pThis->CritSect);
1404 AssertRCReturn(rc, rc);
1405 }
1406
1407 /*
1408 * This must be done prior to registering the HPET, right?
1409 */
1410 hpetR3Reset(pDevIns);
1411
1412 /*
1413 * Register the HPET and get helpers.
1414 */
1415 PDMHPETREG HpetReg;
1416 HpetReg.u32Version = PDM_HPETREG_VERSION;
1417 rc = PDMDevHlpHPETRegister(pDevIns, &HpetReg, &pThis->pHpetHlpR3);
1418 AssertRCReturn(rc, rc);
1419
1420 /*
1421 * Register the MMIO range, PDM API requests page aligned
1422 * addresses and sizes.
1423 */
1424 rc = PDMDevHlpMMIORegister(pDevIns, HPET_BASE, HPET_BAR_SIZE, pThis,
1425 IOMMMIO_FLAGS_READ_DWORD_QWORD | IOMMMIO_FLAGS_WRITE_ONLY_DWORD_QWORD,
1426 hpetMMIOWrite, hpetMMIORead, "HPET Memory");
1427 AssertRCReturn(rc, rc);
1428
1429 if (fRCEnabled)
1430 {
1431 rc = PDMDevHlpMMIORegisterRC(pDevIns, HPET_BASE, HPET_BAR_SIZE, NIL_RTRCPTR /*pvUser*/, "hpetMMIOWrite", "hpetMMIORead");
1432 AssertRCReturn(rc, rc);
1433
1434 pThis->pHpetHlpRC = pThis->pHpetHlpR3->pfnGetRCHelpers(pDevIns);
1435 }
1436
1437 if (fR0Enabled)
1438 {
1439 rc = PDMDevHlpMMIORegisterR0(pDevIns, HPET_BASE, HPET_BAR_SIZE, NIL_RTR0PTR /*pvUser*/,
1440 "hpetMMIOWrite", "hpetMMIORead");
1441 AssertRCReturn(rc, rc);
1442
1443 pThis->pHpetHlpR0 = pThis->pHpetHlpR3->pfnGetR0Helpers(pDevIns);
1444 AssertReturn(pThis->pHpetHlpR0 != NIL_RTR0PTR, VERR_INTERNAL_ERROR);
1445 }
1446
1447 /* Register SSM callbacks */
1448 rc = PDMDevHlpSSMRegister3(pDevIns, HPET_SAVED_STATE_VERSION, sizeof(*pThis), hpetR3LiveExec, hpetR3SaveExec, hpetR3LoadExec);
1449 AssertRCReturn(rc, rc);
1450
1451 /* Register an info callback. */
1452 PDMDevHlpDBGFInfoRegister(pDevIns, "hpet", "Display HPET status. (no arguments)", hpetR3Info);
1453
1454 return VINF_SUCCESS;
1455}
1456
1457
1458/**
1459 * The device registration structure.
1460 */
1461const PDMDEVREG g_DeviceHPET =
1462{
1463 /* u32Version */
1464 PDM_DEVREG_VERSION,
1465 /* szName */
1466 "hpet",
1467 /* szRCMod */
1468 "VBoxDDGC.gc",
1469 /* szR0Mod */
1470 "VBoxDDR0.r0",
1471 /* pszDescription */
1472 " High Precision Event Timer (HPET) Device",
1473 /* fFlags */
1474 PDM_DEVREG_FLAGS_HOST_BITS_DEFAULT | PDM_DEVREG_FLAGS_GUEST_BITS_32_64 | PDM_DEVREG_FLAGS_PAE36
1475 | PDM_DEVREG_FLAGS_RC | PDM_DEVREG_FLAGS_R0,
1476 /* fClass */
1477 PDM_DEVREG_CLASS_PIT,
1478 /* cMaxInstances */
1479 1,
1480 /* cbInstance */
1481 sizeof(HPET),
1482 /* pfnConstruct */
1483 hpetR3Construct,
1484 /* pfnDestruct */
1485 NULL,
1486 /* pfnRelocate */
1487 hpetR3Relocate,
1488 /* pfnMemSetup */
1489 NULL,
1490 /* pfnPowerOn */
1491 NULL,
1492 /* pfnReset */
1493 hpetR3Reset,
1494 /* pfnSuspend */
1495 NULL,
1496 /* pfnResume */
1497 NULL,
1498 /* pfnAttach */
1499 NULL,
1500 /* pfnDetach */
1501 NULL,
1502 /* pfnQueryInterface. */
1503 NULL,
1504 /* pfnInitComplete */
1505 NULL,
1506 /* pfnPowerOff */
1507 NULL,
1508 /* pfnSoftReset */
1509 NULL,
1510 /* u32VersionEnd */
1511 PDM_DEVREG_VERSION
1512};
1513
1514#endif /* IN_RING3 */
1515#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
1516
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