}
 }
 
+struct vchiq_io_copy_callback_context {
+       VCHIQ_ELEMENT_T *current_element;
+       size_t current_element_offset;
+       unsigned long elements_to_go;
+       size_t current_offset;
+};
+
+static ssize_t
+vchiq_ioc_copy_element_data(
+       void *context,
+       void *dest,
+       size_t offset,
+       size_t maxsize)
+{
+       long res;
+       size_t bytes_this_round;
+       struct vchiq_io_copy_callback_context *copy_context =
+               (struct vchiq_io_copy_callback_context *)context;
+
+       if (offset != copy_context->current_offset)
+               return 0;
+
+       if (!copy_context->elements_to_go)
+               return 0;
+
+       /*
+        * Complex logic here to handle the case of 0 size elements
+        * in the middle of the array of elements.
+        *
+        * Need to skip over these 0 size elements.
+        */
+       while (1) {
+               bytes_this_round = min(copy_context->current_element->size -
+                                      copy_context->current_element_offset,
+                                      maxsize);
+
+               if (bytes_this_round)
+                       break;
+
+               copy_context->elements_to_go--;
+               copy_context->current_element++;
+               copy_context->current_element_offset = 0;
+
+               if (!copy_context->elements_to_go)
+                       return 0;
+       }
+
+       res = copy_from_user(dest,
+                            copy_context->current_element->data +
+                            copy_context->current_element_offset,
+                            bytes_this_round);
+
+       if (res != 0)
+               return -EFAULT;
+
+       copy_context->current_element_offset += bytes_this_round;
+       copy_context->current_offset += bytes_this_round;
+
+       /*
+        * Check if done with current element, and if so advance to the next.
+        */
+       if (copy_context->current_element_offset ==
+           copy_context->current_element->size) {
+               copy_context->elements_to_go--;
+               copy_context->current_element++;
+               copy_context->current_element_offset = 0;
+       }
+
+       return bytes_this_round;
+}
+
+/**************************************************************************
+ *
+ *   vchiq_ioc_queue_message
+ *
+ **************************************************************************/
+static VCHIQ_STATUS_T
+vchiq_ioc_queue_message(VCHIQ_SERVICE_HANDLE_T handle,
+                       VCHIQ_ELEMENT_T *elements,
+                       unsigned long count)
+{
+       struct vchiq_io_copy_callback_context context;
+       unsigned long i;
+       size_t total_size = 0;
+
+       context.current_element = elements;
+       context.current_element_offset = 0;
+       context.elements_to_go = count;
+       context.current_offset = 0;
+
+       for (i = 0; i < count; i++) {
+               if (!elements[i].data && elements[i].size != 0)
+                       return -EFAULT;
+
+               total_size += elements[i].size;
+       }
+
+       return vchiq_queue_message(handle, vchiq_ioc_copy_element_data,
+                                  &context, total_size);
+}
+
 /****************************************************************************
 *
 *   vchiq_ioctl
                        VCHIQ_ELEMENT_T elements[MAX_ELEMENTS];
                        if (copy_from_user(elements, args.elements,
                                args.count * sizeof(VCHIQ_ELEMENT_T)) == 0)
-                               status = vchiq_queue_message
+                               status = vchiq_ioc_queue_message
                                        (args.handle,
                                        elements, args.count);
                        else
 
 ** enough for a header. This relies on header size being a power of two, which
 ** has been verified earlier by a static assertion. */
 
-static inline unsigned int
-calc_stride(unsigned int size)
+static inline size_t
+calc_stride(size_t size)
 {
        /* Allow room for the header */
        size += sizeof(VCHIQ_HEADER_T);
 /* Called from queue_message, by the slot handler and application threads,
 ** with slot_mutex held */
 static VCHIQ_HEADER_T *
-reserve_space(VCHIQ_STATE_T *state, int space, int is_blocking)
+reserve_space(VCHIQ_STATE_T *state, size_t space, int is_blocking)
 {
        VCHIQ_SHARED_STATE_T *local = state->local;
        int tx_pos = state->local_tx_pos;
        }
 }
 
