aboutsummaryrefslogtreecommitdiff
path: root/libpsn00b/libc/start.c
blob: fe6dedd0d9219ec43079f84248611de124a9ba6b (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
/*
 * PSn00bSDK startup code
 * (C) 2021 Lameguy64, spicyjpeg - MPL licensed
 */

#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <stddef.h>

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

/* BIOS argv parser (unused, interferes with child executable argv passing) */

int			__argc;
const char	**__argv;

#if 0

#define ARGC_MAX 16

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

static void _parse_kernel_args(void) {
	// 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;
	}
}

#endif

/* Main */

// 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 int main(int 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.
int _start_inner(int argc, const char **argv) {
	__builtin_memset(__bss_start, 0, (void *) _end - (void *) __bss_start);

	// Initialize the heap and place it after the executable, assuming 2 MB of
	// RAM. Note that InitHeap() can be called again in main().
	InitHeap((void *) _end + 4, (void *) 0x801ffff8 - (void *) _end);

	//_parse_kernel_args();
	__argc = argc;
	__argv = argv;

	// 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 (int i = (int) __CTOR_LIST__[0]; i >= 1; i--)
		__CTOR_LIST__[i]();

	int value = main(argc, argv);

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

	//*KERNEL_RETURN_VALUE = value;
	return value;
}