Changeset View
Changeset View
Standalone View
Standalone View
sys/amd64/amd64/vm_machdep.c
Show First 20 Lines • Show All 131 Lines • ▼ Show 20 Lines | if (use_xsave) { | ||||
sf = (struct savefpu_ymm *)res; | sf = (struct savefpu_ymm *)res; | ||||
bzero(&sf->sv_xstate.sx_hd, sizeof(sf->sv_xstate.sx_hd)); | bzero(&sf->sv_xstate.sx_hd, sizeof(sf->sv_xstate.sx_hd)); | ||||
sf->sv_xstate.sx_hd.xstate_bv = xsave_mask; | sf->sv_xstate.sx_hd.xstate_bv = xsave_mask; | ||||
} | } | ||||
return (res); | return (res); | ||||
} | } | ||||
/* | /* | ||||
* Common code shared between cpu_fork() and cpu_copy_thread() for | |||||
* initializing a thread. | |||||
*/ | |||||
static void | |||||
copy_thread(struct thread *td1, struct thread *td2) | |||||
{ | |||||
struct pcb *pcb2; | |||||
pcb2 = td2->td_pcb; | |||||
/* Ensure that td1's pcb is up to date for user threads. */ | |||||
if ((td2->td_pflags & TDP_KTHREAD) == 0) { | |||||
MPASS(td1 == curthread); | |||||
fpuexit(td1); | |||||
update_pcb_bases(td1->td_pcb); | |||||
} | |||||
/* Copy td1's pcb */ | |||||
bcopy(td1->td_pcb, pcb2, sizeof(*pcb2)); | |||||
/* Properly initialize pcb_save */ | |||||
pcb2->pcb_save = get_pcb_user_save_pcb(pcb2); | |||||
/* Kernel threads start with clean FPU and segment bases. */ | |||||
if ((td2->td_pflags & TDP_KTHREAD) != 0) { | |||||
pcb2->pcb_fsbase = 0; | |||||
pcb2->pcb_gsbase = 0; | |||||
clear_pcb_flags(pcb2, PCB_FPUINITDONE | PCB_USERFPUINITDONE | | |||||
PCB_KERNFPU | PCB_KERNFPU_THR); | |||||
} else { | |||||
MPASS((pcb2->pcb_flags & (PCB_KERNFPU | PCB_KERNFPU_THR)) == 0); | |||||
bcopy(get_pcb_user_save_td(td1), get_pcb_user_save_pcb(pcb2), | |||||
cpu_max_ext_state_size); | |||||
} | |||||
/* | |||||
* Set registers for trampoline to user mode. Leave space for the | |||||
* return address on stack. These are the kernel mode register values. | |||||
*/ | |||||
pcb2->pcb_r12 = (register_t)fork_return; /* fork_trampoline argument */ | |||||
pcb2->pcb_rbp = 0; | |||||
pcb2->pcb_rsp = (register_t)td2->td_frame - sizeof(void *); | |||||
pcb2->pcb_rbx = (register_t)td2; /* fork_trampoline argument */ | |||||
pcb2->pcb_rip = (register_t)fork_trampoline; | |||||
/*- | |||||
* pcb2->pcb_dr*: cloned above. | |||||
* pcb2->pcb_savefpu: cloned above. | |||||
* pcb2->pcb_flags: cloned above. | |||||
* pcb2->pcb_onfault: cloned above (always NULL here?). | |||||
* pcb2->pcb_[fg]sbase: cloned above | |||||
*/ | |||||
/* Setup to release spin count in fork_exit(). */ | |||||
td2->td_md.md_spinlock_count = 1; | |||||
td2->td_md.md_saved_flags = PSL_KERNEL | PSL_I; | |||||
pmap_thread_init_invl_gen(td2); | |||||
} | |||||
/* | |||||
* Finish a fork operation, with process p2 nearly set up. | * Finish a fork operation, with process p2 nearly set up. | ||||
* Copy and update the pcb, set up the stack so that the child | * Copy and update the pcb, set up the stack so that the child | ||||
* ready to run and return to user mode. | * ready to run and return to user mode. | ||||
*/ | */ | ||||
void | void | ||||
cpu_fork(struct thread *td1, struct proc *p2, struct thread *td2, int flags) | cpu_fork(struct thread *td1, struct proc *p2, struct thread *td2, int flags) | ||||
{ | { | ||||
struct proc *p1; | struct proc *p1; | ||||
Show All 11 Lines | if ((flags & RFMEM) == 0) { | ||||
pldt->ldt_refcnt > 1 && | pldt->ldt_refcnt > 1 && | ||||
user_ldt_alloc(p1, 1) == NULL) | user_ldt_alloc(p1, 1) == NULL) | ||||
panic("could not copy LDT"); | panic("could not copy LDT"); | ||||
mtx_unlock(&dt_lock); | mtx_unlock(&dt_lock); | ||||
} | } | ||||
return; | return; | ||||
} | } | ||||
/* Ensure that td1's pcb is up to date for user processes. */ | |||||
if ((td2->td_pflags & TDP_KTHREAD) == 0) { | |||||
MPASS(td1 == curthread); | |||||
fpuexit(td1); | |||||
update_pcb_bases(td1->td_pcb); | |||||
} | |||||
/* Point the stack and pcb to the actual location */ | /* Point the stack and pcb to the actual location */ | ||||
set_top_of_stack_td(td2); | set_top_of_stack_td(td2); | ||||
td2->td_pcb = pcb2 = get_pcb_td(td2); | td2->td_pcb = pcb2 = get_pcb_td(td2); | ||||
/* Copy td1's pcb */ | copy_thread(td1, td2); | ||||
bcopy(td1->td_pcb, pcb2, sizeof(*pcb2)); | |||||
/* Properly initialize pcb_save */ | /* Point mdproc and then copy over p1's contents */ | ||||
pcb2->pcb_save = get_pcb_user_save_pcb(pcb2); | |||||
/* Kernel processes start with clean FPU and segment bases. */ | |||||
if ((td2->td_pflags & TDP_KTHREAD) != 0) { | |||||
pcb2->pcb_fsbase = 0; | |||||
pcb2->pcb_gsbase = 0; | |||||
clear_pcb_flags(pcb2, PCB_FPUINITDONE | PCB_USERFPUINITDONE | | |||||
PCB_KERNFPU | PCB_KERNFPU_THR); | |||||
} else { | |||||
MPASS((pcb2->pcb_flags & (PCB_KERNFPU | PCB_KERNFPU_THR)) == 0); | |||||
bcopy(get_pcb_user_save_td(td1), get_pcb_user_save_pcb(pcb2), | |||||
cpu_max_ext_state_size); | |||||
} | |||||
/* Point mdproc and then copy over td1's contents */ | |||||
mdp2 = &p2->p_md; | mdp2 = &p2->p_md; | ||||
bcopy(&p1->p_md, mdp2, sizeof(*mdp2)); | bcopy(&p1->p_md, mdp2, sizeof(*mdp2)); | ||||
/* | /* | ||||
* Copy the trap frame for the return to user mode as if from a | * Copy the trap frame for the return to user mode as if from a | ||||
* syscall. This copies most of the user mode register values. | * syscall. This copies most of the user mode register values. | ||||
*/ | */ | ||||
td2->td_frame = (struct trapframe *)td2->td_md.md_stack_base - 1; | td2->td_frame = (struct trapframe *)td2->td_md.md_stack_base - 1; | ||||
bcopy(td1->td_frame, td2->td_frame, sizeof(struct trapframe)); | bcopy(td1->td_frame, td2->td_frame, sizeof(struct trapframe)); | ||||
td2->td_frame->tf_rax = 0; /* Child returns zero */ | td2->td_frame->tf_rax = 0; /* Child returns zero */ | ||||
td2->td_frame->tf_rflags &= ~PSL_C; /* success */ | td2->td_frame->tf_rflags &= ~PSL_C; /* success */ | ||||
td2->td_frame->tf_rdx = 1; | td2->td_frame->tf_rdx = 1; | ||||
/* | /* | ||||
* If the parent process has the trap bit set (i.e. a debugger | * If the parent process has the trap bit set (i.e. a debugger | ||||
* had single stepped the process to the system call), we need | * had single stepped the process to the system call), we need | ||||
* to clear the trap flag from the new frame. | * to clear the trap flag from the new frame. | ||||
*/ | */ | ||||
td2->td_frame->tf_rflags &= ~PSL_T; | td2->td_frame->tf_rflags &= ~PSL_T; | ||||
/* | /* As on i386, do not copy io permission bitmap. */ | ||||
* Set registers for trampoline to user mode. Leave space for the | |||||
* return address on stack. These are the kernel mode register values. | |||||
*/ | |||||
pcb2->pcb_r12 = (register_t)fork_return; /* fork_trampoline argument */ | |||||
pcb2->pcb_rbp = 0; | |||||
pcb2->pcb_rsp = (register_t)td2->td_frame - sizeof(void *); | |||||
pcb2->pcb_rbx = (register_t)td2; /* fork_trampoline argument */ | |||||
pcb2->pcb_rip = (register_t)fork_trampoline; | |||||
/*- | |||||
* pcb2->pcb_dr*: cloned above. | |||||
* pcb2->pcb_savefpu: cloned above. | |||||
* pcb2->pcb_flags: cloned above. | |||||
* pcb2->pcb_onfault: cloned above (always NULL here?). | |||||
* pcb2->pcb_[fg]sbase: cloned above | |||||
*/ | |||||
/* Setup to release spin count in fork_exit(). */ | |||||
td2->td_md.md_spinlock_count = 1; | |||||
td2->td_md.md_saved_flags = PSL_KERNEL | PSL_I; | |||||
pmap_thread_init_invl_gen(td2); | |||||
/* As an i386, do not copy io permission bitmap. */ | |||||
pcb2->pcb_tssp = NULL; | pcb2->pcb_tssp = NULL; | ||||
/* New segment registers. */ | /* New segment registers. */ | ||||
set_pcb_flags_raw(pcb2, PCB_FULL_IRET); | set_pcb_flags_raw(pcb2, PCB_FULL_IRET); | ||||
/* Copy the LDT, if necessary. */ | /* Copy the LDT, if necessary. */ | ||||
mdp1 = &td1->td_proc->p_md; | mdp1 = &td1->td_proc->p_md; | ||||
mdp2 = &p2->p_md; | mdp2 = &p2->p_md; | ||||
Show All 21 Lines | if (mdp1->md_ldt != NULL) { | ||||
mdp2->md_ldt = NULL; | mdp2->md_ldt = NULL; | ||||
mtx_unlock(&dt_lock); | mtx_unlock(&dt_lock); | ||||
/* | /* | ||||
* Now, cpu_switch() can schedule the new process. | * Now, cpu_switch() can schedule the new process. | ||||
* pcb_rsp is loaded pointing to the cpu_switch() stack frame | * pcb_rsp is loaded pointing to the cpu_switch() stack frame | ||||
* containing the return address when exiting cpu_switch. | * containing the return address when exiting cpu_switch. | ||||
* This will normally be to fork_trampoline(), which will have | * This will normally be to fork_trampoline(), which will have | ||||
* %ebx loaded with the new proc's pointer. fork_trampoline() | * %rbx loaded with the new proc's pointer. fork_trampoline() | ||||
* will set up a stack to call fork_return(p, frame); to complete | * will set up a stack to call fork_return(p, frame); to complete | ||||
* the return to user-mode. | * the return to user-mode. | ||||
*/ | */ | ||||
} | } | ||||
/* | /* | ||||
* Intercept the return address from a freshly forked process that has NOT | * Intercept the return address from a freshly forked process that has NOT | ||||
* been scheduled yet. | * been scheduled yet. | ||||
▲ Show 20 Lines • Show All 279 Lines • ▼ Show 20 Lines | |||||
* thread, about to return to userspace. Put enough state in the new | * thread, about to return to userspace. Put enough state in the new | ||||
* thread's PCB to get it to go back to the fork_return(), which | * thread's PCB to get it to go back to the fork_return(), which | ||||
* finalizes the thread state and handles peculiarities of the first | * finalizes the thread state and handles peculiarities of the first | ||||
* return to userspace for the new thread. | * return to userspace for the new thread. | ||||
*/ | */ | ||||
void | void | ||||
cpu_copy_thread(struct thread *td, struct thread *td0) | cpu_copy_thread(struct thread *td, struct thread *td0) | ||||
{ | { | ||||
struct pcb *pcb2; | copy_thread(td0, td); | ||||
pcb2 = td->td_pcb; | |||||
/* Ensure that td0's pcb is up to date for user threads. */ | |||||
if ((td->td_pflags & TDP_KTHREAD) == 0) { | |||||
MPASS(td0 == curthread); | |||||
fpuexit(td0); | |||||
update_pcb_bases(td0->td_pcb); | |||||
} | |||||
/* | /* | ||||
* Copy the upcall pcb. This loads kernel regs. | |||||
* Those not loaded individually below get their default | |||||
* values here. | |||||
*/ | |||||
bcopy(td0->td_pcb, pcb2, sizeof(*pcb2)); | |||||
pcb2->pcb_save = get_pcb_user_save_pcb(pcb2); | |||||
/* Kernel threads start with clean FPU and segment bases. */ | |||||
if ((td->td_pflags & TDP_KTHREAD) != 0) { | |||||
pcb2->pcb_fsbase = 0; | |||||
pcb2->pcb_gsbase = 0; | |||||
clear_pcb_flags(pcb2, PCB_FPUINITDONE | PCB_USERFPUINITDONE | | |||||
PCB_KERNFPU | PCB_KERNFPU_THR); | |||||
} else { | |||||
MPASS((pcb2->pcb_flags & (PCB_KERNFPU | PCB_KERNFPU_THR)) == 0); | |||||
bcopy(get_pcb_user_save_td(td0), pcb2->pcb_save, | |||||
cpu_max_ext_state_size); | |||||
} | |||||
set_pcb_flags_raw(pcb2, PCB_FULL_IRET); | |||||
/* | |||||
* Copy user general-purpose registers. | * Copy user general-purpose registers. | ||||
* | * | ||||
* Some of these registers are rewritten by cpu_set_upcall() | * Some of these registers are rewritten by cpu_set_upcall() | ||||
* and linux_set_upcall_kse(). | * and linux_set_upcall_kse(). | ||||
*/ | */ | ||||
bcopy(td0->td_frame, td->td_frame, sizeof(struct trapframe)); | bcopy(td0->td_frame, td->td_frame, sizeof(struct trapframe)); | ||||
/* If the current thread has the trap bit set (i.e. a debugger had | /* If the current thread has the trap bit set (i.e. a debugger had | ||||
* single stepped the process to the system call), we need to clear | * single stepped the process to the system call), we need to clear | ||||
* the trap flag from the new frame. Otherwise, the new thread will | * the trap flag from the new frame. Otherwise, the new thread will | ||||
* receive a (likely unexpected) SIGTRAP when it executes the first | * receive a (likely unexpected) SIGTRAP when it executes the first | ||||
* instruction after returning to userland. | * instruction after returning to userland. | ||||
*/ | */ | ||||
td->td_frame->tf_rflags &= ~PSL_T; | td->td_frame->tf_rflags &= ~PSL_T; | ||||
/* | set_pcb_flags_raw(td->td_pcb, PCB_FULL_IRET); | ||||
* Set registers for trampoline to user mode. Leave space for the | |||||
* return address on stack. These are the kernel mode register values. | |||||
*/ | |||||
pcb2->pcb_r12 = (register_t)fork_return; /* trampoline arg */ | |||||
pcb2->pcb_rbp = 0; | |||||
pcb2->pcb_rsp = (register_t)td->td_frame - sizeof(void *); /* trampoline arg */ | |||||
pcb2->pcb_rbx = (register_t)td; /* trampoline arg */ | |||||
pcb2->pcb_rip = (register_t)fork_trampoline; | |||||
/* | |||||
* If we didn't copy the pcb, we'd need to do the following registers: | |||||
* pcb2->pcb_dr*: cloned above. | |||||
* pcb2->pcb_savefpu: cloned above. | |||||
* pcb2->pcb_onfault: cloned above (always NULL here?). | |||||
* pcb2->pcb_[fg]sbase: cloned above | |||||
*/ | |||||
/* Setup to release spin count in fork_exit(). */ | |||||
td->td_md.md_spinlock_count = 1; | |||||
td->td_md.md_saved_flags = PSL_KERNEL | PSL_I; | |||||
pmap_thread_init_invl_gen(td); | |||||
} | } | ||||
/* | /* | ||||
* Set that machine state for performing an upcall that starts | * Set that machine state for performing an upcall that starts | ||||
* the entry function with the given argument. | * the entry function with the given argument. | ||||
*/ | */ | ||||
void | void | ||||
cpu_set_upcall(struct thread *td, void (*entry)(void *), void *arg, | cpu_set_upcall(struct thread *td, void (*entry)(void *), void *arg, | ||||
▲ Show 20 Lines • Show All 110 Lines • Show Last 20 Lines |