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 <log.h>
8 #include <semihosting.h>
9 #include <linux/errno.h>
10 #include <linux/string.h>
11
12 #define SYSOPEN 0x01
13 #define SYSCLOSE 0x02
14 #define SYSWRITEC 0x03
15 #define SYSWRITE0 0x04
16 #define SYSWRITE 0x05
17 #define SYSREAD 0x06
18 #define SYSREADC 0x07
19 #define SYSISERROR 0x08
20 #define SYSSEEK 0x0A
21 #define SYSFLEN 0x0C
22 #define SYSERRNO 0x13
23
24 #if CONFIG_IS_ENABLED(SEMIHOSTING_FALLBACK)
25 static bool _semihosting_enabled = true;
26 static bool try_semihosting = true;
27
semihosting_enabled(void)28 bool semihosting_enabled(void)
29 {
30 if (try_semihosting) {
31 smh_trap(SYSERRNO, NULL);
32 try_semihosting = false;
33 }
34
35 return _semihosting_enabled;
36 }
37
disable_semihosting(void)38 void disable_semihosting(void)
39 {
40 _semihosting_enabled = false;
41 }
42 #endif
43
44 /**
45 * smh_errno() - Read the host's errno
46 *
47 * This gets the value of the host's errno and negates it. The host's errno may
48 * or may not be set, so only call this function if a previous semihosting call
49 * has failed.
50 *
51 * Return: a negative error value
52 */
smh_errno(void)53 static int smh_errno(void)
54 {
55 long ret = smh_trap(SYSERRNO, NULL);
56
57 if (ret > 0 && ret < INT_MAX)
58 return -ret;
59 return -EIO;
60 }
61
smh_open(const char * fname,enum smh_open_mode mode)62 long smh_open(const char *fname, enum smh_open_mode mode)
63 {
64 long fd;
65 struct smh_open_s {
66 const char *fname;
67 unsigned long mode;
68 size_t len;
69 } open;
70
71 debug("%s: file \'%s\', mode \'%u\'\n", __func__, fname, mode);
72
73 open.fname = fname;
74 open.len = strlen(fname);
75 open.mode = mode;
76
77 /* Open the file on the host */
78 fd = smh_trap(SYSOPEN, &open);
79 if (fd == -1)
80 return smh_errno();
81 return fd;
82 }
83
84 /**
85 * struct smg_rdwr_s - Arguments for read and write
86 * @fd: A file descriptor returned from smh_open()
87 * @memp: Pointer to a buffer of memory of at least @len bytes
88 * @len: The number of bytes to read or write
89 */
90 struct smh_rdwr_s {
91 long fd;
92 void *memp;
93 size_t len;
94 };
95
smh_read(long fd,void * memp,size_t len)96 long smh_read(long fd, void *memp, size_t len)
97 {
98 long ret;
99 struct smh_rdwr_s read;
100
101 debug("%s: fd %ld, memp %p, len %zu\n", __func__, fd, memp, len);
102
103 read.fd = fd;
104 read.memp = memp;
105 read.len = len;
106
107 ret = smh_trap(SYSREAD, &read);
108 if (ret < 0)
109 return smh_errno();
110 return len - ret;
111 }
112
smh_write(long fd,const void * memp,size_t len,ulong * written)113 long smh_write(long fd, const void *memp, size_t len, ulong *written)
114 {
115 long ret;
116 struct smh_rdwr_s write;
117
118 debug("%s: fd %ld, memp %p, len %zu\n", __func__, fd, memp, len);
119
120 write.fd = fd;
121 write.memp = (void *)memp;
122 write.len = len;
123
124 ret = smh_trap(SYSWRITE, &write);
125 *written = len - ret;
126 if (ret)
127 return smh_errno();
128 return 0;
129 }
130
smh_close(long fd)131 long smh_close(long fd)
132 {
133 long ret;
134
135 debug("%s: fd %ld\n", __func__, fd);
136
137 ret = smh_trap(SYSCLOSE, &fd);
138 if (ret == -1)
139 return smh_errno();
140 return 0;
141 }
142
smh_flen(long fd)143 long smh_flen(long fd)
144 {
145 long ret;
146
147 debug("%s: fd %ld\n", __func__, fd);
148
149 ret = smh_trap(SYSFLEN, &fd);
150 if (ret == -1)
151 return smh_errno();
152 return ret;
153 }
154
smh_seek(long fd,long pos)155 long smh_seek(long fd, long pos)
156 {
157 long ret;
158 struct smh_seek_s {
159 long fd;
160 long pos;
161 } seek;
162
163 debug("%s: fd %ld pos %ld\n", __func__, fd, pos);
164
165 seek.fd = fd;
166 seek.pos = pos;
167
168 ret = smh_trap(SYSSEEK, &seek);
169 if (ret)
170 return smh_errno();
171 return 0;
172 }
173
smh_getc(void)174 int smh_getc(void)
175 {
176 return smh_trap(SYSREADC, NULL);
177 }
178
smh_putc(char ch)179 void smh_putc(char ch)
180 {
181 smh_trap(SYSWRITEC, &ch);
182 }
183
smh_puts(const char * s)184 void smh_puts(const char *s)
185 {
186 smh_trap(SYSWRITE0, (char *)s);
187 }
188