Changeset View
Changeset View
Standalone View
Standalone View
sys/i386/i386/vm_machdep.c
Show First 20 Lines • Show All 127 Lines • ▼ Show 20 Lines | alloc_fpusave(int flags) | ||||
res = malloc(cpu_max_ext_state_size, M_DEVBUF, flags); | res = malloc(cpu_max_ext_state_size, M_DEVBUF, flags); | ||||
if (use_xsave) { | 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); | |||||
td1->td_pcb->pcb_gs = rgs(); | |||||
critical_enter(); | |||||
if (PCPU_GET(fpcurthread) == td1) | |||||
npxsave(td1->td_pcb->pcb_save); | |||||
critical_exit(); | |||||
} | |||||
/* 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 NPX and segment bases. */ | |||||
if ((td2->td_pflags & TDP_KTHREAD) != 0) { | |||||
pcb2->pcb_gs = _udatasel; | |||||
set_fsbase(td2, 0); | |||||
set_gsbase(td2, 0); | |||||
pcb2->pcb_flags &= ~(PCB_NPXINITDONE | PCB_NPXUSERINITDONE | | |||||
PCB_KERNNPX | PCB_KERNNPX_THR); | |||||
} else { | |||||
MPASS((pcb2->pcb_flags & (PCB_KERNNPX | PCB_KERNNPX_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_edi = 0; | |||||
pcb2->pcb_esi = (int)fork_return; /* trampoline arg */ | |||||
pcb2->pcb_ebp = 0; | |||||
pcb2->pcb_esp = (int)td2->td_frame - sizeof(void *); /* trampoline arg */ | |||||
pcb2->pcb_ebx = (int)td2; /* trampoline arg */ | |||||
pcb2->pcb_eip = (int)fork_trampoline + setidt_disp; | |||||
/* | |||||
* If we didn't copy the pcb, we'd need to do the following registers: | |||||
* pcb2->pcb_cr3: cloned above. | |||||
* 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_gs: cloned above. | |||||
* pcb2->pcb_ext: cleared below. | |||||
*/ | |||||
pcb2->pcb_ext = NULL; | |||||
/* 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; | |||||
} | |||||
/* | |||||
* 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 17 Lines | if ((flags & RFMEM) == 0) { | ||||
set_user_ldt(mdp1); | set_user_ldt(mdp1); | ||||
user_ldt_deref(pldt1); | user_ldt_deref(pldt1); | ||||
} else | } else | ||||
mtx_unlock_spin(&dt_lock); | mtx_unlock_spin(&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); | |||||
td1->td_pcb->pcb_gs = rgs(); | |||||
critical_enter(); | |||||
if (PCPU_GET(fpcurthread) == td1) | |||||
npxsave(td1->td_pcb->pcb_save); | |||||
critical_exit(); | |||||
} | |||||
/* Point the pcb to the top of the stack */ | /* Point the pcb to the top of the stack */ | ||||
pcb2 = get_pcb_td(td2); | pcb2 = get_pcb_td(td2); | ||||
td2->td_pcb = pcb2; | td2->td_pcb = pcb2; | ||||
/* Copy td1's pcb */ | copy_thread(td1, td2); | ||||
bcopy(td1->td_pcb, pcb2, sizeof(*pcb2)); | |||||
/* Properly initialize pcb_save */ | |||||
pcb2->pcb_save = get_pcb_user_save_pcb(pcb2); | |||||
/* Kernel processes start with clean NPX and segment bases. */ | |||||
if ((td2->td_pflags & TDP_KTHREAD) != 0) { | |||||
pcb2->pcb_gs = _udatasel; | |||||
set_fsbase(td2, 0); | |||||
set_gsbase(td2, 0); | |||||
pcb2->pcb_flags &= ~(PCB_NPXINITDONE | PCB_NPXUSERINITDONE | | |||||
PCB_KERNNPX | PCB_KERNNPX_THR); | |||||
} else { | |||||
MPASS((pcb2->pcb_flags & (PCB_KERNNPX | PCB_KERNNPX_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 */ | /* 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. | ||||
* The -VM86_STACK_SPACE (-16) is so we can expand the trapframe | * The -VM86_STACK_SPACE (-16) is so we can expand the trapframe | ||||
Show All 9 Lines | cpu_fork(struct thread *td1, struct proc *p2, struct thread *td2, int flags) | ||||
/* | /* | ||||
* 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_eflags &= ~PSL_T; | td2->td_frame->tf_eflags &= ~PSL_T; | ||||
/* | /* Set cr3 for the new process. */ | ||||
* Set registers for trampoline to user mode. Leave space for the | |||||
* return address on stack. These are the kernel mode register values. | |||||
*/ | |||||
pcb2->pcb_cr3 = pmap_get_cr3(vmspace_pmap(p2->p_vmspace)); | pcb2->pcb_cr3 = pmap_get_cr3(vmspace_pmap(p2->p_vmspace)); | ||||
pcb2->pcb_edi = 0; | |||||
pcb2->pcb_esi = (int)fork_return; /* fork_trampoline argument */ | |||||
pcb2->pcb_ebp = 0; | |||||
pcb2->pcb_esp = (int)td2->td_frame - sizeof(void *); | |||||
pcb2->pcb_ebx = (int)td2; /* fork_trampoline argument */ | |||||
pcb2->pcb_eip = (int)fork_trampoline + setidt_disp; | |||||
/*- | |||||
* 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_gs: cloned above. | |||||
* pcb2->pcb_ext: cleared below. | |||||
*/ | |||||
/* | /* | ||||
* XXX don't copy the i/o pages. this should probably be fixed. | * XXX don't copy the i/o pages. this should probably be fixed. | ||||
*/ | */ | ||||
pcb2->pcb_ext = 0; | pcb2->pcb_ext = NULL; | ||||
/* Copy the LDT, if necessary. */ | /* Copy the LDT, if necessary. */ | ||||
mtx_lock_spin(&dt_lock); | mtx_lock_spin(&dt_lock); | ||||
if (mdp2->md_ldt != NULL) { | if (mdp2->md_ldt != NULL) { | ||||
if (flags & RFMEM) { | if (flags & RFMEM) { | ||||
mdp2->md_ldt->ldt_refcnt++; | mdp2->md_ldt->ldt_refcnt++; | ||||
} else { | } else { | ||||
mdp2->md_ldt = user_ldt_alloc(mdp2, | mdp2->md_ldt = user_ldt_alloc(mdp2, | ||||
mdp2->md_ldt->ldt_len); | mdp2->md_ldt->ldt_len); | ||||
if (mdp2->md_ldt == NULL) | if (mdp2->md_ldt == NULL) | ||||
panic("could not copy LDT"); | panic("could not copy LDT"); | ||||
} | } | ||||
} | } | ||||
mtx_unlock_spin(&dt_lock); | mtx_unlock_spin(&dt_lock); | ||||
/* 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; | |||||
/* | /* | ||||
* Now, cpu_switch() can schedule the new process. | * Now, cpu_switch() can schedule the new process. | ||||
* pcb_esp is loaded pointing to the cpu_switch() stack frame | * pcb_esp 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() | * %ebx 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. | ||||
▲ Show 20 Lines • Show All 151 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); | ||||
/* Point the pcb to the top of the stack. */ | |||||
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); | |||||
td0->td_pcb->pcb_gs = rgs(); | |||||
critical_enter(); | |||||
if (PCPU_GET(fpcurthread) == td0) | |||||
npxsave(td0->td_pcb->pcb_save); | |||||
critical_exit(); | |||||
} | |||||
/* | /* | ||||
* 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 NPX and segment bases. */ | |||||
if ((td->td_pflags & TDP_KTHREAD) != 0) { | |||||
pcb2->pcb_gs = _udatasel; | |||||
set_fsbase(td, 0); | |||||
set_gsbase(td, 0); | |||||
pcb2->pcb_flags &= ~(PCB_NPXINITDONE | PCB_NPXUSERINITDONE | | |||||
PCB_KERNNPX | PCB_KERNNPX_THR); | |||||
} else { | |||||
MPASS((pcb2->pcb_flags & (PCB_KERNNPX | PCB_KERNNPX_THR)) == 0); | |||||
bcopy(get_pcb_user_save_td(td0), pcb2->pcb_save, | |||||
cpu_max_ext_state_size); | |||||
} | |||||
/* | |||||
* 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_eflags &= ~PSL_T; | td->td_frame->tf_eflags &= ~PSL_T; | ||||
/* | |||||
* Set registers for trampoline to user mode. Leave space for the | |||||
* return address on stack. These are the kernel mode register values. | |||||
*/ | |||||
pcb2->pcb_edi = 0; | |||||
pcb2->pcb_esi = (int)fork_return; /* trampoline arg */ | |||||
pcb2->pcb_ebp = 0; | |||||
pcb2->pcb_esp = (int)td->td_frame - sizeof(void *); /* trampoline arg */ | |||||
pcb2->pcb_ebx = (int)td; /* trampoline arg */ | |||||
pcb2->pcb_eip = (int)fork_trampoline + setidt_disp; | |||||
/* | |||||
* If we didn't copy the pcb, we'd need to do the following registers: | |||||
* pcb2->pcb_cr3: cloned above. | |||||
* 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_gs: cloned above. | |||||
* pcb2->pcb_ext: cleared below. | |||||
*/ | |||||
pcb2->pcb_ext = NULL; | |||||
/* 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; | |||||
} | } | ||||
/* | /* | ||||
* 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 193 Lines • Show Last 20 Lines |