Skip to content

Commit aa5bce9

Browse files
committed
feat: uefi os support
1 parent 777048c commit aa5bce9

File tree

21 files changed

+2227
-7
lines changed

21 files changed

+2227
-7
lines changed

builder/build.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -750,7 +750,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
750750
ldflags = append(ldflags,
751751
"--lto-O"+strconv.Itoa(speedLevel),
752752
"-cache_path_lto", filepath.Join(cacheDir, "thinlto"))
753-
} else {
753+
} else if config.Options.GOOS != "uefi" {
754754
// Options for the ELF linker.
755755
ldflags = append(ldflags,
756756
"--lto-O"+strconv.Itoa(speedLevel),

compileopts/target.go

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,40 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
375375
"--no-insert-timestamp",
376376
"--no-dynamicbase",
377377
)
378+
} else if goos == "uefi" {
379+
spec.Triple = strings.ReplaceAll(triple, "-unknown-uefi", "-unknown-windows-gnu")
380+
spec.GOOS = "linux"
381+
spec.Linker = "ld.lld"
382+
spec.Libc = "picolibc"
383+
spec.CFlags = append(spec.CFlags,
384+
"-ffreestanding",
385+
"-fshort-wchar",
386+
"-fshort-enums",
387+
"-mno-red-zone",
388+
)
389+
switch goarch {
390+
case "amd64":
391+
spec.LDFlags = append(spec.LDFlags,
392+
"-m", "i386pep",
393+
"--image-base", "0x400000",
394+
)
395+
case "arm64":
396+
spec.LDFlags = append(spec.LDFlags,
397+
"-m", "arm64pe",
398+
)
399+
}
400+
spec.LDFlags = append(spec.LDFlags,
401+
"--entry", "efi_main",
402+
"--subsystem", "efi_application",
403+
"-Bdynamic",
404+
"--gc-sections",
405+
"--no-insert-timestamp",
406+
"--no-dynamicbase",
407+
)
408+
spec.ExtraFiles = append(spec.ExtraFiles,
409+
"src/machine/uefi/asm_"+goarch+".S",
410+
"src/device/x86/cpu_"+goarch+".S",
411+
)
378412
} else if goos == "wasip1" {
379413
spec.GC = "" // use default GC
380414
spec.Scheduler = "asyncify"
@@ -396,7 +430,7 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
396430
}
397431
if goarch != "wasm" {
398432
suffix := ""
399-
if goos == "windows" && goarch == "amd64" {
433+
if (goos == "windows" || goos == "uefi") && goarch == "amd64" {
400434
// Windows uses a different calling convention on amd64 from other
401435
// operating systems so we need separate assembly files.
402436
suffix = "_windows"

src/device/x86/cpu.go

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
package x86
2+
3+
const (
4+
// CPUID_TIME_STAMP_COUNTER
5+
// EAX Returns processor base frequency information described by the
6+
// type CPUID_PROCESSOR_FREQUENCY_EAX.
7+
// EBX Returns maximum frequency information described by the type
8+
// CPUID_PROCESSOR_FREQUENCY_EBX.
9+
// ECX Returns bus frequency information described by the type
10+
// CPUID_PROCESSOR_FREQUENCY_ECX.
11+
// EDX Reserved.
12+
CPUID_TIME_STAMP_COUNTER = 0x15
13+
14+
// CPUID_PROCESSOR_FREQUENCY
15+
// EAX Returns processor base frequency information described by the
16+
// type CPUID_PROCESSOR_FREQUENCY_EAX.
17+
// EBX Returns maximum frequency information described by the type
18+
// CPUID_PROCESSOR_FREQUENCY_EBX.
19+
// ECX Returns bus frequency information described by the type
20+
// CPUID_PROCESSOR_FREQUENCY_ECX.
21+
// EDX Reserved.
22+
CPUID_PROCESSOR_FREQUENCY = 0x16
23+
)
24+
25+
//export asmReadRdtsc
26+
func AsmReadRdtsc() uint64
27+
28+
//export asmCpuid
29+
func AsmCpuid(index int, registerEax *uint32, registerRbx *uint32, registerEcx *uint32, registerEdx *uint32)
30+
31+
func InternalGetPerformanceCounterFrequency() uint64 {
32+
return CpuidCoreClockCalculateTscFrequency()
33+
}
34+
35+
func GetTimeInNanoSecond(ticks uint64) int64 {
36+
frequency := InternalGetPerformanceCounterFrequency()
37+
return ConvertTimeInNanoSecond(frequency, ticks)
38+
}
39+
40+
func ConvertTimeInNanoSecond(frequency uint64, ticks uint64) int64 {
41+
// Ticks
42+
// Time = --------- x 1,000,000,000
43+
// Frequency
44+
nanoSeconds := (ticks / frequency) * 1000000000
45+
remainder := ticks % frequency
46+
47+
// Ensure (Remainder * 1,000,000,000) will not overflow 64-bit.
48+
// Since 2^29 < 1,000,000,000 = 0x3B9ACA00 < 2^30, Remainder should < 2^(64-30) = 2^34,
49+
// i.e. highest bit set in Remainder should <= 33.
50+
//
51+
shift := highBitSet64(remainder) - 32
52+
if shift < 0 {
53+
shift = 0
54+
}
55+
remainder = remainder >> shift
56+
frequency = frequency >> shift
57+
nanoSeconds += remainder * 1000000000 / frequency
58+
59+
return int64(nanoSeconds)
60+
}
61+
62+
func GetTicksFromNanoSeconds(nano uint64) uint64 {
63+
frequency := InternalGetPerformanceCounterFrequency()
64+
return nano * frequency / 1000000000
65+
}
66+
67+
func highBitSet64(operand uint64) int {
68+
if operand == (operand & 0xffffffff) {
69+
return highBitSet32(uint32(operand))
70+
}
71+
return highBitSet32(uint32(operand>>32)) + 32
72+
}
73+
74+
func highBitSet32(operand uint32) int {
75+
if operand == 0 {
76+
return -1
77+
}
78+
bitIndex := 32
79+
for operand > 0 {
80+
bitIndex--
81+
operand <<= 1
82+
}
83+
return bitIndex
84+
}
85+
86+
/*
87+
88+
UINT64
89+
EFIAPI
90+
GetTimeInNanoSecond (
91+
IN UINT64 Ticks
92+
)
93+
{
94+
UINT64 Frequency;
95+
UINT64 NanoSeconds;
96+
UINT64 Remainder;
97+
INTN Shift;
98+
99+
Frequency = GetPerformanceCounterProperties (NULL, NULL);
100+
101+
NanoSeconds = MultU64x32 (DivU64x64Remainder (Ticks, Frequency, &Remainder), 1000000000u);
102+
103+
//
104+
// Ensure (Remainder * 1,000,000,000) will not overflow 64-bit.
105+
// Since 2^29 < 1,000,000,000 = 0x3B9ACA00 < 2^30, Remainder should < 2^(64-30) = 2^34,
106+
// i.e. highest bit set in Remainder should <= 33.
107+
//
108+
Shift = MAX (0, HighBitSet64 (Remainder) - 33);
109+
Remainder = RShiftU64 (Remainder, (UINTN)Shift);
110+
Frequency = RShiftU64 (Frequency, (UINTN)Shift);
111+
NanoSeconds += DivU64x64Remainder (MultU64x32 (Remainder, 1000000000u), Frequency, NULL);
112+
113+
return NanoSeconds;
114+
}
115+
*/
116+
117+
func CpuidCoreClockCalculateTscFrequency() uint64 {
118+
var TscFrequency uint64
119+
var CoreXtalFrequency uint64
120+
var RegEax uint32
121+
var RegEbx uint32
122+
var RegEcx uint32
123+
124+
AsmCpuid(CPUID_TIME_STAMP_COUNTER, &RegEax, &RegEbx, &RegEcx, nil)
125+
126+
// If EAX or EBX returns 0, the XTAL ratio is not enumerated.
127+
if (RegEax == 0) || (RegEbx == 0) {
128+
return 0
129+
}
130+
131+
// If ECX returns 0, the XTAL frequency is not enumerated.
132+
// And PcdCpuCoreCrystalClockFrequency defined should base on processor series.
133+
//
134+
if RegEcx == 0 {
135+
// Specifies CPUID Leaf 0x15 Time Stamp Counter and Nominal Core Crystal Clock Frequency.
136+
// Intel Xeon Processor Scalable Family with CPUID signature 06_55H = 25000000 (25MHz)
137+
// 6th and 7th generation Intel Core processors and Intel Xeon W Processor Family = 24000000 (24MHz)
138+
// Intel Atom processors based on Goldmont Microarchitecture with CPUID signature 06_5CH = 19200000 (19.2MHz)
139+
CoreXtalFrequency = 24000000
140+
} else {
141+
CoreXtalFrequency = uint64(RegEcx)
142+
}
143+
144+
// Calculate TSC frequency = (ECX, Core Xtal Frequency) * EBX/EAX
145+
TscFrequency = ((CoreXtalFrequency * uint64(RegEbx)) + (uint64(RegEax) / 2)) / uint64(RegEax)
146+
return TscFrequency
147+
}

src/device/x86/cpu_amd64.S

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
.section .text
2+
3+
.global asmReadRdtsc
4+
asmReadRdtsc:
5+
rdtsc
6+
shlq $0x20, %rdx
7+
orq %rdx, %rax
8+
ret
9+
10+
// @param Index The 32-bit value to load into EAX prior to invoking the CPUID
11+
// instruction.
12+
// @param RegisterEax A pointer to the 32-bit EAX value returned by the CPUID
13+
// instruction. This is an optional parameter that may be NULL.
14+
// @param RegisterEbx A pointer to the 32-bit EBX value returned by the CPUID
15+
// instruction. This is an optional parameter that may be NULL.
16+
// @param RegisterEcx A pointer to the 32-bit ECX value returned by the CPUID
17+
// instruction. This is an optional parameter that may be NULL.
18+
// @param RegisterEdx A pointer to the 32-bit EDX value returned by the CPUID
19+
// instruction. This is an optional parameter that may be NULL.
20+
// @return Index.
21+
.global asmCpuid
22+
asmCpuid:
23+
// rcx = Index
24+
// rdx = RegisterEax
25+
// r8 = RegisterEbx
26+
// r9 = RegisterEcx
27+
// rsp + 0x28 = RegisterEdx
28+
pushq %rbx
29+
30+
mov %ecx, %eax // eax <- index
31+
pushq %rax // save Index on stack
32+
33+
pushq %rdx // RegisterEax
34+
cpuid
35+
36+
test %r9, %r9 // RegisterEcx
37+
jz .SkipEcx
38+
mov %ecx, (%r9)
39+
.SkipEcx:
40+
popq %rcx // RegisterEax
41+
jrcxz .SkipEax
42+
mov %eax, (%rcx)
43+
.SkipEax:
44+
mov %r8, %rcx // RegisterEbx
45+
jrcxz .SkipEbx
46+
mov %ebx, (%rcx)
47+
.SkipEbx:
48+
mov 0x38(%rsp), %rcx // 0x28 + 0x10
49+
jrcxz .SkipEdx
50+
mov %edx, (%rcx)
51+
.SkipEdx:
52+
popq %rax // restore Index to rax as return value
53+
popq %rbx
54+
ret

src/machine/uefi/asm_amd64.S

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
.section .text
2+
3+
.global uefiCall0
4+
uefiCall0:
5+
pushq %rbp
6+
movq %rsp, %rbp
7+
callq *%rcx
8+
movq %rbp, %rsp
9+
popq %rbp
10+
ret
11+
12+
.global uefiCall1
13+
uefiCall1:
14+
pushq %rbp
15+
movq %rsp, %rbp
16+
movq %rcx, %rax // rax = fn
17+
movq %rdx, %rcx
18+
callq *%rax
19+
movq %rbp, %rsp
20+
popq %rbp
21+
ret
22+
23+
.global uefiCall2
24+
uefiCall2:
25+
pushq %rbp
26+
movq %rsp, %rbp
27+
movq %rcx, %rax // rax = fn
28+
movq %rdx, %rcx
29+
movq %r8, %rdx
30+
callq *%rax
31+
movq %rbp, %rsp
32+
popq %rbp
33+
ret
34+
35+
.global uefiCall3
36+
uefiCall3:
37+
pushq %rbp
38+
movq %rsp, %rbp
39+
movq %rcx, %rax // rax = fn
40+
movq %rdx, %rcx
41+
movq %r8, %rdx
42+
movq %r9, %r8
43+
callq *%rax
44+
movq %rbp, %rsp
45+
popq %rbp
46+
ret
47+
48+
.global uefiCall4
49+
uefiCall4:
50+
pushq %rbp
51+
movq %rsp, %rbp
52+
movq %rcx, %rax // rax = fn
53+
movq %rdx, %rcx
54+
movq %r8, %rdx
55+
movq %r9, %r8
56+
57+
movq 0x30(%rsp),%r9 // 0x08(return_address) + 0x08(pushq rbp) + 0x20
58+
59+
callq *%rax
60+
movq %rbp, %rsp
61+
popq %rbp
62+
ret
63+
64+
.global uefiCall5
65+
uefiCall5:
66+
pushq %rbp
67+
movq %rsp, %rbp
68+
movq %rcx, %rax // rax = fn
69+
movq %rdx, %rcx // a
70+
movq %r8, %rdx // b
71+
movq %r9, %r8 // c
72+
movq 0x30(%rbp),%r9 // d
73+
movq 0x38(%rbp),%r10 // e
74+
movq %r10,0x20(%rsp)
75+
callq *%rax
76+
movq %rbp, %rsp
77+
popq %rbp
78+
ret
79+
80+
.global uefiCall6
81+
uefiCall6:
82+
pushq %rbp
83+
movq %rsp, %rbp
84+
movq %rcx, %rax // rax = fn
85+
movq %rdx, %rcx // a
86+
movq %r8, %rdx // b
87+
movq %r9, %r8 // c
88+
movq 0x30(%rbp),%r9 // d
89+
movq 0x38(%rbp),%r10 // e
90+
movq %r10,0x20(%rsp)
91+
movq 0x40(%rbp),%r10 // f
92+
movq %r10,0x28(%rsp)
93+
callq *%rax
94+
movq %rbp, %rsp
95+
popq %rbp
96+
ret

src/machine/uefi/call.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package uefi
2+
3+
//go:nosplit
4+
//export uefiCall0
5+
func UefiCall0(fn uintptr) EFI_STATUS
6+
7+
//go:nosplit
8+
//go:export uefiCall1
9+
func UefiCall1(fn uintptr, a uintptr) EFI_STATUS
10+
11+
//go:nosplit
12+
//go:export uefiCall2
13+
func UefiCall2(fn uintptr, a uintptr, b uintptr) EFI_STATUS
14+
15+
//go:nosplit
16+
//go:export uefiCall3
17+
func UefiCall3(fn uintptr, a uintptr, b uintptr, c uintptr) EFI_STATUS
18+
19+
//go:nosplit
20+
//go:export uefiCall4
21+
func UefiCall4(fn uintptr, a uintptr, b uintptr, c uintptr, d uintptr) EFI_STATUS
22+
23+
//go:nosplit
24+
//go:export uefiCall5
25+
func UefiCall5(fn uintptr, a uintptr, b uintptr, c uintptr, d uintptr, e uintptr) EFI_STATUS
26+
27+
//go:nosplit
28+
//go:export uefiCall6
29+
func UefiCall6(fn uintptr, a uintptr, b uintptr, c uintptr, d uintptr, e uintptr, f uintptr) EFI_STATUS

0 commit comments

Comments
 (0)