+int radeon_cs2_ioctl(struct drm_device *dev, void *data, struct drm_file *fpriv)
+{
+ struct drm_radeon_cs_parser parser;
+ struct drm_radeon_private *dev_priv = dev->dev_private;
+ struct drm_radeon_cs2 *cs = data;
+ uint32_t cs_id;
+ struct drm_radeon_cs_chunk __user **chunk_ptr = NULL;
+ uint64_t *chunk_array;
+ uint64_t *chunk_array_ptr;
+ uint32_t card_offset;
+ long size;
+ int r, i;
+ RING_LOCALS;
+
+ /* set command stream id to 0 which is fake id */
+ cs_id = 0;
+ DRM_COPY_TO_USER(&cs->cs_id, &cs_id, sizeof(uint32_t));
+
+ if (dev_priv == NULL) {
+ DRM_ERROR("called with no initialization\n");
+ return -EINVAL;
+ }
+ if (!cs->num_chunks) {
+ return 0;
+ }
+
+
+ chunk_array = drm_calloc(cs->num_chunks, sizeof(uint64_t), DRM_MEM_DRIVER);
+ if (!chunk_array) {
+ return -ENOMEM;
+ }
+
+ chunk_array_ptr = (uint64_t *)(unsigned long)(cs->chunks);
+
+ if (DRM_COPY_FROM_USER(chunk_array, chunk_array_ptr, sizeof(uint64_t)*cs->num_chunks)) {
+ r = -EFAULT;
+ goto out;
+ }
+
+ parser.reloc_index = -1;
+ parser.ib_index = -1;
+ parser.num_chunks = cs->num_chunks;
+ /* copy out the chunk headers */
+ parser.chunks = drm_calloc(parser.num_chunks, sizeof(struct drm_radeon_kernel_chunk), DRM_MEM_DRIVER);
+ if (!parser.chunks) {
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < parser.num_chunks; i++) {
+ struct drm_radeon_cs_chunk user_chunk;
+
+ chunk_ptr = (void __user *)(unsigned long)chunk_array[i];
+
+ if (DRM_COPY_FROM_USER(&user_chunk, chunk_ptr, sizeof(struct drm_radeon_cs_chunk))){
+ r = -EFAULT;
+ goto out;
+ }
+ parser.chunks[i].chunk_id = user_chunk.chunk_id;
+
+ if (parser.chunks[i].chunk_id == RADEON_CHUNK_ID_RELOCS)
+ parser.reloc_index = i;
+
+ if (parser.chunks[i].chunk_id == RADEON_CHUNK_ID_IB)
+ parser.ib_index = i;
+
+ if (parser.chunks[i].chunk_id == RADEON_CHUNK_ID_OLD) {
+ parser.ib_index = i;
+ parser.reloc_index = -1;
+ }
+
+ parser.chunks[i].length_dw = user_chunk.length_dw;
+ parser.chunks[i].chunk_data = (uint32_t *)(unsigned long)user_chunk.chunk_data;
+
+ parser.chunks[i].kdata = NULL;
+
+ switch(parser.chunks[i].chunk_id) {
+ case RADEON_CHUNK_ID_RELOCS:
+ case RADEON_CHUNK_ID_IB:
+ case RADEON_CHUNK_ID_OLD: {
+ /* copy from user the relocs chunk */
+ int size = parser.chunks[i].length_dw * sizeof(uint32_t);
+ parser.chunks[i].kdata = drm_alloc(size, DRM_MEM_DRIVER);
+ if (!parser.chunks[i].kdata) {
+ r = -ENOMEM;
+ goto out;
+ }
+
+ if (DRM_COPY_FROM_USER(parser.chunks[i].kdata, parser.chunks[i].chunk_data, size)) {
+ r = -EFAULT;
+ goto out;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ DRM_DEBUG("chunk %d %d %d %p\n", i, parser.chunks[i].chunk_id, parser.chunks[i].length_dw,
+ parser.chunks[i].chunk_data);
+ }
+
+
+ if (parser.chunks[parser.ib_index].length_dw > (16 * 1024)) {
+ DRM_ERROR("cs->dwords too big: %d\n", parser.chunks[parser.ib_index].length_dw);
+ r = -EINVAL;
+ goto out;
+ }
+
+ /* get ib */
+ r = dev_priv->cs.ib_get(&parser, &card_offset);
+ if (r) {
+ DRM_ERROR("ib_get failed\n");
+ goto out;
+ }
+
+ /* now parse command stream */
+ r = dev_priv->cs.parse(&parser);
+ if (r) {
+ goto out;
+ }
+
+ BEGIN_RING(4);
+ OUT_RING(CP_PACKET0(RADEON_CP_IB_BASE, 1));
+ OUT_RING(card_offset);
+ OUT_RING(parser.chunks[parser.ib_index].length_dw);
+ OUT_RING(CP_PACKET2());
+ ADVANCE_RING();
+
+ /* emit cs id sequence */
+ dev_priv->cs.id_emit(dev, &cs_id);
+ COMMIT_RING();
+
+ DRM_COPY_TO_USER(&cs->cs_id, &cs_id, sizeof(uint32_t));
+out:
+ dev_priv->cs.ib_free(&parser);
+
+ for (i = 0; i < parser.num_chunks; i++) {
+ if (parser.chunks[i].kdata)
+ drm_free(parser.chunks[i].kdata, parser.chunks[i].length_dw * sizeof(uint32_t), DRM_MEM_DRIVER);
+ }
+
+ drm_free(parser.chunks, sizeof(struct drm_radeon_kernel_chunk)*parser.num_chunks, DRM_MEM_DRIVER);
+ drm_free(chunk_array, sizeof(uint64_t)*parser.num_chunks, DRM_MEM_DRIVER);
+
+ return r;
+}
+