blob: 23dbacc648cd8d847e9aac57057120448a5c14b8 [file] [log] [blame]
/* arch/arm/mach-msm/htc_drm-8x60.c
*
* Copyright (C) 2011 HTC Corporation.
* Author: Eddic Hsien <eddic_hsien@htc.com>
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* 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.
*
*/
#include <linux/module.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/random.h>
#include <linux/vmalloc.h>
#include <linux/mm.h>
#if !defined(CONFIG_ARCH_MSM7X30) && !defined(CONFIG_ARCH_MSM7X27A)
#include <mach/scm.h>
#else
#include <linux/platform_device.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <asm/io.h>
#include <asm/system.h>
#include <linux/semaphore.h>
#include <mach/msm_rpcrouter.h>
#include <mach/oem_rapi_client.h>
#define OEM_RAPI_PROG 0x3000006B
#define OEM_RAPI_VERS 0x00010001
#define OEM_RAPI_NULL_PROC 0
#define OEM_RAPI_RPC_GLUE_CODE_INFO_REMOTE_PROC 1
#define OEM_RAPI_STREAMING_FUNCTION_PROC 2
#define OEM_RAPI_CLIENT_MAX_OUT_BUFF_SIZE 128
#endif
#define DEVICE_NAME "htcdrm"
#define HTCDRM_IOCTL_WIDEVINE 0x2563
#define HTCDRM_IOCTL_DISCRETIX 0x2596
#define HTCDRM_IOCTL_CPRM 0x2564
#define HTCDRM_IOCTL_GDRIVE 0x2568
#define DEVICE_ID_LEN 32
#define WIDEVINE_KEYBOX_LEN 128
#define CPRM_KEY_LEN 188
#define HTC_DRM_DEBUG 0
#define TAG "[HTCDRM] "
#undef PDEBUG
#if HTC_DRM_DEBUG
#define PDEBUG(fmt, args...) printk(KERN_DEBUG TAG "[D] %s(%i, %s): " fmt "\n", \
__func__, current->pid, current->comm, ## args)
#else
#define PDEBUG(fmt, args...) do {} while (0)
#endif
#undef PERR
#define PERR(fmt, args...) printk(KERN_ERR TAG "[K] %s(%i, %s): " fmt "\n", \
__func__, current->pid, current->comm, ## args)
#undef PINFO
#define PINFO(fmt, args...) printk(KERN_INFO TAG "[I] %s(%i, %s): " fmt "\n", \
__func__, current->pid, current->comm, ## args)
#if !defined(CONFIG_ARCH_MSM7X30) && !defined(CONFIG_ARCH_MSM7X27A)
#define UP(S)
#else
#define UP(S) up(S)
#endif
static int htcdrm_major;
static struct class *htcdrm_class;
static const struct file_operations htcdrm_fops;
typedef struct _htc_drm_msg_s {
int func;
int offset;
unsigned char *req_buf;
int req_len;
unsigned char *resp_buf;
int resp_len;
} htc_drm_msg_s;
enum {
HTC_OEMCRYPTO_STORE_KEYBOX = 1,
HTC_OEMCRYPTO_GET_KEYBOX,
HTC_OEMCRYPTO_IDENTIFY_DEVICE,
HTC_OEMCRYPTO_GET_RANDOM,
HTC_OEMCRYPTO_IS_KEYBOX_VALID,
};
enum {
HTC_GDRIVE_GET_VOUCHER = 1,
HTC_GDRIVE_CREATE_VOUCHER_SIGNATURE,
HTC_GDRIVE_REDEEM_UPDATE,
};
#if !defined(CONFIG_ARCH_MSM7X30) && !defined(CONFIG_ARCH_MSM7X27A)
static unsigned char *htc_device_id;
static unsigned char *htc_keybox;
typedef struct _htc_drm_dix_msg_s {
int func;
int ret;
unsigned char *buf;
int buf_len;
int flags;
} htc_drm_dix_msg_s;
typedef union {
struct {
void *buffer;
int size;
} memref;
struct {
unsigned int a;
unsigned int b;
} value;
} TEE_Param;
#define TEE_PARAM_TYPE_NONE 0
#define TEE_PARAM_TYPE_VALUE_INPUT 1
#define TEE_PARAM_TYPE_VALUE_OUTPUT 2
#define TEE_PARAM_TYPE_VALUE_INOUT 3
#define TEE_PARAM_TYPE_MEMREF_INPUT 5
#define TEE_PARAM_TYPE_MEMREF_OUTPUT 6
#define TEE_PARAM_TYPE_MEMREF_INOUT 7
#define TEE_PARAM_TYPE_GET(t, i) (((t) >> (i*4)) & 0xF)
enum {
TEE_FUNC_TA_OpenSession = 1,
TEE_FUNC_TA_CloseSession,
TEE_FUNC_TA_InvokeCommand,
TEE_FUNC_TA_CreateEntryPoint,
TEE_FUNC_TA_DestroyEntryPoint,
TEE_FUNC_SECURE_STORAGE_INIT,
TEE_FUNC_SECURE_STORAGE_SYNC,
TEE_FUNC_HTC_HEAP_INIT,
TEE_FUNC_HTC_SMEM_INIT,
};
struct CMD_TA_OpenSession {
unsigned int paramTypes;
TEE_Param params[4];
void **sessionContext;
};
struct CMD_TA_CloseSession {
void *sessionContext;
};
struct CMD_TA_InvokeCommand {
void *sessionContext;
unsigned int commandID;
unsigned int paramTypes;
TEE_Param params[4];
};
struct CMD_SECURE_STORAGE {
unsigned char *image_base;
unsigned int image_size;
};
#define DX_ALLOC_TZ_HEAP 0
#if DX_ALLOC_TZ_HEAP
#define DISCRETIX_HEAP_SIZE (1024 * 1024)
static int secure_storage_init;
static unsigned char *discretix_tz_heap;
#else
#define DISCRETIX_HEAP_SIZE 0
#endif
#define DX_PRE_ALLOC_BUFFER 0
#endif
#if defined(CONFIG_ARCH_MSM7X30) || defined(CONFIG_ARCH_MSM7X27A)
struct htc_keybox_dev {
struct platform_device *pdev;
struct cdev cdev;
struct device *device;
struct class *class;
dev_t dev_num;
unsigned char keybox_buf[OEM_RAPI_CLIENT_MAX_OUT_BUFF_SIZE];
struct semaphore sem;
} htc_keybox_dev;
static struct msm_rpc_client *rpc_client;
static uint32_t open_count;
static DEFINE_MUTEX(oem_rapi_client_lock);
static int oem_rapi_client_cb(struct msm_rpc_client *client,
struct rpc_request_hdr *req,
struct msm_rpc_xdr *xdr)
{
uint32_t cb_id, accept_status;
int rc;
void *cb_func;
uint32_t temp;
struct oem_rapi_client_streaming_func_cb_arg arg;
struct oem_rapi_client_streaming_func_cb_ret ret;
arg.input = NULL;
ret.out_len = NULL;
ret.output = NULL;
xdr_recv_uint32(xdr, &cb_id);
xdr_recv_uint32(xdr, &arg.event);
xdr_recv_uint32(xdr, (uint32_t *)(&arg.handle));
xdr_recv_uint32(xdr, &arg.in_len);
xdr_recv_bytes(xdr, (void **)&arg.input, &temp);
xdr_recv_uint32(xdr, &arg.out_len_valid);
if (arg.out_len_valid) {
ret.out_len = kmalloc(sizeof(*ret.out_len), GFP_KERNEL);
if (!ret.out_len) {
accept_status = RPC_ACCEPTSTAT_SYSTEM_ERR;
goto oem_rapi_send_ack;
}
}
xdr_recv_uint32(xdr, &arg.output_valid);
if (arg.output_valid) {
xdr_recv_uint32(xdr, &arg.output_size);
ret.output = kmalloc(arg.output_size, GFP_KERNEL);
if (!ret.output) {
accept_status = RPC_ACCEPTSTAT_SYSTEM_ERR;
goto oem_rapi_send_ack;
}
}
cb_func = msm_rpc_get_cb_func(client, cb_id);
if (cb_func) {
rc = ((int (*)(struct oem_rapi_client_streaming_func_cb_arg *,
struct oem_rapi_client_streaming_func_cb_ret *))
cb_func)(&arg, &ret);
if (rc)
accept_status = RPC_ACCEPTSTAT_SYSTEM_ERR;
else
accept_status = RPC_ACCEPTSTAT_SUCCESS;
} else
accept_status = RPC_ACCEPTSTAT_SYSTEM_ERR;
oem_rapi_send_ack:
xdr_start_accepted_reply(xdr, accept_status);
if (accept_status == RPC_ACCEPTSTAT_SUCCESS) {
uint32_t temp = sizeof(uint32_t);
xdr_send_pointer(xdr, (void **)&(ret.out_len), temp,
xdr_send_uint32);
if (ret.output && ret.out_len)
xdr_send_bytes(xdr, (const void **)&ret.output,
ret.out_len);
else {
temp = 0;
xdr_send_uint32(xdr, &temp);
}
}
rc = xdr_send_msg(xdr);
if (rc)
PERR("sending reply failed: %d", rc);
kfree(arg.input);
kfree(ret.out_len);
kfree(ret.output);
return 0;
}
static int oem_rapi_client_streaming_function_arg(struct msm_rpc_client *client,
struct msm_rpc_xdr *xdr,
void *data)
{
int cb_id;
struct oem_rapi_client_streaming_func_arg *arg = data;
cb_id = msm_rpc_add_cb_func(client, (void *)arg->cb_func);
if ((cb_id < 0) && (cb_id != MSM_RPC_CLIENT_NULL_CB_ID))
return cb_id;
xdr_send_uint32(xdr, &arg->event);
xdr_send_uint32(xdr, &cb_id);
xdr_send_uint32(xdr, (uint32_t *)(&arg->handle));
xdr_send_uint32(xdr, &arg->in_len);
xdr_send_bytes(xdr, (const void **)&arg->input,
&arg->in_len);
xdr_send_uint32(xdr, &arg->out_len_valid);
xdr_send_uint32(xdr, &arg->output_valid);
if (arg->output_valid)
xdr_send_uint32(xdr, &arg->output_size);
return 0;
}
static int oem_rapi_client_streaming_function_ret(struct msm_rpc_client *client,
struct msm_rpc_xdr *xdr,
void *data)
{
struct oem_rapi_client_streaming_func_ret *ret = data;
uint32_t temp;
xdr_recv_pointer(xdr, (void **)&(ret->out_len), sizeof(uint32_t),
xdr_recv_uint32);
if (ret->out_len && *ret->out_len)
xdr_recv_bytes(xdr, (void **)&ret->output, &temp);
return 0;
}
int oem_rapi_client_streaming_function(
struct msm_rpc_client *client,
struct oem_rapi_client_streaming_func_arg *arg,
struct oem_rapi_client_streaming_func_ret *ret)
{
return msm_rpc_client_req2(client,
OEM_RAPI_STREAMING_FUNCTION_PROC,
oem_rapi_client_streaming_function_arg, arg,
oem_rapi_client_streaming_function_ret,
ret, -1);
}
EXPORT_SYMBOL(oem_rapi_client_streaming_function);
int oem_rapi_client_close(void)
{
mutex_lock(&oem_rapi_client_lock);
if (open_count > 0) {
if (--open_count == 0) {
msm_rpc_unregister_client(rpc_client);
PINFO("Disconnected from remote oem rapi server");
}
}
mutex_unlock(&oem_rapi_client_lock);
return 0;
}
EXPORT_SYMBOL(oem_rapi_client_close);
struct msm_rpc_client *oem_rapi_client_init(void)
{
mutex_lock(&oem_rapi_client_lock);
if (open_count == 0) {
rpc_client = msm_rpc_register_client2("oemrapiclient",
OEM_RAPI_PROG,
OEM_RAPI_VERS, 0,
oem_rapi_client_cb);
if (!IS_ERR(rpc_client))
open_count++;
} else {
open_count++;
}
mutex_unlock(&oem_rapi_client_lock);
return rpc_client;
}
EXPORT_SYMBOL(oem_rapi_client_init);
static ssize_t htc_keybox_read(struct htc_keybox_dev *dev, char *buf, size_t size, loff_t p)
{
unsigned int count = size;
int ret_rpc = 0;
struct oem_rapi_client_streaming_func_arg arg;
struct oem_rapi_client_streaming_func_ret ret;
unsigned char nullbuf[OEM_RAPI_CLIENT_MAX_OUT_BUFF_SIZE];
memset(dev->keybox_buf, 56, OEM_RAPI_CLIENT_MAX_OUT_BUFF_SIZE);
memset(nullbuf, 0, OEM_RAPI_CLIENT_MAX_OUT_BUFF_SIZE);
PDEBUG("start:");
if (p >= OEM_RAPI_CLIENT_MAX_OUT_BUFF_SIZE)
return count ? -ENXIO : 0;
PDEBUG("oem_rapi_client_streaming_function start:");
if (count == 0xFF) {
arg.event = OEM_RAPI_CLIENT_EVENT_WIDEVINE_READ_DEVICE_ID;
memset(dev->keybox_buf, 57, OEM_RAPI_CLIENT_MAX_OUT_BUFF_SIZE);
PINFO("Get device id via oem_rapi");
} else if (count > OEM_RAPI_CLIENT_MAX_OUT_BUFF_SIZE - p) {
count = OEM_RAPI_CLIENT_MAX_OUT_BUFF_SIZE - p;
arg.event = OEM_RAPI_CLIENT_EVENT_WIDEVINE_READ_KEYBOX;
PINFO("Get keybox via oem_rapi");
} else {
arg.event = OEM_RAPI_CLIENT_EVENT_WIDEVINE_READ_KEYBOX;
PINFO("Get keybox via oem_rapi");
}
arg.cb_func = NULL;
arg.handle = (void *)0;
arg.in_len = 0;
arg.input = (char *)nullbuf;
arg.out_len_valid = 1;
arg.output_valid = 1;
arg.output_size = OEM_RAPI_CLIENT_MAX_OUT_BUFF_SIZE;
ret.out_len = NULL;
ret.output = NULL;
ret_rpc = oem_rapi_client_streaming_function(rpc_client, &arg, &ret);
if (ret_rpc) {
PERR("Get data from modem failed: %d", ret_rpc);
return -EFAULT;
}
PDEBUG("Data obtained from modem %d, ", *(ret.out_len));
memcpy(dev->keybox_buf, ret.output, *(ret.out_len));
kfree(ret.out_len);
kfree(ret.output);
return 0;
}
static ssize_t htc_keybox_write(struct htc_keybox_dev *dev, const char *buf, size_t size, loff_t p)
{
unsigned int count = size;
int ret_rpc = 0;
struct oem_rapi_client_streaming_func_arg arg;
struct oem_rapi_client_streaming_func_ret ret;
PDEBUG("start:");
if (p >= OEM_RAPI_CLIENT_MAX_OUT_BUFF_SIZE)
return count ? -ENXIO : 0;
if (count > OEM_RAPI_CLIENT_MAX_OUT_BUFF_SIZE - p)
count = OEM_RAPI_CLIENT_MAX_OUT_BUFF_SIZE - p;
PDEBUG("oem_rapi_client_streaming_function start:");
arg.event = OEM_RAPI_CLIENT_EVENT_WIDEVINE_WRITE_KEYBOX;
arg.cb_func = NULL;
arg.handle = (void *)0;
arg.in_len = OEM_RAPI_CLIENT_MAX_OUT_BUFF_SIZE;
arg.input = (char *)dev->keybox_buf;
arg.out_len_valid = 0;
arg.output_valid = 0;
arg.output_size = 0;
ret.out_len = NULL;
ret.output = NULL;
ret_rpc = oem_rapi_client_streaming_function(rpc_client, &arg, &ret);
if (ret_rpc) {
PERR("Send data from modem failed: %d", ret_rpc);
return -EFAULT;
}
PDEBUG("Data sent to modem %s", dev->keybox_buf);
return 0;
}
static struct htc_keybox_dev *keybox_dev;
#endif
static unsigned char *htc_device_id;
static unsigned char *htc_keybox;
#if !defined(CONFIG_ARCH_MSM7X30) && !defined(CONFIG_ARCH_MSM7X27A)
#define DX_SMEM_SIZE PAGE_SIZE
static int discretix_smem_size = DX_SMEM_SIZE;
static unsigned char *discretix_smem_ptr;
static unsigned char *discretix_smem_phy;
static unsigned char *discretix_smem_area;
static DEFINE_MUTEX(dx_lock);
#if (DX_PRE_ALLOC_BUFFER)
#define DX_PRE_ALLOC_BUFFER_SIZE ((512 + 64) * 1024)
static unsigned char *dx_memory_pool_ptr;
static unsigned char *dx_memory_pool_phy;
static unsigned int dx_mem_offset;
static unsigned int max_ofs;
static void reset_dx_memory_pool(void)
{
if (max_ofs < dx_mem_offset)
max_ofs = dx_mem_offset;
dx_mem_offset = 0;
}
static unsigned char *dx_kzalloc(int size)
{
unsigned char *m = NULL;
unsigned int mask;
if (dx_memory_pool_ptr == NULL)
return NULL;
if (dx_mem_offset + size <= DX_PRE_ALLOC_BUFFER_SIZE) {
m = dx_memory_pool_ptr + dx_mem_offset;
memset(m, 0, size);
dx_mem_offset += size;
mask = 4096 - 1;
dx_mem_offset = (dx_mem_offset + mask) & ~mask;
}
return m;
}
static unsigned char *dx_virt_to_phys(unsigned char *virt)
{
return (dx_memory_pool_phy + (virt - dx_memory_pool_ptr));
}
#endif
#if 0
static long htcdrm_discretix_cmd(unsigned int command, unsigned long arg)
{
htc_drm_dix_msg_s hdix;
int ret = 0, i;
unsigned char *ptr, *data, *image, *image_u;
unsigned char *kbuf[4], *ubuf[4];
#if defined(DX_PRE_ALLOC_BUFFER)
unsigned int *sessionContext;
#else
unsigned int sessionContext;
#endif
unsigned long start, end;
image_u = NULL;
image = NULL;
#if defined(DX_PRE_ALLOC_BUFFER)
reset_dx_memory_pool();
sessionContext = (unsigned int *)dx_kzalloc(sizeof(int));
if (!sessionContext)
return -EFAULT;
#endif
if (copy_from_user(&hdix, (void __user *)arg, sizeof(hdix))) {
PERR("copy_from_user error (msg)");
return -EFAULT;
}
PDEBUG("htcdrm_discretix_ioctl func: %x", hdix.func);
switch (hdix.func) {
case TEE_FUNC_TA_OpenSession:
if (hdix.buf_len != sizeof(struct CMD_TA_OpenSession)) {
PERR("OpenSession size error");
return -EFAULT;
}
break;
case TEE_FUNC_TA_InvokeCommand:
if (hdix.buf_len != sizeof(struct CMD_TA_InvokeCommand)) {
PERR("InvokeCommand size error");
return -EFAULT;
}
break;
case TEE_FUNC_TA_CloseSession:
if (hdix.buf_len != sizeof(struct CMD_TA_CloseSession)) {
PERR("CloseSession size error");
return -EFAULT;
}
break;
case TEE_FUNC_TA_CreateEntryPoint:
if (hdix.buf_len != 0) {
PERR("CreateEntryPoint size error");
return -EFAULT;
}
break;
case TEE_FUNC_TA_DestroyEntryPoint:
if (hdix.buf_len != 0) {
PERR("DestroyEntryPoint size error");
return -EFAULT;
}
break;
case TEE_FUNC_SECURE_STORAGE_INIT:
if (secure_storage_init)
return 0;
case TEE_FUNC_HTC_HEAP_INIT:
case TEE_FUNC_HTC_SMEM_INIT:
case TEE_FUNC_SECURE_STORAGE_SYNC:
if (hdix.buf_len != sizeof(struct CMD_SECURE_STORAGE)) {
PERR("SECURE_STORAGE size error");
return -EFAULT;
}
break;
default:
PERR("func: %d error", hdix.func);
return -EFAULT;
}
if (hdix.buf_len != 0) {
#if defined(DX_PRE_ALLOC_BUFFER)
ptr = dx_kzalloc(hdix.buf_len);
#else
ptr = kzalloc(hdix.buf_len, GFP_KERNEL);
#endif
if (ptr == NULL) {
PERR("allocate the space for data failed (%d)", hdix.buf_len);
return -EFAULT;
}
if (copy_from_user(ptr, (void __user *)hdix.buf, hdix.buf_len)) {
PERR("copy_from_user error (data)");
#if !defined(DX_PRE_ALLOC_BUFFER)
kfree(ptr);
#endif
return -EFAULT;
}
data = hdix.buf;
#if defined(DX_PRE_ALLOC_BUFFER)
hdix.buf = (unsigned char *)dx_virt_to_phys(ptr);
#else
hdix.buf = (unsigned char *)virt_to_phys(ptr);
#endif
} else {
data = NULL;
ptr = NULL;
}
for (i = 0; i < 4; i++)
kbuf[i] = NULL;
PDEBUG("func = %x", hdix.func);
switch (hdix.func) {
case TEE_FUNC_HTC_HEAP_INIT:
{
struct CMD_SECURE_STORAGE *s;
s = (struct CMD_SECURE_STORAGE *)ptr;
s->image_base = (unsigned char *)virt_to_phys(discretix_tz_heap);
s->image_size = DISCRETIX_HEAP_SIZE;
}
break;
case TEE_FUNC_HTC_SMEM_INIT:
{
struct CMD_SECURE_STORAGE *s;
s = (struct CMD_SECURE_STORAGE *)ptr;
s->image_base = (unsigned char *)virt_to_phys(discretix_smem_area);
s->image_size = discretix_smem_size;
}
break;
case TEE_FUNC_SECURE_STORAGE_INIT:
{
struct CMD_SECURE_STORAGE *s;
s = (struct CMD_SECURE_STORAGE *)ptr;
#if defined(DX_PRE_ALLOC_BUFFER)
image = dx_kzalloc(s->image_size);
#else
image = kzalloc(s->image_size, GFP_KERNEL);
#endif
if (image == NULL) {
PERR("allocate the space for fat8 image failed");
return -1;
}
if (copy_from_user(image, (void __user *)s->image_base, s->image_size)) {
PERR("copy_from_user error (image)");
#if !defined(DX_PRE_ALLOC_BUFFER)
kfree(image);
#endif
return -EFAULT;
}
#if defined(DX_PRE_ALLOC_BUFFER)
s->image_base = (unsigned char *)dx_virt_to_phys(image);
#else
s->image_base = (unsigned char *)virt_to_phys(image);
#endif
}
break;
case TEE_FUNC_SECURE_STORAGE_SYNC:
{
struct CMD_SECURE_STORAGE *s;
s = (struct CMD_SECURE_STORAGE *)ptr;
#if defined(DX_PRE_ALLOC_BUFFER)
image = dx_kzalloc(s->image_size);
#else
image = kzalloc(s->image_size, GFP_KERNEL);
#endif
if (image == NULL) {
PERR("allocate the space for fat8 image failed");
return -1;
}
image_u = s->image_base;
#if defined(DX_PRE_ALLOC_BUFFER)
s->image_base = (unsigned char *)dx_virt_to_phys(image);
#else
s->image_base = (unsigned char *)virt_to_phys(image);
#endif
}
break;
case TEE_FUNC_TA_OpenSession:
{
struct CMD_TA_OpenSession *s;
s = (struct CMD_TA_OpenSession *)ptr;
#if defined(DX_PRE_ALLOC_BUFFER)
*sessionContext = 0;
s->sessionContext = (void *)dx_virt_to_phys((char *)sessionContext);
#else
sessionContext = 0;
s->sessionContext = (void *)virt_to_phys(&sessionContext);
#endif
for (i = 0; i < 4; i++) {
int ptype;
ubuf[i] = s->params[i].memref.buffer;
ptype = TEE_PARAM_TYPE_GET(s->paramTypes, i);
switch (ptype) {
case TEE_PARAM_TYPE_MEMREF_INPUT:
case TEE_PARAM_TYPE_MEMREF_OUTPUT:
case TEE_PARAM_TYPE_MEMREF_INOUT:
if (s->params[i].memref.size != 0) {
#if defined(DX_PRE_ALLOC_BUFFER)
kbuf[i] = dx_kzalloc(s->params[i].memref.size);
#else
kbuf[i] = kzalloc(s->params[i].memref.size, GFP_KERNEL);
#endif
if (kbuf[i] == NULL) {
PERR("allocate the space for buffer failed (%d)", s->params[i].memref.size);
ret = -EFAULT;
goto discretix_error_exit;
}
#if defined(DX_PRE_ALLOC_BUFFER)
s->params[i].memref.buffer = (unsigned char *)dx_virt_to_phys(kbuf[i]);
#else
s->params[i].memref.buffer = (unsigned char *)virt_to_phys(kbuf[i]);
#endif
} else
kbuf[i] = NULL;
if ((ptype == TEE_PARAM_TYPE_MEMREF_INPUT) ||
(ptype == TEE_PARAM_TYPE_MEMREF_INOUT)) {
if (copy_from_user(kbuf[i], (void __user *)ubuf[i], s->params[i].memref.size)) {
PERR("copy_from_user error (buffer)");
ret = -EFAULT;
goto discretix_error_exit;
}
}
break;
default:
break;
}
}
}
break;
case TEE_FUNC_TA_InvokeCommand:
{
struct CMD_TA_InvokeCommand *s;
s = (struct CMD_TA_InvokeCommand *)ptr;
if (!(s->sessionContext)) {
PERR("session context is null");
ret = -EFAULT;
goto discretix_error_exit;
}
for (i = 0; i < 4; i++) {
int ptype;
ubuf[i] = s->params[i].memref.buffer;
ptype = TEE_PARAM_TYPE_GET(s->paramTypes, i);
switch (ptype) {
case TEE_PARAM_TYPE_MEMREF_INPUT:
case TEE_PARAM_TYPE_MEMREF_OUTPUT:
case TEE_PARAM_TYPE_MEMREF_INOUT:
if (s->params[i].memref.size != 0) {
#if defined(DX_PRE_ALLOC_BUFFER)
kbuf[i] = dx_kzalloc(s->params[i].memref.size);
#else
kbuf[i] = kzalloc(s->params[i].memref.size, GFP_KERNEL);
#endif
if (kbuf[i] == NULL) {
PERR("allocate the space for buffer failed (%d)", s->params[i].memref.size);
ret = -EFAULT;
goto discretix_error_exit;
}
#if defined(DX_PRE_ALLOC_BUFFER)
s->params[i].memref.buffer = (unsigned char *)dx_virt_to_phys(kbuf[i]);
#else
s->params[i].memref.buffer = (unsigned char *)virt_to_phys(kbuf[i]);
#endif
} else
kbuf[i] = NULL;
if ((ptype == TEE_PARAM_TYPE_MEMREF_INPUT) ||
(ptype == TEE_PARAM_TYPE_MEMREF_INOUT)) {
if (copy_from_user(kbuf[i], (void __user *)ubuf[i], s->params[i].memref.size)) {
PERR("copy_from_user error (buffer)");
ret = -EFAULT;
goto discretix_error_exit;
}
}
break;
default:
break;
}
}
}
break;
default:
break;
}
PDEBUG("##### TZ DX");
ret = secure_3rd_party_syscall(0, (unsigned char *)&hdix, sizeof(hdix));
if (ptr) {
start = (unsigned long)ptr;
end = start + hdix.buf_len;
scm_flush_range(start, end);
}
PDEBUG("##### TZ DX ret=%d(%x)", ret, ret);
if (ret) {
PERR("3rd party syscall failed (%d)", ret);
goto discretix_error_exit;
}
if (hdix.ret) {
PERR("[DX] func:%x ret:%x", hdix.func, hdix.ret);
}
switch (hdix.func) {
case TEE_FUNC_TA_OpenSession:
{
struct CMD_TA_OpenSession *s;
s = (struct CMD_TA_OpenSession *)ptr;
#if defined(DX_PRE_ALLOC_BUFFER)
start = (unsigned long)sessionContext;
#else
start = (unsigned long)&sessionContext;
#endif
end = start + 4;
scm_flush_range(start, end);
#if defined(DX_PRE_ALLOC_BUFFER)
s->sessionContext = (void *)(*sessionContext);
#else
s->sessionContext = (void *)sessionContext;
#endif
for (i = 0; i < 4; i++) {
int ptype;
ptype = TEE_PARAM_TYPE_GET(s->paramTypes, i);
switch (ptype) {
case TEE_PARAM_TYPE_MEMREF_OUTPUT:
case TEE_PARAM_TYPE_MEMREF_INOUT:
if (s->params[i].memref.size != 0) {
start = (unsigned long)kbuf[i];
end = start + s->params[i].memref.size;
scm_flush_range(start, end);
if (copy_to_user((void __user *)ubuf[i], kbuf[i], s->params[i].memref.size)) {
PERR("copy_to_user error (buffer)");
ret = -EFAULT;
goto discretix_error_exit;
}
}
break;
default:
break;
}
}
}
break;
case TEE_FUNC_TA_InvokeCommand:
{
struct CMD_TA_InvokeCommand *s;
s = (struct CMD_TA_InvokeCommand *)ptr;
for (i = 0; i < 4; i++) {
int ptype;
ptype = TEE_PARAM_TYPE_GET(s->paramTypes, i);
switch (ptype) {
case TEE_PARAM_TYPE_MEMREF_OUTPUT:
case TEE_PARAM_TYPE_MEMREF_INOUT:
if (s->params[i].memref.size != 0) {
start = (unsigned long)kbuf[i];
end = start + s->params[i].memref.size;
scm_flush_range(start, end);
if (copy_to_user((void __user *)ubuf[i], kbuf[i], s->params[i].memref.size)) {
PERR("copy_to_user error (buffer)");
ret = -EFAULT;
goto discretix_error_exit;
}
}
break;
default:
break;
}
}
}
break;
case TEE_FUNC_SECURE_STORAGE_INIT:
{
struct CMD_SECURE_STORAGE *s;
s = (struct CMD_SECURE_STORAGE *)ptr;
s->image_base = image_u;
#if !defined(DX_PRE_ALLOC_BUFFER)
kfree(image);
#endif
secure_storage_init = 1;
}
break;
case TEE_FUNC_SECURE_STORAGE_SYNC:
{
struct CMD_SECURE_STORAGE *s;
s = (struct CMD_SECURE_STORAGE *)ptr;
s->image_base = image_u;
start = (unsigned long)image;
end = start + s->image_size;
scm_flush_range(start, end);
if (copy_to_user((void __user *)image_u, image, s->image_size))
PERR("copy_to_user error (image)");
#if !defined(DX_PRE_ALLOC_BUFFER)
kfree(image);
#endif
PDEBUG("sync htc ssd");
}
break;
default:
break;
}
if (hdix.buf_len != 0) {
if (copy_to_user((void __user *)data, ptr, hdix.buf_len)) {
PERR("copy_to_user error (data)");
ret = -EFAULT;
goto discretix_error_exit;
}
}
if (copy_to_user((void __user *)arg, &hdix, sizeof(hdix))) {
PERR("copy_to_user error (msg)");
ret = -EFAULT;
goto discretix_error_exit;
}
discretix_error_exit:
#if !defined(DX_PRE_ALLOC_BUFFER)
for (i = 0; i < 4; i++)
if (kbuf[i] != NULL)
kfree(kbuf[i]);
kfree(ptr);
#endif
return ret;
}
#endif
#if DX_ALLOC_TZ_HEAP
static int htcdrm_discretix_init_heap(void)
{
htc_drm_dix_msg_s hdix;
struct CMD_SECURE_STORAGE *s;
int ret;
static int init_flag;
if (init_flag)
return 0;
if (discretix_tz_heap == NULL) {
PERR("discretix_tz_heap null");
return 0;
}
s = (struct CMD_SECURE_STORAGE *)kzalloc(sizeof(struct CMD_SECURE_STORAGE), GFP_KERNEL);
s->image_base = (unsigned char *)virt_to_phys(discretix_tz_heap);
s->image_size = DISCRETIX_HEAP_SIZE;
memset((char *)&hdix, 0, sizeof(hdix));
hdix.func = TEE_FUNC_HTC_HEAP_INIT;
hdix.buf = (unsigned char *)virt_to_phys(s);
hdix.buf_len = sizeof(struct CMD_SECURE_STORAGE);
ret = secure_3rd_party_syscall(0, (unsigned char *)&hdix, sizeof(hdix));
if (ret)
PERR("3rd party syscall failed (%d)", ret);
kfree(s);
init_flag = 1;
return ret;
}
#endif
static long htcdrm_discretix_ioctl(struct file *file, unsigned int command, unsigned long arg)
{
#if DX_ALLOC_TZ_HEAP
htc_drm_dix_msg_s hdix;
if (copy_from_user(&hdix, (void __user *)arg, sizeof(hdix))) {
PERR("copy_from_user error (msg)");
return -EFAULT;
}
if (hdix.func == TEE_FUNC_SECURE_STORAGE_INIT) {
htcdrm_discretix_init_heap();
}
return htcdrm_discretix_cmd(command, arg);
#endif
return 0;
}
#endif
static long htcdrm_gdrive_ioctl(struct file *file, unsigned int command, unsigned long arg) {
htc_drm_msg_s hmsg;
unsigned char *in_buf = NULL, *out_buf = NULL;
int ret = 0;
int ii;
unsigned long start, end;
PDEBUG("%s entry(%d)", __func__, __LINE__);
if (copy_from_user(&hmsg, (void __user *)arg, sizeof(hmsg))) {
PERR("copy_from_user error (msg)");
return -EFAULT;
}
switch(hmsg.func) {
case HTC_GDRIVE_GET_VOUCHER:
if ((hmsg.resp_buf == NULL) || !hmsg.resp_len) {
PERR("invalid arguments");
return -EFAULT;
}
out_buf = kmalloc(hmsg.resp_len, GFP_KERNEL);
if (!out_buf) {
PERR("Alloc memory fail");
return -ENOMEM;
}
ret = secure_access_item(0, ITEM_GDRIVE_DATA, hmsg.resp_len, out_buf);
if (ret)
PERR("get GDrive voucher failed (%d)", ret);
else {
start = (unsigned long)out_buf;
end = start + hmsg.resp_len;
scm_inv_range(start, end);
if (copy_to_user((void __user *)hmsg.resp_buf, out_buf, *(unsigned int *)(out_buf) + 4)) {
PERR("copy_to_user error (gdrive voucher)");
ret = -EFAULT;
}
}
kfree(out_buf);
break;
case HTC_GDRIVE_CREATE_VOUCHER_SIGNATURE:
PDEBUG("%s entry(%d)", __func__, __LINE__);
if ((hmsg.resp_buf == NULL) || !hmsg.resp_len) {
PERR("invalid arguments");
return -EFAULT;
}
if ((in_buf = kmalloc(hmsg.req_len, GFP_KERNEL)) == NULL) {
PERR("Alloc memory fail");
return -ENOMEM;
}
if ((out_buf = kmalloc(hmsg.resp_len, GFP_KERNEL)) == NULL) {
PERR("Alloc memory fail");
return -ENOMEM;
}
if (copy_from_user(in_buf, (void __user *)hmsg.req_buf, hmsg.req_len)) {
PERR("copy_from_user error (msg)");
kfree(in_buf);
kfree(out_buf);
return -EFAULT;
}
for (ii = 0; ii < hmsg.req_len; ii++) {
PDEBUG("%02x", in_buf[ii]);
}
ret = secure_access_item(1, ITEM_VOUCHER_SIG_DATA, hmsg.req_len, in_buf);
if (ret)
PERR("put GDrive data fail (%d)", ret);
else {
PDEBUG("Read data from TZ start\n");
ret = secure_access_item(0, ITEM_VOUCHER_SIG_DATA, hmsg.resp_len, out_buf);
if (ret)
PERR("get voucher signature fail (%d)", ret);
else {
start = (unsigned long)out_buf;
end = start + hmsg.resp_len;
scm_inv_range(start, end);
if (copy_to_user((void __user *)hmsg.resp_buf, out_buf, hmsg.resp_len)) {
PERR("copy_to_user error (gdrive voucher)");
ret = -EFAULT;
}
}
}
kfree(in_buf);
kfree(out_buf);
break;
case HTC_GDRIVE_REDEEM_UPDATE:
break;
default:
PERR("command error");
return -EFAULT;
}
return ret;
}
static long htcdrm_ioctl(struct file *file, unsigned int command, unsigned long arg)
{
htc_drm_msg_s hmsg;
int ret = 0;
unsigned char *ptr;
static unsigned char htc_cprmkey[CPRM_KEY_LEN]={0};
PDEBUG("command = %x", command);
switch (command) {
case HTCDRM_IOCTL_WIDEVINE:
if (copy_from_user(&hmsg, (void __user *)arg, sizeof(hmsg))) {
PERR("copy_from_user error (msg)");
return -EFAULT;
}
#if defined(CONFIG_ARCH_MSM7X30) || defined(CONFIG_ARCH_MSM7X27A)
oem_rapi_client_init();
if (down_interruptible(&keybox_dev->sem)) {
PERR("interrupt error");
return -EFAULT;
}
#endif
PDEBUG("func = %x", hmsg.func);
switch (hmsg.func) {
case HTC_OEMCRYPTO_STORE_KEYBOX:
if ((hmsg.req_buf == NULL) || (hmsg.req_len != WIDEVINE_KEYBOX_LEN)) {
PERR("invalid arguments");
UP(&keybox_dev->sem);
return -EFAULT;
}
if (copy_from_user(htc_keybox, (void __user *)hmsg.req_buf, hmsg.req_len)) {
PERR("copy_from_user error (keybox)");
UP(&keybox_dev->sem);
return -EFAULT;
}
#if defined(CONFIG_ARCH_MSM7X30) || defined(CONFIG_ARCH_MSM7X27A)
memcpy(keybox_dev->keybox_buf, htc_keybox, WIDEVINE_KEYBOX_LEN);
ret = htc_keybox_write(keybox_dev, htc_keybox, hmsg.req_len, hmsg.offset);
oem_rapi_client_close();
#else
ret = secure_access_item(1, ITEM_KEYBOX_PROVISION, hmsg.req_len,
htc_keybox);
#endif
if (ret)
PERR("provision keybox failed (%d)", ret);
UP(&keybox_dev->sem);
break;
case HTC_OEMCRYPTO_GET_KEYBOX:
if ((hmsg.resp_buf == NULL) || !hmsg.resp_len ||
((hmsg.offset + hmsg.resp_len) > WIDEVINE_KEYBOX_LEN)) {
PERR("invalid arguments");
UP(&keybox_dev->sem);
return -EFAULT;
}
#if defined(CONFIG_ARCH_MSM7X30) || defined(CONFIG_ARCH_MSM7X27A)
ret = htc_keybox_read(keybox_dev, htc_keybox, hmsg.resp_len, hmsg.offset);
oem_rapi_client_close();
htc_keybox = keybox_dev->keybox_buf;
#else
ret = secure_access_item(0, ITEM_KEYBOX_DATA, WIDEVINE_KEYBOX_LEN,
htc_keybox);
#endif
if (ret)
PERR("get keybox failed (%d)", ret);
else {
if (copy_to_user((void __user *)hmsg.resp_buf, htc_keybox + hmsg.offset, hmsg.resp_len)) {
PERR("copy_to_user error (keybox)");
UP(&keybox_dev->sem);
return -EFAULT;
}
}
UP(&keybox_dev->sem);
break;
case HTC_OEMCRYPTO_IDENTIFY_DEVICE:
if ((hmsg.resp_buf == NULL) || ((hmsg.resp_len != DEVICE_ID_LEN) && (hmsg.resp_len != 0xFF))) {
PERR("invalid arguments");
UP(&keybox_dev->sem);
return -EFAULT;
}
#if defined(CONFIG_ARCH_MSM7X30) || defined(CONFIG_ARCH_MSM7X27A)
ret = htc_keybox_read(keybox_dev, htc_device_id, hmsg.resp_len, hmsg.offset);
oem_rapi_client_close();
htc_device_id = keybox_dev->keybox_buf;
#else
ret = secure_access_item(0, ITEM_DEVICE_ID, DEVICE_ID_LEN,
htc_device_id);
#endif
if (ret)
PERR("get device ID failed (%d)", ret);
else {
if (copy_to_user((void __user *)hmsg.resp_buf, htc_device_id, DEVICE_ID_LEN)) {
PERR("copy_to_user error (device ID)");
UP(&keybox_dev->sem);
return -EFAULT;
}
}
UP(&keybox_dev->sem);
break;
case HTC_OEMCRYPTO_GET_RANDOM:
if ((hmsg.resp_buf == NULL) || !hmsg.resp_len) {
PERR("invalid arguments");
UP(&keybox_dev->sem);
return -EFAULT;
}
ptr = kzalloc(hmsg.resp_len, GFP_KERNEL);
if (ptr == NULL) {
PERR("allocate the space for random data failed");
UP(&keybox_dev->sem);
return -1;
}
#if defined(CONFIG_ARCH_MSM7X30) || defined(CONFIG_ARCH_MSM7X27A)
get_random_bytes(ptr, hmsg.resp_len);
PINFO("Data get from random entropy");
#else
get_random_bytes(ptr, hmsg.resp_len);
#endif
if (ret)
PERR("get random data failed (%d)", ret);
else {
if (copy_to_user((void __user *)hmsg.resp_buf, ptr, hmsg.resp_len)) {
PERR("copy_to_user error (random data)");
kfree(ptr);
UP(&keybox_dev->sem);
return -EFAULT;
}
}
UP(&keybox_dev->sem);
kfree(ptr);
break;
case HTC_OEMCRYPTO_IS_KEYBOX_VALID:
#if defined(CONFIG_ARCH_MSM7X30) || defined(CONFIG_ARCH_MSM7X27A)
#else
ret = secure_access_item(0, ITEM_VALIDATE_KEYBOX, WIDEVINE_KEYBOX_LEN, NULL);
#endif
UP(&keybox_dev->sem);
return ret;
default:
UP(&keybox_dev->sem);
PERR("func error");
return -EFAULT;
}
break;
#if !defined(CONFIG_ARCH_MSM7X30) && !defined(CONFIG_ARCH_MSM7X27A)
case HTCDRM_IOCTL_DISCRETIX:
mutex_lock(&dx_lock);
ret = htcdrm_discretix_ioctl(file, command, arg);
mutex_unlock(&dx_lock);
break;
#endif
case HTCDRM_IOCTL_CPRM:
if (copy_from_user(&hmsg, (void __user *)arg, sizeof(hmsg))) {
PERR("copy_from_user error (msg)");
return -EFAULT;
}
if ((hmsg.resp_buf == NULL) || !hmsg.resp_len ) {
PERR("invalid arguments");
return -EFAULT;
}
ret = secure_access_item(0, ITEM_CPRMKEY_DATA, CPRM_KEY_LEN, htc_cprmkey);
if (ret)
PERR("get cprmkey failed (%d)", ret);
else {
if (copy_to_user( (void __user *)hmsg.resp_buf , htc_cprmkey , hmsg.resp_len)) {
PERR("copy_to_user error (cprmkey)");
return -EFAULT;
}
}
break;
case HTCDRM_IOCTL_GDRIVE:
ret = htcdrm_gdrive_ioctl(file, command, arg);
break;
default:
PERR("command error");
return -EFAULT;
}
return ret;
}
#if !defined(CONFIG_ARCH_MSM7X30) && !defined(CONFIG_ARCH_MSM7X27A)
static int htcdrm_open(struct inode *inode, struct file *filp)
{
return 0;
}
static int htcdrm_release(struct inode *inode, struct file *filp)
{
return 0;
}
static int htcdrm_mmap(struct file *filp, struct vm_area_struct *vma)
{
int ret;
long length = vma->vm_end - vma->vm_start;
if (length > discretix_smem_size)
return -EIO;
PDEBUG("htcdrm_mmap %lx", length);
ret = remap_pfn_range(vma,
vma->vm_start,
virt_to_phys((void *)discretix_smem_area) >> PAGE_SHIFT,
length,
vma->vm_page_prot);
return ret;
}
static const struct file_operations htcdrm_fops = {
.unlocked_ioctl = htcdrm_ioctl,
.open = htcdrm_open,
.release = htcdrm_release,
.mmap = htcdrm_mmap,
.owner = THIS_MODULE,
};
#else
static const struct file_operations htcdrm_fops = {
.unlocked_ioctl = htcdrm_ioctl,
.owner = THIS_MODULE,
};
#endif
static int __init htcdrm_init(void)
{
int ret;
htc_device_id = kzalloc(DEVICE_ID_LEN, GFP_KERNEL);
if (htc_device_id == NULL) {
PERR("allocate the space for device ID failed");
return -1;
}
htc_keybox = kzalloc(WIDEVINE_KEYBOX_LEN, GFP_KERNEL);
if (htc_keybox == NULL) {
PERR("allocate the space for keybox failed");
kfree(htc_device_id);
return -1;
}
#if !defined(CONFIG_ARCH_MSM7X30) && !defined(CONFIG_ARCH_MSM7X27A)
discretix_smem_ptr = kzalloc(discretix_smem_size + (2 * PAGE_SIZE), GFP_KERNEL);
if (discretix_smem_ptr == NULL) {
PERR("allocate the space for DX smem failed");
kfree(htc_device_id);
return -1;
}
discretix_smem_area = (unsigned char *)((((unsigned long)discretix_smem_ptr) + PAGE_SIZE - 1) & PAGE_MASK);
discretix_smem_phy = (unsigned char *)virt_to_phys(discretix_smem_area);
#if DX_ALLOC_TZ_HEAP
discretix_tz_heap = kzalloc(DISCRETIX_HEAP_SIZE, GFP_KERNEL);
if (discretix_tz_heap == NULL) {
PERR("allocate the space for discretix heap failed");
kfree(htc_device_id);
return -1;
}
#endif
#if (DX_PRE_ALLOC_BUFFER)
dx_memory_pool_ptr = (unsigned char *)kzalloc(DX_PRE_ALLOC_BUFFER_SIZE, GFP_KERNEL);
if (dx_memory_pool_ptr == NULL) {
PERR("allocate dx_memory_pool failed");
kfree(htc_device_id);
return -1;
}
dx_memory_pool_phy = (unsigned char *)virt_to_phys(dx_memory_pool_ptr);
#endif
#endif
ret = register_chrdev(0, DEVICE_NAME, &htcdrm_fops);
if (ret < 0) {
PERR("register module fail");
kfree(htc_device_id);
kfree(htc_keybox);
return ret;
}
htcdrm_major = ret;
htcdrm_class = class_create(THIS_MODULE, "htcdrm");
device_create(htcdrm_class, NULL, MKDEV(htcdrm_major, 0), NULL, DEVICE_NAME);
#if defined(CONFIG_ARCH_MSM7X30) || defined(CONFIG_ARCH_MSM7X27A)
keybox_dev = kzalloc(sizeof(htc_keybox_dev), GFP_KERNEL);
if (keybox_dev == NULL) {
PERR("allocate space for keybox_dev failed");
kfree(keybox_dev);
return -1;
}
sema_init(&keybox_dev->sem, 1);
#endif
PDEBUG("register module ok");
return 0;
}
static void __exit htcdrm_exit(void)
{
device_destroy(htcdrm_class, MKDEV(htcdrm_major, 0));
class_unregister(htcdrm_class);
class_destroy(htcdrm_class);
unregister_chrdev(htcdrm_major, DEVICE_NAME);
kfree(htc_device_id);
kfree(htc_keybox);
#if defined(CONFIG_ARCH_MSM7X30) || defined(CONFIG_ARCH_MSM7X27A)
kfree(keybox_dev);
#endif
#if !defined(CONFIG_ARCH_MSM7X30) && !defined(CONFIG_ARCH_MSM7X27A)
#if DX_ALLOC_TZ_HEAP
kfree(discretix_tz_heap);
#endif
kfree(discretix_smem_ptr);
#endif
PDEBUG("un-registered module ok");
}
#if DX_ALLOC_TZ_HEAP
module_param(max_ofs, int, S_IRUGO);
MODULE_PARM_DESC(max_ofs, "htcdrm dx max_ofs");
#endif
module_init(htcdrm_init);
module_exit(htcdrm_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Eddic Hsien");