cputimer.c
来自「Next BIOS Source code : Extensible Firmw」· C语言 代码 · 共 216 行
C
216 行
/*++
Copyright (c) 1999 - 2002 Intel Corporation. All rights reserved
This software and associated documentation (if any) is furnished
under a license and may only be used or copied in accordance
with the terms of the license. Except as permitted by such
license, no part of this software or documentation may be
reproduced, stored in a retrieval system, or transmitted in any
form or by any means without the express written consent of
Intel Corporation.
Module Name:
tsc.c
Abstract:
TSC calibration and timestamp code. Used to calculate intervals for FwTimerTick()
--*/
#include "efi.h"
#include "efilib.h"
#include "efifw.h"
#include "PlIntCtrl.h"
#include "CpuInterrupt.h"
typedef union
{
UINT64 qw;
struct
{
UINT32 low;
UINT32 high;
} dw;
} QUADWORD;
static QUADWORD PlCpuFreq, NumberOfCpuTicks;
static volatile BOOLEAN DoTscCalibration = FALSE;
UINT32 CpuFrequencyAdjustment;
//
// the __declspec (naked) attribute causes compiler to omit prelogue and
// epilogue code from function. This allows for an explicit "iret" rather than
// an implicit "ret"
//
// also note that this function must be re-entrant by the time RestoreTPL is
// called. That is to say, any non-stack based data operations must be complete
// by this time.
__declspec (naked)
STATIC
VOID
SystemTimerHandler(
VOID
)
{
EFI_TPL OriginalTPL;
QUADWORD NewTickCount;
QUADWORD ElapsedTicks;
QUADWORD ElapsedTime; // number of microseconds since last interrupt
UINT32 Remainder;
static QUADWORD OldTickCount;
__asm {
push ebp
mov ebp, esp
sub esp, __LOCAL_SIZE
pushad // save context
// in al, 0x61 // system control port B
// or al, 0x80 // set bit 7
// out 0x61, al // clear interrupt from system timer
_emit 0x0f // rdtsc = 0fh 31h
_emit 0x31
mov NewTickCount.dw.low, eax
mov NewTickCount.dw.high, edx
}
ElapsedTicks.qw = NewTickCount.qw - OldTickCount.qw;
ElapsedTime.qw = DivU64x32(MultU64x32 (ElapsedTicks.qw, 10000000 / CpuFrequencyAdjustment),PlCpuFreq.dw.low, &Remainder); // ElapsedTime == 100 NS units since last call...
OldTickCount.qw = NewTickCount.qw;
__asm {
call PlEOI // Issue EOI to PIC
sti // enable interrupts
}
OriginalTPL = BS->RaiseTPL (TPL_HIGH_LEVEL); // Implicitly disables interrupts
FwTimerTick (ElapsedTime.dw.low);
BS->RestoreTPL (OriginalTPL); // Implicitly re-enables interrupts and causes queued events to fire
__asm {
popad // restore context
add esp, __LOCAL_SIZE
pop ebp
iretd // return to interrupted task
}
}
#define NUM_CALIBRATION_TICKS 5
#define NS_PER_TIMER_TICK 54925415
#define CALIBRATION_DURATION_IN_NS (NUM_CALIBRATION_TICKS * NS_PER_TIMER_TICK)
__declspec (naked)
STATIC
VOID
CalibrateTSC(
VOID
)
{
static UINTN CalibrationCount = 0;
static QUADWORD OrigTS;
QUADWORD NewTS;
__asm {
push ebp
mov ebp, esp
sub esp, __LOCAL_SIZE
pushad // save context
}
if (DoTscCalibration == TRUE)
{
__asm {
_emit 0x0f // rdtsc = 0fh 31h
_emit 0x31
mov NewTS.dw.low, eax
mov NewTS.dw.high, edx
}
if (CalibrationCount == 0)
OrigTS.qw = NewTS.qw;
if (CalibrationCount < NUM_CALIBRATION_TICKS)
CalibrationCount++;
else
{
DoTscCalibration = FALSE;
CalibrationCount = 0;
NumberOfCpuTicks.qw = NewTS.qw - OrigTS.qw;
}
}
PlEOI();
__asm {
popad // restore context
add esp, __LOCAL_SIZE
pop ebp
iretd // return to interrupted task
}
}
STATIC
VOID
PlCalibrateCpuFreq(
VOID
)
/*++
Routine Description:
This routine computes the number of itterations of StallLoop() that need to be
use for a 1 uS delay, a 16 uS delay, and a 256 uS delay. These values will
vary based on CPU speed, cache speed, and main memory speed. These values are
later used by PlStall() to perform very accurate stalls.
Arguments:
None
Returns:
None
--*/
{
UINT32 Remainder;
NumberOfCpuTicks.qw = 0;
DoTscCalibration = FALSE;
PL->SetInterruptState(FALSE);
InstallInterruptHandler(PlGetVectorFromIrq(0), CalibrateTSC);
PlEnableTimerInterrupt();
PL->SetInterruptState(TRUE);
for (DoTscCalibration = TRUE; DoTscCalibration == TRUE; );
PL->SetInterruptState(FALSE);
PlDisableTimerInterrupt();
// CpuFreq = TscClocks * 1,000,000,000 / CALIBRATION_DURATION_IN_NS
PlCpuFreq.qw = DivU64x32 (MultU64x32 (NumberOfCpuTicks.qw, 1000000000), CALIBRATION_DURATION_IN_NS, &Remainder);
// CpuFreq must fit within 31 bits for timer tick handler to work properly
// so insure this is the case and adjust if necessary
CpuFrequencyAdjustment = 1;
while (PlCpuFreq.qw & 0xffffffff80000000) {
CpuFrequencyAdjustment <<= 1;
PlCpuFreq.qw = RShiftU64 (PlCpuFreq.qw, 1);
}
__asm mov eax, PlCpuFreq.dw.low
__asm mov edx, PlCpuFreq.dw.high
}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?