@@ -0,0 +1,481 @@
1//
2// tests for demand-paged exec.
3// each test prints "dexectest: <name>: OK" or "... FAIL".
4// run all: dexectest; run one: dexectest <name>.
5//
6
7#include "kernel/types.h"
8#include "kernel/riscv.h"
9#include "kernel/fcntl.h"
10#include "kernel/elf.h"
11#include "kernel/vm.h"
12#include "user/user.h"
13
14#define NBIG 16 // pages in big[]
15#define PERPAGE (PGSIZE / sizeof(int))
16#define PAGE(k) (k * PERPAGE)
17
18// 16 pages of initialized data. They are part of the file, so after
19// exec each of them is loaded from the file when first touched.
20// Every int is 0x5a5a5a5a except the first of each page, which is
21// 1000 + the page number. Each test touches its own pages first.
22int big[NBIG * PERPAGE] __attribute__((aligned(PGSIZE))) = { 23 [0 ... NBIG * PERPAGE - 1] = 0x5a5a5a5a,
24 [PAGE(0)] = 1000, [PAGE(1)] = 1001, [PAGE(2)] = 1002,
25 [PAGE(3)] = 1003, [PAGE(4)] = 1004, [PAGE(5)] = 1005,
26 [PAGE(6)] = 1006, [PAGE(7)] = 1007, [PAGE(8)] = 1008,
27 [PAGE(9)] = 1009, [PAGE(10)] = 1010, [PAGE(11)] = 1011,
28 [PAGE(12)] = 1012, [PAGE(13)] = 1013, [PAGE(14)] = 1014,
29 [PAGE(15)] = 1015,
30};
31
32// a page of read-only data, in the text segment.
33const char ro[PGSIZE] __attribute__((aligned(PGSIZE))) = "read-only"; 34
35// bss: never written, must read as zeros.
37
38char *self; // this program's file name, argv[0]
39int failed;
40
41void
42result(char *name, int ok) 43{
44 printf("dexectest: %s: %s\n", name, ok ? "OK" : "FAIL"); 45 if (!ok)
46 failed = 1;
47}
48
49// does page k of big[] hold what the file says?
50int
51pageok(int k)
52{
53 int *q = &big[PAGE(k)]; 54
55 if (q[0] != 1000 + k)
56 return 0;
57 for (int i = 1; i < PERPAGE; i++)
58 if (q[i] != 0x5a5a5a5a)
59 return 0;
60 return 1;
61}
62
63// count free pages by allocating them all with sbrk.
64int
66{
68 int n = 0;
69
71 n++;
73 return n;
74}
75
76// read this program's ELF header and program headers.
77int
79{
81 int fd, n = 0;
82
84 return -1;
87 return -1;
88 }
89 for (int i = 0; i < elf.phnum && n < max; i++) { 90 // read() can't seek: reopen and skip to the i-th header.
93 char skip[64];
96 int m = off > sizeof(skip) ? sizeof(skip) : off; 97 if (read(fd, skip, m) != m) 98 break;
100 }
101 if (read(fd, &ph[n], sizeof(ph[n])) != sizeof(ph[n])) 102 break;
104 n++;
105 }
107 return n;
108}
109
110// file offset of virtual address va, or -1.
111int
112fileoff(char *file, uint64 va) 113{
115 int n = readphdrs(file, ph, 4); 116
117 for (int i = 0; i < n; i++)
119 return ph[i].off + (va - ph[i].vaddr); 120 return -1;
121}
122
123// after exec, touching a page of the program costs one page,
124// and only then.
125void
126lazytest(void)
127{
128 volatile int x;
129
130 countfree(); // first call allocates page-table pages; ignore it 135 (void)x;
136 printf("dexectest: lazy: touching 2 data pages took %d free pages\n", 137 before - after);
138 result("lazy", before - after == 2 && pageok(1) && pageok(2));
139}
140
141// a child forked before a page is touched loads it from the same
142// file.
143void
145{
148 printf("dexectest: fork failed\n"); 149 result("fork", 0);
150 return;
151 }
153 exit(pageok(3) ? 0 : 1); 154 int xstatus;
156 result("fork", xstatus == 0 && pageok(3));
157}
158
159// the kernel copies to and from untouched program pages while it
160// holds spinlocks: pipewrite and piperead hold pi->lock, kwait holds
161// wait_lock and the child's p->lock.
162void
163lockedtest(void)
164{
165 int fds[2];
166 int *dst = &big[PAGE(4)]; 168 int *src = &big[PAGE(8)]; 169
171 result("locked", 0);
172 return;
173 }
176 result("locked", 0);
177 return;
178 }
180 int v[2] = {4004, 4005};
181 write(fds[1], v, sizeof(v)); 183 }
184 int m = write(fds[1], src, 2 * sizeof(int)); // from page 8 186 int n = 0, k;
187 while (n < 4 * sizeof(int) &&
188 (k = read(fds[0], (char *)dst + n, 4 * sizeof(int) - n)) > 0) 189 n += k; // into page 4
192 // the pipe holds the child's two ints and page 8's first two, in
193 // either order.
194 int ok = (dst[0] == 4004 && dst[1] == 4005 && dst[2] == 1008 &&
195 dst[3] == 0x5a5a5a5a) ||
196 (dst[0] == 1008 && dst[1] == 0x5a5a5a5a && dst[2] == 4004 &&
197 dst[3] == 4005);
198 result("locked", m == 2 * sizeof(int) && n == 4 * sizeof(int) && ok &&
200}
201
202// read() from this program's own file into its own unloaded pages:
203// fileread holds the file's inode lock while it copies.
204void
205selfreadtest(void)
206{
207 char *dst = (char *)&big[PAGE(6)]; 209
210 if (fd < 0) {
211 printf("dexectest: cannot open %s\n", self); 212 result("selfread", 0);
213 return;
214 }
215 int n = read(fd, dst, PGSIZE); // page 6: data, writable 218
220 int m = read(fd, (char *)ro, 16); // text: must fail 222 result("selfread", ok && m < 0 && strcmp(ro, "read-only") == 0); 223}
224
225// copy file src to dst.
226int
227copyfile(char *src, char *dst)
228{
230 int n, in, out;
231
233 return -1;
236 return -1;
237 }
240 break;
243 return n == 0 ? 0 : -1;
244}
245
246// start a copy of this program as a helper that waits for a byte
247// on its stdin, then checks the pages of big[] (see main).
248// returns its pid; *go is the pipe to write the byte to.
249int
250starthelper(char *file, int *go) 251{
252 int down[2], up[2];
253 char c;
254
256 return -1;
259 return -1;
271 }
274 // the helper writes one byte once it runs; until then we must
275 // not change its file.
276 if (read(up[0], &c, 1) != 1) { 280 return -1;
281 }
283 *go = down[1];
285}
286
287// what the helper reports by its exit status, or -1 if killed.
288int
289finishhelper(int go)
290{
291 int xstatus;
292
296 return xstatus;
297}
298
299// the file of a running program is unlinked: the process keeps
300// it alive, and its pages still load.
301void
302unlinktest(void)
303{
304 int go;
305
306 if (copyfile(self, "dxcopy") < 0) {
307 result("unlink", 0);
308 return;
309 }
310 int pid = starthelper("dxcopy", &go); 312 result("unlink", 0);
313 return;
314 }
316 int st = finishhelper(go);
317 result("unlink", r == 0 && st == 0 && open("dxcopy", O_RDONLY) < 0); 318}
319
320// the file of a running program is truncated: a page that is not
321// loaded yet can't be read, so the process is killed.
322void
323truncatetest(void)
324{
325 int go;
326
327 if (copyfile(self, "dxcopy") < 0) {
328 result("truncate", 0);
329 return;
330 }
331 int pid = starthelper("dxcopy", &go); 333 result("truncate", 0);
334 return;
335 }
337 int st = finishhelper(go);
339 result("truncate", st == -1);
340}
341
342// the file of a running program is overwritten: a page that is
343// not loaded yet comes from the new contents.
344void
345modifytest(void)
346{
347 int go, fd;
348 int v = 2007;
349
350 if (copyfile(self, "dxcopy") < 0) {
351 result("modify", 0);
352 return;
353 }
354 int off = fileoff("dxcopy", (uint64)&big[PAGE(7)]); 355 int pid = starthelper("dxcopy", &go); 357 result("modify", 0);
358 return;
359 }
360 // overwrite the first int of page 7 in the file.
362 char skip[512];
364 int m = left > sizeof(skip) ? sizeof(skip) : left; 367 }
368 write(fd, &v, sizeof(v)); 370 int st = finishhelper(go);
372 // the helper exits with 3 if it saw 2007 in page 7.
373 result("modify", st == 3);
374}
375
376// sbrk(-n) below the end of the image and back again: the pages
377// that come back must be zeros, not the file's bytes. The child
378// gives back its own stack doing this, so from then on it calls
379// only the system-call stubs, which use no stack, until it exits.
380void
381shrinktest(void)
382{
385 result("shrink", 0);
386 return;
387 }
389 int n = sbrk(0) - (char *)&big[PAGE(8)]; 392 exit(big[PAGE(9)]); // 0 if page 9 came back zeroed 393 }
394 int xstatus;
396 result("shrink", xstatus == 0);
397}
398
399// the whole image, touched at last: the text is the file's bytes,
400// with zeros after them, and the bss is zero.
401void
402contenttest(void)
403{
406 int ok = 1;
407 int n = readphdrs(self, ph, 4);
408
409 if (n < 1)
410 ok = 0;
411 for (int i = 0; i < n; i++) {
413 continue; // data: the program has written some of it
414 // compare the segment with the file.
421 }
422 char *m = (char *)ph[i].vaddr; 423 for (uint64 done = 0; done < ph[i].filesz;) { 427 ok = 0;
428 done += k;
429 }
431 // the rest of the last page is zero.
434 if (*(char *)a != 0)
435 ok = 0;
436 }
437 for (int k = 0; k < NBIG; k++)
438 if (!pageok(k) && k != 4 && k != 5 && k != 6) // 4-6 were written
439 ok = 0;
440 for (int i = 0; i < sizeof(zeros); i++)
441 if (zeros[i] != 0)
442 ok = 0;
443 result("content", ok);
444}
445
448 void (*f)(void);
449} tests[] = {
450 {"lazy", lazytest}, {"fork", forktest}, 451 {"locked", lockedtest}, {"selfread", selfreadtest},
452 {"unlink", unlinktest}, {"truncate", truncatetest},
453 {"modify", modifytest}, {"shrink", shrinktest},
454 {"content", contenttest},
455};
456
457int
459{
461 if (argc == 2 && strcmp(argv[1], "helper") == 0) { 462 // started by starthelper: say we run, wait for the go byte,
463 // then touch pages 7 to 15.
464 char c;
466 if (read(0, &c, 1) != 1) 468 if (big[PAGE(7)] == 2007) 469 exit(3); // modifytest changed the file 470 for (int k = 7; k < NBIG; k++)
471 if (!pageok(k))
474 }
475
476 for (int i = 0; i < sizeof(tests) / sizeof(tests[0]); i++)
478 tests[i].f();
479 printf("dexectest: %s\n", failed ? "SOME TESTS FAILED" : "ALL OK"); 481}