+static ssize_t
+memcpy_copy_callback(
+       void *context, void *dest,
+       size_t offset, size_t maxsize)
+{
+       void *src = context;
+
+       memcpy(dest + offset, src + offset, maxsize);
+       return maxsize;
+}
+
+static ssize_t
+copy_message_data(
+       ssize_t (*copy_callback)(void *context, void *dest,
+                                size_t offset, size_t maxsize),
+       void *context,
+       void *dest,
+       size_t size)
+{
+       size_t pos = 0;
+
+       while (pos < size) {
+               ssize_t callback_result;
+               size_t max_bytes = size - pos;
+
+               callback_result =
+                       copy_callback(context, dest + pos,
+                                     pos, max_bytes);
+
+               if (callback_result < 0)
+                       return callback_result;
+
+               if (!callback_result)
+                       return -EIO;
+
+               if (callback_result > max_bytes)
+                       return -EIO;
+
+               pos += callback_result;
+       }
+
+       return size;
+}
+
 /* Called by the slot handler and application threads */
 static VCHIQ_STATUS_T
 queue_message(VCHIQ_STATE_T *state, VCHIQ_SERVICE_T *service,
-       int msgid, const VCHIQ_ELEMENT_T *elements,
-       int count, int size, int flags)
+       int msgid,
+       ssize_t (*copy_callback)(void *context, void *dest,
+                                size_t offset, size_t maxsize),
+       void *context,
+       size_t size,
+       int flags)
 {
        VCHIQ_SHARED_STATE_T *local;
        VCHIQ_SERVICE_QUOTA_T *service_quota = NULL;
        VCHIQ_HEADER_T *header;
        int type = VCHIQ_MSG_TYPE(msgid);
 
-       unsigned int stride;
+       size_t stride;
 
        local = state->local;
 
        }
 
        if (type == VCHIQ_MSG_DATA) {
-               int i, pos;
+               ssize_t callback_result;
                int tx_end_index;
                int slot_use_count;
 
                BUG_ON((flags & (QMFLAGS_NO_MUTEX_LOCK |
                                 QMFLAGS_NO_MUTEX_UNLOCK)) != 0);
 
-               for (i = 0, pos = 0; i < (unsigned int)count;
-                       pos += elements[i++].size)
-                       if (elements[i].size) {
-                               if (vchiq_copy_from_user
-                                       (header->data + pos, elements[i].data,
-                                       (size_t) elements[i].size) !=
-                                       VCHIQ_SUCCESS) {
-                                       mutex_unlock(&state->slot_mutex);
-                                       VCHIQ_SERVICE_STATS_INC(service,
+               callback_result =
+                       copy_message_data(copy_callback, context,
+                                         header->data, size);
+
+               if (callback_result < 0) {
+                       mutex_unlock(&state->slot_mutex);
+                       VCHIQ_SERVICE_STATS_INC(service,
                                                error_count);
-                                       return VCHIQ_ERROR;
-                               }
-                               if (i == 0) {
-                                       if (SRVTRACE_ENABLED(service,
-                                                       VCHIQ_LOG_INFO))
-                                               vchiq_log_dump_mem("Sent", 0,
-                                                       header->data + pos,
-                                                       min(64u,
-                                                       elements[0].size));
-                               }
-                       }
+                       return VCHIQ_ERROR;
+               }
+
+               if (SRVTRACE_ENABLED(service,
+                                    VCHIQ_LOG_INFO))
+                       vchiq_log_dump_mem("Sent", 0,
+                                          header->data,
+                                          min((size_t)64,
+                                              (size_t)callback_result));
 
                spin_lock("a_spinlock);
                service_quota->message_use_count++;
                        header, size, VCHIQ_MSG_SRCPORT(msgid),
                        VCHIQ_MSG_DSTPORT(msgid));
                if (size != 0) {
-                       WARN_ON(!((count == 1) && (size == elements[0].size)));
-                       memcpy(header->data, elements[0].data,
-                               elements[0].size);
+                       /* It is assumed for now that this code path
+                        * only happens from calls inside this file.
+                        *
+                        * External callers are through the vchiq_queue_message
+                        * path which always sets the type to be VCHIQ_MSG_DATA
+                        *
+                        * At first glance this appears to be correct but
+                        * more review is needed.
+                        */
+                       copy_message_data(copy_callback, context,
+                                         header->data, size);
                }
                VCHIQ_STATS_INC(state, ctrl_tx_count);
        }
 /* Called by the slot handler and application threads */
 static VCHIQ_STATUS_T
 queue_message_sync(VCHIQ_STATE_T *state, VCHIQ_SERVICE_T *service,
-       int msgid, const VCHIQ_ELEMENT_T *elements,
-       int count, int size, int is_blocking)
+       int msgid,
+       ssize_t (*copy_callback)(void *context, void *dest,
+                                size_t offset, size_t maxsize),
+       void *context,
+       int size,
+       int is_blocking)
 {
        VCHIQ_SHARED_STATE_T *local;
        VCHIQ_HEADER_T *header;
+       ssize_t callback_result;
 
        local = state->local;
 
                                state->id, oldmsgid);
        }
 
