1 /** @file
2   UEFI Miscellaneous boot Services Stall service implementation
3 
4 Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
5 SPDX-License-Identifier: BSD-2-Clause-Patent
6 
7 **/
8 
9 //
10 // Include statements
11 //
12 
13 #include "DxeMain.h"
14 
15 /**
16   Internal worker function to call the Metronome Architectural Protocol for
17   the number of ticks specified by the UINT64 Counter value.  WaitForTick()
18   service of the Metronome Architectural Protocol uses a UINT32 for the number
19   of ticks to wait, so this function loops when Counter is larger than 0xffffffff.
20 
21   @param  Counter           Number of ticks to wait.
22 
23 **/
24 VOID
CoreInternalWaitForTick(IN UINT64 Counter)25 CoreInternalWaitForTick (
26   IN UINT64  Counter
27   )
28 {
29   while (RShiftU64 (Counter, 32) > 0) {
30     gMetronome->WaitForTick (gMetronome, 0xffffffff);
31     Counter -= 0xffffffff;
32   }
33   gMetronome->WaitForTick (gMetronome, (UINT32)Counter);
34 }
35 
36 /**
37   Introduces a fine-grained stall.
38 
39   @param  Microseconds           The number of microseconds to stall execution.
40 
41   @retval EFI_SUCCESS            Execution was stalled for at least the requested
42                                  amount of microseconds.
43   @retval EFI_NOT_AVAILABLE_YET  gMetronome is not available yet
44 
45 **/
46 EFI_STATUS
47 EFIAPI
CoreStall(IN UINTN Microseconds)48 CoreStall (
49   IN UINTN            Microseconds
50   )
51 {
52   UINT64  Counter;
53   UINT32  Remainder;
54   UINTN   Index;
55 
56   if (gMetronome == NULL) {
57     return EFI_NOT_AVAILABLE_YET;
58   }
59 
60   //
61   // Counter = Microseconds * 10 / gMetronome->TickPeriod
62   // 0x1999999999999999 = (2^64 - 1) / 10
63   //
64   if ((UINT64) Microseconds > 0x1999999999999999ULL) {
65     //
66     // Microseconds is too large to multiple by 10 first.  Perform the divide
67     // operation first and loop 10 times to avoid 64-bit math overflow.
68     //
69     Counter = DivU64x32Remainder (
70                 Microseconds,
71                 gMetronome->TickPeriod,
72                 &Remainder
73                 );
74     for (Index = 0; Index < 10; Index++) {
75       CoreInternalWaitForTick (Counter);
76     }
77 
78     if (Remainder != 0) {
79       //
80       // If Remainder was not zero, then normally, Counter would be rounded
81       // up by 1 tick.  In this case, since a loop for 10 counts was used
82       // to emulate the multiply by 10 operation, Counter needs to be rounded
83       // up by 10 counts.
84       //
85       CoreInternalWaitForTick (10);
86     }
87   } else {
88     //
89     // Calculate the number of ticks by dividing the number of microseconds by
90     // the TickPeriod.  Calculation is based on 100ns unit.
91     //
92     Counter = DivU64x32Remainder (
93                 MultU64x32 (Microseconds, 10),
94                 gMetronome->TickPeriod,
95                 &Remainder
96                 );
97     if (Remainder != 0) {
98       //
99       // If Remainder is not zero, then round Counter up by one tick.
100       //
101       Counter++;
102     }
103     CoreInternalWaitForTick (Counter);
104   }
105 
106   return EFI_SUCCESS;
107 }
108