uboot/arch/arm/lib/semihosting.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Copyright (C) 2022 Sean Anderson <sean.anderson@seco.com>
   4 * Copyright 2014 Broadcom Corporation
   5 */
   6
   7/*
   8 * This code has been tested on arm64/aarch64 fastmodel only.  An untested
   9 * placeholder exists for armv7 architectures, but since they are commonly
  10 * available in silicon now, fastmodel usage makes less sense for them.
  11 */
  12#include <common.h>
  13#include <log.h>
  14#include <semihosting.h>
  15
  16#define SYSOPEN         0x01
  17#define SYSCLOSE        0x02
  18#define SYSWRITEC       0x03
  19#define SYSWRITE0       0x04
  20#define SYSWRITE        0x05
  21#define SYSREAD         0x06
  22#define SYSREADC        0x07
  23#define SYSISERROR      0x08
  24#define SYSSEEK         0x0A
  25#define SYSFLEN         0x0C
  26#define SYSERRNO        0x13
  27
  28/*
  29 * Call the handler
  30 */
  31static noinline long smh_trap(unsigned int sysnum, void *addr)
  32{
  33        register long result asm("r0");
  34#if defined(CONFIG_ARM64)
  35        asm volatile ("hlt #0xf000" : "=r" (result) : "0"(sysnum), "r"(addr) : "memory");
  36#elif defined(CONFIG_CPU_V7M)
  37        asm volatile ("bkpt #0xAB" : "=r" (result) : "0"(sysnum), "r"(addr) : "memory");
  38#else
  39        /* Note - untested placeholder */
  40        asm volatile ("svc #0x123456" : "=r" (result) : "0"(sysnum), "r"(addr) : "memory");
  41#endif
  42        return result;
  43}
  44
  45#if CONFIG_IS_ENABLED(SEMIHOSTING_FALLBACK)
  46static bool _semihosting_enabled = true;
  47static bool try_semihosting = true;
  48
  49bool semihosting_enabled(void)
  50{
  51        if (try_semihosting) {
  52                smh_trap(SYSERRNO, NULL);
  53                try_semihosting = false;
  54        }
  55
  56        return _semihosting_enabled;
  57}
  58
  59void disable_semihosting(void)
  60{
  61        _semihosting_enabled = false;
  62}
  63#endif
  64
  65/**
  66 * smh_errno() - Read the host's errno
  67 *
  68 * This gets the value of the host's errno and negates it. The host's errno may
  69 * or may not be set, so only call this function if a previous semihosting call
  70 * has failed.
  71 *
  72 * Return: a negative error value
  73 */
  74static int smh_errno(void)
  75{
  76        long ret = smh_trap(SYSERRNO, NULL);
  77
  78        if (ret > 0 && ret < INT_MAX)
  79                return -ret;
  80        return -EIO;
  81}
  82
  83long smh_open(const char *fname, enum smh_open_mode mode)
  84{
  85        long fd;
  86        struct smh_open_s {
  87                const char *fname;
  88                unsigned long mode;
  89                size_t len;
  90        } open;
  91
  92        debug("%s: file \'%s\', mode \'%u\'\n", __func__, fname, mode);
  93
  94        open.fname = fname;
  95        open.len = strlen(fname);
  96        open.mode = mode;
  97
  98        /* Open the file on the host */
  99        fd = smh_trap(SYSOPEN, &open);
 100        if (fd == -1)
 101                return smh_errno();
 102        return fd;
 103}
 104
 105/**
 106 * struct smg_rdwr_s - Arguments for read and write
 107 * @fd: A file descriptor returned from smh_open()
 108 * @memp: Pointer to a buffer of memory of at least @len bytes
 109 * @len: The number of bytes to read or write
 110 */
 111struct smh_rdwr_s {
 112        long fd;
 113        void *memp;
 114        size_t len;
 115};
 116
 117long smh_read(long fd, void *memp, size_t len)
 118{
 119        long ret;
 120        struct smh_rdwr_s read;
 121
 122        debug("%s: fd %ld, memp %p, len %zu\n", __func__, fd, memp, len);
 123
 124        read.fd = fd;
 125        read.memp = memp;
 126        read.len = len;
 127
 128        ret = smh_trap(SYSREAD, &read);
 129        if (ret < 0)
 130                return smh_errno();
 131        return len - ret;
 132}
 133
 134long smh_write(long fd, const void *memp, size_t len, ulong *written)
 135{
 136        long ret;
 137        struct smh_rdwr_s write;
 138
 139        debug("%s: fd %ld, memp %p, len %zu\n", __func__, fd, memp, len);
 140
 141        write.fd = fd;
 142        write.memp = (void *)memp;
 143        write.len = len;
 144
 145        ret = smh_trap(SYSWRITE, &write);
 146        *written = len - ret;
 147        if (ret)
 148                return smh_errno();
 149        return 0;
 150}
 151
 152long smh_close(long fd)
 153{
 154        long ret;
 155
 156        debug("%s: fd %ld\n", __func__, fd);
 157
 158        ret = smh_trap(SYSCLOSE, &fd);
 159        if (ret == -1)
 160                return smh_errno();
 161        return 0;
 162}
 163
 164long smh_flen(long fd)
 165{
 166        long ret;
 167
 168        debug("%s: fd %ld\n", __func__, fd);
 169
 170        ret = smh_trap(SYSFLEN, &fd);
 171        if (ret == -1)
 172                return smh_errno();
 173        return ret;
 174}
 175
 176long smh_seek(long fd, long pos)
 177{
 178        long ret;
 179        struct smh_seek_s {
 180                long fd;
 181                long pos;
 182        } seek;
 183
 184        debug("%s: fd %ld pos %ld\n", __func__, fd, pos);
 185
 186        seek.fd = fd;
 187        seek.pos = pos;
 188
 189        ret = smh_trap(SYSSEEK, &seek);
 190        if (ret)
 191                return smh_errno();
 192        return 0;
 193}
 194
 195int smh_getc(void)
 196{
 197        return smh_trap(SYSREADC, NULL);
 198}
 199
 200void smh_putc(char ch)
 201{
 202        smh_trap(SYSWRITEC, &ch);
 203}
 204
 205void smh_puts(const char *s)
 206{
 207        smh_trap(SYSWRITE0, (char *)s);
 208}
 209