drm/rockchip: gem: support secure memory

Change-Id: I91dfbbfbf5d13983edfb79585e9beb980566f784
Signed-off-by: Mark Yao <mark.yao@rock-chips.com>
Signed-off-by: Jianqun Xu <xjq@rock-chips.com>
Signed-off-by: Sandy Huang <hjc@rock-chips.com>
This commit is contained in:
Mark Yao
2017-07-25 17:27:03 +08:00
committed by Tao Huang
parent 7730f2ade7
commit 4304170a70
4 changed files with 173 additions and 7 deletions
@@ -9,8 +9,10 @@
#include <linux/dma-buf.h>
#include <linux/dma-mapping.h>
#include <linux/dma-iommu.h>
#include <linux/genalloc.h>
#include <linux/pm_runtime.h>
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/of_graph.h>
#include <linux/of_platform.h>
#include <linux/component.h>
@@ -528,6 +530,46 @@ err_unlock:
drm_modeset_unlock_all(drm);
}
static int rockchip_gem_pool_init(struct drm_device *drm)
{
struct rockchip_drm_private *private = drm->dev_private;
struct device_node *np = drm->dev->of_node;
struct device_node *node;
phys_addr_t start, size;
struct resource res;
int ret;
node = of_parse_phandle(np, "secure-memory-region", 0);
if (!node)
return -ENXIO;
ret = of_address_to_resource(node, 0, &res);
if (ret)
return ret;
start = res.start;
size = resource_size(&res);
if (!size)
return -ENOMEM;
private->secure_buffer_pool = gen_pool_create(PAGE_SHIFT, -1);
if (!private->secure_buffer_pool)
return -ENOMEM;
gen_pool_add(private->secure_buffer_pool, start, size, -1);
return 0;
}
static void rockchip_gem_pool_destroy(struct drm_device *drm)
{
struct rockchip_drm_private *private = drm->dev_private;
if (!private->secure_buffer_pool)
return;
gen_pool_destroy(private->secure_buffer_pool);
}
static int rockchip_drm_bind(struct device *dev)
{
struct drm_device *drm_dev;
@@ -587,12 +629,15 @@ static int rockchip_drm_bind(struct device *dev)
/* init kms poll for handling hpd */
drm_kms_helper_poll_init(drm_dev);
rockchip_gem_pool_init(drm_dev);
ret = drm_dev_register(drm_dev, 0);
if (ret)
goto err_kms_helper_poll_fini;
return 0;
err_kms_helper_poll_fini:
rockchip_gem_pool_destroy(drm_dev);
drm_kms_helper_poll_fini(drm_dev);
rockchip_drm_fbdev_fini(drm_dev);
err_unbind_all:
@@ -611,6 +656,7 @@ static void rockchip_drm_unbind(struct device *dev)
drm_dev_unregister(drm_dev);
rockchip_drm_fbdev_fini(drm_dev);
rockchip_gem_pool_destroy(drm_dev);
drm_kms_helper_poll_fini(drm_dev);
drm_atomic_helper_shutdown(drm_dev);
@@ -165,6 +165,7 @@ struct rockchip_drm_private {
struct drm_fb_helper *fbdev_helper;
struct drm_gem_object *fbdev_bo;
struct iommu_domain *domain;
struct gen_pool *secure_buffer_pool;
struct mutex mm_lock;
struct drm_mm mm;
struct list_head psr_list;
+117 -6
View File
@@ -13,6 +13,12 @@
#include <drm/drm_prime.h>
#include <drm/drm_vma_manager.h>
#include <linux/dma-buf.h>
#include <linux/genalloc.h>
#include <linux/iommu.h>
#include <linux/pagemap.h>
#include <linux/vmalloc.h>
#include "rockchip_drm_drv.h"
#include "rockchip_drm_gem.h"
@@ -287,6 +293,91 @@ static int rockchip_gem_alloc_dma(struct rockchip_gem_object *rk_obj,
return 0;
}
static inline void *drm_calloc_large(size_t nmemb, size_t size)
{
if (size != 0 && nmemb > SIZE_MAX / size)
return NULL;
if (size * nmemb <= PAGE_SIZE)
return kcalloc(nmemb, size, GFP_KERNEL);
return __vmalloc(size * nmemb,
GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO);
}
static inline void drm_free_large(void *ptr)
{
kvfree(ptr);
}
static int rockchip_gem_alloc_secure(struct rockchip_gem_object *rk_obj)
{
struct drm_gem_object *obj = &rk_obj->base;
struct drm_device *drm = obj->dev;
struct rockchip_drm_private *private = drm->dev_private;
unsigned long paddr;
struct sg_table *sgt;
int ret = 0, i;
if (!private->secure_buffer_pool) {
DRM_ERROR("No secure buffer pool found\n");
return -ENOMEM;
}
paddr = gen_pool_alloc(private->secure_buffer_pool, rk_obj->base.size);
if (!paddr) {
DRM_ERROR("failed to allocate secure buffer\n");
return -ENOMEM;
}
rk_obj->dma_handle = paddr;
rk_obj->num_pages = rk_obj->base.size >> PAGE_SHIFT;
rk_obj->pages = drm_calloc_large(rk_obj->num_pages,
sizeof(*rk_obj->pages));
if (!rk_obj->pages) {
DRM_ERROR("failed to allocate pages.\n");
ret = -ENOMEM;
goto err_buf_free;
}
i = 0;
while (i < rk_obj->num_pages) {
rk_obj->pages[i] = phys_to_page(paddr);
paddr += PAGE_SIZE;
i++;
}
sgt = drm_prime_pages_to_sg(obj->dev, rk_obj->pages, rk_obj->num_pages);
if (IS_ERR(sgt)) {
ret = PTR_ERR(sgt);
goto err_free_pages;
}
rk_obj->sgt = sgt;
return 0;
err_free_pages:
drm_free_large(rk_obj->pages);
err_buf_free:
gen_pool_free(private->secure_buffer_pool, paddr, rk_obj->base.size);
return ret;
}
static void rockchip_gem_free_secure(struct rockchip_gem_object *rk_obj)
{
struct drm_gem_object *obj = &rk_obj->base;
struct drm_device *drm = obj->dev;
struct rockchip_drm_private *private = drm->dev_private;
drm_free_large(rk_obj->pages);
sg_free_table(rk_obj->sgt);
kfree(rk_obj->sgt);
gen_pool_free(private->secure_buffer_pool, rk_obj->dma_handle,
rk_obj->base.size);
}
static int rockchip_gem_alloc_buf(struct rockchip_gem_object *rk_obj,
bool alloc_kmap)
{
@@ -294,10 +385,24 @@ static int rockchip_gem_alloc_buf(struct rockchip_gem_object *rk_obj,
struct drm_device *drm = obj->dev;
struct rockchip_drm_private *private = drm->dev_private;
if (private->domain)
return rockchip_gem_alloc_iommu(rk_obj, alloc_kmap);
else
if (!private->domain)
rk_obj->flags |= ROCKCHIP_BO_CONTIG;
if (rk_obj->flags & ROCKCHIP_BO_SECURE) {
rk_obj->buf_type = ROCKCHIP_GEM_BUF_TYPE_SECURE;
rk_obj->flags |= ROCKCHIP_BO_CONTIG;
if (alloc_kmap) {
DRM_ERROR("Not allow alloc secure buffer with kmap\n");
return -EINVAL;
}
return rockchip_gem_alloc_secure(rk_obj);
} else if (rk_obj->flags & ROCKCHIP_BO_CONTIG) {
rk_obj->buf_type = ROCKCHIP_GEM_BUF_TYPE_CMA;
return rockchip_gem_alloc_dma(rk_obj, alloc_kmap);
} else {
rk_obj->buf_type = ROCKCHIP_GEM_BUF_TYPE_SHMEM;
return rockchip_gem_alloc_iommu(rk_obj, alloc_kmap);
}
}
static void rockchip_gem_free_iommu(struct rockchip_gem_object *rk_obj)
@@ -318,7 +423,9 @@ static void rockchip_gem_free_dma(struct rockchip_gem_object *rk_obj)
static void rockchip_gem_free_buf(struct rockchip_gem_object *rk_obj)
{
if (rk_obj->pages)
if (rk_obj->buf_type == ROCKCHIP_GEM_BUF_TYPE_SECURE)
rockchip_gem_free_secure(rk_obj);
else if (rk_obj->pages)
rockchip_gem_free_iommu(rk_obj);
else
rockchip_gem_free_dma(rk_obj);
@@ -359,10 +466,14 @@ static int rockchip_drm_gem_object_mmap(struct drm_gem_object *obj,
*/
vma->vm_flags &= ~VM_PFNMAP;
if (rk_obj->pages)
if (rk_obj->buf_type == ROCKCHIP_GEM_BUF_TYPE_SECURE) {
DRM_ERROR("Disallow mmap for secure buffer\n");
ret = -EINVAL;
} else if (rk_obj->pages) {
ret = rockchip_drm_gem_object_mmap_iommu(obj, vma);
else
} else {
ret = rockchip_drm_gem_object_mmap_dma(obj, vma);
}
if (ret)
drm_gem_vm_close(vma);
+9 -1
View File
@@ -11,12 +11,20 @@
#define to_rockchip_obj(x) container_of(x, struct rockchip_gem_object, base)
enum rockchip_gem_buf_type {
ROCKCHIP_GEM_BUF_TYPE_CMA,
ROCKCHIP_GEM_BUF_TYPE_SHMEM,
ROCKCHIP_GEM_BUF_TYPE_SECURE,
};
struct rockchip_gem_object {
struct drm_gem_object base;
unsigned int flags;
enum rockchip_gem_buf_type buf_type;
void *kvaddr;
dma_addr_t dma_addr;
dma_addr_t dma_addr; /* iova if iommu enable, otherwise physical address */
dma_addr_t dma_handle; /* physical address */
/* Used when IOMMU is disabled */
unsigned long dma_attrs;