drm/rockchip: gem: support secure memory

Change-Id: I91dfbbfbf5d13983edfb79585e9beb980566f784
Signed-off-by: Mark Yao <mark.yao@rock-chips.com>
Signed-off-by: jay.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 f89f356895
commit d0ca05b8a4
5 changed files with 152 additions and 5 deletions

View File

@ -22,6 +22,7 @@
#include <drm/drm_of.h>
#include <linux/dma-mapping.h>
#include <linux/dma-iommu.h>
#include <linux/genalloc.h>
#include <linux/pm_runtime.h>
#include <linux/memblock.h>
#include <linux/module.h>
@ -864,6 +865,46 @@ static void rockchip_iommu_cleanup(struct drm_device *drm_dev)
iommu_domain_free(private->domain);
}
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;
@ -919,6 +960,7 @@ 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);
#ifndef MODULE
show_loader_logo(drm_dev);
#endif
@ -929,6 +971,7 @@ static int rockchip_drm_bind(struct device *dev)
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:
@ -950,6 +993,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);

View File

@ -67,6 +67,7 @@ struct rockchip_drm_private {
struct drm_gem_object *fbdev_bo;
struct drm_atomic_state *state;
struct iommu_domain *domain;
struct gen_pool *secure_buffer_pool;
struct mutex mm_lock;
struct drm_mm mm;
struct list_head psr_list;

View File

@ -18,8 +18,10 @@
#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"
@ -293,6 +295,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, PAGE_KERNEL);
}
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(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)
{
@ -303,10 +390,17 @@ static int rockchip_gem_alloc_buf(struct rockchip_gem_object *rk_obj,
if (!private->domain)
rk_obj->flags |= ROCKCHIP_BO_CONTIG;
if (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);
@ -331,7 +425,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);
@ -408,10 +504,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);

View File

@ -20,6 +20,7 @@
enum rockchip_gem_buf_type {
ROCKCHIP_GEM_BUF_TYPE_CMA,
ROCKCHIP_GEM_BUF_TYPE_SHMEM,
ROCKCHIP_GEM_BUF_TYPE_SECURE,
};
struct rockchip_gem_object {

View File

@ -25,6 +25,7 @@ enum drm_rockchip_gem_mem_type {
ROCKCHIP_BO_CACHABLE = 1 << 1,
/* write-combine mapping. */
ROCKCHIP_BO_WC = 1 << 2,
ROCKCHIP_BO_SECURE = 1 << 3,
ROCKCHIP_BO_MASK = ROCKCHIP_BO_CONTIG | ROCKCHIP_BO_CACHABLE |
ROCKCHIP_BO_WC
};