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