VirtualBox

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

Last change on this file since 42811 was 42663, checked in by vboxsync, 12 years ago

HPET: More comments; do not lose high dword of timer config.

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