Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support UEFI OS #3996

Open
wants to merge 5 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions builder/builder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ func TestClangAttributes(t *testing.T) {
"riscv-qemu",
"wasi",
"wasm",
"uefi-amd64",
}
if hasBuiltinTools {
// hasBuiltinTools is set when TinyGo is statically linked with LLVM,
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
Loading
Loading