From b8a8e70bd55e98802adf1672e7b230fe07c62a1a Mon Sep 17 00:00:00 2001 From: Matthew Hiles Date: Fri, 1 May 2026 14:41:52 -0400 Subject: [PATCH 01/21] runtime: split baremetal memory setup --- src/runtime/baremetal.go | 31 ----------------------------- src/runtime/baremetal_memory.go | 35 +++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 31 deletions(-) create mode 100644 src/runtime/baremetal_memory.go diff --git a/src/runtime/baremetal.go b/src/runtime/baremetal.go index 9915f191b2..179b80ef8c 100644 --- a/src/runtime/baremetal.go +++ b/src/runtime/baremetal.go @@ -4,39 +4,8 @@ package runtime import ( "sync/atomic" - "unsafe" ) -//go:extern _heap_start -var heapStartSymbol [0]byte - -//go:extern _heap_end -var heapEndSymbol [0]byte - -//go:extern _globals_start -var globalsStartSymbol [0]byte - -//go:extern _globals_end -var globalsEndSymbol [0]byte - -//go:extern _stack_top -var stackTopSymbol [0]byte - -var ( - heapStart = uintptr(unsafe.Pointer(&heapStartSymbol)) - heapEnd = uintptr(unsafe.Pointer(&heapEndSymbol)) - globalsStart = uintptr(unsafe.Pointer(&globalsStartSymbol)) - globalsEnd = uintptr(unsafe.Pointer(&globalsEndSymbol)) - stackTop = uintptr(unsafe.Pointer(&stackTopSymbol)) -) - -// growHeap tries to grow the heap size. It returns true if it succeeds, false -// otherwise. -func growHeap() bool { - // On baremetal, there is no way the heap can be grown. - return false -} - //export malloc func libc_malloc(size uintptr) unsafe.Pointer { // Note: this zeroes the returned buffer which is not necessary. diff --git a/src/runtime/baremetal_memory.go b/src/runtime/baremetal_memory.go new file mode 100644 index 0000000000..02f870ad69 --- /dev/null +++ b/src/runtime/baremetal_memory.go @@ -0,0 +1,35 @@ +//go:build baremetal + +package runtime + +import "unsafe" + +//go:extern _heap_start +var heapStartSymbol [0]byte + +//go:extern _heap_end +var heapEndSymbol [0]byte + +//go:extern _globals_start +var globalsStartSymbol [0]byte + +//go:extern _globals_end +var globalsEndSymbol [0]byte + +//go:extern _stack_top +var stackTopSymbol [0]byte + +var ( + heapStart = uintptr(unsafe.Pointer(&heapStartSymbol)) + heapEnd = uintptr(unsafe.Pointer(&heapEndSymbol)) + globalsStart = uintptr(unsafe.Pointer(&globalsStartSymbol)) + globalsEnd = uintptr(unsafe.Pointer(&globalsEndSymbol)) + stackTop = uintptr(unsafe.Pointer(&stackTopSymbol)) +) + +// growHeap tries to grow the heap size. It returns true if it succeeds, false +// otherwise. +func growHeap() bool { + // On baremetal, there is no way the heap can be grown. + return false +} From e906cf7ed02599657b6cf2d2a1d21b5ac70cc4f8 Mon Sep 17 00:00:00 2001 From: Matthew Hiles Date: Fri, 1 May 2026 14:42:06 -0400 Subject: [PATCH 02/21] runtime: extract Windows PE globals scan helper --- src/runtime/os_windows.go | 59 +--------------------------------- src/runtime/os_windows_pe.go | 62 ++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 58 deletions(-) create mode 100644 src/runtime/os_windows_pe.go diff --git a/src/runtime/os_windows.go b/src/runtime/os_windows.go index a124e7ab14..4003a2ab34 100644 --- a/src/runtime/os_windows.go +++ b/src/runtime/os_windows.go @@ -7,44 +7,6 @@ const GOOS = "windows" //export GetModuleHandleExA func _GetModuleHandleExA(dwFlags uint32, lpModuleName unsafe.Pointer, phModule **exeHeader) bool -// MS-DOS stub with PE header offset: -// https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#ms-dos-stub-image-only -type exeHeader struct { - signature uint16 - _ [58]byte // skip DOS header - peHeader uint32 // at offset 0x3C -} - -// COFF file header: -// https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#file-headers -type peHeader struct { - magic uint32 - machine uint16 - numberOfSections uint16 - timeDateStamp uint32 - pointerToSymbolTable uint32 - numberOfSymbols uint32 - sizeOfOptionalHeader uint16 - characteristics uint16 -} - -// COFF section header: -// https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#section-table-section-headers -type peSection struct { - name [8]byte - virtualSize uint32 - virtualAddress uint32 - sizeOfRawData uint32 - pointerToRawData uint32 - pointerToRelocations uint32 - pointerToLinenumbers uint32 - numberOfRelocations uint16 - numberOfLinenumbers uint16 - characteristics uint32 -} - -var module *exeHeader - // Mark global variables. // Unfortunately, the linker doesn't provide symbols for the start and end of // the data/bss sections. Therefore these addresses need to be determined at @@ -57,9 +19,6 @@ func findGlobals(found func(start, end uintptr)) { const ( // https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-getmodulehandleexa GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT = 0x00000002 - - // https://docs.microsoft.com/en-us/windows/win32/debug/pe-format - IMAGE_SCN_MEM_WRITE = 0x80000000 ) if module == nil { @@ -72,23 +31,7 @@ func findGlobals(found func(start, end uintptr)) { } } - // Find the PE header at offset 0x3C. - pe := (*peHeader)(unsafe.Add(unsafe.Pointer(module), module.peHeader)) - if gcAsserts && pe.magic != 0x00004550 { // 0x4550 is "PE" - runtimePanic("cannot find PE header") - } - - // Iterate through sections. - section := (*peSection)(unsafe.Pointer(uintptr(unsafe.Pointer(pe)) + uintptr(pe.sizeOfOptionalHeader) + unsafe.Sizeof(peHeader{}))) - for i := 0; i < int(pe.numberOfSections); i++ { - if section.characteristics&IMAGE_SCN_MEM_WRITE != 0 { - // Found a writable section. Scan the entire section for roots. - start := uintptr(unsafe.Pointer(module)) + uintptr(section.virtualAddress) - end := uintptr(unsafe.Pointer(module)) + uintptr(section.virtualAddress) + uintptr(section.virtualSize) - found(start, end) - } - section = (*peSection)(unsafe.Add(unsafe.Pointer(section), unsafe.Sizeof(peSection{}))) - } + findGlobalsForPE(found) } type systeminfo struct { diff --git a/src/runtime/os_windows_pe.go b/src/runtime/os_windows_pe.go new file mode 100644 index 0000000000..bd27af5751 --- /dev/null +++ b/src/runtime/os_windows_pe.go @@ -0,0 +1,62 @@ +//go:build windows + +package runtime + +import "unsafe" + +// MS-DOS stub with PE header offset: +// https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#ms-dos-stub-image-only +type exeHeader struct { + signature uint16 + _ [58]byte // skip DOS header + peHeader uint32 // at offset 0x3C +} + +// COFF file header: +// https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#file-headers +type peHeader struct { + magic uint32 + machine uint16 + numberOfSections uint16 + timeDateStamp uint32 + pointerToSymbolTable uint32 + numberOfSymbols uint32 + sizeOfOptionalHeader uint16 + characteristics uint16 +} + +// COFF section header: +// https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#section-table-section-headers +type peSection struct { + name [8]byte + virtualSize uint32 + virtualAddress uint32 + sizeOfRawData uint32 + pointerToRawData uint32 + pointerToRelocations uint32 + pointerToLinenumbers uint32 + numberOfRelocations uint16 + numberOfLinenumbers uint16 + characteristics uint32 +} + +var module *exeHeader + +func findGlobalsForPE(found func(start, end uintptr)) { + const imageSCNMemWrite = 0x80000000 + + pe := (*peHeader)(unsafe.Add(unsafe.Pointer(module), module.peHeader)) + if gcAsserts && pe.magic != 0x00004550 { // 0x4550 is "PE" + runtimePanic("cannot find PE header") + } + + section := (*peSection)(unsafe.Pointer(uintptr(unsafe.Pointer(pe)) + uintptr(pe.sizeOfOptionalHeader) + unsafe.Sizeof(peHeader{}))) + for i := 0; i < int(pe.numberOfSections); i++ { + if section.characteristics&imageSCNMemWrite != 0 { + start := uintptr(unsafe.Pointer(module)) + uintptr(section.virtualAddress) + end := uintptr(unsafe.Pointer(module)) + uintptr(section.virtualAddress) + uintptr(section.virtualSize) + found(start, end) + } + section = (*peSection)(unsafe.Add(unsafe.Pointer(section), unsafe.Sizeof(peSection{}))) + } +} From 36d6c7cc028dc79b1a6c08ba98c9ce593e94d22b Mon Sep 17 00:00:00 2001 From: Matthew Hiles Date: Fri, 1 May 2026 15:32:15 -0400 Subject: [PATCH 03/21] compileopts,builder: add target linker flavor override --- builder/build.go | 11 +++++---- compileopts/config.go | 16 +++++++++++++ compileopts/target.go | 3 ++- compileopts/target_test.go | 48 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 73 insertions(+), 5 deletions(-) diff --git a/builder/build.go b/builder/build.go index 325f6b4906..b8037e8c47 100644 --- a/builder/build.go +++ b/builder/build.go @@ -842,22 +842,25 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe } ldflags = append(ldflags, "-mllvm", "-mcpu="+config.CPU()) ldflags = append(ldflags, "-mllvm", "-mattr="+config.Features()) // needed for MIPS softfloat - if config.GOOS() == "windows" { - // Options for the MinGW wrapper for the lld COFF linker. + switch config.LinkerFlavor() { + case "coff": + // Options for driving ld.lld in PE/COFF mode. ldflags = append(ldflags, "-Xlink=/opt:lldlto="+strconv.Itoa(speedLevel), "--thinlto-cache-dir="+filepath.Join(cacheDir, "thinlto")) - } else if config.GOOS() == "darwin" { + case "darwin": // Options for the ld64-compatible lld linker. ldflags = append(ldflags, "--lto-O"+strconv.Itoa(speedLevel), "-cache_path_lto", filepath.Join(cacheDir, "thinlto")) - } else { + case "gnu": // Options for the ELF linker. ldflags = append(ldflags, "--lto-O"+strconv.Itoa(speedLevel), "--thinlto-cache-dir="+filepath.Join(cacheDir, "thinlto"), ) + default: + return fmt.Errorf("unknown linker flavor: %s", config.LinkerFlavor()) } if config.CodeModel() != "default" { ldflags = append(ldflags, diff --git a/compileopts/config.go b/compileopts/config.go index 1920d2b9cb..99fb8cd8d8 100644 --- a/compileopts/config.go +++ b/compileopts/config.go @@ -466,6 +466,22 @@ func (c *Config) LDFlags() []string { return ldflags } +// LinkerFlavor returns how the configured linker should be driven. +// Usually this is derived from GOOS, but targets may override it explicitly. +func (c *Config) LinkerFlavor() string { + if c.Target.LinkerFlavor != "" { + return c.Target.LinkerFlavor + } + switch c.GOOS() { + case "windows": + return "coff" + case "darwin": + return "darwin" + default: + return "gnu" + } +} + // ExtraFiles returns the list of extra files to be built and linked with the // executable. This can include extra C and assembly files. func (c *Config) ExtraFiles() []string { diff --git a/compileopts/target.go b/compileopts/target.go index 4e0269e6b0..3c09f7df93 100644 --- a/compileopts/target.go +++ b/compileopts/target.go @@ -38,7 +38,8 @@ type TargetSpec struct { Scheduler string `json:"scheduler,omitempty"` Serial string `json:"serial,omitempty"` // which serial output to use (uart, usb, none) Linker string `json:"linker,omitempty"` - RTLib string `json:"rtlib,omitempty"` // compiler runtime library (libgcc, compiler-rt) + LinkerFlavor string `json:"linker-flavor,omitempty"` // how to drive the configured linker (for example: gnu, coff, darwin) + RTLib string `json:"rtlib,omitempty"` // compiler runtime library (libgcc, compiler-rt) Libc string `json:"libc,omitempty"` AutoStackSize *bool `json:"automatic-stack-size,omitempty"` // Determine stack size automatically at compile time. DefaultStackSize uint64 `json:"default-stack-size,omitempty"` // Default stack size if the size couldn't be determined at compile time. diff --git a/compileopts/target_test.go b/compileopts/target_test.go index d8a17a5e34..c700418839 100644 --- a/compileopts/target_test.go +++ b/compileopts/target_test.go @@ -112,3 +112,51 @@ func TestOverrideProperties(t *testing.T) { } } + +func TestConfigLinkerFlavor(t *testing.T) { + tests := []struct { + name string + target *TargetSpec + goos string + want string + }{ + { + name: "default gnu", + target: &TargetSpec{}, + goos: "linux", + want: "gnu", + }, + { + name: "default coff", + target: &TargetSpec{}, + goos: "windows", + want: "coff", + }, + { + name: "default darwin", + target: &TargetSpec{}, + goos: "darwin", + want: "darwin", + }, + { + name: "target override", + target: &TargetSpec{ + LinkerFlavor: "coff", + }, + goos: "linux", + want: "coff", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + config := &Config{ + Options: &Options{GOOS: tc.goos}, + Target: tc.target, + } + if got := config.LinkerFlavor(); got != tc.want { + t.Fatalf("LinkerFlavor() = %q, want %q", got, tc.want) + } + }) + } +} From 2ad100488573fb83bd66505009b52bff3527b01f Mon Sep 17 00:00:00 2001 From: Matthew Hiles Date: Fri, 1 May 2026 15:49:30 -0400 Subject: [PATCH 04/21] machine,runtime,targets: add minimal uefi-amd64 target --- builder/builder_test.go | 1 + src/device/x86/cpu.go | 74 ++++++++++++++++ src/device/x86/cpu_amd64.S | 43 ++++++++++ src/machine/uefi/arch_x86.go | 17 ++++ src/machine/uefi/asm_amd64.S | 86 +++++++++++++++++++ src/machine/uefi/call.go | 25 ++++++ src/machine/uefi/clock.go | 39 +++++++++ src/machine/uefi/efidef.go | 59 +++++++++++++ src/machine/uefi/efistatus.go | 15 ++++ src/machine/uefi/runtime.go | 23 +++++ src/machine/uefi/simple_text_out.go | 29 +++++++ src/machine/uefi/tables.go | 103 +++++++++++++++++++++++ src/runtime/baremetal.go | 1 + src/runtime/baremetal_memory.go | 2 +- src/runtime/gc_globals.go | 2 +- src/runtime/gc_leaking_uefi.go | 9 ++ src/runtime/interrupt/interrupt_none.go | 2 +- src/runtime/runtime_uefi.go | 107 ++++++++++++++++++++++++ targets/uefi-amd64.json | 41 +++++++++ 19 files changed, 675 insertions(+), 3 deletions(-) create mode 100644 src/device/x86/cpu.go create mode 100644 src/device/x86/cpu_amd64.S create mode 100644 src/machine/uefi/arch_x86.go create mode 100644 src/machine/uefi/asm_amd64.S create mode 100644 src/machine/uefi/call.go create mode 100644 src/machine/uefi/clock.go create mode 100644 src/machine/uefi/efidef.go create mode 100644 src/machine/uefi/efistatus.go create mode 100644 src/machine/uefi/runtime.go create mode 100644 src/machine/uefi/simple_text_out.go create mode 100644 src/machine/uefi/tables.go create mode 100644 src/runtime/gc_leaking_uefi.go create mode 100644 src/runtime/runtime_uefi.go create mode 100644 targets/uefi-amd64.json diff --git a/builder/builder_test.go b/builder/builder_test.go index 8e62d9183c..941ed3251a 100644 --- a/builder/builder_test.go +++ b/builder/builder_test.go @@ -35,6 +35,7 @@ func TestClangAttributes(t *testing.T) { "nintendoswitch", "riscv-qemu", "tkey", + "uefi-amd64", "wasip1", "wasip2", "wasm", diff --git a/src/device/x86/cpu.go b/src/device/x86/cpu.go new file mode 100644 index 0000000000..32ccb7f2eb --- /dev/null +++ b/src/device/x86/cpu.go @@ -0,0 +1,74 @@ +//go:build amd64 + +package x86 + +const ( + CPUIDTimeStampCounter = 0x15 + CPUIDProcessorFrequency = 0x16 +) + +type CPUExtendedFamily uint16 + +const ( + CPUFamilyIntelCore CPUExtendedFamily = 6 +) + +//export asmPause +func AsmPause() + +//export asmReadRdtsc +func AsmReadRdtsc() uint64 + +//export asmCpuid +func AsmCpuid(index uint32, registerEax *uint32, registerEbx *uint32, registerEcx *uint32, registerEdx *uint32) int + +var maxCpuidIndex uint32 +var stdVendorName0 uint32 +var stdCpuid1Eax uint32 + +func init() { + AsmCpuid(0, &maxCpuidIndex, &stdVendorName0, nil, nil) + AsmCpuid(1, &stdCpuid1Eax, nil, nil, nil) +} + +func getExtendedCPUFamily() CPUExtendedFamily { + family := CPUExtendedFamily((stdCpuid1Eax >> 8) & 0x0f) + family += CPUExtendedFamily((stdCpuid1Eax >> 20) & 0xff) + return family +} + +func isIntel() bool { + return stdVendorName0 == 0x756e6547 +} + +func isIntelFamilyCore() bool { + return isIntel() && getExtendedCPUFamily() == CPUFamilyIntelCore +} + +func InternalGetPerformanceCounterFrequency() uint64 { + if maxCpuidIndex >= CPUIDTimeStampCounter { + return cpuidCoreClockCalculateTSCFrequency() + } + return 0 +} + +func cpuidCoreClockCalculateTSCFrequency() uint64 { + var eax uint32 + var ebx uint32 + var ecx uint32 + + AsmCpuid(CPUIDTimeStampCounter, &eax, &ebx, &ecx, nil) + if eax == 0 || ebx == 0 { + return 0 + } + + coreCrystalFrequency := uint64(ecx) + if coreCrystalFrequency == 0 { + if !isIntelFamilyCore() { + return 0 + } + coreCrystalFrequency = 24000000 + } + + return ((coreCrystalFrequency * uint64(ebx)) + (uint64(eax) / 2)) / uint64(eax) +} diff --git a/src/device/x86/cpu_amd64.S b/src/device/x86/cpu_amd64.S new file mode 100644 index 0000000000..5e924f1cc3 --- /dev/null +++ b/src/device/x86/cpu_amd64.S @@ -0,0 +1,43 @@ +.section .text + +.global asmPause +asmPause: + pause + ret + +.global asmReadRdtsc +asmReadRdtsc: + rdtsc + shlq $0x20, %rdx + orq %rdx, %rax + ret + +.global asmCpuid +asmCpuid: + pushq %rbx + + mov %ecx, %eax + pushq %rax + + pushq %rdx + cpuid + + test %r9, %r9 + jz .SkipEcx + mov %ecx, (%r9) +.SkipEcx: + popq %rcx + jrcxz .SkipEax + mov %eax, (%rcx) +.SkipEax: + mov %r8, %rcx + jrcxz .SkipEbx + mov %ebx, (%rcx) +.SkipEbx: + mov 0x38(%rsp), %rcx + jrcxz .SkipEdx + mov %edx, (%rcx) +.SkipEdx: + popq %rax + popq %rbx + ret diff --git a/src/machine/uefi/arch_x86.go b/src/machine/uefi/arch_x86.go new file mode 100644 index 0000000000..a2dbc7685e --- /dev/null +++ b/src/machine/uefi/arch_x86.go @@ -0,0 +1,17 @@ +//go:build i386 || amd64 + +package uefi + +import "device/x86" + +func Ticks() uint64 { + return x86.AsmReadRdtsc() +} + +func CpuPause() { + x86.AsmPause() +} + +func getTSCFrequency() uint64 { + return x86.InternalGetPerformanceCounterFrequency() +} diff --git a/src/machine/uefi/asm_amd64.S b/src/machine/uefi/asm_amd64.S new file mode 100644 index 0000000000..015f7353db --- /dev/null +++ b/src/machine/uefi/asm_amd64.S @@ -0,0 +1,86 @@ +.section .text + +.global uefiCall0 +uefiCall0: + pushq %rbp + movq %rsp, %rbp + subq $0x20, %rsp + callq *%rcx + movq %rbp, %rsp + popq %rbp + ret + +.global uefiCall1 +uefiCall1: + pushq %rbp + movq %rsp, %rbp + subq $0x20, %rsp + movq %rcx, %rax + movq %rdx, %rcx + callq *%rax + movq %rbp, %rsp + popq %rbp + ret + +.global uefiCall2 +uefiCall2: + pushq %rbp + movq %rsp, %rbp + subq $0x20, %rsp + movq %rcx, %rax + movq %rdx, %rcx + movq %r8, %rdx + callq *%rax + movq %rbp, %rsp + popq %rbp + ret + +.global uefiCall3 +uefiCall3: + pushq %rbp + movq %rsp, %rbp + subq $0x20, %rsp + movq %rcx, %rax + 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 + subq $0x20, %rsp + movq %rcx, %rax + movq %rdx, %rcx + movq %r8, %rdx + movq %r9, %r8 + movq 0x30(%rbp), %r9 + callq *%rax + movq %rbp, %rsp + popq %rbp + ret + +.global uefiCall5 +uefiCall5: + pushq %rbp + movq %rsp, %rbp + subq $0x28, %rsp + movq %rcx, %rax + movq %rdx, %rcx + movq %r8, %rdx + movq %r9, %r8 + movq 0x30(%rbp), %r9 + movq 0x38(%rbp), %r10 + movq %r10, 0x20(%rsp) + callq *%rax + movq %rbp, %rsp + popq %rbp + ret + +.global ___chkstk_ms +___chkstk_ms: + ret diff --git a/src/machine/uefi/call.go b/src/machine/uefi/call.go new file mode 100644 index 0000000000..82b4b7f955 --- /dev/null +++ b/src/machine/uefi/call.go @@ -0,0 +1,25 @@ +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 diff --git a/src/machine/uefi/clock.go b/src/machine/uefi/clock.go new file mode 100644 index 0000000000..cae519eea0 --- /dev/null +++ b/src/machine/uefi/clock.go @@ -0,0 +1,39 @@ +//go:build uefi + +package uefi + +import "sync" + +var calibrateMutex sync.Mutex +var calculatedFrequency uint64 + +func TicksFrequency() uint64 { + frequency := getTSCFrequency() + if frequency > 0 { + return frequency + } + + calibrateMutex.Lock() + defer calibrateMutex.Unlock() + if calculatedFrequency > 0 { + return calculatedFrequency + } + + var event EFI_EVENT + var index UINTN + if BS().CreateEvent(EVT_TIMER, TPL_CALLBACK, nil, nil, &event) != EFI_SUCCESS { + return 0 + } + defer BS().CloseEvent(event) + + start := Ticks() + if BS().SetTimer(event, TimerPeriodic, 250*10000) != EFI_SUCCESS { + return 0 + } + if BS().WaitForEvent(1, &event, &index) != EFI_SUCCESS { + return 0 + } + + calculatedFrequency = (Ticks() - start) * 4 + return calculatedFrequency +} diff --git a/src/machine/uefi/efidef.go b/src/machine/uefi/efidef.go new file mode 100644 index 0000000000..13124a69c6 --- /dev/null +++ b/src/machine/uefi/efidef.go @@ -0,0 +1,59 @@ +package uefi + +type UINTN uintptr +type EFI_STATUS UINTN +type EFI_TPL UINTN +type EFI_HANDLE uintptr +type EFI_EVENT uintptr +type EFI_PHYSICAL_ADDRESS uint64 + +type CHAR16 uint16 +type BOOLEAN bool +type VOID byte + +type EFI_TABLE_HEADER struct { + Signature uint64 + Revision uint32 + HeaderSize uint32 + CRC32 uint32 + Reserved uint32 +} + +type EFI_ALLOCATE_TYPE int + +const ( + AllocateAnyPages EFI_ALLOCATE_TYPE = iota + AllocateMaxAddress + AllocateAddress +) + +type EFI_MEMORY_TYPE int + +const ( + EfiReservedMemoryType EFI_MEMORY_TYPE = iota + EfiLoaderCode + EfiLoaderData + EfiBootServicesCode + EfiBootServicesData + EfiRuntimeServicesCode + EfiRuntimeServicesData + EfiConventionalMemory +) + +type EVENT_TYPE uint32 + +const ( + EVT_TIMER EVENT_TYPE = 0x80000000 +) + +const ( + TPL_CALLBACK EFI_TPL = 8 +) + +type EFI_TIMER_DELAY int + +const ( + TimerCancel EFI_TIMER_DELAY = iota + TimerPeriodic + TimerRelative +) diff --git a/src/machine/uefi/efistatus.go b/src/machine/uefi/efistatus.go new file mode 100644 index 0000000000..ed7c086c6e --- /dev/null +++ b/src/machine/uefi/efistatus.go @@ -0,0 +1,15 @@ +package uefi + +const ( + uintnSize = 32 << (^uintptr(0) >> 63) + errorMask = 1 << uintptr(uintnSize-1) +) + +const ( + EFI_SUCCESS EFI_STATUS = 0 + EFI_INVALID_PARAMETER EFI_STATUS = errorMask | 2 + EFI_UNSUPPORTED EFI_STATUS = errorMask | 3 + EFI_NOT_READY EFI_STATUS = errorMask | 6 + EFI_OUT_OF_RESOURCES EFI_STATUS = errorMask | 9 + EFI_ABORTED EFI_STATUS = errorMask | 21 +) diff --git a/src/machine/uefi/runtime.go b/src/machine/uefi/runtime.go new file mode 100644 index 0000000000..118bc6881e --- /dev/null +++ b/src/machine/uefi/runtime.go @@ -0,0 +1,23 @@ +package uefi + +import "unsafe" + +var imageHandle EFI_HANDLE +var systemTable *EFI_SYSTEM_TABLE + +func Init(handle uintptr, table uintptr) { + imageHandle = EFI_HANDLE(handle) + systemTable = (*EFI_SYSTEM_TABLE)(unsafe.Pointer(table)) +} + +func ST() *EFI_SYSTEM_TABLE { + return systemTable +} + +func BS() *EFI_BOOT_SERVICES { + return systemTable.BootServices +} + +func GetImageHandle() EFI_HANDLE { + return imageHandle +} diff --git a/src/machine/uefi/simple_text_out.go b/src/machine/uefi/simple_text_out.go new file mode 100644 index 0000000000..3da17f6d7e --- /dev/null +++ b/src/machine/uefi/simple_text_out.go @@ -0,0 +1,29 @@ +package uefi + +import "unsafe" + +type EFI_SIMPLE_TEXT_OUTPUT_MODE struct { + MaxMode int32 + Mode int32 + Attribute int32 + CursorColumn int32 + CursorRow int32 + CursorVisible BOOLEAN +} + +type EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL struct { + reset uintptr + outputString uintptr + testString uintptr + queryMode uintptr + setMode uintptr + setAttribute uintptr + clearScreen uintptr + setCursorPosition uintptr + enableCursor uintptr + Mode *EFI_SIMPLE_TEXT_OUTPUT_MODE +} + +func (p *EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL) OutputString(s *CHAR16) EFI_STATUS { + return UefiCall2(p.outputString, uintptr(unsafe.Pointer(p)), uintptr(unsafe.Pointer(s))) +} diff --git a/src/machine/uefi/tables.go b/src/machine/uefi/tables.go new file mode 100644 index 0000000000..9f169ba947 --- /dev/null +++ b/src/machine/uefi/tables.go @@ -0,0 +1,103 @@ +package uefi + +import "unsafe" + +type EFI_RUNTIME_SERVICES struct { + Hdr EFI_TABLE_HEADER + getTime uintptr + setTime uintptr + getWakeupTime uintptr + setWakeupTime uintptr + setVirtualAddressMap uintptr + convertPointer uintptr + getVariable uintptr + getNextVariableName uintptr + setVariable uintptr + getNextHighMonoCount uintptr + resetSystem uintptr + updateCapsule uintptr + queryCapsuleCaps uintptr + queryVariableInfo uintptr +} + +type EFI_BOOT_SERVICES struct { + Hdr EFI_TABLE_HEADER + raiseTPL uintptr + restoreTPL uintptr + allocatePages uintptr + freePages uintptr + getMemoryMap uintptr + allocatePool uintptr + freePool uintptr + createEvent uintptr + setTimer uintptr + waitForEvent uintptr + signalEvent uintptr + closeEvent uintptr + checkEvent uintptr + installProtocolInterface uintptr + reinstallProtocolIFace uintptr + uninstallProtocolIFace uintptr + handleProtocol uintptr + reserved *VOID + registerProtocolNotify uintptr + locateHandle uintptr + locateDevicePath uintptr + installConfigurationTable uintptr + loadImage uintptr + startImage uintptr + exit uintptr + unloadImage uintptr + exitBootServices uintptr + getNextMonotonicCount uintptr + stall uintptr + setWatchdogTimer uintptr +} + +func (p *EFI_BOOT_SERVICES) AllocatePages(typ EFI_ALLOCATE_TYPE, memoryType EFI_MEMORY_TYPE, pages UINTN, memory *EFI_PHYSICAL_ADDRESS) EFI_STATUS { + return UefiCall4(p.allocatePages, uintptr(typ), uintptr(memoryType), uintptr(pages), uintptr(unsafe.Pointer(memory))) +} + +func (p *EFI_BOOT_SERVICES) CreateEvent(typ EVENT_TYPE, notifyTPL EFI_TPL, notifyFunction unsafe.Pointer, notifyContext unsafe.Pointer, event *EFI_EVENT) EFI_STATUS { + return UefiCall5(p.createEvent, uintptr(typ), uintptr(notifyTPL), uintptr(notifyFunction), uintptr(notifyContext), uintptr(unsafe.Pointer(event))) +} + +func (p *EFI_BOOT_SERVICES) SetTimer(event EFI_EVENT, typ EFI_TIMER_DELAY, triggerTime uint64) EFI_STATUS { + return UefiCall3(p.setTimer, uintptr(event), uintptr(typ), uintptr(triggerTime)) +} + +func (p *EFI_BOOT_SERVICES) WaitForEvent(numberOfEvents UINTN, event *EFI_EVENT, index *UINTN) EFI_STATUS { + return UefiCall3(p.waitForEvent, uintptr(numberOfEvents), uintptr(unsafe.Pointer(event)), uintptr(unsafe.Pointer(index))) +} + +func (p *EFI_BOOT_SERVICES) CloseEvent(event EFI_EVENT) EFI_STATUS { + return UefiCall1(p.closeEvent, uintptr(event)) +} + +func (p *EFI_BOOT_SERVICES) CheckEvent(event EFI_EVENT) EFI_STATUS { + return UefiCall1(p.checkEvent, uintptr(event)) +} + +func (p *EFI_BOOT_SERVICES) Exit(imageHandle EFI_HANDLE, exitStatus EFI_STATUS, exitDataSize UINTN, exitData *CHAR16) EFI_STATUS { + return UefiCall4(p.exit, uintptr(imageHandle), uintptr(exitStatus), uintptr(exitDataSize), uintptr(unsafe.Pointer(exitData))) +} + +func (p *EFI_BOOT_SERVICES) SetWatchdogTimer(timeout UINTN, watchdogCode uint64, dataSize UINTN, watchdogData *CHAR16) EFI_STATUS { + return UefiCall4(p.setWatchdogTimer, uintptr(timeout), uintptr(watchdogCode), uintptr(dataSize), uintptr(unsafe.Pointer(watchdogData))) +} + +type EFI_SYSTEM_TABLE struct { + Hdr EFI_TABLE_HEADER + FirmwareVendor *CHAR16 + FirmwareRevision uint32 + ConsoleInHandle EFI_HANDLE + ConIn *VOID + ConsoleOutHandle EFI_HANDLE + ConOut *EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL + StandardErrorHandle EFI_HANDLE + StdErr *EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL + RuntimeServices *EFI_RUNTIME_SERVICES + BootServices *EFI_BOOT_SERVICES + NumberOfTableEntries UINTN + ConfigurationTable *VOID +} diff --git a/src/runtime/baremetal.go b/src/runtime/baremetal.go index 179b80ef8c..6dd29e490d 100644 --- a/src/runtime/baremetal.go +++ b/src/runtime/baremetal.go @@ -4,6 +4,7 @@ package runtime import ( "sync/atomic" + "unsafe" ) //export malloc 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/runtime_uefi.go b/src/runtime/runtime_uefi.go new file mode 100644 index 0000000000..3f8e855dc8 --- /dev/null +++ b/src/runtime/runtime_uefi.go @@ -0,0 +1,107 @@ +//go:build uefi + +package runtime + +import "machine/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 + +func ticks() timeUnit { + return timeUnit(uefi.Ticks()) +} + +func nanosecondsToTicks(ns int64) timeUnit { + frequency := int64(uefi.TicksFrequency()) + if frequency == 0 { + return timeUnit(ns) + } + return timeUnit(ns * 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 +} + +//go:noinline +func runMain() { + run() +} + +//export efi_main +func main(imageHandle uintptr, systemTable uintptr) uintptr { + uefi.Init(imageHandle, systemTable) + preinit() + stackTop = getCurrentStackPointer() + runMain() + uefi.BS().Exit(uefi.GetImageHandle(), 0, 0, nil) + return 0 +} diff --git a/targets/uefi-amd64.json b/targets/uefi-amd64.json new file mode 100644 index 0000000000..3ec6f1235d --- /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": "+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/x86/cpu_amd64.S", + "src/machine/uefi/asm_amd64.S", + "src/runtime/asm_amd64_windows.S" + ], + "gdb": ["gdb-multiarch", "gdb"] +} From 0d905c1bd35774413bc61119669baf5ba79437d2 Mon Sep 17 00:00:00 2001 From: Matthew Hiles Date: Fri, 1 May 2026 16:57:28 -0400 Subject: [PATCH 05/21] runtime,examples: add clean UEFI exit path test --- examples/uefi-exit/main.go | 4 ++++ src/machine/uefi/tables.go | 4 ++++ src/runtime/runtime_uefi.go | 6 +++++- 3 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 examples/uefi-exit/main.go diff --git a/examples/uefi-exit/main.go b/examples/uefi-exit/main.go new file mode 100644 index 0000000000..da29a2cadf --- /dev/null +++ b/examples/uefi-exit/main.go @@ -0,0 +1,4 @@ +package main + +func main() { +} diff --git a/src/machine/uefi/tables.go b/src/machine/uefi/tables.go index 9f169ba947..14487ed829 100644 --- a/src/machine/uefi/tables.go +++ b/src/machine/uefi/tables.go @@ -58,6 +58,10 @@ func (p *EFI_BOOT_SERVICES) AllocatePages(typ EFI_ALLOCATE_TYPE, memoryType EFI_ return UefiCall4(p.allocatePages, uintptr(typ), uintptr(memoryType), uintptr(pages), uintptr(unsafe.Pointer(memory))) } +func (p *EFI_BOOT_SERVICES) FreePages(memory EFI_PHYSICAL_ADDRESS, pages UINTN) EFI_STATUS { + return UefiCall2(p.freePages, uintptr(memory), uintptr(pages)) +} + func (p *EFI_BOOT_SERVICES) CreateEvent(typ EVENT_TYPE, notifyTPL EFI_TPL, notifyFunction unsafe.Pointer, notifyContext unsafe.Pointer, event *EFI_EVENT) EFI_STATUS { return UefiCall5(p.createEvent, uintptr(typ), uintptr(notifyTPL), uintptr(notifyFunction), uintptr(notifyContext), uintptr(unsafe.Pointer(event))) } diff --git a/src/runtime/runtime_uefi.go b/src/runtime/runtime_uefi.go index 3f8e855dc8..fa72d42a29 100644 --- a/src/runtime/runtime_uefi.go +++ b/src/runtime/runtime_uefi.go @@ -102,6 +102,10 @@ func main(imageHandle uintptr, systemTable uintptr) uintptr { preinit() stackTop = getCurrentStackPointer() runMain() - uefi.BS().Exit(uefi.GetImageHandle(), 0, 0, nil) + + if heapStart != 0 { + uefi.BS().FreePages(uefi.EFI_PHYSICAL_ADDRESS(heapStart), uefi.UINTN(heapSize/4096)) + } + return 0 } From 6ad501dbc8351e3379ddcdccb7d8648f5f3066f2 Mon Sep 17 00:00:00 2001 From: Matthew Hiles Date: Fri, 1 May 2026 17:15:44 -0400 Subject: [PATCH 06/21] machine,x86: fix amd64 ABI stack handling --- src/device/x86/cpu_amd64.S | 2 +- src/machine/uefi/asm_amd64.S | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/device/x86/cpu_amd64.S b/src/device/x86/cpu_amd64.S index 5e924f1cc3..655e53a8b3 100644 --- a/src/device/x86/cpu_amd64.S +++ b/src/device/x86/cpu_amd64.S @@ -34,7 +34,7 @@ asmCpuid: jrcxz .SkipEbx mov %ebx, (%rcx) .SkipEbx: - mov 0x38(%rsp), %rcx + mov 0x40(%rsp), %rcx jrcxz .SkipEdx mov %edx, (%rcx) .SkipEdx: diff --git a/src/machine/uefi/asm_amd64.S b/src/machine/uefi/asm_amd64.S index 015f7353db..e8328a367a 100644 --- a/src/machine/uefi/asm_amd64.S +++ b/src/machine/uefi/asm_amd64.S @@ -68,7 +68,7 @@ uefiCall4: uefiCall5: pushq %rbp movq %rsp, %rbp - subq $0x28, %rsp + subq $0x30, %rsp movq %rcx, %rax movq %rdx, %rcx movq %r8, %rdx From 2929a18590f0dc717ef875046de59c37383e6095 Mon Sep 17 00:00:00 2001 From: Matthew Hiles Date: Sat, 2 May 2026 09:47:53 -0400 Subject: [PATCH 07/21] machine/uefi: add CHAR16 conversion helpers --- src/machine/uefi/char16.go | 77 +++++++++++++++++++++++++++++++++ src/machine/uefi/char16_test.go | 60 +++++++++++++++++++++++++ 2 files changed, 137 insertions(+) create mode 100644 src/machine/uefi/char16.go create mode 100644 src/machine/uefi/char16_test.go diff --git a/src/machine/uefi/char16.go b/src/machine/uefi/char16.go new file mode 100644 index 0000000000..3c3818c7c1 --- /dev/null +++ b/src/machine/uefi/char16.go @@ -0,0 +1,77 @@ +package uefi + +import ( + "unicode/utf16" + "unsafe" +) + +// StringToCHAR16 converts a Go string to a UTF-16 code unit slice. +func StringToCHAR16(s string) []CHAR16 { + if s == "" { + return nil + } + + encoded := utf16.Encode([]rune(s)) + out := make([]CHAR16, len(encoded)) + for i, r := range encoded { + out[i] = CHAR16(r) + } + return out +} + +// StringToCHAR16Z converts a Go string to a NUL-terminated UTF-16 code unit slice. +func StringToCHAR16Z(s string) []CHAR16 { + out := StringToCHAR16(s) + return append(out, 0) +} + +// BytesToCHAR16 converts UTF-8 text bytes to a UTF-16 code unit slice. +func BytesToCHAR16(b []byte) []CHAR16 { + return StringToCHAR16(string(b)) +} + +// BytesToCHAR16Z converts UTF-8 text bytes to a NUL-terminated UTF-16 code unit slice. +func BytesToCHAR16Z(b []byte) []CHAR16 { + return StringToCHAR16Z(string(b)) +} + +// CHAR16ToString converts a UTF-16 code unit slice to a Go string. +func CHAR16ToString(input []CHAR16) string { + if len(input) == 0 { + return "" + } + + units := make([]uint16, len(input)) + for i, c := range input { + units[i] = uint16(c) + } + return string(utf16.Decode(units)) +} + +// CHAR16ToBytes converts a UTF-16 code unit slice to UTF-8 text bytes. +func CHAR16ToBytes(input []CHAR16) []byte { + return []byte(CHAR16ToString(input)) +} + +// CHAR16PtrToString converts a NUL-terminated UTF-16 string pointer to a Go string. +func CHAR16PtrToString(input *CHAR16) string { + if input == nil { + return "" + } + + ptr := uintptr(unsafe.Pointer(input)) + length := 0 + for *(*CHAR16)(unsafe.Pointer(ptr)) != 0 { + length++ + ptr += 2 + } + return CHAR16PtrLenToString(input, length) +} + +// CHAR16PtrLenToString converts a UTF-16 string pointer with a known code unit count to a Go string. +func CHAR16PtrLenToString(input *CHAR16, length int) string { + if input == nil || length <= 0 { + return "" + } + return CHAR16ToString(unsafe.Slice(input, length)) +} diff --git a/src/machine/uefi/char16_test.go b/src/machine/uefi/char16_test.go new file mode 100644 index 0000000000..e0800cc057 --- /dev/null +++ b/src/machine/uefi/char16_test.go @@ -0,0 +1,60 @@ +package uefi + +import "testing" + +func TestStringToCHAR16RoundTrip(t *testing.T) { + input := "Hello, δΈ–η•Œ" + encoded := StringToCHAR16(input) + + if got := CHAR16ToString(encoded); got != input { + t.Fatalf("round trip mismatch: got %q want %q", got, input) + } +} + +func TestStringToCHAR16UsesSurrogatePairs(t *testing.T) { + input := "πŸ™‚" + encoded := StringToCHAR16(input) + want := []CHAR16{0xD83D, 0xDE42} + + if len(encoded) != len(want) { + t.Fatalf("encoded length mismatch: got %d want %d", len(encoded), len(want)) + } + for i := range want { + if encoded[i] != want[i] { + t.Fatalf("encoded[%d] mismatch: got %#x want %#x", i, encoded[i], want[i]) + } + } +} + +func TestStringToCHAR16ZTerminates(t *testing.T) { + encoded := StringToCHAR16Z("abc") + + if len(encoded) != 4 { + t.Fatalf("terminated length mismatch: got %d want 4", len(encoded)) + } + if encoded[len(encoded)-1] != 0 { + t.Fatalf("missing NUL terminator: got %#x", encoded[len(encoded)-1]) + } + if got := CHAR16PtrToString(&encoded[0]); got != "abc" { + t.Fatalf("pointer round trip mismatch: got %q want %q", got, "abc") + } +} + +func TestBytesToCHAR16RoundTrip(t *testing.T) { + input := []byte("UEFI Β΅") + encoded := BytesToCHAR16(input) + + got := CHAR16ToBytes(encoded) + if string(got) != string(input) { + t.Fatalf("byte round trip mismatch: got %q want %q", got, input) + } +} + +func TestCHAR16PtrHelpersHandleNil(t *testing.T) { + if got := CHAR16PtrToString(nil); got != "" { + t.Fatalf("CHAR16PtrToString(nil) = %q, want empty string", got) + } + if got := CHAR16PtrLenToString(nil, 3); got != "" { + t.Fatalf("CHAR16PtrLenToString(nil, 3) = %q, want empty string", got) + } +} From da76f637b2d79febf5644b2b7d163f2df902f222 Mon Sep 17 00:00:00 2001 From: Matthew Hiles Date: Sat, 2 May 2026 10:59:23 -0400 Subject: [PATCH 08/21] machine/uefi: add loaded image protocol support --- src/machine/uefi/device_path.go | 7 ++++++ src/machine/uefi/efidef.go | 7 ++++++ src/machine/uefi/loaded_image.go | 37 ++++++++++++++++++++++++++++++++ src/machine/uefi/tables.go | 4 ++++ 4 files changed, 55 insertions(+) create mode 100644 src/machine/uefi/device_path.go create mode 100644 src/machine/uefi/loaded_image.go diff --git a/src/machine/uefi/device_path.go b/src/machine/uefi/device_path.go new file mode 100644 index 0000000000..f66c28fda9 --- /dev/null +++ b/src/machine/uefi/device_path.go @@ -0,0 +1,7 @@ +package uefi + +type EFI_DEVICE_PATH_PROTOCOL struct { + Type uint8 + SubType uint8 + Length [2]uint8 +} diff --git a/src/machine/uefi/efidef.go b/src/machine/uefi/efidef.go index 13124a69c6..ef5f1c825b 100644 --- a/src/machine/uefi/efidef.go +++ b/src/machine/uefi/efidef.go @@ -11,6 +11,13 @@ type CHAR16 uint16 type BOOLEAN bool type VOID byte +type EFI_GUID struct { + Data1 uint32 + Data2 uint16 + Data3 uint16 + Data4 [8]byte +} + type EFI_TABLE_HEADER struct { Signature uint64 Revision uint32 diff --git a/src/machine/uefi/loaded_image.go b/src/machine/uefi/loaded_image.go new file mode 100644 index 0000000000..0417177e81 --- /dev/null +++ b/src/machine/uefi/loaded_image.go @@ -0,0 +1,37 @@ +package uefi + +import "unsafe" + +var EFI_LOADED_IMAGE_PROTOCOL_GUID = EFI_GUID{ + 0x5B1B31A1, 0x9562, 0x11D2, + [8]byte{0x8E, 0x3F, 0x00, 0xA0, 0xC9, 0x69, 0x72, 0x3B}, +} + +type EFI_LOADED_IMAGE_PROTOCOL struct { + Revision uint32 + ParentHandle EFI_HANDLE + SystemTable *EFI_SYSTEM_TABLE + DeviceHandle EFI_HANDLE + FilePath *EFI_DEVICE_PATH_PROTOCOL + Reserved *VOID + LoadOptionsSize uint32 + LoadOptions *VOID + ImageBase *VOID + ImageSize uint64 + ImageCodeType EFI_MEMORY_TYPE + ImageDataType EFI_MEMORY_TYPE + unload uintptr +} + +func GetLoadedImageProtocol() (*EFI_LOADED_IMAGE_PROTOCOL, EFI_STATUS) { + var lip *EFI_LOADED_IMAGE_PROTOCOL + status := BS().HandleProtocol( + GetImageHandle(), + &EFI_LOADED_IMAGE_PROTOCOL_GUID, + unsafe.Pointer(&lip), + ) + if status != EFI_SUCCESS { + return nil, status + } + return lip, EFI_SUCCESS +} diff --git a/src/machine/uefi/tables.go b/src/machine/uefi/tables.go index 14487ed829..d27c5a28f9 100644 --- a/src/machine/uefi/tables.go +++ b/src/machine/uefi/tables.go @@ -82,6 +82,10 @@ func (p *EFI_BOOT_SERVICES) CheckEvent(event EFI_EVENT) EFI_STATUS { return UefiCall1(p.checkEvent, uintptr(event)) } +func (p *EFI_BOOT_SERVICES) HandleProtocol(handle EFI_HANDLE, protocol *EFI_GUID, iface unsafe.Pointer) EFI_STATUS { + return UefiCall3(p.handleProtocol, uintptr(handle), uintptr(unsafe.Pointer(protocol)), uintptr(iface)) +} + func (p *EFI_BOOT_SERVICES) Exit(imageHandle EFI_HANDLE, exitStatus EFI_STATUS, exitDataSize UINTN, exitData *CHAR16) EFI_STATUS { return UefiCall4(p.exit, uintptr(imageHandle), uintptr(exitStatus), uintptr(exitDataSize), uintptr(unsafe.Pointer(exitData))) } From f0cc366074840f8c9a44a58903099a5926b77dc0 Mon Sep 17 00:00:00 2001 From: Matthew Hiles Date: Sat, 2 May 2026 12:20:42 -0400 Subject: [PATCH 09/21] runtime: add UEFI PE globals support --- src/runtime/os_uefi.go | 30 ++++++++++++++++++++++++++++++ src/runtime/os_windows_pe.go | 2 +- 2 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 src/runtime/os_uefi.go diff --git a/src/runtime/os_uefi.go b/src/runtime/os_uefi.go new file mode 100644 index 0000000000..81dca5b336 --- /dev/null +++ b/src/runtime/os_uefi.go @@ -0,0 +1,30 @@ +//go:build uefi + +package runtime + +import ( + "machine/uefi" + "unsafe" +) + +// loadedImageBase caches the loaded image base so PE globals discovery can +// locate writable sections without relying on linker-provided symbols. +var loadedImageBase uintptr + +func init() { + lip, status := uefi.GetLoadedImageProtocol() + if status != uefi.EFI_SUCCESS || lip == nil { + return + } + + loadedImageBase = uintptr(unsafe.Pointer(lip.ImageBase)) + module = (*exeHeader)(unsafe.Pointer(loadedImageBase)) +} + +func findGlobals(found func(start, end uintptr)) { + if loadedImageBase == 0 { + return + } + + findGlobalsForPE(found) +} diff --git a/src/runtime/os_windows_pe.go b/src/runtime/os_windows_pe.go index bd27af5751..01ff558533 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 From 84dd7a995577dec88c0b6926a49534e352fc372e Mon Sep 17 00:00:00 2001 From: Matthew Hiles Date: Sat, 2 May 2026 13:06:54 -0400 Subject: [PATCH 10/21] machine,runtime,x86: add UEFI time and text output support --- src/device/x86/cpu.go | 8 ++++---- src/device/x86/cpu_amd64.S | 4 ---- src/machine/uefi/simple_text_out.go | 5 +++++ src/machine/uefi/tables.go | 4 ++++ src/runtime/runtime_uefi.go | 9 +++++++++ 5 files changed, 22 insertions(+), 8 deletions(-) diff --git a/src/device/x86/cpu.go b/src/device/x86/cpu.go index 32ccb7f2eb..bf9ad9ce22 100644 --- a/src/device/x86/cpu.go +++ b/src/device/x86/cpu.go @@ -20,15 +20,15 @@ func AsmPause() func AsmReadRdtsc() uint64 //export asmCpuid -func AsmCpuid(index uint32, registerEax *uint32, registerEbx *uint32, registerEcx *uint32, registerEdx *uint32) int +func AsmCpuid(index uint32, registerEax *uint32, registerEbx *uint32, registerEcx *uint32) int var maxCpuidIndex uint32 var stdVendorName0 uint32 var stdCpuid1Eax uint32 func init() { - AsmCpuid(0, &maxCpuidIndex, &stdVendorName0, nil, nil) - AsmCpuid(1, &stdCpuid1Eax, nil, nil, nil) + AsmCpuid(0, &maxCpuidIndex, &stdVendorName0, nil) + AsmCpuid(1, &stdCpuid1Eax, nil, nil) } func getExtendedCPUFamily() CPUExtendedFamily { @@ -57,7 +57,7 @@ func cpuidCoreClockCalculateTSCFrequency() uint64 { var ebx uint32 var ecx uint32 - AsmCpuid(CPUIDTimeStampCounter, &eax, &ebx, &ecx, nil) + AsmCpuid(CPUIDTimeStampCounter, &eax, &ebx, &ecx) if eax == 0 || ebx == 0 { return 0 } diff --git a/src/device/x86/cpu_amd64.S b/src/device/x86/cpu_amd64.S index 655e53a8b3..ce1990c4cf 100644 --- a/src/device/x86/cpu_amd64.S +++ b/src/device/x86/cpu_amd64.S @@ -34,10 +34,6 @@ asmCpuid: jrcxz .SkipEbx mov %ebx, (%rcx) .SkipEbx: - mov 0x40(%rsp), %rcx - jrcxz .SkipEdx - mov %edx, (%rcx) -.SkipEdx: popq %rax popq %rbx ret diff --git a/src/machine/uefi/simple_text_out.go b/src/machine/uefi/simple_text_out.go index 3da17f6d7e..a7f6b762d5 100644 --- a/src/machine/uefi/simple_text_out.go +++ b/src/machine/uefi/simple_text_out.go @@ -27,3 +27,8 @@ type EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL struct { func (p *EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL) OutputString(s *CHAR16) EFI_STATUS { return UefiCall2(p.outputString, uintptr(unsafe.Pointer(p)), uintptr(unsafe.Pointer(s))) } + +func (p *EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL) WriteString(s string) EFI_STATUS { + buf := StringToCHAR16Z(s) + return p.OutputString(&buf[0]) +} diff --git a/src/machine/uefi/tables.go b/src/machine/uefi/tables.go index d27c5a28f9..e01f1df9d0 100644 --- a/src/machine/uefi/tables.go +++ b/src/machine/uefi/tables.go @@ -20,6 +20,10 @@ type EFI_RUNTIME_SERVICES struct { queryVariableInfo uintptr } +func (p *EFI_RUNTIME_SERVICES) GetTime(time *EFI_TIME, capabilities *EFI_TIME_CAPABILITIES) EFI_STATUS { + return UefiCall2(p.getTime, uintptr(unsafe.Pointer(time)), uintptr(unsafe.Pointer(capabilities))) +} + type EFI_BOOT_SERVICES struct { Hdr EFI_TABLE_HEADER raiseTPL uintptr diff --git a/src/runtime/runtime_uefi.go b/src/runtime/runtime_uefi.go index fa72d42a29..a55b4fde39 100644 --- a/src/runtime/runtime_uefi.go +++ b/src/runtime/runtime_uefi.go @@ -91,6 +91,15 @@ func growHeap() bool { return false } +func init() { + mono := nanotime() + efiTime, status := uefi.GetTime() + if status == uefi.EFI_SUCCESS { + sec, nsec := efiTime.GetEpoch() + timeOffset.Store(sec*1000000000 + int64(nsec) - mono) + } +} + //go:noinline func runMain() { run() From 1cb02805dca3f60b8a7283aa5c09c8da61fba59d Mon Sep 17 00:00:00 2001 From: Matthew Hiles Date: Sat, 2 May 2026 13:30:27 -0400 Subject: [PATCH 11/21] machine,runtime,x86: add UEFI text and time support --- examples/uefi-time/main.go | 80 +++++++++++++++++++++ src/machine/uefi/efistatus.go | 108 ++++++++++++++++++++++++++-- src/machine/uefi/simple_text_out.go | 5 -- src/machine/uefi/text_output.go | 41 +++++++++++ src/machine/uefi/time.go | 101 ++++++++++++++++++++++++++ 5 files changed, 324 insertions(+), 11 deletions(-) create mode 100644 examples/uefi-time/main.go create mode 100644 src/machine/uefi/text_output.go create mode 100644 src/machine/uefi/time.go diff --git a/examples/uefi-time/main.go b/examples/uefi-time/main.go new file mode 100644 index 0000000000..013c58671a --- /dev/null +++ b/examples/uefi-time/main.go @@ -0,0 +1,80 @@ +package main + +import ( + "fmt" + "machine/uefi" + "time" +) + +func main() { + conOut := uefi.ConsoleOut() + + writeString(conOut, "UEFI time probe\r\n") + + efiTime, status := uefi.GetTime() + if status != uefi.EFI_SUCCESS { + writeString(conOut, "GetTime failed\r\n") + return + } + + sec, nsec := efiTime.GetEpoch() + writeString(conOut, "EFI: ") + writePaddedUint(conOut, uint64(efiTime.Year), 4) + writeString(conOut, "-") + writePaddedUint(conOut, uint64(efiTime.Month), 2) + writeString(conOut, "-") + writePaddedUint(conOut, uint64(efiTime.Day), 2) + writeString(conOut, " ") + writePaddedUint(conOut, uint64(efiTime.Hour), 2) + writeString(conOut, ":") + writePaddedUint(conOut, uint64(efiTime.Minute), 2) + writeString(conOut, ":") + writePaddedUint(conOut, uint64(efiTime.Second), 2) + writeString(conOut, ".") + writePaddedUint(conOut, uint64(efiTime.Nanosecond), 9) + writeString(conOut, "\r\n") + + writeString(conOut, "Epoch: ") + writeInt(conOut, sec) + writeString(conOut, " s, ") + writeInt(conOut, int64(nsec)) + writeString(conOut, " ns\r\n") + + now := time.Now() + writeString(conOut, "Go Unix: ") + writeInt(conOut, now.Unix()) + writeString(conOut, "\r\n") + + fmt.Fprintln(conOut, "fmt via io.Writer works") +} + +func writeString(conOut *uefi.TextOutput, s string) { + _, _ = conOut.WriteString(s) +} + +func writePaddedUint(conOut *uefi.TextOutput, v uint64, width int) { + var buf [32]byte + i := len(buf) + for { + i-- + buf[i] = byte('0' + v%10) + v /= 10 + if v == 0 { + break + } + } + for len(buf)-i < width { + i-- + buf[i] = '0' + } + writeString(conOut, string(buf[i:])) +} + +func writeInt(conOut *uefi.TextOutput, v int64) { + if v < 0 { + writeString(conOut, "-") + writePaddedUint(conOut, uint64(-v), 1) + return + } + writePaddedUint(conOut, uint64(v), 1) +} diff --git a/src/machine/uefi/efistatus.go b/src/machine/uefi/efistatus.go index ed7c086c6e..842cd66612 100644 --- a/src/machine/uefi/efistatus.go +++ b/src/machine/uefi/efistatus.go @@ -6,10 +6,106 @@ const ( ) const ( - EFI_SUCCESS EFI_STATUS = 0 - EFI_INVALID_PARAMETER EFI_STATUS = errorMask | 2 - EFI_UNSUPPORTED EFI_STATUS = errorMask | 3 - EFI_NOT_READY EFI_STATUS = errorMask | 6 - EFI_OUT_OF_RESOURCES EFI_STATUS = errorMask | 9 - EFI_ABORTED EFI_STATUS = errorMask | 21 + EFI_SUCCESS EFI_STATUS = 0 + EFI_LOAD_ERROR EFI_STATUS = errorMask | 1 + EFI_INVALID_PARAMETER EFI_STATUS = errorMask | 2 + EFI_UNSUPPORTED EFI_STATUS = errorMask | 3 + EFI_BAD_BUFFER_SIZE EFI_STATUS = errorMask | 4 + EFI_BUFFER_TOO_SMALL EFI_STATUS = errorMask | 5 + EFI_NOT_READY EFI_STATUS = errorMask | 6 + EFI_DEVICE_ERROR EFI_STATUS = errorMask | 7 + EFI_WRITE_PROTECTED EFI_STATUS = errorMask | 8 + EFI_OUT_OF_RESOURCES EFI_STATUS = errorMask | 9 + EFI_VOLUME_CORRUPTED EFI_STATUS = errorMask | 10 + EFI_VOLUME_FULL EFI_STATUS = errorMask | 11 + EFI_NO_MEDIA EFI_STATUS = errorMask | 12 + EFI_MEDIA_CHANGED EFI_STATUS = errorMask | 13 + EFI_NOT_FOUND EFI_STATUS = errorMask | 14 + EFI_ACCESS_DENIED EFI_STATUS = errorMask | 15 + EFI_NO_RESPONSE EFI_STATUS = errorMask | 16 + EFI_NO_MAPPING EFI_STATUS = errorMask | 17 + EFI_TIMEOUT EFI_STATUS = errorMask | 18 + EFI_NOT_STARTED EFI_STATUS = errorMask | 19 + EFI_ALREADY_STARTED EFI_STATUS = errorMask | 20 + EFI_ABORTED EFI_STATUS = errorMask | 21 + EFI_ICMP_ERROR EFI_STATUS = errorMask | 22 + EFI_TFTP_ERROR EFI_STATUS = errorMask | 23 + EFI_PROTOCOL_ERROR EFI_STATUS = errorMask | 24 + EFI_INCOMPATIBLE_VERSION EFI_STATUS = errorMask | 25 + EFI_SECURITY_VIOLATION EFI_STATUS = errorMask | 26 + EFI_CRC_ERROR EFI_STATUS = errorMask | 27 + EFI_END_OF_MEDIA EFI_STATUS = errorMask | 28 + EFI_END_OF_FILE EFI_STATUS = errorMask | 31 + EFI_INVALID_LANGUAGE EFI_STATUS = errorMask | 32 + EFI_COMPROMISED_DATA EFI_STATUS = errorMask | 33 + EFI_IP_ADDRESS_CONFLICT EFI_STATUS = errorMask | 34 + EFI_HTTP_ERROR EFI_STATUS = errorMask | 35 ) + +var errMap = map[EFI_STATUS]*Error{} + +var ( + ErrLoadError = newError(EFI_LOAD_ERROR, "image failed to load") + ErrInvalidParameter = newError(EFI_INVALID_PARAMETER, "a parameter was incorrect") + ErrUnsupported = newError(EFI_UNSUPPORTED, "operation not supported") + ErrBadBufferSize = newError(EFI_BAD_BUFFER_SIZE, "buffer size incorrect for request") + ErrBufferTooSmall = newError(EFI_BUFFER_TOO_SMALL, "buffer too small; size returned in parameter") + ErrNotReady = newError(EFI_NOT_READY, "no data pending") + ErrDeviceError = newError(EFI_DEVICE_ERROR, "physical device reported an error") + ErrWriteProtected = newError(EFI_WRITE_PROTECTED, "device is write-protected") + ErrOutOfResources = newError(EFI_OUT_OF_RESOURCES, "out of resources") + ErrVolumeCorrupted = newError(EFI_VOLUME_CORRUPTED, "filesystem inconsistency detected") + ErrVolumeFull = newError(EFI_VOLUME_FULL, "no more space on filesystem") + ErrNoMedia = newError(EFI_NO_MEDIA, "device contains no medium") + ErrMediaChanged = newError(EFI_MEDIA_CHANGED, "medium changed since last access") + ErrNotFound = newError(EFI_NOT_FOUND, "item not found") + ErrAccessDenied = newError(EFI_ACCESS_DENIED, "access denied") + ErrNoResponse = newError(EFI_NO_RESPONSE, "server not found or no response") + ErrNoMapping = newError(EFI_NO_MAPPING, "no device mapping exists") + ErrTimeout = newError(EFI_TIMEOUT, "timeout expired") + ErrNotStarted = newError(EFI_NOT_STARTED, "protocol not started") + ErrAlreadyStarted = newError(EFI_ALREADY_STARTED, "protocol already started") + ErrAborted = newError(EFI_ABORTED, "operation aborted") + ErrICMPError = newError(EFI_ICMP_ERROR, "ICMP error during network operation") + ErrTFTPError = newError(EFI_TFTP_ERROR, "TFTP error during network operation") + ErrProtocolError = newError(EFI_PROTOCOL_ERROR, "protocol error during network operation") + ErrIncompatibleVersion = newError(EFI_INCOMPATIBLE_VERSION, "requested version incompatible") + ErrSecurityViolation = newError(EFI_SECURITY_VIOLATION, "security violation") + ErrCRCError = newError(EFI_CRC_ERROR, "CRC error detected") + ErrEndOfMedia = newError(EFI_END_OF_MEDIA, "beginning or end of media reached") + ErrEndOfFile = newError(EFI_END_OF_FILE, "end of file reached") + ErrInvalidLanguage = newError(EFI_INVALID_LANGUAGE, "invalid language specified") + ErrCompromisedData = newError(EFI_COMPROMISED_DATA, "data security status unknown or compromised") + ErrIPAddressConflict = newError(EFI_IP_ADDRESS_CONFLICT, "IP address conflict detected") + ErrHTTPError = newError(EFI_HTTP_ERROR, "HTTP error during network operation") +) + +type Error struct { + code EFI_STATUS + msg string +} + +func newError(code EFI_STATUS, msg string) *Error { + err := &Error{code: code, msg: msg} + errMap[code] = err + return err +} + +func (e *Error) Error() string { + return e.msg +} + +func (e *Error) Status() EFI_STATUS { + return e.code +} + +func StatusError(status EFI_STATUS) *Error { + if status == EFI_SUCCESS { + return nil + } + err, ok := errMap[status] + if !ok { + return newError(status, "unknown EFI error") + } + return err +} diff --git a/src/machine/uefi/simple_text_out.go b/src/machine/uefi/simple_text_out.go index a7f6b762d5..3da17f6d7e 100644 --- a/src/machine/uefi/simple_text_out.go +++ b/src/machine/uefi/simple_text_out.go @@ -27,8 +27,3 @@ type EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL struct { func (p *EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL) OutputString(s *CHAR16) EFI_STATUS { return UefiCall2(p.outputString, uintptr(unsafe.Pointer(p)), uintptr(unsafe.Pointer(s))) } - -func (p *EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL) WriteString(s string) EFI_STATUS { - buf := StringToCHAR16Z(s) - return p.OutputString(&buf[0]) -} diff --git a/src/machine/uefi/text_output.go b/src/machine/uefi/text_output.go new file mode 100644 index 0000000000..7bc4ffeebb --- /dev/null +++ b/src/machine/uefi/text_output.go @@ -0,0 +1,41 @@ +package uefi + +import "errors" + +var errNilTextOutputProtocol = errors.New("uefi: nil simple text output protocol") + +type TextOutput struct { + proto *EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL +} + +func NewTextOutput(proto *EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL) *TextOutput { + return &TextOutput{proto: proto} +} + +func ConsoleOut() *TextOutput { + return NewTextOutput(ST().ConOut) +} + +func StandardError() *TextOutput { + return NewTextOutput(ST().StdErr) +} + +func (w *TextOutput) Write(p []byte) (int, error) { + if w == nil || w.proto == nil { + return 0, errNilTextOutputProtocol + } + if len(p) == 0 { + return 0, nil + } + + buf := StringToCHAR16Z(string(p)) + status := w.proto.OutputString(&buf[0]) + if status != EFI_SUCCESS { + return 0, StatusError(status) + } + return len(p), nil +} + +func (w *TextOutput) WriteString(s string) (int, error) { + return w.Write([]byte(s)) +} diff --git a/src/machine/uefi/time.go b/src/machine/uefi/time.go new file mode 100644 index 0000000000..9445097c63 --- /dev/null +++ b/src/machine/uefi/time.go @@ -0,0 +1,101 @@ +package uefi + +type EFI_TIME struct { + Year uint16 + Month byte + Day byte + Hour byte + Minute byte + Second byte + Pad1 byte + Nanosecond uint32 + TimeZone int16 + Daylight byte + Pad2 byte +} + +type EFI_TIME_CAPABILITIES struct { + Resolution uint32 + Accuracy uint32 + SetsToZero BOOLEAN +} + +func GetTime() (EFI_TIME, EFI_STATUS) { + var time EFI_TIME + status := ST().RuntimeServices.GetTime(&time, nil) + return time, status +} + +func (t *EFI_TIME) GetEpoch() (sec int64, nsec int32) { + year := int(t.Year) + month := int(t.Month) - 1 + + d := daysSinceEpoch(year) + d += uint64(daysBefore[month-1]) + if isLeap(year) && month >= 3 { + d++ + } + d += uint64(t.Day - 1) + + abs := d * secondsPerDay + abs += uint64(uint64(t.Hour)*uint64(secondsPerHour) + uint64(t.Minute)*uint64(secondsPerMinute) + uint64(t.Second)) + + sec = int64(abs) + (absoluteToInternal + internalToUnix) + nsec = int32(t.Nanosecond) + return +} + +const ( + secondsPerMinute = 60 + secondsPerHour = 60 * secondsPerMinute + secondsPerDay = 24 * secondsPerHour + daysPer400Years = 365*400 + 97 + daysPer100Years = 365*100 + 24 + daysPer4Years = 365*4 + 1 + + absoluteZeroYear = -292277022399 + internalYear = 1 + + absoluteToInternal int64 = (absoluteZeroYear - internalYear) * 365.2425 * secondsPerDay + unixToInternal int64 = (1969*365 + 1969/4 - 1969/100 + 1969/400) * secondsPerDay + internalToUnix int64 = -unixToInternal +) + +var daysBefore = [...]int32{ + 0, + 31, + 31 + 28, + 31 + 28 + 31, + 31 + 28 + 31 + 30, + 31 + 28 + 31 + 30 + 31, + 31 + 28 + 31 + 30 + 31 + 30, + 31 + 28 + 31 + 30 + 31 + 30 + 31, + 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31, + 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30, + 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31, + 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30, + 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 + 31, +} + +func daysSinceEpoch(year int) uint64 { + y := uint64(int64(year) - absoluteZeroYear) + + n := y / 400 + y -= 400 * n + d := daysPer400Years * n + + n = y / 100 + y -= 100 * n + d += daysPer100Years * n + + n = y / 4 + y -= 4 * n + d += daysPer4Years * n + + d += 365 * y + return d +} + +func isLeap(year int) bool { + return year%4 == 0 && (year%100 != 0 || year%400 == 0) +} From 408bfbbd7bc242b06e7e66d191405c657e4b0517 Mon Sep 17 00:00:00 2001 From: Matthew Hiles Date: Sat, 2 May 2026 14:05:32 -0400 Subject: [PATCH 12/21] machine,runtime,examples: add UEFI text input support --- examples/uefi-key/main.go | 89 +++++++++++++++++ src/machine/uefi/simple_text_in.go | 150 +++++++++++++++++++++++++++++ src/machine/uefi/tables.go | 14 ++- src/machine/uefi/text_input.go | 132 +++++++++++++++++++++++++ src/runtime/runtime_uefi.go | 52 ++++++++++ 5 files changed, 436 insertions(+), 1 deletion(-) create mode 100644 examples/uefi-key/main.go create mode 100644 src/machine/uefi/simple_text_in.go create mode 100644 src/machine/uefi/text_input.go diff --git a/examples/uefi-key/main.go b/examples/uefi-key/main.go new file mode 100644 index 0000000000..8500ff561d --- /dev/null +++ b/examples/uefi-key/main.go @@ -0,0 +1,89 @@ +package main + +import "machine/uefi" + +func main() { + conOut := uefi.ConsoleOut() + conIn, err := uefi.ConsoleInput() + if err != nil { + _, _ = conOut.WriteString("ConsoleInput unavailable\r\n") + return + } + + if conIn.HasTextInputEx() { + _, _ = conOut.WriteString("STIEx available\r\n") + } + if conIn.HasTextInput() { + _, _ = conOut.WriteString("STIP available\r\n") + } + _, _ = conOut.WriteString("Press keys, ESC to exit...\r\n") + + for { + key, source, err := conIn.ReadKeyWithSource() + if err != nil { + _, _ = conOut.WriteString("ReadKey failed\r\n") + _, _ = conOut.WriteString("Source: ") + _, _ = conOut.WriteString(source.String()) + _, _ = conOut.WriteString("\r\n") + if statusErr, ok := err.(*uefi.Error); ok { + _, _ = conOut.WriteString("EFI_STATUS: ") + writePaddedUint(conOut, uint64(statusErr.Status()), 1) + _, _ = conOut.WriteString("\r\n") + } + return + } + + _, _ = conOut.WriteString("Source: ") + _, _ = conOut.WriteString(source.String()) + _, _ = conOut.WriteString("\r\nScanCode: ") + writePaddedUint(conOut, uint64(key.Key.ScanCode), 1) + _, _ = conOut.WriteString("\r\nUnicode: ") + writePaddedUint(conOut, uint64(key.Key.UnicodeChar), 1) + _, _ = conOut.WriteString("\r\nShiftState: ") + writeHexUint(conOut, uint64(key.KeyState.KeyShiftState), 8) + _, _ = conOut.WriteString("\r\nToggleState: ") + writeHexUint(conOut, uint64(key.KeyState.KeyToggleState), 2) + _, _ = conOut.WriteString("\r\n\r\n") + + if key.Key.ScanCode == 23 { + return + } + } +} + +func writePaddedUint(conOut *uefi.TextOutput, v uint64, width int) { + var buf [32]byte + i := len(buf) + for { + i-- + buf[i] = byte('0' + v%10) + v /= 10 + if v == 0 { + break + } + } + for len(buf)-i < width { + i-- + buf[i] = '0' + } + _, _ = conOut.WriteString(string(buf[i:])) +} + +func writeHexUint(conOut *uefi.TextOutput, v uint64, width int) { + const digits = "0123456789ABCDEF" + var buf [32]byte + i := len(buf) + for { + i-- + buf[i] = digits[v&0xF] + v >>= 4 + if v == 0 { + break + } + } + for len(buf)-i < width { + i-- + buf[i] = '0' + } + _, _ = conOut.WriteString(string(buf[i:])) +} diff --git a/src/machine/uefi/simple_text_in.go b/src/machine/uefi/simple_text_in.go new file mode 100644 index 0000000000..d76dfb59b1 --- /dev/null +++ b/src/machine/uefi/simple_text_in.go @@ -0,0 +1,150 @@ +package uefi + +import "unsafe" + +func booleanArg(v BOOLEAN) uintptr { + if v { + return 1 + } + return 0 +} + +type EFI_KEY_TOGGLE_STATE uint8 + +const ( + EFI_SCROLL_LOCK_ACTIVE EFI_KEY_TOGGLE_STATE = 0x01 + EFI_NUM_LOCK_ACTIVE EFI_KEY_TOGGLE_STATE = 0x02 + EFI_CAPS_LOCK_ACTIVE EFI_KEY_TOGGLE_STATE = 0x04 + EFI_KEY_STATE_EXPOSED EFI_KEY_TOGGLE_STATE = 0x40 + EFI_TOGGLE_STATE_VALID EFI_KEY_TOGGLE_STATE = 0x80 +) + +const ( + EFI_SHIFT_STATE_VALID = 0x80000000 + EFI_RIGHT_SHIFT_PRESSED = 0x00000001 + EFI_LEFT_SHIFT_PRESSED = 0x00000002 + EFI_RIGHT_CONTROL_PRESSED = 0x00000004 + EFI_LEFT_CONTROL_PRESSED = 0x00000008 + EFI_RIGHT_ALT_PRESSED = 0x00000010 + EFI_LEFT_ALT_PRESSED = 0x00000020 + EFI_RIGHT_LOGO_PRESSED = 0x00000040 + EFI_LEFT_LOGO_PRESSED = 0x00000080 + EFI_MENU_KEY_PRESSED = 0x00000100 + EFI_SYS_REQ_PRESSED = 0x00000200 +) + +type EFI_INPUT_KEY struct { + ScanCode uint16 + UnicodeChar CHAR16 +} + +type EFI_SIMPLE_TEXT_INPUT_PROTOCOL struct { + reset uintptr + readKeyStroke uintptr + WaitForKey EFI_EVENT +} + +func (p *EFI_SIMPLE_TEXT_INPUT_PROTOCOL) Reset(extendedVerification BOOLEAN) EFI_STATUS { + return UefiCall2(p.reset, uintptr(unsafe.Pointer(p)), booleanArg(extendedVerification)) +} + +func (p *EFI_SIMPLE_TEXT_INPUT_PROTOCOL) ReadKeyStroke(key *EFI_INPUT_KEY) EFI_STATUS { + return UefiCall2(p.readKeyStroke, uintptr(unsafe.Pointer(p)), uintptr(unsafe.Pointer(key))) +} + +func (p *EFI_SIMPLE_TEXT_INPUT_PROTOCOL) GetKey() (EFI_INPUT_KEY, EFI_STATUS) { + var key EFI_INPUT_KEY + var status EFI_STATUS + for { + for { + status = BS().CheckEvent(p.WaitForKey) + if status == EFI_SUCCESS { + break + } + if status != EFI_NOT_READY { + return key, status + } + CpuPause() + } + status = p.ReadKeyStroke(&key) + if status == EFI_SUCCESS { + return key, EFI_SUCCESS + } + if status != EFI_NOT_READY { + return key, status + } + } +} + +var SimpleTextInputExProtocolGUID = EFI_GUID{ + 0xdd9e7534, 0x7762, 0x4698, + [8]byte{0x8c, 0x14, 0xf5, 0x85, 0x17, 0xa6, 0x25, 0xaa}, +} + +type EFI_KEY_STATE struct { + KeyShiftState uint32 + KeyToggleState EFI_KEY_TOGGLE_STATE +} + +type EFI_KEY_DATA struct { + Key EFI_INPUT_KEY + KeyState EFI_KEY_STATE +} + +type EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL struct { + resetEx uintptr + readKeyStrokeEx uintptr + WaitForKeyEx EFI_EVENT + setState uintptr + registerKeystrokeNotify uintptr + unregisterKeystrokeNotify uintptr +} + +func (p *EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL) Reset(extendedVerification BOOLEAN) EFI_STATUS { + return UefiCall2(p.resetEx, uintptr(unsafe.Pointer(p)), booleanArg(extendedVerification)) +} + +func (p *EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL) ReadKeyStroke(key *EFI_KEY_DATA) EFI_STATUS { + return UefiCall2(p.readKeyStrokeEx, uintptr(unsafe.Pointer(p)), uintptr(unsafe.Pointer(key))) +} + +func (p *EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL) GetKey() (EFI_KEY_DATA, EFI_STATUS) { + var key EFI_KEY_DATA + var status EFI_STATUS + for { + for { + status = BS().CheckEvent(p.WaitForKeyEx) + if status == EFI_SUCCESS { + break + } + if status != EFI_NOT_READY { + return key, status + } + CpuPause() + } + status = p.ReadKeyStroke(&key) + if status == EFI_SUCCESS { + return key, EFI_SUCCESS + } + if status != EFI_NOT_READY { + return key, status + } + } +} + +func SimpleTextInProtocol() (*EFI_SIMPLE_TEXT_INPUT_PROTOCOL, EFI_STATUS) { + st := ST() + if st == nil || st.ConIn == nil { + return nil, EFI_NOT_FOUND + } + return st.ConIn, EFI_SUCCESS +} + +func SimpleTextInExProtocol() (*EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL, EFI_STATUS) { + var iface *EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL + status := BS().LocateProtocol(&SimpleTextInputExProtocolGUID, nil, unsafe.Pointer(&iface)) + if status != EFI_SUCCESS { + return nil, status + } + return iface, EFI_SUCCESS +} diff --git a/src/machine/uefi/tables.go b/src/machine/uefi/tables.go index e01f1df9d0..c90b568747 100644 --- a/src/machine/uefi/tables.go +++ b/src/machine/uefi/tables.go @@ -56,6 +56,14 @@ type EFI_BOOT_SERVICES struct { getNextMonotonicCount uintptr stall uintptr setWatchdogTimer uintptr + connectController uintptr + disconnectController uintptr + openProtocol uintptr + closeProtocol uintptr + openProtocolInformation uintptr + protocolsPerHandle uintptr + locateHandleBuffer uintptr + locateProtocol uintptr } func (p *EFI_BOOT_SERVICES) AllocatePages(typ EFI_ALLOCATE_TYPE, memoryType EFI_MEMORY_TYPE, pages UINTN, memory *EFI_PHYSICAL_ADDRESS) EFI_STATUS { @@ -90,6 +98,10 @@ func (p *EFI_BOOT_SERVICES) HandleProtocol(handle EFI_HANDLE, protocol *EFI_GUID return UefiCall3(p.handleProtocol, uintptr(handle), uintptr(unsafe.Pointer(protocol)), uintptr(iface)) } +func (p *EFI_BOOT_SERVICES) LocateProtocol(protocol *EFI_GUID, registration *VOID, iface unsafe.Pointer) EFI_STATUS { + return UefiCall3(p.locateProtocol, uintptr(unsafe.Pointer(protocol)), uintptr(unsafe.Pointer(registration)), uintptr(iface)) +} + func (p *EFI_BOOT_SERVICES) Exit(imageHandle EFI_HANDLE, exitStatus EFI_STATUS, exitDataSize UINTN, exitData *CHAR16) EFI_STATUS { return UefiCall4(p.exit, uintptr(imageHandle), uintptr(exitStatus), uintptr(exitDataSize), uintptr(unsafe.Pointer(exitData))) } @@ -103,7 +115,7 @@ type EFI_SYSTEM_TABLE struct { FirmwareVendor *CHAR16 FirmwareRevision uint32 ConsoleInHandle EFI_HANDLE - ConIn *VOID + ConIn *EFI_SIMPLE_TEXT_INPUT_PROTOCOL ConsoleOutHandle EFI_HANDLE ConOut *EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL StandardErrorHandle EFI_HANDLE diff --git a/src/machine/uefi/text_input.go b/src/machine/uefi/text_input.go new file mode 100644 index 0000000000..e3b022640c --- /dev/null +++ b/src/machine/uefi/text_input.go @@ -0,0 +1,132 @@ +package uefi + +import ( + "errors" + "unicode/utf8" +) + +var errNilTextInputProtocol = errors.New("uefi: nil simple text input protocol") + +type TextInputSource uint8 + +const ( + TextInputNone TextInputSource = iota + TextInputSimpleTextInputEx + TextInputSimpleTextInput +) + +type TextInput struct { + protoEx *EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL + proto *EFI_SIMPLE_TEXT_INPUT_PROTOCOL + pending [utf8.UTFMax]byte + start int + end int +} + +func NewTextInput(proto *EFI_SIMPLE_TEXT_INPUT_PROTOCOL) *TextInput { + return &TextInput{proto: proto} +} + +func NewTextInputEx(proto *EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL) *TextInput { + return &TextInput{protoEx: proto} +} + +func ConsoleInput() (*TextInput, error) { + r := &TextInput{} + if protoEx, status := SimpleTextInExProtocol(); status == EFI_SUCCESS { + r.protoEx = protoEx + } + if proto, status := SimpleTextInProtocol(); status == EFI_SUCCESS { + r.proto = proto + } + if r.protoEx == nil && r.proto == nil { + return nil, ErrNotFound + } + return r, nil +} + +func (r *TextInput) Read(p []byte) (int, error) { + if r == nil || (r.protoEx == nil && r.proto == nil) { + return 0, errNilTextInputProtocol + } + if len(p) == 0 { + return 0, nil + } + + n := 0 + for n < len(p) { + if r.start != r.end { + p[n] = r.pending[r.start] + r.start++ + n++ + continue + } + + key, err := r.ReadKey() + if err != nil { + if n != 0 { + return n, nil + } + return 0, err + } + if key.Key.UnicodeChar == 0 { + continue + } + + runeValue := rune(key.Key.UnicodeChar) + r.end = utf8.EncodeRune(r.pending[:], runeValue) + r.start = 0 + } + + return n, nil +} + +func (r *TextInput) ReadByte() (byte, error) { + var buf [1]byte + _, err := r.Read(buf[:]) + return buf[0], err +} + +func (r *TextInput) ReadKey() (EFI_KEY_DATA, error) { + key, _, err := r.ReadKeyWithSource() + return key, err +} + +func (r *TextInput) ReadKeyWithSource() (EFI_KEY_DATA, TextInputSource, error) { + if r == nil { + return EFI_KEY_DATA{}, TextInputNone, errNilTextInputProtocol + } + if r.protoEx != nil { + key, status := r.protoEx.GetKey() + if status == EFI_SUCCESS { + return key, TextInputSimpleTextInputEx, nil + } + if r.proto == nil { + return key, TextInputSimpleTextInputEx, StatusError(status) + } + } + if r.proto != nil { + key, status := r.proto.GetKey() + return EFI_KEY_DATA{Key: key}, TextInputSimpleTextInput, StatusError(status) + } + return EFI_KEY_DATA{}, TextInputNone, errNilTextInputProtocol +} + +func (s TextInputSource) String() string { + switch s { + case TextInputSimpleTextInputEx: + return "STIEx" + case TextInputSimpleTextInput: + return "STIP" + default: + return "none" + } +} + +func (r *TextInput) HasTextInputEx() bool { + return r != nil && r.protoEx != nil +} + +func (r *TextInput) HasTextInput() bool { + return r != nil && r.proto != nil +} diff --git a/src/runtime/runtime_uefi.go b/src/runtime/runtime_uefi.go index a55b4fde39..5f77fdfa71 100644 --- a/src/runtime/runtime_uefi.go +++ b/src/runtime/runtime_uefi.go @@ -16,6 +16,8 @@ var heapSize uintptr = 64 * 1024 * 1024 var heapStart, heapEnd uintptr var stackTop uintptr var allocatePagesAddress uefi.EFI_PHYSICAL_ADDRESS +var consoleInEx *uefi.EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL +var consoleIn *uefi.EFI_SIMPLE_TEXT_INPUT_PROTOCOL func ticks() timeUnit { return timeUnit(uefi.Ticks()) @@ -62,6 +64,12 @@ func abort() { func preinit() { uefi.BS().SetWatchdogTimer(0, 0, 0, nil) + if protoEx, status := uefi.SimpleTextInExProtocol(); status == uefi.EFI_SUCCESS { + consoleInEx = protoEx + } + if proto, status := uefi.SimpleTextInProtocol(); status == uefi.EFI_SUCCESS { + consoleIn = proto + } if !growHeap() { runtimePanic("could not allocate initial UEFI heap") } @@ -105,6 +113,50 @@ func runMain() { run() } +func buffered() int { + if consoleInEx != nil { + if uefi.BS().CheckEvent(consoleInEx.WaitForKeyEx) == uefi.EFI_SUCCESS { + return 1 + } + return 0 + } + if consoleIn != nil { + if uefi.BS().CheckEvent(consoleIn.WaitForKey) == uefi.EFI_SUCCESS { + return 1 + } + return 0 + } + return 0 +} + +func getchar() byte { + for { + if consoleInEx != nil { + key, status := consoleInEx.GetKey() + if status == uefi.EFI_SUCCESS && key.Key.UnicodeChar != 0 { + return byte(key.Key.UnicodeChar) + } + if status != uefi.EFI_SUCCESS && status != uefi.EFI_NOT_READY && consoleIn == nil { + return 0 + } + if status == uefi.EFI_SUCCESS || status == uefi.EFI_NOT_READY { + continue + } + } + if consoleIn != nil { + key, status := consoleIn.GetKey() + if status == uefi.EFI_SUCCESS && key.UnicodeChar != 0 { + return byte(key.UnicodeChar) + } + if status != uefi.EFI_SUCCESS && status != uefi.EFI_NOT_READY { + return 0 + } + continue + } + return 0 + } +} + //export efi_main func main(imageHandle uintptr, systemTable uintptr) uintptr { uefi.Init(imageHandle, systemTable) From 308a0ffa7fa701cbff2949165716f54810269205 Mon Sep 17 00:00:00 2001 From: Matthew Hiles Date: Sun, 3 May 2026 09:13:37 -0400 Subject: [PATCH 13/21] machine,examples: add UEFI graphics output support --- examples/uefi-gop/main.go | 79 ++++++++++++ src/machine/uefi/asm_amd64.S | 115 +++++++++++++++++ src/machine/uefi/call.go | 20 +++ src/machine/uefi/graphics_output_protocol.go | 124 +++++++++++++++++++ 4 files changed, 338 insertions(+) create mode 100644 examples/uefi-gop/main.go create mode 100644 src/machine/uefi/graphics_output_protocol.go diff --git a/examples/uefi-gop/main.go b/examples/uefi-gop/main.go new file mode 100644 index 0000000000..2e4f997634 --- /dev/null +++ b/examples/uefi-gop/main.go @@ -0,0 +1,79 @@ +package main + +import "machine/uefi" + +func main() { + conOut := uefi.ConsoleOut() + conIn, _ := uefi.ConsoleInput() + + gop, status := uefi.GraphicsOutputProtocol() + if status != uefi.EFI_SUCCESS { + _, _ = conOut.WriteString("GraphicsOutputProtocol unavailable\r\n") + _, _ = conOut.WriteString("EFI_STATUS: ") + writeUint(conOut, uint64(status)) + _, _ = conOut.WriteString("\r\n") + return + } + + if gop.Mode == nil || gop.Mode.Info == nil { + _, _ = conOut.WriteString("GOP mode info unavailable\r\n") + return + } + + info := gop.Mode.Info + _, _ = conOut.WriteString("GOP current mode\r\n") + _, _ = conOut.WriteString("Mode: ") + writeUint(conOut, uint64(gop.Mode.Mode)) + _, _ = conOut.WriteString("\r\nResolution: ") + writeUint(conOut, uint64(info.HorizontalResolution)) + _, _ = conOut.WriteString("x") + writeUint(conOut, uint64(info.VerticalResolution)) + _, _ = conOut.WriteString("\r\nPixelFormat: ") + writeUint(conOut, uint64(info.PixelFormat)) + _, _ = conOut.WriteString("\r\nFramebuffer bytes: ") + writeUint(conOut, uint64(gop.Mode.FrameBufferSize)) + _, _ = conOut.WriteString("\r\nPress ESC to exit after draw\r\n") + + width := uefi.UINTN(info.HorizontalResolution) + height := uefi.UINTN(info.VerticalResolution) + halfWidth := width / 2 + halfHeight := height / 2 + + fillRect(gop, 0, 0, halfWidth, halfHeight, uefi.EFI_GRAPHICS_OUTPUT_BLT_PIXEL{Red: 0xC0}) + fillRect(gop, halfWidth, 0, width-halfWidth, halfHeight, uefi.EFI_GRAPHICS_OUTPUT_BLT_PIXEL{Green: 0xC0}) + fillRect(gop, 0, halfHeight, halfWidth, height-halfHeight, uefi.EFI_GRAPHICS_OUTPUT_BLT_PIXEL{Blue: 0xC0}) + fillRect(gop, halfWidth, halfHeight, width-halfWidth, height-halfHeight, uefi.EFI_GRAPHICS_OUTPUT_BLT_PIXEL{Red: 0xC0, Green: 0xC0, Blue: 0xC0}) + + if conIn == nil { + return + } + for { + key, _, err := conIn.ReadKeyWithSource() + if err != nil { + return + } + if key.Key.ScanCode == 23 { + return + } + } +} + +func fillRect(gop *uefi.EFI_GRAPHICS_OUTPUT_PROTOCOL, x, y, width, height uefi.UINTN, color uefi.EFI_GRAPHICS_OUTPUT_BLT_PIXEL) { + if gop.Blt(&color, uefi.BltVideoFill, 0, 0, x, y, width, height, 0) != uefi.EFI_SUCCESS { + return + } +} + +func writeUint(conOut *uefi.TextOutput, v uint64) { + var buf [32]byte + i := len(buf) + for { + i-- + buf[i] = byte('0' + v%10) + v /= 10 + if v == 0 { + break + } + } + _, _ = conOut.WriteString(string(buf[i:])) +} diff --git a/src/machine/uefi/asm_amd64.S b/src/machine/uefi/asm_amd64.S index e8328a367a..5037432991 100644 --- a/src/machine/uefi/asm_amd64.S +++ b/src/machine/uefi/asm_amd64.S @@ -81,6 +81,121 @@ uefiCall5: popq %rbp ret +.global uefiCall6 +uefiCall6: + pushq %rbp + movq %rsp, %rbp + subq $0x30, %rsp + movq %rcx, %rax + movq %rdx, %rcx + movq %r8, %rdx + movq %r9, %r8 + movq 0x30(%rbp), %r9 + movq 0x38(%rbp), %r10 + movq %r10, 0x20(%rsp) + movq 0x40(%rbp), %r10 + movq %r10, 0x28(%rsp) + callq *%rax + movq %rbp, %rsp + popq %rbp + ret + +.global uefiCall7 +uefiCall7: + pushq %rbp + movq %rsp, %rbp + subq $0x40, %rsp + movq %rcx, %rax + movq %rdx, %rcx + movq %r8, %rdx + movq %r9, %r8 + movq 0x30(%rbp), %r9 + movq 0x38(%rbp), %r10 + movq %r10, 0x20(%rsp) + movq 0x40(%rbp), %r10 + movq %r10, 0x28(%rsp) + movq 0x48(%rbp), %r10 + movq %r10, 0x30(%rsp) + callq *%rax + movq %rbp, %rsp + popq %rbp + ret + +.global uefiCall8 +uefiCall8: + pushq %rbp + movq %rsp, %rbp + subq $0x40, %rsp + movq %rcx, %rax + movq %rdx, %rcx + movq %r8, %rdx + movq %r9, %r8 + movq 0x30(%rbp), %r9 + movq 0x38(%rbp), %r10 + movq %r10, 0x20(%rsp) + movq 0x40(%rbp), %r10 + movq %r10, 0x28(%rsp) + movq 0x48(%rbp), %r10 + movq %r10, 0x30(%rsp) + movq 0x50(%rbp), %r10 + movq %r10, 0x38(%rsp) + callq *%rax + movq %rbp, %rsp + popq %rbp + ret + +.global uefiCall9 +uefiCall9: + pushq %rbp + movq %rsp, %rbp + subq $0x50, %rsp + movq %rcx, %rax + movq %rdx, %rcx + movq %r8, %rdx + movq %r9, %r8 + movq 0x30(%rbp), %r9 + movq 0x38(%rbp), %r10 + movq %r10, 0x20(%rsp) + movq 0x40(%rbp), %r10 + movq %r10, 0x28(%rsp) + movq 0x48(%rbp), %r10 + movq %r10, 0x30(%rsp) + movq 0x50(%rbp), %r10 + movq %r10, 0x38(%rsp) + movq 0x58(%rbp), %r10 + movq %r10, 0x40(%rsp) + callq *%rax + movq %rbp, %rsp + popq %rbp + ret + +.global uefiCall10 +uefiCall10: + pushq %rbp + movq %rsp, %rbp + subq $0x50, %rsp + movq %rcx, %rax + movq %rdx, %rcx + movq %r8, %rdx + movq %r9, %r8 + movq 0x30(%rbp), %r9 + movq 0x38(%rbp), %r10 + movq %r10, 0x20(%rsp) + movq 0x40(%rbp), %r10 + movq %r10, 0x28(%rsp) + movq 0x48(%rbp), %r10 + movq %r10, 0x30(%rsp) + movq 0x50(%rbp), %r10 + movq %r10, 0x38(%rsp) + movq 0x58(%rbp), %r10 + movq %r10, 0x40(%rsp) + movq 0x60(%rbp), %r10 + movq %r10, 0x48(%rsp) + callq *%rax + movq %rbp, %rsp + popq %rbp + ret + .global ___chkstk_ms ___chkstk_ms: ret diff --git a/src/machine/uefi/call.go b/src/machine/uefi/call.go index 82b4b7f955..0ae162fce5 100644 --- a/src/machine/uefi/call.go +++ b/src/machine/uefi/call.go @@ -23,3 +23,23 @@ func UefiCall4(fn uintptr, a uintptr, b uintptr, c uintptr, d uintptr) EFI_STATU //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 + +//go:nosplit +//go:export uefiCall7 +func UefiCall7(fn uintptr, a uintptr, b uintptr, c uintptr, d uintptr, e uintptr, f uintptr, g uintptr) EFI_STATUS + +//go:nosplit +//go:export uefiCall8 +func UefiCall8(fn uintptr, a uintptr, b uintptr, c uintptr, d uintptr, e uintptr, f uintptr, g uintptr, h uintptr) EFI_STATUS + +//go:nosplit +//go:export uefiCall9 +func UefiCall9(fn uintptr, a uintptr, b uintptr, c uintptr, d uintptr, e uintptr, f uintptr, g uintptr, h uintptr, i uintptr) EFI_STATUS + +//go:nosplit +//go:export uefiCall10 +func UefiCall10(fn uintptr, a uintptr, b uintptr, c uintptr, d uintptr, e uintptr, f uintptr, g uintptr, h uintptr, i uintptr, j uintptr) EFI_STATUS diff --git a/src/machine/uefi/graphics_output_protocol.go b/src/machine/uefi/graphics_output_protocol.go new file mode 100644 index 0000000000..2ed6802a1a --- /dev/null +++ b/src/machine/uefi/graphics_output_protocol.go @@ -0,0 +1,124 @@ +package uefi + +import "unsafe" + +var GraphicsOutputProtocolGUID = EFI_GUID{ + 0x9042a9de, 0x23dc, 0x4a38, + [8]byte{0x96, 0xfb, 0x7a, 0xde, 0xd0, 0x80, 0x51, 0x6a}, +} + +type EFI_GRAPHICS_PIXEL_FORMAT uint32 + +const ( + PixelRedGreenBlueReserved8BitPerColor EFI_GRAPHICS_PIXEL_FORMAT = iota + PixelBlueGreenRedReserved8BitPerColor + PixelBitMask + PixelBltOnly + PixelFormatMax +) + +type EFI_GRAPHICS_OUTPUT_BLT_OPERATION uint32 + +const ( + BltVideoFill EFI_GRAPHICS_OUTPUT_BLT_OPERATION = iota + BltVideoToBltBuffer + BltBufferToVideo + BltVideoToVideo + BltOperationMax +) + +type EFI_PIXEL_BITMASK struct { + RedMask uint32 + GreenMask uint32 + BlueMask uint32 + ReservedMask uint32 +} + +type EFI_GRAPHICS_OUTPUT_MODE_INFORMATION struct { + Version uint32 + HorizontalResolution uint32 + VerticalResolution uint32 + PixelFormat EFI_GRAPHICS_PIXEL_FORMAT + PixelInformation EFI_PIXEL_BITMASK + PixelsPerScanLine uint32 +} + +type EFI_GRAPHICS_OUTPUT_BLT_PIXEL struct { + Blue uint8 + Green uint8 + Red uint8 + Reserved uint8 +} + +type EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE struct { + MaxMode uint32 + Mode uint32 + Info *EFI_GRAPHICS_OUTPUT_MODE_INFORMATION + SizeOfInfo UINTN + FrameBufferBase EFI_PHYSICAL_ADDRESS + FrameBufferSize UINTN +} + +type EFI_GRAPHICS_OUTPUT_PROTOCOL struct { + queryMode uintptr + setMode uintptr + blt uintptr + Mode *EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE +} + +func (p *EFI_GRAPHICS_OUTPUT_PROTOCOL) QueryMode( + modeNumber uint32, + sizeOfInfo *UINTN, + info **EFI_GRAPHICS_OUTPUT_MODE_INFORMATION, +) EFI_STATUS { + return UefiCall4( + p.queryMode, + uintptr(unsafe.Pointer(p)), + uintptr(modeNumber), + uintptr(unsafe.Pointer(sizeOfInfo)), + uintptr(unsafe.Pointer(info)), + ) +} + +func (p *EFI_GRAPHICS_OUTPUT_PROTOCOL) SetMode(modeNumber uint32) EFI_STATUS { + return UefiCall2( + p.setMode, + uintptr(unsafe.Pointer(p)), + uintptr(modeNumber), + ) +} + +func (p *EFI_GRAPHICS_OUTPUT_PROTOCOL) Blt( + bltBuffer *EFI_GRAPHICS_OUTPUT_BLT_PIXEL, + bltOperation EFI_GRAPHICS_OUTPUT_BLT_OPERATION, + sourceX UINTN, + sourceY UINTN, + destinationX UINTN, + destinationY UINTN, + width UINTN, + height UINTN, + delta UINTN, +) EFI_STATUS { + return UefiCall10( + p.blt, + uintptr(unsafe.Pointer(p)), + uintptr(unsafe.Pointer(bltBuffer)), + uintptr(bltOperation), + uintptr(sourceX), + uintptr(sourceY), + uintptr(destinationX), + uintptr(destinationY), + uintptr(width), + uintptr(height), + uintptr(delta), + ) +} + +func GraphicsOutputProtocol() (*EFI_GRAPHICS_OUTPUT_PROTOCOL, EFI_STATUS) { + var iface *EFI_GRAPHICS_OUTPUT_PROTOCOL + status := BS().LocateProtocol(&GraphicsOutputProtocolGUID, nil, unsafe.Pointer(&iface)) + if status != EFI_SUCCESS { + return nil, status + } + return iface, EFI_SUCCESS +} From 8a7f0855f78aac41b98e6479ddbc92e2ed950d7c Mon Sep 17 00:00:00 2001 From: Matthew Hiles Date: Sun, 3 May 2026 12:58:28 -0400 Subject: [PATCH 14/21] add rest of STOP methods (direct UEFI ABI only) --- src/machine/uefi/simple_text_out.go | 38 +++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/src/machine/uefi/simple_text_out.go b/src/machine/uefi/simple_text_out.go index 3da17f6d7e..8317b30f03 100644 --- a/src/machine/uefi/simple_text_out.go +++ b/src/machine/uefi/simple_text_out.go @@ -24,6 +24,44 @@ type EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL struct { Mode *EFI_SIMPLE_TEXT_OUTPUT_MODE } +func (p *EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL) Reset(extendedVerification BOOLEAN) EFI_STATUS { + return UefiCall2(p.reset, uintptr(unsafe.Pointer(p)), booleanArg(extendedVerification)) +} + func (p *EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL) OutputString(s *CHAR16) EFI_STATUS { return UefiCall2(p.outputString, uintptr(unsafe.Pointer(p)), uintptr(unsafe.Pointer(s))) } + +func (p *EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL) TestString(s *CHAR16) EFI_STATUS { + return UefiCall2(p.testString, uintptr(unsafe.Pointer(p)), uintptr(unsafe.Pointer(s))) +} + +func (p *EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL) QueryMode(modeNumber UINTN, columns *UINTN, rows *UINTN) EFI_STATUS { + return UefiCall4( + p.queryMode, + uintptr(unsafe.Pointer(p)), + uintptr(modeNumber), + uintptr(unsafe.Pointer(columns)), + uintptr(unsafe.Pointer(rows)), + ) +} + +func (p *EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL) SetMode(modeNumber UINTN) EFI_STATUS { + return UefiCall2(p.setMode, uintptr(unsafe.Pointer(p)), uintptr(modeNumber)) +} + +func (p *EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL) SetAttribute(attribute UINTN) EFI_STATUS { + return UefiCall2(p.setAttribute, uintptr(unsafe.Pointer(p)), uintptr(attribute)) +} + +func (p *EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL) ClearScreen() EFI_STATUS { + return UefiCall1(p.clearScreen, uintptr(unsafe.Pointer(p))) +} + +func (p *EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL) SetCursorPosition(column UINTN, row UINTN) EFI_STATUS { + return UefiCall3(p.setCursorPosition, uintptr(unsafe.Pointer(p)), uintptr(column), uintptr(row)) +} + +func (p *EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL) EnableCursor(visible BOOLEAN) EFI_STATUS { + return UefiCall2(p.enableCursor, uintptr(unsafe.Pointer(p)), booleanArg(visible)) +} From f62bc0e97ffde3a88827161c4eee9ad7f321c700 Mon Sep 17 00:00:00 2001 From: Matthew Hiles Date: Mon, 4 May 2026 11:44:44 -0400 Subject: [PATCH 15/21] runtime: fix UEFI sleep tick conversion --- src/runtime/runtime_uefi.go | 15 ++++++++++++++- src/runtime/scheduler_cooperative.go | 3 +++ src/runtime/sleep_custom_default.go | 7 +++++++ src/runtime/sleep_custom_uefi.go | 14 ++++++++++++++ src/runtime/wait_other.go | 2 +- 5 files changed, 39 insertions(+), 2 deletions(-) create mode 100644 src/runtime/sleep_custom_default.go create mode 100644 src/runtime/sleep_custom_uefi.go diff --git a/src/runtime/runtime_uefi.go b/src/runtime/runtime_uefi.go index 5f77fdfa71..b4de782e48 100644 --- a/src/runtime/runtime_uefi.go +++ b/src/runtime/runtime_uefi.go @@ -18,6 +18,9 @@ var stackTop uintptr var allocatePagesAddress uefi.EFI_PHYSICAL_ADDRESS var consoleInEx *uefi.EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL var consoleIn *uefi.EFI_SIMPLE_TEXT_INPUT_PROTOCOL +var waitForEventsFunction = func() { + uefi.CpuPause() +} func ticks() timeUnit { return timeUnit(uefi.Ticks()) @@ -28,7 +31,9 @@ func nanosecondsToTicks(ns int64) timeUnit { if frequency == 0 { return timeUnit(ns) } - return timeUnit(ns * frequency / 1000000000) + seconds := ns / 1000000000 + remainder := ns % 1000000000 + return timeUnit(seconds*frequency + (remainder*frequency)/1000000000) } func ticksToNanoseconds(t timeUnit) int64 { @@ -108,6 +113,14 @@ func init() { } } +func SetWaitForEvents(f func()) { + waitForEventsFunction = f +} + +func waitForEvents() { + waitForEventsFunction() +} + //go:noinline func runMain() { run() diff --git a/src/runtime/scheduler_cooperative.go b/src/runtime/scheduler_cooperative.go index 2c2bf09926..18674f8099 100644 --- a/src/runtime/scheduler_cooperative.go +++ b/src/runtime/scheduler_cooperative.go @@ -235,6 +235,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 f1487e3969..3ba8ae6818 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 +//go:build !tinygo.riscv && !cortexm && !(linux && !baremetal && !tinygo.wasm && !nintendoswitch) && !darwin && !uefi package runtime From d108e99cbc3aa0723a3f5e8e6c3b3cd2fcc2d499 Mon Sep 17 00:00:00 2001 From: Matthew Hiles Date: Mon, 4 May 2026 11:42:23 -0400 Subject: [PATCH 16/21] machine/uefi: make text input waits cooperative --- src/machine/uefi/runtime.go | 34 +++++++++++++----------------- src/machine/uefi/simple_text_in.go | 24 ++++++--------------- src/machine/uefi/util.go | 29 +++++++++++++++++++++++++ 3 files changed, 50 insertions(+), 37 deletions(-) create mode 100644 src/machine/uefi/util.go diff --git a/src/machine/uefi/runtime.go b/src/machine/uefi/runtime.go index 118bc6881e..e833ec44fe 100644 --- a/src/machine/uefi/runtime.go +++ b/src/machine/uefi/runtime.go @@ -1,23 +1,19 @@ package uefi -import "unsafe" +//go:linkname gosched runtime.Gosched +func gosched() -var imageHandle EFI_HANDLE -var systemTable *EFI_SYSTEM_TABLE - -func Init(handle uintptr, table uintptr) { - imageHandle = EFI_HANDLE(handle) - systemTable = (*EFI_SYSTEM_TABLE)(unsafe.Pointer(table)) -} - -func ST() *EFI_SYSTEM_TABLE { - return systemTable -} - -func BS() *EFI_BOOT_SERVICES { - return systemTable.BootServices -} - -func GetImageHandle() EFI_HANDLE { - return imageHandle +// WaitForEvent blocks while yielding to the TinyGo scheduler so other +// goroutines can continue to run. +func WaitForEvent(event EFI_EVENT) EFI_STATUS { + for { + status := BS().CheckEvent(event) + if status == EFI_SUCCESS { + return EFI_SUCCESS + } + if status != EFI_NOT_READY { + return status + } + gosched() + } } diff --git a/src/machine/uefi/simple_text_in.go b/src/machine/uefi/simple_text_in.go index d76dfb59b1..c55bad3aff 100644 --- a/src/machine/uefi/simple_text_in.go +++ b/src/machine/uefi/simple_text_in.go @@ -56,15 +56,9 @@ func (p *EFI_SIMPLE_TEXT_INPUT_PROTOCOL) GetKey() (EFI_INPUT_KEY, EFI_STATUS) { var key EFI_INPUT_KEY var status EFI_STATUS for { - for { - status = BS().CheckEvent(p.WaitForKey) - if status == EFI_SUCCESS { - break - } - if status != EFI_NOT_READY { - return key, status - } - CpuPause() + status = WaitForEvent(p.WaitForKey) + if status != EFI_SUCCESS { + return key, status } status = p.ReadKeyStroke(&key) if status == EFI_SUCCESS { @@ -112,15 +106,9 @@ func (p *EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL) GetKey() (EFI_KEY_DATA, EFI_STATUS) var key EFI_KEY_DATA var status EFI_STATUS for { - for { - status = BS().CheckEvent(p.WaitForKeyEx) - if status == EFI_SUCCESS { - break - } - if status != EFI_NOT_READY { - return key, status - } - CpuPause() + status = WaitForEvent(p.WaitForKeyEx) + if status != EFI_SUCCESS { + return key, status } status = p.ReadKeyStroke(&key) if status == EFI_SUCCESS { diff --git a/src/machine/uefi/util.go b/src/machine/uefi/util.go new file mode 100644 index 0000000000..a37672a8ec --- /dev/null +++ b/src/machine/uefi/util.go @@ -0,0 +1,29 @@ +//go:build uefi + +package uefi + +import "unsafe" + +var systemTable *EFI_SYSTEM_TABLE +var imageHandle uintptr + +//go:nobounds +func Init(argImageHandle uintptr, argSystemTable uintptr) { + systemTable = (*EFI_SYSTEM_TABLE)(unsafe.Pointer(argSystemTable)) + imageHandle = argImageHandle +} + +func ST() *EFI_SYSTEM_TABLE { + return systemTable +} + +func BS() *EFI_BOOT_SERVICES { + if systemTable == nil { + return nil + } + return systemTable.BootServices +} + +func GetImageHandle() EFI_HANDLE { + return EFI_HANDLE(imageHandle) +} From 43029f51fe82b73b41364add342c95e3dbed14ca Mon Sep 17 00:00:00 2001 From: Matthew Hiles Date: Sat, 6 Jun 2026 15:40:48 -0400 Subject: [PATCH 17/21] address TestClangAttributes/uefi-amd64 failure --- targets/uefi-amd64.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/targets/uefi-amd64.json b/targets/uefi-amd64.json index 3ec6f1235d..d82b5d3949 100644 --- a/targets/uefi-amd64.json +++ b/targets/uefi-amd64.json @@ -2,7 +2,7 @@ "build-tags": ["uefi", "baremetal", "linux", "amd64"], "llvm-target": "x86_64-unknown-windows-gnu", "cpu": "x86-64", - "features": "+cx8,+fxsr,+mmx,+sse,+sse2,+x87", + "features": "+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87", "goos": "linux", "goarch": "amd64", "gc": "leaking", From c55215328d44fabd4c52d6e754bcfa1643a22055 Mon Sep 17 00:00:00 2001 From: Matthew Hiles Date: Sat, 6 Jun 2026 15:43:54 -0400 Subject: [PATCH 18/21] address TestConfigLinkerFlavor bug --- compileopts/config.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/compileopts/config.go b/compileopts/config.go index 71ebebb2c6..a007c3f4aa 100644 --- a/compileopts/config.go +++ b/compileopts/config.go @@ -472,7 +472,11 @@ func (c *Config) LinkerFlavor() string { if c.Target.LinkerFlavor != "" { return c.Target.LinkerFlavor } - switch c.GOOS() { + goos := c.GOOS() + if goos == "" { + goos = c.Options.GOOS + } + switch goos { case "windows": return "coff" case "darwin": From 119691c2af32a41a65166e7f53b82fccc162e4ac Mon Sep 17 00:00:00 2001 From: Matthew Hiles Date: Sun, 7 Jun 2026 17:08:50 -0400 Subject: [PATCH 19/21] uefi: move raw bindings to device package --- examples/uefi-gop/main.go | 2 +- examples/uefi-key/main.go | 2 +- examples/uefi-time/main.go | 2 +- src/{machine => device}/uefi/arch_x86.go | 0 src/{machine => device}/uefi/asm_amd64.S | 0 src/{machine => device}/uefi/call.go | 0 src/{machine => device}/uefi/char16.go | 0 src/{machine => device}/uefi/char16_test.go | 0 src/{machine => device}/uefi/clock.go | 0 src/{machine => device}/uefi/device_path.go | 0 src/{machine => device}/uefi/efidef.go | 0 src/{machine => device}/uefi/efistatus.go | 0 .../uefi/graphics_output_protocol.go | 0 src/{machine => device}/uefi/loaded_image.go | 0 src/{machine => device}/uefi/runtime.go | 0 .../uefi/simple_text_in.go | 0 .../uefi/simple_text_out.go | 0 src/{machine => device}/uefi/tables.go | 0 src/{machine => device}/uefi/text_input.go | 0 src/{machine => device}/uefi/text_output.go | 0 src/{machine => device}/uefi/time.go | 0 src/{machine => device}/uefi/util.go | 0 src/machine/machine_uefi.go | 165 ++++++++++++++++++ src/runtime/os_uefi.go | 2 +- src/runtime/runtime_uefi.go | 2 +- targets/uefi-amd64.json | 2 +- 26 files changed, 171 insertions(+), 6 deletions(-) rename src/{machine => device}/uefi/arch_x86.go (100%) rename src/{machine => device}/uefi/asm_amd64.S (100%) rename src/{machine => device}/uefi/call.go (100%) rename src/{machine => device}/uefi/char16.go (100%) rename src/{machine => device}/uefi/char16_test.go (100%) rename src/{machine => device}/uefi/clock.go (100%) rename src/{machine => device}/uefi/device_path.go (100%) rename src/{machine => device}/uefi/efidef.go (100%) rename src/{machine => device}/uefi/efistatus.go (100%) rename src/{machine => device}/uefi/graphics_output_protocol.go (100%) rename src/{machine => device}/uefi/loaded_image.go (100%) rename src/{machine => device}/uefi/runtime.go (100%) rename src/{machine => device}/uefi/simple_text_in.go (100%) rename src/{machine => device}/uefi/simple_text_out.go (100%) rename src/{machine => device}/uefi/tables.go (100%) rename src/{machine => device}/uefi/text_input.go (100%) rename src/{machine => device}/uefi/text_output.go (100%) rename src/{machine => device}/uefi/time.go (100%) rename src/{machine => device}/uefi/util.go (100%) create mode 100644 src/machine/machine_uefi.go diff --git a/examples/uefi-gop/main.go b/examples/uefi-gop/main.go index 2e4f997634..05587b0f53 100644 --- a/examples/uefi-gop/main.go +++ b/examples/uefi-gop/main.go @@ -1,6 +1,6 @@ package main -import "machine/uefi" +import uefi "machine" func main() { conOut := uefi.ConsoleOut() diff --git a/examples/uefi-key/main.go b/examples/uefi-key/main.go index 8500ff561d..f3cbdc8396 100644 --- a/examples/uefi-key/main.go +++ b/examples/uefi-key/main.go @@ -1,6 +1,6 @@ package main -import "machine/uefi" +import uefi "machine" func main() { conOut := uefi.ConsoleOut() diff --git a/examples/uefi-time/main.go b/examples/uefi-time/main.go index 013c58671a..e3cae39b6b 100644 --- a/examples/uefi-time/main.go +++ b/examples/uefi-time/main.go @@ -2,7 +2,7 @@ package main import ( "fmt" - "machine/uefi" + uefi "machine" "time" ) diff --git a/src/machine/uefi/arch_x86.go b/src/device/uefi/arch_x86.go similarity index 100% rename from src/machine/uefi/arch_x86.go rename to src/device/uefi/arch_x86.go diff --git a/src/machine/uefi/asm_amd64.S b/src/device/uefi/asm_amd64.S similarity index 100% rename from src/machine/uefi/asm_amd64.S rename to src/device/uefi/asm_amd64.S diff --git a/src/machine/uefi/call.go b/src/device/uefi/call.go similarity index 100% rename from src/machine/uefi/call.go rename to src/device/uefi/call.go diff --git a/src/machine/uefi/char16.go b/src/device/uefi/char16.go similarity index 100% rename from src/machine/uefi/char16.go rename to src/device/uefi/char16.go diff --git a/src/machine/uefi/char16_test.go b/src/device/uefi/char16_test.go similarity index 100% rename from src/machine/uefi/char16_test.go rename to src/device/uefi/char16_test.go diff --git a/src/machine/uefi/clock.go b/src/device/uefi/clock.go similarity index 100% rename from src/machine/uefi/clock.go rename to src/device/uefi/clock.go diff --git a/src/machine/uefi/device_path.go b/src/device/uefi/device_path.go similarity index 100% rename from src/machine/uefi/device_path.go rename to src/device/uefi/device_path.go diff --git a/src/machine/uefi/efidef.go b/src/device/uefi/efidef.go similarity index 100% rename from src/machine/uefi/efidef.go rename to src/device/uefi/efidef.go diff --git a/src/machine/uefi/efistatus.go b/src/device/uefi/efistatus.go similarity index 100% rename from src/machine/uefi/efistatus.go rename to src/device/uefi/efistatus.go diff --git a/src/machine/uefi/graphics_output_protocol.go b/src/device/uefi/graphics_output_protocol.go similarity index 100% rename from src/machine/uefi/graphics_output_protocol.go rename to src/device/uefi/graphics_output_protocol.go diff --git a/src/machine/uefi/loaded_image.go b/src/device/uefi/loaded_image.go similarity index 100% rename from src/machine/uefi/loaded_image.go rename to src/device/uefi/loaded_image.go diff --git a/src/machine/uefi/runtime.go b/src/device/uefi/runtime.go similarity index 100% rename from src/machine/uefi/runtime.go rename to src/device/uefi/runtime.go diff --git a/src/machine/uefi/simple_text_in.go b/src/device/uefi/simple_text_in.go similarity index 100% rename from src/machine/uefi/simple_text_in.go rename to src/device/uefi/simple_text_in.go diff --git a/src/machine/uefi/simple_text_out.go b/src/device/uefi/simple_text_out.go similarity index 100% rename from src/machine/uefi/simple_text_out.go rename to src/device/uefi/simple_text_out.go diff --git a/src/machine/uefi/tables.go b/src/device/uefi/tables.go similarity index 100% rename from src/machine/uefi/tables.go rename to src/device/uefi/tables.go diff --git a/src/machine/uefi/text_input.go b/src/device/uefi/text_input.go similarity index 100% rename from src/machine/uefi/text_input.go rename to src/device/uefi/text_input.go diff --git a/src/machine/uefi/text_output.go b/src/device/uefi/text_output.go similarity index 100% rename from src/machine/uefi/text_output.go rename to src/device/uefi/text_output.go diff --git a/src/machine/uefi/time.go b/src/device/uefi/time.go similarity index 100% rename from src/machine/uefi/time.go rename to src/device/uefi/time.go diff --git a/src/machine/uefi/util.go b/src/device/uefi/util.go similarity index 100% rename from src/machine/uefi/util.go rename to src/device/uefi/util.go diff --git a/src/machine/machine_uefi.go b/src/machine/machine_uefi.go new file mode 100644 index 0000000000..31cb0c8c0d --- /dev/null +++ b/src/machine/machine_uefi.go @@ -0,0 +1,165 @@ +//go:build uefi + +package machine + +import ( + deviceuefi "device/uefi" +) + +const deviceName = "UEFI" + +type ( + EFI_STATUS = deviceuefi.EFI_STATUS + EFI_TIME = deviceuefi.EFI_TIME + EFI_TIME_CAPABILITIES = deviceuefi.EFI_TIME_CAPABILITIES + EFI_KEY_DATA = deviceuefi.EFI_KEY_DATA + EFI_INPUT_KEY = deviceuefi.EFI_INPUT_KEY + EFI_KEY_STATE = deviceuefi.EFI_KEY_STATE + EFI_KEY_TOGGLE_STATE = deviceuefi.EFI_KEY_TOGGLE_STATE + EFI_GUID = deviceuefi.EFI_GUID + UINTN = deviceuefi.UINTN + + EFI_GRAPHICS_OUTPUT_PROTOCOL = deviceuefi.EFI_GRAPHICS_OUTPUT_PROTOCOL + EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE = deviceuefi.EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE + EFI_GRAPHICS_OUTPUT_MODE_INFORMATION = deviceuefi.EFI_GRAPHICS_OUTPUT_MODE_INFORMATION + EFI_GRAPHICS_OUTPUT_BLT_PIXEL = deviceuefi.EFI_GRAPHICS_OUTPUT_BLT_PIXEL + EFI_GRAPHICS_OUTPUT_BLT_OPERATION = deviceuefi.EFI_GRAPHICS_OUTPUT_BLT_OPERATION + EFI_GRAPHICS_PIXEL_FORMAT = deviceuefi.EFI_GRAPHICS_PIXEL_FORMAT + EFI_PIXEL_BITMASK = deviceuefi.EFI_PIXEL_BITMASK + + EFI_LOADED_IMAGE_PROTOCOL = deviceuefi.EFI_LOADED_IMAGE_PROTOCOL + EFI_DEVICE_PATH_PROTOCOL = deviceuefi.EFI_DEVICE_PATH_PROTOCOL + + TextOutput = deviceuefi.TextOutput + TextInput = deviceuefi.TextInput + TextInputSource = deviceuefi.TextInputSource + + 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 + + TextInputNone = deviceuefi.TextInputNone + TextInputSimpleTextInputEx = deviceuefi.TextInputSimpleTextInputEx + TextInputSimpleTextInput = deviceuefi.TextInputSimpleTextInput + + BltVideoFill = deviceuefi.BltVideoFill + BltVideoToBltBuffer = deviceuefi.BltVideoToBltBuffer + BltBufferToVideo = deviceuefi.BltBufferToVideo + BltVideoToVideo = deviceuefi.BltVideoToVideo + BltOperationMax = deviceuefi.BltOperationMax + + PixelRedGreenBlueReserved8BitPerColor = deviceuefi.PixelRedGreenBlueReserved8BitPerColor + PixelBlueGreenRedReserved8BitPerColor = deviceuefi.PixelBlueGreenRedReserved8BitPerColor + PixelBitMask = deviceuefi.PixelBitMask + PixelBltOnly = deviceuefi.PixelBltOnly + PixelFormatMax = deviceuefi.PixelFormatMax +) + +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 GetTime() (EFI_TIME, EFI_STATUS) { + return deviceuefi.GetTime() +} + +func ConsoleOut() *TextOutput { + return deviceuefi.ConsoleOut() +} + +func StandardError() *TextOutput { + return deviceuefi.StandardError() +} + +func ConsoleInput() (*TextInput, error) { + return deviceuefi.ConsoleInput() +} + +func GraphicsOutputProtocol() (*EFI_GRAPHICS_OUTPUT_PROTOCOL, EFI_STATUS) { + return deviceuefi.GraphicsOutputProtocol() +} + +func GetLoadedImageProtocol() (*EFI_LOADED_IMAGE_PROTOCOL, EFI_STATUS) { + return deviceuefi.GetLoadedImageProtocol() +} + +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/os_uefi.go b/src/runtime/os_uefi.go index 81dca5b336..fa16d38f63 100644 --- a/src/runtime/os_uefi.go +++ b/src/runtime/os_uefi.go @@ -3,7 +3,7 @@ package runtime import ( - "machine/uefi" + "device/uefi" "unsafe" ) diff --git a/src/runtime/runtime_uefi.go b/src/runtime/runtime_uefi.go index b4de782e48..6d6bd10d95 100644 --- a/src/runtime/runtime_uefi.go +++ b/src/runtime/runtime_uefi.go @@ -2,7 +2,7 @@ package runtime -import "machine/uefi" +import "device/uefi" //go:linkname procPin sync/atomic.runtime_procPin func procPin() { diff --git a/targets/uefi-amd64.json b/targets/uefi-amd64.json index d82b5d3949..8f309e9d65 100644 --- a/targets/uefi-amd64.json +++ b/targets/uefi-amd64.json @@ -34,7 +34,7 @@ ], "extra-files": [ "src/device/x86/cpu_amd64.S", - "src/machine/uefi/asm_amd64.S", + "src/device/uefi/asm_amd64.S", "src/runtime/asm_amd64_windows.S" ], "gdb": ["gdb-multiarch", "gdb"] From cc08fe55ef8c151304246a18ba81a098f1441b6c Mon Sep 17 00:00:00 2001 From: Matthew Hiles Date: Sun, 7 Jun 2026 17:24:07 -0400 Subject: [PATCH 20/21] strip down implementation to bare minimum --- examples/uefi-gop/main.go | 79 ----------- examples/uefi-key/main.go | 89 ------------- examples/uefi-time/main.go | 80 ------------ src/device/uefi/char16_test.go | 60 --------- src/device/uefi/device_path.go | 7 - src/device/uefi/graphics_output_protocol.go | 124 ------------------ src/device/uefi/loaded_image.go | 37 ------ src/device/uefi/runtime.go | 19 --- src/device/uefi/simple_text_in.go | 138 -------------------- src/device/uefi/simple_text_out.go | 7 + src/device/uefi/tables.go | 6 +- src/device/uefi/text_input.go | 132 ------------------- src/device/uefi/time.go | 101 -------------- src/machine/machine_uefi.go | 58 +------- src/runtime/os_uefi.go | 30 ----- src/runtime/runtime_uefi.go | 55 +------- src/runtime/runtime_uefi_schednone.go | 32 +++++ 17 files changed, 43 insertions(+), 1011 deletions(-) delete mode 100644 examples/uefi-gop/main.go delete mode 100644 examples/uefi-key/main.go delete mode 100644 examples/uefi-time/main.go delete mode 100644 src/device/uefi/char16_test.go delete mode 100644 src/device/uefi/device_path.go delete mode 100644 src/device/uefi/graphics_output_protocol.go delete mode 100644 src/device/uefi/loaded_image.go delete mode 100644 src/device/uefi/runtime.go delete mode 100644 src/device/uefi/simple_text_in.go delete mode 100644 src/device/uefi/text_input.go delete mode 100644 src/device/uefi/time.go delete mode 100644 src/runtime/os_uefi.go create mode 100644 src/runtime/runtime_uefi_schednone.go diff --git a/examples/uefi-gop/main.go b/examples/uefi-gop/main.go deleted file mode 100644 index 05587b0f53..0000000000 --- a/examples/uefi-gop/main.go +++ /dev/null @@ -1,79 +0,0 @@ -package main - -import uefi "machine" - -func main() { - conOut := uefi.ConsoleOut() - conIn, _ := uefi.ConsoleInput() - - gop, status := uefi.GraphicsOutputProtocol() - if status != uefi.EFI_SUCCESS { - _, _ = conOut.WriteString("GraphicsOutputProtocol unavailable\r\n") - _, _ = conOut.WriteString("EFI_STATUS: ") - writeUint(conOut, uint64(status)) - _, _ = conOut.WriteString("\r\n") - return - } - - if gop.Mode == nil || gop.Mode.Info == nil { - _, _ = conOut.WriteString("GOP mode info unavailable\r\n") - return - } - - info := gop.Mode.Info - _, _ = conOut.WriteString("GOP current mode\r\n") - _, _ = conOut.WriteString("Mode: ") - writeUint(conOut, uint64(gop.Mode.Mode)) - _, _ = conOut.WriteString("\r\nResolution: ") - writeUint(conOut, uint64(info.HorizontalResolution)) - _, _ = conOut.WriteString("x") - writeUint(conOut, uint64(info.VerticalResolution)) - _, _ = conOut.WriteString("\r\nPixelFormat: ") - writeUint(conOut, uint64(info.PixelFormat)) - _, _ = conOut.WriteString("\r\nFramebuffer bytes: ") - writeUint(conOut, uint64(gop.Mode.FrameBufferSize)) - _, _ = conOut.WriteString("\r\nPress ESC to exit after draw\r\n") - - width := uefi.UINTN(info.HorizontalResolution) - height := uefi.UINTN(info.VerticalResolution) - halfWidth := width / 2 - halfHeight := height / 2 - - fillRect(gop, 0, 0, halfWidth, halfHeight, uefi.EFI_GRAPHICS_OUTPUT_BLT_PIXEL{Red: 0xC0}) - fillRect(gop, halfWidth, 0, width-halfWidth, halfHeight, uefi.EFI_GRAPHICS_OUTPUT_BLT_PIXEL{Green: 0xC0}) - fillRect(gop, 0, halfHeight, halfWidth, height-halfHeight, uefi.EFI_GRAPHICS_OUTPUT_BLT_PIXEL{Blue: 0xC0}) - fillRect(gop, halfWidth, halfHeight, width-halfWidth, height-halfHeight, uefi.EFI_GRAPHICS_OUTPUT_BLT_PIXEL{Red: 0xC0, Green: 0xC0, Blue: 0xC0}) - - if conIn == nil { - return - } - for { - key, _, err := conIn.ReadKeyWithSource() - if err != nil { - return - } - if key.Key.ScanCode == 23 { - return - } - } -} - -func fillRect(gop *uefi.EFI_GRAPHICS_OUTPUT_PROTOCOL, x, y, width, height uefi.UINTN, color uefi.EFI_GRAPHICS_OUTPUT_BLT_PIXEL) { - if gop.Blt(&color, uefi.BltVideoFill, 0, 0, x, y, width, height, 0) != uefi.EFI_SUCCESS { - return - } -} - -func writeUint(conOut *uefi.TextOutput, v uint64) { - var buf [32]byte - i := len(buf) - for { - i-- - buf[i] = byte('0' + v%10) - v /= 10 - if v == 0 { - break - } - } - _, _ = conOut.WriteString(string(buf[i:])) -} diff --git a/examples/uefi-key/main.go b/examples/uefi-key/main.go deleted file mode 100644 index f3cbdc8396..0000000000 --- a/examples/uefi-key/main.go +++ /dev/null @@ -1,89 +0,0 @@ -package main - -import uefi "machine" - -func main() { - conOut := uefi.ConsoleOut() - conIn, err := uefi.ConsoleInput() - if err != nil { - _, _ = conOut.WriteString("ConsoleInput unavailable\r\n") - return - } - - if conIn.HasTextInputEx() { - _, _ = conOut.WriteString("STIEx available\r\n") - } - if conIn.HasTextInput() { - _, _ = conOut.WriteString("STIP available\r\n") - } - _, _ = conOut.WriteString("Press keys, ESC to exit...\r\n") - - for { - key, source, err := conIn.ReadKeyWithSource() - if err != nil { - _, _ = conOut.WriteString("ReadKey failed\r\n") - _, _ = conOut.WriteString("Source: ") - _, _ = conOut.WriteString(source.String()) - _, _ = conOut.WriteString("\r\n") - if statusErr, ok := err.(*uefi.Error); ok { - _, _ = conOut.WriteString("EFI_STATUS: ") - writePaddedUint(conOut, uint64(statusErr.Status()), 1) - _, _ = conOut.WriteString("\r\n") - } - return - } - - _, _ = conOut.WriteString("Source: ") - _, _ = conOut.WriteString(source.String()) - _, _ = conOut.WriteString("\r\nScanCode: ") - writePaddedUint(conOut, uint64(key.Key.ScanCode), 1) - _, _ = conOut.WriteString("\r\nUnicode: ") - writePaddedUint(conOut, uint64(key.Key.UnicodeChar), 1) - _, _ = conOut.WriteString("\r\nShiftState: ") - writeHexUint(conOut, uint64(key.KeyState.KeyShiftState), 8) - _, _ = conOut.WriteString("\r\nToggleState: ") - writeHexUint(conOut, uint64(key.KeyState.KeyToggleState), 2) - _, _ = conOut.WriteString("\r\n\r\n") - - if key.Key.ScanCode == 23 { - return - } - } -} - -func writePaddedUint(conOut *uefi.TextOutput, v uint64, width int) { - var buf [32]byte - i := len(buf) - for { - i-- - buf[i] = byte('0' + v%10) - v /= 10 - if v == 0 { - break - } - } - for len(buf)-i < width { - i-- - buf[i] = '0' - } - _, _ = conOut.WriteString(string(buf[i:])) -} - -func writeHexUint(conOut *uefi.TextOutput, v uint64, width int) { - const digits = "0123456789ABCDEF" - var buf [32]byte - i := len(buf) - for { - i-- - buf[i] = digits[v&0xF] - v >>= 4 - if v == 0 { - break - } - } - for len(buf)-i < width { - i-- - buf[i] = '0' - } - _, _ = conOut.WriteString(string(buf[i:])) -} diff --git a/examples/uefi-time/main.go b/examples/uefi-time/main.go deleted file mode 100644 index e3cae39b6b..0000000000 --- a/examples/uefi-time/main.go +++ /dev/null @@ -1,80 +0,0 @@ -package main - -import ( - "fmt" - uefi "machine" - "time" -) - -func main() { - conOut := uefi.ConsoleOut() - - writeString(conOut, "UEFI time probe\r\n") - - efiTime, status := uefi.GetTime() - if status != uefi.EFI_SUCCESS { - writeString(conOut, "GetTime failed\r\n") - return - } - - sec, nsec := efiTime.GetEpoch() - writeString(conOut, "EFI: ") - writePaddedUint(conOut, uint64(efiTime.Year), 4) - writeString(conOut, "-") - writePaddedUint(conOut, uint64(efiTime.Month), 2) - writeString(conOut, "-") - writePaddedUint(conOut, uint64(efiTime.Day), 2) - writeString(conOut, " ") - writePaddedUint(conOut, uint64(efiTime.Hour), 2) - writeString(conOut, ":") - writePaddedUint(conOut, uint64(efiTime.Minute), 2) - writeString(conOut, ":") - writePaddedUint(conOut, uint64(efiTime.Second), 2) - writeString(conOut, ".") - writePaddedUint(conOut, uint64(efiTime.Nanosecond), 9) - writeString(conOut, "\r\n") - - writeString(conOut, "Epoch: ") - writeInt(conOut, sec) - writeString(conOut, " s, ") - writeInt(conOut, int64(nsec)) - writeString(conOut, " ns\r\n") - - now := time.Now() - writeString(conOut, "Go Unix: ") - writeInt(conOut, now.Unix()) - writeString(conOut, "\r\n") - - fmt.Fprintln(conOut, "fmt via io.Writer works") -} - -func writeString(conOut *uefi.TextOutput, s string) { - _, _ = conOut.WriteString(s) -} - -func writePaddedUint(conOut *uefi.TextOutput, v uint64, width int) { - var buf [32]byte - i := len(buf) - for { - i-- - buf[i] = byte('0' + v%10) - v /= 10 - if v == 0 { - break - } - } - for len(buf)-i < width { - i-- - buf[i] = '0' - } - writeString(conOut, string(buf[i:])) -} - -func writeInt(conOut *uefi.TextOutput, v int64) { - if v < 0 { - writeString(conOut, "-") - writePaddedUint(conOut, uint64(-v), 1) - return - } - writePaddedUint(conOut, uint64(v), 1) -} diff --git a/src/device/uefi/char16_test.go b/src/device/uefi/char16_test.go deleted file mode 100644 index e0800cc057..0000000000 --- a/src/device/uefi/char16_test.go +++ /dev/null @@ -1,60 +0,0 @@ -package uefi - -import "testing" - -func TestStringToCHAR16RoundTrip(t *testing.T) { - input := "Hello, δΈ–η•Œ" - encoded := StringToCHAR16(input) - - if got := CHAR16ToString(encoded); got != input { - t.Fatalf("round trip mismatch: got %q want %q", got, input) - } -} - -func TestStringToCHAR16UsesSurrogatePairs(t *testing.T) { - input := "πŸ™‚" - encoded := StringToCHAR16(input) - want := []CHAR16{0xD83D, 0xDE42} - - if len(encoded) != len(want) { - t.Fatalf("encoded length mismatch: got %d want %d", len(encoded), len(want)) - } - for i := range want { - if encoded[i] != want[i] { - t.Fatalf("encoded[%d] mismatch: got %#x want %#x", i, encoded[i], want[i]) - } - } -} - -func TestStringToCHAR16ZTerminates(t *testing.T) { - encoded := StringToCHAR16Z("abc") - - if len(encoded) != 4 { - t.Fatalf("terminated length mismatch: got %d want 4", len(encoded)) - } - if encoded[len(encoded)-1] != 0 { - t.Fatalf("missing NUL terminator: got %#x", encoded[len(encoded)-1]) - } - if got := CHAR16PtrToString(&encoded[0]); got != "abc" { - t.Fatalf("pointer round trip mismatch: got %q want %q", got, "abc") - } -} - -func TestBytesToCHAR16RoundTrip(t *testing.T) { - input := []byte("UEFI Β΅") - encoded := BytesToCHAR16(input) - - got := CHAR16ToBytes(encoded) - if string(got) != string(input) { - t.Fatalf("byte round trip mismatch: got %q want %q", got, input) - } -} - -func TestCHAR16PtrHelpersHandleNil(t *testing.T) { - if got := CHAR16PtrToString(nil); got != "" { - t.Fatalf("CHAR16PtrToString(nil) = %q, want empty string", got) - } - if got := CHAR16PtrLenToString(nil, 3); got != "" { - t.Fatalf("CHAR16PtrLenToString(nil, 3) = %q, want empty string", got) - } -} diff --git a/src/device/uefi/device_path.go b/src/device/uefi/device_path.go deleted file mode 100644 index f66c28fda9..0000000000 --- a/src/device/uefi/device_path.go +++ /dev/null @@ -1,7 +0,0 @@ -package uefi - -type EFI_DEVICE_PATH_PROTOCOL struct { - Type uint8 - SubType uint8 - Length [2]uint8 -} diff --git a/src/device/uefi/graphics_output_protocol.go b/src/device/uefi/graphics_output_protocol.go deleted file mode 100644 index 2ed6802a1a..0000000000 --- a/src/device/uefi/graphics_output_protocol.go +++ /dev/null @@ -1,124 +0,0 @@ -package uefi - -import "unsafe" - -var GraphicsOutputProtocolGUID = EFI_GUID{ - 0x9042a9de, 0x23dc, 0x4a38, - [8]byte{0x96, 0xfb, 0x7a, 0xde, 0xd0, 0x80, 0x51, 0x6a}, -} - -type EFI_GRAPHICS_PIXEL_FORMAT uint32 - -const ( - PixelRedGreenBlueReserved8BitPerColor EFI_GRAPHICS_PIXEL_FORMAT = iota - PixelBlueGreenRedReserved8BitPerColor - PixelBitMask - PixelBltOnly - PixelFormatMax -) - -type EFI_GRAPHICS_OUTPUT_BLT_OPERATION uint32 - -const ( - BltVideoFill EFI_GRAPHICS_OUTPUT_BLT_OPERATION = iota - BltVideoToBltBuffer - BltBufferToVideo - BltVideoToVideo - BltOperationMax -) - -type EFI_PIXEL_BITMASK struct { - RedMask uint32 - GreenMask uint32 - BlueMask uint32 - ReservedMask uint32 -} - -type EFI_GRAPHICS_OUTPUT_MODE_INFORMATION struct { - Version uint32 - HorizontalResolution uint32 - VerticalResolution uint32 - PixelFormat EFI_GRAPHICS_PIXEL_FORMAT - PixelInformation EFI_PIXEL_BITMASK - PixelsPerScanLine uint32 -} - -type EFI_GRAPHICS_OUTPUT_BLT_PIXEL struct { - Blue uint8 - Green uint8 - Red uint8 - Reserved uint8 -} - -type EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE struct { - MaxMode uint32 - Mode uint32 - Info *EFI_GRAPHICS_OUTPUT_MODE_INFORMATION - SizeOfInfo UINTN - FrameBufferBase EFI_PHYSICAL_ADDRESS - FrameBufferSize UINTN -} - -type EFI_GRAPHICS_OUTPUT_PROTOCOL struct { - queryMode uintptr - setMode uintptr - blt uintptr - Mode *EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE -} - -func (p *EFI_GRAPHICS_OUTPUT_PROTOCOL) QueryMode( - modeNumber uint32, - sizeOfInfo *UINTN, - info **EFI_GRAPHICS_OUTPUT_MODE_INFORMATION, -) EFI_STATUS { - return UefiCall4( - p.queryMode, - uintptr(unsafe.Pointer(p)), - uintptr(modeNumber), - uintptr(unsafe.Pointer(sizeOfInfo)), - uintptr(unsafe.Pointer(info)), - ) -} - -func (p *EFI_GRAPHICS_OUTPUT_PROTOCOL) SetMode(modeNumber uint32) EFI_STATUS { - return UefiCall2( - p.setMode, - uintptr(unsafe.Pointer(p)), - uintptr(modeNumber), - ) -} - -func (p *EFI_GRAPHICS_OUTPUT_PROTOCOL) Blt( - bltBuffer *EFI_GRAPHICS_OUTPUT_BLT_PIXEL, - bltOperation EFI_GRAPHICS_OUTPUT_BLT_OPERATION, - sourceX UINTN, - sourceY UINTN, - destinationX UINTN, - destinationY UINTN, - width UINTN, - height UINTN, - delta UINTN, -) EFI_STATUS { - return UefiCall10( - p.blt, - uintptr(unsafe.Pointer(p)), - uintptr(unsafe.Pointer(bltBuffer)), - uintptr(bltOperation), - uintptr(sourceX), - uintptr(sourceY), - uintptr(destinationX), - uintptr(destinationY), - uintptr(width), - uintptr(height), - uintptr(delta), - ) -} - -func GraphicsOutputProtocol() (*EFI_GRAPHICS_OUTPUT_PROTOCOL, EFI_STATUS) { - var iface *EFI_GRAPHICS_OUTPUT_PROTOCOL - status := BS().LocateProtocol(&GraphicsOutputProtocolGUID, nil, unsafe.Pointer(&iface)) - if status != EFI_SUCCESS { - return nil, status - } - return iface, EFI_SUCCESS -} diff --git a/src/device/uefi/loaded_image.go b/src/device/uefi/loaded_image.go deleted file mode 100644 index 0417177e81..0000000000 --- a/src/device/uefi/loaded_image.go +++ /dev/null @@ -1,37 +0,0 @@ -package uefi - -import "unsafe" - -var EFI_LOADED_IMAGE_PROTOCOL_GUID = EFI_GUID{ - 0x5B1B31A1, 0x9562, 0x11D2, - [8]byte{0x8E, 0x3F, 0x00, 0xA0, 0xC9, 0x69, 0x72, 0x3B}, -} - -type EFI_LOADED_IMAGE_PROTOCOL struct { - Revision uint32 - ParentHandle EFI_HANDLE - SystemTable *EFI_SYSTEM_TABLE - DeviceHandle EFI_HANDLE - FilePath *EFI_DEVICE_PATH_PROTOCOL - Reserved *VOID - LoadOptionsSize uint32 - LoadOptions *VOID - ImageBase *VOID - ImageSize uint64 - ImageCodeType EFI_MEMORY_TYPE - ImageDataType EFI_MEMORY_TYPE - unload uintptr -} - -func GetLoadedImageProtocol() (*EFI_LOADED_IMAGE_PROTOCOL, EFI_STATUS) { - var lip *EFI_LOADED_IMAGE_PROTOCOL - status := BS().HandleProtocol( - GetImageHandle(), - &EFI_LOADED_IMAGE_PROTOCOL_GUID, - unsafe.Pointer(&lip), - ) - if status != EFI_SUCCESS { - return nil, status - } - return lip, EFI_SUCCESS -} diff --git a/src/device/uefi/runtime.go b/src/device/uefi/runtime.go deleted file mode 100644 index e833ec44fe..0000000000 --- a/src/device/uefi/runtime.go +++ /dev/null @@ -1,19 +0,0 @@ -package uefi - -//go:linkname gosched runtime.Gosched -func gosched() - -// WaitForEvent blocks while yielding to the TinyGo scheduler so other -// goroutines can continue to run. -func WaitForEvent(event EFI_EVENT) EFI_STATUS { - for { - status := BS().CheckEvent(event) - if status == EFI_SUCCESS { - return EFI_SUCCESS - } - if status != EFI_NOT_READY { - return status - } - gosched() - } -} diff --git a/src/device/uefi/simple_text_in.go b/src/device/uefi/simple_text_in.go deleted file mode 100644 index c55bad3aff..0000000000 --- a/src/device/uefi/simple_text_in.go +++ /dev/null @@ -1,138 +0,0 @@ -package uefi - -import "unsafe" - -func booleanArg(v BOOLEAN) uintptr { - if v { - return 1 - } - return 0 -} - -type EFI_KEY_TOGGLE_STATE uint8 - -const ( - EFI_SCROLL_LOCK_ACTIVE EFI_KEY_TOGGLE_STATE = 0x01 - EFI_NUM_LOCK_ACTIVE EFI_KEY_TOGGLE_STATE = 0x02 - EFI_CAPS_LOCK_ACTIVE EFI_KEY_TOGGLE_STATE = 0x04 - EFI_KEY_STATE_EXPOSED EFI_KEY_TOGGLE_STATE = 0x40 - EFI_TOGGLE_STATE_VALID EFI_KEY_TOGGLE_STATE = 0x80 -) - -const ( - EFI_SHIFT_STATE_VALID = 0x80000000 - EFI_RIGHT_SHIFT_PRESSED = 0x00000001 - EFI_LEFT_SHIFT_PRESSED = 0x00000002 - EFI_RIGHT_CONTROL_PRESSED = 0x00000004 - EFI_LEFT_CONTROL_PRESSED = 0x00000008 - EFI_RIGHT_ALT_PRESSED = 0x00000010 - EFI_LEFT_ALT_PRESSED = 0x00000020 - EFI_RIGHT_LOGO_PRESSED = 0x00000040 - EFI_LEFT_LOGO_PRESSED = 0x00000080 - EFI_MENU_KEY_PRESSED = 0x00000100 - EFI_SYS_REQ_PRESSED = 0x00000200 -) - -type EFI_INPUT_KEY struct { - ScanCode uint16 - UnicodeChar CHAR16 -} - -type EFI_SIMPLE_TEXT_INPUT_PROTOCOL struct { - reset uintptr - readKeyStroke uintptr - WaitForKey EFI_EVENT -} - -func (p *EFI_SIMPLE_TEXT_INPUT_PROTOCOL) Reset(extendedVerification BOOLEAN) EFI_STATUS { - return UefiCall2(p.reset, uintptr(unsafe.Pointer(p)), booleanArg(extendedVerification)) -} - -func (p *EFI_SIMPLE_TEXT_INPUT_PROTOCOL) ReadKeyStroke(key *EFI_INPUT_KEY) EFI_STATUS { - return UefiCall2(p.readKeyStroke, uintptr(unsafe.Pointer(p)), uintptr(unsafe.Pointer(key))) -} - -func (p *EFI_SIMPLE_TEXT_INPUT_PROTOCOL) GetKey() (EFI_INPUT_KEY, EFI_STATUS) { - var key EFI_INPUT_KEY - var status EFI_STATUS - for { - status = WaitForEvent(p.WaitForKey) - if status != EFI_SUCCESS { - return key, status - } - status = p.ReadKeyStroke(&key) - if status == EFI_SUCCESS { - return key, EFI_SUCCESS - } - if status != EFI_NOT_READY { - return key, status - } - } -} - -var SimpleTextInputExProtocolGUID = EFI_GUID{ - 0xdd9e7534, 0x7762, 0x4698, - [8]byte{0x8c, 0x14, 0xf5, 0x85, 0x17, 0xa6, 0x25, 0xaa}, -} - -type EFI_KEY_STATE struct { - KeyShiftState uint32 - KeyToggleState EFI_KEY_TOGGLE_STATE -} - -type EFI_KEY_DATA struct { - Key EFI_INPUT_KEY - KeyState EFI_KEY_STATE -} - -type EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL struct { - resetEx uintptr - readKeyStrokeEx uintptr - WaitForKeyEx EFI_EVENT - setState uintptr - registerKeystrokeNotify uintptr - unregisterKeystrokeNotify uintptr -} - -func (p *EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL) Reset(extendedVerification BOOLEAN) EFI_STATUS { - return UefiCall2(p.resetEx, uintptr(unsafe.Pointer(p)), booleanArg(extendedVerification)) -} - -func (p *EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL) ReadKeyStroke(key *EFI_KEY_DATA) EFI_STATUS { - return UefiCall2(p.readKeyStrokeEx, uintptr(unsafe.Pointer(p)), uintptr(unsafe.Pointer(key))) -} - -func (p *EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL) GetKey() (EFI_KEY_DATA, EFI_STATUS) { - var key EFI_KEY_DATA - var status EFI_STATUS - for { - status = WaitForEvent(p.WaitForKeyEx) - if status != EFI_SUCCESS { - return key, status - } - status = p.ReadKeyStroke(&key) - if status == EFI_SUCCESS { - return key, EFI_SUCCESS - } - if status != EFI_NOT_READY { - return key, status - } - } -} - -func SimpleTextInProtocol() (*EFI_SIMPLE_TEXT_INPUT_PROTOCOL, EFI_STATUS) { - st := ST() - if st == nil || st.ConIn == nil { - return nil, EFI_NOT_FOUND - } - return st.ConIn, EFI_SUCCESS -} - -func SimpleTextInExProtocol() (*EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL, EFI_STATUS) { - var iface *EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL - status := BS().LocateProtocol(&SimpleTextInputExProtocolGUID, nil, unsafe.Pointer(&iface)) - if status != EFI_SUCCESS { - return nil, status - } - return iface, EFI_SUCCESS -} diff --git a/src/device/uefi/simple_text_out.go b/src/device/uefi/simple_text_out.go index 8317b30f03..72e664475d 100644 --- a/src/device/uefi/simple_text_out.go +++ b/src/device/uefi/simple_text_out.go @@ -2,6 +2,13 @@ package uefi import "unsafe" +func booleanArg(v BOOLEAN) uintptr { + if v { + return 1 + } + return 0 +} + type EFI_SIMPLE_TEXT_OUTPUT_MODE struct { MaxMode int32 Mode int32 diff --git a/src/device/uefi/tables.go b/src/device/uefi/tables.go index c90b568747..230cbc8005 100644 --- a/src/device/uefi/tables.go +++ b/src/device/uefi/tables.go @@ -20,10 +20,6 @@ type EFI_RUNTIME_SERVICES struct { queryVariableInfo uintptr } -func (p *EFI_RUNTIME_SERVICES) GetTime(time *EFI_TIME, capabilities *EFI_TIME_CAPABILITIES) EFI_STATUS { - return UefiCall2(p.getTime, uintptr(unsafe.Pointer(time)), uintptr(unsafe.Pointer(capabilities))) -} - type EFI_BOOT_SERVICES struct { Hdr EFI_TABLE_HEADER raiseTPL uintptr @@ -115,7 +111,7 @@ type EFI_SYSTEM_TABLE struct { FirmwareVendor *CHAR16 FirmwareRevision uint32 ConsoleInHandle EFI_HANDLE - ConIn *EFI_SIMPLE_TEXT_INPUT_PROTOCOL + ConIn *VOID ConsoleOutHandle EFI_HANDLE ConOut *EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL StandardErrorHandle EFI_HANDLE diff --git a/src/device/uefi/text_input.go b/src/device/uefi/text_input.go deleted file mode 100644 index e3b022640c..0000000000 --- a/src/device/uefi/text_input.go +++ /dev/null @@ -1,132 +0,0 @@ -package uefi - -import ( - "errors" - "unicode/utf8" -) - -var errNilTextInputProtocol = errors.New("uefi: nil simple text input protocol") - -type TextInputSource uint8 - -const ( - TextInputNone TextInputSource = iota - TextInputSimpleTextInputEx - TextInputSimpleTextInput -) - -type TextInput struct { - protoEx *EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL - proto *EFI_SIMPLE_TEXT_INPUT_PROTOCOL - pending [utf8.UTFMax]byte - start int - end int -} - -func NewTextInput(proto *EFI_SIMPLE_TEXT_INPUT_PROTOCOL) *TextInput { - return &TextInput{proto: proto} -} - -func NewTextInputEx(proto *EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL) *TextInput { - return &TextInput{protoEx: proto} -} - -func ConsoleInput() (*TextInput, error) { - r := &TextInput{} - if protoEx, status := SimpleTextInExProtocol(); status == EFI_SUCCESS { - r.protoEx = protoEx - } - if proto, status := SimpleTextInProtocol(); status == EFI_SUCCESS { - r.proto = proto - } - if r.protoEx == nil && r.proto == nil { - return nil, ErrNotFound - } - return r, nil -} - -func (r *TextInput) Read(p []byte) (int, error) { - if r == nil || (r.protoEx == nil && r.proto == nil) { - return 0, errNilTextInputProtocol - } - if len(p) == 0 { - return 0, nil - } - - n := 0 - for n < len(p) { - if r.start != r.end { - p[n] = r.pending[r.start] - r.start++ - n++ - continue - } - - key, err := r.ReadKey() - if err != nil { - if n != 0 { - return n, nil - } - return 0, err - } - if key.Key.UnicodeChar == 0 { - continue - } - - runeValue := rune(key.Key.UnicodeChar) - r.end = utf8.EncodeRune(r.pending[:], runeValue) - r.start = 0 - } - - return n, nil -} - -func (r *TextInput) ReadByte() (byte, error) { - var buf [1]byte - _, err := r.Read(buf[:]) - return buf[0], err -} - -func (r *TextInput) ReadKey() (EFI_KEY_DATA, error) { - key, _, err := r.ReadKeyWithSource() - return key, err -} - -func (r *TextInput) ReadKeyWithSource() (EFI_KEY_DATA, TextInputSource, error) { - if r == nil { - return EFI_KEY_DATA{}, TextInputNone, errNilTextInputProtocol - } - if r.protoEx != nil { - key, status := r.protoEx.GetKey() - if status == EFI_SUCCESS { - return key, TextInputSimpleTextInputEx, nil - } - if r.proto == nil { - return key, TextInputSimpleTextInputEx, StatusError(status) - } - } - if r.proto != nil { - key, status := r.proto.GetKey() - return EFI_KEY_DATA{Key: key}, TextInputSimpleTextInput, StatusError(status) - } - return EFI_KEY_DATA{}, TextInputNone, errNilTextInputProtocol -} - -func (s TextInputSource) String() string { - switch s { - case TextInputSimpleTextInputEx: - return "STIEx" - case TextInputSimpleTextInput: - return "STIP" - default: - return "none" - } -} - -func (r *TextInput) HasTextInputEx() bool { - return r != nil && r.protoEx != nil -} - -func (r *TextInput) HasTextInput() bool { - return r != nil && r.proto != nil -} diff --git a/src/device/uefi/time.go b/src/device/uefi/time.go deleted file mode 100644 index 9445097c63..0000000000 --- a/src/device/uefi/time.go +++ /dev/null @@ -1,101 +0,0 @@ -package uefi - -type EFI_TIME struct { - Year uint16 - Month byte - Day byte - Hour byte - Minute byte - Second byte - Pad1 byte - Nanosecond uint32 - TimeZone int16 - Daylight byte - Pad2 byte -} - -type EFI_TIME_CAPABILITIES struct { - Resolution uint32 - Accuracy uint32 - SetsToZero BOOLEAN -} - -func GetTime() (EFI_TIME, EFI_STATUS) { - var time EFI_TIME - status := ST().RuntimeServices.GetTime(&time, nil) - return time, status -} - -func (t *EFI_TIME) GetEpoch() (sec int64, nsec int32) { - year := int(t.Year) - month := int(t.Month) - 1 - - d := daysSinceEpoch(year) - d += uint64(daysBefore[month-1]) - if isLeap(year) && month >= 3 { - d++ - } - d += uint64(t.Day - 1) - - abs := d * secondsPerDay - abs += uint64(uint64(t.Hour)*uint64(secondsPerHour) + uint64(t.Minute)*uint64(secondsPerMinute) + uint64(t.Second)) - - sec = int64(abs) + (absoluteToInternal + internalToUnix) - nsec = int32(t.Nanosecond) - return -} - -const ( - secondsPerMinute = 60 - secondsPerHour = 60 * secondsPerMinute - secondsPerDay = 24 * secondsPerHour - daysPer400Years = 365*400 + 97 - daysPer100Years = 365*100 + 24 - daysPer4Years = 365*4 + 1 - - absoluteZeroYear = -292277022399 - internalYear = 1 - - absoluteToInternal int64 = (absoluteZeroYear - internalYear) * 365.2425 * secondsPerDay - unixToInternal int64 = (1969*365 + 1969/4 - 1969/100 + 1969/400) * secondsPerDay - internalToUnix int64 = -unixToInternal -) - -var daysBefore = [...]int32{ - 0, - 31, - 31 + 28, - 31 + 28 + 31, - 31 + 28 + 31 + 30, - 31 + 28 + 31 + 30 + 31, - 31 + 28 + 31 + 30 + 31 + 30, - 31 + 28 + 31 + 30 + 31 + 30 + 31, - 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31, - 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30, - 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31, - 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30, - 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 + 31, -} - -func daysSinceEpoch(year int) uint64 { - y := uint64(int64(year) - absoluteZeroYear) - - n := y / 400 - y -= 400 * n - d := daysPer400Years * n - - n = y / 100 - y -= 100 * n - d += daysPer100Years * n - - n = y / 4 - y -= 4 * n - d += daysPer4Years * n - - d += 365 * y - return d -} - -func isLeap(year int) bool { - return year%4 == 0 && (year%100 != 0 || year%400 == 0) -} diff --git a/src/machine/machine_uefi.go b/src/machine/machine_uefi.go index 31cb0c8c0d..94659d2b3e 100644 --- a/src/machine/machine_uefi.go +++ b/src/machine/machine_uefi.go @@ -9,30 +9,8 @@ import ( const deviceName = "UEFI" type ( - EFI_STATUS = deviceuefi.EFI_STATUS - EFI_TIME = deviceuefi.EFI_TIME - EFI_TIME_CAPABILITIES = deviceuefi.EFI_TIME_CAPABILITIES - EFI_KEY_DATA = deviceuefi.EFI_KEY_DATA - EFI_INPUT_KEY = deviceuefi.EFI_INPUT_KEY - EFI_KEY_STATE = deviceuefi.EFI_KEY_STATE - EFI_KEY_TOGGLE_STATE = deviceuefi.EFI_KEY_TOGGLE_STATE - EFI_GUID = deviceuefi.EFI_GUID - UINTN = deviceuefi.UINTN - - EFI_GRAPHICS_OUTPUT_PROTOCOL = deviceuefi.EFI_GRAPHICS_OUTPUT_PROTOCOL - EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE = deviceuefi.EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE - EFI_GRAPHICS_OUTPUT_MODE_INFORMATION = deviceuefi.EFI_GRAPHICS_OUTPUT_MODE_INFORMATION - EFI_GRAPHICS_OUTPUT_BLT_PIXEL = deviceuefi.EFI_GRAPHICS_OUTPUT_BLT_PIXEL - EFI_GRAPHICS_OUTPUT_BLT_OPERATION = deviceuefi.EFI_GRAPHICS_OUTPUT_BLT_OPERATION - EFI_GRAPHICS_PIXEL_FORMAT = deviceuefi.EFI_GRAPHICS_PIXEL_FORMAT - EFI_PIXEL_BITMASK = deviceuefi.EFI_PIXEL_BITMASK - - EFI_LOADED_IMAGE_PROTOCOL = deviceuefi.EFI_LOADED_IMAGE_PROTOCOL - EFI_DEVICE_PATH_PROTOCOL = deviceuefi.EFI_DEVICE_PATH_PROTOCOL - - TextOutput = deviceuefi.TextOutput - TextInput = deviceuefi.TextInput - TextInputSource = deviceuefi.TextInputSource + EFI_STATUS = deviceuefi.EFI_STATUS + TextOutput = deviceuefi.TextOutput Error = deviceuefi.Error ) @@ -72,22 +50,6 @@ const ( EFI_COMPROMISED_DATA = deviceuefi.EFI_COMPROMISED_DATA EFI_IP_ADDRESS_CONFLICT = deviceuefi.EFI_IP_ADDRESS_CONFLICT EFI_HTTP_ERROR = deviceuefi.EFI_HTTP_ERROR - - TextInputNone = deviceuefi.TextInputNone - TextInputSimpleTextInputEx = deviceuefi.TextInputSimpleTextInputEx - TextInputSimpleTextInput = deviceuefi.TextInputSimpleTextInput - - BltVideoFill = deviceuefi.BltVideoFill - BltVideoToBltBuffer = deviceuefi.BltVideoToBltBuffer - BltBufferToVideo = deviceuefi.BltBufferToVideo - BltVideoToVideo = deviceuefi.BltVideoToVideo - BltOperationMax = deviceuefi.BltOperationMax - - PixelRedGreenBlueReserved8BitPerColor = deviceuefi.PixelRedGreenBlueReserved8BitPerColor - PixelBlueGreenRedReserved8BitPerColor = deviceuefi.PixelBlueGreenRedReserved8BitPerColor - PixelBitMask = deviceuefi.PixelBitMask - PixelBltOnly = deviceuefi.PixelBltOnly - PixelFormatMax = deviceuefi.PixelFormatMax ) var ( @@ -126,10 +88,6 @@ var ( ErrHTTPError = deviceuefi.ErrHTTPError ) -func GetTime() (EFI_TIME, EFI_STATUS) { - return deviceuefi.GetTime() -} - func ConsoleOut() *TextOutput { return deviceuefi.ConsoleOut() } @@ -138,18 +96,6 @@ func StandardError() *TextOutput { return deviceuefi.StandardError() } -func ConsoleInput() (*TextInput, error) { - return deviceuefi.ConsoleInput() -} - -func GraphicsOutputProtocol() (*EFI_GRAPHICS_OUTPUT_PROTOCOL, EFI_STATUS) { - return deviceuefi.GraphicsOutputProtocol() -} - -func GetLoadedImageProtocol() (*EFI_LOADED_IMAGE_PROTOCOL, EFI_STATUS) { - return deviceuefi.GetLoadedImageProtocol() -} - func StatusError(status EFI_STATUS) *Error { return deviceuefi.StatusError(status) } diff --git a/src/runtime/os_uefi.go b/src/runtime/os_uefi.go deleted file mode 100644 index fa16d38f63..0000000000 --- a/src/runtime/os_uefi.go +++ /dev/null @@ -1,30 +0,0 @@ -//go:build uefi - -package runtime - -import ( - "device/uefi" - "unsafe" -) - -// loadedImageBase caches the loaded image base so PE globals discovery can -// locate writable sections without relying on linker-provided symbols. -var loadedImageBase uintptr - -func init() { - lip, status := uefi.GetLoadedImageProtocol() - if status != uefi.EFI_SUCCESS || lip == nil { - return - } - - loadedImageBase = uintptr(unsafe.Pointer(lip.ImageBase)) - module = (*exeHeader)(unsafe.Pointer(loadedImageBase)) -} - -func findGlobals(found func(start, end uintptr)) { - if loadedImageBase == 0 { - return - } - - findGlobalsForPE(found) -} diff --git a/src/runtime/runtime_uefi.go b/src/runtime/runtime_uefi.go index 6d6bd10d95..9849ec9d92 100644 --- a/src/runtime/runtime_uefi.go +++ b/src/runtime/runtime_uefi.go @@ -16,8 +16,6 @@ var heapSize uintptr = 64 * 1024 * 1024 var heapStart, heapEnd uintptr var stackTop uintptr var allocatePagesAddress uefi.EFI_PHYSICAL_ADDRESS -var consoleInEx *uefi.EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL -var consoleIn *uefi.EFI_SIMPLE_TEXT_INPUT_PROTOCOL var waitForEventsFunction = func() { uefi.CpuPause() } @@ -69,12 +67,6 @@ func abort() { func preinit() { uefi.BS().SetWatchdogTimer(0, 0, 0, nil) - if protoEx, status := uefi.SimpleTextInExProtocol(); status == uefi.EFI_SUCCESS { - consoleInEx = protoEx - } - if proto, status := uefi.SimpleTextInProtocol(); status == uefi.EFI_SUCCESS { - consoleIn = proto - } if !growHeap() { runtimePanic("could not allocate initial UEFI heap") } @@ -104,15 +96,6 @@ func growHeap() bool { return false } -func init() { - mono := nanotime() - efiTime, status := uefi.GetTime() - if status == uefi.EFI_SUCCESS { - sec, nsec := efiTime.GetEpoch() - timeOffset.Store(sec*1000000000 + int64(nsec) - mono) - } -} - func SetWaitForEvents(f func()) { waitForEventsFunction = f } @@ -127,47 +110,11 @@ func runMain() { } func buffered() int { - if consoleInEx != nil { - if uefi.BS().CheckEvent(consoleInEx.WaitForKeyEx) == uefi.EFI_SUCCESS { - return 1 - } - return 0 - } - if consoleIn != nil { - if uefi.BS().CheckEvent(consoleIn.WaitForKey) == uefi.EFI_SUCCESS { - return 1 - } - return 0 - } return 0 } func getchar() byte { - for { - if consoleInEx != nil { - key, status := consoleInEx.GetKey() - if status == uefi.EFI_SUCCESS && key.Key.UnicodeChar != 0 { - return byte(key.Key.UnicodeChar) - } - if status != uefi.EFI_SUCCESS && status != uefi.EFI_NOT_READY && consoleIn == nil { - return 0 - } - if status == uefi.EFI_SUCCESS || status == uefi.EFI_NOT_READY { - continue - } - } - if consoleIn != nil { - key, status := consoleIn.GetKey() - if status == uefi.EFI_SUCCESS && key.UnicodeChar != 0 { - return byte(key.UnicodeChar) - } - if status != uefi.EFI_SUCCESS && status != uefi.EFI_NOT_READY { - return 0 - } - continue - } - return 0 - } + return 0 } //export efi_main 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() +} From 5d19dd9207735b3f1015795206f9d6bdbcf50a3d Mon Sep 17 00:00:00 2001 From: Matthew Hiles Date: Tue, 9 Jun 2026 08:39:51 -0400 Subject: [PATCH 21/21] amd64: rename x86 package --- src/device/{x86 => amd64}/cpu.go | 2 +- src/device/{x86 => amd64}/cpu_amd64.S | 0 src/device/uefi/arch_x86.go | 8 ++++---- targets/uefi-amd64.json | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) rename src/device/{x86 => amd64}/cpu.go (99%) rename src/device/{x86 => amd64}/cpu_amd64.S (100%) diff --git a/src/device/x86/cpu.go b/src/device/amd64/cpu.go similarity index 99% rename from src/device/x86/cpu.go rename to src/device/amd64/cpu.go index bf9ad9ce22..32cbfe798c 100644 --- a/src/device/x86/cpu.go +++ b/src/device/amd64/cpu.go @@ -1,6 +1,6 @@ //go:build amd64 -package x86 +package amd64 const ( CPUIDTimeStampCounter = 0x15 diff --git a/src/device/x86/cpu_amd64.S b/src/device/amd64/cpu_amd64.S similarity index 100% rename from src/device/x86/cpu_amd64.S rename to src/device/amd64/cpu_amd64.S diff --git a/src/device/uefi/arch_x86.go b/src/device/uefi/arch_x86.go index a2dbc7685e..3eb344fe94 100644 --- a/src/device/uefi/arch_x86.go +++ b/src/device/uefi/arch_x86.go @@ -2,16 +2,16 @@ package uefi -import "device/x86" +import "device/amd64" func Ticks() uint64 { - return x86.AsmReadRdtsc() + return amd64.AsmReadRdtsc() } func CpuPause() { - x86.AsmPause() + amd64.AsmPause() } func getTSCFrequency() uint64 { - return x86.InternalGetPerformanceCounterFrequency() + return amd64.InternalGetPerformanceCounterFrequency() } diff --git a/targets/uefi-amd64.json b/targets/uefi-amd64.json index 8f309e9d65..c594aecce6 100644 --- a/targets/uefi-amd64.json +++ b/targets/uefi-amd64.json @@ -33,7 +33,7 @@ "--no-dynamicbase" ], "extra-files": [ - "src/device/x86/cpu_amd64.S", + "src/device/amd64/cpu_amd64.S", "src/device/uefi/asm_amd64.S", "src/runtime/asm_amd64_windows.S" ],