/* * Copyright (C) 2009 Motorola, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* Necessary includes for device drivers */ #include #include #include /* printk() */ #include /* kmalloc() */ #include #include /* error codes */ #include /* size_t */ #include #include #include /* copy_from/to_user */ #include #include #include #include #include "sec_core.h" static ssize_t sec_read(struct file *filp, char *buf, size_t count, loff_t *f_pos); static int sec_open(struct inode *inode, struct file *filp); static int sec_release(struct inode *inode, struct file *filp); static long sec_ioctl(struct file *file, unsigned int ioctl_num, unsigned long ioctl_param); static int sec_init(void); static void sec_exit(void); #define TZBSP_SVC_OEM 254 /* Structure that declares the usual file */ /* access functions */ const struct file_operations sec_fops = { .read = sec_read, .open = sec_open, .release = sec_release, .unlocked_ioctl = sec_ioctl }; /* Mapping of the module init and exit functions */ module_init(sec_init); module_exit(sec_exit); static struct miscdevice sec_dev = { MISC_DYNAMIC_MINOR, "sec", &sec_fops }; static DEFINE_MUTEX(sec_core_lock); /****************************************************************************/ /* KERNEL DRIVER APIs, ONLY IOCTL RESPONDS BACK TO USER SPACE REQUESTS */ /****************************************************************************/ int sec_init(void) { int result; result = misc_register(&sec_dev); if (result) { printk(KERN_ERR "sec: cannot obtain major number\n"); return result; } printk(KERN_INFO "Inserting sec module\n"); return 0; } void sec_exit(void) { /* Freeing the major number */ misc_deregister(&sec_dev); } int sec_open(struct inode *inode, struct file *filp) { /* Not supported, return Success */ return 0; } int sec_release(struct inode *inode, struct file *filp) { /* Not supported, return Success */ return 0; } ssize_t sec_read(struct file *filp, char *buf, size_t count, loff_t *f_pos) { /* Not supported, return Success */ return 0; } static int data_was_updated(u8 *buffer, int size) { int i; for (i = 0; i < size; i++) if (buffer[i] != 0xff) return 1; return 0; } long sec_ioctl(struct file *file, unsigned int ioctl_num, unsigned long ioctl_param) { struct { int mot_cmd; int parm1; int parm2; int parm3; } my_cmd; struct SEC_EFUSE_PARM_T efuse_data; u8 *shared_mem; long ret_val = SEC_KM_FAIL; u32 ctr; mutex_lock(&sec_core_lock); switch (ioctl_num) { case SEC_IOCTL_READ_PROC_ID: for (ctr = 0; ctr < 10 && ret_val != SEC_KM_SUCCESS; ctr++) { shared_mem = kmalloc(SEC_PROC_ID_SIZE, GFP_KERNEL | GFP_DMA); if (!shared_mem) continue; memset(shared_mem, 0xff, SEC_PROC_ID_SIZE); my_cmd.mot_cmd = 10; my_cmd.parm1 = virt_to_phys(shared_mem); my_cmd.parm2 = SEC_PROC_ID_SIZE; if (scm_call(TZBSP_SVC_OEM, 1, &my_cmd, sizeof(my_cmd), NULL, 0) == 0) { if (copy_to_user((void __user *)ioctl_param, (const void *) shared_mem, SEC_PROC_ID_SIZE) == 0) { /* check data was actually updated */ if (data_was_updated(shared_mem, SEC_PROC_ID_SIZE)) ret_val = SEC_KM_SUCCESS; else printk(KERN_ERR "sec: not updated.\n"); } } else printk(KERN_ERR "sec: scm call failed!\n"); kfree(shared_mem); } break; case SEC_IOCTL_WRITE_FUSE: if (copy_from_user(&efuse_data, (void __user *) ioctl_param, sizeof(efuse_data)) != 0) { break; } my_cmd.mot_cmd = 2; my_cmd.parm1 = efuse_data.which_bank; my_cmd.parm2 = efuse_data.efuse_value; if (scm_call(TZBSP_SVC_OEM, 1, &my_cmd, sizeof(my_cmd), NULL, 0) == 0) { ret_val = SEC_KM_SUCCESS; } break; case SEC_IOCTL_READ_FUSE: if (copy_from_user(&efuse_data, (void *)ioctl_param, sizeof(efuse_data)) != 0) { break; } for (ctr = 0; ctr < 10 && ret_val != SEC_KM_SUCCESS; ctr++) { shared_mem = kmalloc(4, GFP_KERNEL | GFP_DMA); if (!shared_mem) continue; memset(shared_mem, 0xff, 4); my_cmd.mot_cmd = 1; my_cmd.parm1 = efuse_data.which_bank; my_cmd.parm2 = virt_to_phys(shared_mem); if (scm_call(TZBSP_SVC_OEM, 1, &my_cmd, sizeof(my_cmd), NULL, 0) == 0) { efuse_data.efuse_value = *(u32 *)shared_mem; if (copy_to_user((void *)ioctl_param, &efuse_data, sizeof(efuse_data)) == 0) { /* check data was actually updated */ if (data_was_updated(shared_mem, 4)) ret_val = SEC_KM_SUCCESS; else printk(KERN_ERR "sec: not updated!\n"); } } else printk(KERN_ERR "sec: scm call failed!\n"); kfree(shared_mem); } break; case SEC_IOCTL_GET_TZ_VERSION: shared_mem = kmalloc(4, GFP_KERNEL); my_cmd.mot_cmd = 11; my_cmd.parm1 = virt_to_phys(shared_mem); if (scm_call(TZBSP_SVC_OEM, 1, &my_cmd, sizeof(my_cmd), NULL, 0) == 0) { if (copy_to_user((void *)ioctl_param, shared_mem, 4) == 0) { ret_val = SEC_KM_SUCCESS; } } kfree(shared_mem); default: break; } mutex_unlock(&sec_core_lock); return ret_val; } /****************************************************************************/ /*Kernel Module License Information */ /****************************************************************************/ MODULE_LICENSE("GPL"); MODULE_AUTHOR("MOTOROLA");