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