Skip to content

Commit

Permalink
feat: uefi os support
Browse files Browse the repository at this point in the history
  • Loading branch information
jclab-joseph committed Nov 13, 2023
1 parent 777048c commit 575ab46
Show file tree
Hide file tree
Showing 20 changed files with 2,223 additions and 7 deletions.
2 changes: 1 addition & 1 deletion builder/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -750,7 +750,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
ldflags = append(ldflags,
"--lto-O"+strconv.Itoa(speedLevel),
"-cache_path_lto", filepath.Join(cacheDir, "thinlto"))
} else {
} else if config.Options.GOOS != "uefi" {
// Options for the ELF linker.
ldflags = append(ldflags,
"--lto-O"+strconv.Itoa(speedLevel),
Expand Down
36 changes: 35 additions & 1 deletion compileopts/target.go
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,40 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
"--no-insert-timestamp",
"--no-dynamicbase",
)
} else if goos == "uefi" {
spec.Triple = strings.ReplaceAll(triple, "-unknown-uefi", "-unknown-windows-gnu")
spec.GOOS = "linux"
spec.Linker = "ld.lld"
spec.Libc = "picolibc"
spec.CFlags = append(spec.CFlags,
"-ffreestanding",
"-fshort-wchar",
"-fshort-enums",
"-mno-red-zone",
)
switch goarch {
case "amd64":
spec.LDFlags = append(spec.LDFlags,
"-m", "i386pep",
"--image-base", "0x400000",
)
case "arm64":
spec.LDFlags = append(spec.LDFlags,
"-m", "arm64pe",
)
}
spec.LDFlags = append(spec.LDFlags,
"--entry", "efi_main",
"--subsystem", "efi_application",
"-Bdynamic",
"--gc-sections",
"--no-insert-timestamp",
"--no-dynamicbase",
)
spec.ExtraFiles = append(spec.ExtraFiles,
"src/machine/uefi/asm_"+goarch+".S",
"src/device/x86/cpu_"+goarch+".S",
)
} else if goos == "wasip1" {
spec.GC = "" // use default GC
spec.Scheduler = "asyncify"
Expand All @@ -396,7 +430,7 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
}
if goarch != "wasm" {
suffix := ""
if goos == "windows" && goarch == "amd64" {
if (goos == "windows" || goos == "uefi") && goarch == "amd64" {
// Windows uses a different calling convention on amd64 from other
// operating systems so we need separate assembly files.
suffix = "_windows"
Expand Down
147 changes: 147 additions & 0 deletions src/device/x86/cpu.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
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
)

//export asmReadRdtsc
func AsmReadRdtsc() uint64

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

func InternalGetPerformanceCounterFrequency() uint64 {
return CpuidCoreClockCalculateTscFrequency()
}

func GetTimeInNanoSecond(ticks uint64) int64 {
frequency := InternalGetPerformanceCounterFrequency()
return ConvertTimeInNanoSecond(frequency, ticks)
}

func ConvertTimeInNanoSecond(frequency uint64, ticks uint64) int64 {
// Ticks
// Time = --------- x 1,000,000,000
// Frequency
nanoSeconds := (ticks / frequency) * 1000000000
remainder := ticks % frequency

// 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 := highBitSet64(remainder) - 32
if shift < 0 {
shift = 0
}
remainder = remainder >> shift
frequency = frequency >> shift
nanoSeconds += remainder * 1000000000 / frequency

return int64(nanoSeconds)
}

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

func highBitSet64(operand uint64) int {
if operand == (operand & 0xffffffff) {
return highBitSet32(uint32(operand))
}
return highBitSet32(uint32(operand>>32)) + 32
}

func highBitSet32(operand uint32) int {
if operand == 0 {
return -1
}
bitIndex := 32
for operand > 0 {
bitIndex--
operand <<= 1
}
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
}
54 changes: 54 additions & 0 deletions src/device/x86/cpu_amd64.S
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
.section .text

.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
96 changes: 96 additions & 0 deletions src/machine/uefi/asm_amd64.S
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
.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(%rsp),%r9 // 0x08(return_address) + 0x08(pushq rbp) + 0x20

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

.global uefiCall5
uefiCall5:
pushq %rbp
movq %rsp, %rbp
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
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

0 comments on commit 575ab46

Please sign in to comment.