blob: 4840e64ffa2f45d1f2d8b39a9fe0a72cb28220d5 [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>
Mitchel Humpherys4f8be2e2012-09-06 10:41:41 -070031#include <linux/msm_ion.h>
Mona Hossain2892b6b2012-02-17 13:53:11 -080032#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>
Ramesh Masavarapua26cce72012-04-09 12:32:25 -070036#include <mach/board.h>
Mona Hossain2892b6b2012-02-17 13:53:11 -080037#include <mach/msm_bus.h>
38#include <mach/msm_bus_board.h>
39#include <mach/scm.h>
40#include <mach/peripheral-loader.h>
41#include "qseecom_legacy.h"
42
43#define QSEECOM_DEV "qseecom"
44#define QSEOS_VERSION_13 0x13
45#define QSEOS_VERSION_14 0x14
46#define QSEOS_CHECK_VERSION_CMD 0x00001803;
47
48enum qseecom_command_scm_resp_type {
49 QSEOS_APP_ID = 0xEE01,
50 QSEOS_LISTENER_ID
51};
52
53enum qseecom_qceos_cmd_id {
54 QSEOS_APP_START_COMMAND = 0x01,
55 QSEOS_APP_SHUTDOWN_COMMAND,
56 QSEOS_APP_LOOKUP_COMMAND,
57 QSEOS_REGISTER_LISTENER,
58 QSEOS_DEREGISTER_LISTENER,
59 QSEOS_CLIENT_SEND_DATA_COMMAND,
60 QSEOS_LISTENER_DATA_RSP_COMMAND,
Mona Hossain5ab9d772012-04-11 21:00:40 -070061 QSEOS_LOAD_EXTERNAL_ELF_COMMAND,
62 QSEOS_UNLOAD_EXTERNAL_ELF_COMMAND,
Mona Hossain2892b6b2012-02-17 13:53:11 -080063 QSEOS_CMD_MAX = 0xEFFFFFFF
64};
65
66enum qseecom_qceos_cmd_status {
67 QSEOS_RESULT_SUCCESS = 0,
68 QSEOS_RESULT_INCOMPLETE,
69 QSEOS_RESULT_FAILURE = 0xFFFFFFFF
70};
71
Ramesh Masavarapua26cce72012-04-09 12:32:25 -070072enum qseecom_clk_definitions {
73 CLK_DFAB = 0,
74 CLK_SFPB,
75};
76
Mona Hossain2892b6b2012-02-17 13:53:11 -080077__packed struct qseecom_check_app_ireq {
78 uint32_t qsee_cmd_id;
79 char app_name[MAX_APP_NAME_SIZE];
80};
81
82__packed struct qseecom_load_app_ireq {
83 uint32_t qsee_cmd_id;
84 uint32_t mdt_len; /* Length of the mdt file */
85 uint32_t img_len; /* Length of .bxx and .mdt files */
86 uint32_t phy_addr; /* phy addr of the start of image */
87 char app_name[MAX_APP_NAME_SIZE]; /* application name*/
88};
89
90__packed struct qseecom_unload_app_ireq {
91 uint32_t qsee_cmd_id;
92 uint32_t app_id;
93};
94
95__packed struct qseecom_register_listener_ireq {
96 uint32_t qsee_cmd_id;
97 uint32_t listener_id;
98 void *sb_ptr;
99 uint32_t sb_len;
100};
101
102__packed struct qseecom_unregister_listener_ireq {
103 uint32_t qsee_cmd_id;
104 uint32_t listener_id;
105};
106
107__packed struct qseecom_client_send_data_ireq {
108 uint32_t qsee_cmd_id;
109 uint32_t app_id;
110 void *req_ptr;
111 uint32_t req_len;
112 void *rsp_ptr; /* First 4 bytes should always be the return status */
113 uint32_t rsp_len;
114};
115
116/* send_data resp */
117__packed struct qseecom_client_listener_data_irsp {
118 uint32_t qsee_cmd_id;
119 uint32_t listener_id;
120};
121
122/*
123 * struct qseecom_command_scm_resp - qseecom response buffer
124 * @cmd_status: value from enum tz_sched_cmd_status
125 * @sb_in_rsp_addr: points to physical location of response
126 * buffer
127 * @sb_in_rsp_len: length of command response
128 */
129__packed struct qseecom_command_scm_resp {
130 uint32_t result;
131 enum qseecom_command_scm_resp_type resp_type;
132 unsigned int data;
133};
134
135static struct class *driver_class;
136static dev_t qseecom_device_no;
137static struct cdev qseecom_cdev;
138
139/* Data structures used in legacy support */
140static void *pil;
141static uint32_t pil_ref_cnt;
142static DEFINE_MUTEX(pil_access_lock);
143
Mona Hossain2892b6b2012-02-17 13:53:11 -0800144static DEFINE_MUTEX(qsee_bw_mutex);
Ramesh Masavarapua26cce72012-04-09 12:32:25 -0700145static DEFINE_MUTEX(qsee_sfpb_bw_mutex);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800146static DEFINE_MUTEX(app_access_lock);
147
148static int qsee_bw_count;
Ramesh Masavarapua26cce72012-04-09 12:32:25 -0700149static int qsee_sfpb_bw_count;
Mona Hossain2892b6b2012-02-17 13:53:11 -0800150static uint32_t qsee_perf_client;
151
152struct qseecom_registered_listener_list {
153 struct list_head list;
154 struct qseecom_register_listener_req svc;
155 u8 *sb_reg_req;
156 u8 *sb_virt;
157 s32 sb_phys;
158 size_t sb_length;
159 struct ion_handle *ihandle; /* Retrieve phy addr */
160
161 wait_queue_head_t rcv_req_wq;
162 int rcv_req_flag;
163};
164
165struct qseecom_registered_app_list {
166 struct list_head list;
167 u32 app_id;
168 u32 ref_cnt;
169};
170
171struct qseecom_control {
172 struct ion_client *ion_clnt; /* Ion client */
173 struct list_head registered_listener_list_head;
174 spinlock_t registered_listener_list_lock;
175
176 struct list_head registered_app_list_head;
177 spinlock_t registered_app_list_lock;
178
179 wait_queue_head_t send_resp_wq;
180 int send_resp_flag;
181
182 uint32_t qseos_version;
183};
184
185struct qseecom_client_handle {
186 u32 app_id;
187 u8 *sb_virt;
188 s32 sb_phys;
189 uint32_t user_virt_sb_base;
190 size_t sb_length;
191 struct ion_handle *ihandle; /* Retrieve phy addr */
192};
193
194struct qseecom_listener_handle {
195 u32 id;
196};
197
198static struct qseecom_control qseecom;
199
200struct qseecom_dev_handle {
201 bool service;
202 union {
203 struct qseecom_client_handle client;
204 struct qseecom_listener_handle listener;
205 };
206 bool released;
207 int abort;
208 wait_queue_head_t abort_wq;
209 atomic_t ioctl_count;
210};
211
Ramesh Masavarapua26cce72012-04-09 12:32:25 -0700212/* Function proto types */
213static int qsee_vote_for_clock(int32_t);
214static void qsee_disable_clock_vote(int32_t);
215
Mona Hossain2892b6b2012-02-17 13:53:11 -0800216static int __qseecom_is_svc_unique(struct qseecom_dev_handle *data,
Mona Hossain0af10ab2012-02-28 18:26:41 -0800217 struct qseecom_register_listener_req *svc)
Mona Hossain2892b6b2012-02-17 13:53:11 -0800218{
219 struct qseecom_registered_listener_list *ptr;
220 int unique = 1;
221 unsigned long flags;
222
223 spin_lock_irqsave(&qseecom.registered_listener_list_lock, flags);
224 list_for_each_entry(ptr, &qseecom.registered_listener_list_head, list) {
Mona Hossain0af10ab2012-02-28 18:26:41 -0800225 if (ptr->svc.listener_id == svc->listener_id) {
Mona Hossain2892b6b2012-02-17 13:53:11 -0800226 pr_err("Service id: %u is already registered\n",
227 ptr->svc.listener_id);
228 unique = 0;
229 break;
230 }
231 }
232 spin_unlock_irqrestore(&qseecom.registered_listener_list_lock, flags);
233 return unique;
234}
235
236static struct qseecom_registered_listener_list *__qseecom_find_svc(
237 int32_t listener_id)
238{
239 struct qseecom_registered_listener_list *entry = NULL;
240 unsigned long flags;
241
242 spin_lock_irqsave(&qseecom.registered_listener_list_lock, flags);
243 list_for_each_entry(entry, &qseecom.registered_listener_list_head, list)
244 {
245 if (entry->svc.listener_id == listener_id)
246 break;
247 }
248 spin_unlock_irqrestore(&qseecom.registered_listener_list_lock, flags);
249 return entry;
250}
251
252static int __qseecom_set_sb_memory(struct qseecom_registered_listener_list *svc,
253 struct qseecom_dev_handle *handle,
254 struct qseecom_register_listener_req *listener)
255{
256 int ret = 0;
Mona Hossain2892b6b2012-02-17 13:53:11 -0800257 struct qseecom_register_listener_ireq req;
258 struct qseecom_command_scm_resp resp;
259 ion_phys_addr_t pa;
260
261 /* Get the handle of the shared fd */
Laura Abbottb14ed962012-01-30 14:18:08 -0800262 svc->ihandle = ion_import_dma_buf(qseecom.ion_clnt,
263 listener->ifd_data_fd);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800264 if (svc->ihandle == NULL) {
265 pr_err("Ion client could not retrieve the handle\n");
266 return -ENOMEM;
267 }
268
269 /* Get the physical address of the ION BUF */
270 ret = ion_phys(qseecom.ion_clnt, svc->ihandle, &pa, &svc->sb_length);
271
272 /* Populate the structure for sending scm call to load image */
Mitchel Humpherys911b4b72012-09-12 14:42:50 -0700273 svc->sb_virt = (char *) ion_map_kernel(qseecom.ion_clnt, svc->ihandle);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800274 svc->sb_phys = pa;
275
276 if (qseecom.qseos_version == QSEOS_VERSION_14) {
277 req.qsee_cmd_id = QSEOS_REGISTER_LISTENER;
278 req.listener_id = svc->svc.listener_id;
279 req.sb_len = svc->sb_length;
280 req.sb_ptr = (void *)svc->sb_phys;
281
282 resp.result = QSEOS_RESULT_INCOMPLETE;
283
284 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &req,
285 sizeof(req), &resp, sizeof(resp));
286 if (ret) {
287 pr_err("qseecom_scm_call failed with err: %d\n", ret);
288 return -EINVAL;
289 }
290
291 if (resp.result != QSEOS_RESULT_SUCCESS) {
292 pr_err("Error SB registration req: resp.result = %d\n",
293 resp.result);
294 return -EPERM;
295 }
296 } else {
297 struct qseecom_command cmd;
298 struct qseecom_response resp;
299 struct qse_pr_init_sb_req_s sb_init_req;
300 struct qse_pr_init_sb_rsp_s sb_init_rsp;
301
302 svc->sb_reg_req = kzalloc((sizeof(sb_init_req) +
303 sizeof(sb_init_rsp)), GFP_KERNEL);
304
305 sb_init_req.pr_cmd = TZ_SCHED_CMD_ID_REGISTER_LISTENER;
306 sb_init_req.listener_id = svc->svc.listener_id;
307 sb_init_req.sb_len = svc->sb_length;
308 sb_init_req.sb_ptr = svc->sb_phys;
309
310 memcpy(svc->sb_reg_req, &sb_init_req, sizeof(sb_init_req));
311
312 /* It will always be a new cmd from this method */
313 cmd.cmd_type = TZ_SCHED_CMD_NEW;
314 cmd.sb_in_cmd_addr = (u8 *)(virt_to_phys(svc->sb_reg_req));
315 cmd.sb_in_cmd_len = sizeof(sb_init_req);
316
317 resp.cmd_status = TZ_SCHED_STATUS_INCOMPLETE;
318
319 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &cmd, sizeof(cmd)
320 , &resp, sizeof(resp));
321
322 if (ret) {
323 pr_err("qseecom_scm_call failed with err: %d\n", ret);
324 return -EINVAL;
325 }
326
327 if (resp.cmd_status != TZ_SCHED_STATUS_COMPLETE) {
328 pr_err("SB registration fail resp.cmd_status %d\n",
329 resp.cmd_status);
330 return -EINVAL;
331 }
332 memset(svc->sb_virt, 0, svc->sb_length);
333 }
334 return 0;
335}
336
337static int qseecom_register_listener(struct qseecom_dev_handle *data,
338 void __user *argp)
339{
340 int ret = 0;
341 unsigned long flags;
342 struct qseecom_register_listener_req rcvd_lstnr;
343 struct qseecom_registered_listener_list *new_entry;
344
345 ret = copy_from_user(&rcvd_lstnr, argp, sizeof(rcvd_lstnr));
346 if (ret) {
347 pr_err("copy_from_user failed\n");
348 return ret;
349 }
Mona Hossain0af10ab2012-02-28 18:26:41 -0800350 data->listener.id = 0;
351 data->service = true;
352 if (!__qseecom_is_svc_unique(data, &rcvd_lstnr)) {
Mona Hossain2892b6b2012-02-17 13:53:11 -0800353 pr_err("Service is not unique and is already registered\n");
Mona Hossain0af10ab2012-02-28 18:26:41 -0800354 data->released = true;
355 return -EBUSY;
Mona Hossain2892b6b2012-02-17 13:53:11 -0800356 }
357
358 new_entry = kmalloc(sizeof(*new_entry), GFP_KERNEL);
359 if (!new_entry) {
360 pr_err("kmalloc failed\n");
361 return -ENOMEM;
362 }
363 memcpy(&new_entry->svc, &rcvd_lstnr, sizeof(rcvd_lstnr));
364 new_entry->rcv_req_flag = 0;
365
366 new_entry->svc.listener_id = rcvd_lstnr.listener_id;
367 new_entry->sb_length = rcvd_lstnr.sb_size;
368 if (__qseecom_set_sb_memory(new_entry, data, &rcvd_lstnr)) {
369 pr_err("qseecom_set_sb_memoryfailed\n");
370 kzfree(new_entry);
371 return -ENOMEM;
372 }
Mona Hossain0af10ab2012-02-28 18:26:41 -0800373
Mona Hossain2892b6b2012-02-17 13:53:11 -0800374 data->listener.id = rcvd_lstnr.listener_id;
Mona Hossain2892b6b2012-02-17 13:53:11 -0800375 init_waitqueue_head(&new_entry->rcv_req_wq);
376
377 spin_lock_irqsave(&qseecom.registered_listener_list_lock, flags);
378 list_add_tail(&new_entry->list, &qseecom.registered_listener_list_head);
379 spin_unlock_irqrestore(&qseecom.registered_listener_list_lock, flags);
Mona Hossain0af10ab2012-02-28 18:26:41 -0800380
Mona Hossain2892b6b2012-02-17 13:53:11 -0800381 return ret;
382}
383
384static int qseecom_unregister_listener(struct qseecom_dev_handle *data)
385{
386 int ret = 0;
387 unsigned long flags;
388 uint32_t unmap_mem = 0;
389 struct qseecom_register_listener_ireq req;
390 struct qseecom_registered_listener_list *ptr_svc = NULL;
391 struct qseecom_command_scm_resp resp;
392 struct ion_handle *ihandle = NULL; /* Retrieve phy addr */
393
394 if (qseecom.qseos_version == QSEOS_VERSION_14) {
395 req.qsee_cmd_id = QSEOS_DEREGISTER_LISTENER;
396 req.listener_id = data->listener.id;
397 resp.result = QSEOS_RESULT_INCOMPLETE;
398
399 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &req,
400 sizeof(req), &resp, sizeof(resp));
401 if (ret) {
402 pr_err("qseecom_scm_call failed with err: %d\n", ret);
403 return ret;
404 }
405
406 if (resp.result != QSEOS_RESULT_SUCCESS) {
407 pr_err("SB deregistartion: result=%d\n", resp.result);
408 return -EPERM;
409 }
410 } else {
411 struct qse_pr_init_sb_req_s sb_init_req;
412 struct qseecom_command cmd;
413 struct qseecom_response resp;
414 struct qseecom_registered_listener_list *svc;
415
416 svc = __qseecom_find_svc(data->listener.id);
417 sb_init_req.pr_cmd = TZ_SCHED_CMD_ID_REGISTER_LISTENER;
418 sb_init_req.listener_id = data->listener.id;
419 sb_init_req.sb_len = 0;
420 sb_init_req.sb_ptr = 0;
421
422 memcpy(svc->sb_reg_req, &sb_init_req, sizeof(sb_init_req));
423
424 /* It will always be a new cmd from this method */
425 cmd.cmd_type = TZ_SCHED_CMD_NEW;
426 cmd.sb_in_cmd_addr = (u8 *)(virt_to_phys(svc->sb_reg_req));
427 cmd.sb_in_cmd_len = sizeof(sb_init_req);
428 resp.cmd_status = TZ_SCHED_STATUS_INCOMPLETE;
429
430 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &cmd, sizeof(cmd),
431 &resp, sizeof(resp));
432 if (ret) {
433 pr_err("qseecom_scm_call failed with err: %d\n", ret);
434 return ret;
435 }
436 kzfree(svc->sb_reg_req);
437 if (resp.cmd_status != TZ_SCHED_STATUS_COMPLETE) {
438 pr_err("Error with SB initialization\n");
439 return -EPERM;
440 }
441 }
442 data->abort = 1;
443 spin_lock_irqsave(&qseecom.registered_listener_list_lock, flags);
444 list_for_each_entry(ptr_svc, &qseecom.registered_listener_list_head,
445 list) {
446 if (ptr_svc->svc.listener_id == data->listener.id) {
447 wake_up_all(&ptr_svc->rcv_req_wq);
448 break;
449 }
450 }
451 spin_unlock_irqrestore(&qseecom.registered_listener_list_lock, flags);
452
453 while (atomic_read(&data->ioctl_count) > 1) {
Mona Hossainacea1022012-04-09 13:37:27 -0700454 if (wait_event_freezable(data->abort_wq,
Mona Hossain2892b6b2012-02-17 13:53:11 -0800455 atomic_read(&data->ioctl_count) <= 1)) {
456 pr_err("Interrupted from abort\n");
457 ret = -ERESTARTSYS;
458 break;
459 }
460 }
461
462 spin_lock_irqsave(&qseecom.registered_listener_list_lock, flags);
463 list_for_each_entry(ptr_svc,
464 &qseecom.registered_listener_list_head,
465 list)
466 {
467 if (ptr_svc->svc.listener_id == data->listener.id) {
468 if (ptr_svc->sb_virt) {
469 unmap_mem = 1;
470 ihandle = ptr_svc->ihandle;
471 }
472 list_del(&ptr_svc->list);
473 kzfree(ptr_svc);
474 break;
475 }
476 }
477 spin_unlock_irqrestore(&qseecom.registered_listener_list_lock, flags);
478
479 /* Unmap the memory */
480 if (unmap_mem) {
481 if (!IS_ERR_OR_NULL(ihandle)) {
482 ion_unmap_kernel(qseecom.ion_clnt, ihandle);
483 ion_free(qseecom.ion_clnt, ihandle);
484 }
485 }
486 data->released = true;
487 return ret;
488}
489
490static int qseecom_set_client_mem_param(struct qseecom_dev_handle *data,
491 void __user *argp)
492{
493 ion_phys_addr_t pa;
494 int32_t ret;
Mona Hossain2892b6b2012-02-17 13:53:11 -0800495 struct qseecom_set_sb_mem_param_req req;
496 uint32_t len;
497
498 /* Copy the relevant information needed for loading the image */
499 if (__copy_from_user(&req, (void __user *)argp, sizeof(req)))
500 return -EFAULT;
501
Mona Hossain2892b6b2012-02-17 13:53:11 -0800502 /* Get the handle of the shared fd */
Laura Abbottb14ed962012-01-30 14:18:08 -0800503 data->client.ihandle = ion_import_dma_buf(qseecom.ion_clnt,
504 req.ifd_data_fd);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800505 if (IS_ERR_OR_NULL(data->client.ihandle)) {
506 pr_err("Ion client could not retrieve the handle\n");
507 return -ENOMEM;
508 }
509 /* Get the physical address of the ION BUF */
510 ret = ion_phys(qseecom.ion_clnt, data->client.ihandle, &pa, &len);
511 /* Populate the structure for sending scm call to load image */
512 data->client.sb_virt = (char *) ion_map_kernel(qseecom.ion_clnt,
Mitchel Humpherys911b4b72012-09-12 14:42:50 -0700513 data->client.ihandle);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800514 data->client.sb_phys = pa;
515 data->client.sb_length = req.sb_len;
516 data->client.user_virt_sb_base = req.virt_sb_base;
517 return 0;
518}
519
Mona Hossain2892b6b2012-02-17 13:53:11 -0800520static int __qseecom_listener_has_sent_rsp(struct qseecom_dev_handle *data)
521{
522 int ret;
523 ret = (qseecom.send_resp_flag != 0);
524 return ret || data->abort;
525}
526
527static int __qseecom_process_incomplete_cmd(struct qseecom_dev_handle *data,
528 struct qseecom_command_scm_resp *resp)
529{
530 int ret = 0;
531 uint32_t lstnr;
532 unsigned long flags;
533 struct qseecom_client_listener_data_irsp send_data_rsp;
534 struct qseecom_registered_listener_list *ptr_svc = NULL;
535
536
537 while (resp->result == QSEOS_RESULT_INCOMPLETE) {
538 lstnr = resp->data;
539 /*
540 * Wake up blocking lsitener service with the lstnr id
541 */
542 spin_lock_irqsave(&qseecom.registered_listener_list_lock,
543 flags);
544 list_for_each_entry(ptr_svc,
545 &qseecom.registered_listener_list_head, list) {
546 if (ptr_svc->svc.listener_id == lstnr) {
547 ptr_svc->rcv_req_flag = 1;
548 wake_up_interruptible(&ptr_svc->rcv_req_wq);
549 break;
550 }
551 }
552 spin_unlock_irqrestore(&qseecom.registered_listener_list_lock,
553 flags);
554 if (ptr_svc->svc.listener_id != lstnr) {
555 pr_warning("Service requested for does on exist\n");
556 return -ERESTARTSYS;
557 }
558 pr_debug("waking up rcv_req_wq and "
559 "waiting for send_resp_wq\n");
Mona Hossainacea1022012-04-09 13:37:27 -0700560 if (wait_event_freezable(qseecom.send_resp_wq,
Mona Hossain2892b6b2012-02-17 13:53:11 -0800561 __qseecom_listener_has_sent_rsp(data))) {
562 pr_warning("Interrupted: exiting send_cmd loop\n");
563 return -ERESTARTSYS;
564 }
565
566 if (data->abort) {
Mona Hossaina5f1aab2012-03-29 10:18:07 -0700567 pr_err("Aborting listener service %d\n",
568 data->listener.id);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800569 return -ENODEV;
570 }
571 qseecom.send_resp_flag = 0;
572 send_data_rsp.qsee_cmd_id = QSEOS_LISTENER_DATA_RSP_COMMAND;
573 send_data_rsp.listener_id = lstnr ;
574
575 ret = scm_call(SCM_SVC_TZSCHEDULER, 1,
576 (const void *)&send_data_rsp,
577 sizeof(send_data_rsp), resp,
578 sizeof(*resp));
579 if (ret) {
580 pr_err("qseecom_scm_call failed with err: %d\n", ret);
581 return ret;
582 }
Mona Hossainbb0bca12012-04-12 11:47:45 -0700583 if (resp->result == QSEOS_RESULT_FAILURE) {
584 pr_err("Response result %d not supported\n",
585 resp->result);
586 return -EINVAL;
587 }
Mona Hossain2892b6b2012-02-17 13:53:11 -0800588 }
589 return ret;
590}
591
Ramesh Masavarapu1ee4ac82012-06-22 15:38:34 -0700592static int __qseecom_check_app_exists(struct qseecom_check_app_ireq req)
593{
594 int32_t ret;
595 struct qseecom_command_scm_resp resp;
596
597 /* SCM_CALL to check if app_id for the mentioned app exists */
598 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &req,
599 sizeof(struct qseecom_check_app_ireq),
600 &resp, sizeof(resp));
601 if (ret) {
602 pr_err("scm_call to check if app is already loaded failed\n");
603 return -EINVAL;
604 }
605
Ramesh Masavarapu13d99672012-07-17 11:05:15 -0700606 if (resp.result == QSEOS_RESULT_FAILURE) {
607 return 0;
608 } else {
609 switch (resp.resp_type) {
610 /*qsee returned listener type response */
611 case QSEOS_LISTENER_ID:
612 pr_err("resp type is of listener type instead of app");
613 return -EINVAL;
614 break;
615 case QSEOS_APP_ID:
616 return resp.data;
617 default:
618 pr_err("invalid resp type (%d) from qsee",
619 resp.resp_type);
620 return -ENODEV;
621 break;
622 }
623 }
Ramesh Masavarapu1ee4ac82012-06-22 15:38:34 -0700624}
625
Mona Hossain2892b6b2012-02-17 13:53:11 -0800626static int qseecom_load_app(struct qseecom_dev_handle *data, void __user *argp)
627{
628 struct qseecom_registered_app_list *entry = NULL;
629 unsigned long flags = 0;
630 u32 app_id = 0;
631 struct ion_handle *ihandle; /* Ion handle */
632 struct qseecom_load_img_req load_img_req;
633 int32_t ret;
634 ion_phys_addr_t pa = 0;
635 uint32_t len;
636 struct qseecom_command_scm_resp resp;
637 struct qseecom_check_app_ireq req;
Ramesh Masavarapu13d99672012-07-17 11:05:15 -0700638 struct qseecom_load_app_ireq load_req;
639
Mona Hossain2892b6b2012-02-17 13:53:11 -0800640 /* Copy the relevant information needed for loading the image */
641 if (__copy_from_user(&load_img_req,
642 (void __user *)argp,
643 sizeof(struct qseecom_load_img_req))) {
644 pr_err("copy_from_user failed\n");
645 return -EFAULT;
646 }
Ramesh Masavarapua26cce72012-04-09 12:32:25 -0700647 /* Vote for the SFPB clock */
648 ret = qsee_vote_for_clock(CLK_SFPB);
649 if (ret)
650 pr_warning("Unable to vote for SFPB clock");
Mona Hossain2892b6b2012-02-17 13:53:11 -0800651
652 req.qsee_cmd_id = QSEOS_APP_LOOKUP_COMMAND;
653 memcpy(req.app_name, load_img_req.img_name, MAX_APP_NAME_SIZE);
654
Ramesh Masavarapu13d99672012-07-17 11:05:15 -0700655 pr_warn("App (%s) does not exist, loading apps for first time\n",
Mona Hossaina5f1aab2012-03-29 10:18:07 -0700656 (char *)(req.app_name));
Ramesh Masavarapu13d99672012-07-17 11:05:15 -0700657 /* Get the handle of the shared fd */
658 ihandle = ion_import_dma_buf(qseecom.ion_clnt,
Mona Hossain2892b6b2012-02-17 13:53:11 -0800659 load_img_req.ifd_data_fd);
Ramesh Masavarapu13d99672012-07-17 11:05:15 -0700660 if (IS_ERR_OR_NULL(ihandle)) {
661 pr_err("Ion client could not retrieve the handle\n");
662 qsee_disable_clock_vote(CLK_SFPB);
663 return -ENOMEM;
664 }
Mona Hossain2892b6b2012-02-17 13:53:11 -0800665
Ramesh Masavarapu13d99672012-07-17 11:05:15 -0700666 /* Get the physical address of the ION BUF */
667 ret = ion_phys(qseecom.ion_clnt, ihandle, &pa, &len);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800668
Ramesh Masavarapu13d99672012-07-17 11:05:15 -0700669 /* Populate the structure for sending scm call to load image */
670 load_req.qsee_cmd_id = QSEOS_APP_START_COMMAND;
671 load_req.mdt_len = load_img_req.mdt_len;
672 load_req.img_len = load_img_req.img_len;
673 load_req.phy_addr = pa;
Mona Hossain2892b6b2012-02-17 13:53:11 -0800674
Ramesh Masavarapu13d99672012-07-17 11:05:15 -0700675 /* SCM_CALL to load the app and get the app_id back */
676 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &load_req,
677 sizeof(struct qseecom_load_app_ireq),
678 &resp, sizeof(resp));
679 if (ret) {
680 pr_err("scm_call to load app failed\n");
681 return -EINVAL;
682 }
Mona Hossain2892b6b2012-02-17 13:53:11 -0800683
Ramesh Masavarapu13d99672012-07-17 11:05:15 -0700684 if (resp.result == QSEOS_RESULT_FAILURE) {
685 pr_err("scm_call rsp.result is QSEOS_RESULT_FAILURE\n");
Mona Hossain2892b6b2012-02-17 13:53:11 -0800686 if (!IS_ERR_OR_NULL(ihandle))
687 ion_free(qseecom.ion_clnt, ihandle);
Ramesh Masavarapu13d99672012-07-17 11:05:15 -0700688 qsee_disable_clock_vote(CLK_SFPB);
689 return -EFAULT;
Mona Hossain2892b6b2012-02-17 13:53:11 -0800690 }
Ramesh Masavarapu13d99672012-07-17 11:05:15 -0700691
692 if (resp.result == QSEOS_RESULT_INCOMPLETE) {
693 ret = __qseecom_process_incomplete_cmd(data, &resp);
694 if (ret) {
695 pr_err("process_incomplete_cmd failed err: %d\n",
696 ret);
697 if (!IS_ERR_OR_NULL(ihandle))
698 ion_free(qseecom.ion_clnt, ihandle);
699 qsee_disable_clock_vote(CLK_SFPB);
700 return ret;
701 }
702 }
703
704 if (resp.result != QSEOS_RESULT_SUCCESS) {
705 pr_err("scm_call failed resp.result unknown, %d\n",
706 resp.result);
707 if (!IS_ERR_OR_NULL(ihandle))
708 ion_free(qseecom.ion_clnt, ihandle);
709 qsee_disable_clock_vote(CLK_SFPB);
710 return -EFAULT;
711 }
712
713 app_id = resp.data;
714
715 entry = kmalloc(sizeof(*entry), GFP_KERNEL);
716 if (!entry) {
717 pr_err("kmalloc failed\n");
718 qsee_disable_clock_vote(CLK_SFPB);
719 return -ENOMEM;
720 }
721 entry->app_id = app_id;
722 entry->ref_cnt = 1;
723
724 /* Deallocate the handle */
725 if (!IS_ERR_OR_NULL(ihandle))
726 ion_free(qseecom.ion_clnt, ihandle);
727
728 spin_lock_irqsave(&qseecom.registered_app_list_lock, flags);
729 list_add_tail(&entry->list, &qseecom.registered_app_list_head);
730 spin_unlock_irqrestore(&qseecom.registered_app_list_lock, flags);
731
732 pr_warn("App with id %d (%s) now loaded\n", app_id,
733 (char *)(req.app_name));
734
Mona Hossain2892b6b2012-02-17 13:53:11 -0800735 data->client.app_id = app_id;
736 load_img_req.app_id = app_id;
737 if (copy_to_user(argp, &load_img_req, sizeof(load_img_req))) {
738 pr_err("copy_to_user failed\n");
739 kzfree(entry);
Ramesh Masavarapua26cce72012-04-09 12:32:25 -0700740 qsee_disable_clock_vote(CLK_SFPB);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800741 return -EFAULT;
742 }
Ramesh Masavarapua26cce72012-04-09 12:32:25 -0700743 qsee_disable_clock_vote(CLK_SFPB);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800744 return 0;
745}
746
747static int __qseecom_cleanup_app(struct qseecom_dev_handle *data)
748{
749 wake_up_all(&qseecom.send_resp_wq);
750 while (atomic_read(&data->ioctl_count) > 1) {
Mona Hossainacea1022012-04-09 13:37:27 -0700751 if (wait_event_freezable(data->abort_wq,
Mona Hossain2892b6b2012-02-17 13:53:11 -0800752 atomic_read(&data->ioctl_count) <= 1)) {
753 pr_err("Interrupted from abort\n");
754 return -ERESTARTSYS;
755 break;
756 }
757 }
758 /* Set unload app */
759 return 1;
760}
761
762static int qseecom_unload_app(struct qseecom_dev_handle *data)
763{
764 unsigned long flags;
765 int ret = 0;
766 struct qseecom_command_scm_resp resp;
767 struct qseecom_registered_app_list *ptr_app;
Mona Hossain340dba82012-08-07 19:54:46 -0700768 bool unload = false;
769 bool found_app = false;
Mona Hossain2892b6b2012-02-17 13:53:11 -0800770
Mona Hossain1fb538f2012-08-30 16:19:38 -0700771 if ((qseecom.qseos_version == QSEOS_VERSION_14) &&
772 (data->client.app_id > 0)) {
Mona Hossain2892b6b2012-02-17 13:53:11 -0800773 spin_lock_irqsave(&qseecom.registered_app_list_lock, flags);
774 list_for_each_entry(ptr_app, &qseecom.registered_app_list_head,
775 list) {
776 if (ptr_app->app_id == data->client.app_id) {
Mona Hossain340dba82012-08-07 19:54:46 -0700777 found_app = true;
Mona Hossain2892b6b2012-02-17 13:53:11 -0800778 if (ptr_app->ref_cnt == 1) {
Mona Hossain340dba82012-08-07 19:54:46 -0700779 unload = true;
Mona Hossain2892b6b2012-02-17 13:53:11 -0800780 break;
781 } else {
782 ptr_app->ref_cnt--;
Mona Hossain1fb538f2012-08-30 16:19:38 -0700783 pr_warn("Can't unload app(%d) inuse\n",
Mona Hossaina5f1aab2012-03-29 10:18:07 -0700784 ptr_app->app_id);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800785 break;
786 }
787 }
788 }
789 spin_unlock_irqrestore(&qseecom.registered_app_list_lock,
790 flags);
Mona Hossain1fb538f2012-08-30 16:19:38 -0700791 if (found_app == false) {
792 pr_err("Cannot find app with id = %d\n",
793 data->client.app_id);
794 return -EINVAL;
795 }
Mona Hossain2892b6b2012-02-17 13:53:11 -0800796 }
797
798 if ((unload) && (qseecom.qseos_version == QSEOS_VERSION_14)) {
799 struct qseecom_unload_app_ireq req;
800
Mona Hossain340dba82012-08-07 19:54:46 -0700801 __qseecom_cleanup_app(data);
802 spin_lock_irqsave(&qseecom.registered_app_list_lock, flags);
803 list_del(&ptr_app->list);
804 kzfree(ptr_app);
805 spin_unlock_irqrestore(&qseecom.registered_app_list_lock,
806 flags);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800807 /* Populate the structure for sending scm call to load image */
808 req.qsee_cmd_id = QSEOS_APP_SHUTDOWN_COMMAND;
809 req.app_id = data->client.app_id;
810
811 /* SCM_CALL to unload the app */
812 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &req,
813 sizeof(struct qseecom_unload_app_ireq),
814 &resp, sizeof(resp));
815 if (ret) {
Mona Hossainbb0bca12012-04-12 11:47:45 -0700816 pr_err("scm_call to unload app (id = %d) failed\n",
817 req.app_id);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800818 return -EFAULT;
Mona Hossainbb0bca12012-04-12 11:47:45 -0700819 } else {
820 pr_warn("App id %d now unloaded\n", req.app_id);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800821 }
822 if (resp.result == QSEOS_RESULT_INCOMPLETE) {
823 ret = __qseecom_process_incomplete_cmd(data, &resp);
824 if (ret) {
825 pr_err("process_incomplete_cmd fail err: %d\n",
826 ret);
827 return ret;
828 }
829 }
830 }
831
832 if (qseecom.qseos_version == QSEOS_VERSION_13) {
833 data->abort = 1;
834 wake_up_all(&qseecom.send_resp_wq);
835 while (atomic_read(&data->ioctl_count) > 0) {
Mona Hossainacea1022012-04-09 13:37:27 -0700836 if (wait_event_freezable(data->abort_wq,
Mona Hossain2892b6b2012-02-17 13:53:11 -0800837 atomic_read(&data->ioctl_count) <= 0)) {
838 pr_err("Interrupted from abort\n");
839 ret = -ERESTARTSYS;
840 break;
841 }
842 }
Mona Hossain2892b6b2012-02-17 13:53:11 -0800843 }
Mona Hossain340dba82012-08-07 19:54:46 -0700844 if (!IS_ERR_OR_NULL(data->client.ihandle)) {
845 ion_unmap_kernel(qseecom.ion_clnt, data->client.ihandle);
846 ion_free(qseecom.ion_clnt, data->client.ihandle);
847 data->client.ihandle = NULL;
848 }
Mona Hossain2892b6b2012-02-17 13:53:11 -0800849 data->released = true;
850 return ret;
851}
852
853static uint32_t __qseecom_uvirt_to_kphys(struct qseecom_dev_handle *data,
854 uint32_t virt)
855{
856 return data->client.sb_phys + (virt - data->client.user_virt_sb_base);
857}
858
859static int __qseecom_send_cmd_legacy(struct qseecom_dev_handle *data,
860 struct qseecom_send_cmd_req *req)
861{
862 int ret = 0;
863 unsigned long flags;
864 u32 reqd_len_sb_in = 0;
865 struct qseecom_command cmd;
866 struct qseecom_response resp;
867
868
869 if (req->cmd_req_buf == NULL || req->resp_buf == NULL) {
870 pr_err("cmd buffer or response buffer is null\n");
871 return -EINVAL;
872 }
873
874 if (req->cmd_req_len <= 0 ||
875 req->resp_len <= 0 ||
876 req->cmd_req_len > data->client.sb_length ||
877 req->resp_len > data->client.sb_length) {
878 pr_err("cmd buffer length or "
879 "response buffer length not valid\n");
880 return -EINVAL;
881 }
882
883 reqd_len_sb_in = req->cmd_req_len + req->resp_len;
884 if (reqd_len_sb_in > data->client.sb_length) {
885 pr_debug("Not enough memory to fit cmd_buf and "
886 "resp_buf. Required: %u, Available: %u\n",
887 reqd_len_sb_in, data->client.sb_length);
888 return -ENOMEM;
889 }
890 cmd.cmd_type = TZ_SCHED_CMD_NEW;
891 cmd.sb_in_cmd_addr = (u8 *) data->client.sb_phys;
892 cmd.sb_in_cmd_len = req->cmd_req_len;
893
894 resp.cmd_status = TZ_SCHED_STATUS_INCOMPLETE;
895 resp.sb_in_rsp_addr = (u8 *)data->client.sb_phys + req->cmd_req_len;
896 resp.sb_in_rsp_len = req->resp_len;
897
898 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, (const void *)&cmd,
899 sizeof(cmd), &resp, sizeof(resp));
900
901 if (ret) {
902 pr_err("qseecom_scm_call_legacy failed with err: %d\n", ret);
903 return ret;
904 }
905
906 while (resp.cmd_status != TZ_SCHED_STATUS_COMPLETE) {
907 /*
908 * If cmd is incomplete, get the callback cmd out from SB out
909 * and put it on the list
910 */
911 struct qseecom_registered_listener_list *ptr_svc = NULL;
912 /*
913 * We don't know which service can handle the command. so we
914 * wake up all blocking services and let them figure out if
915 * they can handle the given command.
916 */
917 spin_lock_irqsave(&qseecom.registered_listener_list_lock,
918 flags);
919 list_for_each_entry(ptr_svc,
920 &qseecom.registered_listener_list_head, list) {
921 ptr_svc->rcv_req_flag = 1;
922 wake_up_interruptible(&ptr_svc->rcv_req_wq);
923 }
924 spin_unlock_irqrestore(&qseecom.registered_listener_list_lock,
925 flags);
926
927 pr_debug("waking up rcv_req_wq and "
928 "waiting for send_resp_wq\n");
Mona Hossainacea1022012-04-09 13:37:27 -0700929 if (wait_event_freezable(qseecom.send_resp_wq,
Mona Hossain2892b6b2012-02-17 13:53:11 -0800930 __qseecom_listener_has_sent_rsp(data))) {
931 pr_warning("qseecom Interrupted: exiting send_cmd loop\n");
932 return -ERESTARTSYS;
933 }
934
935 if (data->abort) {
936 pr_err("Aborting driver\n");
937 return -ENODEV;
938 }
939 qseecom.send_resp_flag = 0;
940 cmd.cmd_type = TZ_SCHED_CMD_PENDING;
941 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, (const void *)&cmd,
942 sizeof(cmd), &resp, sizeof(resp));
943 if (ret) {
944 pr_err("qseecom_scm_call failed with err: %d\n", ret);
945 return ret;
946 }
947 }
948 return ret;
949}
950
951static int __qseecom_send_cmd(struct qseecom_dev_handle *data,
952 struct qseecom_send_cmd_req *req)
953{
954 int ret = 0;
955 u32 reqd_len_sb_in = 0;
956 struct qseecom_client_send_data_ireq send_data_req;
957 struct qseecom_command_scm_resp resp;
958
959 if (req->cmd_req_buf == NULL || req->resp_buf == NULL) {
960 pr_err("cmd buffer or response buffer is null\n");
961 return -EINVAL;
962 }
963
964 if (req->cmd_req_len <= 0 ||
965 req->resp_len <= 0 ||
966 req->cmd_req_len > data->client.sb_length ||
967 req->resp_len > data->client.sb_length) {
968 pr_err("cmd buffer length or "
969 "response buffer length not valid\n");
970 return -EINVAL;
971 }
972
973 reqd_len_sb_in = req->cmd_req_len + req->resp_len;
974 if (reqd_len_sb_in > data->client.sb_length) {
975 pr_debug("Not enough memory to fit cmd_buf and "
976 "resp_buf. Required: %u, Available: %u\n",
977 reqd_len_sb_in, data->client.sb_length);
978 return -ENOMEM;
979 }
980
981 send_data_req.qsee_cmd_id = QSEOS_CLIENT_SEND_DATA_COMMAND;
982 send_data_req.app_id = data->client.app_id;
983 send_data_req.req_ptr = (void *)(__qseecom_uvirt_to_kphys(data,
984 (uint32_t)req->cmd_req_buf));
985 send_data_req.req_len = req->cmd_req_len;
986 send_data_req.rsp_ptr = (void *)(__qseecom_uvirt_to_kphys(data,
987 (uint32_t)req->resp_buf));
988 send_data_req.rsp_len = req->resp_len;
989
990 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, (const void *) &send_data_req,
991 sizeof(send_data_req),
992 &resp, sizeof(resp));
993 if (ret) {
994 pr_err("qseecom_scm_call failed with err: %d\n", ret);
995 return ret;
996 }
997
998 if (resp.result == QSEOS_RESULT_INCOMPLETE) {
999 ret = __qseecom_process_incomplete_cmd(data, &resp);
1000 if (ret) {
1001 pr_err("process_incomplete_cmd failed err: %d\n", ret);
1002 return ret;
1003 }
Mona Hossainbb0bca12012-04-12 11:47:45 -07001004 } else {
1005 if (resp.result != QSEOS_RESULT_SUCCESS) {
1006 pr_err("Response result %d not supported\n",
1007 resp.result);
1008 ret = -EINVAL;
1009 }
Mona Hossain2892b6b2012-02-17 13:53:11 -08001010 }
1011 return ret;
1012}
1013
1014
1015static int qseecom_send_cmd(struct qseecom_dev_handle *data, void __user *argp)
1016{
1017 int ret = 0;
1018 struct qseecom_send_cmd_req req;
1019
1020 ret = copy_from_user(&req, argp, sizeof(req));
1021 if (ret) {
1022 pr_err("copy_from_user failed\n");
1023 return ret;
1024 }
1025 if (qseecom.qseos_version == QSEOS_VERSION_14)
1026 ret = __qseecom_send_cmd(data, &req);
1027 else
1028 ret = __qseecom_send_cmd_legacy(data, &req);
1029 if (ret)
1030 return ret;
1031
1032 pr_debug("sending cmd_req->rsp size: %u, ptr: 0x%p\n",
1033 req.resp_len, req.resp_buf);
1034 return ret;
1035}
1036
1037static int __qseecom_send_cmd_req_clean_up(
1038 struct qseecom_send_modfd_cmd_req *req)
1039{
1040 char *field;
1041 uint32_t *update;
1042 int ret = 0;
1043 int i = 0;
1044
1045 for (i = 0; i < MAX_ION_FD; i++) {
Mona Hossaina5f1aab2012-03-29 10:18:07 -07001046 if (req->ifd_data[i].fd > 0) {
Mona Hossain2892b6b2012-02-17 13:53:11 -08001047 field = (char *)req->cmd_req_buf +
1048 req->ifd_data[i].cmd_buf_offset;
1049 update = (uint32_t *) field;
1050 *update = 0;
1051 }
1052 }
1053 return ret;
1054}
1055
1056static int __qseecom_update_with_phy_addr(
1057 struct qseecom_send_modfd_cmd_req *req)
1058{
1059 struct ion_handle *ihandle;
1060 char *field;
1061 uint32_t *update;
1062 ion_phys_addr_t pa;
1063 int ret = 0;
1064 int i = 0;
1065 uint32_t length;
1066
1067 for (i = 0; i < MAX_ION_FD; i++) {
Mona Hossaina5f1aab2012-03-29 10:18:07 -07001068 if (req->ifd_data[i].fd > 0) {
Mona Hossain2892b6b2012-02-17 13:53:11 -08001069 /* Get the handle of the shared fd */
Laura Abbottb14ed962012-01-30 14:18:08 -08001070 ihandle = ion_import_dma_buf(qseecom.ion_clnt,
Mona Hossain2892b6b2012-02-17 13:53:11 -08001071 req->ifd_data[i].fd);
1072 if (IS_ERR_OR_NULL(ihandle)) {
1073 pr_err("Ion client can't retrieve the handle\n");
1074 return -ENOMEM;
1075 }
1076 field = (char *) req->cmd_req_buf +
1077 req->ifd_data[i].cmd_buf_offset;
1078 update = (uint32_t *) field;
1079
1080 /* Populate the cmd data structure with the phys_addr */
1081 ret = ion_phys(qseecom.ion_clnt, ihandle, &pa, &length);
1082 if (ret)
1083 return -ENOMEM;
1084
1085 *update = (uint32_t)pa;
1086 /* Deallocate the handle */
1087 if (!IS_ERR_OR_NULL(ihandle))
1088 ion_free(qseecom.ion_clnt, ihandle);
1089 }
1090 }
1091 return ret;
1092}
1093
1094static int qseecom_send_modfd_cmd(struct qseecom_dev_handle *data,
1095 void __user *argp)
1096{
1097 int ret = 0;
1098 struct qseecom_send_modfd_cmd_req req;
1099 struct qseecom_send_cmd_req send_cmd_req;
1100
1101 ret = copy_from_user(&req, argp, sizeof(req));
1102 if (ret) {
1103 pr_err("copy_from_user failed\n");
1104 return ret;
1105 }
1106 send_cmd_req.cmd_req_buf = req.cmd_req_buf;
1107 send_cmd_req.cmd_req_len = req.cmd_req_len;
1108 send_cmd_req.resp_buf = req.resp_buf;
1109 send_cmd_req.resp_len = req.resp_len;
1110
1111 ret = __qseecom_update_with_phy_addr(&req);
1112 if (ret)
1113 return ret;
1114 if (qseecom.qseos_version == QSEOS_VERSION_14)
1115 ret = __qseecom_send_cmd(data, &send_cmd_req);
1116 else
1117 ret = __qseecom_send_cmd_legacy(data, &send_cmd_req);
1118 __qseecom_send_cmd_req_clean_up(&req);
1119
1120 if (ret)
1121 return ret;
1122
1123 pr_debug("sending cmd_req->rsp size: %u, ptr: 0x%p\n",
1124 req.resp_len, req.resp_buf);
1125 return ret;
1126}
1127
1128static int __qseecom_listener_has_rcvd_req(struct qseecom_dev_handle *data,
1129 struct qseecom_registered_listener_list *svc)
1130{
1131 int ret;
1132 ret = (svc->rcv_req_flag != 0);
1133 return ret || data->abort;
1134}
1135
1136static int qseecom_receive_req(struct qseecom_dev_handle *data)
1137{
1138 int ret = 0;
1139 struct qseecom_registered_listener_list *this_lstnr;
1140
1141 this_lstnr = __qseecom_find_svc(data->listener.id);
1142 while (1) {
Mona Hossainacea1022012-04-09 13:37:27 -07001143 if (wait_event_freezable(this_lstnr->rcv_req_wq,
Mona Hossain2892b6b2012-02-17 13:53:11 -08001144 __qseecom_listener_has_rcvd_req(data,
1145 this_lstnr))) {
1146 pr_warning("Interrupted: exiting wait_rcv_req loop\n");
1147 /* woken up for different reason */
1148 return -ERESTARTSYS;
1149 }
1150
1151 if (data->abort) {
1152 pr_err("Aborting driver!\n");
1153 return -ENODEV;
1154 }
1155 this_lstnr->rcv_req_flag = 0;
1156 if (qseecom.qseos_version == QSEOS_VERSION_13) {
1157 if (*((uint32_t *)this_lstnr->sb_virt) != 0)
1158 break;
1159 } else {
1160 break;
1161 }
1162 }
1163 return ret;
1164}
1165
1166static int qseecom_send_resp(void)
1167{
1168 qseecom.send_resp_flag = 1;
1169 wake_up_interruptible(&qseecom.send_resp_wq);
1170 return 0;
1171}
1172
1173static int qseecom_get_qseos_version(struct qseecom_dev_handle *data,
1174 void __user *argp)
1175{
1176 struct qseecom_qseos_version_req req;
1177
1178 if (copy_from_user(&req, argp, sizeof(req))) {
1179 pr_err("copy_from_user failed");
1180 return -EINVAL;
1181 }
1182 req.qseos_version = qseecom.qseos_version;
1183 if (copy_to_user(argp, &req, sizeof(req))) {
1184 pr_err("copy_to_user failed");
1185 return -EINVAL;
1186 }
1187 return 0;
1188}
1189
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001190static int qsee_vote_for_clock(int32_t clk_type)
Mona Hossain2892b6b2012-02-17 13:53:11 -08001191{
1192 int ret = 0;
1193
1194 if (!qsee_perf_client)
Ramesh Masavarapue640e842012-04-03 11:21:54 -07001195 return ret;
Mona Hossain2892b6b2012-02-17 13:53:11 -08001196
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001197 switch (clk_type) {
1198 case CLK_DFAB:
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001199 mutex_lock(&qsee_bw_mutex);
1200 if (!qsee_bw_count) {
1201 ret = msm_bus_scale_client_update_request(
1202 qsee_perf_client, 1);
1203 if (ret)
1204 pr_err("DFAB Bandwidth req failed (%d)\n",
1205 ret);
1206 else
1207 qsee_bw_count++;
1208 }
1209 mutex_unlock(&qsee_bw_mutex);
1210 break;
1211 case CLK_SFPB:
1212 mutex_lock(&qsee_sfpb_bw_mutex);
1213 if (!qsee_sfpb_bw_count) {
1214 ret = msm_bus_scale_client_update_request(
1215 qsee_perf_client, 2);
1216 if (ret)
1217 pr_err("SFPB Bandwidth req failed (%d)\n",
1218 ret);
1219 else
1220 qsee_sfpb_bw_count++;
1221 }
1222 mutex_unlock(&qsee_sfpb_bw_mutex);
1223 break;
1224 default:
1225 pr_err("Clock type not defined\n");
1226 break;
Mona Hossain2892b6b2012-02-17 13:53:11 -08001227 }
Mona Hossain2892b6b2012-02-17 13:53:11 -08001228 return ret;
1229}
1230
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001231static void qsee_disable_clock_vote(int32_t clk_type)
Mona Hossain2892b6b2012-02-17 13:53:11 -08001232{
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001233 int32_t ret = 0;
Ramesh Masavarapua1bc0e42012-03-05 07:42:48 -08001234
Mona Hossain2892b6b2012-02-17 13:53:11 -08001235 if (!qsee_perf_client)
Ramesh Masavarapua1bc0e42012-03-05 07:42:48 -08001236 return;
1237
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001238 switch (clk_type) {
1239 case CLK_DFAB:
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001240 mutex_lock(&qsee_bw_mutex);
1241 if (qsee_bw_count > 0) {
1242 if (qsee_bw_count-- == 1) {
1243 ret = msm_bus_scale_client_update_request(
1244 qsee_perf_client, 0);
1245 if (ret)
1246 pr_err("SFPB Bandwidth req fail (%d)\n",
1247 ret);
1248 }
1249 }
1250 mutex_unlock(&qsee_bw_mutex);
1251 break;
1252 case CLK_SFPB:
1253 mutex_lock(&qsee_sfpb_bw_mutex);
1254 if (qsee_sfpb_bw_count > 0) {
1255 if (qsee_sfpb_bw_count-- == 1) {
1256 ret = msm_bus_scale_client_update_request(
1257 qsee_perf_client, 0);
1258 if (ret)
1259 pr_err("SFPB Bandwidth req fail (%d)\n",
1260 ret);
1261 }
1262 }
1263 mutex_unlock(&qsee_sfpb_bw_mutex);
1264 break;
1265 default:
1266 pr_err("Clock type not defined\n");
1267 break;
Ramesh Masavarapue640e842012-04-03 11:21:54 -07001268 }
Mona Hossain2892b6b2012-02-17 13:53:11 -08001269
Mona Hossain2892b6b2012-02-17 13:53:11 -08001270}
1271
Mona Hossain5ab9d772012-04-11 21:00:40 -07001272static int qseecom_load_external_elf(struct qseecom_dev_handle *data,
1273 void __user *argp)
1274{
1275 struct ion_handle *ihandle; /* Ion handle */
1276 struct qseecom_load_img_req load_img_req;
Ramesh Masavarapu41af8ab2012-05-21 11:08:00 -07001277 int ret;
1278 int set_cpu_ret = 0;
Mona Hossain5ab9d772012-04-11 21:00:40 -07001279 ion_phys_addr_t pa = 0;
1280 uint32_t len;
Ramesh Masavarapu41af8ab2012-05-21 11:08:00 -07001281 struct cpumask mask;
Mona Hossain5ab9d772012-04-11 21:00:40 -07001282 struct qseecom_load_app_ireq load_req;
1283 struct qseecom_command_scm_resp resp;
1284
1285 /* Copy the relevant information needed for loading the image */
1286 if (__copy_from_user(&load_img_req,
1287 (void __user *)argp,
1288 sizeof(struct qseecom_load_img_req))) {
1289 pr_err("copy_from_user failed\n");
1290 return -EFAULT;
1291 }
1292
1293 /* Get the handle of the shared fd */
Laura Abbottb14ed962012-01-30 14:18:08 -08001294 ihandle = ion_import_dma_buf(qseecom.ion_clnt,
Mona Hossain5ab9d772012-04-11 21:00:40 -07001295 load_img_req.ifd_data_fd);
1296 if (IS_ERR_OR_NULL(ihandle)) {
1297 pr_err("Ion client could not retrieve the handle\n");
1298 return -ENOMEM;
1299 }
1300
1301 /* Get the physical address of the ION BUF */
1302 ret = ion_phys(qseecom.ion_clnt, ihandle, &pa, &len);
1303
1304 /* Populate the structure for sending scm call to load image */
1305 load_req.qsee_cmd_id = QSEOS_LOAD_EXTERNAL_ELF_COMMAND;
1306 load_req.mdt_len = load_img_req.mdt_len;
1307 load_req.img_len = load_img_req.img_len;
1308 load_req.phy_addr = pa;
1309
Ramesh Masavarapu41af8ab2012-05-21 11:08:00 -07001310 /* SCM_CALL tied to Core0 */
1311 mask = CPU_MASK_CPU0;
1312 set_cpu_ret = set_cpus_allowed_ptr(current, &mask);
1313 if (set_cpu_ret) {
1314 pr_err("set_cpus_allowed_ptr failed : ret %d\n",
1315 set_cpu_ret);
1316 ret = -EFAULT;
1317 goto qseecom_load_external_elf_set_cpu_err;
1318 }
1319
Mona Hossain5ab9d772012-04-11 21:00:40 -07001320 /* SCM_CALL to load the external elf */
1321 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &load_req,
1322 sizeof(struct qseecom_load_app_ireq),
1323 &resp, sizeof(resp));
1324 if (ret) {
Ramesh Masavarapu41af8ab2012-05-21 11:08:00 -07001325 pr_err("scm_call to load failed : ret %d\n",
Mona Hossain5ab9d772012-04-11 21:00:40 -07001326 ret);
1327 ret = -EFAULT;
Ramesh Masavarapu41af8ab2012-05-21 11:08:00 -07001328 goto qseecom_load_external_elf_scm_err;
Mona Hossain5ab9d772012-04-11 21:00:40 -07001329 }
1330
1331 if (resp.result == QSEOS_RESULT_INCOMPLETE) {
1332 ret = __qseecom_process_incomplete_cmd(data, &resp);
1333 if (ret)
1334 pr_err("process_incomplete_cmd failed err: %d\n",
1335 ret);
1336 } else {
1337 if (resp.result != QSEOS_RESULT_SUCCESS) {
1338 pr_err("scm_call to load image failed resp.result =%d\n",
1339 resp.result);
1340 ret = -EFAULT;
1341 }
1342 }
Ramesh Masavarapu41af8ab2012-05-21 11:08:00 -07001343
1344qseecom_load_external_elf_scm_err:
1345 /* Restore the CPU mask */
1346 mask = CPU_MASK_ALL;
1347 set_cpu_ret = set_cpus_allowed_ptr(current, &mask);
1348 if (set_cpu_ret) {
1349 pr_err("set_cpus_allowed_ptr failed to restore mask: ret %d\n",
1350 set_cpu_ret);
1351 ret = -EFAULT;
1352 }
1353
1354qseecom_load_external_elf_set_cpu_err:
Mona Hossain5ab9d772012-04-11 21:00:40 -07001355 /* Deallocate the handle */
1356 if (!IS_ERR_OR_NULL(ihandle))
1357 ion_free(qseecom.ion_clnt, ihandle);
1358
1359 return ret;
1360}
1361
1362static int qseecom_unload_external_elf(struct qseecom_dev_handle *data)
1363{
1364 int ret = 0;
Ramesh Masavarapu41af8ab2012-05-21 11:08:00 -07001365 int set_cpu_ret = 0;
Mona Hossain5ab9d772012-04-11 21:00:40 -07001366 struct qseecom_command_scm_resp resp;
1367 struct qseecom_unload_app_ireq req;
Ramesh Masavarapu41af8ab2012-05-21 11:08:00 -07001368 struct cpumask mask;
Mona Hossain5ab9d772012-04-11 21:00:40 -07001369
1370 /* Populate the structure for sending scm call to unload image */
1371 req.qsee_cmd_id = QSEOS_UNLOAD_EXTERNAL_ELF_COMMAND;
Ramesh Masavarapu41af8ab2012-05-21 11:08:00 -07001372
1373 /* SCM_CALL tied to Core0 */
1374 mask = CPU_MASK_CPU0;
1375 ret = set_cpus_allowed_ptr(current, &mask);
1376 if (ret) {
1377 pr_err("set_cpus_allowed_ptr failed : ret %d\n",
1378 ret);
1379 return -EFAULT;
1380 }
1381
Mona Hossain5ab9d772012-04-11 21:00:40 -07001382 /* SCM_CALL to unload the external elf */
1383 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &req,
1384 sizeof(struct qseecom_unload_app_ireq),
1385 &resp, sizeof(resp));
1386 if (ret) {
1387 pr_err("scm_call to unload failed : ret %d\n",
1388 ret);
Ramesh Masavarapu41af8ab2012-05-21 11:08:00 -07001389 ret = -EFAULT;
1390 goto qseecom_unload_external_elf_scm_err;
Mona Hossain5ab9d772012-04-11 21:00:40 -07001391 }
1392 if (resp.result == QSEOS_RESULT_INCOMPLETE) {
1393 ret = __qseecom_process_incomplete_cmd(data, &resp);
1394 if (ret)
1395 pr_err("process_incomplete_cmd fail err: %d\n",
1396 ret);
1397 } else {
1398 if (resp.result != QSEOS_RESULT_SUCCESS) {
1399 pr_err("scm_call to unload image failed resp.result =%d\n",
1400 resp.result);
1401 ret = -EFAULT;
1402 }
1403 }
Ramesh Masavarapu41af8ab2012-05-21 11:08:00 -07001404
1405qseecom_unload_external_elf_scm_err:
1406 /* Restore the CPU mask */
1407 mask = CPU_MASK_ALL;
1408 set_cpu_ret = set_cpus_allowed_ptr(current, &mask);
1409 if (set_cpu_ret) {
1410 pr_err("set_cpus_allowed_ptr failed to restore mask: ret %d\n",
1411 set_cpu_ret);
1412 ret = -EFAULT;
1413 }
1414
Mona Hossain5ab9d772012-04-11 21:00:40 -07001415 return ret;
1416}
Mona Hossain2892b6b2012-02-17 13:53:11 -08001417
Ramesh Masavarapu1ee4ac82012-06-22 15:38:34 -07001418static int qseecom_query_app_loaded(struct qseecom_dev_handle *data,
1419 void __user *argp)
1420{
1421
1422 int32_t ret;
1423 struct qseecom_qseos_app_load_query query_req;
1424 struct qseecom_check_app_ireq req;
Ramesh Masavarapu13d99672012-07-17 11:05:15 -07001425 struct qseecom_registered_app_list *entry = NULL;
1426 unsigned long flags = 0;
Ramesh Masavarapu1ee4ac82012-06-22 15:38:34 -07001427
1428 /* Copy the relevant information needed for loading the image */
1429 if (__copy_from_user(&query_req,
1430 (void __user *)argp,
1431 sizeof(struct qseecom_qseos_app_load_query))) {
1432 pr_err("copy_from_user failed\n");
1433 return -EFAULT;
1434 }
1435
1436 req.qsee_cmd_id = QSEOS_APP_LOOKUP_COMMAND;
1437 memcpy(req.app_name, query_req.app_name, MAX_APP_NAME_SIZE);
1438
1439 ret = __qseecom_check_app_exists(req);
Ramesh Masavarapu13d99672012-07-17 11:05:15 -07001440
1441 if ((ret == -EINVAL) || (ret == -ENODEV)) {
Ramesh Masavarapu1ee4ac82012-06-22 15:38:34 -07001442 pr_err(" scm call to check if app is loaded failed");
1443 return ret; /* scm call failed */
1444 } else if (ret > 0) {
Ramesh Masavarapu13d99672012-07-17 11:05:15 -07001445 pr_warn("App id %d (%s) already exists\n", ret,
1446 (char *)(req.app_name));
1447 spin_lock_irqsave(&qseecom.registered_app_list_lock, flags);
1448 list_for_each_entry(entry,
1449 &qseecom.registered_app_list_head, list){
1450 if (entry->app_id == ret) {
1451 entry->ref_cnt++;
1452 break;
1453 }
1454 }
1455 spin_unlock_irqrestore(
1456 &qseecom.registered_app_list_lock, flags);
1457 data->client.app_id = ret;
1458 query_req.app_id = ret;
1459
1460 if (copy_to_user(argp, &query_req, sizeof(query_req))) {
1461 pr_err("copy_to_user failed\n");
1462 return -EFAULT;
1463 }
Ramesh Masavarapu1ee4ac82012-06-22 15:38:34 -07001464 return -EEXIST; /* app already loaded */
1465 } else {
1466 return 0; /* app not loaded */
1467 }
1468}
1469
Mona Hossain2892b6b2012-02-17 13:53:11 -08001470static long qseecom_ioctl(struct file *file, unsigned cmd,
1471 unsigned long arg)
1472{
1473 int ret = 0;
1474 struct qseecom_dev_handle *data = file->private_data;
1475 void __user *argp = (void __user *) arg;
1476
1477 if (data->abort) {
1478 pr_err("Aborting qseecom driver\n");
1479 return -ENODEV;
1480 }
1481
1482 switch (cmd) {
1483 case QSEECOM_IOCTL_REGISTER_LISTENER_REQ: {
1484 pr_debug("ioctl register_listener_req()\n");
1485 atomic_inc(&data->ioctl_count);
1486 ret = qseecom_register_listener(data, argp);
1487 atomic_dec(&data->ioctl_count);
1488 wake_up_all(&data->abort_wq);
1489 if (ret)
1490 pr_err("failed qseecom_register_listener: %d\n", ret);
1491 break;
1492 }
1493 case QSEECOM_IOCTL_UNREGISTER_LISTENER_REQ: {
1494 pr_debug("ioctl unregister_listener_req()\n");
1495 atomic_inc(&data->ioctl_count);
1496 ret = qseecom_unregister_listener(data);
1497 atomic_dec(&data->ioctl_count);
1498 wake_up_all(&data->abort_wq);
1499 if (ret)
1500 pr_err("failed qseecom_unregister_listener: %d\n", ret);
1501 break;
1502 }
1503 case QSEECOM_IOCTL_SEND_CMD_REQ: {
1504 /* Only one client allowed here at a time */
Ramesh Masavarapud8610112012-06-26 15:32:52 -07001505 mutex_lock(&app_access_lock);
Mona Hossain2892b6b2012-02-17 13:53:11 -08001506 atomic_inc(&data->ioctl_count);
1507 ret = qseecom_send_cmd(data, argp);
1508 atomic_dec(&data->ioctl_count);
1509 wake_up_all(&data->abort_wq);
Ramesh Masavarapud8610112012-06-26 15:32:52 -07001510 mutex_unlock(&app_access_lock);
Mona Hossain2892b6b2012-02-17 13:53:11 -08001511 if (ret)
1512 pr_err("failed qseecom_send_cmd: %d\n", ret);
1513 break;
1514 }
1515 case QSEECOM_IOCTL_SEND_MODFD_CMD_REQ: {
1516 /* Only one client allowed here at a time */
Ramesh Masavarapud8610112012-06-26 15:32:52 -07001517 mutex_lock(&app_access_lock);
Mona Hossain2892b6b2012-02-17 13:53:11 -08001518 atomic_inc(&data->ioctl_count);
1519 ret = qseecom_send_modfd_cmd(data, argp);
1520 atomic_dec(&data->ioctl_count);
1521 wake_up_all(&data->abort_wq);
Ramesh Masavarapud8610112012-06-26 15:32:52 -07001522 mutex_unlock(&app_access_lock);
Mona Hossain2892b6b2012-02-17 13:53:11 -08001523 if (ret)
1524 pr_err("failed qseecom_send_cmd: %d\n", ret);
1525 break;
1526 }
1527 case QSEECOM_IOCTL_RECEIVE_REQ: {
1528 atomic_inc(&data->ioctl_count);
1529 ret = qseecom_receive_req(data);
1530 atomic_dec(&data->ioctl_count);
1531 wake_up_all(&data->abort_wq);
1532 if (ret)
1533 pr_err("failed qseecom_receive_req: %d\n", ret);
1534 break;
1535 }
1536 case QSEECOM_IOCTL_SEND_RESP_REQ: {
1537 atomic_inc(&data->ioctl_count);
1538 ret = qseecom_send_resp();
1539 atomic_dec(&data->ioctl_count);
1540 wake_up_all(&data->abort_wq);
1541 if (ret)
1542 pr_err("failed qseecom_send_resp: %d\n", ret);
1543 break;
1544 }
1545 case QSEECOM_IOCTL_SET_MEM_PARAM_REQ: {
1546 ret = qseecom_set_client_mem_param(data, argp);
1547 if (ret)
1548 pr_err("failed Qqseecom_set_mem_param request: %d\n",
1549 ret);
1550 break;
1551 }
1552 case QSEECOM_IOCTL_LOAD_APP_REQ: {
1553 mutex_lock(&app_access_lock);
1554 atomic_inc(&data->ioctl_count);
1555 ret = qseecom_load_app(data, argp);
1556 atomic_dec(&data->ioctl_count);
1557 mutex_unlock(&app_access_lock);
1558 if (ret)
1559 pr_err("failed load_app request: %d\n", ret);
1560 break;
1561 }
1562 case QSEECOM_IOCTL_UNLOAD_APP_REQ: {
1563 mutex_lock(&app_access_lock);
1564 atomic_inc(&data->ioctl_count);
1565 ret = qseecom_unload_app(data);
1566 atomic_dec(&data->ioctl_count);
1567 mutex_unlock(&app_access_lock);
1568 if (ret)
1569 pr_err("failed unload_app request: %d\n", ret);
1570 break;
1571 }
1572 case QSEECOM_IOCTL_GET_QSEOS_VERSION_REQ: {
1573 atomic_inc(&data->ioctl_count);
1574 ret = qseecom_get_qseos_version(data, argp);
1575 if (ret)
1576 pr_err("qseecom_get_qseos_version: %d\n", ret);
1577 atomic_dec(&data->ioctl_count);
1578 break;
1579 }
1580 case QSEECOM_IOCTL_PERF_ENABLE_REQ:{
1581 atomic_inc(&data->ioctl_count);
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001582 ret = qsee_vote_for_clock(CLK_DFAB);
Mona Hossain2892b6b2012-02-17 13:53:11 -08001583 if (ret)
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001584 pr_err("Failed to vote for DFAB clock%d\n", ret);
Mona Hossain2892b6b2012-02-17 13:53:11 -08001585 atomic_dec(&data->ioctl_count);
1586 break;
1587 }
1588 case QSEECOM_IOCTL_PERF_DISABLE_REQ:{
1589 atomic_inc(&data->ioctl_count);
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001590 qsee_disable_clock_vote(CLK_DFAB);
Mona Hossain2892b6b2012-02-17 13:53:11 -08001591 atomic_dec(&data->ioctl_count);
1592 break;
1593 }
Mona Hossain5ab9d772012-04-11 21:00:40 -07001594 case QSEECOM_IOCTL_LOAD_EXTERNAL_ELF_REQ: {
1595 data->released = true;
1596 if (qseecom.qseos_version == QSEOS_VERSION_13) {
1597 pr_err("Loading External elf image unsupported in rev 0x13\n");
1598 ret = -EINVAL;
1599 break;
1600 }
1601 mutex_lock(&app_access_lock);
1602 atomic_inc(&data->ioctl_count);
1603 ret = qseecom_load_external_elf(data, argp);
1604 atomic_dec(&data->ioctl_count);
1605 mutex_unlock(&app_access_lock);
1606 if (ret)
1607 pr_err("failed load_external_elf request: %d\n", ret);
1608 break;
1609 }
1610 case QSEECOM_IOCTL_UNLOAD_EXTERNAL_ELF_REQ: {
1611 data->released = true;
1612 if (qseecom.qseos_version == QSEOS_VERSION_13) {
1613 pr_err("Unloading External elf image unsupported in rev 0x13\n");
1614 ret = -EINVAL;
1615 break;
1616 }
1617 mutex_lock(&app_access_lock);
1618 atomic_inc(&data->ioctl_count);
1619 ret = qseecom_unload_external_elf(data);
1620 atomic_dec(&data->ioctl_count);
1621 mutex_unlock(&app_access_lock);
1622 if (ret)
1623 pr_err("failed unload_app request: %d\n", ret);
1624 break;
1625 }
Ramesh Masavarapu1ee4ac82012-06-22 15:38:34 -07001626 case QSEECOM_IOCTL_APP_LOADED_QUERY_REQ: {
1627 mutex_lock(&app_access_lock);
1628 atomic_inc(&data->ioctl_count);
1629 ret = qseecom_query_app_loaded(data, argp);
1630 atomic_dec(&data->ioctl_count);
1631 mutex_unlock(&app_access_lock);
1632 break;
1633 }
Mona Hossain2892b6b2012-02-17 13:53:11 -08001634 default:
1635 return -EINVAL;
1636 }
1637 return ret;
1638}
1639
1640static int qseecom_open(struct inode *inode, struct file *file)
1641{
1642 int ret = 0;
1643 struct qseecom_dev_handle *data;
1644
1645 data = kzalloc(sizeof(*data), GFP_KERNEL);
1646 if (!data) {
1647 pr_err("kmalloc failed\n");
1648 return -ENOMEM;
1649 }
1650 file->private_data = data;
1651 data->abort = 0;
1652 data->service = false;
1653 data->released = false;
1654 init_waitqueue_head(&data->abort_wq);
1655 atomic_set(&data->ioctl_count, 0);
Mona Hossain7cdbfaf2012-03-15 18:56:10 -07001656 if (qseecom.qseos_version == QSEOS_VERSION_13) {
1657 int pil_error;
1658 mutex_lock(&pil_access_lock);
1659 if (pil_ref_cnt == 0) {
1660 pil = pil_get("tzapps");
1661 if (IS_ERR(pil)) {
1662 pr_err("Playready PIL image load failed\n");
1663 pil_error = PTR_ERR(pil);
1664 pil = NULL;
1665 pr_debug("tzapps image load FAILED\n");
1666 mutex_unlock(&pil_access_lock);
1667 return pil_error;
1668 }
1669 }
1670 pil_ref_cnt++;
1671 mutex_unlock(&pil_access_lock);
1672 }
Mona Hossain2892b6b2012-02-17 13:53:11 -08001673 return ret;
1674}
1675
1676static int qseecom_release(struct inode *inode, struct file *file)
1677{
1678 struct qseecom_dev_handle *data = file->private_data;
1679 int ret = 0;
1680
1681 if (data->released == false) {
1682 pr_warn("data->released == false\n");
1683 if (data->service)
1684 ret = qseecom_unregister_listener(data);
1685 else
1686 ret = qseecom_unload_app(data);
1687 if (ret) {
1688 pr_err("Close failed\n");
1689 return ret;
1690 }
1691 }
Mona Hossain7cdbfaf2012-03-15 18:56:10 -07001692 if (qseecom.qseos_version == QSEOS_VERSION_13) {
1693 mutex_lock(&pil_access_lock);
1694 if (pil_ref_cnt == 1)
1695 pil_put(pil);
1696 pil_ref_cnt--;
1697 mutex_unlock(&pil_access_lock);
1698 }
Mona Hossain2892b6b2012-02-17 13:53:11 -08001699 kfree(data);
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001700 qsee_disable_clock_vote(CLK_DFAB);
Ramesh Masavarapua1bc0e42012-03-05 07:42:48 -08001701
Mona Hossain2892b6b2012-02-17 13:53:11 -08001702 return ret;
1703}
1704
Mona Hossain2892b6b2012-02-17 13:53:11 -08001705static const struct file_operations qseecom_fops = {
1706 .owner = THIS_MODULE,
1707 .unlocked_ioctl = qseecom_ioctl,
1708 .open = qseecom_open,
1709 .release = qseecom_release
1710};
1711
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001712static int __devinit qseecom_probe(struct platform_device *pdev)
Mona Hossain2892b6b2012-02-17 13:53:11 -08001713{
1714 int rc;
Mona Hossain2892b6b2012-02-17 13:53:11 -08001715 struct device *class_dev;
1716 char qsee_not_legacy = 0;
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001717 struct msm_bus_scale_pdata *qseecom_platform_support;
Mona Hossain2892b6b2012-02-17 13:53:11 -08001718 uint32_t system_call_id = QSEOS_CHECK_VERSION_CMD;
1719
Ramesh Masavarapue640e842012-04-03 11:21:54 -07001720 qsee_bw_count = 0;
Ramesh Masavarapue640e842012-04-03 11:21:54 -07001721 qsee_perf_client = 0;
1722
Mona Hossain2892b6b2012-02-17 13:53:11 -08001723 rc = alloc_chrdev_region(&qseecom_device_no, 0, 1, QSEECOM_DEV);
1724 if (rc < 0) {
1725 pr_err("alloc_chrdev_region failed %d\n", rc);
1726 return rc;
1727 }
1728
1729 driver_class = class_create(THIS_MODULE, QSEECOM_DEV);
1730 if (IS_ERR(driver_class)) {
1731 rc = -ENOMEM;
1732 pr_err("class_create failed %d\n", rc);
1733 goto unregister_chrdev_region;
1734 }
1735
1736 class_dev = device_create(driver_class, NULL, qseecom_device_no, NULL,
1737 QSEECOM_DEV);
1738 if (!class_dev) {
1739 pr_err("class_device_create failed %d\n", rc);
1740 rc = -ENOMEM;
1741 goto class_destroy;
1742 }
1743
1744 cdev_init(&qseecom_cdev, &qseecom_fops);
1745 qseecom_cdev.owner = THIS_MODULE;
1746
1747 rc = cdev_add(&qseecom_cdev, MKDEV(MAJOR(qseecom_device_no), 0), 1);
1748 if (rc < 0) {
1749 pr_err("cdev_add failed %d\n", rc);
1750 goto err;
1751 }
1752
1753 INIT_LIST_HEAD(&qseecom.registered_listener_list_head);
1754 spin_lock_init(&qseecom.registered_listener_list_lock);
1755 INIT_LIST_HEAD(&qseecom.registered_app_list_head);
1756 spin_lock_init(&qseecom.registered_app_list_lock);
1757 init_waitqueue_head(&qseecom.send_resp_wq);
1758 qseecom.send_resp_flag = 0;
1759
1760 rc = scm_call(6, 1, &system_call_id, sizeof(system_call_id),
1761 &qsee_not_legacy, sizeof(qsee_not_legacy));
1762 if (rc) {
1763 pr_err("Failed to retrieve QSEE version information %d\n", rc);
1764 goto err;
1765 }
1766 if (qsee_not_legacy)
1767 qseecom.qseos_version = QSEOS_VERSION_14;
1768 else {
1769 qseecom.qseos_version = QSEOS_VERSION_13;
1770 pil = NULL;
1771 pil_ref_cnt = 0;
1772 }
1773 /* Create ION msm client */
1774 qseecom.ion_clnt = msm_ion_client_create(0x03, "qseecom-kernel");
1775 if (qseecom.ion_clnt == NULL) {
1776 pr_err("Ion client cannot be created\n");
1777 rc = -ENOMEM;
1778 goto err;
1779 }
1780
1781 /* register client for bus scaling */
Ramesh Masavarapufb1f01e2012-06-14 09:40:40 -07001782 if (!pdev->dev.of_node) {
1783 qseecom_platform_support = (struct msm_bus_scale_pdata *)
1784 pdev->dev.platform_data;
1785 qsee_perf_client = msm_bus_scale_register_client(
1786 qseecom_platform_support);
1787
Ramesh Masavarapu1e8c7242012-09-04 11:52:57 -07001788 if (!qsee_perf_client)
Ramesh Masavarapufb1f01e2012-06-14 09:40:40 -07001789 pr_err("Unable to register bus client\n");
Mona Hossain2892b6b2012-02-17 13:53:11 -08001790 }
1791 return 0;
Ramesh Masavarapua1bc0e42012-03-05 07:42:48 -08001792
Mona Hossain2892b6b2012-02-17 13:53:11 -08001793err:
1794 device_destroy(driver_class, qseecom_device_no);
1795class_destroy:
1796 class_destroy(driver_class);
1797unregister_chrdev_region:
1798 unregister_chrdev_region(qseecom_device_no, 1);
1799 return rc;
1800}
1801
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001802static int __devinit qseecom_remove(struct platform_device *pdev)
1803{
1804 if (pdev->dev.platform_data != NULL)
1805 msm_bus_scale_unregister_client(qsee_perf_client);
1806 return 0;
1807};
1808
Ramesh Masavarapufb1f01e2012-06-14 09:40:40 -07001809static struct of_device_id qseecom_match[] = {
1810 {
1811 .compatible = "qcom,qseecom",
1812 },
1813 {}
1814};
1815
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001816static struct platform_driver qseecom_plat_driver = {
1817 .probe = qseecom_probe,
1818 .remove = qseecom_remove,
1819 .driver = {
1820 .name = "qseecom",
1821 .owner = THIS_MODULE,
Ramesh Masavarapufb1f01e2012-06-14 09:40:40 -07001822 .of_match_table = qseecom_match,
Ramesh Masavarapua26cce72012-04-09 12:32:25 -07001823 },
1824};
1825
1826static int __devinit qseecom_init(void)
1827{
1828 return platform_driver_register(&qseecom_plat_driver);
1829}
1830
1831static void __devexit qseecom_exit(void)
Mona Hossain2892b6b2012-02-17 13:53:11 -08001832{
Mona Hossain2892b6b2012-02-17 13:53:11 -08001833 device_destroy(driver_class, qseecom_device_no);
1834 class_destroy(driver_class);
1835 unregister_chrdev_region(qseecom_device_no, 1);
1836 ion_client_destroy(qseecom.ion_clnt);
1837}
1838
1839MODULE_LICENSE("GPL v2");
1840MODULE_DESCRIPTION("Qualcomm Secure Execution Environment Communicator");
1841
1842module_init(qseecom_init);
1843module_exit(qseecom_exit);