sandbox: Add a way to write data to the host filesystem
[platform/kernel/u-boot.git] / arch / sandbox / cpu / os.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (c) 2011 The Chromium OS Authors.
4  */
5
6 #include <dirent.h>
7 #include <errno.h>
8 #include <fcntl.h>
9 #include <getopt.h>
10 #include <setjmp.h>
11 #include <stdio.h>
12 #include <stdint.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <termios.h>
16 #include <time.h>
17 #include <unistd.h>
18 #include <sys/mman.h>
19 #include <sys/stat.h>
20 #include <sys/time.h>
21 #include <sys/types.h>
22 #include <linux/types.h>
23
24 #include <asm/getopt.h>
25 #include <asm/sections.h>
26 #include <asm/state.h>
27 #include <os.h>
28 #include <rtc_def.h>
29
30 /* Operating System Interface */
31
32 struct os_mem_hdr {
33         size_t length;          /* number of bytes in the block */
34 };
35
36 ssize_t os_read(int fd, void *buf, size_t count)
37 {
38         return read(fd, buf, count);
39 }
40
41 ssize_t os_read_no_block(int fd, void *buf, size_t count)
42 {
43         const int flags = fcntl(fd, F_GETFL, 0);
44
45         fcntl(fd, F_SETFL, flags | O_NONBLOCK);
46         return os_read(fd, buf, count);
47 }
48
49 ssize_t os_write(int fd, const void *buf, size_t count)
50 {
51         return write(fd, buf, count);
52 }
53
54 off_t os_lseek(int fd, off_t offset, int whence)
55 {
56         if (whence == OS_SEEK_SET)
57                 whence = SEEK_SET;
58         else if (whence == OS_SEEK_CUR)
59                 whence = SEEK_CUR;
60         else if (whence == OS_SEEK_END)
61                 whence = SEEK_END;
62         else
63                 os_exit(1);
64         return lseek(fd, offset, whence);
65 }
66
67 int os_open(const char *pathname, int os_flags)
68 {
69         int flags;
70
71         switch (os_flags & OS_O_MASK) {
72         case OS_O_RDONLY:
73         default:
74                 flags = O_RDONLY;
75                 break;
76
77         case OS_O_WRONLY:
78                 flags = O_WRONLY;
79                 break;
80
81         case OS_O_RDWR:
82                 flags = O_RDWR;
83                 break;
84         }
85
86         if (os_flags & OS_O_CREAT)
87                 flags |= O_CREAT;
88         if (os_flags & OS_O_TRUNC)
89                 flags |= O_TRUNC;
90
91         return open(pathname, flags, 0777);
92 }
93
94 int os_close(int fd)
95 {
96         return close(fd);
97 }
98
99 int os_unlink(const char *pathname)
100 {
101         return unlink(pathname);
102 }
103
104 void os_exit(int exit_code)
105 {
106         exit(exit_code);
107 }
108
109 int os_write_file(const char *name, const void *buf, int size)
110 {
111         char fname[256];
112         int fd;
113
114         fd = os_open(fname, OS_O_WRONLY | OS_O_CREAT | OS_O_TRUNC);
115         if (fd < 0) {
116                 printf("Cannot open file '%s'\n", fname);
117                 return -EIO;
118         }
119         if (os_write(fd, buf, size) != size) {
120                 printf("Cannot write to file '%s'\n", fname);
121                 return -EIO;
122         }
123         os_close(fd);
124         printf("Write '%s', size %#x (%d)\n", name, size, size);
125
126         return 0;
127 }
128
129 /* Restore tty state when we exit */
130 static struct termios orig_term;
131 static bool term_setup;
132
133 void os_fd_restore(void)
134 {
135         if (term_setup) {
136                 tcsetattr(0, TCSANOW, &orig_term);
137                 term_setup = false;
138         }
139 }
140
141 /* Put tty into raw mode so <tab> and <ctrl+c> work */
142 void os_tty_raw(int fd, bool allow_sigs)
143 {
144         struct termios term;
145
146         if (term_setup)
147                 return;
148
149         /* If not a tty, don't complain */
150         if (tcgetattr(fd, &orig_term))
151                 return;
152
153         term = orig_term;
154         term.c_iflag = IGNBRK | IGNPAR;
155         term.c_oflag = OPOST | ONLCR;
156         term.c_cflag = CS8 | CREAD | CLOCAL;
157         term.c_lflag = allow_sigs ? ISIG : 0;
158         if (tcsetattr(fd, TCSANOW, &term))
159                 return;
160
161         term_setup = true;
162         atexit(os_fd_restore);
163 }
164
165 void *os_malloc(size_t length)
166 {
167         struct os_mem_hdr *hdr;
168         int page_size = getpagesize();
169
170         hdr = mmap(NULL, length + page_size,
171                    PROT_READ | PROT_WRITE | PROT_EXEC,
172                    MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
173         if (hdr == MAP_FAILED)
174                 return NULL;
175         hdr->length = length;
176
177         return (void *)hdr + page_size;
178 }
179
180 void os_free(void *ptr)
181 {
182         struct os_mem_hdr *hdr = ptr;
183
184         hdr--;
185         if (ptr)
186                 munmap(hdr, hdr->length + sizeof(*hdr));
187 }
188
189 void *os_realloc(void *ptr, size_t length)
190 {
191         struct os_mem_hdr *hdr = ptr;
192         void *buf = NULL;
193
194         hdr--;
195         if (length != 0) {
196                 buf = os_malloc(length);
197                 if (!buf)
198                         return buf;
199                 if (ptr) {
200                         if (length > hdr->length)
201                                 length = hdr->length;
202                         memcpy(buf, ptr, length);
203                 }
204         }
205         os_free(ptr);
206
207         return buf;
208 }
209
210 void os_usleep(unsigned long usec)
211 {
212         usleep(usec);
213 }
214
215 uint64_t __attribute__((no_instrument_function)) os_get_nsec(void)
216 {
217 #if defined(CLOCK_MONOTONIC) && defined(_POSIX_MONOTONIC_CLOCK)
218         struct timespec tp;
219         if (EINVAL == clock_gettime(CLOCK_MONOTONIC, &tp)) {
220                 struct timeval tv;
221
222                 gettimeofday(&tv, NULL);
223                 tp.tv_sec = tv.tv_sec;
224                 tp.tv_nsec = tv.tv_usec * 1000;
225         }
226         return tp.tv_sec * 1000000000ULL + tp.tv_nsec;
227 #else
228         struct timeval tv;
229         gettimeofday(&tv, NULL);
230         return tv.tv_sec * 1000000000ULL + tv.tv_usec * 1000;
231 #endif
232 }
233
234 static char *short_opts;
235 static struct option *long_opts;
236
237 int os_parse_args(struct sandbox_state *state, int argc, char *argv[])
238 {
239         struct sandbox_cmdline_option **sb_opt = __u_boot_sandbox_option_start;
240         size_t num_options = __u_boot_sandbox_option_count();
241         size_t i;
242
243         int hidden_short_opt;
244         size_t si;
245
246         int c;
247
248         if (short_opts || long_opts)
249                 return 1;
250
251         state->argc = argc;
252         state->argv = argv;
253
254         /* dynamically construct the arguments to the system getopt_long */
255         short_opts = os_malloc(sizeof(*short_opts) * num_options * 2 + 1);
256         long_opts = os_malloc(sizeof(*long_opts) * num_options);
257         if (!short_opts || !long_opts)
258                 return 1;
259
260         /*
261          * getopt_long requires "val" to be unique (since that is what the
262          * func returns), so generate unique values automatically for flags
263          * that don't have a short option.  pick 0x100 as that is above the
264          * single byte range (where ASCII/ISO-XXXX-X charsets live).
265          */
266         hidden_short_opt = 0x100;
267         si = 0;
268         for (i = 0; i < num_options; ++i) {
269                 long_opts[i].name = sb_opt[i]->flag;
270                 long_opts[i].has_arg = sb_opt[i]->has_arg ?
271                         required_argument : no_argument;
272                 long_opts[i].flag = NULL;
273
274                 if (sb_opt[i]->flag_short) {
275                         short_opts[si++] = long_opts[i].val = sb_opt[i]->flag_short;
276                         if (long_opts[i].has_arg == required_argument)
277                                 short_opts[si++] = ':';
278                 } else
279                         long_opts[i].val = sb_opt[i]->flag_short = hidden_short_opt++;
280         }
281         short_opts[si] = '\0';
282
283         /* we need to handle output ourselves since u-boot provides printf */
284         opterr = 0;
285
286         /*
287          * walk all of the options the user gave us on the command line,
288          * figure out what u-boot option structure they belong to (via
289          * the unique short val key), and call the appropriate callback.
290          */
291         while ((c = getopt_long(argc, argv, short_opts, long_opts, NULL)) != -1) {
292                 for (i = 0; i < num_options; ++i) {
293                         if (sb_opt[i]->flag_short == c) {
294                                 if (sb_opt[i]->callback(state, optarg)) {
295                                         state->parse_err = sb_opt[i]->flag;
296                                         return 0;
297                                 }
298                                 break;
299                         }
300                 }
301                 if (i == num_options) {
302                         /*
303                          * store the faulting flag for later display.  we have to
304                          * store the flag itself as the getopt parsing itself is
305                          * tricky: need to handle the following flags (assume all
306                          * of the below are unknown):
307                          *   -a        optopt='a' optind=<next>
308                          *   -abbbb    optopt='a' optind=<this>
309                          *   -aaaaa    optopt='a' optind=<this>
310                          *   --a       optopt=0   optind=<this>
311                          * as you can see, it is impossible to determine the exact
312                          * faulting flag without doing the parsing ourselves, so
313                          * we just report the specific flag that failed.
314                          */
315                         if (optopt) {
316                                 static char parse_err[3] = { '-', 0, '\0', };
317                                 parse_err[1] = optopt;
318                                 state->parse_err = parse_err;
319                         } else
320                                 state->parse_err = argv[optind - 1];
321                         break;
322                 }
323         }
324
325         return 0;
326 }
327
328 void os_dirent_free(struct os_dirent_node *node)
329 {
330         struct os_dirent_node *next;
331
332         while (node) {
333                 next = node->next;
334                 free(node);
335                 node = next;
336         }
337 }
338
339 int os_dirent_ls(const char *dirname, struct os_dirent_node **headp)
340 {
341         struct dirent *entry;
342         struct os_dirent_node *head, *node, *next;
343         struct stat buf;
344         DIR *dir;
345         int ret;
346         char *fname;
347         char *old_fname;
348         int len;
349         int dirlen;
350
351         *headp = NULL;
352         dir = opendir(dirname);
353         if (!dir)
354                 return -1;
355
356         /* Create a buffer upfront, with typically sufficient size */
357         dirlen = strlen(dirname) + 2;
358         len = dirlen + 256;
359         fname = malloc(len);
360         if (!fname) {
361                 ret = -ENOMEM;
362                 goto done;
363         }
364
365         for (node = head = NULL;; node = next) {
366                 errno = 0;
367                 entry = readdir(dir);
368                 if (!entry) {
369                         ret = errno;
370                         break;
371                 }
372                 next = malloc(sizeof(*node) + strlen(entry->d_name) + 1);
373                 if (!next) {
374                         os_dirent_free(head);
375                         ret = -ENOMEM;
376                         goto done;
377                 }
378                 if (dirlen + strlen(entry->d_name) > len) {
379                         len = dirlen + strlen(entry->d_name);
380                         old_fname = fname;
381                         fname = realloc(fname, len);
382                         if (!fname) {
383                                 free(old_fname);
384                                 free(next);
385                                 os_dirent_free(head);
386                                 ret = -ENOMEM;
387                                 goto done;
388                         }
389                 }
390                 next->next = NULL;
391                 strcpy(next->name, entry->d_name);
392                 switch (entry->d_type) {
393                 case DT_REG:
394                         next->type = OS_FILET_REG;
395                         break;
396                 case DT_DIR:
397                         next->type = OS_FILET_DIR;
398                         break;
399                 case DT_LNK:
400                         next->type = OS_FILET_LNK;
401                         break;
402                 default:
403                         next->type = OS_FILET_UNKNOWN;
404                 }
405                 next->size = 0;
406                 snprintf(fname, len, "%s/%s", dirname, next->name);
407                 if (!stat(fname, &buf))
408                         next->size = buf.st_size;
409                 if (node)
410                         node->next = next;
411                 else
412                         head = next;
413         }
414         *headp = head;
415
416 done:
417         closedir(dir);
418         free(fname);
419         return ret;
420 }
421
422 const char *os_dirent_typename[OS_FILET_COUNT] = {
423         "   ",
424         "SYM",
425         "DIR",
426         "???",
427 };
428
429 const char *os_dirent_get_typename(enum os_dirent_t type)
430 {
431         if (type >= OS_FILET_REG && type < OS_FILET_COUNT)
432                 return os_dirent_typename[type];
433
434         return os_dirent_typename[OS_FILET_UNKNOWN];
435 }
436
437 int os_get_filesize(const char *fname, loff_t *size)
438 {
439         struct stat buf;
440         int ret;
441
442         ret = stat(fname, &buf);
443         if (ret)
444                 return ret;
445         *size = buf.st_size;
446         return 0;
447 }
448
449 void os_putc(int ch)
450 {
451         putchar(ch);
452 }
453
454 void os_puts(const char *str)
455 {
456         while (*str)
457                 os_putc(*str++);
458 }
459
460 int os_write_ram_buf(const char *fname)
461 {
462         struct sandbox_state *state = state_get_current();
463         int fd, ret;
464
465         fd = open(fname, O_CREAT | O_WRONLY, 0777);
466         if (fd < 0)
467                 return -ENOENT;
468         ret = write(fd, state->ram_buf, state->ram_size);
469         close(fd);
470         if (ret != state->ram_size)
471                 return -EIO;
472
473         return 0;
474 }
475
476 int os_read_ram_buf(const char *fname)
477 {
478         struct sandbox_state *state = state_get_current();
479         int fd, ret;
480         loff_t size;
481
482         ret = os_get_filesize(fname, &size);
483         if (ret < 0)
484                 return ret;
485         if (size != state->ram_size)
486                 return -ENOSPC;
487         fd = open(fname, O_RDONLY);
488         if (fd < 0)
489                 return -ENOENT;
490
491         ret = read(fd, state->ram_buf, state->ram_size);
492         close(fd);
493         if (ret != state->ram_size)
494                 return -EIO;
495
496         return 0;
497 }
498
499 static int make_exec(char *fname, const void *data, int size)
500 {
501         int fd;
502
503         strcpy(fname, "/tmp/u-boot.jump.XXXXXX");
504         fd = mkstemp(fname);
505         if (fd < 0)
506                 return -ENOENT;
507         if (write(fd, data, size) < 0)
508                 return -EIO;
509         close(fd);
510         if (chmod(fname, 0777))
511                 return -ENOEXEC;
512
513         return 0;
514 }
515
516 static int add_args(char ***argvp, const char *add_args[], int count)
517 {
518         char **argv;
519         int argc;
520
521         for (argv = *argvp, argc = 0; (*argvp)[argc]; argc++)
522                 ;
523
524         argv = malloc((argc + count + 1) * sizeof(char *));
525         if (!argv) {
526                 printf("Out of memory for %d argv\n", count);
527                 return -ENOMEM;
528         }
529         memcpy(argv, *argvp, argc * sizeof(char *));
530         memcpy(argv + argc, add_args, count * sizeof(char *));
531         argv[argc + count] = NULL;
532
533         *argvp = argv;
534         return 0;
535 }
536
537 int os_jump_to_image(const void *dest, int size)
538 {
539         struct sandbox_state *state = state_get_current();
540         char fname[30], mem_fname[30];
541         int fd, err;
542         const char *extra_args[5];
543         char **argv = state->argv;
544 #ifdef DEBUG
545         int argc, i;
546 #endif
547
548         err = make_exec(fname, dest, size);
549         if (err)
550                 return err;
551
552         strcpy(mem_fname, "/tmp/u-boot.mem.XXXXXX");
553         fd = mkstemp(mem_fname);
554         if (fd < 0)
555                 return -ENOENT;
556         close(fd);
557         err = os_write_ram_buf(mem_fname);
558         if (err)
559                 return err;
560
561         os_fd_restore();
562
563         extra_args[0] = "-j";
564         extra_args[1] = fname;
565         extra_args[2] = "-m";
566         extra_args[3] = mem_fname;
567         extra_args[4] = "--rm_memory";
568         err = add_args(&argv, extra_args,
569                        sizeof(extra_args) / sizeof(extra_args[0]));
570         if (err)
571                 return err;
572
573 #ifdef DEBUG
574         for (i = 0; argv[i]; i++)
575                 printf("%d %s\n", i, argv[i]);
576 #endif
577
578         if (state_uninit())
579                 os_exit(2);
580
581         err = execv(fname, argv);
582         free(argv);
583         if (err)
584                 return err;
585
586         return unlink(fname);
587 }
588
589 int os_find_u_boot(char *fname, int maxlen)
590 {
591         struct sandbox_state *state = state_get_current();
592         const char *progname = state->argv[0];
593         int len = strlen(progname);
594         char *p;
595         int fd;
596
597         if (len >= maxlen || len < 4)
598                 return -ENOSPC;
599
600         /* Look for 'u-boot' in the same directory as 'u-boot-spl' */
601         strcpy(fname, progname);
602         if (!strcmp(fname + len - 4, "-spl")) {
603                 fname[len - 4] = '\0';
604                 fd = os_open(fname, O_RDONLY);
605                 if (fd >= 0) {
606                         close(fd);
607                         return 0;
608                 }
609         }
610
611         /* Look for 'u-boot' in the parent directory of spl/ */
612         p = strstr(fname, "/spl/");
613         if (p) {
614                 strcpy(p, p + 4);
615                 fd = os_open(fname, O_RDONLY);
616                 if (fd >= 0) {
617                         close(fd);
618                         return 0;
619                 }
620         }
621
622         return -ENOENT;
623 }
624
625 int os_spl_to_uboot(const char *fname)
626 {
627         struct sandbox_state *state = state_get_current();
628         char *argv[state->argc + 1];
629         int ret;
630
631         memcpy(argv, state->argv, sizeof(char *) * (state->argc + 1));
632         argv[0] = (char *)fname;
633         ret = execv(fname, argv);
634         if (ret)
635                 return ret;
636
637         return unlink(fname);
638 }
639
640 void os_localtime(struct rtc_time *rt)
641 {
642         time_t t = time(NULL);
643         struct tm *tm;
644
645         tm = localtime(&t);
646         rt->tm_sec = tm->tm_sec;
647         rt->tm_min = tm->tm_min;
648         rt->tm_hour = tm->tm_hour;
649         rt->tm_mday = tm->tm_mday;
650         rt->tm_mon = tm->tm_mon + 1;
651         rt->tm_year = tm->tm_year + 1900;
652         rt->tm_wday = tm->tm_wday;
653         rt->tm_yday = tm->tm_yday;
654         rt->tm_isdst = tm->tm_isdst;
655 }
656
657 void os_abort(void)
658 {
659         abort();
660 }
661
662 int os_mprotect_allow(void *start, size_t len)
663 {
664         int page_size = getpagesize();
665
666         /* Move start to the start of a page, len to the end */
667         start = (void *)(((ulong)start) & ~(page_size - 1));
668         len = (len + page_size * 2) & ~(page_size - 1);
669
670         return mprotect(start, len, PROT_READ | PROT_WRITE);
671 }