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