global: Migrate CONFIG_SYS_FSL* symbols to the CFG_SYS namespace
[platform/kernel/u-boot.git] / arch / arm / lib / semihosting.c
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 #include <common.h>
8 #include <log.h>
9 #include <semihosting.h>
10
11 #define SYSOPEN         0x01
12 #define SYSCLOSE        0x02
13 #define SYSWRITEC       0x03
14 #define SYSWRITE0       0x04
15 #define SYSWRITE        0x05
16 #define SYSREAD         0x06
17 #define SYSREADC        0x07
18 #define SYSISERROR      0x08
19 #define SYSSEEK         0x0A
20 #define SYSFLEN         0x0C
21 #define SYSERRNO        0x13
22
23 /*
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
29 #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
39 /*
40  * Call the handler
41  */
42 static long smh_trap(unsigned int sysnum, void *addr)
43 {
44         register long result asm("r0");
45         register void *_addr asm("r1") = addr;
46
47         /*
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          */
55         asm volatile (SMH_TRAP
56                       : "=r" (result)
57                       : "0"(sysnum), "r"(USE_PTR(_addr))
58                       : "memory");
59
60         return result;
61 }
62
63 #if CONFIG_IS_ENABLED(SEMIHOSTING_FALLBACK)
64 static bool _semihosting_enabled = true;
65 static bool try_semihosting = true;
66
67 bool 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
77 void disable_semihosting(void)
78 {
79         _semihosting_enabled = false;
80 }
81 #endif
82
83 /**
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
91  */
92 static 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
101 long smh_open(const char *fname, enum smh_open_mode mode)
102 {
103         long fd;
104         struct smh_open_s {
105                 const char *fname;
106                 unsigned long mode;
107                 size_t len;
108         } open;
109
110         debug("%s: file \'%s\', mode \'%u\'\n", __func__, fname, mode);
111
112         open.fname = fname;
113         open.len = strlen(fname);
114         open.mode = mode;
115
116         /* Open the file on the host */
117         fd = smh_trap(SYSOPEN, &open);
118         if (fd == -1)
119                 return smh_errno();
120         return fd;
121 }
122
123 /**
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  */
129 struct smh_rdwr_s {
130         long fd;
131         void *memp;
132         size_t len;
133 };
134
135 long smh_read(long fd, void *memp, size_t len)
136 {
137         long ret;
138         struct smh_rdwr_s read;
139
140         debug("%s: fd %ld, memp %p, len %zu\n", __func__, fd, memp, len);
141
142         read.fd = fd;
143         read.memp = memp;
144         read.len = len;
145
146         ret = smh_trap(SYSREAD, &read);
147         if (ret < 0)
148                 return smh_errno();
149         return len - ret;
150 }
151
152 long 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
170 long smh_close(long fd)
171 {
172         long ret;
173
174         debug("%s: fd %ld\n", __func__, fd);
175
176         ret = smh_trap(SYSCLOSE, &fd);
177         if (ret == -1)
178                 return smh_errno();
179         return 0;
180 }
181
182 long smh_flen(long fd)
183 {
184         long ret;
185
186         debug("%s: fd %ld\n", __func__, fd);
187
188         ret = smh_trap(SYSFLEN, &fd);
189         if (ret == -1)
190                 return smh_errno();
191         return ret;
192 }
193
194 long 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 }
212
213 int smh_getc(void)
214 {
215         return smh_trap(SYSREADC, NULL);
216 }
217
218 void smh_putc(char ch)
219 {
220         smh_trap(SYSWRITEC, &ch);
221 }
222
223 void smh_puts(const char *s)
224 {
225         smh_trap(SYSWRITE0, (char *)s);
226 }