Merge tag 'u-boot-amlogic-20181207' of git://git.denx.de/u-boot-amlogic
[platform/kernel/u-boot.git] / arch / sandbox / cpu / os.c
index 4d5f805..a8d01e4 100644 (file)
@@ -1,12 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0+
 /*
  * Copyright (c) 2011 The Chromium OS Authors.
- * SPDX-License-Identifier:    GPL-2.0+
  */
 
 #include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <getopt.h>
+#include <setjmp.h>
 #include <stdio.h>
 #include <stdint.h>
 #include <stdlib.h>
@@ -24,6 +25,7 @@
 #include <asm/sections.h>
 #include <asm/state.h>
 #include <os.h>
+#include <rtc_def.h>
 
 /* Operating System Interface */
 
@@ -36,14 +38,6 @@ ssize_t os_read(int fd, void *buf, size_t count)
        return read(fd, buf, count);
 }
 
-ssize_t os_read_no_block(int fd, void *buf, size_t count)
-{
-       const int flags = fcntl(fd, F_GETFL, 0);
-
-       fcntl(fd, F_SETFL, flags | O_NONBLOCK);
-       return os_read(fd, buf, count);
-}
-
 ssize_t os_write(int fd, const void *buf, size_t count)
 {
        return write(fd, buf, count);
@@ -83,6 +77,8 @@ int os_open(const char *pathname, int os_flags)
 
        if (os_flags & OS_O_CREAT)
                flags |= O_CREAT;
+       if (os_flags & OS_O_TRUNC)
+               flags |= O_TRUNC;
 
        return open(pathname, flags, 0777);
 }
@@ -102,24 +98,91 @@ void os_exit(int exit_code)
        exit(exit_code);
 }
 
+int os_write_file(const char *fname, const void *buf, int size)
+{
+       int fd;
+
+       fd = os_open(fname, OS_O_WRONLY | OS_O_CREAT | OS_O_TRUNC);
+       if (fd < 0) {
+               printf("Cannot open file '%s'\n", fname);
+               return -EIO;
+       }
+       if (os_write(fd, buf, size) != size) {
+               printf("Cannot write to file '%s'\n", fname);
+               os_close(fd);
+               return -EIO;
+       }
+       os_close(fd);
+
+       return 0;
+}
+
+int os_read_file(const char *fname, void **bufp, int *sizep)
+{
+       off_t size;
+       int ret = -EIO;
+       int fd;
+
+       fd = os_open(fname, OS_O_RDONLY);
+       if (fd < 0) {
+               printf("Cannot open file '%s'\n", fname);
+               goto err;
+       }
+       size = os_lseek(fd, 0, OS_SEEK_END);
+       if (size < 0) {
+               printf("Cannot seek to end of file '%s'\n", fname);
+               goto err;
+       }
+       if (os_lseek(fd, 0, OS_SEEK_SET) < 0) {
+               printf("Cannot seek to start of file '%s'\n", fname);
+               goto err;
+       }
+       *bufp = os_malloc(size);
+       if (!*bufp) {
+               printf("Not enough memory to read file '%s'\n", fname);
+               ret = -ENOMEM;
+               goto err;
+       }
+       if (os_read(fd, *bufp, size) != size) {
+               printf("Cannot read from file '%s'\n", fname);
+               goto err;
+       }
+       os_close(fd);
+       *sizep = size;
+
+       return 0;
+err:
+       os_close(fd);
+       return ret;
+}
+
 /* Restore tty state when we exit */
 static struct termios orig_term;
 static bool term_setup;
+static bool term_nonblock;
 
