]> Git Repo - u-boot.git/blob - arch/sandbox/cpu/os.c
Merge tag 'efi-next' of https://gitlab.denx.de/u-boot/custodians/u-boot-efi into...
[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 <signal.h>
12 #include <stdio.h>
13 #include <stdint.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <termios.h>
17 #include <time.h>
18 #include <unistd.h>
19 #include <sys/mman.h>
20 #include <sys/stat.h>
21 #include <sys/time.h>
22 #include <sys/types.h>
23 #include <linux/types.h>
24
25 #include <asm/getopt.h>
26 #include <asm/sections.h>
27 #include <asm/state.h>
28 #include <os.h>
29 #include <rtc_def.h>
30
31 /* Operating System Interface */
32
33 struct os_mem_hdr {
34         size_t length;          /* number of bytes in the block */
35 };
36
37 ssize_t os_read(int fd, void *buf, size_t count)
38 {
39         return read(fd, buf, count);
40 }
41
42 ssize_t os_write(int fd, const void *buf, size_t count)
43 {
44         return write(fd, buf, count);
45 }
46
47 off_t os_lseek(int fd, off_t offset, int whence)
48 {
49         if (whence == OS_SEEK_SET)
50                 whence = SEEK_SET;
51         else if (whence == OS_SEEK_CUR)
52                 whence = SEEK_CUR;
53         else if (whence == OS_SEEK_END)
54                 whence = SEEK_END;
55         else
56                 os_exit(1);
57         return lseek(fd, offset, whence);
58 }
59
60 int os_open(const char *pathname, int os_flags)
61 {
62         int flags;
63
64         switch (os_flags & OS_O_MASK) {
65         case OS_O_RDONLY:
66         default:
67                 flags = O_RDONLY;
68                 break;
69
70         case OS_O_WRONLY:
71                 flags = O_WRONLY;
72                 break;
73
74         case OS_O_RDWR:
75                 flags = O_RDWR;
76                 break;
77         }
78
79         if (os_flags & OS_O_CREAT)
80                 flags |= O_CREAT;
81         if (os_flags & OS_O_TRUNC)
82                 flags |= O_TRUNC;
83         /*
84          * During a cold reset execv() is used to relaunch the U-Boot binary.
85          * We must ensure that all files are closed in this case.
86          */
87         flags |= O_CLOEXEC;
88
89         return open(pathname, flags, 0777);
90 }
91
92 int os_close(int fd)
93 {
94         /* Do not close the console input */
95         if (fd)
96                 return close(fd);
97         return -1;
98 }
99
100 int os_unlink(const char *pathname)
101 {
102         return unlink(pathname);
103 }
104
105 void os_exit(int exit_code)
106 {
107         exit(exit_code);
108 }
109
110 int os_write_file(const char *fname, const void *buf, int size)
111 {
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                 os_close(fd);
122                 return -EIO;
123         }
124         os_close(fd);
125
126         return 0;
127 }
128
129 int os_read_file(const char *fname, void **bufp, int *sizep)
130 {
131         off_t size;
132         int ret = -EIO;
133         int fd;
134
135         fd = os_open(fname, OS_O_RDONLY);
136         if (fd < 0) {
137                 printf("Cannot open file '%s'\n", fname);
138                 goto err;
139         }
140         size = os_lseek(fd, 0, OS_SEEK_END);
141         if (size < 0) {
142                 printf("Cannot seek to end of file '%s'\n", fname);
143                 goto err;
144         }
145         if (os_lseek(fd, 0, OS_SEEK_SET) < 0) {
146                 printf("Cannot seek to start of file '%s'\n", fname);
147                 goto err;
148         }
149         *bufp = malloc(size);
150         if (!*bufp) {
151                 printf("Not enough memory to read file '%s'\n", fname);
152                 ret = -ENOMEM;
153                 goto err;
154         }
155         if (os_read(fd, *bufp, size) != size) {
156                 printf("Cannot read from file '%s'\n", fname);
157                 goto err;
158         }
159         os_close(fd);
160         *sizep = size;
161
162         return 0;
163 err:
164         os_close(fd);
165         return ret;
166 }
167
168 /* Restore tty state when we exit */
169 static struct termios orig_term;
170 static bool term_setup;
171 static bool term_nonblock;
172
173 void os_fd_restore(void)
174 {
175         if (term_setup) {
176                 int flags;
177
178                 tcsetattr(0, TCSANOW, &orig_term);
179                 if (term_nonblock) {
180                         flags = fcntl(0, F_GETFL, 0);
181                         fcntl(0, F_SETFL, flags & ~O_NONBLOCK);
182                 }
183                 term_setup = false;
184         }
185 }
186
187 static void os_sigint_handler(int sig)
188 {
189         os_fd_restore();
190         signal(SIGINT, SIG_DFL);
191         raise(SIGINT);
192 }
193
194 /* Put tty into raw mode so <tab> and <ctrl+c> work */
195 void os_tty_raw(int fd, bool allow_sigs)
196 {
197         struct termios term;
198         int flags;
199
200         if (term_setup)
201                 return;
202
203         /* If not a tty, don't complain */
204         if (tcgetattr(fd, &orig_term))
205                 return;
206
207         term = orig_term;
208         term.c_iflag = IGNBRK | IGNPAR;
209         term.c_oflag = OPOST | ONLCR;
210         term.c_cflag = CS8 | CREAD | CLOCAL;
211         term.c_lflag = allow_sigs ? ISIG : 0;
212         if (tcsetattr(fd, TCSANOW, &term))
213                 return;
214
215         flags = fcntl(fd, F_GETFL, 0);
216         if (!(flags & O_NONBLOCK)) {
217                 if (fcntl(fd, F_SETFL, flags | O_NONBLOCK))
218                         return;
219                 term_nonblock = true;
220         }
221
222         term_setup = true;
223         atexit(os_fd_restore);
224         signal(SIGINT, os_sigint_handler);
225 }
226
227 void *os_malloc(size_t length)
228 {
229         int page_size = getpagesize();
230         struct os_mem_hdr *hdr;
231
232         /*
233          * Use an address that is hopefully available to us so that pointers
234          * to this memory are fairly obvious. If we end up with a different
235          * address, that's fine too.
236          */
237         hdr = mmap((void *)0x10000000, length + page_size,
238                    PROT_READ | PROT_WRITE | PROT_EXEC,
239                    MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
240         if (hdr == MAP_FAILED)
241                 return NULL;
242         hdr->length = length;
243
244         return (void *)hdr + page_size;
245 }
246
247 void os_free(void *ptr)
248 {
249         int page_size = getpagesize();
250         struct os_mem_hdr *hdr;
251
252         if (ptr) {
253                 hdr = ptr - page_size;
254                 munmap(hdr, hdr->length + page_size);
255         }
256 }
257
258 void os_usleep(unsigned long usec)
259 {
260         usleep(usec);
261 }
262
263 uint64_t __attribute__((no_instrument_function)) os_get_nsec(void)
264 {
265 #if defined(CLOCK_MONOTONIC) && defined(_POSIX_MONOTONIC_CLOCK)
266         struct timespec tp;
267         if (EINVAL == clock_gettime(CLOCK_MONOTONIC, &tp)) {
268                 struct timeval tv;
269
270                 gettimeofday(&tv, NULL);
271                 tp.tv_sec = tv.tv_sec;
272                 tp.tv_nsec = tv.tv_usec * 1000;
273         }
274         return tp.tv_sec * 1000000000ULL + tp.tv_nsec;
275 #else
276         struct timeval tv;
277         gettimeofday(&tv, NULL);
278         return tv.tv_sec * 1000000000ULL + tv.tv_usec * 1000;
279 #endif
280 }
281
282 static char *short_opts;
283 static struct option *long_opts;
284
285 int os_parse_args(struct sandbox_state *state, int argc, char *argv[])
286 {
287         struct sandbox_cmdline_option **sb_opt = __u_boot_sandbox_option_start;
288         size_t num_options = __u_boot_sandbox_option_count();
289         size_t i;
290
291         int hidden_short_opt;
292         size_t si;
293
294         int c;
295
296         if (short_opts || long_opts)
297                 return 1;
298
299         state->argc = argc;
300         state->argv = argv;
301
302         /* dynamically construct the arguments to the system getopt_long */
303         short_opts = malloc(sizeof(*short_opts) * num_options * 2 + 1);
304         long_opts = malloc(sizeof(*long_opts) * (num_options + 1));
305         if (!short_opts || !long_opts)
306                 return 1;
307
308         /*
309          * getopt_long requires "val" to be unique (since that is what the
310          * func returns), so generate unique values automatically for flags
311          * that don't have a short option.  pick 0x100 as that is above the
312          * single byte range (where ASCII/ISO-XXXX-X charsets live).
313          */
314         hidden_short_opt = 0x100;
315         si = 0;
316         for (i = 0; i < num_options; ++i) {
317                 long_opts[i].name = sb_opt[i]->flag;
318                 long_opts[i].has_arg = sb_opt[i]->has_arg ?
319                         required_argument : no_argument;
320                 long_opts[i].flag = NULL;
321
322                 if (sb_opt[i]->flag_short) {
323                         short_opts[si++] = long_opts[i].val = sb_opt[i]->flag_short;
324                         if (long_opts[i].has_arg == required_argument)
325                                 short_opts[si++] = ':';
326                 } else
327                         long_opts[i].val = sb_opt[i]->flag_short = hidden_short_opt++;
328         }
329         short_opts[si] = '\0';
330
331         /* we need to handle output ourselves since u-boot provides printf */
332         opterr = 0;
333
334         memset(&long_opts[num_options], '\0', sizeof(*long_opts));
335         /*
336          * walk all of the options the user gave us on the command line,
337          * figure out what u-boot option structure they belong to (via
338          * the unique short val key), and call the appropriate callback.
339          */
340         while ((c = getopt_long(argc, argv, short_opts, long_opts, NULL)) != -1) {
341                 for (i = 0; i < num_options; ++i) {
342                         if (sb_opt[i]->flag_short == c) {
343                                 if (sb_opt[i]->callback(state, optarg)) {
344                                         state->parse_err = sb_opt[i]->flag;
345                                         return 0;
346                                 }
347                                 break;
348                         }
349                 }
350                 if (i == num_options) {
351                         /*
352                          * store the faulting flag for later display.  we have to
353                          * store the flag itself as the getopt parsing itself is
354                          * tricky: need to handle the following flags (assume all
355                          * of the below are unknown):
356                          *   -a        optopt='a' optind=<next>
357                          *   -abbbb    optopt='a' optind=<this>
358                          *   -aaaaa    optopt='a' optind=<this>
359                          *   --a       optopt=0   optind=<this>
360                          * as you can see, it is impossible to determine the exact
361                          * faulting flag without doing the parsing ourselves, so
362                          * we just report the specific flag that failed.
363                          */
364                         if (optopt) {
365                                 static char parse_err[3] = { '-', 0, '\0', };
366                                 parse_err[1] = optopt;
367                                 state->parse_err = parse_err;
368                         } else
369                                 state->parse_err = argv[optind - 1];
370                         break;
371                 }
372         }
373
374         return 0;
375 }
376
377 void os_dirent_free(struct os_dirent_node *node)
378 {
379         struct os_dirent_node *next;
380
381         while (node) {
382                 next = node->next;
383                 free(node);
384                 node = next;
385         }
386 }
387
388 int os_dirent_ls(const char *dirname, struct os_dirent_node **headp)
389 {
390         struct dirent *entry;
391         struct os_dirent_node *head, *node, *next;
392         struct stat buf;
393         DIR *dir;
394         int ret;
395         char *fname;
396         char *old_fname;
397         int len;
398         int dirlen;
399
400         *headp = NULL;
401         dir = opendir(dirname);
402         if (!dir)
403                 return -1;
404
405         /* Create a buffer upfront, with typically sufficient size */
406         dirlen = strlen(dirname) + 2;
407         len = dirlen + 256;
408         fname = malloc(len);
409         if (!fname) {
410                 ret = -ENOMEM;
411                 goto done;
412         }
413
414         for (node = head = NULL;; node = next) {
415                 errno = 0;
416                 entry = readdir(dir);
417                 if (!entry) {
418                         ret = errno;
419                         break;
420                 }
421                 next = malloc(sizeof(*node) + strlen(entry->d_name) + 1);
422                 if (!next) {
423                         os_dirent_free(head);
424                         ret = -ENOMEM;
425                         goto done;
426                 }
427                 if (dirlen + strlen(entry->d_name) > len) {
428                         len = dirlen + strlen(entry->d_name);
429                         old_fname = fname;
430                         fname = realloc(fname, len);
431                         if (!fname) {
432                                 free(old_fname);
433                                 free(next);
434                                 os_dirent_free(head);
435                                 ret = -ENOMEM;
436                                 goto done;
437                         }
438                 }
439                 next->next = NULL;
440                 strcpy(next->name, entry->d_name);
441                 switch (entry->d_type) {
442                 case DT_REG:
443                         next->type = OS_FILET_REG;
444                         break;
445                 case DT_DIR:
446                         next->type = OS_FILET_DIR;
447                         break;
448                 case DT_LNK:
449                         next->type = OS_FILET_LNK;
450                         break;
451                 default:
452                         next->type = OS_FILET_UNKNOWN;
453                 }
454                 next->size = 0;
455                 snprintf(fname, len, "%s/%s", dirname, next->name);
456                 if (!stat(fname, &buf))
457                         next->size = buf.st_size;
458                 if (node)
459                         node->next = next;
460                 else
461                         head = next;
462         }
463         *headp = head;
464
465 done:
466         closedir(dir);
467         free(fname);
468         return ret;
469 }
470
471 const char *os_dirent_typename[OS_FILET_COUNT] = {
472         "   ",
473         "SYM",
474         "DIR",
475         "???",
476 };
477
478 const char *os_dirent_get_typename(enum os_dirent_t type)
479 {
480         if (type >= OS_FILET_REG && type < OS_FILET_COUNT)
481                 return os_dirent_typename[type];
482
483         return os_dirent_typename[OS_FILET_UNKNOWN];
484 }
485
486 int os_get_filesize(const char *fname, loff_t *size)
487 {
488         struct stat buf;
489         int ret;
490
491         ret = stat(fname, &buf);
492         if (ret)
493                 return ret;
494         *size = buf.st_size;
495         return 0;
496 }
497
498 void os_putc(int ch)
499 {
500         putchar(ch);
501 }
502
503 void os_puts(const char *str)
504 {
505         while (*str)
506                 os_putc(*str++);
507 }
508
509 int os_write_ram_buf(const char *fname)
510 {
511         struct sandbox_state *state = state_get_current();
512         int fd, ret;
513
514         fd = open(fname, O_CREAT | O_WRONLY, 0777);
515         if (fd < 0)
516                 return -ENOENT;
517         ret = write(fd, state->ram_buf, state->ram_size);
518         close(fd);
519         if (ret != state->ram_size)
520                 return -EIO;
521
522         return 0;
523 }
524
525 int os_read_ram_buf(const char *fname)
526 {
527         struct sandbox_state *state = state_get_current();
528         int fd, ret;
529         loff_t size;
530
531         ret = os_get_filesize(fname, &size);
532         if (ret < 0)
533                 return ret;
534         if (size != state->ram_size)
535                 return -ENOSPC;
536         fd = open(fname, O_RDONLY);
537         if (fd < 0)
538                 return -ENOENT;
539
540         ret = read(fd, state->ram_buf, state->ram_size);
541         close(fd);
542         if (ret != state->ram_size)
543                 return -EIO;
544
545         return 0;
546 }
547
548 static int make_exec(char *fname, const void *data, int size)
549 {
550         int fd;
551
552         strcpy(fname, "/tmp/u-boot.jump.XXXXXX");
553         fd = mkstemp(fname);
554         if (fd < 0)
555                 return -ENOENT;
556         if (write(fd, data, size) < 0)
557                 return -EIO;
558         close(fd);
559         if (chmod(fname, 0777))
560                 return -ENOEXEC;
561
562         return 0;
563 }
564
565 /**
566  * add_args() - Allocate a new argv with the given args
567  *
568  * This is used to create a new argv array with all the old arguments and some
569  * new ones that are passed in
570  *
571  * @argvp:  Returns newly allocated args list
572  * @add_args: Arguments to add, each a string
573  * @count: Number of arguments in @add_args
574  * @return 0 if OK, -ENOMEM if out of memory
575  */
576 static int add_args(char ***argvp, char *add_args[], int count)
577 {
578         char **argv, **ap;
579         int argc;
580
581         for (argc = 0; (*argvp)[argc]; argc++)
582                 ;
583
584         argv = malloc((argc + count + 1) * sizeof(char *));
585         if (!argv) {
586                 printf("Out of memory for %d argv\n", count);
587                 return -ENOMEM;
588         }
589         for (ap = *argvp, argc = 0; *ap; ap++) {
590                 char *arg = *ap;
591
592                 /* Drop args that we don't want to propagate */
593                 if (*arg == '-' && strlen(arg) == 2) {
594                         switch (arg[1]) {
595                         case 'j':
596                         case 'm':
597                                 ap++;
598                                 continue;
599                         }
600                 } else if (!strcmp(arg, "--rm_memory")) {
601                         ap++;
602                         continue;
603                 }
604                 argv[argc++] = arg;
605         }
606
607         memcpy(argv + argc, add_args, count * sizeof(char *));
608         argv[argc + count] = NULL;
609
610         *argvp = argv;
611         return 0;
612 }
613
614 /**
615  * os_jump_to_file() - Jump to a new program
616  *
617  * This saves the memory buffer, sets up arguments to the new process, then
618  * execs it.
619  *
620  * @fname: Filename to exec
621  * @return does not return on success, any return value is an error
622  */
623 static int os_jump_to_file(const char *fname)
624 {
625         struct sandbox_state *state = state_get_current();
626         char mem_fname[30];
627         int fd, err;
628         char *extra_args[5];
629         char **argv = state->argv;
630         int argc;
631 #ifdef DEBUG
632         int i;
633 #endif
634
635         strcpy(mem_fname, "/tmp/u-boot.mem.XXXXXX");
636         fd = mkstemp(mem_fname);
637         if (fd < 0)
638                 return -ENOENT;
639         close(fd);
640         err = os_write_ram_buf(mem_fname);
641         if (err)
642                 return err;
643
644         os_fd_restore();
645
646         extra_args[0] = "-j";
647         extra_args[1] = (char *)fname;
648         extra_args[2] = "-m";
649         extra_args[3] = mem_fname;
650         argc = 4;
651         if (state->ram_buf_rm)
652                 extra_args[argc++] = "--rm_memory";
653         err = add_args(&argv, extra_args, argc);
654         if (err)
655                 return err;
656         argv[0] = (char *)fname;
657
658 #ifdef DEBUG
659         for (i = 0; argv[i]; i++)
660                 printf("%d %s\n", i, argv[i]);
661 #endif
662
663         if (state_uninit())
664                 os_exit(2);
665
666         err = execv(fname, argv);
667         free(argv);
668         if (err) {
669                 perror("Unable to run image");
670                 printf("Image filename '%s'\n", fname);
671                 return err;
672         }
673
674         return unlink(fname);
675 }
676
677 int os_jump_to_image(const void *dest, int size)
678 {
679         char fname[30];
680         int err;
681
682         err = make_exec(fname, dest, size);
683         if (err)
684                 return err;
685
686         return os_jump_to_file(fname);
687 }
688
689 int os_find_u_boot(char *fname, int maxlen)
690 {
691         struct sandbox_state *state = state_get_current();
692         const char *progname = state->argv[0];
693         int len = strlen(progname);
694         const char *suffix;
695         char *p;
696         int fd;
697
698         if (len >= maxlen || len < 4)
699                 return -ENOSPC;
700
701         strcpy(fname, progname);
702         suffix = fname + len - 4;
703
704         /* If we are TPL, boot to SPL */
705         if (!strcmp(suffix, "-tpl")) {
706                 fname[len - 3] = 's';
707                 fd = os_open(fname, O_RDONLY);
708                 if (fd >= 0) {
709                         close(fd);
710                         return 0;
711                 }
712
713                 /* Look for 'u-boot-tpl' in the tpl/ directory */
714                 p = strstr(fname, "/tpl/");
715                 if (p) {
716                         p[1] = 's';
717                         fd = os_open(fname, O_RDONLY);
718                         if (fd >= 0) {
719                                 close(fd);
720                                 return 0;
721                         }
722                 }
723                 return -ENOENT;
724         }
725
726         /* Look for 'u-boot' in the same directory as 'u-boot-spl' */
727         if (!strcmp(suffix, "-spl")) {
728                 fname[len - 4] = '\0';
729                 fd = os_open(fname, O_RDONLY);
730                 if (fd >= 0) {
731                         close(fd);
732                         return 0;
733                 }
734         }
735
736         /* Look for 'u-boot' in the parent directory of spl/ */
737         p = strstr(fname, "spl/");
738         if (p) {
739                 /* Remove the "spl" characters */
740                 memmove(p, p + 4, strlen(p + 4) + 1);
741                 fd = os_open(fname, O_RDONLY);
742                 if (fd >= 0) {
743                         close(fd);
744                         return 0;
745                 }
746         }
747
748         return -ENOENT;
749 }
750
751 int os_spl_to_uboot(const char *fname)
752 {
753         return os_jump_to_file(fname);
754 }
755
756 void os_localtime(struct rtc_time *rt)
757 {
758         time_t t = time(NULL);
759         struct tm *tm;
760
761         tm = localtime(&t);
762         rt->tm_sec = tm->tm_sec;
763         rt->tm_min = tm->tm_min;
764         rt->tm_hour = tm->tm_hour;
765         rt->tm_mday = tm->tm_mday;
766         rt->tm_mon = tm->tm_mon + 1;
767         rt->tm_year = tm->tm_year + 1900;
768         rt->tm_wday = tm->tm_wday;
769         rt->tm_yday = tm->tm_yday;
770         rt->tm_isdst = tm->tm_isdst;
771 }
772
773 void os_abort(void)
774 {
775         abort();
776 }
777
778 int os_mprotect_allow(void *start, size_t len)
779 {
780         int page_size = getpagesize();
781
782         /* Move start to the start of a page, len to the end */
783         start = (void *)(((ulong)start) & ~(page_size - 1));
784         len = (len + page_size * 2) & ~(page_size - 1);
785
786         return mprotect(start, len, PROT_READ | PROT_WRITE);
787 }
788
789 void *os_find_text_base(void)
790 {
791         char line[500];
792         void *base = NULL;
793         int len;
794         int fd;
795
796         /*
797          * This code assumes that the first line of /proc/self/maps holds
798          * information about the text, for example:
799          *
800          * 5622d9907000-5622d9a55000 r-xp 00000000 08:01 15067168   u-boot
801          *
802          * The first hex value is assumed to be the address.
803          *
804          * This is tested in Linux 4.15.
805          */
806         fd = open("/proc/self/maps", O_RDONLY);
807         if (fd == -1)
808                 return NULL;
809         len = read(fd, line, sizeof(line));
810         if (len > 0) {
811                 char *end = memchr(line, '-', len);
812
813                 if (end) {
814                         uintptr_t addr;
815
816                         *end = '\0';
817                         if (sscanf(line, "%zx", &addr) == 1)
818                                 base = (void *)addr;
819                 }
820         }
821         close(fd);
822
823         return base;
824 }
825
826 void os_relaunch(char *argv[])
827 {
828         execv(argv[0], argv);
829         os_exit(1);
830 }
This page took 0.072961 seconds and 4 git commands to generate.