/* $NetBSD: mach_task.c,v 1.25 2003/04/29 22:12:51 manu Exp $ */ /*- * Copyright (c) 2002-2003 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Emmanuel Dreyfus * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include "opt_compat_darwin.h" #include __KERNEL_RCSID(0, "$NetBSD: mach_task.c,v 1.25 2003/04/29 22:12:51 manu Exp $"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef COMPAT_DARWIN #include #endif int mach_task_get_special_port(args) struct mach_trap_args *args; { mach_task_get_special_port_request_t *req = args->smsg; mach_task_get_special_port_reply_t *rep = args->rmsg; size_t *msglen = args->rsize; struct lwp *l = args->l; struct mach_emuldata *med; struct mach_right *mr; int error; med = (struct mach_emuldata *)l->l_proc->p_emuldata; switch (req->req_which_port) { case MACH_TASK_KERNEL_PORT: mr = mach_right_get(med->med_kernel, l, MACH_PORT_TYPE_SEND, 0); break; case MACH_TASK_HOST_PORT: mr = mach_right_get(med->med_host, l, MACH_PORT_TYPE_SEND, 0); break; case MACH_TASK_BOOTSTRAP_PORT: mr = mach_right_get(med->med_bootstrap, l, MACH_PORT_TYPE_SEND, 0); #ifdef DEBUG_MACH printf("*** get bootstrap right %p, port %p, recv %p [%p]\n", mr, mr->mr_port, mr->mr_port->mp_recv, mr->mr_port->mp_recv->mr_sethead); #endif break; case MACH_TASK_WIRED_LEDGER_PORT: case MACH_TASK_PAGED_LEDGER_PORT: default: uprintf("mach_task_get_special_port(): unimpl. port %d\n", req->req_which_port); return mach_msg_error(args, error); break; } rep->rep_msgh.msgh_bits = MACH_MSGH_REPLY_LOCAL_BITS(MACH_MSG_TYPE_MOVE_SEND_ONCE) | MACH_MSGH_BITS_COMPLEX; rep->rep_msgh.msgh_size = sizeof(*rep) - sizeof(rep->rep_trailer); rep->rep_msgh.msgh_local_port = req->req_msgh.msgh_local_port; rep->rep_msgh.msgh_id = req->req_msgh.msgh_id + 100; rep->rep_msgh_body.msgh_descriptor_count = 1; rep->rep_special_port.name = (mach_port_t)mr->mr_name; rep->rep_special_port.disposition = 0x11; /* XXX why? */ rep->rep_trailer.msgh_trailer_size = 8; *msglen = sizeof(*rep); return 0; } int mach_ports_lookup(args) struct mach_trap_args *args; { mach_ports_lookup_request_t *req = args->smsg; mach_ports_lookup_reply_t *rep = args->rmsg; size_t *msglen = args->rsize; struct lwp *l = args->l; struct mach_emuldata *med; struct mach_right *msp[7]; vaddr_t va; int error; /* * This is some out of band data sent with the reply. In the * encountered situation the out of band data has always been null * filled. We have to see more of this in order to fully understand * how this trap works. */ va = vm_map_min(&l->l_proc->p_vmspace->vm_map); if ((error = uvm_map(&l->l_proc->p_vmspace->vm_map, &va, PAGE_SIZE, NULL, UVM_UNKNOWN_OFFSET, 0, UVM_MAPFLAG(UVM_PROT_RW, UVM_PROT_ALL, UVM_INH_COPY, UVM_ADV_NORMAL, UVM_FLAG_COPYONW))) != 0) return mach_msg_error(args, error); med = (struct mach_emuldata *)l->l_proc->p_emuldata; msp[0] = MACH_PORT_DEAD; msp[3] = MACH_PORT_DEAD; msp[5] = MACH_PORT_DEAD; msp[6] = MACH_PORT_DEAD; msp[MACH_TASK_KERNEL_PORT] = mach_right_get(med->med_kernel, l, MACH_PORT_TYPE_SEND, 0); msp[MACH_TASK_HOST_PORT] = mach_right_get(med->med_host, l, MACH_PORT_TYPE_SEND, 0); msp[MACH_TASK_BOOTSTRAP_PORT] = mach_right_get(med->med_bootstrap, l, MACH_PORT_TYPE_SEND, 0); #ifdef DEBUG_MACH printf("mach_ports_lookup: kernel %08x, host %08x, boostrap %08x\n", msp[MACH_TASK_KERNEL_PORT]->mr_name, msp[MACH_TASK_HOST_PORT]->mr_name, msp[MACH_TASK_BOOTSTRAP_PORT]->mr_name); #endif /* * On Darwin, the data seems always null... */ if ((error = copyout(&msp[0], (void *)va, sizeof(msp))) != 0) return mach_msg_error(args, error); rep->rep_msgh.msgh_bits = MACH_MSGH_REPLY_LOCAL_BITS(MACH_MSG_TYPE_MOVE_SEND_ONCE) | MACH_MSGH_BITS_COMPLEX; rep->rep_msgh.msgh_size = sizeof(*rep) - sizeof(rep->rep_trailer); rep->rep_msgh.msgh_local_port = req->req_msgh.msgh_local_port; rep->rep_msgh.msgh_id = req->req_msgh.msgh_id + 100; rep->rep_msgh_body.msgh_descriptor_count = 1; /* XXX why ? */ rep->rep_init_port_set.address = (void *)va; rep->rep_init_port_set.count = 3; /* XXX why ? */ rep->rep_init_port_set.copy = 2; /* XXX why ? */ rep->rep_init_port_set.disposition = 0x11; /* XXX why? */ rep->rep_init_port_set.type = 2; /* XXX why? */ rep->rep_init_port_set_count = 3; /* XXX why? */ rep->rep_trailer.msgh_trailer_size = 8; *msglen = sizeof(*rep); return 0; } int mach_task_set_special_port(args) struct mach_trap_args *args; { mach_task_set_special_port_request_t *req = args->smsg; mach_task_set_special_port_reply_t *rep = args->rmsg; size_t *msglen = args->rsize; struct lwp *l = args->l; mach_port_t mn; struct mach_right *mr; struct mach_port *mp; struct mach_emuldata *med; mn = req->req_special_port.name; /* Null port ? */ if (mn == NULL) return mach_msg_error(args, 0); /* Does the inserted port exists? */ if ((mr = mach_right_check(mn, l, MACH_PORT_TYPE_ALL_RIGHTS)) == 0) return mach_msg_error(args, EPERM); if (mr->mr_type == MACH_PORT_TYPE_DEAD_NAME) return mach_msg_error(args, EINVAL); med = (struct mach_emuldata *)l->l_proc->p_emuldata; switch (req->req_which_port) { case MACH_TASK_KERNEL_PORT: mp = med->med_kernel; med->med_kernel = mr->mr_port; mp->mp_refcount--; if (mp->mp_refcount == 0) mach_port_put(mp); break; case MACH_TASK_HOST_PORT: mp = med->med_host; med->med_host = mr->mr_port; mp->mp_refcount--; if (mp->mp_refcount == 0) mach_port_put(mp); break; case MACH_TASK_BOOTSTRAP_PORT: mp = med->med_bootstrap; med->med_bootstrap = mr->mr_port; mp->mp_refcount--; if (mp->mp_refcount == 0) mach_port_put(mp); #ifdef COMPAT_DARWIN /* * mach_init sets the bootstrap port for any new process */ { struct darwin_emuldata *ded; ded = l->l_proc->p_emuldata; if (ded->ded_fakepid == 1) { mach_bootstrap_port = med->med_bootstrap; #ifdef DEBUG_DARWIN printf("*** New bootstrap port %p, " "recv %p [%p]\n", mach_bootstrap_port, mach_bootstrap_port->mp_recv, mach_bootstrap_port->mp_recv->mr_sethead); #endif /* DEBUG_DARWIN */ } } #endif /* COMPAT_DARWIN */ break; default: uprintf("mach_task_set_special_port: unimplemented port %d\n", req->req_which_port); } rep->rep_msgh.msgh_bits = MACH_MSGH_REPLY_LOCAL_BITS(MACH_MSG_TYPE_MOVE_SEND_ONCE); rep->rep_msgh.msgh_size = sizeof(*rep) - sizeof(rep->rep_trailer); rep->rep_msgh.msgh_local_port = req->req_msgh.msgh_local_port; rep->rep_msgh.msgh_id = req->req_msgh.msgh_id + 100; rep->rep_trailer.msgh_trailer_size = 8; *msglen = sizeof(*rep); return 0; } int mach_task_threads(args) struct mach_trap_args *args; { mach_task_threads_request_t *req = args->smsg; mach_task_threads_reply_t *rep = args->rmsg; size_t *msglen = args->rsize; struct lwp *l = args->l; struct mach_emuldata *med; int error; vaddr_t va; size_t size; int i; struct mach_right *mr; mach_msg_port_descriptor_t *mpd; med = l->l_proc->p_emuldata; size = l->l_proc->p_nlwps * sizeof(*mpd); va = vm_map_min(&l->l_proc->p_vmspace->vm_map); if ((error = uvm_map(&l->l_proc->p_vmspace->vm_map, &va, round_page(size), NULL, UVM_UNKNOWN_OFFSET, 0, UVM_MAPFLAG(UVM_PROT_RW, UVM_PROT_ALL, UVM_INH_COPY, UVM_ADV_NORMAL, UVM_FLAG_COPYONW))) != 0) return mach_msg_error(args, error); mpd = malloc(size, M_TEMP, M_WAITOK); for (i = 0; i < l->l_proc->p_nlwps; i++) { /* XXX each thread should have a kernel port */ mr = mach_right_get(med->med_kernel, l, MACH_PORT_TYPE_SEND, 0); mpd[i].name = mr->mr_name; mpd[i].disposition = 0x11; } if ((error = copyout(mpd, (void *)va, size)) != 0) { free(mpd, M_TEMP); return mach_msg_error(args, error); } free(mpd, M_TEMP); rep->rep_msgh.msgh_bits = MACH_MSGH_REPLY_LOCAL_BITS(MACH_MSG_TYPE_MOVE_SEND_ONCE) | MACH_MSGH_BITS_COMPLEX; rep->rep_msgh.msgh_size = sizeof(*rep) - sizeof(rep->rep_trailer); rep->rep_msgh.msgh_local_port = req->req_msgh.msgh_local_port; rep->rep_msgh.msgh_id = req->req_msgh.msgh_id + 100; rep->rep_body.msgh_descriptor_count = 1; rep->rep_list.address = (void *)va; rep->rep_list.count = l->l_proc->p_nlwps; rep->rep_list.disposition = 0x11; rep->rep_count = l->l_proc->p_nlwps; rep->rep_trailer.msgh_trailer_size = 8; *msglen = sizeof(*rep); return 0; } int mach_task_get_exception_ports(args) struct mach_trap_args *args; { mach_task_get_exception_ports_request_t *req = args->smsg; mach_task_get_exception_ports_reply_t *rep = args->rmsg; struct lwp *l = args->l; size_t *msglen = args->rsize; struct mach_emuldata *med; med = l->l_proc->p_emuldata; rep->rep_msgh.msgh_bits = MACH_MSGH_REPLY_LOCAL_BITS(MACH_MSG_TYPE_MOVE_SEND_ONCE); rep->rep_msgh.msgh_size = sizeof(*rep) - sizeof(rep->rep_trailer); rep->rep_msgh.msgh_local_port = req->req_msgh.msgh_local_port; rep->rep_msgh.msgh_id = req->req_msgh.msgh_id + 100; rep->rep_trailer.msgh_trailer_size = 8; *msglen = sizeof(*rep); return 0; } int mach_task_set_exception_ports(args) struct mach_trap_args *args; { mach_task_set_exception_ports_request_t *req = args->smsg; mach_task_set_exception_ports_reply_t *rep = args->rmsg; struct lwp *l = args->l; size_t *msglen = args->rsize; struct mach_emuldata *med; med = l->l_proc->p_emuldata; uprintf("mach_task_set_exception_ports\n"); rep->rep_msgh.msgh_bits = MACH_MSGH_REPLY_LOCAL_BITS(MACH_MSG_TYPE_MOVE_SEND_ONCE); rep->rep_msgh.msgh_size = sizeof(*rep) - sizeof(rep->rep_trailer); rep->rep_msgh.msgh_local_port = req->req_msgh.msgh_local_port; rep->rep_msgh.msgh_id = req->req_msgh.msgh_id + 100; rep->rep_trailer.msgh_trailer_size = 8; *msglen = sizeof(*rep); return 0; } int mach_task_info(args) struct mach_trap_args *args; { mach_task_info_request_t *req = args->smsg; mach_task_info_reply_t *rep = args->rmsg; struct lwp *l = args->l; size_t *msglen = args->rsize; int count; switch(req->req_flavor) { case MACH_TASK_BASIC_INFO: { struct mach_task_basic_info *mtbi; struct rusage *ru; count = sizeof(*mtbi) / sizeof(mach_integer_t); if (req->req_count < count) return mach_msg_error(args, ENOBUFS); ru = &l->l_proc->p_stats->p_ru; mtbi = (struct mach_task_basic_info *)&rep->rep_info[0]; mtbi->mtbi_suspend_count = ru->ru_nvcsw + ru->ru_nivcsw; mtbi->mtbi_virtual_size = ru->ru_ixrss; mtbi->mtbi_resident_size = ru->ru_maxrss; mtbi->mtbi_user_time.seconds = ru->ru_utime.tv_sec; mtbi->mtbi_user_time.microseconds = ru->ru_utime.tv_usec; mtbi->mtbi_system_time.seconds = ru->ru_stime.tv_sec; mtbi->mtbi_system_time.microseconds = ru->ru_stime.tv_usec; mtbi->mtbi_policy = 0; *msglen = sizeof(*rep) + (count * sizeof(mach_integer_t)); break; } /* XXX this is supposed to be about threads, not processes... */ case MACH_TASK_THREAD_TIMES_INFO: { struct mach_task_thread_times_info *mttti; struct rusage *ru; count = sizeof(*mttti) / sizeof(mach_integer_t); if (req->req_count < count) return mach_msg_error(args, ENOBUFS); ru = &l->l_proc->p_stats->p_ru; mttti = (struct mach_task_thread_times_info *)&rep->rep_info[0]; mttti->mttti_user_time.seconds = ru->ru_utime.tv_sec; mttti->mttti_user_time.microseconds = ru->ru_utime.tv_usec; mttti->mttti_system_time.seconds = ru->ru_stime.tv_sec; mttti->mttti_system_time.microseconds = ru->ru_stime.tv_usec; *msglen = sizeof(*rep) + (count * sizeof(mach_integer_t)); break; } /* XXX a few statistics missing here */ case MACH_TASK_EVENTS_INFO: { struct mach_task_events_info *mtei; struct rusage *ru; count = sizeof(*mtei) / sizeof(mach_integer_t); if (req->req_count < count) return mach_msg_error(args, ENOBUFS); mtei = (struct mach_task_events_info *)&rep->rep_info[0]; ru = &l->l_proc->p_stats->p_ru; mtei->mtei_faults = ru->ru_majflt; mtei->mtei_pageins = ru->ru_minflt; mtei->mtei_cow_faults = 0; /* XXX */ mtei->mtei_message_sent = ru->ru_msgsnd; mtei->mtei_message_received = ru->ru_msgrcv; mtei->mtei_syscalls_mach = 0; /* XXX */ mtei->mtei_syscalls_unix = 0; /* XXX */ mtei->mtei_csw = 0; /* XXX */ *msglen = sizeof(*rep) + (count * sizeof(mach_integer_t)); break; } default: uprintf("mach_task_info: unsupported flavor %d\n", req->req_flavor); return mach_msg_error(args, EINVAL); }; rep->rep_msgh.msgh_bits = MACH_MSGH_REPLY_LOCAL_BITS(MACH_MSG_TYPE_MOVE_SEND_ONCE); rep->rep_msgh.msgh_size = *msglen - sizeof(rep->rep_trailer); rep->rep_msgh.msgh_local_port = req->req_msgh.msgh_local_port; rep->rep_msgh.msgh_id = req->req_msgh.msgh_id + 100; rep->rep_count = count; rep->rep_info[count + 1] = 8; /* Trailer */ return 0; } int mach_task_suspend(args) struct mach_trap_args *args; { mach_task_suspend_request_t *req = args->smsg; mach_task_suspend_reply_t *rep = args->rmsg; size_t *msglen = args->rsize; mach_port_t mn; struct mach_right *mr; struct lwp *l; struct proc *p; struct mach_emuldata *med; int s; /* * XXX Two bugs when gdb calls this function: * - gdb uses a port it never allocated (apparently) * - this makes mach_right_check panic because of the lock operation * on a draining lock. */ return mach_msg_error(args, 0); /* XXX more permission checks nescessary here? */ mn = req->req_msgh.msgh_remote_port; if ((mr = mach_right_check(mn, l, MACH_PORT_TYPE_ALL_RIGHTS)) == 0) return mach_msg_error(args, EINVAL); if ((mr->mr_port == NULL) || (mr->mr_port->mp_recv == NULL)) { #ifdef DEBUG_MACH printf("mach_task_suspend: port = %p, recv = %p\n", mr->mr_port, mr->mr_port->mp_recv); #endif return mach_msg_error(args, EINVAL); } l = mr->mr_port->mp_recv->mr_lwp; p = l->l_proc; med = p->p_emuldata; med->med_suspend++; /* XXX Mach also has a per thread semaphore */ if (p->p_stat == SACTIVE) { sigminusset(&contsigmask, &p->p_sigctx.ps_siglist); SCHED_LOCK(s); p->p_stat = SSTOP; l->l_stat = LSSTOP; p->p_nrlwps--; mi_switch(l, NULL); SCHED_ASSERT_UNLOCKED(); splx(s); } rep->rep_msgh.msgh_bits = MACH_MSGH_REPLY_LOCAL_BITS(MACH_MSG_TYPE_MOVE_SEND_ONCE); rep->rep_msgh.msgh_size = sizeof(*rep) - sizeof(rep->rep_trailer); rep->rep_msgh.msgh_local_port = req->req_msgh.msgh_local_port; rep->rep_msgh.msgh_id = req->req_msgh.msgh_id + 100; rep->rep_trailer.msgh_trailer_size = 8; *msglen = sizeof(*rep); return 0; } int mach_task_resume(args) struct mach_trap_args *args; { mach_task_resume_request_t *req = args->smsg; mach_task_resume_reply_t *rep = args->rmsg; size_t *msglen = args->rsize; mach_port_t mn; struct mach_right *mr; struct lwp *l; struct proc *p; struct mach_emuldata *med; int s; /* XXX more permission checks nescessary here? */ mn = req->req_msgh.msgh_remote_port; if ((mr = mach_right_check(mn, l, MACH_PORT_TYPE_ALL_RIGHTS)) == 0) return mach_msg_error(args, EINVAL); if ((mr->mr_port == NULL) || (mr->mr_port->mp_recv == NULL)) { #ifdef DEBUG_MACH printf("mach_task_resume: port = %p, recv = %p\n", mr->mr_port, mr->mr_port->mp_recv); #endif return mach_msg_error(args, EINVAL); } l = mr->mr_port->mp_recv->mr_lwp; p = l->l_proc; med = p->p_emuldata; med->med_suspend--; /* XXX Mach also has a per thread semaphore */ if (med->med_suspend > 0) return mach_msg_error(args, 0); /* XXX error code */ /* XXX We should also wake up the stopped thread... */ if (p->p_stat == SSTOP) { sigminusset(&stopsigmask, &p->p_sigctx.ps_siglist); SCHED_LOCK(s); p->p_stat = SACTIVE; SCHED_ASSERT_UNLOCKED(); splx(s); } rep->rep_msgh.msgh_bits = MACH_MSGH_REPLY_LOCAL_BITS(MACH_MSG_TYPE_MOVE_SEND_ONCE); rep->rep_msgh.msgh_size = sizeof(*rep) - sizeof(rep->rep_trailer); rep->rep_msgh.msgh_local_port = req->req_msgh.msgh_local_port; rep->rep_msgh.msgh_id = req->req_msgh.msgh_id + 100; rep->rep_trailer.msgh_trailer_size = 8; *msglen = sizeof(*rep); return 0; } int mach_sys_task_for_pid(l, v, retval) struct lwp *l; void *v; register_t *retval; { struct mach_sys_task_for_pid_args /* { syscallarg(mach_port_t) target_tport; syscallarg(int) pid; syscallarg(mach_port_t) *t; } */ *uap = v; struct mach_right *mr; struct mach_emuldata *med; struct lwp *tl; struct proc *tp; struct proc *p; int error; if ((mr = mach_right_check(SCARG(uap, target_tport), l, MACH_PORT_TYPE_ALL_RIGHTS)) == NULL) return EPERM; if (mr->mr_port->mp_datatype != MACH_MP_PROC) return EINVAL; tp = (struct proc *)mr->mr_port->mp_data; tl = LIST_FIRST(&tp->p_lwps); if ((p = pfind(SCARG(uap, pid))) == NULL) return ESRCH; /* This will only work on a Mach process */ if ((p->p_emul != &emul_mach) && #ifdef COMPAT_DARWIN (p->p_emul != &emul_darwin) && #endif 1) return EINVAL; med = p->p_emuldata; if ((mr = mach_right_get(med->med_kernel, tl, MACH_PORT_TYPE_SEND, 0)) == NULL) return EINVAL; if ((error = copyout(&mr->mr_name, SCARG(uap, t), sizeof(mr->mr_name))) != 0) return error; return 0; }