/*===========================================================================* * swap_memreq * *===========================================================================*/ static void swap_memreq(struct proc *src_rp, struct proc *dst_rp) { /* If either the source or the destination process is part of the VM request * chain, but not both, then swap the process pointers in the chain. */ struct proc **rpp; if (RTS_ISSET(src_rp, RTS_VMREQUEST) == RTS_ISSET(dst_rp, RTS_VMREQUEST)) return; /* nothing to do */ for (rpp = &vmrequest; *rpp != NULL; rpp = &(*rpp)->p_vmrequest.nextrequestor) { if (*rpp == src_rp) { dst_rp->p_vmrequest.nextrequestor = src_rp->p_vmrequest.nextrequestor; *rpp = dst_rp; break; } else if (*rpp == dst_rp) { src_rp->p_vmrequest.nextrequestor = dst_rp->p_vmrequest.nextrequestor; *rpp = src_rp; break; } } }
/*===========================================================================* * do_runctl * *===========================================================================*/ int do_runctl(struct proc * caller, message * m_ptr) { /* Control a process's RTS_PROC_STOP flag. Used for process management. * If the process is queued sending a message or stopped for system call * tracing, and the RC_DELAY request flag is given, set MF_SIG_DELAY instead * of RTS_PROC_STOP, and send a SIGSNDELAY signal later when the process is done * sending (ending the delay). Used by PM for safe signal delivery. */ int proc_nr, action, flags; register struct proc *rp; /* Extract the message parameters and do sanity checking. */ if (!isokendpt(m_ptr->RC_ENDPT, &proc_nr)) return(EINVAL); if (iskerneln(proc_nr)) return(EPERM); rp = proc_addr(proc_nr); action = m_ptr->RC_ACTION; flags = m_ptr->RC_FLAGS; /* Is the target sending or syscall-traced? Then set MF_SIG_DELAY instead. * Do this only when the RC_DELAY flag is set in the request flags field. * The process will not become runnable before PM has called SYS_ENDKSIG. * Note that asynchronous messages are not covered: a process using SENDA * should not also install signal handlers *and* expect POSIX compliance. */ if (action == RC_STOP && (flags & RC_DELAY)) { if (RTS_ISSET(rp, RTS_SENDING) || (rp->p_misc_flags & MF_SC_DEFER)) rp->p_misc_flags |= MF_SIG_DELAY; if (rp->p_misc_flags & MF_SIG_DELAY) return (EBUSY); } /* Either set or clear the stop flag. */ switch (action) { case RC_STOP: #if CONFIG_SMP /* check if we must stop a process on a different CPU */ if (rp->p_cpu != cpuid) { smp_schedule_stop_proc(rp); break; } #endif RTS_SET(rp, RTS_PROC_STOP); break; case RC_RESUME: assert(RTS_ISSET(rp, RTS_PROC_STOP)); RTS_UNSET(rp, RTS_PROC_STOP); break; default: return(EINVAL); } return(OK); }
static void kernel_call_finish(struct proc * caller, message *msg, int result) { if(result == VMSUSPEND) { /* Special case: message has to be saved for handling * until VM tells us it's allowed. VM has been notified * and we must wait for its reply to restart the call. */ assert(RTS_ISSET(caller, RTS_VMREQUEST)); assert(caller->p_vmrequest.type == VMSTYPE_KERNELCALL); caller->p_vmrequest.saved.reqmsg = *msg; caller->p_misc_flags |= MF_KCALL_RESUME; } else { /* * call is finished, we could have been suspended because of VM, * remove the request message */ caller->p_vmrequest.saved.reqmsg.m_source = NONE; if (result != EDONTREPLY) { /* copy the result as a message to the original user buffer */ msg->m_source = SYSTEM; msg->m_type = result; /* report status of call */ #if DEBUG_IPC_HOOK hook_ipc_msgkresult(msg, caller); #endif if (copy_msg_to_user(msg, (message *)caller->p_delivermsg_vir)) { printf("WARNING wrong user pointer 0x%08x from " "process %s / %d\n", caller->p_delivermsg_vir, caller->p_name, caller->p_endpoint); cause_sig(proc_nr(caller), SIGSEGV); } } } }
PUBLIC void save_fpu(struct proc *pr) { #ifdef CONFIG_SMP if (cpuid == pr->p_cpu) { if (get_cpulocal_var(fpu_owner) == pr) { disable_fpu_exception(); save_local_fpu(pr); } } else { int stopped; /* remember if the process was already stopped */ stopped = RTS_ISSET(pr, RTS_PROC_STOP); /* stop the remote process and force it's context to be saved */ smp_schedule_stop_proc_save_ctx(pr); /* * If the process wasn't stopped let the process run again. The * process is kept block by the fact that the kernel cannot run * on its cpu */ if (!stopped) RTS_UNSET(pr, RTS_PROC_STOP); } #else if (get_cpulocal_var(fpu_owner) == pr) { disable_fpu_exception(); save_local_fpu(pr); } #endif }
void smp_schedule_vminhibit(struct proc * p) { if (proc_is_runnable(p)) smp_schedule_sync(p, SCHED_IPI_VM_INHIBIT); else RTS_SET(p, RTS_VMINHIBIT); assert(RTS_ISSET(p, RTS_VMINHIBIT)); }
void smp_schedule_stop_proc(struct proc * p) { if (proc_is_runnable(p)) smp_schedule_sync(p, SCHED_IPI_STOP_PROC); else RTS_SET(p, RTS_PROC_STOP); assert(RTS_ISSET(p, RTS_PROC_STOP)); }
void smp_schedule_stop_proc_save_ctx(struct proc * p) { /* * stop the processes and force the complete context of the process to * be saved (i.e. including FPU state and such) */ smp_schedule_sync(p, SCHED_IPI_STOP_PROC | SCHED_IPI_SAVE_CTX); assert(RTS_ISSET(p, RTS_PROC_STOP)); }
void smp_schedule_migrate_proc(struct proc * p, unsigned dest_cpu) { /* * stop the processes and force the complete context of the process to * be saved (i.e. including FPU state and such) */ smp_schedule_sync(p, SCHED_IPI_STOP_PROC | SCHED_IPI_SAVE_CTX); assert(RTS_ISSET(p, RTS_PROC_STOP)); /* assign the new cpu and let the process run again */ p->p_cpu = dest_cpu; RTS_UNSET(p, RTS_PROC_STOP); }
/*===========================================================================* * do_endksig * *===========================================================================*/ int do_endksig(struct proc * caller, message * m_ptr) { /* Finish up after a kernel type signal, caused by a SYS_KILL message or a * call to cause_sig by a task. This is called by a signal manager after * processing a signal it got with SYS_GETKSIG. */ register struct proc *rp; int proc_nr; /* Get process pointer and verify that it had signals pending. If the * process is already dead its flags will be reset. */ if(!isokendpt(m_ptr->m_sigcalls.endpt, &proc_nr)) return EINVAL; rp = proc_addr(proc_nr); if (caller->p_endpoint != priv(rp)->s_sig_mgr) return(EPERM); if (!RTS_ISSET(rp, RTS_SIG_PENDING)) return(EINVAL); /* The signal manager has finished one kernel signal. Is the process ready? */ if (!RTS_ISSET(rp, RTS_SIGNALED)) /* new signal arrived */ RTS_UNSET(rp, RTS_SIG_PENDING); /* remove pending flag */ return(OK); }
/*===========================================================================* * do_setgrant * *===========================================================================*/ int do_setgrant(struct proc * caller, message * m_ptr) { int r; /* Copy grant table set in priv. struct. */ if (RTS_ISSET(caller, RTS_NO_PRIV) || !(priv(caller))) { r = EPERM; } else { _K_SET_GRANT_TABLE(caller, m_ptr->m_lsys_krn_sys_setgrant.addr, m_ptr->m_lsys_krn_sys_setgrant.size); r = OK; } return r; }
void pagefault(struct proc *pr, int trap_errno) { int s; vir_bytes ph; u32_t pte; if(pagefault_count != 1) minix_panic("recursive pagefault", pagefault_count); /* Don't schedule this process until pagefault is handled. */ if(RTS_ISSET(pr, PAGEFAULT)) minix_panic("PAGEFAULT set", pr->p_endpoint); RTS_LOCK_SET(pr, PAGEFAULT); if(pr->p_endpoint <= INIT_PROC_NR) { /* Page fault we can't / don't want to * handle. */ kprintf("pagefault for process %d ('%s'), pc = 0x%x\n", pr->p_endpoint, pr->p_name, pr->p_reg.pc); proc_stacktrace(pr); minix_panic("page fault in system process", pr->p_endpoint); return; } /* Save pagefault details, suspend process, * add process to pagefault chain, * and tell VM there is a pagefault to be * handled. */ pr->p_pagefault.pf_virtual = pagefault_cr2; pr->p_pagefault.pf_flags = trap_errno; pr->p_nextpagefault = pagefaults; pagefaults = pr; lock_notify(HARDWARE, VM_PROC_NR); pagefault_count = 0; #if 0 kprintf("pagefault for process %d ('%s'), pc = 0x%x\n", pr->p_endpoint, pr->p_name, pr->p_reg.pc); proc_stacktrace(pr); #endif return; }
/*===========================================================================* * abort_proc_ipc_send * *===========================================================================*/ void abort_proc_ipc_send(struct proc *rp) { if(RTS_ISSET(rp, RTS_SENDING)) { struct proc **xpp; RTS_UNSET(rp, RTS_SENDING); rp->p_misc_flags &= ~MF_SENDING_FROM_KERNEL; xpp = &(proc_addr(_ENDPOINT_P(rp->p_sendto_e))->p_caller_q); while (*xpp) { if(*xpp == rp) { *xpp = rp->p_q_link; rp->p_q_link = NULL; break; } xpp = &(*xpp)->p_q_link; } } }
/*===========================================================================* * do_sigsend * *===========================================================================*/ PUBLIC int do_sigsend(struct proc * caller, message * m_ptr) { /* Handle sys_sigsend, POSIX-style signal handling. */ struct sigmsg smsg; register struct proc *rp; struct sigcontext sc, *scp; struct sigframe fr, *frp; int proc_nr, r; if (!isokendpt(m_ptr->SIG_ENDPT, &proc_nr)) return(EINVAL); if (iskerneln(proc_nr)) return(EPERM); rp = proc_addr(proc_nr); /* Get the sigmsg structure into our address space. */ if((r=data_copy_vmcheck(caller, caller->p_endpoint, (vir_bytes) m_ptr->SIG_CTXT_PTR, KERNEL, (vir_bytes) &smsg, (phys_bytes) sizeof(struct sigmsg))) != OK) return r; /* Compute the user stack pointer where sigcontext will be stored. */ scp = (struct sigcontext *) smsg.sm_stkptr - 1; /* Copy the registers to the sigcontext structure. */ memcpy(&sc.sc_regs, (char *) &rp->p_reg, sizeof(sigregs)); #if (_MINIX_CHIP == _CHIP_INTEL) if(proc_used_fpu(rp)) { /* save the FPU context before saving it to the sig context */ save_fpu(rp); memcpy(&sc.sc_fpu_state, rp->p_fpu_state.fpu_save_area_p, FPU_XFP_SIZE); } #endif /* Finish the sigcontext initialization. */ sc.sc_mask = smsg.sm_mask; sc.sc_flags = rp->p_misc_flags & MF_FPU_INITIALIZED; /* Copy the sigcontext structure to the user's stack. */ if((r=data_copy_vmcheck(caller, KERNEL, (vir_bytes) &sc, m_ptr->SIG_ENDPT, (vir_bytes) scp, (vir_bytes) sizeof(struct sigcontext))) != OK) return r; /* Initialize the sigframe structure. */ frp = (struct sigframe *) scp - 1; fr.sf_scpcopy = scp; fr.sf_retadr2= (void (*)()) rp->p_reg.pc; fr.sf_fp = rp->p_reg.fp; rp->p_reg.fp = (reg_t) &frp->sf_fp; fr.sf_scp = scp; fpu_sigcontext(rp, &fr, &sc); fr.sf_signo = smsg.sm_signo; fr.sf_retadr = (void (*)()) smsg.sm_sigreturn; /* Copy the sigframe structure to the user's stack. */ if((r=data_copy_vmcheck(caller, KERNEL, (vir_bytes) &fr, m_ptr->SIG_ENDPT, (vir_bytes) frp, (vir_bytes) sizeof(struct sigframe))) != OK) return r; /* Reset user registers to execute the signal handler. */ rp->p_reg.sp = (reg_t) frp; rp->p_reg.pc = (reg_t) smsg.sm_sighandler; /* Signal handler should get clean FPU. */ rp->p_misc_flags &= ~MF_FPU_INITIALIZED; if(!RTS_ISSET(rp, RTS_PROC_STOP)) { printf("system: warning: sigsend a running process\n"); printf("caller stack: "); proc_stacktrace(caller); } return(OK); }
/*===========================================================================* * do_vmctl * *===========================================================================*/ int do_vmctl(struct proc * caller, message * m_ptr) { int proc_nr; endpoint_t ep = m_ptr->SVMCTL_WHO; struct proc *p, *rp, **rpp, *target; if(ep == SELF) { ep = caller->p_endpoint; } if(!isokendpt(ep, &proc_nr)) { printf("do_vmctl: unexpected endpoint %d from VM\n", ep); return EINVAL; } p = proc_addr(proc_nr); switch(m_ptr->SVMCTL_PARAM) { case VMCTL_CLEAR_PAGEFAULT: assert(RTS_ISSET(p,RTS_PAGEFAULT)); RTS_UNSET(p, RTS_PAGEFAULT); return OK; case VMCTL_MEMREQ_GET: /* Send VM the information about the memory request. We can * not simply send the first request on the list, because IPC * filters may forbid VM from getting requests for particular * sources. However, IPC filters are used only in rare cases. */ for (rpp = &vmrequest; *rpp != NULL; rpp = &(*rpp)->p_vmrequest.nextrequestor) { rp = *rpp; assert(RTS_ISSET(rp, RTS_VMREQUEST)); okendpt(rp->p_vmrequest.target, &proc_nr); target = proc_addr(proc_nr); /* Check against IPC filters. */ if (!allow_ipc_filtered_memreq(rp, target)) continue; /* Reply with request fields. */ if (rp->p_vmrequest.req_type != VMPTYPE_CHECK) panic("VMREQUEST wrong type"); m_ptr->SVMCTL_MRG_TARGET = rp->p_vmrequest.target; m_ptr->SVMCTL_MRG_ADDR = rp->p_vmrequest.params.check.start; m_ptr->SVMCTL_MRG_LENGTH = rp->p_vmrequest.params.check.length; m_ptr->SVMCTL_MRG_FLAG = rp->p_vmrequest.params.check.writeflag; m_ptr->SVMCTL_MRG_REQUESTOR = (void *) rp->p_endpoint; rp->p_vmrequest.vmresult = VMSUSPEND; /* Remove from request chain. */ *rpp = rp->p_vmrequest.nextrequestor; return rp->p_vmrequest.req_type; } return ENOENT; case VMCTL_MEMREQ_REPLY: assert(RTS_ISSET(p, RTS_VMREQUEST)); assert(p->p_vmrequest.vmresult == VMSUSPEND); okendpt(p->p_vmrequest.target, &proc_nr); target = proc_addr(proc_nr); p->p_vmrequest.vmresult = m_ptr->SVMCTL_VALUE; assert(p->p_vmrequest.vmresult != VMSUSPEND); switch(p->p_vmrequest.type) { case VMSTYPE_KERNELCALL: /* * we will have to resume execution of the kernel call * as soon the scheduler picks up this process again */ p->p_misc_flags |= MF_KCALL_RESUME; break; case VMSTYPE_DELIVERMSG: assert(p->p_misc_flags & MF_DELIVERMSG); assert(p == target); assert(RTS_ISSET(p, RTS_VMREQUEST)); break; case VMSTYPE_MAP: assert(RTS_ISSET(p, RTS_VMREQUEST)); break; default: panic("strange request type: %d",p->p_vmrequest.type); } RTS_UNSET(p, RTS_VMREQUEST); return OK; case VMCTL_KERN_PHYSMAP: { int i = m_ptr->SVMCTL_VALUE; return arch_phys_map(i, (phys_bytes *) &m_ptr->SVMCTL_MAP_PHYS_ADDR, (phys_bytes *) &m_ptr->SVMCTL_MAP_PHYS_LEN, &m_ptr->SVMCTL_MAP_FLAGS); } case VMCTL_KERN_MAP_REPLY: { return arch_phys_map_reply(m_ptr->SVMCTL_VALUE, (vir_bytes) m_ptr->SVMCTL_MAP_VIR_ADDR); } case VMCTL_VMINHIBIT_SET: /* check if we must stop a process on a different CPU */ #if CONFIG_SMP if (p->p_cpu != cpuid) { smp_schedule_vminhibit(p); } else #endif RTS_SET(p, RTS_VMINHIBIT); #if CONFIG_SMP p->p_misc_flags |= MF_FLUSH_TLB; #endif return OK; case VMCTL_VMINHIBIT_CLEAR: assert(RTS_ISSET(p, RTS_VMINHIBIT)); /* * the processes is certainly not runnable, no need to tell its * cpu */ RTS_UNSET(p, RTS_VMINHIBIT); #ifdef CONFIG_SMP if (p->p_misc_flags & MF_SENDA_VM_MISS) { struct priv *privp; p->p_misc_flags &= ~MF_SENDA_VM_MISS; privp = priv(p); try_deliver_senda(p, (asynmsg_t *) privp->s_asyntab, privp->s_asynsize); } /* * We don't know whether kernel has the changed mapping * installed to access userspace memory. And if so, on what CPU. * More over we don't know what mapping has changed and how and * therefore we must invalidate all mappings we have anywhere. * Next time we map memory, we map it fresh. */ bits_fill(p->p_stale_tlb, CONFIG_MAX_CPUS); #endif return OK; case VMCTL_CLEARMAPCACHE: /* VM says: forget about old mappings we have cached. */ mem_clear_mapcache(); return OK; case VMCTL_BOOTINHIBIT_CLEAR: RTS_UNSET(p, RTS_BOOTINHIBIT); return OK; } /* Try architecture-specific vmctls. */ return arch_do_vmctl(m_ptr, p); }
/*===========================================================================* * do_fork * *===========================================================================*/ int do_fork(struct proc * caller, message * m_ptr) { /* Handle sys_fork(). * m_lsys_krn_sys_fork.endpt has forked. * The child is m_lsys_krn_sys_fork.slot. */ #if defined(__i386__) char *old_fpu_save_area_p; #endif register struct proc *rpc; /* child process pointer */ struct proc *rpp; /* parent process pointer */ int gen; int p_proc; int namelen; if(!isokendpt(m_ptr->m_lsys_krn_sys_fork.endpt, &p_proc)) return EINVAL; rpp = proc_addr(p_proc); rpc = proc_addr(m_ptr->m_lsys_krn_sys_fork.slot); if (isemptyp(rpp) || ! isemptyp(rpc)) return(EINVAL); assert(!(rpp->p_misc_flags & MF_DELIVERMSG)); /* needs to be receiving so we know where the message buffer is */ if(!RTS_ISSET(rpp, RTS_RECEIVING)) { printf("kernel: fork not done synchronously?\n"); return EINVAL; } /* make sure that the FPU context is saved in parent before copy */ save_fpu(rpp); /* Copy parent 'proc' struct to child. And reinitialize some fields. */ gen = _ENDPOINT_G(rpc->p_endpoint); #if defined(__i386__) old_fpu_save_area_p = rpc->p_seg.fpu_state; #endif *rpc = *rpp; /* copy 'proc' struct */ #if defined(__i386__) rpc->p_seg.fpu_state = old_fpu_save_area_p; if(proc_used_fpu(rpp)) memcpy(rpc->p_seg.fpu_state, rpp->p_seg.fpu_state, FPU_XFP_SIZE); #endif if(++gen >= _ENDPOINT_MAX_GENERATION) /* increase generation */ gen = 1; /* generation number wraparound */ rpc->p_nr = m_ptr->m_lsys_krn_sys_fork.slot; /* this was obliterated by copy */ rpc->p_endpoint = _ENDPOINT(gen, rpc->p_nr); /* new endpoint of slot */ rpc->p_reg.retreg = 0; /* child sees pid = 0 to know it is child */ rpc->p_user_time = 0; /* set all the accounting times to 0 */ rpc->p_sys_time = 0; rpc->p_misc_flags &= ~(MF_VIRT_TIMER | MF_PROF_TIMER | MF_SC_TRACE | MF_SPROF_SEEN | MF_STEP); rpc->p_virt_left = 0; /* disable, clear the process-virtual timers */ rpc->p_prof_left = 0; /* Mark process name as being a forked copy */ namelen = strlen(rpc->p_name); #define FORKSTR "*F" if(namelen+strlen(FORKSTR) < sizeof(rpc->p_name)) strcat(rpc->p_name, FORKSTR); /* the child process is not runnable until it's scheduled. */ RTS_SET(rpc, RTS_NO_QUANTUM); reset_proc_accounting(rpc); rpc->p_cpu_time_left = 0; rpc->p_cycles = 0; rpc->p_kcall_cycles = 0; rpc->p_kipc_cycles = 0; rpc->p_signal_received = 0; /* If the parent is a privileged process, take away the privileges from the * child process and inhibit it from running by setting the NO_PRIV flag. * The caller should explicitly set the new privileges before executing. */ if (priv(rpp)->s_flags & SYS_PROC) { rpc->p_priv = priv_addr(USER_PRIV_ID); rpc->p_rts_flags |= RTS_NO_PRIV; } /* Calculate endpoint identifier, so caller knows what it is. */ m_ptr->m_krn_lsys_sys_fork.endpt = rpc->p_endpoint; m_ptr->m_krn_lsys_sys_fork.msgaddr = rpp->p_delivermsg_vir; /* Don't schedule process in VM mode until it has a new pagetable. */ if(m_ptr->m_lsys_krn_sys_fork.flags & PFF_VMINHIBIT) { RTS_SET(rpc, RTS_VMINHIBIT); } /* * Only one in group should have RTS_SIGNALED, child doesn't inherit tracing. */ RTS_UNSET(rpc, (RTS_SIGNALED | RTS_SIG_PENDING | RTS_P_STOP)); (void) sigemptyset(&rpc->p_pending); #if defined(__i386__) rpc->p_seg.p_cr3 = 0; rpc->p_seg.p_cr3_v = NULL; #elif defined(__arm__) rpc->p_seg.p_ttbr = 0; rpc->p_seg.p_ttbr_v = NULL; #endif return OK; }
/*===========================================================================* * do_privctl * *===========================================================================*/ PUBLIC int do_privctl(struct proc * caller, message * m_ptr) { /* Handle sys_privctl(). Update a process' privileges. If the process is not * yet a system process, make sure it gets its own privilege structure. */ struct proc *rp; proc_nr_t proc_nr; sys_id_t priv_id; int ipc_to_m, kcalls; int i, r; struct io_range io_range; struct mem_range mem_range; struct priv priv; int irq; /* Check whether caller is allowed to make this call. Privileged proceses * can only update the privileges of processes that are inhibited from * running by the RTS_NO_PRIV flag. This flag is set when a privileged process * forks. */ if (! (priv(caller)->s_flags & SYS_PROC)) return(EPERM); if(m_ptr->CTL_ENDPT == SELF) proc_nr = _ENDPOINT_P(caller->p_endpoint); else if(!isokendpt(m_ptr->CTL_ENDPT, &proc_nr)) return(EINVAL); rp = proc_addr(proc_nr); switch(m_ptr->CTL_REQUEST) { case SYS_PRIV_ALLOW: /* Allow process to run. Make sure its privilege structure has already * been set. */ if (!RTS_ISSET(rp, RTS_NO_PRIV) || priv(rp)->s_proc_nr == NONE) { return(EPERM); } RTS_UNSET(rp, RTS_NO_PRIV); return(OK); case SYS_PRIV_YIELD: /* Allow process to run and suspend the caller. */ if (!RTS_ISSET(rp, RTS_NO_PRIV) || priv(rp)->s_proc_nr == NONE) { return(EPERM); } RTS_SET(caller, RTS_NO_PRIV); RTS_UNSET(rp, RTS_NO_PRIV); return(OK); case SYS_PRIV_DISALLOW: /* Disallow process from running. */ if (RTS_ISSET(rp, RTS_NO_PRIV)) return(EPERM); RTS_SET(rp, RTS_NO_PRIV); return(OK); case SYS_PRIV_SET_SYS: /* Set a privilege structure of a blocked system process. */ if (! RTS_ISSET(rp, RTS_NO_PRIV)) return(EPERM); /* Check whether a static or dynamic privilege id must be allocated. */ priv_id = NULL_PRIV_ID; if (m_ptr->CTL_ARG_PTR) { /* Copy privilege structure from caller */ if((r=data_copy(caller->p_endpoint, (vir_bytes) m_ptr->CTL_ARG_PTR, KERNEL, (vir_bytes) &priv, sizeof(priv))) != OK) return r; /* See if the caller wants to assign a static privilege id. */ if(!(priv.s_flags & DYN_PRIV_ID)) { priv_id = priv.s_id; } } /* Make sure this process has its own privileges structure. This may * fail, since there are only a limited number of system processes. * Then copy privileges from the caller and restore some defaults. */ if ((i=get_priv(rp, priv_id)) != OK) { printf("do_privctl: unable to allocate priv_id %d: %d\n", priv_id, i); return(i); } priv_id = priv(rp)->s_id; /* backup privilege id */ *priv(rp) = *priv(caller); /* copy from caller */ priv(rp)->s_id = priv_id; /* restore privilege id */ priv(rp)->s_proc_nr = proc_nr; /* reassociate process nr */ for (i=0; i< NR_SYS_CHUNKS; i++) /* remove pending: */ priv(rp)->s_notify_pending.chunk[i] = 0; /* - notifications */ priv(rp)->s_int_pending = 0; /* - interrupts */ (void) sigemptyset(&priv(rp)->s_sig_pending); /* - signals */ reset_timer(&priv(rp)->s_alarm_timer); /* - alarm */ priv(rp)->s_asyntab= -1; /* - asynsends */ priv(rp)->s_asynsize= 0; /* Set defaults for privilege bitmaps. */ priv(rp)->s_flags= DSRV_F; /* privilege flags */ priv(rp)->s_trap_mask= DSRV_T; /* allowed traps */ ipc_to_m = DSRV_M; /* allowed targets */ fill_sendto_mask(rp, ipc_to_m); kcalls = DSRV_KC; /* allowed kernel calls */ for(i = 0; i < SYS_CALL_MASK_SIZE; i++) { priv(rp)->s_k_call_mask[i] = (kcalls == NO_C ? 0 : (~0)); } /* Set the default signal managers. */ priv(rp)->s_sig_mgr = DSRV_SM; priv(rp)->s_bak_sig_mgr = NONE; /* Set defaults for resources: no I/O resources, no memory resources, * no IRQs, no grant table */ priv(rp)->s_nr_io_range= 0; priv(rp)->s_nr_mem_range= 0; priv(rp)->s_nr_irq= 0; priv(rp)->s_grant_table= 0; priv(rp)->s_grant_entries= 0; /* Override defaults if the caller has supplied a privilege structure. */ if (m_ptr->CTL_ARG_PTR) { if((r = update_priv(rp, &priv)) != OK) { return r; } } return(OK); case SYS_PRIV_SET_USER: /* Set a privilege structure of a blocked user process. */ if (!RTS_ISSET(rp, RTS_NO_PRIV)) return(EPERM); /* Link the process to the privilege structure of the root user * process all the user processes share. */ priv(rp) = priv_addr(USER_PRIV_ID); return(OK); case SYS_PRIV_ADD_IO: if (RTS_ISSET(rp, RTS_NO_PRIV)) return(EPERM); /* Only system processes get I/O resources? */ if (!(priv(rp)->s_flags & SYS_PROC)) return EPERM; #if 0 /* XXX -- do we need a call for this? */ if (strcmp(rp->p_name, "fxp") == 0 || strcmp(rp->p_name, "rtl8139") == 0) { printf("setting ipc_stats_target to %d\n", rp->p_endpoint); ipc_stats_target= rp->p_endpoint; } #endif /* Get the I/O range */ data_copy(caller->p_endpoint, (vir_bytes) m_ptr->CTL_ARG_PTR, KERNEL, (vir_bytes) &io_range, sizeof(io_range)); priv(rp)->s_flags |= CHECK_IO_PORT; /* Check I/O accesses */ i= priv(rp)->s_nr_io_range; if (i >= NR_IO_RANGE) { printf("do_privctl: %d already has %d i/o ranges.\n", rp->p_endpoint, i); return ENOMEM; } priv(rp)->s_io_tab[i].ior_base= io_range.ior_base; priv(rp)->s_io_tab[i].ior_limit= io_range.ior_limit; priv(rp)->s_nr_io_range++; return OK; case SYS_PRIV_ADD_MEM: if (RTS_ISSET(rp, RTS_NO_PRIV)) return(EPERM); /* Only system processes get memory resources? */ if (!(priv(rp)->s_flags & SYS_PROC)) return EPERM; /* Get the memory range */ if((r=data_copy(caller->p_endpoint, (vir_bytes) m_ptr->CTL_ARG_PTR, KERNEL, (vir_bytes) &mem_range, sizeof(mem_range))) != OK) return r; priv(rp)->s_flags |= CHECK_MEM; /* Check memory mappings */ i= priv(rp)->s_nr_mem_range; if (i >= NR_MEM_RANGE) { printf("do_privctl: %d already has %d mem ranges.\n", rp->p_endpoint, i); return ENOMEM; } priv(rp)->s_mem_tab[i].mr_base= mem_range.mr_base; priv(rp)->s_mem_tab[i].mr_limit= mem_range.mr_limit; priv(rp)->s_nr_mem_range++; return OK; case SYS_PRIV_ADD_IRQ: if (RTS_ISSET(rp, RTS_NO_PRIV)) return(EPERM); /* Only system processes get IRQs? */ if (!(priv(rp)->s_flags & SYS_PROC)) return EPERM; data_copy(caller->p_endpoint, (vir_bytes) m_ptr->CTL_ARG_PTR, KERNEL, (vir_bytes) &irq, sizeof(irq)); priv(rp)->s_flags |= CHECK_IRQ; /* Check IRQs */ i= priv(rp)->s_nr_irq; if (i >= NR_IRQ) { printf("do_privctl: %d already has %d irq's.\n", rp->p_endpoint, i); return ENOMEM; } priv(rp)->s_irq_tab[i]= irq; priv(rp)->s_nr_irq++; return OK; case SYS_PRIV_QUERY_MEM: { phys_bytes addr, limit; struct priv *sp; /* See if a certain process is allowed to map in certain physical * memory. */ addr = (phys_bytes) m_ptr->CTL_PHYSSTART; limit = addr + (phys_bytes) m_ptr->CTL_PHYSLEN - 1; if(limit < addr) return EPERM; if(!(sp = priv(rp))) return EPERM; if (!(sp->s_flags & SYS_PROC)) return EPERM; for(i = 0; i < sp->s_nr_mem_range; i++) { if(addr >= sp->s_mem_tab[i].mr_base && limit <= sp->s_mem_tab[i].mr_limit) return OK; } return EPERM; } case SYS_PRIV_UPDATE_SYS: /* Update the privilege structure of a system process. */ if(!m_ptr->CTL_ARG_PTR) return EINVAL; /* Copy privilege structure from caller */ if((r=data_copy(caller->p_endpoint, (vir_bytes) m_ptr->CTL_ARG_PTR, KERNEL, (vir_bytes) &priv, sizeof(priv))) != OK) return r; /* Override settings in existing privilege structure. */ if((r = update_priv(rp, &priv)) != OK) { return r; } return(OK); default: printf("do_privctl: bad request %d\n", m_ptr->CTL_REQUEST); return EINVAL; } }
/* * Return the LWP status of a process, along with additional information in * case the process is sleeping (LSSLEEP): a wchan value and text to indicate * what the process is sleeping on, and possibly a flag field modification to * indicate that the sleep is interruptible. */ static int get_lwp_stat(int mslot, uint64_t * wcptr, char * wmptr, size_t wmsz, int32_t * flag) { struct mproc *mp; struct fproc_light *fp; struct proc *kp; const char *wmesg; uint64_t wchan; endpoint_t endpt; mp = &mproc_tab[mslot]; fp = &fproc_tab[mslot]; kp = &proc_tab[NR_TASKS + mslot]; /* * First cover all the cases that the process is not sleeping. In * those cases, we need not return additional sleep information either. */ if (mp->mp_flags & (TRACE_ZOMBIE | ZOMBIE)) return LSZOMB; if (mp->mp_flags & EXITING) return LSDEAD; if ((mp->mp_flags & TRACE_STOPPED) || RTS_ISSET(kp, RTS_P_STOP)) return LSSTOP; if (proc_is_runnable(kp)) return LSRUN; /* * The process is sleeping. In that case, we must also figure out why, * and return an appropriate wchan value and human-readable wmesg text. * * The process can be blocked on either a known sleep state in PM or * VFS, or otherwise on IPC communication with another process, or * otherwise on a kernel RTS flag. In each case, decide what to use as * wchan value and wmesg text, and whether the sleep is interruptible. * * The wchan value should be unique for the sleep reason. We use its * lower eight bits to indicate a class: * 0x00 = kernel task * 0x01 = kerel RTS block * 0x02 = PM call * 0x03 = VFS call * 0x04 = MIB call * 0xff = blocked on process * The upper bits are used for class-specific information. The actual * value does not really matter, as long as it is nonzero and there is * no overlap between the different values. */ wchan = 0; wmesg = NULL; /* * First see if the process is marked as blocked in the tables of PM or * VFS. Such a block reason is always an interruptible sleep. Note * that we do not use the kernel table at all in this case: each of the * three tables is consistent within itself, but not necessarily * consistent with any of the other tables, so we avoid internal * mismatches if we can. */ if (mp->mp_flags & WAITING) { wchan = 0x102; wmesg = "wait"; } else if (mp->mp_flags & SIGSUSPENDED) { wchan = 0x202; wmesg = "pause"; } else if (fp->fpl_blocked_on != FP_BLOCKED_ON_NONE) { wchan = (fp->fpl_blocked_on << 8) | 0x03; switch (fp->fpl_blocked_on) { case FP_BLOCKED_ON_PIPE: wmesg = "pipe"; break; case FP_BLOCKED_ON_FLOCK: wmesg = "flock"; break; case FP_BLOCKED_ON_POPEN: wmesg = "popen"; break; case FP_BLOCKED_ON_SELECT: wmesg = "select"; break; case FP_BLOCKED_ON_CDEV: case FP_BLOCKED_ON_SDEV: /* * Add the task (= character or socket driver) endpoint * to the wchan value, and use the driver's process * name, without parentheses, as wmesg text. */ wchan |= (uint64_t)fp->fpl_task << 16; fill_wmesg(wmptr, wmsz, fp->fpl_task, FALSE /*ipc*/); break; default: /* A newly added flag we don't yet know about? */ wmesg = "???"; break; } } if (wchan != 0) { *wcptr = wchan; if (wmesg != NULL) /* NULL means "already set" here */ strlcpy(wmptr, wmesg, wmsz); *flag |= L_SINTR; } /* * See if the process is blocked on sending or receiving. If not, then * use one of the kernel RTS flags as reason. */ endpt = P_BLOCKEDON(kp); switch (endpt) { case MIB_PROC_NR: /* This is really just aesthetics. */ wchan = 0x04; wmesg = "sysctl"; break; case NONE: /* * The process is not running, but also not blocked on IPC with * another process. This means it must be stopped on a kernel * RTS flag. */ wchan = ((uint64_t)kp->p_rts_flags << 8) | 0x01; if (RTS_ISSET(kp, RTS_PROC_STOP)) wmesg = "kstop"; else if (RTS_ISSET(kp, RTS_SIGNALED) || RTS_ISSET(kp, RTS_SIGNALED)) wmesg = "ksignal"; else if (RTS_ISSET(kp, RTS_NO_PRIV)) wmesg = "knopriv"; else if (RTS_ISSET(kp, RTS_PAGEFAULT) || RTS_ISSET(kp, RTS_VMREQTARGET)) wmesg = "fault"; else if (RTS_ISSET(kp, RTS_NO_QUANTUM)) wmesg = "sched"; else wmesg = "kflag"; break; case ANY: /* * If the process is blocked receiving from ANY, mark it as * being in an interruptible sleep. This looks nicer, even * though "interruptible" is not applicable to services at all. */ *flag |= L_SINTR; break; } /* * If at this point wchan is still zero, the process is blocked sending * or receiving. Use a wchan value based on the target endpoint, and * use "(procname)" as wmesg text. */ if (wchan == 0) { *wcptr = ((uint64_t)endpt << 8) | 0xff; fill_wmesg(wmptr, wmsz, endpt, TRUE /*ipc*/); } else { *wcptr = wchan; if (wmesg != NULL) /* NULL means "already set" here */ strlcpy(wmptr, wmesg, wmsz); } return LSSLEEP; }
int runqueues_ok_cpu(unsigned cpu) { int q, l = 0; register struct proc *xp; struct proc **rdy_head, **rdy_tail; rdy_head = get_cpu_var(cpu, run_q_head); rdy_tail = get_cpu_var(cpu, run_q_tail); for (xp = BEG_PROC_ADDR; xp < END_PROC_ADDR; ++xp) { xp->p_found = 0; if (l++ > MAX_LOOP) panic("check error"); } for (q=l=0; q < NR_SCHED_QUEUES; q++) { if (rdy_head[q] && !rdy_tail[q]) { printf("head but no tail in %d\n", q); return 0; } if (!rdy_head[q] && rdy_tail[q]) { printf("tail but no head in %d\n", q); return 0; } if (rdy_tail[q] && rdy_tail[q]->p_nextready) { printf("tail and tail->next not null in %d\n", q); return 0; } for(xp = rdy_head[q]; xp; xp = xp->p_nextready) { const vir_bytes vxp = (vir_bytes) xp; vir_bytes dxp; if(vxp < (vir_bytes) BEG_PROC_ADDR || vxp >= (vir_bytes) END_PROC_ADDR) { printf("xp out of range\n"); return 0; } dxp = vxp - (vir_bytes) BEG_PROC_ADDR; if(dxp % sizeof(struct proc)) { printf("xp not a real pointer"); return 0; } if(!proc_ptr_ok(xp)) { printf("xp bogus pointer"); return 0; } if (RTS_ISSET(xp, RTS_SLOT_FREE)) { printf("scheduling error: dead proc q %d %d\n", q, xp->p_endpoint); return 0; } if (!proc_is_runnable(xp)) { printf("scheduling error: unready on runq %d proc %d\n", q, xp->p_nr); return 0; } if (xp->p_priority != q) { printf("scheduling error: wrong priority q %d proc %d ep %d name %s\n", q, xp->p_nr, xp->p_endpoint, xp->p_name); return 0; } if (xp->p_found) { printf("scheduling error: double sched q %d proc %d\n", q, xp->p_nr); return 0; } xp->p_found = 1; if (!xp->p_nextready && rdy_tail[q] != xp) { printf("sched err: last element not tail q %d proc %d\n", q, xp->p_nr); return 0; } if (l++ > MAX_LOOP) { printf("loop in schedule queue?"); return 0; } } } for (xp = BEG_PROC_ADDR; xp < END_PROC_ADDR; ++xp) { if(!proc_ptr_ok(xp)) { printf("xp bogus pointer in proc table\n"); return 0; } if (isemptyp(xp)) continue; if(proc_is_runnable(xp) && !xp->p_found) { printf("sched error: ready proc %d not on queue\n", xp->p_nr); return 0; } } /* All is ok. */ return 1; }
/*===========================================================================* * lin_lin_copy * *===========================================================================*/ static int lin_lin_copy(struct proc *srcproc, vir_bytes srclinaddr, struct proc *dstproc, vir_bytes dstlinaddr, vir_bytes bytes) { u32_t addr; proc_nr_t procslot; assert(vm_running); assert(nfreepdes >= MAX_FREEPDES); assert(get_cpulocal_var(ptproc)); assert(get_cpulocal_var(proc_ptr)); assert(read_cr3() == get_cpulocal_var(ptproc)->p_seg.p_cr3); procslot = get_cpulocal_var(ptproc)->p_nr; assert(procslot >= 0 && procslot < I386_VM_DIR_ENTRIES); if(srcproc) assert(!RTS_ISSET(srcproc, RTS_SLOT_FREE)); if(dstproc) assert(!RTS_ISSET(dstproc, RTS_SLOT_FREE)); assert(!RTS_ISSET(get_cpulocal_var(ptproc), RTS_SLOT_FREE)); assert(get_cpulocal_var(ptproc)->p_seg.p_cr3_v); if(srcproc) assert(!RTS_ISSET(srcproc, RTS_VMINHIBIT)); if(dstproc) assert(!RTS_ISSET(dstproc, RTS_VMINHIBIT)); while(bytes > 0) { phys_bytes srcptr, dstptr; vir_bytes chunk = bytes; int changed = 0; #ifdef CONFIG_SMP unsigned cpu = cpuid; if (GET_BIT(srcproc->p_stale_tlb, cpu)) { changed = 1; UNSET_BIT(srcproc->p_stale_tlb, cpu); } if (GET_BIT(dstproc->p_stale_tlb, cpu)) { changed = 1; UNSET_BIT(dstproc->p_stale_tlb, cpu); } #endif /* Set up 4MB ranges. */ srcptr = createpde(srcproc, srclinaddr, &chunk, 0, &changed); dstptr = createpde(dstproc, dstlinaddr, &chunk, 1, &changed); if(changed) reload_cr3(); /* Copy pages. */ PHYS_COPY_CATCH(srcptr, dstptr, chunk, addr); if(addr) { /* If addr is nonzero, a page fault was caught. */ if(addr >= srcptr && addr < (srcptr + chunk)) { return EFAULT_SRC; } if(addr >= dstptr && addr < (dstptr + chunk)) { return EFAULT_DST; } panic("lin_lin_copy fault out of range"); /* Not reached. */ return EFAULT; } /* Update counter and addresses for next iteration, if any. */ bytes -= chunk; srclinaddr += chunk; dstlinaddr += chunk; } if(srcproc) assert(!RTS_ISSET(srcproc, RTS_SLOT_FREE)); if(dstproc) assert(!RTS_ISSET(dstproc, RTS_SLOT_FREE)); assert(!RTS_ISSET(get_cpulocal_var(ptproc), RTS_SLOT_FREE)); assert(get_cpulocal_var(ptproc)->p_seg.p_cr3_v); return OK; }
PRIVATE void pagefault( struct proc *pr, struct exception_frame * frame, int is_nested) { int in_physcopy = 0; reg_t pagefaultcr2; message m_pagefault; int err; assert(frame); pagefaultcr2 = read_cr2(); #if 0 printf("kernel: pagefault in pr %d, addr 0x%lx, his cr3 0x%lx, actual cr3 0x%lx\n", pr->p_endpoint, pagefaultcr2, pr->p_seg.p_cr3, read_cr3()); #endif if(pr->p_seg.p_cr3) { assert(pr->p_seg.p_cr3 == read_cr3()); } in_physcopy = (frame->eip > (vir_bytes) phys_copy) && (frame->eip < (vir_bytes) phys_copy_fault); if((is_nested || iskernelp(pr)) && catch_pagefaults && in_physcopy) { #if 0 printf("pf caught! addr 0x%lx\n", pagefaultcr2); #endif if (is_nested) { frame->eip = (reg_t) phys_copy_fault_in_kernel; } else { pr->p_reg.pc = (reg_t) phys_copy_fault; pr->p_reg.retreg = pagefaultcr2; } return; } if(is_nested) { panic("pagefault in kernel at pc 0x%lx address 0x%lx", frame->eip, pagefaultcr2); } /* System processes that don't have their own page table can't * have page faults. VM does have its own page table but also * can't have page faults (because VM has to handle them). */ if((pr->p_endpoint <= INIT_PROC_NR && !(pr->p_misc_flags & MF_FULLVM)) || pr->p_endpoint == VM_PROC_NR) { /* Page fault we can't / don't want to * handle. */ printf("pagefault for process %d ('%s'), pc = 0x%x, addr = 0x%x, flags = 0x%x, is_nested %d\n", pr->p_endpoint, pr->p_name, pr->p_reg.pc, pagefaultcr2, frame->errcode, is_nested); proc_stacktrace(pr); printf("pc of pagefault: 0x%lx\n", frame->eip); panic("page fault in system process: %d", pr->p_endpoint); return; } /* Don't schedule this process until pagefault is handled. */ assert(pr->p_seg.p_cr3 == read_cr3()); assert(!RTS_ISSET(pr, RTS_PAGEFAULT)); RTS_SET(pr, RTS_PAGEFAULT); /* tell Vm about the pagefault */ m_pagefault.m_source = pr->p_endpoint; m_pagefault.m_type = VM_PAGEFAULT; m_pagefault.VPF_ADDR = pagefaultcr2; m_pagefault.VPF_FLAGS = frame->errcode; if ((err = mini_send(pr, VM_PROC_NR, &m_pagefault, FROM_KERNEL))) { panic("WARNING: pagefault: mini_send returned %d\n", err); } return; }
/*===========================================================================* * lin_lin_copy * *===========================================================================*/ static int lin_lin_copy(struct proc *srcproc, vir_bytes srclinaddr, struct proc *dstproc, vir_bytes dstlinaddr, vir_bytes bytes) { u32_t addr; proc_nr_t procslot; assert(get_cpulocal_var(ptproc)); assert(get_cpulocal_var(proc_ptr)); assert(read_ttbr0() == get_cpulocal_var(ptproc)->p_seg.p_ttbr); procslot = get_cpulocal_var(ptproc)->p_nr; assert(procslot >= 0 && procslot < ARM_VM_DIR_ENTRIES); if(srcproc) assert(!RTS_ISSET(srcproc, RTS_SLOT_FREE)); if(dstproc) assert(!RTS_ISSET(dstproc, RTS_SLOT_FREE)); assert(!RTS_ISSET(get_cpulocal_var(ptproc), RTS_SLOT_FREE)); assert(get_cpulocal_var(ptproc)->p_seg.p_ttbr_v); if(srcproc) assert(!RTS_ISSET(srcproc, RTS_VMINHIBIT)); if(dstproc) assert(!RTS_ISSET(dstproc, RTS_VMINHIBIT)); while(bytes > 0) { phys_bytes srcptr, dstptr; vir_bytes chunk = bytes; int changed = 0; #ifdef CONFIG_SMP unsigned cpu = cpuid; if (srcproc && GET_BIT(srcproc->p_stale_tlb, cpu)) { changed = 1; UNSET_BIT(srcproc->p_stale_tlb, cpu); } if (dstproc && GET_BIT(dstproc->p_stale_tlb, cpu)) { changed = 1; UNSET_BIT(dstproc->p_stale_tlb, cpu); } #endif /* Set up 1MB ranges. */ srcptr = createpde(srcproc, srclinaddr, &chunk, 0, &changed); dstptr = createpde(dstproc, dstlinaddr, &chunk, 1, &changed); if(changed) { reload_ttbr0(); } /* Copy pages. */ PHYS_COPY_CATCH(srcptr, dstptr, chunk, addr); if(addr) { /* If addr is nonzero, a page fault was caught. * * phys_copy does all memory accesses word-aligned (rounded * down), so pagefaults can occur at a lower address than * the specified offsets. compute the lower bounds for sanity * check use. */ vir_bytes src_aligned = srcptr & ~0x3, dst_aligned = dstptr & ~0x3; if(addr >= src_aligned && addr < (srcptr + chunk)) { return EFAULT_SRC; } if(addr >= dst_aligned && addr < (dstptr + chunk)) { return EFAULT_DST; } panic("lin_lin_copy fault out of range"); /* Not reached. */ return EFAULT; } /* Update counter and addresses for next iteration, if any. */ bytes -= chunk; srclinaddr += chunk; dstlinaddr += chunk; } if(srcproc) assert(!RTS_ISSET(srcproc, RTS_SLOT_FREE)); if(dstproc) assert(!RTS_ISSET(dstproc, RTS_SLOT_FREE)); assert(!RTS_ISSET(get_cpulocal_var(ptproc), RTS_SLOT_FREE)); assert(get_cpulocal_var(ptproc)->p_seg.p_ttbr_v); return OK; }
/*===========================================================================* * sys_task * *===========================================================================*/ PUBLIC void sys_task() { /* Main entry point of sys_task. Get the message and dispatch on type. */ static message m; register int result; register struct proc *caller_ptr; int s; int call_nr; int n = 0; /* Initialize the system task. */ initialize(); while (TRUE) { struct proc *restarting; restarting = vmrestart_check(&m); if(!restarting) { int r; /* Get work. Block and wait until a request message arrives. */ if((r=receive(ANY, &m)) != OK) minix_panic("receive() failed", r); } sys_call_code = (unsigned) m.m_type; call_nr = sys_call_code - KERNEL_CALL; who_e = m.m_source; okendpt(who_e, &who_p); caller_ptr = proc_addr(who_p); /* See if the caller made a valid request and try to handle it. */ if (call_nr < 0 || call_nr >= NR_SYS_CALLS) { /* check call number */ kprintf("SYSTEM: illegal request %d from %d.\n", call_nr,m.m_source); result = EBADREQUEST; /* illegal message type */ } else if (!GET_BIT(priv(caller_ptr)->s_k_call_mask, call_nr)) { result = ECALLDENIED; /* illegal message type */ } else { result = (*call_vec[call_nr])(&m); /* handle the system call */ } if(result == VMSUSPEND) { /* Special case: message has to be saved for handling * until VM tells us it's allowed. VM has been notified * and we must wait for its reply to restart the call. */ vmassert(RTS_ISSET(caller_ptr, VMREQUEST)); vmassert(caller_ptr->p_vmrequest.type == VMSTYPE_KERNELCALL); memcpy(&caller_ptr->p_vmrequest.saved.reqmsg, &m, sizeof(m)); } else if (result != EDONTREPLY) { /* Send a reply, unless inhibited by a handler function. * Use the kernel function lock_send() to prevent a system * call trap. */ if(restarting) { vmassert(!RTS_ISSET(restarting, VMREQUEST)); #if 0 vmassert(!RTS_ISSET(restarting, VMREQTARGET)); #endif } m.m_type = result; /* report status of call */ if(WILLRECEIVE(caller_ptr, SYSTEM)) { if (OK != (s=lock_send(m.m_source, &m))) { kprintf("SYSTEM, reply to %d failed: %d\n", m.m_source, s); } } else { kprintf("SYSTEM: not replying to %d; not ready\n", caller_ptr->p_endpoint); } } } }
/*===========================================================================* * do_sigsend * *===========================================================================*/ int do_sigsend(struct proc * caller, message * m_ptr) { /* Handle sys_sigsend, POSIX-style signal handling. */ struct sigmsg smsg; register struct proc *rp; struct sigcontext sc, *scp; struct sigframe fr, *frp; int proc_nr, r; if (!isokendpt(m_ptr->SIG_ENDPT, &proc_nr)) return(EINVAL); if (iskerneln(proc_nr)) return(EPERM); rp = proc_addr(proc_nr); /* Get the sigmsg structure into our address space. */ if((r=data_copy_vmcheck(caller, caller->p_endpoint, (vir_bytes) m_ptr->SIG_CTXT_PTR, KERNEL, (vir_bytes) &smsg, (phys_bytes) sizeof(struct sigmsg))) != OK) return r; /* Compute the user stack pointer where sigcontext will be stored. */ smsg.sm_stkptr = arch_get_sp(rp); scp = (struct sigcontext *) smsg.sm_stkptr - 1; /* Copy the registers to the sigcontext structure. */ memcpy(&sc.sc_regs, (char *) &rp->p_reg, sizeof(sigregs)); #if defined(__i386__) sc.trap_style = rp->p_seg.p_kern_trap_style; if(sc.trap_style == KTS_NONE) { printf("do_sigsend: sigsend an unsaved process\n"); return EINVAL; } if(proc_used_fpu(rp)) { /* save the FPU context before saving it to the sig context */ save_fpu(rp); memcpy(&sc.sc_fpu_state, rp->p_seg.fpu_state, FPU_XFP_SIZE); } #endif /* Finish the sigcontext initialization. */ sc.sc_mask = smsg.sm_mask; sc.sc_flags = rp->p_misc_flags & MF_FPU_INITIALIZED; /* Copy the sigcontext structure to the user's stack. */ if((r=data_copy_vmcheck(caller, KERNEL, (vir_bytes) &sc, m_ptr->SIG_ENDPT, (vir_bytes) scp, (vir_bytes) sizeof(struct sigcontext))) != OK) return r; /* Initialize the sigframe structure. */ frp = (struct sigframe *) scp - 1; fr.sf_scpcopy = scp; fr.sf_retadr2= (void (*)()) rp->p_reg.pc; fr.sf_fp = rp->p_reg.fp; rp->p_reg.fp = (reg_t) &frp->sf_fp; fr.sf_scp = scp; fpu_sigcontext(rp, &fr, &sc); fr.sf_signo = smsg.sm_signo; fr.sf_retadr = (void (*)()) smsg.sm_sigreturn; #if defined(__arm__) /* use the ARM link register to set the return address from the signal * handler */ rp->p_reg.lr = (reg_t) fr.sf_retadr; if(rp->p_reg.lr & 1) { printf("sigsend: LSB LR makes no sense.\n"); } /* pass signal handler parameters in registers */ rp->p_reg.retreg = (reg_t) fr.sf_signo; rp->p_reg.r1 = (reg_t) fr.sf_code; rp->p_reg.r2 = (reg_t) fr.sf_scp; rp->p_misc_flags |= MF_CONTEXT_SET; #endif /* Copy the sigframe structure to the user's stack. */ if((r=data_copy_vmcheck(caller, KERNEL, (vir_bytes) &fr, m_ptr->SIG_ENDPT, (vir_bytes) frp, (vir_bytes) sizeof(struct sigframe))) != OK) return r; /* Reset user registers to execute the signal handler. */ rp->p_reg.sp = (reg_t) frp; rp->p_reg.pc = (reg_t) smsg.sm_sighandler; /* Signal handler should get clean FPU. */ rp->p_misc_flags &= ~MF_FPU_INITIALIZED; if(!RTS_ISSET(rp, RTS_PROC_STOP)) { printf("system: warning: sigsend a running process\n"); printf("caller stack: "); proc_stacktrace(caller); } return(OK); }
/*===========================================================================* * do_vmctl * *===========================================================================*/ PUBLIC int do_vmctl(struct proc * caller, message * m_ptr) { int proc_nr; endpoint_t ep = m_ptr->SVMCTL_WHO; struct proc *p, *rp, *target; int err; if(ep == SELF) { ep = caller->p_endpoint; } if(!isokendpt(ep, &proc_nr)) { printf("do_vmctl: unexpected endpoint %d from VM\n", ep); return EINVAL; } p = proc_addr(proc_nr); switch(m_ptr->SVMCTL_PARAM) { case VMCTL_CLEAR_PAGEFAULT: assert(RTS_ISSET(p,RTS_PAGEFAULT)); RTS_UNSET(p, RTS_PAGEFAULT); return OK; case VMCTL_MEMREQ_GET: /* Send VM the information about the memory request. */ if(!(rp = vmrequest)) return ESRCH; assert(RTS_ISSET(rp, RTS_VMREQUEST)); okendpt(rp->p_vmrequest.target, &proc_nr); target = proc_addr(proc_nr); /* Reply with request fields. */ switch(rp->p_vmrequest.req_type) { case VMPTYPE_CHECK: m_ptr->SVMCTL_MRG_TARGET = rp->p_vmrequest.target; m_ptr->SVMCTL_MRG_ADDR = rp->p_vmrequest.params.check.start; m_ptr->SVMCTL_MRG_LENGTH = rp->p_vmrequest.params.check.length; m_ptr->SVMCTL_MRG_FLAG = rp->p_vmrequest.params.check.writeflag; m_ptr->SVMCTL_MRG_REQUESTOR = (void *) rp->p_endpoint; break; case VMPTYPE_SMAP: case VMPTYPE_SUNMAP: case VMPTYPE_COWMAP: assert(RTS_ISSET(target,RTS_VMREQTARGET)); RTS_UNSET(target, RTS_VMREQTARGET); m_ptr->SVMCTL_MRG_TARGET = rp->p_vmrequest.target; m_ptr->SVMCTL_MRG_ADDR = rp->p_vmrequest.params.map.vir_d; m_ptr->SVMCTL_MRG_EP2 = rp->p_vmrequest.params.map.ep_s; m_ptr->SVMCTL_MRG_ADDR2 = rp->p_vmrequest.params.map.vir_s; m_ptr->SVMCTL_MRG_LENGTH = rp->p_vmrequest.params.map.length; m_ptr->SVMCTL_MRG_FLAG = rp->p_vmrequest.params.map.writeflag; m_ptr->SVMCTL_MRG_REQUESTOR = (void *) rp->p_endpoint; break; default: panic("VMREQUEST wrong type"); } rp->p_vmrequest.vmresult = VMSUSPEND; /* Remove from request chain. */ vmrequest = vmrequest->p_vmrequest.nextrequestor; return rp->p_vmrequest.req_type; case VMCTL_MEMREQ_REPLY: assert(RTS_ISSET(p, RTS_VMREQUEST)); assert(p->p_vmrequest.vmresult == VMSUSPEND); okendpt(p->p_vmrequest.target, &proc_nr); target = proc_addr(proc_nr); p->p_vmrequest.vmresult = m_ptr->SVMCTL_VALUE; assert(p->p_vmrequest.vmresult != VMSUSPEND); switch(p->p_vmrequest.type) { case VMSTYPE_KERNELCALL: /* * we will have to resume execution of the kernel call * as soon the scheduler picks up this process again */ p->p_misc_flags |= MF_KCALL_RESUME; break; case VMSTYPE_DELIVERMSG: assert(p->p_misc_flags & MF_DELIVERMSG); assert(p == target); assert(RTS_ISSET(p, RTS_VMREQUEST)); break; case VMSTYPE_MAP: assert(RTS_ISSET(p, RTS_VMREQUEST)); break; default: panic("strange request type: %d",p->p_vmrequest.type); } RTS_UNSET(p, RTS_VMREQUEST); return OK; case VMCTL_ENABLE_PAGING: if(vm_running) panic("do_vmctl: paging already enabled"); if (arch_enable_paging(caller, m_ptr) != OK) panic("do_vmctl: paging enabling failed"); return OK; case VMCTL_KERN_PHYSMAP: { int i = m_ptr->SVMCTL_VALUE; return arch_phys_map(i, (phys_bytes *) &m_ptr->SVMCTL_MAP_PHYS_ADDR, (phys_bytes *) &m_ptr->SVMCTL_MAP_PHYS_LEN, &m_ptr->SVMCTL_MAP_FLAGS); } case VMCTL_KERN_MAP_REPLY: { return arch_phys_map_reply(m_ptr->SVMCTL_VALUE, (vir_bytes) m_ptr->SVMCTL_MAP_VIR_ADDR); } case VMCTL_VMINHIBIT_SET: /* check if we must stop a process on a different CPU */ #if CONFIG_SMP if (p->p_cpu != cpuid) { smp_schedule_vminhibit(p); } else #endif RTS_SET(p, RTS_VMINHIBIT); #if CONFIG_SMP p->p_misc_flags |= MF_FLUSH_TLB; #endif return OK; case VMCTL_VMINHIBIT_CLEAR: assert(RTS_ISSET(p, RTS_VMINHIBIT)); /* * the processes is certainly not runnable, no need to tell its * cpu */ RTS_UNSET(p, RTS_VMINHIBIT); return OK; } /* Try architecture-specific vmctls. */ return arch_do_vmctl(m_ptr, p); }