blob: 939c0f75132b26dffa31abce4d9d181b51f6f461 [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Darwin Rambod32d4112014-06-09 11:12:59 -07002/*
Sean Anderson56e3fe82022-03-22 16:59:18 -04003 * Copyright (C) 2022 Sean Anderson <sean.anderson@seco.com>
Darwin Rambod32d4112014-06-09 11:12:59 -07004 * Copyright 2014 Broadcom Corporation
Darwin Rambod32d4112014-06-09 11:12:59 -07005 */
6
Darwin Rambod32d4112014-06-09 11:12:59 -07007#include <common.h>
Simon Glass0f2af882020-05-10 11:40:05 -06008#include <log.h>
Sean Andersonadf073a2022-03-22 16:59:14 -04009#include <semihosting.h>
Darwin Rambod32d4112014-06-09 11:12:59 -070010
11#define SYSOPEN 0x01
12#define SYSCLOSE 0x02
Sean Anderson1e9f4432022-03-22 16:59:23 -040013#define SYSWRITEC 0x03
14#define SYSWRITE0 0x04
Sean Anderson56e3fe82022-03-22 16:59:18 -040015#define SYSWRITE 0x05
Darwin Rambod32d4112014-06-09 11:12:59 -070016#define SYSREAD 0x06
Sean Anderson1e9f4432022-03-22 16:59:23 -040017#define SYSREADC 0x07
Sean Anderson52cf0f02022-03-22 16:59:30 -040018#define SYSISERROR 0x08
Sean Anderson56e3fe82022-03-22 16:59:18 -040019#define SYSSEEK 0x0A
Darwin Rambod32d4112014-06-09 11:12:59 -070020#define SYSFLEN 0x0C
Sean Anderson003eaa82022-03-22 16:59:16 -040021#define SYSERRNO 0x13
Darwin Rambod32d4112014-06-09 11:12:59 -070022
Andre Przywarab94ebde2022-10-05 17:38:48 +010023/*
24 * Macro to force the compiler to *populate* memory (for an array or struct)
25 * before passing the pointer to an inline assembly call.
26 */
27#define USE_PTR(ptr) *(const char (*)[]) (ptr)
28
Andre Przywarac1b6d762022-10-05 17:38:47 +010029#if defined(CONFIG_ARM64)
30 #define SMH_TRAP "hlt #0xf000"
31#elif defined(CONFIG_CPU_V7M)
32 #define SMH_TRAP "bkpt #0xAB"
33#elif defined(CONFIG_SYS_THUMB_BUILD)
34 #define SMH_TRAP "svc #0xab"
35#else
36 #define SMH_TRAP "svc #0x123456"
37#endif
38
Darwin Rambod32d4112014-06-09 11:12:59 -070039/*
40 * Call the handler
41 */
Andre Przywara0cd671b2022-10-05 17:38:49 +010042static long smh_trap(unsigned int sysnum, void *addr)
Darwin Rambod32d4112014-06-09 11:12:59 -070043{
Linus Walleij6fda2652014-12-15 11:05:56 +010044 register long result asm("r0");
Andre Przywara0cd671b2022-10-05 17:38:49 +010045 register void *_addr asm("r1") = addr;
Andre Przywarac1b6d762022-10-05 17:38:47 +010046
Andre Przywarab94ebde2022-10-05 17:38:48 +010047 /*
48 * We need a memory clobber (aka compiler barrier) for two reasons:
49 * - The compiler needs to populate any data structures pointed to
50 * by "addr" *before* the trap instruction is called.
51 * - At least the SYSREAD function puts the result into memory pointed
52 * to by "addr", so the compiler must not use a cached version of
53 * the previous content, after the call has finished.
54 */
Andre Przywarac1b6d762022-10-05 17:38:47 +010055 asm volatile (SMH_TRAP
56 : "=r" (result)
Andre Przywara0cd671b2022-10-05 17:38:49 +010057 : "0"(sysnum), "r"(USE_PTR(_addr))
Andre Przywarac1b6d762022-10-05 17:38:47 +010058 : "memory");
59
Darwin Rambod32d4112014-06-09 11:12:59 -070060 return result;
61}
62
Sean Anderson52cf0f02022-03-22 16:59:30 -040063#if CONFIG_IS_ENABLED(SEMIHOSTING_FALLBACK)
64static bool _semihosting_enabled = true;
65static bool try_semihosting = true;
66
67bool semihosting_enabled(void)
68{
69 if (try_semihosting) {
70 smh_trap(SYSERRNO, NULL);
71 try_semihosting = false;
72 }
73
74 return _semihosting_enabled;
75}
76
77void disable_semihosting(void)
78{
79 _semihosting_enabled = false;
80}
81#endif
82
Sean Anderson003eaa82022-03-22 16:59:16 -040083/**
84 * smh_errno() - Read the host's errno
85 *
86 * This gets the value of the host's errno and negates it. The host's errno may
87 * or may not be set, so only call this function if a previous semihosting call
88 * has failed.
89 *
90 * Return: a negative error value
Darwin Rambod32d4112014-06-09 11:12:59 -070091 */
Sean Anderson003eaa82022-03-22 16:59:16 -040092static int smh_errno(void)
93{
94 long ret = smh_trap(SYSERRNO, NULL);
95
96 if (ret > 0 && ret < INT_MAX)
97 return -ret;
98 return -EIO;
99}
100
Sean Andersona3a6c692022-03-22 16:59:15 -0400101long smh_open(const char *fname, enum smh_open_mode mode)
Darwin Rambod32d4112014-06-09 11:12:59 -0700102{
Linus Walleij6fda2652014-12-15 11:05:56 +0100103 long fd;
Linus Walleij7cc8ca82014-12-15 11:06:05 +0100104 struct smh_open_s {
105 const char *fname;
106 unsigned long mode;
107 size_t len;
108 } open;
Darwin Rambod32d4112014-06-09 11:12:59 -0700109
Sean Andersona3a6c692022-03-22 16:59:15 -0400110 debug("%s: file \'%s\', mode \'%u\'\n", __func__, fname, mode);
Darwin Rambod32d4112014-06-09 11:12:59 -0700111
Linus Walleij7cc8ca82014-12-15 11:06:05 +0100112 open.fname = fname;
113 open.len = strlen(fname);
114 open.mode = mode;
Darwin Rambod32d4112014-06-09 11:12:59 -0700115
Linus Walleij7cc8ca82014-12-15 11:06:05 +0100116 /* Open the file on the host */
117 fd = smh_trap(SYSOPEN, &open);
118 if (fd == -1)
Sean Anderson003eaa82022-03-22 16:59:16 -0400119 return smh_errno();
Linus Walleij7cc8ca82014-12-15 11:06:05 +0100120 return fd;
Darwin Rambod32d4112014-06-09 11:12:59 -0700121}
122
Sean Anderson56e3fe82022-03-22 16:59:18 -0400123/**
124 * struct smg_rdwr_s - Arguments for read and write
125 * @fd: A file descriptor returned from smh_open()
126 * @memp: Pointer to a buffer of memory of at least @len bytes
127 * @len: The number of bytes to read or write
128 */
129struct smh_rdwr_s {
130 long fd;
131 void *memp;
132 size_t len;
133};
134
Sean Andersonadf073a2022-03-22 16:59:14 -0400135long smh_read(long fd, void *memp, size_t len)
Darwin Rambod32d4112014-06-09 11:12:59 -0700136{
Linus Walleij6fda2652014-12-15 11:05:56 +0100137 long ret;
Sean Anderson56e3fe82022-03-22 16:59:18 -0400138 struct smh_rdwr_s read;
Darwin Rambod32d4112014-06-09 11:12:59 -0700139
Vadzim Dambrouski27382152015-10-19 19:40:15 +0300140 debug("%s: fd %ld, memp %p, len %zu\n", __func__, fd, memp, len);
Darwin Rambod32d4112014-06-09 11:12:59 -0700141
142 read.fd = fd;
143 read.memp = memp;
144 read.len = len;
145
146 ret = smh_trap(SYSREAD, &read);
Sean Anderson003eaa82022-03-22 16:59:16 -0400147 if (ret < 0)
148 return smh_errno();
149 return len - ret;
Darwin Rambod32d4112014-06-09 11:12:59 -0700150}
151
Sean Anderson56e3fe82022-03-22 16:59:18 -0400152long smh_write(long fd, const void *memp, size_t len, ulong *written)
153{
154 long ret;
155 struct smh_rdwr_s write;
156
157 debug("%s: fd %ld, memp %p, len %zu\n", __func__, fd, memp, len);
158
159 write.fd = fd;
160 write.memp = (void *)memp;
161 write.len = len;
162
163 ret = smh_trap(SYSWRITE, &write);
164 *written = len - ret;
165 if (ret)
166 return smh_errno();
167 return 0;
168}
169
Sean Andersonadf073a2022-03-22 16:59:14 -0400170long smh_close(long fd)
Darwin Rambod32d4112014-06-09 11:12:59 -0700171{
Linus Walleij6fda2652014-12-15 11:05:56 +0100172 long ret;
Darwin Rambod32d4112014-06-09 11:12:59 -0700173
Linus Walleij6fda2652014-12-15 11:05:56 +0100174 debug("%s: fd %ld\n", __func__, fd);
Darwin Rambod32d4112014-06-09 11:12:59 -0700175
Linus Walleij6fda2652014-12-15 11:05:56 +0100176 ret = smh_trap(SYSCLOSE, &fd);
Darwin Rambod32d4112014-06-09 11:12:59 -0700177 if (ret == -1)
Sean Anderson003eaa82022-03-22 16:59:16 -0400178 return smh_errno();
179 return 0;
Darwin Rambod32d4112014-06-09 11:12:59 -0700180}
181
Sean Andersonadf073a2022-03-22 16:59:14 -0400182long smh_flen(long fd)
Darwin Rambod32d4112014-06-09 11:12:59 -0700183{
Linus Walleij6fda2652014-12-15 11:05:56 +0100184 long ret;
Darwin Rambod32d4112014-06-09 11:12:59 -0700185
Linus Walleij6fda2652014-12-15 11:05:56 +0100186 debug("%s: fd %ld\n", __func__, fd);
Darwin Rambod32d4112014-06-09 11:12:59 -0700187
Linus Walleij6fda2652014-12-15 11:05:56 +0100188 ret = smh_trap(SYSFLEN, &fd);
Darwin Rambod32d4112014-06-09 11:12:59 -0700189 if (ret == -1)
Sean Anderson003eaa82022-03-22 16:59:16 -0400190 return smh_errno();
Darwin Rambod32d4112014-06-09 11:12:59 -0700191 return ret;
192}
193
Sean Anderson56e3fe82022-03-22 16:59:18 -0400194long smh_seek(long fd, long pos)
195{
196 long ret;
197 struct smh_seek_s {
198 long fd;
199 long pos;
200 } seek;
201
202 debug("%s: fd %ld pos %ld\n", __func__, fd, pos);
203
204 seek.fd = fd;
205 seek.pos = pos;
206
207 ret = smh_trap(SYSSEEK, &seek);
208 if (ret)
209 return smh_errno();
210 return 0;
211}
Sean Anderson1e9f4432022-03-22 16:59:23 -0400212
213int smh_getc(void)
214{
215 return smh_trap(SYSREADC, NULL);
216}
217
218void smh_putc(char ch)
219{
220 smh_trap(SYSWRITEC, &ch);
221}
222
223void smh_puts(const char *s)
224{
225 smh_trap(SYSWRITE0, (char *)s);
226}