Changeset 88353 in vbox
- Timestamp:
- Apr 2, 2021 9:52:41 PM (4 years ago)
- svn:sync-xref-src-repo-rev:
- 143597
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Devices/PC/DevHPET.cpp
r88352 r88353 2 2 /** @file 3 3 * HPET virtual device - High Precision Event Timer emulation. 4 * 5 * This implementation is based on the (generic) Intel IA-PC HPET specification 6 * and the Intel ICH9 datasheet. 7 * 8 * Typical windows 1809 usage (efi, smp) is to do repated one-shots and 9 * a variable rate. The reprogramming sequence is as follows (all accesses 10 * are 32-bit): 11 * -# counter register read. 12 * -# timer 0: config register read. 13 * -# timer 0: write 0x134 to config register. 14 * -# timer 0: write comparator register. 15 * -# timer 0: write 0x134 to config register. 16 * -# timer 0: read comparator register. 17 * -# counter register read. 18 * 19 * Typical linux will configure the timer at Hz but not necessarily enable 20 * interrupts (HPET_TN_ENABLE not set). It would be nice to emulate this 21 * mode without using timers. 22 * 4 23 */ 5 24 … … 14 33 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the 15 34 * 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 specification19 * and the Intel ICH9 datasheet.20 35 */ 21 36 … … 132 147 #define HPET_TN_PERIODIC_CAP RT_BIT_64(4) 133 148 #define HPET_TN_SIZE_CAP RT_BIT_64(5) 134 #define HPET_TN_SETVAL RT_BIT_64(6) 149 #define HPET_TN_SETVAL RT_BIT_64(6) /**< Periodic timers only: Change COMPARATOR as well as ACCUMULATOR. */ 135 150 #define HPET_TN_32BIT RT_BIT_64(8) 136 151 #define HPET_TN_INT_ROUTE_MASK UINT64_C(0x3e00) … … 723 738 { 724 739 DEVHPET_LOCK_BOTH_RETURN(pDevIns, pThis, VINF_IOM_R3_MMIO_WRITE); 725 Log(("HPET[%u]: write32 HPET_TN_CMP: %#x\n", iTimerNo, u32NewValue));726 740 uint64_t fConfig = ASMAtomicUoReadU64(&pHpetTimer->u64Config); 727 728 /** @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." 729 * See https://wiki.osdev.org/HPET and hpet_clkevt_set_state_periodic() in linux. */ 741 Log(("HPET[%u]: write32 HPET_TN_CMP: %#x (fCfg=%#RX32)\n", iTimerNo, u32NewValue, (uint32_t)fConfig)); 742 730 743 if (fConfig & HPET_TN_PERIODIC) 731 744 ASMAtomicUoWriteU64(&pHpetTimer->u64Period, RT_MAKE_U64(u32NewValue, RT_HI_U32(pHpetTimer->u64Period))); 732 ASMAtomicUoWriteU64(&pHpetTimer->u64Cmp, RT_MAKE_U64(u32NewValue, RT_HI_U32(pHpetTimer->u64Cmp))); 745 746 if (!(fConfig & HPET_TN_PERIODIC) || (fConfig & HPET_TN_SETVAL)) 747 ASMAtomicUoWriteU64(&pHpetTimer->u64Cmp, RT_MAKE_U64(u32NewValue, RT_HI_U32(pHpetTimer->u64Cmp))); 748 733 749 ASMAtomicAndU64(&pHpetTimer->u64Config, ~HPET_TN_SETVAL); 734 750 Log2(("HPET[%u]: after32 HPET_TN_CMP cmp=%#llx per=%#llx\n", iTimerNo, pHpetTimer->u64Cmp, pHpetTimer->u64Period)); … … 740 756 } 741 757 758 /** @todo figure out how exactly it behaves wrt to HPET_TN_SETVAL */ 742 759 case HPET_TN_CMP + 4: /* upper bits of comparator register */ 743 760 { … … 747 764 if (!hpet32bitTimerEx(fConfig)) 748 765 { 749 Log(("HPET[%u]: write32 HPET_TN_CMP + 4: %#x\n", iTimerNo, u32NewValue)); 750 /** @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." 751 * See https://wiki.osdev.org/HPET and hpet_clkevt_set_state_periodic() in linux. */ 766 Log(("HPET[%u]: write32 HPET_TN_CMP + 4: %#x (fCfg=%#RX32)\n", iTimerNo, u32NewValue, (uint32_t)fConfig)); 752 767 if (fConfig & HPET_TN_PERIODIC) 753 768 ASMAtomicUoWriteU64(&pHpetTimer->u64Period, RT_MAKE_U64(RT_LO_U32(pHpetTimer->u64Period), u32NewValue)); 754 ASMAtomicUoWriteU64(&pHpetTimer->u64Cmp, RT_MAKE_U64(RT_LO_U32(pHpetTimer->u64Cmp), u32NewValue)); 769 770 if (!(fConfig & HPET_TN_PERIODIC) || (fConfig & HPET_TN_SETVAL)) 771 ASMAtomicUoWriteU64(&pHpetTimer->u64Cmp, RT_MAKE_U64(RT_LO_U32(pHpetTimer->u64Cmp), u32NewValue)); 772 755 773 ASMAtomicAndU64(&pHpetTimer->u64Config, ~HPET_TN_SETVAL); 756 774 Log2(("HPET[%u]: after32 HPET_TN_CMP+4: cmp=%#llx per=%#llx\n", iTimerNo, pHpetTimer->u64Cmp, pHpetTimer->u64Period)); … … 760 778 } 761 779 else 762 Log(("HPET[%u]: write32 HPET_TN_CMP + 4: %#x - but timer is 32-bit!! \n", iTimerNo, u32NewValue));780 Log(("HPET[%u]: write32 HPET_TN_CMP + 4: %#x - but timer is 32-bit!! (fCfg=%#RX32)\n", iTimerNo, u32NewValue, (uint32_t)fConfig)); 763 781 DEVHPET_UNLOCK_BOTH(pDevIns, pThis); 764 782 break; … … 793 811 * @param iTimerNo The timer being written to. 794 812 * @param iTimerReg The register being written to. 795 * @param u 32NewValue The value being written.813 * @param u64NewValue The value being written. 796 814 * 797 815 * @remarks The caller should not hold any locks. … … 818 836 { 819 837 DEVHPET_LOCK_BOTH_RETURN(pDevIns, pThis, VINF_IOM_R3_MMIO_WRITE); 820 Log(("HPET[%u]: write64 HPET_TN_CMP: %#RX64\n", iTimerNo, u64NewValue));821 838 uint64_t fConfig = ASMAtomicUoReadU64(&pHpetTimer->u64Config); 822 823 /** @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." 824 * See https://wiki.osdev.org/HPET and hpet_clkevt_set_state_periodic() in linux. */ 839 Log(("HPET[%u]: write64 HPET_TN_CMP: %#RX64 (fCfg=%#RX64)\n", iTimerNo, u64NewValue, (uint32_t)fConfig)); 840 841 /** @todo not sure if this is right, but it is consistent with the 32-bit config 842 * change behaviour and defensive wrt mixups. */ 843 if (!hpet32bitTimerEx(fConfig)) 844 { /* likely */ } 845 else 846 u64NewValue = (uint32_t)u64NewValue; 847 825 848 if (fConfig & HPET_TN_PERIODIC) 826 849 ASMAtomicUoWriteU64(&pHpetTimer->u64Period, u64NewValue); 827 ASMAtomicUoWriteU64(&pHpetTimer->u64Cmp, u64NewValue); 850 851 if (!(fConfig & HPET_TN_PERIODIC) || (fConfig & HPET_TN_SETVAL)) 852 ASMAtomicUoWriteU64(&pHpetTimer->u64Cmp, u64NewValue); 853 828 854 ASMAtomicAndU64(&pHpetTimer->u64Config, ~HPET_TN_SETVAL); 829 855 Log2(("HPET[%u]: after64 HPET_TN_CMP cmp=%#llx per=%#llx\n", iTimerNo, pHpetTimer->u64Cmp, pHpetTimer->u64Period)); … … 1380 1406 for (unsigned i = 0; i < RT_ELEMENTS(pThis->aTimers); i++) 1381 1407 { 1382 pHlp->pfnPrintf(pHlp, " %d: comparator=%016RX64 period(hidden)=%016RX64 cfg=%016RX64\n", 1408 static const struct 1409 { 1410 const char *psz; 1411 uint32_t cch; 1412 uint32_t fFlags; 1413 } s_aFlags[] = 1414 { 1415 { RT_STR_TUPLE(" lvl"), HPET_TN_INT_TYPE }, 1416 { RT_STR_TUPLE(" en"), HPET_TN_ENABLE }, 1417 { RT_STR_TUPLE(" per"), HPET_TN_PERIODIC }, 1418 { RT_STR_TUPLE(" cap_per"), HPET_TN_PERIODIC_CAP }, 1419 { RT_STR_TUPLE(" cap_64"), HPET_TN_SIZE_CAP }, 1420 { RT_STR_TUPLE(" setval"), HPET_TN_SETVAL }, 1421 { RT_STR_TUPLE(" 32b"), HPET_TN_32BIT }, 1422 }; 1423 char szTmp[64]; 1424 uint64_t fCfg = pThis->aTimers[i].u64Config; 1425 size_t off = 0; 1426 for (unsigned j = 0; j < RT_ELEMENTS(s_aFlags); j++) 1427 if (fCfg & s_aFlags[j].fFlags) 1428 { 1429 memcpy(&szTmp[off], s_aFlags[j].psz, s_aFlags[j].cch); 1430 off += s_aFlags[j].cch; 1431 fCfg &= ~(uint64_t)s_aFlags[j].fFlags; 1432 } 1433 szTmp[off] = '\0'; 1434 Assert(off < sizeof(szTmp)); 1435 1436 pHlp->pfnPrintf(pHlp, 1437 " %d: comparator=%016RX64 accumulator=%016RX64 (%RU64 ns)\n" 1438 " config=%016RX64 irq=%d%s\n", 1383 1439 pThis->aTimers[i].idxTimer, 1384 1440 pThis->aTimers[i].u64Cmp, 1385 1441 pThis->aTimers[i].u64Period, 1386 pThis->aTimers[i].u64Config); 1442 hpetTicksToNs(pThis, pThis->aTimers[i].u64Period), 1443 pThis->aTimers[i].u64Config, 1444 hpetR3TimerGetIrq(pThis, &pThis->aTimers[i], pThis->aTimers[i].u64Config), 1445 szTmp); 1387 1446 } 1388 1447 }
Note:
See TracChangeset
for help on using the changeset viewer.