+ if (act->mact.qactTimer == 0 || soonest <= act->mact.qactTimer)
+ act->mact.qactTimer = soonest; /* Set lowest timer */
+ }
+
+ return;
+}
+
+
+
+/*-----------------------------------------------------------------------
+** vmm_stop_vm
+**
+** This function prevents the specified VM(s) to from running.
+** If any is currently executing, the execution is intercepted
+** with a code of kVmmStopped. Note that execution of the VM is
+** blocked until a vmmExecuteVM is called with the start flag set to 1.
+** This provides the ability for a thread to stop execution of a VM and
+** insure that it will not be run until the emulator has processed the
+** "virtual" interruption.
+**
+** Inputs:
+** vmmask - 32 bit mask corresponding to the VMs to put in stop state
+** NOTE: if this mask is all 0s, any executing VM is intercepted with
+* a kVmmStopped (but not marked stopped), otherwise this is a no-op. Also note that there
+** note that there is a potential race here and the VM may not stop.
+**
+** Outputs:
+** kernel return code indicating success
+** or if no VMs are enabled, an invalid syscall exception.
+-----------------------------------------------------------------------*/
+
+int vmm_stop_vm(struct savearea *save)
+{
+
+ thread_act_t act;
+ vmmCntrlTable *CTable;
+ int cvi, i;
+ task_t task;
+ thread_act_t fact;
+ unsigned int vmmask;
+ ReturnHandler *stopapc;
+
+ ml_set_interrupts_enabled(TRUE); /* This can take a bit of time so pass interruptions */
+
+ task = current_task(); /* Figure out who we are */
+
+ task_lock(task); /* Lock our task */
+
+ fact = (thread_act_t)task->thr_acts.next; /* Get the first activation on task */
+ act = 0; /* Pretend we didn't find it yet */
+
+ for(i = 0; i < task->thr_act_count; i++) { /* All of the activations */
+ if(fact->mact.vmmControl) { /* Is this a virtual machine monitor? */
+ act = fact; /* Yeah... */
+ break; /* Bail the loop... */
+ }
+ fact = (thread_act_t)fact->thr_acts.next; /* Go to the next one */
+ }
+
+ if(!((unsigned int)act)) { /* See if we have VMMs yet */
+ task_unlock(task); /* No, unlock the task */
+ ml_set_interrupts_enabled(FALSE); /* Set back interruptions */
+ return 0; /* Go generate a syscall exception */
+ }
+
+ act_lock_thread(act); /* Make sure this stays 'round */
+ task_unlock(task); /* Safe to release now */
+
+ CTable = act->mact.vmmControl; /* Get the pointer to the table */
+
+ if(!((unsigned int)CTable & -2)) { /* Are there any all the way up yet? */
+ act_unlock_thread(act); /* Unlock the activation */
+ ml_set_interrupts_enabled(FALSE); /* Set back interruptions */
+ return 0; /* Go generate a syscall exception */
+ }
+
+ if(!(vmmask = save->save_r3)) { /* Get the stop mask and check if all zeros */
+ act_unlock_thread(act); /* Unlock the activation */
+ ml_set_interrupts_enabled(FALSE); /* Set back interruptions */
+ save->save_r3 = KERN_SUCCESS; /* Set success */
+ return 1; /* Return... */
+ }
+
+ for(cvi = 0; cvi < kVmmMaxContexts; cvi++) { /* Search slots */
+ if((0x80000000 & vmmask) && (CTable->vmmc[cvi].vmmFlags & vmmInUse)) { /* See if we need to stop and if it is in use */
+ hw_atomic_or(&CTable->vmmc[cvi].vmmFlags, vmmXStop); /* Set this one to stop */
+ }
+ vmmask = vmmask << 1; /* Slide mask over */
+ }
+
+ if(hw_compare_and_store(0, 1, &act->mact.emPendRupts)) { /* See if there is already a stop pending and lock out others if not */
+ act_unlock_thread(act); /* Already one pending, unlock the activation */
+ ml_set_interrupts_enabled(FALSE); /* Set back interruptions */
+ save->save_r3 = KERN_SUCCESS; /* Say we did it... */
+ return 1; /* Leave */
+ }
+
+ if(!(stopapc = (ReturnHandler *)kalloc(sizeof(ReturnHandler)))) { /* Get a return handler control block */
+ act->mact.emPendRupts = 0; /* No memory, say we have given up request */
+ act_unlock_thread(act); /* Unlock the activation */
+ ml_set_interrupts_enabled(FALSE); /* Set back interruptions */
+ save->save_r3 = KERN_RESOURCE_SHORTAGE; /* No storage... */
+ return 1; /* Return... */
+ }
+
+ ml_set_interrupts_enabled(FALSE); /* Disable interruptions for now */
+
+ stopapc->handler = vmm_interrupt; /* Set interruption routine */
+
+ stopapc->next = act->handlers; /* Put our interrupt at the start of the list */
+ act->handlers = stopapc; /* Point to us */
+
+ act_set_apc(act); /* Set an APC AST */
+ ml_set_interrupts_enabled(TRUE); /* Enable interruptions now */
+
+ act_unlock_thread(act); /* Unlock the activation */
+
+ ml_set_interrupts_enabled(FALSE); /* Set back interruptions */
+ save->save_r3 = KERN_SUCCESS; /* Hip, hip, horay... */
+ return 1;
+}
+
+/*-----------------------------------------------------------------------
+** vmm_interrupt
+**
+** This function is executed asynchronously from an APC AST.
+** It is to be used for anything that needs to interrupt a running VM.
+** This include any kind of interruption generation (other than timer pop)
+** or entering the stopped state.
+**
+** Inputs:
+** ReturnHandler *rh - the return handler control block as required by the APC.
+** thread_act_t act - the activation
+**
+** Outputs:
+** Whatever needed to be done is done.
+-----------------------------------------------------------------------*/
+
+void vmm_interrupt(ReturnHandler *rh, thread_act_t act) {
+
+ vmmCntrlTable *CTable;
+ savearea *sv;
+ boolean_t inter;
+
+
+
+ kfree((vm_offset_t)rh, sizeof(ReturnHandler)); /* Release the return handler block */
+
+ inter = ml_set_interrupts_enabled(FALSE); /* Disable interruptions for now */
+
+ act->mact.emPendRupts = 0; /* Say that there are no more interrupts pending */
+ CTable = act->mact.vmmControl; /* Get the pointer to the table */
+
+ if(!((unsigned int)CTable & -2)) return; /* Leave if we aren't doing VMs any more... */
+
+ if(act->mact.vmmCEntry && (act->mact.vmmCEntry->vmmFlags & vmmXStop)) { /* Do we need to stop the running guy? */
+ sv = find_user_regs(act); /* Get the user state registers */
+ if(!sv) { /* Did we find something? */
+ panic("vmm_interrupt: no user context; act = %08X\n", act);