123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594 |
- /*
- * Mach Operating System
- * Copyright (c) 1991,1990,1989 Carnegie Mellon University.
- * Copyright (c) 1993,1994 The University of Utah and
- * the Computer Systems Laboratory (CSL).
- * All rights reserved.
- *
- * Permission to use, copy, modify and distribute this software and its
- * documentation is hereby granted, provided that both the copyright
- * notice and this permission notice appear in all copies of the
- * software, derivative works or modified versions, and any portions
- * thereof, and that both notices appear in supporting documentation.
- *
- * CARNEGIE MELLON, THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF
- * THIS SOFTWARE IN ITS "AS IS" CONDITION, AND DISCLAIM ANY LIABILITY
- * OF ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF
- * THIS SOFTWARE.
- *
- * Carnegie Mellon requests users of this software to return to
- *
- * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
- * School of Computer Science
- * Carnegie Mellon University
- * Pittsburgh PA 15213-3890
- *
- * any improvements or extensions that they make and grant Carnegie Mellon
- * the rights to redistribute these changes.
- */
- /*
- */
- /*
- * File: ipc/mach_msg.c
- * Author: Rich Draves
- * Date: 1989
- *
- * Exported message traps. See mach/message.h.
- */
- #include <glue/gnulinux.h>
- #include <linux/printk.h>
- #include <mach/kern_return.h>
- #include <mach/port.h>
- #include <mach/message.h>
- #include <kern/assert.h>
- #include <kern/counters.h>
- #include <kern/debug.h>
- #include <kern/lock.h>
- #include <kern/printf.h>
- #include <kern/sched_prim.h>
- #include <kern/ipc_sched.h>
- #include <kern/exception.h>
- #include <vm/vm_map.h>
- #include <ipc/ipc_kmsg.h>
- #include <ipc/ipc_marequest.h>
- #include <ipc/ipc_mqueue.h>
- #include <ipc/ipc_object.h>
- #include <ipc/ipc_notify.h>
- #include <ipc/ipc_port.h>
- #include <ipc/ipc_pset.h>
- #include <ipc/ipc_space.h>
- #include <ipc/ipc_thread.h>
- #include <ipc/ipc_entry.h>
- #include <ipc/mach_msg.h>
- #include <glue/locore.h>
- #define thread_syscall_return(ret) return ret
- /*
- * Routine: mach_msg_send
- * Purpose:
- * Send a message.
- * Conditions:
- * Nothing locked.
- * Returns:
- * MACH_MSG_SUCCESS Sent the message.
- * MACH_SEND_MSG_TOO_SMALL Message smaller than a header.
- * MACH_SEND_NO_BUFFER Couldn't allocate buffer.
- * MACH_SEND_INVALID_DATA Couldn't copy message data.
- * MACH_SEND_INVALID_HEADER
- * Illegal value in the message header bits.
- * MACH_SEND_INVALID_DEST The space is dead.
- * MACH_SEND_INVALID_NOTIFY Bad notify port.
- * MACH_SEND_INVALID_DEST Can't copyin destination port.
- * MACH_SEND_INVALID_REPLY Can't copyin reply port.
- * MACH_SEND_TIMED_OUT Timeout expired without delivery.
- * MACH_SEND_INTERRUPTED Delivery interrupted.
- * MACH_SEND_NO_NOTIFY Can't allocate a msg-accepted request.
- * MACH_SEND_WILL_NOTIFY Msg-accepted notif. requested.
- * MACH_SEND_NOTIFY_IN_PROGRESS
- * This space has already forced a message to this port.
- */
- mach_msg_return_t
- mach_msg_send(
- mach_msg_header_t *msg,
- mach_msg_option_t option,
- mach_msg_size_t send_size,
- mach_msg_timeout_t time_out,
- mach_port_t notify)
- {
- ipc_space_t space = current_space();
- vm_map_t map = current_map();
- ipc_kmsg_t kmsg;
- mach_msg_return_t mr;
- mr = ipc_kmsg_get(msg, send_size, &kmsg);
- if (mr != MACH_MSG_SUCCESS)
- return mr;
- if (option & MACH_SEND_CANCEL) {
- if (notify == MACH_PORT_NULL)
- mr = MACH_SEND_INVALID_NOTIFY;
- else
- mr = ipc_kmsg_copyin(kmsg, space, map, notify);
- } else
- mr = ipc_kmsg_copyin(kmsg, space, map, MACH_PORT_NULL);
- if (mr != MACH_MSG_SUCCESS) {
- ikm_free(kmsg);
- return mr;
- }
- if (option & MACH_SEND_NOTIFY) {
- mr = ipc_mqueue_send(kmsg, MACH_SEND_TIMEOUT,
- ((option & MACH_SEND_TIMEOUT) ?
- time_out : MACH_MSG_TIMEOUT_NONE));
- if (mr == MACH_SEND_TIMED_OUT) {
- ipc_port_t dest = (ipc_port_t)
- kmsg->ikm_header.msgh_remote_port;
- if (notify == MACH_PORT_NULL)
- mr = MACH_SEND_INVALID_NOTIFY;
- else
- mr = ipc_marequest_create(space, dest,
- notify, &kmsg->ikm_marequest);
- if (mr == MACH_MSG_SUCCESS) {
- ipc_mqueue_send_always(kmsg);
- return MACH_SEND_WILL_NOTIFY;
- }
- }
- } else
- mr = ipc_mqueue_send(kmsg, option & MACH_SEND_TIMEOUT,
- time_out);
- if (mr != MACH_MSG_SUCCESS) {
- mr |= ipc_kmsg_copyout_pseudo(kmsg, space, map);
- assert(kmsg->ikm_marequest == IMAR_NULL);
- (void) ipc_kmsg_put(msg, kmsg, kmsg->ikm_header.msgh_size);
- }
- return mr;
- }
- /*
- * Routine: mach_msg_receive
- * 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(
- mach_msg_header_t *msg,
- mach_msg_option_t option,
- mach_msg_size_t rcv_size,
- mach_port_t rcv_name,
- mach_msg_timeout_t time_out,
- mach_port_t notify)
- {
- ipc_thread_t self = current_thread();
- ipc_space_t space = current_space();
- vm_map_t map = current_map();
- ipc_object_t object;
- ipc_mqueue_t mqueue;
- ipc_kmsg_t kmsg;
- mach_port_seqno_t seqno;
- mach_msg_return_t mr;
- mr = ipc_mqueue_copyin(space, rcv_name, &mqueue, &object);
- if (mr != MACH_MSG_SUCCESS)
- return mr;
- /* hold ref for object; mqueue is locked */
- /*
- * ipc_mqueue_receive may not return, because if we block
- * then our kernel stack may be discarded. So we save
- * state here for mach_msg_receive_continue to pick up.
- */
- self->ith_msg = msg;
- self->ith_option = option;
- self->ith_rcv_size = rcv_size;
- self->ith_timeout = time_out;
- self->ith_notify = notify;
- self->ith_object = object;
- self->ith_mqueue = mqueue;
- if (option & MACH_RCV_LARGE) {
- mr = ipc_mqueue_receive(mqueue, option & MACH_RCV_TIMEOUT,
- rcv_size, time_out,
- FALSE, mach_msg_receive_continue,
- &kmsg, &seqno);
- /* mqueue is unlocked */
- ipc_object_release(object);
- if (mr != MACH_MSG_SUCCESS) {
- if (mr == MACH_RCV_TOO_LARGE) {
- mach_msg_size_t real_size =
- (mach_msg_size_t) (vm_offset_t) kmsg;
- assert(real_size > rcv_size);
- (void) copyout(&real_size,
- &msg->msgh_size,
- sizeof(mach_msg_size_t));
- }
- return mr;
- }
- kmsg->ikm_header.msgh_seqno = seqno;
- assert(kmsg->ikm_header.msgh_size <= rcv_size);
- } else {
- mr = ipc_mqueue_receive(mqueue, option & MACH_RCV_TIMEOUT,
- MACH_MSG_SIZE_MAX, time_out,
- FALSE, mach_msg_receive_continue,
- &kmsg, &seqno);
- /* mqueue is unlocked */
- ipc_object_release(object);
- if (mr != MACH_MSG_SUCCESS)
- return mr;
- kmsg->ikm_header.msgh_seqno = seqno;
- if (kmsg->ikm_header.msgh_size > rcv_size) {
- ipc_kmsg_copyout_dest(kmsg, space);
- (void) ipc_kmsg_put(msg, kmsg, sizeof *msg);
- return MACH_RCV_TOO_LARGE;
- }
- }
- if (option & MACH_RCV_NOTIFY) {
- if (notify == MACH_PORT_NULL)
- mr = MACH_RCV_INVALID_NOTIFY;
- else
- mr = ipc_kmsg_copyout(kmsg, space, map, notify);
- } else
- mr = ipc_kmsg_copyout(kmsg, space, map, MACH_PORT_NULL);
- if (mr != MACH_MSG_SUCCESS) {
- if ((mr &~ MACH_MSG_MASK) == MACH_RCV_BODY_ERROR) {
- (void) ipc_kmsg_put(msg, kmsg,
- kmsg->ikm_header.msgh_size);
- } else {
- ipc_kmsg_copyout_dest(kmsg, space);
- (void) ipc_kmsg_put(msg, kmsg, sizeof *msg);
- }
- return mr;
- }
- return ipc_kmsg_put(msg, kmsg, kmsg->ikm_header.msgh_size);
- }
- /*
- * Routine: mach_msg_receive_continue
- * Purpose:
- * Continue after blocking for a message.
- * Conditions:
- * Nothing locked. We are running on a new kernel stack,
- * with the receive state saved in the thread. From here
- * control goes back to user space.
- */
- void
- mach_msg_receive_continue(void)
- {
- ipc_thread_t self = current_thread();
- ipc_space_t space = current_space();
- vm_map_t map = current_map();
- mach_msg_header_t *msg = self->ith_msg;
- mach_msg_option_t option = self->ith_option;
- mach_msg_size_t rcv_size = self->ith_rcv_size;
- mach_msg_timeout_t time_out = self->ith_timeout;
- mach_port_t notify = self->ith_notify;
- ipc_object_t object = self->ith_object;
- ipc_mqueue_t mqueue = self->ith_mqueue;
- ipc_kmsg_t kmsg;
- mach_port_seqno_t seqno;
- mach_msg_return_t mr;
- if (option & MACH_RCV_LARGE) {
- mr = ipc_mqueue_receive(mqueue, option & MACH_RCV_TIMEOUT,
- rcv_size, time_out,
- TRUE, mach_msg_receive_continue,
- &kmsg, &seqno);
- /* mqueue is unlocked */
- ipc_object_release(object);
- if (mr != MACH_MSG_SUCCESS) {
- if (mr == MACH_RCV_TOO_LARGE) {
- mach_msg_size_t real_size =
- (mach_msg_size_t) (vm_offset_t) kmsg;
- assert(real_size > rcv_size);
- (void) copyout(&real_size,
- &msg->msgh_size,
- sizeof(mach_msg_size_t));
- }
- thread_syscall_return(mr);
- /*NOTREACHED*/
- }
- kmsg->ikm_header.msgh_seqno = seqno;
- assert(kmsg->ikm_header.msgh_size <= rcv_size);
- } else {
- mr = ipc_mqueue_receive(mqueue, option & MACH_RCV_TIMEOUT,
- MACH_MSG_SIZE_MAX, time_out,
- TRUE, mach_msg_receive_continue,
- &kmsg, &seqno);
- /* mqueue is unlocked */
- ipc_object_release(object);
- if (mr != MACH_MSG_SUCCESS) {
- thread_syscall_return(mr);
- /*NOTREACHED*/
- }
- kmsg->ikm_header.msgh_seqno = seqno;
- if (kmsg->ikm_header.msgh_size > rcv_size) {
- ipc_kmsg_copyout_dest(kmsg, space);
- (void) ipc_kmsg_put(msg, kmsg, sizeof *msg);
- thread_syscall_return(MACH_RCV_TOO_LARGE);
- /*NOTREACHED*/
- }
- }
- if (option & MACH_RCV_NOTIFY) {
- if (notify == MACH_PORT_NULL)
- mr = MACH_RCV_INVALID_NOTIFY;
- else
- mr = ipc_kmsg_copyout(kmsg, space, map, notify);
- } else
- mr = ipc_kmsg_copyout(kmsg, space, map, MACH_PORT_NULL);
- if (mr != MACH_MSG_SUCCESS) {
- if ((mr &~ MACH_MSG_MASK) == MACH_RCV_BODY_ERROR) {
- (void) ipc_kmsg_put(msg, kmsg,
- kmsg->ikm_header.msgh_size);
- } else {
- ipc_kmsg_copyout_dest(kmsg, space);
- (void) ipc_kmsg_put(msg, kmsg, sizeof *msg);
- }
- thread_syscall_return(mr);
- /*NOTREACHED*/
- }
- mr = ipc_kmsg_put(msg, kmsg, kmsg->ikm_header.msgh_size);
- thread_syscall_return(mr);
- /*NOTREACHED*/
- }
- /*
- * Routine: mach_msg_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_trap(
- mach_msg_header_t *msg,
- mach_msg_option_t option,
- mach_msg_size_t send_size,
- mach_msg_size_t rcv_size,
- mach_port_t rcv_name,
- mach_msg_timeout_t time_out,
- mach_port_t notify)
- {
- mach_msg_return_t mr;
- if (option & MACH_SEND_MSG) {
-
- printk(KERN_ERR "option & MACH_SEND_MSG\n");
-
- ipc_space_t space = current_space();
- vm_map_t map = current_map();
- ipc_kmsg_t kmsg;
- mr = ipc_kmsg_get(msg, send_size, &kmsg);
- if (mr != MACH_MSG_SUCCESS)
- return mr;
- mr = ipc_kmsg_copyin(kmsg, space, map, MACH_PORT_NULL);
- if (mr != MACH_MSG_SUCCESS) {
- ikm_free(kmsg);
- return mr;
- }
- mr = ipc_mqueue_send(kmsg, MACH_MSG_OPTION_NONE,
- MACH_MSG_TIMEOUT_NONE);
- if (mr != MACH_MSG_SUCCESS) {
- mr |= ipc_kmsg_copyout_pseudo(kmsg, space, map);
- assert(kmsg->ikm_marequest == IMAR_NULL);
- (void) ipc_kmsg_put(msg, kmsg,
- kmsg->ikm_header.msgh_size);
- return mr;
- }
-
- }
-
- if (option & MACH_RCV_MSG) {
- printk(KERN_ERR "option & MACH_SEND_MSG\n");
- ipc_thread_t self = current_thread();
- ipc_space_t space = current_space();
- vm_map_t map = current_map();
- ipc_object_t object;
- ipc_mqueue_t mqueue;
- ipc_kmsg_t kmsg;
- mach_port_seqno_t seqno;
- mr = ipc_mqueue_copyin(space, rcv_name, &mqueue, &object);
- if (mr != MACH_MSG_SUCCESS)
- return mr;
- //printk(KERN_DEBUG "ipc_mqueue_copyin\n");
- /* hold ref for object; mqueue is locked */
- /*
- * ipc_mqueue_receive may not return, because if we block
- * then our kernel stack may be discarded. So we save
- * state here for mach_msg_continue to pick up.
- */
- self->ith_msg = msg;
- self->ith_rcv_size = rcv_size;
- self->ith_object = object;
- self->ith_mqueue = mqueue;
- mr = ipc_mqueue_receive(mqueue,
- MACH_MSG_OPTION_NONE,
- MACH_MSG_SIZE_MAX,
- MACH_MSG_TIMEOUT_NONE,
- FALSE, mach_msg_continue,
- &kmsg, &seqno);
- /* mqueue is unlocked */
- ipc_object_release(object);
- if (mr != MACH_MSG_SUCCESS)
- return mr;
- kmsg->ikm_header.msgh_seqno = seqno;
- if (rcv_size < kmsg->ikm_header.msgh_size) {
- ipc_kmsg_copyout_dest(kmsg, space);
- (void) ipc_kmsg_put(msg, kmsg, sizeof *msg);
- return MACH_RCV_TOO_LARGE;
- }
- mr = ipc_kmsg_copyout(kmsg, space, map, MACH_PORT_NULL);
- if (mr != MACH_MSG_SUCCESS) {
- if ((mr &~ MACH_MSG_MASK) == MACH_RCV_BODY_ERROR) {
- (void) ipc_kmsg_put(msg, kmsg,
- kmsg->ikm_header.msgh_size);
- } else {
- ipc_kmsg_copyout_dest(kmsg, space);
- (void) ipc_kmsg_put(msg, kmsg, sizeof *msg);
- }
- return mr;
- }
- ipc_kmsg_put(msg, kmsg, kmsg->ikm_header.msgh_size);
- }
- return MACH_MSG_SUCCESS;
- }
- /*
- * Routine: mach_msg_continue
- * Purpose:
- * Continue after blocking for a message.
- * Conditions:
- * Nothing locked. We are running on a new kernel stack,
- * with the receive state saved in the thread. From here
- * control goes back to user space.
- */
- void
- mach_msg_continue(void)
- {
- ipc_thread_t thread = current_thread();
- task_t task = thread->task;
- ipc_space_t space = task->itk_space;
- vm_map_t map = task->map;
- mach_msg_header_t *msg = thread->ith_msg;
- mach_msg_size_t rcv_size = thread->ith_rcv_size;
- ipc_object_t object = thread->ith_object;
- ipc_mqueue_t mqueue = thread->ith_mqueue;
- ipc_kmsg_t kmsg;
- mach_port_seqno_t seqno;
- mach_msg_return_t mr;
- mr = ipc_mqueue_receive(mqueue, MACH_MSG_OPTION_NONE,
- MACH_MSG_SIZE_MAX, MACH_MSG_TIMEOUT_NONE,
- TRUE, mach_msg_continue, &kmsg, &seqno);
- /* mqueue is unlocked */
- ipc_object_release(object);
- if (mr != MACH_MSG_SUCCESS) {
- thread_syscall_return(mr);
- /*NOTREACHED*/
- }
- kmsg->ikm_header.msgh_seqno = seqno;
- if (kmsg->ikm_header.msgh_size > rcv_size) {
- ipc_kmsg_copyout_dest(kmsg, space);
- (void) ipc_kmsg_put(msg, kmsg, sizeof *msg);
- thread_syscall_return(MACH_RCV_TOO_LARGE);
- /*NOTREACHED*/
- }
- mr = ipc_kmsg_copyout(kmsg, space, map, MACH_PORT_NULL);
- if (mr != MACH_MSG_SUCCESS) {
- if ((mr &~ MACH_MSG_MASK) == MACH_RCV_BODY_ERROR) {
- (void) ipc_kmsg_put(msg, kmsg,
- kmsg->ikm_header.msgh_size);
- } else {
- ipc_kmsg_copyout_dest(kmsg, space);
- (void) ipc_kmsg_put(msg, kmsg, sizeof *msg);
- }
- thread_syscall_return(mr);
- /*NOTREACHED*/
- }
- mr = ipc_kmsg_put(msg, kmsg, kmsg->ikm_header.msgh_size);
- thread_syscall_return(mr);
- /*NOTREACHED*/
- }
- #if 0
- /*
- * Routine: mach_msg_interrupt
- * Purpose:
- * Attempts to force a thread waiting at mach_msg_continue or
- * mach_msg_receive_continue into a clean point. Returns TRUE
- * if this was possible.
- * Conditions:
- * Nothing locked. The thread must NOT be runnable.
- */
- boolean_t
- mach_msg_interrupt(thread_t thread)
- {
- ipc_mqueue_t mqueue;
- assert((thread->swap_func == (void (*)()) mach_msg_continue) ||
- (thread->swap_func == (void (*)()) mach_msg_receive_continue));
- mqueue = thread->ith_mqueue;
- imq_lock(mqueue);
- if (thread->ith_state != MACH_RCV_IN_PROGRESS) {
- /*
- * The thread is no longer waiting for a message.
- * It may have a message sitting in ith_kmsg.
- * We can't clean this up.
- */
- imq_unlock(mqueue);
- return FALSE;
- }
- ipc_thread_rmqueue(&mqueue->imq_threads, thread);
- imq_unlock(mqueue);
- ipc_object_release(thread->ith_object);
- thread_set_syscall_return(thread, MACH_RCV_INTERRUPTED);
- thread->swap_func = thread_exception_return;
- return TRUE;
- }
- #endif
|