diff --git a/src/device/uefi/runtime.go b/src/device/uefi/runtime.go new file mode 100644 index 000000000..532b02abf --- /dev/null +++ b/src/device/uefi/runtime.go @@ -0,0 +1,21 @@ +package uefi + +import _ "unsafe" + +//go:linkname gosched runtime.Gosched +func gosched() + +// WaitForEvent blocks while yielding to the TinyGo scheduler so other +// goroutines can continue to run. +func WaitForEvent(event EFI_EVENT) EFI_STATUS { + for { + status := BS().CheckEvent(event) + if status == EFI_SUCCESS { + return EFI_SUCCESS + } + if status != EFI_NOT_READY { + return status + } + gosched() + } +} diff --git a/src/device/uefi/tables.go b/src/device/uefi/tables.go index 230cbc800..8eb2bf825 100644 --- a/src/device/uefi/tables.go +++ b/src/device/uefi/tables.go @@ -20,6 +20,10 @@ type EFI_RUNTIME_SERVICES struct { queryVariableInfo uintptr } +func (p *EFI_RUNTIME_SERVICES) GetTime(time *EFI_TIME, capabilities *EFI_TIME_CAPABILITIES) EFI_STATUS { + return UefiCall2(p.getTime, uintptr(unsafe.Pointer(time)), uintptr(unsafe.Pointer(capabilities))) +} + type EFI_BOOT_SERVICES struct { Hdr EFI_TABLE_HEADER raiseTPL uintptr @@ -82,6 +86,10 @@ func (p *EFI_BOOT_SERVICES) WaitForEvent(numberOfEvents UINTN, event *EFI_EVENT, return UefiCall3(p.waitForEvent, uintptr(numberOfEvents), uintptr(unsafe.Pointer(event)), uintptr(unsafe.Pointer(index))) } +func (p *EFI_BOOT_SERVICES) SignalEvent(event EFI_EVENT) EFI_STATUS { + return UefiCall1(p.signalEvent, uintptr(event)) +} + func (p *EFI_BOOT_SERVICES) CloseEvent(event EFI_EVENT) EFI_STATUS { return UefiCall1(p.closeEvent, uintptr(event)) } diff --git a/src/device/uefi/time.go b/src/device/uefi/time.go new file mode 100644 index 000000000..77d13260c --- /dev/null +++ b/src/device/uefi/time.go @@ -0,0 +1,104 @@ +package uefi + +type EFI_TIME struct { + Year uint16 + Month byte + Day byte + Hour byte + Minute byte + Second byte + Pad1 byte + Nanosecond uint32 + TimeZone int16 + Daylight byte + Pad2 byte +} + +type EFI_TIME_CAPABILITIES struct { + Resolution uint32 + Accuracy uint32 + SetsToZero BOOLEAN +} + +func GetTime() (EFI_TIME, EFI_STATUS) { + var time EFI_TIME + status := ST().RuntimeServices.GetTime(&time, nil) + return time, status +} + +func (t *EFI_TIME) GetEpoch() (sec int64, nsec int32) { + if t.TimeZone != 0x07FF { // EFI_UNSPECIFIED_TIMEZONE + sec -= int64(t.TimeZone) * 60 + } + year := int(t.Year) + month := int(t.Month) + + d := daysSinceEpoch(year) + d += uint64(daysBefore[month-1]) + if isLeap(year) && month > 2 { + d++ + } + d += uint64(t.Day - 1) + + abs := d * secondsPerDay + abs += uint64(uint64(t.Hour)*uint64(secondsPerHour) + uint64(t.Minute)*uint64(secondsPerMinute) + uint64(t.Second)) + + sec = int64(abs) + (absoluteToInternal + internalToUnix) + nsec = int32(t.Nanosecond) + return +} + +const ( + secondsPerMinute = 60 + secondsPerHour = 60 * secondsPerMinute + secondsPerDay = 24 * secondsPerHour + daysPer400Years = 365*400 + 97 + daysPer100Years = 365*100 + 24 + daysPer4Years = 365*4 + 1 + + absoluteZeroYear = -292277022399 + internalYear = 1 + + absoluteToInternal int64 = (absoluteZeroYear - internalYear) * 365.2425 * secondsPerDay + unixToInternal int64 = (1969*365 + 1969/4 - 1969/100 + 1969/400) * secondsPerDay + internalToUnix int64 = -unixToInternal +) + +var daysBefore = [...]int32{ + 0, + 31, + 31 + 28, + 31 + 28 + 31, + 31 + 28 + 31 + 30, + 31 + 28 + 31 + 30 + 31, + 31 + 28 + 31 + 30 + 31 + 30, + 31 + 28 + 31 + 30 + 31 + 30 + 31, + 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31, + 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30, + 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31, + 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30, + 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 + 31, +} + +func daysSinceEpoch(year int) uint64 { + y := uint64(int64(year) - absoluteZeroYear) + + n := y / 400 + y -= 400 * n + d := daysPer400Years * n + + n = y / 100 + y -= 100 * n + d += daysPer100Years * n + + n = y / 4 + y -= 4 * n + d += daysPer4Years * n + + d += 365 * y + return d +} + +func isLeap(year int) bool { + return year%4 == 0 && (year%100 != 0 || year%400 == 0) +} diff --git a/src/machine/machine_uefi.go b/src/machine/machine_uefi.go index 94659d2b3..9d655f0f4 100644 --- a/src/machine/machine_uefi.go +++ b/src/machine/machine_uefi.go @@ -9,8 +9,10 @@ import ( const deviceName = "UEFI" type ( - EFI_STATUS = deviceuefi.EFI_STATUS - TextOutput = deviceuefi.TextOutput + EFI_STATUS = deviceuefi.EFI_STATUS + EFI_TIME = deviceuefi.EFI_TIME + EFI_TIME_CAPABILITIES = deviceuefi.EFI_TIME_CAPABILITIES + TextOutput = deviceuefi.TextOutput Error = deviceuefi.Error ) @@ -88,6 +90,10 @@ var ( ErrHTTPError = deviceuefi.ErrHTTPError ) +func GetTime() (EFI_TIME, EFI_STATUS) { + return deviceuefi.GetTime() +} + func ConsoleOut() *TextOutput { return deviceuefi.ConsoleOut() } diff --git a/src/runtime/runtime_uefi.go b/src/runtime/runtime_uefi.go index c97ab748e..9104adb82 100644 --- a/src/runtime/runtime_uefi.go +++ b/src/runtime/runtime_uefi.go @@ -56,9 +56,8 @@ func sleepTicks(d timeUnit) { func putchar(c byte) { if c == '\n' { - buf := [4]uefi.CHAR16{'\r', 0, '\n', 0} + buf := [2]uefi.CHAR16{uefi.CHAR16('\r'), 0} uefi.ST().ConOut.OutputString(&buf[0]) - return } buf := [2]uefi.CHAR16{uefi.CHAR16(c), 0} uefi.ST().ConOut.OutputString(&buf[0]) @@ -103,6 +102,15 @@ func growHeap() bool { return false } +func init() { + mono := nanotime() + efiTime, status := uefi.GetTime() + if status == uefi.EFI_SUCCESS { + sec, nsec := efiTime.GetEpoch() + timeOffset.Store(sec*1000000000 + int64(nsec) - mono) + } +} + func SetWaitForEvents(f func()) { waitForEventsFunction = f }