Merge tag 'fsl-qoriq-2022-10-18' of https://source.denx.de/u-boot/custodians/u-boot...
[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 /*
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  */
31 static 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)
46 static bool _semihosting_enabled = true;
47 static bool try_semihosting = true;
48
49 bool 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
59 void 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  */
74 static 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
83 long 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  */
111 struct smh_rdwr_s {
112         long fd;
113         void *memp;
114         size_t len;
115 };
116
117 long 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
134 long 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
152 long 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
164 long 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
176 long 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
195 int smh_getc(void)
196 {
197         return smh_trap(SYSREADC, NULL);
198 }
199
200 void smh_putc(char ch)
201 {
202         smh_trap(SYSWRITEC, &ch);
203 }
204
205 void smh_puts(const char *s)
206 {
207         smh_trap(SYSWRITE0, (char *)s);
208 }