xref: /reactos/boot/environ/lib/platform/time.c (revision 8a978a17)
1 /*
2  * COPYRIGHT:       See COPYING.ARM in the top level directory
3  * PROJECT:         ReactOS UEFI Boot Library
4  * FILE:            boot/environ/lib/platform/time.c
5  * PURPOSE:         Boot Library Time Management Routines
6  * PROGRAMMER:      Alex Ionescu (alex.ionescu@reactos.org)
7  */
8 
9 /* INCLUDES ******************************************************************/
10 
11 #include "bl.h"
12 
13 /* DATA VARIABLES ************************************************************/
14 
15 ULONGLONG BlpTimePerformanceFrequency;
16 
17 /* FUNCTIONS *****************************************************************/
18 
19 NTSTATUS
20 BlpTimeMeasureTscFrequency (
21     VOID
22     )
23 {
24 #if defined(_M_IX86) || defined(_M_X64)
25     ULONG Count;
26     INT CpuInfo[4];
27     ULONGLONG TimeStamp1, TimeStamp2, Delta;
28 
29     /* Check if the ISVM bit it set, meaning we're in a hypervisor */
30     __cpuid(CpuInfo, 1);
31     Count = CpuInfo[2] & 0x80000000 ? 10 : 1;
32 
33     /* Loop trying to get an accurate TSC */
34     do
35     {
36         /* Stall for 1us and get count 1 */
37         EfiStall(1);
38         TimeStamp1 = __rdtsc();
39 
40         /* Stall for 1000us and get count 2*/
41         EfiStall(1000);
42         TimeStamp2 = __rdtsc();
43 
44         /* Stall for 9000us and get the difference */
45         EfiStall(9000);
46         Delta = __rdtsc() - TimeStamp2;
47 
48         /* Keep going as long as the TSC is fluctuating */
49         --Count;
50     } while (((TimeStamp2 - TimeStamp1) > Delta) && (Count));
51 
52     /* Set the frequency based on the two measurements we took */
53     BlpTimePerformanceFrequency = 125 * (Delta - (TimeStamp2 - TimeStamp1)) & 0x1FFFFFFFFFFFFFF;
54     return STATUS_SUCCESS;
55 #else
56     EfiPrintf(L"BlpTimeMeasureTscFrequency not implemented for this platform.\r\n");
57     return STATUS_NOT_IMPLEMENTED;
58 #endif
59 }
60 
61 NTSTATUS
62 BlpTimeCalibratePerformanceCounter (
63     VOID
64     )
65 {
66 #if defined(_M_IX86) || defined(_M_X64)
67     INT CpuInfo[4];
68 
69     /* Check if the ISVM bit it set, meaning we're in a hypervisor */
70     __cpuid(CpuInfo, 1);
71     if (CpuInfo[2] & 0x80000000)
72     {
73         /* Get the Hypervisor Identification Leaf */
74         __cpuid(CpuInfo, 0x40000001);
75 
76         /* Is this Hyper-V? */
77         if (CpuInfo[0] == '1#vH')
78         {
79             /* Get the Hypervisor Feature Identification Leaf */
80             __cpuid(CpuInfo, 0x40000003);
81 
82             /* Check if HV_X64_MSR_REFERENCE_TSC is present */
83             if (CpuInfo[3] & 0x100)
84             {
85                 /* Read the TSC frequency from the MSR */
86                 BlpTimePerformanceFrequency = __readmsr(0x40000022);
87                 return STATUS_SUCCESS;
88             }
89         }
90     }
91 
92     /* On other systems, compute it */
93     return BlpTimeMeasureTscFrequency();
94 #else
95     EfiPrintf(L"BlpTimeCalibratePerformanceCounter not implemented for this platform.\r\n");
96     return STATUS_NOT_IMPLEMENTED;
97 #endif
98 }
99 
100 ULONGLONG
101 BlTimeQueryPerformanceCounter (
102     _Out_opt_ PLARGE_INTEGER Frequency
103     )
104 {
105 #if defined(_M_IX86) || defined(_M_X64)
106     /* Check if caller wants frequency */
107     if (Frequency)
108     {
109         /* Return it */
110         Frequency->QuadPart = BlpTimePerformanceFrequency;
111     }
112 
113     /* Return the TSC value */
114     return __rdtsc();
115 #else
116     EfiPrintf(L"BlTimeQueryPerformanceCounter not implemented for this platform.\r\n");
117     return 0;
118 #endif
119 };
120