Merge tag 'u-boot-atmel-fixes-2020.07-a' of https://gitlab.denx.de/u-boot/custodians...
[oweals/u-boot.git] / arch / arm / lib / semihosting.c
index e9d524e18acabe35bf2f6233471215cf6e185e03..904fddd6c04e0dcc279b588e3c3b642a2a215bb2 100644 (file)
@@ -1,7 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0+
 /*
  * Copyright 2014 Broadcom Corporation
- *
- * SPDX-License-Identifier:    GPL-2.0+
  */
 
 /*
@@ -13,7 +12,9 @@
  * for them.
  */
 #include <common.h>
-#include <asm/semihosting.h>
+#include <command.h>
+#include <env.h>
+#include <log.h>
 
 #define SYSOPEN                0x01
 #define SYSCLOSE       0x02
 #define MODE_READ      0x0
 #define MODE_READBIN   0x1
 
-static long smh_read(long fd, void *memp, size_t len);
-static long smh_open(const char *fname, char *modestr);
-static long smh_close(long fd);
-static long smh_len_fd(long fd);
-
 /*
  * Call the handler
  */
-static long smh_trap(unsigned int sysnum, void *addr)
+static noinline long smh_trap(unsigned int sysnum, void *addr)
 {
        register long result asm("r0");
 #if defined(CONFIG_ARM64)
        asm volatile ("hlt #0xf000" : "=r" (result) : "0"(sysnum), "r"(addr));
+#elif defined(CONFIG_CPU_V7M)
+       asm volatile ("bkpt #0xAB" : "=r" (result) : "0"(sysnum), "r"(addr));
 #else
        /* Note - untested placeholder */
        asm volatile ("svc #0x123456" : "=r" (result) : "0"(sysnum), "r"(addr));
@@ -44,60 +42,43 @@ static long smh_trap(unsigned int sysnum, void *addr)
 }
 
 /*
- * Open, load a file into memory, and close it. Check that the available space
- * is sufficient to store the entire file. Return the bytes actually read from
- * the file as seen by the read function. The verbose flag enables some extra
- * printing of successful read status.
+ * Open a file on the host. Mode is "r" or "rb" currently. Returns a file
+ * descriptor or -1 on error.
  */
-int smh_load(const char *fname, void *memp, int avail, int verbose)
+static long smh_open(const char *fname, char *modestr)
 {
-       long ret;
        long fd;
-       size_t len;
-
-       ret = -1;
-
-       debug("%s: fname \'%s\', avail %u, memp %p\n", __func__, fname,
-             avail, memp);
-
-       /* Open the file */
-       fd = smh_open(fname, "rb");
-       if (fd == -1)
-               return -1;
+       unsigned long mode;
+       struct smh_open_s {
+               const char *fname;
+               unsigned long mode;
+               size_t len;
+       } open;
 
-       /* Get the file length */
-       ret = smh_len_fd(fd);
-       if (ret == -1) {
-               smh_close(fd);
-               return -1;
-       }
+       debug("%s: file \'%s\', mode \'%s\'\n", __func__, fname, modestr);
 
-       /* Check that the file will fit in the supplied buffer */
-       if (ret > avail) {
-               printf("%s: ERROR ret %ld, avail %u\n", __func__, ret,
-                      avail);
-               smh_close(fd);
+       /* Check the file mode */
+       if (!(strcmp(modestr, "r"))) {
+               mode = MODE_READ;
+       } else if (!(strcmp(modestr, "rb"))) {
+               mode = MODE_READBIN;
+       } else {
+               printf("%s: ERROR mode \'%s\' not supported\n", __func__,
+                      modestr);
                return -1;
        }
 
-       len = ret;
-
-       /* Read the file into the buffer */
-       ret = smh_read(fd, memp, len);
-       if (ret == 0) {
-               /* Print successful load information if requested */
-               if (verbose) {
-                       printf("\n%s\n", fname);
-                       printf("    0x%8p dest\n", memp);
-                       printf("    0x%08lx size\n", len);
-                       printf("    0x%08x avail\n", avail);
-               }
-       }
+       open.fname = fname;
+       open.len = strlen(fname);
+       open.mode = mode;
 
-       /* Close the file */
-       smh_close(fd);
+       /* Open the file on the host */
+       fd = smh_trap(SYSOPEN, &open);
+       if (fd == -1)
+               printf("%s: ERROR fd %ld for file \'%s\'\n", __func__, fd,
+                      fname);
 
-       return ret;
+       return fd;
 }
 
 /*
@@ -112,7 +93,7 @@ static long smh_read(long fd, void *memp, size_t len)
                size_t len;
        } read;
 
-       debug("%s: fd %ld, memp %p, len %lu\n", __func__, fd, memp, len);
+       debug("%s: fd %ld, memp %p, len %zu\n", __func__, fd, memp, len);
 
        read.fd = fd;
        read.memp = memp;
@@ -126,7 +107,7 @@ static long smh_read(long fd, void *memp, size_t len)
                 * hard to maintain partial read loops and such, just fail
                 * with an error message.
                 */
-               printf("%s: ERROR ret %ld, fd %ld, len %lu memp %p\n",
+               printf("%s: ERROR ret %ld, fd %ld, len %zu memp %p\n",
                       __func__, ret, fd, len, memp);
                return -1;
        }
@@ -134,46 +115,6 @@ static long smh_read(long fd, void *memp, size_t len)
        return 0;
 }
 
