summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorAlex Waterman <alexw@nvidia.com>2015-06-05 17:04:12 -0400
committerTerje Bergstrom <tbergstrom@nvidia.com>2015-06-11 13:16:25 -0400
commit00721d4cb879c1d0a7b80375be2f7ce7c4e4a6d8 (patch)
tree913001120bd8d6f98378e099b32f092356540e47 /drivers
parent9e83f881b7fd796dd9319a6a88253e4166dbd830 (diff)
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 <alexw@nvidia.com> Reviewed-on: http://git-master/r/753379 Reviewed-by: Terje Bergstrom <tbergstrom@nvidia.com> Tested-by: Terje Bergstrom <tbergstrom@nvidia.com> Reviewed-on: http://git-master/r/755814 Reviewed-by: Automatic_Commit_Validation_User
Diffstat (limited to 'drivers')
-rw-r--r--drivers/gpu/nvgpu/gk20a/channel_gk20a.c52
1 files changed, 40 insertions, 12 deletions
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,
1790 start = c->gpfifo.put; 1790 start = c->gpfifo.put;
1791 end = start + num_entries; 1791 end = start + num_entries;
1792 1792
1793 if (end > c->gpfifo.entry_num) { 1793 /*
1794 int length0 = c->gpfifo.entry_num - start; 1794 * This WAR is in place for handling invalid simulation GPFIFO entries
1795 int length1 = num_entries - length0; 1795 * passed to us from userspace. This will be removed once these invalid
1796 * GPFIFO entries are handled in userspace.
1797 */
1798 if (tegra_platform_is_linsim()) {
1799 int i;
1800 struct gpfifo *gpfifo_entries = c->gpfifo.mem.cpu_va;
1796 1801
1797 memcpy((struct gpfifo *)c->gpfifo.mem.cpu_va + start, gpfifo, 1802 for (i = 0; i < num_entries; i++) {
1798 length0 * sizeof(*gpfifo)); 1803 int index = (start + i) & (c->gpfifo.entry_num - 1);
1799 1804
1800 memcpy((struct gpfifo *)c->gpfifo.mem.cpu_va, gpfifo + length0, 1805 /* Copy the entry... */
1801 length1 * sizeof(*gpfifo)); 1806 gpfifo_entries[index].entry0 = gpfifo[i].entry0;
1807 gpfifo_entries[index].entry1 = gpfifo[i].entry1;
1802 1808
1803 trace_write_pushbuffer_range(c, gpfifo, length0); 1809 /*
1804 trace_write_pushbuffer_range(c, gpfifo + length0, length1); 1810 * And here's the WAR: if the length is 0 clear the
1811 * opcode field (bottom 8 bits).
1812 */
1813 if (!pbdma_gp_entry1_length_v(gpfifo[i].entry1))
1814 gpfifo_entries[index].entry1 &= ~0xff;
1815
1816 trace_write_pushbuffer_range(c, gpfifo, num_entries);
1817 }
1805 } else { 1818 } else {
1806 memcpy((struct gpfifo *)c->gpfifo.mem.cpu_va + start, gpfifo, 1819 if (end > c->gpfifo.entry_num) {
1807 num_entries * sizeof(*gpfifo)); 1820 int length0 = c->gpfifo.entry_num - start;
1821 int length1 = num_entries - length0;
1808 1822
1809 trace_write_pushbuffer_range(c, gpfifo, num_entries); 1823 memcpy((struct gpfifo *)c->gpfifo.mem.cpu_va + start,
1824 gpfifo, length0 * sizeof(*gpfifo));
1825
1826 memcpy((struct gpfifo *)c->gpfifo.mem.cpu_va,
1827 gpfifo + length0, length1 * sizeof(*gpfifo));
1828
1829 trace_write_pushbuffer_range(c, gpfifo, length0);
1830 trace_write_pushbuffer_range(c, gpfifo + length0,
1831 length1);
1832 } else {
1833 memcpy((struct gpfifo *)c->gpfifo.mem.cpu_va + start,
1834 gpfifo, num_entries * sizeof(*gpfifo));
1835
1836 trace_write_pushbuffer_range(c, gpfifo, num_entries);
1837 }
1810 } 1838 }
1811 c->gpfifo.put = (c->gpfifo.put + num_entries) & 1839 c->gpfifo.put = (c->gpfifo.put + num_entries) &
1812 (c->gpfifo.entry_num - 1); 1840 (c->gpfifo.entry_num - 1);