Ejemplo n.º 1
0
/*
 *	Routine:	ipc_mqueue_select_on_thread
 *	Purpose:
 *		A receiver discovered that there was a message on the queue
 *		before he had to block.  Pick the message off the queue and
 *		"post" it to thread.
 *	Conditions:
 *		mqueue locked.
 *              thread not locked.
 *		There is a message.
 *	Returns:
 *		MACH_MSG_SUCCESS	Actually selected a message for ourselves.
 *		MACH_RCV_TOO_LARGE  May or may not have pull it, but it is large
 */
void
ipc_mqueue_select_on_thread(
	ipc_mqueue_t		mqueue,
	mach_msg_option_t	option,
	mach_msg_size_t		max_size,
	thread_t                thread)
{
	ipc_kmsg_t kmsg;
	mach_msg_return_t mr = MACH_MSG_SUCCESS;
	mach_msg_size_t rcv_size;

	/*
	 * Do some sanity checking of our ability to receive
	 * before pulling the message off the queue.
	 */
	kmsg = ipc_kmsg_queue_first(&mqueue->imq_messages);
	assert(kmsg != IKM_NULL);

	/*
	 * If we really can't receive it, but we had the
	 * MACH_RCV_LARGE option set, then don't take it off
	 * the queue, instead return the appropriate error
	 * (and size needed).
	 */
	rcv_size = ipc_kmsg_copyout_size(kmsg, thread->map);
	if (rcv_size + REQUESTED_TRAILER_SIZE(thread_is_64bit(thread), option) > max_size) {
		mr = MACH_RCV_TOO_LARGE;
		if (option & MACH_RCV_LARGE) {
			thread->ith_receiver_name = mqueue->imq_receiver_name;
			thread->ith_kmsg = IKM_NULL;
			thread->ith_msize = rcv_size;
			thread->ith_seqno = 0;
			thread->ith_state = mr;
			return;
		}
	}

	ipc_kmsg_rmqueue_first_macro(&mqueue->imq_messages, kmsg);
	ipc_mqueue_release_msgcount(mqueue);
	thread->ith_seqno = mqueue->imq_seqno++;
	thread->ith_kmsg = kmsg;
	thread->ith_state = mr;

	current_task()->messages_received++;
	return;
}
Ejemplo n.º 2
0
/*
 *	Routine:	ipc_mqueue_add
 *	Purpose:
 *		Associate the portset's mqueue with the port's mqueue.
 *		This has to be done so that posting the port will wakeup
 *		a portset waiter.  If there are waiters on the portset
 *		mqueue and messages on the port mqueue, try to match them
 *		up now.
 *	Conditions:
 *		May block.
 */
kern_return_t
ipc_mqueue_add(
	ipc_mqueue_t	 port_mqueue,
	ipc_mqueue_t	 set_mqueue,
	wait_queue_link_t wql)
{
	wait_queue_t	 port_waitq = &port_mqueue->imq_wait_queue;
	wait_queue_set_t set_waitq = &set_mqueue->imq_set_queue;
	ipc_kmsg_queue_t kmsgq;
	ipc_kmsg_t       kmsg, next;
	kern_return_t	 kr;
	spl_t		 s;

	kr = wait_queue_link_noalloc(port_waitq, set_waitq, wql);
	if (kr != KERN_SUCCESS)
		return kr;

	/*
	 * Now that the set has been added to the port, there may be
	 * messages queued on the port and threads waiting on the set
	 * waitq.  Lets get them together.
	 */
	s = splsched();
	imq_lock(port_mqueue);
	kmsgq = &port_mqueue->imq_messages;
	for (kmsg = ipc_kmsg_queue_first(kmsgq);
	     kmsg != IKM_NULL;
	     kmsg = next) {
		next = ipc_kmsg_queue_next(kmsgq, kmsg);

		for (;;) {
			thread_t th;
			mach_msg_size_t msize;

			th = wait_queue_wakeup64_identity_locked(
						port_waitq,
						IPC_MQUEUE_RECEIVE,
						THREAD_AWAKENED,
						FALSE);
			/* waitq/mqueue still locked, thread locked */

			if (th == THREAD_NULL)
				goto leave;

			/*
			 * If the receiver waited with a facility not directly
			 * related to Mach messaging, then it isn't prepared to get
			 * handed the message directly.  Just set it running, and
			 * go look for another thread that can.
			 */
			if (th->ith_state != MACH_RCV_IN_PROGRESS) {
				  thread_unlock(th);
				  continue;
			}

			/*
			 * Found a receiver. see if they can handle the message
			 * correctly (the message is not too large for them, or
			 * they didn't care to be informed that the message was
			 * too large).  If they can't handle it, take them off
			 * the list and let them go back and figure it out and
			 * just move onto the next.
			 */
			msize = ipc_kmsg_copyout_size(kmsg, th->map);
			if (th->ith_msize <
					(msize + REQUESTED_TRAILER_SIZE(thread_is_64bit(th), th->ith_option))) {
				th->ith_state = MACH_RCV_TOO_LARGE;
				th->ith_msize = msize;
				if (th->ith_option & MACH_RCV_LARGE) {
					/*
					 * let him go without message
					 */
					th->ith_receiver_name = port_mqueue->imq_receiver_name;
					th->ith_kmsg = IKM_NULL;
					th->ith_seqno = 0;
					thread_unlock(th);
					continue; /* find another thread */
				}
			} else {
				th->ith_state = MACH_MSG_SUCCESS;
			}

			/*
			 * This thread is going to take this message,
			 * so give it to him.
			 */
			ipc_kmsg_rmqueue(kmsgq, kmsg);
			ipc_mqueue_release_msgcount(port_mqueue);

			th->ith_kmsg = kmsg;
			th->ith_seqno = port_mqueue->imq_seqno++;
			thread_unlock(th);
			break;  /* go to next message */
		}
			
	}
 leave:
	imq_unlock(port_mqueue);
	splx(s);
	return KERN_SUCCESS;
}
Ejemplo n.º 3
0
/*
 *	Routine:	ipc_mqueue_post
 *	Purpose:
 *		Post a message to a waiting receiver or enqueue it.  If a
 *		receiver is waiting, we can release our reserved space in
 *		the message queue.
 *
 *	Conditions:
 *		If we need to queue, our space in the message queue is reserved.
 */