-/*
- * Open a file on the host. Mode is "r" or "rb" currently. Returns a file
- * descriptor or -1 on error.
- */
-static long smh_open(const char *fname, char *modestr)
-{
-       long fd;
-       unsigned long mode;
-       struct smh_open_s {
-               const char *fname;
-               unsigned long mode;
-               size_t len;
-       } open;
-
-       debug("%s: file \'%s\', mode \'%s\'\n", __func__, fname, modestr);
-
-       /* Check the file mode */
-       if (!(strcmp(modestr, "r"))) {
-               mode = MODE_READ;
-       } else if (!(strcmp(modestr, "rb"))) {
-               mode = MODE_READBIN;
-       } else {
-               printf("%s: ERROR mode \'%s\' not supported\n", __func__,
-                      modestr);
-               return -1;
-       }
-
-       open.fname = fname;
-       open.len = strlen(fname);
-       open.mode = mode;
-
-       /* Open the file on the host */
-       fd = smh_trap(SYSOPEN, &open);
-       if (fd == -1)
-               printf("%s: ERROR fd %ld for file \'%s\'\n", __func__, fd,
-                      fname);
-
-       return fd;
-}
-
 /*
  * Close the file using the file descriptor
  */
@@ -206,36 +147,72 @@ static long smh_len_fd(long fd)
        return ret;
 }
 
-/*
- * Get the file length from the filename
- */
-long smh_len(const char *fname)
+static int smh_load_file(const char * const name, ulong load_addr,
+                        ulong *end_addr)
 {
-       long ret;
        long fd;
        long len;
+       long ret;
 
-       debug("%s: file \'%s\'\n", __func__, fname);
-
-       /* Open the file */
-       fd = smh_open(fname, "rb");
-       if (fd < 0)
-               return fd;
+       fd = smh_open(name, "rb");
+       if (fd == -1)
+               return -1;
 
-       /* Get the file length */
        len = smh_len_fd(fd);
        if (len < 0) {
                smh_close(fd);
-               return len;
+               return -1;
        }
 
-       /* Close the file */
-       ret = smh_close(fd);
-       if (ret < 0)
-               return ret;
+       ret = smh_read(fd, (void *)load_addr, len);
+       smh_close(fd);
 
-       debug("%s: returning len %ld\n", __func__, len);
+       if (ret == 0) {
+               *end_addr = load_addr + len - 1;
+               printf("loaded file %s from %08lX to %08lX, %08lX bytes\n",
+                      name,
+                      load_addr,
+                      *end_addr,
+                      len);
+       } else {
+               printf("read failed\n");
+               return 0;
+       }
 
-       /* Return the file length (or -1 error indication) */
-       return len;
+       return 0;
 }
+
+static int do_smhload(struct cmd_tbl *cmdtp, int flag, int argc,
+                     char *const argv[])
+{
+       if (argc == 3 || argc == 4) {
+               ulong load_addr;
+               ulong end_addr = 0;
+               int ret;
+               char end_str[64];
+
+               load_addr = simple_strtoul(argv[2], NULL, 16);
+               if (!load_addr)
+                       return -1;
+
+               ret = smh_load_file(argv[1], load_addr, &end_addr);
+               if (ret < 0)
+                       return CMD_RET_FAILURE;
+
+               /* Optionally save returned end to the environment */
+               if (argc == 4) {
+                       sprintf(end_str, "0x%08lx", end_addr);
+                       env_set(argv[3], end_str);
+               }
+       } else {
+               return CMD_RET_USAGE;
+       }
+       return 0;
+}
+
+U_BOOT_CMD(smhload, 4, 0, do_smhload, "load a file using semihosting",
+          "<file> 0x<address> [end var]\n"
+          "    - load a semihosted file to the address specified\n"
+          "      if the optional [end var] is specified, the end\n"
+          "      address of the file will be stored in this environment\n"
+          "      variable.\n");