-       if (service) {
-               int i, pos;
+       vchiq_log_info(vchiq_sync_log_level,
+                      "%d: qms %s@%pK,%x (%d->%d)", state->id,
+                      msg_type_str(VCHIQ_MSG_TYPE(msgid)),
+                      header, size, VCHIQ_MSG_SRCPORT(msgid),
+                      VCHIQ_MSG_DSTPORT(msgid));
 
-               vchiq_log_info(vchiq_sync_log_level,
-                       "%d: qms %s@%pK,%x (%d->%d)", state->id,
-                       msg_type_str(VCHIQ_MSG_TYPE(msgid)),
-                       header, size, VCHIQ_MSG_SRCPORT(msgid),
-                       VCHIQ_MSG_DSTPORT(msgid));
+       callback_result =
+               copy_message_data(copy_callback, context,
+                                 header->data, size);
 
-               for (i = 0, pos = 0; i < (unsigned int)count;
-                       pos += elements[i++].size)
-                       if (elements[i].size) {
-                               if (vchiq_copy_from_user
-                                       (header->data + pos, elements[i].data,
-                                       (size_t) elements[i].size) !=
-                                       VCHIQ_SUCCESS) {
-                                       mutex_unlock(&state->sync_mutex);
-                                       VCHIQ_SERVICE_STATS_INC(service,
-                                               error_count);
-                                       return VCHIQ_ERROR;
-                               }
-                               if (i == 0) {
-                                       if (vchiq_sync_log_level >=
-                                               VCHIQ_LOG_TRACE)
-                                               vchiq_log_dump_mem("Sent Sync",
-                                                       0, header->data + pos,
-                                                       min(64u,
-                                                       elements[0].size));
-                               }
-                       }
+       if (callback_result < 0) {
+               mutex_unlock(&state->slot_mutex);
+               VCHIQ_SERVICE_STATS_INC(service,
+                                       error_count);
+               return VCHIQ_ERROR;
+       }
+
+       if (service) {
+               if (SRVTRACE_ENABLED(service,
+                                    VCHIQ_LOG_INFO))
+                       vchiq_log_dump_mem("Sent", 0,
+                                          header->data,
+                                          min((size_t)64,
+                                              (size_t)callback_result));
 
                VCHIQ_SERVICE_STATS_INC(service, ctrl_tx_count);
                VCHIQ_SERVICE_STATS_ADD(service, ctrl_tx_bytes, size);
        } else {
-               vchiq_log_info(vchiq_sync_log_level,
-                       "%d: qms %s@%pK,%x (%d->%d)", state->id,
-                       msg_type_str(VCHIQ_MSG_TYPE(msgid)),
-                       header, size, VCHIQ_MSG_SRCPORT(msgid),
-                       VCHIQ_MSG_DSTPORT(msgid));
-               if (size != 0) {
-                       WARN_ON(!((count == 1) && (size == elements[0].size)));
-                       memcpy(header->data, elements[0].data,
-                               elements[0].size);
-               }
                VCHIQ_STATS_INC(state, ctrl_tx_count);
        }
 
                                VCHIQ_MSG_BULK_RX_DONE : VCHIQ_MSG_BULK_TX_DONE;
                        int msgid = VCHIQ_MAKE_MSG(msgtype, service->localport,
                                service->remoteport);
-                       VCHIQ_ELEMENT_T element = { &bulk->actual, 4 };
                        /* Only reply to non-dummy bulk requests */
                        if (bulk->remote_data) {
-                               status = queue_message(service->state, NULL,
-                                       msgid, &element, 1, 4, 0);
+                               status = queue_message(
+                                               service->state,
+                                               NULL,
+                                               msgid,
+                                               memcpy_copy_callback,
+                                               &bulk->actual,
+                                               4,
+                                               0);
                                if (status != VCHIQ_SUCCESS)
                                        break;
                        }
                                struct vchiq_openack_payload ack_payload = {
                                        service->version
                                };
-                               VCHIQ_ELEMENT_T body = {
-                                       &ack_payload,
-                                       sizeof(ack_payload)
-                               };
 
                                if (state->version_common <
                                    VCHIQ_VERSION_SYNCHRONOUS_MODE)
                                if (service->sync &&
                                    (state->version_common >=
                                     VCHIQ_VERSION_SYNCHRONOUS_MODE)) {
-                                       if (queue_message_sync(state, NULL,
+                                       if (queue_message_sync(
+                                               state,
+                                               NULL,
                                                VCHIQ_MAKE_MSG(
                                                        VCHIQ_MSG_OPENACK,
                                                        service->localport,
                                                        remoteport),
-                                               &body, 1, sizeof(ack_payload),
+                                               memcpy_copy_callback,
+                                               &ack_payload,
+                                               sizeof(ack_payload),
                                                0) == VCHIQ_RETRY)
                                                goto bail_not_ready;
                                } else {
-                                       if (queue_message(state, NULL,
-                                               VCHIQ_MAKE_MSG(
+                                       if (queue_message(state,
+                                                       NULL,
+                                                       VCHIQ_MAKE_MSG(
                                                        VCHIQ_MSG_OPENACK,
                                                        service->localport,
                                                        remoteport),
-                                               &body, 1, sizeof(ack_payload),
+                                               memcpy_copy_callback,
+                                               &ack_payload,
+                                               sizeof(ack_payload),
                                                0) == VCHIQ_RETRY)
                                                goto bail_not_ready;
                                }
                service->version,
                service->version_min
        };
-       VCHIQ_ELEMENT_T body = { &payload, sizeof(payload) };
        VCHIQ_STATUS_T status = VCHIQ_SUCCESS;
 
        service->client_id = client_id;
        vchiq_use_service_internal(service);
-       status = queue_message(service->state, NULL,
-               VCHIQ_MAKE_MSG(VCHIQ_MSG_OPEN, service->localport, 0),
-               &body, 1, sizeof(payload), QMFLAGS_IS_BLOCKING);
+       status = queue_message(service->state,
+                              NULL,
+                              VCHIQ_MAKE_MSG(VCHIQ_MSG_OPEN,
+                                             service->localport,
+                                             0),
+                              memcpy_copy_callback,
+                              &payload,
+                              sizeof(payload),
+                              QMFLAGS_IS_BLOCKING);
        if (status == VCHIQ_SUCCESS) {
                /* Wait for the ACK/NAK */
                if (down_interruptible(&service->remove_event) != 0) {
                                VCHIQ_POLL_TXNOTIFY : VCHIQ_POLL_RXNOTIFY);
        } else {
                int payload[2] = { (int)(long)bulk->data, bulk->size };
-               VCHIQ_ELEMENT_T element = { payload, sizeof(payload) };
 
-               status = queue_message(state, NULL,
-                       VCHIQ_MAKE_MSG(dir_msgtype,
-                               service->localport, service->remoteport),
-                       &element, 1, sizeof(payload),
-                       QMFLAGS_IS_BLOCKING |
-                       QMFLAGS_NO_MUTEX_LOCK |
-                       QMFLAGS_NO_MUTEX_UNLOCK);
+               status = queue_message(state,
+                                      NULL,
+                                      VCHIQ_MAKE_MSG(dir_msgtype,
+                                                     service->localport,
+                                                     service->remoteport),
+                                      memcpy_copy_callback,
+                                      &payload,
+                                      sizeof(payload),
+                                      QMFLAGS_IS_BLOCKING |
+                                      QMFLAGS_NO_MUTEX_LOCK |
+                                      QMFLAGS_NO_MUTEX_UNLOCK);
                if (status != VCHIQ_SUCCESS) {
                        goto unlock_both_error_exit;
                }
 
 VCHIQ_STATUS_T
 vchiq_queue_message(VCHIQ_SERVICE_HANDLE_T handle,
-       const VCHIQ_ELEMENT_T *elements, unsigned int count)
+                   ssize_t (*copy_callback)(void *context, void *dest,
+                                            size_t offset, size_t maxsize),
+                   void *context,
+                   size_t size)
 {
        VCHIQ_SERVICE_T *service = find_service_by_handle(handle);
        VCHIQ_STATUS_T status = VCHIQ_ERROR;
 
-       unsigned int size = 0;
-       unsigned int i;
-
        if (!service ||
                (vchiq_check_service(service) != VCHIQ_SUCCESS))
                goto error_exit;
 
-       for (i = 0; i < (unsigned int)count; i++) {
-               if (elements[i].size) {
-                       if (elements[i].data == NULL) {
-                               VCHIQ_SERVICE_STATS_INC(service, error_count);
-                               goto error_exit;
-                       }
-                       size += elements[i].size;
-               }
+       if (!size) {
+               VCHIQ_SERVICE_STATS_INC(service, error_count);
+               goto error_exit;
+
        }
 
        if (size > VCHIQ_MAX_MSG_SIZE) {
                                VCHIQ_MAKE_MSG(VCHIQ_MSG_DATA,
                                        service->localport,
                                        service->remoteport),
-                               elements, count, size, 1);
+                               copy_callback, context, size, 1);
                break;
        case VCHIQ_SRVSTATE_OPENSYNC:
                status = queue_message_sync(service->state, service,
                                VCHIQ_MAKE_MSG(VCHIQ_MSG_DATA,
                                        service->localport,
                                        service->remoteport),
-                               elements, count, size, 1);
+                               copy_callback, context, size, 1);
                break;
        default:
                status = VCHIQ_ERROR;