+/* ================================================================
+ * Helper functions for client state checking and fixup
+ */
+
+static __inline__ int radeon_check_and_fixup_offset(drm_radeon_private_t *
+ dev_priv,
+ drm_file_t * filp_priv,
+ u32 *offset)
+{
+ u64 off = *offset;
+ u32 fb_end = dev_priv->fb_location + dev_priv->fb_size - 1;
+ struct drm_radeon_driver_file_fields *radeon_priv;
+
+ /* Hrm ... the story of the offset ... So this function converts
+ * the various ideas of what userland clients might have for an
+ * offset in the card address space into an offset into the card
+ * address space :) So with a sane client, it should just keep
+ * the value intact and just do some boundary checking. However,
+ * not all clients are sane. Some older clients pass us 0 based
+ * offsets relative to the start of the framebuffer and some may
+ * assume the AGP aperture it appended to the framebuffer, so we
+ * try to detect those cases and fix them up.
+ *
+ * Note: It might be a good idea here to make sure the offset lands
+ * in some "allowed" area to protect things like the PCIE GART...
+ */
+
+ /* First, the best case, the offset already lands in either the
+ * framebuffer or the GART mapped space
+ */
+ if (radeon_check_offset(dev_priv, off))
+ return 0;
+
+ /* Ok, that didn't happen... now check if we have a zero based
+ * offset that fits in the framebuffer + gart space, apply the
+ * magic offset we get from SETPARAM or calculated from fb_location
+ */
+ if (off < (dev_priv->fb_size + dev_priv->gart_size)) {
+ radeon_priv = filp_priv->driver_priv;
+ off += radeon_priv->radeon_fb_delta;
+ }
+
+ /* Finally, assume we aimed at a GART offset if beyond the fb */
+ if (off > fb_end)
+ off = off - fb_end - 1 + dev_priv->gart_vm_start;
+
+ /* Now recheck and fail if out of bounds */
+ if (radeon_check_offset(dev_priv, off)) {
+ DRM_DEBUG("offset fixed up to 0x%x\n", (unsigned int)off);
+ *offset = off;
+ return 0;
+ }
+ return DRM_ERR(EINVAL);
+}
+
+static __inline__ int radeon_check_and_fixup_packets(drm_radeon_private_t *
+ dev_priv,
+ drm_file_t * filp_priv,
+ int id, u32 *data)
+{
+ switch (id) {
+
+ case RADEON_EMIT_PP_MISC:
+ if (radeon_check_and_fixup_offset(dev_priv, filp_priv,
+ &data[(RADEON_RB3D_DEPTHOFFSET - RADEON_PP_MISC) / 4])) {
+ DRM_ERROR("Invalid depth buffer offset\n");
+ return DRM_ERR(EINVAL);
+ }
+ break;
+
+ case RADEON_EMIT_PP_CNTL:
+ if (radeon_check_and_fixup_offset(dev_priv, filp_priv,
+ &data[(RADEON_RB3D_COLOROFFSET - RADEON_PP_CNTL) / 4])) {
+ DRM_ERROR("Invalid colour buffer offset\n");
+ return DRM_ERR(EINVAL);
+ }
+ break;
+
+ case R200_EMIT_PP_TXOFFSET_0:
+ case R200_EMIT_PP_TXOFFSET_1:
+ case R200_EMIT_PP_TXOFFSET_2:
+ case R200_EMIT_PP_TXOFFSET_3:
+ case R200_EMIT_PP_TXOFFSET_4:
+ case R200_EMIT_PP_TXOFFSET_5:
+ if (radeon_check_and_fixup_offset(dev_priv, filp_priv,
+ &data[0])) {
+ DRM_ERROR("Invalid R200 texture offset\n");
+ return DRM_ERR(EINVAL);
+ }
+ break;
+
+ case RADEON_EMIT_PP_TXFILTER_0:
+ case RADEON_EMIT_PP_TXFILTER_1:
+ case RADEON_EMIT_PP_TXFILTER_2:
+ if (radeon_check_and_fixup_offset(dev_priv, filp_priv,
+ &data[(RADEON_PP_TXOFFSET_0 - RADEON_PP_TXFILTER_0) / 4])) {
+ DRM_ERROR("Invalid R100 texture offset\n");
+ return DRM_ERR(EINVAL);
+ }
+ break;
+
+ case R200_EMIT_PP_CUBIC_OFFSETS_0:
+ case R200_EMIT_PP_CUBIC_OFFSETS_1:
+ case R200_EMIT_PP_CUBIC_OFFSETS_2:
+ case R200_EMIT_PP_CUBIC_OFFSETS_3:
+ case R200_EMIT_PP_CUBIC_OFFSETS_4:
+ case R200_EMIT_PP_CUBIC_OFFSETS_5:{
+ int i;
+ for (i = 0; i < 5; i++) {
+ if (radeon_check_and_fixup_offset(dev_priv,
+ filp_priv,
+ &data[i])) {
+ DRM_ERROR
+ ("Invalid R200 cubic texture offset\n");
+ return DRM_ERR(EINVAL);
+ }
+ }
+ break;
+ }
+
+ case RADEON_EMIT_PP_CUBIC_OFFSETS_T0:
+ case RADEON_EMIT_PP_CUBIC_OFFSETS_T1:
+ case RADEON_EMIT_PP_CUBIC_OFFSETS_T2:{
+ int i;
+ for (i = 0; i < 5; i++) {
+ if (radeon_check_and_fixup_offset(dev_priv,
+ filp_priv,
+ &data[i])) {
+ DRM_ERROR
+ ("Invalid R100 cubic texture offset\n");
+ return DRM_ERR(EINVAL);
+ }
+ }
+ }
+ break;
+
+ case R200_EMIT_VAP_CTL:{
+ RING_LOCALS;
+ BEGIN_RING(2);
+ OUT_RING_REG(RADEON_SE_TCL_STATE_FLUSH, 0);
+ ADVANCE_RING();
+ }
+ break;
+
+ case RADEON_EMIT_RB3D_COLORPITCH:
+ case RADEON_EMIT_RE_LINE_PATTERN:
+ case RADEON_EMIT_SE_LINE_WIDTH:
+ case RADEON_EMIT_PP_LUM_MATRIX:
+ case RADEON_EMIT_PP_ROT_MATRIX_0:
+ case RADEON_EMIT_RB3D_STENCILREFMASK:
+ case RADEON_EMIT_SE_VPORT_XSCALE:
+ case RADEON_EMIT_SE_CNTL:
+ case RADEON_EMIT_SE_CNTL_STATUS:
+ case RADEON_EMIT_RE_MISC:
+ case RADEON_EMIT_PP_BORDER_COLOR_0:
+ case RADEON_EMIT_PP_BORDER_COLOR_1:
+ case RADEON_EMIT_PP_BORDER_COLOR_2:
+ case RADEON_EMIT_SE_ZBIAS_FACTOR:
+ case RADEON_EMIT_SE_TCL_OUTPUT_VTX_FMT:
+ case RADEON_EMIT_SE_TCL_MATERIAL_EMMISSIVE_RED:
+ case R200_EMIT_PP_TXCBLEND_0:
+ case R200_EMIT_PP_TXCBLEND_1:
+ case R200_EMIT_PP_TXCBLEND_2:
+ case R200_EMIT_PP_TXCBLEND_3:
+ case R200_EMIT_PP_TXCBLEND_4:
+ case R200_EMIT_PP_TXCBLEND_5:
+ case R200_EMIT_PP_TXCBLEND_6:
+ case R200_EMIT_PP_TXCBLEND_7:
+ case R200_EMIT_TCL_LIGHT_MODEL_CTL_0:
+ case R200_EMIT_TFACTOR_0:
+ case R200_EMIT_VTX_FMT_0:
+ case R200_EMIT_MATRIX_SELECT_0:
+ case R200_EMIT_TEX_PROC_CTL_2:
+ case R200_EMIT_TCL_UCP_VERT_BLEND_CTL:
+ case R200_EMIT_PP_TXFILTER_0:
+ case R200_EMIT_PP_TXFILTER_1:
+ case R200_EMIT_PP_TXFILTER_2:
+ case R200_EMIT_PP_TXFILTER_3:
+ case R200_EMIT_PP_TXFILTER_4:
+ case R200_EMIT_PP_TXFILTER_5:
+ case R200_EMIT_VTE_CNTL:
+ case R200_EMIT_OUTPUT_VTX_COMP_SEL:
+ case R200_EMIT_PP_TAM_DEBUG3:
+ case R200_EMIT_PP_CNTL_X:
+ case R200_EMIT_RB3D_DEPTHXY_OFFSET:
+ case R200_EMIT_RE_AUX_SCISSOR_CNTL:
+ case R200_EMIT_RE_SCISSOR_TL_0:
+ case R200_EMIT_RE_SCISSOR_TL_1:
+ case R200_EMIT_RE_SCISSOR_TL_2:
+ case R200_EMIT_SE_VAP_CNTL_STATUS:
+ case R200_EMIT_SE_VTX_STATE_CNTL:
+ case R200_EMIT_RE_POINTSIZE:
+ case R200_EMIT_TCL_INPUT_VTX_VECTOR_ADDR_0:
+ case R200_EMIT_PP_CUBIC_FACES_0:
+ case R200_EMIT_PP_CUBIC_FACES_1:
+ case R200_EMIT_PP_CUBIC_FACES_2:
+ case R200_EMIT_PP_CUBIC_FACES_3:
+ case R200_EMIT_PP_CUBIC_FACES_4:
+ case R200_EMIT_PP_CUBIC_FACES_5:
+ case RADEON_EMIT_PP_TEX_SIZE_0:
+ case RADEON_EMIT_PP_TEX_SIZE_1:
+ case RADEON_EMIT_PP_TEX_SIZE_2:
+ case R200_EMIT_RB3D_BLENDCOLOR:
+ case R200_EMIT_TCL_POINT_SPRITE_CNTL:
+ case RADEON_EMIT_PP_CUBIC_FACES_0:
+ case RADEON_EMIT_PP_CUBIC_FACES_1:
+ case RADEON_EMIT_PP_CUBIC_FACES_2:
+ case R200_EMIT_PP_TRI_PERF_CNTL:
+ case R200_EMIT_PP_AFS_0:
+ case R200_EMIT_PP_AFS_1:
+ case R200_EMIT_ATF_TFACTOR:
+ case R200_EMIT_PP_TXCTLALL_0:
+ case R200_EMIT_PP_TXCTLALL_1:
+ case R200_EMIT_PP_TXCTLALL_2:
+ case R200_EMIT_PP_TXCTLALL_3:
+ case R200_EMIT_PP_TXCTLALL_4:
+ case R200_EMIT_PP_TXCTLALL_5:
+ case R200_EMIT_VAP_PVS_CNTL:
+ /* These packets don't contain memory offsets */
+ break;
+
+ default:
+ DRM_ERROR("Unknown state packet ID %d\n", id);
+ return DRM_ERR(EINVAL);
+ }
+
+ return 0;
+}
+
+static __inline__ int radeon_check_and_fixup_packet3(drm_radeon_private_t *
+ dev_priv,
+ drm_file_t *filp_priv,
+ drm_radeon_kcmd_buffer_t *
+ cmdbuf,
+ unsigned int *cmdsz)
+{
+ u32 *cmd = (u32 *) cmdbuf->buf;
+ u32 offset, narrays;
+ int count, i, k;
+
+ *cmdsz = 2 + ((cmd[0] & RADEON_CP_PACKET_COUNT_MASK) >> 16);
+
+ if ((cmd[0] & 0xc0000000) != RADEON_CP_PACKET3) {
+ DRM_ERROR("Not a type 3 packet\n");
+ return DRM_ERR(EINVAL);
+ }
+
+ if (4 * *cmdsz > cmdbuf->bufsz) {
+ DRM_ERROR("Packet size larger than size of data provided\n");
+ return DRM_ERR(EINVAL);
+ }
+
+ switch(cmd[0] & 0xff00) {
+ /* XXX Are there old drivers needing other packets? */
+
+ case RADEON_3D_DRAW_IMMD:
+ case RADEON_3D_DRAW_VBUF:
+ case RADEON_3D_DRAW_INDX:
+ case RADEON_WAIT_FOR_IDLE:
+ case RADEON_CP_NOP:
+ case RADEON_3D_CLEAR_ZMASK:
+/* case RADEON_CP_NEXT_CHAR:
+ case RADEON_CP_PLY_NEXTSCAN:
+ case RADEON_CP_SET_SCISSORS: */ /* probably safe but will never need them? */
+ /* these packets are safe */
+ break;
+
+ case RADEON_CP_3D_DRAW_IMMD_2:
+ case RADEON_CP_3D_DRAW_VBUF_2:
+ case RADEON_CP_3D_DRAW_INDX_2:
+ case RADEON_3D_CLEAR_HIZ:
+ /* safe but r200 only */
+ if (dev_priv->microcode_version != UCODE_R200) {
+ DRM_ERROR("Invalid 3d packet for r100-class chip\n");
+ return DRM_ERR(EINVAL);
+ }
+ break;
+
+ case RADEON_3D_LOAD_VBPNTR:
+ count = (cmd[0] >> 16) & 0x3fff;
+
+ if (count > 18) { /* 12 arrays max */
+ DRM_ERROR("Too large payload in 3D_LOAD_VBPNTR (count=%d)\n",
+ count);
+ return DRM_ERR(EINVAL);
+ }
+
+ /* carefully check packet contents */
+ narrays = cmd[1] & ~0xc000;
+ k = 0;
+ i = 2;
+ while ((k < narrays) && (i < (count + 2))) {
+ i++; /* skip attribute field */
+ if (radeon_check_and_fixup_offset(dev_priv, filp_priv, &cmd[i])) {
+ DRM_ERROR
+ ("Invalid offset (k=%d i=%d) in 3D_LOAD_VBPNTR packet.\n",
+ k, i);
+ return DRM_ERR(EINVAL);
+ }
+ k++;
+ i++;
+ if (k == narrays)
+ break;
+ /* have one more to process, they come in pairs */
+ if (radeon_check_and_fixup_offset(dev_priv, filp_priv, &cmd[i])) {
+ DRM_ERROR
+ ("Invalid offset (k=%d i=%d) in 3D_LOAD_VBPNTR packet.\n",
+ k, i);
+ return DRM_ERR(EINVAL);
+ }
+ k++;
+ i++;
+ }
+ /* do the counts match what we expect ? */
+ if ((k != narrays) || (i != (count + 2))) {
+ DRM_ERROR
+ ("Malformed 3D_LOAD_VBPNTR packet (k=%d i=%d narrays=%d count+1=%d).\n",
+ k, i, narrays, count + 1);
+ return DRM_ERR(EINVAL);
+ }
+ break;
+
+ case RADEON_3D_RNDR_GEN_INDX_PRIM:
+ if (dev_priv->microcode_version != UCODE_R100) {
+ DRM_ERROR("Invalid 3d packet for r200-class chip\n");
+ return DRM_ERR(EINVAL);
+ }
+ if (radeon_check_and_fixup_offset(dev_priv, filp_priv, &cmd[1])) {
+ DRM_ERROR("Invalid rndr_gen_indx offset\n");
+ return DRM_ERR(EINVAL);
+ }
+ break;
+
+ case RADEON_CP_INDX_BUFFER:
+ if (dev_priv->microcode_version != UCODE_R200) {
+ DRM_ERROR("Invalid 3d packet for r100-class chip\n");
+ return DRM_ERR(EINVAL);
+ }
+ if ((cmd[1] & 0x8000ffff) != 0x80000810) {
+ DRM_ERROR("Invalid indx_buffer reg address %08X\n", cmd[1]);
+ return DRM_ERR(EINVAL);
+ }
+ if (radeon_check_and_fixup_offset(dev_priv, filp_priv, &cmd[2])) {
+ DRM_ERROR("Invalid indx_buffer offset is %08X\n", cmd[2]);
+ return DRM_ERR(EINVAL);
+ }
+ break;
+
+ case RADEON_CNTL_HOSTDATA_BLT:
+ case RADEON_CNTL_PAINT_MULTI:
+ case RADEON_CNTL_BITBLT_MULTI:
+ /* MSB of opcode: next DWORD GUI_CNTL */
+ if (cmd[1] & (RADEON_GMC_SRC_PITCH_OFFSET_CNTL
+ | RADEON_GMC_DST_PITCH_OFFSET_CNTL)) {
+ offset = cmd[2] << 10;
+ if (radeon_check_and_fixup_offset
+ (dev_priv, filp_priv, &offset)) {
+ DRM_ERROR("Invalid first packet offset\n");
+ return DRM_ERR(EINVAL);
+ }
+ cmd[2] = (cmd[2] & 0xffc00000) | offset >> 10;
+ }
+
+ if ((cmd[1] & RADEON_GMC_SRC_PITCH_OFFSET_CNTL) &&
+ (cmd[1] & RADEON_GMC_DST_PITCH_OFFSET_CNTL)) {
+ offset = cmd[3] << 10;
+ if (radeon_check_and_fixup_offset
+ (dev_priv, filp_priv, &offset)) {
+ DRM_ERROR("Invalid second packet offset\n");
+ return DRM_ERR(EINVAL);
+ }
+ cmd[3] = (cmd[3] & 0xffc00000) | offset >> 10;
+ }
+ break;
+
+ default:
+ DRM_ERROR("Invalid packet type %x\n", cmd[0] & 0xff00);
+ return DRM_ERR(EINVAL);
+ }
+
+ return 0;
+}