Skip to content

Commit

Permalink
gpu: host1x: Validate syncpoints in the firewall
Browse files Browse the repository at this point in the history
Validate number of syncpoint increments in the stream, check whether the last
command of the stream is a syncpoint increment awaiting for the HW operation
completion and check whether syncpoints ID belongs to the jobs client.

Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
  • Loading branch information
digetx committed Aug 18, 2017
1 parent 889df15 commit c8b6c82
Show file tree
Hide file tree
Showing 6 changed files with 88 additions and 4 deletions.
1 change: 1 addition & 0 deletions drivers/gpu/drm/tegra/drm.c
Original file line number Diff line number Diff line change
Expand Up @@ -596,6 +596,7 @@ int tegra_drm_submit(struct tegra_drm_context *context,

job->is_addr_reg = context->client->ops->is_addr_reg;
job->is_valid_class = context->client->ops->is_valid_class;
job->is_valid_syncpt_id = context->client->ops->is_valid_syncpt_id;
job->syncpt_incrs = syncpt.incrs;
job->syncpt_id = syncpt.id;
job->timeout = 10000;
Expand Down
1 change: 1 addition & 0 deletions drivers/gpu/drm/tegra/drm.h
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ struct tegra_drm_client_ops {
void (*close_channel)(struct tegra_drm_context *context);
int (*is_addr_reg)(struct device *dev, u32 class, u32 offset);
int (*is_valid_class)(u32 class);
int (*is_valid_syncpt_id)(struct device *dev, u32 syncpt_id);
int (*submit)(struct tegra_drm_context *context,
struct drm_tegra_submit *args, struct drm_device *drm,
struct drm_file *file);
Expand Down
17 changes: 17 additions & 0 deletions drivers/gpu/drm/tegra/gr2d.c
Original file line number Diff line number Diff line change
Expand Up @@ -115,11 +115,28 @@ static int gr2d_is_valid_class(u32 class)
class == HOST1X_CLASS_GR2D_SB);
}

static int gr2d_is_valid_syncpt_id(struct device *dev, u32 syncpt_id)
{
struct gr2d *gr2d = dev_get_drvdata(dev);
struct host1x_syncpt *sp;
unsigned int i;

for (i = 0; i < gr2d->client.base.num_syncpts; i++) {
sp = gr2d->client.base.syncpts[i];

if (host1x_syncpt_id(sp) == syncpt_id)
return 1;
}

return 0;
}

static const struct tegra_drm_client_ops gr2d_ops = {
.open_channel = gr2d_open_channel,
.close_channel = gr2d_close_channel,
.is_addr_reg = gr2d_is_addr_reg,
.is_valid_class = gr2d_is_valid_class,
.is_valid_syncpt_id = gr2d_is_valid_syncpt_id,
.submit = tegra_drm_submit,
};

Expand Down
17 changes: 17 additions & 0 deletions drivers/gpu/drm/tegra/gr3d.c
Original file line number Diff line number Diff line change
Expand Up @@ -118,10 +118,27 @@ static int gr3d_is_addr_reg(struct device *dev, u32 class, u32 offset)
return 0;
}

static int gr3d_is_valid_syncpt_id(struct device *dev, u32 syncpt_id)
{
struct gr3d *gr3d = dev_get_drvdata(dev);
struct host1x_syncpt *sp;
unsigned int i;

for (i = 0; i < gr3d->client.base.num_syncpts; i++) {
sp = gr3d->client.base.syncpts[i];

if (host1x_syncpt_id(sp) == syncpt_id)
return 1;
}

return 0;
}

static const struct tegra_drm_client_ops gr3d_ops = {
.open_channel = gr3d_open_channel,
.close_channel = gr3d_close_channel,
.is_addr_reg = gr3d_is_addr_reg,
.is_valid_syncpt_id = gr3d_is_valid_syncpt_id,
.submit = tegra_drm_submit,
};

Expand Down
53 changes: 49 additions & 4 deletions drivers/gpu/host1x/job.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#include "job.h"
#include "syncpt.h"

#define HOST1X_INCR_SYNCPT_OFFSET 0x0
#define HOST1X_WAIT_SYNCPT_OFFSET 0x8

