Skip to content

Commit

Permalink
feat: time.now
Browse files Browse the repository at this point in the history
  • Loading branch information
jclab-joseph committed Nov 14, 2023
1 parent 78da201 commit 907d376
Show file tree
Hide file tree
Showing 5 changed files with 309 additions and 111 deletions.
132 changes: 62 additions & 70 deletions src/device/x86/cpu.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//go:build amd64

package x86

const (
Expand Down Expand Up @@ -26,15 +28,69 @@ const (
func AsmReadRdtsc() uint64

//export asmCpuid
func AsmCpuid(index int, registerEax *uint32, registerRbx *uint32, registerEcx *uint32, registerEdx *uint32)
func AsmCpuid(index int, registerEax *uint32, registerRbx *uint32, registerEcx *uint32, registerEdx *uint32) int

var maxCpuidIndex int

func init() {
maxCpuidIndex = AsmCpuid(0, nil, nil, nil, nil)
}

func GetMaxCpuidIndex() int {
return maxCpuidIndex
}

func InternalGetPerformanceCounterFrequency() uint64 {
return CpuidCoreClockCalculateTscFrequency()
//FIXME: how to find it?
//See https://github.com/torvalds/linux/blob/master/arch/x86/kernel/tsc.c

if maxCpuidIndex >= CPUID_TIME_STAMP_COUNTER {
return CpuidCoreClockCalculateTscFrequency()
}

return 0
}

func GetTimeInNanoSecond(ticks uint64) int64 {
frequency := InternalGetPerformanceCounterFrequency()
return ConvertTimeInNanoSecond(frequency, ticks)
//func GetTimeInNanoSecond(ticks uint64) int64 {
// frequency := InternalGetPerformanceCounterFrequency()
// return ConvertTimeInNanoSecond(frequency, ticks)
//}
//
//func GetTicksFromNanoSeconds(nano uint64) uint64 {
// frequency := InternalGetPerformanceCounterFrequency()
// return nano * frequency / 1000000000
//}

func CpuidCoreClockCalculateTscFrequency() uint64 {
var TscFrequency uint64
var CoreXtalFrequency uint64
var RegEax uint32
var RegEbx uint32
var RegEcx uint32

AsmCpuid(CPUID_TIME_STAMP_COUNTER, &RegEax, &RegEbx, &RegEcx, nil)

// If EAX or EBX returns 0, the XTAL ratio is not enumerated.
if (RegEax == 0) || (RegEbx == 0) {
return 0
}

// If ECX returns 0, the XTAL frequency is not enumerated.
// And PcdCpuCoreCrystalClockFrequency defined should base on processor series.
//
if RegEcx == 0 {
// Specifies CPUID Leaf 0x15 Time Stamp Counter and Nominal Core Crystal Clock Frequency.
// Intel Xeon Processor Scalable Family with CPUID signature 06_55H = 25000000 (25MHz)
// 6th and 7th generation Intel Core processors and Intel Xeon W Processor Family = 24000000 (24MHz)
// Intel Atom processors based on Goldmont Microarchitecture with CPUID signature 06_5CH = 19200000 (19.2MHz)
CoreXtalFrequency = 24000000
} else {
CoreXtalFrequency = uint64(RegEcx)
}

// Calculate TSC frequency = (ECX, Core Xtal Frequency) * EBX/EAX
TscFrequency = ((CoreXtalFrequency * uint64(RegEbx)) + (uint64(RegEax) / 2)) / uint64(RegEax)
return TscFrequency
}

func ConvertTimeInNanoSecond(frequency uint64, ticks uint64) int64 {
Expand All @@ -59,8 +115,7 @@ func ConvertTimeInNanoSecond(frequency uint64, ticks uint64) int64 {
return int64(nanoSeconds)
}

func GetTicksFromNanoSeconds(nano uint64) uint64 {
frequency := InternalGetPerformanceCounterFrequency()
func ConvertTicksFromNanoSeconds(frequency uint64, nano uint64) uint64 {
return nano * frequency / 1000000000
}

Expand All @@ -82,66 +137,3 @@ func highBitSet32(operand uint32) int {
}
return bitIndex
}

/*
UINT64
EFIAPI
GetTimeInNanoSecond (
IN UINT64 Ticks
)
{
UINT64 Frequency;
UINT64 NanoSeconds;
UINT64 Remainder;
INTN Shift;
Frequency = GetPerformanceCounterProperties (NULL, NULL);
NanoSeconds = MultU64x32 (DivU64x64Remainder (Ticks, Frequency, &Remainder), 1000000000u);
//
// Ensure (Remainder * 1,000,000,000) will not overflow 64-bit.
// Since 2^29 < 1,000,000,000 = 0x3B9ACA00 < 2^30, Remainder should < 2^(64-30) = 2^34,
// i.e. highest bit set in Remainder should <= 33.
//
Shift = MAX (0, HighBitSet64 (Remainder) - 33);
Remainder = RShiftU64 (Remainder, (UINTN)Shift);
Frequency = RShiftU64 (Frequency, (UINTN)Shift);
NanoSeconds += DivU64x64Remainder (MultU64x32 (Remainder, 1000000000u), Frequency, NULL);
return NanoSeconds;
}
*/

func CpuidCoreClockCalculateTscFrequency() uint64 {
var TscFrequency uint64
var CoreXtalFrequency uint64
var RegEax uint32
var RegEbx uint32
var RegEcx uint32

AsmCpuid(CPUID_TIME_STAMP_COUNTER, &RegEax, &RegEbx, &RegEcx, nil)

// If EAX or EBX returns 0, the XTAL ratio is not enumerated.
if (RegEax == 0) || (RegEbx == 0) {
return 0
}

// If ECX returns 0, the XTAL frequency is not enumerated.
// And PcdCpuCoreCrystalClockFrequency defined should base on processor series.
//
if RegEcx == 0 {
// Specifies CPUID Leaf 0x15 Time Stamp Counter and Nominal Core Crystal Clock Frequency.
// Intel Xeon Processor Scalable Family with CPUID signature 06_55H = 25000000 (25MHz)
// 6th and 7th generation Intel Core processors and Intel Xeon W Processor Family = 24000000 (24MHz)
// Intel Atom processors based on Goldmont Microarchitecture with CPUID signature 06_5CH = 19200000 (19.2MHz)
CoreXtalFrequency = 24000000
} else {
CoreXtalFrequency = uint64(RegEcx)
}

// Calculate TSC frequency = (ECX, Core Xtal Frequency) * EBX/EAX
TscFrequency = ((CoreXtalFrequency * uint64(RegEbx)) + (uint64(RegEax) / 2)) / uint64(RegEax)
return TscFrequency
}
59 changes: 59 additions & 0 deletions src/machine/uefi/clock.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
//go:build uefi

package uefi

import (
"device/x86"
"sync"
)

var calibrateMutex sync.Mutex
var calculatedFrequency uint64

func GetTscFrequency() uint64 {
frequence := x86.InternalGetPerformanceCounterFrequency()
if frequence > 0 {
return frequence
}

var event EFI_EVENT
var status EFI_STATUS
var index UINTN

calibrateMutex.Lock()
defer calibrateMutex.Unlock()

freq := calculatedFrequency
if freq > 0 {
return freq
}

bs := systemTable.BootServices

status = bs.CreateEvent(EVT_TIMER, TPL_CALLBACK, nil, nil, &event)
if status != EFI_SUCCESS {
return 0
}
defer bs.CloseEvent(event)

st := x86.AsmReadRdtsc()
status = bs.SetTimer(event, TimerPeriodic, 250*10000)
if status != EFI_SUCCESS {
return 0
}
status = bs.WaitForEvent(1, &event, &index)
diff := x86.AsmReadRdtsc() - st

calculatedFrequency = diff * 4

return calculatedFrequency
}

func GetTime() (EFI_TIME, EFI_STATUS) {
var status EFI_STATUS
var time EFI_TIME

status = systemTable.RuntimeServices.GetTime(&time, nil)

return time, status
}
70 changes: 38 additions & 32 deletions src/machine/uefi/efiapi.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,19 +88,21 @@ type EFI_CONVERT_POINTER func(DebugDisposition UINTN, Address *uintptr) EFI_STAT

//region: EFI Events

type EVENT_TYPE uint32

const (
EVT_TIMER = 0x80000000
EVT_RUNTIME = 0x40000000
EVT_RUNTIME_CONTEXT = 0x20000000
EVT_TIMER EVENT_TYPE = 0x80000000
EVT_RUNTIME EVENT_TYPE = 0x40000000
EVT_RUNTIME_CONTEXT EVENT_TYPE = 0x20000000

EVT_NOTIFY_WAIT = 0x00000100
EVT_NOTIFY_SIGNAL = 0x00000200
EVT_NOTIFY_WAIT EVENT_TYPE = 0x00000100
EVT_NOTIFY_SIGNAL EVENT_TYPE = 0x00000200

EVT_SIGNAL_EXIT_BOOT_SERVICES = 0x00000201
EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE = 0x60000202
EVT_SIGNAL_EXIT_BOOT_SERVICES EVENT_TYPE = 0x00000201
EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE EVENT_TYPE = 0x60000202

EVT_EFI_SIGNAL_MASK = 0x000000FF
EVT_EFI_SIGNAL_MAX = 4
EVT_EFI_SIGNAL_MASK EVENT_TYPE = 0x000000FF
EVT_EFI_SIGNAL_MAX EVENT_TYPE = 4

EFI_EVENT_TIMER = EVT_TIMER
EFI_EVENT_RUNTIME = EVT_RUNTIME
Expand Down Expand Up @@ -171,14 +173,14 @@ type EFI_CHECK_EVENT func(Event EFI_EVENT) EFI_STATUS
//region: Task priority level

const (
TPL_APPLICATION = 4
TPL_CALLBACK = 8
TPL_NOTIFY = 16
TPL_HIGH_LEVEL = 31
EFI_TPL_APPLICATION = TPL_APPLICATION
EFI_TPL_CALLBACK = TPL_CALLBACK
EFI_TPL_NOTIFY = TPL_NOTIFY
EFI_TPL_HIGH_LEVEL = TPL_HIGH_LEVEL
TPL_APPLICATION EFI_TPL = 4
TPL_CALLBACK EFI_TPL = 8
TPL_NOTIFY EFI_TPL = 16
TPL_HIGH_LEVEL EFI_TPL = 31
EFI_TPL_APPLICATION = TPL_APPLICATION
EFI_TPL_CALLBACK = TPL_CALLBACK
EFI_TPL_NOTIFY = TPL_NOTIFY
EFI_TPL_HIGH_LEVEL = TPL_HIGH_LEVEL
)

// EFI_RAISE_TPL
Expand Down Expand Up @@ -633,35 +635,39 @@ type EFI_RUNTIME_SERVICES struct {
//
// Time services
//
GetTime *EFI_GET_TIME
SetTime *EFI_SET_TIME
GetWakeupTime *EFI_GET_WAKEUP_TIME
SetWakeupTime *EFI_SET_WAKEUP_TIME
getTime uintptr
setTime uintptr
getWakeupTime uintptr
setWakeupTime uintptr

//
// Virtual memory services
//
SetVirtualAddressMap *EFI_SET_VIRTUAL_ADDRESS_MAP
ConvertPointer *EFI_CONVERT_POINTER
setVirtualAddressMap *EFI_SET_VIRTUAL_ADDRESS_MAP
convertPointer *EFI_CONVERT_POINTER

//
// Variable serviers
//

GetVariable *EFI_GET_VARIABLE
GetNextVariableName *EFI_GET_NEXT_VARIABLE_NAME
SetVariable *EFI_SET_VARIABLE
getVariable uintptr
getNextVariableName uintptr
setVariable uintptr

//
// Misc
//

GetNextHighMonotonicCount *EFI_GET_NEXT_HIGH_MONO_COUNT
ResetSystem *EFI_RESET_SYSTEM
getNextHighMonotonicCount uintptr
resetSystem uintptr

updateCapsule uintptr
queryCapsuleCapabilities uintptr
queryVariableInfo uintptr
}

UpdateCapsule *EFI_UPDATE_CAPSULE
QueryCapsuleCapabilities *EFI_QUERY_CAPSULE_CAPABILITIES
QueryVariableInfo *EFI_QUERY_VARIABLE_INFO
func (s *EFI_RUNTIME_SERVICES) GetTime(Time *EFI_TIME, Capabilities *EFI_TIME_CAPABILITIES) EFI_STATUS {
return UefiCall2(s.getTime, uintptr(unsafe.Pointer(Time)), uintptr(unsafe.Pointer(Capabilities)))
}

//endregion
Expand Down Expand Up @@ -772,7 +778,7 @@ func (s *EFI_BOOT_SERVICES) FreePool(Buffer uintptr) EFI_STATUS {
// @param NotifyFunction IN
// @param NotifyContext IN
// @param Event OUT
func (s *EFI_BOOT_SERVICES) CreateEvent(Type uint32, NotifyTpl EFI_TPL, NotifyFunction unsafe.Pointer, NotifyContext unsafe.Pointer, Event *EFI_EVENT) EFI_STATUS {
func (s *EFI_BOOT_SERVICES) CreateEvent(Type EVENT_TYPE, NotifyTpl EFI_TPL, NotifyFunction unsafe.Pointer, NotifyContext unsafe.Pointer, Event *EFI_EVENT) EFI_STATUS {
return UefiCall5(s.createEvent, uintptr(Type), uintptr(NotifyTpl), uintptr(NotifyFunction), uintptr(NotifyContext), uintptr(unsafe.Pointer(Event)))
}

Expand Down
15 changes: 10 additions & 5 deletions src/machine/uefi/util.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//go:build uefi

package uefi

import (
Expand Down Expand Up @@ -29,6 +31,14 @@ func GetImageHandle() EFI_HANDLE {
return EFI_HANDLE(imageHandle)
}

func Ticks() uint64 {
return x86.AsmReadRdtsc()
}

func TicksFrequency() uint64 {
return GetTscFrequency()
}

var hexConst [16]CHAR16 = [16]CHAR16{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}

var printBuf [256]CHAR16
Expand All @@ -55,8 +65,3 @@ func DebugPrint(text string, val uint64) {

systemTable.ConOut.OutputString(systemTable.ConOut, (*CHAR16)(unsafe.Pointer(&printBuf[0])))
}

//go:linkname ticks runtime.machineTicks
func ticks() uint64 {
return x86.AsmReadRdtsc()
}
Loading

0 comments on commit 907d376

Please sign in to comment.