Skip to content

Commit 387bca8

Browse files
racerxdlaykevl
authored andcommitted
nintendoswitch: Add env parser and removed unused stuff
* Heap allocation based on available ram * Added homebrew launcher parser (for overriden heap) * Removed unused stuff (moved to gonx) * Kept require code at minimum to work in a real device * Moved everything to a single file
1 parent d424b3d commit 387bca8

9 files changed

+300
-122
lines changed

src/runtime/dynamic_arm64.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,15 @@ func dynamicLoader(base uintptr, dyn *dyn64) {
5959
for relasz > 0 && rela != nil {
6060
switch rela.Info {
6161
case rAARCH64_RELATIVE:
62+
if debugLoader {
63+
println("relocating ", uintptr(rela.Addend), " to ", base+uintptr(rela.Addend))
64+
}
6265
ptr := (*uint64)(unsafe.Pointer(base + uintptr(rela.Off)))
6366
*ptr = uint64(base + uintptr(rela.Addend))
67+
default:
68+
if debugLoader {
69+
println("unknown section to load:", rela.Info)
70+
}
6471
}
6572

6673
rptr := uintptr(unsafe.Pointer(rela))

src/runtime/runtime_nintendoswitch.go

Lines changed: 200 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -8,35 +8,67 @@ type timeUnit int64
88

99
const asyncScheduler = false
1010

11+
const (
12+
// Handles
13+
infoTypeTotalMemorySize = 6 // Total amount of memory available for process.
14+
infoTypeUsedMemorySize = 7 // Amount of memory currently used by process.
15+
currentProcessHandle = 0xFFFF8001 // Pseudo handle for the current process.
16+
17+
// Types of config Entry
18+
envEntryTypeEndOfList = 0 // Entry list terminator.
19+
envEntryTypeMainThreadHandle = 1 // Provides the handle to the main thread.
20+
envEntryTypeOverrideHeap = 3 // Provides heap override information.
21+
22+
// Default heap size allocated by libnx
23+
defaultHeapSize = 0x2000000 * 16
24+
25+
debugInit = false
26+
)
27+
28+
//go:extern _saved_return_address
29+
var savedReturnAddress uintptr
30+
31+
//export __stack_top
1132
var stackTop uintptr
1233

34+
//go:extern _context
35+
var context uintptr
36+
37+
//go:extern _main_thread
38+
var mainThread uintptr
39+
40+
var (
41+
heapStart = uintptr(0)
42+
heapEnd = uintptr(0)
43+
usedRam = uint64(0)
44+
totalRam = uint64(0)
45+
totalHeap = uint64(0)
46+
)
47+
1348
func postinit() {}
1449

50+
func preinit() {
51+
// Unsafe to use heap here
52+
setupEnv()
53+
setupHeap()
54+
}
55+
1556
// Entry point for Go. Initialize all packages and call main.main().
1657
//export main
17-
func main() int {
58+
func main() {
1859
preinit()
19-
20-
// Obtain the initial stack pointer right before calling the run() function.
21-
// The run function has been moved to a separate (non-inlined) function so
22-
// that the correct stack pointer is read.
23-
stackTop = getCurrentStackPointer()
24-
runMain()
60+
run()
2561

2662
// Call exit to correctly finish the program
2763
// Without this, the application crashes at start, not sure why
28-
return exit(0)
29-
}
30-
31-
// Must be a separate function to get the correct stack pointer.
32-
//go:noinline
33-
func runMain() {
34-
run()
64+
for {
65+
exit(0)
66+
}
3567
}
3668

3769
// sleepTicks sleeps for the specified system ticks
3870
func sleepTicks(d timeUnit) {
39-
sleepThread(uint64(ticksToNanoseconds(d)))
71+
svcSleepThread(uint64(ticksToNanoseconds(d)))
4072
}
4173

4274
// armTicksToNs converts cpu ticks to nanoseconds
@@ -60,7 +92,7 @@ var position = 0
6092

6193
func putchar(c byte) {
6294
if c == '\n' || position >= len(stdoutBuffer) {
63-
nxOutputString(&stdoutBuffer[0], uint64(position))
95+
svcOutputDebugString(&stdoutBuffer[0], uint64(position))
6496
position = 0
6597
return
6698
}
@@ -85,11 +117,159 @@ func write(fd int32, buf *byte, count int) int {
85117
return count
86118
}
87119

88-
//export sleepThread
89-
func sleepThread(nanos uint64)
120+
// exit checks if a savedReturnAddress were provided by the launcher
121+
// if so, calls the nxExit which restores the stack and returns to launcher
122+
// otherwise just calls systemcall exit
123+
func exit(code int) {
124+
if savedReturnAddress == 0 {
125+
svcExitProcess(code)
126+
return
127+
}
128+
129+
nxExit(code, stackTop, savedReturnAddress)
130+
}
131+
132+
type configEntry struct {
133+
Key uint32
134+
Flags uint32
135+
Value [2]uint64
136+
}
90137

91-
//export exit
92-
func exit(code int) int
138+
func setupEnv() {
139+
if debugInit {
140+
println("Saved Return Address:", savedReturnAddress)
141+
println("Context:", context)
142+
println("Main Thread Handle:", mainThread)
143+
}
144+
145+
// See https://switchbrew.org/w/index.php?title=Homebrew_ABI
146+
// Here we parse only the required configs for initializing
147+
if context != 0 {
148+
ptr := context
149+
entry := (*configEntry)(unsafe.Pointer(ptr))
150+
for entry.Key != envEntryTypeEndOfList {
151+
switch entry.Key {
152+
case envEntryTypeOverrideHeap:
153+
if debugInit {
154+
println("Got heap override")
155+
}
156+
heapStart = uintptr(entry.Value[0])
157+
heapEnd = heapStart + uintptr(entry.Value[1])
158+
case envEntryTypeMainThreadHandle:
159+
mainThread = uintptr(entry.Value[0])
160+
default:
161+
if entry.Flags&1 > 0 {
162+
// Mandatory but not parsed
163+
runtimePanic("mandatory config entry not parsed")
164+
}
165+
}
166+
ptr += unsafe.Sizeof(configEntry{})
167+
entry = (*configEntry)(unsafe.Pointer(ptr))
168+
}
169+
}
170+
// Fetch used / total RAM for allocating HEAP
171+
svcGetInfo(&totalRam, infoTypeTotalMemorySize, currentProcessHandle, 0)
172+
svcGetInfo(&usedRam, infoTypeUsedMemorySize, currentProcessHandle, 0)
173+
}
174+
175+
func setupHeap() {
176+
if heapStart != 0 {
177+
if debugInit {
178+
print("Heap already overrided by hblauncher")
179+
}
180+
// Already overrided
181+
return
182+
}
183+
184+
if debugInit {
185+
print("No heap override. Using normal initialization")
186+
}
187+
188+
size := uint32(defaultHeapSize)
189+
190+
if totalRam > usedRam+0x200000 {
191+
// Get maximum possible heap
192+
size = uint32(totalRam-usedRam-0x200000) & ^uint32(0x1FFFFF)
193+
}
194+
195+
if size < defaultHeapSize {
196+
size = defaultHeapSize
197+
}
198+
199+
if debugInit {
200+
println("Trying to allocate", size, "bytes of heap")
201+
}
202+
203+
svcSetHeapSize(&heapStart, uint64(size))
204+
205+
if heapStart == 0 {
206+
runtimePanic("failed to allocate heap")
207+
}
208+
209+
totalHeap = uint64(size)
210+
211+
heapEnd = heapStart + uintptr(size)
212+
213+
if debugInit {
214+
println("Heap Start", heapStart)
215+
println("Heap End ", heapEnd)
216+
println("Total Heap", totalHeap)
217+
}
218+
}
219+
220+
// getHeapBase returns the start address of the heap
221+
// this is externally linked by gonx
222+
func getHeapBase() uintptr {
223+
return heapStart
224+
}
225+
226+
// getHeapEnd returns the end address of the heap
227+
// this is externally linked by gonx
228+
func getHeapEnd() uintptr {
229+
return heapEnd
230+
}
231+
232+
// getContextPtr returns the hblauncher context
233+
// this is externally linked by gonx
234+
func getContextPtr() uintptr {
235+
return context
236+
}
237+
238+
// getMainThreadHandle returns the main thread handler if any
239+
// this is externally linked by gonx
240+
func getMainThreadHandle() uintptr {
241+
return mainThread
242+
}
93243

94244
//export armGetSystemTick
95245
func getArmSystemTick() int64
246+
247+
// nxExit exits the program to homebrew launcher
248+
//export __nx_exit
249+
func nxExit(code int, stackTop uintptr, exitFunction uintptr)
250+
251+
// Horizon System Calls
252+
// svcSetHeapSize Set the process heap to a given size. It can both extend and shrink the heap.
253+
// svc 0x01
254+
//export svcSetHeapSize
255+
func svcSetHeapSize(addr *uintptr, length uint64) uint64
256+
257+
// svcExitProcess Exits the current process.
258+
// svc 0x07
259+
//export svcExitProcess
260+
func svcExitProcess(code int)
261+
262+
// svcSleepThread Sleeps the current thread for the specified amount of time.
263+
// svc 0x0B
264+
//export svcSleepThread
265+
func svcSleepThread(nanos uint64)
266+
267+
// svcOutputDebugString Outputs debug text, if used during debugging.
268+
// svc 0x27
269+
//export svcOutputDebugString
270+
func svcOutputDebugString(str *uint8, size uint64) uint64
271+
272+
// svcGetInfo Retrieves information about the system, or a certain kernel object.
273+
// svc 0x29
274+
//export svcGetInfo
275+
func svcGetInfo(output *uint64, id0 uint32, handle uint32, id1 uint64) uint64
Lines changed: 33 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,40 @@
1-
.section .text.armGetSystemTick, "ax", %progbits
2-
.global armGetSystemTick
3-
.type armGetSystemTick, %function
4-
.align 2
5-
armGetSystemTick:
1+
// Macro for writing less code
2+
.macro FUNC name
3+
.section .text.\name, "ax", %progbits
4+
.global \name
5+
.type \name, %function
6+
.align 2
7+
\name:
8+
.endm
9+
10+
FUNC armGetSystemTick
611
mrs x0, cntpct_el0
712
ret
813

9-
.section .text.nxOutputString, "ax", %progbits
10-
.global nxOutputString
11-
.type nxOutputString, %function
12-
.align 2
13-
.cfi_startproc
14-
nxOutputString:
15-
svc 0x27
16-
ret
17-
.cfi_endproc
14+
// Horizon System Calls
15+
// https://switchbrew.org/wiki/SVC
16+
FUNC svcSetHeapSize
17+
str x0, [sp, #-16]!
18+
svc 0x1
19+
ldr x2, [sp], #16
20+
str x1, [x2]
21+
ret
1822

19-
.section .text.exit, "ax", %progbits
20-
.global exit
21-
.type exit, %function
22-
.align 2
23-
exit:
24-
svc 0x7
25-
ret
23+
FUNC svcExitProcess
24+
svc 0x7
25+
ret
2626

27-
.section .text.setHeapSize, "ax", %progbits
28-
.global setHeapSize
29-
.type setHeapSize, %function
30-
.align 2
31-
setHeapSize:
32-
str x0, [sp, #-16]!
33-
svc 0x1
34-
ldr x2, [sp], #16
35-
str x1, [x2]
36-
ret
27+
FUNC svcSleepThread
28+
svc 0xB
29+
ret
3730

31+
FUNC svcOutputDebugString
32+
svc 0x27
33+
ret
3834

39-
.section .text.sleepThread, "ax", %progbits
40-
.global sleepThread
41-
.type sleepThread, %function
42-
.align 2
43-
sleepThread:
44-
svc 0xB
45-
ret
35+
FUNC svcGetInfo
36+
str x0, [sp, #-16]!
37+
svc 0x29
38+
ldr x2, [sp], #16
39+
str x1, [x2]
40+
ret

src/runtime/runtime_nintendoswitch_heap.go

Lines changed: 0 additions & 25 deletions
This file was deleted.

src/runtime/runtime_nintendoswitch_noheap.go

Lines changed: 0 additions & 7 deletions
This file was deleted.

0 commit comments

Comments
 (0)