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:
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user