tinygo/src/runtime/runtime_nintendoswitch.go
Ayke van Laethem fcd88356db avr: fix time.Sleep() in init code
In the early days of TinyGo, the idea of `postinit` was to enable
interrupts only after initializers have run. Which kind of makes
sense... except that `time.Sleep` is allowed in init code and
`time.Sleep` requires interrupts to be enabled. Therefore, interrupts
must be enabled while initializers are being run.

This commit simply moves the enabling of interrupts to a point right
before running package initializers. It also removes `runtime.postinit`,
which is not necessary anymore (and was only used on AVR).
2022-01-02 19:41:44 +01:00

278 строки
6,4 КиБ
Go

// +build nintendoswitch
package runtime
import "unsafe"
type timeUnit int64
const (
// Handles
infoTypeTotalMemorySize = 6 // Total amount of memory available for process.
infoTypeUsedMemorySize = 7 // Amount of memory currently used by process.
currentProcessHandle = 0xFFFF8001 // Pseudo handle for the current process.
// Types of config Entry
envEntryTypeEndOfList = 0 // Entry list terminator.
envEntryTypeMainThreadHandle = 1 // Provides the handle to the main thread.
envEntryTypeOverrideHeap = 3 // Provides heap override information.
// Default heap size allocated by libnx
defaultHeapSize = 0x2000000 * 16
debugInit = false
)
//go:extern _saved_return_address
var savedReturnAddress uintptr
//export __stack_top
var stackTop uintptr
//go:extern _context
var context uintptr
//go:extern _main_thread
var mainThread uintptr
var (
heapStart = uintptr(0)
heapEnd = uintptr(0)
usedRam = uint64(0)
totalRam = uint64(0)
totalHeap = uint64(0)
)
func preinit() {
// Unsafe to use heap here
setupEnv()
setupHeap()
}
// Entry point for Go. Initialize all packages and call main.main().
//export main
func main() {
preinit()
run()
// Call exit to correctly finish the program
// Without this, the application crashes at start, not sure why
for {
exit(0)
}
}
// sleepTicks sleeps for the specified system ticks
func sleepTicks(d timeUnit) {
svcSleepThread(uint64(ticksToNanoseconds(d)))
}
// armTicksToNs converts cpu ticks to nanoseconds
// Nintendo Switch CPU ticks has a fixed rate at 19200000
// It is basically 52 ns per tick
// The formula 625 / 12 is equivalent to 1e9 / 19200000
func ticksToNanoseconds(tick timeUnit) int64 {
return int64(tick * 625 / 12)
}
func nanosecondsToTicks(ns int64) timeUnit {
return timeUnit(12 * ns / 625)
}
func ticks() timeUnit {
return timeUnit(ticksToNanoseconds(timeUnit(getArmSystemTick())))
}
var stdoutBuffer = make([]byte, 120)
var position = 0
func putchar(c byte) {
if c == '\n' || position >= len(stdoutBuffer) {
svcOutputDebugString(&stdoutBuffer[0], uint64(position))
position = 0
return
}
stdoutBuffer[position] = c
position++
}
func abort() {
for {
exit(1)
}
}
//export write
func write(fd int32, buf *byte, count int) int {
// TODO: Proper handling write
for i := 0; i < count; i++ {
putchar(*buf)
buf = (*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(buf)) + 1))
}
return count
}
// exit checks if a savedReturnAddress were provided by the launcher
// if so, calls the nxExit which restores the stack and returns to launcher
// otherwise just calls systemcall exit
func exit(code int) {
if savedReturnAddress == 0 {
svcExitProcess(code)
return
}
nxExit(code, stackTop, savedReturnAddress)
}
type configEntry struct {
Key uint32
Flags uint32
Value [2]uint64
}
func setupEnv() {
if debugInit {
println("Saved Return Address:", savedReturnAddress)
println("Context:", context)
println("Main Thread Handle:", mainThread)
}
// See https://switchbrew.org/w/index.php?title=Homebrew_ABI
// Here we parse only the required configs for initializing
if context != 0 {
ptr := context
entry := (*configEntry)(unsafe.Pointer(ptr))
for entry.Key != envEntryTypeEndOfList {
switch entry.Key {
case envEntryTypeOverrideHeap:
if debugInit {
println("Got heap override")
}
heapStart = uintptr(entry.Value[0])
heapEnd = heapStart + uintptr(entry.Value[1])
case envEntryTypeMainThreadHandle:
mainThread = uintptr(entry.Value[0])
default:
if entry.Flags&1 > 0 {
// Mandatory but not parsed
runtimePanic("mandatory config entry not parsed")
}
}
ptr += unsafe.Sizeof(configEntry{})
entry = (*configEntry)(unsafe.Pointer(ptr))
}
}
// Fetch used / total RAM for allocating HEAP
svcGetInfo(&totalRam, infoTypeTotalMemorySize, currentProcessHandle, 0)
svcGetInfo(&usedRam, infoTypeUsedMemorySize, currentProcessHandle, 0)
}
func setupHeap() {
if heapStart != 0 {
if debugInit {
print("Heap already overrided by hblauncher")
}
// Already overrided
return
}
if debugInit {
print("No heap override. Using normal initialization")
}
size := uint32(defaultHeapSize)
if totalRam > usedRam+0x200000 {
// Get maximum possible heap
size = uint32(totalRam-usedRam-0x200000) & ^uint32(0x1FFFFF)
}
if size < defaultHeapSize {
size = defaultHeapSize
}
if debugInit {
println("Trying to allocate", size, "bytes of heap")
}
svcSetHeapSize(&heapStart, uint64(size))
if heapStart == 0 {
runtimePanic("failed to allocate heap")
}
totalHeap = uint64(size)
heapEnd = heapStart + uintptr(size)
if debugInit {
println("Heap Start", heapStart)
println("Heap End ", heapEnd)
println("Total Heap", totalHeap)
}
}
// growHeap tries to grow the heap size. It returns true if it succeeds, false
// otherwise.
func growHeap() bool {
// Growing the heap is unimplemented.
return false
}
// getHeapBase returns the start address of the heap
// this is externally linked by gonx
func getHeapBase() uintptr {
return heapStart
}
// getHeapEnd returns the end address of the heap
// this is externally linked by gonx
func getHeapEnd() uintptr {
return heapEnd
}
// getContextPtr returns the hblauncher context
// this is externally linked by gonx
func getContextPtr() uintptr {
return context
}
// getMainThreadHandle returns the main thread handler if any
// this is externally linked by gonx
func getMainThreadHandle() uintptr {
return mainThread
}
//export armGetSystemTick
func getArmSystemTick() int64
// nxExit exits the program to homebrew launcher
//export __nx_exit
func nxExit(code int, stackTop uintptr, exitFunction uintptr)
// Horizon System Calls
// svcSetHeapSize Set the process heap to a given size. It can both extend and shrink the heap.
// svc 0x01
//export svcSetHeapSize
func svcSetHeapSize(addr *uintptr, length uint64) uint64
// svcExitProcess Exits the current process.
// svc 0x07
//export svcExitProcess
func svcExitProcess(code int)
// svcSleepThread Sleeps the current thread for the specified amount of time.
// svc 0x0B
//export svcSleepThread
func svcSleepThread(nanos uint64)
// svcOutputDebugString Outputs debug text, if used during debugging.
// svc 0x27
//export svcOutputDebugString
func svcOutputDebugString(str *uint8, size uint64) uint64
// svcGetInfo Retrieves information about the system, or a certain kernel object.
// svc 0x29
//export svcGetInfo
func svcGetInfo(output *uint64, id0 uint32, handle uint32, id1 uint64) uint64