diff options
author | Dmitry Osipenko <digetx@gmail.com> | 2017-06-14 19:18:37 -0400 |
---|---|---|
committer | Thierry Reding <treding@nvidia.com> | 2017-06-15 08:23:50 -0400 |
commit | 0f563a4bf66e5182f0882efee398f7e6bc0bb1be (patch) | |
tree | 3197816e7ddebd5d8360c6aae439d20a956b6a95 | |
parent | ef81624994845a50e90c6a0ff57f17d1c96bff78 (diff) |
gpu: host1x: Forbid unrelated SETCLASS opcode in the firewall
Several channels could be made to write the same unit concurrently via
the SETCLASS opcode, trusting userspace is a bad idea. It should be
possible to drop the per-client channel reservation and add a per-unit
locking by inserting MLOCK's to the command stream to re-allow the
SETCLASS opcode, but it will be much more work. Let's forbid the
unit-unrelated class changes for now.
Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
Reviewed-by: Erik Faye-Lund <kusmabite@gmail.com>
Reviewed-by: Mikko Perttunen <mperttunen@nvidia.com>
Signed-off-by: Thierry Reding <treding@nvidia.com>
-rw-r--r-- | drivers/gpu/drm/tegra/drm.c | 1 | ||||
-rw-r--r-- | drivers/gpu/drm/tegra/drm.h | 1 | ||||
-rw-r--r-- | drivers/gpu/drm/tegra/gr2d.c | 7 | ||||
-rw-r--r-- | drivers/gpu/host1x/job.c | 24 | ||||
-rw-r--r-- | include/linux/host1x.h | 3 |
5 files changed, 32 insertions, 4 deletions
diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c index b44f1eddb570..4a46ba846a0f 100644 --- a/drivers/gpu/drm/tegra/drm.c +++ b/drivers/gpu/drm/tegra/drm.c | |||
@@ -532,6 +532,7 @@ int tegra_drm_submit(struct tegra_drm_context *context, | |||
532 | } | 532 | } |
533 | 533 | ||
534 | job->is_addr_reg = context->client->ops->is_addr_reg; | 534 | job->is_addr_reg = context->client->ops->is_addr_reg; |
535 | job->is_valid_class = context->client->ops->is_valid_class; | ||
535 | job->syncpt_incrs = syncpt.incrs; | 536 | job->syncpt_incrs = syncpt.incrs; |
536 | job->syncpt_id = syncpt.id; | 537 | job->syncpt_id = syncpt.id; |
537 | job->timeout = 10000; | 538 | job->timeout = 10000; |
diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h index 85aa2e3d9d4e..6d6da01282f3 100644 --- a/drivers/gpu/drm/tegra/drm.h +++ b/drivers/gpu/drm/tegra/drm.h | |||
@@ -83,6 +83,7 @@ struct tegra_drm_client_ops { | |||
83 | struct tegra_drm_context *context); | 83 | struct tegra_drm_context *context); |
84 | void (*close_channel)(struct tegra_drm_context *context); | 84 | void (*close_channel)(struct tegra_drm_context *context); |
85 | int (*is_addr_reg)(struct device *dev, u32 class, u32 offset); | 85 | int (*is_addr_reg)(struct device *dev, u32 class, u32 offset); |
86 | int (*is_valid_class)(u32 class); | ||
86 | int (*submit)(struct tegra_drm_context *context, | 87 | int (*submit)(struct tegra_drm_context *context, |
87 | struct drm_tegra_submit *args, struct drm_device *drm, | 88 | struct drm_tegra_submit *args, struct drm_device *drm, |
88 | struct drm_file *file); | 89 | struct drm_file *file); |
diff --git a/drivers/gpu/drm/tegra/gr2d.c b/drivers/gpu/drm/tegra/gr2d.c index 02cd3e37a6ec..fbe0b8b25b42 100644 --- a/drivers/gpu/drm/tegra/gr2d.c +++ b/drivers/gpu/drm/tegra/gr2d.c | |||
@@ -109,10 +109,17 @@ static int gr2d_is_addr_reg(struct device *dev, u32 class, u32 offset) | |||
109 | return 0; | 109 | return 0; |
110 | } | 110 | } |
111 | 111 | ||
112 | static int gr2d_is_valid_class(u32 class) | ||
113 | { | ||
114 | return (class == HOST1X_CLASS_GR2D || | ||
115 | class == HOST1X_CLASS_GR2D_SB); | ||
116 | } | ||
117 | |||
112 | static const struct tegra_drm_client_ops gr2d_ops = { | 118 | static const struct tegra_drm_client_ops gr2d_ops = { |
113 | .open_channel = gr2d_open_channel, | 119 | .open_channel = gr2d_open_channel, |
114 | .close_channel = gr2d_close_channel, | 120 | .close_channel = gr2d_close_channel, |
115 | .is_addr_reg = gr2d_is_addr_reg, | 121 | .is_addr_reg = gr2d_is_addr_reg, |
122 | .is_valid_class = gr2d_is_valid_class, | ||
116 | .submit = tegra_drm_submit, | 123 | .submit = tegra_drm_submit, |
117 | }; | 124 | }; |
118 | 125 | ||
diff --git a/drivers/gpu/host1x/job.c b/drivers/gpu/host1x/job.c index 54230ec4f81e..ef746f7afb88 100644 --- a/drivers/gpu/host1x/job.c +++ b/drivers/gpu/host1x/job.c | |||
@@ -356,6 +356,9 @@ struct host1x_firewall { | |||
356 | 356 | ||
357 | static int check_register(struct host1x_firewall *fw, unsigned long offset) | 357 | static int check_register(struct host1x_firewall *fw, unsigned long offset) |
358 | { | 358 | { |
359 | if (!fw->job->is_addr_reg) | ||
360 | return 0; | ||
361 | |||
359 | if (fw->job->is_addr_reg(fw->dev, fw->class, offset)) { | 362 | if (fw->job->is_addr_reg(fw->dev, fw->class, offset)) { |
360 | if (!fw->num_relocs) | 363 | if (!fw->num_relocs) |
361 | return -EINVAL; | 364 | return -EINVAL; |
@@ -370,6 +373,19 @@ static int check_register(struct host1x_firewall *fw, unsigned long offset) | |||
370 | return 0; | 373 | return 0; |
371 | } | 374 | } |
372 | 375 | ||
376 | static int check_class(struct host1x_firewall *fw, u32 class) | ||
377 | { | ||
378 | if (!fw->job->is_valid_class) { | ||
379 | if (fw->class != class) | ||
380 | return -EINVAL; | ||
381 | } else { | ||
382 | if (!fw->job->is_valid_class(fw->class)) | ||
383 | return -EINVAL; | ||
384 | } | ||
385 | |||
386 | return 0; | ||
387 | } | ||
388 | |||
373 | static int check_mask(struct host1x_firewall *fw) | 389 | static int check_mask(struct host1x_firewall *fw) |
374 | { | 390 | { |
375 | u32 mask = fw->mask; | 391 | u32 mask = fw->mask; |
@@ -443,11 +459,9 @@ static int validate(struct host1x_firewall *fw, struct host1x_job_gather *g) | |||
443 | { | 459 | { |
444 | u32 *cmdbuf_base = (u32 *)fw->job->gather_copy_mapped + | 460 | u32 *cmdbuf_base = (u32 *)fw->job->gather_copy_mapped + |
445 | (g->offset / sizeof(u32)); | 461 | (g->offset / sizeof(u32)); |
462 | u32 job_class = fw->class; | ||
446 | int err = 0; | 463 | int err = 0; |
447 | 464 | ||
448 | if (!fw->job->is_addr_reg) | ||
449 | return 0; | ||
450 | |||
451 | fw->words = g->words; | 465 | fw->words = g->words; |
452 | fw->cmdbuf = g->bo; | 466 | fw->cmdbuf = g->bo; |
453 | fw->offset = 0; | 467 | fw->offset = 0; |
@@ -467,7 +481,9 @@ static int validate(struct host1x_firewall *fw, struct host1x_job_gather *g) | |||
467 | fw->class = word >> 6 & 0x3ff; | 481 | fw->class = word >> 6 & 0x3ff; |
468 | fw->mask = word & 0x3f; | 482 | fw->mask = word & 0x3f; |
469 | fw->reg = word >> 16 & 0xfff; | 483 | fw->reg = word >> 16 & 0xfff; |
470 | err = check_mask(fw); | 484 | err = check_class(fw, job_class); |
485 | if (!err) | ||
486 | err = check_mask(fw); | ||
471 | if (err) | 487 | if (err) |
472 | goto out; | 488 | goto out; |
473 | break; | 489 | break; |
diff --git a/include/linux/host1x.h b/include/linux/host1x.h index ba0b245da732..b5358f855d9e 100644 --- a/include/linux/host1x.h +++ b/include/linux/host1x.h | |||
@@ -251,6 +251,9 @@ struct host1x_job { | |||
251 | /* Check if register is marked as an address reg */ | 251 | /* Check if register is marked as an address reg */ |
252 | int (*is_addr_reg)(struct device *dev, u32 reg, u32 class); | 252 | int (*is_addr_reg)(struct device *dev, u32 reg, u32 class); |
253 | 253 | ||
254 | /* Check if class belongs to the unit */ | ||
255 | int (*is_valid_class)(u32 class); | ||
256 | |||
254 | /* Request a SETCLASS to this class */ | 257 | /* Request a SETCLASS to this class */ |
255 | u32 class; | 258 | u32 class; |
256 | 259 | ||