Mach port right copy的全过程

1. mach_msg_server()(libsyscall\mach\Mach_msg.c)

/*
 *  Routine:    mach_msg_server
 *  Purpose:
 *      A simple generic server function.  Note that changes here
 *      should be considered for duplication above.
 */
mach_msg_return_t
mach_msg_server(
    boolean_t (*demux)(mach_msg_header_t *, mach_msg_header_t *),
    mach_msg_size_t max_size,
    mach_port_t rcv_name,
    mach_msg_options_t options)
{
    mig_reply_error_t *bufRequest, *bufReply;
    mach_msg_size_t request_size;
    mach_msg_size_t new_request_alloc;
    mach_msg_size_t request_alloc;
    mach_msg_size_t trailer_alloc;
    mach_msg_size_t reply_alloc;
    mach_msg_return_t mr;
    kern_return_t kr;
    mach_port_t self = mach_task_self_;
    voucher_mach_msg_state_t old_state = VOUCHER_MACH_MSG_STATE_UNCHANGED;
    boolean_t buffers_swapped = FALSE;

    options &= ~(MACH_SEND_MSG|MACH_RCV_MSG|MACH_RCV_VOUCHER|MACH_RCV_OVERWRITE);

    reply_alloc = (mach_msg_size_t)round_page((options & MACH_SEND_TRAILER) ?
                          (max_size + MAX_TRAILER_SIZE) : max_size);

    kr = vm_allocate(self,
             (vm_address_t *)&bufReply,
             reply_alloc,
             VM_MAKE_TAG(VM_MEMORY_MACH_MSG)|TRUE);
    if (kr != KERN_SUCCESS)
        return kr;

    request_alloc = 0;
    trailer_alloc = REQUESTED_TRAILER_SIZE(options);
    new_request_alloc = (mach_msg_size_t)round_page(max_size + trailer_alloc);

    request_size = (options & MACH_RCV_LARGE) ?
    new_request_alloc : max_size + trailer_alloc;

    for (;;) {
        if (request_alloc < new_request_alloc) {
            request_alloc = new_request_alloc;
            kr = vm_allocate(self,
                     (vm_address_t *)&bufRequest,
                     request_alloc,
                     VM_MAKE_TAG(VM_MEMORY_MACH_MSG)|TRUE);
            if (kr != KERN_SUCCESS) {
                vm_deallocate(self,
                          (vm_address_t)bufReply,
                          reply_alloc);
                return kr;
            }
        }

        mr = mach_msg(&bufRequest->Head, MACH_RCV_MSG|MACH_RCV_VOUCHER|options,
                  0, request_size, rcv_name,
                  MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);

        while (mr == MACH_MSG_SUCCESS) {
            /* we have another request message */

            buffers_swapped = FALSE;
            old_state = voucher_mach_msg_adopt(&bufRequest->Head);

            (void) (*demux)(&bufRequest->Head, &bufReply->Head);

            if (!(bufReply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX)) {
                if (bufReply->RetCode == MIG_NO_REPLY)
                    bufReply->Head.msgh_remote_port = MACH_PORT_NULL;
                else if ((bufReply->RetCode != KERN_SUCCESS) &&
                     (bufRequest->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX)) {
                    /* destroy the request - but not the reply port */
                    bufRequest->Head.msgh_remote_port = MACH_PORT_NULL;
                    mach_msg_destroy(&bufRequest->Head);
                }
            }

            /*
             * We don't want to block indefinitely because the client
             * isn't receiving messages from the reply port.
             * If we have a send-once right for the reply port, then
             * this isn't a concern because the send won't block.
             * If we have a send right, we need to use MACH_SEND_TIMEOUT.
             * To avoid falling off the kernel's fast RPC path,
             * we only supply MACH_SEND_TIMEOUT when absolutely necessary.
             */
            if (bufReply->Head.msgh_remote_port != MACH_PORT_NULL) {
                if (request_alloc == reply_alloc) {
                    mig_reply_error_t *bufTemp;

                    mr = mach_msg(
                          &bufReply->Head,
                          (MACH_MSGH_BITS_REMOTE(bufReply->Head.msgh_bits) ==
                           MACH_MSG_TYPE_MOVE_SEND_ONCE) ?
                          MACH_SEND_MSG|MACH_RCV_MSG|MACH_RCV_TIMEOUT|MACH_RCV_VOUCHER|options :
                          MACH_SEND_MSG|MACH_RCV_MSG|MACH_SEND_TIMEOUT|MACH_RCV_TIMEOUT|MACH_RCV_VOUCHER|options,
                          bufReply->Head.msgh_size, request_size, rcv_name,
                          MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);

                    /* swap request and reply */
                    bufTemp = bufRequest;
                    bufRequest = bufReply;
                    bufReply = bufTemp;
                    buffers_swapped = TRUE;
                } else {
                    mr = mach_msg_overwrite(
                        &bufReply->Head,
                        (MACH_MSGH_BITS_REMOTE(bufReply->Head.msgh_bits) ==
                         MACH_MSG_TYPE_MOVE_SEND_ONCE) ?
                         MACH_SEND_MSG|MACH_RCV_MSG|MACH_RCV_TIMEOUT|MACH_RCV_VOUCHER|options :
                         MACH_SEND_MSG|MACH_RCV_MSG|MACH_SEND_TIMEOUT|MACH_RCV_TIMEOUT|MACH_RCV_VOUCHER|options,
                        bufReply->Head.msgh_size, request_size, rcv_name,
                        MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL,
                        &bufRequest->Head, 0);
                }

                if ((mr != MACH_SEND_INVALID_DEST) &&
                    (mr != MACH_SEND_TIMED_OUT) &&
                    (mr != MACH_RCV_TIMED_OUT)) {

                    voucher_mach_msg_revert(old_state);
                    old_state = VOUCHER_MACH_MSG_STATE_UNCHANGED;

                    continue;
                }
            }
            /* 
             * Need to destroy the reply msg in case if there was a send timeout or
             * invalid destination. The reply msg would be swapped with request msg
             * if buffers_swapped is true, thus destroy request msg instead of
             * reply msg in such cases.
             */
            if (mr != MACH_RCV_TIMED_OUT) {
                if (buffers_swapped) {
                    if (bufRequest->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX)
                        mach_msg_destroy(&bufRequest->Head);
                } else {
                    if (bufReply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX)
                        mach_msg_destroy(&bufReply->Head);
                }
            }
            voucher_mach_msg_revert(old_state);
            old_state = VOUCHER_MACH_MSG_STATE_UNCHANGED;

            mr = mach_msg(&bufRequest->Head, MACH_RCV_MSG|MACH_RCV_VOUCHER|options,
                    0, request_size, rcv_name,
                    MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);

        } /* while (mr == MACH_MSG_SUCCESS) */

        if ((mr == MACH_RCV_TOO_LARGE) && (options & MACH_RCV_LARGE)) {
            new_request_alloc = (mach_msg_size_t)round_page(bufRequest->Head.msgh_size +
                                    trailer_alloc);
            request_size = new_request_alloc;
            vm_deallocate(self,
                      (vm_address_t) bufRequest,
                      request_alloc);
            continue;
        }

        break;

2. mach_msg_overwrite()(libsyscall\mach\Mach_msg.c)

/*
 *  Routine:    mach_msg_overwrite
 *  Purpose:
 *      Send and/or receive a message.  If the message operation
 *      is interrupted, and the user did not request an indication
 *      of that fact, then restart the appropriate parts of the
 *      operation.
 *
 *      Distinct send and receive buffers may be specified.  If
 *      no separate receive buffer is specified, the msg parameter
 *      will be used for both send and receive operations.
 *
 *      In addition to a distinct receive buffer, that buffer may
 *      already contain scatter control information to direct the
 *      receiving of the message.
 */
mach_msg_return_t
mach_msg_overwrite(msg, option, send_size, rcv_limit, rcv_name, timeout, 
           notify, rcv_msg, rcv_scatter_size)
    mach_msg_header_t *msg;
    mach_msg_option_t option;
    mach_msg_size_t send_size;
    mach_msg_size_t rcv_limit;
    mach_port_t rcv_name;
    mach_msg_timeout_t timeout;
    mach_port_t notify;
    mach_msg_header_t *rcv_msg;
    mach_msg_size_t rcv_scatter_size;
{
    mach_msg_return_t mr;

    /*
     * Consider the following cases:
     *  1) Errors in pseudo-receive (eg, MACH_SEND_INTERRUPTED
     *  plus special bits).
     *  2) Use of MACH_SEND_INTERRUPT/MACH_RCV_INTERRUPT options.
     *  3) RPC calls with interruptions in one/both halves.
     *
     * We refrain from passing the option bits that we implement
     * to the kernel.  This prevents their presence from inhibiting
     * the kernel's fast paths (when it checks the option value).
     */

    mr = mach_msg_overwrite_trap(msg, option &~ LIBMACH_OPTIONS,
               send_size, rcv_limit, rcv_name,
               timeout, notify, rcv_msg, rcv_scatter_size);
    if (mr == MACH_MSG_SUCCESS)
        return MACH_MSG_SUCCESS;

    if ((option & MACH_SEND_INTERRUPT) == 0)
        while (mr == MACH_SEND_INTERRUPTED)
            mr = mach_msg_overwrite_trap(msg,
                option &~ LIBMACH_OPTIONS,
                send_size, rcv_limit, rcv_name,
                timeout, notify, rcv_msg, rcv_scatter_size);

    if ((option & MACH_RCV_INTERRUPT) == 0)
        while (mr == MACH_RCV_INTERRUPTED)
            mr = mach_msg_overwrite_trap(msg,
                option &~ (LIBMACH_OPTIONS|MACH_SEND_MSG),
                0, rcv_limit, rcv_name,
                timeout, notify, rcv_msg, rcv_scatter_size);

    return mr;
}

3. mach_msg_overwrite_trap()(osfmk\ipc\Mach_msg.c)

/*
 *  Routine:    mach_msg_overwrite_trap [mach trap]
 *  Purpose:
 *      Possibly send a message; possibly receive a message.
 *  Conditions:
 *      Nothing locked.
 *  Returns:
 *      All of mach_msg_send and mach_msg_receive error codes.
 */

mach_msg_return_t
mach_msg_overwrite_trap(
    struct mach_msg_overwrite_trap_args *args)
{
    mach_vm_address_t   msg_addr = args->msg;
    mach_msg_option_t   option = args->option;
    mach_msg_size_t     send_size = args->send_size;
    mach_msg_size_t     rcv_size = args->rcv_size;
    mach_port_name_t    rcv_name = args->rcv_name;
    mach_msg_timeout_t  msg_timeout = args->timeout;
    __unused mach_port_name_t notify = args->notify;
    mach_vm_address_t   rcv_msg_addr = args->rcv_msg;
    __unused mach_port_seqno_t temp_seqno = 0;

    mach_msg_return_t  mr = MACH_MSG_SUCCESS;
    vm_map_t map = current_map();

    /* Only accept options allowed by the user */
    option &= MACH_MSG_OPTION_USER;

    if (option & MACH_SEND_MSG) {
        ipc_space_t space = current_space();
        ipc_kmsg_t kmsg;

        mr = ipc_kmsg_get(msg_addr, send_size, &kmsg);

        if (mr != MACH_MSG_SUCCESS)
            return mr;

        mr = ipc_kmsg_copyin(kmsg, space, map, &option);

        if (mr != MACH_MSG_SUCCESS) {
            ipc_kmsg_free(kmsg);
            return mr;
        }

        mr = ipc_kmsg_send(kmsg, option, msg_timeout);

        if (mr != MACH_MSG_SUCCESS) {
            mr |= ipc_kmsg_copyout_pseudo(kmsg, space, map, MACH_MSG_BODY_NULL);
            (void) ipc_kmsg_put(msg_addr, kmsg, kmsg->ikm_header->msgh_size);
            return mr;
        }

    }

    if (option & MACH_RCV_MSG) {
        thread_t self = current_thread();
        ipc_space_t space = current_space();
        ipc_object_t object;
        ipc_mqueue_t mqueue;

        mr = ipc_mqueue_copyin(space, rcv_name, &mqueue, &object);
        if (mr != MACH_MSG_SUCCESS) {
            return mr;
        }
        /* hold ref for object */

        if (rcv_msg_addr != (mach_vm_address_t)0)
            self->ith_msg_addr = rcv_msg_addr;
        else
            self->ith_msg_addr = msg_addr;
        self->ith_object = object;
        self->ith_msize = rcv_size;
        self->ith_option = option;
        self->ith_receiver_name = MACH_PORT_NULL;
        self->ith_continuation = thread_syscall_return;

        ipc_mqueue_receive(mqueue, option, rcv_size, msg_timeout, THREAD_ABORTSAFE);
        if ((option & MACH_RCV_TIMEOUT) && msg_timeout == 0)
            thread_poll_yield(self);
        return mach_msg_receive_results();
    }

    return MACH_MSG_SUCCESS;
}

4. mach_msg_receive_results()(osfmk\ipc\Mach_msg.c)

/*
 *  Routine:    mach_msg_receive_results
 *  Purpose:
 *      Receive a message.
 *  Conditions:
 *      Nothing locked.
 *  Returns:
 *      MACH_MSG_SUCCESS    Received a message.
 *      MACH_RCV_INVALID_NAME   The name doesn't denote a right,
 *          or the denoted right is not receive or port set.
 *      MACH_RCV_IN_SET     Receive right is a member of a set.
 *      MACH_RCV_TOO_LARGE  Message wouldn't fit into buffer.
 *      MACH_RCV_TIMED_OUT  Timeout expired without a message.
 *      MACH_RCV_INTERRUPTED    Reception interrupted.
 *      MACH_RCV_PORT_DIED  Port/set died while receiving.
 *      MACH_RCV_PORT_CHANGED   Port moved into set while receiving.
 *      MACH_RCV_INVALID_DATA   Couldn't copy to user buffer.
 *      MACH_RCV_INVALID_NOTIFY Bad notify port.
 *      MACH_RCV_HEADER_ERROR
 */

mach_msg_return_t
mach_msg_receive_results(void)
{
    thread_t          self = current_thread();
    ipc_space_t       space = current_space();
    vm_map_t          map = current_map();

    //self指向的是thread结构体中的变量,其中kmsg是接受的消息
    //下面是thread结构体中的处理由port接受消息的子结构体
    //struct {
    //      mach_msg_return_t   state;      /* receive state */
    //      mach_port_seqno_t   seqno;      /* seqno of recvd message */
    //      ipc_object_t        object;     /* object received on */
    //      mach_vm_address_t   msg_addr;   /* receive buffer pointer */
    //      mach_msg_size_t     msize;      /* max size for recvd msg */
    //      mach_msg_option_t   option;     /* options for receive */
    //      mach_port_name_t    receiver_name;  /* the receive port name */
    //      struct ipc_kmsg     *kmsg;      /* received message */
    //      mach_msg_continue_t continuation;
    //  } receive;

    //这些结构体的引用都被定义为如下宏
    //#define ith_state             saved.receive.state
    //#define ith_object            saved.receive.object
    //#define ith_msg_addr          saved.receive.msg_addr
    //#define ith_msize             saved.receive.msize
    //#define ith_option            saved.receive.option
    //#define ith_receiver_name     saved.receive.receiver_name
    //#define ith_continuation      saved.receive.continuation
    //#define ith_kmsg              saved.receive.kmsg
    //#define ith_seqno             saved.receive.seqno

    ipc_object_t      object = self->ith_object;          /* object received on */
    mach_msg_return_t mr = self->ith_state;             /* receive state */
    mach_vm_address_t msg_addr = self->ith_msg_addr;   /* receive buffer pointer */
    mach_msg_option_t option = self->ith_option;      /* options for receive */
    ipc_kmsg_t        kmsg = self->ith_kmsg;          /* received message */
    mach_port_seqno_t seqno = self->ith_seqno;        /* seqno序号 of recvd message */
    mach_msg_trailer_size_t trailer_size;

    io_release(object);

    if (mr != MACH_MSG_SUCCESS) {

      if (mr == MACH_RCV_TOO_LARGE ) {
        if (option & MACH_RCV_LARGE) {
          /*
           * We need to inform the user-level code that it needs more
           * space.  The value for how much space was returned in the
           * msize save area instead of the message (which was left on
           * the queue).
           */
          if (option & MACH_RCV_LARGE_IDENTITY) {
              if (copyout((char *) &self->ith_receiver_name,
                  msg_addr + offsetof(mach_msg_user_header_t, msgh_local_port),
                  sizeof(mach_port_name_t)))
                  mr = MACH_RCV_INVALID_DATA;
          }
          if (copyout((char *) &self->ith_msize,
              msg_addr + offsetof(mach_msg_user_header_t, msgh_size),
              sizeof(mach_msg_size_t)))
            mr = MACH_RCV_INVALID_DATA;
        } else {

            /* discard importance in message */
            ipc_importance_clean(kmsg);

        if (msg_receive_error(kmsg, msg_addr, option, seqno, space)
            == MACH_RCV_INVALID_DATA)
            mr = MACH_RCV_INVALID_DATA;
        }
      }
      return mr;
    }

#if IMPORTANCE_INHERITANCE

    /* adopt/transform any importance attributes carried in the message */
    ipc_importance_receive(kmsg, option);

#endif  /* IMPORTANCE_INHERITANCE */

    /* auto redeem the voucher in the message */
    ipc_voucher_receive_postprocessing(kmsg, option);

    trailer_size = ipc_kmsg_add_trailer(kmsg, space, option, self, seqno, FALSE, 
            kmsg->ikm_header->msgh_remote_port->ip_context);
    //将消息从space中拷贝出来
    mr = ipc_kmsg_copyout(kmsg, space, map, MACH_MSG_BODY_NULL, option);

    if (mr != MACH_MSG_SUCCESS) {
        /* already received importance, so have to undo that here */
        ipc_importance_unreceive(kmsg, option);

        if ((mr &~ MACH_MSG_MASK) == MACH_RCV_BODY_ERROR) {
            if (ipc_kmsg_put(msg_addr, kmsg, kmsg->ikm_header->msgh_size +
               trailer_size) == MACH_RCV_INVALID_DATA)
                mr = MACH_RCV_INVALID_DATA;
        } 
        else {
            if (msg_receive_error(kmsg, msg_addr, option, seqno, space) 
                        == MACH_RCV_INVALID_DATA)
                mr = MACH_RCV_INVALID_DATA;
        }
    } else {
        mr = ipc_kmsg_put(msg_addr,
                  kmsg,
                  kmsg->ikm_header->msgh_size + 
                  trailer_size);
    }

    return mr;
}

5. ipc_kmsg_copyout()(osfmk\ipc\Ipc_kmsg.c)

mach_msg_return_t
ipc_kmsg_copyout(
    ipc_kmsg_t      kmsg,
    ipc_space_t     space,
    vm_map_t        map,
    mach_msg_body_t     *slist,
     mach_msg_option_t  option)
{
    mach_msg_return_t mr;
    //拷贝header部分
    mr = ipc_kmsg_copyout_header(kmsg, space, option);
    if (mr != MACH_MSG_SUCCESS) {
        return mr;
    }
    //拷贝复杂消息中的body部分
    if (kmsg->ikm_header->msgh_bits & MACH_MSGH_BITS_COMPLEX) {
        mr = ipc_kmsg_copyout_body(kmsg, space, map, slist);

        if (mr != MACH_MSG_SUCCESS)
            mr |= MACH_RCV_BODY_ERROR;
    }

    return mr;
}

6. ipc_kmsg_copyout_body()(osfmk\ipc\Ipc_kmsg.c)

先介绍一个结构体:

/*
 *  This structure is only the header for a kmsg buffer;
 *  the actual buffer is normally larger.  The rest of the buffer
 *  holds the body of the message.
 *
 *  In a kmsg, the port fields hold pointers to ports instead
 *  of port names.  These pointers hold references.
 *
 *  The ikm_header.msgh_remote_port field is the destination
 *  of the message.
 */

struct ipc_kmsg {
    mach_msg_size_t             ikm_size;
    struct ipc_kmsg             *ikm_next;  /* next message on port/discard queue */
    struct ipc_kmsg             *ikm_prev;  /* prev message on port/discard queue */
    mach_msg_header_t           *ikm_header;
    ipc_port_t              ikm_prealloc;   /* port we were preallocated from */
    ipc_port_t              ikm_voucher;    /* voucher port carried */
    struct ipc_importance_elem      *ikm_importance;/* inherited from */
    queue_chain_t               ikm_inheritance;/* inherited from link */
};
/*
 *  Routine:    ipc_kmsg_copyout_body
 *  Purpose:
 *      "Copy-out" port rights and out-of-line memory
 *      in the body of a message.
 *
 *      The error codes are a combination of special bits.
 *      The copyout proceeds despite errors.
 *  Conditions:
 *      Nothing locked.
 *  Returns:
 *      MACH_MSG_SUCCESS    Successful copyout.
 *      MACH_MSG_IPC_SPACE  No room for port right in name space.
 *      MACH_MSG_VM_SPACE   No room for memory in address space.
 *      MACH_MSG_IPC_KERNEL Resource shortage handling port right.
 *      MACH_MSG_VM_KERNEL  Resource shortage handling memory.
 *      MACH_MSG_INVALID_RT_DESCRIPTOR Descriptor incompatible with RT
 */

mach_msg_return_t
ipc_kmsg_copyout_body(
        ipc_kmsg_t      kmsg,
    ipc_space_t     space,
    vm_map_t        map,
    mach_msg_body_t     *slist)
{
    mach_msg_body_t         *body;
    mach_msg_descriptor_t   *kern_dsc, *user_dsc;
    mach_msg_descriptor_t   *saddr;
    mach_msg_type_number_t  dsc_count, sdsc_count;
    int i;
    mach_msg_return_t       mr = MACH_MSG_SUCCESS;
    boolean_t           is_task_64bit = (map->max_offset > VM_MAX_ADDRESS);

    body = (mach_msg_body_t *) (kmsg->ikm_header + 1);
    dsc_count = body->msgh_descriptor_count;
    kern_dsc = (mach_msg_descriptor_t *) (body + 1);
    /* Point user_dsc just after the end of all the descriptors */
    user_dsc = &kern_dsc[dsc_count];

    /* Do scatter list setup */
    if (slist != MACH_MSG_BODY_NULL) {
    panic("Scatter lists disabled");
    saddr = (mach_msg_descriptor_t *) (slist + 1);
    sdsc_count = slist->msgh_descriptor_count;
    }
    else {
    saddr = MACH_MSG_DESCRIPTOR_NULL;
    sdsc_count = 0;
    }

    /* Now process the descriptors */
    for (i = dsc_count-1; i >= 0; i--) {
        switch (kern_dsc[i].type.type) {

            case MACH_MSG_PORT_DESCRIPTOR: 
                user_dsc = ipc_kmsg_copyout_port_descriptor(&kern_dsc[i], user_dsc, space, &mr);
                break;
            case MACH_MSG_OOL_VOLATILE_DESCRIPTOR:
            case MACH_MSG_OOL_DESCRIPTOR : 
                user_dsc = ipc_kmsg_copyout_ool_descriptor(
                        (mach_msg_ool_descriptor_t *)&kern_dsc[i], user_dsc, is_task_64bit, map, &mr);
                break;
            case MACH_MSG_OOL_PORTS_DESCRIPTOR : 
                //将kern_dsc拷贝都user_dscuser_dsc = ipc_kmsg_copyout_ool_ports_descriptor(
                        (mach_msg_ool_ports_descriptor_t *)&kern_dsc[i], user_dsc, is_task_64bit, map, space, kmsg, &mr);
                break;
            default : {
                          panic("untyped IPC copyout body: invalid message descriptor");
                      }
        }
    }
    //检测是否拷贝成功
    if(user_dsc != kern_dsc) {
        vm_offset_t dsc_adjust = (vm_offset_t)user_dsc - (vm_offset_t)kern_dsc;
        memmove((char *)((vm_offset_t)kmsg->ikm_header + dsc_adjust), kmsg->ikm_header, sizeof(mach_msg_base_t));
        kmsg->ikm_header = (mach_msg_header_t *)((vm_offset_t)kmsg->ikm_header + dsc_adjust);
        /* Update the message size for the smaller user representation */
        kmsg->ikm_header->msgh_size -= (mach_msg_size_t)dsc_adjust;
    }

    return mr;
}

7. ipc_kmsg_copyout_ool_ports_descriptor()(osfmk\ipc\Ipc_kmsg.c)

ipc_kmsg_copyout_ool_ports_descriptor(mach_msg_ool_ports_descriptor_t *dsc,
        mach_msg_descriptor_t *user_dsc,
        int is_64bit,
        vm_map_t map,
        ipc_space_t space,
        ipc_kmsg_t kmsg,
        mach_msg_return_t *mr)
{
    mach_vm_offset_t        rcv_addr = 0;
    mach_msg_type_name_t        disp;
    mach_msg_type_number_t      count, i;
    vm_size_t                   ports_length, names_length;

    mach_msg_copy_options_t copy_options = MACH_MSG_VIRTUAL_COPY;

    //SKIP_PORT_DESCRIPTORS(saddr, sdsc_count);

    count = dsc->count;
    disp = dsc->disposition;
    ports_length = count * sizeof(mach_port_t);
    names_length = count * sizeof(mach_port_name_t);

    if (ports_length != 0 && dsc->address != 0) {

        /*
         * Check to see if there is an overwrite descriptor
         * specified in the scatter list for this ool data.
         * The descriptor has already been verified.
         */

        ... ...

        if (copy_options == MACH_MSG_VIRTUAL_COPY) {
            /*
             * Dynamically allocate the region
             */
            int anywhere = VM_FLAGS_ANYWHERE;
        if (vm_kernel_map_is_kernel(map)) anywhere |= VM_MAKE_TAG(VM_KERN_MEMORY_IPC);
        else                              anywhere |= VM_MAKE_TAG(VM_MEMORY_MACH_MSG);

            kern_return_t kr;
            //分配一个buff
            if ((kr = mach_vm_allocate(map, &rcv_addr, 
                            (mach_vm_size_t)names_length,
                            anywhere)) != KERN_SUCCESS) {
                ipc_kmsg_clean_body(kmsg, 1, (mach_msg_descriptor_t *)dsc);
                rcv_addr = 0;

                if (kr == KERN_RESOURCE_SHORTAGE){
                    *mr |= MACH_MSG_VM_KERNEL;
                } else {
                    *mr |= MACH_MSG_VM_SPACE;
                }
            }
        }

        /*
         * Handle the port rights and copy out the names
         * for those rights out to user-space.
         */
        if (rcv_addr != 0) {
            mach_port_t *objects = (mach_port_t *) dsc->address;
            mach_port_name_t *names = (mach_port_name_t *) dsc->address;

            /* copyout port rights carried in the message */
            //拷贝消息中的port rights
            for ( i = 0; i < count ; i++) {
                ipc_object_t object = (ipc_object_t)objects[i];

                *mr |= ipc_kmsg_copyout_object(space, object,
                        disp, &names[i]);
            }

            /* copyout to memory allocated above */
            void *data = dsc->address;
            if (copyoutmap(map, data, rcv_addr, names_length) != KERN_SUCCESS)
                *mr |= MACH_MSG_VM_SPACE;
            kfree(data, ports_length);
        }
    } else {
        rcv_addr = 0;
    }

    /*
     * Now update the descriptor based on the information
     * calculated above.
     */
    if(current_task() == kernel_task) {
        mach_msg_ool_ports_descriptor_t *user_ool_dsc = (typeof(user_ool_dsc))user_dsc;
        user_ool_dsc--;

        user_ool_dsc->address = (void *)(uintptr_t)rcv_addr;
        user_ool_dsc->deallocate = (copy_options == MACH_MSG_VIRTUAL_COPY) ?
            TRUE : FALSE;
        user_ool_dsc->copy = copy_options;
        user_ool_dsc->disposition = disp;
        user_ool_dsc->type = MACH_MSG_OOL_PORTS_DESCRIPTOR;
        user_ool_dsc->count = count;

        user_dsc = (typeof(user_dsc))user_ool_dsc;
    } if (is_64bit) {
        mach_msg_ool_ports_descriptor64_t *user_ool_dsc = (typeof(user_ool_dsc))user_dsc;
        user_ool_dsc--;

        user_ool_dsc->address = rcv_addr;
        user_ool_dsc->deallocate = (copy_options == MACH_MSG_VIRTUAL_COPY) ?
            TRUE : FALSE;
        user_ool_dsc->copy = copy_options;
        user_ool_dsc->disposition = disp;
        user_ool_dsc->type = MACH_MSG_OOL_PORTS_DESCRIPTOR;
        user_ool_dsc->count = count;

        user_dsc = (typeof(user_dsc))user_ool_dsc;
    } else {
        mach_msg_ool_ports_descriptor32_t *user_ool_dsc = (typeof(user_ool_dsc))user_dsc;
        user_ool_dsc--;

        user_ool_dsc->address = CAST_DOWN_EXPLICIT(uint32_t, rcv_addr);
        user_ool_dsc->count = count;
        user_ool_dsc->deallocate = (copy_options == MACH_MSG_VIRTUAL_COPY) ?
            TRUE : FALSE;
        user_ool_dsc->copy = copy_options;
        user_ool_dsc->disposition = disp;
        user_ool_dsc->type = MACH_MSG_OOL_PORTS_DESCRIPTOR;

        user_dsc = (typeof(user_dsc))user_ool_dsc;
    }
    return user_dsc;
}

8. ipc_kmsg_copyout_object()(osfmk\ipc\Ipc_kmsg.c)

/*
 *  Routine:    ipc_kmsg_copyout_object
 *  Purpose:
 *      Copy-out a port right.  Always returns a name,
 *      even for unsuccessful return codes.  Always
 *      consumes the supplied object.
 *  Conditions:
 *      Nothing locked.
 *  Returns:
 *      MACH_MSG_SUCCESS    The space acquired the right
 *          (name is valid) or the object is dead (MACH_PORT_DEAD).
 *      MACH_MSG_IPC_SPACE  No room in space for the right,
 *          or the space is dead.  (Name is MACH_PORT_NULL.)
 *      MACH_MSG_IPC_KERNEL Kernel resource shortage.
 *          (Name is MACH_PORT_NULL.)
 */

mach_msg_return_t
ipc_kmsg_copyout_object(
    ipc_space_t     space,
    ipc_object_t        object,
    mach_msg_type_name_t    msgt_name,
    mach_port_name_t    *namep)
{
    kern_return_t kr;

    if (!IO_VALID(object)) {
        *namep = CAST_MACH_PORT_TO_NAME(object);
        return MACH_MSG_SUCCESS;
    }
    //拷贝一个port right
    kr = ipc_object_copyout(space, object, msgt_name, TRUE, namep);
    if (kr != KERN_SUCCESS) {
        ipc_object_destroy(object, msgt_name);

        if (kr == KERN_INVALID_CAPABILITY)
            *namep = MACH_PORT_DEAD;
        else {
            *namep = MACH_PORT_NULL;

            if (kr == KERN_RESOURCE_SHORTAGE)
                return MACH_MSG_IPC_KERNEL;
            else
                return MACH_MSG_IPC_SPACE;
        }
    }

    return MACH_MSG_SUCCESS;
}

9. ipc_object_copyout()(osfmk\ipc\Ipc_object.c)

/*
 *  Routine:    ipc_object_copyout
 *  Purpose:
 *      Copyout a capability, placing it into a space.
 *      If successful, consumes a ref for the object.
 *  Conditions:
 *      Nothing locked.
 *  Returns:
 *      KERN_SUCCESS        Copied out object, consumed ref.
 *      KERN_INVALID_TASK   The space is dead.
 *      KERN_INVALID_CAPABILITY The object is dead.
 *      KERN_NO_SPACE       No room in space for another right.
 *      KERN_RESOURCE_SHORTAGE  No memory available.
 *      KERN_UREFS_OVERFLOW Urefs limit exceeded
 *          and overflow wasn't specified.
 */

kern_return_t
ipc_object_copyout(
    ipc_space_t     space,
    ipc_object_t        object,
    mach_msg_type_name_t    msgt_name,
    boolean_t       overflow,
    mach_port_name_t    *namep)
{
    mach_port_name_t name;
    ipc_entry_t entry;
    kern_return_t kr;

    assert(IO_VALID(object));
    assert(io_otype(object) == IOT_PORT);

    is_write_lock(space);

    for (;;) {
        if (!is_active(space)) {
            is_write_unlock(space);
            return KERN_INVALID_TASK;
        }
        //这一步是判断space中是否已经存在相应的port
        if ((msgt_name != MACH_MSG_TYPE_PORT_SEND_ONCE) &&
            ipc_right_reverse(space, object, &name, &entry)) { 
            /* object is locked and active */

            assert(entry->ie_bits & MACH_PORT_TYPE_SEND_RECEIVE);
            break;
        }
        //如果没有,则分配一个entry来存储port
        name = CAST_MACH_PORT_TO_NAME(object);
        kr = ipc_entry_get(space, &name, &entry);
        if (kr != KERN_SUCCESS) {
            /* unlocks/locks space, so must start again */

            kr = ipc_entry_grow_table(space, ITS_SIZE_NONE);
            if (kr != KERN_SUCCESS)
                return kr; /* space is unlocked */

            continue;
        }

        assert(IE_BITS_TYPE(entry->ie_bits) == MACH_PORT_TYPE_NONE);
        assert(entry->ie_object == IO_NULL);

        io_lock(object);
        if (!io_active(object)) {
            io_unlock(object);
            ipc_entry_dealloc(space, name, entry);
            is_write_unlock(space);
            return KERN_INVALID_CAPABILITY;
        }

        entry->ie_object = object;
        break;
    }

    /* space is write-locked and active, object is locked and active */

    //called when a right will be copied into a space
    kr = ipc_right_copyout(space, name, entry,
                   msgt_name, overflow, object);

    /* object is unlocked */
    is_write_unlock(space);

    if (kr == KERN_SUCCESS)
        *namep = name;
    return kr;
}

10. ipc_right_copyout()(osfmk\ipc\Ipc_right.c)

/*
 *  Routine:    ipc_right_copyout
 *  Purpose:
 *      Copyout a capability to a space.
 *      If successful, consumes a ref for the object.
 *
 *      Always succeeds when given a newly-allocated entry,
 *      because user-reference overflow isn't a possibility.
 *
 *      If copying out the object would cause the user-reference
 *      count in the entry to overflow, and overflow is TRUE,
 *      then instead the user-reference count is left pegged
 *      to its maximum value and the copyout succeeds anyway.
 *  Conditions:
 *      The space is write-locked and active.
 *      The object is locked and active.
 *      The object is unlocked; the space isn't.
 *  Returns:
 *      KERN_SUCCESS        Copied out capability.
 *      KERN_UREFS_OVERFLOW User-refs would overflow;
 *          guaranteed not to happen with a fresh entry
 *          or if overflow=TRUE was specified.
 */

kern_return_t
ipc_right_copyout(
    ipc_space_t     space,
    mach_port_name_t    name,
    ipc_entry_t     entry,
    mach_msg_type_name_t    msgt_name,
    boolean_t       overflow,
    ipc_object_t        object)
{
    ipc_entry_bits_t bits;
    ipc_port_t port;

    bits = entry->ie_bits;

    assert(IO_VALID(object));
    assert(io_otype(object) == IOT_PORT);
    assert(io_active(object));
    assert(entry->ie_object == object);

    port = (ipc_port_t) object;

    switch (msgt_name) {
        case MACH_MSG_TYPE_PORT_SEND_ONCE:

        assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_NONE);
        assert(port->ip_sorights > 0);

        /* transfer send-once right and ref to entry */
        ip_unlock(port);

        entry->ie_bits = bits | (MACH_PORT_TYPE_SEND_ONCE | 1);
        ipc_entry_modified(space, name, entry);
        break;

        case MACH_MSG_TYPE_PORT_SEND:
        assert(port->ip_srights > 0);

        if (bits & MACH_PORT_TYPE_SEND) {
            mach_port_urefs_t urefs = IE_BITS_UREFS(bits);

            assert(port->ip_srights > 1);
            assert(urefs > 0);
            assert(urefs < MACH_PORT_UREFS_MAX);
            //判断urefs是否达到最大值,如果达到最大值,就不用再+1了,否了溢出到type段了
            if (urefs+1 == MACH_PORT_UREFS_MAX) {
                if (overflow) {
                    /* leave urefs pegged to maximum */

                    port->ip_srights--;
                    ip_unlock(port);
                    ip_release(port);
                    return KERN_SUCCESS;
                }

                ip_unlock(port);
                return KERN_UREFS_OVERFLOW;
            }
            port->ip_srights--;
            ip_unlock(port);
            ip_release(port);

        } else if (bits & MACH_PORT_TYPE_RECEIVE) {
            assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_RECEIVE);
            assert(IE_BITS_UREFS(bits) == 0);

            /* transfer send right to entry */
            ip_unlock(port);
            ip_release(port);

        } else {
            assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_NONE);
            assert(IE_BITS_UREFS(bits) == 0);

            /* transfer send right and ref to entry */
            ip_unlock(port);

            /* entry is locked holding ref, so can use port */

            ipc_hash_insert(space, (ipc_object_t) port,
                    name, entry);
        }
        //如果urefs没有达到最大值,则将uref递增1
        entry->ie_bits = (bits | MACH_PORT_TYPE_SEND) + 1;
        ipc_entry_modified(space, name, entry);
        break;

        case MACH_MSG_TYPE_PORT_RECEIVE: {
        ipc_port_t dest;

#if IMPORTANCE_INHERITANCE
        natural_t assertcnt = port->ip_impcount;
#endif /* IMPORTANCE_INHERITANCE */

        assert(port->ip_mscount == 0);
        assert(port->ip_receiver_name == MACH_PORT_NULL);
        dest = port->ip_destination;

        port->ip_receiver_name = name;
        port->ip_receiver = space;

        assert((bits & MACH_PORT_TYPE_RECEIVE) == 0);

        if (bits & MACH_PORT_TYPE_SEND) {
            assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND);
            assert(IE_BITS_UREFS(bits) > 0);
            assert(port->ip_srights > 0);

            ip_unlock(port);
            ip_release(port);

            /* entry is locked holding ref, so can use port */

            ipc_hash_delete(space, (ipc_object_t) port,
                    name, entry);
        } else {
            assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_NONE);
            assert(IE_BITS_UREFS(bits) == 0);

            /* transfer ref to entry */
            ip_unlock(port);
        }

        entry->ie_bits = bits | MACH_PORT_TYPE_RECEIVE;
        ipc_entry_modified(space, name, entry);

        if (dest != IP_NULL) {
#if IMPORTANCE_INHERITANCE
            /*
             * Deduct the assertion counts we contributed to
             * the old destination port.  They've already
             * been reflected into the task as a result of
             * getting enqueued.
             */
            ip_lock(dest);
            ipc_port_impcount_delta(dest, 0 - assertcnt, IP_NULL);
            ip_unlock(dest);
#endif /* IMPORTANCE_INHERITANCE */
            ip_release(dest);
        }
        break;
        }

        default:
        panic("ipc_right_copyout: strange rights");
    }
    return KERN_SUCCESS;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值