struct host1x_job *host1x_job_alloc(struct host1x_channel *ch,
Expand Down Expand Up @@ -373,11 +374,16 @@ struct host1x_firewall {
struct host1x_bo *cmdbuf;
unsigned int offset;

unsigned int syncpt_incrs;

u32 *cmdbuf_base;
u32 words;
u32 class;
u32 reg;
u32 mask;
u32 count;

bool last;
};

static int check_register(struct host1x_firewall *fw, unsigned long offset,
Expand All @@ -400,6 +406,38 @@ static int check_register(struct host1x_firewall *fw, unsigned long offset,
fw->reloc++;
}

if (offset == HOST1X_INCR_SYNCPT_OFFSET) {
u32 word = fw->cmdbuf_base[fw->offset];
unsigned int cond = (word >> 8) & 0xff;
unsigned int syncpt_id = word & 0xff;

if (!fw->syncpt_incrs)
return -EINVAL;

/*
* Syncpoint increment must be the last command of the
* stream in order to ensure that all outstanding HW
* operations complete before jobs fence would signal.
*/
if (fw->syncpt_incrs == 1) {
if (!fw->last)
return -EINVAL;

if (fw->words != (immediate ? 0 : 1))
return -EINVAL;

/* condition must be OP_DONE */
if (cond != 1)
return -EINVAL;
}

if (fw->job->is_valid_syncpt_id)
if (!fw->job->is_valid_syncpt_id(fw->dev, syncpt_id))
return -EINVAL;

fw->syncpt_incrs--;
}

if (offset == HOST1X_WAIT_SYNCPT_OFFSET) {
if (fw->class != HOST1X_CLASS_HOST1X)
return -EINVAL;
Expand Down Expand Up @@ -499,17 +537,23 @@ static int check_nonincr(struct host1x_firewall *fw)
return 0;
}

static int validate(struct host1x_firewall *fw, struct host1x_job_gather *g)
static int validate(struct host1x_firewall *fw, struct host1x_job_gather *g,
bool last_gather)
{
u32 *cmdbuf_base = (u32 *)fw->job->gather_copy_mapped +
(g->offset / sizeof(u32));
u32 job_class = fw->class;
int err = 0;

fw->cmdbuf_base = cmdbuf_base;
fw->last = last_gather;
fw->words = g->words;
fw->cmdbuf = g->bo;
fw->offset = 0;

if (!fw->syncpt_incrs)
return -EINVAL;

while (fw->words && !err) {
u32 word = cmdbuf_base[fw->offset];
u32 opcode = (word & 0xf0000000) >> 28;
Expand Down Expand Up @@ -585,6 +629,7 @@ static inline int copy_gathers(struct host1x_job *job, struct device *dev)
fw.num_relocs = job->num_relocs;
fw.waitchk = job->waitchk;
fw.num_waitchks = job->num_waitchk;
fw.syncpt_incrs = job->syncpt_incrs;
fw.class = job->class;

for (i = 0; i < job->num_gathers; i++) {
Expand Down Expand Up @@ -625,14 +670,14 @@ static inline int copy_gathers(struct host1x_job *job, struct device *dev)
g->offset = offset;

/* Validate the job */
if (validate(&fw, g))
if (validate(&fw, g, i == job->num_gathers - 1))
return -EINVAL;

offset += g->words * sizeof(u32);
}

/* No relocs and waitchks should remain at this point */
if (fw.num_relocs || fw.num_waitchks)
/* No relocs, waitchks and syncpts should remain at this point */
if (fw.num_relocs || fw.num_waitchks || fw.syncpt_incrs)
return -EINVAL;

return 0;
Expand Down
3 changes: 3 additions & 0 deletions include/linux/host1x.h
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,9 @@ struct host1x_job {
/* Check if class belongs to the unit */
int (*is_valid_class)(u32 class);

/* Check if syncpoint ID belongs to the client */
int (*is_valid_syncpt_id)(struct device *dev, u32 syncpt_id);

/* Request a SETCLASS to this class */
u32 class;

Expand Down

0 comments on commit c8b6c82

Please sign in to comment.