void
ipc_mqueue_post(
	register ipc_mqueue_t 	mqueue,
	register ipc_kmsg_t		kmsg)
{
	spl_t s;

	/*
	 *	While the msg queue	is locked, we have control of the
	 *  kmsg, so the ref in	it for the port is still good.
	 *
	 *	Check for a receiver for the message.
	 */
	s = splsched();
	imq_lock(mqueue);
	for (;;) {
		wait_queue_t waitq = &mqueue->imq_wait_queue;
		thread_t receiver;
		mach_msg_size_t msize;

		receiver = wait_queue_wakeup64_identity_locked(
							waitq,
							IPC_MQUEUE_RECEIVE,
							THREAD_AWAKENED,
							FALSE);
		/* waitq still locked, thread locked */

		if (receiver == THREAD_NULL) {
			/* 
			 * no receivers; queue kmsg
			 */
			assert(mqueue->imq_msgcount > 0);
			ipc_kmsg_enqueue_macro(&mqueue->imq_messages, kmsg);
			break;
		}
	
		/*
		 * If the receiver waited with a facility not directly
		 * related to Mach messaging, then it isn't prepared to get
		 * handed the message directly.  Just set it running, and
		 * go look for another thread that can.
		 */
		if (receiver->ith_state != MACH_RCV_IN_PROGRESS) {
				  thread_unlock(receiver);
				  continue;
		}

	
		/*
		 * We found a waiting thread.
		 * If the message is too large or the scatter list is too small
		 * the thread we wake up will get that as its status.
		 */
		msize =	ipc_kmsg_copyout_size(kmsg, receiver->map);
		if (receiver->ith_msize <
				(msize + REQUESTED_TRAILER_SIZE(thread_is_64bit(receiver), receiver->ith_option))) {
			receiver->ith_msize = msize;
			receiver->ith_state = MACH_RCV_TOO_LARGE;
		} else {
			receiver->ith_state = MACH_MSG_SUCCESS;
		}

		/*
		 * If there is no problem with the upcoming receive, or the
		 * receiver thread didn't specifically ask for special too
		 * large error condition, go ahead and select it anyway.
		 */
		if ((receiver->ith_state == MACH_MSG_SUCCESS) ||
		    !(receiver->ith_option & MACH_RCV_LARGE)) {

			receiver->ith_kmsg = kmsg;
			receiver->ith_seqno = mqueue->imq_seqno++;
			thread_unlock(receiver);

			/* we didn't need our reserved spot in the queue */
			ipc_mqueue_release_msgcount(mqueue);
			break;
		}

		/*
		 * Otherwise, this thread needs to be released to run
		 * and handle its error without getting the message.  We
		 * need to go back and pick another one.
		 */
		receiver->ith_receiver_name = mqueue->imq_receiver_name;
		receiver->ith_kmsg = IKM_NULL;
		receiver->ith_seqno = 0;
		thread_unlock(receiver);
	}

	imq_unlock(mqueue);
	splx(s);
	
	current_task()->messages_sent++;
	return;
}
Ejemplo n.º 4
0
static inline uint32_t target_mach_msg_trap(
        mach_msg_header_t *hdr, uint32_t options, uint32_t send_size,
        uint32_t rcv_size, uint32_t rcv_name, uint32_t time_out, uint32_t notify)
{
    extern int mach_msg_trap(mach_msg_header_t *, mach_msg_option_t,
          mach_msg_size_t, mach_msg_size_t, mach_port_t,
          mach_msg_timeout_t, mach_port_t);
    mach_msg_audit_trailer_t *trailer;
    mach_msg_id_t msg_id;
    uint32_t ret = 0;
    int i;

    swap_mach_msg(hdr, bswap_in);

    msg_id = hdr->msgh_id;

    print_description_msg_header(hdr);

    ret = mach_msg_trap(hdr, options, send_size, rcv_size, rcv_name, time_out, notify);

    print_mach_msg_return(ret);

    if( (options & MACH_RCV_MSG) && (REQUESTED_TRAILER_SIZE(options) > 0) )
    {
        /* XXX: the kernel always return the full trailer with MACH_SEND_MSG, so we should
                probably always bswap it  */
        /* warning: according to Mac OS X Internals (the book) msg_size might be expressed in
                    natural_t units but according to xnu/osfmk/mach/message.h: "The size of
                    the message must be specified in bytes" */
        trailer = (mach_msg_audit_trailer_t *)((uint8_t *)hdr + hdr->msgh_size);
        /* XXX: Should probably do that based on the option asked by the sender, but dealing
        with kernel answer seems more sound */
        switch(trailer->msgh_trailer_size)
        {
            case sizeof(mach_msg_audit_trailer_t):
                for(i = 0; i < 8; i++)
                    tswap32s(&trailer->msgh_audit.val[i]);
                /* Fall in mach_msg_security_trailer_t case */
            case sizeof(mach_msg_security_trailer_t):
                tswap32s(&trailer->msgh_sender.val[0]);
                tswap32s(&trailer->msgh_sender.val[1]);
                /* Fall in mach_msg_seqno_trailer_t case */
            case sizeof(mach_msg_seqno_trailer_t):
                tswap32s(&trailer->msgh_seqno);
                /* Fall in mach_msg_trailer_t case */
            case sizeof(mach_msg_trailer_t):
                tswap32s(&trailer->msgh_trailer_type);
                tswap32s(&trailer->msgh_trailer_size);
                break;
            case 0:
                /* Safer not to byteswap, but probably wrong */
                break;
            default:
                qerror("unknow trailer type given its size %d\n", trailer->msgh_trailer_size);
                break;
        }
    }

    /* Special message handling */
    switch (msg_id) {
        case 200: /* host_info */
        {
            mig_reply_error_t *err = (mig_reply_error_t *)hdr;
            struct {
                uint32_t unknow1;
                uint32_t max_cpus;
                uint32_t avail_cpus;
                uint32_t memory_size;
                uint32_t cpu_type;
                uint32_t cpu_subtype;
            } *data = (void *)(err+1);

            DPRINTF("maxcpu = 0x%x\n",   data->max_cpus);
            DPRINTF("numcpu = 0x%x\n",   data->avail_cpus);
            DPRINTF("memsize = 0x%x\n",  data->memory_size);

#if defined(TARGET_I386)
            data->cpu_type = CPU_TYPE_I386;
            DPRINTF("cpu_type changed to 0x%x(i386)\n", data->cpu_type);
            data->cpu_subtype = CPU_SUBTYPE_PENT;
            DPRINTF("cpu_subtype changed to 0x%x(i386_pent)\n", data->cpu_subtype);
#elif defined(TARGET_PPC)
            data->cpu_type = CPU_TYPE_POWERPC;
            DPRINTF("cpu_type changed to 0x%x(ppc)\n", data->cpu_type);
            data->cpu_subtype = CPU_SUBTYPE_POWERPC_750;
            DPRINTF("cpu_subtype changed to 0x%x(ppc_all)\n", data->cpu_subtype);
#else
# error target not supported
#endif
            break;
        }
        case 202: /* host_page_size */
        {
            mig_reply_error_t *err = (mig_reply_error_t *)hdr;
            uint32_t *pagesize = (uint32_t *)(err+1);

            DPRINTF("pagesize = %d\n", *pagesize);
            break;
        }
        default: break;
    }

    swap_mach_msg(hdr, bswap_out);

    return ret;
}