/* IPC related system calls. Copyright 1999, 2000, 2001 Johan Rydberg, jrydberg@opencores.org. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include "tm.h" #include "trace.h" #include "libkern.h" #include "ipc-object.h" #include "ipc-port.h" #include "ipc-kmsg.h" #include "task.h" #include "thread.h" /* rtmk_msg_trap Kernel IPC entry point. The kernel do not operate on port rights. We can make some assumptions; 1. A message send from the kernel is aways complex. 2. There are no reply port. ??? what about blocking? */ kern_return_t rtmk_msg_trap (struct rtmk_msg_header *msgh, rtmk_msg_option_t options, rtmk_msg_size_t send_size, struct ipc_port *send_port, rtmk_msg_size_t recv_size, struct ipc_port *recv_port, rtmk_msg_timeout_t timeout) { struct ipc_kmsg *ikm; kern_return_t kr; assert ((options & RTMK_MSG_RECEIVE) == 0); assert ((options & RTMK_MSG_SEND) == RTMK_MSG_SEND); /* First we check if the port is valid. If it is not, this is a major fault, and we should crash and burn. This is also the case when we tries to send to a kernel port. */ assert (IPC_PORT_VALID (send_port)); assert (send_port->rcvobject != TASK_KERNEL()->ipc_object); /* When we arrive here. We know the following things: . the port is valid, . and receive rights is located outside of the kernel So we can directly start to copy things into a kernel message. */ ikm = (struct ipc_kmsg *) ipc_kmsg_alloc (send_size); assert (ikm != 0); /* Copy in the message body into the kernel message buffer. */ memcpy (&ikm->header, msgh, send_size); kr = ipc_kmsg_copyin_kernel (TASK_KERNEL (), ikm); if (kr != KERN_SUCCESS) return kr; thread_lock_write (& send_port->lock); /* If there are a waiting thread we try to pass the message directly to the thread without blocking. */ retry_waiting_thread: if (ipc_tqueue_empty (& send_port->waiting_threads) == false) { struct thread *thread; thread = ipc_tqueue_dequeue (& send_port->waiting_threads); thread_lock (thread); /* If there's not room for the message we continue to the next thread (reporting error to thread). */ if (thread->ipc_length < ikm->length) { thread->ipc_result = MSG_RCV_TOO_SMALL; thread_unlock (thread); thread_interrupt (thread); goto retry_waiting_thread; } thread->ipc_result = MSG_SUCCESS; thread->ipc_kmsg = ikm; thread_unlock (thread); thread_lock_unlock (& send_port->lock); /* ??? interrupt thread? */ thread_wakeup ((int) send_port); } else { bool result; /* Enqueue message on port message queue. Since we do not want to block the kernel (??) we override message queue. */ result = ipc_mqueue_enqueue (send_port->mqueue, ikm, true); thread_lock_unlock (& send_port->lock); assert (result == false); } return KERN_SUCCESS; }