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_dsc中
user_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;
}