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