blob: 937490d1ac21b085cc75687a84fa4632da7c1164 [file] [log] [blame]
Mona Hossaina5f1aab2012-03-29 10:18:07 -07001
Mona Hossainacea1022012-04-09 13:37:27 -07002
Mona Hossain2892b6b2012-02-17 13:53:11 -08003/* Qualcomm Secure Execution Environment Communicator (QSEECOM) driver
4 *
5 * Copyright (c) 2012, Code Aurora Forum. All rights reserved.
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 and
9 * only version 2 as published by the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 */
16
17#define pr_fmt(fmt) "QSEECOM: %s: " fmt, __func__
18
19#include <linux/kernel.h>
20#include <linux/slab.h>
21#include <linux/module.h>
22#include <linux/fs.h>
23#include <linux/platform_device.h>
24#include <linux/debugfs.h>
25#include <linux/cdev.h>
26#include <linux/uaccess.h>
27#include <linux/sched.h>
28#include <linux/list.h>
29#include <linux/mutex.h>
30#include <linux/io.h>
31#include <linux/ion.h>
32#include <linux/types.h>
33#include <linux/clk.h>
34#include <linux/qseecom.h>
Mona Hossainacea1022012-04-09 13:37:27 -070035#include <linux/freezer.h>
Mona Hossain2892b6b2012-02-17 13:53:11 -080036#include <mach/msm_bus.h>
37#include <mach/msm_bus_board.h>
38#include <mach/scm.h>
39#include <mach/peripheral-loader.h>
40#include "qseecom_legacy.h"
41
42#define QSEECOM_DEV "qseecom"
43#define QSEOS_VERSION_13 0x13
44#define QSEOS_VERSION_14 0x14
45#define QSEOS_CHECK_VERSION_CMD 0x00001803;
46
47enum qseecom_command_scm_resp_type {
48 QSEOS_APP_ID = 0xEE01,
49 QSEOS_LISTENER_ID
50};
51
52enum qseecom_qceos_cmd_id {
53 QSEOS_APP_START_COMMAND = 0x01,
54 QSEOS_APP_SHUTDOWN_COMMAND,
55 QSEOS_APP_LOOKUP_COMMAND,
56 QSEOS_REGISTER_LISTENER,
57 QSEOS_DEREGISTER_LISTENER,
58 QSEOS_CLIENT_SEND_DATA_COMMAND,
59 QSEOS_LISTENER_DATA_RSP_COMMAND,
60 QSEOS_CMD_MAX = 0xEFFFFFFF
61};
62
63enum qseecom_qceos_cmd_status {
64 QSEOS_RESULT_SUCCESS = 0,
65 QSEOS_RESULT_INCOMPLETE,
66 QSEOS_RESULT_FAILURE = 0xFFFFFFFF
67};
68
69__packed struct qseecom_check_app_ireq {
70 uint32_t qsee_cmd_id;
71 char app_name[MAX_APP_NAME_SIZE];
72};
73
74__packed struct qseecom_load_app_ireq {
75 uint32_t qsee_cmd_id;
76 uint32_t mdt_len; /* Length of the mdt file */
77 uint32_t img_len; /* Length of .bxx and .mdt files */
78 uint32_t phy_addr; /* phy addr of the start of image */
79 char app_name[MAX_APP_NAME_SIZE]; /* application name*/
80};
81
82__packed struct qseecom_unload_app_ireq {
83 uint32_t qsee_cmd_id;
84 uint32_t app_id;
85};
86
87__packed struct qseecom_register_listener_ireq {
88 uint32_t qsee_cmd_id;
89 uint32_t listener_id;
90 void *sb_ptr;
91 uint32_t sb_len;
92};
93
94__packed struct qseecom_unregister_listener_ireq {
95 uint32_t qsee_cmd_id;
96 uint32_t listener_id;
97};
98
99__packed struct qseecom_client_send_data_ireq {
100 uint32_t qsee_cmd_id;
101 uint32_t app_id;
102 void *req_ptr;
103 uint32_t req_len;
104 void *rsp_ptr; /* First 4 bytes should always be the return status */
105 uint32_t rsp_len;
106};
107
108/* send_data resp */
109__packed struct qseecom_client_listener_data_irsp {
110 uint32_t qsee_cmd_id;
111 uint32_t listener_id;
112};
113
114/*
115 * struct qseecom_command_scm_resp - qseecom response buffer
116 * @cmd_status: value from enum tz_sched_cmd_status
117 * @sb_in_rsp_addr: points to physical location of response
118 * buffer
119 * @sb_in_rsp_len: length of command response
120 */
121__packed struct qseecom_command_scm_resp {
122 uint32_t result;
123 enum qseecom_command_scm_resp_type resp_type;
124 unsigned int data;
125};
126
127static struct class *driver_class;
128static dev_t qseecom_device_no;
129static struct cdev qseecom_cdev;
130
131/* Data structures used in legacy support */
132static void *pil;
133static uint32_t pil_ref_cnt;
134static DEFINE_MUTEX(pil_access_lock);
135
136static DEFINE_MUTEX(send_msg_lock);
137static DEFINE_MUTEX(qsee_bw_mutex);
138static DEFINE_MUTEX(app_access_lock);
139
140static int qsee_bw_count;
141static struct clk *qseecom_bus_clk;
142static uint32_t qsee_perf_client;
143
144struct qseecom_registered_listener_list {
145 struct list_head list;
146 struct qseecom_register_listener_req svc;
147 u8 *sb_reg_req;
148 u8 *sb_virt;
149 s32 sb_phys;
150 size_t sb_length;
151 struct ion_handle *ihandle; /* Retrieve phy addr */
152
153 wait_queue_head_t rcv_req_wq;
154 int rcv_req_flag;
155};
156
157struct qseecom_registered_app_list {
158 struct list_head list;
159 u32 app_id;
160 u32 ref_cnt;
161};
162
163struct qseecom_control {
164 struct ion_client *ion_clnt; /* Ion client */
165 struct list_head registered_listener_list_head;
166 spinlock_t registered_listener_list_lock;
167
168 struct list_head registered_app_list_head;
169 spinlock_t registered_app_list_lock;
170
171 wait_queue_head_t send_resp_wq;
172 int send_resp_flag;
173
174 uint32_t qseos_version;
175};
176
177struct qseecom_client_handle {
178 u32 app_id;
179 u8 *sb_virt;
180 s32 sb_phys;
181 uint32_t user_virt_sb_base;
182 size_t sb_length;
183 struct ion_handle *ihandle; /* Retrieve phy addr */
184};
185
186struct qseecom_listener_handle {
187 u32 id;
188};
189
190static struct qseecom_control qseecom;
191
192struct qseecom_dev_handle {
193 bool service;
194 union {
195 struct qseecom_client_handle client;
196 struct qseecom_listener_handle listener;
197 };
198 bool released;
199 int abort;
200 wait_queue_head_t abort_wq;
201 atomic_t ioctl_count;
202};
203
204static int __qseecom_is_svc_unique(struct qseecom_dev_handle *data,
Mona Hossain0af10ab2012-02-28 18:26:41 -0800205 struct qseecom_register_listener_req *svc)
Mona Hossain2892b6b2012-02-17 13:53:11 -0800206{
207 struct qseecom_registered_listener_list *ptr;
208 int unique = 1;
209 unsigned long flags;
210
211 spin_lock_irqsave(&qseecom.registered_listener_list_lock, flags);
212 list_for_each_entry(ptr, &qseecom.registered_listener_list_head, list) {
Mona Hossain0af10ab2012-02-28 18:26:41 -0800213 if (ptr->svc.listener_id == svc->listener_id) {
Mona Hossain2892b6b2012-02-17 13:53:11 -0800214 pr_err("Service id: %u is already registered\n",
215 ptr->svc.listener_id);
216 unique = 0;
217 break;
218 }
219 }
220 spin_unlock_irqrestore(&qseecom.registered_listener_list_lock, flags);
221 return unique;
222}
223
224static struct qseecom_registered_listener_list *__qseecom_find_svc(
225 int32_t listener_id)
226{
227 struct qseecom_registered_listener_list *entry = NULL;
228 unsigned long flags;
229
230 spin_lock_irqsave(&qseecom.registered_listener_list_lock, flags);
231 list_for_each_entry(entry, &qseecom.registered_listener_list_head, list)
232 {
233 if (entry->svc.listener_id == listener_id)
234 break;
235 }
236 spin_unlock_irqrestore(&qseecom.registered_listener_list_lock, flags);
237 return entry;
238}
239
240static int __qseecom_set_sb_memory(struct qseecom_registered_listener_list *svc,
241 struct qseecom_dev_handle *handle,
242 struct qseecom_register_listener_req *listener)
243{
244 int ret = 0;
245 unsigned int flags = 0;
246 struct qseecom_register_listener_ireq req;
247 struct qseecom_command_scm_resp resp;
248 ion_phys_addr_t pa;
249
250 /* Get the handle of the shared fd */
251 svc->ihandle = ion_import_fd(qseecom.ion_clnt, listener->ifd_data_fd);
252 if (svc->ihandle == NULL) {
253 pr_err("Ion client could not retrieve the handle\n");
254 return -ENOMEM;
255 }
256
257 /* Get the physical address of the ION BUF */
258 ret = ion_phys(qseecom.ion_clnt, svc->ihandle, &pa, &svc->sb_length);
259
260 /* Populate the structure for sending scm call to load image */
261 svc->sb_virt = (char *) ion_map_kernel(qseecom.ion_clnt,
262 svc->ihandle, flags);
263 svc->sb_phys = pa;
264
265 if (qseecom.qseos_version == QSEOS_VERSION_14) {
266 req.qsee_cmd_id = QSEOS_REGISTER_LISTENER;
267 req.listener_id = svc->svc.listener_id;
268 req.sb_len = svc->sb_length;
269 req.sb_ptr = (void *)svc->sb_phys;
270
271 resp.result = QSEOS_RESULT_INCOMPLETE;
272
273 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &req,
274 sizeof(req), &resp, sizeof(resp));
275 if (ret) {
276 pr_err("qseecom_scm_call failed with err: %d\n", ret);
277 return -EINVAL;
278 }
279
280 if (resp.result != QSEOS_RESULT_SUCCESS) {
281 pr_err("Error SB registration req: resp.result = %d\n",
282 resp.result);
283 return -EPERM;
284 }
285 } else {
286 struct qseecom_command cmd;
287 struct qseecom_response resp;
288 struct qse_pr_init_sb_req_s sb_init_req;
289 struct qse_pr_init_sb_rsp_s sb_init_rsp;
290
291 svc->sb_reg_req = kzalloc((sizeof(sb_init_req) +
292 sizeof(sb_init_rsp)), GFP_KERNEL);
293
294 sb_init_req.pr_cmd = TZ_SCHED_CMD_ID_REGISTER_LISTENER;
295 sb_init_req.listener_id = svc->svc.listener_id;
296 sb_init_req.sb_len = svc->sb_length;
297 sb_init_req.sb_ptr = svc->sb_phys;
298
299 memcpy(svc->sb_reg_req, &sb_init_req, sizeof(sb_init_req));
300
301 /* It will always be a new cmd from this method */
302 cmd.cmd_type = TZ_SCHED_CMD_NEW;
303 cmd.sb_in_cmd_addr = (u8 *)(virt_to_phys(svc->sb_reg_req));
304 cmd.sb_in_cmd_len = sizeof(sb_init_req);
305
306 resp.cmd_status = TZ_SCHED_STATUS_INCOMPLETE;
307
308 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &cmd, sizeof(cmd)
309 , &resp, sizeof(resp));
310
311 if (ret) {
312 pr_err("qseecom_scm_call failed with err: %d\n", ret);
313 return -EINVAL;
314 }
315
316 if (resp.cmd_status != TZ_SCHED_STATUS_COMPLETE) {
317 pr_err("SB registration fail resp.cmd_status %d\n",
318 resp.cmd_status);
319 return -EINVAL;
320 }
321 memset(svc->sb_virt, 0, svc->sb_length);
322 }
323 return 0;
324}
325
326static int qseecom_register_listener(struct qseecom_dev_handle *data,
327 void __user *argp)
328{
329 int ret = 0;
330 unsigned long flags;
331 struct qseecom_register_listener_req rcvd_lstnr;
332 struct qseecom_registered_listener_list *new_entry;
333
334 ret = copy_from_user(&rcvd_lstnr, argp, sizeof(rcvd_lstnr));
335 if (ret) {
336 pr_err("copy_from_user failed\n");
337 return ret;
338 }
Mona Hossain0af10ab2012-02-28 18:26:41 -0800339 data->listener.id = 0;
340 data->service = true;
341 if (!__qseecom_is_svc_unique(data, &rcvd_lstnr)) {
Mona Hossain2892b6b2012-02-17 13:53:11 -0800342 pr_err("Service is not unique and is already registered\n");
Mona Hossain0af10ab2012-02-28 18:26:41 -0800343 data->released = true;
344 return -EBUSY;
Mona Hossain2892b6b2012-02-17 13:53:11 -0800345 }
346
347 new_entry = kmalloc(sizeof(*new_entry), GFP_KERNEL);
348 if (!new_entry) {
349 pr_err("kmalloc failed\n");
350 return -ENOMEM;
351 }
352 memcpy(&new_entry->svc, &rcvd_lstnr, sizeof(rcvd_lstnr));
353 new_entry->rcv_req_flag = 0;
354
355 new_entry->svc.listener_id = rcvd_lstnr.listener_id;
356 new_entry->sb_length = rcvd_lstnr.sb_size;
357 if (__qseecom_set_sb_memory(new_entry, data, &rcvd_lstnr)) {
358 pr_err("qseecom_set_sb_memoryfailed\n");
359 kzfree(new_entry);
360 return -ENOMEM;
361 }
Mona Hossain0af10ab2012-02-28 18:26:41 -0800362
Mona Hossain2892b6b2012-02-17 13:53:11 -0800363 data->listener.id = rcvd_lstnr.listener_id;
Mona Hossain2892b6b2012-02-17 13:53:11 -0800364 init_waitqueue_head(&new_entry->rcv_req_wq);
365
366 spin_lock_irqsave(&qseecom.registered_listener_list_lock, flags);
367 list_add_tail(&new_entry->list, &qseecom.registered_listener_list_head);
368 spin_unlock_irqrestore(&qseecom.registered_listener_list_lock, flags);
Mona Hossain0af10ab2012-02-28 18:26:41 -0800369
Mona Hossain2892b6b2012-02-17 13:53:11 -0800370 return ret;
371}
372
373static int qseecom_unregister_listener(struct qseecom_dev_handle *data)
374{
375 int ret = 0;
376 unsigned long flags;
377 uint32_t unmap_mem = 0;
378 struct qseecom_register_listener_ireq req;
379 struct qseecom_registered_listener_list *ptr_svc = NULL;
380 struct qseecom_command_scm_resp resp;
381 struct ion_handle *ihandle = NULL; /* Retrieve phy addr */
382
383 if (qseecom.qseos_version == QSEOS_VERSION_14) {
384 req.qsee_cmd_id = QSEOS_DEREGISTER_LISTENER;
385 req.listener_id = data->listener.id;
386 resp.result = QSEOS_RESULT_INCOMPLETE;
387
388 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &req,
389 sizeof(req), &resp, sizeof(resp));
390 if (ret) {
391 pr_err("qseecom_scm_call failed with err: %d\n", ret);
392 return ret;
393 }
394
395 if (resp.result != QSEOS_RESULT_SUCCESS) {
396 pr_err("SB deregistartion: result=%d\n", resp.result);
397 return -EPERM;
398 }
399 } else {
400 struct qse_pr_init_sb_req_s sb_init_req;
401 struct qseecom_command cmd;
402 struct qseecom_response resp;
403 struct qseecom_registered_listener_list *svc;
404
405 svc = __qseecom_find_svc(data->listener.id);
406 sb_init_req.pr_cmd = TZ_SCHED_CMD_ID_REGISTER_LISTENER;
407 sb_init_req.listener_id = data->listener.id;
408 sb_init_req.sb_len = 0;
409 sb_init_req.sb_ptr = 0;
410
411 memcpy(svc->sb_reg_req, &sb_init_req, sizeof(sb_init_req));
412
413 /* It will always be a new cmd from this method */
414 cmd.cmd_type = TZ_SCHED_CMD_NEW;
415 cmd.sb_in_cmd_addr = (u8 *)(virt_to_phys(svc->sb_reg_req));
416 cmd.sb_in_cmd_len = sizeof(sb_init_req);
417 resp.cmd_status = TZ_SCHED_STATUS_INCOMPLETE;
418
419 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &cmd, sizeof(cmd),
420 &resp, sizeof(resp));
421 if (ret) {
422 pr_err("qseecom_scm_call failed with err: %d\n", ret);
423 return ret;
424 }
425 kzfree(svc->sb_reg_req);
426 if (resp.cmd_status != TZ_SCHED_STATUS_COMPLETE) {
427 pr_err("Error with SB initialization\n");
428 return -EPERM;
429 }
430 }
431 data->abort = 1;
432 spin_lock_irqsave(&qseecom.registered_listener_list_lock, flags);
433 list_for_each_entry(ptr_svc, &qseecom.registered_listener_list_head,
434 list) {
435 if (ptr_svc->svc.listener_id == data->listener.id) {
436 wake_up_all(&ptr_svc->rcv_req_wq);
437 break;
438 }
439 }
440 spin_unlock_irqrestore(&qseecom.registered_listener_list_lock, flags);
441
442 while (atomic_read(&data->ioctl_count) > 1) {
Mona Hossainacea1022012-04-09 13:37:27 -0700443 if (wait_event_freezable(data->abort_wq,
Mona Hossain2892b6b2012-02-17 13:53:11 -0800444 atomic_read(&data->ioctl_count) <= 1)) {
445 pr_err("Interrupted from abort\n");
446 ret = -ERESTARTSYS;
447 break;
448 }
449 }
450
451 spin_lock_irqsave(&qseecom.registered_listener_list_lock, flags);
452 list_for_each_entry(ptr_svc,
453 &qseecom.registered_listener_list_head,
454 list)
455 {
456 if (ptr_svc->svc.listener_id == data->listener.id) {
457 if (ptr_svc->sb_virt) {
458 unmap_mem = 1;
459 ihandle = ptr_svc->ihandle;
460 }
461 list_del(&ptr_svc->list);
462 kzfree(ptr_svc);
463 break;
464 }
465 }
466 spin_unlock_irqrestore(&qseecom.registered_listener_list_lock, flags);
467
468 /* Unmap the memory */
469 if (unmap_mem) {
470 if (!IS_ERR_OR_NULL(ihandle)) {
471 ion_unmap_kernel(qseecom.ion_clnt, ihandle);
472 ion_free(qseecom.ion_clnt, ihandle);
473 }
474 }
475 data->released = true;
476 return ret;
477}
478
479static int qseecom_set_client_mem_param(struct qseecom_dev_handle *data,
480 void __user *argp)
481{
482 ion_phys_addr_t pa;
483 int32_t ret;
484 unsigned int flags = 0;
485 struct qseecom_set_sb_mem_param_req req;
486 uint32_t len;
487
488 /* Copy the relevant information needed for loading the image */
489 if (__copy_from_user(&req, (void __user *)argp, sizeof(req)))
490 return -EFAULT;
491
Mona Hossain2892b6b2012-02-17 13:53:11 -0800492 /* Get the handle of the shared fd */
493 data->client.ihandle = ion_import_fd(qseecom.ion_clnt, req.ifd_data_fd);
494 if (IS_ERR_OR_NULL(data->client.ihandle)) {
495 pr_err("Ion client could not retrieve the handle\n");
496 return -ENOMEM;
497 }
498 /* Get the physical address of the ION BUF */
499 ret = ion_phys(qseecom.ion_clnt, data->client.ihandle, &pa, &len);
500 /* Populate the structure for sending scm call to load image */
501 data->client.sb_virt = (char *) ion_map_kernel(qseecom.ion_clnt,
502 data->client.ihandle,
503 flags);
504 data->client.sb_phys = pa;
505 data->client.sb_length = req.sb_len;
506 data->client.user_virt_sb_base = req.virt_sb_base;
507 return 0;
508}
509
510
511static int __qseecom_listener_has_sent_rsp(struct qseecom_dev_handle *data)
512{
513 int ret;
514 ret = (qseecom.send_resp_flag != 0);
515 return ret || data->abort;
516}
517
518static int __qseecom_process_incomplete_cmd(struct qseecom_dev_handle *data,
519 struct qseecom_command_scm_resp *resp)
520{
521 int ret = 0;
522 uint32_t lstnr;
523 unsigned long flags;
524 struct qseecom_client_listener_data_irsp send_data_rsp;
525 struct qseecom_registered_listener_list *ptr_svc = NULL;
526
527
528 while (resp->result == QSEOS_RESULT_INCOMPLETE) {
529 lstnr = resp->data;
530 /*
531 * Wake up blocking lsitener service with the lstnr id
532 */
533 spin_lock_irqsave(&qseecom.registered_listener_list_lock,
534 flags);
535 list_for_each_entry(ptr_svc,
536 &qseecom.registered_listener_list_head, list) {
537 if (ptr_svc->svc.listener_id == lstnr) {
538 ptr_svc->rcv_req_flag = 1;
539 wake_up_interruptible(&ptr_svc->rcv_req_wq);
540 break;
541 }
542 }
543 spin_unlock_irqrestore(&qseecom.registered_listener_list_lock,
544 flags);
545 if (ptr_svc->svc.listener_id != lstnr) {
546 pr_warning("Service requested for does on exist\n");
547 return -ERESTARTSYS;
548 }
549 pr_debug("waking up rcv_req_wq and "
550 "waiting for send_resp_wq\n");
Mona Hossainacea1022012-04-09 13:37:27 -0700551 if (wait_event_freezable(qseecom.send_resp_wq,
Mona Hossain2892b6b2012-02-17 13:53:11 -0800552 __qseecom_listener_has_sent_rsp(data))) {
553 pr_warning("Interrupted: exiting send_cmd loop\n");
554 return -ERESTARTSYS;
555 }
556
557 if (data->abort) {
Mona Hossaina5f1aab2012-03-29 10:18:07 -0700558 pr_err("Aborting listener service %d\n",
559 data->listener.id);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800560 return -ENODEV;
561 }
562 qseecom.send_resp_flag = 0;
563 send_data_rsp.qsee_cmd_id = QSEOS_LISTENER_DATA_RSP_COMMAND;
564 send_data_rsp.listener_id = lstnr ;
565
566 ret = scm_call(SCM_SVC_TZSCHEDULER, 1,
567 (const void *)&send_data_rsp,
568 sizeof(send_data_rsp), resp,
569 sizeof(*resp));
570 if (ret) {
571 pr_err("qseecom_scm_call failed with err: %d\n", ret);
572 return ret;
573 }
574 }
575 return ret;
576}
577
578static int qseecom_load_app(struct qseecom_dev_handle *data, void __user *argp)
579{
580 struct qseecom_registered_app_list *entry = NULL;
581 unsigned long flags = 0;
582 u32 app_id = 0;
583 struct ion_handle *ihandle; /* Ion handle */
584 struct qseecom_load_img_req load_img_req;
585 int32_t ret;
586 ion_phys_addr_t pa = 0;
587 uint32_t len;
588 struct qseecom_command_scm_resp resp;
589 struct qseecom_check_app_ireq req;
590 /* Copy the relevant information needed for loading the image */
591 if (__copy_from_user(&load_img_req,
592 (void __user *)argp,
593 sizeof(struct qseecom_load_img_req))) {
594 pr_err("copy_from_user failed\n");
595 return -EFAULT;
596 }
597
598 req.qsee_cmd_id = QSEOS_APP_LOOKUP_COMMAND;
599 memcpy(req.app_name, load_img_req.img_name, MAX_APP_NAME_SIZE);
600
601 /* SCM_CALL to check if app_id for the mentioned app exists */
602 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &req,
603 sizeof(struct qseecom_check_app_ireq),
604 &resp, sizeof(resp));
605
606 if (resp.result == QSEOS_RESULT_FAILURE)
607 app_id = 0;
608 else
609 app_id = resp.data;
610
611 if (app_id) {
Mona Hossaina5f1aab2012-03-29 10:18:07 -0700612 pr_warn("App id %d (%s) already exists\n", app_id,
613 (char *)(req.app_name));
Mona Hossain2892b6b2012-02-17 13:53:11 -0800614 spin_lock_irqsave(&qseecom.registered_app_list_lock, flags);
615 list_for_each_entry(entry,
616 &qseecom.registered_app_list_head, list){
617 if (entry->app_id == app_id) {
618 entry->ref_cnt++;
619 break;
620 }
621 }
622 spin_unlock_irqrestore(
623 &qseecom.registered_app_list_lock, flags);
624 } else {
625 struct qseecom_load_app_ireq load_req;
626
Mona Hossaina5f1aab2012-03-29 10:18:07 -0700627 pr_warn("App (%s) does not exist, loading apps for first time\n",
628 (char *)(load_req.app_name));
Mona Hossain2892b6b2012-02-17 13:53:11 -0800629 /* Get the handle of the shared fd */
630 ihandle = ion_import_fd(qseecom.ion_clnt,
631 load_img_req.ifd_data_fd);
632 if (IS_ERR_OR_NULL(ihandle)) {
633 pr_err("Ion client could not retrieve the handle\n");
634 return -ENOMEM;
635 }
636
637 /* Get the physical address of the ION BUF */
638 ret = ion_phys(qseecom.ion_clnt, ihandle, &pa, &len);
639
640 /* Populate the structure for sending scm call to load image */
641 load_req.qsee_cmd_id = QSEOS_APP_START_COMMAND;
642 load_req.mdt_len = load_img_req.mdt_len;
643 load_req.img_len = load_img_req.img_len;
644 load_req.phy_addr = pa;
645
646 /* SCM_CALL to load the app and get the app_id back */
647 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &load_req,
648 sizeof(struct qseecom_load_app_ireq),
649 &resp, sizeof(resp));
650
Mona Hossaina5f1aab2012-03-29 10:18:07 -0700651 if (resp.result == QSEOS_RESULT_FAILURE) {
652 pr_err("scm_call failed resp.result QSEOS_RESULT_FAILURE -1\n");
653 if (!IS_ERR_OR_NULL(ihandle))
654 ion_free(qseecom.ion_clnt, ihandle);
655 return -EFAULT;
656 }
657
Mona Hossain2892b6b2012-02-17 13:53:11 -0800658 if (resp.result == QSEOS_RESULT_INCOMPLETE) {
659 ret = __qseecom_process_incomplete_cmd(data, &resp);
660 if (ret) {
661 pr_err("process_incomplete_cmd failed err: %d\n",
662 ret);
663 if (!IS_ERR_OR_NULL(ihandle))
664 ion_free(qseecom.ion_clnt, ihandle);
665 return ret;
666 }
667 }
668 if (resp.result != QSEOS_RESULT_SUCCESS) {
Mona Hossaina5f1aab2012-03-29 10:18:07 -0700669 pr_err("scm_call failed resp.result unknown, %d\n",
670 resp.result);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800671 if (!IS_ERR_OR_NULL(ihandle))
672 ion_free(qseecom.ion_clnt, ihandle);
673 return -EFAULT;
674 }
675
676 app_id = resp.data;
677
678 entry = kmalloc(sizeof(*entry), GFP_KERNEL);
679 if (!entry) {
680 pr_err("kmalloc failed\n");
681 return -ENOMEM;
682 }
683 entry->app_id = app_id;
684 entry->ref_cnt = 1;
685
686 /* Deallocate the handle */
687 if (!IS_ERR_OR_NULL(ihandle))
688 ion_free(qseecom.ion_clnt, ihandle);
689
690 spin_lock_irqsave(&qseecom.registered_app_list_lock, flags);
691 list_add_tail(&entry->list, &qseecom.registered_app_list_head);
692 spin_unlock_irqrestore(&qseecom.registered_app_list_lock,
693 flags);
Mona Hossaina5f1aab2012-03-29 10:18:07 -0700694
695 pr_warn("App with id %d (%s) now loaded\n", app_id,
696 (char *)(load_req.app_name));
Mona Hossain2892b6b2012-02-17 13:53:11 -0800697 }
698 data->client.app_id = app_id;
699 load_img_req.app_id = app_id;
700 if (copy_to_user(argp, &load_img_req, sizeof(load_img_req))) {
701 pr_err("copy_to_user failed\n");
702 kzfree(entry);
703 return -EFAULT;
704 }
705 return 0;
706}
707
708static int __qseecom_cleanup_app(struct qseecom_dev_handle *data)
709{
710 wake_up_all(&qseecom.send_resp_wq);
711 while (atomic_read(&data->ioctl_count) > 1) {
Mona Hossainacea1022012-04-09 13:37:27 -0700712 if (wait_event_freezable(data->abort_wq,
Mona Hossain2892b6b2012-02-17 13:53:11 -0800713 atomic_read(&data->ioctl_count) <= 1)) {
714 pr_err("Interrupted from abort\n");
715 return -ERESTARTSYS;
716 break;
717 }
718 }
719 /* Set unload app */
720 return 1;
721}
722
723static int qseecom_unload_app(struct qseecom_dev_handle *data)
724{
725 unsigned long flags;
726 int ret = 0;
727 struct qseecom_command_scm_resp resp;
728 struct qseecom_registered_app_list *ptr_app;
729 uint32_t unload = 0;
730
731 if (qseecom.qseos_version == QSEOS_VERSION_14) {
732 spin_lock_irqsave(&qseecom.registered_app_list_lock, flags);
733 list_for_each_entry(ptr_app, &qseecom.registered_app_list_head,
734 list) {
735 if (ptr_app->app_id == data->client.app_id) {
736 if (ptr_app->ref_cnt == 1) {
737 unload = __qseecom_cleanup_app(data);
738 list_del(&ptr_app->list);
739 kzfree(ptr_app);
Mona Hossaina5f1aab2012-03-29 10:18:07 -0700740 pr_warn("App id %d now unloaded\n",
741 ptr_app->app_id);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800742 break;
743 } else {
744 ptr_app->ref_cnt--;
Mona Hossain0af10ab2012-02-28 18:26:41 -0800745 data->released = true;
Mona Hossaina5f1aab2012-03-29 10:18:07 -0700746 pr_warn("Can't unload app with id %d (it is inuse)\n",
747 ptr_app->app_id);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800748 break;
749 }
750 }
751 }
752 spin_unlock_irqrestore(&qseecom.registered_app_list_lock,
753 flags);
754 }
755 if (!IS_ERR_OR_NULL(data->client.ihandle)) {
756 ion_unmap_kernel(qseecom.ion_clnt, data->client.ihandle);
757 ion_free(qseecom.ion_clnt, data->client.ihandle);
758 }
759
760 if ((unload) && (qseecom.qseos_version == QSEOS_VERSION_14)) {
761 struct qseecom_unload_app_ireq req;
762
763 /* Populate the structure for sending scm call to load image */
764 req.qsee_cmd_id = QSEOS_APP_SHUTDOWN_COMMAND;
765 req.app_id = data->client.app_id;
766
767 /* SCM_CALL to unload the app */
768 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &req,
769 sizeof(struct qseecom_unload_app_ireq),
770 &resp, sizeof(resp));
771 if (ret) {
Mona Hossaina5f1aab2012-03-29 10:18:07 -0700772 pr_err("Fail to unload app id %d\n", req.app_id);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800773 return -EFAULT;
774 }
775 if (resp.result == QSEOS_RESULT_INCOMPLETE) {
776 ret = __qseecom_process_incomplete_cmd(data, &resp);
777 if (ret) {
778 pr_err("process_incomplete_cmd fail err: %d\n",
779 ret);
780 return ret;
781 }
782 }
783 }
784
785 if (qseecom.qseos_version == QSEOS_VERSION_13) {
786 data->abort = 1;
787 wake_up_all(&qseecom.send_resp_wq);
788 while (atomic_read(&data->ioctl_count) > 0) {
Mona Hossainacea1022012-04-09 13:37:27 -0700789 if (wait_event_freezable(data->abort_wq,
Mona Hossain2892b6b2012-02-17 13:53:11 -0800790 atomic_read(&data->ioctl_count) <= 0)) {
791 pr_err("Interrupted from abort\n");
792 ret = -ERESTARTSYS;
793 break;
794 }
795 }
Mona Hossain2892b6b2012-02-17 13:53:11 -0800796 }
797 data->released = true;
798 return ret;
799}
800
801static uint32_t __qseecom_uvirt_to_kphys(struct qseecom_dev_handle *data,
802 uint32_t virt)
803{
804 return data->client.sb_phys + (virt - data->client.user_virt_sb_base);
805}
806
807static int __qseecom_send_cmd_legacy(struct qseecom_dev_handle *data,
808 struct qseecom_send_cmd_req *req)
809{
810 int ret = 0;
811 unsigned long flags;
812 u32 reqd_len_sb_in = 0;
813 struct qseecom_command cmd;
814 struct qseecom_response resp;
815
816
817 if (req->cmd_req_buf == NULL || req->resp_buf == NULL) {
818 pr_err("cmd buffer or response buffer is null\n");
819 return -EINVAL;
820 }
821
822 if (req->cmd_req_len <= 0 ||
823 req->resp_len <= 0 ||
824 req->cmd_req_len > data->client.sb_length ||
825 req->resp_len > data->client.sb_length) {
826 pr_err("cmd buffer length or "
827 "response buffer length not valid\n");
828 return -EINVAL;
829 }
830
831 reqd_len_sb_in = req->cmd_req_len + req->resp_len;
832 if (reqd_len_sb_in > data->client.sb_length) {
833 pr_debug("Not enough memory to fit cmd_buf and "
834 "resp_buf. Required: %u, Available: %u\n",
835 reqd_len_sb_in, data->client.sb_length);
836 return -ENOMEM;
837 }
838 cmd.cmd_type = TZ_SCHED_CMD_NEW;
839 cmd.sb_in_cmd_addr = (u8 *) data->client.sb_phys;
840 cmd.sb_in_cmd_len = req->cmd_req_len;
841
842 resp.cmd_status = TZ_SCHED_STATUS_INCOMPLETE;
843 resp.sb_in_rsp_addr = (u8 *)data->client.sb_phys + req->cmd_req_len;
844 resp.sb_in_rsp_len = req->resp_len;
845
846 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, (const void *)&cmd,
847 sizeof(cmd), &resp, sizeof(resp));
848
849 if (ret) {
850 pr_err("qseecom_scm_call_legacy failed with err: %d\n", ret);
851 return ret;
852 }
853
854 while (resp.cmd_status != TZ_SCHED_STATUS_COMPLETE) {
855 /*
856 * If cmd is incomplete, get the callback cmd out from SB out
857 * and put it on the list
858 */
859 struct qseecom_registered_listener_list *ptr_svc = NULL;
860 /*
861 * We don't know which service can handle the command. so we
862 * wake up all blocking services and let them figure out if
863 * they can handle the given command.
864 */
865 spin_lock_irqsave(&qseecom.registered_listener_list_lock,
866 flags);
867 list_for_each_entry(ptr_svc,
868 &qseecom.registered_listener_list_head, list) {
869 ptr_svc->rcv_req_flag = 1;
870 wake_up_interruptible(&ptr_svc->rcv_req_wq);
871 }
872 spin_unlock_irqrestore(&qseecom.registered_listener_list_lock,
873 flags);
874
875 pr_debug("waking up rcv_req_wq and "
876 "waiting for send_resp_wq\n");
Mona Hossainacea1022012-04-09 13:37:27 -0700877 if (wait_event_freezable(qseecom.send_resp_wq,
Mona Hossain2892b6b2012-02-17 13:53:11 -0800878 __qseecom_listener_has_sent_rsp(data))) {
879 pr_warning("qseecom Interrupted: exiting send_cmd loop\n");
880 return -ERESTARTSYS;
881 }
882
883 if (data->abort) {
884 pr_err("Aborting driver\n");
885 return -ENODEV;
886 }
887 qseecom.send_resp_flag = 0;
888 cmd.cmd_type = TZ_SCHED_CMD_PENDING;
889 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, (const void *)&cmd,
890 sizeof(cmd), &resp, sizeof(resp));
891 if (ret) {
892 pr_err("qseecom_scm_call failed with err: %d\n", ret);
893 return ret;
894 }
895 }
896 return ret;
897}
898
899static int __qseecom_send_cmd(struct qseecom_dev_handle *data,
900 struct qseecom_send_cmd_req *req)
901{
902 int ret = 0;
903 u32 reqd_len_sb_in = 0;
904 struct qseecom_client_send_data_ireq send_data_req;
905 struct qseecom_command_scm_resp resp;
906
907 if (req->cmd_req_buf == NULL || req->resp_buf == NULL) {
908 pr_err("cmd buffer or response buffer is null\n");
909 return -EINVAL;
910 }
911
912 if (req->cmd_req_len <= 0 ||
913 req->resp_len <= 0 ||
914 req->cmd_req_len > data->client.sb_length ||
915 req->resp_len > data->client.sb_length) {
916 pr_err("cmd buffer length or "
917 "response buffer length not valid\n");
918 return -EINVAL;
919 }
920
921 reqd_len_sb_in = req->cmd_req_len + req->resp_len;
922 if (reqd_len_sb_in > data->client.sb_length) {
923 pr_debug("Not enough memory to fit cmd_buf and "
924 "resp_buf. Required: %u, Available: %u\n",
925 reqd_len_sb_in, data->client.sb_length);
926 return -ENOMEM;
927 }
928
929 send_data_req.qsee_cmd_id = QSEOS_CLIENT_SEND_DATA_COMMAND;
930 send_data_req.app_id = data->client.app_id;
931 send_data_req.req_ptr = (void *)(__qseecom_uvirt_to_kphys(data,
932 (uint32_t)req->cmd_req_buf));
933 send_data_req.req_len = req->cmd_req_len;
934 send_data_req.rsp_ptr = (void *)(__qseecom_uvirt_to_kphys(data,
935 (uint32_t)req->resp_buf));
936 send_data_req.rsp_len = req->resp_len;
937
938 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, (const void *) &send_data_req,
939 sizeof(send_data_req),
940 &resp, sizeof(resp));
941 if (ret) {
942 pr_err("qseecom_scm_call failed with err: %d\n", ret);
943 return ret;
944 }
945
946 if (resp.result == QSEOS_RESULT_INCOMPLETE) {
947 ret = __qseecom_process_incomplete_cmd(data, &resp);
948 if (ret) {
949 pr_err("process_incomplete_cmd failed err: %d\n", ret);
950 return ret;
951 }
952 }
953 return ret;
954}
955
956
957static int qseecom_send_cmd(struct qseecom_dev_handle *data, void __user *argp)
958{
959 int ret = 0;
960 struct qseecom_send_cmd_req req;
961
962 ret = copy_from_user(&req, argp, sizeof(req));
963 if (ret) {
964 pr_err("copy_from_user failed\n");
965 return ret;
966 }
967 if (qseecom.qseos_version == QSEOS_VERSION_14)
968 ret = __qseecom_send_cmd(data, &req);
969 else
970 ret = __qseecom_send_cmd_legacy(data, &req);
971 if (ret)
972 return ret;
973
974 pr_debug("sending cmd_req->rsp size: %u, ptr: 0x%p\n",
975 req.resp_len, req.resp_buf);
976 return ret;
977}
978
979static int __qseecom_send_cmd_req_clean_up(
980 struct qseecom_send_modfd_cmd_req *req)
981{
982 char *field;
983 uint32_t *update;
984 int ret = 0;
985 int i = 0;
986
987 for (i = 0; i < MAX_ION_FD; i++) {
Mona Hossaina5f1aab2012-03-29 10:18:07 -0700988 if (req->ifd_data[i].fd > 0) {
Mona Hossain2892b6b2012-02-17 13:53:11 -0800989 field = (char *)req->cmd_req_buf +
990 req->ifd_data[i].cmd_buf_offset;
991 update = (uint32_t *) field;
992 *update = 0;
993 }
994 }
995 return ret;
996}
997
998static int __qseecom_update_with_phy_addr(
999 struct qseecom_send_modfd_cmd_req *req)
1000{
1001 struct ion_handle *ihandle;
1002 char *field;
1003 uint32_t *update;
1004 ion_phys_addr_t pa;
1005 int ret = 0;
1006 int i = 0;
1007 uint32_t length;
1008
1009 for (i = 0; i < MAX_ION_FD; i++) {
Mona Hossaina5f1aab2012-03-29 10:18:07 -07001010 if (req->ifd_data[i].fd > 0) {
Mona Hossain2892b6b2012-02-17 13:53:11 -08001011 /* Get the handle of the shared fd */
1012 ihandle = ion_import_fd(qseecom.ion_clnt,
1013 req->ifd_data[i].fd);
1014 if (IS_ERR_OR_NULL(ihandle)) {
1015 pr_err("Ion client can't retrieve the handle\n");
1016 return -ENOMEM;
1017 }
1018 field = (char *) req->cmd_req_buf +
1019 req->ifd_data[i].cmd_buf_offset;
1020 update = (uint32_t *) field;
1021
1022 /* Populate the cmd data structure with the phys_addr */
1023 ret = ion_phys(qseecom.ion_clnt, ihandle, &pa, &length);
1024 if (ret)
1025 return -ENOMEM;
1026
1027 *update = (uint32_t)pa;
1028 /* Deallocate the handle */
1029 if (!IS_ERR_OR_NULL(ihandle))
1030 ion_free(qseecom.ion_clnt, ihandle);
1031 }
1032 }
1033 return ret;
1034}
1035
1036static int qseecom_send_modfd_cmd(struct qseecom_dev_handle *data,
1037 void __user *argp)
1038{
1039 int ret = 0;
1040 struct qseecom_send_modfd_cmd_req req;
1041 struct qseecom_send_cmd_req send_cmd_req;
1042
1043 ret = copy_from_user(&req, argp, sizeof(req));
1044 if (ret) {
1045 pr_err("copy_from_user failed\n");
1046 return ret;
1047 }
1048 send_cmd_req.cmd_req_buf = req.cmd_req_buf;
1049 send_cmd_req.cmd_req_len = req.cmd_req_len;
1050 send_cmd_req.resp_buf = req.resp_buf;
1051 send_cmd_req.resp_len = req.resp_len;
1052
1053 ret = __qseecom_update_with_phy_addr(&req);
1054 if (ret)
1055 return ret;
1056 if (qseecom.qseos_version == QSEOS_VERSION_14)
1057 ret = __qseecom_send_cmd(data, &send_cmd_req);
1058 else
1059 ret = __qseecom_send_cmd_legacy(data, &send_cmd_req);
1060 __qseecom_send_cmd_req_clean_up(&req);
1061
1062 if (ret)
1063 return ret;
1064
1065 pr_debug("sending cmd_req->rsp size: %u, ptr: 0x%p\n",
1066 req.resp_len, req.resp_buf);
1067 return ret;
1068}
1069
1070static int __qseecom_listener_has_rcvd_req(struct qseecom_dev_handle *data,
1071 struct qseecom_registered_listener_list *svc)
1072{
1073 int ret;
1074 ret = (svc->rcv_req_flag != 0);
1075 return ret || data->abort;
1076}
1077
1078static int qseecom_receive_req(struct qseecom_dev_handle *data)
1079{
1080 int ret = 0;
1081 struct qseecom_registered_listener_list *this_lstnr;
1082
1083 this_lstnr = __qseecom_find_svc(data->listener.id);
1084 while (1) {
Mona Hossainacea1022012-04-09 13:37:27 -07001085 if (wait_event_freezable(this_lstnr->rcv_req_wq,
Mona Hossain2892b6b2012-02-17 13:53:11 -08001086 __qseecom_listener_has_rcvd_req(data,
1087 this_lstnr))) {
1088 pr_warning("Interrupted: exiting wait_rcv_req loop\n");
1089 /* woken up for different reason */
1090 return -ERESTARTSYS;
1091 }
1092
1093 if (data->abort) {
1094 pr_err("Aborting driver!\n");
1095 return -ENODEV;
1096 }
1097 this_lstnr->rcv_req_flag = 0;
1098 if (qseecom.qseos_version == QSEOS_VERSION_13) {
1099 if (*((uint32_t *)this_lstnr->sb_virt) != 0)
1100 break;
1101 } else {
1102 break;
1103 }
1104 }
1105 return ret;
1106}
1107
1108static int qseecom_send_resp(void)
1109{
1110 qseecom.send_resp_flag = 1;
1111 wake_up_interruptible(&qseecom.send_resp_wq);
1112 return 0;
1113}
1114
1115static int qseecom_get_qseos_version(struct qseecom_dev_handle *data,
1116 void __user *argp)
1117{
1118 struct qseecom_qseos_version_req req;
1119
1120 if (copy_from_user(&req, argp, sizeof(req))) {
1121 pr_err("copy_from_user failed");
1122 return -EINVAL;
1123 }
1124 req.qseos_version = qseecom.qseos_version;
1125 if (copy_to_user(argp, &req, sizeof(req))) {
1126 pr_err("copy_to_user failed");
1127 return -EINVAL;
1128 }
1129 return 0;
1130}
1131
1132static int qsee_vote_for_clock(void)
1133{
1134 int ret = 0;
1135
1136 if (!qsee_perf_client)
Ramesh Masavarapue640e842012-04-03 11:21:54 -07001137 return ret;
Mona Hossain2892b6b2012-02-17 13:53:11 -08001138
Ramesh Masavarapua1bc0e42012-03-05 07:42:48 -08001139 /* Check if the clk is valid */
Ramesh Masavarapue640e842012-04-03 11:21:54 -07001140 if (IS_ERR_OR_NULL(qseecom_bus_clk)) {
1141 pr_warn("qseecom bus clock is null or error");
Ramesh Masavarapua1bc0e42012-03-05 07:42:48 -08001142 return -EINVAL;
Ramesh Masavarapue640e842012-04-03 11:21:54 -07001143 }
Ramesh Masavarapua1bc0e42012-03-05 07:42:48 -08001144
Mona Hossain2892b6b2012-02-17 13:53:11 -08001145 mutex_lock(&qsee_bw_mutex);
1146 if (!qsee_bw_count) {
1147 ret = msm_bus_scale_client_update_request(
1148 qsee_perf_client, 1);
1149 if (ret) {
1150 pr_err("Bandwidth request failed (%d)\n", ret);
1151 } else {
1152 ret = clk_enable(qseecom_bus_clk);
1153 if (ret)
1154 pr_err("Clock enable failed\n");
1155 }
1156 }
1157 if (ret)
1158 msm_bus_scale_client_update_request(qsee_perf_client, 0);
1159 else
1160 qsee_bw_count++;
1161
1162 mutex_unlock(&qsee_bw_mutex);
1163 return ret;
1164}
1165
1166static void qsee_disable_clock_vote(void)
1167{
Ramesh Masavarapua1bc0e42012-03-05 07:42:48 -08001168
Mona Hossain2892b6b2012-02-17 13:53:11 -08001169 if (!qsee_perf_client)
Ramesh Masavarapua1bc0e42012-03-05 07:42:48 -08001170 return;
1171
1172 /* Check if the clk is valid */
Ramesh Masavarapue640e842012-04-03 11:21:54 -07001173 if (IS_ERR_OR_NULL(qseecom_bus_clk)) {
1174 pr_warn("qseecom bus clock is null or error");
Ramesh Masavarapua1bc0e42012-03-05 07:42:48 -08001175 return;
Ramesh Masavarapue640e842012-04-03 11:21:54 -07001176 }
Mona Hossain2892b6b2012-02-17 13:53:11 -08001177
1178 mutex_lock(&qsee_bw_mutex);
1179 if (qsee_bw_count > 0) {
1180 if (qsee_bw_count-- == 1) {
1181 msm_bus_scale_client_update_request(qsee_perf_client,
1182 0);
1183 clk_disable(qseecom_bus_clk);
1184 }
1185 }
1186 mutex_unlock(&qsee_bw_mutex);
1187}
1188
1189
1190static long qseecom_ioctl(struct file *file, unsigned cmd,
1191 unsigned long arg)
1192{
1193 int ret = 0;
1194 struct qseecom_dev_handle *data = file->private_data;
1195 void __user *argp = (void __user *) arg;
1196
1197 if (data->abort) {
1198 pr_err("Aborting qseecom driver\n");
1199 return -ENODEV;
1200 }
1201
1202 switch (cmd) {
1203 case QSEECOM_IOCTL_REGISTER_LISTENER_REQ: {
1204 pr_debug("ioctl register_listener_req()\n");
1205 atomic_inc(&data->ioctl_count);
1206 ret = qseecom_register_listener(data, argp);
1207 atomic_dec(&data->ioctl_count);
1208 wake_up_all(&data->abort_wq);
1209 if (ret)
1210 pr_err("failed qseecom_register_listener: %d\n", ret);
1211 break;
1212 }
1213 case QSEECOM_IOCTL_UNREGISTER_LISTENER_REQ: {
1214 pr_debug("ioctl unregister_listener_req()\n");
1215 atomic_inc(&data->ioctl_count);
1216 ret = qseecom_unregister_listener(data);
1217 atomic_dec(&data->ioctl_count);
1218 wake_up_all(&data->abort_wq);
1219 if (ret)
1220 pr_err("failed qseecom_unregister_listener: %d\n", ret);
1221 break;
1222 }
1223 case QSEECOM_IOCTL_SEND_CMD_REQ: {
1224 /* Only one client allowed here at a time */
1225 mutex_lock(&send_msg_lock);
1226 atomic_inc(&data->ioctl_count);
1227 ret = qseecom_send_cmd(data, argp);
1228 atomic_dec(&data->ioctl_count);
1229 wake_up_all(&data->abort_wq);
1230 mutex_unlock(&send_msg_lock);
1231 if (ret)
1232 pr_err("failed qseecom_send_cmd: %d\n", ret);
1233 break;
1234 }
1235 case QSEECOM_IOCTL_SEND_MODFD_CMD_REQ: {
1236 /* Only one client allowed here at a time */
1237 mutex_lock(&send_msg_lock);
1238 atomic_inc(&data->ioctl_count);
1239 ret = qseecom_send_modfd_cmd(data, argp);
1240 atomic_dec(&data->ioctl_count);
1241 wake_up_all(&data->abort_wq);
1242 mutex_unlock(&send_msg_lock);
1243 if (ret)
1244 pr_err("failed qseecom_send_cmd: %d\n", ret);
1245 break;
1246 }
1247 case QSEECOM_IOCTL_RECEIVE_REQ: {
1248 atomic_inc(&data->ioctl_count);
1249 ret = qseecom_receive_req(data);
1250 atomic_dec(&data->ioctl_count);
1251 wake_up_all(&data->abort_wq);
1252 if (ret)
1253 pr_err("failed qseecom_receive_req: %d\n", ret);
1254 break;
1255 }
1256 case QSEECOM_IOCTL_SEND_RESP_REQ: {
1257 atomic_inc(&data->ioctl_count);
1258 ret = qseecom_send_resp();
1259 atomic_dec(&data->ioctl_count);
1260 wake_up_all(&data->abort_wq);
1261 if (ret)
1262 pr_err("failed qseecom_send_resp: %d\n", ret);
1263 break;
1264 }
1265 case QSEECOM_IOCTL_SET_MEM_PARAM_REQ: {
1266 ret = qseecom_set_client_mem_param(data, argp);
1267 if (ret)
1268 pr_err("failed Qqseecom_set_mem_param request: %d\n",
1269 ret);
1270 break;
1271 }
1272 case QSEECOM_IOCTL_LOAD_APP_REQ: {
1273 mutex_lock(&app_access_lock);
1274 atomic_inc(&data->ioctl_count);
1275 ret = qseecom_load_app(data, argp);
1276 atomic_dec(&data->ioctl_count);
1277 mutex_unlock(&app_access_lock);
1278 if (ret)
1279 pr_err("failed load_app request: %d\n", ret);
1280 break;
1281 }
1282 case QSEECOM_IOCTL_UNLOAD_APP_REQ: {
1283 mutex_lock(&app_access_lock);
1284 atomic_inc(&data->ioctl_count);
1285 ret = qseecom_unload_app(data);
1286 atomic_dec(&data->ioctl_count);
1287 mutex_unlock(&app_access_lock);
1288 if (ret)
1289 pr_err("failed unload_app request: %d\n", ret);
1290 break;
1291 }
1292 case QSEECOM_IOCTL_GET_QSEOS_VERSION_REQ: {
1293 atomic_inc(&data->ioctl_count);
1294 ret = qseecom_get_qseos_version(data, argp);
1295 if (ret)
1296 pr_err("qseecom_get_qseos_version: %d\n", ret);
1297 atomic_dec(&data->ioctl_count);
1298 break;
1299 }
1300 case QSEECOM_IOCTL_PERF_ENABLE_REQ:{
1301 atomic_inc(&data->ioctl_count);
1302 ret = qsee_vote_for_clock();
1303 if (ret)
1304 pr_err("Failed to vote for clock%d\n", ret);
1305 atomic_dec(&data->ioctl_count);
1306 break;
1307 }
1308 case QSEECOM_IOCTL_PERF_DISABLE_REQ:{
1309 atomic_inc(&data->ioctl_count);
1310 qsee_disable_clock_vote();
1311 atomic_dec(&data->ioctl_count);
1312 break;
1313 }
1314 default:
1315 return -EINVAL;
1316 }
1317 return ret;
1318}
1319
1320static int qseecom_open(struct inode *inode, struct file *file)
1321{
1322 int ret = 0;
1323 struct qseecom_dev_handle *data;
1324
1325 data = kzalloc(sizeof(*data), GFP_KERNEL);
1326 if (!data) {
1327 pr_err("kmalloc failed\n");
1328 return -ENOMEM;
1329 }
1330 file->private_data = data;
1331 data->abort = 0;
1332 data->service = false;
1333 data->released = false;
1334 init_waitqueue_head(&data->abort_wq);
1335 atomic_set(&data->ioctl_count, 0);
Mona Hossain7cdbfaf2012-03-15 18:56:10 -07001336 if (qseecom.qseos_version == QSEOS_VERSION_13) {
1337 int pil_error;
1338 mutex_lock(&pil_access_lock);
1339 if (pil_ref_cnt == 0) {
1340 pil = pil_get("tzapps");
1341 if (IS_ERR(pil)) {
1342 pr_err("Playready PIL image load failed\n");
1343 pil_error = PTR_ERR(pil);
1344 pil = NULL;
1345 pr_debug("tzapps image load FAILED\n");
1346 mutex_unlock(&pil_access_lock);
1347 return pil_error;
1348 }
1349 }
1350 pil_ref_cnt++;
1351 mutex_unlock(&pil_access_lock);
1352 }
Mona Hossain2892b6b2012-02-17 13:53:11 -08001353 return ret;
1354}
1355
1356static int qseecom_release(struct inode *inode, struct file *file)
1357{
1358 struct qseecom_dev_handle *data = file->private_data;
1359 int ret = 0;
1360
1361 if (data->released == false) {
1362 pr_warn("data->released == false\n");
1363 if (data->service)
1364 ret = qseecom_unregister_listener(data);
1365 else
1366 ret = qseecom_unload_app(data);
1367 if (ret) {
1368 pr_err("Close failed\n");
1369 return ret;
1370 }
1371 }
Mona Hossain7cdbfaf2012-03-15 18:56:10 -07001372 if (qseecom.qseos_version == QSEOS_VERSION_13) {
1373 mutex_lock(&pil_access_lock);
1374 if (pil_ref_cnt == 1)
1375 pil_put(pil);
1376 pil_ref_cnt--;
1377 mutex_unlock(&pil_access_lock);
1378 }
Mona Hossain2892b6b2012-02-17 13:53:11 -08001379 kfree(data);
Ramesh Masavarapua1bc0e42012-03-05 07:42:48 -08001380 qsee_disable_clock_vote();
1381
Mona Hossain2892b6b2012-02-17 13:53:11 -08001382 return ret;
1383}
1384
1385/* qseecom bus scaling */
1386static struct msm_bus_paths qsee_bw_table[] = {
1387 {
1388 .vectors = (struct msm_bus_vectors[]){
1389 {
1390 .src = MSM_BUS_MASTER_SPS,
1391 .dst = MSM_BUS_SLAVE_EBI_CH0,
1392 },
1393 },
1394 .num_paths = 1,
1395 },
1396 {
1397 .vectors = (struct msm_bus_vectors[]){
1398 {
1399 .src = MSM_BUS_MASTER_SPS,
1400 .dst = MSM_BUS_SLAVE_EBI_CH0,
1401 .ib = (492 * 8) * 1000000UL,
1402 .ab = (492 * 8) * 100000UL,
1403 },
1404 },
1405 .num_paths = 1,
1406 },
1407};
1408
1409static struct msm_bus_scale_pdata qsee_bus_pdata = {
1410 .usecase = qsee_bw_table,
1411 .num_usecases = ARRAY_SIZE(qsee_bw_table),
1412 .name = "qsee",
1413};
1414
1415static const struct file_operations qseecom_fops = {
1416 .owner = THIS_MODULE,
1417 .unlocked_ioctl = qseecom_ioctl,
1418 .open = qseecom_open,
1419 .release = qseecom_release
1420};
1421
1422static int __init qseecom_init(void)
1423{
1424 int rc;
Mona Hossain2892b6b2012-02-17 13:53:11 -08001425 struct device *class_dev;
1426 char qsee_not_legacy = 0;
1427 uint32_t system_call_id = QSEOS_CHECK_VERSION_CMD;
1428
Ramesh Masavarapue640e842012-04-03 11:21:54 -07001429 qsee_bw_count = 0;
1430 qseecom_bus_clk = NULL;
1431 qsee_perf_client = 0;
1432
Mona Hossain2892b6b2012-02-17 13:53:11 -08001433 rc = alloc_chrdev_region(&qseecom_device_no, 0, 1, QSEECOM_DEV);
1434 if (rc < 0) {
1435 pr_err("alloc_chrdev_region failed %d\n", rc);
1436 return rc;
1437 }
1438
1439 driver_class = class_create(THIS_MODULE, QSEECOM_DEV);
1440 if (IS_ERR(driver_class)) {
1441 rc = -ENOMEM;
1442 pr_err("class_create failed %d\n", rc);
1443 goto unregister_chrdev_region;
1444 }
1445
1446 class_dev = device_create(driver_class, NULL, qseecom_device_no, NULL,
1447 QSEECOM_DEV);
1448 if (!class_dev) {
1449 pr_err("class_device_create failed %d\n", rc);
1450 rc = -ENOMEM;
1451 goto class_destroy;
1452 }
1453
1454 cdev_init(&qseecom_cdev, &qseecom_fops);
1455 qseecom_cdev.owner = THIS_MODULE;
1456
1457 rc = cdev_add(&qseecom_cdev, MKDEV(MAJOR(qseecom_device_no), 0), 1);
1458 if (rc < 0) {
1459 pr_err("cdev_add failed %d\n", rc);
1460 goto err;
1461 }
1462
1463 INIT_LIST_HEAD(&qseecom.registered_listener_list_head);
1464 spin_lock_init(&qseecom.registered_listener_list_lock);
1465 INIT_LIST_HEAD(&qseecom.registered_app_list_head);
1466 spin_lock_init(&qseecom.registered_app_list_lock);
1467 init_waitqueue_head(&qseecom.send_resp_wq);
1468 qseecom.send_resp_flag = 0;
1469
1470 rc = scm_call(6, 1, &system_call_id, sizeof(system_call_id),
1471 &qsee_not_legacy, sizeof(qsee_not_legacy));
1472 if (rc) {
1473 pr_err("Failed to retrieve QSEE version information %d\n", rc);
1474 goto err;
1475 }
1476 if (qsee_not_legacy)
1477 qseecom.qseos_version = QSEOS_VERSION_14;
1478 else {
1479 qseecom.qseos_version = QSEOS_VERSION_13;
1480 pil = NULL;
1481 pil_ref_cnt = 0;
1482 }
1483 /* Create ION msm client */
1484 qseecom.ion_clnt = msm_ion_client_create(0x03, "qseecom-kernel");
1485 if (qseecom.ion_clnt == NULL) {
1486 pr_err("Ion client cannot be created\n");
1487 rc = -ENOMEM;
1488 goto err;
1489 }
1490
1491 /* register client for bus scaling */
Ramesh Masavarapua1bc0e42012-03-05 07:42:48 -08001492 qsee_perf_client = msm_bus_scale_register_client(
1493 &qsee_bus_pdata);
1494 if (!qsee_perf_client) {
Mona Hossain2892b6b2012-02-17 13:53:11 -08001495 pr_err("Unable to register bus client\n");
Ramesh Masavarapue640e842012-04-03 11:21:54 -07001496 } else {
1497 qseecom_bus_clk = clk_get(class_dev, "bus_clk");
Ramesh Masavarapua1bc0e42012-03-05 07:42:48 -08001498 if (IS_ERR(qseecom_bus_clk)) {
Mona Hossain2892b6b2012-02-17 13:53:11 -08001499 qseecom_bus_clk = NULL;
Ramesh Masavarapua1bc0e42012-03-05 07:42:48 -08001500 } else if (qseecom_bus_clk != NULL) {
1501 pr_debug("Enabled DFAB clock");
1502 clk_set_rate(qseecom_bus_clk, 64000000);
Mona Hossain2892b6b2012-02-17 13:53:11 -08001503 }
Mona Hossain2892b6b2012-02-17 13:53:11 -08001504 }
1505 return 0;
Ramesh Masavarapua1bc0e42012-03-05 07:42:48 -08001506
Mona Hossain2892b6b2012-02-17 13:53:11 -08001507err:
1508 device_destroy(driver_class, qseecom_device_no);
1509class_destroy:
1510 class_destroy(driver_class);
1511unregister_chrdev_region:
1512 unregister_chrdev_region(qseecom_device_no, 1);
1513 return rc;
1514}
1515
1516static void __exit qseecom_exit(void)
1517{
Ramesh Masavarapua1bc0e42012-03-05 07:42:48 -08001518 clk_put(qseecom_bus_clk);
1519
Mona Hossain2892b6b2012-02-17 13:53:11 -08001520 device_destroy(driver_class, qseecom_device_no);
1521 class_destroy(driver_class);
1522 unregister_chrdev_region(qseecom_device_no, 1);
1523 ion_client_destroy(qseecom.ion_clnt);
1524}
1525
1526MODULE_LICENSE("GPL v2");
1527MODULE_DESCRIPTION("Qualcomm Secure Execution Environment Communicator");
1528
1529module_init(qseecom_init);
1530module_exit(qseecom_exit);