// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2022 Sean Anderson * Copyright 2014 Broadcom Corporation */ /* * This code has been tested on arm64/aarch64 fastmodel only. An untested * placeholder exists for armv7 architectures, but since they are commonly * available in silicon now, fastmodel usage makes less sense for them. */ #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 /* * Call the handler */ 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)); #endif 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); }