Changeset 88351 in vbox
- Timestamp:
- Apr 2, 2021 9:50:33 PM (4 years ago)
- svn:sync-xref-src-repo-rev:
- 143595
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Devices/PC/DevHPET.cpp
r87767 r88351 204 204 /** 205 205 * A HPET timer. 206 * 207 * @note To avoid excessive locking, we many of the updates atomically. 206 208 */ 207 209 typedef struct HPETTIMER … … 237 239 } HPETTIMER; 238 240 AssertCompileMemberAlignment(HPETTIMER, u64Config, sizeof(uint64_t)); 241 AssertCompileSizeAlignment(HPETTIMER, 64); 239 242 /** Pointer to the shared state of an HPET timer. */ 240 243 typedef HPETTIMER *PHPETTIMER; … … 271 274 bool fIch9; 272 275 /** Size alignment padding. */ 273 uint8_t abPadding0[7]; 276 uint8_t abPadding0[7+8]; 277 278 /** The handle of the MMIO region. */ 279 IOMMMIOHANDLE hMmio; 274 280 275 281 /** Global device lock. */ 276 282 PDMCRITSECT CritSect; 277 278 /** The handle of the MMIO region. */279 IOMMMIOHANDLE hMmio;280 283 281 284 STAMCOUNTER StatCounterRead4Byte; … … 283 286 STAMCOUNTER StatCounterWriteLow; 284 287 STAMCOUNTER StatCounterWriteHigh; 288 STAMCOUNTER StatZeroDeltaHack; 285 289 } HPET; 290 AssertCompileMemberAlignment(HPET, aTimers, 64); 291 AssertCompileMemberAlignment(HPET, CritSect, 64); 286 292 /** Pointer to the shared HPET device state. */ 287 293 typedef HPET *PHPET; … … 334 340 #ifndef VBOX_DEVICE_STRUCT_TESTCASE 335 341 342 DECLINLINE(bool) hpet32bitTimerEx(uint64_t fConfig) 343 { 344 return !(fConfig & HPET_TN_SIZE_CAP) 345 || (fConfig & HPET_TN_32BIT); 346 } 347 336 348 337 349 DECLINLINE(bool) hpet32bitTimer(PHPETTIMER pHpetTimer) 338 350 { 339 uint64_t u64Cfg = pHpetTimer->u64Config;340 return ((u64Cfg & HPET_TN_SIZE_CAP) == 0) || ((u64Cfg & HPET_TN_32BIT) != 0); 341 } 351 return hpet32bitTimerEx(ASMAtomicUoReadU64(&pHpetTimer->u64Config)); 352 } 353 342 354 343 355 DECLINLINE(uint64_t) hpetInvalidValue(PHPETTIMER pHpetTimer) … … 356 368 } 357 369 358 DECLINLINE(uint64_t) hpetGetTicks(PPDMDEVINS pDevIns, PCHPET pThis) 359 { 360 /* 361 * We can use any timer to get current time, they all go with the same speed. 362 */ 363 return nsToHpetTicks(pThis, PDMDevHlpTimerGet(pDevIns, pThis->aTimers[0].hTimer) + pThis->u64HpetOffset); 370 DECLINLINE(uint64_t) hpetGetTicksEx(PCHPET pThis, uint64_t tsNow) 371 { 372 return nsToHpetTicks(pThis, tsNow + pThis->u64HpetOffset); 364 373 } 365 374 … … 383 392 } 384 393 385 DECLINLINE(uint64_t) hpetComputeDiff(PHPETTIMER pHpetTimer, uint64_t u64Now) 386 { 387 388 if (hpet32bitTimer(pHpetTimer)) 389 { 390 uint32_t u32Diff; 391 392 u32Diff = (uint32_t)pHpetTimer->u64Cmp - (uint32_t)u64Now; 393 u32Diff = (int32_t)u32Diff > 0 ? u32Diff : (uint32_t)0; 394 return (uint64_t)u32Diff; 395 } 396 uint64_t u64Diff; 397 398 u64Diff = pHpetTimer->u64Cmp - u64Now; 399 u64Diff = (int64_t)u64Diff > 0 ? u64Diff : (uint64_t)0; 400 return u64Diff; 401 } 402 403 404 static void hpetAdjustComparator(PHPETTIMER pHpetTimer, uint64_t u64Now) 405 { 406 if ((pHpetTimer->u64Config & HPET_TN_PERIODIC)) 407 { 408 uint64_t u64Period = pHpetTimer->u64Period; 409 if (u64Period) 410 { 411 uint64_t cPeriods = (u64Now - pHpetTimer->u64Cmp) / u64Period; 412 pHpetTimer->u64Cmp += (cPeriods + 1) * u64Period; 413 } 414 } 394 DECLINLINE(uint64_t) hpetComputeDiff(uint64_t fConfig, uint64_t uCmp, uint64_t uHpetNow) 395 { 396 if (hpet32bitTimerEx(fConfig)) 397 { 398 uint32_t u32Diff = (uint32_t)uCmp - (uint32_t)uHpetNow; 399 if ((int32_t)u32Diff > 0) 400 return u32Diff; 401 } 402 else 403 { 404 uint64_t u64Diff = uCmp - uHpetNow; 405 if ((int64_t)u64Diff > 0) 406 return u64Diff; 407 } 408 return 0; 409 } 410 411 412 DECLINLINE(uint64_t) hpetAdjustComparator(PHPETTIMER pHpetTimer, uint64_t fConfig, uint64_t uCmp, 413 uint64_t uPeriod, uint64_t uHpetNow) 414 { 415 if (fConfig & HPET_TN_PERIODIC) 416 { 417 if (uPeriod) 418 { 419 uint64_t cPeriods = (uHpetNow - uCmp) / uPeriod; 420 uCmp += (cPeriods + 1) * uPeriod; 421 ASMAtomicWriteU64(&pHpetTimer->u64Cmp, uCmp); 422 } 423 } 424 return uCmp; 415 425 } 416 426 … … 422 432 * @param pThis The shared HPET state. 423 433 * @param pHpetTimer The timer. 424 */ 425 DECLINLINE(void) hpetTimerSetFrequencyHint(PPDMDEVINS pDevIns, PHPET pThis, PHPETTIMER pHpetTimer) 426 { 427 if (pHpetTimer->u64Config & HPET_TN_PERIODIC) 428 { 429 uint64_t const u64Period = pHpetTimer->u64Period; 430 uint32_t const u32Freq = pThis->u32Period; 431 if (u64Period > 0 && u64Period < u32Freq) 432 PDMDevHlpTimerSetFrequencyHint(pDevIns, pHpetTimer->hTimer, u32Freq / (uint32_t)u64Period); 433 } 434 } 435 436 437 static void hpetProgramTimer(PPDMDEVINS pDevIns, PHPET pThis, PHPETTIMER pHpetTimer) 438 { 439 /* no wrapping on new timers */ 440 pHpetTimer->u8Wrap = 0; 441 442 uint64_t u64Ticks = hpetGetTicks(pDevIns, pThis); 443 hpetAdjustComparator(pHpetTimer, u64Ticks); 444 445 uint64_t u64Diff = hpetComputeDiff(pHpetTimer, u64Ticks); 434 * @param fConfig Already read config value. 435 * @param uPeriod Already read period value. 436 */ 437 DECLINLINE(void) hpetTimerSetFrequencyHint(PPDMDEVINS pDevIns, PHPET pThis, PHPETTIMER pHpetTimer, 438 uint64_t fConfig, uint64_t uPeriod) 439 { 440 if (fConfig & HPET_TN_PERIODIC) 441 { 442 uint64_t const nsPeriod = hpetTicksToNs(pThis, uPeriod); 443 if (nsPeriod < RT_NS_100MS) 444 PDMDevHlpTimerSetFrequencyHint(pDevIns, pHpetTimer->hTimer, RT_NS_1SEC / (uint32_t)nsPeriod); 445 } 446 } 447 448 449 /** 450 * Programs an HPET timer, arming hTimer for the next IRQ. 451 * 452 * @param pDevIns The device instance. 453 * @param pThis The HPET instance data. 454 * @param pHpetTimer The HPET timer to program. The wrap-around indicator is 455 * updates, and for periodic timer the comparator. 456 * @param tsNow The current virtual sync clock time. 457 * @note Caller must both the virtual sync (timer) and HPET locks. 458 */ 459 static void hpetProgramTimer(PPDMDEVINS pDevIns, PHPET pThis, PHPETTIMER pHpetTimer, uint64_t const tsNow) 460 { 461 /* 462 * Calculate the number of HPET ticks to the next timer IRQ, but 463 * first updating comparator if periodic timer. 464 */ 465 uint64_t const fConfig = pHpetTimer->u64Config; 466 uint64_t const uPeriod = pHpetTimer->u64Period; 467 uint64_t uCmp = pHpetTimer->u64Cmp; 468 uint64_t const uHpetNow = hpetGetTicksEx(pThis, tsNow); 469 uCmp = hpetAdjustComparator(pHpetTimer, fConfig, uCmp, uPeriod, uHpetNow); 470 uint64_t uHpetDelta = hpetComputeDiff(fConfig, uCmp, uHpetNow); 446 471 447 472 /* … … 449 474 * counter wraps in addition to an interrupt with comparator match. 450 475 */ 451 if ( hpet32bitTimer(pHpetTimer) 452 && !(pHpetTimer->u64Config & HPET_TN_PERIODIC)) 453 { 454 uint32_t u32TillWrap = 0xffffffff - (uint32_t)u64Ticks + 1; 455 if (u32TillWrap < (uint32_t)u64Diff) 456 { 457 Log(("wrap on timer %d: till=%u ticks=%lld diff64=%lld\n", 458 pHpetTimer->idxTimer, u32TillWrap, u64Ticks, u64Diff)); 459 u64Diff = u32TillWrap; 460 pHpetTimer->u8Wrap = 1; 461 } 462 } 476 bool fWrap = false; 477 if ( hpet32bitTimerEx(fConfig) 478 && !(fConfig & HPET_TN_PERIODIC)) 479 { 480 uint32_t cHpetTicksTillWrap = UINT32_MAX - (uint32_t)uHpetNow + 1; 481 if (cHpetTicksTillWrap < (uint32_t)uHpetDelta) 482 { 483 Log(("HPET[%u]: wrap: till=%u ticks=%lld diff64=%lld\n", 484 pHpetTimer->idxTimer, cHpetTicksTillWrap, uHpetNow, uHpetDelta)); 485 uHpetDelta = cHpetTicksTillWrap; 486 fWrap = true; 487 } 488 } 489 pHpetTimer->u8Wrap = fWrap; 463 490 464 491 /* … … 466 493 */ 467 494 #if 1 /** @todo HACK, rethink, may have negative impact on the guest */ 468 if (u64Diff == 0) 469 u64Diff = 100000; /* 1 millisecond */ 495 if (uHpetDelta != 0) 496 { /* likely? */ } 497 else 498 { 499 Log(("HPET[%u]: Applying zero delta hack!\n", pHpetTimer->idxTimer)); 500 STAM_REL_COUNTER_INC(&pThis->StatZeroDeltaHack); 501 /** @todo lower this. */ 502 uHpetDelta = pThis->fIch9 ? 14318 : 100000; /* 1 millisecond */ 503 } 470 504 #endif 471 505 506 /* 507 * Arm the timer. 508 */ 472 509 uint64_t u64TickLimit = pThis->fIch9 ? HPET_TICKS_IN_100YR_ICH9 : HPET_TICKS_IN_100YR_PIIX; 473 if (u64Diff <= u64TickLimit) 474 { 475 Log4(("HPET: next IRQ in %lld ticks (%lld ns)\n", u64Diff, hpetTicksToNs(pThis, u64Diff))); 510 if (uHpetDelta <= u64TickLimit) 511 { 512 uint64_t const tsDeadline = tsNow + hpetTicksToNs(pThis, uHpetDelta); 513 Log4(("HPET[%u]: next IRQ in %lld ticks (at %llu)\n", pHpetTimer->idxTimer, uHpetDelta, tsDeadline)); 514 PDMDevHlpTimerSet(pDevIns, pHpetTimer->hTimer, tsDeadline); 515 hpetTimerSetFrequencyHint(pDevIns, pThis, pHpetTimer, fConfig, uPeriod); 476 516 STAM_REL_COUNTER_INC(&pHpetTimer->StatSetTimer); 477 PDMDevHlpTimerSetNano(pDevIns, pHpetTimer->hTimer, hpetTicksToNs(pThis, u64Diff));478 517 } 479 518 else 480 { 481 LogRelMax(10, ("HPET: Not scheduling an interrupt more than 100 years in the future.\n")); 482 } 483 hpetTimerSetFrequencyHint(pDevIns, pThis, pHpetTimer); 519 LogRelMax(10, ("HPET[%u]: Not scheduling an interrupt more than 100 years in the future.\n", pHpetTimer->idxTimer)); 484 520 } 485 521 … … 491 527 * Reads a HPET timer register. 492 528 * 493 * @ param pDevIns The device instance.529 * @returns The register value. 494 530 * @param pThis The HPET instance. 495 531 * @param iTimerNo The timer index. 496 532 * @param iTimerReg The index of the timer register to read. 497 * @param pu32Value Where to return the register value. 498 * 499 * @remarks ASSUMES the caller holds the HPET lock. 500 */ 501 static void hpetTimerRegRead32(PPDMDEVINS pDevIns, PCHPET pThis, uint32_t iTimerNo, uint32_t iTimerReg, uint32_t *pu32Value) 502 { 503 Assert(PDMDevHlpCritSectIsOwner(pDevIns, &pThis->CritSect)); 504 RT_NOREF(pDevIns); 505 533 * 534 * @note No locking required. 535 */ 536 static uint32_t hpetTimerRegRead32(PHPET pThis, uint32_t iTimerNo, uint32_t iTimerReg) 537 { 506 538 uint32_t u32Value; 507 539 if ( iTimerNo < HPET_CAP_GET_TIMERS(pThis->u32Capabilities) 508 540 && iTimerNo < RT_ELEMENTS(pThis->aTimers) ) 509 541 { 510 P CHPETTIMER pHpetTimer = &pThis->aTimers[iTimerNo];542 PHPETTIMER pHpetTimer = &pThis->aTimers[iTimerNo]; 511 543 switch (iTimerReg) 512 544 { 513 545 case HPET_TN_CFG: 514 u32Value = (uint32_t) pHpetTimer->u64Config;515 Log((" read HPET_TN_CFG on %d: %#x\n", iTimerNo, u32Value));546 u32Value = (uint32_t)ASMAtomicReadU64(&pHpetTimer->u64Config); 547 Log(("HPET[%u]: read32 HPET_TN_CFG: %#x\n", iTimerNo, u32Value)); 516 548 break; 517 549 518 550 case HPET_TN_CFG + 4: 519 u32Value = (uint32_t)( pHpetTimer->u64Config>> 32);520 Log((" read HPET_TN_CFG+4 on %d: %#x\n", iTimerNo, u32Value));551 u32Value = (uint32_t)(ASMAtomicReadU64(&pHpetTimer->u64Config) >> 32); 552 Log(("HPET[%u]: read32 HPET_TN_CFG+4: %#x\n", iTimerNo, u32Value)); 521 553 break; 522 554 523 555 case HPET_TN_CMP: 524 u32Value = (uint32_t)pHpetTimer->u64Cmp; 525 Log(("read HPET_TN_CMP on %d: %#x (%#llx)\n", pHpetTimer->idxTimer, u32Value, pHpetTimer->u64Cmp)); 526 break; 556 { 557 uint64_t uCmp = ASMAtomicReadU64(&pHpetTimer->u64Cmp); 558 u32Value = (uint32_t)uCmp; 559 Log(("HPET[%u]: read32 HPET_TN_CMP: %#x (%#RX64)\n", pHpetTimer->idxTimer, u32Value, uCmp)); 560 break; 561 } 527 562 528 563 case HPET_TN_CMP + 4: 529 u32Value = (uint32_t)(pHpetTimer->u64Cmp >> 32); 530 Log(("read HPET_TN_CMP+4 on %d: %#x (%#llx)\n", pHpetTimer->idxTimer, u32Value, pHpetTimer->u64Cmp)); 531 break; 564 { 565 uint64_t uCmp = ASMAtomicReadU64(&pHpetTimer->u64Cmp); 566 u32Value = (uint32_t)(uCmp >> 32); 567 Log(("HPET[%u]: read32 HPET_TN_CMP+4: %#x (%#RX64)\n", pHpetTimer->idxTimer, u32Value, uCmp)); 568 break; 569 } 532 570 533 571 case HPET_TN_ROUTE: 534 572 u32Value = (uint32_t)(pHpetTimer->u64Fsb >> 32); /** @todo Looks wrong, but since it's not supported, who cares. */ 535 Log((" read HPET_TN_ROUTE on %d: %#x\n", iTimerNo, u32Value));573 Log(("HPET[%u]: read32 HPET_TN_ROUTE: %#x\n", iTimerNo, u32Value)); 536 574 break; 537 575 538 576 default: 539 { 540 LogRelMax(10, ("HPET: Invalid HPET register read %d on %d\n", iTimerReg, pHpetTimer->idxTimer)); 577 LogRelMax(10, ("HPET[%u]: Invalid HPET register read: %d\n", iTimerNo, iTimerReg)); 541 578 u32Value = 0; 542 579 break; 543 }544 580 } 545 581 } … … 549 585 u32Value = 0; 550 586 } 551 *pu32Value = u32Value; 587 return u32Value; 588 } 589 590 591 /** 592 * Reads a HPET timer register, 64-bit access. 593 * 594 * @returns The register value. 595 * @param pThis The HPET instance. 596 * @param iTimerNo The timer index. 597 * @param iTimerReg The index of the timer register to read. 598 */ 599 static uint64_t hpetTimerRegRead64(PHPET pThis, uint32_t iTimerNo, uint32_t iTimerReg) 600 { 601 uint64_t u64Value; 602 if ( iTimerNo < HPET_CAP_GET_TIMERS(pThis->u32Capabilities) 603 && iTimerNo < RT_ELEMENTS(pThis->aTimers) ) 604 { 605 PHPETTIMER pHpetTimer = &pThis->aTimers[iTimerNo]; 606 switch (iTimerReg) 607 { 608 case HPET_TN_CFG: 609 u64Value = ASMAtomicReadU64(&pHpetTimer->u64Config); 610 Log(("HPET[%u]: read64 HPET_TN_CFG: %#RX64\n", iTimerNo, u64Value)); 611 break; 612 613 case HPET_TN_CMP: 614 u64Value = ASMAtomicReadU64(&pHpetTimer->u64Config); 615 Log(("HPET[%u]: read64 HPET_TN_CMP: %#RX64\n", iTimerNo, u64Value)); 616 break; 617 618 case HPET_TN_ROUTE: 619 u64Value = (uint32_t)(pHpetTimer->u64Fsb >> 32); /** @todo Looks wrong, but since it's not supported, who cares. */ 620 Log(("HPET[%u]: read64 HPET_TN_ROUTE: %#RX64\n", iTimerNo, u64Value)); 621 break; 622 623 default: 624 LogRelMax(10, ("HPET[%u]: Invalid 64-bit HPET register read64: %d\n", iTimerNo, iTimerReg)); 625 u64Value = 0; 626 break; 627 } 628 } 629 else 630 { 631 LogRelMax(10, ("HPET: Using timer above configured range: %d\n", iTimerNo)); 632 u64Value = 0; 633 } 634 return u64Value; 552 635 } 553 636 … … 564 647 * @param u32NewValue The value being written. 565 648 * 566 * @remarks The caller should not hold the device lock, unless it also holds 567 * the TM lock. 649 * @remarks The caller should not hold any locks. 568 650 */ 569 651 static VBOXSTRICTRC hpetTimerRegWrite32(PPDMDEVINS pDevIns, PHPET pThis, uint32_t iTimerNo, 570 652 uint32_t iTimerReg, uint32_t u32NewValue) 571 653 { 572 Assert(!PDMDevHlpCritSectIsOwner(pDevIns, &pThis->CritSect) || PDMDevHlpTimerIsLockOwner(pDevIns, pThis->aTimers[0].hTimer)); 573 574 if ( iTimerNo >= HPET_CAP_GET_TIMERS(pThis->u32Capabilities) 575 || iTimerNo >= RT_ELEMENTS(pThis->aTimers) ) /* Parfait - see above. */ 576 { 577 LogRelMax(10, ("HPET: Using timer above configured range: %d\n", iTimerNo)); 578 return VINF_SUCCESS; 579 } 580 PHPETTIMER pHpetTimer = &pThis->aTimers[iTimerNo]; 581 582 switch (iTimerReg) 583 { 584 case HPET_TN_CFG: 585 { 586 DEVHPET_LOCK_RETURN(pDevIns, pThis, VINF_IOM_R3_MMIO_WRITE); 587 uint64_t u64Mask = HPET_TN_CFG_WRITE_MASK; 588 589 Log(("write HPET_TN_CFG: %d: %x\n", iTimerNo, u32NewValue)); 590 if (pHpetTimer->u64Config & HPET_TN_PERIODIC_CAP) 591 u64Mask |= HPET_TN_PERIODIC; 592 593 if (pHpetTimer->u64Config & HPET_TN_SIZE_CAP) 594 u64Mask |= HPET_TN_32BIT; 595 else 596 u32NewValue &= ~HPET_TN_32BIT; 597 598 if (u32NewValue & HPET_TN_32BIT) 654 Assert(!PDMDevHlpCritSectIsOwner(pDevIns, &pThis->CritSect)); 655 Assert(!PDMDevHlpTimerIsLockOwner(pDevIns, pThis->aTimers[0].hTimer)); 656 657 if ( iTimerNo < HPET_CAP_GET_TIMERS(pThis->u32Capabilities) 658 && iTimerNo < RT_ELEMENTS(pThis->aTimers) ) /* Parfait - see above. */ 659 { 660 PHPETTIMER pHpetTimer = &pThis->aTimers[iTimerNo]; 661 662 switch (iTimerReg) 663 { 664 case HPET_TN_CFG: 599 665 { 600 Log(("setting timer %d to 32-bit mode\n", iTimerNo)); 601 pHpetTimer->u64Cmp = (uint32_t)pHpetTimer->u64Cmp; 602 pHpetTimer->u64Period = (uint32_t)pHpetTimer->u64Period; 666 /* 667 * Calculate the writable mask and see if anything actually changed 668 * before doing any locking. Windows 10 (1809) does two CFG writes 669 * with the same value (0x134) when reprogramming the HPET#0 timer. 670 */ 671 uint64_t fConfig = ASMAtomicUoReadU64(&pHpetTimer->u64Config); 672 uint64_t const fMask = HPET_TN_CFG_WRITE_MASK 673 | (fConfig & HPET_TN_PERIODIC_CAP ? HPET_TN_PERIODIC : 0) 674 | (fConfig & HPET_TN_SIZE_CAP ? HPET_TN_32BIT : 0); 675 if ((u32NewValue & fMask) == (fConfig & fMask)) 676 Log(("HPET[%u]: write32 HPET_TN_CFG: %#x - no change (%#RX64)\n", iTimerNo, u32NewValue, fConfig)); 677 else 678 { 679 #ifndef IN_RING3 680 /* Return to ring-3 (where LogRel works) to complain about level-triggered interrupts. */ 681 if ((u32NewValue & HPET_TN_INT_TYPE) == HPET_TIMER_TYPE_LEVEL) 682 return VINF_IOM_R3_MMIO_WRITE; 683 #endif 684 DEVHPET_LOCK_RETURN(pDevIns, pThis, VINF_IOM_R3_MMIO_WRITE); 685 686 fConfig = ASMAtomicUoReadU64(&pHpetTimer->u64Config); 687 uint64_t const fConfigNew = hpetUpdateMasked(u32NewValue, fConfig, fMask); 688 Log(("HPET[%u]: write HPET_TN_CFG: %#RX64 -> %#RX64\n", iTimerNo, fConfig, fConfigNew)); 689 690 if ((fConfigNew & HPET_TN_32BIT) == (fConfig & HPET_TN_32BIT)) 691 { /* likely it stays the same */ } 692 else if (fConfigNew & HPET_TN_32BIT) 693 { 694 Log(("HPET[%u]: Changing timer to 32-bit mode.\n", iTimerNo)); 695 ASMAtomicUoWriteU64(&pHpetTimer->u64Cmp, (uint32_t)pHpetTimer->u64Cmp); 696 ASMAtomicUoWriteU64(&pHpetTimer->u64Period, (uint32_t)pHpetTimer->u64Period); 697 } 698 else 699 Log(("HPET[%u]: Changing timer to 64-bit mode.\n", iTimerNo)); 700 ASMAtomicWriteU64(&pHpetTimer->u64Config, fConfigNew); 701 702 DEVHPET_UNLOCK(pDevIns, pThis); 703 704 if (RT_LIKELY((fConfigNew & HPET_TN_INT_TYPE) != HPET_TIMER_TYPE_LEVEL)) 705 { /* likely */ } 706 else 707 { 708 LogRelMax(10, ("HPET[%u]: Level-triggered config not yet supported\n", iTimerNo)); 709 AssertFailed(); 710 } 711 } 712 break; 603 713 } 604 if ((u32NewValue & HPET_TN_INT_TYPE) == HPET_TIMER_TYPE_LEVEL) 714 715 case HPET_TN_CFG + 4: /* Interrupt capabilities - read only. */ 716 Log(("HPET[%u]: write32 HPET_TN_CFG + 4 (ignored)\n", iTimerNo)); 717 break; 718 719 case HPET_TN_CMP: /* lower bits of comparator register */ 605 720 { 606 LogRelMax(10, ("HPET: Level-triggered config not yet supported\n")); 607 AssertFailed(); 721 DEVHPET_LOCK_BOTH_RETURN(pDevIns, pThis, VINF_IOM_R3_MMIO_WRITE); 722 Log(("HPET[%u]: write32 HPET_TN_CMP: %#x\n", iTimerNo, u32NewValue)); 723 uint64_t fConfig = ASMAtomicUoReadU64(&pHpetTimer->u64Config); 724 725 /** @todo r=bird: This is wrong. Specification says: "By writing this bit to a 1, the software is then allowed to directly set a periodic timer’s accumulator." 726 * See https://wiki.osdev.org/HPET and hpet_clkevt_set_state_periodic() in linux. */ 727 if (fConfig & HPET_TN_PERIODIC) 728 ASMAtomicUoWriteU64(&pHpetTimer->u64Period, RT_MAKE_U64(u32NewValue, RT_HI_U32(pHpetTimer->u64Period))); 729 ASMAtomicUoWriteU64(&pHpetTimer->u64Cmp, RT_MAKE_U64(u32NewValue, RT_HI_U32(pHpetTimer->u64Cmp))); 730 ASMAtomicAndU64(&pHpetTimer->u64Config, ~HPET_TN_SETVAL); 731 Log2(("HPET[%u]: after32 HPET_TN_CMP cmp=%#llx per=%#llx\n", iTimerNo, pHpetTimer->u64Cmp, pHpetTimer->u64Period)); 732 733 if (pThis->u64HpetConfig & HPET_CFG_ENABLE) 734 hpetProgramTimer(pDevIns, pThis, pHpetTimer, PDMDevHlpTimerGet(pDevIns, pHpetTimer->hTimer)); 735 DEVHPET_UNLOCK_BOTH(pDevIns, pThis); 736 break; 608 737 } 609 738 610 /* We only care about lower 32-bits so far */ 611 pHpetTimer->u64Config = hpetUpdateMasked(u32NewValue, pHpetTimer->u64Config, u64Mask); 612 DEVHPET_UNLOCK(pDevIns, pThis); 613 break; 614 } 615 616 case HPET_TN_CFG + 4: /* Interrupt capabilities - read only. */ 617 Log(("write HPET_TN_CFG + 4, useless\n")); 618 break; 619 620 case HPET_TN_CMP: /* lower bits of comparator register */ 621 { 622 DEVHPET_LOCK_BOTH_RETURN(pDevIns, pThis, VINF_IOM_R3_MMIO_WRITE); 623 Log(("write HPET_TN_CMP on %d: %#x\n", iTimerNo, u32NewValue)); 624 625 if (pHpetTimer->u64Config & HPET_TN_PERIODIC) 626 pHpetTimer->u64Period = RT_MAKE_U64(u32NewValue, RT_HI_U32(pHpetTimer->u64Period)); 627 pHpetTimer->u64Cmp = RT_MAKE_U64(u32NewValue, RT_HI_U32(pHpetTimer->u64Cmp)); 628 pHpetTimer->u64Config &= ~HPET_TN_SETVAL; 629 Log2(("after HPET_TN_CMP cmp=%#llx per=%#llx\n", pHpetTimer->u64Cmp, pHpetTimer->u64Period)); 630 631 if (pThis->u64HpetConfig & HPET_CFG_ENABLE) 632 hpetProgramTimer(pDevIns, pThis, pHpetTimer); 633 DEVHPET_UNLOCK_BOTH(pDevIns, pThis); 634 break; 635 } 636 637 case HPET_TN_CMP + 4: /* upper bits of comparator register */ 638 { 639 DEVHPET_LOCK_BOTH_RETURN(pDevIns, pThis, VINF_IOM_R3_MMIO_WRITE); 640 Log(("write HPET_TN_CMP + 4 on %d: %#x\n", iTimerNo, u32NewValue)); 641 if (!hpet32bitTimer(pHpetTimer)) 739 case HPET_TN_CMP + 4: /* upper bits of comparator register */ 642 740 { 643 if (pHpetTimer->u64Config & HPET_TN_PERIODIC) 644 pHpetTimer->u64Period = RT_MAKE_U64(RT_LO_U32(pHpetTimer->u64Period), u32NewValue); 645 pHpetTimer->u64Cmp = RT_MAKE_U64(RT_LO_U32(pHpetTimer->u64Cmp), u32NewValue); 646 647 Log2(("after HPET_TN_CMP+4 cmp=%llx per=%llx tmr=%d\n", pHpetTimer->u64Cmp, pHpetTimer->u64Period, iTimerNo)); 648 649 pHpetTimer->u64Config &= ~HPET_TN_SETVAL; 741 DEVHPET_LOCK_BOTH_RETURN(pDevIns, pThis, VINF_IOM_R3_MMIO_WRITE); 742 uint64_t fConfig = ASMAtomicUoReadU64(&pHpetTimer->u64Config); 743 744 if (!hpet32bitTimerEx(fConfig)) 745 { 746 Log(("HPET[%u]: write32 HPET_TN_CMP + 4: %#x\n", iTimerNo, u32NewValue)); 747 /** @todo r=bird: This is wrong. Specification says: "By writing this bit to a 1, the software is then allowed to directly set a periodic timer’s accumulator." 748 * See https://wiki.osdev.org/HPET and hpet_clkevt_set_state_periodic() in linux. */ 749 if (fConfig & HPET_TN_PERIODIC) 750 ASMAtomicUoWriteU64(&pHpetTimer->u64Period, RT_MAKE_U64(RT_LO_U32(pHpetTimer->u64Period), u32NewValue)); 751 ASMAtomicUoWriteU64(&pHpetTimer->u64Cmp, RT_MAKE_U64(RT_LO_U32(pHpetTimer->u64Cmp), u32NewValue)); 752 ASMAtomicAndU64(&pHpetTimer->u64Config, ~HPET_TN_SETVAL); 753 Log2(("HPET[%u]: after32 HPET_TN_CMP+4: cmp=%#llx per=%#llx\n", iTimerNo, pHpetTimer->u64Cmp, pHpetTimer->u64Period)); 754 755 if (pThis->u64HpetConfig & HPET_CFG_ENABLE) 756 hpetProgramTimer(pDevIns, pThis, pHpetTimer, PDMDevHlpTimerGet(pDevIns, pHpetTimer->hTimer)); 757 } 758 else 759 Log(("HPET[%u]: write32 HPET_TN_CMP + 4: %#x - but timer is 32-bit!!\n", iTimerNo, u32NewValue)); 760 DEVHPET_UNLOCK_BOTH(pDevIns, pThis); 761 break; 762 } 763 764 case HPET_TN_ROUTE: 765 Log(("HPET[%u]: write32 HPET_TN_ROUTE (ignored)\n", iTimerNo)); 766 break; 767 768 case HPET_TN_ROUTE + 4: 769 Log(("HPET[%u]: write32 HPET_TN_ROUTE + 4 (ignored)\n", iTimerNo)); 770 break; 771 772 default: 773 LogRelMax(10, ("HPET[%u]: Invalid timer register write: %d\n", iTimerNo, iTimerReg)); 774 break; 775 } 776 } 777 else 778 LogRelMax(10, ("HPET: Using timer above configured range: %d (reg %#x)\n", iTimerNo, iTimerReg)); 779 return VINF_SUCCESS; 780 } 781 782 783 /** 784 * 32-bit write to a HPET timer register. 785 * 786 * @returns Strict VBox status code. 787 * 788 * @param pDevIns The device instance. 789 * @param pThis The shared HPET state. 790 * @param iTimerNo The timer being written to. 791 * @param iTimerReg The register being written to. 792 * @param u32NewValue The value being written. 793 * 794 * @remarks The caller should not hold any locks. 795 */ 796 static VBOXSTRICTRC hpetTimerRegWrite64(PPDMDEVINS pDevIns, PHPET pThis, uint32_t iTimerNo, 797 uint32_t iTimerReg, uint64_t u64NewValue) 798 { 799 Assert(!PDMDevHlpCritSectIsOwner(pDevIns, &pThis->CritSect)); 800 Assert(!PDMDevHlpTimerIsLockOwner(pDevIns, pThis->aTimers[0].hTimer)); 801 Assert(!(iTimerReg & 7)); 802 803 if ( iTimerNo < HPET_CAP_GET_TIMERS(pThis->u32Capabilities) 804 && iTimerNo < RT_ELEMENTS(pThis->aTimers) ) /* Parfait - see above. */ 805 { 806 PHPETTIMER pHpetTimer = &pThis->aTimers[iTimerNo]; 807 808 switch (iTimerReg) 809 { 810 case HPET_TN_CFG: 811 /* The upper 32 bits are not writable, so join paths with the 32-bit version. */ 812 return hpetTimerRegWrite32(pDevIns, pThis, iTimerNo, iTimerReg, (uint32_t)u64NewValue); 813 814 case HPET_TN_CMP: 815 { 816 DEVHPET_LOCK_BOTH_RETURN(pDevIns, pThis, VINF_IOM_R3_MMIO_WRITE); 817 Log(("HPET[%u]: write64 HPET_TN_CMP: %#RX64\n", iTimerNo, u64NewValue)); 818 uint64_t fConfig = ASMAtomicUoReadU64(&pHpetTimer->u64Config); 819 820 /** @todo r=bird: This is wrong. Specification says: "By writing this bit to a 1, the software is then allowed to directly set a periodic timer’s accumulator." 821 * See https://wiki.osdev.org/HPET and hpet_clkevt_set_state_periodic() in linux. */ 822 if (fConfig & HPET_TN_PERIODIC) 823 ASMAtomicUoWriteU64(&pHpetTimer->u64Period, u64NewValue); 824 ASMAtomicUoWriteU64(&pHpetTimer->u64Cmp, u64NewValue); 825 ASMAtomicAndU64(&pHpetTimer->u64Config, ~HPET_TN_SETVAL); 826 Log2(("HPET[%u]: after64 HPET_TN_CMP cmp=%#llx per=%#llx\n", iTimerNo, pHpetTimer->u64Cmp, pHpetTimer->u64Period)); 650 827 651 828 if (pThis->u64HpetConfig & HPET_CFG_ENABLE) 652 hpetProgramTimer(pDevIns, pThis, pHpetTimer); 829 hpetProgramTimer(pDevIns, pThis, pHpetTimer, PDMDevHlpTimerGet(pDevIns, pHpetTimer->hTimer)); 830 DEVHPET_UNLOCK_BOTH(pDevIns, pThis); 831 break; 653 832 } 654 DEVHPET_UNLOCK_BOTH(pDevIns, pThis); 655 break; 656 } 657 658 case HPET_TN_ROUTE: 659 Log(("write HPET_TN_ROUTE\n")); 660 break; 661 662 case HPET_TN_ROUTE + 4: 663 Log(("write HPET_TN_ROUTE + 4\n")); 664 break; 665 666 default: 667 LogRelMax(10, ("HPET: Invalid timer register write: %d\n", iTimerReg)); 668 break; 669 } 670 833 834 case HPET_TN_ROUTE: 835 Log(("HPET[%u]: write64 HPET_TN_ROUTE (ignored)\n", iTimerNo)); 836 break; 837 838 default: 839 LogRelMax(10, ("HPET[%u]: Invalid timer register write: %d\n", iTimerNo, iTimerReg)); 840 break; 841 } 842 } 843 else 844 LogRelMax(10, ("HPET: Using timer above configured range: %d (reg %#x)\n", iTimerNo, iTimerReg)); 671 845 return VINF_SUCCESS; 672 846 } … … 725 899 case HPET_COUNTER + 4: 726 900 { 727 STAM_REL_COUNTER_INC(&pThis->StatCounterRead4Byte); 901 /** @todo We don't technically need to sit on the virtualsync lock here to 902 * read it, but it helps wrt quality... */ 728 903 DEVHPET_LOCK_BOTH_RETURN(pDevIns, pThis, VINF_IOM_R3_MMIO_READ); 729 904 730 905 uint64_t u64Ticks; 731 906 if (pThis->u64HpetConfig & HPET_CFG_ENABLE) 732 u64Ticks = hpetGetTicks(pDevIns, pThis); 907 { 908 uint64_t const tsNow = PDMDevHlpTimerGet(pDevIns, pThis->aTimers[0].hTimer); 909 PDMDevHlpTimerUnlockClock(pDevIns, pThis->aTimers[0].hTimer); 910 u64Ticks = hpetGetTicksEx(pThis, tsNow); 911 } 733 912 else 913 { 914 PDMDevHlpTimerUnlockClock(pDevIns, pThis->aTimers[0].hTimer); 734 915 u64Ticks = pThis->u64HpetCounter; 735 736 DEVHPET_UNLOCK_BOTH(pDevIns, pThis); 916 } 917 918 STAM_REL_COUNTER_INC(&pThis->StatCounterRead4Byte); 919 DEVHPET_UNLOCK(pDevIns, pThis); 737 920 738 921 /** @todo is it correct? */ 739 u32Value = (idxReg == HPET_COUNTER)? (uint32_t)u64Ticks : (uint32_t)(u64Ticks >> 32);922 u32Value = idxReg == HPET_COUNTER ? (uint32_t)u64Ticks : (uint32_t)(u64Ticks >> 32); 740 923 Log(("read HPET_COUNTER: %s part value %x (%#llx)\n", (idxReg == HPET_COUNTER) ? "low" : "high", u32Value, u64Ticks)); 741 924 break; … … 817 1000 } 818 1001 819 pThis->u64HpetConfig = hpetUpdateMasked(u32NewValue, iOldValue, HPET_CFG_WRITE_MASK); 820 821 uint32_t const cTimers = HPET_CAP_GET_TIMERS(pThis->u32Capabilities); 1002 /* Updating it using an atomic write just to be on the safe side. */ 1003 ASMAtomicWriteU64(&pThis->u64HpetConfig, hpetUpdateMasked(u32NewValue, iOldValue, HPET_CFG_WRITE_MASK)); 1004 1005 uint32_t const cTimers = RT_MIN(HPET_CAP_GET_TIMERS(pThis->u32Capabilities), RT_ELEMENTS(pThis->aTimers)); 822 1006 if (hpetBitJustSet(iOldValue, u32NewValue, HPET_CFG_ENABLE)) 823 1007 { 824 /** @todo Only get the time stamp once when reprogramming? */ 825 /* Enable main counter and interrupt generation. */ 1008 /* 1009 * Enable main counter and interrupt generation. 1010 */ 826 1011 uint64_t u64TickLimit = pThis->fIch9 ? HPET_TICKS_IN_100YR_ICH9 : HPET_TICKS_IN_100YR_PIIX; 827 1012 if (pThis->u64HpetCounter <= u64TickLimit) 828 { 829 pThis->u64HpetOffset = hpetTicksToNs(pThis, pThis->u64HpetCounter) 830 - PDMDevHlpTimerGet(pDevIns, pThis->aTimers[0].hTimer); 831 } 1013 pThis->u64HpetOffset = hpetTicksToNs(pThis, pThis->u64HpetCounter); 832 1014 else 833 1015 { 834 1016 LogRelMax(10, ("HPET: Counter set more than 100 years in the future, reducing.\n")); 835 pThis->u64HpetOffset = 1000000LL * 60 * 60 * 24 * 365 * 100 836 - PDMDevHlpTimerGet(pDevIns, pThis->aTimers[0].hTimer); 1017 pThis->u64HpetOffset = 1000000LL * 60 * 60 * 24 * 365 * 100; 837 1018 } 1019 1020 uint64_t const tsNow = PDMDevHlpTimerGet(pDevIns, pThis->aTimers[0].hTimer); 1021 pThis->u64HpetOffset -= tsNow; 1022 838 1023 for (uint32_t i = 0; i < cTimers; i++) 839 1024 if (pThis->aTimers[i].u64Cmp != hpetInvalidValue(&pThis->aTimers[i])) 840 hpetProgramTimer(pDevIns, pThis, &pThis->aTimers[i] );1025 hpetProgramTimer(pDevIns, pThis, &pThis->aTimers[i], tsNow); 841 1026 } 842 1027 else if (hpetBitJustCleared(iOldValue, u32NewValue, HPET_CFG_ENABLE)) 843 1028 { 844 /* Halt main counter and disable interrupt generation. */ 845 pThis->u64HpetCounter = hpetGetTicks(pDevIns, pThis); 1029 /* 1030 * Halt main counter and disable interrupt generation. 1031 */ 1032 pThis->u64HpetCounter = hpetGetTicksEx(pThis, PDMDevHlpTimerGet(pDevIns, pThis->aTimers[0].hTimer)); 846 1033 for (uint32_t i = 0; i < cTimers; i++) 847 1034 PDMDevHlpTimerStop(pDevIns, pThis->aTimers[i].hTimer); … … 854 1041 case HPET_CFG + 4: 855 1042 { 1043 /** @todo r=bird: Is the whole upper part of the config register really 1044 * writable? Only 2 bits are writable in the lower part... */ 856 1045 DEVHPET_LOCK_RETURN(pDevIns, pThis, VINF_IOM_R3_MMIO_WRITE); 857 1046 pThis->u64HpetConfig = hpetUpdateMasked((uint64_t)u32NewValue << 32, … … 886 1075 DEVHPET_LOCK_RETURN(pDevIns, pThis, VINF_IOM_R3_MMIO_WRITE); 887 1076 pThis->u64HpetCounter = RT_MAKE_U64(u32NewValue, RT_HI_U32(pThis->u64HpetCounter)); 1077 /** @todo how is this supposed to work if the HPET is enabled? */ 888 1078 Log(("write HPET_COUNTER: %#x -> %llx\n", u32NewValue, pThis->u64HpetCounter)); 889 1079 DEVHPET_UNLOCK(pDevIns, pThis); … … 921 1111 NOREF(pvUser); 922 1112 Assert(cb == 4 || cb == 8); 1113 Assert(!(off & (cb - 1))); 923 1114 924 1115 LogFlow(("hpetMMIORead (%d): %RGp\n", cb, off)); … … 932 1123 if (off >= 0x100 && off < 0x400) 933 1124 { 934 DEVHPET_LOCK_RETURN(pDevIns, pThis, VINF_IOM_R3_MMIO_READ); 935 hpetTimerRegRead32(pDevIns, pThis, 936 (uint32_t)(off - 0x100) / 0x20, 937 (uint32_t)(off - 0x100) % 0x20, 938 (uint32_t *)pv); 939 DEVHPET_UNLOCK(pDevIns, pThis); 1125 *(uint32_t *)pv = hpetTimerRegRead32(pThis, 1126 (uint32_t)(off - 0x100) / 0x20, 1127 (uint32_t)(off - 0x100) % 0x20); 940 1128 rc = VINF_SUCCESS; 941 1129 } … … 952 1140 if (off == HPET_COUNTER) 953 1141 { 1142 /** @todo We don't technically need to sit on the virtualsync lock here to 1143 * read it, but it helps wrt quality... */ 954 1144 /* When reading HPET counter we must read it in a single read, 955 1145 to avoid unexpected time jumps on 32-bit overflow. */ 1146 DEVHPET_LOCK_BOTH_RETURN(pDevIns, pThis, VINF_IOM_R3_MMIO_READ); 1147 1148 if (pThis->u64HpetConfig & HPET_CFG_ENABLE) 1149 { 1150 uint64_t const tsNow = PDMDevHlpTimerGet(pDevIns, pThis->aTimers[0].hTimer); 1151 PDMDevHlpTimerUnlockClock(pDevIns, pThis->aTimers[0].hTimer); 1152 pValue->u = hpetGetTicksEx(pThis, tsNow); 1153 } 1154 else 1155 { 1156 PDMDevHlpTimerUnlockClock(pDevIns, pThis->aTimers[0].hTimer); 1157 pValue->u = pThis->u64HpetCounter; 1158 } 1159 956 1160 STAM_REL_COUNTER_INC(&pThis->StatCounterRead8Byte); 957 DEVHPET_LOCK_BOTH_RETURN(pDevIns, pThis, VINF_IOM_R3_MMIO_READ); 958 if (pThis->u64HpetConfig & HPET_CFG_ENABLE) 959 pValue->u = hpetGetTicks(pDevIns, pThis); 960 else 961 pValue->u = pThis->u64HpetCounter; 962 DEVHPET_UNLOCK_BOTH(pDevIns, pThis); 1161 DEVHPET_UNLOCK(pDevIns, pThis); 963 1162 rc = VINF_SUCCESS; 964 1163 } 965 1164 else 966 1165 { 967 DEVHPET_LOCK_RETURN(pDevIns, pThis, VINF_IOM_R3_MMIO_READ);968 1166 if (off >= 0x100 && off < 0x400) 969 1167 { 970 1168 uint32_t iTimer = (uint32_t)(off - 0x100) / 0x20; 971 1169 uint32_t iTimerReg = (uint32_t)(off - 0x100) % 0x20; 972 hpetTimerRegRead32(pDevIns, pThis, iTimer, iTimerReg, &pValue->s.Lo);973 hpetTimerRegRead32(pDevIns, pThis, iTimer, iTimerReg + 4, &pValue->s.Hi);1170 Assert(!(iTimerReg & 7)); 1171 pValue->u = hpetTimerRegRead64(pThis, iTimer, iTimerReg); 974 1172 rc = VINF_SUCCESS; 975 1173 } … … 977 1175 { 978 1176 /* for most 8-byte accesses we just split them, happens under lock anyway. */ 1177 DEVHPET_LOCK_RETURN(pDevIns, pThis, VINF_IOM_R3_MMIO_READ); 979 1178 rc = hpetConfigRegRead32(pDevIns, pThis, off, &pValue->s.Lo); 980 1179 if (rc == VINF_SUCCESS) 981 1180 rc = hpetConfigRegRead32(pDevIns, pThis, off + 4, &pValue->s.Hi); 1181 DEVHPET_UNLOCK(pDevIns, pThis); 982 1182 } 983 DEVHPET_UNLOCK(pDevIns, pThis);984 1183 } 985 1184 } … … 998 1197 NOREF(pvUser); 999 1198 Assert(cb == 4 || cb == 8); 1199 Assert(!(off & (cb - 1))); 1000 1200 1001 1201 VBOXSTRICTRC rc; … … 1015 1215 * 8-byte access. 1016 1216 */ 1017 /* Split the access and rely on the locking to prevent trouble. */1018 DEVHPET_LOCK_BOTH_RETURN(pDevIns, pThis, VINF_IOM_R3_MMIO_WRITE);1019 RTUINT64U uValue;1020 uValue.u = *(uint64_t const *)pv;1021 1217 if (off >= 0x100 && off < 0x400) 1022 { 1023 uint32_t iTimer = (uint32_t)(off - 0x100) / 0x20; 1024 uint32_t iTimerReg = (uint32_t)(off - 0x100) % 0x20; 1025 /** @todo Consider handling iTimerReg == HPET_TN_CMP specially here */ 1026 rc = hpetTimerRegWrite32(pDevIns, pThis, iTimer, iTimerReg, uValue.s.Lo); 1027 if (RT_LIKELY(rc == VINF_SUCCESS)) 1028 rc = hpetTimerRegWrite32(pDevIns, pThis, iTimer, iTimerReg + 4, uValue.s.Hi); 1029 } 1218 rc = hpetTimerRegWrite64(pDevIns, pThis, 1219 (uint32_t)(off - 0x100) / 0x20, 1220 (uint32_t)(off - 0x100) % 0x20, 1221 *(uint64_t const *)pv); 1030 1222 else 1031 1223 { 1224 /* Split the access and rely on the locking to prevent trouble. */ 1225 RTUINT64U uValue; 1226 uValue.u = *(uint64_t const *)pv; 1227 DEVHPET_LOCK_BOTH_RETURN(pDevIns, pThis, VINF_IOM_R3_MMIO_WRITE); 1032 1228 rc = hpetConfigRegWrite32(pDevIns, pThis, off, uValue.s.Lo); 1033 1229 if (RT_LIKELY(rc == VINF_SUCCESS)) 1034 1230 rc = hpetConfigRegWrite32(pDevIns, pThis, off + 4, uValue.s.Hi); 1035 }1036 DEVHPET_UNLOCK_BOTH(pDevIns, pThis);1231 DEVHPET_UNLOCK_BOTH(pDevIns, pThis); 1232 } 1037 1233 } 1038 1234 … … 1050 1246 * @param pThis The shared HPET state. 1051 1247 * @param pHpetTimer The HPET timer. 1052 */ 1053 static uint32_t hpetR3TimerGetIrq(PHPET pThis, PCHPETTIMER pHpetTimer) 1248 * @param fConfig The HPET timer config value. 1249 */ 1250 DECLINLINE(uint32_t) hpetR3TimerGetIrq(PHPET pThis, PCHPETTIMER pHpetTimer, uint64_t fConfig) 1054 1251 { 1055 1252 /* … … 1061 1258 * to the different ICs. 1062 1259 */ 1063 if ( (pHpetTimer->idxTimer <= 1)1260 if ( pHpetTimer->idxTimer <= 1 1064 1261 && (pThis->u64HpetConfig & HPET_CFG_LEGACY)) 1065 return (pHpetTimer->idxTimer == 0) ? 0 : 8; 1066 1067 return (pHpetTimer->u64Config & HPET_TN_INT_ROUTE_MASK) >> HPET_TN_INT_ROUTE_SHIFT; 1068 } 1069 1070 1071 /** 1072 * Used by hpetR3Timer to update the IRQ status. 1073 * 1074 * @param pDevIns The device instance. 1075 * @param pThis The shared HPET state. 1076 * @param pHpetTimer The HPET timer. 1077 */ 1078 static void hpetR3TimerUpdateIrq(PPDMDEVINS pDevIns, PHPET pThis, PHPETTIMER pHpetTimer) 1079 { 1080 /** @todo is it correct? */ 1081 if ( !!(pHpetTimer->u64Config & HPET_TN_ENABLE) 1082 && !!(pThis->u64HpetConfig & HPET_CFG_ENABLE)) 1083 { 1084 uint32_t irq = hpetR3TimerGetIrq(pThis, pHpetTimer); 1085 Log4(("HPET: raising IRQ %d\n", irq)); 1086 1087 /* ISR bits are only set in level-triggered mode. */ 1088 if ((pHpetTimer->u64Config & HPET_TN_INT_TYPE) == HPET_TIMER_TYPE_LEVEL) 1089 pThis->u64Isr |= UINT64_C(1) << pHpetTimer->idxTimer; 1090 1091 /* We trigger flip/flop in edge-triggered mode and do nothing in 1092 level-triggered mode yet. */ 1093 if ((pHpetTimer->u64Config & HPET_TN_INT_TYPE) == HPET_TIMER_TYPE_EDGE) 1094 { 1095 PHPETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PHPETCC); 1096 AssertReturnVoid(pThisCC); 1097 pThisCC->pHpetHlp->pfnSetIrq(pDevIns, irq, PDM_IRQ_LEVEL_FLIP_FLOP); 1098 STAM_REL_COUNTER_INC(&pHpetTimer->StatSetIrq); 1099 } 1100 else 1101 AssertFailed(); 1102 /** @todo implement IRQs in level-triggered mode */ 1103 } 1262 return pHpetTimer->idxTimer == 0 ? 0 : 8; 1263 1264 return (fConfig & HPET_TN_INT_ROUTE_MASK) >> HPET_TN_INT_ROUTE_SHIFT; 1104 1265 } 1105 1266 … … 1107 1268 /** 1108 1269 * @callback_method_impl{FNTMTIMERDEV, Device timer callback function.} 1270 * 1271 * @note Only the virtual sync lock is held when called. 1109 1272 */ 1110 1273 static DECLCALLBACK(void) hpetR3Timer(PPDMDEVINS pDevIns, TMTIMERHANDLE hTimer, void *pvUser) 1111 1274 { 1112 PHPET pThis = PDMDEVINS_2_DATA(pDevIns, PHPET); 1113 PHPETTIMER pHpetTimer = (HPETTIMER *)pvUser; 1114 uint64_t u64Period = pHpetTimer->u64Period; 1115 uint64_t u64CurTick = hpetGetTicks(pDevIns, pThis); 1116 uint64_t u64Diff; 1275 PHPET pThis = PDMDEVINS_2_DATA(pDevIns, PHPET); 1276 PHPETTIMER pHpetTimer = (HPETTIMER *)pvUser; 1277 1278 /* 1279 * Read the timer configuration values we need first. 1280 * 1281 * The comparator and period are only written while owning the virtual sync 1282 * lock, so we don't run any risk there. The configuration register is 1283 * written with only the device lock, so must be a bit more careful with it. 1284 */ 1285 uint64_t uCmp = ASMAtomicUoReadU64(&pHpetTimer->u64Cmp); 1286 uint64_t const uPeriod = ASMAtomicUoReadU64(&pHpetTimer->u64Period); 1287 uint64_t const fConfig = ASMAtomicUoReadU64(&pHpetTimer->u64Config); 1117 1288 Assert(hTimer == pHpetTimer->hTimer); 1118 1289 1119 if ( pHpetTimer->u64Config & HPET_TN_PERIODIC)1120 { 1121 if (u 64Period)1122 { 1123 hpetAdjustComparator(pHpetTimer, u64CurTick);1124 1125 u 64Diff = hpetComputeDiff(pHpetTimer, u64CurTick);1126 1127 uint64_t u64TickLimit = pThis->fIch9 ? HPET_TICKS_IN_100YR_ICH9 : HPET_TICKS_IN_100YR_PIIX;1128 if ( u64Diff <= u64TickLimit)1290 if (fConfig & HPET_TN_PERIODIC) 1291 { 1292 if (uPeriod) 1293 { 1294 uint64_t const tsNow = PDMDevHlpTimerGet(pDevIns, pHpetTimer->hTimer); 1295 uint64_t const uHpetNow = hpetGetTicksEx(pThis, tsNow); 1296 uCmp = hpetAdjustComparator(pHpetTimer, fConfig, uCmp, uPeriod, uHpetNow); 1297 uint64_t const cTicksDiff = hpetComputeDiff(fConfig, uCmp, uHpetNow); 1298 uint64_t const u64TickLimit = pThis->fIch9 ? HPET_TICKS_IN_100YR_ICH9 : HPET_TICKS_IN_100YR_PIIX; 1299 if (cTicksDiff <= u64TickLimit) 1129 1300 { 1130 Log4(("HPET: periodic: next in %llu\n", hpetTicksToNs(pThis, u64Diff))); 1301 uint64_t const tsDeadline = tsNow + hpetTicksToNs(pThis, cTicksDiff); 1302 Log4(("HPET: periodic: next in %llu\n", tsDeadline)); 1303 PDMDevHlpTimerSet(pDevIns, hTimer, tsDeadline); 1131 1304 STAM_REL_COUNTER_INC(&pHpetTimer->StatSetTimer); 1132 PDMDevHlpTimerSetNano(pDevIns, hTimer, hpetTicksToNs(pThis, u64Diff));1133 1305 } 1134 1306 else … … 1138 1310 } 1139 1311 } 1140 else if (hpet32bitTimer(pHpetTimer)) 1141 { 1142 /* For 32-bit non-periodic timers, generate wrap-around interrupts. */ 1143 if (pHpetTimer->u8Wrap) 1144 { 1145 u64Diff = hpetComputeDiff(pHpetTimer, u64CurTick); 1146 PDMDevHlpTimerSetNano(pDevIns, hTimer, hpetTicksToNs(pThis, u64Diff)); 1147 pHpetTimer->u8Wrap = 0; 1148 } 1149 } 1150 1151 /* Should it really be under lock, does it really matter? */ 1152 hpetR3TimerUpdateIrq(pDevIns, pThis, pHpetTimer); 1312 /* For 32-bit non-periodic timers, generate wrap-around interrupts. */ 1313 else if (pHpetTimer->u8Wrap && hpet32bitTimerEx(fConfig)) 1314 { 1315 pHpetTimer->u8Wrap = 0; /* (only modified while owning the virtual sync lock) */ 1316 uint64_t const tsNow = PDMDevHlpTimerGet(pDevIns, hTimer); 1317 uint64_t const uHpetNow = nsToHpetTicks(pThis, tsNow + pThis->u64HpetOffset); 1318 uint64_t const cTicksDiff = hpetComputeDiff(fConfig, uCmp, uHpetNow); 1319 uint64_t const tsDeadline = tsNow + hpetTicksToNs(pThis, cTicksDiff); 1320 Log4(("HPET: post-wrap deadline: %llu\n", tsDeadline)); 1321 PDMDevHlpTimerSet(pDevIns, pHpetTimer->hTimer, tsDeadline); 1322 } 1323 1324 /* 1325 * IRQ update. 1326 */ 1327 if ( (fConfig & HPET_TN_ENABLE) 1328 && (pThis->u64HpetConfig & HPET_CFG_ENABLE)) 1329 { 1330 AssertCompile(HPET_TN_INT_TYPE == 2); 1331 1332 /* We trigger flip/flop in edge-triggered mode and do nothing in 1333 level-triggered mode yet. */ 1334 if ((fConfig & HPET_TN_INT_TYPE) == HPET_TIMER_TYPE_EDGE) 1335 { 1336 PHPETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PHPETCC); 1337 AssertReturnVoid(pThisCC); 1338 1339 uint32_t const uIrq = hpetR3TimerGetIrq(pThis, pHpetTimer, fConfig); 1340 Log4(("HPET: raising IRQ %u\n", uIrq)); 1341 1342 pThisCC->pHpetHlp->pfnSetIrq(pDevIns, uIrq, PDM_IRQ_LEVEL_FLIP_FLOP); 1343 STAM_REL_COUNTER_INC(&pHpetTimer->StatSetIrq); 1344 } 1345 /* ISR bits are only set in level-triggered mode. */ 1346 else 1347 { 1348 Assert((fConfig & HPET_TN_INT_TYPE) == HPET_TIMER_TYPE_LEVEL); 1349 ASMAtomicOrU64(&pThis->u64Isr, RT_BIT_64(pHpetTimer->idxTimer)); 1350 /** @todo implement IRQs in level-triggered mode */ 1351 } 1352 } 1353 1153 1354 } 1154 1355 … … 1321 1522 PHPETTIMER pHpetTimer = &pThis->aTimers[iTimer]; 1322 1523 if (PDMDevHlpTimerIsActive(pDevIns, pHpetTimer->hTimer)) 1323 hpetTimerSetFrequencyHint(pDevIns, pThis, pHpetTimer );1524 hpetTimerSetFrequencyHint(pDevIns, pThis, pHpetTimer, pHpetTimer->u64Config, pHpetTimer->u64Period); 1324 1525 } 1325 1526 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect); … … 1363 1564 1364 1565 /* capable of periodic operations and 64-bits */ 1566 uint64_t fConfig; 1365 1567 if (pThis->fIch9) 1366 pHpetTimer->u64Config = (i == 0) 1367 ? (HPET_TN_PERIODIC_CAP | HPET_TN_SIZE_CAP) 1368 : 0; 1568 fConfig = i == 0 ? HPET_TN_PERIODIC_CAP | HPET_TN_SIZE_CAP : 0; 1369 1569 else 1370 pHpetTimer->u64Config = HPET_TN_PERIODIC_CAP | HPET_TN_SIZE_CAP;1570 fConfig = HPET_TN_PERIODIC_CAP | HPET_TN_SIZE_CAP; 1371 1571 1372 1572 /* We can do all IRQs */ 1373 1573 uint32_t u32RoutingCap = 0xffffffff; 1374 pHpetTimer->u64Config |= ((uint64_t)u32RoutingCap) << HPET_TN_INT_ROUTE_CAP_SHIFT; 1574 fConfig |= ((uint64_t)u32RoutingCap) << HPET_TN_INT_ROUTE_CAP_SHIFT; 1575 ASMAtomicWriteU64(&pHpetTimer->u64Config, fConfig); 1375 1576 pHpetTimer->u64Period = 0; 1376 1577 pHpetTimer->u8Wrap = 0; … … 1460 1661 { 1461 1662 PHPETTIMER pHpetTimer = &pThis->aTimers[i]; 1462 1463 1663 rc = PDMDevHlpTimerCreate(pDevIns, TMCLOCK_VIRTUAL_SYNC, hpetR3Timer, pHpetTimer, 1464 1664 TMTIMER_FLAGS_NO_CRIT_SECT | TMTIMER_FLAGS_RING0, 1465 1665 s_apszTimerNames[i], &pThis->aTimers[i].hTimer); 1466 1666 AssertRCReturn(rc, rc); 1467 /** @todo r=bird: This is TOTALLY MESSED UP! Why do we need 1468 * DEVHPET_LOCK_BOTH_RETURN() when the timers use the same critsect as 1469 * we do?!? */ 1470 rc = PDMDevHlpTimerSetCritSect(pDevIns, pThis->aTimers[i].hTimer, &pThis->CritSect); 1471 AssertRCReturn(rc, rc); 1667 uint64_t const cTicksPerSec = PDMDevHlpTimerGetFreq(pDevIns, pThis->aTimers[i].hTimer); 1668 if (cTicksPerSec != RT_NS_1SEC) 1669 return PDMDevHlpVMSetError(pDevIns, VERR_INTERNAL_ERROR_2, RT_SRC_POS, 1670 "Unexpected timer resolution %RU64, code assumes nanonsecond resolution!", cTicksPerSec); 1472 1671 } 1473 1672 … … 1509 1708 /* Statistics: */ 1510 1709 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatCounterRead4Byte, STAMTYPE_COUNTER, 1511 " CounterRead4Byte", STAMUNIT_OCCURENCES, "HPET_COUNTER 32-bit reads");1710 "ReadCounter32bit", STAMUNIT_OCCURENCES, "HPET_COUNTER 32-bit reads"); 1512 1711 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatCounterRead8Byte, STAMTYPE_COUNTER, 1513 " CounterRead8Byte", STAMUNIT_OCCURENCES, "HPET_COUNTER 64-bit reads");1712 "ReadCounter64bit", STAMUNIT_OCCURENCES, "HPET_COUNTER 64-bit reads"); 1514 1713 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatCounterWriteLow, STAMTYPE_COUNTER, 1515 " CounterWriteLow", STAMUNIT_OCCURENCES, "Low HPET_COUNTER writes");1714 "WriteCounterLow", STAMUNIT_OCCURENCES, "Low HPET_COUNTER writes"); 1516 1715 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatCounterWriteHigh, STAMTYPE_COUNTER, 1517 "CounterWriteHigh", STAMUNIT_OCCURENCES, "High HPET_COUNTER writes"); 1716 "WriteCounterHigh", STAMUNIT_OCCURENCES, "High HPET_COUNTER writes"); 1717 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatZeroDeltaHack, STAMTYPE_COUNTER, 1718 "ZeroDeltaHacks", STAMUNIT_OCCURENCES, "High HPET_COUNTER writes"); 1719 1518 1720 for (unsigned i = 0; i < RT_ELEMENTS(pThis->aTimers); i++) 1519 1721 {
Note:
See TracChangeset
for help on using the changeset viewer.