Commit 203270c0 authored by Rob Herring's avatar Rob Herring

drm/panfrost: Add a no execute flag for BO allocations

Executable buffers have an alignment restriction that they can't cross
16MB boundary as the GPU program counter is 24-bits. This restriction is
currently not handled and we just get lucky. As current userspace
assumes all BOs are executable, that has to remain the default. So add a
new PANFROST_BO_NOEXEC flag to allow userspace to indicate which BOs are
not executable.

There is also a restriction that executable buffers cannot start or end
on a 4GB boundary. This is mostly avoided as there is only 4GB of space
currently and the beginning is already blocked out for NULL ptr
detection. Add support to handle this restriction fully regardless of
the current constraints.

For existing userspace, all created BOs remain executable, but the GPU
VA alignment will be increased to the size of the BO. This shouldn't
matter as there is plenty of GPU VA space.

Cc: Tomeu Vizoso <tomeu.vizoso@collabora.com>
Cc: Boris Brezillon <boris.brezillon@collabora.com>
Cc: Robin Murphy <robin.murphy@arm.com>
Reviewed-by: default avatarSteven Price <steven.price@arm.com>
Acked-by: default avatarAlyssa Rosenzweig <alyssa.rosenzweig@collabora.com>
Signed-off-by: default avatarRob Herring <robh@kernel.org>
Link: https://patchwork.freedesktop.org/patch/msgid/20190808222200.13176-6-robh@kernel.org
parent eecbb3cd
...@@ -78,18 +78,19 @@ static int panfrost_ioctl_get_param(struct drm_device *ddev, void *data, struct ...@@ -78,18 +78,19 @@ static int panfrost_ioctl_get_param(struct drm_device *ddev, void *data, struct
static int panfrost_ioctl_create_bo(struct drm_device *dev, void *data, static int panfrost_ioctl_create_bo(struct drm_device *dev, void *data,
struct drm_file *file) struct drm_file *file)
{ {
struct drm_gem_shmem_object *shmem; struct panfrost_gem_object *bo;
struct drm_panfrost_create_bo *args = data; struct drm_panfrost_create_bo *args = data;
if (!args->size || args->flags || args->pad) if (!args->size || args->pad ||
(args->flags & ~PANFROST_BO_NOEXEC))
return -EINVAL; return -EINVAL;
shmem = drm_gem_shmem_create_with_handle(file, dev, args->size, bo = panfrost_gem_create_with_handle(file, dev, args->size, args->flags,
&args->handle); &args->handle);
if (IS_ERR(shmem)) if (IS_ERR(bo))
return PTR_ERR(shmem); return PTR_ERR(bo);
args->offset = to_panfrost_bo(&shmem->base)->node.start << PAGE_SHIFT; args->offset = bo->node.start << PAGE_SHIFT;
return 0; return 0;
} }
...@@ -364,6 +365,32 @@ int panfrost_unstable_ioctl_check(void) ...@@ -364,6 +365,32 @@ int panfrost_unstable_ioctl_check(void)
return 0; return 0;
} }
#define PFN_4G (SZ_4G >> PAGE_SHIFT)
#define PFN_4G_MASK (PFN_4G - 1)
#define PFN_16M (SZ_16M >> PAGE_SHIFT)
static void panfrost_drm_mm_color_adjust(const struct drm_mm_node *node,
unsigned long color,
u64 *start, u64 *end)
{
/* Executable buffers can't start or end on a 4GB boundary */
if (!(color & PANFROST_BO_NOEXEC)) {
u64 next_seg;
if ((*start & PFN_4G_MASK) == 0)
(*start)++;
if ((*end & PFN_4G_MASK) == 0)
(*end)--;
next_seg = ALIGN(*start, PFN_4G);
if (next_seg - *start <= PFN_16M)
*start = next_seg + 1;
*end = min(*end, ALIGN(*start, PFN_4G) - 1);
}
}
static int static int
panfrost_open(struct drm_device *dev, struct drm_file *file) panfrost_open(struct drm_device *dev, struct drm_file *file)
{ {
...@@ -461,6 +488,7 @@ static int panfrost_probe(struct platform_device *pdev) ...@@ -461,6 +488,7 @@ static int panfrost_probe(struct platform_device *pdev)
/* 4G enough for now. can be 48-bit */ /* 4G enough for now. can be 48-bit */
drm_mm_init(&pfdev->mm, SZ_32M >> PAGE_SHIFT, (SZ_4G - SZ_32M) >> PAGE_SHIFT); drm_mm_init(&pfdev->mm, SZ_32M >> PAGE_SHIFT, (SZ_4G - SZ_32M) >> PAGE_SHIFT);
pfdev->mm.color_adjust = panfrost_drm_mm_color_adjust;
pm_runtime_use_autosuspend(pfdev->dev); pm_runtime_use_autosuspend(pfdev->dev);
pm_runtime_set_autosuspend_delay(pfdev->dev, 50); /* ~3 frames */ pm_runtime_set_autosuspend_delay(pfdev->dev, 50); /* ~3 frames */
......
...@@ -31,13 +31,25 @@ static int panfrost_gem_open(struct drm_gem_object *obj, struct drm_file *file_p ...@@ -31,13 +31,25 @@ static int panfrost_gem_open(struct drm_gem_object *obj, struct drm_file *file_p
{ {
int ret; int ret;
size_t size = obj->size; size_t size = obj->size;
u64 align = size >= SZ_2M ? SZ_2M >> PAGE_SHIFT : 0; u64 align;
struct panfrost_gem_object *bo = to_panfrost_bo(obj); struct panfrost_gem_object *bo = to_panfrost_bo(obj);
struct panfrost_device *pfdev = obj->dev->dev_private; struct panfrost_device *pfdev = obj->dev->dev_private;
unsigned long color = bo->noexec ? PANFROST_BO_NOEXEC : 0;
/*
* Executable buffers cannot cross a 16MB boundary as the program
* counter is 24-bits. We assume executable buffers will be less than
* 16MB and aligning executable buffers to their size will avoid
* crossing a 16MB boundary.
*/
if (!bo->noexec)
align = size >> PAGE_SHIFT;
else
align = size >= SZ_2M ? SZ_2M >> PAGE_SHIFT : 0;
spin_lock(&pfdev->mm_lock); spin_lock(&pfdev->mm_lock);
ret = drm_mm_insert_node_generic(&pfdev->mm, &bo->node, ret = drm_mm_insert_node_generic(&pfdev->mm, &bo->node,
size >> PAGE_SHIFT, align, 0, 0); size >> PAGE_SHIFT, align, color, 0);
if (ret) if (ret)
goto out; goto out;
...@@ -98,16 +110,50 @@ struct drm_gem_object *panfrost_gem_create_object(struct drm_device *dev, size_t ...@@ -98,16 +110,50 @@ struct drm_gem_object *panfrost_gem_create_object(struct drm_device *dev, size_t
return &obj->base.base; return &obj->base.base;
} }
struct panfrost_gem_object *
panfrost_gem_create_with_handle(struct drm_file *file_priv,
struct drm_device *dev, size_t size,
u32 flags,
uint32_t *handle)
{
int ret;
struct drm_gem_shmem_object *shmem;
struct panfrost_gem_object *bo;
shmem = drm_gem_shmem_create(dev, size);
if (IS_ERR(shmem))
return ERR_CAST(shmem);
bo = to_panfrost_bo(&shmem->base);
bo->noexec = !!(flags & PANFROST_BO_NOEXEC);
/*
* Allocate an id of idr table where the obj is registered
* and handle has the id what user can see.
*/
ret = drm_gem_handle_create(file_priv, &shmem->base, handle);
/* drop reference from allocate - handle holds it now. */
drm_gem_object_put_unlocked(&shmem->base);
if (ret)
return ERR_PTR(ret);
return bo;
}
struct drm_gem_object * struct drm_gem_object *
panfrost_gem_prime_import_sg_table(struct drm_device *dev, panfrost_gem_prime_import_sg_table(struct drm_device *dev,
struct dma_buf_attachment *attach, struct dma_buf_attachment *attach,
struct sg_table *sgt) struct sg_table *sgt)
{ {
struct drm_gem_object *obj; struct drm_gem_object *obj;
struct panfrost_gem_object *bo;
obj = drm_gem_shmem_prime_import_sg_table(dev, attach, sgt); obj = drm_gem_shmem_prime_import_sg_table(dev, attach, sgt);
if (IS_ERR(obj)) if (IS_ERR(obj))
return ERR_CAST(obj); return ERR_CAST(obj);
bo = to_panfrost_bo(obj);
bo->noexec = true;
return obj; return obj;
} }
...@@ -11,7 +11,8 @@ struct panfrost_gem_object { ...@@ -11,7 +11,8 @@ struct panfrost_gem_object {
struct drm_gem_shmem_object base; struct drm_gem_shmem_object base;
struct drm_mm_node node; struct drm_mm_node node;
bool is_mapped; bool is_mapped :1;
bool noexec :1;
}; };
static inline static inline
...@@ -27,6 +28,12 @@ panfrost_gem_prime_import_sg_table(struct drm_device *dev, ...@@ -27,6 +28,12 @@ panfrost_gem_prime_import_sg_table(struct drm_device *dev,
struct dma_buf_attachment *attach, struct dma_buf_attachment *attach,
struct sg_table *sgt); struct sg_table *sgt);
struct panfrost_gem_object *
panfrost_gem_create_with_handle(struct drm_file *file_priv,
struct drm_device *dev, size_t size,
u32 flags,
uint32_t *handle);
void panfrost_gem_shrinker_init(struct drm_device *dev); void panfrost_gem_shrinker_init(struct drm_device *dev);
void panfrost_gem_shrinker_cleanup(struct drm_device *dev); void panfrost_gem_shrinker_cleanup(struct drm_device *dev);
......
...@@ -190,6 +190,9 @@ int panfrost_mmu_map(struct panfrost_gem_object *bo) ...@@ -190,6 +190,9 @@ int panfrost_mmu_map(struct panfrost_gem_object *bo)
if (WARN_ON(bo->is_mapped)) if (WARN_ON(bo->is_mapped))
return 0; return 0;
if (bo->noexec)
prot |= IOMMU_NOEXEC;
sgt = drm_gem_shmem_get_pages_sgt(obj); sgt = drm_gem_shmem_get_pages_sgt(obj);
if (WARN_ON(IS_ERR(sgt))) if (WARN_ON(IS_ERR(sgt)))
return PTR_ERR(sgt); return PTR_ERR(sgt);
......
...@@ -84,6 +84,8 @@ struct drm_panfrost_wait_bo { ...@@ -84,6 +84,8 @@ struct drm_panfrost_wait_bo {
__s64 timeout_ns; /* absolute */ __s64 timeout_ns; /* absolute */
}; };
#define PANFROST_BO_NOEXEC 1
/** /**
* struct drm_panfrost_create_bo - ioctl argument for creating Panfrost BOs. * struct drm_panfrost_create_bo - ioctl argument for creating Panfrost BOs.
* *
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment