@@ -49,7 +49,8 @@ static int panfrost_ioctl_create_bo(struct drm_device *dev, void *data,
struct panfrost_gem_object *bo;
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;
bo = panfrost_gem_create_with_handle(file, dev, args->size, args->flags,
@@ -367,6 +368,32 @@ static struct drm_driver panfrost_drm_driver = {
.gem_prime_mmap = drm_gem_prime_mmap,
};
+#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 panfrost_probe(struct platform_device *pdev)
{
struct panfrost_device *pfdev;
@@ -394,6 +421,7 @@ static int panfrost_probe(struct platform_device *pdev)
/* 4G enough for now. can be 48-bit */
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_set_autosuspend_delay(pfdev->dev, 50); /* ~3 frames */
@@ -66,11 +66,23 @@ static int panfrost_gem_map(struct panfrost_device *pfdev, struct panfrost_gem_o
{
int ret;
size_t size = bo->base.base.size;
- u64 align = size >= SZ_2M ? SZ_2M >> PAGE_SHIFT : 0;
+ u64 align;
+ 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);
ret = drm_mm_insert_node_generic(&pfdev->mm, &bo->node,
- size >> PAGE_SHIFT, align, 0, 0);
+ size >> PAGE_SHIFT, align, color, 0);
spin_unlock(&pfdev->mm_lock);
if (ret)
return ret;
@@ -96,6 +108,7 @@ panfrost_gem_create_with_handle(struct drm_file *file_priv,
return ERR_CAST(shmem);
bo = to_panfrost_bo(&shmem->base);
+ bo->noexec = !!(flags & PANFROST_BO_NOEXEC);
ret = panfrost_gem_map(pfdev, bo);
if (ret)
@@ -123,6 +136,7 @@ panfrost_gem_prime_import_sg_table(struct drm_device *dev,
return ERR_CAST(obj);
pobj = to_panfrost_bo(obj);
+ pobj->noexec = true;
ret = panfrost_gem_map(pfdev, pobj);
if (ret)
@@ -11,7 +11,8 @@ struct panfrost_gem_object {
struct drm_gem_shmem_object base;
struct drm_mm_node node;
- bool is_mapped;
+ bool is_mapped :1;
+ bool noexec :1;
};
static inline
@@ -190,6 +190,9 @@ int panfrost_mmu_map(struct panfrost_gem_object *bo)
if (WARN_ON(bo->is_mapped))
return 0;
+ if (bo->noexec)
+ prot |= IOMMU_NOEXEC;
+
sgt = drm_gem_shmem_get_pages_sgt(obj);
if (WARN_ON(IS_ERR(sgt)))
return PTR_ERR(sgt);
@@ -82,6 +82,8 @@ struct drm_panfrost_wait_bo {
__s64 timeout_ns; /* absolute */
};
+#define PANFROST_BO_NOEXEC 1
+
/**
* struct drm_panfrost_create_bo - ioctl argument for creating Panfrost BOs.
*