uboot/arch/arm/lib/semihosting.c
<<
>>
Prefs
   1/*
   2 * Copyright 2014 Broadcom Corporation
   3 *
   4 * SPDX-License-Identifier:     GPL-2.0+
   5 */
   6
   7/*
   8 * Minimal semihosting implementation for reading files into memory. If more
   9 * features like writing files or console output are required they can be
  10 * added later. This code has been tested on arm64/aarch64 fastmodel only.
  11 * An untested placeholder exists for armv7 architectures, but since they
  12 * are commonly available in silicon now, fastmodel usage makes less sense
  13 * for them.
  14 */
  15#include <common.h>
  16#include <command.h>
  17
  18#define SYSOPEN         0x01
  19#define SYSCLOSE        0x02
  20#define SYSREAD         0x06
  21#define SYSFLEN         0x0C
  22
  23#define MODE_READ       0x0
  24#define MODE_READBIN    0x1
  25
  26/*
  27 * Call the handler
  28 */
  29static noinline long smh_trap(unsigned int sysnum, void *addr)
  30{
  31        register long result asm("r0");
  32#if defined(CONFIG_ARM64)
  33        asm volatile ("hlt #0xf000" : "=r" (result) : "0"(sysnum), "r"(addr));
  34#elif defined(CONFIG_CPU_V7M)
  35        asm volatile ("bkpt #0xAB" : "=r" (result) : "0"(sysnum), "r"(addr));
  36#else
  37        /* Note - untested placeholder */
  38        asm volatile ("svc #0x123456" : "=r" (result) : "0"(sysnum), "r"(addr));
  39#endif
  40        return result;
  41}
  42
  43/*
  44 * Open a file on the host. Mode is "r" or "rb" currently. Returns a file
  45 * descriptor or -1 on error.
  46 */
  47static long smh_open(const char *fname, char *modestr)
  48{
  49        long fd;
  50        unsigned long mode;
  51        struct smh_open_s {
  52                const char *fname;
  53                unsigned long mode;
  54                size_t len;
  55        } open;
  56
  57        debug("%s: file \'%s\', mode \'%s\'\n", __func__, fname, modestr);
  58
  59        /* Check the file mode */
  60        if (!(strcmp(modestr, "r"))) {
  61                mode = MODE_READ;
  62        } else if (!(strcmp(modestr, "rb"))) {
  63                mode = MODE_READBIN;
  64        } else {
  65                printf("%s: ERROR mode \'%s\' not supported\n", __func__,
  66                       modestr);
  67                return -1;
  68        }
  69
  70        open.fname = fname;
  71        open.len = strlen(fname);
  72        open.mode = mode;
  73
  74        /* Open the file on the host */
  75        fd = smh_trap(SYSOPEN, &open);
  76        if (fd == -1)
  77                printf("%s: ERROR fd %ld for file \'%s\'\n", __func__, fd,
  78                       fname);
  79
  80        return fd;
  81}
  82
  83/*
  84 * Read 'len' bytes of file into 'memp'. Returns 0 on success, else failure
  85 */
  86static long smh_read(long fd, void *memp, size_t len)
  87{
  88        long ret;
  89        struct smh_read_s {
  90                long fd;
  91                void *memp;
  92                size_t len;
  93        } read;
  94
  95        debug("%s: fd %ld, memp %p, len %zu\n", __func__, fd, memp, len);
  96
  97        read.fd = fd;
  98        read.memp = memp;
  99        read.len = len;
 100
 101        ret = smh_trap(SYSREAD, &read);
 102        if (ret < 0) {
 103                /*
 104                 * The ARM handler allows for returning partial lengths,
 105                 * but in practice this never happens so rather than create
 106                 * hard to maintain partial read loops and such, just fail
 107                 * with an error message.
 108                 */
 109                printf("%s: ERROR ret %ld, fd %ld, len %zu memp %p\n",
 110                       __func__, ret, fd, len, memp);
 111                return -1;
 112        }
 113
 114        return 0;
 115}
 116
 117/*
 118 * Close the file using the file descriptor
 119 */
 120static long smh_close(long fd)
 121{
 122        long ret;
 123
 124        debug("%s: fd %ld\n", __func__, fd);
 125
 126        ret = smh_trap(SYSCLOSE, &fd);
 127        if (ret == -1)
 128                printf("%s: ERROR fd %ld\n", __func__, fd);
 129
 130        return ret;
 131}
 132
 133/*
 134 * Get the file length from the file descriptor
 135 */
 136static long smh_len_fd(long fd)
 137{
 138        long ret;
 139
 140        debug("%s: fd %ld\n", __func__, fd);
 141
 142        ret = smh_trap(SYSFLEN, &fd);
 143        if (ret == -1)
 144                printf("%s: ERROR ret %ld, fd %ld\n", __func__, ret, fd);
 145
 146        return ret;
 147}
 148
 149static int smh_load_file(const char * const name, ulong load_addr,
 150                         ulong *end_addr)
 151{
 152        long fd;
 153        long len;
 154        long ret;
 155
 156        fd = smh_open(name, "rb");
 157        if (fd == -1)
 158                return -1;
 159
 160        len = smh_len_fd(fd);
 161        if (len < 0) {
 162                smh_close(fd);
 163                return -1;
 164        }
 165
 166        ret = smh_read(fd, (void *)load_addr, len);
 167        smh_close(fd);
 168
 169        if (ret == 0) {
 170                *end_addr = load_addr + len - 1;
 171                printf("loaded file %s from %08lX to %08lX, %08lX bytes\n",
 172                       name,
 173                       load_addr,
 174                       *end_addr,
 175                       len);
 176        } else {
 177                printf("read failed\n");
 178                return 0;
 179        }
 180
 181        return 0;
 182}
 183
 184static int do_smhload(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
 185{
 186        if (argc == 3 || argc == 4) {
 187                ulong load_addr;
 188                ulong end_addr = 0;
 189                int ret;
 190                char end_str[64];
 191
 192                load_addr = simple_strtoul(argv[2], NULL, 16);
 193                if (!load_addr)
 194                        return -1;
 195
 196                ret = smh_load_file(argv[1], load_addr, &end_addr);
 197                if (ret < 0)
 198                        return CMD_RET_FAILURE;
 199
 200                /* Optionally save returned end to the environment */
 201                if (argc == 4) {
 202                        sprintf(end_str, "0x%08lx", end_addr);
 203                        env_set(argv[3], end_str);
 204                }
 205        } else {
 206                return CMD_RET_USAGE;
 207        }
 208        return 0;
 209}
 210
 211U_BOOT_CMD(smhload, 4, 0, do_smhload, "load a file using semihosting",
 212           "<file> 0x<address> [end var]\n"
 213           "    - load a semihosted file to the address specified\n"
 214           "      if the optional [end var] is specified, the end\n"
 215           "      address of the file will be stored in this environment\n"
 216           "      variable.\n");
 217