@@ -0,0 +1,260 @@
1// alarmtest: tests for sigalarm and sigreturn.
2
3#include "kernel/types.h"
4#include "kernel/stat.h"
5#include "user/user.h"
6
7volatile int count; // calls of periodic
8
9// the first function in this file: in this build it sits at
10// user address 0, so a kernel that treats a handler of 0 as
11// "no handler" never calls it.
12void
13periodic()
14{
15 count++;
16 sigreturn();
17}
18
19volatile int depth, maxdepth, slowcalls;
20
21// a handler that runs for 3 ticks or more, longer than its interval.
22void
23slow()
24{
25 int t0;
26
27 depth++;
28 if (depth > maxdepth)
29 maxdepth = depth;
30 if (depth > 1)
31 printf("alarmtest: slow entered while already running\n"); 34 for (volatile int i = 0; i < 100000; i++)
35 ;
36 slowcalls++;
37 depth--;
38 sigreturn();
39}
40
41// burn user CPU time until *c reaches n or t ticks pass.
42// returns 1 if *c reached n.
43int
44waitfor(volatile int *c, int n, int t)
45{
47
48 while (*c < n) {
50 return 0;
51 for (volatile int i = 0; i < 100000; i++)
52 ;
53 }
54 return 1;
55}
56
57int
58check(char *what, int ok)
59{
60 printf("alarmtest: %s: %s\n", what, ok ? "OK" : "FAIL"); 61 return ok;
62}
63
64// the handler runs every 2 ticks; sigalarm(0, 0) stops it.
65int
66test_called(void)
67{
68 int ok, n;
69
70 printf("alarmtest: periodic is at address %p\n", periodic); 71 count = 0;
72 sigalarm(2, periodic);
73 ok = waitfor(&count, 5, 300);
74 sigalarm(0, 0);
75 n = count;
76 waitfor(&count, n + 1, 10);
77 printf("alarmtest: %d calls; %d more in 10 ticks after sigalarm(0, 0)\n", n, 78 count - n);
79 return check("handler called, then stopped", ok && count == n) +
80 check("sigreturn outside a handler fails", sigreturn() == -1);
81}
82
83// load register r with eight copies of the byte 0xb.
84#define FILL(r, b) "li " #r ", 0x" #b #b #b #b #b #b #b #b "\n"
85// regs[i] = the register (i is its number, in decimal)
86#define SAVE(r, i) "sd " #r ", " #i "*8(a0)\n"
87
88uint64 regs[32];
89
90// fill 26 registers, spin with the counter in a0, save them all.
91void
92fillspin(void)
93{
94 asm volatile(FILL(ra, 01) FILL(t0, 05) FILL(t1, 06) FILL(t2, 07)
95 FILL(s1, 09) FILL(a1, 11) FILL(a2, 12) FILL(a3, 13)
96 FILL(a4, 14) FILL(a5, 15) FILL(a6, 16) FILL(a7, 17)
97 FILL(s2, 18) FILL(s3, 19) FILL(s4, 20) FILL(s5, 21)
98 FILL(s6, 22) FILL(s7, 23) FILL(s8, 24) FILL(s9, 25)
99 FILL(s10, 26) FILL(s11, 27) FILL(t3, 28) FILL(t4, 29) 100 FILL(t5, 30) FILL(t6, 31)
101 "li a0, 20000000\n"
102 "1: addi a0, a0, -1\n"
103 "bgtz a0, 1b\n"
104 "la a0, regs\n"
105 SAVE(ra, 1) SAVE(t0, 5) SAVE(t1, 6) SAVE(t2, 7)
106 SAVE(s1, 9) SAVE(a1, 11) SAVE(a2, 12) SAVE(a3, 13)
107 SAVE(a4, 14) SAVE(a5, 15) SAVE(a6, 16) SAVE(a7, 17)
108 SAVE(s2, 18) SAVE(s3, 19) SAVE(s4, 20) SAVE(s5, 21)
109 SAVE(s6, 22) SAVE(s7, 23) SAVE(s8, 24) SAVE(s9, 25)
110 SAVE(s10, 26) SAVE(s11, 27) SAVE(t3, 28) SAVE(t4, 29) 111 SAVE(t5, 30) SAVE(t6, 31)
112 :
113 :
114 : "ra", "t0", "t1", "t2", "s1", "a0", "a1", "a2", "a3", "a4",
115 "a5", "a6", "a7", "s2", "s3", "s4", "s5", "s6", "s7", "s8",
116 "s9", "s10", "s11", "t3", "t4", "t5", "t6", "memory");
117}
118
119// the registers fillspin checks, by number. FILL writes the number's
120// decimal digits as hex: x11 holds 0x1111111111111111.
121int regno[] = {1, 5, 6, 7, 9, 11, 12, 13, 14, 15, 16, 17, 18,
122 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31};
123
124// the handler interrupts fillspin; every register must survive.
125int
126test_registers(void)
127{
128 int c0, hits = 0, bad = 0, t0 = uptime(); 129
130 count = 0;
131 sigalarm(1, periodic);
132 while (hits < 10 && uptime() - t0 < 300) { 133 c0 = count;
134 fillspin();
135 if (count != c0)
136 hits++;
137 for (int i = 0; i < sizeof(regno) / sizeof(regno[0]); i++) {
138 int n = regno[i];
139 uint64 want = 0x0101010101010101ULL * (n / 10 * 16 + n % 10);
140 if (regs[n] != want) {
141 printf("alarmtest: x%d is 0x%lx, not 0x%lx\n", n, regs[n], want); 143 }
144 }
145 }
146 sigalarm(0, 0);
147 printf("alarmtest: the handler ran during %d fillspin runs (%d calls)\n", 148 hits, count);
149 return check("registers preserved", hits == 10 && bad == 0); 150}
151
152// a handler that runs longer than the interval is not entered again.
153int
154test_reentry(void)
155{
156 int ok;
157
158 depth = maxdepth = slowcalls = 0;
159 sigalarm(1, slow);
160 ok = waitfor(&slowcalls, 3, 300);
161 sigalarm(0, 0);
162 printf("alarmtest: slow ran %d times, nested at most %d deep\n", slowcalls, 163 maxdepth);
164 return check("no re-entry", ok && maxdepth == 1);
165}
166
167// spin with a0 holding 0x0a0a0a0a0a0a0a0a; return a0.
168uint64
169a0spin(void)
170{
171 uint64 a0;
172
173 asm volatile("li a0, 0x0a0a0a0a0a0a0a0a\n"
174 "li t0, 20000000\n"
175 "1: addi t0, t0, -1\n"
176 "bgtz t0, 1b\n"
177 "mv %0, a0\n"
178 : "=r"(a0)
179 :
180 : "a0", "t0");
181 return a0;
182}
183
184// sigreturn is a system call, and syscall() stores its return
185// value in a0: the interrupted code's a0 must still survive.
186int
187test_a0(void)
188{
189 int c0, hits = 0, bad = 0, t0 = uptime(); 190 uint64 a0;
191
192 count = 0;
193 sigalarm(1, periodic);
194 while (hits < 10 && uptime() - t0 < 300) { 195 c0 = count;
196 a0 = a0spin();
197 if (count != c0)
198 hits++;
199 if (a0 != 0x0a0a0a0a0a0a0a0aULL) {
200 printf("alarmtest: a0 is 0x%lx after sigreturn\n", a0); 202 }
203 }
204 sigalarm(0, 0);
205 printf("alarmtest: the handler ran during %d a0spin runs (%d calls)\n", hits, 206 count);
207 return check("sigreturn restores a0", hits == 10 && bad == 0); 208}
209
210// a forked child, and an exec'd program, start with no alarm.
211int
212test_forkexec(void)
213{
215 char *argv[] = {"alarmtest", "exec", 0}; 216
217 sigalarm(1, periodic);
220 count = 0;
221 waitfor(&count, 1, 10);
223 }
227 // turn the alarm on in this process, then replace its program.
228 sigalarm(1, periodic);
231 }
233 sigalarm(0, 0);
234 printf("alarmtest: handler calls in the forked child: %d, after exec: %d\n", 235 st1, st2);
236 return check("fork and exec start with the alarm off", st1 == 0 && st2 == 0);
237}
238
239int
241{
242 int ok = 0;
243
244 if (argc == 2 && strcmp(argv[1], "exec") == 0) { 245 // the exec'd half of test_forkexec: periodic is at the same
246 // address in this new image, so a surviving alarm would call it.
247 waitfor(&count, 1, 10);
249 }
250 ok += test_called();
251 ok += test_registers();
252 ok += test_reentry();
253 ok += test_a0();
254 ok += test_forkexec();
255 if (ok == 6)
256 printf("alarmtest: 6 of 6 checks OK\n"); 257 else
258 printf("alarmtest: %d of 6 checks FAILED\n", 6 - ok); 260}