diff --git a/builder/builder_test.go b/builder/builder_test.go index fe4d395792..3a878ea4cd 100644 --- a/builder/builder_test.go +++ b/builder/builder_test.go @@ -36,6 +36,7 @@ func TestClangAttributes(t *testing.T) { "nintendoswitch", "riscv-qemu", "tkey", + "uefi-amd64", "wasip1", "wasip2", "wasm", diff --git a/src/machine/machine_uefi.go b/src/machine/machine_uefi.go new file mode 100644 index 0000000000..94659d2b3e --- /dev/null +++ b/src/machine/machine_uefi.go @@ -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 +} diff --git a/src/runtime/baremetal_memory.go b/src/runtime/baremetal_memory.go index 02f870ad69..52e30b5312 100644 --- a/src/runtime/baremetal_memory.go +++ b/src/runtime/baremetal_memory.go @@ -1,4 +1,4 @@ -//go:build baremetal +//go:build baremetal && !uefi package runtime diff --git a/src/runtime/gc_globals.go b/src/runtime/gc_globals.go index 3e8f857618..58e70ca3e1 100644 --- a/src/runtime/gc_globals.go +++ b/src/runtime/gc_globals.go @@ -1,4 +1,4 @@ -//go:build baremetal || tinygo.wasm +//go:build (baremetal || tinygo.wasm) && !uefi package runtime diff --git a/src/runtime/gc_leaking_uefi.go b/src/runtime/gc_leaking_uefi.go new file mode 100644 index 0000000000..4985221b35 --- /dev/null +++ b/src/runtime/gc_leaking_uefi.go @@ -0,0 +1,9 @@ +//go:build gc.leaking && uefi + +package runtime + +import _ "unsafe" + +//go:export tinygo_scanstack +func tinygo_scanstack() { +} diff --git a/src/runtime/interrupt/interrupt_none.go b/src/runtime/interrupt/interrupt_none.go index ea8bdb68c6..22f1644f0f 100644 --- a/src/runtime/interrupt/interrupt_none.go +++ b/src/runtime/interrupt/interrupt_none.go @@ -1,4 +1,4 @@ -//go:build !baremetal || tkey +//go:build !baremetal || tkey || uefi package interrupt diff --git a/src/runtime/os_windows_pe.go b/src/runtime/os_windows_pe.go index febe0b2105..53bc62f2ba 100644 --- a/src/runtime/os_windows_pe.go +++ b/src/runtime/os_windows_pe.go @@ -1,4 +1,4 @@ -//go:build windows +//go:build windows || uefi package runtime diff --git a/src/runtime/runtime_uefi.go b/src/runtime/runtime_uefi.go new file mode 100644 index 0000000000..9849ec9d92 --- /dev/null +++ b/src/runtime/runtime_uefi.go @@ -0,0 +1,132 @@ +//go:build uefi + +package runtime + +import "device/uefi" + +//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) { + 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 +} diff --git a/src/runtime/runtime_uefi_schednone.go b/src/runtime/runtime_uefi_schednone.go new file mode 100644 index 0000000000..a0fb9523d5 --- /dev/null +++ b/src/runtime/runtime_uefi_schednone.go @@ -0,0 +1,32 @@ +//go:build uefi && scheduler.none + +package runtime + +//go:noinline +func runProgram() { + runProgramInitRand() + runProgramInitHeap() + runProgramInitAll() + runProgramCallMain() + mainExited = true +} + +//go:noinline +func runProgramInitRand() { + initRand() +} + +//go:noinline +func runProgramInitHeap() { + initHeap() +} + +//go:noinline +func runProgramInitAll() { + initAll() +} + +//go:noinline +func runProgramCallMain() { + callMain() +} diff --git a/src/runtime/scheduler_cooperative.go b/src/runtime/scheduler_cooperative.go index d30ec41962..c5dca0127a 100644 --- a/src/runtime/scheduler_cooperative.go +++ b/src/runtime/scheduler_cooperative.go @@ -244,6 +244,9 @@ func sleep(duration int64) { if duration <= 0 { return } + if schedulerSleepCustom(duration) { + return + } addSleepTask(task.Current(), nanosecondsToTicks(duration)) task.Pause() diff --git a/src/runtime/sleep_custom_default.go b/src/runtime/sleep_custom_default.go new file mode 100644 index 0000000000..a32fe1dce3 --- /dev/null +++ b/src/runtime/sleep_custom_default.go @@ -0,0 +1,7 @@ +//go:build !(scheduler.tasks && uefi) + +package runtime + +func schedulerSleepCustom(duration int64) bool { + return false +} diff --git a/src/runtime/sleep_custom_uefi.go b/src/runtime/sleep_custom_uefi.go new file mode 100644 index 0000000000..784f3b4f28 --- /dev/null +++ b/src/runtime/sleep_custom_uefi.go @@ -0,0 +1,14 @@ +//go:build scheduler.tasks && uefi + +package runtime + +//go:linkname gosched runtime.Gosched +func gosched() + +func schedulerSleepCustom(duration int64) bool { + deadline := ticks() + nanosecondsToTicks(duration) + for ticks() < deadline { + gosched() + } + return true +} diff --git a/src/runtime/wait_other.go b/src/runtime/wait_other.go index ebac63d743..037cb37e71 100644 --- a/src/runtime/wait_other.go +++ b/src/runtime/wait_other.go @@ -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 diff --git a/targets/uefi-amd64.json b/targets/uefi-amd64.json new file mode 100644 index 0000000000..c594aecce6 --- /dev/null +++ b/targets/uefi-amd64.json @@ -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"] +}