blob: 6026fdaa59976bfd0a99789fa6381b99052cc512 [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,
Mona Hossain5ab9d772012-04-11 21:00:40 -070060 QSEOS_LOAD_EXTERNAL_ELF_COMMAND,
61 QSEOS_UNLOAD_EXTERNAL_ELF_COMMAND,
Mona Hossain2892b6b2012-02-17 13:53:11 -080062 QSEOS_CMD_MAX = 0xEFFFFFFF
63};
64
65enum qseecom_qceos_cmd_status {
66 QSEOS_RESULT_SUCCESS = 0,
67 QSEOS_RESULT_INCOMPLETE,
68 QSEOS_RESULT_FAILURE = 0xFFFFFFFF
69};
70
71__packed struct qseecom_check_app_ireq {
72 uint32_t qsee_cmd_id;
73 char app_name[MAX_APP_NAME_SIZE];
74};
75
76__packed struct qseecom_load_app_ireq {
77 uint32_t qsee_cmd_id;
78 uint32_t mdt_len; /* Length of the mdt file */
79 uint32_t img_len; /* Length of .bxx and .mdt files */
80 uint32_t phy_addr; /* phy addr of the start of image */
81 char app_name[MAX_APP_NAME_SIZE]; /* application name*/
82};
83
84__packed struct qseecom_unload_app_ireq {
85 uint32_t qsee_cmd_id;
86 uint32_t app_id;
87};
88
89__packed struct qseecom_register_listener_ireq {
90 uint32_t qsee_cmd_id;
91 uint32_t listener_id;
92 void *sb_ptr;
93 uint32_t sb_len;
94};
95
96__packed struct qseecom_unregister_listener_ireq {
97 uint32_t qsee_cmd_id;
98 uint32_t listener_id;
99};
100
101__packed struct qseecom_client_send_data_ireq {
102 uint32_t qsee_cmd_id;
103 uint32_t app_id;
104 void *req_ptr;
105 uint32_t req_len;
106 void *rsp_ptr; /* First 4 bytes should always be the return status */
107 uint32_t rsp_len;
108};
109
110/* send_data resp */
111__packed struct qseecom_client_listener_data_irsp {
112 uint32_t qsee_cmd_id;
113 uint32_t listener_id;
114};
115
116/*
117 * struct qseecom_command_scm_resp - qseecom response buffer
118 * @cmd_status: value from enum tz_sched_cmd_status
119 * @sb_in_rsp_addr: points to physical location of response
120 * buffer
121 * @sb_in_rsp_len: length of command response
122 */
123__packed struct qseecom_command_scm_resp {
124 uint32_t result;
125 enum qseecom_command_scm_resp_type resp_type;
126 unsigned int data;
127};
128
129static struct class *driver_class;
130static dev_t qseecom_device_no;
131static struct cdev qseecom_cdev;
132
133/* Data structures used in legacy support */
134static void *pil;
135static uint32_t pil_ref_cnt;
136static DEFINE_MUTEX(pil_access_lock);
137
138static DEFINE_MUTEX(send_msg_lock);
139static DEFINE_MUTEX(qsee_bw_mutex);
140static DEFINE_MUTEX(app_access_lock);
141
142static int qsee_bw_count;
143static struct clk *qseecom_bus_clk;
144static uint32_t qsee_perf_client;
145
146struct qseecom_registered_listener_list {
147 struct list_head list;
148 struct qseecom_register_listener_req svc;
149 u8 *sb_reg_req;
150 u8 *sb_virt;
151 s32 sb_phys;
152 size_t sb_length;
153 struct ion_handle *ihandle; /* Retrieve phy addr */
154
155 wait_queue_head_t rcv_req_wq;
156 int rcv_req_flag;
157};
158
159struct qseecom_registered_app_list {
160 struct list_head list;
161 u32 app_id;
162 u32 ref_cnt;
163};
164
165struct qseecom_control {
166 struct ion_client *ion_clnt; /* Ion client */
167 struct list_head registered_listener_list_head;
168 spinlock_t registered_listener_list_lock;
169
170 struct list_head registered_app_list_head;
171 spinlock_t registered_app_list_lock;
172
173 wait_queue_head_t send_resp_wq;
174 int send_resp_flag;
175
176 uint32_t qseos_version;
177};
178
179struct qseecom_client_handle {
180 u32 app_id;
181 u8 *sb_virt;
182 s32 sb_phys;
183 uint32_t user_virt_sb_base;
184 size_t sb_length;
185 struct ion_handle *ihandle; /* Retrieve phy addr */
186};
187
188struct qseecom_listener_handle {
189 u32 id;
190};
191
192static struct qseecom_control qseecom;
193
194struct qseecom_dev_handle {
195 bool service;
196 union {
197 struct qseecom_client_handle client;
198 struct qseecom_listener_handle listener;
199 };
200 bool released;
201 int abort;
202 wait_queue_head_t abort_wq;
203 atomic_t ioctl_count;
204};
205
206static int __qseecom_is_svc_unique(struct qseecom_dev_handle *data,
Mona Hossain0af10ab2012-02-28 18:26:41 -0800207 struct qseecom_register_listener_req *svc)
Mona Hossain2892b6b2012-02-17 13:53:11 -0800208{
209 struct qseecom_registered_listener_list *ptr;
210 int unique = 1;
211 unsigned long flags;
212
213 spin_lock_irqsave(&qseecom.registered_listener_list_lock, flags);
214 list_for_each_entry(ptr, &qseecom.registered_listener_list_head, list) {
Mona Hossain0af10ab2012-02-28 18:26:41 -0800215 if (ptr->svc.listener_id == svc->listener_id) {
Mona Hossain2892b6b2012-02-17 13:53:11 -0800216 pr_err("Service id: %u is already registered\n",
217 ptr->svc.listener_id);
218 unique = 0;
219 break;
220 }
221 }
222 spin_unlock_irqrestore(&qseecom.registered_listener_list_lock, flags);
223 return unique;
224}
225
226static struct qseecom_registered_listener_list *__qseecom_find_svc(
227 int32_t listener_id)
228{
229 struct qseecom_registered_listener_list *entry = NULL;
230 unsigned long flags;
231
232 spin_lock_irqsave(&qseecom.registered_listener_list_lock, flags);
233 list_for_each_entry(entry, &qseecom.registered_listener_list_head, list)
234 {
235 if (entry->svc.listener_id == listener_id)
236 break;
237 }
238 spin_unlock_irqrestore(&qseecom.registered_listener_list_lock, flags);
239 return entry;
240}
241
242static int __qseecom_set_sb_memory(struct qseecom_registered_listener_list *svc,
243 struct qseecom_dev_handle *handle,
244 struct qseecom_register_listener_req *listener)
245{
246 int ret = 0;
247 unsigned int flags = 0;
248 struct qseecom_register_listener_ireq req;
249 struct qseecom_command_scm_resp resp;
250 ion_phys_addr_t pa;
251
252 /* Get the handle of the shared fd */
253 svc->ihandle = ion_import_fd(qseecom.ion_clnt, listener->ifd_data_fd);
254 if (svc->ihandle == NULL) {
255 pr_err("Ion client could not retrieve the handle\n");
256 return -ENOMEM;
257 }
258
259 /* Get the physical address of the ION BUF */
260 ret = ion_phys(qseecom.ion_clnt, svc->ihandle, &pa, &svc->sb_length);
261
262 /* Populate the structure for sending scm call to load image */
263 svc->sb_virt = (char *) ion_map_kernel(qseecom.ion_clnt,
264 svc->ihandle, flags);
265 svc->sb_phys = pa;
266
267 if (qseecom.qseos_version == QSEOS_VERSION_14) {
268 req.qsee_cmd_id = QSEOS_REGISTER_LISTENER;
269 req.listener_id = svc->svc.listener_id;
270 req.sb_len = svc->sb_length;
271 req.sb_ptr = (void *)svc->sb_phys;
272
273 resp.result = QSEOS_RESULT_INCOMPLETE;
274
275 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &req,
276 sizeof(req), &resp, sizeof(resp));
277 if (ret) {
278 pr_err("qseecom_scm_call failed with err: %d\n", ret);
279 return -EINVAL;
280 }
281
282 if (resp.result != QSEOS_RESULT_SUCCESS) {
283 pr_err("Error SB registration req: resp.result = %d\n",
284 resp.result);
285 return -EPERM;
286 }
287 } else {
288 struct qseecom_command cmd;
289 struct qseecom_response resp;
290 struct qse_pr_init_sb_req_s sb_init_req;
291 struct qse_pr_init_sb_rsp_s sb_init_rsp;
292
293 svc->sb_reg_req = kzalloc((sizeof(sb_init_req) +
294 sizeof(sb_init_rsp)), GFP_KERNEL);
295
296 sb_init_req.pr_cmd = TZ_SCHED_CMD_ID_REGISTER_LISTENER;
297 sb_init_req.listener_id = svc->svc.listener_id;
298 sb_init_req.sb_len = svc->sb_length;
299 sb_init_req.sb_ptr = svc->sb_phys;
300
301 memcpy(svc->sb_reg_req, &sb_init_req, sizeof(sb_init_req));
302
303 /* It will always be a new cmd from this method */
304 cmd.cmd_type = TZ_SCHED_CMD_NEW;
305 cmd.sb_in_cmd_addr = (u8 *)(virt_to_phys(svc->sb_reg_req));
306 cmd.sb_in_cmd_len = sizeof(sb_init_req);
307
308 resp.cmd_status = TZ_SCHED_STATUS_INCOMPLETE;
309
310 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &cmd, sizeof(cmd)
311 , &resp, sizeof(resp));
312
313 if (ret) {
314 pr_err("qseecom_scm_call failed with err: %d\n", ret);
315 return -EINVAL;
316 }
317
318 if (resp.cmd_status != TZ_SCHED_STATUS_COMPLETE) {
319 pr_err("SB registration fail resp.cmd_status %d\n",
320 resp.cmd_status);
321 return -EINVAL;
322 }
323 memset(svc->sb_virt, 0, svc->sb_length);
324 }
325 return 0;
326}
327
328static int qseecom_register_listener(struct qseecom_dev_handle *data,
329 void __user *argp)
330{
331 int ret = 0;
332 unsigned long flags;
333 struct qseecom_register_listener_req rcvd_lstnr;
334 struct qseecom_registered_listener_list *new_entry;
335
336 ret = copy_from_user(&rcvd_lstnr, argp, sizeof(rcvd_lstnr));
337 if (ret) {
338 pr_err("copy_from_user failed\n");
339 return ret;
340 }
Mona Hossain0af10ab2012-02-28 18:26:41 -0800341 data->listener.id = 0;
342 data->service = true;
343 if (!__qseecom_is_svc_unique(data, &rcvd_lstnr)) {
Mona Hossain2892b6b2012-02-17 13:53:11 -0800344 pr_err("Service is not unique and is already registered\n");
Mona Hossain0af10ab2012-02-28 18:26:41 -0800345 data->released = true;
346 return -EBUSY;
Mona Hossain2892b6b2012-02-17 13:53:11 -0800347 }
348
349 new_entry = kmalloc(sizeof(*new_entry), GFP_KERNEL);
350 if (!new_entry) {
351 pr_err("kmalloc failed\n");
352 return -ENOMEM;
353 }
354 memcpy(&new_entry->svc, &rcvd_lstnr, sizeof(rcvd_lstnr));
355 new_entry->rcv_req_flag = 0;
356
357 new_entry->svc.listener_id = rcvd_lstnr.listener_id;
358 new_entry->sb_length = rcvd_lstnr.sb_size;
359 if (__qseecom_set_sb_memory(new_entry, data, &rcvd_lstnr)) {
360 pr_err("qseecom_set_sb_memoryfailed\n");
361 kzfree(new_entry);
362 return -ENOMEM;
363 }
Mona Hossain0af10ab2012-02-28 18:26:41 -0800364
Mona Hossain2892b6b2012-02-17 13:53:11 -0800365 data->listener.id = rcvd_lstnr.listener_id;
Mona Hossain2892b6b2012-02-17 13:53:11 -0800366 init_waitqueue_head(&new_entry->rcv_req_wq);
367
368 spin_lock_irqsave(&qseecom.registered_listener_list_lock, flags);
369 list_add_tail(&new_entry->list, &qseecom.registered_listener_list_head);
370 spin_unlock_irqrestore(&qseecom.registered_listener_list_lock, flags);
Mona Hossain0af10ab2012-02-28 18:26:41 -0800371
Mona Hossain2892b6b2012-02-17 13:53:11 -0800372 return ret;
373}
374
375static int qseecom_unregister_listener(struct qseecom_dev_handle *data)
376{
377 int ret = 0;
378 unsigned long flags;
379 uint32_t unmap_mem = 0;
380 struct qseecom_register_listener_ireq req;
381 struct qseecom_registered_listener_list *ptr_svc = NULL;
382 struct qseecom_command_scm_resp resp;
383 struct ion_handle *ihandle = NULL; /* Retrieve phy addr */
384
385 if (qseecom.qseos_version == QSEOS_VERSION_14) {
386 req.qsee_cmd_id = QSEOS_DEREGISTER_LISTENER;
387 req.listener_id = data->listener.id;
388 resp.result = QSEOS_RESULT_INCOMPLETE;
389
390 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &req,
391 sizeof(req), &resp, sizeof(resp));
392 if (ret) {
393 pr_err("qseecom_scm_call failed with err: %d\n", ret);
394 return ret;
395 }
396
397 if (resp.result != QSEOS_RESULT_SUCCESS) {
398 pr_err("SB deregistartion: result=%d\n", resp.result);
399 return -EPERM;
400 }
401 } else {
402 struct qse_pr_init_sb_req_s sb_init_req;
403 struct qseecom_command cmd;
404 struct qseecom_response resp;
405 struct qseecom_registered_listener_list *svc;
406
407 svc = __qseecom_find_svc(data->listener.id);
408 sb_init_req.pr_cmd = TZ_SCHED_CMD_ID_REGISTER_LISTENER;
409 sb_init_req.listener_id = data->listener.id;
410 sb_init_req.sb_len = 0;
411 sb_init_req.sb_ptr = 0;
412
413 memcpy(svc->sb_reg_req, &sb_init_req, sizeof(sb_init_req));
414
415 /* It will always be a new cmd from this method */
416 cmd.cmd_type = TZ_SCHED_CMD_NEW;
417 cmd.sb_in_cmd_addr = (u8 *)(virt_to_phys(svc->sb_reg_req));
418 cmd.sb_in_cmd_len = sizeof(sb_init_req);
419 resp.cmd_status = TZ_SCHED_STATUS_INCOMPLETE;
420
421 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &cmd, sizeof(cmd),
422 &resp, sizeof(resp));
423 if (ret) {
424 pr_err("qseecom_scm_call failed with err: %d\n", ret);
425 return ret;
426 }
427 kzfree(svc->sb_reg_req);
428 if (resp.cmd_status != TZ_SCHED_STATUS_COMPLETE) {
429 pr_err("Error with SB initialization\n");
430 return -EPERM;
431 }
432 }
433 data->abort = 1;
434 spin_lock_irqsave(&qseecom.registered_listener_list_lock, flags);
435 list_for_each_entry(ptr_svc, &qseecom.registered_listener_list_head,
436 list) {
437 if (ptr_svc->svc.listener_id == data->listener.id) {
438 wake_up_all(&ptr_svc->rcv_req_wq);
439 break;
440 }
441 }
442 spin_unlock_irqrestore(&qseecom.registered_listener_list_lock, flags);
443
444 while (atomic_read(&data->ioctl_count) > 1) {
Mona Hossainacea1022012-04-09 13:37:27 -0700445 if (wait_event_freezable(data->abort_wq,
Mona Hossain2892b6b2012-02-17 13:53:11 -0800446 atomic_read(&data->ioctl_count) <= 1)) {
447 pr_err("Interrupted from abort\n");
448 ret = -ERESTARTSYS;
449 break;
450 }
451 }
452
453 spin_lock_irqsave(&qseecom.registered_listener_list_lock, flags);
454 list_for_each_entry(ptr_svc,
455 &qseecom.registered_listener_list_head,
456 list)
457 {
458 if (ptr_svc->svc.listener_id == data->listener.id) {
459 if (ptr_svc->sb_virt) {
460 unmap_mem = 1;
461 ihandle = ptr_svc->ihandle;
462 }
463 list_del(&ptr_svc->list);
464 kzfree(ptr_svc);
465 break;
466 }
467 }
468 spin_unlock_irqrestore(&qseecom.registered_listener_list_lock, flags);
469
470 /* Unmap the memory */
471 if (unmap_mem) {
472 if (!IS_ERR_OR_NULL(ihandle)) {
473 ion_unmap_kernel(qseecom.ion_clnt, ihandle);
474 ion_free(qseecom.ion_clnt, ihandle);
475 }
476 }
477 data->released = true;
478 return ret;
479}
480
481static int qseecom_set_client_mem_param(struct qseecom_dev_handle *data,
482 void __user *argp)
483{
484 ion_phys_addr_t pa;
485 int32_t ret;
486 unsigned int flags = 0;
487 struct qseecom_set_sb_mem_param_req req;
488 uint32_t len;
489
490 /* Copy the relevant information needed for loading the image */
491 if (__copy_from_user(&req, (void __user *)argp, sizeof(req)))
492 return -EFAULT;
493
Mona Hossain2892b6b2012-02-17 13:53:11 -0800494 /* Get the handle of the shared fd */
495 data->client.ihandle = ion_import_fd(qseecom.ion_clnt, req.ifd_data_fd);
496 if (IS_ERR_OR_NULL(data->client.ihandle)) {
497 pr_err("Ion client could not retrieve the handle\n");
498 return -ENOMEM;
499 }
500 /* Get the physical address of the ION BUF */
501 ret = ion_phys(qseecom.ion_clnt, data->client.ihandle, &pa, &len);
502 /* Populate the structure for sending scm call to load image */
503 data->client.sb_virt = (char *) ion_map_kernel(qseecom.ion_clnt,
504 data->client.ihandle,
505 flags);
506 data->client.sb_phys = pa;
507 data->client.sb_length = req.sb_len;
508 data->client.user_virt_sb_base = req.virt_sb_base;
509 return 0;
510}
511
512
513static int __qseecom_listener_has_sent_rsp(struct qseecom_dev_handle *data)
514{
515 int ret;
516 ret = (qseecom.send_resp_flag != 0);
517 return ret || data->abort;
518}
519
520static int __qseecom_process_incomplete_cmd(struct qseecom_dev_handle *data,
521 struct qseecom_command_scm_resp *resp)
522{
523 int ret = 0;
524 uint32_t lstnr;
525 unsigned long flags;
526 struct qseecom_client_listener_data_irsp send_data_rsp;
527 struct qseecom_registered_listener_list *ptr_svc = NULL;
528
529
530 while (resp->result == QSEOS_RESULT_INCOMPLETE) {
531 lstnr = resp->data;
532 /*
533 * Wake up blocking lsitener service with the lstnr id
534 */
535 spin_lock_irqsave(&qseecom.registered_listener_list_lock,
536 flags);
537 list_for_each_entry(ptr_svc,
538 &qseecom.registered_listener_list_head, list) {
539 if (ptr_svc->svc.listener_id == lstnr) {
540 ptr_svc->rcv_req_flag = 1;
541 wake_up_interruptible(&ptr_svc->rcv_req_wq);
542 break;
543 }
544 }
545 spin_unlock_irqrestore(&qseecom.registered_listener_list_lock,
546 flags);
547 if (ptr_svc->svc.listener_id != lstnr) {
548 pr_warning("Service requested for does on exist\n");
549 return -ERESTARTSYS;
550 }
551 pr_debug("waking up rcv_req_wq and "
552 "waiting for send_resp_wq\n");
Mona Hossainacea1022012-04-09 13:37:27 -0700553 if (wait_event_freezable(qseecom.send_resp_wq,
Mona Hossain2892b6b2012-02-17 13:53:11 -0800554 __qseecom_listener_has_sent_rsp(data))) {
555 pr_warning("Interrupted: exiting send_cmd loop\n");
556 return -ERESTARTSYS;
557 }
558
559 if (data->abort) {
Mona Hossaina5f1aab2012-03-29 10:18:07 -0700560 pr_err("Aborting listener service %d\n",
561 data->listener.id);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800562 return -ENODEV;
563 }
564 qseecom.send_resp_flag = 0;
565 send_data_rsp.qsee_cmd_id = QSEOS_LISTENER_DATA_RSP_COMMAND;
566 send_data_rsp.listener_id = lstnr ;
567
568 ret = scm_call(SCM_SVC_TZSCHEDULER, 1,
569 (const void *)&send_data_rsp,
570 sizeof(send_data_rsp), resp,
571 sizeof(*resp));
572 if (ret) {
573 pr_err("qseecom_scm_call failed with err: %d\n", ret);
574 return ret;
575 }
Mona Hossainbb0bca12012-04-12 11:47:45 -0700576 if (resp->result == QSEOS_RESULT_FAILURE) {
577 pr_err("Response result %d not supported\n",
578 resp->result);
579 return -EINVAL;
580 }
Mona Hossain2892b6b2012-02-17 13:53:11 -0800581 }
582 return ret;
583}
584
585static int qseecom_load_app(struct qseecom_dev_handle *data, void __user *argp)
586{
587 struct qseecom_registered_app_list *entry = NULL;
588 unsigned long flags = 0;
589 u32 app_id = 0;
590 struct ion_handle *ihandle; /* Ion handle */
591 struct qseecom_load_img_req load_img_req;
592 int32_t ret;
593 ion_phys_addr_t pa = 0;
594 uint32_t len;
595 struct qseecom_command_scm_resp resp;
596 struct qseecom_check_app_ireq req;
597 /* Copy the relevant information needed for loading the image */
598 if (__copy_from_user(&load_img_req,
599 (void __user *)argp,
600 sizeof(struct qseecom_load_img_req))) {
601 pr_err("copy_from_user failed\n");
602 return -EFAULT;
603 }
604
605 req.qsee_cmd_id = QSEOS_APP_LOOKUP_COMMAND;
606 memcpy(req.app_name, load_img_req.img_name, MAX_APP_NAME_SIZE);
607
608 /* SCM_CALL to check if app_id for the mentioned app exists */
609 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &req,
610 sizeof(struct qseecom_check_app_ireq),
611 &resp, sizeof(resp));
Mona Hossainbb0bca12012-04-12 11:47:45 -0700612 if (ret) {
613 pr_err("scm_call to check if app is already loaded failed\n");
614 return -EINVAL;
615 }
Mona Hossain2892b6b2012-02-17 13:53:11 -0800616
617 if (resp.result == QSEOS_RESULT_FAILURE)
618 app_id = 0;
619 else
620 app_id = resp.data;
621
622 if (app_id) {
Mona Hossaina5f1aab2012-03-29 10:18:07 -0700623 pr_warn("App id %d (%s) already exists\n", app_id,
624 (char *)(req.app_name));
Mona Hossain2892b6b2012-02-17 13:53:11 -0800625 spin_lock_irqsave(&qseecom.registered_app_list_lock, flags);
626 list_for_each_entry(entry,
627 &qseecom.registered_app_list_head, list){
628 if (entry->app_id == app_id) {
629 entry->ref_cnt++;
630 break;
631 }
632 }
633 spin_unlock_irqrestore(
634 &qseecom.registered_app_list_lock, flags);
635 } else {
636 struct qseecom_load_app_ireq load_req;
637
Mona Hossaina5f1aab2012-03-29 10:18:07 -0700638 pr_warn("App (%s) does not exist, loading apps for first time\n",
Mona Hossainbb0bca12012-04-12 11:47:45 -0700639 (char *)(req.app_name));
Mona Hossain2892b6b2012-02-17 13:53:11 -0800640 /* Get the handle of the shared fd */
641 ihandle = ion_import_fd(qseecom.ion_clnt,
642 load_img_req.ifd_data_fd);
643 if (IS_ERR_OR_NULL(ihandle)) {
644 pr_err("Ion client could not retrieve the handle\n");
645 return -ENOMEM;
646 }
647
648 /* Get the physical address of the ION BUF */
649 ret = ion_phys(qseecom.ion_clnt, ihandle, &pa, &len);
650
651 /* Populate the structure for sending scm call to load image */
652 load_req.qsee_cmd_id = QSEOS_APP_START_COMMAND;
653 load_req.mdt_len = load_img_req.mdt_len;
654 load_req.img_len = load_img_req.img_len;
655 load_req.phy_addr = pa;
656
657 /* SCM_CALL to load the app and get the app_id back */
658 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &load_req,
659 sizeof(struct qseecom_load_app_ireq),
660 &resp, sizeof(resp));
Mona Hossainbb0bca12012-04-12 11:47:45 -0700661 if (ret) {
662 pr_err("scm_call to load app failed\n");
663 return -EINVAL;
664 }
Mona Hossain2892b6b2012-02-17 13:53:11 -0800665
Mona Hossaina5f1aab2012-03-29 10:18:07 -0700666 if (resp.result == QSEOS_RESULT_FAILURE) {
Mona Hossainbb0bca12012-04-12 11:47:45 -0700667 pr_err("scm_call rsp.result is QSEOS_RESULT_FAILURE\n");
Mona Hossaina5f1aab2012-03-29 10:18:07 -0700668 if (!IS_ERR_OR_NULL(ihandle))
669 ion_free(qseecom.ion_clnt, ihandle);
670 return -EFAULT;
671 }
672
Mona Hossain2892b6b2012-02-17 13:53:11 -0800673 if (resp.result == QSEOS_RESULT_INCOMPLETE) {
674 ret = __qseecom_process_incomplete_cmd(data, &resp);
675 if (ret) {
676 pr_err("process_incomplete_cmd failed err: %d\n",
677 ret);
678 if (!IS_ERR_OR_NULL(ihandle))
679 ion_free(qseecom.ion_clnt, ihandle);
680 return ret;
681 }
682 }
683 if (resp.result != QSEOS_RESULT_SUCCESS) {
Mona Hossaina5f1aab2012-03-29 10:18:07 -0700684 pr_err("scm_call failed resp.result unknown, %d\n",
685 resp.result);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800686 if (!IS_ERR_OR_NULL(ihandle))
687 ion_free(qseecom.ion_clnt, ihandle);
688 return -EFAULT;
689 }
690
691 app_id = resp.data;
692
693 entry = kmalloc(sizeof(*entry), GFP_KERNEL);
694 if (!entry) {
695 pr_err("kmalloc failed\n");
696 return -ENOMEM;
697 }
698 entry->app_id = app_id;
699 entry->ref_cnt = 1;
700
701 /* Deallocate the handle */
702 if (!IS_ERR_OR_NULL(ihandle))
703 ion_free(qseecom.ion_clnt, ihandle);
704
705 spin_lock_irqsave(&qseecom.registered_app_list_lock, flags);
706 list_add_tail(&entry->list, &qseecom.registered_app_list_head);
707 spin_unlock_irqrestore(&qseecom.registered_app_list_lock,
708 flags);
Mona Hossaina5f1aab2012-03-29 10:18:07 -0700709
Mona Hossainbb0bca12012-04-12 11:47:45 -0700710 pr_warn("App with id %d (%s) now loaded\n", app_id,
711 (char *)(req.app_name));
Mona Hossain2892b6b2012-02-17 13:53:11 -0800712 }
713 data->client.app_id = app_id;
714 load_img_req.app_id = app_id;
715 if (copy_to_user(argp, &load_img_req, sizeof(load_img_req))) {
716 pr_err("copy_to_user failed\n");
717 kzfree(entry);
718 return -EFAULT;
719 }
720 return 0;
721}
722
723static int __qseecom_cleanup_app(struct qseecom_dev_handle *data)
724{
725 wake_up_all(&qseecom.send_resp_wq);
726 while (atomic_read(&data->ioctl_count) > 1) {
Mona Hossainacea1022012-04-09 13:37:27 -0700727 if (wait_event_freezable(data->abort_wq,
Mona Hossain2892b6b2012-02-17 13:53:11 -0800728 atomic_read(&data->ioctl_count) <= 1)) {
729 pr_err("Interrupted from abort\n");
730 return -ERESTARTSYS;
731 break;
732 }
733 }
734 /* Set unload app */
735 return 1;
736}
737
738static int qseecom_unload_app(struct qseecom_dev_handle *data)
739{
740 unsigned long flags;
741 int ret = 0;
742 struct qseecom_command_scm_resp resp;
743 struct qseecom_registered_app_list *ptr_app;
744 uint32_t unload = 0;
745
746 if (qseecom.qseos_version == QSEOS_VERSION_14) {
747 spin_lock_irqsave(&qseecom.registered_app_list_lock, flags);
748 list_for_each_entry(ptr_app, &qseecom.registered_app_list_head,
749 list) {
750 if (ptr_app->app_id == data->client.app_id) {
751 if (ptr_app->ref_cnt == 1) {
752 unload = __qseecom_cleanup_app(data);
753 list_del(&ptr_app->list);
754 kzfree(ptr_app);
755 break;
756 } else {
757 ptr_app->ref_cnt--;
Mona Hossain0af10ab2012-02-28 18:26:41 -0800758 data->released = true;
Mona Hossaina5f1aab2012-03-29 10:18:07 -0700759 pr_warn("Can't unload app with id %d (it is inuse)\n",
760 ptr_app->app_id);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800761 break;
762 }
763 }
764 }
765 spin_unlock_irqrestore(&qseecom.registered_app_list_lock,
766 flags);
767 }
768 if (!IS_ERR_OR_NULL(data->client.ihandle)) {
769 ion_unmap_kernel(qseecom.ion_clnt, data->client.ihandle);
770 ion_free(qseecom.ion_clnt, data->client.ihandle);
771 }
772
773 if ((unload) && (qseecom.qseos_version == QSEOS_VERSION_14)) {
774 struct qseecom_unload_app_ireq req;
775
776 /* Populate the structure for sending scm call to load image */
777 req.qsee_cmd_id = QSEOS_APP_SHUTDOWN_COMMAND;
778 req.app_id = data->client.app_id;
779
780 /* SCM_CALL to unload the app */
781 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &req,
782 sizeof(struct qseecom_unload_app_ireq),
783 &resp, sizeof(resp));
784 if (ret) {
Mona Hossainbb0bca12012-04-12 11:47:45 -0700785 pr_err("scm_call to unload app (id = %d) failed\n",
786 req.app_id);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800787 return -EFAULT;
Mona Hossainbb0bca12012-04-12 11:47:45 -0700788 } else {
789 pr_warn("App id %d now unloaded\n", req.app_id);
Mona Hossain2892b6b2012-02-17 13:53:11 -0800790 }
791 if (resp.result == QSEOS_RESULT_INCOMPLETE) {
792 ret = __qseecom_process_incomplete_cmd(data, &resp);
793 if (ret) {
794 pr_err("process_incomplete_cmd fail err: %d\n",
795 ret);
796 return ret;
797 }
798 }
799 }
800
801 if (qseecom.qseos_version == QSEOS_VERSION_13) {
802 data->abort = 1;
803 wake_up_all(&qseecom.send_resp_wq);
804 while (atomic_read(&data->ioctl_count) > 0) {
Mona Hossainacea1022012-04-09 13:37:27 -0700805 if (wait_event_freezable(data->abort_wq,
Mona Hossain2892b6b2012-02-17 13:53:11 -0800806 atomic_read(&data->ioctl_count) <= 0)) {
807 pr_err("Interrupted from abort\n");
808 ret = -ERESTARTSYS;
809 break;
810 }
811 }
Mona Hossain2892b6b2012-02-17 13:53:11 -0800812 }
813 data->released = true;
814 return ret;
815}
816
817static uint32_t __qseecom_uvirt_to_kphys(struct qseecom_dev_handle *data,
818 uint32_t virt)
819{
820 return data->client.sb_phys + (virt - data->client.user_virt_sb_base);
821}
822
823static int __qseecom_send_cmd_legacy(struct qseecom_dev_handle *data,
824 struct qseecom_send_cmd_req *req)
825{
826 int ret = 0;
827 unsigned long flags;
828 u32 reqd_len_sb_in = 0;
829 struct qseecom_command cmd;
830 struct qseecom_response resp;
831
832
833 if (req->cmd_req_buf == NULL || req->resp_buf == NULL) {
834 pr_err("cmd buffer or response buffer is null\n");
835 return -EINVAL;
836 }
837
838 if (req->cmd_req_len <= 0 ||
839 req->resp_len <= 0 ||
840 req->cmd_req_len > data->client.sb_length ||
841 req->resp_len > data->client.sb_length) {
842 pr_err("cmd buffer length or "
843 "response buffer length not valid\n");
844 return -EINVAL;
845 }
846
847 reqd_len_sb_in = req->cmd_req_len + req->resp_len;
848 if (reqd_len_sb_in > data->client.sb_length) {
849 pr_debug("Not enough memory to fit cmd_buf and "
850 "resp_buf. Required: %u, Available: %u\n",
851 reqd_len_sb_in, data->client.sb_length);
852 return -ENOMEM;
853 }
854 cmd.cmd_type = TZ_SCHED_CMD_NEW;
855 cmd.sb_in_cmd_addr = (u8 *) data->client.sb_phys;
856 cmd.sb_in_cmd_len = req->cmd_req_len;
857
858 resp.cmd_status = TZ_SCHED_STATUS_INCOMPLETE;
859 resp.sb_in_rsp_addr = (u8 *)data->client.sb_phys + req->cmd_req_len;
860 resp.sb_in_rsp_len = req->resp_len;
861
862 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, (const void *)&cmd,
863 sizeof(cmd), &resp, sizeof(resp));
864
865 if (ret) {
866 pr_err("qseecom_scm_call_legacy failed with err: %d\n", ret);
867 return ret;
868 }
869
870 while (resp.cmd_status != TZ_SCHED_STATUS_COMPLETE) {
871 /*
872 * If cmd is incomplete, get the callback cmd out from SB out
873 * and put it on the list
874 */
875 struct qseecom_registered_listener_list *ptr_svc = NULL;
876 /*
877 * We don't know which service can handle the command. so we
878 * wake up all blocking services and let them figure out if
879 * they can handle the given command.
880 */
881 spin_lock_irqsave(&qseecom.registered_listener_list_lock,
882 flags);
883 list_for_each_entry(ptr_svc,
884 &qseecom.registered_listener_list_head, list) {
885 ptr_svc->rcv_req_flag = 1;
886 wake_up_interruptible(&ptr_svc->rcv_req_wq);
887 }
888 spin_unlock_irqrestore(&qseecom.registered_listener_list_lock,
889 flags);
890
891 pr_debug("waking up rcv_req_wq and "
892 "waiting for send_resp_wq\n");
Mona Hossainacea1022012-04-09 13:37:27 -0700893 if (wait_event_freezable(qseecom.send_resp_wq,
Mona Hossain2892b6b2012-02-17 13:53:11 -0800894 __qseecom_listener_has_sent_rsp(data))) {
895 pr_warning("qseecom Interrupted: exiting send_cmd loop\n");
896 return -ERESTARTSYS;
897 }
898
899 if (data->abort) {
900 pr_err("Aborting driver\n");
901 return -ENODEV;
902 }
903 qseecom.send_resp_flag = 0;
904 cmd.cmd_type = TZ_SCHED_CMD_PENDING;
905 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, (const void *)&cmd,
906 sizeof(cmd), &resp, sizeof(resp));
907 if (ret) {
908 pr_err("qseecom_scm_call failed with err: %d\n", ret);
909 return ret;
910 }
911 }
912 return ret;
913}
914
915static int __qseecom_send_cmd(struct qseecom_dev_handle *data,
916 struct qseecom_send_cmd_req *req)
917{
918 int ret = 0;
919 u32 reqd_len_sb_in = 0;
920 struct qseecom_client_send_data_ireq send_data_req;
921 struct qseecom_command_scm_resp resp;
922
923 if (req->cmd_req_buf == NULL || req->resp_buf == NULL) {
924 pr_err("cmd buffer or response buffer is null\n");
925 return -EINVAL;
926 }
927
928 if (req->cmd_req_len <= 0 ||
929 req->resp_len <= 0 ||
930 req->cmd_req_len > data->client.sb_length ||
931 req->resp_len > data->client.sb_length) {
932 pr_err("cmd buffer length or "
933 "response buffer length not valid\n");
934 return -EINVAL;
935 }
936
937 reqd_len_sb_in = req->cmd_req_len + req->resp_len;
938 if (reqd_len_sb_in > data->client.sb_length) {
939 pr_debug("Not enough memory to fit cmd_buf and "
940 "resp_buf. Required: %u, Available: %u\n",
941 reqd_len_sb_in, data->client.sb_length);
942 return -ENOMEM;
943 }
944
945 send_data_req.qsee_cmd_id = QSEOS_CLIENT_SEND_DATA_COMMAND;
946 send_data_req.app_id = data->client.app_id;
947 send_data_req.req_ptr = (void *)(__qseecom_uvirt_to_kphys(data,
948 (uint32_t)req->cmd_req_buf));
949 send_data_req.req_len = req->cmd_req_len;
950 send_data_req.rsp_ptr = (void *)(__qseecom_uvirt_to_kphys(data,
951 (uint32_t)req->resp_buf));
952 send_data_req.rsp_len = req->resp_len;
953
954 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, (const void *) &send_data_req,
955 sizeof(send_data_req),
956 &resp, sizeof(resp));
957 if (ret) {
958 pr_err("qseecom_scm_call failed with err: %d\n", ret);
959 return ret;
960 }
961
962 if (resp.result == QSEOS_RESULT_INCOMPLETE) {
963 ret = __qseecom_process_incomplete_cmd(data, &resp);
964 if (ret) {
965 pr_err("process_incomplete_cmd failed err: %d\n", ret);
966 return ret;
967 }
Mona Hossainbb0bca12012-04-12 11:47:45 -0700968 } else {
969 if (resp.result != QSEOS_RESULT_SUCCESS) {
970 pr_err("Response result %d not supported\n",
971 resp.result);
972 ret = -EINVAL;
973 }
Mona Hossain2892b6b2012-02-17 13:53:11 -0800974 }
975 return ret;
976}
977
978
979static int qseecom_send_cmd(struct qseecom_dev_handle *data, void __user *argp)
980{
981 int ret = 0;
982 struct qseecom_send_cmd_req req;
983
984 ret = copy_from_user(&req, argp, sizeof(req));
985 if (ret) {
986 pr_err("copy_from_user failed\n");
987 return ret;
988 }
989 if (qseecom.qseos_version == QSEOS_VERSION_14)
990 ret = __qseecom_send_cmd(data, &req);
991 else
992 ret = __qseecom_send_cmd_legacy(data, &req);
993 if (ret)
994 return ret;
995
996 pr_debug("sending cmd_req->rsp size: %u, ptr: 0x%p\n",
997 req.resp_len, req.resp_buf);
998 return ret;
999}
1000
1001static int __qseecom_send_cmd_req_clean_up(
1002 struct qseecom_send_modfd_cmd_req *req)
1003{
1004 char *field;
1005 uint32_t *update;
1006 int ret = 0;
1007 int i = 0;
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 field = (char *)req->cmd_req_buf +
1012 req->ifd_data[i].cmd_buf_offset;
1013 update = (uint32_t *) field;
1014 *update = 0;
1015 }
1016 }
1017 return ret;
1018}
1019
1020static int __qseecom_update_with_phy_addr(
1021 struct qseecom_send_modfd_cmd_req *req)
1022{
1023 struct ion_handle *ihandle;
1024 char *field;
1025 uint32_t *update;
1026 ion_phys_addr_t pa;
1027 int ret = 0;
1028 int i = 0;
1029 uint32_t length;
1030
1031 for (i = 0; i < MAX_ION_FD; i++) {
Mona Hossaina5f1aab2012-03-29 10:18:07 -07001032 if (req->ifd_data[i].fd > 0) {
Mona Hossain2892b6b2012-02-17 13:53:11 -08001033 /* Get the handle of the shared fd */
1034 ihandle = ion_import_fd(qseecom.ion_clnt,
1035 req->ifd_data[i].fd);
1036 if (IS_ERR_OR_NULL(ihandle)) {
1037 pr_err("Ion client can't retrieve the handle\n");
1038 return -ENOMEM;
1039 }
1040 field = (char *) req->cmd_req_buf +
1041 req->ifd_data[i].cmd_buf_offset;
1042 update = (uint32_t *) field;
1043
1044 /* Populate the cmd data structure with the phys_addr */
1045 ret = ion_phys(qseecom.ion_clnt, ihandle, &pa, &length);
1046 if (ret)
1047 return -ENOMEM;
1048
1049 *update = (uint32_t)pa;
1050 /* Deallocate the handle */
1051 if (!IS_ERR_OR_NULL(ihandle))
1052 ion_free(qseecom.ion_clnt, ihandle);
1053 }
1054 }
1055 return ret;
1056}
1057
1058static int qseecom_send_modfd_cmd(struct qseecom_dev_handle *data,
1059 void __user *argp)
1060{
1061 int ret = 0;
1062 struct qseecom_send_modfd_cmd_req req;
1063 struct qseecom_send_cmd_req send_cmd_req;
1064
1065 ret = copy_from_user(&req, argp, sizeof(req));
1066 if (ret) {
1067 pr_err("copy_from_user failed\n");
1068 return ret;
1069 }
1070 send_cmd_req.cmd_req_buf = req.cmd_req_buf;
1071 send_cmd_req.cmd_req_len = req.cmd_req_len;
1072 send_cmd_req.resp_buf = req.resp_buf;
1073 send_cmd_req.resp_len = req.resp_len;
1074
1075 ret = __qseecom_update_with_phy_addr(&req);
1076 if (ret)
1077 return ret;
1078 if (qseecom.qseos_version == QSEOS_VERSION_14)
1079 ret = __qseecom_send_cmd(data, &send_cmd_req);
1080 else
1081 ret = __qseecom_send_cmd_legacy(data, &send_cmd_req);
1082 __qseecom_send_cmd_req_clean_up(&req);
1083
1084 if (ret)
1085 return ret;
1086
1087 pr_debug("sending cmd_req->rsp size: %u, ptr: 0x%p\n",
1088 req.resp_len, req.resp_buf);
1089 return ret;
1090}
1091
1092static int __qseecom_listener_has_rcvd_req(struct qseecom_dev_handle *data,
1093 struct qseecom_registered_listener_list *svc)
1094{
1095 int ret;
1096 ret = (svc->rcv_req_flag != 0);
1097 return ret || data->abort;
1098}
1099
1100static int qseecom_receive_req(struct qseecom_dev_handle *data)
1101{
1102 int ret = 0;
1103 struct qseecom_registered_listener_list *this_lstnr;
1104
1105 this_lstnr = __qseecom_find_svc(data->listener.id);
1106 while (1) {
Mona Hossainacea1022012-04-09 13:37:27 -07001107 if (wait_event_freezable(this_lstnr->rcv_req_wq,
Mona Hossain2892b6b2012-02-17 13:53:11 -08001108 __qseecom_listener_has_rcvd_req(data,
1109 this_lstnr))) {
1110 pr_warning("Interrupted: exiting wait_rcv_req loop\n");
1111 /* woken up for different reason */
1112 return -ERESTARTSYS;
1113 }
1114
1115 if (data->abort) {
1116 pr_err("Aborting driver!\n");
1117 return -ENODEV;
1118 }
1119 this_lstnr->rcv_req_flag = 0;
1120 if (qseecom.qseos_version == QSEOS_VERSION_13) {
1121 if (*((uint32_t *)this_lstnr->sb_virt) != 0)
1122 break;
1123 } else {
1124 break;
1125 }
1126 }
1127 return ret;
1128}
1129
1130static int qseecom_send_resp(void)
1131{
1132 qseecom.send_resp_flag = 1;
1133 wake_up_interruptible(&qseecom.send_resp_wq);
1134 return 0;
1135}
1136
1137static int qseecom_get_qseos_version(struct qseecom_dev_handle *data,
1138 void __user *argp)
1139{
1140 struct qseecom_qseos_version_req req;
1141
1142 if (copy_from_user(&req, argp, sizeof(req))) {
1143 pr_err("copy_from_user failed");
1144 return -EINVAL;
1145 }
1146 req.qseos_version = qseecom.qseos_version;
1147 if (copy_to_user(argp, &req, sizeof(req))) {
1148 pr_err("copy_to_user failed");
1149 return -EINVAL;
1150 }
1151 return 0;
1152}
1153
1154static int qsee_vote_for_clock(void)
1155{
1156 int ret = 0;
1157
1158 if (!qsee_perf_client)
Ramesh Masavarapue640e842012-04-03 11:21:54 -07001159 return ret;
Mona Hossain2892b6b2012-02-17 13:53:11 -08001160
Ramesh Masavarapua1bc0e42012-03-05 07:42:48 -08001161 /* Check if the clk is valid */
Ramesh Masavarapue640e842012-04-03 11:21:54 -07001162 if (IS_ERR_OR_NULL(qseecom_bus_clk)) {
1163 pr_warn("qseecom bus clock is null or error");
Ramesh Masavarapua1bc0e42012-03-05 07:42:48 -08001164 return -EINVAL;
Ramesh Masavarapue640e842012-04-03 11:21:54 -07001165 }
Ramesh Masavarapua1bc0e42012-03-05 07:42:48 -08001166
Mona Hossain2892b6b2012-02-17 13:53:11 -08001167 mutex_lock(&qsee_bw_mutex);
1168 if (!qsee_bw_count) {
1169 ret = msm_bus_scale_client_update_request(
1170 qsee_perf_client, 1);
1171 if (ret) {
1172 pr_err("Bandwidth request failed (%d)\n", ret);
1173 } else {
1174 ret = clk_enable(qseecom_bus_clk);
1175 if (ret)
1176 pr_err("Clock enable failed\n");
1177 }
1178 }
1179 if (ret)
1180 msm_bus_scale_client_update_request(qsee_perf_client, 0);
1181 else
1182 qsee_bw_count++;
1183
1184 mutex_unlock(&qsee_bw_mutex);
1185 return ret;
1186}
1187
1188static void qsee_disable_clock_vote(void)
1189{
Ramesh Masavarapua1bc0e42012-03-05 07:42:48 -08001190
Mona Hossain2892b6b2012-02-17 13:53:11 -08001191 if (!qsee_perf_client)
Ramesh Masavarapua1bc0e42012-03-05 07:42:48 -08001192 return;
1193
1194 /* Check if the clk is valid */
Ramesh Masavarapue640e842012-04-03 11:21:54 -07001195 if (IS_ERR_OR_NULL(qseecom_bus_clk)) {
1196 pr_warn("qseecom bus clock is null or error");
Ramesh Masavarapua1bc0e42012-03-05 07:42:48 -08001197 return;
Ramesh Masavarapue640e842012-04-03 11:21:54 -07001198 }
Mona Hossain2892b6b2012-02-17 13:53:11 -08001199
1200 mutex_lock(&qsee_bw_mutex);
1201 if (qsee_bw_count > 0) {
1202 if (qsee_bw_count-- == 1) {
1203 msm_bus_scale_client_update_request(qsee_perf_client,
1204 0);
1205 clk_disable(qseecom_bus_clk);
1206 }
1207 }
1208 mutex_unlock(&qsee_bw_mutex);
1209}
1210
Mona Hossain5ab9d772012-04-11 21:00:40 -07001211static int qseecom_load_external_elf(struct qseecom_dev_handle *data,
1212 void __user *argp)
1213{
1214 struct ion_handle *ihandle; /* Ion handle */
1215 struct qseecom_load_img_req load_img_req;
1216 int32_t ret;
1217 ion_phys_addr_t pa = 0;
1218 uint32_t len;
1219 struct qseecom_load_app_ireq load_req;
1220 struct qseecom_command_scm_resp resp;
1221
1222 /* Copy the relevant information needed for loading the image */
1223 if (__copy_from_user(&load_img_req,
1224 (void __user *)argp,
1225 sizeof(struct qseecom_load_img_req))) {
1226 pr_err("copy_from_user failed\n");
1227 return -EFAULT;
1228 }
1229
1230 /* Get the handle of the shared fd */
1231 ihandle = ion_import_fd(qseecom.ion_clnt,
1232 load_img_req.ifd_data_fd);
1233 if (IS_ERR_OR_NULL(ihandle)) {
1234 pr_err("Ion client could not retrieve the handle\n");
1235 return -ENOMEM;
1236 }
1237
1238 /* Get the physical address of the ION BUF */
1239 ret = ion_phys(qseecom.ion_clnt, ihandle, &pa, &len);
1240
1241 /* Populate the structure for sending scm call to load image */
1242 load_req.qsee_cmd_id = QSEOS_LOAD_EXTERNAL_ELF_COMMAND;
1243 load_req.mdt_len = load_img_req.mdt_len;
1244 load_req.img_len = load_img_req.img_len;
1245 load_req.phy_addr = pa;
1246
1247 /* SCM_CALL to load the external elf */
1248 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &load_req,
1249 sizeof(struct qseecom_load_app_ireq),
1250 &resp, sizeof(resp));
1251 if (ret) {
1252 pr_err("scm_call to unload failed : ret %d\n",
1253 ret);
1254 ret = -EFAULT;
1255 }
1256
1257 if (resp.result == QSEOS_RESULT_INCOMPLETE) {
1258 ret = __qseecom_process_incomplete_cmd(data, &resp);
1259 if (ret)
1260 pr_err("process_incomplete_cmd failed err: %d\n",
1261 ret);
1262 } else {
1263 if (resp.result != QSEOS_RESULT_SUCCESS) {
1264 pr_err("scm_call to load image failed resp.result =%d\n",
1265 resp.result);
1266 ret = -EFAULT;
1267 }
1268 }
1269 /* Deallocate the handle */
1270 if (!IS_ERR_OR_NULL(ihandle))
1271 ion_free(qseecom.ion_clnt, ihandle);
1272
1273 return ret;
1274}
1275
1276static int qseecom_unload_external_elf(struct qseecom_dev_handle *data)
1277{
1278 int ret = 0;
1279 struct qseecom_command_scm_resp resp;
1280 struct qseecom_unload_app_ireq req;
1281
1282 /* Populate the structure for sending scm call to unload image */
1283 req.qsee_cmd_id = QSEOS_UNLOAD_EXTERNAL_ELF_COMMAND;
1284 /* SCM_CALL to unload the external elf */
1285 ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &req,
1286 sizeof(struct qseecom_unload_app_ireq),
1287 &resp, sizeof(resp));
1288 if (ret) {
1289 pr_err("scm_call to unload failed : ret %d\n",
1290 ret);
1291 return -EFAULT;
1292 }
1293 if (resp.result == QSEOS_RESULT_INCOMPLETE) {
1294 ret = __qseecom_process_incomplete_cmd(data, &resp);
1295 if (ret)
1296 pr_err("process_incomplete_cmd fail err: %d\n",
1297 ret);
1298 } else {
1299 if (resp.result != QSEOS_RESULT_SUCCESS) {
1300 pr_err("scm_call to unload image failed resp.result =%d\n",
1301 resp.result);
1302 ret = -EFAULT;
1303 }
1304 }
1305 return ret;
1306}
Mona Hossain2892b6b2012-02-17 13:53:11 -08001307
1308static long qseecom_ioctl(struct file *file, unsigned cmd,
1309 unsigned long arg)
1310{
1311 int ret = 0;
1312 struct qseecom_dev_handle *data = file->private_data;
1313 void __user *argp = (void __user *) arg;
1314
1315 if (data->abort) {
1316 pr_err("Aborting qseecom driver\n");
1317 return -ENODEV;
1318 }
1319
1320 switch (cmd) {
1321 case QSEECOM_IOCTL_REGISTER_LISTENER_REQ: {
1322 pr_debug("ioctl register_listener_req()\n");
1323 atomic_inc(&data->ioctl_count);
1324 ret = qseecom_register_listener(data, argp);
1325 atomic_dec(&data->ioctl_count);
1326 wake_up_all(&data->abort_wq);
1327 if (ret)
1328 pr_err("failed qseecom_register_listener: %d\n", ret);
1329 break;
1330 }
1331 case QSEECOM_IOCTL_UNREGISTER_LISTENER_REQ: {
1332 pr_debug("ioctl unregister_listener_req()\n");
1333 atomic_inc(&data->ioctl_count);
1334 ret = qseecom_unregister_listener(data);
1335 atomic_dec(&data->ioctl_count);
1336 wake_up_all(&data->abort_wq);
1337 if (ret)
1338 pr_err("failed qseecom_unregister_listener: %d\n", ret);
1339 break;
1340 }
1341 case QSEECOM_IOCTL_SEND_CMD_REQ: {
1342 /* Only one client allowed here at a time */
1343 mutex_lock(&send_msg_lock);
1344 atomic_inc(&data->ioctl_count);
1345 ret = qseecom_send_cmd(data, argp);
1346 atomic_dec(&data->ioctl_count);
1347 wake_up_all(&data->abort_wq);
1348 mutex_unlock(&send_msg_lock);
1349 if (ret)
1350 pr_err("failed qseecom_send_cmd: %d\n", ret);
1351 break;
1352 }
1353 case QSEECOM_IOCTL_SEND_MODFD_CMD_REQ: {
1354 /* Only one client allowed here at a time */
1355 mutex_lock(&send_msg_lock);
1356 atomic_inc(&data->ioctl_count);
1357 ret = qseecom_send_modfd_cmd(data, argp);
1358 atomic_dec(&data->ioctl_count);
1359 wake_up_all(&data->abort_wq);
1360 mutex_unlock(&send_msg_lock);
1361 if (ret)
1362 pr_err("failed qseecom_send_cmd: %d\n", ret);
1363 break;
1364 }
1365 case QSEECOM_IOCTL_RECEIVE_REQ: {
1366 atomic_inc(&data->ioctl_count);
1367 ret = qseecom_receive_req(data);
1368 atomic_dec(&data->ioctl_count);
1369 wake_up_all(&data->abort_wq);
1370 if (ret)
1371 pr_err("failed qseecom_receive_req: %d\n", ret);
1372 break;
1373 }
1374 case QSEECOM_IOCTL_SEND_RESP_REQ: {
1375 atomic_inc(&data->ioctl_count);
1376 ret = qseecom_send_resp();
1377 atomic_dec(&data->ioctl_count);
1378 wake_up_all(&data->abort_wq);
1379 if (ret)
1380 pr_err("failed qseecom_send_resp: %d\n", ret);
1381 break;
1382 }
1383 case QSEECOM_IOCTL_SET_MEM_PARAM_REQ: {
1384 ret = qseecom_set_client_mem_param(data, argp);
1385 if (ret)
1386 pr_err("failed Qqseecom_set_mem_param request: %d\n",
1387 ret);
1388 break;
1389 }
1390 case QSEECOM_IOCTL_LOAD_APP_REQ: {
1391 mutex_lock(&app_access_lock);
1392 atomic_inc(&data->ioctl_count);
1393 ret = qseecom_load_app(data, argp);
1394 atomic_dec(&data->ioctl_count);
1395 mutex_unlock(&app_access_lock);
1396 if (ret)
1397 pr_err("failed load_app request: %d\n", ret);
1398 break;
1399 }
1400 case QSEECOM_IOCTL_UNLOAD_APP_REQ: {
1401 mutex_lock(&app_access_lock);
1402 atomic_inc(&data->ioctl_count);
1403 ret = qseecom_unload_app(data);
1404 atomic_dec(&data->ioctl_count);
1405 mutex_unlock(&app_access_lock);
1406 if (ret)
1407 pr_err("failed unload_app request: %d\n", ret);
1408 break;
1409 }
1410 case QSEECOM_IOCTL_GET_QSEOS_VERSION_REQ: {
1411 atomic_inc(&data->ioctl_count);
1412 ret = qseecom_get_qseos_version(data, argp);
1413 if (ret)
1414 pr_err("qseecom_get_qseos_version: %d\n", ret);
1415 atomic_dec(&data->ioctl_count);
1416 break;
1417 }
1418 case QSEECOM_IOCTL_PERF_ENABLE_REQ:{
1419 atomic_inc(&data->ioctl_count);
1420 ret = qsee_vote_for_clock();
1421 if (ret)
1422 pr_err("Failed to vote for clock%d\n", ret);
1423 atomic_dec(&data->ioctl_count);
1424 break;
1425 }
1426 case QSEECOM_IOCTL_PERF_DISABLE_REQ:{
1427 atomic_inc(&data->ioctl_count);
1428 qsee_disable_clock_vote();
1429 atomic_dec(&data->ioctl_count);
1430 break;
1431 }
Mona Hossain5ab9d772012-04-11 21:00:40 -07001432 case QSEECOM_IOCTL_LOAD_EXTERNAL_ELF_REQ: {
1433 data->released = true;
1434 if (qseecom.qseos_version == QSEOS_VERSION_13) {
1435 pr_err("Loading External elf image unsupported in rev 0x13\n");
1436 ret = -EINVAL;
1437 break;
1438 }
1439 mutex_lock(&app_access_lock);
1440 atomic_inc(&data->ioctl_count);
1441 ret = qseecom_load_external_elf(data, argp);
1442 atomic_dec(&data->ioctl_count);
1443 mutex_unlock(&app_access_lock);
1444 if (ret)
1445 pr_err("failed load_external_elf request: %d\n", ret);
1446 break;
1447 }
1448 case QSEECOM_IOCTL_UNLOAD_EXTERNAL_ELF_REQ: {
1449 data->released = true;
1450 if (qseecom.qseos_version == QSEOS_VERSION_13) {
1451 pr_err("Unloading External elf image unsupported in rev 0x13\n");
1452 ret = -EINVAL;
1453 break;
1454 }
1455 mutex_lock(&app_access_lock);
1456 atomic_inc(&data->ioctl_count);
1457 ret = qseecom_unload_external_elf(data);
1458 atomic_dec(&data->ioctl_count);
1459 mutex_unlock(&app_access_lock);
1460 if (ret)
1461 pr_err("failed unload_app request: %d\n", ret);
1462 break;
1463 }
Mona Hossain2892b6b2012-02-17 13:53:11 -08001464 default:
1465 return -EINVAL;
1466 }
1467 return ret;
1468}
1469
1470static int qseecom_open(struct inode *inode, struct file *file)
1471{
1472 int ret = 0;
1473 struct qseecom_dev_handle *data;
1474
1475 data = kzalloc(sizeof(*data), GFP_KERNEL);
1476 if (!data) {
1477 pr_err("kmalloc failed\n");
1478 return -ENOMEM;
1479 }
1480 file->private_data = data;
1481 data->abort = 0;
1482 data->service = false;
1483 data->released = false;
1484 init_waitqueue_head(&data->abort_wq);
1485 atomic_set(&data->ioctl_count, 0);
Mona Hossain7cdbfaf2012-03-15 18:56:10 -07001486 if (qseecom.qseos_version == QSEOS_VERSION_13) {
1487 int pil_error;
1488 mutex_lock(&pil_access_lock);
1489 if (pil_ref_cnt == 0) {
1490 pil = pil_get("tzapps");
1491 if (IS_ERR(pil)) {
1492 pr_err("Playready PIL image load failed\n");
1493 pil_error = PTR_ERR(pil);
1494 pil = NULL;
1495 pr_debug("tzapps image load FAILED\n");
1496 mutex_unlock(&pil_access_lock);
1497 return pil_error;
1498 }
1499 }
1500 pil_ref_cnt++;
1501 mutex_unlock(&pil_access_lock);
1502 }
Mona Hossain2892b6b2012-02-17 13:53:11 -08001503 return ret;
1504}
1505
1506static int qseecom_release(struct inode *inode, struct file *file)
1507{
1508 struct qseecom_dev_handle *data = file->private_data;
1509 int ret = 0;
1510
1511 if (data->released == false) {
1512 pr_warn("data->released == false\n");
1513 if (data->service)
1514 ret = qseecom_unregister_listener(data);
1515 else
1516 ret = qseecom_unload_app(data);
1517 if (ret) {
1518 pr_err("Close failed\n");
1519 return ret;
1520 }
1521 }
Mona Hossain7cdbfaf2012-03-15 18:56:10 -07001522 if (qseecom.qseos_version == QSEOS_VERSION_13) {
1523 mutex_lock(&pil_access_lock);
1524 if (pil_ref_cnt == 1)
1525 pil_put(pil);
1526 pil_ref_cnt--;
1527 mutex_unlock(&pil_access_lock);
1528 }
Mona Hossain2892b6b2012-02-17 13:53:11 -08001529 kfree(data);
Ramesh Masavarapua1bc0e42012-03-05 07:42:48 -08001530 qsee_disable_clock_vote();
1531
Mona Hossain2892b6b2012-02-17 13:53:11 -08001532 return ret;
1533}
1534
1535/* qseecom bus scaling */
1536static struct msm_bus_paths qsee_bw_table[] = {
1537 {
1538 .vectors = (struct msm_bus_vectors[]){
1539 {
1540 .src = MSM_BUS_MASTER_SPS,
1541 .dst = MSM_BUS_SLAVE_EBI_CH0,
1542 },
1543 },
1544 .num_paths = 1,
1545 },
1546 {
1547 .vectors = (struct msm_bus_vectors[]){
1548 {
1549 .src = MSM_BUS_MASTER_SPS,
1550 .dst = MSM_BUS_SLAVE_EBI_CH0,
1551 .ib = (492 * 8) * 1000000UL,
1552 .ab = (492 * 8) * 100000UL,
1553 },
1554 },
1555 .num_paths = 1,
1556 },
1557};
1558
1559static struct msm_bus_scale_pdata qsee_bus_pdata = {
1560 .usecase = qsee_bw_table,
1561 .num_usecases = ARRAY_SIZE(qsee_bw_table),
1562 .name = "qsee",
1563};
1564
1565static const struct file_operations qseecom_fops = {
1566 .owner = THIS_MODULE,
1567 .unlocked_ioctl = qseecom_ioctl,
1568 .open = qseecom_open,
1569 .release = qseecom_release
1570};
1571
1572static int __init qseecom_init(void)
1573{
1574 int rc;
Mona Hossain2892b6b2012-02-17 13:53:11 -08001575 struct device *class_dev;
1576 char qsee_not_legacy = 0;
1577 uint32_t system_call_id = QSEOS_CHECK_VERSION_CMD;
1578
Ramesh Masavarapue640e842012-04-03 11:21:54 -07001579 qsee_bw_count = 0;
1580 qseecom_bus_clk = NULL;
1581 qsee_perf_client = 0;
1582
Mona Hossain2892b6b2012-02-17 13:53:11 -08001583 rc = alloc_chrdev_region(&qseecom_device_no, 0, 1, QSEECOM_DEV);
1584 if (rc < 0) {
1585 pr_err("alloc_chrdev_region failed %d\n", rc);
1586 return rc;
1587 }
1588
1589 driver_class = class_create(THIS_MODULE, QSEECOM_DEV);
1590 if (IS_ERR(driver_class)) {
1591 rc = -ENOMEM;
1592 pr_err("class_create failed %d\n", rc);
1593 goto unregister_chrdev_region;
1594 }
1595
1596 class_dev = device_create(driver_class, NULL, qseecom_device_no, NULL,
1597 QSEECOM_DEV);
1598 if (!class_dev) {
1599 pr_err("class_device_create failed %d\n", rc);
1600 rc = -ENOMEM;
1601 goto class_destroy;
1602 }
1603
1604 cdev_init(&qseecom_cdev, &qseecom_fops);
1605 qseecom_cdev.owner = THIS_MODULE;
1606
1607 rc = cdev_add(&qseecom_cdev, MKDEV(MAJOR(qseecom_device_no), 0), 1);
1608 if (rc < 0) {
1609 pr_err("cdev_add failed %d\n", rc);
1610 goto err;
1611 }
1612
1613 INIT_LIST_HEAD(&qseecom.registered_listener_list_head);
1614 spin_lock_init(&qseecom.registered_listener_list_lock);
1615 INIT_LIST_HEAD(&qseecom.registered_app_list_head);
1616 spin_lock_init(&qseecom.registered_app_list_lock);
1617 init_waitqueue_head(&qseecom.send_resp_wq);
1618 qseecom.send_resp_flag = 0;
1619
1620 rc = scm_call(6, 1, &system_call_id, sizeof(system_call_id),
1621 &qsee_not_legacy, sizeof(qsee_not_legacy));
1622 if (rc) {
1623 pr_err("Failed to retrieve QSEE version information %d\n", rc);
1624 goto err;
1625 }
1626 if (qsee_not_legacy)
1627 qseecom.qseos_version = QSEOS_VERSION_14;
1628 else {
1629 qseecom.qseos_version = QSEOS_VERSION_13;
1630 pil = NULL;
1631 pil_ref_cnt = 0;
1632 }
1633 /* Create ION msm client */
1634 qseecom.ion_clnt = msm_ion_client_create(0x03, "qseecom-kernel");
1635 if (qseecom.ion_clnt == NULL) {
1636 pr_err("Ion client cannot be created\n");
1637 rc = -ENOMEM;
1638 goto err;
1639 }
1640
1641 /* register client for bus scaling */
Ramesh Masavarapua1bc0e42012-03-05 07:42:48 -08001642 qsee_perf_client = msm_bus_scale_register_client(
1643 &qsee_bus_pdata);
1644 if (!qsee_perf_client) {
Mona Hossain2892b6b2012-02-17 13:53:11 -08001645 pr_err("Unable to register bus client\n");
Ramesh Masavarapue640e842012-04-03 11:21:54 -07001646 } else {
1647 qseecom_bus_clk = clk_get(class_dev, "bus_clk");
Ramesh Masavarapua1bc0e42012-03-05 07:42:48 -08001648 if (IS_ERR(qseecom_bus_clk)) {
Mona Hossain2892b6b2012-02-17 13:53:11 -08001649 qseecom_bus_clk = NULL;
Ramesh Masavarapua1bc0e42012-03-05 07:42:48 -08001650 } else if (qseecom_bus_clk != NULL) {
1651 pr_debug("Enabled DFAB clock");
1652 clk_set_rate(qseecom_bus_clk, 64000000);
Mona Hossain2892b6b2012-02-17 13:53:11 -08001653 }
Mona Hossain2892b6b2012-02-17 13:53:11 -08001654 }
1655 return 0;
Ramesh Masavarapua1bc0e42012-03-05 07:42:48 -08001656
Mona Hossain2892b6b2012-02-17 13:53:11 -08001657err:
1658 device_destroy(driver_class, qseecom_device_no);
1659class_destroy:
1660 class_destroy(driver_class);
1661unregister_chrdev_region:
1662 unregister_chrdev_region(qseecom_device_no, 1);
1663 return rc;
1664}
1665
1666static void __exit qseecom_exit(void)
1667{
Ramesh Masavarapua1bc0e42012-03-05 07:42:48 -08001668 clk_put(qseecom_bus_clk);
1669
Mona Hossain2892b6b2012-02-17 13:53:11 -08001670 device_destroy(driver_class, qseecom_device_no);
1671 class_destroy(driver_class);
1672 unregister_chrdev_region(qseecom_device_no, 1);
1673 ion_client_destroy(qseecom.ion_clnt);
1674}
1675
1676MODULE_LICENSE("GPL v2");
1677MODULE_DESCRIPTION("Qualcomm Secure Execution Environment Communicator");
1678
1679module_init(qseecom_init);
1680module_exit(qseecom_exit);