Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions GNUmakefile
Original file line number Diff line number Diff line change
Expand Up @@ -770,6 +770,10 @@ ifneq ($(WASM), 0)
$(TINYGO) build -size short -o wasm.wasm -target=wasm examples/wasm/export
$(TINYGO) build -size short -o wasm.wasm -target=wasm examples/wasm/main
endif
$(TINYGO) build -size short -o test.exe -target=uefi-amd64 examples/empty
@$(MD5SUM) test.exe
$(TINYGO) build -size short -o test.exe -target=uefi-amd64 examples/time-offset
@$(MD5SUM) test.exe
# test various compiler flags
$(TINYGO) build -size short -o test.hex -target=pca10040 -gc=none -scheduler=none examples/blinky1
@$(MD5SUM) test.hex
Expand All @@ -786,6 +790,7 @@ endif
GOOS=windows GOARCH=arm64 $(TINYGO) build -size short -o test.exe ./testdata/cgo
GOOS=darwin GOARCH=amd64 $(TINYGO) build -size short -o test ./testdata/cgo
GOOS=darwin GOARCH=arm64 $(TINYGO) build -size short -o test ./testdata/cgo
$(TINYGO) build -size short -o test.exe -target=uefi-amd64 ./testdata/cgo
ifneq ($(OS),Windows_NT)
# TODO: this does not yet work on Windows. Somehow, unused functions are
# not garbage collected.
Expand Down
10 changes: 9 additions & 1 deletion builder/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -740,7 +740,15 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
ldflags = append(ldflags, dependency.result)
}
ldflags = append(ldflags, "-mllvm", "-mcpu="+config.CPU())
if config.GOOS() == "windows" {
buildTags := config.BuildTags()
isWindowsLinker := config.GOOS() == "windows"
for _, tag := range buildTags {
if tag == "uefi" {
isWindowsLinker = true
}
}

if isWindowsLinker {
// Options for the MinGW wrapper for the lld COFF linker.
ldflags = append(ldflags,
"-Xlink=/opt:lldlto="+strconv.Itoa(speedLevel),
Expand Down
141 changes: 141 additions & 0 deletions src/device/x86/cpu.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
//go:build amd64

package x86

const (
// CPUID_TIME_STAMP_COUNTER
// EAX Returns processor base frequency information described by the
// type CPUID_PROCESSOR_FREQUENCY_EAX.
// EBX Returns maximum frequency information described by the type
// CPUID_PROCESSOR_FREQUENCY_EBX.
// ECX Returns bus frequency information described by the type
// CPUID_PROCESSOR_FREQUENCY_ECX.
// EDX Reserved.
CPUID_TIME_STAMP_COUNTER = 0x15

// CPUID_PROCESSOR_FREQUENCY
// EAX Returns processor base frequency information described by the
// type CPUID_PROCESSOR_FREQUENCY_EAX.
// EBX Returns maximum frequency information described by the type
// CPUID_PROCESSOR_FREQUENCY_EBX.
// ECX Returns bus frequency information described by the type
// CPUID_PROCESSOR_FREQUENCY_ECX.
// EDX Reserved.
CPUID_PROCESSOR_FREQUENCY = 0x16
)

type CpuExtendedFamily uint16

const (
// AMD
CPU_FAMILY_AMD_11H CpuExtendedFamily = 0x11
// Intel
CPU_FAMILY_INTEL_CORE CpuExtendedFamily = 6
CPU_MODEL_NEHALEM CpuExtendedFamily = 0x1e
CPU_MODEL_NEHALEM_EP CpuExtendedFamily = 0x1a
CPU_MODEL_NEHALEM_EX CpuExtendedFamily = 0x2e
CPU_MODEL_WESTMERE CpuExtendedFamily = 0x25
CPU_MODEL_WESTMERE_EP CpuExtendedFamily = 0x2c
CPU_MODEL_WESTMERE_EX CpuExtendedFamily = 0x2f
CPU_MODEL_SANDYBRIDGE CpuExtendedFamily = 0x2a
CPU_MODEL_SANDYBRIDGE_EP CpuExtendedFamily = 0x2d
CPU_MODEL_IVYBRIDGE_EP CpuExtendedFamily = 0x3a
CPU_MODEL_HASWELL_E3 CpuExtendedFamily = 0x3c
CPU_MODEL_HASWELL_E7 CpuExtendedFamily = 0x3f
CPU_MODEL_BROADWELL CpuExtendedFamily = 0x3d
)

func GetExtendedCpuFamily() CpuExtendedFamily {
var family CpuExtendedFamily
family = CpuExtendedFamily((stdCpuid1Eax >> 8) & 0x0f)
family += CpuExtendedFamily((stdCpuid1Eax >> 20) & 0xff)
return family
}

//export asmPause
func AsmPause()

//export asmReadRdtsc
func AsmReadRdtsc() uint64

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

var maxCpuidIndex uint32
var stdVendorName0 uint32
var stdCpuid1Eax uint32
var stdCpuid1Ebx uint32
var stdCpuid1Ecx uint32
var stdCpuid1Edx uint32

func init() {
AsmCpuid(0, &maxCpuidIndex, &stdVendorName0, nil, nil)
AsmCpuid(1, &stdCpuid1Eax, &stdCpuid1Ebx, &stdCpuid1Ecx, &stdCpuid1Edx)
}

func GetMaxCpuidIndex() uint32 {
return maxCpuidIndex
}

func IsIntel() bool {
return stdVendorName0 == 0x756e6547
}

func IsIntelFamilyCore() bool {
return IsIntel() && GetExtendedCpuFamily() == CPU_FAMILY_INTEL_CORE
}

func IsAmd() bool {
return stdVendorName0 == 0x68747541
}

func InternalGetPerformanceCounterFrequency() uint64 {
if maxCpuidIndex >= CPUID_TIME_STAMP_COUNTER {
return CpuidCoreClockCalculateTscFrequency()
}

return 0
}

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.
// https://github.com/torvalds/linux/blob/master/tools/power/x86/turbostat/turbostat.c
if IsIntelFamilyCore() {
switch GetExtendedCpuFamily() {
case 0x5F: // INTEL_FAM6_ATOM_GOLDMONT_D
CoreXtalFrequency = 25000000
case 0x5C: // INTEL_FAM6_ATOM_GOLDMONT
CoreXtalFrequency = 19200000
case 0x7A: // INTEL_FAM6_ATOM_GOLDMONT_PLUS
CoreXtalFrequency = 19200000
default:
CoreXtalFrequency = 24000000
}
} else {
return 0
}
} 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/device/x86/cpu_amd64.S
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
.section .text

.global asmPause
asmPause:
pause
ret

.global asmReadRdtsc
asmReadRdtsc:
rdtsc
shlq $0x20, %rdx
orq %rdx, %rax
ret

// @param Index The 32-bit value to load into EAX prior to invoking the CPUID
// instruction.
// @param RegisterEax A pointer to the 32-bit EAX value returned by the CPUID
// instruction. This is an optional parameter that may be NULL.
// @param RegisterEbx A pointer to the 32-bit EBX value returned by the CPUID
// instruction. This is an optional parameter that may be NULL.
// @param RegisterEcx A pointer to the 32-bit ECX value returned by the CPUID
// instruction. This is an optional parameter that may be NULL.
// @param RegisterEdx A pointer to the 32-bit EDX value returned by the CPUID
// instruction. This is an optional parameter that may be NULL.
// @return Index.
.global asmCpuid
asmCpuid:
// rcx = Index
// rdx = RegisterEax
// r8 = RegisterEbx
// r9 = RegisterEcx
// rsp + 0x28 = RegisterEdx
pushq %rbx

mov %ecx, %eax // eax <- index
pushq %rax // save Index on stack

pushq %rdx // RegisterEax
cpuid

test %r9, %r9 // RegisterEcx
jz .SkipEcx
mov %ecx, (%r9)
.SkipEcx:
popq %rcx // RegisterEax
jrcxz .SkipEax
mov %eax, (%rcx)
.SkipEax:
mov %r8, %rcx // RegisterEbx
jrcxz .SkipEbx
mov %ebx, (%rcx)
.SkipEbx:
mov 0x38(%rsp), %rcx // 0x28 + 0x10
jrcxz .SkipEdx
mov %edx, (%rcx)
.SkipEdx:
popq %rax // restore Index to rax as return value
popq %rbx
ret
2 changes: 1 addition & 1 deletion src/internal/task/task_stack_amd64.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//go:build scheduler.tasks && amd64 && !windows
//go:build scheduler.tasks && amd64 && !windows && !uefi

package task

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//go:build scheduler.tasks && amd64 && windows
//go:build scheduler.tasks && amd64 && (windows || uefi)

package task

Expand Down
17 changes: 17 additions & 0 deletions src/machine/uefi/arch_x86.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//go:build i386 || amd64

package uefi

import "device/x86"

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

func CpuPause() {
x86.AsmPause()
}

func getTscFrequency() uint64 {
return x86.InternalGetPerformanceCounterFrequency()
}
98 changes: 98 additions & 0 deletions src/machine/uefi/asm_amd64.S
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
.section .text

.global uefiCall0
uefiCall0:
pushq %rbp
movq %rsp, %rbp
callq *%rcx
movq %rbp, %rsp
popq %rbp
ret

.global uefiCall1
uefiCall1:
pushq %rbp
movq %rsp, %rbp
movq %rcx, %rax // rax = fn
movq %rdx, %rcx
callq *%rax
movq %rbp, %rsp
popq %rbp
ret

.global uefiCall2
uefiCall2:
pushq %rbp
movq %rsp, %rbp
movq %rcx, %rax // rax = fn
movq %rdx, %rcx
movq %r8, %rdx
callq *%rax
movq %rbp, %rsp
popq %rbp
ret

.global uefiCall3
uefiCall3:
pushq %rbp
movq %rsp, %rbp
movq %rcx, %rax // rax = fn
movq %rdx, %rcx
movq %r8, %rdx
movq %r9, %r8
callq *%rax
movq %rbp, %rsp
popq %rbp
ret

.global uefiCall4
uefiCall4:
pushq %rbp
movq %rsp, %rbp
movq %rcx, %rax // rax = fn
movq %rdx, %rcx
movq %r8, %rdx
movq %r9, %r8

movq 0x30(%rbp),%r9 // 0x08(return_address) + 0x08(pushq rbp) + 0x20

callq *%rax
movq %rbp, %rsp
popq %rbp
ret

.global uefiCall5
uefiCall5:
pushq %rbp
movq %rsp, %rbp
subq $0x30,%rsp
movq %rcx, %rax // rax = fn
movq %rdx, %rcx // a
movq %r8, %rdx // b
movq %r9, %r8 // c
movq 0x30(%rbp),%r9 // d
movq 0x38(%rbp),%r10 // e
movq %r10,0x20(%rsp)
callq *%rax
movq %rbp, %rsp
popq %rbp
ret

.global uefiCall6
uefiCall6:
pushq %rbp
movq %rsp, %rbp
subq $0x30,%rsp
movq %rcx, %rax // rax = fn
movq %rdx, %rcx // a
movq %r8, %rdx // b
movq %r9, %r8 // c
movq 0x30(%rbp),%r9 // d
movq 0x38(%rbp),%r10 // e
movq %r10,0x20(%rsp)
movq 0x40(%rbp),%r10 // f
movq %r10,0x28(%rsp)
callq *%rax
movq %rbp, %rsp
popq %rbp
ret
29 changes: 29 additions & 0 deletions src/machine/uefi/call.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package uefi

//go:nosplit
//export uefiCall0
func UefiCall0(fn uintptr) EFI_STATUS

//go:nosplit
//go:export uefiCall1
func UefiCall1(fn uintptr, a uintptr) EFI_STATUS

//go:nosplit
//go:export uefiCall2
func UefiCall2(fn uintptr, a uintptr, b uintptr) EFI_STATUS

//go:nosplit
//go:export uefiCall3
func UefiCall3(fn uintptr, a uintptr, b uintptr, c uintptr) EFI_STATUS

//go:nosplit
//go:export uefiCall4
func UefiCall4(fn uintptr, a uintptr, b uintptr, c uintptr, d uintptr) EFI_STATUS

//go:nosplit
//go:export uefiCall5
func UefiCall5(fn uintptr, a uintptr, b uintptr, c uintptr, d uintptr, e uintptr) EFI_STATUS

//go:nosplit
//go:export uefiCall6
func UefiCall6(fn uintptr, a uintptr, b uintptr, c uintptr, d uintptr, e uintptr, f uintptr) EFI_STATUS
Loading