Changeset View
Standalone View
sys/amd64/amd64/vm_machdep.c
Show First 20 Lines • Show All 158 Lines • ▼ Show 20 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. */ | /* Ensure that td1's pcb is up to date for user processes. */ | ||||
if ((td2->td_pflags & TDP_KTHREAD) == 0) { | |||||
MPASS(td1 == curthread); | |||||
fpuexit(td1); | fpuexit(td1); | ||||
if (td1 == curthread) | |||||
update_pcb_bases(td1->td_pcb); | 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 td1's pcb */ | ||||
bcopy(td1->td_pcb, pcb2, sizeof(*pcb2)); | bcopy(td1->td_pcb, pcb2, sizeof(*pcb2)); | ||||
/* Properly initialize pcb_save */ | /* Properly initialize pcb_save */ | ||||
pcb2->pcb_save = get_pcb_user_save_pcb(pcb2); | 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); | |||||
kib: Should we clear PCB_KERNFPU_THR, or leave it alone? | |||||
Done Inline ActionsTo get clean state we should probably clear it as well. That's probably a (small) bug that we don't clear it now. jhb: To get clean state we should probably clear it as well. That's probably a (small) bug that we… | |||||
} else { | |||||
MPASS((pcb2->pcb_flags & (PCB_KERNFPU | PCB_KERNFPU_THR)) == 0); | |||||
bcopy(get_pcb_user_save_td(td1), get_pcb_user_save_pcb(pcb2), | bcopy(get_pcb_user_save_td(td1), get_pcb_user_save_pcb(pcb2), | ||||
cpu_max_ext_state_size); | 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)); | ||||
/* | /* | ||||
* Create a new fresh stack for the new process. | * Create a new fresh stack for the new process. | ||||
* 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 | ||||
▲ Show 20 Lines • Show All 368 Lines • ▼ Show 20 Lines | |||||
*/ | */ | ||||
void | void | ||||
cpu_copy_thread(struct thread *td, struct thread *td0) | cpu_copy_thread(struct thread *td, struct thread *td0) | ||||
{ | { | ||||
struct pcb *pcb2; | struct pcb *pcb2; | ||||
pcb2 = td->td_pcb; | pcb2 = td->td_pcb; | ||||
/* Ensure that td0's pcb is up to date. */ | /* Ensure that td0's pcb is up to date for user threads. */ | ||||
if ((td->td_pflags & TDP_KTHREAD) == 0) { | |||||
MPASS(td0 == curthread); | |||||
fpuexit(td0); | fpuexit(td0); | ||||
if (td0 == curthread) | |||||
update_pcb_bases(td0->td_pcb); | update_pcb_bases(td0->td_pcb); | ||||
} | |||||
/* | /* | ||||
* Copy the upcall pcb. This loads kernel regs. | * Copy the upcall pcb. This loads kernel regs. | ||||
* Those not loaded individually below get their default | * Those not loaded individually below get their default | ||||
* values here. | * values here. | ||||
*/ | */ | ||||
bcopy(td0->td_pcb, pcb2, sizeof(*pcb2)); | bcopy(td0->td_pcb, pcb2, sizeof(*pcb2)); | ||||
clear_pcb_flags(pcb2, PCB_KERNFPU); | |||||
pcb2->pcb_save = get_pcb_user_save_pcb(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); | |||||
Done Inline ActionsWe could perhaps assert that neither KERNFPU nor KERNFPU_THR is set here instead of clearing. No user process calling thr_new() should have active kernel FPU state here. cpu_fork() doesn't currently bother clearing this flag (so I didn't add it), but I would like these two code blocks to be as identical as possible between cpu_fork() and cpu_copy_thread(). (It's somewhat temping to see if I could come up with some kind of 'copy_pcb' function for the shared logic between the two functions.) jhb: We could perhaps assert that neither KERNFPU nor KERNFPU_THR is set here instead of clearing. | |||||
bcopy(get_pcb_user_save_td(td0), pcb2->pcb_save, | bcopy(get_pcb_user_save_td(td0), pcb2->pcb_save, | ||||
cpu_max_ext_state_size); | cpu_max_ext_state_size); | ||||
} | |||||
set_pcb_flags_raw(pcb2, PCB_FULL_IRET); | set_pcb_flags_raw(pcb2, PCB_FULL_IRET); | ||||
/* | /* | ||||
* Create a new fresh stack for the new thread. | * Create a new fresh stack for the new thread. | ||||
Done Inline ActionsThis comment seems a bit stale as it doesn't create a stack at all. For exec_setregs where I think this was copied from, we bzero the trap frame before setting registers. I wonder if for the trapframe cpu_thread_copy() shouldn't be more like exec and rely on cpu_set_upcall() instead. jhb: This comment seems a bit stale as it doesn't create a stack at all. For exec_setregs where I… | |||||
Done Inline Actions
I tried doing a bzero here and it didn't work out well. cpu_set_upcall() below doesn't set tf_ss (though it sets all the others) which blew up. The 32-bit cpu_set_upcall() doesn't set any seg regs at all, and linux_set_upcall_kse() assumes a copy instead of clear. I will make the comment more accurate though. jhb: > This comment seems a bit stale as it doesn't create a stack at all. For exec_setregs where I… | |||||
*/ | */ | ||||
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. | ||||
▲ Show 20 Lines • Show All 142 Lines • Show Last 20 Lines |
Should we clear PCB_KERNFPU_THR, or leave it alone?