@@ -8,35 +8,67 @@ type timeUnit int64
88
99const 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
1132var 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+
1348func 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
3870func 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
6193func 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
95245func 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
0 commit comments