diff --git a/GNUmakefile b/GNUmakefile index 99dc3913b6..768bad1a7c 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -924,6 +924,10 @@ ifneq ($(WASM), 0) $(TINYGO) build -size short -o wasm.wasm -target=wasm examples/wasm/main $(TINYGO) build -size short -o wasm.wasm -target=wasm-unknown examples/hello-wasm-unknown endif + $(TINYGO) build -size short -o test.exe -target=uefi-amd64 examples/empty + @$(MD5SUM) test.exe + $(TINYGO) build -size short -o test.exe -target=uefi-amd64 examples/time-offset + @$(MD5SUM) test.exe # test various compiler flags $(TINYGO) build -size short -o test.hex -target=pca10040 -gc=none -scheduler=none examples/blinky1 @$(MD5SUM) test.hex @@ -943,6 +947,7 @@ endif GOOS=windows GOARCH=arm64 $(TINYGO) build -size short -o test.exe ./testdata/cgo GOOS=darwin GOARCH=amd64 $(TINYGO) build -size short -o test ./testdata/cgo GOOS=darwin GOARCH=arm64 $(TINYGO) build -size short -o test ./testdata/cgo + $(TINYGO) build -size short -o test.exe -target=uefi-amd64 ./testdata/cgo ifneq ($(OS),Windows_NT) # TODO: this does not yet work on Windows. Somehow, unused functions are # not garbage collected. diff --git a/builder/build.go b/builder/build.go index cd650cfc49..3dc7cc777c 100644 --- a/builder/build.go +++ b/builder/build.go @@ -807,8 +807,15 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe ldflags = append(ldflags, dependency.result) } ldflags = append(ldflags, "-mllvm", "-mcpu="+config.CPU()) + buildTags := config.BuildTags() + isWindowsLinker := config.GOOS() == "windows" + for _, tag := range buildTags { + if tag == "uefi" { + isWindowsLinker = true + } + } ldflags = append(ldflags, "-mllvm", "-mattr="+config.Features()) // needed for MIPS softfloat - if config.GOOS() == "windows" { + if isWindowsLinker { // Options for the MinGW wrapper for the lld COFF linker. ldflags = append(ldflags, "-Xlink=/opt:lldlto="+strconv.Itoa(speedLevel), diff --git a/builder/builder_test.go b/builder/builder_test.go index 6b84b10070..ead8a00963 100644 --- a/builder/builder_test.go +++ b/builder/builder_test.go @@ -34,6 +34,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..914c9e4705 --- /dev/null +++ b/src/device/x86/cpu.go @@ -0,0 +1,141 @@ +//go:build amd64 + +package x86 + +const ( + // CPUID_TIME_STAMP_COUNTER + // EAX Returns processor base frequency information described by the + // type CPUID_PROCESSOR_FREQUENCY_EAX. + // EBX Returns maximum frequency information described by the type + // CPUID_PROCESSOR_FREQUENCY_EBX. + // ECX Returns bus frequency information described by the type + // CPUID_PROCESSOR_FREQUENCY_ECX. + // EDX Reserved. + CPUID_TIME_STAMP_COUNTER = 0x15 + + // CPUID_PROCESSOR_FREQUENCY + // EAX Returns processor base frequency information described by the + // type CPUID_PROCESSOR_FREQUENCY_EAX. + // EBX Returns maximum frequency information described by the type + // CPUID_PROCESSOR_FREQUENCY_EBX. + // ECX Returns bus frequency information described by the type + // CPUID_PROCESSOR_FREQUENCY_ECX. + // EDX Reserved. + CPUID_PROCESSOR_FREQUENCY = 0x16 +) + +type CpuExtendedFamily uint16 + +const ( + // AMD + CPU_FAMILY_AMD_11H CpuExtendedFamily = 0x11 + // Intel + CPU_FAMILY_INTEL_CORE CpuExtendedFamily = 6 + CPU_MODEL_NEHALEM CpuExtendedFamily = 0x1e + CPU_MODEL_NEHALEM_EP CpuExtendedFamily = 0x1a + CPU_MODEL_NEHALEM_EX CpuExtendedFamily = 0x2e + CPU_MODEL_WESTMERE CpuExtendedFamily = 0x25 + CPU_MODEL_WESTMERE_EP CpuExtendedFamily = 0x2c + CPU_MODEL_WESTMERE_EX CpuExtendedFamily = 0x2f + CPU_MODEL_SANDYBRIDGE CpuExtendedFamily = 0x2a + CPU_MODEL_SANDYBRIDGE_EP CpuExtendedFamily = 0x2d + CPU_MODEL_IVYBRIDGE_EP CpuExtendedFamily = 0x3a + CPU_MODEL_HASWELL_E3 CpuExtendedFamily = 0x3c + CPU_MODEL_HASWELL_E7 CpuExtendedFamily = 0x3f + CPU_MODEL_BROADWELL CpuExtendedFamily = 0x3d +) + +func GetExtendedCpuFamily() CpuExtendedFamily { + var family CpuExtendedFamily + family = CpuExtendedFamily((stdCpuid1Eax >> 8) & 0x0f) + family += CpuExtendedFamily((stdCpuid1Eax >> 20) & 0xff) + return family +} + +//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 +var stdCpuid1Ebx uint32 +var stdCpuid1Ecx uint32 +var stdCpuid1Edx uint32 + +func init() { + AsmCpuid(0, &maxCpuidIndex, &stdVendorName0, nil, nil) + AsmCpuid(1, &stdCpuid1Eax, &stdCpuid1Ebx, &stdCpuid1Ecx, &stdCpuid1Edx) +} + +func GetMaxCpuidIndex() uint32 { + return maxCpuidIndex +} + +func IsIntel() bool { + return stdVendorName0 == 0x756e6547 +} + +func IsIntelFamilyCore() bool { + return IsIntel() && GetExtendedCpuFamily() == CPU_FAMILY_INTEL_CORE +} + +func IsAmd() bool { + return stdVendorName0 == 0x68747541 +} + +func InternalGetPerformanceCounterFrequency() uint64 { + if maxCpuidIndex >= CPUID_TIME_STAMP_COUNTER { + return CpuidCoreClockCalculateTscFrequency() + } + + return 0 +} + +func CpuidCoreClockCalculateTscFrequency() uint64 { + var TscFrequency uint64 + var CoreXtalFrequency uint64 + var RegEax uint32 + var RegEbx uint32 + var RegEcx uint32 + + AsmCpuid(CPUID_TIME_STAMP_COUNTER, &RegEax, &RegEbx, &RegEcx, nil) + + // If EAX or EBX returns 0, the XTAL ratio is not enumerated. + if (RegEax == 0) || (RegEbx == 0) { + return 0 + } + + // If ECX returns 0, the XTAL frequency is not enumerated. + // And PcdCpuCoreCrystalClockFrequency defined should base on processor series. + // + if RegEcx == 0 { + // Specifies CPUID Leaf 0x15 Time Stamp Counter and Nominal Core Crystal Clock Frequency. + // https://github.com/torvalds/linux/blob/master/tools/power/x86/turbostat/turbostat.c + if IsIntelFamilyCore() { + switch GetExtendedCpuFamily() { + case 0x5F: // INTEL_FAM6_ATOM_GOLDMONT_D + CoreXtalFrequency = 25000000 + case 0x5C: // INTEL_FAM6_ATOM_GOLDMONT + CoreXtalFrequency = 19200000 + case 0x7A: // INTEL_FAM6_ATOM_GOLDMONT_PLUS + CoreXtalFrequency = 19200000 + default: + CoreXtalFrequency = 24000000 + } + } else { + return 0 + } + } else { + CoreXtalFrequency = uint64(RegEcx) + } + + // Calculate TSC frequency = (ECX, Core Xtal Frequency) * EBX/EAX + TscFrequency = ((CoreXtalFrequency * uint64(RegEbx)) + (uint64(RegEax) / 2)) / uint64(RegEax) + return TscFrequency +} diff --git a/src/device/x86/cpu_amd64.S b/src/device/x86/cpu_amd64.S new file mode 100644 index 0000000000..57ead8de47 --- /dev/null +++ b/src/device/x86/cpu_amd64.S @@ -0,0 +1,59 @@ +.section .text + +.global asmPause +asmPause: + pause + ret + +.global asmReadRdtsc +asmReadRdtsc: + rdtsc + shlq $0x20, %rdx + orq %rdx, %rax + ret + +// @param Index The 32-bit value to load into EAX prior to invoking the CPUID +// instruction. +// @param RegisterEax A pointer to the 32-bit EAX value returned by the CPUID +// instruction. This is an optional parameter that may be NULL. +// @param RegisterEbx A pointer to the 32-bit EBX value returned by the CPUID +// instruction. This is an optional parameter that may be NULL. +// @param RegisterEcx A pointer to the 32-bit ECX value returned by the CPUID +// instruction. This is an optional parameter that may be NULL. +// @param RegisterEdx A pointer to the 32-bit EDX value returned by the CPUID +// instruction. This is an optional parameter that may be NULL. +// @return Index. +.global asmCpuid +asmCpuid: + // rcx = Index + // rdx = RegisterEax + // r8 = RegisterEbx + // r9 = RegisterEcx + // rsp + 0x28 = RegisterEdx + pushq %rbx + + mov %ecx, %eax // eax <- index + pushq %rax // save Index on stack + + pushq %rdx // RegisterEax + cpuid + + test %r9, %r9 // RegisterEcx + jz .SkipEcx + mov %ecx, (%r9) +.SkipEcx: + popq %rcx // RegisterEax + jrcxz .SkipEax + mov %eax, (%rcx) +.SkipEax: + mov %r8, %rcx // RegisterEbx + jrcxz .SkipEbx + mov %ebx, (%rcx) +.SkipEbx: + mov 0x38(%rsp), %rcx // 0x28 + 0x10 + jrcxz .SkipEdx + mov %edx, (%rcx) +.SkipEdx: + popq %rax // restore Index to rax as return value + popq %rbx + ret diff --git a/src/internal/task/task_none_uefi.go b/src/internal/task/task_none_uefi.go new file mode 100644 index 0000000000..d2a25d0e04 --- /dev/null +++ b/src/internal/task/task_none_uefi.go @@ -0,0 +1,7 @@ +//go:build scheduler.none && uefi + +package task + +//go:export tinygo_task_exit +func task_exit() { +} diff --git a/src/internal/task/task_stack_amd64.go b/src/internal/task/task_stack_amd64.go index d252b1c50d..bfd18b5758 100644 --- a/src/internal/task/task_stack_amd64.go +++ b/src/internal/task/task_stack_amd64.go @@ -1,4 +1,4 @@ -//go:build scheduler.tasks && amd64 && !windows +//go:build scheduler.tasks && amd64 && !windows && !uefi package task diff --git a/src/internal/task/task_stack_amd64_windows.go b/src/internal/task/task_stack_amd64_winabi.go similarity index 97% rename from src/internal/task/task_stack_amd64_windows.go rename to src/internal/task/task_stack_amd64_winabi.go index f174196f35..8d5e8f62c3 100644 --- a/src/internal/task/task_stack_amd64_windows.go +++ b/src/internal/task/task_stack_amd64_winabi.go @@ -1,4 +1,4 @@ -//go:build scheduler.tasks && amd64 && windows +//go:build scheduler.tasks && amd64 && (windows || uefi) package task diff --git a/src/machine/uefi/arch_x86.go b/src/machine/uefi/arch_x86.go new file mode 100644 index 0000000000..4053798de0 --- /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..a95a90e71a --- /dev/null +++ b/src/machine/uefi/asm_amd64.S @@ -0,0 +1,251 @@ +.section .text + +.global uefiCall0 +uefiCall0: + pushq %rbp + movq %rsp, %rbp + subq $0x20, %rsp // Shadow space of UEFI call + callq *%rcx + movq %rbp, %rsp + popq %rbp + ret + +.global uefiCall1 +uefiCall1: + pushq %rbp + movq %rsp, %rbp + subq $0x20, %rsp // Shadow space of UEFI call + movq %rcx, %rax // rax = fn + movq %rdx, %rcx + callq *%rax + movq %rbp, %rsp + popq %rbp + ret + +.global uefiCall2 +uefiCall2: + pushq %rbp + movq %rsp, %rbp + subq $0x20, %rsp // Shadow space of UEFI call + movq %rcx, %rax // rax = fn + 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 // Shadow space of UEFI call + movq %rcx, %rax // rax = fn + 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 // Shadow space of UEFI call + movq %rcx, %rax // rax = fn + movq %rdx, %rcx + movq %r8, %rdx + movq %r9, %r8 + + movq 0x30(%rbp),%r9 // 0x08(return_address) + 0x08(pushq rbp) + 0x20 + + callq *%rax + movq %rbp, %rsp + popq %rbp + ret + +.global uefiCall5 +uefiCall5: + pushq %rbp + movq %rsp, %rbp + subq $0x28,%rsp // 0x20 shadow + 1×8 spill + movq %rcx, %rax // rax = fn + movq %rdx, %rcx // a + movq %r8, %rdx // b + movq %r9, %r8 // c + movq 0x30(%rbp),%r9 // d + movq 0x38(%rbp),%r10 // e + movq %r10,0x20(%rsp) + callq *%rax + movq %rbp, %rsp + popq %rbp + ret + +.global uefiCall6 +uefiCall6: + pushq %rbp + movq %rsp, %rbp + subq $0x28,%rsp // 0x20 shadow + 2×8 spill + movq %rcx, %rax // rax = fn + movq %rdx, %rcx // a + movq %r8, %rdx // b + movq %r9, %r8 // c + movq 0x30(%rbp),%r9 // d + movq 0x38(%rbp),%r10 // e + movq %r10,0x20(%rsp) + movq 0x40(%rbp),%r10 // f + movq %r10,0x28(%rsp) + callq *%rax + movq %rbp, %rsp + popq %rbp + ret + +.global uefiCall7 +uefiCall7: + pushq %rbp + movq %rsp, %rbp + subq $0x38, %rsp // 0x20 shadow + 3*8 spill = 0x38 + + movq %rcx, %rax // RAX ← fn ptr + movq %rdx, %rcx // RCX ← a + movq %r8, %rdx // RDX ← b + movq %r9, %r8 // R8 ← c + movq 0x30(%rbp), %r9 // R9 ← d + + movq 0x38(%rbp), %r10 // e + movq %r10, 0x20(%rsp) + + movq 0x40(%rbp), %r10 // f + movq %r10, 0x28(%rsp) + + movq 0x48(%rbp), %r10 // g + movq %r10, 0x30(%rsp) + + callq *%rax // jump into firmware + + movq %rbp, %rsp // tear-down + popq %rbp + ret + +.global uefiCall8 +uefiCall8: + pushq %rbp + movq %rsp, %rbp + subq $0x40, %rsp // 0x20 shadow + 4*8 spill = 0x40 + + movq %rcx, %rax // RAX ← fn ptr + movq %rdx, %rcx // RCX ← a + movq %r8, %rdx // RDX ← b + movq %r9, %r8 // R8 ← c + movq 0x30(%rbp), %r9 // R9 ← d + + movq 0x38(%rbp), %r10 // e + movq %r10, 0x20(%rsp) + + movq 0x40(%rbp), %r10 // f + movq %r10, 0x28(%rsp) + + movq 0x48(%rbp), %r10 // g + movq %r10, 0x30(%rsp) + + movq 0x50(%rbp), %r10 // h + movq %r10, 0x38(%rsp) + + callq *%rax // jump into firmware + + movq %rbp, %rsp // tear-down + popq %rbp + ret + +.global uefiCall9 +uefiCall9: + pushq %rbp + movq %rsp, %rbp + subq $0x48, %rsp // 0x20 shadow + 5*8 spill = 0x48 + + movq %rcx, %rax // RAX ← fn ptr + movq %rdx, %rcx // RCX ← a + movq %r8, %rdx // RDX ← b + movq %r9, %r8 // R8 ← c + movq 0x30(%rbp), %r9 // R9 ← d + + movq 0x38(%rbp), %r10 // e + movq %r10, 0x20(%rsp) + + movq 0x40(%rbp), %r10 // f + movq %r10, 0x28(%rsp) + + movq 0x48(%rbp), %r10 // g + movq %r10, 0x30(%rsp) + + movq 0x50(%rbp), %r10 // h + movq %r10, 0x38(%rsp) + + movq 0x58(%rbp), %r10 // i + movq %r10, 0x40(%rsp) + + callq *%rax // jump into firmware + + movq %rbp, %rsp // tear-down + popq %rbp + ret + +.global uefiCall10 +uefiCall10: + pushq %rbp + movq %rsp, %rbp + subq $0x50, %rsp // 0x20 shadow + 6*8 spill = 0x50 + + movq %rcx, %rax // RAX ← fn ptr + movq %rdx, %rcx // RCX ← a + movq %r8, %rdx // RDX ← b + movq %r9, %r8 // R8 ← c + movq 0x30(%rbp), %r9 // R9 ← d + + movq 0x38(%rbp), %r10 // e + movq %r10, 0x20(%rsp) + + movq 0x40(%rbp), %r10 // f + movq %r10, 0x28(%rsp) + + movq 0x48(%rbp), %r10 // g + movq %r10, 0x30(%rsp) + + movq 0x50(%rbp), %r10 // h + movq %r10, 0x38(%rsp) + + movq 0x58(%rbp), %r10 // i + movq %r10, 0x40(%rsp) + + movq 0x60(%rbp), %r10 // j + movq %r10, 0x48(%rsp) + + callq *%rax // jump into firmware + + movq %rbp, %rsp // tear-down + popq %rbp + ret + +// MSVC-compatible __chkstk implementation for x86_64 +// This touches each page on the stack to ensure it is committed. + +.global ___chkstk_ms +___chkstk_ms: + ret // Rather than protect the stack, this seems to cause a fault 🤷 + mov %rcx, %r10 // Save the stack allocation size + mov %rsp, %rax // Current stack pointer + sub %r10, %rax // Target stack pointer + cmp %rax, %rsp // Already below limit? + jae .Ldone + +.Lloop: + sub $0x1000, %rsp // Page size (4KB) + movb $0, (%rsp) // Touch page + cmp %rsp, %rax // Reached target? + jne .Lloop + +.Ldone: + mov %rax, %rsp // Update stack pointer + ret diff --git a/src/machine/uefi/call.go b/src/machine/uefi/call.go new file mode 100644 index 0000000000..0ae162fce5 --- /dev/null +++ b/src/machine/uefi/call.go @@ -0,0 +1,45 @@ +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 + +//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/clock.go b/src/machine/uefi/clock.go new file mode 100644 index 0000000000..f9218420ce --- /dev/null +++ b/src/machine/uefi/clock.go @@ -0,0 +1,164 @@ +//go:build uefi + +package uefi + +import ( + "sync" +) + +var calibrateMutex sync.Mutex +var calculatedFrequency uint64 + +func TicksFrequency() uint64 { + frequency := getTscFrequency() + if frequency > 0 { + return frequency + } + + var event EFI_EVENT + var status EFI_STATUS + var index UINTN + + calibrateMutex.Lock() + defer calibrateMutex.Unlock() + + freq := calculatedFrequency + if freq > 0 { + return freq + } + + bs := systemTable.BootServices + + status = bs.CreateEvent(EVT_TIMER, TPL_CALLBACK, nil, nil, &event) + if status != EFI_SUCCESS { + DebugPrint("GetTscFrequency) CreateEvent Failed", uint64(status)) + return 0 + } + defer bs.CloseEvent(event) + + st := Ticks() + status = bs.SetTimer(event, TimerPeriodic, 250*10000) + if status != EFI_SUCCESS { + DebugPrint("GetTscFrequency) SetTimer Failed", uint64(status)) + return 0 + } + status = bs.WaitForEvent(1, &event, &index) + diff := Ticks() - st + + calculatedFrequency = diff * 4 + + return calculatedFrequency +} + +func GetTime() (EFI_TIME, EFI_STATUS) { + var status EFI_STATUS + var time EFI_TIME + + status = systemTable.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 + + // Compute days since the absolute epoch. + d := daysSinceEpoch(year) + + // Add in days before this month. + d += uint64(daysBefore[month-1]) + if isLeap(year) && month >= 3 { + d++ // February 29 + } + + // Add in days before today. + d += uint64(t.Day - 1) + + // Add in time elapsed today. + 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 +} + +// region: time utils +const ( + secondsPerMinute = 60 + secondsPerHour = 60 * secondsPerMinute + secondsPerDay = 24 * secondsPerHour + daysPer400Years = 365*400 + 97 + daysPer100Years = 365*100 + 24 + daysPer4Years = 365*4 + 1 + + // The unsigned zero year for internal calculations. + // Must be 1 mod 400, and times before it will not compute correctly, + // but otherwise can be changed at will. + absoluteZeroYear = -292277022399 + + // The year of the zero Time. + // Assumed by the unixToInternal computation below. + internalYear = 1 + + // Offsets to convert between internal and absolute or Unix times. + absoluteToInternal int64 = (absoluteZeroYear - internalYear) * 365.2425 * secondsPerDay + + unixToInternal int64 = (1969*365 + 1969/4 - 1969/100 + 1969/400) * secondsPerDay + internalToUnix int64 = -unixToInternal +) + +// daysBefore[m] counts the number of days in a non-leap year +// before month m begins. There is an entry for m=12, counting +// the number of days before January of next year (365). +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, +} + +// daysSinceEpoch takes a year and returns the number of days from +// the absolute epoch to the start of that year. +// This is basically (year - zeroYear) * 365, but accounting for leap days. +func daysSinceEpoch(year int) uint64 { + y := uint64(int64(year) - absoluteZeroYear) + + // Add in days from 400-year cycles. + n := y / 400 + y -= 400 * n + d := daysPer400Years * n + + // Add in 100-year cycles. + n = y / 100 + y -= 100 * n + d += daysPer100Years * n + + // Add in 4-year cycles. + n = y / 4 + y -= 4 * n + d += daysPer4Years * n + + // Add in non-leap years. + n = y + d += 365 * n + + return d +} + +func isLeap(year int) bool { + return year%4 == 0 && (year%100 != 0 || year%400 == 0) +} + +//endregion diff --git a/src/machine/uefi/dhcpv4.go b/src/machine/uefi/dhcpv4.go new file mode 100644 index 0000000000..5d19278d06 --- /dev/null +++ b/src/machine/uefi/dhcpv4.go @@ -0,0 +1,231 @@ +package uefi + +import ( + "unsafe" +) + +//--------------------------------------------------------------------------- +// GUIDs +//--------------------------------------------------------------------------- + +// EFI_DHCP4_SERVICE_BINDING_PROTOCOL_GUID — §29.2.1 +var EFI_DHCP4_SERVICE_BINDING_PROTOCOL_GUID = EFI_GUID{ + 0x9d9a39d8, 0xbd42, 0x4a73, + [8]uint8{0xa4, 0xd5, 0x8e, 0xe9, 0x4b, 0xe1, 0x13, 0x80}, +} + +// EFI_DHCP4_PROTOCOL_GUID — §29.2.2 +var EFI_DHCP4_PROTOCOL_GUID = EFI_GUID{ + 0x8a219718, 0x4ef5, 0x4761, + [8]uint8{0x91, 0xc8, 0xc0, 0xf0, 0x4b, 0xda, 0x9e, 0x56}, +} + +//--------------------------------------------------------------------------- +// Enums / States +//--------------------------------------------------------------------------- + +// EFI_DHCP4_STATE — DHCP state machine §29.2.3 +const ( + Dhcp4Stopped = iota + Dhcp4Init + Dhcp4Selecting + Dhcp4Requesting + Dhcp4Bound + Dhcp4Renewing + Dhcp4Rebinding + Dhcp4InitReboot + Dhcp4Rebooting +) + +// EFI_DHCP4_EVENT tracks events in the DHCP process. §29.2.4 +const ( + Dhcp4SendDiscover = iota + 1 + Dhcp4RcvdOffer + Dhcp4SelectOffer + Dhcp4SendRequest + Dhcp4RcvdAck + Dhcp4RcvdNak + Dhcp4SendDecline + Dhcp4BoundCompleted + Dhcp4EnterRenewing + Dhcp4EnterRebinding + Dhcp4AddressLost + Dhcp4Fail +) + +// EFI_DHCP4_PACKET_OPTION 29.2.4 +type EFI_DHCP4_PACKET_OPTION struct { + OpCode uint8 + Length uint8 + Data [1]uint8 +} + +//--------------------------------------------------------------------------- +// EFI_DHCP4_SERVICE_BINDING_PROTOCOL +//--------------------------------------------------------------------------- + +type EFI_DHCP4_SERVICE_BINDING_PROTOCOL struct { + createChild uintptr // (*this, *childHandle) + destroyChild uintptr // (*this, childHandle) +} + +func (p *EFI_DHCP4_SERVICE_BINDING_PROTOCOL) CreateChild(childHandle *EFI_HANDLE) EFI_STATUS { + return UefiCall2( + p.createChild, + uintptr(unsafe.Pointer(p)), + uintptr(unsafe.Pointer(childHandle)), + ) +} + +func (p *EFI_DHCP4_SERVICE_BINDING_PROTOCOL) DestroyChild(childHandle EFI_HANDLE) EFI_STATUS { + return UefiCall2( + p.destroyChild, + uintptr(unsafe.Pointer(p)), + uintptr(childHandle), + ) +} + +//--------------------------------------------------------------------------- +// EFI_DHCP4_PROTOCOL +//--------------------------------------------------------------------------- + +// EFI_DHCP4_CONFIG_DATA is for configuring a DHCP request. §29.2.4 +type EFI_DHCP4_CONFIG_DATA struct { + DiscoverTryCount uint32 + DiscoverTimeout *uint32 + RequestTryCount uint32 + RequestTimeout *uint32 + ClientAddress EFI_IPv4_ADDRESS + Dhcp4Callback EFI_DHCP4_CALLBACK + CallbackContext unsafe.Pointer // not sure about this + OptionCount uint32 + OptionList **EFI_DHCP4_PACKET_OPTION +} + +// EFI_DHCP4_CALLBACK is not yet supported. Needs a PE+ -> SysV trampoline. +type EFI_DHCP4_CALLBACK uintptr + +type EFI_DHCP4_MODE_DATA struct { + State uint32 + ConfigData EFI_DHCP4_CONFIG_DATA + ClientAddress EFI_IPv4_ADDRESS + ClientMac EFI_MAC_ADDRESS + ServerAddress EFI_IPv4_ADDRESS + RouterAddress EFI_IPv4_ADDRESS + SubnetMask EFI_IPv4_ADDRESS +} + +// EFI_DHCP4_PROTOCOL function table §29.2.2 +type EFI_DHCP4_PROTOCOL struct { + getModeData uintptr + configure uintptr + start uintptr + renewRebind uintptr + release uintptr + stop uintptr + build uintptr + transmitReceive uintptr + parse uintptr +} + +func (p *EFI_DHCP4_PROTOCOL) GetModeData(modeData *EFI_DHCP4_MODE_DATA) EFI_STATUS { + return UefiCall2( + p.getModeData, + uintptr(unsafe.Pointer(p)), + uintptr(unsafe.Pointer(modeData)), + ) +} + +func (p *EFI_DHCP4_PROTOCOL) Configure(cfg *EFI_DHCP4_CONFIG_DATA) EFI_STATUS { + return UefiCall2( + p.configure, + uintptr(unsafe.Pointer(p)), + uintptr(unsafe.Pointer(cfg)), + ) +} + +func (p *EFI_DHCP4_PROTOCOL) Start(asyncEvent *EFI_EVENT) EFI_STATUS { + return UefiCall2( + p.start, + uintptr(unsafe.Pointer(p)), + uintptr(unsafe.Pointer(asyncEvent)), + ) +} + +func (p *EFI_DHCP4_PROTOCOL) RenewRebind(asyncEvent *EFI_EVENT) EFI_STATUS { + return UefiCall2(p.renewRebind, uintptr(unsafe.Pointer(p)), uintptr(unsafe.Pointer(asyncEvent))) +} + +func (p *EFI_DHCP4_PROTOCOL) Release(asyncEvent *EFI_EVENT) EFI_STATUS { + return UefiCall2(p.release, uintptr(unsafe.Pointer(p)), uintptr(unsafe.Pointer(asyncEvent))) +} + +func (p *EFI_DHCP4_PROTOCOL) Stop() EFI_STATUS { + return UefiCall1(p.stop, uintptr(unsafe.Pointer(p))) +} + +func (p *EFI_DHCP4_PROTOCOL) Build(packetBuffer unsafe.Pointer) EFI_STATUS { + return UefiCall2(p.build, uintptr(unsafe.Pointer(p)), uintptr(packetBuffer)) +} + +func (p *EFI_DHCP4_PROTOCOL) TransmitReceive(token unsafe.Pointer) EFI_STATUS { + return UefiCall2(p.transmitReceive, uintptr(unsafe.Pointer(p)), uintptr(token)) +} + +func (p *EFI_DHCP4_PROTOCOL) Parse(packetBuffer unsafe.Pointer, parseResult unsafe.Pointer) EFI_STATUS { + return UefiCall3(p.parse, uintptr(unsafe.Pointer(p)), uintptr(packetBuffer), uintptr(parseResult)) +} + +// DHCPv4 wraps EFI_DHCP4_PROTOCOL to provide a more go idiomatic API for handling DHCPv4. +type DHCPv4 struct { + *EFI_DHCP4_PROTOCOL +} + +func EnumerateDHCPv4() ([]*DHCPv4, error) { + var ( + handleCount UINTN + handleBuffer *EFI_HANDLE + ) + status := BS().LocateHandleBuffer(ByProtocol, &EFI_DHCP4_SERVICE_BINDING_PROTOCOL_GUID, nil, &handleCount, &handleBuffer) + if status != EFI_SUCCESS { + return nil, StatusError(status) + } + // if none were found, we should have gotten EFI_NOT_FOUND + + //turn handleBuffer into a slice of EFI_HANDLEs + handleSlice := unsafe.Slice((*EFI_HANDLE)(unsafe.Pointer(handleBuffer)), int(handleCount)) + + dhcpv4s := make([]*DHCPv4, 0, int(handleCount)) + + // Turn Binding handles into Protocol + for i := range handleSlice { + var binding *EFI_DHCP4_SERVICE_BINDING_PROTOCOL + status := BS().HandleProtocol( + handleSlice[i], + &EFI_DHCP4_SERVICE_BINDING_PROTOCOL_GUID, + unsafe.Pointer(&binding), + ) + if status != EFI_SUCCESS { + // just skip, or error out entirely?? Hmm.. + continue + } + var bindChild EFI_HANDLE + // TODO: track and clean up after the children + status = binding.CreateChild(&bindChild) + if status != EFI_SUCCESS { + continue + } + var dhcpp *EFI_DHCP4_PROTOCOL + status = BS().HandleProtocol( + bindChild, + &EFI_DHCP4_PROTOCOL_GUID, + unsafe.Pointer(&dhcpp), + ) + if status != EFI_SUCCESS { + continue + } + dhcpv4s = append(dhcpv4s, &DHCPv4{EFI_DHCP4_PROTOCOL: dhcpp}) + } + + return dhcpv4s, nil +} diff --git a/src/machine/uefi/efidef.go b/src/machine/uefi/efidef.go new file mode 100644 index 0000000000..8b86f37d14 --- /dev/null +++ b/src/machine/uefi/efidef.go @@ -0,0 +1,23 @@ +package uefi + +type UINTN uintptr +type EFI_STATUS UINTN +type EFI_LBA uint64 +type EFI_TPL UINTN +type EFI_HANDLE uintptr +type EFI_EVENT uintptr +type EFI_MAC_ADDRESS [32]uint8 +type EFI_IPv4_ADDRESS [4]uint8 +type EFI_IPv6_ADDRESS [16]uint8 +type EFI_IP_ADDRESS [16]uint8 + +type CHAR16 uint16 +type BOOLEAN bool +type VOID any + +type EFI_GUID struct { + Data1 uint32 + Data2 uint16 + Data3 uint16 + Data4 [8]byte +} diff --git a/src/machine/uefi/efidevp.go b/src/machine/uefi/efidevp.go new file mode 100644 index 0000000000..94fca6cc7a --- /dev/null +++ b/src/machine/uefi/efidevp.go @@ -0,0 +1,10 @@ +package uefi + +// Device Path structures - Section C + +// EFI_DEVICE_PATH_PROTOCOL structure +type EFI_DEVICE_PATH_PROTOCOL struct { + Type uint8 + SubType uint8 + Length [2]uint8 +} diff --git a/src/machine/uefi/efiguid.go b/src/machine/uefi/efiguid.go new file mode 100644 index 0000000000..ada0e16e93 --- /dev/null +++ b/src/machine/uefi/efiguid.go @@ -0,0 +1,4 @@ +package uefi + +var EFI_DEVICE_PATH_PROTOCOL_GUID = EFI_GUID{0x9576e91, 0x6d3f, 0x11d2, [8]uint8{0x8e, 0x39, 0x0, 0xa0, 0xc9, 0x69, 0x72, 0x3b}} +var EFI_LOADED_IMAGE_GUID = EFI_GUID{0x5B1B31A1, 0x9562, 0x11d2, [8]uint8{0x8E, 0x3F, 0x00, 0xA0, 0xC9, 0x69, 0x72, 0x3B}} diff --git a/src/machine/uefi/efiprot.go b/src/machine/uefi/efiprot.go new file mode 100644 index 0000000000..3ee3baffbb --- /dev/null +++ b/src/machine/uefi/efiprot.go @@ -0,0 +1 @@ +package uefi diff --git a/src/machine/uefi/efistatus.go b/src/machine/uefi/efistatus.go new file mode 100644 index 0000000000..d7077bda87 --- /dev/null +++ b/src/machine/uefi/efistatus.go @@ -0,0 +1,112 @@ +package uefi + +const ( + uintnSize = 32 << (^uintptr(0) >> 63) // 32 or 64nt + errorMask = 1 << uintptr(uintnSize-1) +) + +const ( + 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 = make(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 +} + +// StatusError returns the error object given by status. These +// can be checked/managed with errors.Is() and the like. +func StatusError(status EFI_STATUS) *Error { + if status == 0 { + return nil + } + err, ok := errMap[status] + if !ok { + return newError(status, "unknown EFI error") + } + return err +} diff --git a/src/machine/uefi/graphics_output_protocol.go b/src/machine/uefi/graphics_output_protocol.go new file mode 100644 index 0000000000..e650f7ed19 --- /dev/null +++ b/src/machine/uefi/graphics_output_protocol.go @@ -0,0 +1,179 @@ +// Graphics Output Protocol (GOP) – §12.9 UEFI 2.10 +package uefi + +import ( + "unsafe" +) + +//--------------------------------------------------------------------------- +// GUID // +//--------------------------------------------------------------------------- + +// {9042A9DE-23DC-4A38-96FB-7ADED080516A} +var GraphicsOutputProtocolGUID = EFI_GUID{ + 0x9042a9de, 0x23dc, 0x4a38, + [8]uint8{0x96, 0xfb, 0x7a, 0xde, 0xd0, 0x80, 0x51, 0x6a}, +} + +//--------------------------------------------------------------------------- +// Pixel formats & blt operations // +//--------------------------------------------------------------------------- + +const ( + PixelRedGreenBlueReserved8BitPerColor = iota + PixelBlueGreenRedReserved8BitPerColor + PixelBitMask + PixelBltOnly + PixelFormatMax +) + +type EFI_GRAPHICS_OUTPUT_BLT_OPERATION uint32 + +const ( + BltVideoFill EFI_GRAPHICS_OUTPUT_BLT_OPERATION = iota + BltVideoToBltBuffer + BltBufferToVideo + BltVideoToVideo + BltOperationMax +) + + +// 12.9.4 EFI_PIXEL_BITMASK +type EFI_PIXEL_BITMASK struct { + RedMask, GreenMask, BlueMask, ReservedMask uint32 +} + +// 12.9.3 EFI_GRAPHICS_OUTPUT_MODE_INFORMATION +type EFI_GRAPHICS_OUTPUT_MODE_INFORMATION struct { + Version uint32 + HorizontalResolution uint32 + VerticalResolution uint32 + PixelFormat uint32 // values above + PixelInformation EFI_PIXEL_BITMASK // only valid when PixelFormat==PixelBitMask + PixelsPerScanLine uint32 +} + +// 12.9.5 EFI_GRAPHICS_OUTPUT_BLT_PIXEL +type EFI_GRAPHICS_OUTPUT_BLT_PIXEL struct { + Blue, Green, Red, Reserved uint8 +} + +// 12.9.2 EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE +type EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE struct { + MaxMode uint32 // total available modes + Mode uint32 // current mode + Info *EFI_GRAPHICS_OUTPUT_MODE_INFORMATION // mode info for current mode + SizeOfInfo UINTN + FrameBufferBase EFI_PHYSICAL_ADDRESS + FrameBufferSize UINTN +} + +//--------------------------------------------------------------------------- +// EFI_GRAPHICS_OUTPUT_PROTOCOL itself // +//--------------------------------------------------------------------------- + +type EFI_GRAPHICS_OUTPUT_PROTOCOL struct { + queryMode uintptr + setMode uintptr + blt uintptr + Mode *EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE +} + +// QueryMode – returns a filled-in MODE_INFORMATION for ModeNumber. +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)), + ) +} + +// SetMode – switch to the requested graphics mode. +func (p *EFI_GRAPHICS_OUTPUT_PROTOCOL) SetMode(ModeNumber uint32) EFI_STATUS { + return UefiCall2( + p.setMode, + uintptr(unsafe.Pointer(p)), + uintptr(ModeNumber), + ) +} + +// Blt – the work-horse pixel pump. +// +// - BltBuffer may be nil when the operation is VideoFill or VideoToVideo. +// - Delta is bytes per scan line in BltBuffer (0 ⇒ tightly packed). +func (p *EFI_GRAPHICS_OUTPUT_PROTOCOL) Blt( + BltBuffer *EFI_GRAPHICS_OUTPUT_BLT_PIXEL, + BltOperation EFI_GRAPHICS_OUTPUT_BLT_OPERATION, + SourceX, SourceY, + DestinationX, DestinationY, + Width, Height, + 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, error) { + st := ST() + var iFace unsafe.Pointer + status := (*st).BootServices.LocateProtocol( + &GraphicsOutputProtocolGUID, + nil, + unsafe.Pointer(&iFace)) + + if status == EFI_SUCCESS { + gop := (*EFI_GRAPHICS_OUTPUT_PROTOCOL)(iFace) + return gop, nil + } + + return nil, StatusError(status) +} + +// Init finds the highest resolution mode and uses it as the +// current mode. This is not strictly necessary, but it can help +// make some poorly behaved firmware work better. +// +// Highest resolution is calculated by total number of pixels. +func (p *EFI_GRAPHICS_OUTPUT_PROTOCOL) Init() (info *EFI_GRAPHICS_OUTPUT_MODE_INFORMATION) { + var ( + highestModeInfo EFI_GRAPHICS_OUTPUT_MODE_INFORMATION + highestMode uint32 + pixelMax uint32 + ) + + info = new(EFI_GRAPHICS_OUTPUT_MODE_INFORMATION) + for i := uint32(0); i < p.Mode.MaxMode; i++ { + var size UINTN + status := p.QueryMode(i, &size, &info) + if status != EFI_SUCCESS { + // silently skip + continue + } + if pixelCnt := info.HorizontalResolution * info.VerticalResolution; pixelCnt > pixelMax { + highestMode = i + highestModeInfo = *info + pixelMax = pixelCnt + } + } + + // set the mode we found + //if p.Mode.Mode != highestMode { // on the fence if this is better or not + p.SetMode(highestMode) + //} + + return &highestModeInfo +} diff --git a/src/machine/uefi/ip.go b/src/machine/uefi/ip.go new file mode 100644 index 0000000000..3ee3baffbb --- /dev/null +++ b/src/machine/uefi/ip.go @@ -0,0 +1 @@ +package uefi diff --git a/src/machine/uefi/loaded_image.go b/src/machine/uefi/loaded_image.go new file mode 100644 index 0000000000..0a22c8a86e --- /dev/null +++ b/src/machine/uefi/loaded_image.go @@ -0,0 +1,49 @@ +package uefi + +import "unsafe" + +var EFI_LOADED_IMAGE_PROTOCOL_GUID = EFI_GUID{ + 0x5B1B31A1, 0x9562, 0x11d2, + [...]byte{0x8E, 0x3F, 0x00, 0xA0, 0xC9, 0x69, 0x72, 0x3B}} + +// EFI_LOADED_IMAGE_PROTOCOL +// Can be used on any image handle to obtain information about the loaded image. +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 +} + +// Unload +// Unloads an image. +// @param[in] ImageHandle Handle that identifies the image to be unloaded. +// @retval EFI_SUCCESS The image has been unloaded. +// @retval EFI_INVALID_PARAMETER ImageHandle is not a valid image handle. +func (p *EFI_LOADED_IMAGE_PROTOCOL) Unload(ImageHandle EFI_HANDLE) EFI_STATUS { + return UefiCall1(p.unload, uintptr(imageHandle)) +} + +func GetLoadedImageProtocol() (*EFI_LOADED_IMAGE_PROTOCOL, error) { + var lip *EFI_LOADED_IMAGE_PROTOCOL + status := BS().HandleProtocol( + GetImageHandle(), + &EFI_LOADED_IMAGE_PROTOCOL_GUID, + unsafe.Pointer(&lip), + ) + + if status == EFI_SUCCESS { + return lip, nil + } + + return nil, StatusError(status) +} diff --git a/src/machine/uefi/media_io_protocol.go b/src/machine/uefi/media_io_protocol.go new file mode 100644 index 0000000000..160f74772b --- /dev/null +++ b/src/machine/uefi/media_io_protocol.go @@ -0,0 +1,239 @@ +package uefi + +import ( + "errors" + "unsafe" +) + +// Errors Disk IO and Block IO can return +var ( + ErrDiskNotReady = errors.New("disk not ready") + ErrNoMediaPresent = errors.New("no media present") + ErrNegativeOffset = errors.New("negative offset") + ErrReadOnly = errors.New("media is read-only") +) + +//--------------------------------------------------------------------------- +// GUIDs +//--------------------------------------------------------------------------- + +// EFI_DISK_IO_PROTOCOL_GUID = {CE345171-BA0B-11d2-8E4F-00A0C969723B} +var EFI_DISK_IO_PROTOCOL_GUID = EFI_GUID{ + 0xCE345171, 0xBA0B, 0x11D2, + [8]uint8{0x8e, 0x4f, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b}, +} + +// EFI_BLOCK_IO_PROTOCOL_GUID = {964E5B21-6459-11d2-8E39-00A0C969723B} +var EFI_BLOCK_IO_PROTOCOL_GUID = EFI_GUID{ + 0x964E5B21, 0x6459, 0x11D2, + [8]uint8{0x8e, 0x39, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b}, +} + +// Classic subset of MEDIA fields (UEFI 2.x adds more tail fields; we keep the common prefix) +type EFI_BLOCK_IO_MEDIA struct { + MediaId uint32 + RemovableMedia bool + MediaPresent bool + LogicalPartition bool + ReadOnly bool + WriteCaching bool + BlockSize uint32 + IoAlign uint32 + LastBlock EFI_LBA + // Newer fields exist after this point; we intentionally omit for compatibility. +} + +// §Block I/O function table order: Revision, Media, Reset, ReadBlocks, WriteBlocks, FlushBlocks +type EFI_BLOCK_IO_PROTOCOL struct { + Revision uint64 + Media *EFI_BLOCK_IO_MEDIA + reset uintptr // (this, ExtendedVerification: bool) + readBlocks uintptr // (this, MediaId, LBA, BufferSize, Buffer) + writeBlocks uintptr // (this, MediaId, LBA, BufferSize, Buffer) + flushBlocks uintptr // (this) +} + +// Reset the device (optionally extended verification) +func (p *EFI_BLOCK_IO_PROTOCOL) Reset(extendedVerification bool) EFI_STATUS { + return UefiCall2(p.reset, uintptr(unsafe.Pointer(p)), convertBool(extendedVerification)) +} + +// ReadBlocks reads raw LBAs into Buffer (size in bytes). +func (p *EFI_BLOCK_IO_PROTOCOL) ReadBlocks(mediaId uint32, lba EFI_LBA, bufSize UINTN, buffer unsafe.Pointer) EFI_STATUS { + return UefiCall5( + p.readBlocks, + uintptr(unsafe.Pointer(p)), + uintptr(mediaId), + uintptr(lba), + uintptr(bufSize), + uintptr(buffer), + ) +} + + +// WriteBlocks writes raw LBAs from Buffer (size in bytes). +func (p *EFI_BLOCK_IO_PROTOCOL) WriteBlocks(mediaId uint32, lba EFI_LBA, bufSize UINTN, buffer unsafe.Pointer) EFI_STATUS { + return UefiCall5( + p.writeBlocks, + uintptr(unsafe.Pointer(p)), + uintptr(mediaId), + uintptr(lba), + uintptr(bufSize), + uintptr(buffer), + ) +} + +// FlushBlocks flushes any device caches. +func (p *EFI_BLOCK_IO_PROTOCOL) FlushBlocks() EFI_STATUS { + return UefiCall1(p.flushBlocks, uintptr(unsafe.Pointer(p))) +} + +//--------------------------------------------------------------------------- +// Disk I/O: byte-addressed wrapper over Block I/O +//--------------------------------------------------------------------------- + +// §Disk I/O function table order: Revision, ReadDisk, WriteDisk +type EFI_DISK_IO_PROTOCOL struct { + Revision uint64 + readDisk uintptr // (this, MediaId, Offset, BufferSize, Buffer) + writeDisk uintptr // (this, MediaId, Offset, BufferSize, Buffer) +} + +// ReadDisk reads BufferSize bytes at byte Offset into Buffer. +func (p *EFI_DISK_IO_PROTOCOL) ReadDisk(mediaId uint32, offset uint64, bufSize UINTN, buffer unsafe.Pointer) EFI_STATUS { + return UefiCall5( + p.readDisk, + uintptr(unsafe.Pointer(p)), + uintptr(mediaId), + uintptr(offset), + uintptr(bufSize), + uintptr(buffer), + ) +} + +// WriteDisk writes BufferSize bytes at byte Offset from Buffer. +func (p *EFI_DISK_IO_PROTOCOL) WriteDisk(mediaId uint32, offset uint64, bufSize UINTN, buffer unsafe.Pointer) EFI_STATUS { + return UefiCall5( + p.writeDisk, + uintptr(unsafe.Pointer(p)), + uintptr(mediaId), + uintptr(offset), + uintptr(bufSize), + uintptr(buffer), + ) +} + +//--------------------------------------------------------------------------- +// Helpers: locate Disk I/O + Block I/O, and a tiny io.ReaderAt/io.WriterAt adapter +//--------------------------------------------------------------------------- + +// Disk bundles Disk I/O with its backing Block I/O (for MediaId, size, alignment). +type Disk struct { + DiskIO *EFI_DISK_IO_PROTOCOL + BlockIO *EFI_BLOCK_IO_PROTOCOL +} + +// EnumerateDisks returns Disk adapters for every handle that exposes both Disk I/O and Block I/O. +func EnumerateDisks() ([]*Disk, error) { + var ( + handleCount UINTN + handleBuffer *EFI_HANDLE + ) + st := BS().LocateHandleBuffer(ByProtocol, &EFI_DISK_IO_PROTOCOL_GUID, nil, &handleCount, &handleBuffer) + if st == EFI_NOT_FOUND { + return nil, nil + } + if st != EFI_SUCCESS { + return nil, StatusError(st) + } + handles := unsafe.Slice((*EFI_HANDLE)(unsafe.Pointer(handleBuffer)), int(handleCount)) + + disks := make([]*Disk, 0, len(handles)) + for _, h := range handles { + var dio *EFI_DISK_IO_PROTOCOL + if st = BS().HandleProtocol(h, &EFI_DISK_IO_PROTOCOL_GUID, unsafe.Pointer(&dio)); st != EFI_SUCCESS { + continue + } + var bio *EFI_BLOCK_IO_PROTOCOL + if st = BS().HandleProtocol(h, &EFI_BLOCK_IO_PROTOCOL_GUID, unsafe.Pointer(&bio)); st != EFI_SUCCESS { + // Some platforms expose DiskIO without BlockIO on the same handle; try parent inference if you care. + continue + } + disks = append(disks, &Disk{DiskIO: dio, BlockIO: bio}) + } + return disks, nil +} + +// Size returns total bytes addressable on this disk (LastBlock is inclusive). +func (d *Disk) Size() int64 { + if d == nil || d.BlockIO == nil || d.BlockIO.Media == nil { + return 0 + } + blk := int64(d.BlockIO.Media.BlockSize) + // LastBlock is the highest LBA (inclusive), so count = LastBlock+1 + return int64(d.BlockIO.Media.LastBlock+1) * blk +} + +// SectorSize returns the logical block size in bytes. +func (d *Disk) SectorSize() int { + if d == nil || d.BlockIO == nil || d.BlockIO.Media == nil { + return 0 + } + return int(d.BlockIO.Media.BlockSize) +} + +// ReadAt satisfies io.ReaderAt over Disk I/O (byte-addressable). +func (d *Disk) ReadAt(p []byte, off int64) (int, error) { + if d == nil || d.DiskIO == nil || d.BlockIO == nil || d.BlockIO.Media == nil { + return 0, ErrDiskNotReady + } + if !d.BlockIO.Media.MediaPresent { + return 0, ErrNoMediaPresent + } + if off < 0 { + return 0, ErrNegativeOffset + } + if len(p) == 0 { + return 0, nil + } + sz := UINTN(len(p)) + st := d.DiskIO.ReadDisk(d.BlockIO.Media.MediaId, uint64(off), sz, unsafe.Pointer(&p[0])) + if st == EFI_SUCCESS { + return int(sz), nil + } + return 0, StatusError(st) +} + +// WriteAt satisfies io.WriterAt over Disk I/O (byte-addressable). +func (d *Disk) WriteAt(p []byte, off int64) (int, error) { + if d == nil || d.DiskIO == nil || d.BlockIO == nil || d.BlockIO.Media == nil { + return 0, ErrDiskNotReady + } + if d.BlockIO.Media.ReadOnly { + return 0, ErrReadOnly + } + if off < 0 { + return 0, ErrNegativeOffset + } + if len(p) == 0 { + return 0, nil + } + sz := UINTN(len(p)) + st := d.DiskIO.WriteDisk(d.BlockIO.Media.MediaId, uint64(off), sz, unsafe.Pointer(&p[0])) + if st == EFI_SUCCESS { + return int(sz), nil + } + return 0, StatusError(st) +} + +// Flush flushes device caches via Block I/O (if supported). +func (d *Disk) Flush() error { + if d == nil || d.BlockIO == nil { + return ErrDiskNotReady + } + st := d.BlockIO.FlushBlocks() + if st == EFI_SUCCESS { + return nil + } + return StatusError(st) +} diff --git a/src/machine/uefi/runtime.go b/src/machine/uefi/runtime.go new file mode 100644 index 0000000000..fa6f45062c --- /dev/null +++ b/src/machine/uefi/runtime.go @@ -0,0 +1,16 @@ +package uefi + +//go:linkname gosched runtime.Gosched +func gosched() + +// WaitForEvent blocks execution while yielding to other goroutines which differs +// from BS().WaitForEvent, which is a hard-block; the CPU is entirely stalled. +func WaitForEvent(event EFI_EVENT) { + for { + status := BS().CheckEvent(event) + if status == EFI_SUCCESS { + return + } + gosched() + } +} diff --git a/src/machine/uefi/serial_io_protocol.go b/src/machine/uefi/serial_io_protocol.go new file mode 100644 index 0000000000..ce69948178 --- /dev/null +++ b/src/machine/uefi/serial_io_protocol.go @@ -0,0 +1,289 @@ +// Serial I/O Protocol (SIOP) – §12.8 UEFI 2.10 +package uefi + +import ( + "unsafe" +) + +//--------------------------------------------------------------------------- +// GUID // +//--------------------------------------------------------------------------- + +// {BB25CF6F-F1A1-4F11-9E5A-AE8C109A771F} +var EFI_SERIAL_IO_PROTOCOL_GUID = EFI_GUID{ + 0xBB25CF6F, 0xF1D4, 0x11D2, + [8]uint8{0x9a, 0x0c, 0x00, 0x90, 0x27, 0x3f, 0xc1, 0xfd}, +} + +//--------------------------------------------------------------------------- +// Enums / bit-fields // +//--------------------------------------------------------------------------- + +// Parity – §13.4.1 Table 13-3 +const ( + ParityDefault = iota + ParityNone + ParityEven + ParityOdd + ParityMark + ParitySpace +) + +// Stop bits – §13.4.1 Table 13-4 +const ( + StopBitsDefault = iota + StopBits1 + StopBits1_5 + StopBits2 +) + +// Control-bit masks – §13.4.2 +const ( + EFI_SERIAL_DATA_TERMINAL_READY uint32 = 0x0001 + EFI_SERIAL_REQUEST_TO_SEND uint32 = 0x0002 + EFI_SERIAL_CLEAR_TO_SEND uint32 = 0x0010 + EFI_SERIAL_DATA_SET_READY uint32 = 0x0020 + EFI_SERIAL_RING_INDICATE uint32 = 0x0040 + EFI_SERIAL_CARRIER_DETECT uint32 = 0x0080 + EFI_SERIAL_INPUT_BUFFER_EMPTY uint32 = 0x0100 + EFI_SERIAL_OUTPUT_BUFFER_EMPTY uint32 = 0x0200 + EFI_SERIAL_HARDWARE_LOOPBACK_ENABLE uint32 = 0x1000 + EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE uint32 = 0x2000 + EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE uint32 = 0x4000 +) + +// EFI_SERIAL_IO_MODE is the output-only struct for checking the status of +// a serial port. §12.8.1 +type EFI_SERIAL_IO_MODE struct { + ControlMask, Timeout uint32 + BaudRate uint64 + ReceiveFifoDepth, DataBits, Parity, StopBits uint32 +} + +// EFI_SERIAL_IO_PROTOCOL Function table order matches §12.8.1 +type EFI_SERIAL_IO_PROTOCOL struct { + Revision uint32 + reset uintptr // (*this) + setAttributes uintptr // (*this, baud, depth, timeout, parity, databits, stopbits) + setControl uintptr // (*this, control) + getControl uintptr // (*this, *control) + write uintptr // (*this, *bufSize, buf) + read uintptr // (*this, *bufSize, buf) + Mode *EFI_SERIAL_IO_MODE + deviceTypeGuid uintptr +} + +// Reset the device. §12.8.3.1 +func (p *EFI_SERIAL_IO_PROTOCOL) Reset() EFI_STATUS { + return UefiCall1( + p.reset, + uintptr(unsafe.Pointer(p)), + ) +} + +// SetAttributes configures baud/format. +// Setting baudRate, receiveFifoDepth, or timeout to 0 *SHOULD* tell +// the port driver to use sane default values. YMMV. +// §12.8.3.2 +func (p *EFI_SERIAL_IO_PROTOCOL) SetAttributes( + baudRate uint64, + receiveFifoDepth uint32, + timeout uint32, + parity uint32, + dataBits int8, + stopBits uint32, +) EFI_STATUS { + return UefiCall7( + p.setAttributes, + uintptr(unsafe.Pointer(p)), + uintptr(baudRate), + uintptr(receiveFifoDepth), + uintptr(timeout), + uintptr(parity), + uintptr(dataBits), + uintptr(stopBits), + ) +} + +// SetControl sets or clears control bits. +// §12.8.3.3 +func (p *EFI_SERIAL_IO_PROTOCOL) SetControl(control uint32) EFI_STATUS { + return UefiCall2( + p.setControl, + uintptr(unsafe.Pointer(p)), + uintptr(control), + ) +} + +// GetControl queries control bits. +// §12.8.3.4 +func (p *EFI_SERIAL_IO_PROTOCOL) GetControl(control *uint32) EFI_STATUS { + return UefiCall2( + p.getControl, + uintptr(unsafe.Pointer(p)), + uintptr(unsafe.Pointer(control)), + ) +} + +// Write bytes to the UART. When calling, bufSize is the size of buffer. After Write has +// returned, bufSize will be set to the number of bytes actually written. +// §12.8.3.5 +func (p *EFI_SERIAL_IO_PROTOCOL) Write(bufSize *UINTN, buffer unsafe.Pointer) EFI_STATUS { + return UefiCall3( + p.write, + uintptr(unsafe.Pointer(p)), + uintptr(unsafe.Pointer(bufSize)), + uintptr(buffer), + ) +} + +// Read bytes from the UART. When calling, bufSize is size of buffer. +// After Read has returned, bufSize is set to the number of bytes actually read. +// §12.8.3.6 +func (p *EFI_SERIAL_IO_PROTOCOL) Read(bufSize *UINTN, buffer unsafe.Pointer) EFI_STATUS { + return UefiCall3( + p.read, + uintptr(unsafe.Pointer(p)), + uintptr(unsafe.Pointer(bufSize)), + uintptr(buffer), + ) +} + +// SerialPort wraps an EFI_SERIAL_IO_PROTOCOL and provides an idiomatic-go API +// TODO: make serial ports implement os.File +type SerialPort struct { + *EFI_SERIAL_IO_PROTOCOL + flowControl bool +} + +// EnumerateSerialPorts uses UEFI's handle walking API to discover +// serial ports. SerialPorts may be in use for something else, so +// EnumerateSerialPorts doesn't Init() any of the ports it returns. +func EnumerateSerialPorts() ([]*SerialPort, error) { + var ( + handleCount UINTN + handleBuffer *EFI_HANDLE + ) + status := BS().LocateHandleBuffer(ByProtocol, &EFI_SERIAL_IO_PROTOCOL_GUID, nil, &handleCount, &handleBuffer) + if status != EFI_SUCCESS { + return nil, StatusError(status) + } + // if none were found, we should have gotten EFI_NOT_FOUND + + //turn handleBuffer into a slice of EFI_HANDLEs + handleSlice := unsafe.Slice((*EFI_HANDLE)(unsafe.Pointer(handleBuffer)), int(handleCount)) + ports := make([]*SerialPort, int(handleCount)) + + for i := range int(handleCount) { + var serial *EFI_SERIAL_IO_PROTOCOL + status := BS().HandleProtocol( + handleSlice[i], + &EFI_SERIAL_IO_PROTOCOL_GUID, + unsafe.Pointer(&serial), + ) + if status != EFI_SUCCESS { + return nil, StatusError(status) + } + ports[i] = &SerialPort{EFI_SERIAL_IO_PROTOCOL: serial} + } + + return ports, nil +} + +// Init configures sp to use 115200 baud, 8N1. +// Read/Write timeout and Receive FIFO depth +// are left ot the serial driver's discretion. +func (sp *SerialPort) Init() error { + // can't hurt, can it? + status := sp.Reset() + if status != EFI_SUCCESS { + return StatusError(status) + } + // attempt a write to kick lazy FW/drivers into action + sp.Write([]byte{0}) + + status = sp.SetAttributes( + 115200, // BaudRate + 0, // ReceiveFifoDepth (0 = default) + 1, // Timeout (0 = default) + ParityNone, // EFI_PARITY_TYPE (1 = none) + 8, // DataBits + StopBits1, // StopBits (1) + ) + if status != EFI_SUCCESS { + println("SetAttributes failed\r") + return StatusError(status) + } + + return nil +} + +// Read implements io.Reader. Reads are blocking operations, however if the read +// operation times out, this method yields to other goroutines. If using hardware +// flow control, RTS is set and cleared accordingly. +func (sp *SerialPort) Read(buf []byte) (n int, err error) { + var controlBits uint32 + for { + sp.GetControl(&controlBits) + bufLen := UINTN(len(buf)) + // assert RTS and DTR; should be no harm to set/clear these even if hardware flow control + // is not in use. + sp.SetControl(controlBits | EFI_SERIAL_REQUEST_TO_SEND | EFI_SERIAL_DATA_TERMINAL_READY) + status := sp.EFI_SERIAL_IO_PROTOCOL.Read(&bufLen, unsafe.Pointer(&buf[0])) + if bufLen == 0 { + status = EFI_TIMEOUT + } + switch status { + case EFI_SUCCESS: + return int(bufLen), nil + case EFI_TIMEOUT, EFI_NO_RESPONSE: + // deassert RTS + sp.SetControl(controlBits & ^(EFI_SERIAL_REQUEST_TO_SEND)) + gosched() // let other stuff run + continue + default: + println(StatusError(status).Error()) + return 0, StatusError(status) + } + } +} + +/* +func (sp *SerialPort) WriteTo(w io.Writer) (n int64, err error) { + var nr, nw int + // buf := make([]byte, sp.Mode.ReceiveFifoDepth) + buf := make([]byte, 24) + for err == nil { + nr, err = sp.Read(buf) + if err != nil { + return + } + nw, err = sp.Write(buf[:nr]) + n += int64(nw) + } + return +} +*/ + +// Write implements io.Writer. If hardware flow control is enabled and CTS +// is cleared, Write will return (0, nil) indicating no bytes were written, +// but this is not an error. Some consumers of the io.Writer interface may +// not like this. +func (sp *SerialPort) Write(buf []byte) (n int, err error) { + var controlBits uint32 + sp.GetControl(&controlBits) + if sp.flowControl && controlBits&EFI_SERIAL_CLEAR_TO_SEND == 0 { + return 0, nil + } + bufLen := UINTN(len(buf)) + status := sp.EFI_SERIAL_IO_PROTOCOL.Write(&bufLen, unsafe.Pointer(&buf[0])) + if status != EFI_SUCCESS { + return int(bufLen), StatusError(status) + } + return int(bufLen), nil +} + +// UseFlowControl enables hardware flow control when fc is true. +func (sp *SerialPort) UseFlowControl(fc bool) { + sp.flowControl = fc +} diff --git a/src/machine/uefi/simple_file_system_protocol.go b/src/machine/uefi/simple_file_system_protocol.go new file mode 100644 index 0000000000..3db47017aa --- /dev/null +++ b/src/machine/uefi/simple_file_system_protocol.go @@ -0,0 +1,260 @@ +package uefi + +import ( + "unsafe" +) + +//--------------------------------------------------------------------------- +// GUIDs (§13.5 File Protocol and related info GUIDs) +//--------------------------------------------------------------------------- + +// EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID = {964E5B22-6459-11D2-8E39-00A0C969723B} +var EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID = EFI_GUID{ + 0x964E5B22, 0x6459, 0x11D2, + [8]uint8{0x8e, 0x39, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b}, +} + +// gEfiFileInfoGuid = {09576E92-6D3F-11D2-8E39-00A0C969723B} +var EFI_FILE_INFO_ID = EFI_GUID{ + 0x09576e92, 0x6d3f, 0x11d2, + [8]uint8{0x8e, 0x39, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b}, +} + +// gEfiFileSystemInfoGuid = {09576E93-6D3F-11D2-8E39-00A0C969723B} +var EFI_FILE_SYSTEM_INFO_ID = EFI_GUID{ + 0x09576e93, 0x6d3f, 0x11d2, + [8]uint8{0x8e, 0x39, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b}, +} + +// gEfiFileSystemVolumeLabelInfoIdGuid = {DB47D7D3-FE81-11D3-9A35-0090273FC14D} +var EFI_FILE_SYSTEM_VOLUME_LABEL_ID = EFI_GUID{ + 0xdb47d7d3, 0xfe81, 0x11d3, + [8]uint8{0x9a, 0x35, 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d}, +} + +//--------------------------------------------------------------------------- +// Constants (§13.5.1) +//--------------------------------------------------------------------------- + +// Open modes are 64-bit flags +const ( + EFI_FILE_MODE_READ uint64 = 0x0000000000000001 + EFI_FILE_MODE_WRITE uint64 = 0x0000000000000002 + EFI_FILE_MODE_CREATE uint64 = 0x8000000000000000 +) + +// Attribute flags (also 64-bit) +const ( + EFI_FILE_READ_ONLY uint64 = 0x0000000000000001 + EFI_FILE_HIDDEN uint64 = 0x0000000000000002 + EFI_FILE_SYSTEM uint64 = 0x0000000000000004 + EFI_FILE_RESERVED uint64 = 0x0000000000000008 + EFI_FILE_DIRECTORY uint64 = 0x0000000000000010 + EFI_FILE_ARCHIVE uint64 = 0x0000000000000020 +) + +// Optional: known protocol revision values (not strictly required to use) +const ( + EFI_FILE_PROTOCOL_REVISION = 0x00010000 + EFI_FILE_PROTOCOL_REVISION2 = 0x00020000 + EFI_FILE_PROTOCOL_LATEST = EFI_FILE_PROTOCOL_REVISION2 +) + +//--------------------------------------------------------------------------- +// Protocols (§13.4 Simple File System, §13.5 File Protocol) +//--------------------------------------------------------------------------- + +// EFI_SIMPLE_FILE_SYSTEM_PROTOCOL (§13.4.2) +// Function table: Revision, OpenVolume +// OpenVolume returns a handle to the volume root directory (EFI_FILE_PROTOCOL*) + +type EFI_SIMPLE_FILE_SYSTEM_PROTOCOL struct { + Revision uint64 + openVolume uintptr // (this, **EFI_FILE_PROTOCOL) +} + +func (p *EFI_SIMPLE_FILE_SYSTEM_PROTOCOL) OpenVolume(root **EFI_FILE_PROTOCOL) EFI_STATUS { + return UefiCall2( + p.openVolume, + uintptr(unsafe.Pointer(p)), + uintptr(unsafe.Pointer(root)), + ) +} + +// EFI_FILE_PROTOCOL (§13.5.2) +// Function table order: Revision, Open, Close, Delete, Read, Write, GetPosition, SetPosition, GetInfo, SetInfo, Flush + +type EFI_FILE_PROTOCOL struct { + Revision uint64 + open uintptr // (this, **newHandle, *FileName, OpenMode, Attributes) + close uintptr // (this) + delete uintptr // (this) + read uintptr // (this, *BufferSize, Buffer) + write uintptr // (this, *BufferSize, Buffer) + getPosition uintptr // (this, *Position) + setPosition uintptr // (this, Position) + getInfo uintptr // (this, *InfoType, *BufferSize, Buffer) + setInfo uintptr // (this, *InfoType, BufferSize, Buffer) + flush uintptr // (this) +} + +// Open opens a file or directory relative to this handle (which may be a root or directory handle). +// FileName must be a NUL-terminated UTF-16 path using '\\' separators. (§13.5.3) +func (p *EFI_FILE_PROTOCOL) Open(newHandle **EFI_FILE_PROTOCOL, fileName *CHAR16, openMode uint64, attributes uint64) EFI_STATUS { + return UefiCall6( + p.open, + uintptr(unsafe.Pointer(p)), + uintptr(unsafe.Pointer(newHandle)), + uintptr(unsafe.Pointer(fileName)), + uintptr(openMode), + uintptr(attributes), + 0, + ) +} + +// Close closes the file handle. (§13.5.4) +func (p *EFI_FILE_PROTOCOL) Close() EFI_STATUS { + return UefiCall1(p.close, uintptr(unsafe.Pointer(p))) +} + +// Delete deletes the file opened by this handle. (§13.5.5) +func (p *EFI_FILE_PROTOCOL) Delete() EFI_STATUS { + return UefiCall1(p.delete, uintptr(unsafe.Pointer(p))) +} + +// Read reads from the file into Buffer. BufferSize is in/out: on entry, size of Buffer; on return, bytes read. (§13.5.6) +func (p *EFI_FILE_PROTOCOL) Read(bufferSize *UINTN, buffer unsafe.Pointer) EFI_STATUS { + return UefiCall3( + p.read, + uintptr(unsafe.Pointer(p)), + uintptr(unsafe.Pointer(bufferSize)), + uintptr(buffer), + ) +} + +// Write writes to the file from Buffer. BufferSize is in/out: on entry, bytes to write; on return, bytes written. (§13.5.7) +func (p *EFI_FILE_PROTOCOL) Write(bufferSize *UINTN, buffer unsafe.Pointer) EFI_STATUS { + return UefiCall3( + p.write, + uintptr(unsafe.Pointer(p)), + uintptr(unsafe.Pointer(bufferSize)), + uintptr(buffer), + ) +} + +// GetPosition returns the current file position in bytes. (§13.5.8) +func (p *EFI_FILE_PROTOCOL) GetPosition(position *uint64) EFI_STATUS { + return UefiCall2( + p.getPosition, + uintptr(unsafe.Pointer(p)), + uintptr(unsafe.Pointer(position)), + ) +} + +// SetPosition sets the current file position. Setting to 0xFFFFFFFFFFFFFFFF seeks to end-of-file. (§13.5.9) +func (p *EFI_FILE_PROTOCOL) SetPosition(position uint64) EFI_STATUS { + return UefiCall2( + p.setPosition, + uintptr(unsafe.Pointer(p)), + uintptr(position), + ) +} + +// GetInfo retrieves metadata identified by InformationType. Common GUIDs: EFI_FILE_INFO_ID, EFI_FILE_SYSTEM_INFO_ID, EFI_FILE_SYSTEM_VOLUME_LABEL_ID. (§13.5.10) +func (p *EFI_FILE_PROTOCOL) GetInfo(infoType *EFI_GUID, bufferSize *UINTN, buffer unsafe.Pointer) EFI_STATUS { + return UefiCall4( + p.getInfo, + uintptr(unsafe.Pointer(p)), + uintptr(unsafe.Pointer(infoType)), + uintptr(unsafe.Pointer(bufferSize)), + uintptr(buffer), + ) +} + +// SetInfo sets metadata identified by InformationType. (§13.5.11) +func (p *EFI_FILE_PROTOCOL) SetInfo(infoType *EFI_GUID, bufferSize UINTN, buffer unsafe.Pointer) EFI_STATUS { + return UefiCall4( + p.setInfo, + uintptr(unsafe.Pointer(p)), + uintptr(unsafe.Pointer(infoType)), + uintptr(bufferSize), + uintptr(buffer), + ) +} + +// Flush flushes file data and metadata to the device. (§13.5.12) +func (p *EFI_FILE_PROTOCOL) Flush() EFI_STATUS { + return UefiCall1(p.flush, uintptr(unsafe.Pointer(p))) +} + +//--------------------------------------------------------------------------- +// Info structures (§13.5.13, §13.5.15) +//--------------------------------------------------------------------------- + +// EFI_FILE_INFO head (FileName is a variable-length CHAR16[] immediately following this struct) +// Layout must exactly match the spec; FileName is retrieved from the backing buffer used with GetInfo. + +type EFI_FILE_INFO struct { + Size uint64 + FileSize uint64 + PhysicalSize uint64 + CreateTime EFI_TIME + LastAccessTime EFI_TIME + ModificationTime EFI_TIME + Attribute uint64 + // CHAR16 FileName[] follows +} + +// EFI_FILE_SYSTEM_INFO head (VolumeLabel is a variable-length CHAR16[] after the struct) + +type EFI_FILE_SYSTEM_INFO struct { + Size uint64 + ReadOnly bool + _ [7]byte // pad to 8-byte alignment (bool is 1 byte) + VolumeSize uint64 + FreeSpace uint64 + BlockSize uint32 + _ uint32 // padding to keep next CHAR16[] aligned as in C + // CHAR16 VolumeLabel[] follows +} + +// EFI_FILE_SYSTEM_VOLUME_LABEL is just a CHAR16[] volume label; retrieved via GetInfo with EFI_FILE_SYSTEM_VOLUME_LABEL_ID. +// Represented implicitly by reading a buffer of UTF-16 data. + +//--------------------------------------------------------------------------- +// Helpers: enumerate volumes and open root +//--------------------------------------------------------------------------- + +// EnumerateFileSystems locates all Simple File System handles and returns their protocol pointers. +func EnumerateFileSystems() ([]*EFI_SIMPLE_FILE_SYSTEM_PROTOCOL, error) { + var ( + handleCount UINTN + handleBuffer *EFI_HANDLE + ) + st := BS().LocateHandleBuffer(ByProtocol, &EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID, nil, &handleCount, &handleBuffer) + if st == EFI_NOT_FOUND { + return nil, nil + } + if st != EFI_SUCCESS { + return nil, StatusError(st) + } + handles := unsafe.Slice((*EFI_HANDLE)(unsafe.Pointer(handleBuffer)), int(handleCount)) + out := make([]*EFI_SIMPLE_FILE_SYSTEM_PROTOCOL, 0, len(handles)) + for _, h := range handles { + var sfs *EFI_SIMPLE_FILE_SYSTEM_PROTOCOL + if st = BS().HandleProtocol(h, &EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID, unsafe.Pointer(&sfs)); st == EFI_SUCCESS { + out = append(out, sfs) + } + } + return out, nil +} + +// OpenRoot opens the volume root directory for a given SFS. +func OpenRoot(sfs *EFI_SIMPLE_FILE_SYSTEM_PROTOCOL) (*EFI_FILE_PROTOCOL, EFI_STATUS) { + var root *EFI_FILE_PROTOCOL + st := sfs.OpenVolume(&root) + if st != EFI_SUCCESS { + return nil, st + } + return root, EFI_SUCCESS +} diff --git a/src/machine/uefi/simple_network_protocol.go b/src/machine/uefi/simple_network_protocol.go new file mode 100644 index 0000000000..a3f1ca3dee --- /dev/null +++ b/src/machine/uefi/simple_network_protocol.go @@ -0,0 +1,125 @@ +package uefi + +import "unsafe" + +//--------------------------------------------------------------------------- +// GUID +//--------------------------------------------------------------------------- + +// {A19832B9-AC25-11D3-9A2D-0090273FC14D} +var EFI_SIMPLE_NETWORK_PROTOCOL_GUID = EFI_GUID{ + 0xA19832B9, 0xAC25, 0x11D3, + [8]uint8{0x9A, 0x2D, 0x00, 0x90, 0x27, 0x3F, 0xC1, 0x4D}, +} + +//--------------------------------------------------------------------------- +// Enums and Constants – §10.4 +//--------------------------------------------------------------------------- + +const ( + EFI_SIMPLE_NETWORK_STOPPED = 0 + EFI_SIMPLE_NETWORK_STARTED = 1 + EFI_SIMPLE_NETWORK_INITIALIZED = 2 +) + +// --------------------------------------------------------------------------- +// +// Types – §10.4.3 +// +// --------------------------------------------------------------------------- +type EFI_SIMPLE_NETWORK_MODE struct { + State uint32 + HwAddressSize uint32 + MediaHeaderSize uint32 + MaxPacketSize uint32 + NvRamSize uint32 + NvRamAccessSize uint32 + ReceiveFilterMask uint32 + ReceiveFilterSetting uint32 + MaxMCastFilterCount uint32 + MCastFilterCount uint32 + MCastFilter [32]EFI_MAC_ADDRESS + CurrentAddress EFI_MAC_ADDRESS + BroadcastAddress EFI_MAC_ADDRESS + PermanentAddress EFI_MAC_ADDRESS + IfType uint8 + MacAddressChangeable bool + MultipleTxSupported bool + MediaPresentSupported bool + MediaPresent bool +} + +//--------------------------------------------------------------------------- +// Protocol – §10.4.2 +//--------------------------------------------------------------------------- + +type EFI_SIMPLE_NETWORK_PROTOCOL struct { + Revision uint64 + start uintptr // (this) + stop uintptr // (this) + initialize uintptr // (this) + reset uintptr // (this, extVerify) + shutdown uintptr // (this) + receiveFilters uintptr // (this, enable, disable, resetMCast, mCastCount, mCastFilter) + stationAddress uintptr // (this, reset, newAddress) + statistics uintptr // (this, reset, statsSize, stats) + mCastIpToMac uintptr // (this, ipv4, ipv6, mac) + nvData uintptr // (this, readWrite, offset, bufferSize, buffer) + getStatus uintptr // (this, intStatus, txBuf) + transmit uintptr // (this, headerSize, bufferSize, buffer, srcAddr, dstAddr, proto) + receive uintptr // (this, headerSize, bufferSize, buffer, srcAddr, dstAddr, proto) + WaitForPacket EFI_EVENT + Mode *EFI_SIMPLE_NETWORK_MODE +} + +func (snp *EFI_SIMPLE_NETWORK_PROTOCOL) Start() EFI_STATUS { + return UefiCall1(snp.start, uintptr(unsafe.Pointer(snp))) +} + +func (snp *EFI_SIMPLE_NETWORK_PROTOCOL) Stop() EFI_STATUS { + return UefiCall1(snp.stop, uintptr(unsafe.Pointer(snp))) +} + +func (snp *EFI_SIMPLE_NETWORK_PROTOCOL) Initialize() EFI_STATUS { + return UefiCall1(snp.initialize, uintptr(unsafe.Pointer(snp))) +} + +func (snp *EFI_SIMPLE_NETWORK_PROTOCOL) Shutdown() EFI_STATUS { + return UefiCall1(snp.shutdown, uintptr(unsafe.Pointer(snp))) +} + +type SimpleNetworkProtocol struct { + *EFI_SIMPLE_NETWORK_PROTOCOL +} + +func EnumerateSNP() (snps []*SimpleNetworkProtocol, err error) { + var ( + handleCount UINTN + handleBuffer *EFI_HANDLE + ) + status := BS().LocateHandleBuffer(ByProtocol, &EFI_SIMPLE_NETWORK_PROTOCOL_GUID, nil, &handleCount, &handleBuffer) + if status != EFI_SUCCESS { + return nil, StatusError(status) + } + // if none were found, we should have gotten EFI_NOT_FOUND + + //turn handleBuffer into a slice of EFI_HANDLEs + handleSlice := unsafe.Slice((*EFI_HANDLE)(unsafe.Pointer(handleBuffer)), int(handleCount)) + + for i := range handleSlice { + BS().ConnectController(handleSlice[i], nil, nil, true) + var snp *EFI_SIMPLE_NETWORK_PROTOCOL + status := BS().HandleProtocol( + handleSlice[i], + &EFI_DHCP4_SERVICE_BINDING_PROTOCOL_GUID, + unsafe.Pointer(&snp), + ) + if status != EFI_SUCCESS { + // just skip, or error out entirely?? Hmm.. + continue + } + snps = append(snps, &SimpleNetworkProtocol{EFI_SIMPLE_NETWORK_PROTOCOL: snp}) + } + + return snps, nil +} diff --git a/src/machine/uefi/simple_text_in.go b/src/machine/uefi/simple_text_in.go new file mode 100644 index 0000000000..f98db178ae --- /dev/null +++ b/src/machine/uefi/simple_text_in.go @@ -0,0 +1,135 @@ +package uefi + +import ( + "unsafe" +) + +type EFI_KEY_TOGGLE_STATE uint8 + +const ( + EFI_SCROLL_LOCK_ACTIVE EFI_KEY_TOGGLE_STATE = 0x01 + EFI_NUM_LOCK_ACTIVE = 0x02 + EFI_CAPS_LOCK_ACTIVE = 0x04 + EFI_KEY_STATE_EXPOSED = 0x40 + EFI_TOGGLE_STATE_VALID = 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 +) + +// EFI_INPUT_KEY +// The keystroke information for the key that was pressed. +type EFI_INPUT_KEY struct { + ScanCode uint16 + UnicodeChar CHAR16 +} + +// EFI_SIMPLE_TEXT_INPUT_PROTOCOL +// The EFI_SIMPLE_TEXT_INPUT_PROTOCOL is used on the ConsoleIn device. +// It is the minimum required protocol for ConsoleIn. +type EFI_SIMPLE_TEXT_INPUT_PROTOCOL struct { + reset uintptr + readKeyStroke uintptr + WaitForKey EFI_EVENT +} + +// Reset +// Reset the input device and optionally run diagnostics +// @param This Protocol instance pointer. +// @param ExtendedVerification Driver may perform diagnostics on reset. +// @retval EFI_SUCCESS The device was reset. +// @retval EFI_DEVICE_ERROR The device is not functioning properly and could not be reset. +func (p *EFI_SIMPLE_TEXT_INPUT_PROTOCOL) Reset(ExtendedVerification BOOLEAN) EFI_STATUS { + return UefiCall2(p.reset, uintptr(unsafe.Pointer(p)), convertBoolean(ExtendedVerification)) +} + +// ReadKeyStroke +// Reads the next keystroke from the input device. The WaitForKey Event can +// be used to test for existence of a keystroke via WaitForEvent () call. +// @param This Protocol instance pointer. +// @param Key A pointer to a buffer that is filled in with the keystroke +// ..............information for the key that was pressed. +// @retval EFI_SUCCESS The keystroke information was returned. +// @retval EFI_NOT_READY There was no keystroke data available. +// @retval EFI_DEVICE_ERROR The keystroke information was not returned due to +// .........................hardware errors. +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))) +} + +// var EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID = EFI_GUID{ +var SimpleTextInputExProtocolGUID = EFI_GUID{ + 0xdd9e7534, 0x7762, 0x4698, + [...]byte{0x8c, 0x14, 0xf5, 0x85, 0x17, 0xa6, 0x25, 0xaa}} + +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 +} + +type EFI_KEY_STATE struct { + KeyShiftState uint32 + KeyToggleState EFI_KEY_TOGGLE_STATE +} + +func (p *EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL) Reset(ExtendedVerification BOOLEAN) EFI_STATUS { + return UefiCall2(p.resetEx, uintptr(unsafe.Pointer(p)), convertBoolean(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))) +} + +// SimpleTextInExProtocol finds and returns the first handle implementing this protocol +// and returns it. Usually there will only be one or multiple get multiplexed together. +func SimpleTextInExProtocol() (*EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL, error) { + st := ST() + var iFace unsafe.Pointer + status := (*st).BootServices.LocateProtocol( + &SimpleTextInputExProtocolGUID, + nil, + unsafe.Pointer(&iFace)) + if status == EFI_SUCCESS { + stiep := (*EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL)(iFace) + return stiep, nil + } + + return nil, StatusError(status) +} + +// GetKey blocks while waiting to receive a key. It yields to the scheduler +// so other goroutines may continue to work while waiting for a key press. +func (p *EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL) GetKey() EFI_KEY_DATA { + var key EFI_KEY_DATA + + // Wait for key event, while yielding to other routines + WaitForEvent(p.WaitForKeyEx) + + // Read key stroke + status := p.ReadKeyStroke(&key) + if status != EFI_SUCCESS { + return key + } + + return key +} diff --git a/src/machine/uefi/simple_text_out.go b/src/machine/uefi/simple_text_out.go new file mode 100644 index 0000000000..de4dd5059b --- /dev/null +++ b/src/machine/uefi/simple_text_out.go @@ -0,0 +1,157 @@ +package uefi + +import "unsafe" + +type EFI_SIMPLE_TEXT_OUTPUT_MODE struct { + MaxMode int32 + Mode int32 + Attribute int32 + CursorColumn int32 + CursorRow int32 + CursorVisible BOOLEAN +} + +// EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL +// The SIMPLE_TEXT_OUTPUT protocol is used to control text-based output devices. +// It is the minimum required protocol for any handle supplied as the ConsoleOut +// or StandardError device. In addition, the minimum supported text mode of such +// devices is at least 80 x 25 characters. +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 +} + +// Reset +// Reset the text output device hardware and optionally run diagnostics +// @param This The protocol instance pointer. +// @param ExtendedVerification Driver may perform more exhaustive verification +// .............................operation of the device during reset. +// @retval EFI_SUCCESS The text output device was reset. +// @retval EFI_DEVICE_ERROR The text output device is not functioning correctly and +// .............................could not be reset. +func (p *EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL) Reset(ExtendedVerification BOOLEAN) EFI_STATUS { + return UefiCall2(p.reset, uintptr(unsafe.Pointer(p)), convertBoolean(ExtendedVerification)) +} + +// OutputString +// Write a string to the output device. +// @param This The protocol instance pointer. +// @param String The NULL-terminated string to be displayed on the output +// ...............device(s). All output devices must also support the Unicode +// ...............drawing character codes defined in this file. +// @retval EFI_SUCCESS The string was output to the device. +// @retval EFI_DEVICE_ERROR The device reported an error while attempting to output +// ................................the text. +// @retval EFI_UNSUPPORTED The output device's mode is not currently in a +// ................................defined text mode. +// @retval EFI_WARN_UNKNOWN_GLYPH This warning code indicates that some of the +// ................................characters in the string could not be +// ................................rendered and were skipped. +func (p *EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL) OutputString(String *CHAR16) EFI_STATUS { + return UefiCall2(p.outputString, uintptr(unsafe.Pointer(p)), uintptr(unsafe.Pointer(String))) +} + +// TestString +// Verifies that all characters in a string can be output to the +// target device. +// @param This The protocol instance pointer. +// @param String The NULL-terminated string to be examined for the output +// ...............device(s). +// @retval EFI_SUCCESS The device(s) are capable of rendering the output string. +// @retval EFI_UNSUPPORTED Some of the characters in the string cannot be +// .........................rendered by one or more of the output devices mapped +// .........................by the EFI handle. +func (p *EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL) TestString(String *CHAR16) EFI_STATUS { + return UefiCall2(p.testString, uintptr(unsafe.Pointer(p)), uintptr(unsafe.Pointer(String))) +} + +// QueryMode +// Returns information for an available text mode that the output device(s) +// supports. +// @param This The protocol instance pointer. +// @param ModeNumber The mode number to return information on. +// @param Columns Returns the geometry of the text output device for the +// ...................requested ModeNumber. +// @param Rows Returns the geometry of the text output device for the +// ...................requested ModeNumber. +// @retval EFI_SUCCESS The requested mode information was returned. +// @retval EFI_DEVICE_ERROR The device had an error and could not complete the request. +// @retval EFI_UNSUPPORTED The mode number was not valid. +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))) +} + +// SetMode +// Sets the output device(s) to a specified mode. +// @param This The protocol instance pointer. +// @param ModeNumber The mode number to set. +// @retval EFI_SUCCESS The requested text mode was set. +// @retval EFI_DEVICE_ERROR The device had an error and could not complete the request. +// @retval EFI_UNSUPPORTED The mode number was not valid. +func (p *EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL) SetMode(ModeNumber UINTN) EFI_STATUS { + return UefiCall2(p.setMode, uintptr(unsafe.Pointer(p)), uintptr(ModeNumber)) +} + +// SetAttribute +// Sets the background and foreground colors for the OutputString () and +// ClearScreen () functions. +// @param This The protocol instance pointer. +// @param Attribute The attribute to set. Bits 0..3 are the foreground color, and +// ..................bits 4..6 are the background color. All other bits are undefined +// ..................and must be zero. The valid Attributes are defined in this file. +// @retval EFI_SUCCESS The attribute was set. +// @retval EFI_DEVICE_ERROR The device had an error and could not complete the request. +// @retval EFI_UNSUPPORTED The attribute requested is not defined. +func (p *EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL) SetAttribute(Attribute UINTN) EFI_STATUS { + return UefiCall2(p.setAttribute, uintptr(unsafe.Pointer(p)), uintptr(Attribute)) +} + +// ClearScreen +// Clears the output device(s) display to the currently selected background +// color. +// @param This The protocol instance pointer. +// @retval EFI_SUCCESS The operation completed successfully. +// @retval EFI_DEVICE_ERROR The device had an error and could not complete the request. +// @retval EFI_UNSUPPORTED The output device is not in a valid text mode. +func (p *EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL) ClearScreen() EFI_STATUS { + return UefiCall1(p.clearScreen, uintptr(unsafe.Pointer(p))) +} + +// SetCursorPosition +// Sets the current coordinates of the cursor position +// @param This The protocol instance pointer. +// @param Column The position to set the cursor to. Must be greater than or +// ....................equal to zero and less than the number of columns and rows +// ....................by QueryMode (). +// @param Row The position to set the cursor to. Must be greater than or +// ....................equal to zero and less than the number of columns and rows +// ....................by QueryMode (). +// @retval EFI_SUCCESS The operation completed successfully. +// @retval EFI_DEVICE_ERROR The device had an error and could not complete the request. +// @retval EFI_UNSUPPORTED The output device is not in a valid text mode, or the +// .........................cursor position is invalid for the current mode. +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)) +} + +// EnableCursor +// Makes the cursor visible or invisible +// @param This The protocol instance pointer. +// @param Visible If TRUE, the cursor is set to be visible. If FALSE, the cursor is +// ................set to be invisible. +// @retval EFI_SUCCESS The operation completed successfully. +// @retval EFI_DEVICE_ERROR The device had an error and could not complete the +// .........................request, or the device does not support changing +// .........................the cursor mode. +// @retval EFI_UNSUPPORTED The output device is not in a valid text mode. +func (p *EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL) EnableCursor(Visible BOOLEAN) EFI_STATUS { + return UefiCall2(p.enableCursor, uintptr(unsafe.Pointer(p)), convertBoolean(Visible)) +} diff --git a/src/machine/uefi/uefi_base_type.go b/src/machine/uefi/uefi_base_type.go new file mode 100644 index 0000000000..9a7d828dcc --- /dev/null +++ b/src/machine/uefi/uefi_base_type.go @@ -0,0 +1,25 @@ +package uefi + +// EFI_TIME +// EFI Time Abstraction: +// Year: 1900 - 9999 +// Month: 1 - 12 +// Day: 1 - 31 +// Hour: 0 - 23 +// Minute: 0 - 59 +// Second: 0 - 59 +// Nanosecond: 0 - 999,999,999 +// TimeZone: -1440 to 1440 or 2047 +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 +} diff --git a/src/machine/uefi/uefi_spec.go b/src/machine/uefi/uefi_spec.go new file mode 100644 index 0000000000..d14f1abf03 --- /dev/null +++ b/src/machine/uefi/uefi_spec.go @@ -0,0 +1,1422 @@ +package uefi + +import ( + "unsafe" +) + +// EFI_SPECIFICATION_REVISION_MAJORMINOR combines the major and minor revision into a single value. +// @param major Major revision +// @param minor Minor revision +// @return Combined major and minor revision +func EFI_SPECIFICATION_REVISION_MAJORMINOR(major, minor uint16) uint32 { + return (uint32(major) << 16) | uint32(minor) +} + +// EFI_SPECIFICATION_MAJOR_REVISION represents the major revision of the EFI Specification. +const EFI_SPECIFICATION_MAJOR_REVISION uint16 = 1 + +// EFI_SPECIFICATION_MINOR_REVISION represents the minor revision of the EFI Specification. +const EFI_SPECIFICATION_MINOR_REVISION uint16 = 2 + +// EFI_SPECIFICATION_VERSION represents the combined major and minor revision of the EFI Specification. +var EFI_SPECIFICATION_VERSION = EFI_SPECIFICATION_REVISION_MAJORMINOR(EFI_SPECIFICATION_MAJOR_REVISION, EFI_SPECIFICATION_MINOR_REVISION) + +// EFI_TABLE_HEADER +// Standard EFI table header +type EFI_TABLE_HEADER struct { + Signature uint64 + Revision uint32 + HeaderSize uint32 + CRC32 uint32 + Reserved uint32 +} + +type EFI_PHYSICAL_ADDRESS uint64 +type EFI_VIRTUAL_ADDRESS uint64 + +type EFI_ALLOCATE_TYPE int + +const ( + AllocateAnyPages EFI_ALLOCATE_TYPE = iota + AllocateMaxAddress + AllocateAddress + MaxAllocateType +) + +// EFI_MEMORY_TYPE is an enumeration of memory types. +type EFI_MEMORY_TYPE int + +const ( + EfiReservedMemoryType EFI_MEMORY_TYPE = iota + EfiLoaderCode + EfiLoaderData + EfiBootServicesCode + EfiBootServicesData + EfiRuntimeServicesCode + EfiRuntimeServicesData + EfiConventionalMemory + EfiUnusableMemory + EfiACPIReclaimMemory + EfiACPIMemoryNVS + EfiMemoryMappedIO + EfiMemoryMappedIOPortSpace + EfiPalCode + EfiPersistentMemory + EfiUnacceptedMemoryType + EfiMaxMemoryType +) + +// Memory cacheability attribute +const ( + EFI_MEMORY_UC uint64 = 0x0000000000000001 + EFI_MEMORY_WC uint64 = 0x0000000000000002 + EFI_MEMORY_WT uint64 = 0x0000000000000004 + EFI_MEMORY_WB uint64 = 0x0000000000000008 + EFI_MEMORY_UCE uint64 = 0x0000000000000010 +) + +// Physical memory protection attribute +const ( + EFI_MEMORY_WP uint64 = 0x0000000000001000 + EFI_MEMORY_RP uint64 = 0x0000000000002000 + EFI_MEMORY_XP uint64 = 0x0000000000004000 + EFI_MEMORY_RO uint64 = 0x0000000000020000 +) + +// Runtime memory attribute +const ( + EFI_MEMORY_NV uint64 = 0x0000000000008000 + EFI_MEMORY_RUNTIME uint64 = 0x8000000000000000 +) + +// Other memory attribute +const ( + EFI_MEMORY_MORE_RELIABLE uint64 = 0x0000000000010000 + EFI_MEMORY_SP uint64 = 0x0000000000040000 + EFI_MEMORY_CPU_CRYPTO uint64 = 0x0000000000080000 + EFI_MEMORY_ISA_VALID uint64 = 0x4000000000000000 + EFI_MEMORY_ISA_MASK uint64 = 0x0FFFF00000000000 +) + +const EFI_MEMORY_DESCRIPTOR_VERSION = 1 + +// Variable Service attribute bits +const ( + EFI_VARIABLE_NON_VOLATILE uint32 = 0x00000001 + EFI_VARIABLE_BOOTSERVICE_ACCESS = 0x00000002 + EFI_VARIABLE_RUNTIME_ACCESS = 0x00000004 +) + +// EFI_MEMORY_DESCRIPTOR +// Definition of an EFI memory descriptor. +type EFI_MEMORY_DESCRIPTOR struct { + Type uint32 + PhysicalStart EFI_PHYSICAL_ADDRESS + VirtualStart EFI_VIRTUAL_ADDRESS + NumberOfPages uint64 + Attribute uint64 +} + +// EFI_TIME_CAPABILITIES +// This provides the capabilities of the +// real time clock device as exposed through the EFI interfaces. +type EFI_TIME_CAPABILITIES struct { + Resolution uint32 + Accuracy uint32 + SetsToZero BOOLEAN +} + +// EFI_OPEN_PROTOCOL_INFORMATION_ENTRY +// EFI Oprn Protocol Information Entry +type EFI_OPEN_PROTOCOL_INFORMATION_ENTRY struct { + AgentHandle EFI_HANDLE + ControllerHandle EFI_HANDLE + Attributes uint32 + OpenCount uint32 +} + +// EFI_CAPSULE_BLOCK_DESCRIPTOR +// EFI Capsule Block Descriptor +type EFI_CAPSULE_BLOCK_DESCRIPTOR struct { + Length uint64 + DataBlock EFI_PHYSICAL_ADDRESS + ContinuationPointer EFI_PHYSICAL_ADDRESS +} + +// EFI_CAPSULE_HEADER +// EFI Capsule Header. +type EFI_CAPSULE_HEADER struct { + CapsuleGuid EFI_GUID + HeaderSize uint32 + Flags uint32 + CapsuleImageSize uint32 +} + +// EFI_CAPSULE_TABLE +// The EFI System Table entry must point to an array of capsules +// that contain the same CapsuleGuid value. The array must be +// prefixed by a UINT32 that represents the size of the array of capsules. +type EFI_CAPSULE_TABLE struct { + CapsuleArrayNumber uint32 +} + +type EFI_LOCATE_SEARCH_TYPE int + +const ( + AllHandles EFI_LOCATE_SEARCH_TYPE = 0 + ByRegisterNotify EFI_LOCATE_SEARCH_TYPE = 1 + ByProtocol EFI_LOCATE_SEARCH_TYPE = 2 +) + +// region: EFI Runtime Services Table + +type EFI_RESET_TYPE int + +const ( + EfiResetCold EFI_RESET_TYPE = 0 + EfiResetWarm EFI_RESET_TYPE = 1 + EfiResetShutdown EFI_RESET_TYPE = 2 +) + +const EFI_RUNTIME_SERVICES_SIGNATURE = 0x56524553544e5552 + +var ( + EFI_1_02_RUNTIME_SERVICES_REVISION = EFI_SPECIFICATION_REVISION_MAJORMINOR(1, 02) + EFI_1_10_RUNTIME_SERVICES_REVISION = EFI_SPECIFICATION_REVISION_MAJORMINOR(1, 10) + EFI_2_00_RUNTIME_SERVICES_REVISION = EFI_SPECIFICATION_REVISION_MAJORMINOR(2, 00) + EFI_2_10_RUNTIME_SERVICES_REVISION = EFI_SPECIFICATION_REVISION_MAJORMINOR(2, 10) + EFI_2_20_RUNTIME_SERVICES_REVISION = EFI_SPECIFICATION_REVISION_MAJORMINOR(2, 20) + EFI_2_30_RUNTIME_SERVICES_REVISION = EFI_SPECIFICATION_REVISION_MAJORMINOR(2, 30) + EFI_2_31_RUNTIME_SERVICES_REVISION = EFI_SPECIFICATION_REVISION_MAJORMINOR(2, 31) + EFI_2_40_RUNTIME_SERVICES_REVISION = EFI_SPECIFICATION_REVISION_MAJORMINOR(2, 40) + EFI_2_50_RUNTIME_SERVICES_REVISION = EFI_SPECIFICATION_REVISION_MAJORMINOR(2, 50) + EFI_2_60_RUNTIME_SERVICES_REVISION = EFI_SPECIFICATION_REVISION_MAJORMINOR(2, 60) + EFI_2_70_RUNTIME_SERVICES_REVISION = EFI_SPECIFICATION_REVISION_MAJORMINOR(2, 70) + EFI_2_80_RUNTIME_SERVICES_REVISION = EFI_SPECIFICATION_REVISION_MAJORMINOR(2, 80) + EFI_2_90_RUNTIME_SERVICES_REVISION = EFI_SPECIFICATION_REVISION_MAJORMINOR(2, 90) + EFI_2_100_RUNTIME_SERVICES_REVISION = EFI_SPECIFICATION_REVISION_MAJORMINOR(2, 100) + EFI_RUNTIME_SERVICES_REVISION = EFI_SPECIFICATION_VERSION +) + +// EFI_RUNTIME_SERVICES +// EFI Runtime Services Table. +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 + getNextHighMonotonicCount uintptr + resetSystem uintptr + updateCapsule uintptr + queryCapsuleCapabilities uintptr + queryVariableInfo uintptr +} + +// GetTime +// Returns the current time and date information, and the time-keeping capabilities +// of the hardware platform. +// @param[out] Time A pointer to storage to receive a snapshot of the current time. +// @param[out] Capabilities An optional pointer to a buffer to receive the real time clock +// ..............................device's capabilities. +// @retval EFI_SUCCESS The operation completed successfully. +// @retval EFI_INVALID_PARAMETER Time is NULL. +// @retval EFI_DEVICE_ERROR The time could not be retrieved due to hardware error. +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))) +} + +// SetTime +// Sets the current local time and date information. +// @param[in] Time A pointer to the current time. +// @retval EFI_SUCCESS The operation completed successfully. +// @retval EFI_INVALID_PARAMETER A time field is out of range. +// @retval EFI_DEVICE_ERROR The time could not be set due due to hardware error. +func (p *EFI_RUNTIME_SERVICES) SetTime(Time *EFI_TIME) EFI_STATUS { + return UefiCall1(p.setTime, uintptr(unsafe.Pointer(Time))) +} + +// GetWakeupTime +// Returns the current wakeup alarm clock setting. +// @param[out] Enabled Indicates if the alarm is currently enabled or disabled. +// @param[out] Pending Indicates if the alarm signal is pending and requires acknowledgement. +// @param[out] Time The current alarm setting. +// @retval EFI_SUCCESS The alarm settings were returned. +// @retval EFI_INVALID_PARAMETER Enabled is NULL. +// @retval EFI_INVALID_PARAMETER Pending is NULL. +// @retval EFI_INVALID_PARAMETER Time is NULL. +// @retval EFI_DEVICE_ERROR The wakeup time could not be retrieved due to a hardware error. +// @retval EFI_UNSUPPORTED A wakeup timer is not supported on this platform. +func (p *EFI_RUNTIME_SERVICES) GetWakeupTime(Enabled *BOOLEAN, Pending *BOOLEAN, Time *EFI_TIME) EFI_STATUS { + return UefiCall3(p.getWakeupTime, uintptr(unsafe.Pointer(Enabled)), uintptr(unsafe.Pointer(Pending)), uintptr(unsafe.Pointer(Time))) +} + +// SetWakeupTime +// Sets the system wakeup alarm clock time. +// @param[in] Enable Enable or disable the wakeup alarm. +// @param[in] Time If Enable is TRUE, the time to set the wakeup alarm for. +// ..............................If Enable is FALSE, then this parameter is optional, and may be NULL. +// @retval EFI_SUCCESS If Enable is TRUE, then the wakeup alarm was enabled. If +// ..............................Enable is FALSE, then the wakeup alarm was disabled. +// @retval EFI_INVALID_PARAMETER A time field is out of range. +// @retval EFI_DEVICE_ERROR The wakeup time could not be set due to a hardware error. +// @retval EFI_UNSUPPORTED A wakeup timer is not supported on this platform. +func (p *EFI_RUNTIME_SERVICES) SetWakeupTime(Enable BOOLEAN, Time *EFI_TIME) EFI_STATUS { + var enable uintptr = 0 + if Enable { + enable = 1 + } + return UefiCall2(p.setWakeupTime, enable, uintptr(unsafe.Pointer(Time))) +} + +// SetVirtualAddressMap +// Changes the runtime addressing mode of EFI firmware from physical to virtual. +// @param[in] MemoryMapSize The size in bytes of VirtualMap. +// @param[in] DescriptorSize The size in bytes of an entry in the VirtualMap. +// @param[in] DescriptorVersion The version of the structure entries in VirtualMap. +// @param[in] VirtualMap An array of memory descriptors which contain new virtual +// ..............................address mapping information for all runtime ranges. +// @retval EFI_SUCCESS The virtual address map has been applied. +// @retval EFI_UNSUPPORTED EFI firmware is not at runtime, or the EFI firmware is already in +// ..............................virtual address mapped mode. +// @retval EFI_INVALID_PARAMETER DescriptorSize or DescriptorVersion is invalid. +// @retval EFI_NO_MAPPING A virtual address was not supplied for a range in the memory +// ..............................map that requires a mapping. +// @retval EFI_NOT_FOUND A virtual address was supplied for an address that is not found +// ..............................in the memory map. +func (p *EFI_RUNTIME_SERVICES) SetVirtualAddressMap(MemoryMapSize UINTN, DescriptorSize UINTN, DescriptorVersion uint32, VirtualMap *EFI_MEMORY_DESCRIPTOR) EFI_STATUS { + return UefiCall4(p.setVirtualAddressMap, uintptr(MemoryMapSize), uintptr(DescriptorSize), uintptr(DescriptorVersion), uintptr(unsafe.Pointer(VirtualMap))) +} + +// ConvertPointer +// Determines the new virtual address that is to be used on subsequent memory accesses. +// @param[in] DebugDisposition Supplies type information for the pointer being converted. +// @param[in, out] Address A pointer to a pointer that is to be fixed to be the value needed +// .................for the new virtual address mappings being applied. +// @retval EFI_SUCCESS The pointer pointed to by Address was modified. +// @retval EFI_INVALID_PARAMETER 1) Address is NULL. +// ..............................2) *Address is NULL and DebugDisposition does +// ..............................not have the EFI_OPTIONAL_PTR bit set. +// @retval EFI_NOT_FOUND The pointer pointed to by Address was not found to be part +// ..............................of the current memory map. This is normally fatal. +func (p *EFI_RUNTIME_SERVICES) ConvertPointer(DebugDisposition UINTN, Address **VOID) EFI_STATUS { + return UefiCall2(p.convertPointer, uintptr(DebugDisposition), uintptr(unsafe.Pointer(Address))) +} + +// GetVariable +// Returns the value of a variable. +// @param[in] VariableName A Null-terminated string that is the name of the vendor's +// ...............................variable. +// @param[in] VendorGuid A unique identifier for the vendor. +// @param[out] Attributes If not NULL, a pointer to the memory location to return the +// ...............................attributes bitmask for the variable. +// @param[in, out] DataSize On input, the size in bytes of the return Data buffer. +// .................On output the size of data returned in Data. +// @param[out] Data The buffer to return the contents of the variable. May be NULL +// ...............................with a zero DataSize in order to determine the size buffer needed. +// @retval EFI_SUCCESS The function completed successfully. +// @retval EFI_NOT_FOUND The variable was not found. +// @retval EFI_BUFFER_TOO_SMALL The DataSize is too small for the result. +// @retval EFI_INVALID_PARAMETER VariableName is NULL. +// @retval EFI_INVALID_PARAMETER VendorGuid is NULL. +// @retval EFI_INVALID_PARAMETER DataSize is NULL. +// @retval EFI_INVALID_PARAMETER The DataSize is not too small and Data is NULL. +// @retval EFI_DEVICE_ERROR The variable could not be retrieved due to a hardware error. +// @retval EFI_SECURITY_VIOLATION The variable could not be retrieved due to an authentication failure. +func (p *EFI_RUNTIME_SERVICES) GetVariable(VariableName *CHAR16, VendorGuid *EFI_GUID, Attributes *uint32, DataSize *UINTN, Data *VOID) EFI_STATUS { + return UefiCall5(p.getVariable, uintptr(unsafe.Pointer(VariableName)), uintptr(unsafe.Pointer(VendorGuid)), uintptr(unsafe.Pointer(Attributes)), uintptr(unsafe.Pointer(DataSize)), uintptr(unsafe.Pointer(Data))) +} + +// GetNextVariableName +// Enumerates the current variable names. +// @param[in, out] VariableNameSize The size of the VariableName buffer. The size must be large +// .................enough to fit input string supplied in VariableName buffer. +// @param[in, out] VariableName On input, supplies the last VariableName that was returned +// .................by GetNextVariableName(). On output, returns the Nullterminated +// .................string of the current variable. +// @param[in, out] VendorGuid On input, supplies the last VendorGuid that was returned by +// .................GetNextVariableName(). On output, returns the +// .................VendorGuid of the current variable. +// @retval EFI_SUCCESS The function completed successfully. +// @retval EFI_NOT_FOUND The next variable was not found. +// @retval EFI_BUFFER_TOO_SMALL The VariableNameSize is too small for the result. +// ..............................VariableNameSize has been updated with the size needed to complete the request. +// @retval EFI_INVALID_PARAMETER VariableNameSize is NULL. +// @retval EFI_INVALID_PARAMETER VariableName is NULL. +// @retval EFI_INVALID_PARAMETER VendorGuid is NULL. +// @retval EFI_INVALID_PARAMETER The input values of VariableName and VendorGuid are not a name and +// ..............................GUID of an existing variable. +// @retval EFI_INVALID_PARAMETER Null-terminator is not found in the first VariableNameSize bytes of +// ..............................the input VariableName buffer. +// @retval EFI_DEVICE_ERROR The variable could not be retrieved due to a hardware error. +func (p *EFI_RUNTIME_SERVICES) GetNextVariableName(VariableNameSize *UINTN, VariableName *CHAR16, VendorGuid *EFI_GUID) EFI_STATUS { + return UefiCall3(p.getNextVariableName, uintptr(unsafe.Pointer(VariableNameSize)), uintptr(unsafe.Pointer(VariableName)), uintptr(unsafe.Pointer(VendorGuid))) +} + +// SetVariable +// Sets the value of a variable. +// @param[in] VariableName A Null-terminated string that is the name of the vendor's variable. +// ...............................Each VariableName is unique for each VendorGuid. VariableName must +// ...............................contain 1 or more characters. If VariableName is an empty string, +// ...............................then EFI_INVALID_PARAMETER is returned. +// @param[in] VendorGuid A unique identifier for the vendor. +// @param[in] Attributes Attributes bitmask to set for the variable. +// @param[in] DataSize The size in bytes of the Data buffer. Unless the EFI_VARIABLE_APPEND_WRITE or +// ...............................EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS attribute is set, a size of zero +// ...............................causes the variable to be deleted. When the EFI_VARIABLE_APPEND_WRITE attribute is +// ...............................set, then a SetVariable() call with a DataSize of zero will not cause any change to +// ...............................the variable value (the timestamp associated with the variable may be updated however +// ...............................even if no new data value is provided,see the description of the +// ...............................EFI_VARIABLE_AUTHENTICATION_2 descriptor below. In this case the DataSize will not +// ...............................be zero since the EFI_VARIABLE_AUTHENTICATION_2 descriptor will be populated). +// @param[in] Data The contents for the variable. +// @retval EFI_SUCCESS The firmware has successfully stored the variable and its data as +// ...............................defined by the Attributes. +// @retval EFI_INVALID_PARAMETER An invalid combination of attribute bits, name, and GUID was supplied, or the +// ...............................DataSize exceeds the maximum allowed. +// @retval EFI_INVALID_PARAMETER VariableName is an empty string. +// @retval EFI_OUT_OF_RESOURCES Not enough storage is available to hold the variable and its data. +// @retval EFI_DEVICE_ERROR The variable could not be retrieved due to a hardware error. +// @retval EFI_WRITE_PROTECTED The variable in question is read-only. +// @retval EFI_WRITE_PROTECTED The variable in question cannot be deleted. +// @retval EFI_SECURITY_VIOLATION The variable could not be written due to EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACESS being set, +// ...............................but the AuthInfo does NOT pass the validation check carried out by the firmware. +// @retval EFI_NOT_FOUND The variable trying to be updated or deleted was not found. +func (p *EFI_RUNTIME_SERVICES) SetVariable(VariableName *CHAR16, VendorGuid *EFI_GUID, Attributes uint32, DataSize UINTN, Data *VOID) EFI_STATUS { + return UefiCall5(p.setVariable, uintptr(unsafe.Pointer(VariableName)), uintptr(unsafe.Pointer(VendorGuid)), uintptr(Attributes), uintptr(DataSize), uintptr(unsafe.Pointer(Data))) +} + +// GetNextHighMonotonicCount +// Returns the next high 32 bits of the platform's monotonic counter. +// @param[out] HighCount The pointer to returned value. +// @retval EFI_SUCCESS The next high monotonic count was returned. +// @retval EFI_INVALID_PARAMETER HighCount is NULL. +// @retval EFI_DEVICE_ERROR The device is not functioning properly. +func (p *EFI_RUNTIME_SERVICES) GetNextHighMonotonicCount(HighCount *uint32) EFI_STATUS { + return UefiCall1(p.getNextHighMonotonicCount, uintptr(unsafe.Pointer(HighCount))) +} + +// ResetSystem +// Resets the entire platform. +// @param[in] ResetType The type of reset to perform. +// @param[in] ResetStatus The status code for the reset. +// @param[in] DataSize The size, in bytes, of ResetData. +// @param[in] ResetData For a ResetType of EfiResetCold, EfiResetWarm, or +// ..............................EfiResetShutdown the data buffer starts with a Null-terminated +// ..............................string, optionally followed by additional binary data. +// ..............................The string is a description that the caller may use to further +// ..............................indicate the reason for the system reset. +// ..............................For a ResetType of EfiResetPlatformSpecific the data buffer +// ..............................also starts with a Null-terminated string that is followed +// ..............................by an EFI_GUID that describes the specific type of reset to perform. +func (p *EFI_RUNTIME_SERVICES) ResetSystem(ResetType EFI_RESET_TYPE, ResetStatus EFI_STATUS, DataSize UINTN, ResetData *VOID) EFI_STATUS { + return UefiCall4(p.resetSystem, uintptr(ResetType), uintptr(ResetStatus), uintptr(DataSize), uintptr(unsafe.Pointer(ResetData))) +} + +// UpdateCapsule +// Passes capsules to the firmware with both virtual and physical mapping. Depending on the intended +// consumption, the firmware may process the capsule immediately. If the payload should persist +// across a system reset, the reset value returned from EFI_QueryCapsuleCapabilities must +// be passed into ResetSystem() and will cause the capsule to be processed by the firmware as +// part of the reset process. +// @param[in] CapsuleHeaderArray Virtual pointer to an array of virtual pointers to the capsules +// ...............................being passed into update capsule. +// @param[in] CapsuleCount Number of pointers to EFI_CAPSULE_HEADER in +// ...............................CaspuleHeaderArray. +// @param[in] ScatterGatherList Physical pointer to a set of +// ...............................EFI_CAPSULE_BLOCK_DESCRIPTOR that describes the +// ...............................location in physical memory of a set of capsules. +// @retval EFI_SUCCESS Valid capsule was passed. If +// ..............................CAPSULE_FLAGS_PERSIT_ACROSS_RESET is not set, the +// ..............................capsule has been successfully processed by the firmware. +// @retval EFI_INVALID_PARAMETER CapsuleSize is NULL, or an incompatible set of flags were +// ..............................set in the capsule header. +// @retval EFI_INVALID_PARAMETER CapsuleCount is 0. +// @retval EFI_DEVICE_ERROR The capsule update was started, but failed due to a device error. +// @retval EFI_UNSUPPORTED The capsule type is not supported on this platform. +// @retval EFI_OUT_OF_RESOURCES When ExitBootServices() has been previously called this error indicates the capsule +// ..............................is compatible with this platform but is not capable of being submitted or processed +// ..............................in runtime. The caller may resubmit the capsule prior to ExitBootServices(). +// @retval EFI_OUT_OF_RESOURCES When ExitBootServices() has not been previously called then this error indicates +// ..............................the capsule is compatible with this platform but there are insufficient resources to process. +func (p *EFI_RUNTIME_SERVICES) UpdateCapsule(CapsuleHeaderArray **EFI_CAPSULE_HEADER, CapsuleCount UINTN, ScatterGatherList EFI_PHYSICAL_ADDRESS) EFI_STATUS { + return UefiCall3(p.updateCapsule, uintptr(unsafe.Pointer(CapsuleHeaderArray)), uintptr(CapsuleCount), uintptr(ScatterGatherList)) +} + +// QueryCapsuleCapabilities +// Returns if the capsule can be supported via UpdateCapsule(). +// @param[in] CapsuleHeaderArray Virtual pointer to an array of virtual pointers to the capsules +// .................................being passed into update capsule. +// @param[in] CapsuleCount Number of pointers to EFI_CAPSULE_HEADER in +// .................................CaspuleHeaderArray. +// @param[out] MaxiumCapsuleSize On output the maximum size that UpdateCapsule() can +// .................................support as an argument to UpdateCapsule() via +// .................................CapsuleHeaderArray and ScatterGatherList. +// @param[out] ResetType Returns the type of reset required for the capsule update. +// @retval EFI_SUCCESS Valid answer returned. +// @retval EFI_UNSUPPORTED The capsule type is not supported on this platform, and +// ..............................MaximumCapsuleSize and ResetType are undefined. +// @retval EFI_INVALID_PARAMETER MaximumCapsuleSize is NULL. +// @retval EFI_OUT_OF_RESOURCES When ExitBootServices() has been previously called this error indicates the capsule +// ..............................is compatible with this platform but is not capable of being submitted or processed +// ..............................in runtime. The caller may resubmit the capsule prior to ExitBootServices(). +// @retval EFI_OUT_OF_RESOURCES When ExitBootServices() has not been previously called then this error indicates +// ..............................the capsule is compatible with this platform but there are insufficient resources to process. +func (p *EFI_RUNTIME_SERVICES) QueryCapsuleCapabilities(CapsuleHeaderArray **EFI_CAPSULE_HEADER, CapsuleCount UINTN, MaximumCapsuleSize *uint64, ResetType *EFI_RESET_TYPE) EFI_STATUS { + return UefiCall4(p.queryCapsuleCapabilities, uintptr(unsafe.Pointer(CapsuleHeaderArray)), uintptr(CapsuleCount), uintptr(unsafe.Pointer(MaximumCapsuleSize)), uintptr(unsafe.Pointer(ResetType))) +} + +// QueryVariableInfo +// Returns information about the EFI variables. +// @param[in] Attributes Attributes bitmask to specify the type of variables on +// ..........................................which to return information. +// @param[out] MaximumVariableStorageSize On output the maximum size of the storage space +// ..........................................available for the EFI variables associated with the +// ..........................................attributes specified. +// @param[out] RemainingVariableStorageSize Returns the remaining size of the storage space +// ..........................................available for the EFI variables associated with the +// ..........................................attributes specified. +// @param[out] MaximumVariableSize Returns the maximum size of the individual EFI +// ..........................................variables associated with the attributes specified. +// @retval EFI_SUCCESS Valid answer returned. +// @retval EFI_INVALID_PARAMETER An invalid combination of attribute bits was supplied +// @retval EFI_UNSUPPORTED The attribute is not supported on this platform, and the +// .....................................MaximumVariableStorageSize, +// .....................................RemainingVariableStorageSize, MaximumVariableSize +// .....................................are undefined. +func (p *EFI_RUNTIME_SERVICES) QueryVariableInfo(Attributes uint32, MaximumVariableStorageSize *uint64, RemainingVariableStorageSize *uint64, MaximumVariableSize *uint64) EFI_STATUS { + return UefiCall4(p.queryVariableInfo, uintptr(Attributes), uintptr(unsafe.Pointer(MaximumVariableStorageSize)), uintptr(unsafe.Pointer(RemainingVariableStorageSize)), uintptr(unsafe.Pointer(MaximumVariableSize))) +} + +// endregion + +// region: EFI Boot Services Table + +type EVENT_TYPE uint32 + +const ( + EVT_TIMER EVENT_TYPE = 0x80000000 + EVT_RUNTIME EVENT_TYPE = 0x40000000 + EVT_RUNTIME_CONTEXT EVENT_TYPE = 0x20000000 + + EVT_NOTIFY_WAIT EVENT_TYPE = 0x00000100 + EVT_NOTIFY_SIGNAL EVENT_TYPE = 0x00000200 + + EVT_SIGNAL_EXIT_BOOT_SERVICES EVENT_TYPE = 0x00000201 + EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE EVENT_TYPE = 0x60000202 + + EVT_EFI_SIGNAL_MASK EVENT_TYPE = 0x000000FF + EVT_EFI_SIGNAL_MAX EVENT_TYPE = 4 + + EFI_EVENT_TIMER = EVT_TIMER + EFI_EVENT_RUNTIME = EVT_RUNTIME + EFI_EVENT_RUNTIME_CONTEXT = EVT_RUNTIME_CONTEXT + EFI_EVENT_NOTIFY_WAIT = EVT_NOTIFY_WAIT + EFI_EVENT_NOTIFY_SIGNAL = EVT_NOTIFY_SIGNAL + EFI_EVENT_SIGNAL_EXIT_BOOT_SERVICES = EVT_SIGNAL_EXIT_BOOT_SERVICES + EFI_EVENT_SIGNAL_VIRTUAL_ADDRESS_CHANGE = EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE + EFI_EVENT_EFI_SIGNAL_MASK = EVT_EFI_SIGNAL_MASK + EFI_EVENT_EFI_SIGNAL_MAX = EVT_EFI_SIGNAL_MAX +) + +const ( + TPL_APPLICATION EFI_TPL = 4 + TPL_CALLBACK EFI_TPL = 8 + TPL_NOTIFY EFI_TPL = 16 + TPL_HIGH_LEVEL EFI_TPL = 31 + EFI_TPL_APPLICATION = TPL_APPLICATION + EFI_TPL_CALLBACK = TPL_CALLBACK + EFI_TPL_NOTIFY = TPL_NOTIFY + EFI_TPL_HIGH_LEVEL = TPL_HIGH_LEVEL +) + +// EFI_TIMER_DELAY is an enumeration of timer delays. +type EFI_TIMER_DELAY int + +const ( + TimerCancel EFI_TIMER_DELAY = iota + TimerPeriodic + TimerRelative + TimerTypeMax +) + +// Enumeration for EFI_INTERFACE_TYPE +type EFI_INTERFACE_TYPE uint + +const ( + EFI_NATIVE_INTERFACE EFI_INTERFACE_TYPE = iota + EFI_PCODE_INTERFACE +) + +// EFI_BOOT_SERVICES_SIGNATURE +const EFI_BOOT_SERVICES_SIGNATURE = 0x56524553544f4f42 + +// EFI_BOOT_SERVICES_REVISION +var ( + EFI_1_02_BOOT_SERVICES_REVISION = EFI_SPECIFICATION_REVISION_MAJORMINOR(1, 02) + EFI_1_10_BOOT_SERVICES_REVISION = EFI_SPECIFICATION_REVISION_MAJORMINOR(1, 10) + EFI_2_00_BOOT_SERVICES_REVISION = EFI_SPECIFICATION_REVISION_MAJORMINOR(2, 00) + EFI_2_10_BOOT_SERVICES_REVISION = EFI_SPECIFICATION_REVISION_MAJORMINOR(2, 10) + EFI_2_20_BOOT_SERVICES_REVISION = EFI_SPECIFICATION_REVISION_MAJORMINOR(2, 20) + EFI_2_30_BOOT_SERVICES_REVISION = EFI_SPECIFICATION_REVISION_MAJORMINOR(2, 30) + EFI_2_31_BOOT_SERVICES_REVISION = EFI_SPECIFICATION_REVISION_MAJORMINOR(2, 31) + EFI_2_40_BOOT_SERVICES_REVISION = EFI_SPECIFICATION_REVISION_MAJORMINOR(2, 40) + EFI_2_50_BOOT_SERVICES_REVISION = EFI_SPECIFICATION_REVISION_MAJORMINOR(2, 50) + EFI_2_60_BOOT_SERVICES_REVISION = EFI_SPECIFICATION_REVISION_MAJORMINOR(2, 60) + EFI_2_70_BOOT_SERVICES_REVISION = EFI_SPECIFICATION_REVISION_MAJORMINOR(2, 70) + EFI_2_80_BOOT_SERVICES_REVISION = EFI_SPECIFICATION_REVISION_MAJORMINOR(2, 80) + EFI_2_90_BOOT_SERVICES_REVISION = EFI_SPECIFICATION_REVISION_MAJORMINOR(2, 90) + EFI_2_100_BOOT_SERVICES_REVISION = EFI_SPECIFICATION_REVISION_MAJORMINOR(2, 100) + EFI_BOOT_SERVICES_REVISION = EFI_SPECIFICATION_VERSION +) + +// EFI_BOOT_SERVICES +// EFI Boot Services Table. +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 + reinstallProtocolInterface uintptr + uninstallProtocolInterface 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 + connectController uintptr + disconnectController uintptr + openProtocol uintptr + closeProtocol uintptr + openProtocolInformation uintptr + protocolsPerHandle uintptr + locateHandleBuffer uintptr + locateProtocol uintptr + installMultipleProtocolInterfaces uintptr + uninstallMultipleProtocolInterfaces uintptr + calculateCrc32 uintptr + copyMem uintptr + setMem uintptr + createEventEx uintptr +} + +// RaiseTPL +// Raises a task's priority level and returns its previous level. +// @param[in] NewTpl The new task priority level. +// @return Previous task priority level +func (p *EFI_BOOT_SERVICES) RaiseTPL(NewTpl EFI_TPL) EFI_STATUS { + return UefiCall1(p.raiseTPL, uintptr(NewTpl)) +} + +// RestoreTPL +// Restores a task's priority level to its previous value. +// @param[in] OldTpl The previous task priority level to restore. +func (p *EFI_BOOT_SERVICES) RestoreTPL(OldTpl EFI_TPL) EFI_STATUS { + return UefiCall1(p.restoreTPL, uintptr(OldTpl)) +} + +// AllocatePages +// Allocates memory pages from the system. +// @param[in] Type The type of allocation to perform. +// @param[in] MemoryType The type of memory to allocate. +// ..............................MemoryType values in the range 0x70000000..0x7FFFFFFF +// ..............................are reserved for OEM use. MemoryType values in the range +// ..............................0x80000000..0xFFFFFFFF are reserved for use by UEFI OS loaders +// ..............................that are provided by operating system vendors. +// @param[in] Pages The number of contiguous 4 KB pages to allocate. +// @param[in, out] Memory The pointer to a physical address. On input, the way in which the address is +// .................used depends on the value of Type. +// @retval EFI_SUCCESS The requested pages were allocated. +// @retval EFI_INVALID_PARAMETER 1) Type is not AllocateAnyPages or +// ..............................AllocateMaxAddress or AllocateAddress. +// ..............................2) MemoryType is in the range +// ..............................EfiMaxMemoryType..0x6FFFFFFF. +// ..............................3) Memory is NULL. +// ..............................4) MemoryType is EfiPersistentMemory. +// @retval EFI_OUT_OF_RESOURCES The pages could not be allocated. +// @retval EFI_NOT_FOUND The requested pages could not be found. +func (p *EFI_BOOT_SERVICES) AllocatePages(Type EFI_ALLOCATE_TYPE, MemoryType EFI_MEMORY_TYPE, Pages UINTN, Memory *EFI_PHYSICAL_ADDRESS) EFI_STATUS { + return UefiCall4(p.allocatePages, uintptr(Type), uintptr(MemoryType), uintptr(Pages), uintptr(unsafe.Pointer(Memory))) +} + +// FreePages +// Frees memory pages. +// @param[in] Memory The base physical address of the pages to be freed. +// @param[in] Pages The number of contiguous 4 KB pages to free. +// @retval EFI_SUCCESS The requested pages were freed. +// @retval EFI_INVALID_PARAMETER Memory is not a page-aligned address or Pages is invalid. +// @retval EFI_NOT_FOUND The requested memory pages were not allocated with +// ..............................AllocatePages(). +func (p *EFI_BOOT_SERVICES) FreePages(Memory EFI_PHYSICAL_ADDRESS, Pages UINTN) EFI_STATUS { + return UefiCall2(p.freePages, uintptr(Memory), uintptr(Pages)) +} + +// GetMemoryMap +// Returns the current memory map. +// @param[in, out] MemoryMapSize A pointer to the size, in bytes, of the MemoryMap buffer. +// .................On input, this is the size of the buffer allocated by the caller. +// .................On output, it is the size of the buffer returned by the firmware if +// .................the buffer was large enough, or the size of the buffer needed to contain +// .................the map if the buffer was too small. +// @param[out] MemoryMap A pointer to the buffer in which firmware places the current memory +// .......................................map. +// @param[out] MapKey A pointer to the location in which firmware returns the key for the +// .......................................current memory map. +// @param[out] DescriptorSize A pointer to the location in which firmware returns the size, in bytes, of +// .......................................an individual EFI_MEMORY_DESCRIPTOR. +// @param[out] DescriptorVersion A pointer to the location in which firmware returns the version number +// .......................................associated with the EFI_MEMORY_DESCRIPTOR. +// @retval EFI_SUCCESS The memory map was returned in the MemoryMap buffer. +// @retval EFI_BUFFER_TOO_SMALL The MemoryMap buffer was too small. The current buffer size +// ..............................needed to hold the memory map is returned in MemoryMapSize. +// @retval EFI_INVALID_PARAMETER 1) MemoryMapSize is NULL. +// ..............................2) The MemoryMap buffer is not too small and MemoryMap is +// ..............................NULL. +func (p *EFI_BOOT_SERVICES) GetMemoryMap(MemoryMapSize *UINTN, MemoryMap *EFI_MEMORY_DESCRIPTOR, MapKey *UINTN, DescriptorSize *UINTN, DescriptorVersion *uint32) EFI_STATUS { + return UefiCall5(p.getMemoryMap, uintptr(unsafe.Pointer(MemoryMapSize)), uintptr(unsafe.Pointer(MemoryMap)), uintptr(unsafe.Pointer(MapKey)), uintptr(unsafe.Pointer(DescriptorSize)), uintptr(unsafe.Pointer(DescriptorVersion))) +} + +// AllocatePool +// Allocates pool memory. +// @param[in] PoolType The type of pool to allocate. +// ..............................MemoryType values in the range 0x70000000..0x7FFFFFFF +// ..............................are reserved for OEM use. MemoryType values in the range +// ..............................0x80000000..0xFFFFFFFF are reserved for use by UEFI OS loaders +// ..............................that are provided by operating system vendors. +// @param[in] Size The number of bytes to allocate from the pool. +// @param[out] Buffer A pointer to a pointer to the allocated buffer if the call succeeds; +// ..............................undefined otherwise. +// @retval EFI_SUCCESS The requested number of bytes was allocated. +// @retval EFI_OUT_OF_RESOURCES The pool requested could not be allocated. +// @retval EFI_INVALID_PARAMETER Buffer is NULL. +// ..............................PoolType is in the range EfiMaxMemoryType..0x6FFFFFFF. +// ..............................PoolType is EfiPersistentMemory. +func (p *EFI_BOOT_SERVICES) AllocatePool(PoolType EFI_MEMORY_TYPE, Size UINTN, Buffer **VOID) EFI_STATUS { + return UefiCall3(p.allocatePool, uintptr(PoolType), uintptr(Size), uintptr(unsafe.Pointer(Buffer))) +} + +// FreePool +// Returns pool memory to the system. +// @param[in] Buffer The pointer to the buffer to free. +// @retval EFI_SUCCESS The memory was returned to the system. +// @retval EFI_INVALID_PARAMETER Buffer was invalid. +func (p *EFI_BOOT_SERVICES) FreePool(Buffer *VOID) EFI_STATUS { + return UefiCall1(p.freePool, uintptr(unsafe.Pointer(Buffer))) +} + +// CreateEvent +// Creates an event. +// @param[in] Type The type of event to create and its mode and attributes. +// @param[in] NotifyTpl The task priority level of event notifications, if needed. +// @param[in] NotifyFunction The pointer to the event's notification function, if any. +// @param[in] NotifyContext The pointer to the notification function's context; corresponds to parameter +// ..............................Context in the notification function. +// @param[out] Event The pointer to the newly created event if the call succeeds; undefined +// ..............................otherwise. +// @retval EFI_SUCCESS The event structure was created. +// @retval EFI_INVALID_PARAMETER One or more parameters are invalid. +// @retval EFI_OUT_OF_RESOURCES The event could not be allocated. +func (p *EFI_BOOT_SERVICES) CreateEvent(Type EVENT_TYPE, NotifyTpl EFI_TPL, NotifyFunction unsafe.Pointer, NotifyContext unsafe.Pointer, Event *EFI_EVENT) EFI_STATUS { + return UefiCall5(p.createEvent, uintptr(Type), uintptr(NotifyTpl), uintptr(NotifyFunction), uintptr(NotifyContext), uintptr(unsafe.Pointer(Event))) +} + +// SetTimer +// Sets the type of timer and the trigger time for a timer event. +// @param[in] Event The timer event that is to be signaled at the specified time. +// @param[in] Type The type of time that is specified in TriggerTime. +// @param[in] TriggerTime The number of 100ns units until the timer expires. +// ..............................A TriggerTime of 0 is legal. +// ..............................If Type is TimerRelative and TriggerTime is 0, then the timer +// ..............................event will be signaled on the next timer tick. +// ..............................If Type is TimerPeriodic and TriggerTime is 0, then the timer +// ..............................event will be signaled on every timer tick. +// @retval EFI_SUCCESS The event has been set to be signaled at the requested time. +// @retval EFI_INVALID_PARAMETER Event or Type is not valid. +func (p *EFI_BOOT_SERVICES) SetTimer(Event EFI_EVENT, Type EFI_TIMER_DELAY, TriggerTime uint64) EFI_STATUS { + return UefiCall3(p.setTimer, uintptr(Event), uintptr(Type), uintptr(TriggerTime)) +} + +// WaitForEvent +// Stops execution until an event is signaled. +// @param[in] NumberOfEvents The number of events in the Event array. +// @param[in] Event An array of EFI_EVENT. +// @param[out] Index The pointer to the index of the event which satisfied the wait condition. +// @retval EFI_SUCCESS The event indicated by Index was signaled. +// @retval EFI_INVALID_PARAMETER 1) NumberOfEvents is 0. +// ..............................2) The event indicated by Index is of type +// ..............................EVT_NOTIFY_SIGNAL. +// @retval EFI_UNSUPPORTED The current TPL is not TPL_APPLICATION. +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))) +} + +// SignalEvent +// Signals an event. +// @param[in] Event The event to signal. +// @retval EFI_SUCCESS The event has been signaled. +func (p *EFI_BOOT_SERVICES) SignalEvent(Event EFI_EVENT) EFI_STATUS { + return UefiCall1(p.signalEvent, uintptr(Event)) +} + +// CloseEvent +// Closes an event. +// @param[in] Event The event to close. +// @retval EFI_SUCCESS The event has been closed. +func (p *EFI_BOOT_SERVICES) CloseEvent(Event EFI_EVENT) EFI_STATUS { + return UefiCall1(p.closeEvent, uintptr(Event)) +} + +// CheckEvent +// Checks whether an event is in the signaled state. +// @param[in] Event The event to check. +// @retval EFI_SUCCESS The event is in the signaled state. +// @retval EFI_NOT_READY The event is not in the signaled state. +// @retval EFI_INVALID_PARAMETER Event is of type EVT_NOTIFY_SIGNAL. +func (p *EFI_BOOT_SERVICES) CheckEvent(Event EFI_EVENT) EFI_STATUS { + return UefiCall1(p.checkEvent, uintptr(Event)) +} + +// InstallProtocolInterface +// Installs a protocol interface on a device handle. If the handle does not exist, it is created and added +// to the list of handles in the system. InstallMultipleProtocolInterfaces() performs +// more error checking than InstallProtocolInterface(), so it is recommended that +// InstallMultipleProtocolInterfaces() be used in place of +// InstallProtocolInterface() +// @param[in, out] Handle A pointer to the EFI_HANDLE on which the interface is to be installed. +// @param[in] Protocol The numeric ID of the protocol interface. +// @param[in] InterfaceType Indicates whether Interface is supplied in native form. +// @param[in] Interface A pointer to the protocol interface. +// @retval EFI_SUCCESS The protocol interface was installed. +// @retval EFI_OUT_OF_RESOURCES Space for a new handle could not be allocated. +// @retval EFI_INVALID_PARAMETER Handle is NULL. +// @retval EFI_INVALID_PARAMETER Protocol is NULL. +// @retval EFI_INVALID_PARAMETER InterfaceType is not EFI_NATIVE_INTERFACE. +// @retval EFI_INVALID_PARAMETER Protocol is already installed on the handle specified by Handle. +func (p *EFI_BOOT_SERVICES) InstallProtocolInterface(Handle *EFI_HANDLE, Protocol *EFI_GUID, InterfaceType EFI_INTERFACE_TYPE, Interface *VOID) EFI_STATUS { + return UefiCall4(p.installProtocolInterface, uintptr(unsafe.Pointer(Handle)), uintptr(unsafe.Pointer(Protocol)), uintptr(InterfaceType), uintptr(unsafe.Pointer(Interface))) +} + +// ReinstallProtocolInterface +// Reinstalls a protocol interface on a device handle. +// @param[in] Handle Handle on which the interface is to be reinstalled. +// @param[in] Protocol The numeric ID of the interface. +// @param[in] OldInterface A pointer to the old interface. NULL can be used if a structure is not +// ..............................associated with Protocol. +// @param[in] NewInterface A pointer to the new interface. +// @retval EFI_SUCCESS The protocol interface was reinstalled. +// @retval EFI_NOT_FOUND The OldInterface on the handle was not found. +// @retval EFI_ACCESS_DENIED The protocol interface could not be reinstalled, +// ..............................because OldInterface is still being used by a +// ..............................driver that will not release it. +// @retval EFI_INVALID_PARAMETER Handle is NULL. +// @retval EFI_INVALID_PARAMETER Protocol is NULL. +func (p *EFI_BOOT_SERVICES) ReinstallProtocolInterface(Handle EFI_HANDLE, Protocol *EFI_GUID, OldInterface *VOID, NewInterface *VOID) EFI_STATUS { + return UefiCall4(p.reinstallProtocolInterface, uintptr(Handle), uintptr(unsafe.Pointer(Protocol)), uintptr(unsafe.Pointer(OldInterface)), uintptr(unsafe.Pointer(NewInterface))) +} + +// UninstallProtocolInterface +// Removes a protocol interface from a device handle. It is recommended that +// UninstallMultipleProtocolInterfaces() be used in place of +// UninstallProtocolInterface(). +// @param[in] Handle The handle on which the interface was installed. +// @param[in] Protocol The numeric ID of the interface. +// @param[in] Interface A pointer to the interface. +// @retval EFI_SUCCESS The interface was removed. +// @retval EFI_NOT_FOUND The interface was not found. +// @retval EFI_ACCESS_DENIED The interface was not removed because the interface +// ..............................is still being used by a driver. +// @retval EFI_INVALID_PARAMETER Handle is NULL. +// @retval EFI_INVALID_PARAMETER Protocol is NULL. +func (p *EFI_BOOT_SERVICES) UninstallProtocolInterface(Handle EFI_HANDLE, Protocol *EFI_GUID, Interface *VOID) EFI_STATUS { + return UefiCall3(p.uninstallProtocolInterface, uintptr(Handle), uintptr(unsafe.Pointer(Protocol)), uintptr(unsafe.Pointer(Interface))) +} + +// HandleProtocol +// Queries a handle to determine if it supports a specified protocol. +// @param[in] Handle The handle being queried. +// @param[in] Protocol The published unique identifier of the protocol. +// @param[out] Interface Supplies the address where a pointer to the corresponding Protocol +// ..............................Interface is returned. +// @retval EFI_SUCCESS The interface information for the specified protocol was returned. +// @retval EFI_UNSUPPORTED The device does not support the specified protocol. +// @retval EFI_INVALID_PARAMETER Handle is NULL. +// @retval EFI_INVALID_PARAMETER Protocol is NULL. +// @retval EFI_INVALID_PARAMETER Interface is NULL. +func (p *EFI_BOOT_SERVICES) HandleProtocol(Handle EFI_HANDLE, Protocol *EFI_GUID, Interface unsafe.Pointer) EFI_STATUS { + return UefiCall3(p.handleProtocol, uintptr(Handle), uintptr(unsafe.Pointer(Protocol)), uintptr(Interface)) +} + +// RegisterProtocolNotify +// Creates an event that is to be signaled whenever an interface is installed for a specified protocol. +// @param[in] Protocol The numeric ID of the protocol for which the event is to be registered. +// @param[in] Event Event that is to be signaled whenever a protocol interface is registered +// ..............................for Protocol. +// @param[out] Registration A pointer to a memory location to receive the registration value. +// @retval EFI_SUCCESS The notification event has been registered. +// @retval EFI_OUT_OF_RESOURCES Space for the notification event could not be allocated. +// @retval EFI_INVALID_PARAMETER Protocol is NULL. +// @retval EFI_INVALID_PARAMETER Event is NULL. +// @retval EFI_INVALID_PARAMETER Registration is NULL. +func (p *EFI_BOOT_SERVICES) RegisterProtocolNotify(Protocol *EFI_GUID, Event EFI_EVENT, Registration **VOID) EFI_STATUS { + return UefiCall3(p.registerProtocolNotify, uintptr(unsafe.Pointer(Protocol)), uintptr(Event), uintptr(unsafe.Pointer(Registration))) +} + +// LocateHandle +// Returns an array of handles that support a specified protocol. +// @param[in] SearchType Specifies which handle(s) are to be returned. +// @param[in] Protocol Specifies the protocol to search by. +// @param[in] SearchKey Specifies the search key. +// @param[in, out] BufferSize On input, the size in bytes of Buffer. On output, the size in bytes of +// .................the array returned in Buffer (if the buffer was large enough) or the +// .................size, in bytes, of the buffer needed to obtain the array (if the buffer was +// .................not large enough). +// @param[out] Buffer The buffer in which the array is returned. +// @retval EFI_SUCCESS The array of handles was returned. +// @retval EFI_NOT_FOUND No handles match the search. +// @retval EFI_BUFFER_TOO_SMALL The BufferSize is too small for the result. +// @retval EFI_INVALID_PARAMETER SearchType is not a member of EFI_LOCATE_SEARCH_TYPE. +// @retval EFI_INVALID_PARAMETER SearchType is ByRegisterNotify and SearchKey is NULL. +// @retval EFI_INVALID_PARAMETER SearchType is ByProtocol and Protocol is NULL. +// @retval EFI_INVALID_PARAMETER One or more matches are found and BufferSize is NULL. +// @retval EFI_INVALID_PARAMETER BufferSize is large enough for the result and Buffer is NULL. +func (p *EFI_BOOT_SERVICES) LocateHandle(SearchType EFI_LOCATE_SEARCH_TYPE, Protocol *EFI_GUID, SearchKey *VOID, BufferSize *UINTN, Buffer *EFI_HANDLE) EFI_STATUS { + return UefiCall5(p.locateHandle, uintptr(SearchType), uintptr(unsafe.Pointer(Protocol)), uintptr(unsafe.Pointer(SearchKey)), uintptr(unsafe.Pointer(BufferSize)), uintptr(unsafe.Pointer(Buffer))) +} + +// LocateDevicePath +// Locates the handle to a device on the device path that supports the specified protocol. +// @param[in] Protocol Specifies the protocol to search for. +// @param[in, out] DevicePath On input, a pointer to a pointer to the device path. On output, the device +// .................path pointer is modified to point to the remaining part of the device +// .................path. +// @param[out] Device A pointer to the returned device handle. +// @retval EFI_SUCCESS The resulting handle was returned. +// @retval EFI_NOT_FOUND No handles match the search. +// @retval EFI_INVALID_PARAMETER Protocol is NULL. +// @retval EFI_INVALID_PARAMETER DevicePath is NULL. +// @retval EFI_INVALID_PARAMETER A handle matched the search and Device is NULL. +func (p *EFI_BOOT_SERVICES) LocateDevicePath(Protocol *EFI_GUID, DevicePath **EFI_DEVICE_PATH_PROTOCOL, Device *EFI_HANDLE) EFI_STATUS { + return UefiCall3(p.locateDevicePath, uintptr(unsafe.Pointer(Protocol)), uintptr(unsafe.Pointer(DevicePath)), uintptr(unsafe.Pointer(Device))) +} + +// InstallConfigurationTable +// Adds, updates, or removes a configuration table entry from the EFI System Table. +// @param[in] Guid A pointer to the GUID for the entry to add, update, or remove. +// @param[in] Table A pointer to the configuration table for the entry to add, update, or +// ..............................remove. May be NULL. +// @retval EFI_SUCCESS The (Guid, Table) pair was added, updated, or removed. +// @retval EFI_NOT_FOUND An attempt was made to delete a nonexistent entry. +// @retval EFI_INVALID_PARAMETER Guid is NULL. +// @retval EFI_OUT_OF_RESOURCES There is not enough memory available to complete the operation. +func (p *EFI_BOOT_SERVICES) InstallConfigurationTable(Guid *EFI_GUID, Table *VOID) EFI_STATUS { + return UefiCall2(p.installConfigurationTable, uintptr(unsafe.Pointer(Guid)), uintptr(unsafe.Pointer(Table))) +} + +// LoadImage +// Loads an EFI image into memory. +// @param[in] BootPolicy If TRUE, indicates that the request originates from the boot +// ...............................manager, and that the boot manager is attempting to load +// ...............................FilePath as a boot selection. Ignored if SourceBuffer is +// ...............................not NULL. +// @param[in] ParentImageHandle The caller's image handle. +// @param[in] DevicePath The DeviceHandle specific file path from which the image is +// ...............................loaded. +// @param[in] SourceBuffer If not NULL, a pointer to the memory location containing a copy +// ...............................of the image to be loaded. +// @param[in] SourceSize The size in bytes of SourceBuffer. Ignored if SourceBuffer is NULL. +// @param[out] ImageHandle The pointer to the returned image handle that is created when the +// ...............................image is successfully loaded. +// @retval EFI_SUCCESS Image was loaded into memory correctly. +// @retval EFI_NOT_FOUND Both SourceBuffer and DevicePath are NULL. +// @retval EFI_INVALID_PARAMETER One or more parametes are invalid. +// @retval EFI_UNSUPPORTED The image type is not supported. +// @retval EFI_OUT_OF_RESOURCES Image was not loaded due to insufficient resources. +// @retval EFI_LOAD_ERROR Image was not loaded because the image format was corrupt or not +// ...............................understood. +// @retval EFI_DEVICE_ERROR Image was not loaded because the device returned a read error. +// @retval EFI_ACCESS_DENIED Image was not loaded because the platform policy prohibits the +// ...............................image from being loaded. NULL is returned in *ImageHandle. +// @retval EFI_SECURITY_VIOLATION Image was loaded and an ImageHandle was created with a +// ...............................valid EFI_LOADED_IMAGE_PROTOCOL. However, the current +// ...............................platform policy specifies that the image should not be started. +func (p *EFI_BOOT_SERVICES) LoadImage(BootPolicy BOOLEAN, ParentImageHandle EFI_HANDLE, DevicePath *EFI_DEVICE_PATH_PROTOCOL, SourceBuffer *VOID, SourceSize UINTN, ImageHandle *EFI_HANDLE) EFI_STATUS { + return UefiCall6(p.loadImage, convertBoolean(BootPolicy), uintptr(ParentImageHandle), uintptr(unsafe.Pointer(DevicePath)), uintptr(unsafe.Pointer(SourceBuffer)), uintptr(SourceSize), uintptr(unsafe.Pointer(ImageHandle))) +} + +// StartImage +// Transfers control to a loaded image's entry point. +// @param[in] ImageHandle Handle of image to be started. +// @param[out] ExitDataSize The pointer to the size, in bytes, of ExitData. +// @param[out] ExitData The pointer to a pointer to a data buffer that includes a Null-terminated +// ...............................string, optionally followed by additional binary data. +// @retval EFI_INVALID_PARAMETER ImageHandle is either an invalid image handle or the image +// ...............................has already been initialized with StartImage. +// @retval EFI_SECURITY_VIOLATION The current platform policy specifies that the image should not be started. +// @return Exit code from image +func (p *EFI_BOOT_SERVICES) StartImage(ImageHandle EFI_HANDLE, ExitDataSize *UINTN, ExitData **CHAR16) EFI_STATUS { + return UefiCall3(p.startImage, uintptr(ImageHandle), uintptr(unsafe.Pointer(ExitDataSize)), uintptr(unsafe.Pointer(ExitData))) +} + +// Exit +// Terminates a loaded EFI image and returns control to boot services. +// @param[in] ImageHandle Handle that identifies the image. This parameter is passed to the +// ..............................image on entry. +// @param[in] ExitStatus The image's exit code. +// @param[in] ExitDataSize The size, in bytes, of ExitData. Ignored if ExitStatus is EFI_SUCCESS. +// @param[in] ExitData The pointer to a data buffer that includes a Null-terminated string, +// ..............................optionally followed by additional binary data. The string is a +// ..............................description that the caller may use to further indicate the reason +// ..............................for the image's exit. ExitData is only valid if ExitStatus +// ..............................is something other than EFI_SUCCESS. The ExitData buffer +// ..............................must be allocated by calling AllocatePool(). +// @retval EFI_SUCCESS The image specified by ImageHandle was unloaded. +// @retval EFI_INVALID_PARAMETER The image specified by ImageHandle has been loaded and +// ..............................started with LoadImage() and StartImage(), but the +// ..............................image is not the currently executing image. +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))) +} + +// UnloadImage +// Unloads an image. +// @param[in] ImageHandle Handle that identifies the image to be unloaded. +// @retval EFI_SUCCESS The image has been unloaded. +// @retval EFI_INVALID_PARAMETER ImageHandle is not a valid image handle. +func (p *EFI_BOOT_SERVICES) UnloadImage(ImageHandle EFI_HANDLE) EFI_STATUS { + return UefiCall1(p.unloadImage, uintptr(ImageHandle)) +} + +// ExitBootServices +// Terminates all boot services. +// @param[in] ImageHandle Handle that identifies the exiting image. +// @param[in] MapKey Key to the latest memory map. +// @retval EFI_SUCCESS Boot services have been terminated. +// @retval EFI_INVALID_PARAMETER MapKey is incorrect. +func (p *EFI_BOOT_SERVICES) ExitBootServices(ImageHandle EFI_HANDLE, MapKey UINTN) EFI_STATUS { + return UefiCall2(p.exitBootServices, uintptr(ImageHandle), uintptr(MapKey)) +} + +// GetNextMonotonicCount +// Returns a monotonically increasing count for the platform. +// @param[out] Count The pointer to returned value. +// @retval EFI_SUCCESS The next monotonic count was returned. +// @retval EFI_INVALID_PARAMETER Count is NULL. +// @retval EFI_DEVICE_ERROR The device is not functioning properly. +func (p *EFI_BOOT_SERVICES) GetNextMonotonicCount(Count *uint64) EFI_STATUS { + return UefiCall1(p.getNextMonotonicCount, uintptr(unsafe.Pointer(Count))) +} + +// Stall +// Induces a fine-grained stall. +// @param[in] Microseconds The number of microseconds to stall execution. +// @retval EFI_SUCCESS Execution was stalled at least the requested number of +// ..............................Microseconds. +func (p *EFI_BOOT_SERVICES) Stall(Microseconds UINTN) EFI_STATUS { + return UefiCall1(p.stall, uintptr(Microseconds)) +} + +// SetWatchdogTimer +// Sets the system's watchdog timer. +// @param[in] Timeout The number of seconds to set the watchdog timer to. +// @param[in] WatchdogCode The numeric code to log on a watchdog timer timeout event. +// @param[in] DataSize The size, in bytes, of WatchdogData. +// @param[in] WatchdogData A data buffer that includes a Null-terminated string, optionally +// ..............................followed by additional binary data. +// @retval EFI_SUCCESS The timeout has been set. +// @retval EFI_INVALID_PARAMETER The supplied WatchdogCode is invalid. +// @retval EFI_UNSUPPORTED The system does not have a watchdog timer. +// @retval EFI_DEVICE_ERROR The watchdog timer could not be programmed due to a hardware +// ..............................error. +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))) +} + +// ConnectController +// Connects one or more drivers to a controller. +// @param[in] ControllerHandle The handle of the controller to which driver(s) are to be connected. +// @param[in] DriverImageHandle A pointer to an ordered list handles that support the +// ..................................EFI_DRIVER_BINDING_PROTOCOL. +// @param[in] RemainingDevicePath A pointer to the device path that specifies a child of the +// ..................................controller specified by ControllerHandle. +// @param[in] Recursive If TRUE, then ConnectController() is called recursively +// ..................................until the entire tree of controllers below the controller specified +// ..................................by ControllerHandle have been created. If FALSE, then +// ..................................the tree of controllers is only expanded one level. +// @retval EFI_SUCCESS 1) One or more drivers were connected to ControllerHandle. +// ..............................2) No drivers were connected to ControllerHandle, but +// ..............................RemainingDevicePath is not NULL, and it is an End Device +// ..............................Path Node. +// @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. +// @retval EFI_NOT_FOUND 1) There are no EFI_DRIVER_BINDING_PROTOCOL instances +// ..............................present in the system. +// ..............................2) No drivers were connected to ControllerHandle. +// ..............................@retval EFI_SECURITY_VIOLATION +// ..............................The user has no permission to start UEFI device drivers on the device path +// ..............................associated with the ControllerHandle or specified by the RemainingDevicePath. +func (p *EFI_BOOT_SERVICES) ConnectController(ControllerHandle EFI_HANDLE, DriverImageHandle *EFI_HANDLE, RemainingDevicePath *EFI_DEVICE_PATH_PROTOCOL, Recursive BOOLEAN) EFI_STATUS { + return UefiCall4(p.connectController, uintptr(ControllerHandle), uintptr(unsafe.Pointer(DriverImageHandle)), uintptr(unsafe.Pointer(RemainingDevicePath)), convertBoolean(Recursive)) +} + +// DisconnectController +// Disconnects one or more drivers from a controller. +// @param[in] ControllerHandle The handle of the controller from which driver(s) are to be disconnected. +// @param[in] DriverImageHandle The driver to disconnect from ControllerHandle. +// ..................................If DriverImageHandle is NULL, then all the drivers currently managing +// ..................................ControllerHandle are disconnected from ControllerHandle. +// @param[in] ChildHandle The handle of the child to destroy. +// ..................................If ChildHandle is NULL, then all the children of ControllerHandle are +// ..................................destroyed before the drivers are disconnected from ControllerHandle. +// @retval EFI_SUCCESS 1) One or more drivers were disconnected from the controller. +// ..............................2) On entry, no drivers are managing ControllerHandle. +// ..............................3) DriverImageHandle is not NULL, and on entry +// ..............................DriverImageHandle is not managing ControllerHandle. +// @retval EFI_INVALID_PARAMETER 1) ControllerHandle is NULL. +// ..............................2) DriverImageHandle is not NULL, and it is not a valid EFI_HANDLE. +// ..............................3) ChildHandle is not NULL, and it is not a valid EFI_HANDLE. +// ..............................4) DriverImageHandle does not support the EFI_DRIVER_BINDING_PROTOCOL. +// @retval EFI_OUT_OF_RESOURCES There are not enough resources available to disconnect any drivers from +// ..............................ControllerHandle. +// @retval EFI_DEVICE_ERROR The controller could not be disconnected because of a device error. +func (p *EFI_BOOT_SERVICES) DisconnectController(ControllerHandle EFI_HANDLE, DriverImageHandle EFI_HANDLE, ChildHandle EFI_HANDLE) EFI_STATUS { + return UefiCall3(p.disconnectController, uintptr(ControllerHandle), uintptr(DriverImageHandle), uintptr(ChildHandle)) +} + +// OpenProtocol +// Queries a handle to determine if it supports a specified protocol. If the protocol is supported by the +// handle, it opens the protocol on behalf of the calling agent. +// @param[in] Handle The handle for the protocol interface that is being opened. +// @param[in] Protocol The published unique identifier of the protocol. +// @param[out] Interface Supplies the address where a pointer to the corresponding Protocol +// ..............................Interface is returned. +// @param[in] AgentHandle The handle of the agent that is opening the protocol interface +// ..............................specified by Protocol and Interface. +// @param[in] ControllerHandle If the agent that is opening a protocol is a driver that follows the +// ..............................UEFI Driver Model, then this parameter is the controller handle +// ..............................that requires the protocol interface. If the agent does not follow +// ..............................the UEFI Driver Model, then this parameter is optional and may +// ..............................be NULL. +// @param[in] Attributes The open mode of the protocol interface specified by Handle +// ..............................and Protocol. +// @retval EFI_SUCCESS An item was added to the open list for the protocol interface, and the +// ..............................protocol interface was returned in Interface. +// @retval EFI_UNSUPPORTED Handle does not support Protocol. +// @retval EFI_INVALID_PARAMETER One or more parameters are invalid. +// @retval EFI_ACCESS_DENIED Required attributes can't be supported in current environment. +// @retval EFI_ALREADY_STARTED Item on the open list already has requierd attributes whose agent +// ..............................handle is the same as AgentHandle. +func (p *EFI_BOOT_SERVICES) OpenProtocol(Handle EFI_HANDLE, Protocol *EFI_GUID, Interface **VOID, AgentHandle EFI_HANDLE, ControllerHandle EFI_HANDLE, Attributes uint32) EFI_STATUS { + return UefiCall6(p.openProtocol, uintptr(Handle), uintptr(unsafe.Pointer(Protocol)), uintptr(unsafe.Pointer(Interface)), uintptr(AgentHandle), uintptr(ControllerHandle), uintptr(Attributes)) +} + +// CloseProtocol +// Closes a protocol on a handle that was opened using OpenProtocol(). +// @param[in] Handle The handle for the protocol interface that was previously opened +// ..............................with OpenProtocol(), and is now being closed. +// @param[in] Protocol The published unique identifier of the protocol. +// @param[in] AgentHandle The handle of the agent that is closing the protocol interface. +// @param[in] ControllerHandle If the agent that opened a protocol is a driver that follows the +// ..............................UEFI Driver Model, then this parameter is the controller handle +// ..............................that required the protocol interface. +// @retval EFI_SUCCESS The protocol instance was closed. +// @retval EFI_INVALID_PARAMETER 1) Handle is NULL. +// ..............................2) AgentHandle is NULL. +// ..............................3) ControllerHandle is not NULL and ControllerHandle is not a valid EFI_HANDLE. +// ..............................4) Protocol is NULL. +// @retval EFI_NOT_FOUND 1) Handle does not support the protocol specified by Protocol. +// ..............................2) The protocol interface specified by Handle and Protocol is not +// ..............................currently open by AgentHandle and ControllerHandle. +func (p *EFI_BOOT_SERVICES) CloseProtocol(Handle EFI_HANDLE, Protocol *EFI_GUID, AgentHandle EFI_HANDLE, ControllerHandle EFI_HANDLE) EFI_STATUS { + return UefiCall4(p.closeProtocol, uintptr(Handle), uintptr(unsafe.Pointer(Protocol)), uintptr(AgentHandle), uintptr(ControllerHandle)) +} + +// OpenProtocolInformation +// Retrieves the list of agents that currently have a protocol interface opened. +// @param[in] Handle The handle for the protocol interface that is being queried. +// @param[in] Protocol The published unique identifier of the protocol. +// @param[out] EntryBuffer A pointer to a buffer of open protocol information in the form of +// ..............................EFI_OPEN_PROTOCOL_INFORMATION_ENTRY structures. +// @param[out] EntryCount A pointer to the number of entries in EntryBuffer. +// @retval EFI_SUCCESS The open protocol information was returned in EntryBuffer, and the +// ..............................number of entries was returned EntryCount. +// @retval EFI_OUT_OF_RESOURCES There are not enough resources available to allocate EntryBuffer. +// @retval EFI_NOT_FOUND Handle does not support the protocol specified by Protocol. +func (p *EFI_BOOT_SERVICES) OpenProtocolInformation(Handle EFI_HANDLE, Protocol *EFI_GUID, EntryBuffer **EFI_OPEN_PROTOCOL_INFORMATION_ENTRY, EntryCount *UINTN) EFI_STATUS { + return UefiCall4(p.openProtocolInformation, uintptr(Handle), uintptr(unsafe.Pointer(Protocol)), uintptr(unsafe.Pointer(EntryBuffer)), uintptr(unsafe.Pointer(EntryCount))) +} + +// ProtocolsPerHandle +// Retrieves the list of protocol interface GUIDs that are installed on a handle in a buffer allocated +// from pool. +// @param[in] Handle The handle from which to retrieve the list of protocol interface +// .................................GUIDs. +// @param[out] ProtocolBuffer A pointer to the list of protocol interface GUID pointers that are +// .................................installed on Handle. +// @param[out] ProtocolBufferCount A pointer to the number of GUID pointers present in +// .................................ProtocolBuffer. +// @retval EFI_SUCCESS The list of protocol interface GUIDs installed on Handle was returned in +// ..............................ProtocolBuffer. The number of protocol interface GUIDs was +// ..............................returned in ProtocolBufferCount. +// @retval EFI_OUT_OF_RESOURCES There is not enough pool memory to store the results. +// @retval EFI_INVALID_PARAMETER Handle is NULL. +// @retval EFI_INVALID_PARAMETER Handle is not a valid EFI_HANDLE. +// @retval EFI_INVALID_PARAMETER ProtocolBuffer is NULL. +// @retval EFI_INVALID_PARAMETER ProtocolBufferCount is NULL. +func (p *EFI_BOOT_SERVICES) ProtocolsPerHandle(Handle EFI_HANDLE, ProtocolBuffer ***EFI_GUID, ProtocolBufferCount *UINTN) EFI_STATUS { + return UefiCall3(p.protocolsPerHandle, uintptr(Handle), uintptr(unsafe.Pointer(ProtocolBuffer)), uintptr(unsafe.Pointer(ProtocolBufferCount))) +} + +// LocateHandleBuffer +// Returns an array of handles that support the requested protocol in a buffer allocated from pool. +// @param[in] SearchType Specifies which handle(s) are to be returned. +// @param[in] Protocol Provides the protocol to search by. +// ..............................This parameter is only valid for a SearchType of ByProtocol. +// @param[in] SearchKey Supplies the search key depending on the SearchType. +// @param[out] NoHandles The number of handles returned in Buffer. +// @param[out] Buffer A pointer to the buffer to return the requested array of handles that +// ..............................support Protocol. +// @retval EFI_SUCCESS The array of handles was returned in Buffer, and the number of +// ..............................handles in Buffer was returned in NoHandles. +// @retval EFI_NOT_FOUND No handles match the search. +// @retval EFI_OUT_OF_RESOURCES There is not enough pool memory to store the matching results. +// @retval EFI_INVALID_PARAMETER NoHandles is NULL. +// @retval EFI_INVALID_PARAMETER Buffer is NULL. +func (p *EFI_BOOT_SERVICES) LocateHandleBuffer(SearchType EFI_LOCATE_SEARCH_TYPE, Protocol *EFI_GUID, SearchKey *VOID, NoHandles *UINTN, Buffer **EFI_HANDLE) EFI_STATUS { + return UefiCall5(p.locateHandleBuffer, uintptr(SearchType), uintptr(unsafe.Pointer(Protocol)), uintptr(unsafe.Pointer(SearchKey)), uintptr(unsafe.Pointer(NoHandles)), uintptr(unsafe.Pointer(Buffer))) +} + +// LocateProtocol +// Returns the first protocol instance that matches the given protocol. +// @param[in] Protocol Provides the protocol to search for. +// @param[in] Registration Optional registration key returned from +// ..............................RegisterProtocolNotify(). +// @param[out] Interface On return, a pointer to the first interface that matches Protocol and +// ..............................Registration. +// @retval EFI_SUCCESS A protocol instance matching Protocol was found and returned in +// ..............................Interface. +// @retval EFI_NOT_FOUND No protocol instances were found that match Protocol and +// ..............................Registration. +// @retval EFI_INVALID_PARAMETER Interface is NULL. +// ..............................Protocol is NULL. +func (p *EFI_BOOT_SERVICES) LocateProtocol(Protocol *EFI_GUID, Registration *VOID, InterfacePtr unsafe.Pointer) EFI_STATUS { + return UefiCall3(p.locateProtocol, uintptr(unsafe.Pointer(Protocol)), uintptr(unsafe.Pointer(Registration)), uintptr(InterfacePtr)) +} + +// InstallMultipleProtocolInterfaces +// Installs one or more protocol interfaces into the boot services environment. +// @param[in, out] Handle The pointer to a handle to install the new protocol interfaces on, +// .................or a pointer to NULL if a new handle is to be allocated. +// @param ... A variable argument list containing pairs of protocol GUIDs and protocol +// ..............................interfaces. +// @retval EFI_SUCCESS All the protocol interface was installed. +// @retval EFI_OUT_OF_RESOURCES There was not enough memory in pool to install all the protocols. +// @retval EFI_ALREADY_STARTED A Device Path Protocol instance was passed in that is already present in +// ..............................the handle database. +// @retval EFI_INVALID_PARAMETER Handle is NULL. +// @retval EFI_INVALID_PARAMETER Protocol is already installed on the handle specified by Handle. +func (p *EFI_BOOT_SERVICES) InstallMultipleProtocolInterfaces(Handle *EFI_HANDLE) EFI_STATUS { + return UefiCall1(p.installMultipleProtocolInterfaces, uintptr(unsafe.Pointer(Handle))) +} + +// UninstallMultipleProtocolInterfaces +// Removes one or more protocol interfaces into the boot services environment. +// @param[in] Handle The handle to remove the protocol interfaces from. +// @param ... A variable argument list containing pairs of protocol GUIDs and +// ..............................protocol interfaces. +// @retval EFI_SUCCESS All the protocol interfaces were removed. +// @retval EFI_INVALID_PARAMETER One of the protocol interfaces was not previously installed on Handle. +func (p *EFI_BOOT_SERVICES) UninstallMultipleProtocolInterfaces(Handle EFI_HANDLE) EFI_STATUS { + return UefiCall1(p.uninstallMultipleProtocolInterfaces, uintptr(Handle)) +} + +// CalculateCrc32 +// Computes and returns a 32-bit CRC for a data buffer. +// @param[in] Data A pointer to the buffer on which the 32-bit CRC is to be computed. +// @param[in] DataSize The number of bytes in the buffer Data. +// @param[out] Crc32 The 32-bit CRC that was computed for the data buffer specified by Data +// ..............................and DataSize. +// @retval EFI_SUCCESS The 32-bit CRC was computed for the data buffer and returned in +// ..............................Crc32. +// @retval EFI_INVALID_PARAMETER Data is NULL. +// @retval EFI_INVALID_PARAMETER Crc32 is NULL. +// @retval EFI_INVALID_PARAMETER DataSize is 0. +func (p *EFI_BOOT_SERVICES) CalculateCrc32(Data *VOID, DataSize UINTN, Crc32 *uint32) EFI_STATUS { + return UefiCall3(p.calculateCrc32, uintptr(unsafe.Pointer(Data)), uintptr(DataSize), uintptr(unsafe.Pointer(Crc32))) +} + +// CopyMem +// Copies the contents of one buffer to another buffer. +// @param[in] Destination The pointer to the destination buffer of the memory copy. +// @param[in] Source The pointer to the source buffer of the memory copy. +// @param[in] Length Number of bytes to copy from Source to Destination. +func (p *EFI_BOOT_SERVICES) CopyMem(Destination *VOID, Source *VOID, Length UINTN) EFI_STATUS { + return UefiCall3(p.copyMem, uintptr(unsafe.Pointer(Destination)), uintptr(unsafe.Pointer(Source)), uintptr(Length)) +} + +// SetMem +// The SetMem() function fills a buffer with a specified value. +// @param[in] Buffer The pointer to the buffer to fill. +// @param[in] Size Number of bytes in Buffer to fill. +// @param[in] Value Value to fill Buffer with. +func (p *EFI_BOOT_SERVICES) SetMem(Buffer *VOID, Size UINTN, Value byte) EFI_STATUS { + return UefiCall3(p.setMem, uintptr(unsafe.Pointer(Buffer)), uintptr(Size), uintptr(Value)) +} + +// CreateEventEx +// Creates an event in a group. +// @param[in] Type The type of event to create and its mode and attributes. +// @param[in] NotifyTpl The task priority level of event notifications,if needed. +// @param[in] NotifyFunction The pointer to the event's notification function, if any. +// @param[in] NotifyContext The pointer to the notification function's context; corresponds to parameter +// ..............................Context in the notification function. +// @param[in] EventGroup The pointer to the unique identifier of the group to which this event belongs. +// ..............................If this is NULL, then the function behaves as if the parameters were passed +// ..............................to CreateEvent. +// @param[out] Event The pointer to the newly created event if the call succeeds; undefined +// ..............................otherwise. +// @retval EFI_SUCCESS The event structure was created. +// @retval EFI_INVALID_PARAMETER One or more parameters are invalid. +// @retval EFI_OUT_OF_RESOURCES The event could not be allocated. +func (p *EFI_BOOT_SERVICES) CreateEventEx(Type uint32, NotifyTpl EFI_TPL, NotifyFunction unsafe.Pointer, NotifyContext uintptr, EventGroup *EFI_GUID, Event *EFI_EVENT) EFI_STATUS { + return UefiCall6(p.createEventEx, uintptr(Type), uintptr(NotifyTpl), uintptr(NotifyFunction), NotifyContext, uintptr(unsafe.Pointer(EventGroup)), uintptr(unsafe.Pointer(Event))) +} + +// endregion + +// EFI_GUID definitions +var ( + MPS_TABLE_GUID = EFI_GUID{ + Data1: 0xeb9d2d2f, + Data2: 0x2d88, + Data3: 0x11d3, + Data4: [8]uint8{0x9a, 0x16, 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d}, + } + + ACPI_TABLE_GUID = EFI_GUID{ + Data1: 0xeb9d2d30, + Data2: 0x2d88, + Data3: 0x11d3, + Data4: [8]uint8{0x9a, 0x16, 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d}, + } + + ACPI_20_TABLE_GUID = EFI_GUID{ + Data1: 0x8868e871, + Data2: 0xe4f1, + Data3: 0x11d3, + Data4: [8]uint8{0xbc, 0x22, 0x00, 0x80, 0xc7, 0x3c, 0x88, 0x81}, + } + + SMBIOS_TABLE_GUID = EFI_GUID{ + Data1: 0xeb9d2d31, + Data2: 0x2d88, + Data3: 0x11d3, + Data4: [8]uint8{0x9a, 0x16, 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d}, + } + + SMBIOS3_TABLE_GUID = EFI_GUID{ + Data1: 0xf2fd1544, + Data2: 0x9794, + Data3: 0x4a2c, + Data4: [8]uint8{0x99, 0x2e, 0xe5, 0xbb, 0xcf, 0x20, 0xe3, 0x94}, + } + + SAL_SYSTEM_TABLE_GUID = EFI_GUID{ + Data1: 0xeb9d2d32, + Data2: 0x2d88, + Data3: 0x11d3, + Data4: [8]uint8{0x9a, 0x16, 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d}, + } + + EFI_DTB_TABLE_GUID = EFI_GUID{ + Data1: 0xb1b621d5, + Data2: 0xf19c, + Data3: 0x41a5, + Data4: [8]uint8{0x83, 0x0b, 0xd9, 0x15, 0x2c, 0x69, 0xaa, 0xe0}, + } +) + +// EFI_CONFIGURATION_TABLE +// Contains a set of GUID/pointer pairs comprised of the ConfigurationTable field in the +// EFI System Table. +type EFI_CONFIGURATION_TABLE struct { + VendorGuid EFI_GUID + VendorTable *VOID +} + +// region: EFI System Table + +// EFI_SYSTEM_TABLE_SIGNATURE is the signature for the EFI system table. +const EFI_SYSTEM_TABLE_SIGNATURE = 0x5453595320494249 + +var ( + EFI1_02_SYSTEM_TABLE_REVISION = EFI_SPECIFICATION_REVISION_MAJORMINOR(1, 02) + EFI1_10_SYSTEM_TABLE_REVISION = EFI_SPECIFICATION_REVISION_MAJORMINOR(1, 10) + EFI2_00_SYSTEM_TABLE_REVISION = EFI_SPECIFICATION_REVISION_MAJORMINOR(2, 00) + EFI2_10_SYSTEM_TABLE_REVISION = EFI_SPECIFICATION_REVISION_MAJORMINOR(2, 10) + EFI2_20_SYSTEM_TABLE_REVISION = EFI_SPECIFICATION_REVISION_MAJORMINOR(2, 20) + EFI2_30_SYSTEM_TABLE_REVISION = EFI_SPECIFICATION_REVISION_MAJORMINOR(2, 30) + EFI2_31_SYSTEM_TABLE_REVISION = EFI_SPECIFICATION_REVISION_MAJORMINOR(2, 31) + EFI2_40_SYSTEM_TABLE_REVISION = EFI_SPECIFICATION_REVISION_MAJORMINOR(2, 40) + EFI2_50_SYSTEM_TABLE_REVISION = EFI_SPECIFICATION_REVISION_MAJORMINOR(2, 50) + EFI2_60_SYSTEM_TABLE_REVISION = EFI_SPECIFICATION_REVISION_MAJORMINOR(2, 60) + EFI2_70_SYSTEM_TABLE_REVISION = EFI_SPECIFICATION_REVISION_MAJORMINOR(2, 70) + EFI2_80_SYSTEM_TABLE_REVISION = EFI_SPECIFICATION_REVISION_MAJORMINOR(2, 80) + EFI2_90_SYSTEM_TABLE_REVISION = EFI_SPECIFICATION_REVISION_MAJORMINOR(2, 90) + EFI2_100_SYSTEM_TABLE_REVISION = EFI_SPECIFICATION_REVISION_MAJORMINOR(2, 100) + EFISYSTEM_TABLE_REVISION = EFI_SPECIFICATION_VERSION +) + +// EFI_SYSTEM_TABLE +// EFI System Table +type EFI_SYSTEM_TABLE struct { + Hdr EFI_TABLE_HEADER + FirmwareVendor *CHAR16 + FirmwareRevision uint32 + ConsoleInHandle EFI_HANDLE + ConIn *EFI_SIMPLE_TEXT_INPUT_PROTOCOL + 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 *EFI_CONFIGURATION_TABLE +} + +// endregion + +type EFI_LOAD_OPTION struct { + Attributes uint32 + FilePathListLength uint16 +} + +//type EFI_KEY_OPTION struct { +// KeyData EFI_BOOT_KEY_DATA +// BootOptionCrc uint32 +// BootOption uint16 +//} diff --git a/src/machine/uefi/util.go b/src/machine/uefi/util.go new file mode 100644 index 0000000000..ea65f346ab --- /dev/null +++ b/src/machine/uefi/util.go @@ -0,0 +1,107 @@ +//go:build uefi + +package uefi + +import ( + "unicode/utf16" + "unicode/utf8" + "unsafe" +) + +var systemTable *EFI_SYSTEM_TABLE +var imageHandle uintptr + +//go:nobound +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 { + return systemTable.BootServices +} + +func GetImageHandle() EFI_HANDLE { + return EFI_HANDLE(imageHandle) +} + +func StringToUTF16(s string) []CHAR16 { + rs := []rune(s) + c16 := make([]CHAR16, len(rs)) + for i, r := range rs { + c16[i] = CHAR16(r) + } + c16 = append(c16, 0) + return c16 +} + +func UTF16ToString(input []CHAR16) string { + return UTF16PtrLenToString(&input[0], len(input)) +} + +func UTF16PtrToString(input *CHAR16) string { + pointer := uintptr(unsafe.Pointer(input)) + length := 0 + for *(*CHAR16)(unsafe.Pointer(pointer)) != 0 { + length++ + pointer += 2 + } + return UTF16PtrLenToString(input, length) +} + +func UTF16PtrLenToString(input *CHAR16, length int) string { + var output []byte = make([]byte, 0, length) + var u8buf [4]byte + inputSlice := unsafe.Slice((*uint16)(unsafe.Pointer(input)), length) + decodedRunes := utf16.Decode(inputSlice) + for _, r := range decodedRunes { + chunkLength := utf8.EncodeRune(u8buf[:], r) + output = append(output, u8buf[:chunkLength]...) + } + return string(output) +} + +var hexConst [16]CHAR16 = [16]CHAR16{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'} + +var printBuf [256]CHAR16 + +//go:nobounds +//go:noinline +func DebugPrint(text string, val uint64) { + var i int + for i = 0; i < len(text); i++ { + printBuf[i] = CHAR16(text[i]) + } + printBuf[i] = CHAR16(':') + i++ + for j := 0; j < (64 / 4); j++ { + t := int(val>>((15-j)*4)) & 0xf + printBuf[i] = hexConst[t] + i++ + } + printBuf[i] = '\r' + i++ + printBuf[i] = '\n' + i++ + printBuf[i] = 0 + + systemTable.ConOut.OutputString(&printBuf[0]) +} + +func convertBoolean(b BOOLEAN) uintptr { + if b { + return 1 + } + return 0 +} + +func convertBool(b bool) uintptr { + if b { + return 1 + } + return 0 +} diff --git a/src/runtime/baremetal.go b/src/runtime/baremetal.go index aecb189720..8c374b893f 100644 --- a/src/runtime/baremetal.go +++ b/src/runtime/baremetal.go @@ -6,36 +6,6 @@ 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 -} - //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..9652b15c38 --- /dev/null +++ b/src/runtime/baremetal_memory.go @@ -0,0 +1,37 @@ +//go:build baremetal && !uefi + +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 +} diff --git a/src/runtime/gc_blocks.go b/src/runtime/gc_blocks.go index 85f4994908..79904c0698 100644 --- a/src/runtime/gc_blocks.go +++ b/src/runtime/gc_blocks.go @@ -367,7 +367,7 @@ func alloc(size uintptr, layout unsafe.Pointer) unsafe.Pointer { } // Wrap around the end of the heap. - if index == endBlock { + if index >= endBlock { index = 0 // Reset numFreeBlocks as allocations cannot wrap. numFreeBlocks = 0 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..6369e42d4b --- /dev/null +++ b/src/runtime/gc_leaking_uefi.go @@ -0,0 +1,11 @@ +//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/nonhosted.go b/src/runtime/nonhosted.go index 9f01a7621a..a9f1749395 100644 --- a/src/runtime/nonhosted.go +++ b/src/runtime/nonhosted.go @@ -1,4 +1,4 @@ -//go:build baremetal || js || wasm_unknown || nintendoswitch +//go:build (baremetal || js || wasm_unknown || nintendoswitch) && !uefi package runtime diff --git a/src/runtime/os_uefi.go b/src/runtime/os_uefi.go new file mode 100644 index 0000000000..efded26ef2 --- /dev/null +++ b/src/runtime/os_uefi.go @@ -0,0 +1,39 @@ +//go:build uefi + +package runtime + +import ( + "machine/uefi" + "unsafe" +) + +// loadedImageBase is a cache for the ImageBase so we don't risk an allocation while +// running GC. Prevents crashes with gc.precise and scheduler.tasks. +var loadedImageBase uintptr + +func init() { + var img *uefi.EFI_LOADED_IMAGE_PROTOCOL + if uefi.BS().HandleProtocol( + uefi.GetImageHandle(), + &uefi.EFI_LOADED_IMAGE_GUID, + unsafe.Pointer(&img), + ) == uefi.EFI_SUCCESS { + loadedImageBase = uintptr(unsafe.Pointer(img.ImageBase)) + } + module = (*exeHeader)(unsafe.Pointer(loadedImageBase)) +} + +// 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 +// runtime. This might seem complex and it kind of is, but it only compiles to +// around 160 bytes of amd64 instructions. +// Most of this function is based on the documentation in +// https://docs.microsoft.com/en-us/windows/win32/debug/pe-format. +func findGlobals(found func(start, end uintptr)) { + if loadedImageBase == 0 { + return // header not available; skip globals + } + + findGlobalsForPE(found) +} diff --git a/src/runtime/os_winabi.go b/src/runtime/os_winabi.go new file mode 100644 index 0000000000..6afdc9802a --- /dev/null +++ b/src/runtime/os_winabi.go @@ -0,0 +1,70 @@ +//go:build windows || uefi + +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)) { + // Constants used in this function. + const ( + // https://docs.microsoft.com/en-us/windows/win32/debug/pe-format + IMAGE_SCN_MEM_WRITE = 0x80000000 + ) + + pe := (*peHeader)(unsafe.Add(unsafe.Pointer(module), module.peHeader)) + if pe.magic != 0x00004550 { // 0x4550 is "PE" + return + } + + // 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{}))) + } +} 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/runtime_uefi.go b/src/runtime/runtime_uefi.go new file mode 100644 index 0000000000..6775b95f0e --- /dev/null +++ b/src/runtime/runtime_uefi.go @@ -0,0 +1,253 @@ +//go:build uefi + +package runtime + +import ( + "machine/uefi" + "unsafe" +) + +type WaitForEvents func() + +// ticks returns the number of ticks (microseconds) elapsed since power up. +func ticks() timeUnit { + t := uefi.Ticks() + return timeUnit(t) +} + +func nanosecondsToTicks(ns int64) timeUnit { + return timeUnit(ns * int64(uefi.TicksFrequency()) / 1000000000) +} + +func ticksToNanoseconds(ticks timeUnit) int64 { + frequency := timeUnit(uefi.TicksFrequency()) + + // Ticks + // Time = --------- x 1,000,000,000 + // Frequency + nanoSeconds := (ticks / frequency) * 1000000000 + remainder := ticks % frequency + + // Ensure (Remainder * 1,000,000,000) will not overflow 64-bit. + // Since 2^29 < 1,000,000,000 = 0x3B9ACA00 < 2^30, Remainder should < 2^(64-30) = 2^34, + // i.e. highest bit set in Remainder should <= 33. + // + shift := highBitSet64(uint64(remainder)) - 32 + if shift < 0 { + shift = 0 + } + remainder = remainder >> shift + frequency = frequency >> shift + nanoSeconds += remainder * 1000000000 / frequency + + return int64(nanoSeconds) +} + +func highBitSet64(operand uint64) int { + if operand == (operand & 0xffffffff) { + return highBitSet32(uint32(operand)) + } + return highBitSet32(uint32(operand>>32)) + 32 +} + +func highBitSet32(operand uint32) int { + if operand == 0 { + return -1 + } + bitIndex := 32 + for operand > 0 { + bitIndex-- + operand <<= 1 + } + return bitIndex +} + +func sleepTicks(d timeUnit) { + if d == 0 { + return + } + + sleepUntil := ticks() + d + for ticks() < sleepUntil { + uefi.CpuPause() + } +} + +func putchar(c byte) { + buf := [2]uefi.CHAR16{uefi.CHAR16(c), 0} + st := uefi.ST() + 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) +} + +//go:linkname procPin sync/atomic.runtime_procPin +func procPin() { +} + +//go:linkname procUnpin sync/atomic.runtime_procUnpin +func procUnpin() { +} + +// Try for 256MiB, which should be reasonable for an amd64 system running UEFI +// We use growHeap() to do the initial allocation which tries for +// heapSize*4/3, thus 192 * 4 / 3 = 256. +var heapSize uintptr = 192 * 1024 * 1024 + +var heapStart, heapEnd uintptr + +var stackTop uintptr + +var allocatePagesAddress uefi.EFI_PHYSICAL_ADDRESS + +func preinit() { + // always disable watchdog; if the user wants it they can turn it back on + uefi.ST().BootServices.SetWatchdogTimer(0, 0, 0, nil) + + // first time allocating heap + if !growHeap() { + panic("couldn't allocate heap") + } + + // parse and setup any command-line arguments + lip, err := uefi.GetLoadedImageProtocol() + if err != nil { + return + } + + if lip.LoadOptionsSize > 0 { + //naïve splitting: TODO: something that honors quotes + args = strings_fields( + uefi.UTF16PtrLenToString( + (*uefi.CHAR16)(unsafe.Pointer(lip.LoadOptions)), + int(lip.LoadOptionsSize/2)-1)) + } +} + +//go:linkname strings_fields strings.Fields +func strings_fields(string) []string + +// growHeap tries to grow the heap size. It returns true if it succeeds, false +// otherwise. +// +// Current implementation is flawed in that once we've allocated more than half the memory +// we cannot grow any farther as a big enough contiguous chunk is no longer available. +// Additionally, UEFI on real hardware, in contrast to a qemu virtual machine running +// EDK2 Tiano, seems to have much more memory fragmentation. In some cases, allocating +// just a quarter of total available RAM will fail. +// +// TODO: Consider using GetMemoryMap to locate the largest chunk of EfiConventionalMemory. +func growHeap() bool { + // try a 33% bigger heap, page aligned + newHeapSize := ((heapSize * 4) / 3) &^ 4095 + + bs := uefi.BS() + var status uefi.EFI_STATUS + for newHeapSize >= heapSize { + pages := newHeapSize / 4096 + status = 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 { + uefi.DebugPrint("AllocatePages failed", uint64(status)) + return false + } + newHeapSize /= 2 + } + + return false +} + +func init() { + var efiTime uefi.EFI_TIME + + mono := nanotime() + efiTime, status := uefi.GetTime() + if status == uefi.EFI_SUCCESS { + sec, nsec := efiTime.GetEpoch() + timeOffset = sec*1000000000 + int64(nsec) - mono + } +} + +var waitForEventsFunction WaitForEvents = func() { + uefi.CpuPause() +} + +// SetWaitForEvents +// You can implement your own event-loop with BS.CheckEvent or BS.WaitForEvents. +func SetWaitForEvents(f WaitForEvents) { + waitForEventsFunction = f +} + +func waitForEvents() { + waitForEventsFunction() +} + +// Must be a separate function to get the correct stack pointer. +// +//go:noinline +func runMain() { + run() +} + +//export efi_main +func main(imageHandle uintptr, systemTable uintptr) uintptr { + uefi.Init(imageHandle, systemTable) + + preinit() + + // Obtain the initial stack pointer right before calling the run() function. + // The run function has been moved to a separate (non-inlined) function so + // that the correct stack pointer is read. + stackTop = getCurrentStackPointer() + + runMain() + + if heapStart != 0 { + uefi.ST().BootServices.FreePool((*uefi.VOID)(unsafe.Pointer(heapStart))) + } + + uefi.BS().Exit(uefi.GetImageHandle(), 0, 0, nil) + + // For libc compatibility. + return 0 +} + +// lie and say we have a byte ready +func buffered() int { return 1 } + +// getchar blocks trying to get a KeyStroke event +func getchar() byte { + conIn, _ := uefi.SimpleTextInExProtocol() + // GetKey uses Simple Text Input Ex Protocol. + // We probably shouldn't assume STIEP is implemented, + // but it was added in version 2.0 of the spec which + // came out in 2006. + k := conIn.GetKey() + return byte(k.Key.UnicodeChar) +} + +// This is the default set of arguments, if nothing else has been set. +var args = []string{"/proc/self/exe"} + +//go:linkname os_runtime_args os.runtime_args +func os_runtime_args() []string { + return args +} + +var osArgs string +var osEnv string 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 diff --git a/src/syscall/env_nonhosted.go b/src/syscall/env_nonhosted.go index 446ba55d2d..882e659e53 100644 --- a/src/syscall/env_nonhosted.go +++ b/src/syscall/env_nonhosted.go @@ -1,4 +1,4 @@ -//go:build baremetal || js || wasm_unknown +//go:build (baremetal || js || wasm_unknown) && !uefi package syscall diff --git a/src/syscall/env_uefi.go b/src/syscall/env_uefi.go new file mode 100644 index 0000000000..ebdb019702 --- /dev/null +++ b/src/syscall/env_uefi.go @@ -0,0 +1,173 @@ +//go:build uefi + +package syscall + +import ( + "errors" + "machine/uefi" + "unsafe" +) + +// EnvVendor is the UUID used to specify VendorGUID for Getting and Setting +// non-volatile EFI variables. Override this value to have a private variable +// space for an EFI application. +// +// The default value was generated with +// +// uuidgen -N TinyGo -n @oid --sha1 +var EnvVendor = uefi.EFI_GUID{ + 0xaabc54d8, 0x7b8e, 0x5680, + [...]byte{0x9f, 0x6c, 0x68, 0xda, 0x0d, 0xbb, 0xcd, 0xbf}} + +func Environ() []string { + // our implementation of runtime envs is already a freshly-made copy + // so just return that copy, rather than make a second, redudant copy. + return runtime_envs() +} + +func Getenv(key string) (value string, found bool) { + var data []byte + keyC16 := uefi.StringToUTF16(key) + data, found = getkey(keyC16) + if found { + return string(data), true + } + return "", false +} + +func getkey(key []uefi.CHAR16) (value []byte, found bool) { + var ( + dataSize uefi.UINTN + data []byte = make([]byte, 1) + ) + // call with dataSize = 0 so we can be told how big the buffer needs to be; this is passed through dataSize + status := uefi.ST().RuntimeServices.GetVariable( + (*uefi.CHAR16)(unsafe.Pointer(&key[0])), // Variable Name + &EnvVendor, // Vendor GUID + nil, // optional attributes + &dataSize, // Before call: Size of Buffer; after call: how much was written to the buffer + (*uefi.VOID)(unsafe.Pointer(&data[0]))) + switch status { + case uefi.EFI_NOT_FOUND: + return nil, false + case uefi.EFI_BUFFER_TOO_SMALL: + data = make([]byte, int(dataSize)) + status = uefi.ST().RuntimeServices.GetVariable( + (*uefi.CHAR16)(unsafe.Pointer(&key[0])), // Variable Name + &EnvVendor, // Vendor GUID + nil, // optional attributes + &dataSize, // Before call: Size of Buffer; after call: how much was written to the buffer + (*uefi.VOID)(unsafe.Pointer(&data[0]))) + if status != uefi.EFI_SUCCESS { + return nil, false + } + return data, true + default: + return nil, false + } +} + +func Setenv(key, val string) error { + if key == "" { + return errors.New("key must not be empty") + } + + keyC16 := uefi.StringToUTF16(key) + data := []byte(val) + + status := uefi.ST().RuntimeServices.SetVariable( + (*uefi.CHAR16)(unsafe.Pointer(&keyC16[0])), + &EnvVendor, + uefi.EFI_VARIABLE_NON_VOLATILE| + uefi.EFI_VARIABLE_BOOTSERVICE_ACCESS| + uefi.EFI_VARIABLE_RUNTIME_ACCESS, + uefi.UINTN(len(data)), + (*uefi.VOID)(unsafe.Pointer(&data[0])), + ) + + if status == uefi.EFI_SUCCESS { + return nil + } + + return uefi.StatusError(status) +} + +func Unsetenv(key string) (err error) { + // Setting a key to a dataSize of 0 deletes it + keyC16 := uefi.StringToUTF16(key) + + status := uefi.ST().RuntimeServices.SetVariable( + (*uefi.CHAR16)(unsafe.Pointer(&keyC16[0])), + &EnvVendor, + uefi.EFI_VARIABLE_NON_VOLATILE| + uefi.EFI_VARIABLE_BOOTSERVICE_ACCESS| + uefi.EFI_VARIABLE_RUNTIME_ACCESS, + uefi.UINTN(0), + nil, + ) + + if status == uefi.EFI_SUCCESS { + return nil + } + + return uefi.StatusError(status) +} + +func Clearenv() (err error) { + // stub for now + return ENOSYS +} + +func runtime_envs() (env []string) { + // GetNextVariableName is a bit screwy, per the spec: + // To start, you specify a pointer to a null, that is, + // you send a null-terminated, zero-length string + // You supply the last returned variable to GetNextVariableName + // to get the next variable. status is set to + // uefi.EFI_NOT_FOUND when there are no more variables + // to return. No filtering happens via vendorGUID, despite + // the spec saying this is both IN and OUT, it is only + // OUT. + + var ( + varKey = make([]uefi.CHAR16, 1) + varKeySize = uefi.UINTN(2) + status uefi.EFI_STATUS + vendorGUID uefi.EFI_GUID + ) + for { + status = uefi.ST().RuntimeServices.GetNextVariableName( + &varKeySize, + (*uefi.CHAR16)(unsafe.Pointer(&varKey[0])), + &vendorGUID) + + switch status { + case uefi.EFI_BUFFER_TOO_SMALL: + // buffer was too small, the size needed will be in + // varKeySize. + newVarKey := make([]uefi.CHAR16, varKeySize) + copy(newVarKey, varKey) + varKey = newVarKey[:varKeySize/2] + continue + case uefi.EFI_SUCCESS: + // we read a variable name, if it's ours, get it + // and append it to our list + if vendorGUID != EnvVendor { + continue + } + keyString := uefi.UTF16ToString(varKey[:varKeySize/2]) + val, _ := getkey(varKey[:varKeySize/2]) + env = append(env, keyString+"="+string(val)) + case uefi.EFI_NOT_FOUND: + // all done! + return + case uefi.EFI_INVALID_PARAMETER: + // this gets its own branch because it means something in this function + // was done incorrectly. + panic("invalid parameter passed to GetNextVariableName") + default: + // something went wrong; cheese it + panic(uefi.StatusError(status)) + } + } +} diff --git a/targets/uefi-amd64.json b/targets/uefi-amd64.json new file mode 100644 index 0000000000..98cc33f717 --- /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": "precise", + "linker": "ld.lld", + "libc": "picolibc", + "scheduler": "tasks", + "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/internal/task/task_stack_amd64_windows.S", + "src/runtime/asm_amd64_windows.S" + ], + "gdb": ["gdb-multiarch", "gdb"] +}