]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - sys/boot/userboot/test/test.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / sys / boot / userboot / test / test.c
1 /*-
2  * Copyright (c) 2011 Google, Inc.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD$
27  */
28
29 #include <sys/types.h>
30 #include <sys/disk.h>
31 #include <sys/ioctl.h>
32 #include <sys/stat.h>
33 #include <dirent.h>
34 #include <dlfcn.h>
35 #include <err.h>
36 #include <errno.h>
37 #include <fcntl.h>
38 #include <getopt.h>
39 #include <limits.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <termios.h>
44 #include <unistd.h>
45
46 #include <boot/userboot/userboot.h>
47
48 char *host_base = NULL;
49 struct termios term, oldterm;
50 char *image;
51 size_t image_size;
52 int disk_fd = -1;
53
54 uint64_t regs[16];
55 uint64_t pc;
56
57 void test_exit(void *arg, int v);
58
59 /*
60  * Console i/o
61  */
62
63 void
64 test_putc(void *arg, int ch)
65 {
66         char c = ch;
67
68         write(1, &c, 1);
69 }
70
71 int
72 test_getc(void *arg)
73 {
74         char c;
75
76         if (read(0, &c, 1) == 1)
77                 return c;
78         return -1;
79 }
80
81 int
82 test_poll(void *arg)
83 {
84         int n;
85
86         if (ioctl(0, FIONREAD, &n) >= 0)
87                 return (n > 0);
88         return (0);
89 }
90
91 /*
92  * Host filesystem i/o
93  */
94
95 struct test_file {
96         int tf_isdir;
97         size_t tf_size;
98         struct stat tf_stat;
99         union {
100                 int fd;
101                 DIR *dir;
102         } tf_u;
103 };
104
105 int
106 test_open(void *arg, const char *filename, void **h_return)
107 {
108         struct stat st;
109         struct test_file *tf;
110         char path[PATH_MAX];
111
112         if (!host_base)
113                 return (ENOENT);
114
115         strlcpy(path, host_base, PATH_MAX);
116         if (path[strlen(path) - 1] == '/')
117                 path[strlen(path) - 1] = 0;
118         strlcat(path, filename, PATH_MAX);
119         tf = malloc(sizeof(struct test_file));
120         if (stat(path, &tf->tf_stat) < 0) {
121                 free(tf);
122                 return (errno);
123         }
124
125         tf->tf_size = st.st_size;
126         if (S_ISDIR(tf->tf_stat.st_mode)) {
127                 tf->tf_isdir = 1;
128                 tf->tf_u.dir = opendir(path);
129                 if (!tf->tf_u.dir)
130                         goto out;
131                 *h_return = tf;
132                 return (0);
133         }
134         if (S_ISREG(tf->tf_stat.st_mode)) {
135                 tf->tf_isdir = 0;
136                 tf->tf_u.fd = open(path, O_RDONLY);
137                 if (tf->tf_u.fd < 0)
138                         goto out;
139                 *h_return = tf;
140                 return (0);
141         }
142
143 out:
144         free(tf);
145         return (EINVAL);
146 }
147
148 int
149 test_close(void *arg, void *h)
150 {
151         struct test_file *tf = h;
152
153         if (tf->tf_isdir)
154                 closedir(tf->tf_u.dir);
155         else
156                 close(tf->tf_u.fd);
157         free(tf);
158
159         return (0);
160 }
161
162 int
163 test_isdir(void *arg, void *h)
164 {
165         struct test_file *tf = h;
166
167         return (tf->tf_isdir);
168 }
169
170 int
171 test_read(void *arg, void *h, void *dst, size_t size, size_t *resid_return)
172 {
173         struct test_file *tf = h;
174         ssize_t sz;
175
176         if (tf->tf_isdir)
177                 return (EINVAL);
178         sz = read(tf->tf_u.fd, dst, size);
179         if (sz < 0)
180                 return (EINVAL);
181         *resid_return = size - sz;
182         return (0);
183 }
184
185 int
186 test_readdir(void *arg, void *h, uint32_t *fileno_return, uint8_t *type_return,
187     size_t *namelen_return, char *name)
188 {
189         struct test_file *tf = h;
190         struct dirent *dp;
191
192         if (!tf->tf_isdir)
193                 return (EINVAL);
194
195         dp = readdir(tf->tf_u.dir);
196         if (!dp)
197                 return (ENOENT);
198
199         /*
200          * Note: d_namlen is in the range 0..255 and therefore less
201          * than PATH_MAX so we don't need to test before copying.
202          */
203         *fileno_return = dp->d_fileno;
204         *type_return = dp->d_type;
205         *namelen_return = dp->d_namlen;
206         memcpy(name, dp->d_name, dp->d_namlen);
207         name[dp->d_namlen] = 0;
208
209         return (0);
210 }
211
212 int
213 test_seek(void *arg, void *h, uint64_t offset, int whence)
214 {
215         struct test_file *tf = h;
216
217         if (tf->tf_isdir)
218                 return (EINVAL);
219         if (lseek(tf->tf_u.fd, offset, whence) < 0)
220                 return (errno);
221         return (0);
222 }
223
224 int
225 test_stat(void *arg, void *h, int *mode_return, int *uid_return, int *gid_return,
226     uint64_t *size_return)
227 {
228         struct test_file *tf = h;
229
230         *mode_return = tf->tf_stat.st_mode;
231         *uid_return = tf->tf_stat.st_uid;
232         *gid_return = tf->tf_stat.st_gid;
233         *size_return = tf->tf_stat.st_size;
234         return (0);
235 }
236
237 /*
238  * Disk image i/o
239  */
240
241 int
242 test_diskread(void *arg, int unit, uint64_t offset, void *dst, size_t size,
243     size_t *resid_return)
244 {
245         ssize_t n;
246
247         if (unit != 0 || disk_fd == -1)
248                 return (EIO);
249         n = pread(disk_fd, dst, size, offset);
250         if (n < 0)
251                 return (errno);
252         *resid_return = size - n;
253         return (0);
254 }
255
256 int
257 test_diskioctl(void *arg, int unit, u_long cmd, void *data)
258 {
259         struct stat sb;
260
261         if (unit != 0 || disk_fd == -1)
262                 return (EBADF);
263         switch (cmd) {
264         case DIOCGSECTORSIZE:
265                 *(u_int *)data = 512;
266                 break;
267         case DIOCGMEDIASIZE:
268                 if (fstat(disk_fd, &sb) == 0)
269                         *(off_t *)data = sb.st_size;
270                 else
271                         return (ENOTTY);
272                 break;
273         default:
274                 return (ENOTTY);
275         };
276         return (0);
277 }
278
279 /*
280  * Guest virtual machine i/o
281  *
282  * Note: guest addresses are kernel virtual
283  */
284
285 int
286 test_copyin(void *arg, const void *from, uint64_t to, size_t size)
287 {
288
289         to &= 0x7fffffff;
290         if (to > image_size)
291                 return (EFAULT);
292         if (to + size > image_size)
293                 size = image_size - to;
294         memcpy(&image[to], from, size);
295 }
296
297 int
298 test_copyout(void *arg, uint64_t from, void *to, size_t size)
299 {
300
301         from &= 0x7fffffff;
302         if (from > image_size)
303                 return (EFAULT);
304         if (from + size > image_size)
305                 size = image_size - from;
306         memcpy(to, &image[from], size);
307 }
308
309 void
310 test_setreg(void *arg, int r, uint64_t v)
311 {
312
313         if (r < 0 || r >= 16)
314                 return;
315         regs[r] = v;
316 }
317
318 void
319 test_setmsr(void *arg, int r, uint64_t v)
320 {
321 }
322
323 void
324 test_setcr(void *arg, int r, uint64_t v)
325 {
326 }
327
328 void
329 test_setgdt(void *arg, uint64_t v, size_t sz)
330 {
331 }
332
333 void
334 test_exec(void *arg, uint64_t pc)
335 {
336         printf("Execute at 0x%llx\n", pc);
337         test_exit(arg, 0);
338 }
339
340 /*
341  * Misc
342  */
343
344 void
345 test_delay(void *arg, int usec)
346 {
347
348         usleep(usec);
349 }
350
351 void
352 test_exit(void *arg, int v)
353 {
354
355         tcsetattr(0, TCSAFLUSH, &oldterm);
356         exit(v);
357 }
358
359 void
360 test_getmem(void *arg, uint64_t *lowmem, uint64_t *highmem)
361 {
362
363         *lowmem = 128*1024*1024;
364         *highmem = 0;
365 }
366
367 struct loader_callbacks cb = {
368         .putc = test_putc,
369         .getc = test_getc,
370         .poll = test_poll,
371
372         .open = test_open,
373         .close = test_close,
374         .isdir = test_isdir,
375         .read = test_read,
376         .readdir = test_readdir,
377         .seek = test_seek,
378         .stat = test_stat,
379
380         .diskread = test_diskread,
381         .diskioctl = test_diskioctl,
382
383         .copyin = test_copyin,
384         .copyout = test_copyout,
385         .setreg = test_setreg,
386         .setmsr = test_setmsr,
387         .setcr = test_setcr,
388         .setgdt = test_setgdt,
389         .exec = test_exec,
390
391         .delay = test_delay,
392         .exit = test_exit,
393         .getmem = test_getmem,
394 };
395
396 void
397 usage()
398 {
399
400         printf("usage: %s [-d <disk image path>] [-h <host filesystem path>\n");
401         exit(1);
402 }
403
404 int
405 main(int argc, char** argv)
406 {
407         void *h;
408         void (*func)(struct loader_callbacks *, void *, int, int);
409         int opt;
410         char *disk_image = NULL;
411
412         while ((opt = getopt(argc, argv, "d:h:")) != -1) {
413                 switch (opt) {
414                 case 'd':
415                         disk_image = optarg;
416                         break;
417
418                 case 'h':
419                         host_base = optarg;
420                         break;
421
422                 case '?':
423                         usage();
424                 }
425         }
426
427         h = dlopen("/boot/userboot.so",
428             RTLD_LOCAL);
429         if (!h) {
430                 printf("%s\n", dlerror());
431                 return (1);
432         }
433         func = dlsym(h, "loader_main");
434         if (!func) {
435                 printf("%s\n", dlerror());
436                 return (1);
437         }
438
439         image_size = 128*1024*1024;
440         image = malloc(image_size);
441         if (disk_image) {
442                 disk_fd = open(disk_image, O_RDONLY);
443                 if (disk_fd < 0)
444                         err(1, "Can't open disk image '%s'", disk_image);
445         }
446
447         tcgetattr(0, &term);
448         oldterm = term;
449         term.c_iflag &= ~(ICRNL);
450         term.c_lflag &= ~(ICANON|ECHO);
451         tcsetattr(0, TCSAFLUSH, &term);
452
453         func(&cb, NULL, USERBOOT_VERSION_2, disk_fd >= 0);
454 }