diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c index fe4a872a..176a2c44 100644 --- a/drivers/gpu/drm/tegra/drm.c +++ b/drivers/gpu/drm/tegra/drm.c @@ -325,9 +325,6 @@ int tegra_drm_submit(struct tegra_drm_context *context, args->fence = job->syncpt_end; fail: - if (sp) - host1x_syncpt_put(sp); - while (num_refs--) drm_gem_object_put(refs[num_refs]); @@ -731,6 +728,7 @@ static int tegra_gem_get_flags(struct drm_device *drm, void *data, #endif static const struct drm_ioctl_desc tegra_drm_ioctls[] = { +#ifdef CONFIG_DRM_TEGRA_STAGING DRM_IOCTL_DEF_DRV(TEGRA_CHANNEL_OPEN, tegra_drm_ioctl_channel_open, DRM_RENDER_ALLOW), DRM_IOCTL_DEF_DRV(TEGRA_CHANNEL_CLOSE, tegra_drm_ioctl_channel_close, @@ -745,7 +743,6 @@ static const struct drm_ioctl_desc tegra_drm_ioctls[] = { DRM_RENDER_ALLOW), DRM_IOCTL_DEF_DRV(TEGRA_GEM_MMAP, tegra_drm_ioctl_gem_mmap, DRM_RENDER_ALLOW), -#ifdef CONFIG_DRM_TEGRA_STAGING DRM_IOCTL_DEF_DRV(TEGRA_GEM_CREATE_LEGACY, tegra_gem_create, DRM_RENDER_ALLOW), DRM_IOCTL_DEF_DRV(TEGRA_GEM_MMAP_LEGACY, tegra_gem_mmap, DRM_RENDER_ALLOW), diff --git a/drivers/gpu/drm/tegra/uapi/firewall.c b/drivers/gpu/drm/tegra/uapi/firewall.c index a9c5b71b..57427c2d 100644 --- a/drivers/gpu/drm/tegra/uapi/firewall.c +++ b/drivers/gpu/drm/tegra/uapi/firewall.c @@ -12,6 +12,7 @@ struct tegra_drm_firewall { u32 *data; u32 pos; u32 end; + u32 class; }; static int fw_next(struct tegra_drm_firewall *fw, u32 *word) @@ -51,8 +52,8 @@ static int fw_check_reg(struct tegra_drm_firewall *fw, u32 offset) if (!fw->client->ops->is_addr_reg) return 0; - is_addr = fw->client->ops->is_addr_reg( - fw->client->base.dev, fw->client->base.class, offset); + is_addr = fw->client->ops->is_addr_reg(fw->client->base.dev, fw->class, + offset); if (!is_addr) return 0; @@ -97,14 +98,25 @@ static int fw_check_regs_imm(struct tegra_drm_firewall *fw, u32 offset) { bool is_addr; - is_addr = fw->client->ops->is_addr_reg(fw->client->base.dev, - fw->client->base.class, offset); + is_addr = fw->client->ops->is_addr_reg(fw->client->base.dev, fw->class, + offset); if (is_addr) return -EINVAL; return 0; } +static int fw_check_class(struct tegra_drm_firewall *fw, u32 class) +{ + if (!fw->client->ops->is_valid_class) + return -EINVAL; + + if (!fw->client->ops->is_valid_class(class)) + return -EINVAL; + + return 0; +} + enum { HOST1X_OPCODE_SETCLASS = 0x00, HOST1X_OPCODE_INCR = 0x01, @@ -124,7 +136,8 @@ enum { }; int tegra_drm_fw_validate(struct tegra_drm_client *client, u32 *data, u32 start, - u32 words, struct tegra_drm_submit_data *submit) + u32 words, struct tegra_drm_submit_data *submit, + u32 *job_class) { struct tegra_drm_firewall fw = { .submit = submit, @@ -132,13 +145,14 @@ int tegra_drm_fw_validate(struct tegra_drm_client *client, u32 *data, u32 start, .data = data, .pos = start, .end = start+words, + .class = *job_class, }; bool payload_valid = false; u32 payload; int err; while (fw.pos != fw.end) { - u32 word, opcode, offset, count, mask; + u32 word, opcode, offset, count, mask, class; err = fw_next(&fw, &word); if (err) @@ -147,6 +161,16 @@ int tegra_drm_fw_validate(struct tegra_drm_client *client, u32 *data, u32 start, opcode = (word & 0xf0000000) >> 28; switch (opcode) { + case HOST1X_OPCODE_SETCLASS: + offset = word >> 16 & 0xfff; + mask = word & 0x3f; + class = (word >> 6) & 0x3ff; + err = fw_check_class(&fw, class); + fw.class = class; + *job_class = class; + if (!err) + err = fw_check_regs_mask(&fw, offset, mask); + break; case HOST1X_OPCODE_INCR: offset = (word >> 16) & 0xfff; count = word & 0xffff; diff --git a/drivers/gpu/drm/tegra/uapi/submit.c b/drivers/gpu/drm/tegra/uapi/submit.c index e5b89f8a..c9ca266c 100644 --- a/drivers/gpu/drm/tegra/uapi/submit.c +++ b/drivers/gpu/drm/tegra/uapi/submit.c @@ -44,6 +44,9 @@ static void *alloc_copy_user_array(void __user *from, size_t count, size_t size) if (check_mul_overflow(count, size, ©_len)) return ERR_PTR(-EINVAL); + if (copy_len > 0x4000) + return ERR_PTR(-E2BIG); + data = kvmalloc(copy_len, GFP_KERNEL); if (!data) return ERR_PTR(-ENOMEM); @@ -110,8 +113,10 @@ static int submit_write_reloc(struct gather_bo *bo, dma_addr_t iova = mapping->iova + buf->reloc.target_offset; u32 written_ptr = (u32)(iova >> buf->reloc.shift); +#ifdef CONFIG_ARM64 if (buf->flags & DRM_TEGRA_SUBMIT_BUF_RELOC_BLOCKLINEAR) written_ptr |= BIT(39); +#endif if (buf->reloc.gather_offset_words >= bo->gather_data_words) return -EINVAL; @@ -219,7 +224,8 @@ static int submit_job_add_gather(struct host1x_job *job, struct tegra_drm_channel_ctx *ctx, struct drm_tegra_submit_cmd_gather_uptr *cmd, struct gather_bo *bo, u32 *offset, - struct tegra_drm_submit_data *job_data) + struct tegra_drm_submit_data *job_data, + u32 *class) { u32 next_offset; @@ -237,7 +243,7 @@ static int submit_job_add_gather(struct host1x_job *job, return -EINVAL; if (tegra_drm_fw_validate(ctx->client, bo->gather_data, *offset, - cmd->words, job_data)) + cmd->words, job_data, class)) return -EINVAL; host1x_job_add_gather(job, &bo->base, cmd->words, *offset * 4); @@ -254,10 +260,13 @@ static int submit_create_job(struct drm_device *drm, struct host1x_job **pjob, struct tegra_drm_submit_data *job_data) { struct drm_tegra_submit_cmd *cmds; - u32 i, gather_offset = 0; + u32 i, gather_offset = 0, class; struct host1x_job *job; int err; + /* Set initial class for firewall. */ + class = ctx->client->base.class; + cmds = alloc_copy_user_array(u64_to_user_ptr(args->cmds_ptr), args->num_cmds, sizeof(*cmds)); if (IS_ERR(cmds)) @@ -283,7 +292,7 @@ static int submit_create_job(struct drm_device *drm, struct host1x_job **pjob, if (cmd->type == DRM_TEGRA_SUBMIT_CMD_GATHER_UPTR) { err = submit_job_add_gather(job, ctx, &cmd->gather_uptr, bo, &gather_offset, - job_data); + job_data, &class); if (err) goto free_job; } else if (cmd->type == DRM_TEGRA_SUBMIT_CMD_WAIT_SYNCPT) { diff --git a/drivers/gpu/drm/tegra/uapi/submit.h b/drivers/gpu/drm/tegra/uapi/submit.h index 0e51627e..cf6a2f0a 100644 --- a/drivers/gpu/drm/tegra/uapi/submit.h +++ b/drivers/gpu/drm/tegra/uapi/submit.h @@ -15,6 +15,7 @@ struct tegra_drm_submit_data { }; int tegra_drm_fw_validate(struct tegra_drm_client *client, u32 *data, u32 start, - u32 words, struct tegra_drm_submit_data *submit); + u32 words, struct tegra_drm_submit_data *submit, + u32 *job_class); #endif diff --git a/drivers/gpu/drm/tegra/uapi/uapi.c b/drivers/gpu/drm/tegra/uapi/uapi.c index c4eef29b..e8201d67 100644 --- a/drivers/gpu/drm/tegra/uapi/uapi.c +++ b/drivers/gpu/drm/tegra/uapi/uapi.c @@ -215,9 +215,10 @@ int tegra_drm_ioctl_channel_map(struct drm_device *drm, void *data, /* TODO only map the requested part */ mapping->iova = sg_dma_address(mapping->sgt->sgl); - mapping->iova_end = mapping->iova + gem->size; } + mapping->iova_end = mapping->iova + gem->size; + mutex_unlock(&fpriv->lock); err = xa_alloc(&ctx->mappings, &mapping_id, mapping,