From 12ee4c0c03f3dbd38bd0683ccee9056d1ce9b33a Mon Sep 17 00:00:00 2001 From: Jerome Glisse Date: Fri, 8 Feb 2013 09:55:01 -0500 Subject: [PATCH] drm/radeon: copy userspace cmd to local copy before processing it v3 In some rare case were packet is big enough to go over page boundary we might not have copied yet the userspace data into the local copy resulting in kernel reading garbage data. Without this patch kernel might submit unprocessed/unrelocated cmd to the GPU which might lead to GPU lockup. v2: Make sure we do copy all the page and don't forget some when the packet count dw is bigger than 1 page v3: Rebase patch against Linus master Signed-off-by: Jerome Glisse Cc: stable@kernel.org --- drivers/gpu/drm/radeon/evergreen_cs.c | 35 ++++++++++++++++++++++++++++++++++- drivers/gpu/drm/radeon/r600_cs.c | 19 ++++++++++++++++++- 2 files changed, 52 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/radeon/evergreen_cs.c b/drivers/gpu/drm/radeon/evergreen_cs.c index 7a44566..51ad74a 100644 --- a/drivers/gpu/drm/radeon/evergreen_cs.c +++ b/drivers/gpu/drm/radeon/evergreen_cs.c @@ -1021,7 +1021,7 @@ static int evergreen_cs_packet_parse(struct radeon_cs_parser *p, unsigned idx) { struct radeon_cs_chunk *ib_chunk = &p->chunks[p->chunk_ib_idx]; - uint32_t header; + uint32_t header, i, npages; if (idx >= ib_chunk->length_dw) { DRM_ERROR("Can not parse packet at %d after CS end %d !\n", @@ -1052,6 +1052,11 @@ static int evergreen_cs_packet_parse(struct radeon_cs_parser *p, pkt->idx, pkt->type, pkt->count, ib_chunk->length_dw); return -EINVAL; } + /* make sure we copied packet fully from userspace */ + npages = ((idx + pkt->count + 1) >> 10) - (idx >> 10); + for (i = 1; i <= npages; i++) { + radeon_get_ib_value(p, (idx & 0xfffffc00) + i * 0x400); + } return 0; } @@ -2909,12 +2914,16 @@ int evergreen_dma_cs_parse(struct radeon_cs_parser *p) return -EINVAL; } if (tiled) { + /* make sure we copied packet fully from userspace */ + radeon_get_ib_value(p, idx + 6); dst_offset = ib[idx+1]; dst_offset <<= 8; ib[idx+1] += (u32)(dst_reloc->lobj.gpu_offset >> 8); p->idx += count + 7; } else { + /* make sure we copied packet fully from userspace */ + radeon_get_ib_value(p, idx + 2); dst_offset = ib[idx+1]; dst_offset |= ((u64)(ib[idx+2] & 0xff)) << 32; @@ -2945,6 +2954,8 @@ int evergreen_dma_cs_parse(struct radeon_cs_parser *p) switch (misc) { case 0: /* L2T, frame to fields */ + /* make sure we copied packet fully from userspace */ + radeon_get_ib_value(p, idx + 9); if (idx_value & (1 << 31)) { DRM_ERROR("bad L2T, frame to fields DMA_PACKET_COPY\n"); return -EINVAL; @@ -2983,6 +2994,8 @@ int evergreen_dma_cs_parse(struct radeon_cs_parser *p) break; case 1: /* L2T, T2L partial */ + /* make sure we copied packet fully from userspace */ + radeon_get_ib_value(p, idx + 11); if (p->family < CHIP_CAYMAN) { DRM_ERROR("L2T, T2L Partial is cayman only !\n"); return -EINVAL; @@ -3005,6 +3018,8 @@ int evergreen_dma_cs_parse(struct radeon_cs_parser *p) break; case 3: /* L2T, broadcast */ + /* make sure we copied packet fully from userspace */ + radeon_get_ib_value(p, idx + 9); if (idx_value & (1 << 31)) { DRM_ERROR("bad L2T, broadcast DMA_PACKET_COPY\n"); return -EINVAL; @@ -3043,6 +3058,8 @@ int evergreen_dma_cs_parse(struct radeon_cs_parser *p) break; case 4: /* L2T, T2L */ + /* make sure we copied packet fully from userspace */ + radeon_get_ib_value(p, idx + 8); /* detile bit */ if (idx_value & (1 << 31)) { /* tiled src, linear dst */ @@ -3079,6 +3096,8 @@ int evergreen_dma_cs_parse(struct radeon_cs_parser *p) break; case 5: /* T2T partial */ + /* make sure we copied packet fully from userspace */ + radeon_get_ib_value(p, idx + 12); if (p->family < CHIP_CAYMAN) { DRM_ERROR("L2T, T2L Partial is cayman only !\n"); return -EINVAL; @@ -3089,6 +3108,8 @@ int evergreen_dma_cs_parse(struct radeon_cs_parser *p) break; case 7: /* L2T, broadcast */ + /* make sure we copied packet fully from userspace */ + radeon_get_ib_value(p, idx + 9); if (idx_value & (1 << 31)) { DRM_ERROR("bad L2T, broadcast DMA_PACKET_COPY\n"); return -EINVAL; @@ -3132,6 +3153,8 @@ int evergreen_dma_cs_parse(struct radeon_cs_parser *p) } else { switch (misc) { case 0: + /* make sure we copied packet fully from userspace */ + radeon_get_ib_value(p, idx + 8); /* detile bit */ if (idx_value & (1 << 31)) { /* tiled src, linear dst */ @@ -3176,6 +3199,8 @@ int evergreen_dma_cs_parse(struct radeon_cs_parser *p) switch (misc) { case 0: /* L2L, byte */ + /* make sure we copied packet fully from userspace */ + radeon_get_ib_value(p, idx + 4); src_offset = ib[idx+2]; src_offset |= ((u64)(ib[idx+4] & 0xff)) << 32; dst_offset = ib[idx+1]; @@ -3198,6 +3223,8 @@ int evergreen_dma_cs_parse(struct radeon_cs_parser *p) break; case 1: /* L2L, partial */ + /* make sure we copied packet fully from userspace */ + radeon_get_ib_value(p, idx + 8); if (p->family < CHIP_CAYMAN) { DRM_ERROR("L2L Partial is cayman only !\n"); return -EINVAL; @@ -3211,6 +3238,8 @@ int evergreen_dma_cs_parse(struct radeon_cs_parser *p) break; case 4: /* L2L, dw, broadcast */ + /* make sure we copied packet fully from userspace */ + radeon_get_ib_value(p, idx + 6); r = r600_dma_cs_next_reloc(p, &dst2_reloc); if (r) { DRM_ERROR("bad L2L, dw, broadcast DMA_PACKET_COPY\n"); @@ -3251,6 +3280,8 @@ int evergreen_dma_cs_parse(struct radeon_cs_parser *p) } } else { /* L2L, dw */ + /* make sure we copied packet fully from userspace */ + radeon_get_ib_value(p, idx + 4); src_offset = ib[idx+2]; src_offset |= ((u64)(ib[idx+4] & 0xff)) << 32; dst_offset = ib[idx+1]; @@ -3274,6 +3305,8 @@ int evergreen_dma_cs_parse(struct radeon_cs_parser *p) } break; case DMA_PACKET_CONSTANT_FILL: + /* make sure we copied packet fully from userspace */ + radeon_get_ib_value(p, idx + 3); r = r600_dma_cs_next_reloc(p, &dst_reloc); if (r) { DRM_ERROR("bad DMA_PACKET_CONSTANT_FILL\n"); diff --git a/drivers/gpu/drm/radeon/r600_cs.c b/drivers/gpu/drm/radeon/r600_cs.c index 69ec24a..d36f9e6 100644 --- a/drivers/gpu/drm/radeon/r600_cs.c +++ b/drivers/gpu/drm/radeon/r600_cs.c @@ -796,7 +796,7 @@ static int r600_cs_packet_parse(struct radeon_cs_parser *p, unsigned idx) { struct radeon_cs_chunk *ib_chunk = &p->chunks[p->chunk_ib_idx]; - uint32_t header; + uint32_t header, i, npages; if (idx >= ib_chunk->length_dw) { DRM_ERROR("Can not parse packet at %d after CS end %d !\n", @@ -827,6 +827,11 @@ static int r600_cs_packet_parse(struct radeon_cs_parser *p, pkt->idx, pkt->type, pkt->count, ib_chunk->length_dw); return -EINVAL; } + /* make sure we copied packet fully from userspace */ + npages = ((idx + pkt->count + 1) >> 10) - (idx >> 10); + for (i = 1; i <= npages; i++) { + radeon_get_ib_value(p, (idx & 0xfffffc00) + i * 0x400); + } return 0; } @@ -2623,12 +2628,16 @@ int r600_dma_cs_parse(struct radeon_cs_parser *p) return -EINVAL; } if (tiled) { + /* make sure we copied packet fully from userspace */ + radeon_get_ib_value(p, idx + 4); dst_offset = ib[idx+1]; dst_offset <<= 8; ib[idx+1] += (u32)(dst_reloc->lobj.gpu_offset >> 8); p->idx += count + 5; } else { + /* make sure we copied packet fully from userspace */ + radeon_get_ib_value(p, idx + 2); dst_offset = ib[idx+1]; dst_offset |= ((u64)(ib[idx+2] & 0xff)) << 32; @@ -2654,6 +2663,8 @@ int r600_dma_cs_parse(struct radeon_cs_parser *p) return -EINVAL; } if (tiled) { + /* make sure we copied packet fully from userspace */ + radeon_get_ib_value(p, idx + 6); idx_value = radeon_get_ib_value(p, idx + 2); /* detile bit */ if (idx_value & (1 << 31)) { @@ -2680,6 +2691,8 @@ int r600_dma_cs_parse(struct radeon_cs_parser *p) p->idx += 7; } else { if (p->family >= CHIP_RV770) { + /* make sure we copied packet fully from userspace */ + radeon_get_ib_value(p, idx + 4); src_offset = ib[idx+2]; src_offset |= ((u64)(ib[idx+4] & 0xff)) << 32; dst_offset = ib[idx+1]; @@ -2691,6 +2704,8 @@ int r600_dma_cs_parse(struct radeon_cs_parser *p) ib[idx+4] += upper_32_bits(src_reloc->lobj.gpu_offset) & 0xff; p->idx += 5; } else { + /* make sure we copied packet fully from userspace */ + radeon_get_ib_value(p, idx + 3); src_offset = ib[idx+2]; src_offset |= ((u64)(ib[idx+3] & 0xff)) << 32; dst_offset = ib[idx+1]; @@ -2715,6 +2730,8 @@ int r600_dma_cs_parse(struct radeon_cs_parser *p) } break; case DMA_PACKET_CONSTANT_FILL: + /* make sure we copied packet fully from userspace */ + radeon_get_ib_value(p, idx + 3); if (p->family < CHIP_RV770) { DRM_ERROR("Constant Fill is 7xx only !\n"); return -EINVAL; -- 1.7.11.7