mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-07-26 06:38:42 +00:00
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
This commit is contained in:
@@ -657,6 +657,8 @@ smoketest: testchdir
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pico2-ice examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -o test.efi -target=uefi-amd64 examples/test
|
||||
@$(MD5SUM) test.efi
|
||||
# test simulated boards on play.tinygo.org
|
||||
ifneq ($(WASM), 0)
|
||||
GOOS=js GOARCH=wasm $(TINYGO) build -size short -o test.wasm -tags=arduino_uno examples/blinky1
|
||||
|
||||
@@ -37,6 +37,7 @@ func TestClangAttributes(t *testing.T) {
|
||||
"nintendoswitch",
|
||||
"riscv-qemu",
|
||||
"tkey",
|
||||
"uefi-amd64",
|
||||
"wasip1",
|
||||
"wasip2",
|
||||
"wasm",
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
//go:build uefi
|
||||
|
||||
package machine
|
||||
|
||||
import (
|
||||
deviceuefi "device/uefi"
|
||||
)
|
||||
|
||||
const deviceName = "UEFI"
|
||||
|
||||
type (
|
||||
EFI_STATUS = deviceuefi.EFI_STATUS
|
||||
TextOutput = deviceuefi.TextOutput
|
||||
|
||||
Error = deviceuefi.Error
|
||||
)
|
||||
|
||||
const (
|
||||
EFI_SUCCESS = deviceuefi.EFI_SUCCESS
|
||||
EFI_LOAD_ERROR = deviceuefi.EFI_LOAD_ERROR
|
||||
EFI_INVALID_PARAMETER = deviceuefi.EFI_INVALID_PARAMETER
|
||||
EFI_UNSUPPORTED = deviceuefi.EFI_UNSUPPORTED
|
||||
EFI_BAD_BUFFER_SIZE = deviceuefi.EFI_BAD_BUFFER_SIZE
|
||||
EFI_BUFFER_TOO_SMALL = deviceuefi.EFI_BUFFER_TOO_SMALL
|
||||
EFI_NOT_READY = deviceuefi.EFI_NOT_READY
|
||||
EFI_DEVICE_ERROR = deviceuefi.EFI_DEVICE_ERROR
|
||||
EFI_WRITE_PROTECTED = deviceuefi.EFI_WRITE_PROTECTED
|
||||
EFI_OUT_OF_RESOURCES = deviceuefi.EFI_OUT_OF_RESOURCES
|
||||
EFI_VOLUME_CORRUPTED = deviceuefi.EFI_VOLUME_CORRUPTED
|
||||
EFI_VOLUME_FULL = deviceuefi.EFI_VOLUME_FULL
|
||||
EFI_NO_MEDIA = deviceuefi.EFI_NO_MEDIA
|
||||
EFI_MEDIA_CHANGED = deviceuefi.EFI_MEDIA_CHANGED
|
||||
EFI_NOT_FOUND = deviceuefi.EFI_NOT_FOUND
|
||||
EFI_ACCESS_DENIED = deviceuefi.EFI_ACCESS_DENIED
|
||||
EFI_NO_RESPONSE = deviceuefi.EFI_NO_RESPONSE
|
||||
EFI_NO_MAPPING = deviceuefi.EFI_NO_MAPPING
|
||||
EFI_TIMEOUT = deviceuefi.EFI_TIMEOUT
|
||||
EFI_NOT_STARTED = deviceuefi.EFI_NOT_STARTED
|
||||
EFI_ALREADY_STARTED = deviceuefi.EFI_ALREADY_STARTED
|
||||
EFI_ABORTED = deviceuefi.EFI_ABORTED
|
||||
EFI_ICMP_ERROR = deviceuefi.EFI_ICMP_ERROR
|
||||
EFI_TFTP_ERROR = deviceuefi.EFI_TFTP_ERROR
|
||||
EFI_PROTOCOL_ERROR = deviceuefi.EFI_PROTOCOL_ERROR
|
||||
EFI_INCOMPATIBLE_VERSION = deviceuefi.EFI_INCOMPATIBLE_VERSION
|
||||
EFI_SECURITY_VIOLATION = deviceuefi.EFI_SECURITY_VIOLATION
|
||||
EFI_CRC_ERROR = deviceuefi.EFI_CRC_ERROR
|
||||
EFI_END_OF_MEDIA = deviceuefi.EFI_END_OF_MEDIA
|
||||
EFI_END_OF_FILE = deviceuefi.EFI_END_OF_FILE
|
||||
EFI_INVALID_LANGUAGE = deviceuefi.EFI_INVALID_LANGUAGE
|
||||
EFI_COMPROMISED_DATA = deviceuefi.EFI_COMPROMISED_DATA
|
||||
EFI_IP_ADDRESS_CONFLICT = deviceuefi.EFI_IP_ADDRESS_CONFLICT
|
||||
EFI_HTTP_ERROR = deviceuefi.EFI_HTTP_ERROR
|
||||
)
|
||||
|
||||
var (
|
||||
ErrLoadError = deviceuefi.ErrLoadError
|
||||
ErrInvalidParameter = deviceuefi.ErrInvalidParameter
|
||||
ErrUnsupported = deviceuefi.ErrUnsupported
|
||||
ErrBadBufferSize = deviceuefi.ErrBadBufferSize
|
||||
ErrBufferTooSmall = deviceuefi.ErrBufferTooSmall
|
||||
ErrNotReady = deviceuefi.ErrNotReady
|
||||
ErrDeviceError = deviceuefi.ErrDeviceError
|
||||
ErrWriteProtected = deviceuefi.ErrWriteProtected
|
||||
ErrOutOfResources = deviceuefi.ErrOutOfResources
|
||||
ErrVolumeCorrupted = deviceuefi.ErrVolumeCorrupted
|
||||
ErrVolumeFull = deviceuefi.ErrVolumeFull
|
||||
ErrNoMedia = deviceuefi.ErrNoMedia
|
||||
ErrMediaChanged = deviceuefi.ErrMediaChanged
|
||||
ErrNotFound = deviceuefi.ErrNotFound
|
||||
ErrAccessDenied = deviceuefi.ErrAccessDenied
|
||||
ErrNoResponse = deviceuefi.ErrNoResponse
|
||||
ErrNoMapping = deviceuefi.ErrNoMapping
|
||||
ErrTimeout = deviceuefi.ErrTimeout
|
||||
ErrNotStarted = deviceuefi.ErrNotStarted
|
||||
ErrAlreadyStarted = deviceuefi.ErrAlreadyStarted
|
||||
ErrAborted = deviceuefi.ErrAborted
|
||||
ErrICMPError = deviceuefi.ErrICMPError
|
||||
ErrTFTPError = deviceuefi.ErrTFTPError
|
||||
ErrProtocolError = deviceuefi.ErrProtocolError
|
||||
ErrIncompatibleVersion = deviceuefi.ErrIncompatibleVersion
|
||||
ErrSecurityViolation = deviceuefi.ErrSecurityViolation
|
||||
ErrCRCError = deviceuefi.ErrCRCError
|
||||
ErrEndOfMedia = deviceuefi.ErrEndOfMedia
|
||||
ErrEndOfFile = deviceuefi.ErrEndOfFile
|
||||
ErrInvalidLanguage = deviceuefi.ErrInvalidLanguage
|
||||
ErrCompromisedData = deviceuefi.ErrCompromisedData
|
||||
ErrIPAddressConflict = deviceuefi.ErrIPAddressConflict
|
||||
ErrHTTPError = deviceuefi.ErrHTTPError
|
||||
)
|
||||
|
||||
func ConsoleOut() *TextOutput {
|
||||
return deviceuefi.ConsoleOut()
|
||||
}
|
||||
|
||||
func StandardError() *TextOutput {
|
||||
return deviceuefi.StandardError()
|
||||
}
|
||||
|
||||
func StatusError(status EFI_STATUS) *Error {
|
||||
return deviceuefi.StatusError(status)
|
||||
}
|
||||
|
||||
func (Pin) Configure(PinConfig) {
|
||||
}
|
||||
|
||||
func (Pin) Set(bool) {
|
||||
}
|
||||
|
||||
func (Pin) Get() bool {
|
||||
return false
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:build baremetal
|
||||
//go:build baremetal && !uefi
|
||||
|
||||
package runtime
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:build baremetal || tinygo.wasm
|
||||
//go:build (baremetal || tinygo.wasm) && !uefi
|
||||
|
||||
package runtime
|
||||
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
//go:build gc.leaking && uefi
|
||||
|
||||
package runtime
|
||||
|
||||
import _ "unsafe"
|
||||
|
||||
//go:export tinygo_scanstack
|
||||
func tinygo_scanstack() {
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:build !baremetal || tkey
|
||||
//go:build !baremetal || tkey || uefi
|
||||
|
||||
package interrupt
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:build windows
|
||||
//go:build windows || uefi
|
||||
|
||||
package runtime
|
||||
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
//go:build uefi
|
||||
|
||||
package runtime
|
||||
|
||||
import "device/uefi"
|
||||
|
||||
const zeroSizeAllocPtr uintptr = 16
|
||||
|
||||
//go:linkname procPin sync/atomic.runtime_procPin
|
||||
func procPin() {
|
||||
}
|
||||
|
||||
//go:linkname procUnpin sync/atomic.runtime_procUnpin
|
||||
func procUnpin() {
|
||||
}
|
||||
|
||||
var heapSize uintptr = 64 * 1024 * 1024
|
||||
var heapStart, heapEnd uintptr
|
||||
var stackTop uintptr
|
||||
var allocatePagesAddress uefi.EFI_PHYSICAL_ADDRESS
|
||||
var waitForEventsFunction = func() {
|
||||
uefi.CpuPause()
|
||||
}
|
||||
|
||||
func ticks() timeUnit {
|
||||
return timeUnit(uefi.Ticks())
|
||||
}
|
||||
|
||||
func nanosecondsToTicks(ns int64) timeUnit {
|
||||
frequency := int64(uefi.TicksFrequency())
|
||||
if frequency == 0 {
|
||||
return timeUnit(ns)
|
||||
}
|
||||
seconds := ns / 1000000000
|
||||
remainder := ns % 1000000000
|
||||
return timeUnit(seconds*frequency + (remainder*frequency)/1000000000)
|
||||
}
|
||||
|
||||
func ticksToNanoseconds(t timeUnit) int64 {
|
||||
frequency := int64(uefi.TicksFrequency())
|
||||
if frequency == 0 {
|
||||
return int64(t)
|
||||
}
|
||||
return int64(t) * 1000000000 / frequency
|
||||
}
|
||||
|
||||
func sleepTicks(d timeUnit) {
|
||||
if d == 0 {
|
||||
return
|
||||
}
|
||||
end := ticks() + d
|
||||
for ticks() < end {
|
||||
uefi.CpuPause()
|
||||
}
|
||||
}
|
||||
|
||||
func putchar(c byte) {
|
||||
if c == '\n' {
|
||||
buf := [4]uefi.CHAR16{'\r', 0, '\n', 0}
|
||||
uefi.ST().ConOut.OutputString(&buf[0])
|
||||
return
|
||||
}
|
||||
buf := [2]uefi.CHAR16{uefi.CHAR16(c), 0}
|
||||
uefi.ST().ConOut.OutputString(&buf[0])
|
||||
}
|
||||
|
||||
func exit(code int) {
|
||||
uefi.BS().Exit(uefi.GetImageHandle(), uefi.EFI_STATUS(code), 0, nil)
|
||||
}
|
||||
|
||||
func abort() {
|
||||
uefi.BS().Exit(uefi.GetImageHandle(), uefi.EFI_ABORTED, 0, nil)
|
||||
}
|
||||
|
||||
func preinit() {
|
||||
uefi.BS().SetWatchdogTimer(0, 0, 0, nil)
|
||||
if !growHeap() {
|
||||
runtimePanic("could not allocate initial UEFI heap")
|
||||
}
|
||||
}
|
||||
|
||||
func growHeap() bool {
|
||||
newHeapSize := ((heapSize * 4) / 3) &^ 4095
|
||||
for newHeapSize >= heapSize {
|
||||
pages := newHeapSize / 4096
|
||||
status := uefi.BS().AllocatePages(
|
||||
uefi.AllocateAnyPages,
|
||||
uefi.EfiLoaderData,
|
||||
uefi.UINTN(pages),
|
||||
&allocatePagesAddress,
|
||||
)
|
||||
if status == uefi.EFI_SUCCESS {
|
||||
heapStart = uintptr(allocatePagesAddress)
|
||||
heapSize = newHeapSize
|
||||
setHeapEnd(heapStart + heapSize)
|
||||
return true
|
||||
}
|
||||
if status != uefi.EFI_OUT_OF_RESOURCES {
|
||||
return false
|
||||
}
|
||||
newHeapSize /= 2
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func SetWaitForEvents(f func()) {
|
||||
waitForEventsFunction = f
|
||||
}
|
||||
|
||||
func waitForEvents() {
|
||||
waitForEventsFunction()
|
||||
}
|
||||
|
||||
//go:noinline
|
||||
func runMain() {
|
||||
run()
|
||||
}
|
||||
|
||||
func buffered() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func getchar() byte {
|
||||
return 0
|
||||
}
|
||||
|
||||
//export efi_main
|
||||
func main(imageHandle uintptr, systemTable uintptr) uintptr {
|
||||
uefi.Init(imageHandle, systemTable)
|
||||
preinit()
|
||||
stackTop = getCurrentStackPointer()
|
||||
runMain()
|
||||
|
||||
if heapStart != 0 {
|
||||
uefi.BS().FreePages(uefi.EFI_PHYSICAL_ADDRESS(heapStart), uefi.UINTN(heapSize/4096))
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
@@ -248,6 +248,9 @@ func sleep(duration int64) {
|
||||
if duration <= 0 {
|
||||
return
|
||||
}
|
||||
if schedulerSleepCustom(duration) {
|
||||
return
|
||||
}
|
||||
|
||||
addSleepTask(task.Current(), nanosecondsToTicks(duration))
|
||||
task.Pause()
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
//go:build !(scheduler.tasks && uefi)
|
||||
|
||||
package runtime
|
||||
|
||||
func schedulerSleepCustom(duration int64) bool {
|
||||
return false
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:build !tinygo.riscv && !cortexm && !(linux && !baremetal && !tinygo.wasm && !nintendoswitch) && !darwin && !wasip1
|
||||
//go:build !tinygo.riscv && !cortexm && !(linux && !baremetal && !tinygo.wasm && !nintendoswitch) && !darwin && !wasip1 && !uefi
|
||||
|
||||
package runtime
|
||||
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"build-tags": ["uefi", "baremetal", "linux", "amd64"],
|
||||
"llvm-target": "x86_64-unknown-windows-gnu",
|
||||
"cpu": "x86-64",
|
||||
"features": "+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87",
|
||||
"goos": "linux",
|
||||
"goarch": "amd64",
|
||||
"gc": "leaking",
|
||||
"scheduler": "none",
|
||||
"linker": "ld.lld",
|
||||
"linker-flavor": "coff",
|
||||
"libc": "picolibc",
|
||||
"automatic-stack-size": false,
|
||||
"default-stack-size": 65536,
|
||||
"cflags": [
|
||||
"-Werror",
|
||||
"-fshort-enums",
|
||||
"-fomit-frame-pointer",
|
||||
"-fno-exceptions", "-fno-unwind-tables", "-fno-asynchronous-unwind-tables",
|
||||
"-ffunction-sections", "-fdata-sections",
|
||||
"-ffreestanding",
|
||||
"-fshort-wchar",
|
||||
"-mno-red-zone"
|
||||
],
|
||||
"ldflags": [
|
||||
"-m", "i386pep",
|
||||
"--image-base", "0x400000",
|
||||
"--entry", "efi_main",
|
||||
"--subsystem", "efi_application",
|
||||
"-Bdynamic",
|
||||
"--gc-sections",
|
||||
"--no-insert-timestamp",
|
||||
"--no-dynamicbase"
|
||||
],
|
||||
"extra-files": [
|
||||
"src/device/amd64/cpu_amd64.S",
|
||||
"src/device/uefi/asm_amd64.S",
|
||||
"src/runtime/asm_amd64_windows.S"
|
||||
],
|
||||
"gdb": ["gdb-multiarch", "gdb"]
|
||||
}
|
||||
Reference in New Issue
Block a user