// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2022 Sean Anderson * Copyright 2014 Broadcom Corporation */ #include #include #include #define SYSOPEN 0x01 #define SYSCLOSE 0x02 #define SYSWRITEC 0x03 #define SYSWRITE0 0x04 #define SYSWRITE 0x05 #define SYSREAD 0x06 #define SYSREADC 0x07 #define SYSISERROR 0x08 #define SYSSEEK 0x0A #define SYSFLEN 0x0C #define SYSERRNO 0x13 /* * Macro to force the compiler to *populate* memory (for an array or struct) * before passing the pointer to an inline assembly call. */ #define USE_PTR(ptr) *(const char (*)[]) (ptr) #if defined(CONFIG_ARM64) #define SMH_TRAP "hlt #0xf000" #elif defined(CONFIG_CPU_V7M) #define SMH_TRAP "bkpt #0xAB" #elif defined(CONFIG_SYS_THUMB_BUILD) #define SMH_TRAP "svc #0xab" #else #define SMH_TRAP "svc #0x123456" #endif /* * Call the handler */ static long smh_trap(unsigned int sysnum, void *addr) { register long result asm("r0"); register void *_addr asm("r1") = addr; /* * We need a memory clobber (aka compiler barrier) for two reasons: * - The compiler needs to populate any data structures pointed to * by "addr" *before* the trap instruction is called. * - At least the SYSREAD function puts the result into memory pointed * to by "addr", so the compiler must not use a cached version of * the previous content, after the call has finished. */ asm volatile (SMH_TRAP : "=r" (result) : "0"(sysnum), "r"(USE_PTR(_addr)) : "memory"); return result; } #if CONFIG_IS_ENABLED(SEMIHOSTING_FALLBACK) static bool _semihosting_enabled = true; static bool try_semihosting = true; bool semihosting_enabled(void) { if (try_semihosting) { smh_trap(SYSERRNO, NULL); try_semihosting = false; } return _semihosting_enabled; } void disable_semihosting(void) { _semihosting_enabled = false; } #endif /** * smh_errno() - Read the host's errno * * This gets the value of the host's errno and negates it. The host's errno may * or may not be set, so only call this function if a previous semihosting call * has failed. * * Return: a negative error value */ static int smh_errno(void) { long ret = smh_trap(SYSERRNO, NULL); if (ret > 0 && ret < INT_MAX) return -ret; return -EIO; } long smh_open(const char *fname, enum smh_open_mode mode) { long fd; struct smh_open_s { const char *fname; unsigned long mode; size_t len; } open; debug("%s: file \'%s\', mode \'%u\'\n", __func__, fname, mode); open.fname = fname; open.len = strlen(fname); open.mode = mode; /* Open the file on the host */ fd = smh_trap(SYSOPEN, &open); if (fd == -1) return smh_errno(); return fd; } /** * struct smg_rdwr_s - Arguments for read and write * @fd: A file descriptor returned from smh_open() * @memp: Pointer to a buffer of memory of at least @len bytes * @len: The number of bytes to read or write */ struct smh_rdwr_s { long fd; void *memp; size_t len; }; long smh_read(long fd, void *memp, size_t len) { long ret; struct smh_rdwr_s read; debug("%s: fd %ld, memp %p, len %zu\n", __func__, fd, memp, len); read.fd = fd; read.memp = memp; read.len = len; ret = smh_trap(SYSREAD, &read); if (ret < 0) return smh_errno(); return len - ret; } long smh_write(long fd, const void *memp, size_t len, ulong *written) { long ret; struct smh_rdwr_s write; debug("%s: fd %ld, memp %p, len %zu\n", __func__, fd, memp, len); write.fd = fd; write.memp = (void *)memp; write.len = len; ret = smh_trap(SYSWRITE, &write); *written = len - ret; if (ret) return smh_errno(); return 0; } long smh_close(long fd) { long ret; debug("%s: fd %ld\n", __func__, fd); ret = smh_trap(SYSCLOSE, &fd); if (ret == -1) return smh_errno(); return 0; } long smh_flen(long fd) { long ret; debug("%s: fd %ld\n", __func__, fd); ret = smh_trap(SYSFLEN, &fd); if (ret == -1) return smh_errno(); return ret; } long smh_seek(long fd, long pos) { long ret; struct smh_seek_s { long fd; long pos; } seek; debug("%s: fd %ld pos %ld\n", __func__, fd, pos); seek.fd = fd; seek.pos = pos; ret = smh_trap(SYSSEEK, &seek); if (ret) return smh_errno(); return 0; } int smh_getc(void) { return smh_trap(SYSREADC, NULL); } void smh_putc(char ch) { smh_trap(SYSWRITEC, &ch); } void smh_puts(const char *s) { smh_trap(SYSWRITE0, (char *)s); }