VirtualBox

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

Last change on this file since 79756 was 79686, checked in by vboxsync, 5 years ago

HPET: Avoid a potential division overflow whereby a malicious guest could -- the horror -- commit harakiri (see bugref:9463).

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