1 /*
2 * Copyright (c) 2014, Mentor Graphics Corporation
3 * All rights reserved.
4 *
5 * SPDX-License-Identifier: BSD-3-Clause
6 */
7
8 #include <errno.h>
9 #include <metal/mutex.h>
10 #include <metal/spinlock.h>
11 #include <metal/utilities.h>
12 #include <openamp/open_amp.h>
13 #include <openamp/rpmsg_retarget.h>
14 #include <stdio.h>
15 #include <string.h>
16 #include <fcntl.h>
17
18 /*************************************************************************
19 * Description
20 * This files contains rpmsg based redefinitions for C RTL system calls
21 * such as _open, _read, _write, _close.
22 *************************************************************************/
23 static struct rpmsg_rpc_data *rpmsg_default_rpc;
24
rpmsg_rpc_ept_cb(struct rpmsg_endpoint * ept,void * data,size_t len,uint32_t src,void * priv)25 static int rpmsg_rpc_ept_cb(struct rpmsg_endpoint *ept, void *data, size_t len,
26 uint32_t src, void *priv)
27 {
28 struct rpmsg_rpc_syscall *syscall;
29
30 (void)priv;
31 (void)src;
32
33 if (data && ept) {
34 syscall = data;
35 if (syscall->id == TERM_SYSCALL_ID) {
36 rpmsg_destroy_ept(ept);
37 } else {
38 struct rpmsg_rpc_data *rpc;
39
40 rpc = metal_container_of(ept,
41 struct rpmsg_rpc_data,
42 ept);
43 metal_spinlock_acquire(&rpc->buflock);
44 if (rpc->respbuf && rpc->respbuf_len != 0) {
45 if (len > rpc->respbuf_len)
46 len = rpc->respbuf_len;
47 memcpy(rpc->respbuf, data, len);
48 }
49 atomic_flag_clear(&rpc->nacked);
50 metal_spinlock_release(&rpc->buflock);
51 }
52 }
53
54 return RPMSG_SUCCESS;
55 }
56
rpmsg_service_unbind(struct rpmsg_endpoint * ept)57 static void rpmsg_service_unbind(struct rpmsg_endpoint *ept)
58 {
59 struct rpmsg_rpc_data *rpc;
60
61 rpc = metal_container_of(ept, struct rpmsg_rpc_data, ept);
62 rpc->ept_destroyed = 1;
63 rpmsg_destroy_ept(ept);
64 atomic_flag_clear(&rpc->nacked);
65 if (rpc->shutdown_cb)
66 rpc->shutdown_cb(rpc);
67 }
68
rpmsg_rpc_init(struct rpmsg_rpc_data * rpc,struct rpmsg_device * rdev,const char * ept_name,uint32_t ept_addr,uint32_t ept_raddr,void * poll_arg,rpmsg_rpc_poll poll,rpmsg_rpc_shutdown_cb shutdown_cb)69 int rpmsg_rpc_init(struct rpmsg_rpc_data *rpc,
70 struct rpmsg_device *rdev,
71 const char *ept_name, uint32_t ept_addr,
72 uint32_t ept_raddr,
73 void *poll_arg, rpmsg_rpc_poll poll,
74 rpmsg_rpc_shutdown_cb shutdown_cb)
75 {
76 int ret;
77
78 if (!rpc || !rdev)
79 return -EINVAL;
80 metal_spinlock_init(&rpc->buflock);
81 metal_mutex_init(&rpc->lock);
82 rpc->shutdown_cb = shutdown_cb;
83 rpc->poll_arg = poll_arg;
84 rpc->poll = poll;
85 rpc->ept_destroyed = 0;
86 rpc->respbuf = NULL;
87 rpc->respbuf_len = 0;
88 atomic_init(&rpc->nacked, 1);
89 ret = rpmsg_create_ept(&rpc->ept, rdev,
90 ept_name, ept_addr, ept_raddr,
91 rpmsg_rpc_ept_cb, rpmsg_service_unbind);
92 if (ret != 0) {
93 metal_mutex_release(&rpc->lock);
94 return -EINVAL;
95 }
96 while (!is_rpmsg_ept_ready(&rpc->ept)) {
97 if (rpc->poll)
98 rpc->poll(rpc->poll_arg);
99 }
100 return 0;
101 }
102
rpmsg_rpc_release(struct rpmsg_rpc_data * rpc)103 void rpmsg_rpc_release(struct rpmsg_rpc_data *rpc)
104 {
105 if (!rpc)
106 return;
107 if (rpc->ept_destroyed == 0)
108 rpmsg_destroy_ept(&rpc->ept);
109 metal_mutex_acquire(&rpc->lock);
110 metal_spinlock_acquire(&rpc->buflock);
111 rpc->respbuf = NULL;
112 rpc->respbuf_len = 0;
113 metal_spinlock_release(&rpc->buflock);
114 metal_mutex_release(&rpc->lock);
115 metal_mutex_deinit(&rpc->lock);
116 }
117
rpmsg_rpc_send(struct rpmsg_rpc_data * rpc,void * req,size_t len,void * resp,size_t resp_len)118 int rpmsg_rpc_send(struct rpmsg_rpc_data *rpc,
119 void *req, size_t len,
120 void *resp, size_t resp_len)
121 {
122 int ret;
123
124 if (!rpc)
125 return -EINVAL;
126 metal_spinlock_acquire(&rpc->buflock);
127 rpc->respbuf = resp;
128 rpc->respbuf_len = resp_len;
129 metal_spinlock_release(&rpc->buflock);
130 (void)atomic_flag_test_and_set(&rpc->nacked);
131 ret = rpmsg_send(&rpc->ept, req, len);
132 if (ret < 0)
133 return -EINVAL;
134 if (!resp)
135 return ret;
136 while ((atomic_flag_test_and_set(&rpc->nacked))) {
137 if (rpc->poll)
138 rpc->poll(rpc->poll_arg);
139 }
140 return ret;
141 }
142
rpmsg_set_default_rpc(struct rpmsg_rpc_data * rpc)143 void rpmsg_set_default_rpc(struct rpmsg_rpc_data *rpc)
144 {
145 if (!rpc)
146 return;
147 rpmsg_default_rpc = rpc;
148 }
149
150 /*************************************************************************
151 *
152 * FUNCTION
153 *
154 * _open
155 *
156 * DESCRIPTION
157 *
158 * Open a file. Minimal implementation
159 *
160 *************************************************************************/
161 #define MAX_BUF_LEN 496UL
162
_open(const char * filename,int flags,int mode)163 int _open(const char *filename, int flags, int mode)
164 {
165 struct rpmsg_rpc_data *rpc = rpmsg_default_rpc;
166 struct rpmsg_rpc_syscall *syscall;
167 struct rpmsg_rpc_syscall resp;
168 int filename_len = strlen(filename) + 1;
169 unsigned int payload_size = sizeof(*syscall) + filename_len;
170 unsigned char tmpbuf[MAX_BUF_LEN];
171 int ret;
172
173 if (!filename || payload_size > (int)MAX_BUF_LEN) {
174 return -EINVAL;
175 }
176
177 if (!rpc)
178 return -EINVAL;
179
180 /* Construct rpc payload */
181 syscall = (struct rpmsg_rpc_syscall *)tmpbuf;
182 syscall->id = OPEN_SYSCALL_ID;
183 syscall->args.int_field1 = flags;
184 syscall->args.int_field2 = mode;
185 syscall->args.data_len = filename_len;
186 memcpy(tmpbuf + sizeof(*syscall), filename, filename_len);
187
188 resp.id = 0;
189 ret = rpmsg_rpc_send(rpc, tmpbuf, payload_size,
190 (void *)&resp, sizeof(resp));
191 if (ret >= 0) {
192 /* Obtain return args and return to caller */
193 if (resp.id == OPEN_SYSCALL_ID)
194 ret = resp.args.int_field1;
195 else
196 ret = -EINVAL;
197 }
198
199 return ret;
200 }
201
202 /*************************************************************************
203 *
204 * FUNCTION
205 *
206 * _read
207 *
208 * DESCRIPTION
209 *
210 * Low level function to redirect IO to serial.
211 *
212 *************************************************************************/
_read(int fd,char * buffer,int buflen)213 int _read(int fd, char *buffer, int buflen)
214 {
215 struct rpmsg_rpc_syscall syscall;
216 struct rpmsg_rpc_syscall *resp;
217 struct rpmsg_rpc_data *rpc = rpmsg_default_rpc;
218 int payload_size = sizeof(syscall);
219 unsigned char tmpbuf[MAX_BUF_LEN];
220 int ret;
221
222 if (!rpc || !buffer || buflen == 0)
223 return -EINVAL;
224
225 /* Construct rpc payload */
226 syscall.id = READ_SYSCALL_ID;
227 syscall.args.int_field1 = fd;
228 syscall.args.int_field2 = buflen;
229 syscall.args.data_len = 0; /*not used */
230
231 resp = (struct rpmsg_rpc_syscall *)tmpbuf;
232 resp->id = 0;
233 ret = rpmsg_rpc_send(rpc, (void *)&syscall, payload_size,
234 tmpbuf, sizeof(tmpbuf));
235
236 /* Obtain return args and return to caller */
237 if (ret >= 0) {
238 if (resp->id == READ_SYSCALL_ID) {
239 if (resp->args.int_field1 > 0) {
240 int tmplen = resp->args.data_len;
241 unsigned char *tmpptr = tmpbuf;
242
243 tmpptr += sizeof(*resp);
244 if (tmplen > buflen)
245 tmplen = buflen;
246 memcpy(buffer, tmpptr, tmplen);
247 }
248 ret = resp->args.int_field1;
249 } else {
250 ret = -EINVAL;
251 }
252 }
253
254 return ret;
255 }
256
257 /*************************************************************************
258 *
259 * FUNCTION
260 *
261 * _write
262 *
263 * DESCRIPTION
264 *
265 * Low level function to redirect IO to serial.
266 *
267 *************************************************************************/
_write(int fd,const char * ptr,int len)268 int _write(int fd, const char *ptr, int len)
269 {
270 int ret;
271 struct rpmsg_rpc_syscall *syscall;
272 struct rpmsg_rpc_syscall resp;
273 int payload_size = sizeof(*syscall) + len;
274 struct rpmsg_rpc_data *rpc = rpmsg_default_rpc;
275 unsigned char tmpbuf[MAX_BUF_LEN];
276 unsigned char *tmpptr;
277 int null_term = 0;
278
279 if (!rpc)
280 return -EINVAL;
281 if (fd == 1)
282 null_term = 1;
283
284 syscall = (struct rpmsg_rpc_syscall *)tmpbuf;
285 syscall->id = WRITE_SYSCALL_ID;
286 syscall->args.int_field1 = fd;
287 syscall->args.int_field2 = len;
288 syscall->args.data_len = len + null_term;
289 tmpptr = tmpbuf + sizeof(*syscall);
290 memcpy(tmpptr, ptr, len);
291 if (null_term == 1) {
292 *(char *)(tmpptr + len + null_term) = 0;
293 payload_size += 1;
294 }
295 resp.id = 0;
296 ret = rpmsg_rpc_send(rpc, tmpbuf, payload_size,
297 (void *)&resp, sizeof(resp));
298
299 if (ret >= 0) {
300 if (resp.id == WRITE_SYSCALL_ID)
301 ret = resp.args.int_field1;
302 else
303 ret = -EINVAL;
304 }
305
306 return ret;
307
308 }
309
310 /*************************************************************************
311 *
312 * FUNCTION
313 *
314 * _close
315 *
316 * DESCRIPTION
317 *
318 * Close a file. Minimal implementation
319 *
320 *************************************************************************/
_close(int fd)321 int _close(int fd)
322 {
323 int ret;
324 struct rpmsg_rpc_syscall syscall;
325 struct rpmsg_rpc_syscall resp;
326 int payload_size = sizeof(syscall);
327 struct rpmsg_rpc_data *rpc = rpmsg_default_rpc;
328
329 if (!rpc)
330 return -EINVAL;
331 syscall.id = CLOSE_SYSCALL_ID;
332 syscall.args.int_field1 = fd;
333 syscall.args.int_field2 = 0; /*not used */
334 syscall.args.data_len = 0; /*not used */
335
336 resp.id = 0;
337 ret = rpmsg_rpc_send(rpc, (void *)&syscall, payload_size,
338 (void *)&resp, sizeof(resp));
339
340 if (ret >= 0) {
341 if (resp.id == CLOSE_SYSCALL_ID)
342 ret = resp.args.int_field1;
343 else
344 ret = -EINVAL;
345 }
346
347 return ret;
348 }
349