-static void os_fd_restore(void)
+void os_fd_restore(void)
 {
-       if (term_setup)
+       if (term_setup) {
+               int flags;
+
                tcsetattr(0, TCSANOW, &orig_term);
+               if (term_nonblock) {
+                       flags = fcntl(0, F_GETFL, 0);
+                       fcntl(0, F_SETFL, flags & ~O_NONBLOCK);
+               }
+               term_setup = false;
+       }
 }
 
 /* Put tty into raw mode so <tab> and <ctrl+c> work */
 void os_tty_raw(int fd, bool allow_sigs)
 {
        struct termios term;
+       int flags;
 
        if (term_setup)
                return;
-       term_setup = true;
 
        /* If not a tty, don't complain */
        if (tcgetattr(fd, &orig_term))
@@ -133,20 +196,35 @@ void os_tty_raw(int fd, bool allow_sigs)
        if (tcsetattr(fd, TCSANOW, &term))
                return;
 
+       flags = fcntl(fd, F_GETFL, 0);
+       if (!(flags & O_NONBLOCK)) {
+               if (fcntl(fd, F_SETFL, flags | O_NONBLOCK))
+                       return;
+               term_nonblock = true;
+       }
+
+       term_setup = true;
        atexit(os_fd_restore);
 }
 
 void *os_malloc(size_t length)
 {
        struct os_mem_hdr *hdr;
+       int page_size = getpagesize();
 
-       hdr = mmap(NULL, length + sizeof(*hdr), PROT_READ | PROT_WRITE,
+       /*
+        * Use an address that is hopefully available to us so that pointers
+        * to this memory are fairly obvious. If we end up with a different
+        * address, that's fine too.
+        */
+       hdr = mmap((void *)0x10000000, length + page_size,
+                  PROT_READ | PROT_WRITE | PROT_EXEC,
                   MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
        if (hdr == MAP_FAILED)
                return NULL;
        hdr->length = length;
 
-       return hdr + 1;
+       return (void *)hdr + page_size;
 }
 
 void os_free(void *ptr)
@@ -303,47 +381,65 @@ void os_dirent_free(struct os_dirent_node *node)
 
        while (node) {
                next = node->next;
-               free(node);
+               os_free(node);
                node = next;
        }
 }
 
 int os_dirent_ls(const char *dirname, struct os_dirent_node **headp)
 {
-       struct dirent entry, *result;
+       struct dirent *entry;
        struct os_dirent_node *head, *node, *next;
        struct stat buf;
        DIR *dir;
        int ret;
        char *fname;
+       char *old_fname;
        int len;
+       int dirlen;
 
        *headp = NULL;
        dir = opendir(dirname);
        if (!dir)
                return -1;
 
-       /* Create a buffer for the maximum filename length */
-       len = sizeof(entry.d_name) + strlen(dirname) + 2;
-       fname = malloc(len);
+       /* Create a buffer upfront, with typically sufficient size */
+       dirlen = strlen(dirname) + 2;
+       len = dirlen + 256;
+       fname = os_malloc(len);
        if (!fname) {
                ret = -ENOMEM;
                goto done;
        }
 
        for (node = head = NULL;; node = next) {
-               ret = readdir_r(dir, &entry, &result);
-               if (ret || !result)
+               errno = 0;
+               entry = readdir(dir);
+               if (!entry) {
+                       ret = errno;
                        break;
-               next = malloc(sizeof(*node) + strlen(entry.d_name) + 1);
+               }
+               next = os_malloc(sizeof(*node) + strlen(entry->d_name) + 1);
                if (!next) {
                        os_dirent_free(head);
                        ret = -ENOMEM;
                        goto done;
                }
+               if (dirlen + strlen(entry->d_name) > len) {
+                       len = dirlen + strlen(entry->d_name);
+                       old_fname = fname;
+                       fname = os_realloc(fname, len);
+                       if (!fname) {
+                               os_free(old_fname);
+                               os_free(next);
+                               os_dirent_free(head);
+                               ret = -ENOMEM;
+                               goto done;
+                       }
+               }
                next->next = NULL;
-               strcpy(next->name, entry.d_name);
-               switch (entry.d_type) {
+               strcpy(next->name, entry->d_name);
+               switch (entry->d_type) {
                case DT_REG:
                        next->type = OS_FILET_REG;
                        break;
@@ -353,6 +449,8 @@ int os_dirent_ls(const char *dirname, struct os_dirent_node **headp)
                case DT_LNK:
                        next->type = OS_FILET_LNK;
                        break;
+               default:
+                       next->type = OS_FILET_UNKNOWN;
                }
                next->size = 0;
                snprintf(fname, len, "%s/%s", dirname, next->name);
@@ -360,14 +458,14 @@ int os_dirent_ls(const char *dirname, struct os_dirent_node **headp)
                        next->size = buf.st_size;
                if (node)
                        node->next = next;
-               if (!head)
-                       head = node;
+               else
+                       head = next;
        }
        *headp = head;
 
 done:
        closedir(dir);
-       free(fname);
+       os_free(fname);
        return ret;
 }
 
@@ -380,7 +478,7 @@ const char *os_dirent_typename[OS_FILET_COUNT] = {
 
 const char *os_dirent_get_typename(enum os_dirent_t type)
 {
-       if (type >= 0 && type < OS_FILET_COUNT)
+       if (type >= OS_FILET_REG && type < OS_FILET_COUNT)
                return os_dirent_typename[type];
 
        return os_dirent_typename[OS_FILET_UNKNOWN];
@@ -465,20 +563,48 @@ static int make_exec(char *fname, const void *data, int size)
        return 0;
 }
 
-static int add_args(char ***argvp, const char *add_args[], int count)
+/**
+ * add_args() - Allocate a new argv with the given args
+ *
+ * This is used to create a new argv array with all the old arguments and some
+ * new ones that are passed in
+ *
+ * @argvp:  Returns newly allocated args list
+ * @add_args: Arguments to add, each a string
+ * @count: Number of arguments in @add_args
+ * @return 0 if OK, -ENOMEM if out of memory
+ */
+static int add_args(char ***argvp, char *add_args[], int count)
 {
-       char **argv;
+       char **argv, **ap;
        int argc;
 
-       for (argv = *argvp, argc = 0; (*argvp)[argc]; argc++)
+       for (argc = 0; (*argvp)[argc]; argc++)
                ;
 
-       argv = malloc((argc + count + 1) * sizeof(char *));
+       argv = os_malloc((argc + count + 1) * sizeof(char *));
        if (!argv) {
                printf("Out of memory for %d argv\n", count);
                return -ENOMEM;
        }
-       memcpy(argv, *argvp, argc * sizeof(char *));
+       for (ap = *argvp, argc = 0; *ap; ap++) {
+               char *arg = *ap;
+
+               /* Drop args that we don't want to propagate */
+               if (*arg == '-' && strlen(arg) == 2) {
+                       switch (arg[1]) {
+                       case 'j':
+                       case 'm':
+                               ap++;
+                               continue;
+                       }
+               } else if (!strcmp(arg, "--rm_memory")) {
+                       ap++;
+                       continue;
+               }
+               argv[argc++] = arg;
+       }
+
        memcpy(argv + argc, add_args, count * sizeof(char *));
        argv[argc + count] = NULL;
 
@@ -486,21 +612,27 @@ static int add_args(char ***argvp, const char *add_args[], int count)
        return 0;
 }
 
-int os_jump_to_image(const void *dest, int size)
+/**
+ * os_jump_to_file() - Jump to a new program
+ *
+ * This saves the memory buffer, sets up arguments to the new process, then
+ * execs it.
+ *
+ * @fname: Filename to exec
+ * @return does not return on success, any return value is an error
+ */
+static int os_jump_to_file(const char *fname)
 {
        struct sandbox_state *state = state_get_current();
-       char fname[30], mem_fname[30];
+       char mem_fname[30];
        int fd, err;
-       const char *extra_args[5];
+       char *extra_args[5];
        char **argv = state->argv;
+       int argc;
 #ifdef DEBUG
-       int argc, i;
+       int i;
 #endif
 
-       err = make_exec(fname, dest, size);
-       if (err)
-               return err;
-
        strcpy(mem_fname, "/tmp/u-boot.mem.XXXXXX");
        fd = mkstemp(mem_fname);
        if (fd < 0)
@@ -513,14 +645,16 @@ int os_jump_to_image(const void *dest, int size)
        os_fd_restore();
 
        extra_args[0] = "-j";
-       extra_args[1] = fname;
+       extra_args[1] = (char *)fname;
        extra_args[2] = "-m";
        extra_args[3] = mem_fname;
-       extra_args[4] = "--rm_memory";
-       err = add_args(&argv, extra_args,
-                      sizeof(extra_args) / sizeof(extra_args[0]));
+       argc = 4;
+       if (state->ram_buf_rm)
+               extra_args[argc++] = "--rm_memory";
+       err = add_args(&argv, extra_args, argc);
        if (err)
                return err;
+       argv[0] = (char *)fname;
 
 #ifdef DEBUG
        for (i = 0; argv[i]; i++)
@@ -531,9 +665,124 @@ int os_jump_to_image(const void *dest, int size)
                os_exit(2);
 
        err = execv(fname, argv);
-       free(argv);
-       if (err)
+       os_free(argv);
+       if (err) {
+               perror("Unable to run image");
+               printf("Image filename '%s'\n", fname);
                return err;
+       }
 
        return unlink(fname);
 }
+
+int os_jump_to_image(const void *dest, int size)
+{
+       char fname[30];
+       int err;
+
+       err = make_exec(fname, dest, size);
+       if (err)
+               return err;
+
+       return os_jump_to_file(fname);
+}
+
+int os_find_u_boot(char *fname, int maxlen)
+{
+       struct sandbox_state *state = state_get_current();
+       const char *progname = state->argv[0];
+       int len = strlen(progname);
+       const char *suffix;
+       char *p;
+       int fd;
+
+       if (len >= maxlen || len < 4)
+               return -ENOSPC;
+
+       strcpy(fname, progname);
+       suffix = fname + len - 4;
+
+       /* If we are TPL, boot to SPL */
+       if (!strcmp(suffix, "-tpl")) {
+               fname[len - 3] = 's';
+               fd = os_open(fname, O_RDONLY);
+               if (fd >= 0) {
+                       close(fd);
+                       return 0;
+               }
+
+               /* Look for 'u-boot-tpl' in the tpl/ directory */
+               p = strstr(fname, "/tpl/");
+               if (p) {
+                       p[1] = 's';
+                       fd = os_open(fname, O_RDONLY);
+                       if (fd >= 0) {
+                               close(fd);
+                               return 0;
+                       }
+               }
+               return -ENOENT;
+       }
+
+       /* Look for 'u-boot' in the same directory as 'u-boot-spl' */
+       if (!strcmp(suffix, "-spl")) {
+               fname[len - 4] = '\0';
+               fd = os_open(fname, O_RDONLY);
+               if (fd >= 0) {
+                       close(fd);
+                       return 0;
+               }
+       }
+
+       /* Look for 'u-boot' in the parent directory of spl/ */
+       p = strstr(fname, "spl/");
+       if (p) {
+               /* Remove the "spl" characters */
+               memmove(p, p + 4, strlen(p + 4) + 1);
+               fd = os_open(fname, O_RDONLY);
+               if (fd >= 0) {
+                       close(fd);
+                       return 0;
+               }
+       }
+
+       return -ENOENT;
+}
+
+int os_spl_to_uboot(const char *fname)
+{
+       return os_jump_to_file(fname);
+}
+
+void os_localtime(struct rtc_time *rt)
+{
+       time_t t = time(NULL);
+       struct tm *tm;
+
+       tm = localtime(&t);
+       rt->tm_sec = tm->tm_sec;
+       rt->tm_min = tm->tm_min;
+       rt->tm_hour = tm->tm_hour;
+       rt->tm_mday = tm->tm_mday;
+       rt->tm_mon = tm->tm_mon + 1;
+       rt->tm_year = tm->tm_year + 1900;
+       rt->tm_wday = tm->tm_wday;
+       rt->tm_yday = tm->tm_yday;
+       rt->tm_isdst = tm->tm_isdst;
+}
+
+void os_abort(void)
+{
+       abort();
+}
+
+int os_mprotect_allow(void *start, size_t len)
+{
+       int page_size = getpagesize();
+
+       /* Move start to the start of a page, len to the end */
+       start = (void *)(((ulong)start) & ~(page_size - 1));
+       len = (len + page_size * 2) & ~(page_size - 1);
+
+       return mprotect(start, len, PROT_READ | PROT_WRITE);
+}