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 rpc->nacked = (atomic_flag)ATOMIC_FLAG_INIT;
89 atomic_flag_test_and_set(&rpc->nacked);
90 ret = rpmsg_create_ept(&rpc->ept, rdev,
91 ept_name, ept_addr, ept_raddr,
92 rpmsg_rpc_ept_cb, rpmsg_service_unbind);
93 if (ret != 0) {
94 metal_mutex_release(&rpc->lock);
95 return -EINVAL;
96 }
97 while (!is_rpmsg_ept_ready(&rpc->ept)) {
98 if (rpc->poll)
99 rpc->poll(rpc->poll_arg);
100 }
101 return 0;
102 }
103
rpmsg_rpc_release(struct rpmsg_rpc_data * rpc)104 void rpmsg_rpc_release(struct rpmsg_rpc_data *rpc)
105 {
106 if (!rpc)
107 return;
108 if (rpc->ept_destroyed == 0)
109 rpmsg_destroy_ept(&rpc->ept);
110 metal_mutex_acquire(&rpc->lock);
111 metal_spinlock_acquire(&rpc->buflock);
112 rpc->respbuf = NULL;
113 rpc->respbuf_len = 0;
114 metal_spinlock_release(&rpc->buflock);
115 metal_mutex_release(&rpc->lock);
116 metal_mutex_deinit(&rpc->lock);
117 }
118
rpmsg_rpc_send(struct rpmsg_rpc_data * rpc,void * req,size_t len,void * resp,size_t resp_len)119 int rpmsg_rpc_send(struct rpmsg_rpc_data *rpc,
120 void *req, size_t len,
121 void *resp, size_t resp_len)
122 {
123 int ret;
124
125 if (!rpc)
126 return -EINVAL;
127 metal_spinlock_acquire(&rpc->buflock);
128 rpc->respbuf = resp;
129 rpc->respbuf_len = resp_len;
130 metal_spinlock_release(&rpc->buflock);
131 (void)atomic_flag_test_and_set(&rpc->nacked);
132 ret = rpmsg_send(&rpc->ept, req, len);
133 if (ret < 0)
134 return -EINVAL;
135 if (!resp)
136 return ret;
137 while ((atomic_flag_test_and_set(&rpc->nacked))) {
138 if (rpc->poll)
139 rpc->poll(rpc->poll_arg);
140 }
141 return ret;
142 }
143
rpmsg_set_default_rpc(struct rpmsg_rpc_data * rpc)144 void rpmsg_set_default_rpc(struct rpmsg_rpc_data *rpc)
145 {
146 if (!rpc)
147 return;
148 rpmsg_default_rpc = rpc;
149 }
150
151 /*************************************************************************
152 *
153 * FUNCTION
154 *
155 * _open
156 *
157 * DESCRIPTION
158 *
159 * Open a file. Minimal implementation
160 *
161 *************************************************************************/
162 #define MAX_BUF_LEN 496UL
163
_open(const char * filename,int flags,int mode)164 int _open(const char *filename, int flags, int mode)
165 {
166 struct rpmsg_rpc_data *rpc = rpmsg_default_rpc;
167 struct rpmsg_rpc_syscall *syscall;
168 struct rpmsg_rpc_syscall resp;
169 int filename_len = strlen(filename) + 1;
170 unsigned int payload_size = sizeof(*syscall) + filename_len;
171 unsigned char tmpbuf[MAX_BUF_LEN];
172 int ret;
173
174 if (!filename || payload_size > (int)MAX_BUF_LEN) {
175 return -EINVAL;
176 }
177
178 if (!rpc)
179 return -EINVAL;
180
181 /* Construct rpc payload */
182 syscall = (void *)tmpbuf;
183 syscall->id = OPEN_SYSCALL_ID;
184 syscall->args.int_field1 = flags;
185 syscall->args.int_field2 = mode;
186 syscall->args.data_len = filename_len;
187 memcpy(tmpbuf + sizeof(*syscall), filename, filename_len);
188
189 resp.id = 0;
190 ret = rpmsg_rpc_send(rpc, tmpbuf, payload_size,
191 (void *)&resp, sizeof(resp));
192 if (ret >= 0) {
193 /* Obtain return args and return to caller */
194 if (resp.id == OPEN_SYSCALL_ID)
195 ret = resp.args.int_field1;
196 else
197 ret = -EINVAL;
198 }
199
200 return ret;
201 }
202
203 /*************************************************************************
204 *
205 * FUNCTION
206 *
207 * _read
208 *
209 * DESCRIPTION
210 *
211 * Low level function to redirect IO to serial.
212 *
213 *************************************************************************/
_read(int fd,char * buffer,int buflen)214 int _read(int fd, char *buffer, int buflen)
215 {
216 struct rpmsg_rpc_syscall syscall;
217 struct rpmsg_rpc_syscall *resp;
218 struct rpmsg_rpc_data *rpc = rpmsg_default_rpc;
219 int payload_size = sizeof(syscall);
220 unsigned char tmpbuf[MAX_BUF_LEN];
221 int ret;
222
223 if (!rpc || !buffer || buflen == 0)
224 return -EINVAL;
225
226 /* Construct rpc payload */
227 syscall.id = READ_SYSCALL_ID;
228 syscall.args.int_field1 = fd;
229 syscall.args.int_field2 = buflen;
230 syscall.args.data_len = 0; /*not used */
231
232 resp = (void *)tmpbuf;
233 resp->id = 0;
234 ret = rpmsg_rpc_send(rpc, (void *)&syscall, payload_size,
235 tmpbuf, sizeof(tmpbuf));
236
237 /* Obtain return args and return to caller */
238 if (ret >= 0) {
239 if (resp->id == READ_SYSCALL_ID) {
240 if (resp->args.int_field1 > 0) {
241 int tmplen = resp->args.data_len;
242 unsigned char *tmpptr = tmpbuf;
243
244 tmpptr += sizeof(*resp);
245 if (tmplen > buflen)
246 tmplen = buflen;
247 memcpy(buffer, tmpptr, tmplen);
248 }
249 ret = resp->args.int_field1;
250 } else {
251 ret = -EINVAL;
252 }
253 }
254
255 return ret;
256 }
257
258 /*************************************************************************
259 *
260 * FUNCTION
261 *
262 * _write
263 *
264 * DESCRIPTION
265 *
266 * Low level function to redirect IO to serial.
267 *
268 *************************************************************************/
_write(int fd,const char * ptr,int len)269 int _write(int fd, const char *ptr, int len)
270 {
271 int ret;
272 struct rpmsg_rpc_syscall *syscall;
273 struct rpmsg_rpc_syscall resp;
274 int payload_size = sizeof(*syscall) + len;
275 struct rpmsg_rpc_data *rpc = rpmsg_default_rpc;
276 unsigned char tmpbuf[MAX_BUF_LEN];
277 unsigned char *tmpptr;
278 int null_term = 0;
279
280 if (!rpc)
281 return -EINVAL;
282 if (fd == 1)
283 null_term = 1;
284
285 syscall = (void *)tmpbuf;
286 syscall->id = WRITE_SYSCALL_ID;
287 syscall->args.int_field1 = fd;
288 syscall->args.int_field2 = len;
289 syscall->args.data_len = len + null_term;
290 tmpptr = tmpbuf + sizeof(*syscall);
291 memcpy(tmpptr, ptr, len);
292 if (null_term == 1) {
293 *(char *)(tmpptr + len + null_term) = 0;
294 payload_size += 1;
295 }
296 resp.id = 0;
297 ret = rpmsg_rpc_send(rpc, tmpbuf, payload_size,
298 (void *)&resp, sizeof(resp));
299
300 if (ret >= 0) {
301 if (resp.id == WRITE_SYSCALL_ID)
302 ret = resp.args.int_field1;
303 else
304 ret = -EINVAL;
305 }
306
307 return ret;
308
309 }
310
311 /*************************************************************************
312 *
313 * FUNCTION
314 *
315 * _close
316 *
317 * DESCRIPTION
318 *
319 * Close a file. Minimal implementation
320 *
321 *************************************************************************/
_close(int fd)322 int _close(int fd)
323 {
324 int ret;
325 struct rpmsg_rpc_syscall syscall;
326 struct rpmsg_rpc_syscall resp;
327 int payload_size = sizeof(syscall);
328 struct rpmsg_rpc_data *rpc = rpmsg_default_rpc;
329
330 if (!rpc)
331 return -EINVAL;
332 syscall.id = CLOSE_SYSCALL_ID;
333 syscall.args.int_field1 = fd;
334 syscall.args.int_field2 = 0; /*not used */
335 syscall.args.data_len = 0; /*not used */
336
337 resp.id = 0;
338 ret = rpmsg_rpc_send(rpc, (void *)&syscall, payload_size,
339 (void *)&resp, sizeof(resp));
340
341 if (ret >= 0) {
342 if (resp.id == CLOSE_SYSCALL_ID)
343 ret = resp.args.int_field1;
344 else
345 ret = -EINVAL;
346 }
347
348 return ret;
349 }
350