332 lines
6.9 KiB
C
332 lines
6.9 KiB
C
|
/*
|
||
|
* drivers/soc/samsung/secmem.c
|
||
|
*
|
||
|
* Copyright (c) 2015 Samsung Electronics Co., Ltd.
|
||
|
* http://www.samsung.com
|
||
|
*
|
||
|
* This program is free software; you can redistribute it and/or modify
|
||
|
* it under the terms of the GNU General Public License version 2 as
|
||
|
* published by the Free Software Foundation.
|
||
|
*/
|
||
|
|
||
|
#include <linux/device.h>
|
||
|
#include <linux/uaccess.h>
|
||
|
#include <linux/fs.h>
|
||
|
#include <linux/module.h>
|
||
|
#include <linux/miscdevice.h>
|
||
|
#include <linux/mutex.h>
|
||
|
#include <linux/pm_runtime.h>
|
||
|
#include <linux/sched.h>
|
||
|
#include <linux/slab.h>
|
||
|
#include <linux/mm.h>
|
||
|
#include <linux/dma-mapping.h>
|
||
|
#include <linux/export.h>
|
||
|
#include <linux/pm_qos.h>
|
||
|
#include <linux/dma-contiguous.h>
|
||
|
#include <linux/ion_exynos.h>
|
||
|
#include <linux/smc.h>
|
||
|
#include <linux/dma-buf.h>
|
||
|
|
||
|
#include <asm/memory.h>
|
||
|
#include <asm/cacheflush.h>
|
||
|
|
||
|
#include <soc/samsung/secmem.h>
|
||
|
|
||
|
#define SECMEM_DEV_NAME "s5p-smem"
|
||
|
|
||
|
|
||
|
#define DRM_PROT_VER_CHUNK_BASED_PROT 0
|
||
|
#define DRM_PROT_VER_BUFFER_BASED_PROT 1
|
||
|
|
||
|
struct miscdevice secmem;
|
||
|
struct secmem_crypto_driver_ftn *crypto_driver;
|
||
|
|
||
|
uint32_t instance_count;
|
||
|
|
||
|
#if defined(CONFIG_SOC_EXYNOS5433)
|
||
|
static uint32_t secmem_regions[] = {
|
||
|
ION_EXYNOS_ID_G2D_WFD,
|
||
|
ION_EXYNOS_ID_VIDEO,
|
||
|
ION_EXYNOS_ID_SECTBL,
|
||
|
ION_EXYNOS_ID_MFC_FW,
|
||
|
ION_EXYNOS_ID_MFC_NFW,
|
||
|
};
|
||
|
|
||
|
static char *secmem_regions_name[] = {
|
||
|
"g2d_wfd", /* 0 */
|
||
|
"video", /* 1 */
|
||
|
"sectbl", /* 2 */
|
||
|
"mfc_fw", /* 3 */
|
||
|
"mfc_nfw", /* 4 */
|
||
|
NULL
|
||
|
};
|
||
|
#elif defined(CONFIG_SOC_EXYNOS7420)
|
||
|
static uint32_t secmem_regions[] = {
|
||
|
ION_EXYNOS_ID_G2D_WFD,
|
||
|
ION_EXYNOS_ID_VIDEO,
|
||
|
ION_EXYNOS_ID_VIDEO_EXT,
|
||
|
ION_EXYNOS_ID_MFC_FW,
|
||
|
ION_EXYNOS_ID_MFC_NFW,
|
||
|
};
|
||
|
|
||
|
static char *secmem_regions_name[] = {
|
||
|
"g2d_wfd", /* 0 */
|
||
|
"video", /* 1 */
|
||
|
"video_ext", /* 2 */
|
||
|
"mfc_fw", /* 3 */
|
||
|
"mfc_nfw", /* 4 */
|
||
|
NULL
|
||
|
};
|
||
|
#endif
|
||
|
|
||
|
static bool drm_onoff;
|
||
|
static DEFINE_MUTEX(drm_lock);
|
||
|
static DEFINE_MUTEX(smc_lock);
|
||
|
|
||
|
struct secmem_info {
|
||
|
struct device *dev;
|
||
|
bool drm_enabled;
|
||
|
};
|
||
|
|
||
|
struct protect_info {
|
||
|
uint32_t dev;
|
||
|
uint32_t enable;
|
||
|
};
|
||
|
|
||
|
#define SECMEM_IS_PAGE_ALIGNED(addr) (!((addr) & (~PAGE_MASK)))
|
||
|
|
||
|
int drm_enable_locked(struct secmem_info *info, bool enable)
|
||
|
{
|
||
|
if (drm_onoff == enable) {
|
||
|
pr_err("%s: DRM is already %s\n", __func__, drm_onoff ? "on" : "off");
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
drm_onoff = enable;
|
||
|
/*
|
||
|
* this will only allow this instance to turn drm_off either by
|
||
|
* calling the ioctl or by closing the fd
|
||
|
*/
|
||
|
info->drm_enabled = enable;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int secmem_open(struct inode *inode, struct file *file)
|
||
|
{
|
||
|
struct miscdevice *miscdev = file->private_data;
|
||
|
struct device *dev = miscdev->this_device;
|
||
|
struct secmem_info *info;
|
||
|
|
||
|
info = kzalloc(sizeof(struct secmem_info), GFP_KERNEL);
|
||
|
if (!info)
|
||
|
return -ENOMEM;
|
||
|
|
||
|
info->dev = dev;
|
||
|
file->private_data = info;
|
||
|
|
||
|
mutex_lock(&drm_lock);
|
||
|
instance_count++;
|
||
|
mutex_unlock(&drm_lock);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int secmem_release(struct inode *inode, struct file *file)
|
||
|
{
|
||
|
struct secmem_info *info = file->private_data;
|
||
|
|
||
|
/* disable drm if we were the one to turn it on */
|
||
|
mutex_lock(&drm_lock);
|
||
|
instance_count--;
|
||
|
if (instance_count == 0) {
|
||
|
if (info->drm_enabled) {
|
||
|
int ret;
|
||
|
ret = drm_enable_locked(info, false);
|
||
|
if (ret < 0)
|
||
|
pr_err("fail to lock/unlock drm status. lock = %d\n", false);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
mutex_unlock(&drm_lock);
|
||
|
|
||
|
kfree(info);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static long secmem_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
||
|
{
|
||
|
struct secmem_info *info = filp->private_data;
|
||
|
|
||
|
switch (cmd) {
|
||
|
#if defined(CONFIG_ION) || defined(CONFIG_ION_EXYNOS)
|
||
|
case (uint32_t)SECMEM_IOC_GET_FD_PHYS_ADDR:
|
||
|
{
|
||
|
struct secfd_info fd_info;
|
||
|
struct dma_buf *dmabuf;
|
||
|
struct dma_buf_attachment *attachment;
|
||
|
struct sg_table *sgt;
|
||
|
|
||
|
if (copy_from_user(&fd_info, (int __user *)arg,
|
||
|
sizeof(fd_info)))
|
||
|
return -EFAULT;
|
||
|
|
||
|
dmabuf = dma_buf_get(fd_info.fd);
|
||
|
if (IS_ERR_OR_NULL(dmabuf)) {
|
||
|
pr_err("smem ioctl error(%d)\n", __LINE__);
|
||
|
return -ENOMEM;
|
||
|
}
|
||
|
|
||
|
attachment = dma_buf_attach(dmabuf, info->dev);
|
||
|
if (!attachment) {
|
||
|
pr_err("smem ioctl error(%d)\n", __LINE__);
|
||
|
dma_buf_put(dmabuf);
|
||
|
return -ENOMEM;
|
||
|
}
|
||
|
|
||
|
sgt = dma_buf_map_attachment(attachment, DMA_TO_DEVICE);
|
||
|
if (!sgt) {
|
||
|
pr_err("smem ioctl error(%d)\n", __LINE__);
|
||
|
dma_buf_detach(dmabuf, attachment);
|
||
|
dma_buf_put(dmabuf);
|
||
|
return -ENOMEM;
|
||
|
}
|
||
|
|
||
|
fd_info.phys = sg_phys(sgt->sgl);
|
||
|
pr_debug("%s: physical addr from kernel space = 0x%08x\n",
|
||
|
__func__, (unsigned int)fd_info.phys);
|
||
|
|
||
|
dma_buf_unmap_attachment(attachment, sgt, DMA_TO_DEVICE);
|
||
|
dma_buf_detach(dmabuf, attachment);
|
||
|
dma_buf_put(dmabuf);
|
||
|
|
||
|
if (copy_to_user((void __user *)arg, &fd_info, sizeof(fd_info)))
|
||
|
return -EFAULT;
|
||
|
break;
|
||
|
}
|
||
|
#endif
|
||
|
case (uint32_t)SECMEM_IOC_GET_DRM_ONOFF:
|
||
|
smp_rmb();
|
||
|
if (copy_to_user((void __user *)arg, &drm_onoff, sizeof(bool)))
|
||
|
return -EFAULT;
|
||
|
break;
|
||
|
case (uint32_t)SECMEM_IOC_SET_DRM_ONOFF:
|
||
|
{
|
||
|
int ret, val = 0;
|
||
|
|
||
|
if (copy_from_user(&val, (int __user *)arg, sizeof(int)))
|
||
|
return -EFAULT;
|
||
|
|
||
|
mutex_lock(&drm_lock);
|
||
|
if ((info->drm_enabled && !val) ||
|
||
|
(!info->drm_enabled && val)) {
|
||
|
/*
|
||
|
* 1. if we enabled drm, then disable it
|
||
|
* 2. if we don't already hdrm enabled,
|
||
|
* try to enable it.
|
||
|
*/
|
||
|
ret = drm_enable_locked(info, val);
|
||
|
if (ret < 0)
|
||
|
pr_err("fail to lock/unlock drm status. lock = %d\n", val);
|
||
|
}
|
||
|
mutex_unlock(&drm_lock);
|
||
|
break;
|
||
|
}
|
||
|
case (uint32_t)SECMEM_IOC_GET_CRYPTO_LOCK:
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
case (uint32_t)SECMEM_IOC_RELEASE_CRYPTO_LOCK:
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
case (uint32_t)SECMEM_IOC_SET_TZPC:
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
case (uint32_t)SECMEM_IOC_SET_VIDEO_EXT_PROC:
|
||
|
{
|
||
|
int val, ret;
|
||
|
|
||
|
if (copy_from_user(&val, (int __user *)arg, sizeof(int)))
|
||
|
return -EFAULT;
|
||
|
mutex_lock(&smc_lock);
|
||
|
ret = exynos_smc((uint32_t)(SMC_DRM_VIDEO_PROC), val, 0, 0);
|
||
|
if (ret) {
|
||
|
pr_err("Failed to control VIDEO EXT region protection. prot = %d\n", val);
|
||
|
mutex_unlock(&smc_lock);
|
||
|
return -ENOMEM;
|
||
|
}
|
||
|
|
||
|
mutex_unlock(&smc_lock);
|
||
|
break;
|
||
|
}
|
||
|
case (uint32_t)SECMEM_IOC_GET_DRM_PROT_VER:
|
||
|
{
|
||
|
int val;
|
||
|
|
||
|
val = DRM_PROT_VER_BUFFER_BASED_PROT;
|
||
|
if (copy_to_user((void __user *)arg, &val, sizeof(int)))
|
||
|
return -EFAULT;
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
default:
|
||
|
return -ENOTTY;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
#if 0
|
||
|
void secmem_crypto_register(struct secmem_crypto_driver_ftn *ftn)
|
||
|
{
|
||
|
crypto_driver = ftn;
|
||
|
}
|
||
|
EXPORT_SYMBOL(secmem_crypto_register);
|
||
|
|
||
|
void secmem_crypto_deregister(void)
|
||
|
{
|
||
|
crypto_driver = NULL;
|
||
|
}
|
||
|
EXPORT_SYMBOL(secmem_crypto_deregister);
|
||
|
#endif
|
||
|
|
||
|
static const struct file_operations secmem_fops = {
|
||
|
.owner = THIS_MODULE,
|
||
|
.open = secmem_open,
|
||
|
.release = secmem_release,
|
||
|
.compat_ioctl = secmem_ioctl,
|
||
|
.unlocked_ioctl = secmem_ioctl,
|
||
|
};
|
||
|
|
||
|
struct miscdevice secmem = {
|
||
|
.minor = MISC_DYNAMIC_MINOR,
|
||
|
.name = SECMEM_DEV_NAME,
|
||
|
.fops = &secmem_fops,
|
||
|
};
|
||
|
|
||
|
static int __init secmem_init(void)
|
||
|
{
|
||
|
int ret;
|
||
|
|
||
|
ret = misc_register(&secmem);
|
||
|
if (ret) {
|
||
|
pr_err("%s: SECMEM can't register misc on minor=%d\n",
|
||
|
__func__, MISC_DYNAMIC_MINOR);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
crypto_driver = NULL;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static void __exit secmem_exit(void)
|
||
|
{
|
||
|
misc_deregister(&secmem);
|
||
|
}
|
||
|
|
||
|
module_init(secmem_init);
|
||
|
module_exit(secmem_exit);
|