diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c index e383cf922eaa..741d8b94218a 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -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); diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h index 40f84971978c..3b2acdbe508b 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h @@ -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; diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c index fccd525f31b4..97a31e46807b 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c @@ -18,8 +18,10 @@ #include #include +#include #include #include +#include #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); diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_gem.h b/drivers/gpu/drm/rockchip/rockchip_drm_gem.h index d591a275de05..5a1bef717b8e 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_gem.h +++ b/drivers/gpu/drm/rockchip/rockchip_drm_gem.h @@ -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 { diff --git a/include/uapi/drm/rockchip_drm.h b/include/uapi/drm/rockchip_drm.h index ae26f788c5d5..62e7fe45168a 100644 --- a/include/uapi/drm/rockchip_drm.h +++ b/include/uapi/drm/rockchip_drm.h @@ -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 };