VirtualBox

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

Last change on this file since 37466 was 37466, checked in by vboxsync, 14 years ago

VMM,Devices: Automatically use a per-device lock instead of the giant IOM lock. With exception of the PIC, APIC, IOAPIC and PCI buses which are all using the PDM crit sect, there should be no calls between devices. So, this change should be relatively safe.

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