static int evergreen_packet3_check()

in drivers/gpu/drm/radeon/evergreen_cs.c [1792:2562]


static int evergreen_packet3_check(struct radeon_cs_parser *p,
				   struct radeon_cs_packet *pkt)
{
	struct radeon_cs_reloc *reloc;
	struct evergreen_cs_track *track;
	volatile u32 *ib;
	unsigned idx;
	unsigned i;
	unsigned start_reg, end_reg, reg;
	int r;
	u32 idx_value;

	track = (struct evergreen_cs_track *)p->track;
	ib = p->ib.ptr;
	idx = pkt->idx + 1;
	idx_value = radeon_get_ib_value(p, idx);

	switch (pkt->opcode) {
	case PACKET3_SET_PREDICATION:
	{
		int pred_op;
		int tmp;
		uint64_t offset;

		if (pkt->count != 1) {
			DRM_ERROR("bad SET PREDICATION\n");
			return -EINVAL;
		}

		tmp = radeon_get_ib_value(p, idx + 1);
		pred_op = (tmp >> 16) & 0x7;

		/* for the clear predicate operation */
		if (pred_op == 0)
			return 0;

		if (pred_op > 2) {
			DRM_ERROR("bad SET PREDICATION operation %d\n", pred_op);
			return -EINVAL;
		}

		r = radeon_cs_packet_next_reloc(p, &reloc, 0);
		if (r) {
			DRM_ERROR("bad SET PREDICATION\n");
			return -EINVAL;
		}

		offset = reloc->gpu_offset +
		         (idx_value & 0xfffffff0) +
		         ((u64)(tmp & 0xff) << 32);

		ib[idx + 0] = offset;
		ib[idx + 1] = (tmp & 0xffffff00) | (upper_32_bits(offset) & 0xff);
	}
	break;
	case PACKET3_CONTEXT_CONTROL:
		if (pkt->count != 1) {
			DRM_ERROR("bad CONTEXT_CONTROL\n");
			return -EINVAL;
		}
		break;
	case PACKET3_INDEX_TYPE:
	case PACKET3_NUM_INSTANCES:
	case PACKET3_CLEAR_STATE:
		if (pkt->count) {
			DRM_ERROR("bad INDEX_TYPE/NUM_INSTANCES/CLEAR_STATE\n");
			return -EINVAL;
		}
		break;
	case CAYMAN_PACKET3_DEALLOC_STATE:
		if (p->rdev->family < CHIP_CAYMAN) {
			DRM_ERROR("bad PACKET3_DEALLOC_STATE\n");
			return -EINVAL;
		}
		if (pkt->count) {
			DRM_ERROR("bad INDEX_TYPE/NUM_INSTANCES/CLEAR_STATE\n");
			return -EINVAL;
		}
		break;
	case PACKET3_INDEX_BASE:
	{
		uint64_t offset;

		if (pkt->count != 1) {
			DRM_ERROR("bad INDEX_BASE\n");
			return -EINVAL;
		}
		r = radeon_cs_packet_next_reloc(p, &reloc, 0);
		if (r) {
			DRM_ERROR("bad INDEX_BASE\n");
			return -EINVAL;
		}

		offset = reloc->gpu_offset +
		         idx_value +
		         ((u64)(radeon_get_ib_value(p, idx+1) & 0xff) << 32);

		ib[idx+0] = offset;
		ib[idx+1] = upper_32_bits(offset) & 0xff;

		r = evergreen_cs_track_check(p);
		if (r) {
			dev_warn(p->dev, "%s:%d invalid cmd stream\n", __func__, __LINE__);
			return r;
		}
		break;
	}
	case PACKET3_DRAW_INDEX:
	{
		uint64_t offset;
		if (pkt->count != 3) {
			DRM_ERROR("bad DRAW_INDEX\n");
			return -EINVAL;
		}
		r = radeon_cs_packet_next_reloc(p, &reloc, 0);
		if (r) {
			DRM_ERROR("bad DRAW_INDEX\n");
			return -EINVAL;
		}

		offset = reloc->gpu_offset +
		         idx_value +
		         ((u64)(radeon_get_ib_value(p, idx+1) & 0xff) << 32);

		ib[idx+0] = offset;
		ib[idx+1] = upper_32_bits(offset) & 0xff;

		r = evergreen_cs_track_check(p);
		if (r) {
			dev_warn(p->dev, "%s:%d invalid cmd stream\n", __func__, __LINE__);
			return r;
		}
		break;
	}
	case PACKET3_DRAW_INDEX_2:
	{
		uint64_t offset;

		if (pkt->count != 4) {
			DRM_ERROR("bad DRAW_INDEX_2\n");
			return -EINVAL;
		}
		r = radeon_cs_packet_next_reloc(p, &reloc, 0);
		if (r) {
			DRM_ERROR("bad DRAW_INDEX_2\n");
			return -EINVAL;
		}

		offset = reloc->gpu_offset +
		         radeon_get_ib_value(p, idx+1) +
		         ((u64)(radeon_get_ib_value(p, idx+2) & 0xff) << 32);

		ib[idx+1] = offset;
		ib[idx+2] = upper_32_bits(offset) & 0xff;

		r = evergreen_cs_track_check(p);
		if (r) {
			dev_warn(p->dev, "%s:%d invalid cmd stream\n", __func__, __LINE__);
			return r;
		}
		break;
	}
	case PACKET3_DRAW_INDEX_AUTO:
		if (pkt->count != 1) {
			DRM_ERROR("bad DRAW_INDEX_AUTO\n");
			return -EINVAL;
		}
		r = evergreen_cs_track_check(p);
		if (r) {
			dev_warn(p->dev, "%s:%d invalid cmd stream %d\n", __func__, __LINE__, idx);
			return r;
		}
		break;
	case PACKET3_DRAW_INDEX_MULTI_AUTO:
		if (pkt->count != 2) {
			DRM_ERROR("bad DRAW_INDEX_MULTI_AUTO\n");
			return -EINVAL;
		}
		r = evergreen_cs_track_check(p);
		if (r) {
			dev_warn(p->dev, "%s:%d invalid cmd stream %d\n", __func__, __LINE__, idx);
			return r;
		}
		break;
	case PACKET3_DRAW_INDEX_IMMD:
		if (pkt->count < 2) {
			DRM_ERROR("bad DRAW_INDEX_IMMD\n");
			return -EINVAL;
		}
		r = evergreen_cs_track_check(p);
		if (r) {
			dev_warn(p->dev, "%s:%d invalid cmd stream\n", __func__, __LINE__);
			return r;
		}
		break;
	case PACKET3_DRAW_INDEX_OFFSET:
		if (pkt->count != 2) {
			DRM_ERROR("bad DRAW_INDEX_OFFSET\n");
			return -EINVAL;
		}
		r = evergreen_cs_track_check(p);
		if (r) {
			dev_warn(p->dev, "%s:%d invalid cmd stream\n", __func__, __LINE__);
			return r;
		}
		break;
	case PACKET3_DRAW_INDEX_OFFSET_2:
		if (pkt->count != 3) {
			DRM_ERROR("bad DRAW_INDEX_OFFSET_2\n");
			return -EINVAL;
		}
		r = evergreen_cs_track_check(p);
		if (r) {
			dev_warn(p->dev, "%s:%d invalid cmd stream\n", __func__, __LINE__);
			return r;
		}
		break;
	case PACKET3_DISPATCH_DIRECT:
		if (pkt->count != 3) {
			DRM_ERROR("bad DISPATCH_DIRECT\n");
			return -EINVAL;
		}
		r = evergreen_cs_track_check(p);
		if (r) {
			dev_warn(p->dev, "%s:%d invalid cmd stream %d\n", __func__, __LINE__, idx);
			return r;
		}
		break;
	case PACKET3_DISPATCH_INDIRECT:
		if (pkt->count != 1) {
			DRM_ERROR("bad DISPATCH_INDIRECT\n");
			return -EINVAL;
		}
		r = radeon_cs_packet_next_reloc(p, &reloc, 0);
		if (r) {
			DRM_ERROR("bad DISPATCH_INDIRECT\n");
			return -EINVAL;
		}
		ib[idx+0] = idx_value + (u32)(reloc->gpu_offset & 0xffffffff);
		r = evergreen_cs_track_check(p);
		if (r) {
			dev_warn(p->dev, "%s:%d invalid cmd stream\n", __func__, __LINE__);
			return r;
		}
		break;
	case PACKET3_WAIT_REG_MEM:
		if (pkt->count != 5) {
			DRM_ERROR("bad WAIT_REG_MEM\n");
			return -EINVAL;
		}
		/* bit 4 is reg (0) or mem (1) */
		if (idx_value & 0x10) {
			uint64_t offset;

			r = radeon_cs_packet_next_reloc(p, &reloc, 0);
			if (r) {
				DRM_ERROR("bad WAIT_REG_MEM\n");
				return -EINVAL;
			}

			offset = reloc->gpu_offset +
			         (radeon_get_ib_value(p, idx+1) & 0xfffffffc) +
			         ((u64)(radeon_get_ib_value(p, idx+2) & 0xff) << 32);

			ib[idx+1] = (ib[idx+1] & 0x3) | (offset & 0xfffffffc);
			ib[idx+2] = upper_32_bits(offset) & 0xff;
		} else if (idx_value & 0x100) {
			DRM_ERROR("cannot use PFP on REG wait\n");
			return -EINVAL;
		}
		break;
	case PACKET3_CP_DMA:
	{
		u32 command, size, info;
		u64 offset, tmp;
		if (pkt->count != 4) {
			DRM_ERROR("bad CP DMA\n");
			return -EINVAL;
		}
		command = radeon_get_ib_value(p, idx+4);
		size = command & 0x1fffff;
		info = radeon_get_ib_value(p, idx+1);
		if ((((info & 0x60000000) >> 29) != 0) || /* src = GDS or DATA */
		    (((info & 0x00300000) >> 20) != 0) || /* dst = GDS */
		    ((((info & 0x00300000) >> 20) == 0) &&
		     (command & PACKET3_CP_DMA_CMD_DAS)) || /* dst = register */
		    ((((info & 0x60000000) >> 29) == 0) &&
		     (command & PACKET3_CP_DMA_CMD_SAS))) { /* src = register */
			/* non mem to mem copies requires dw aligned count */
			if (size % 4) {
				DRM_ERROR("CP DMA command requires dw count alignment\n");
				return -EINVAL;
			}
		}
		if (command & PACKET3_CP_DMA_CMD_SAS) {
			/* src address space is register */
			/* GDS is ok */
			if (((info & 0x60000000) >> 29) != 1) {
				DRM_ERROR("CP DMA SAS not supported\n");
				return -EINVAL;
			}
		} else {
			if (command & PACKET3_CP_DMA_CMD_SAIC) {
				DRM_ERROR("CP DMA SAIC only supported for registers\n");
				return -EINVAL;
			}
			/* src address space is memory */
			if (((info & 0x60000000) >> 29) == 0) {
				r = radeon_cs_packet_next_reloc(p, &reloc, 0);
				if (r) {
					DRM_ERROR("bad CP DMA SRC\n");
					return -EINVAL;
				}

				tmp = radeon_get_ib_value(p, idx) +
					((u64)(radeon_get_ib_value(p, idx+1) & 0xff) << 32);

				offset = reloc->gpu_offset + tmp;

				if ((tmp + size) > radeon_bo_size(reloc->robj)) {
					dev_warn(p->dev, "CP DMA src buffer too small (%llu %lu)\n",
						 tmp + size, radeon_bo_size(reloc->robj));
					return -EINVAL;
				}

				ib[idx] = offset;
				ib[idx+1] = (ib[idx+1] & 0xffffff00) | (upper_32_bits(offset) & 0xff);
			} else if (((info & 0x60000000) >> 29) != 2) {
				DRM_ERROR("bad CP DMA SRC_SEL\n");
				return -EINVAL;
			}
		}
		if (command & PACKET3_CP_DMA_CMD_DAS) {
			/* dst address space is register */
			/* GDS is ok */
			if (((info & 0x00300000) >> 20) != 1) {
				DRM_ERROR("CP DMA DAS not supported\n");
				return -EINVAL;
			}
		} else {
			/* dst address space is memory */
			if (command & PACKET3_CP_DMA_CMD_DAIC) {
				DRM_ERROR("CP DMA DAIC only supported for registers\n");
				return -EINVAL;
			}
			if (((info & 0x00300000) >> 20) == 0) {
				r = radeon_cs_packet_next_reloc(p, &reloc, 0);
				if (r) {
					DRM_ERROR("bad CP DMA DST\n");
					return -EINVAL;
				}

				tmp = radeon_get_ib_value(p, idx+2) +
					((u64)(radeon_get_ib_value(p, idx+3) & 0xff) << 32);

				offset = reloc->gpu_offset + tmp;

				if ((tmp + size) > radeon_bo_size(reloc->robj)) {
					dev_warn(p->dev, "CP DMA dst buffer too small (%llu %lu)\n",
						 tmp + size, radeon_bo_size(reloc->robj));
					return -EINVAL;
				}

				ib[idx+2] = offset;
				ib[idx+3] = upper_32_bits(offset) & 0xff;
			} else {
				DRM_ERROR("bad CP DMA DST_SEL\n");
				return -EINVAL;
			}
		}
		break;
	}
	case PACKET3_SURFACE_SYNC:
		if (pkt->count != 3) {
			DRM_ERROR("bad SURFACE_SYNC\n");
			return -EINVAL;
		}
		/* 0xffffffff/0x0 is flush all cache flag */
		if (radeon_get_ib_value(p, idx + 1) != 0xffffffff ||
		    radeon_get_ib_value(p, idx + 2) != 0) {
			r = radeon_cs_packet_next_reloc(p, &reloc, 0);
			if (r) {
				DRM_ERROR("bad SURFACE_SYNC\n");
				return -EINVAL;
			}
			ib[idx+2] += (u32)((reloc->gpu_offset >> 8) & 0xffffffff);
		}
		break;
	case PACKET3_EVENT_WRITE:
		if (pkt->count != 2 && pkt->count != 0) {
			DRM_ERROR("bad EVENT_WRITE\n");
			return -EINVAL;
		}
		if (pkt->count) {
			uint64_t offset;

			r = radeon_cs_packet_next_reloc(p, &reloc, 0);
			if (r) {
				DRM_ERROR("bad EVENT_WRITE\n");
				return -EINVAL;
			}
			offset = reloc->gpu_offset +
			         (radeon_get_ib_value(p, idx+1) & 0xfffffff8) +
			         ((u64)(radeon_get_ib_value(p, idx+2) & 0xff) << 32);

			ib[idx+1] = offset & 0xfffffff8;
			ib[idx+2] = upper_32_bits(offset) & 0xff;
		}
		break;
	case PACKET3_EVENT_WRITE_EOP:
	{
		uint64_t offset;

		if (pkt->count != 4) {
			DRM_ERROR("bad EVENT_WRITE_EOP\n");
			return -EINVAL;
		}
		r = radeon_cs_packet_next_reloc(p, &reloc, 0);
		if (r) {
			DRM_ERROR("bad EVENT_WRITE_EOP\n");
			return -EINVAL;
		}

		offset = reloc->gpu_offset +
		         (radeon_get_ib_value(p, idx+1) & 0xfffffffc) +
		         ((u64)(radeon_get_ib_value(p, idx+2) & 0xff) << 32);

		ib[idx+1] = offset & 0xfffffffc;
		ib[idx+2] = (ib[idx+2] & 0xffffff00) | (upper_32_bits(offset) & 0xff);
		break;
	}
	case PACKET3_EVENT_WRITE_EOS:
	{
		uint64_t offset;

		if (pkt->count != 3) {
			DRM_ERROR("bad EVENT_WRITE_EOS\n");
			return -EINVAL;
		}
		r = radeon_cs_packet_next_reloc(p, &reloc, 0);
		if (r) {
			DRM_ERROR("bad EVENT_WRITE_EOS\n");
			return -EINVAL;
		}

		offset = reloc->gpu_offset +
		         (radeon_get_ib_value(p, idx+1) & 0xfffffffc) +
		         ((u64)(radeon_get_ib_value(p, idx+2) & 0xff) << 32);

		ib[idx+1] = offset & 0xfffffffc;
		ib[idx+2] = (ib[idx+2] & 0xffffff00) | (upper_32_bits(offset) & 0xff);
		break;
	}
	case PACKET3_SET_CONFIG_REG:
		start_reg = (idx_value << 2) + PACKET3_SET_CONFIG_REG_START;
		end_reg = 4 * pkt->count + start_reg - 4;
		if ((start_reg < PACKET3_SET_CONFIG_REG_START) ||
		    (start_reg >= PACKET3_SET_CONFIG_REG_END) ||
		    (end_reg >= PACKET3_SET_CONFIG_REG_END)) {
			DRM_ERROR("bad PACKET3_SET_CONFIG_REG\n");
			return -EINVAL;
		}
		for (i = 0; i < pkt->count; i++) {
			reg = start_reg + (4 * i);
			r = evergreen_cs_check_reg(p, reg, idx+1+i);
			if (r)
				return r;
		}
		break;
	case PACKET3_SET_CONTEXT_REG:
		start_reg = (idx_value << 2) + PACKET3_SET_CONTEXT_REG_START;
		end_reg = 4 * pkt->count + start_reg - 4;
		if ((start_reg < PACKET3_SET_CONTEXT_REG_START) ||
		    (start_reg >= PACKET3_SET_CONTEXT_REG_END) ||
		    (end_reg >= PACKET3_SET_CONTEXT_REG_END)) {
			DRM_ERROR("bad PACKET3_SET_CONTEXT_REG\n");
			return -EINVAL;
		}
		for (i = 0; i < pkt->count; i++) {
			reg = start_reg + (4 * i);
			r = evergreen_cs_check_reg(p, reg, idx+1+i);
			if (r)
				return r;
		}
		break;
	case PACKET3_SET_RESOURCE:
		if (pkt->count % 8) {
			DRM_ERROR("bad SET_RESOURCE\n");
			return -EINVAL;
		}
		start_reg = (idx_value << 2) + PACKET3_SET_RESOURCE_START;
		end_reg = 4 * pkt->count + start_reg - 4;
		if ((start_reg < PACKET3_SET_RESOURCE_START) ||
		    (start_reg >= PACKET3_SET_RESOURCE_END) ||
		    (end_reg >= PACKET3_SET_RESOURCE_END)) {
			DRM_ERROR("bad SET_RESOURCE\n");
			return -EINVAL;
		}
		for (i = 0; i < (pkt->count / 8); i++) {
			struct radeon_bo *texture, *mipmap;
			u32 toffset, moffset;
			u32 size, offset, mip_address, tex_dim;

			switch (G__SQ_CONSTANT_TYPE(radeon_get_ib_value(p, idx+1+(i*8)+7))) {
			case SQ_TEX_VTX_VALID_TEXTURE:
				/* tex base */
				r = radeon_cs_packet_next_reloc(p, &reloc, 0);
				if (r) {
					DRM_ERROR("bad SET_RESOURCE (tex)\n");
					return -EINVAL;
				}
				if (!(p->cs_flags & RADEON_CS_KEEP_TILING_FLAGS)) {
					ib[idx+1+(i*8)+1] |=
						TEX_ARRAY_MODE(evergreen_cs_get_aray_mode(reloc->tiling_flags));
					if (reloc->tiling_flags & RADEON_TILING_MACRO) {
						unsigned bankw, bankh, mtaspect, tile_split;

						evergreen_tiling_fields(reloc->tiling_flags,
									&bankw, &bankh, &mtaspect,
									&tile_split);
						ib[idx+1+(i*8)+6] |= TEX_TILE_SPLIT(tile_split);
						ib[idx+1+(i*8)+7] |=
							TEX_BANK_WIDTH(bankw) |
							TEX_BANK_HEIGHT(bankh) |
							MACRO_TILE_ASPECT(mtaspect) |
							TEX_NUM_BANKS(evergreen_cs_get_num_banks(track->nbanks));
					}
				}
				texture = reloc->robj;
				toffset = (u32)((reloc->gpu_offset >> 8) & 0xffffffff);

				/* tex mip base */
				tex_dim = ib[idx+1+(i*8)+0] & 0x7;
				mip_address = ib[idx+1+(i*8)+3];

				if ((tex_dim == SQ_TEX_DIM_2D_MSAA || tex_dim == SQ_TEX_DIM_2D_ARRAY_MSAA) &&
				    !mip_address &&
				    !radeon_cs_packet_next_is_pkt3_nop(p)) {
					/* MIP_ADDRESS should point to FMASK for an MSAA texture.
					 * It should be 0 if FMASK is disabled. */
					moffset = 0;
					mipmap = NULL;
				} else {
					r = radeon_cs_packet_next_reloc(p, &reloc, 0);
					if (r) {
						DRM_ERROR("bad SET_RESOURCE (tex)\n");
						return -EINVAL;
					}
					moffset = (u32)((reloc->gpu_offset >> 8) & 0xffffffff);
					mipmap = reloc->robj;
				}

				r = evergreen_cs_track_validate_texture(p, texture, mipmap, idx+1+(i*8));
				if (r)
					return r;
				ib[idx+1+(i*8)+2] += toffset;
				ib[idx+1+(i*8)+3] += moffset;
				break;
			case SQ_TEX_VTX_VALID_BUFFER:
			{
				uint64_t offset64;
				/* vtx base */
				r = radeon_cs_packet_next_reloc(p, &reloc, 0);
				if (r) {
					DRM_ERROR("bad SET_RESOURCE (vtx)\n");
					return -EINVAL;
				}
				offset = radeon_get_ib_value(p, idx+1+(i*8)+0);
				size = radeon_get_ib_value(p, idx+1+(i*8)+1);
				if (p->rdev && (size + offset) > radeon_bo_size(reloc->robj)) {
					/* force size to size of the buffer */
					dev_warn(p->dev, "vbo resource seems too big for the bo\n");
					ib[idx+1+(i*8)+1] = radeon_bo_size(reloc->robj) - offset;
				}

				offset64 = reloc->gpu_offset + offset;
				ib[idx+1+(i*8)+0] = offset64;
				ib[idx+1+(i*8)+2] = (ib[idx+1+(i*8)+2] & 0xffffff00) |
						    (upper_32_bits(offset64) & 0xff);
				break;
			}
			case SQ_TEX_VTX_INVALID_TEXTURE:
			case SQ_TEX_VTX_INVALID_BUFFER:
			default:
				DRM_ERROR("bad SET_RESOURCE\n");
				return -EINVAL;
			}
		}
		break;
	case PACKET3_SET_ALU_CONST:
		/* XXX fix me ALU const buffers only */
		break;
	case PACKET3_SET_BOOL_CONST:
		start_reg = (idx_value << 2) + PACKET3_SET_BOOL_CONST_START;
		end_reg = 4 * pkt->count + start_reg - 4;
		if ((start_reg < PACKET3_SET_BOOL_CONST_START) ||
		    (start_reg >= PACKET3_SET_BOOL_CONST_END) ||
		    (end_reg >= PACKET3_SET_BOOL_CONST_END)) {
			DRM_ERROR("bad SET_BOOL_CONST\n");
			return -EINVAL;
		}
		break;
	case PACKET3_SET_LOOP_CONST:
		start_reg = (idx_value << 2) + PACKET3_SET_LOOP_CONST_START;
		end_reg = 4 * pkt->count + start_reg - 4;
		if ((start_reg < PACKET3_SET_LOOP_CONST_START) ||
		    (start_reg >= PACKET3_SET_LOOP_CONST_END) ||
		    (end_reg >= PACKET3_SET_LOOP_CONST_END)) {
			DRM_ERROR("bad SET_LOOP_CONST\n");
			return -EINVAL;
		}
		break;
	case PACKET3_SET_CTL_CONST:
		start_reg = (idx_value << 2) + PACKET3_SET_CTL_CONST_START;
		end_reg = 4 * pkt->count + start_reg - 4;
		if ((start_reg < PACKET3_SET_CTL_CONST_START) ||
		    (start_reg >= PACKET3_SET_CTL_CONST_END) ||
		    (end_reg >= PACKET3_SET_CTL_CONST_END)) {
			DRM_ERROR("bad SET_CTL_CONST\n");
			return -EINVAL;
		}
		break;
	case PACKET3_SET_SAMPLER:
		if (pkt->count % 3) {
			DRM_ERROR("bad SET_SAMPLER\n");
			return -EINVAL;
		}
		start_reg = (idx_value << 2) + PACKET3_SET_SAMPLER_START;
		end_reg = 4 * pkt->count + start_reg - 4;
		if ((start_reg < PACKET3_SET_SAMPLER_START) ||
		    (start_reg >= PACKET3_SET_SAMPLER_END) ||
		    (end_reg >= PACKET3_SET_SAMPLER_END)) {
			DRM_ERROR("bad SET_SAMPLER\n");
			return -EINVAL;
		}
		break;
	case PACKET3_STRMOUT_BUFFER_UPDATE:
		if (pkt->count != 4) {
			DRM_ERROR("bad STRMOUT_BUFFER_UPDATE (invalid count)\n");
			return -EINVAL;
		}
		/* Updating memory at DST_ADDRESS. */
		if (idx_value & 0x1) {
			u64 offset;
			r = radeon_cs_packet_next_reloc(p, &reloc, 0);
			if (r) {
				DRM_ERROR("bad STRMOUT_BUFFER_UPDATE (missing dst reloc)\n");
				return -EINVAL;
			}
			offset = radeon_get_ib_value(p, idx+1);
			offset += ((u64)(radeon_get_ib_value(p, idx+2) & 0xff)) << 32;
			if ((offset + 4) > radeon_bo_size(reloc->robj)) {
				DRM_ERROR("bad STRMOUT_BUFFER_UPDATE dst bo too small: 0x%llx, 0x%lx\n",
					  offset + 4, radeon_bo_size(reloc->robj));
				return -EINVAL;
			}
			offset += reloc->gpu_offset;
			ib[idx+1] = offset;
			ib[idx+2] = upper_32_bits(offset) & 0xff;
		}
		/* Reading data from SRC_ADDRESS. */
		if (((idx_value >> 1) & 0x3) == 2) {
			u64 offset;
			r = radeon_cs_packet_next_reloc(p, &reloc, 0);
			if (r) {
				DRM_ERROR("bad STRMOUT_BUFFER_UPDATE (missing src reloc)\n");
				return -EINVAL;
			}
			offset = radeon_get_ib_value(p, idx+3);
			offset += ((u64)(radeon_get_ib_value(p, idx+4) & 0xff)) << 32;
			if ((offset + 4) > radeon_bo_size(reloc->robj)) {
				DRM_ERROR("bad STRMOUT_BUFFER_UPDATE src bo too small: 0x%llx, 0x%lx\n",
					  offset + 4, radeon_bo_size(reloc->robj));
				return -EINVAL;
			}
			offset += reloc->gpu_offset;
			ib[idx+3] = offset;
			ib[idx+4] = upper_32_bits(offset) & 0xff;
		}
		break;
	case PACKET3_MEM_WRITE:
	{
		u64 offset;

		if (pkt->count != 3) {
			DRM_ERROR("bad MEM_WRITE (invalid count)\n");
			return -EINVAL;
		}
		r = radeon_cs_packet_next_reloc(p, &reloc, 0);
		if (r) {
			DRM_ERROR("bad MEM_WRITE (missing reloc)\n");
			return -EINVAL;
		}
		offset = radeon_get_ib_value(p, idx+0);
		offset += ((u64)(radeon_get_ib_value(p, idx+1) & 0xff)) << 32UL;
		if (offset & 0x7) {
			DRM_ERROR("bad MEM_WRITE (address not qwords aligned)\n");
			return -EINVAL;
		}
		if ((offset + 8) > radeon_bo_size(reloc->robj)) {
			DRM_ERROR("bad MEM_WRITE bo too small: 0x%llx, 0x%lx\n",
				  offset + 8, radeon_bo_size(reloc->robj));
			return -EINVAL;
		}
		offset += reloc->gpu_offset;
		ib[idx+0] = offset;
		ib[idx+1] = upper_32_bits(offset) & 0xff;
		break;
	}
	case PACKET3_COPY_DW:
		if (pkt->count != 4) {
			DRM_ERROR("bad COPY_DW (invalid count)\n");
			return -EINVAL;
		}
		if (idx_value & 0x1) {
			u64 offset;
			/* SRC is memory. */
			r = radeon_cs_packet_next_reloc(p, &reloc, 0);
			if (r) {
				DRM_ERROR("bad COPY_DW (missing src reloc)\n");
				return -EINVAL;
			}
			offset = radeon_get_ib_value(p, idx+1);
			offset += ((u64)(radeon_get_ib_value(p, idx+2) & 0xff)) << 32;
			if ((offset + 4) > radeon_bo_size(reloc->robj)) {
				DRM_ERROR("bad COPY_DW src bo too small: 0x%llx, 0x%lx\n",
					  offset + 4, radeon_bo_size(reloc->robj));
				return -EINVAL;
			}
			offset += reloc->gpu_offset;
			ib[idx+1] = offset;
			ib[idx+2] = upper_32_bits(offset) & 0xff;
		} else {
			/* SRC is a reg. */
			reg = radeon_get_ib_value(p, idx+1) << 2;
			if (!evergreen_is_safe_reg(p, reg, idx+1))
				return -EINVAL;
		}
		if (idx_value & 0x2) {
			u64 offset;
			/* DST is memory. */
			r = radeon_cs_packet_next_reloc(p, &reloc, 0);
			if (r) {
				DRM_ERROR("bad COPY_DW (missing dst reloc)\n");
				return -EINVAL;
			}
			offset = radeon_get_ib_value(p, idx+3);
			offset += ((u64)(radeon_get_ib_value(p, idx+4) & 0xff)) << 32;
			if ((offset + 4) > radeon_bo_size(reloc->robj)) {
				DRM_ERROR("bad COPY_DW dst bo too small: 0x%llx, 0x%lx\n",
					  offset + 4, radeon_bo_size(reloc->robj));
				return -EINVAL;
			}
			offset += reloc->gpu_offset;
			ib[idx+3] = offset;
			ib[idx+4] = upper_32_bits(offset) & 0xff;
		} else {
			/* DST is a reg. */
			reg = radeon_get_ib_value(p, idx+3) << 2;
			if (!evergreen_is_safe_reg(p, reg, idx+3))
				return -EINVAL;
		}
		break;
	case PACKET3_NOP:
		break;
	default:
		DRM_ERROR("Packet3 opcode %x not supported\n", pkt->opcode);
		return -EINVAL;
	}
	return 0;
}