VirtualBox

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

最後變更 在這個檔案從42635是 42635,由 vboxsync 提交於 13 年 前

HPET: Comments, cleanup; no functional changes.

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette