blob: e662f4308a2febe71dd53c34c40fee71480b8c96 [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/* Qualcomm TrustZone communicator driver
2 *
Mona Hossain6e2e2692012-01-05 11:28:04 -08003 * Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07004 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 and
7 * only version 2 as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 */
14
15#define KMSG_COMPONENT "TZCOM"
16#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
17
18#include <linux/kernel.h>
19#include <linux/slab.h>
20#include <linux/module.h>
21#include <linux/fs.h>
22#include <linux/platform_device.h>
23#include <linux/debugfs.h>
24#include <linux/cdev.h>
25#include <linux/uaccess.h>
26#include <linux/sched.h>
27#include <linux/list.h>
28#include <linux/mutex.h>
29#include <linux/android_pmem.h>
30#include <linux/io.h>
Mona Hossain6e2e2692012-01-05 11:28:04 -080031#include <linux/ion.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070032#include <mach/scm.h>
33#include <mach/peripheral-loader.h>
34#include <linux/tzcom.h>
35#include "tzcomi.h"
36
37#define TZCOM_DEV "tzcom"
38
39#define TZSCHEDULER_CMD_ID 1 /* CMD id of the trustzone scheduler */
40
41#undef PDEBUG
42#define PDEBUG(fmt, args...) pr_debug("%s(%i, %s): " fmt "\n", \
43 __func__, current->pid, current->comm, ## args)
44
45#undef PERR
46#define PERR(fmt, args...) pr_err("%s(%i, %s): " fmt "\n", \
47 __func__, current->pid, current->comm, ## args)
48
Sachin Shahf0b51cd2011-08-11 11:54:57 -070049#undef PWARN
50#define PWARN(fmt, args...) pr_warning("%s(%i, %s): " fmt "\n", \
51 __func__, current->pid, current->comm, ## args)
52
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070053
54static struct class *driver_class;
55static dev_t tzcom_device_no;
56static struct cdev tzcom_cdev;
Mona Hossain6e2e2692012-01-05 11:28:04 -080057struct ion_client *ion_clnt;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070058static u8 *sb_in_virt;
59static s32 sb_in_phys;
60static size_t sb_in_length = 20 * SZ_1K;
61static u8 *sb_out_virt;
62static s32 sb_out_phys;
63static size_t sb_out_length = 20 * SZ_1K;
64
65static void *pil;
66
67static atomic_t svc_instance_ctr = ATOMIC_INIT(0);
68static DEFINE_MUTEX(sb_in_lock);
69static DEFINE_MUTEX(sb_out_lock);
70static DEFINE_MUTEX(send_cmd_lock);
71
72struct tzcom_callback_list {
73 struct list_head list;
74 struct tzcom_callback callback;
75};
76
77struct tzcom_registered_svc_list {
78 struct list_head list;
79 struct tzcom_register_svc_op_req svc;
80 wait_queue_head_t next_cmd_wq;
81 int next_cmd_flag;
82};
83
84struct tzcom_data_t {
85 struct list_head callback_list_head;
86 struct mutex callback_list_lock;
87 struct list_head registered_svc_list_head;
88 spinlock_t registered_svc_list_lock;
89 wait_queue_head_t cont_cmd_wq;
90 int cont_cmd_flag;
91 u32 handled_cmd_svc_instance_id;
Sachin Shahd7e02d42011-09-12 20:31:02 -070092 int abort;
93 wait_queue_head_t abort_wq;
94 atomic_t ioctl_count;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070095};
96
97static int tzcom_scm_call(const void *cmd_buf, size_t cmd_len,
98 void *resp_buf, size_t resp_len)
99{
100 return scm_call(SCM_SVC_TZSCHEDULER, TZSCHEDULER_CMD_ID,
101 cmd_buf, cmd_len, resp_buf, resp_len);
102}
103
104static s32 tzcom_virt_to_phys(u8 *virt)
105{
106 if (virt >= sb_in_virt &&
107 virt < (sb_in_virt + sb_in_length)) {
108 return sb_in_phys + (virt - sb_in_virt);
109 } else if (virt >= sb_out_virt &&
110 virt < (sb_out_virt + sb_out_length)) {
111 return sb_out_phys + (virt - sb_out_virt);
112 } else {
113 return virt_to_phys(virt);
114 }
115}
116
117static u8 *tzcom_phys_to_virt(s32 phys)
118{
119 if (phys >= sb_in_phys &&
120 phys < (sb_in_phys + sb_in_length)) {
121 return sb_in_virt + (phys - sb_in_phys);
122 } else if (phys >= sb_out_phys &&
123 phys < (sb_out_phys + sb_out_length)) {
124 return sb_out_virt + (phys - sb_out_phys);
125 } else {
126 return phys_to_virt(phys);
127 }
128}
129
130static int __tzcom_is_svc_unique(struct tzcom_data_t *data,
131 struct tzcom_register_svc_op_req svc)
132{
133 struct tzcom_registered_svc_list *ptr;
134 int unique = 1;
135 unsigned long flags;
136
137 spin_lock_irqsave(&data->registered_svc_list_lock, flags);
138 list_for_each_entry(ptr, &data->registered_svc_list_head, list) {
139 if (ptr->svc.svc_id == svc.svc_id) {
140 PERR("Service id: %u is already registered",
141 ptr->svc.svc_id);
142 unique = 0;
143 break;
144 } else if (svc.cmd_id_low >= ptr->svc.cmd_id_low &&
145 svc.cmd_id_low <= ptr->svc.cmd_id_high) {
146 PERR("Cmd id low falls in the range of another"
147 "registered service");
148 unique = 0;
149 break;
150 } else if (svc.cmd_id_high >= ptr->svc.cmd_id_low &&
151 svc.cmd_id_high <= ptr->svc.cmd_id_high) {
152 PERR("Cmd id high falls in the range of another"
153 "registered service");
154 unique = 0;
155 break;
156 }
157 }
158 spin_unlock_irqrestore(&data->registered_svc_list_lock, flags);
159 return unique;
160}
161
162static int tzcom_register_service(struct tzcom_data_t *data, void __user *argp)
163{
164 int ret;
165 unsigned long flags;
166 struct tzcom_register_svc_op_req rcvd_svc;
167 struct tzcom_registered_svc_list *new_entry;
168
169 ret = copy_from_user(&rcvd_svc, argp, sizeof(rcvd_svc));
170
171 if (ret) {
Sachin Shahf0b51cd2011-08-11 11:54:57 -0700172 PERR("copy_from_user failed");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700173 return ret;
174 }
175
176 PDEBUG("svc_id: %u, cmd_id_low: %u, cmd_id_high: %u",
177 rcvd_svc.svc_id, rcvd_svc.cmd_id_low,
178 rcvd_svc.cmd_id_high);
179 if (!__tzcom_is_svc_unique(data, rcvd_svc)) {
Sachin Shahf0b51cd2011-08-11 11:54:57 -0700180 PERR("Provided service is not unique");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700181 return -EINVAL;
182 }
183
184 rcvd_svc.instance_id = atomic_inc_return(&svc_instance_ctr);
185
186 ret = copy_to_user(argp, &rcvd_svc, sizeof(rcvd_svc));
187 if (ret) {
Sachin Shahf0b51cd2011-08-11 11:54:57 -0700188 PERR("copy_to_user failed");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700189 return ret;
190 }
191
192 new_entry = kmalloc(sizeof(*new_entry), GFP_KERNEL);
193 if (!new_entry) {
Sachin Shahf0b51cd2011-08-11 11:54:57 -0700194 PERR("kmalloc failed");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700195 return -ENOMEM;
196 }
197 memcpy(&new_entry->svc, &rcvd_svc, sizeof(rcvd_svc));
198 new_entry->next_cmd_flag = 0;
199 init_waitqueue_head(&new_entry->next_cmd_wq);
200
201 spin_lock_irqsave(&data->registered_svc_list_lock, flags);
202 list_add_tail(&new_entry->list, &data->registered_svc_list_head);
203 spin_unlock_irqrestore(&data->registered_svc_list_lock, flags);
204
205
206 return ret;
207}
208
209static int tzcom_unregister_service(struct tzcom_data_t *data,
210 void __user *argp)
211{
212 int ret = 0;
213 unsigned long flags;
214 struct tzcom_unregister_svc_op_req req;
Sachin Shahbae4ec02011-08-15 19:52:31 -0700215 struct tzcom_registered_svc_list *ptr, *next;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700216 ret = copy_from_user(&req, argp, sizeof(req));
217 if (ret) {
Sachin Shahf0b51cd2011-08-11 11:54:57 -0700218 PERR("copy_from_user failed");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700219 return ret;
220 }
221
222 spin_lock_irqsave(&data->registered_svc_list_lock, flags);
Sachin Shahbae4ec02011-08-15 19:52:31 -0700223 list_for_each_entry_safe(ptr, next, &data->registered_svc_list_head,
224 list) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700225 if (req.svc_id == ptr->svc.svc_id &&
226 req.instance_id == ptr->svc.instance_id) {
227 wake_up_all(&ptr->next_cmd_wq);
228 list_del(&ptr->list);
229 kfree(ptr);
230 spin_unlock_irqrestore(&data->registered_svc_list_lock,
231 flags);
232 return 0;
233 }
234 }
235 spin_unlock_irqrestore(&data->registered_svc_list_lock, flags);
236
237 return -EINVAL;
238}
239
Sachin Shahd7e02d42011-09-12 20:31:02 -0700240static int __tzcom_is_cont_cmd(struct tzcom_data_t *data)
241{
242 int ret;
243 ret = (data->cont_cmd_flag != 0);
244 return ret || data->abort;
245}
246
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700247/**
248 * +---------+ +-----+ +-----------------+
249 * | TZCOM | | SCM | | TZCOM_SCHEDULER |
250 * +----+----+ +--+--+ +--------+--------+
251 * | | |
252 * | scm_call | |
253 * |------------------------------------->| |
254 * | cmd_buf = struct tzcom_command { | |
255 * | cmd_type, |------------------>|
256 * +------+------------- sb_in_cmd_addr, | |
257 * | | sb_in_cmd_len | |
258 * | | } | |
259 * | | resp_buf = struct tzcom_response { | |
260 * | cmd_status, | |
261 * | +---------- sb_in_rsp_addr, | |
262 * | | sb_in_rsp_len |<------------------|
263 * | | }
264 * | | struct tzcom_callback {---------+
265 * | | uint32_t cmd_id; |
266 * | | uint32_t sb_out_cb_data_len;|
267 * | +---------------+ uint32_t sb_out_cb_data_off;|
268 * | | } |
269 * | _________________________|_______________________________ |
270 * | +-----------------------+| +----------------------+ |
271 * +--->+ copy from req.cmd_buf |+>| copy to req.resp_buf | |
272 * +-----------------------+ +----------------------+ |
273 * _________________________________________________________ |
274 * INPUT SHARED BUFFER |
275 * +------------------------------------------------------------------------+
276 * | _________________________________________________________
277 * | +---------------------------------------------+
278 * +->| cmd_id | data_len | data_off | data... |
279 * +---------------------------------------------+
280 * |<------------>|copy to next_cmd.req_buf
281 * _________________________________________________________
282 * OUTPUT SHARED BUFFER
283 */
Mona Hossain6e2e2692012-01-05 11:28:04 -0800284static int __tzcom_send_cmd(struct tzcom_data_t *data,
285 struct tzcom_send_cmd_op_req *req)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700286{
287 int ret = 0;
288 unsigned long flags;
289 u32 reqd_len_sb_in = 0;
290 u32 reqd_len_sb_out = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700291 struct tzcom_command cmd;
292 struct tzcom_response resp;
293 struct tzcom_callback *next_callback;
294 void *cb_data = NULL;
295 struct tzcom_callback_list *new_entry;
296 struct tzcom_callback *cb;
297 size_t new_entry_len = 0;
298 struct tzcom_registered_svc_list *ptr_svc;
299
Mona Hossain6e2e2692012-01-05 11:28:04 -0800300 if (req->cmd_buf == NULL || req->resp_buf == NULL) {
Sachin Shahf0b51cd2011-08-11 11:54:57 -0700301 PERR("cmd buffer or response buffer is null");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700302 return -EINVAL;
303 }
304
Mona Hossain6e2e2692012-01-05 11:28:04 -0800305 if (req->cmd_len <= 0 || req->resp_len <= 0 ||
306 req->cmd_len > sb_in_length || req->resp_len > sb_in_length) {
Sachin Shahf0b51cd2011-08-11 11:54:57 -0700307 PERR("cmd buffer length or "
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700308 "response buffer length not valid");
309 return -EINVAL;
310 }
311 PDEBUG("received cmd_req.req: 0x%p",
Mona Hossain6e2e2692012-01-05 11:28:04 -0800312 req->cmd_buf);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700313 PDEBUG("received cmd_req.rsp size: %u, ptr: 0x%p",
Mona Hossain6e2e2692012-01-05 11:28:04 -0800314 req->resp_len,
315 req->resp_buf);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700316
Mona Hossain6e2e2692012-01-05 11:28:04 -0800317 reqd_len_sb_in = req->cmd_len + req->resp_len;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700318 if (reqd_len_sb_in > sb_in_length) {
319 PDEBUG("Not enough memory to fit cmd_buf and "
320 "resp_buf. Required: %u, Available: %u",
321 reqd_len_sb_in, sb_in_length);
322 return -ENOMEM;
323 }
324
Mona Hossain6e2e2692012-01-05 11:28:04 -0800325 /* Copy req->cmd_buf to SB in and set
326 * req->resp_buf to SB in + cmd_len
327 */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700328 mutex_lock(&sb_in_lock);
329 PDEBUG("Before memcpy on sb_in");
Mona Hossain6e2e2692012-01-05 11:28:04 -0800330 memcpy(sb_in_virt, req->cmd_buf, req->cmd_len);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700331 PDEBUG("After memcpy on sb_in");
332
333 /* cmd_type will always be a new here */
334 cmd.cmd_type = TZ_SCHED_CMD_NEW;
335 cmd.sb_in_cmd_addr = (u8 *) tzcom_virt_to_phys(sb_in_virt);
Mona Hossain6e2e2692012-01-05 11:28:04 -0800336 cmd.sb_in_cmd_len = req->cmd_len;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700337
338 resp.cmd_status = TZ_SCHED_STATUS_INCOMPLETE;
339 resp.sb_in_rsp_addr = (u8 *) tzcom_virt_to_phys(sb_in_virt +
Mona Hossain6e2e2692012-01-05 11:28:04 -0800340 req->cmd_len);
341 resp.sb_in_rsp_len = req->resp_len;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700342
Mona Hossain6e2e2692012-01-05 11:28:04 -0800343 PDEBUG("before call tzcom_scm_call, cmd_id = : %u", req->cmd_id);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700344 PDEBUG("before call tzcom_scm_call, sizeof(cmd) = : %u", sizeof(cmd));
345
Sachin Shahbae4ec02011-08-15 19:52:31 -0700346 ret = tzcom_scm_call((const void *) &cmd, sizeof(cmd),
347 &resp, sizeof(resp));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700348 mutex_unlock(&sb_in_lock);
349
Sachin Shahbae4ec02011-08-15 19:52:31 -0700350 if (ret) {
351 PERR("tzcom_scm_call failed with err: %d", ret);
352 return ret;
353 }
354
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700355 while (resp.cmd_status != TZ_SCHED_STATUS_COMPLETE) {
356 /*
357 * If cmd is incomplete, get the callback cmd out from SB out
358 * and put it on the list
359 */
360 PDEBUG("cmd_status is incomplete.");
361 next_callback = (struct tzcom_callback *)sb_out_virt;
362
363 mutex_lock(&sb_out_lock);
364 reqd_len_sb_out = sizeof(*next_callback)
365 + next_callback->sb_out_cb_data_len;
Sachin Shahbae4ec02011-08-15 19:52:31 -0700366 if (reqd_len_sb_out > sb_out_length ||
367 reqd_len_sb_out < sizeof(*next_callback) ||
368 next_callback->sb_out_cb_data_len > sb_out_length) {
369 PERR("Incorrect callback data length"
370 " Required: %u, Available: %u, Min: %u",
371 reqd_len_sb_out, sb_out_length,
372 sizeof(*next_callback));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700373 mutex_unlock(&sb_out_lock);
374 return -ENOMEM;
375 }
376
377 /* Assumption is cb_data_off is sizeof(tzcom_callback) */
378 new_entry_len = sizeof(*new_entry)
379 + next_callback->sb_out_cb_data_len;
380 new_entry = kmalloc(new_entry_len, GFP_KERNEL);
381 if (!new_entry) {
382 PERR("kmalloc failed");
383 mutex_unlock(&sb_out_lock);
384 return -ENOMEM;
385 }
386
387 cb = &new_entry->callback;
388 cb->cmd_id = next_callback->cmd_id;
389 cb->sb_out_cb_data_len = next_callback->sb_out_cb_data_len;
Sachin Shahbae4ec02011-08-15 19:52:31 -0700390 cb->sb_out_cb_data_off = sizeof(*cb);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700391
392 cb_data = (u8 *)next_callback
393 + next_callback->sb_out_cb_data_off;
394 memcpy((u8 *)cb + cb->sb_out_cb_data_off, cb_data,
395 next_callback->sb_out_cb_data_len);
396 mutex_unlock(&sb_out_lock);
397
398 mutex_lock(&data->callback_list_lock);
399 list_add_tail(&new_entry->list, &data->callback_list_head);
400 mutex_unlock(&data->callback_list_lock);
401
402 /*
403 * We don't know which service can handle the command. so we
404 * wake up all blocking services and let them figure out if
405 * they can handle the given command.
406 */
407 spin_lock_irqsave(&data->registered_svc_list_lock, flags);
408 list_for_each_entry(ptr_svc,
409 &data->registered_svc_list_head, list) {
410 ptr_svc->next_cmd_flag = 1;
411 wake_up_interruptible(&ptr_svc->next_cmd_wq);
412 }
413 spin_unlock_irqrestore(&data->registered_svc_list_lock,
414 flags);
415
416 PDEBUG("waking up next_cmd_wq and "
417 "waiting for cont_cmd_wq");
418 if (wait_event_interruptible(data->cont_cmd_wq,
Sachin Shahd7e02d42011-09-12 20:31:02 -0700419 __tzcom_is_cont_cmd(data))) {
Sachin Shahf0b51cd2011-08-11 11:54:57 -0700420 PWARN("Interrupted: exiting send_cmd loop");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700421 return -ERESTARTSYS;
422 }
Sachin Shahd7e02d42011-09-12 20:31:02 -0700423
424 if (data->abort) {
425 PERR("Aborting driver");
426 return -ENODEV;
427 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700428 data->cont_cmd_flag = 0;
429 cmd.cmd_type = TZ_SCHED_CMD_PENDING;
430 mutex_lock(&sb_in_lock);
Sachin Shahbae4ec02011-08-15 19:52:31 -0700431 ret = tzcom_scm_call((const void *) &cmd, sizeof(cmd), &resp,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700432 sizeof(resp));
433 mutex_unlock(&sb_in_lock);
Sachin Shahbae4ec02011-08-15 19:52:31 -0700434 if (ret) {
435 PERR("tzcom_scm_call failed with err: %d", ret);
436 return ret;
437 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700438 }
439
440 mutex_lock(&sb_in_lock);
441 resp.sb_in_rsp_addr = sb_in_virt + cmd.sb_in_cmd_len;
Mona Hossain6e2e2692012-01-05 11:28:04 -0800442 resp.sb_in_rsp_len = req->resp_len;
443 memcpy(req->resp_buf, resp.sb_in_rsp_addr, resp.sb_in_rsp_len);
Sachin Shahc3f8dd32011-06-17 11:39:10 -0700444 /* Zero out memory for security purpose */
445 memset(sb_in_virt, 0, reqd_len_sb_in);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700446 mutex_unlock(&sb_in_lock);
447
Mona Hossain6e2e2692012-01-05 11:28:04 -0800448 return ret;
449}
450
451
452static int tzcom_send_cmd(struct tzcom_data_t *data, void __user *argp)
453{
454 int ret = 0;
455 struct tzcom_send_cmd_op_req req;
456
457 ret = copy_from_user(&req, argp, sizeof(req));
458 if (ret) {
459 PERR("copy_from_user failed");
460 return ret;
461 }
462 ret = __tzcom_send_cmd(data, &req);
463 if (ret)
464 return ret;
465
466 PDEBUG("sending cmd_req->rsp "
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700467 "size: %u, ptr: 0x%p", req.resp_len,
468 req.resp_buf);
469 ret = copy_to_user(argp, &req, sizeof(req));
470 if (ret) {
471 PDEBUG("copy_to_user failed");
472 return ret;
473 }
Mona Hossain6e2e2692012-01-05 11:28:04 -0800474 return ret;
475}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700476
Mona Hossain6e2e2692012-01-05 11:28:04 -0800477static int __tzcom_send_cmd_req_clean_up(
478 struct tzcom_send_cmd_fd_op_req *req)
479{
480 char *field;
481 uint32_t *update;
482 int ret = 0;
483 int i = 0;
484
485 for (i = 0; i < MAX_ION_FD; i++) {
486 if (req->ifd_data[i].fd != 0) {
487 field = (char *)req->cmd_buf +
488 req->ifd_data[i].cmd_buf_offset;
489 update = (uint32_t *) field;
490 *update = 0;
491 }
492 }
493 return ret;
494}
495
496static int __tzcom_update_with_phy_addr(
497 struct tzcom_send_cmd_fd_op_req *req)
498{
499 struct ion_handle *ihandle;
500 char *field;
501 uint32_t *update;
502 ion_phys_addr_t pa;
503 int ret = 0;
504 int i = 0;
505 uint32_t length;
506
507 for (i = 0; i < MAX_ION_FD; i++) {
508 if (req->ifd_data[i].fd != 0) {
509 /* Get the handle of the shared fd */
510 ihandle = ion_import_fd(ion_clnt, req->ifd_data[i].fd);
511 if (ihandle == NULL) {
512 PERR("Ion client can't retrieve the handle\n");
513 return -ENOMEM;
514 }
515 field = (char *) req->cmd_buf +
516 req->ifd_data[i].cmd_buf_offset;
517 update = (uint32_t *) field;
518
519 /* Populate the cmd data structure with the phys_addr */
520 ret = ion_phys(ion_clnt, ihandle, &pa, &length);
521 if (ret)
522 return -ENOMEM;
523
524 *update = (uint32_t)pa;
525 ion_free(ion_clnt, ihandle);
526 }
527 }
528 return ret;
529}
530
531static int tzcom_send_cmd_with_fd(struct tzcom_data_t *data,
532 void __user *argp)
533{
534 int ret = 0;
535 struct tzcom_send_cmd_fd_op_req req;
536 struct tzcom_send_cmd_op_req send_cmd_req;
537
538 ret = copy_from_user(&req, argp, sizeof(req));
539 if (ret) {
540 PERR("copy_from_user failed");
541 return ret;
542 }
543
544 send_cmd_req.cmd_id = req.cmd_id;
545 send_cmd_req.cmd_buf = req.cmd_buf;
546 send_cmd_req.cmd_len = req.cmd_len;
547 send_cmd_req.resp_buf = req.resp_buf;
548 send_cmd_req.resp_len = req.resp_len;
549
550 ret = __tzcom_update_with_phy_addr(&req);
551 if (ret)
552 return ret;
553 ret = __tzcom_send_cmd(data, &send_cmd_req);
554 __tzcom_send_cmd_req_clean_up(&req);
555
556 if (ret)
557 return ret;
558
559 PDEBUG("sending cmd_req->rsp "
560 "size: %u, ptr: 0x%p", req.resp_len,
561 req.resp_buf);
562 ret = copy_to_user(argp, &req, sizeof(req));
563 if (ret) {
564 PDEBUG("copy_to_user failed");
565 return ret;
566 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700567 return ret;
568}
569
570static struct tzcom_registered_svc_list *__tzcom_find_svc(
571 struct tzcom_data_t *data,
572 uint32_t instance_id)
573{
574 struct tzcom_registered_svc_list *entry;
575 unsigned long flags;
576
577 spin_lock_irqsave(&data->registered_svc_list_lock, flags);
578 list_for_each_entry(entry,
579 &data->registered_svc_list_head, list) {
580 if (entry->svc.instance_id == instance_id)
581 break;
582 }
583 spin_unlock_irqrestore(&data->registered_svc_list_lock, flags);
584
585 return entry;
586}
587
588static int __tzcom_copy_cmd(struct tzcom_data_t *data,
589 struct tzcom_next_cmd_op_req *req,
590 struct tzcom_registered_svc_list *ptr_svc)
591{
592 int found = 0;
593 int ret = -EAGAIN;
Sachin Shahbae4ec02011-08-15 19:52:31 -0700594 struct tzcom_callback_list *entry, *next;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700595 struct tzcom_callback *cb;
596
597 PDEBUG("In here");
598 mutex_lock(&data->callback_list_lock);
599 PDEBUG("Before looping through cmd and svc lists.");
Sachin Shahbae4ec02011-08-15 19:52:31 -0700600 list_for_each_entry_safe(entry, next, &data->callback_list_head, list) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700601 cb = &entry->callback;
602 if (req->svc_id == ptr_svc->svc.svc_id &&
603 req->instance_id == ptr_svc->svc.instance_id &&
604 cb->cmd_id >= ptr_svc->svc.cmd_id_low &&
605 cb->cmd_id <= ptr_svc->svc.cmd_id_high) {
606 PDEBUG("Found matching entry");
607 found = 1;
608 if (cb->sb_out_cb_data_len <= req->req_len) {
609 PDEBUG("copying cmd buffer %p to req "
610 "buffer %p, length: %u",
611 (u8 *)cb + cb->sb_out_cb_data_off,
612 req->req_buf, cb->sb_out_cb_data_len);
613 req->cmd_id = cb->cmd_id;
614 ret = copy_to_user(req->req_buf,
615 (u8 *)cb + cb->sb_out_cb_data_off,
616 cb->sb_out_cb_data_len);
617 if (ret) {
Sachin Shahf0b51cd2011-08-11 11:54:57 -0700618 PERR("copy_to_user failed");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700619 break;
620 }
621 list_del(&entry->list);
622 kfree(entry);
623 ret = 0;
624 } else {
Sachin Shahf0b51cd2011-08-11 11:54:57 -0700625 PERR("callback data buffer is "
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700626 "larger than provided buffer."
627 "Required: %u, Provided: %u",
628 cb->sb_out_cb_data_len,
629 req->req_len);
630 ret = -ENOMEM;
631 }
632 break;
633 }
634 }
635 PDEBUG("After looping through cmd and svc lists.");
636 mutex_unlock(&data->callback_list_lock);
637 return ret;
638}
639
Sachin Shahd7e02d42011-09-12 20:31:02 -0700640static int __tzcom_is_next_cmd(struct tzcom_data_t *data,
641 struct tzcom_registered_svc_list *svc)
642{
643 int ret;
644 ret = (svc->next_cmd_flag != 0);
645 return ret || data->abort;
646}
647
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700648static int tzcom_read_next_cmd(struct tzcom_data_t *data, void __user *argp)
649{
650 int ret = 0;
651 struct tzcom_next_cmd_op_req req;
652 struct tzcom_registered_svc_list *this_svc;
653
654 ret = copy_from_user(&req, argp, sizeof(req));
655 if (ret) {
Sachin Shahf0b51cd2011-08-11 11:54:57 -0700656 PERR("copy_from_user failed");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700657 return ret;
658 }
659
660 if (req.instance_id > atomic_read(&svc_instance_ctr)) {
Sachin Shahf0b51cd2011-08-11 11:54:57 -0700661 PERR("Invalid instance_id for the request");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700662 return -EINVAL;
663 }
664
665 if (!req.req_buf || req.req_len == 0) {
Sachin Shahf0b51cd2011-08-11 11:54:57 -0700666 PERR("Invalid request buffer or buffer length");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700667 return -EINVAL;
668 }
669
670 PDEBUG("Before next_cmd loop");
671 this_svc = __tzcom_find_svc(data, req.instance_id);
672
673 while (1) {
674 PDEBUG("Before wait_event next_cmd.");
675 if (wait_event_interruptible(this_svc->next_cmd_wq,
Sachin Shahd7e02d42011-09-12 20:31:02 -0700676 __tzcom_is_next_cmd(data, this_svc))) {
Sachin Shahf0b51cd2011-08-11 11:54:57 -0700677 PWARN("Interrupted: exiting wait_next_cmd loop");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700678 /* woken up for different reason */
679 return -ERESTARTSYS;
680 }
Sachin Shahd7e02d42011-09-12 20:31:02 -0700681
682 if (data->abort) {
683 PERR("Aborting driver");
684 return -ENODEV;
685 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700686 PDEBUG("After wait_event next_cmd.");
687 this_svc->next_cmd_flag = 0;
688
689 ret = __tzcom_copy_cmd(data, &req, this_svc);
690 if (ret == 0) {
691 PDEBUG("Successfully found svc for cmd");
692 data->handled_cmd_svc_instance_id = req.instance_id;
693 break;
694 } else if (ret == -ENOMEM) {
Sachin Shahf0b51cd2011-08-11 11:54:57 -0700695 PERR("Not enough memory");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700696 return ret;
697 }
698 }
699 ret = copy_to_user(argp, &req, sizeof(req));
700 if (ret) {
Sachin Shahf0b51cd2011-08-11 11:54:57 -0700701 PERR("copy_to_user failed");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700702 return ret;
703 }
704 PDEBUG("copy_to_user is done.");
705 return ret;
706}
707
708static int tzcom_cont_cmd(struct tzcom_data_t *data, void __user *argp)
709{
710 int ret = 0;
711 struct tzcom_cont_cmd_op_req req;
712 ret = copy_from_user(&req, argp, sizeof(req));
713 if (ret) {
Sachin Shahf0b51cd2011-08-11 11:54:57 -0700714 PERR("copy_from_user failed");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700715 return ret;
716 }
717
718 /*
719 * Only the svc instance that handled the cmd (in read_next_cmd method)
720 * can call continue cmd
721 */
722 if (data->handled_cmd_svc_instance_id != req.instance_id) {
Sachin Shahf0b51cd2011-08-11 11:54:57 -0700723 PWARN("Only the service instance that handled the last "
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700724 "callback can continue cmd. "
725 "Expected: %u, Received: %u",
726 data->handled_cmd_svc_instance_id,
727 req.instance_id);
728 return -EINVAL;
729 }
730
731 if (req.resp_buf) {
732 mutex_lock(&sb_out_lock);
733 memcpy(sb_out_virt, req.resp_buf, req.resp_len);
734 mutex_unlock(&sb_out_lock);
735 }
736
737 data->cont_cmd_flag = 1;
738 wake_up_interruptible(&data->cont_cmd_wq);
739 return ret;
740}
741
Sachin Shahd7e02d42011-09-12 20:31:02 -0700742static int tzcom_abort(struct tzcom_data_t *data)
743{
744 int ret = 0;
745 unsigned long flags;
746 struct tzcom_registered_svc_list *lsvc, *nsvc;
747 if (data->abort) {
748 PERR("Already aborting");
749 return -EINVAL;
750 }
751
752 data->abort = 1;
753
754 PDEBUG("Waking up cont_cmd_wq");
755 wake_up_all(&data->cont_cmd_wq);
756
757 spin_lock_irqsave(&data->registered_svc_list_lock, flags);
758 PDEBUG("Before waking up service wait queues");
759 list_for_each_entry_safe(lsvc, nsvc,
760 &data->registered_svc_list_head, list) {
761 wake_up_all(&lsvc->next_cmd_wq);
762 }
763 spin_unlock_irqrestore(&data->registered_svc_list_lock, flags);
764
765 PDEBUG("ioctl_count before loop: %d", atomic_read(&data->ioctl_count));
766 while (atomic_read(&data->ioctl_count) > 0) {
767 if (wait_event_interruptible(data->abort_wq,
768 atomic_read(&data->ioctl_count) <= 0)) {
769 PERR("Interrupted from abort");
770 ret = -ERESTARTSYS;
771 break;
772 }
773 }
774 return ret;
775}
776
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700777static long tzcom_ioctl(struct file *file, unsigned cmd,
778 unsigned long arg)
779{
780 int ret = 0;
781 struct tzcom_data_t *tzcom_data = file->private_data;
782 void __user *argp = (void __user *) arg;
783 PDEBUG("enter tzcom_ioctl()");
Sachin Shahd7e02d42011-09-12 20:31:02 -0700784 if (tzcom_data->abort) {
785 PERR("Aborting tzcom driver");
786 return -ENODEV;
787 }
788
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700789 switch (cmd) {
790 case TZCOM_IOCTL_REGISTER_SERVICE_REQ: {
791 PDEBUG("ioctl register_service_req()");
Sachin Shahd7e02d42011-09-12 20:31:02 -0700792 atomic_inc(&tzcom_data->ioctl_count);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700793 ret = tzcom_register_service(tzcom_data, argp);
Sachin Shahd7e02d42011-09-12 20:31:02 -0700794 atomic_dec(&tzcom_data->ioctl_count);
795 wake_up_interruptible(&tzcom_data->abort_wq);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700796 if (ret)
Sachin Shahf0b51cd2011-08-11 11:54:57 -0700797 PERR("failed tzcom_register_service: %d", ret);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700798 break;
799 }
800 case TZCOM_IOCTL_UNREGISTER_SERVICE_REQ: {
801 PDEBUG("ioctl unregister_service_req()");
Sachin Shahd7e02d42011-09-12 20:31:02 -0700802 atomic_inc(&tzcom_data->ioctl_count);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700803 ret = tzcom_unregister_service(tzcom_data, argp);
Sachin Shahd7e02d42011-09-12 20:31:02 -0700804 atomic_dec(&tzcom_data->ioctl_count);
805 wake_up_interruptible(&tzcom_data->abort_wq);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700806 if (ret)
Sachin Shahf0b51cd2011-08-11 11:54:57 -0700807 PERR("failed tzcom_unregister_service: %d", ret);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700808 break;
809 }
810 case TZCOM_IOCTL_SEND_CMD_REQ: {
811 PDEBUG("ioctl send_cmd_req()");
812 /* Only one client allowed here at a time */
813 mutex_lock(&send_cmd_lock);
Sachin Shahd7e02d42011-09-12 20:31:02 -0700814 atomic_inc(&tzcom_data->ioctl_count);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700815 ret = tzcom_send_cmd(tzcom_data, argp);
Sachin Shahd7e02d42011-09-12 20:31:02 -0700816 atomic_dec(&tzcom_data->ioctl_count);
817 wake_up_interruptible(&tzcom_data->abort_wq);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700818 mutex_unlock(&send_cmd_lock);
819 if (ret)
Sachin Shahf0b51cd2011-08-11 11:54:57 -0700820 PERR("failed tzcom_send_cmd: %d", ret);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700821 break;
822 }
Mona Hossain6e2e2692012-01-05 11:28:04 -0800823 case TZCOM_IOCTL_SEND_CMD_FD_REQ: {
824 PDEBUG("ioctl send_cmd_req()");
825 /* Only one client allowed here at a time */
826 mutex_lock(&send_cmd_lock);
827 atomic_inc(&tzcom_data->ioctl_count);
828 ret = tzcom_send_cmd_with_fd(tzcom_data, argp);
829 atomic_dec(&tzcom_data->ioctl_count);
830 wake_up_interruptible(&tzcom_data->abort_wq);
831 mutex_unlock(&send_cmd_lock);
832 if (ret)
833 PERR("failed tzcom_send_cmd: %d", ret);
834 break;
835 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700836 case TZCOM_IOCTL_READ_NEXT_CMD_REQ: {
837 PDEBUG("ioctl read_next_cmd_req()");
Sachin Shahd7e02d42011-09-12 20:31:02 -0700838 atomic_inc(&tzcom_data->ioctl_count);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700839 ret = tzcom_read_next_cmd(tzcom_data, argp);
Sachin Shahd7e02d42011-09-12 20:31:02 -0700840 atomic_dec(&tzcom_data->ioctl_count);
841 wake_up_interruptible(&tzcom_data->abort_wq);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700842 if (ret)
Sachin Shahf0b51cd2011-08-11 11:54:57 -0700843 PERR("failed tzcom_read_next: %d", ret);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700844 break;
845 }
846 case TZCOM_IOCTL_CONTINUE_CMD_REQ: {
847 PDEBUG("ioctl continue_cmd_req()");
Sachin Shahd7e02d42011-09-12 20:31:02 -0700848 atomic_inc(&tzcom_data->ioctl_count);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700849 ret = tzcom_cont_cmd(tzcom_data, argp);
Sachin Shahd7e02d42011-09-12 20:31:02 -0700850 atomic_dec(&tzcom_data->ioctl_count);
851 wake_up_interruptible(&tzcom_data->abort_wq);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700852 if (ret)
Sachin Shahf0b51cd2011-08-11 11:54:57 -0700853 PERR("failed tzcom_cont_cmd: %d", ret);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700854 break;
855 }
Sachin Shahd7e02d42011-09-12 20:31:02 -0700856 case TZCOM_IOCTL_ABORT_REQ: {
857 PDEBUG("ioctl abort_req()");
858 ret = tzcom_abort(tzcom_data);
859 if (ret)
860 PERR("failed tzcom_abort: %d", ret);
861 break;
862 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700863 default:
864 return -EINVAL;
865 }
866 return ret;
867}
868
869static int tzcom_open(struct inode *inode, struct file *file)
870{
Sachin Shahbae4ec02011-08-15 19:52:31 -0700871 int ret;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700872 long pil_error;
873 struct tz_pr_init_sb_req_s sb_out_init_req;
874 struct tz_pr_init_sb_rsp_s sb_out_init_rsp;
875 void *rsp_addr_virt;
876 struct tzcom_command cmd;
877 struct tzcom_response resp;
878 struct tzcom_data_t *tzcom_data;
879
880 PDEBUG("In here");
881 if (pil == NULL) {
Stephen Boyd700819d2011-09-30 17:14:13 -0700882 pil = pil_get("tzapps");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700883 if (IS_ERR(pil)) {
884 PERR("Playready PIL image load failed");
885 pil_error = PTR_ERR(pil);
886 pil = NULL;
887 return pil_error;
888 }
Stephen Boyd700819d2011-09-30 17:14:13 -0700889 PDEBUG("tzapps image loaded successfully");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700890 }
891
892 sb_out_init_req.pr_cmd = TZ_SCHED_CMD_ID_INIT_SB_OUT;
893 sb_out_init_req.sb_len = sb_out_length;
894 sb_out_init_req.sb_ptr = tzcom_virt_to_phys(sb_out_virt);
895 PDEBUG("sb_out_init_req { pr_cmd: %d, sb_len: %u, "
896 "sb_ptr (phys): 0x%x }",
897 sb_out_init_req.pr_cmd,
898 sb_out_init_req.sb_len,
899 sb_out_init_req.sb_ptr);
900
901 mutex_lock(&sb_in_lock);
902 PDEBUG("Before memcpy on sb_in");
903 memcpy(sb_in_virt, &sb_out_init_req, sizeof(sb_out_init_req));
904 PDEBUG("After memcpy on sb_in");
905
906 /* It will always be a new cmd from this method */
907 cmd.cmd_type = TZ_SCHED_CMD_NEW;
908 cmd.sb_in_cmd_addr = (u8 *) tzcom_virt_to_phys(sb_in_virt);
909 cmd.sb_in_cmd_len = sizeof(sb_out_init_req);
910 PDEBUG("tzcom_command { cmd_type: %u, sb_in_cmd_addr: %p, "
911 "sb_in_cmd_len: %u }",
912 cmd.cmd_type, cmd.sb_in_cmd_addr, cmd.sb_in_cmd_len);
913
Sachin Shahf3b54ab2011-07-20 12:03:26 -0700914 resp.cmd_status = TZ_SCHED_STATUS_INCOMPLETE;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700915
916 PDEBUG("Before scm_call for sb_init");
Sachin Shahbae4ec02011-08-15 19:52:31 -0700917 ret = tzcom_scm_call(&cmd, sizeof(cmd), &resp, sizeof(resp));
918 if (ret) {
919 PERR("tzcom_scm_call failed with err: %d", ret);
920 return ret;
921 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700922 PDEBUG("After scm_call for sb_init");
Sachin Shahbae4ec02011-08-15 19:52:31 -0700923
Sachin Shahf3b54ab2011-07-20 12:03:26 -0700924 PDEBUG("tzcom_response after scm cmd_status: %u", resp.cmd_status);
925 if (resp.cmd_status == TZ_SCHED_STATUS_COMPLETE) {
926 resp.sb_in_rsp_addr = (u8 *)cmd.sb_in_cmd_addr +
927 cmd.sb_in_cmd_len;
928 resp.sb_in_rsp_len = sizeof(sb_out_init_rsp);
929 PDEBUG("tzcom_response sb_in_rsp_addr: %p, sb_in_rsp_len: %u",
930 resp.sb_in_rsp_addr, resp.sb_in_rsp_len);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700931 rsp_addr_virt = tzcom_phys_to_virt((unsigned long)
932 resp.sb_in_rsp_addr);
933 PDEBUG("Received response phys: %p, virt: %p",
Sachin Shahf3b54ab2011-07-20 12:03:26 -0700934 resp.sb_in_rsp_addr, rsp_addr_virt);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700935 memcpy(&sb_out_init_rsp, rsp_addr_virt, resp.sb_in_rsp_len);
936 } else {
937 PERR("Error with SB initialization");
938 mutex_unlock(&sb_in_lock);
939 return -EPERM;
940 }
941 mutex_unlock(&sb_in_lock);
942
943 PDEBUG("sb_out_init_rsp { pr_cmd: %d, ret: %d }",
944 sb_out_init_rsp.pr_cmd, sb_out_init_rsp.ret);
945
946 if (sb_out_init_rsp.ret) {
947 PERR("sb_out_init_req failed: %d", sb_out_init_rsp.ret);
948 return -EPERM;
949 }
950
951 tzcom_data = kmalloc(sizeof(*tzcom_data), GFP_KERNEL);
952 if (!tzcom_data) {
953 PERR("kmalloc failed");
954 return -ENOMEM;
955 }
956 file->private_data = tzcom_data;
957
958 INIT_LIST_HEAD(&tzcom_data->callback_list_head);
959 mutex_init(&tzcom_data->callback_list_lock);
960
961 INIT_LIST_HEAD(&tzcom_data->registered_svc_list_head);
962 spin_lock_init(&tzcom_data->registered_svc_list_lock);
963
964 init_waitqueue_head(&tzcom_data->cont_cmd_wq);
965 tzcom_data->cont_cmd_flag = 0;
966 tzcom_data->handled_cmd_svc_instance_id = 0;
Sachin Shahd7e02d42011-09-12 20:31:02 -0700967 tzcom_data->abort = 0;
968 init_waitqueue_head(&tzcom_data->abort_wq);
969 atomic_set(&tzcom_data->ioctl_count, 0);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700970 return 0;
971}
972
973static int tzcom_release(struct inode *inode, struct file *file)
974{
975 struct tzcom_data_t *tzcom_data = file->private_data;
976 struct tzcom_callback_list *lcb, *ncb;
977 struct tzcom_registered_svc_list *lsvc, *nsvc;
Sachin Shahd7e02d42011-09-12 20:31:02 -0700978 unsigned long flags;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700979 PDEBUG("In here");
980
Sachin Shahd7e02d42011-09-12 20:31:02 -0700981 if (!tzcom_data->abort) {
982 PDEBUG("Calling abort");
983 tzcom_abort(tzcom_data);
984 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700985
Sachin Shahd7e02d42011-09-12 20:31:02 -0700986 PDEBUG("Before removing callback list");
987 mutex_lock(&tzcom_data->callback_list_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700988 list_for_each_entry_safe(lcb, ncb,
989 &tzcom_data->callback_list_head, list) {
990 list_del(&lcb->list);
991 kfree(lcb);
992 }
Sachin Shahd7e02d42011-09-12 20:31:02 -0700993 mutex_unlock(&tzcom_data->callback_list_lock);
994 PDEBUG("After removing callback list");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700995
Sachin Shahd7e02d42011-09-12 20:31:02 -0700996 PDEBUG("Before removing svc list");
997 spin_lock_irqsave(&tzcom_data->registered_svc_list_lock, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700998 list_for_each_entry_safe(lsvc, nsvc,
999 &tzcom_data->registered_svc_list_head, list) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001000 list_del(&lsvc->list);
1001 kfree(lsvc);
1002 }
Sachin Shahd7e02d42011-09-12 20:31:02 -07001003 spin_unlock_irqrestore(&tzcom_data->registered_svc_list_lock, flags);
1004 PDEBUG("After removing svc list");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001005
Sachin Shahd7e02d42011-09-12 20:31:02 -07001006 PDEBUG("Freeing tzcom data");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001007 kfree(tzcom_data);
1008 return 0;
1009}
1010
1011static const struct file_operations tzcom_fops = {
1012 .owner = THIS_MODULE,
1013 .unlocked_ioctl = tzcom_ioctl,
1014 .open = tzcom_open,
1015 .release = tzcom_release
1016};
1017
1018static int __init tzcom_init(void)
1019{
1020 int rc;
1021 struct device *class_dev;
1022
1023 PDEBUG("Hello tzcom");
1024
1025 rc = alloc_chrdev_region(&tzcom_device_no, 0, 1, TZCOM_DEV);
1026 if (rc < 0) {
1027 PERR("alloc_chrdev_region failed %d", rc);
1028 return rc;
1029 }
1030
1031 driver_class = class_create(THIS_MODULE, TZCOM_DEV);
1032 if (IS_ERR(driver_class)) {
1033 rc = -ENOMEM;
1034 PERR("class_create failed %d", rc);
1035 goto unregister_chrdev_region;
1036 }
1037
1038 class_dev = device_create(driver_class, NULL, tzcom_device_no, NULL,
1039 TZCOM_DEV);
1040 if (!class_dev) {
1041 PERR("class_device_create failed %d", rc);
1042 rc = -ENOMEM;
1043 goto class_destroy;
1044 }
1045
1046 cdev_init(&tzcom_cdev, &tzcom_fops);
1047 tzcom_cdev.owner = THIS_MODULE;
1048
1049 rc = cdev_add(&tzcom_cdev, MKDEV(MAJOR(tzcom_device_no), 0), 1);
1050 if (rc < 0) {
1051 PERR("cdev_add failed %d", rc);
1052 goto class_device_destroy;
1053 }
1054
1055 sb_in_phys = pmem_kalloc(sb_in_length, PMEM_MEMTYPE_EBI1 |
1056 PMEM_ALIGNMENT_4K);
1057 if (IS_ERR((void *)sb_in_phys)) {
1058 PERR("could not allocte in kernel pmem buffers for sb_in");
Sachin Shah545fbfa2011-09-27 14:37:16 -07001059 sb_in_phys = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001060 rc = -ENOMEM;
1061 goto class_device_destroy;
1062 }
1063 PDEBUG("physical_addr for sb_in: 0x%x", sb_in_phys);
1064
1065 sb_in_virt = (u8 *) ioremap((unsigned long)sb_in_phys,
1066 sb_in_length);
1067 if (!sb_in_virt) {
1068 PERR("Shared buffer IN allocation failed.");
1069 rc = -ENOMEM;
1070 goto class_device_destroy;
1071 }
1072 PDEBUG("sb_in virt address: %p, phys address: 0x%x",
1073 sb_in_virt, tzcom_virt_to_phys(sb_in_virt));
1074
1075 sb_out_phys = pmem_kalloc(sb_out_length, PMEM_MEMTYPE_EBI1 |
1076 PMEM_ALIGNMENT_4K);
1077 if (IS_ERR((void *)sb_out_phys)) {
1078 PERR("could not allocte in kernel pmem buffers for sb_out");
Sachin Shah545fbfa2011-09-27 14:37:16 -07001079 sb_out_phys = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001080 rc = -ENOMEM;
1081 goto class_device_destroy;
1082 }
1083 PDEBUG("physical_addr for sb_out: 0x%x", sb_out_phys);
1084
1085 sb_out_virt = (u8 *) ioremap((unsigned long)sb_out_phys,
1086 sb_out_length);
1087 if (!sb_out_virt) {
1088 PERR("Shared buffer OUT allocation failed.");
1089 rc = -ENOMEM;
1090 goto class_device_destroy;
1091 }
1092 PDEBUG("sb_out virt address: %p, phys address: 0x%x",
1093 sb_out_virt, tzcom_virt_to_phys(sb_out_virt));
Mona Hossain6e2e2692012-01-05 11:28:04 -08001094 ion_clnt = msm_ion_client_create(0x03, "tzcom");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001095 /* Initialized in tzcom_open */
1096 pil = NULL;
1097
1098 return 0;
1099
1100class_device_destroy:
1101 if (sb_in_virt)
1102 iounmap(sb_in_virt);
1103 if (sb_in_phys)
1104 pmem_kfree(sb_in_phys);
1105 if (sb_out_virt)
1106 iounmap(sb_out_virt);
1107 if (sb_out_phys)
1108 pmem_kfree(sb_out_phys);
1109 device_destroy(driver_class, tzcom_device_no);
1110class_destroy:
1111 class_destroy(driver_class);
1112unregister_chrdev_region:
1113 unregister_chrdev_region(tzcom_device_no, 1);
1114 return rc;
1115}
1116
1117static void __exit tzcom_exit(void)
1118{
1119 PDEBUG("Goodbye tzcom");
1120 if (sb_in_virt)
1121 iounmap(sb_in_virt);
1122 if (sb_in_phys)
1123 pmem_kfree(sb_in_phys);
1124 if (sb_out_virt)
1125 iounmap(sb_out_virt);
1126 if (sb_out_phys)
1127 pmem_kfree(sb_out_phys);
1128 if (pil != NULL) {
Stephen Boyd700819d2011-09-30 17:14:13 -07001129 pil_put(pil);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001130 pil = NULL;
1131 }
1132 device_destroy(driver_class, tzcom_device_no);
1133 class_destroy(driver_class);
1134 unregister_chrdev_region(tzcom_device_no, 1);
Mona Hossain6e2e2692012-01-05 11:28:04 -08001135 ion_client_destroy(ion_clnt);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001136}
1137
1138
1139MODULE_LICENSE("GPL v2");
1140MODULE_AUTHOR("Sachin Shah <sachins@codeaurora.org>");
1141MODULE_DESCRIPTION("Qualcomm TrustZone Communicator");
1142MODULE_VERSION("1.00");
1143
1144module_init(tzcom_init);
1145module_exit(tzcom_exit);