aboutsummaryrefslogtreecommitdiff
path: root/libpsn00b/libc/start.c
blob: c5872dfeae0b6d4e228676dc0f92734158c6c46d (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
/*
 * PSn00bSDK startup code
 * (C) 2021 Lameguy64, spicyjpeg - MPL licensed
 */

#include <sys/types.h>
#include <string.h>
#include <malloc.h>

#define KERNEL_ARG_STRING   ((const char *)   0x80000180)
#define KERNEL_RETURN_VALUE ((volatile int *) 0x8000dffc)

/* Argument parsing */

int32_t    __argc;
const char **__argv;

#define ARGC_MAX 16

static const char *_argv_buffer[ARGC_MAX];
static char       _arg_string_buffer[132];

static void _parse_kernel_args() {
	// Copy the argument string from kernel memory into a private buffer (which
	// won't be cleared or deallocated) and trim it at the first newline.
	memset(_arg_string_buffer, 0, 132);
	strncpy(_arg_string_buffer, KERNEL_ARG_STRING, 128);

	for (char *ptr = _arg_string_buffer; *ptr; ptr++) {
		if ((*ptr == '\r') || (*ptr == '\n')) {
			*ptr = 0;
			break;
		}
	}

	__argv = _argv_buffer;
	for (__argc = 0; __argc < ARGC_MAX; __argc++) {
		const char *ptr;
		if (!__argc)
			ptr = strtok(_arg_string_buffer, " ");
		else
			ptr = strtok(0, " ");

		_argv_buffer[__argc] = ptr;
		if (!ptr)
			break;
	}
}

/* Main */

// How much space at the end of RAM to leave for the stack (instead of using it
// as heap). By default 128 KB are reserved for the stack, but this constant
// can be overridden in main.c (or anywhere else) simply by redeclaring it
// without the weak attribute.
const int32_t __attribute__((weak)) STACK_MAX_SIZE = 0x20000;

// These are defined by the linker script. Note that these are *NOT* pointers,
// they are virtual symbols whose location matches their value. The simplest
// way to turn them into pointers is to declare them as arrays, so here we go.
extern uint8_t __text_start[];
extern uint8_t __bss_start[];
extern uint8_t _end[];
//extern uint8_t _gp[];

extern void (*__CTOR_LIST__[])(void);
extern void (*__DTOR_LIST__[])(void);

extern int32_t main(int32_t argc, const char* argv[]);

// Even though _start() usually takes no arguments, this implementation allows
// parent executables to pass args directly to child executables without having
// to overwrite the arg strings in kernel RAM.
void _start(int32_t override_argc, const char **override_argv) {
	__asm__ volatile("la $gp, _gp;");
	
	// Mem init assembly function (clears BSS and InitHeap to _end which is
	// not possible to do purely in C because the linker complains about
	// relocation truncated to fit: R_MIPS_GPREL16 against `_end'
	// Workaround is to do it in assembly because la pseudo-op doesn't use
	// stupid gp relative addressing
	//_mem_init();

	// Clear BSS 4 bytes at a time. BSS is always aligned to 4 bytes by the
	// linker script.
	for (uint32_t *i = (uint32_t *) __bss_start; i < (uint32_t *) _end; i++)
		*i = 0;

	// Calculate how much RAM is available after the loaded executable and
	// initialize heap accordingly.
	void         *exe_end = _end + 4;
	unsigned int exe_size = (unsigned int) exe_end - (unsigned int) __text_start;
	InitHeap(exe_end, 0x200000 - (exe_size + STACK_MAX_SIZE));

	if (override_argv) {
		__argc = override_argc;
		__argv = override_argv;
	} else {
		_parse_kernel_args();
	}

	// Call the global constructors (if any) to initialize global objects
	// before calling main(). Constructors are put by the linker script in a
	// length-prefixed array in reverse order.
	for (uint32_t i = (uint32_t) __CTOR_LIST__[0]; i >= 1; i--)
		__CTOR_LIST__[i]();

	// Store main()'s return value into the kernel return value area (for child
	// executables).
	*KERNEL_RETURN_VALUE = main(__argc, __argv);

	// Call global destructors (in forward order).
	for (uint32_t i = 0; i < (uint32_t) __DTOR_LIST__[0]; i++)
		__DTOR_LIST__[i + 1]();
}