blob: a430f86bf3e0a59779dd38ff65e834999a569bf7 [file] [log] [blame]
Karthikeyan Ramasubramanian1668cc62012-09-23 22:23:36 -06001/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12
13#include <linux/module.h>
14#include <linux/types.h>
15#include <linux/net.h>
16#include <linux/socket.h>
17#include <linux/errno.h>
18#include <linux/mm.h>
19#include <linux/poll.h>
20#include <linux/fcntl.h>
21#include <linux/gfp.h>
22#include <linux/msm_ipc.h>
23
24#include <asm/string.h>
25#include <asm/atomic.h>
26
27#include <net/sock.h>
28
Karthikeyan Ramasubramanian70444192012-07-12 10:25:49 -060029#include <mach/msm_ipc_router.h>
30
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070031#include "ipc_router.h"
Karthikeyan Ramasubramanian1668cc62012-09-23 22:23:36 -060032#include "msm_ipc_router_security.h"
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070033
34#define msm_ipc_sk(sk) ((struct msm_ipc_sock *)(sk))
35#define msm_ipc_sk_port(sk) ((struct msm_ipc_port *)(msm_ipc_sk(sk)->port))
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070036
37static int sockets_enabled;
38static struct proto msm_ipc_proto;
39static const struct proto_ops msm_ipc_proto_ops;
40
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070041static struct sk_buff_head *msm_ipc_router_build_msg(unsigned int num_sect,
42 struct iovec const *msg_sect,
43 size_t total_len)
44{
45 struct sk_buff_head *msg_head;
46 struct sk_buff *msg;
47 int i, copied, first = 1;
48 int data_size = 0, request_size, offset;
49 void *data;
50
51 for (i = 0; i < num_sect; i++)
52 data_size += msg_sect[i].iov_len;
53
54 if (!data_size)
55 return NULL;
56
57 msg_head = kmalloc(sizeof(struct sk_buff_head), GFP_KERNEL);
58 if (!msg_head) {
59 pr_err("%s: cannot allocate skb_head\n", __func__);
60 return NULL;
61 }
62 skb_queue_head_init(msg_head);
63
64 for (copied = 1, i = 0; copied && (i < num_sect); i++) {
65 data_size = msg_sect[i].iov_len;
66 offset = 0;
67 while (offset != msg_sect[i].iov_len) {
68 request_size = data_size;
69 if (first)
70 request_size += IPC_ROUTER_HDR_SIZE;
71
72 msg = alloc_skb(request_size, GFP_KERNEL);
73 if (!msg) {
74 if (request_size <= (PAGE_SIZE/2)) {
75 pr_err("%s: cannot allocated skb\n",
76 __func__);
77 goto msg_build_failure;
78 }
79 data_size = data_size / 2;
80 continue;
81 }
82
83 if (first) {
84 skb_reserve(msg, IPC_ROUTER_HDR_SIZE);
85 first = 0;
86 }
87
88 data = skb_put(msg, data_size);
89 copied = !copy_from_user(msg->data,
90 msg_sect[i].iov_base + offset,
91 data_size);
92 if (!copied) {
93 pr_err("%s: copy_from_user failed\n",
94 __func__);
95 kfree_skb(msg);
96 goto msg_build_failure;
97 }
98 skb_queue_tail(msg_head, msg);
99 offset += data_size;
100 data_size = msg_sect[i].iov_len - offset;
101 }
102 }
103 return msg_head;
104
105msg_build_failure:
106 while (!skb_queue_empty(msg_head)) {
107 msg = skb_dequeue(msg_head);
108 kfree_skb(msg);
109 }
110 kfree(msg_head);
111 return NULL;
112}
113
114static int msm_ipc_router_extract_msg(struct msghdr *m,
115 struct sk_buff_head *msg_head)
116{
Karthikeyan Ramasubramanian2272b882013-04-05 11:30:53 -0600117 struct sockaddr_msm_ipc *addr;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700118 struct rr_header *hdr;
119 struct sk_buff *temp;
120 int offset = 0, data_len = 0, copy_len;
121
122 if (!m || !msg_head) {
123 pr_err("%s: Invalid pointers passed\n", __func__);
124 return -EINVAL;
125 }
Karthikeyan Ramasubramanian2272b882013-04-05 11:30:53 -0600126 addr = (struct sockaddr_msm_ipc *)m->msg_name;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700127
128 temp = skb_peek(msg_head);
129 hdr = (struct rr_header *)(temp->data);
Karthikeyan Ramasubramanian2272b882013-04-05 11:30:53 -0600130 if (addr && (hdr->src_port_id != IPC_ROUTER_ADDRESS)) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700131 addr->family = AF_MSM_IPC;
132 addr->address.addrtype = MSM_IPC_ADDR_ID;
133 addr->address.addr.port_addr.node_id = hdr->src_node_id;
134 addr->address.addr.port_addr.port_id = hdr->src_port_id;
135 m->msg_namelen = sizeof(struct sockaddr_msm_ipc);
136 }
137
138 data_len = hdr->size;
139 skb_pull(temp, IPC_ROUTER_HDR_SIZE);
140 skb_queue_walk(msg_head, temp) {
141 copy_len = data_len < temp->len ? data_len : temp->len;
142 if (copy_to_user(m->msg_iov->iov_base + offset, temp->data,
143 copy_len)) {
144 pr_err("%s: Copy to user failed\n", __func__);
145 return -EFAULT;
146 }
147 offset += copy_len;
148 data_len -= copy_len;
149 }
150 return offset;
151}
152
153static void msm_ipc_router_release_msg(struct sk_buff_head *msg_head)
154{
155 struct sk_buff *temp;
156
157 if (!msg_head) {
158 pr_err("%s: Invalid msg pointer\n", __func__);
159 return;
160 }
161
162 while (!skb_queue_empty(msg_head)) {
163 temp = skb_dequeue(msg_head);
164 kfree_skb(temp);
165 }
166 kfree(msg_head);
167}
168
169static int msm_ipc_router_create(struct net *net,
170 struct socket *sock,
171 int protocol,
172 int kern)
173{
174 struct sock *sk;
175 struct msm_ipc_port *port_ptr;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700176
177 if (unlikely(protocol != 0)) {
178 pr_err("%s: Protocol not supported\n", __func__);
179 return -EPROTONOSUPPORT;
180 }
181
182 switch (sock->type) {
183 case SOCK_DGRAM:
184 break;
185 default:
186 pr_err("%s: Protocol type not supported\n", __func__);
187 return -EPROTOTYPE;
188 }
189
190 sk = sk_alloc(net, AF_MSM_IPC, GFP_KERNEL, &msm_ipc_proto);
191 if (!sk) {
192 pr_err("%s: sk_alloc failed\n", __func__);
193 return -ENOMEM;
194 }
195
196 port_ptr = msm_ipc_router_create_raw_port(sk, NULL, NULL);
197 if (!port_ptr) {
198 pr_err("%s: port_ptr alloc failed\n", __func__);
199 sk_free(sk);
200 return -ENOMEM;
201 }
202
Karthikeyan Ramasubramanian1668cc62012-09-23 22:23:36 -0600203 port_ptr->check_send_permissions = msm_ipc_check_send_permissions;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700204 sock->ops = &msm_ipc_proto_ops;
205 sock_init_data(sock, sk);
206 sk->sk_rcvtimeo = DEFAULT_RCV_TIMEO;
207
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700208 msm_ipc_sk(sk)->port = port_ptr;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700209
210 return 0;
211}
212
213int msm_ipc_router_bind(struct socket *sock, struct sockaddr *uaddr,
214 int uaddr_len)
215{
216 struct sockaddr_msm_ipc *addr = (struct sockaddr_msm_ipc *)uaddr;
217 struct sock *sk = sock->sk;
218 struct msm_ipc_port *port_ptr;
219 int ret;
Karthikeyan Ramasubramanianaf90ba82013-06-03 12:05:50 -0600220 void *pil;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700221
222 if (!sk)
223 return -EINVAL;
224
Karthikeyan Ramasubramanian54426332013-01-24 11:45:41 -0700225 if (!check_permissions()) {
226 pr_err("%s: %s Do not have permissions\n",
227 __func__, current->comm);
228 return -EPERM;
229 }
230
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700231 if (!uaddr_len) {
232 pr_err("%s: Invalid address length\n", __func__);
233 return -EINVAL;
234 }
235
236 if (addr->family != AF_MSM_IPC) {
237 pr_err("%s: Address family is incorrect\n", __func__);
238 return -EAFNOSUPPORT;
239 }
240
241 if (addr->address.addrtype != MSM_IPC_ADDR_NAME) {
242 pr_err("%s: Address type is incorrect\n", __func__);
243 return -EINVAL;
244 }
245
246 port_ptr = msm_ipc_sk_port(sk);
247 if (!port_ptr)
248 return -ENODEV;
249
Karthikeyan Ramasubramanianaf90ba82013-06-03 12:05:50 -0600250 pil = msm_ipc_load_default_node();
251 msm_ipc_sk(sk)->default_pil = pil;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700252 lock_sock(sk);
253
254 ret = msm_ipc_router_register_server(port_ptr, &addr->address);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700255
256 release_sock(sk);
257 return ret;
258}
259
260static int msm_ipc_router_sendmsg(struct kiocb *iocb, struct socket *sock,
261 struct msghdr *m, size_t total_len)
262{
263 struct sock *sk = sock->sk;
264 struct msm_ipc_port *port_ptr = msm_ipc_sk_port(sk);
265 struct sockaddr_msm_ipc *dest = (struct sockaddr_msm_ipc *)m->msg_name;
266 struct sk_buff_head *msg;
267 int ret;
268
269 if (!dest)
270 return -EDESTADDRREQ;
271
272 if (m->msg_namelen < sizeof(*dest) || dest->family != AF_MSM_IPC)
273 return -EINVAL;
274
275 if (total_len > MAX_IPC_PKT_SIZE)
276 return -EINVAL;
277
278 lock_sock(sk);
279 msg = msm_ipc_router_build_msg(m->msg_iovlen, m->msg_iov, total_len);
280 if (!msg) {
281 pr_err("%s: Msg build failure\n", __func__);
282 ret = -ENOMEM;
283 goto out_sendmsg;
284 }
285
Karthikeyan Ramasubramaniand6dbfae2013-01-16 09:00:28 -0700286 if (port_ptr->type == CLIENT_PORT)
287 wait_for_irsc_completion();
288
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700289 ret = msm_ipc_router_send_to(port_ptr, msg, &dest->address);
290 if (ret == (IPC_ROUTER_HDR_SIZE + total_len))
291 ret = total_len;
292
293out_sendmsg:
294 release_sock(sk);
295 return ret;
296}
297
298static int msm_ipc_router_recvmsg(struct kiocb *iocb, struct socket *sock,
299 struct msghdr *m, size_t buf_len, int flags)
300{
301 struct sock *sk = sock->sk;
302 struct msm_ipc_port *port_ptr = msm_ipc_sk_port(sk);
303 struct sk_buff_head *msg;
304 long timeout;
305 int ret;
306
307 if (m->msg_iovlen != 1)
308 return -EOPNOTSUPP;
309
310 if (!buf_len)
311 return -EINVAL;
312
313 lock_sock(sk);
314 timeout = sk->sk_rcvtimeo;
315 mutex_lock(&port_ptr->port_rx_q_lock);
316 while (list_empty(&port_ptr->port_rx_q)) {
317 mutex_unlock(&port_ptr->port_rx_q_lock);
318 release_sock(sk);
319 if (timeout < 0) {
320 ret = wait_event_interruptible(
321 port_ptr->port_rx_wait_q,
322 !list_empty(&port_ptr->port_rx_q));
323 if (ret)
324 return ret;
325 } else if (timeout > 0) {
326 timeout = wait_event_interruptible_timeout(
327 port_ptr->port_rx_wait_q,
328 !list_empty(&port_ptr->port_rx_q),
329 timeout);
330 if (timeout < 0)
331 return -EFAULT;
332 }
333
334 if (timeout == 0)
335 return -ETIMEDOUT;
336 lock_sock(sk);
337 mutex_lock(&port_ptr->port_rx_q_lock);
338 }
339 mutex_unlock(&port_ptr->port_rx_q_lock);
340
341 ret = msm_ipc_router_read(port_ptr, &msg, buf_len);
342 if (ret <= 0 || !msg) {
343 release_sock(sk);
344 return ret;
345 }
346
347 ret = msm_ipc_router_extract_msg(m, msg);
348 msm_ipc_router_release_msg(msg);
349 msg = NULL;
350 release_sock(sk);
351 return ret;
352}
353
354static int msm_ipc_router_ioctl(struct socket *sock,
355 unsigned int cmd, unsigned long arg)
356{
357 struct sock *sk = sock->sk;
358 struct msm_ipc_port *port_ptr;
359 struct server_lookup_args server_arg;
Karthikeyan Ramasubramaniandfde01b2012-06-12 14:25:13 -0600360 struct msm_ipc_server_info *srv_info = NULL;
361 unsigned int n, srv_info_sz = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700362 int ret;
Karthikeyan Ramasubramanianaf90ba82013-06-03 12:05:50 -0600363 void *pil;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700364
365 if (!sk)
366 return -EINVAL;
367
368 lock_sock(sk);
369 port_ptr = msm_ipc_sk_port(sock->sk);
370 if (!port_ptr) {
371 release_sock(sk);
372 return -EINVAL;
373 }
374
375 switch (cmd) {
376 case IPC_ROUTER_IOCTL_GET_VERSION:
377 n = IPC_ROUTER_VERSION;
378 ret = put_user(n, (unsigned int *)arg);
379 break;
380
381 case IPC_ROUTER_IOCTL_GET_MTU:
382 n = (MAX_IPC_PKT_SIZE - IPC_ROUTER_HDR_SIZE);
383 ret = put_user(n, (unsigned int *)arg);
384 break;
385
386 case IPC_ROUTER_IOCTL_GET_CURR_PKT_SIZE:
387 ret = msm_ipc_router_get_curr_pkt_size(port_ptr);
388 break;
389
390 case IPC_ROUTER_IOCTL_LOOKUP_SERVER:
Karthikeyan Ramasubramanianaf90ba82013-06-03 12:05:50 -0600391 pil = msm_ipc_load_default_node();
392 msm_ipc_sk(sk)->default_pil = pil;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700393 ret = copy_from_user(&server_arg, (void *)arg,
394 sizeof(server_arg));
395 if (ret) {
396 ret = -EFAULT;
397 break;
398 }
399
400 if (server_arg.num_entries_in_array < 0) {
401 ret = -EINVAL;
402 break;
403 }
404 if (server_arg.num_entries_in_array) {
Karthikeyan Ramasubramaniandfde01b2012-06-12 14:25:13 -0600405 srv_info_sz = server_arg.num_entries_in_array *
406 sizeof(*srv_info);
Karthikeyan Ramasubramanianabe0b062013-02-26 16:37:50 -0700407 if ((srv_info_sz / sizeof(*srv_info)) !=
408 server_arg.num_entries_in_array) {
409 pr_err("%s: Integer Overflow %d * %d\n",
410 __func__, sizeof(*srv_info),
411 server_arg.num_entries_in_array);
412 ret = -EINVAL;
413 break;
414 }
Karthikeyan Ramasubramaniandfde01b2012-06-12 14:25:13 -0600415 srv_info = kmalloc(srv_info_sz, GFP_KERNEL);
416 if (!srv_info) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700417 ret = -ENOMEM;
418 break;
419 }
420 }
421 ret = msm_ipc_router_lookup_server_name(&server_arg.port_name,
Karthikeyan Ramasubramaniandfde01b2012-06-12 14:25:13 -0600422 srv_info, server_arg.num_entries_in_array,
Karthikeyan Ramasubramaniancc450c92011-07-27 14:38:15 -0600423 server_arg.lookup_mask);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700424 if (ret < 0) {
425 pr_err("%s: Server not found\n", __func__);
426 ret = -ENODEV;
Karthikeyan Ramasubramaniandfde01b2012-06-12 14:25:13 -0600427 kfree(srv_info);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700428 break;
429 }
430 server_arg.num_entries_found = ret;
431
432 ret = copy_to_user((void *)arg, &server_arg,
433 sizeof(server_arg));
Karthikeyan Ramasubramaniandfde01b2012-06-12 14:25:13 -0600434 if (srv_info_sz) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700435 ret = copy_to_user((void *)(arg + sizeof(server_arg)),
Karthikeyan Ramasubramaniandfde01b2012-06-12 14:25:13 -0600436 srv_info, srv_info_sz);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700437 if (ret)
438 ret = -EFAULT;
Karthikeyan Ramasubramaniandfde01b2012-06-12 14:25:13 -0600439 kfree(srv_info);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700440 }
441 break;
442
443 case IPC_ROUTER_IOCTL_BIND_CONTROL_PORT:
444 ret = msm_ipc_router_bind_control_port(port_ptr);
445 break;
446
Karthikeyan Ramasubramanian1668cc62012-09-23 22:23:36 -0600447 case IPC_ROUTER_IOCTL_CONFIG_SEC_RULES:
448 ret = msm_ipc_config_sec_rules((void *)arg);
Karthikeyan Ramasubramaniand6dbfae2013-01-16 09:00:28 -0700449 if (ret != -EPERM)
450 port_ptr->type = IRSC_PORT;
Karthikeyan Ramasubramanian1668cc62012-09-23 22:23:36 -0600451 break;
452
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700453 default:
454 ret = -EINVAL;
455 }
456 release_sock(sk);
457 return ret;
458}
459
460static unsigned int msm_ipc_router_poll(struct file *file,
461 struct socket *sock, poll_table *wait)
462{
463 struct sock *sk = sock->sk;
464 struct msm_ipc_port *port_ptr;
465 uint32_t mask = 0;
466
467 if (!sk)
468 return -EINVAL;
469
470 port_ptr = msm_ipc_sk_port(sk);
471 if (!port_ptr)
472 return -EINVAL;
473
474 poll_wait(file, &port_ptr->port_rx_wait_q, wait);
475
476 if (!list_empty(&port_ptr->port_rx_q))
477 mask |= (POLLRDNORM | POLLIN);
478
479 return mask;
480}
481
482static int msm_ipc_router_close(struct socket *sock)
483{
484 struct sock *sk = sock->sk;
485 struct msm_ipc_port *port_ptr = msm_ipc_sk_port(sk);
Karthikeyan Ramasubramanianaf90ba82013-06-03 12:05:50 -0600486 void *pil = msm_ipc_sk(sk)->default_pil;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700487 int ret;
488
489 lock_sock(sk);
490 ret = msm_ipc_router_close_port(port_ptr);
Karthikeyan Ramasubramanianaf90ba82013-06-03 12:05:50 -0600491 if (pil)
492 msm_ipc_unload_default_node(pil);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700493 release_sock(sk);
494 sock_put(sk);
495 sock->sk = NULL;
496
497 return ret;
498}
499
500static const struct net_proto_family msm_ipc_family_ops = {
501 .owner = THIS_MODULE,
502 .family = AF_MSM_IPC,
503 .create = msm_ipc_router_create
504};
505
506static const struct proto_ops msm_ipc_proto_ops = {
507 .owner = THIS_MODULE,
508 .family = AF_MSM_IPC,
509 .bind = msm_ipc_router_bind,
510 .connect = sock_no_connect,
511 .sendmsg = msm_ipc_router_sendmsg,
512 .recvmsg = msm_ipc_router_recvmsg,
513 .ioctl = msm_ipc_router_ioctl,
514 .poll = msm_ipc_router_poll,
515 .setsockopt = sock_no_setsockopt,
516 .getsockopt = sock_no_getsockopt,
517 .release = msm_ipc_router_close,
518};
519
520static struct proto msm_ipc_proto = {
521 .name = "MSM_IPC",
522 .owner = THIS_MODULE,
523 .obj_size = sizeof(struct msm_ipc_sock),
524};
525
526int msm_ipc_router_init_sockets(void)
527{
528 int ret;
529
530 ret = proto_register(&msm_ipc_proto, 1);
531 if (ret) {
532 pr_err("Failed to register MSM_IPC protocol type\n");
533 goto out_init_sockets;
534 }
535
536 ret = sock_register(&msm_ipc_family_ops);
537 if (ret) {
538 pr_err("Failed to register MSM_IPC socket type\n");
539 proto_unregister(&msm_ipc_proto);
540 goto out_init_sockets;
541 }
542
543 sockets_enabled = 1;
544out_init_sockets:
545 return ret;
546}
547
548void msm_ipc_router_exit_sockets(void)
549{
550 if (!sockets_enabled)
551 return;
552
553 sockets_enabled = 0;
554 sock_unregister(msm_ipc_family_ops.family);
555 proto_unregister(&msm_ipc_proto);
556}