aboutsummaryrefslogtreecommitdiff
path: root/arch/riscv64/conf/init.c
blob: 01e219e6a9bd4a37b5e9708b8dcb3fa5fddefcf3 (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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
/* This file is allowed to be undocumented as it is only temporarily in this
 * tree. At some point in the (hopefully) near future, I intend to move the
 * kernel code and initrd generation stuff into separate repositories, to make
 * things easier for myself. For now though, this is good enough.
 */

/* compile with riscv64-unknown-elf-gcc -ffreestanding -nostdlib */
/* create initrd with echo init | cpio -H newc -o > initrd */
#include <stdint.h>
#include "../../../include/apos/syscalls.h"

#define ecall() do { asm volatile ("ecall"                               \
		                   :                                     \
		                   :                                     \
		                   : "a0", "a1", "a2", "a3", "a4", "a5", \
		                   "memory");                            \
} while (0)

static void sys_noop()
{
	long register a0 asm ("a0") = SYS_NOOP;
	ecall();
}

static void sys_putch(char c)
{
	long register a0 asm ("a0") = SYS_PUTCH;
	long register a1 asm ("a1") = c;
	ecall();
}

static uint64_t sys_timebase()
{
	long register a0 asm ("a0") = SYS_TIMEBASE;
	long register a1 asm ("a1") = 0;
	long register a2 asm ("a2") = 0;
	ecall();
#if defined(_LP64)
	return a1;
#else
	uint64_t t = a1;
	t <<= 32;
	return t + a2;
#endif
}

static uint64_t sys_ticks()
{
	long register a0 asm ("a0") = SYS_TICKS;
	long register a1 asm ("a1") = 0;
	long register a2 asm ("a2") = 0;
	ecall();
#if defined(_LP64)
	return a1;
#else
	uint64_t t = a1;
	t <<= 32;
	return t + a2;
#endif
}

static void puts(const char *s)
{
	while (*s)
		sys_putch(*s++);
}

static void _print_number(uint64_t v)
{
	if (v == 0)
		return;

	_print_number(v / 10);
	sys_putch((v % 10) + '0');
}

static void print_number(uint64_t v)
{
	if (v == 0) {
		sys_putch('0');
		return;
	}

	_print_number(v);
}

static void print_value(const char *s, uint64_t v)
{
	puts(s);
	puts(": ");

	print_number(v);
	sys_putch('\n');
}

static uint64_t sys_fork()
{
	long register a0 asm ("a0") = SYS_FORK;
	long register a1 asm ("a1") = 0;
	ecall();
	if (a0 != 0)
		print_value("fork() failed with error ", a0);
	return a1;
}

static uint64_t sys_swap(long tid)
{
	long register a0 asm ("a0") = SYS_SWAP;
	long register a1 asm ("a1") = tid;
	ecall();
	if (a0 != 0)
		print_value("swap() failed with error ", a0);
	return a0;
}

static void sys_ipc_server(void *f)
{
	long register a0 asm ("a0") = SYS_IPC_SERVER;
	long register a1 asm ("a1") = (long)f;
	ecall();
	if (a0 != 0)
		print_value("ipc_server() failed with error ", a0);
}

static uint64_t sys_ipc_req(long tid, long *d0, long *d1, long *d2, long *d3)
{
	long register a0 asm ("a0") = SYS_IPC_REQ;
	long register a1 asm ("a1") = tid;
	long register a2 asm ("a2") = *d0;
	long register a3 asm ("a3") = *d1;
	long register a4 asm ("a4") = *d2;
	long register a5 asm ("a5") = *d3;
	ecall();
	if (a0)
		print_value("ipc_req() failed with error ", a0);

	*d0 = a2;
	*d1 = a3;
	*d2 = a4;
	*d3 = a5;
	return a0;
}

static void sys_ipc_resp(long d0, long d1, long d2, long d3)
{
	long register a0 asm ("a0") = SYS_IPC_RESP;
	long register a1 asm ("a1") = d0;
	long register a2 asm ("a2") = d1;
	long register a3 asm ("a3") = d2;
	long register a4 asm ("a4") = d3;
	ecall();
}

static void sys_poweroff(long type)
{
	long register a0 asm ("a0") = SYS_POWEROFF;
	long register a1 asm ("a1") = type;
	ecall();
}

void callback(long status, long tid, long d0, long d1, long d2, long d3)
{
	sys_ipc_resp(d0, d1, d2, d3);
}

#define CSR_TIME "0xc01"
#define csr_read(csr, \
	         res) __asm__ volatile ("csrr %0, " csr : "=r" (res) :: "memory")

void _start()
{
	sys_noop();
	puts("Hello, world!\n");

	uint64_t second = sys_timebase();
	print_value("Timebase", second);

	uint64_t n = 0, i, start;
	start = i = sys_ticks();
	print_value("Start ticks", start);

	while (i < start + second) {
		i = sys_ticks();
		n++;
	}

	print_value("End ticks", i);
	print_value("Syscalls per second", n);

	puts("Setting callback...");
	sys_ipc_server(callback);

	puts("Starting fork():\n");
	long pid = sys_fork();
	if (pid != 0) {
		print_value("Child pid", pid);
		puts("Swapping to child...\n");
		while(1) sys_swap(pid);
	}

	puts("Hello from child!\n");

	puts("Doing swaps...\n");
	csr_read(CSR_TIME, i);
	start = i; n = 0;
	while (i < start + second) {
		sys_swap(1);
		csr_read(CSR_TIME, i);
		n++;
	}

	print_value("Swaps (both ways) per second", n);

	puts("Doing ipc requests...\n");
	long d0, d1, d2, d3;
	csr_read(CSR_TIME, i);
	start = i; n = 0;
	while (i < start + second) {
		sys_ipc_req(1, &d0, &d1, &d2, &d3);
		csr_read(CSR_TIME, i);
		n++;
	}

	print_value("IPC requests per second", n);

	sys_poweroff(0);
}