-
-
-static void
-act_machine_switch_pcb( thread_t new )
-{
- pcb_t pcb = new->machine.pcb;
- struct real_descriptor *ldtp;
- vm_offset_t pcb_stack_top;
- vm_offset_t hi_pcb_stack_top;
- vm_offset_t hi_iss;
- cpu_data_t *cdp = current_cpu_datap();
-
- assert(new->kernel_stack != 0);
- STACK_IEL(new->kernel_stack)->saved_state = pcb->iss;
-
- if (!cpu_mode_is64bit()) {
- x86_saved_state32_tagged_t *hi_iss32;
-
- /*
- * Save a pointer to the top of the "kernel" stack -
- * actually the place in the PCB where a trap into
- * kernel mode will push the registers.
- */
- hi_iss = (vm_offset_t)((unsigned long)
- pmap_cpu_high_map_vaddr(cpu_number(), HIGH_CPU_ISS0) |
- ((unsigned long)pcb->iss & PAGE_MASK));
-
- cdp->cpu_hi_iss = (void *)hi_iss;
-
- pmap_high_map(pcb->iss_pte0, HIGH_CPU_ISS0);
- pmap_high_map(pcb->iss_pte1, HIGH_CPU_ISS1);
-
- hi_iss32 = (x86_saved_state32_tagged_t *) hi_iss;
- assert(hi_iss32->tag == x86_SAVED_STATE32);
-
- hi_pcb_stack_top = (int) (hi_iss32 + 1);
-
- /*
- * For fast syscall, top of interrupt stack points to pcb stack
- */
- *(vm_offset_t *) current_sstk() = hi_pcb_stack_top;
-
- current_ktss()->esp0 = hi_pcb_stack_top;
-/* XXX: This check is performed against the thread save state flavor rather than the
- * task's 64-bit feature flag because of the thread/task 64-bit state divergence
- * that can arise in task_set_64bit() on x86. When that is addressed, we can
- * revert to checking the task 64 bit feature flag. The assert below is retained
- * for that reason.
- */
- } else if (is_saved_state64(pcb->iss)) {
- x86_saved_state64_tagged_t *iss64;
- vm_offset_t isf;
-
- assert(is_saved_state64(pcb->iss));
-
- iss64 = (x86_saved_state64_tagged_t *) pcb->iss;
-
- /*
- * Set pointer to PCB's interrupt stack frame in cpu data.
- * Used by syscall and double-fault trap handlers.
- */
- isf = (vm_offset_t) &iss64->state.isf;
- cdp->cpu_uber.cu_isf = UBER64(isf);
- pcb_stack_top = (vm_offset_t) (iss64 + 1);
- /* require 16-byte alignment */
- assert((pcb_stack_top & 0xF) == 0);
- /* Interrupt stack is pcb */
- current_ktss64()->rsp0 = UBER64(pcb_stack_top);
-
- /*
- * Top of temporary sysenter stack points to pcb stack.
- * Although this is not normally used by 64-bit users,
- * it needs to be set in case a sysenter is attempted.
- */
- *current_sstk64() = UBER64(pcb_stack_top);
-
- cdp->cpu_task_map = new->map->pmap->pm_kernel_cr3 ?
- TASK_MAP_64BIT_SHARED : TASK_MAP_64BIT;
-
- /*
- * Enable the 64-bit user code segment, USER64_CS.
- */
- ldt_desc_p(USER64_CS)->access |= ACC_PL_U;
-
- } else {
- x86_saved_state_compat32_t *iss32compat;
- vm_offset_t isf;
-
- assert(is_saved_state32(pcb->iss));
- iss32compat = (x86_saved_state_compat32_t *) pcb->iss;
-
- pcb_stack_top = (int) (iss32compat + 1);
- /* require 16-byte alignment */
- assert((pcb_stack_top & 0xF) == 0);
-
- /*
- * Set pointer to PCB's interrupt stack frame in cpu data.
- * Used by debug trap handler.
- */
- isf = (vm_offset_t) &iss32compat->isf64;
- cdp->cpu_uber.cu_isf = UBER64(isf);
-
- /* Top of temporary sysenter stack points to pcb stack */
- *current_sstk64() = UBER64(pcb_stack_top);
-
- /* Interrupt stack is pcb */
- current_ktss64()->rsp0 = UBER64(pcb_stack_top);
-
- cdp->cpu_task_map = TASK_MAP_32BIT;
-
- /*
- * Disable USER64_CS
- */
- ldt_desc_p(USER64_CS)->access &= ~ACC_PL_U;
- }
-
- /*
- * Set the thread`s cthread (a.k.a pthread)
- * For 32-bit user this involves setting the USER_CTHREAD
- * descriptor in the LDT to point to the cthread data.
- * The involves copying in the pre-initialized descriptor.
- */
- ldtp = (struct real_descriptor *)current_ldt();
- ldtp[sel_idx(USER_CTHREAD)] = pcb->cthread_desc;
- if (pcb->uldt_selector != 0)
- ldtp[sel_idx(pcb->uldt_selector)] = pcb->uldt_desc;
- /*
- * For 64-bit, we additionally set the 64-bit User GS base
- * address. On return to 64-bit user, the GS.Base MSR will be written.
- */
- cdp->cpu_uber.cu_user_gs_base = pcb->cthread_self;
-
- /*
- * Set the thread`s LDT or LDT entry.
- */
- if (new->task == TASK_NULL || new->task->i386_ldt == 0) {
- /*
- * Use system LDT.
- */
- ml_cpu_set_ldt(KERNEL_LDT);
- } else {
- /*
- * Task has its own LDT.
- */
- user_ldt_set(new);
- }
-}
-