From 00721d4cb879c1d0a7b80375be2f7ce7c4e4a6d8 Mon Sep 17 00:00:00 2001 From: Alex Waterman Date: Fri, 5 Jun 2015 14:04:12 -0700 Subject: gpu: nvgpu: WAR for bad GPFIFO entries from userspace Userspace sometimes sends GPFIFO entries with a zero length. This is a problem when the address of the pushbuffer of zero length is larger than 32 bits. The high bits are interpreted as an opcode and either triggers an operation that should not happen or is trated as invalid. Oddly, this WAR is only necessary on simulation. Change-Id: I8be007c50f46d3e35c6a0e8512be88a8e68ee739 Signed-off-by: Alex Waterman Reviewed-on: http://git-master/r/753379 Reviewed-by: Terje Bergstrom Tested-by: Terje Bergstrom Reviewed-on: http://git-master/r/755814 Reviewed-by: Automatic_Commit_Validation_User --- drivers/gpu/nvgpu/gk20a/channel_gk20a.c | 52 +++++++++++++++++++++++++-------- 1 file changed, 40 insertions(+), 12 deletions(-) (limited to 'drivers/gpu/nvgpu/gk20a') diff --git a/drivers/gpu/nvgpu/gk20a/channel_gk20a.c b/drivers/gpu/nvgpu/gk20a/channel_gk20a.c index 8ad1dcca..2ba160dd 100644 --- a/drivers/gpu/nvgpu/gk20a/channel_gk20a.c +++ b/drivers/gpu/nvgpu/gk20a/channel_gk20a.c @@ -1790,23 +1790,51 @@ int gk20a_submit_channel_gpfifo(struct channel_gk20a *c, start = c->gpfifo.put; end = start + num_entries; - if (end > c->gpfifo.entry_num) { - int length0 = c->gpfifo.entry_num - start; - int length1 = num_entries - length0; + /* + * This WAR is in place for handling invalid simulation GPFIFO entries + * passed to us from userspace. This will be removed once these invalid + * GPFIFO entries are handled in userspace. + */ + if (tegra_platform_is_linsim()) { + int i; + struct gpfifo *gpfifo_entries = c->gpfifo.mem.cpu_va; - memcpy((struct gpfifo *)c->gpfifo.mem.cpu_va + start, gpfifo, - length0 * sizeof(*gpfifo)); + for (i = 0; i < num_entries; i++) { + int index = (start + i) & (c->gpfifo.entry_num - 1); - memcpy((struct gpfifo *)c->gpfifo.mem.cpu_va, gpfifo + length0, - length1 * sizeof(*gpfifo)); + /* Copy the entry... */ + gpfifo_entries[index].entry0 = gpfifo[i].entry0; + gpfifo_entries[index].entry1 = gpfifo[i].entry1; - trace_write_pushbuffer_range(c, gpfifo, length0); - trace_write_pushbuffer_range(c, gpfifo + length0, length1); + /* + * And here's the WAR: if the length is 0 clear the + * opcode field (bottom 8 bits). + */ + if (!pbdma_gp_entry1_length_v(gpfifo[i].entry1)) + gpfifo_entries[index].entry1 &= ~0xff; + + trace_write_pushbuffer_range(c, gpfifo, num_entries); + } } else { - memcpy((struct gpfifo *)c->gpfifo.mem.cpu_va + start, gpfifo, - num_entries * sizeof(*gpfifo)); + if (end > c->gpfifo.entry_num) { + int length0 = c->gpfifo.entry_num - start; + int length1 = num_entries - length0; - trace_write_pushbuffer_range(c, gpfifo, num_entries); + memcpy((struct gpfifo *)c->gpfifo.mem.cpu_va + start, + gpfifo, length0 * sizeof(*gpfifo)); + + memcpy((struct gpfifo *)c->gpfifo.mem.cpu_va, + gpfifo + length0, length1 * sizeof(*gpfifo)); + + trace_write_pushbuffer_range(c, gpfifo, length0); + trace_write_pushbuffer_range(c, gpfifo + length0, + length1); + } else { + memcpy((struct gpfifo *)c->gpfifo.mem.cpu_va + start, + gpfifo, num_entries * sizeof(*gpfifo)); + + trace_write_pushbuffer_range(c, gpfifo, num_entries); + } } c->gpfifo.put = (c->gpfifo.put + num_entries) & (c->gpfifo.entry_num - 1); -- cgit v1.2.2