From 4f516ad8c54b4c7aad0ee4fced8fee896dfe3436 Mon Sep 17 00:00:00 2001 From: Matthew Hiles Date: Mon, 15 Jun 2026 14:45:37 -0400 Subject: [PATCH] add device/uefi and device/amd64 --- src/device/amd64/cpu.go | 74 +++++++++++ src/device/amd64/cpu_amd64.S | 39 ++++++ src/device/uefi/arch_x86.go | 17 +++ src/device/uefi/asm_amd64.S | 201 +++++++++++++++++++++++++++++ src/device/uefi/call.go | 45 +++++++ src/device/uefi/char16.go | 77 +++++++++++ src/device/uefi/clock.go | 39 ++++++ src/device/uefi/efidef.go | 66 ++++++++++ src/device/uefi/efistatus.go | 111 ++++++++++++++++ src/device/uefi/simple_text_out.go | 74 +++++++++++ src/device/uefi/tables.go | 123 ++++++++++++++++++ src/device/uefi/text_output.go | 41 ++++++ src/device/uefi/util.go | 29 +++++ 13 files changed, 936 insertions(+) create mode 100644 src/device/amd64/cpu.go create mode 100644 src/device/amd64/cpu_amd64.S create mode 100644 src/device/uefi/arch_x86.go create mode 100644 src/device/uefi/asm_amd64.S create mode 100644 src/device/uefi/call.go create mode 100644 src/device/uefi/char16.go create mode 100644 src/device/uefi/clock.go create mode 100644 src/device/uefi/efidef.go create mode 100644 src/device/uefi/efistatus.go create mode 100644 src/device/uefi/simple_text_out.go create mode 100644 src/device/uefi/tables.go create mode 100644 src/device/uefi/text_output.go create mode 100644 src/device/uefi/util.go diff --git a/src/device/amd64/cpu.go b/src/device/amd64/cpu.go new file mode 100644 index 0000000000..32cbfe798c --- /dev/null +++ b/src/device/amd64/cpu.go @@ -0,0 +1,74 @@ +//go:build amd64 + +package amd64 + +const ( + CPUIDTimeStampCounter = 0x15 + CPUIDProcessorFrequency = 0x16 +) + +type CPUExtendedFamily uint16 + +const ( + CPUFamilyIntelCore CPUExtendedFamily = 6 +) + +//export asmPause +func AsmPause() + +//export asmReadRdtsc +func AsmReadRdtsc() uint64 + +//export asmCpuid +func AsmCpuid(index uint32, registerEax *uint32, registerEbx *uint32, registerEcx *uint32) int + +var maxCpuidIndex uint32 +var stdVendorName0 uint32 +var stdCpuid1Eax uint32 + +func init() { + AsmCpuid(0, &maxCpuidIndex, &stdVendorName0, nil) + AsmCpuid(1, &stdCpuid1Eax, nil, nil) +} + +func getExtendedCPUFamily() CPUExtendedFamily { + family := CPUExtendedFamily((stdCpuid1Eax >> 8) & 0x0f) + family += CPUExtendedFamily((stdCpuid1Eax >> 20) & 0xff) + return family +} + +func isIntel() bool { + return stdVendorName0 == 0x756e6547 +} + +func isIntelFamilyCore() bool { + return isIntel() && getExtendedCPUFamily() == CPUFamilyIntelCore +} + +func InternalGetPerformanceCounterFrequency() uint64 { + if maxCpuidIndex >= CPUIDTimeStampCounter { + return cpuidCoreClockCalculateTSCFrequency() + } + return 0 +} + +func cpuidCoreClockCalculateTSCFrequency() uint64 { + var eax uint32 + var ebx uint32 + var ecx uint32 + + AsmCpuid(CPUIDTimeStampCounter, &eax, &ebx, &ecx) + if eax == 0 || ebx == 0 { + return 0 + } + + coreCrystalFrequency := uint64(ecx) + if coreCrystalFrequency == 0 { + if !isIntelFamilyCore() { + return 0 + } + coreCrystalFrequency = 24000000 + } + + return ((coreCrystalFrequency * uint64(ebx)) + (uint64(eax) / 2)) / uint64(eax) +} diff --git a/src/device/amd64/cpu_amd64.S b/src/device/amd64/cpu_amd64.S new file mode 100644 index 0000000000..ce1990c4cf --- /dev/null +++ b/src/device/amd64/cpu_amd64.S @@ -0,0 +1,39 @@ +.section .text + +.global asmPause +asmPause: + pause + ret + +.global asmReadRdtsc +asmReadRdtsc: + rdtsc + shlq $0x20, %rdx + orq %rdx, %rax + ret + +.global asmCpuid +asmCpuid: + pushq %rbx + + mov %ecx, %eax + pushq %rax + + pushq %rdx + cpuid + + test %r9, %r9 + jz .SkipEcx + mov %ecx, (%r9) +.SkipEcx: + popq %rcx + jrcxz .SkipEax + mov %eax, (%rcx) +.SkipEax: + mov %r8, %rcx + jrcxz .SkipEbx + mov %ebx, (%rcx) +.SkipEbx: + popq %rax + popq %rbx + ret diff --git a/src/device/uefi/arch_x86.go b/src/device/uefi/arch_x86.go new file mode 100644 index 0000000000..3eb344fe94 --- /dev/null +++ b/src/device/uefi/arch_x86.go @@ -0,0 +1,17 @@ +//go:build i386 || amd64 + +package uefi + +import "device/amd64" + +func Ticks() uint64 { + return amd64.AsmReadRdtsc() +} + +func CpuPause() { + amd64.AsmPause() +} + +func getTSCFrequency() uint64 { + return amd64.InternalGetPerformanceCounterFrequency() +} diff --git a/src/device/uefi/asm_amd64.S b/src/device/uefi/asm_amd64.S new file mode 100644 index 0000000000..5037432991 --- /dev/null +++ b/src/device/uefi/asm_amd64.S @@ -0,0 +1,201 @@ +.section .text + +.global uefiCall0 +uefiCall0: + pushq %rbp + movq %rsp, %rbp + subq $0x20, %rsp + callq *%rcx + movq %rbp, %rsp + popq %rbp + ret + +.global uefiCall1 +uefiCall1: + pushq %rbp + movq %rsp, %rbp + subq $0x20, %rsp + movq %rcx, %rax + movq %rdx, %rcx + callq *%rax + movq %rbp, %rsp + popq %rbp + ret + +.global uefiCall2 +uefiCall2: + pushq %rbp + movq %rsp, %rbp + subq $0x20, %rsp + movq %rcx, %rax + movq %rdx, %rcx + movq %r8, %rdx + callq *%rax + movq %rbp, %rsp + popq %rbp + ret + +.global uefiCall3 +uefiCall3: + pushq %rbp + movq %rsp, %rbp + subq $0x20, %rsp + movq %rcx, %rax + movq %rdx, %rcx + movq %r8, %rdx + movq %r9, %r8 + callq *%rax + movq %rbp, %rsp + popq %rbp + ret + +.global uefiCall4 +uefiCall4: + pushq %rbp + movq %rsp, %rbp + subq $0x20, %rsp + movq %rcx, %rax + movq %rdx, %rcx + movq %r8, %rdx + movq %r9, %r8 + movq 0x30(%rbp), %r9 + callq *%rax + movq %rbp, %rsp + popq %rbp + ret + +.global uefiCall5 +uefiCall5: + pushq %rbp + movq %rsp, %rbp + subq $0x30, %rsp + movq %rcx, %rax + movq %rdx, %rcx + movq %r8, %rdx + movq %r9, %r8 + movq 0x30(%rbp), %r9 + movq 0x38(%rbp), %r10 + movq %r10, 0x20(%rsp) + callq *%rax + movq %rbp, %rsp + popq %rbp + ret + +.global uefiCall6 +uefiCall6: + pushq %rbp + movq %rsp, %rbp + subq $0x30, %rsp + movq %rcx, %rax + movq %rdx, %rcx + movq %r8, %rdx + movq %r9, %r8 + movq 0x30(%rbp), %r9 + movq 0x38(%rbp), %r10 + movq %r10, 0x20(%rsp) + movq 0x40(%rbp), %r10 + movq %r10, 0x28(%rsp) + callq *%rax + movq %rbp, %rsp + popq %rbp + ret + +.global uefiCall7 +uefiCall7: + pushq %rbp + movq %rsp, %rbp + subq $0x40, %rsp + movq %rcx, %rax + movq %rdx, %rcx + movq %r8, %rdx + movq %r9, %r8 + movq 0x30(%rbp), %r9 + movq 0x38(%rbp), %r10 + movq %r10, 0x20(%rsp) + movq 0x40(%rbp), %r10 + movq %r10, 0x28(%rsp) + movq 0x48(%rbp), %r10 + movq %r10, 0x30(%rsp) + callq *%rax + movq %rbp, %rsp + popq %rbp + ret + +.global uefiCall8 +uefiCall8: + pushq %rbp + movq %rsp, %rbp + subq $0x40, %rsp + movq %rcx, %rax + movq %rdx, %rcx + movq %r8, %rdx + movq %r9, %r8 + movq 0x30(%rbp), %r9 + movq 0x38(%rbp), %r10 + movq %r10, 0x20(%rsp) + movq 0x40(%rbp), %r10 + movq %r10, 0x28(%rsp) + movq 0x48(%rbp), %r10 + movq %r10, 0x30(%rsp) + movq 0x50(%rbp), %r10 + movq %r10, 0x38(%rsp) + callq *%rax + movq %rbp, %rsp + popq %rbp + ret + +.global uefiCall9 +uefiCall9: + pushq %rbp + movq %rsp, %rbp + subq $0x50, %rsp + movq %rcx, %rax + movq %rdx, %rcx + movq %r8, %rdx + movq %r9, %r8 + movq 0x30(%rbp), %r9 + movq 0x38(%rbp), %r10 + movq %r10, 0x20(%rsp) + movq 0x40(%rbp), %r10 + movq %r10, 0x28(%rsp) + movq 0x48(%rbp), %r10 + movq %r10, 0x30(%rsp) + movq 0x50(%rbp), %r10 + movq %r10, 0x38(%rsp) + movq 0x58(%rbp), %r10 + movq %r10, 0x40(%rsp) + callq *%rax + movq %rbp, %rsp + popq %rbp + ret + +.global uefiCall10 +uefiCall10: + pushq %rbp + movq %rsp, %rbp + subq $0x50, %rsp + movq %rcx, %rax + movq %rdx, %rcx + movq %r8, %rdx + movq %r9, %r8 + movq 0x30(%rbp), %r9 + movq 0x38(%rbp), %r10 + movq %r10, 0x20(%rsp) + movq 0x40(%rbp), %r10 + movq %r10, 0x28(%rsp) + movq 0x48(%rbp), %r10 + movq %r10, 0x30(%rsp) + movq 0x50(%rbp), %r10 + movq %r10, 0x38(%rsp) + movq 0x58(%rbp), %r10 + movq %r10, 0x40(%rsp) + movq 0x60(%rbp), %r10 + movq %r10, 0x48(%rsp) + callq *%rax + movq %rbp, %rsp + popq %rbp + ret + +.global ___chkstk_ms +___chkstk_ms: + ret diff --git a/src/device/uefi/call.go b/src/device/uefi/call.go new file mode 100644 index 0000000000..0ae162fce5 --- /dev/null +++ b/src/device/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/device/uefi/char16.go b/src/device/uefi/char16.go new file mode 100644 index 0000000000..3c3818c7c1 --- /dev/null +++ b/src/device/uefi/char16.go @@ -0,0 +1,77 @@ +package uefi + +import ( + "unicode/utf16" + "unsafe" +) + +// StringToCHAR16 converts a Go string to a UTF-16 code unit slice. +func StringToCHAR16(s string) []CHAR16 { + if s == "" { + return nil + } + + encoded := utf16.Encode([]rune(s)) + out := make([]CHAR16, len(encoded)) + for i, r := range encoded { + out[i] = CHAR16(r) + } + return out +} + +// StringToCHAR16Z converts a Go string to a NUL-terminated UTF-16 code unit slice. +func StringToCHAR16Z(s string) []CHAR16 { + out := StringToCHAR16(s) + return append(out, 0) +} + +// BytesToCHAR16 converts UTF-8 text bytes to a UTF-16 code unit slice. +func BytesToCHAR16(b []byte) []CHAR16 { + return StringToCHAR16(string(b)) +} + +// BytesToCHAR16Z converts UTF-8 text bytes to a NUL-terminated UTF-16 code unit slice. +func BytesToCHAR16Z(b []byte) []CHAR16 { + return StringToCHAR16Z(string(b)) +} + +// CHAR16ToString converts a UTF-16 code unit slice to a Go string. +func CHAR16ToString(input []CHAR16) string { + if len(input) == 0 { + return "" + } + + units := make([]uint16, len(input)) + for i, c := range input { + units[i] = uint16(c) + } + return string(utf16.Decode(units)) +} + +// CHAR16ToBytes converts a UTF-16 code unit slice to UTF-8 text bytes. +func CHAR16ToBytes(input []CHAR16) []byte { + return []byte(CHAR16ToString(input)) +} + +// CHAR16PtrToString converts a NUL-terminated UTF-16 string pointer to a Go string. +func CHAR16PtrToString(input *CHAR16) string { + if input == nil { + return "" + } + + ptr := uintptr(unsafe.Pointer(input)) + length := 0 + for *(*CHAR16)(unsafe.Pointer(ptr)) != 0 { + length++ + ptr += 2 + } + return CHAR16PtrLenToString(input, length) +} + +// CHAR16PtrLenToString converts a UTF-16 string pointer with a known code unit count to a Go string. +func CHAR16PtrLenToString(input *CHAR16, length int) string { + if input == nil || length <= 0 { + return "" + } + return CHAR16ToString(unsafe.Slice(input, length)) +} diff --git a/src/device/uefi/clock.go b/src/device/uefi/clock.go new file mode 100644 index 0000000000..cae519eea0 --- /dev/null +++ b/src/device/uefi/clock.go @@ -0,0 +1,39 @@ +//go:build uefi + +package uefi + +import "sync" + +var calibrateMutex sync.Mutex +var calculatedFrequency uint64 + +func TicksFrequency() uint64 { + frequency := getTSCFrequency() + if frequency > 0 { + return frequency + } + + calibrateMutex.Lock() + defer calibrateMutex.Unlock() + if calculatedFrequency > 0 { + return calculatedFrequency + } + + var event EFI_EVENT + var index UINTN + if BS().CreateEvent(EVT_TIMER, TPL_CALLBACK, nil, nil, &event) != EFI_SUCCESS { + return 0 + } + defer BS().CloseEvent(event) + + start := Ticks() + if BS().SetTimer(event, TimerPeriodic, 250*10000) != EFI_SUCCESS { + return 0 + } + if BS().WaitForEvent(1, &event, &index) != EFI_SUCCESS { + return 0 + } + + calculatedFrequency = (Ticks() - start) * 4 + return calculatedFrequency +} diff --git a/src/device/uefi/efidef.go b/src/device/uefi/efidef.go new file mode 100644 index 0000000000..ef5f1c825b --- /dev/null +++ b/src/device/uefi/efidef.go @@ -0,0 +1,66 @@ +package uefi + +type UINTN uintptr +type EFI_STATUS UINTN +type EFI_TPL UINTN +type EFI_HANDLE uintptr +type EFI_EVENT uintptr +type EFI_PHYSICAL_ADDRESS uint64 + +type CHAR16 uint16 +type BOOLEAN bool +type VOID byte + +type EFI_GUID struct { + Data1 uint32 + Data2 uint16 + Data3 uint16 + Data4 [8]byte +} + +type EFI_TABLE_HEADER struct { + Signature uint64 + Revision uint32 + HeaderSize uint32 + CRC32 uint32 + Reserved uint32 +} + +type EFI_ALLOCATE_TYPE int + +const ( + AllocateAnyPages EFI_ALLOCATE_TYPE = iota + AllocateMaxAddress + AllocateAddress +) + +type EFI_MEMORY_TYPE int + +const ( + EfiReservedMemoryType EFI_MEMORY_TYPE = iota + EfiLoaderCode + EfiLoaderData + EfiBootServicesCode + EfiBootServicesData + EfiRuntimeServicesCode + EfiRuntimeServicesData + EfiConventionalMemory +) + +type EVENT_TYPE uint32 + +const ( + EVT_TIMER EVENT_TYPE = 0x80000000 +) + +const ( + TPL_CALLBACK EFI_TPL = 8 +) + +type EFI_TIMER_DELAY int + +const ( + TimerCancel EFI_TIMER_DELAY = iota + TimerPeriodic + TimerRelative +) diff --git a/src/device/uefi/efistatus.go b/src/device/uefi/efistatus.go new file mode 100644 index 0000000000..842cd66612 --- /dev/null +++ b/src/device/uefi/efistatus.go @@ -0,0 +1,111 @@ +package uefi + +const ( + uintnSize = 32 << (^uintptr(0) >> 63) + 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 = map[EFI_STATUS]*Error{} + +var ( + ErrLoadError = newError(EFI_LOAD_ERROR, "image failed to load") + ErrInvalidParameter = newError(EFI_INVALID_PARAMETER, "a parameter was incorrect") + ErrUnsupported = newError(EFI_UNSUPPORTED, "operation not supported") + ErrBadBufferSize = newError(EFI_BAD_BUFFER_SIZE, "buffer size incorrect for request") + ErrBufferTooSmall = newError(EFI_BUFFER_TOO_SMALL, "buffer too small; size returned in parameter") + ErrNotReady = newError(EFI_NOT_READY, "no data pending") + ErrDeviceError = newError(EFI_DEVICE_ERROR, "physical device reported an error") + ErrWriteProtected = newError(EFI_WRITE_PROTECTED, "device is write-protected") + ErrOutOfResources = newError(EFI_OUT_OF_RESOURCES, "out of resources") + ErrVolumeCorrupted = newError(EFI_VOLUME_CORRUPTED, "filesystem inconsistency detected") + ErrVolumeFull = newError(EFI_VOLUME_FULL, "no more space on filesystem") + ErrNoMedia = newError(EFI_NO_MEDIA, "device contains no medium") + ErrMediaChanged = newError(EFI_MEDIA_CHANGED, "medium changed since last access") + ErrNotFound = newError(EFI_NOT_FOUND, "item not found") + ErrAccessDenied = newError(EFI_ACCESS_DENIED, "access denied") + ErrNoResponse = newError(EFI_NO_RESPONSE, "server not found or no response") + ErrNoMapping = newError(EFI_NO_MAPPING, "no device mapping exists") + ErrTimeout = newError(EFI_TIMEOUT, "timeout expired") + ErrNotStarted = newError(EFI_NOT_STARTED, "protocol not started") + ErrAlreadyStarted = newError(EFI_ALREADY_STARTED, "protocol already started") + ErrAborted = newError(EFI_ABORTED, "operation aborted") + ErrICMPError = newError(EFI_ICMP_ERROR, "ICMP error during network operation") + ErrTFTPError = newError(EFI_TFTP_ERROR, "TFTP error during network operation") + ErrProtocolError = newError(EFI_PROTOCOL_ERROR, "protocol error during network operation") + ErrIncompatibleVersion = newError(EFI_INCOMPATIBLE_VERSION, "requested version incompatible") + ErrSecurityViolation = newError(EFI_SECURITY_VIOLATION, "security violation") + ErrCRCError = newError(EFI_CRC_ERROR, "CRC error detected") + ErrEndOfMedia = newError(EFI_END_OF_MEDIA, "beginning or end of media reached") + ErrEndOfFile = newError(EFI_END_OF_FILE, "end of file reached") + ErrInvalidLanguage = newError(EFI_INVALID_LANGUAGE, "invalid language specified") + ErrCompromisedData = newError(EFI_COMPROMISED_DATA, "data security status unknown or compromised") + ErrIPAddressConflict = newError(EFI_IP_ADDRESS_CONFLICT, "IP address conflict detected") + ErrHTTPError = newError(EFI_HTTP_ERROR, "HTTP error during network operation") +) + +type Error struct { + code EFI_STATUS + msg string +} + +func newError(code EFI_STATUS, msg string) *Error { + err := &Error{code: code, msg: msg} + errMap[code] = err + return err +} + +func (e *Error) Error() string { + return e.msg +} + +func (e *Error) Status() EFI_STATUS { + return e.code +} + +func StatusError(status EFI_STATUS) *Error { + if status == EFI_SUCCESS { + return nil + } + err, ok := errMap[status] + if !ok { + return newError(status, "unknown EFI error") + } + return err +} diff --git a/src/device/uefi/simple_text_out.go b/src/device/uefi/simple_text_out.go new file mode 100644 index 0000000000..72e664475d --- /dev/null +++ b/src/device/uefi/simple_text_out.go @@ -0,0 +1,74 @@ +package uefi + +import "unsafe" + +func booleanArg(v BOOLEAN) uintptr { + if v { + return 1 + } + return 0 +} + +type EFI_SIMPLE_TEXT_OUTPUT_MODE struct { + MaxMode int32 + Mode int32 + Attribute int32 + CursorColumn int32 + CursorRow int32 + CursorVisible BOOLEAN +} + +type EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL struct { + reset uintptr + outputString uintptr + testString uintptr + queryMode uintptr + setMode uintptr + setAttribute uintptr + clearScreen uintptr + setCursorPosition uintptr + enableCursor uintptr + Mode *EFI_SIMPLE_TEXT_OUTPUT_MODE +} + +func (p *EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL) Reset(extendedVerification BOOLEAN) EFI_STATUS { + return UefiCall2(p.reset, uintptr(unsafe.Pointer(p)), booleanArg(extendedVerification)) +} + +func (p *EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL) OutputString(s *CHAR16) EFI_STATUS { + return UefiCall2(p.outputString, uintptr(unsafe.Pointer(p)), uintptr(unsafe.Pointer(s))) +} + +func (p *EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL) TestString(s *CHAR16) EFI_STATUS { + return UefiCall2(p.testString, uintptr(unsafe.Pointer(p)), uintptr(unsafe.Pointer(s))) +} + +func (p *EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL) QueryMode(modeNumber UINTN, columns *UINTN, rows *UINTN) EFI_STATUS { + return UefiCall4( + p.queryMode, + uintptr(unsafe.Pointer(p)), + uintptr(modeNumber), + uintptr(unsafe.Pointer(columns)), + uintptr(unsafe.Pointer(rows)), + ) +} + +func (p *EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL) SetMode(modeNumber UINTN) EFI_STATUS { + return UefiCall2(p.setMode, uintptr(unsafe.Pointer(p)), uintptr(modeNumber)) +} + +func (p *EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL) SetAttribute(attribute UINTN) EFI_STATUS { + return UefiCall2(p.setAttribute, uintptr(unsafe.Pointer(p)), uintptr(attribute)) +} + +func (p *EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL) ClearScreen() EFI_STATUS { + return UefiCall1(p.clearScreen, uintptr(unsafe.Pointer(p))) +} + +func (p *EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL) SetCursorPosition(column UINTN, row UINTN) EFI_STATUS { + return UefiCall3(p.setCursorPosition, uintptr(unsafe.Pointer(p)), uintptr(column), uintptr(row)) +} + +func (p *EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL) EnableCursor(visible BOOLEAN) EFI_STATUS { + return UefiCall2(p.enableCursor, uintptr(unsafe.Pointer(p)), booleanArg(visible)) +} diff --git a/src/device/uefi/tables.go b/src/device/uefi/tables.go new file mode 100644 index 0000000000..230cbc8005 --- /dev/null +++ b/src/device/uefi/tables.go @@ -0,0 +1,123 @@ +package uefi + +import "unsafe" + +type EFI_RUNTIME_SERVICES struct { + Hdr EFI_TABLE_HEADER + getTime uintptr + setTime uintptr + getWakeupTime uintptr + setWakeupTime uintptr + setVirtualAddressMap uintptr + convertPointer uintptr + getVariable uintptr + getNextVariableName uintptr + setVariable uintptr + getNextHighMonoCount uintptr + resetSystem uintptr + updateCapsule uintptr + queryCapsuleCaps uintptr + queryVariableInfo uintptr +} + +type EFI_BOOT_SERVICES struct { + Hdr EFI_TABLE_HEADER + raiseTPL uintptr + restoreTPL uintptr + allocatePages uintptr + freePages uintptr + getMemoryMap uintptr + allocatePool uintptr + freePool uintptr + createEvent uintptr + setTimer uintptr + waitForEvent uintptr + signalEvent uintptr + closeEvent uintptr + checkEvent uintptr + installProtocolInterface uintptr + reinstallProtocolIFace uintptr + uninstallProtocolIFace uintptr + handleProtocol uintptr + reserved *VOID + registerProtocolNotify uintptr + locateHandle uintptr + locateDevicePath uintptr + installConfigurationTable uintptr + loadImage uintptr + startImage uintptr + exit uintptr + unloadImage uintptr + exitBootServices uintptr + getNextMonotonicCount uintptr + stall uintptr + setWatchdogTimer uintptr + connectController uintptr + disconnectController uintptr + openProtocol uintptr + closeProtocol uintptr + openProtocolInformation uintptr + protocolsPerHandle uintptr + locateHandleBuffer uintptr + locateProtocol uintptr +} + +func (p *EFI_BOOT_SERVICES) AllocatePages(typ EFI_ALLOCATE_TYPE, memoryType EFI_MEMORY_TYPE, pages UINTN, memory *EFI_PHYSICAL_ADDRESS) EFI_STATUS { + return UefiCall4(p.allocatePages, uintptr(typ), uintptr(memoryType), uintptr(pages), uintptr(unsafe.Pointer(memory))) +} + +func (p *EFI_BOOT_SERVICES) FreePages(memory EFI_PHYSICAL_ADDRESS, pages UINTN) EFI_STATUS { + return UefiCall2(p.freePages, uintptr(memory), uintptr(pages)) +} + +func (p *EFI_BOOT_SERVICES) CreateEvent(typ EVENT_TYPE, notifyTPL EFI_TPL, notifyFunction unsafe.Pointer, notifyContext unsafe.Pointer, event *EFI_EVENT) EFI_STATUS { + return UefiCall5(p.createEvent, uintptr(typ), uintptr(notifyTPL), uintptr(notifyFunction), uintptr(notifyContext), uintptr(unsafe.Pointer(event))) +} + +func (p *EFI_BOOT_SERVICES) SetTimer(event EFI_EVENT, typ EFI_TIMER_DELAY, triggerTime uint64) EFI_STATUS { + return UefiCall3(p.setTimer, uintptr(event), uintptr(typ), uintptr(triggerTime)) +} + +func (p *EFI_BOOT_SERVICES) WaitForEvent(numberOfEvents UINTN, event *EFI_EVENT, index *UINTN) EFI_STATUS { + return UefiCall3(p.waitForEvent, uintptr(numberOfEvents), uintptr(unsafe.Pointer(event)), uintptr(unsafe.Pointer(index))) +} + +func (p *EFI_BOOT_SERVICES) CloseEvent(event EFI_EVENT) EFI_STATUS { + return UefiCall1(p.closeEvent, uintptr(event)) +} + +func (p *EFI_BOOT_SERVICES) CheckEvent(event EFI_EVENT) EFI_STATUS { + return UefiCall1(p.checkEvent, uintptr(event)) +} + +func (p *EFI_BOOT_SERVICES) HandleProtocol(handle EFI_HANDLE, protocol *EFI_GUID, iface unsafe.Pointer) EFI_STATUS { + return UefiCall3(p.handleProtocol, uintptr(handle), uintptr(unsafe.Pointer(protocol)), uintptr(iface)) +} + +func (p *EFI_BOOT_SERVICES) LocateProtocol(protocol *EFI_GUID, registration *VOID, iface unsafe.Pointer) EFI_STATUS { + return UefiCall3(p.locateProtocol, uintptr(unsafe.Pointer(protocol)), uintptr(unsafe.Pointer(registration)), uintptr(iface)) +} + +func (p *EFI_BOOT_SERVICES) Exit(imageHandle EFI_HANDLE, exitStatus EFI_STATUS, exitDataSize UINTN, exitData *CHAR16) EFI_STATUS { + return UefiCall4(p.exit, uintptr(imageHandle), uintptr(exitStatus), uintptr(exitDataSize), uintptr(unsafe.Pointer(exitData))) +} + +func (p *EFI_BOOT_SERVICES) SetWatchdogTimer(timeout UINTN, watchdogCode uint64, dataSize UINTN, watchdogData *CHAR16) EFI_STATUS { + return UefiCall4(p.setWatchdogTimer, uintptr(timeout), uintptr(watchdogCode), uintptr(dataSize), uintptr(unsafe.Pointer(watchdogData))) +} + +type EFI_SYSTEM_TABLE struct { + Hdr EFI_TABLE_HEADER + FirmwareVendor *CHAR16 + FirmwareRevision uint32 + ConsoleInHandle EFI_HANDLE + ConIn *VOID + ConsoleOutHandle EFI_HANDLE + ConOut *EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL + StandardErrorHandle EFI_HANDLE + StdErr *EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL + RuntimeServices *EFI_RUNTIME_SERVICES + BootServices *EFI_BOOT_SERVICES + NumberOfTableEntries UINTN + ConfigurationTable *VOID +} diff --git a/src/device/uefi/text_output.go b/src/device/uefi/text_output.go new file mode 100644 index 0000000000..7bc4ffeebb --- /dev/null +++ b/src/device/uefi/text_output.go @@ -0,0 +1,41 @@ +package uefi + +import "errors" + +var errNilTextOutputProtocol = errors.New("uefi: nil simple text output protocol") + +type TextOutput struct { + proto *EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL +} + +func NewTextOutput(proto *EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL) *TextOutput { + return &TextOutput{proto: proto} +} + +func ConsoleOut() *TextOutput { + return NewTextOutput(ST().ConOut) +} + +func StandardError() *TextOutput { + return NewTextOutput(ST().StdErr) +} + +func (w *TextOutput) Write(p []byte) (int, error) { + if w == nil || w.proto == nil { + return 0, errNilTextOutputProtocol + } + if len(p) == 0 { + return 0, nil + } + + buf := StringToCHAR16Z(string(p)) + status := w.proto.OutputString(&buf[0]) + if status != EFI_SUCCESS { + return 0, StatusError(status) + } + return len(p), nil +} + +func (w *TextOutput) WriteString(s string) (int, error) { + return w.Write([]byte(s)) +} diff --git a/src/device/uefi/util.go b/src/device/uefi/util.go new file mode 100644 index 0000000000..a37672a8ec --- /dev/null +++ b/src/device/uefi/util.go @@ -0,0 +1,29 @@ +//go:build uefi + +package uefi + +import "unsafe" + +var systemTable *EFI_SYSTEM_TABLE +var imageHandle uintptr + +//go:nobounds +func Init(argImageHandle uintptr, argSystemTable uintptr) { + systemTable = (*EFI_SYSTEM_TABLE)(unsafe.Pointer(argSystemTable)) + imageHandle = argImageHandle +} + +func ST() *EFI_SYSTEM_TABLE { + return systemTable +} + +func BS() *EFI_BOOT_SERVICES { + if systemTable == nil { + return nil + } + return systemTable.BootServices +} + +func GetImageHandle() EFI_HANDLE { + return EFI_HANDLE(imageHandle) +}