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#else
  35        /* Note - untested placeholder */
  36        asm volatile ("svc #0x123456" : "=r" (result) : "0"(sysnum), "r"(addr));
  37#endif
  38        return result;
  39}
  40
  41/*
  42 * Open a file on the host. Mode is "r" or "rb" currently. Returns a file
  43 * descriptor or -1 on error.
  44 */
  45static long smh_open(const char *fname, char *modestr)
  46{
  47        long fd;
  48        unsigned long mode;
  49        struct smh_open_s {
  50                const char *fname;
  51                unsigned long mode;
  52                size_t len;
  53        } open;
  54
  55        debug("%s: file \'%s\', mode \'%s\'\n", __func__, fname, modestr);
  56
  57        /* Check the file mode */
  58        if (!(strcmp(modestr, "r"))) {
  59                mode = MODE_READ;
  60        } else if (!(strcmp(modestr, "rb"))) {
  61                mode = MODE_READBIN;
  62        } else {
  63                printf("%s: ERROR mode \'%s\' not supported\n", __func__,
  64                       modestr);
  65                return -1;
  66        }
  67
  68        open.fname = fname;
  69        open.len = strlen(fname);
  70        open.mode = mode;
  71
  72        /* Open the file on the host */
  73        fd = smh_trap(SYSOPEN, &open);
  74        if (fd == -1)
  75                printf("%s: ERROR fd %ld for file \'%s\'\n", __func__, fd,
  76                       fname);
  77
  78        return fd;
  79}
  80
  81/*
  82 * Read 'len' bytes of file into 'memp'. Returns 0 on success, else failure
  83 */
  84static long smh_read(long fd, void *memp, size_t len)
  85{
  86        long ret;
  87        struct smh_read_s {
  88                long fd;
  89                void *memp;
  90                size_t len;
  91        } read;
  92
  93        debug("%s: fd %ld, memp %p, len %lu\n", __func__, fd, memp, len);
  94
  95        read.fd = fd;
  96        read.memp = memp;
  97        read.len = len;
  98
  99        ret = smh_trap(SYSREAD, &read);
 100        if (ret < 0) {
 101                /*
 102                 * The ARM handler allows for returning partial lengths,
 103                 * but in practice this never happens so rather than create
 104                 * hard to maintain partial read loops and such, just fail
 105                 * with an error message.
 106                 */
 107                printf("%s: ERROR ret %ld, fd %ld, len %lu memp %p\n",
 108                       __func__, ret, fd, len, memp);
 109                return -1;
 110        }
 111
 112        return 0;
 113}
 114
 115/*
 116 * Close the file using the file descriptor
 117 */
 118static long smh_close(long fd)
 119{
 120        long ret;
 121
 122        debug("%s: fd %ld\n", __func__, fd);
 123
 124        ret = smh_trap(SYSCLOSE, &fd);
 125        if (ret == -1)
 126                printf("%s: ERROR fd %ld\n", __func__, fd);
 127
 128        return ret;
 129}
 130
 131/*
 132 * Get the file length from the file descriptor
 133 */
 134static long smh_len_fd(long fd)
 135{
 136        long ret;
 137
 138        debug("%s: fd %ld\n", __func__, fd);
 139
 140        ret = smh_trap(SYSFLEN, &fd);
 141        if (ret == -1)
 142                printf("%s: ERROR ret %ld, fd %ld\n", __func__, ret, fd);
 143
 144        return ret;
 145}
 146
 147static int smh_load_file(const char * const name, ulong load_addr,
 148                         ulong *end_addr)
 149{
 150        long fd;
 151        long len;
 152        long ret;
 153
 154        fd = smh_open(name, "rb");
 155        if (fd == -1)
 156                return -1;
 157
 158        len = smh_len_fd(fd);
 159        if (len < 0) {
 160                smh_close(fd);
 161                return -1;
 162        }
 163
 164        ret = smh_read(fd, (void *)load_addr, len);
 165        smh_close(fd);
 166
 167        if (ret == 0) {
 168                *end_addr = load_addr + len - 1;
 169                printf("loaded file %s from %08lX to %08lX, %08lX bytes\n",
 170                       name,
 171                       load_addr,
 172                       *end_addr,
 173                       len);
 174        } else {
 175                printf("read failed\n");
 176                return 0;
 177        }
 178
 179        return 0;
 180}
 181
 182static int do_smhload(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
 183{
 184        if (argc == 3 || argc == 4) {
 185                ulong load_addr;
 186                ulong end_addr = 0;
 187                ulong ret;
 188                char end_str[64];
 189
 190                load_addr = simple_strtoul(argv[2], NULL, 16);
 191                if (!load_addr)
 192                        return -1;
 193
 194                ret = smh_load_file(argv[1], load_addr, &end_addr);
 195                if (ret < 0)
 196                        return 1;
 197
 198                /* Optionally save returned end to the environment */
 199                if (argc == 4) {
 200                        sprintf(end_str, "0x%08lx", end_addr);
 201                        setenv(argv[3], end_str);
 202                }
 203        } else {
 204                return CMD_RET_USAGE;
 205        }
 206        return 0;
 207}
 208
 209U_BOOT_CMD(smhload, 4, 0, do_smhload, "load a file using semihosting",
 210           "<file> 0x<address> [end var]\n"
 211           "    - load a semihosted file to the address specified\n"
 212           "      if the optional [end var] is specified, the end\n"
 213           "      address of the file will be stored in this environment\n"
 214           "      variable.\n");
 215