VirtualBox

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

Last change on this file since 78125 was 76553, checked in by vboxsync, 6 years ago

scm --update-copyright-year

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