Skip to content

Commit 9c2eafc

Browse files
authored
targets: minimal uefi target support (#5452)
* runtime: split baremetal memory setup * runtime: extract Windows PE globals scan helper * compileopts,builder: add target linker flavor override * machine,runtime,targets: add minimal uefi-amd64 target * runtime,examples: add clean UEFI exit path test * machine,x86: fix amd64 ABI stack handling * machine/uefi: add CHAR16 conversion helpers * machine/uefi: add loaded image protocol support * runtime: add UEFI PE globals support * machine,runtime,x86: add UEFI time and text output support * machine,runtime,x86: add UEFI text and time support * machine,runtime,examples: add UEFI text input support * machine,examples: add UEFI graphics output support * add rest of STOP methods (direct UEFI ABI only) * runtime: fix UEFI sleep tick conversion * machine/uefi: make text input waits cooperative * address TestClangAttributes/uefi-amd64 failure * address TestConfigLinkerFlavor bug * uefi: move raw bindings to device package * strip down implementation to bare minimum * amd64: rename x86 package * do git restore upstream/dev for src/net * add smoketest for uefi-amd64; add required zeroSizeAllocPtr constant * address PR issues: remove unused files; make putchar() insert '\r' before '\n' * swap lines around
1 parent 3797e89 commit 9c2eafc

13 files changed

Lines changed: 318 additions & 5 deletions

GNUmakefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -657,6 +657,8 @@ smoketest: testchdir
657657
@$(MD5SUM) test.hex
658658
$(TINYGO) build -size short -o test.hex -target=pico2-ice examples/blinky1
659659
@$(MD5SUM) test.hex
660+
$(TINYGO) build -o test.efi -target=uefi-amd64 examples/test
661+
@$(MD5SUM) test.efi
660662
# test simulated boards on play.tinygo.org
661663
ifneq ($(WASM), 0)
662664
GOOS=js GOARCH=wasm $(TINYGO) build -size short -o test.wasm -tags=arduino_uno examples/blinky1

builder/builder_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ func TestClangAttributes(t *testing.T) {
3737
"nintendoswitch",
3838
"riscv-qemu",
3939
"tkey",
40+
"uefi-amd64",
4041
"wasip1",
4142
"wasip2",
4243
"wasm",

src/machine/machine_uefi.go

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
//go:build uefi
2+
3+
package machine
4+
5+
import (
6+
deviceuefi "device/uefi"
7+
)
8+
9+
const deviceName = "UEFI"
10+
11+
type (
12+
EFI_STATUS = deviceuefi.EFI_STATUS
13+
TextOutput = deviceuefi.TextOutput
14+
15+
Error = deviceuefi.Error
16+
)
17+
18+
const (
19+
EFI_SUCCESS = deviceuefi.EFI_SUCCESS
20+
EFI_LOAD_ERROR = deviceuefi.EFI_LOAD_ERROR
21+
EFI_INVALID_PARAMETER = deviceuefi.EFI_INVALID_PARAMETER
22+
EFI_UNSUPPORTED = deviceuefi.EFI_UNSUPPORTED
23+
EFI_BAD_BUFFER_SIZE = deviceuefi.EFI_BAD_BUFFER_SIZE
24+
EFI_BUFFER_TOO_SMALL = deviceuefi.EFI_BUFFER_TOO_SMALL
25+
EFI_NOT_READY = deviceuefi.EFI_NOT_READY
26+
EFI_DEVICE_ERROR = deviceuefi.EFI_DEVICE_ERROR
27+
EFI_WRITE_PROTECTED = deviceuefi.EFI_WRITE_PROTECTED
28+
EFI_OUT_OF_RESOURCES = deviceuefi.EFI_OUT_OF_RESOURCES
29+
EFI_VOLUME_CORRUPTED = deviceuefi.EFI_VOLUME_CORRUPTED
30+
EFI_VOLUME_FULL = deviceuefi.EFI_VOLUME_FULL
31+
EFI_NO_MEDIA = deviceuefi.EFI_NO_MEDIA
32+
EFI_MEDIA_CHANGED = deviceuefi.EFI_MEDIA_CHANGED
33+
EFI_NOT_FOUND = deviceuefi.EFI_NOT_FOUND
34+
EFI_ACCESS_DENIED = deviceuefi.EFI_ACCESS_DENIED
35+
EFI_NO_RESPONSE = deviceuefi.EFI_NO_RESPONSE
36+
EFI_NO_MAPPING = deviceuefi.EFI_NO_MAPPING
37+
EFI_TIMEOUT = deviceuefi.EFI_TIMEOUT
38+
EFI_NOT_STARTED = deviceuefi.EFI_NOT_STARTED
39+
EFI_ALREADY_STARTED = deviceuefi.EFI_ALREADY_STARTED
40+
EFI_ABORTED = deviceuefi.EFI_ABORTED
41+
EFI_ICMP_ERROR = deviceuefi.EFI_ICMP_ERROR
42+
EFI_TFTP_ERROR = deviceuefi.EFI_TFTP_ERROR
43+
EFI_PROTOCOL_ERROR = deviceuefi.EFI_PROTOCOL_ERROR
44+
EFI_INCOMPATIBLE_VERSION = deviceuefi.EFI_INCOMPATIBLE_VERSION
45+
EFI_SECURITY_VIOLATION = deviceuefi.EFI_SECURITY_VIOLATION
46+
EFI_CRC_ERROR = deviceuefi.EFI_CRC_ERROR
47+
EFI_END_OF_MEDIA = deviceuefi.EFI_END_OF_MEDIA
48+
EFI_END_OF_FILE = deviceuefi.EFI_END_OF_FILE
49+
EFI_INVALID_LANGUAGE = deviceuefi.EFI_INVALID_LANGUAGE
50+
EFI_COMPROMISED_DATA = deviceuefi.EFI_COMPROMISED_DATA
51+
EFI_IP_ADDRESS_CONFLICT = deviceuefi.EFI_IP_ADDRESS_CONFLICT
52+
EFI_HTTP_ERROR = deviceuefi.EFI_HTTP_ERROR
53+
)
54+
55+
var (
56+
ErrLoadError = deviceuefi.ErrLoadError
57+
ErrInvalidParameter = deviceuefi.ErrInvalidParameter
58+
ErrUnsupported = deviceuefi.ErrUnsupported
59+
ErrBadBufferSize = deviceuefi.ErrBadBufferSize
60+
ErrBufferTooSmall = deviceuefi.ErrBufferTooSmall
61+
ErrNotReady = deviceuefi.ErrNotReady
62+
ErrDeviceError = deviceuefi.ErrDeviceError
63+
ErrWriteProtected = deviceuefi.ErrWriteProtected
64+
ErrOutOfResources = deviceuefi.ErrOutOfResources
65+
ErrVolumeCorrupted = deviceuefi.ErrVolumeCorrupted
66+
ErrVolumeFull = deviceuefi.ErrVolumeFull
67+
ErrNoMedia = deviceuefi.ErrNoMedia
68+
ErrMediaChanged = deviceuefi.ErrMediaChanged
69+
ErrNotFound = deviceuefi.ErrNotFound
70+
ErrAccessDenied = deviceuefi.ErrAccessDenied
71+
ErrNoResponse = deviceuefi.ErrNoResponse
72+
ErrNoMapping = deviceuefi.ErrNoMapping
73+
ErrTimeout = deviceuefi.ErrTimeout
74+
ErrNotStarted = deviceuefi.ErrNotStarted
75+
ErrAlreadyStarted = deviceuefi.ErrAlreadyStarted
76+
ErrAborted = deviceuefi.ErrAborted
77+
ErrICMPError = deviceuefi.ErrICMPError
78+
ErrTFTPError = deviceuefi.ErrTFTPError
79+
ErrProtocolError = deviceuefi.ErrProtocolError
80+
ErrIncompatibleVersion = deviceuefi.ErrIncompatibleVersion
81+
ErrSecurityViolation = deviceuefi.ErrSecurityViolation
82+
ErrCRCError = deviceuefi.ErrCRCError
83+
ErrEndOfMedia = deviceuefi.ErrEndOfMedia
84+
ErrEndOfFile = deviceuefi.ErrEndOfFile
85+
ErrInvalidLanguage = deviceuefi.ErrInvalidLanguage
86+
ErrCompromisedData = deviceuefi.ErrCompromisedData
87+
ErrIPAddressConflict = deviceuefi.ErrIPAddressConflict
88+
ErrHTTPError = deviceuefi.ErrHTTPError
89+
)
90+
91+
func ConsoleOut() *TextOutput {
92+
return deviceuefi.ConsoleOut()
93+
}
94+
95+
func StandardError() *TextOutput {
96+
return deviceuefi.StandardError()
97+
}
98+
99+
func StatusError(status EFI_STATUS) *Error {
100+
return deviceuefi.StatusError(status)
101+
}
102+
103+
func (Pin) Configure(PinConfig) {
104+
}
105+
106+
func (Pin) Set(bool) {
107+
}
108+
109+
func (Pin) Get() bool {
110+
return false
111+
}

src/runtime/baremetal_memory.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//go:build baremetal
1+
//go:build baremetal && !uefi
22

33
package runtime
44

src/runtime/gc_globals.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//go:build baremetal || tinygo.wasm
1+
//go:build (baremetal || tinygo.wasm) && !uefi
22

33
package runtime
44

src/runtime/gc_leaking_uefi.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
//go:build gc.leaking && uefi
2+
3+
package runtime
4+
5+
import _ "unsafe"
6+
7+
//go:export tinygo_scanstack
8+
func tinygo_scanstack() {
9+
}

src/runtime/interrupt/interrupt_none.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//go:build !baremetal || tkey
1+
//go:build !baremetal || tkey || uefi
22

33
package interrupt
44

src/runtime/os_windows_pe.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//go:build windows
1+
//go:build windows || uefi
22

33
package runtime
44

src/runtime/runtime_uefi.go

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
//go:build uefi
2+
3+
package runtime
4+
5+
import "device/uefi"
6+
7+
const zeroSizeAllocPtr uintptr = 16
8+
9+
//go:linkname procPin sync/atomic.runtime_procPin
10+
func procPin() {
11+
}
12+
13+
//go:linkname procUnpin sync/atomic.runtime_procUnpin
14+
func procUnpin() {
15+
}
16+
17+
var heapSize uintptr = 64 * 1024 * 1024
18+
var heapStart, heapEnd uintptr
19+
var stackTop uintptr
20+
var allocatePagesAddress uefi.EFI_PHYSICAL_ADDRESS
21+
var waitForEventsFunction = func() {
22+
uefi.CpuPause()
23+
}
24+
25+
func ticks() timeUnit {
26+
return timeUnit(uefi.Ticks())
27+
}
28+
29+
func nanosecondsToTicks(ns int64) timeUnit {
30+
frequency := int64(uefi.TicksFrequency())
31+
if frequency == 0 {
32+
return timeUnit(ns)
33+
}
34+
seconds := ns / 1000000000
35+
remainder := ns % 1000000000
36+
return timeUnit(seconds*frequency + (remainder*frequency)/1000000000)
37+
}
38+
39+
func ticksToNanoseconds(t timeUnit) int64 {
40+
frequency := int64(uefi.TicksFrequency())
41+
if frequency == 0 {
42+
return int64(t)
43+
}
44+
return int64(t) * 1000000000 / frequency
45+
}
46+
47+
func sleepTicks(d timeUnit) {
48+
if d == 0 {
49+
return
50+
}
51+
end := ticks() + d
52+
for ticks() < end {
53+
uefi.CpuPause()
54+
}
55+
}
56+
57+
func putchar(c byte) {
58+
if c == '\n' {
59+
buf := [4]uefi.CHAR16{'\r', 0, '\n', 0}
60+
uefi.ST().ConOut.OutputString(&buf[0])
61+
return
62+
}
63+
buf := [2]uefi.CHAR16{uefi.CHAR16(c), 0}
64+
uefi.ST().ConOut.OutputString(&buf[0])
65+
}
66+
67+
func exit(code int) {
68+
uefi.BS().Exit(uefi.GetImageHandle(), uefi.EFI_STATUS(code), 0, nil)
69+
}
70+
71+
func abort() {
72+
uefi.BS().Exit(uefi.GetImageHandle(), uefi.EFI_ABORTED, 0, nil)
73+
}
74+
75+
func preinit() {
76+
uefi.BS().SetWatchdogTimer(0, 0, 0, nil)
77+
if !growHeap() {
78+
runtimePanic("could not allocate initial UEFI heap")
79+
}
80+
}
81+
82+
func growHeap() bool {
83+
newHeapSize := ((heapSize * 4) / 3) &^ 4095
84+
for newHeapSize >= heapSize {
85+
pages := newHeapSize / 4096
86+
status := uefi.BS().AllocatePages(
87+
uefi.AllocateAnyPages,
88+
uefi.EfiLoaderData,
89+
uefi.UINTN(pages),
90+
&allocatePagesAddress,
91+
)
92+
if status == uefi.EFI_SUCCESS {
93+
heapStart = uintptr(allocatePagesAddress)
94+
heapSize = newHeapSize
95+
setHeapEnd(heapStart + heapSize)
96+
return true
97+
}
98+
if status != uefi.EFI_OUT_OF_RESOURCES {
99+
return false
100+
}
101+
newHeapSize /= 2
102+
}
103+
return false
104+
}
105+
106+
func SetWaitForEvents(f func()) {
107+
waitForEventsFunction = f
108+
}
109+
110+
func waitForEvents() {
111+
waitForEventsFunction()
112+
}
113+
114+
//go:noinline
115+
func runMain() {
116+
run()
117+
}
118+
119+
func buffered() int {
120+
return 0
121+
}
122+
123+
func getchar() byte {
124+
return 0
125+
}
126+
127+
//export efi_main
128+
func main(imageHandle uintptr, systemTable uintptr) uintptr {
129+
uefi.Init(imageHandle, systemTable)
130+
preinit()
131+
stackTop = getCurrentStackPointer()
132+
runMain()
133+
134+
if heapStart != 0 {
135+
uefi.BS().FreePages(uefi.EFI_PHYSICAL_ADDRESS(heapStart), uefi.UINTN(heapSize/4096))
136+
}
137+
138+
return 0
139+
}

src/runtime/scheduler_cooperative.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,9 @@ func sleep(duration int64) {
248248
if duration <= 0 {
249249
return
250250
}
251+
if schedulerSleepCustom(duration) {
252+
return
253+
}
251254

252255
addSleepTask(task.Current(), nanosecondsToTicks(duration))
253256
task.Pause()

0 commit comments

Comments
 (0)