android_kernel_samsung_hero.../drivers/misc/tzic64.c
2016-08-17 16:41:52 +08:00

356 lines
9 KiB
C

/*
* Samsung TZIC Driver
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* 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.
*/
#define KMSG_COMPONENT "TZIC"
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/platform_device.h>
#include <linux/debugfs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/sched.h>
#include <linux/list.h>
#include <linux/mutex.h>
#include <linux/android_pmem.h>
#include <linux/io.h>
#include <soc/qcom/scm.h> // multiple oemflag
//#include <mach/scm.h> // one oemflag => old version
#include <linux/types.h>
#define TZIC_DEV "tzic"
static DEFINE_MUTEX(tzic_mutex);
static struct class *driver_class;
static dev_t tzic_device_no;
static struct cdev tzic_cdev;
#define HLOS_IMG_TAMPER_FUSE 0
typedef enum {
OEMFLAG_MIN_FLAG = 2,
OEMFLAG_TZ_DRM,
OEMFLAG_FIDD,
OEMFLAG_CC,
OEMFLAG_NUM_OF_FLAG,
} Sec_OemFlagID_t;
typedef struct
{
u32 name;
u32 value;
}t_flag;
#ifndef SCM_SVC_FUSE
#define SCM_SVC_FUSE 0x08
#endif
#define SCM_BLOW_SW_FUSE_ID 0x01
#define SCM_IS_SW_FUSE_BLOWN_ID 0x02
#define TZIC_IOC_MAGIC 0x9E
#define TZIC_IOCTL_GET_FUSE_REQ _IO(TZIC_IOC_MAGIC, 0)
#define TZIC_IOCTL_SET_FUSE_REQ _IO(TZIC_IOC_MAGIC, 1)
#define TZIC_IOCTL_SET_FUSE_REQ_DEFAULT _IO(TZIC_IOC_MAGIC, 2)
#define TZIC_IOCTL_GET_FUSE_REQ_NEW _IO(TZIC_IOC_MAGIC, 10)
#define TZIC_IOCTL_SET_FUSE_REQ_NEW _IO(TZIC_IOC_MAGIC, 11)
#define STATE_IC_BAD 1
#define STATE_IC_GOOD 0
#define LOG printk
static int ic = STATE_IC_GOOD;
static int set_tamper_fuse_cmd(void);
static uint8_t get_tamper_fuse_cmd(void);
static int set_tamper_fuse_cmd_new(uint32_t flag);
static uint8_t get_tamper_fuse_cmd_new(uint32_t flag);
static int set_tamper_fuse_cmd()
{
struct scm_desc desc = {0};
uint32_t fuse_id;
desc.args[0] = fuse_id = HLOS_IMG_TAMPER_FUSE;
desc.arginfo = SCM_ARGS(1);
if (!is_scm_armv8()) {
return scm_call(SCM_SVC_FUSE, SCM_BLOW_SW_FUSE_ID, &fuse_id,
sizeof(fuse_id), NULL, 0);
} else {
return scm_call2(SCM_SIP_FNID(SCM_SVC_FUSE, SCM_BLOW_SW_FUSE_ID),
&desc);
}
}
static int set_tamper_fuse_cmd_new(uint32_t flag)
{
struct scm_desc desc = {0};
uint32_t fuse_id;
desc.args[0] = fuse_id = flag;
desc.arginfo = SCM_ARGS(1);
if (!is_scm_armv8()) {
return scm_call(SCM_SVC_FUSE, SCM_BLOW_SW_FUSE_ID, &fuse_id,
sizeof(fuse_id), NULL, 0);
} else {
return scm_call2(SCM_SIP_FNID(SCM_SVC_FUSE, SCM_BLOW_SW_FUSE_ID),
&desc);
}
}
static uint8_t get_tamper_fuse_cmd()
{
int ret;
uint32_t fuse_id;
uint8_t resp_buf;
size_t resp_len;
struct scm_desc desc = {0};
resp_len = sizeof(resp_buf);
desc.args[0] = fuse_id = HLOS_IMG_TAMPER_FUSE;
desc.arginfo = SCM_ARGS(1);
if (!is_scm_armv8()) {
ret = scm_call(SCM_SVC_FUSE, SCM_IS_SW_FUSE_BLOWN_ID, &fuse_id,
sizeof(fuse_id), &resp_buf, resp_len);
} else {
ret = scm_call2(SCM_SIP_FNID(SCM_SVC_FUSE, SCM_IS_SW_FUSE_BLOWN_ID),
&desc);
resp_buf = desc.ret[0];
}
if (ret) {
printk("scm_call/2 returned %d", ret);
resp_buf = 0xff;
}
ic = resp_buf;
return resp_buf;
}
static uint8_t get_tamper_fuse_cmd_new(uint32_t flag)
{
int ret;
uint32_t fuse_id;
uint8_t resp_buf;
size_t resp_len;
struct scm_desc desc = {0};
resp_len = sizeof(resp_buf);
desc.args[0] = fuse_id = flag;
desc.arginfo = SCM_ARGS(1);
if (!is_scm_armv8()) {
ret = scm_call(SCM_SVC_FUSE, SCM_IS_SW_FUSE_BLOWN_ID, &fuse_id,
sizeof(fuse_id), &resp_buf, resp_len);
} else {
ret = scm_call2(SCM_SIP_FNID(SCM_SVC_FUSE, SCM_IS_SW_FUSE_BLOWN_ID),
&desc);
resp_buf = desc.ret[0];
}
if (ret) {
printk("scm_call/2 returned %d", ret);
resp_buf = 0xff;
}
ic = resp_buf;
return resp_buf;
}
static long tzic_ioctl(struct file *file, unsigned cmd,
unsigned long arg)
{
int ret = 0;
int i = 0;
t_flag param;
switch(cmd){
case TZIC_IOCTL_GET_FUSE_REQ:
LOG(KERN_INFO "[oemflag]get_fuse\n");
ret = get_tamper_fuse_cmd();
LOG(KERN_INFO "[oemflag]tamper_fuse value = %x\n", ret);
break;
case TZIC_IOCTL_SET_FUSE_REQ:
LOG(KERN_INFO "[oemflag]set_fuse\n");
ret = get_tamper_fuse_cmd();
LOG(KERN_INFO "[oemflag]tamper_fuse before = %x\n", ret);
LOG(KERN_INFO "[oemflag]ioctl set_fuse\n");
mutex_lock(&tzic_mutex);
ret = set_tamper_fuse_cmd();
mutex_unlock(&tzic_mutex);
if (ret)
LOG(KERN_INFO "[oemflag]failed tzic_set_fuse_cmd: %d\n", ret);
ret = get_tamper_fuse_cmd();
LOG(KERN_INFO "[oemflag]tamper_fuse after = %x\n", ret);
break;
case TZIC_IOCTL_SET_FUSE_REQ_DEFAULT://SET ALL OEM FLAG EXCEPT 0
LOG(KERN_INFO "[oemflag]set_fuse_default\n");
ret=copy_from_user( &param, (void *)arg, sizeof(param) );
if(ret) {
LOG(KERN_INFO "[oemflag]ERROR copy from user\n");
return ret;
}
for (i=OEMFLAG_MIN_FLAG+1;i<OEMFLAG_NUM_OF_FLAG;i++){
param.name=i;
LOG(KERN_INFO "[oemflag]set_fuse_name : %d\n", param.name);
ret = get_tamper_fuse_cmd_new(param.name);
LOG(KERN_INFO "[oemflag]tamper_fuse before = %x\n", ret);
LOG(KERN_INFO "[oemflag]ioctl set_fuse\n");
mutex_lock(&tzic_mutex);
ret = set_tamper_fuse_cmd_new(param.name);
mutex_unlock(&tzic_mutex);
if (ret)
LOG(KERN_INFO "[oemflag]failed tzic_set_fuse_cmd: %d\n", ret);
ret = get_tamper_fuse_cmd_new(param.name);
LOG(KERN_INFO "[oemflag]tamper_fuse after = %x\n", ret);
}
break;
case TZIC_IOCTL_GET_FUSE_REQ_NEW:
LOG(KERN_INFO "[oemflag]get_fuse\n");
ret=copy_from_user( &param, (void *)arg, sizeof(param) );
if(ret) {
LOG(KERN_INFO "[oemflag]ERROR copy from user\n");
return ret;
}
if ((OEMFLAG_MIN_FLAG < param.name) && (param.name < OEMFLAG_NUM_OF_FLAG)){
LOG(KERN_INFO "[oemflag]get_fuse_name : %d\n", param.name);
ret = get_tamper_fuse_cmd_new(param.name);
LOG(KERN_INFO "[oemflag]tamper_fuse value = %x\n", ret);
} else {
LOG(KERN_INFO "[oemflag]command error\n");
return -EINVAL;
}
break;
case TZIC_IOCTL_SET_FUSE_REQ_NEW:
LOG(KERN_INFO "[oemflag]set_fuse\n");
ret=copy_from_user( &param, (void *)arg, sizeof(param) );
if(ret) {
LOG(KERN_INFO "[oemflag]ERROR copy from user\n");
return ret;
}
if ((OEMFLAG_MIN_FLAG < param.name) && (param.name < OEMFLAG_NUM_OF_FLAG)){
LOG(KERN_INFO "[oemflag]set_fuse_name : %d\n", param.name);
ret = get_tamper_fuse_cmd_new(param.name);
LOG(KERN_INFO "[oemflag]tamper_fuse before = %x\n", ret);
LOG(KERN_INFO "[oemflag]ioctl set_fuse\n");
//Qualcomm DRM oemflag only support HLOS_IMG_TAMPER_FUSE
if (param.name == OEMFLAG_TZ_DRM) {
mutex_lock(&tzic_mutex);
ret = set_tamper_fuse_cmd();
mutex_unlock(&tzic_mutex);
if (ret)
LOG(KERN_INFO "[oemflag]failed tzic_set_fuse_cmd: %d\n", ret);
}
mutex_lock(&tzic_mutex);
ret = set_tamper_fuse_cmd_new(param.name);
mutex_unlock(&tzic_mutex);
if (ret)
LOG(KERN_INFO "[oemflag]failed tzic_set_fuse_cmd: %d\n", ret);
ret = get_tamper_fuse_cmd_new(param.name);
LOG(KERN_INFO "[oemflag]tamper_fuse after = %x\n", ret);
} else {
LOG(KERN_INFO "[oemflag]command error\n");
return -EINVAL;
}
break;
default:
LOG(KERN_INFO "[oemflag]command error\n");
return -EINVAL;
}
return ret;
}
static const struct file_operations tzic_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = tzic_ioctl,
.compat_ioctl = tzic_ioctl,
};
static int __init tzic_init(void)
{
int rc;
struct device *class_dev;
LOG(KERN_INFO "init tzic");
rc = alloc_chrdev_region(&tzic_device_no, 0, 1, TZIC_DEV);
if (rc < 0) {
LOG(KERN_INFO "alloc_chrdev_region failed %d", rc);
return rc;
}
driver_class = class_create(THIS_MODULE, TZIC_DEV);
if (IS_ERR(driver_class)) {
rc = -ENOMEM;
LOG(KERN_INFO "class_create failed %d", rc);
goto unregister_chrdev_region;
}
class_dev = device_create(driver_class, NULL, tzic_device_no, NULL,
TZIC_DEV);
if (!class_dev) {
LOG(KERN_INFO "class_device_create failed %d", rc);
rc = -ENOMEM;
goto class_destroy;
}
cdev_init(&tzic_cdev, &tzic_fops);
tzic_cdev.owner = THIS_MODULE;
rc = cdev_add(&tzic_cdev, MKDEV(MAJOR(tzic_device_no), 0), 1);
if (rc < 0) {
LOG(KERN_INFO "cdev_add failed %d", rc);
goto class_device_destroy;
}
return 0;
class_device_destroy:
device_destroy(driver_class, tzic_device_no);
class_destroy:
class_destroy(driver_class);
unregister_chrdev_region:
unregister_chrdev_region(tzic_device_no, 1);
return rc;
}
static void __exit tzic_exit(void)
{
LOG(KERN_INFO "exit tzic");
device_destroy(driver_class, tzic_device_no);
class_destroy(driver_class);
unregister_chrdev_region(tzic_device_no, 1);
}
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Samsung TZIC Driver");
MODULE_VERSION("1.00");
module_init(tzic_init);
module_exit(tzic_exit);