307 lines
9.3 KiB
C
307 lines
9.3 KiB
C
/*
|
|
* plex86: run multiple x86 operating systems concurrently
|
|
* Copyright (C) 1999-2003 Kevin P. Lawton
|
|
*
|
|
* fault-mon.c: fault/int handlers for VM monitor - monitor space.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
|
|
#include "plex86.h"
|
|
#define IN_MONITOR_SPACE
|
|
#include "monitor.h"
|
|
|
|
|
|
|
|
|
|
|
|
/* The monitor stack frame. When an exception or interrupt occurrs
|
|
* during the execution of either guest or monitor code, the following
|
|
* values are pushed.
|
|
*
|
|
* ss
|
|
* esp
|
|
* eflags Values pushed by the CPU and interrupt stub. To simplify
|
|
* cs things, the stub pushes an error of zero for those
|
|
* eip events which don't naturally cause an error push, and
|
|
* error also pushes the vector of the exception/interrupt.
|
|
* vector
|
|
*
|
|
* eax
|
|
* ecx
|
|
* edx General registers, pushed with a PUSHA instruction,
|
|
* ebx by code below.
|
|
* <esp>
|
|
* ebp
|
|
* esi
|
|
* edi
|
|
*
|
|
* es
|
|
* ds Segment selectors, pushed by code below.
|
|
* fs
|
|
* gs
|
|
*/
|
|
|
|
void handleMonFault(guest_context_t *monContext);
|
|
|
|
static inline
|
|
Bit32u readCR2(void)
|
|
{
|
|
Bit32u cr2;
|
|
asm volatile ("movl %%cr2, %0" : "=r" (cr2));
|
|
return( cr2 );
|
|
}
|
|
|
|
|
|
asm (
|
|
".text \n\t"
|
|
|
|
/* __handle_fault: This is called by all of the monitor's fault handler
|
|
* stubs. A fault could have originated from execution of the guest
|
|
* (due to virtualization conditions or natural fault generation) or
|
|
* from the monitor (currently only due to bugs in the monitor).
|
|
*/
|
|
".globl __handle_fault \n\t"
|
|
"__handle_fault: \n\t"
|
|
" pushal \n\t" /* Save general registers */
|
|
" pushl %es \n\t" /* Save segment registers */
|
|
" pushl %ds \n\t"
|
|
" pushl %fs \n\t"
|
|
" pushl %gs \n\t"
|
|
" movl 60(%esp), %eax \n\t" /* CS pushed by CPU from fault */
|
|
" andl $3, %eax \n\t" /* Check CS.RPL bits */
|
|
" jz __fault_from_mon \n\t" /* RPL0 means from monitor */
|
|
|
|
/* We have determined that the fault was from guest code. Prepare
|
|
* to call the monitor C code to do most of the fault handling.
|
|
*/
|
|
"__fault_from_guest: \n\t"
|
|
" movl %ss, %eax \n\t" /* Copy SS into DS/ES */
|
|
" movl %eax, %ds \n\t"
|
|
" movl %eax, %es \n\t"
|
|
" cld \n\t" /* gcc-compiled code needs this */
|
|
" pushl %esp \n\t" /* Push pointer to saved guest context for C call.*/
|
|
" call handleGuestFault\n\t" /* Call the C monitor fault handler. */
|
|
" addl $4, %esp \n\t" /* Remove arg from stack. */
|
|
".globl __ret_to_guest \n\t" /* Fault handled, work back to guest. */
|
|
"__ret_to_guest: \n\t"
|
|
/* Return to the guest. Restore registers from the monitor stack. */
|
|
" popl %gs \n\t" /* Restore guest segments */
|
|
" popl %fs \n\t"
|
|
" popl %ds \n\t"
|
|
" popl %es \n\t"
|
|
" popal \n\t" /* Restore guest general registers */
|
|
" addl $8, %esp \n\t" /* Ignore vector and error dwords */
|
|
" iret \n\t" /* Resume execution of guest */
|
|
|
|
|
|
"__fault_from_mon: \n\t"
|
|
" cld \n\t" /* gcc-compiled code needs this */
|
|
" pushl %esp \n\t" /* Push pointer to context. */
|
|
" call handleMonFault \n\t" /* Call C code for real work */
|
|
" addl $4, %esp \n\t"
|
|
/* Return to monitor. Restore state from the monitor stack. */
|
|
"__ret_to_monitor: \n\t"
|
|
" popl %gs \n\t" /* Restore monitor segments */
|
|
" popl %fs \n\t"
|
|
" popl %ds \n\t"
|
|
" popl %es \n\t"
|
|
" popal \n\t" /* Restore monitor general registers */
|
|
" addl $8, %esp \n\t" /* ignore vector and error dwords */
|
|
" iret \n\t" /* Resume execution of monitor */
|
|
|
|
|
|
/*
|
|
* Hardware interrupt handler stub
|
|
*/
|
|
".globl __handle_int \n\t" /* Return to monitor code */
|
|
"__handle_int: \n\t"
|
|
" pushal \n\t" /* Save guest general registers */
|
|
" pushl %es \n\t" /* Save guest segment registers */
|
|
" pushl %ds \n\t"
|
|
" pushl %fs \n\t"
|
|
" pushl %gs \n\t"
|
|
|
|
" movl %ss, %eax \n\t" /* Copy SS into DS/ES */
|
|
" movl %eax, %ds \n\t"
|
|
" movl %eax, %es \n\t"
|
|
" cld \n\t" /* gcc-compiled code needs this */
|
|
" pushl %esp \n\t"
|
|
" call handleInt \n\t" /* monitor interrupt handler */
|
|
" addl $4, %esp \n\t"
|
|
" cmpl $0x1, %eax \n\t" /* Was interrupt generated from monitor code? */
|
|
" je __ret_to_monitor\n\t" /* Yes, so return to monitor code */
|
|
" jmp __ret_to_guest \n\t" /* No, so return to guest code */
|
|
);
|
|
|
|
|
|
|
|
unsigned
|
|
handleInt(guest_context_t *context)
|
|
/*
|
|
* handleInt(): Redirect a hardware interrupt back to the host
|
|
*/
|
|
{
|
|
nexus_t *nexus = (nexus_t *) (((Bit32u) context) & 0xfffff000);
|
|
vm_t *vm = (vm_t *) nexus->vm;
|
|
unsigned from_monitor;
|
|
Bit64u t1;
|
|
|
|
t1 = vm_rdtsc();
|
|
|
|
if ( (context->cs & 0x0003) == 0x0003 ) {
|
|
/* End of elapsed guest execution duration. Add elapsed */
|
|
/* cycles to time framework. */
|
|
vm->system.cyclesElapsed += (t1 - vm->system.t0);
|
|
|
|
from_monitor = 0; /* Event from guest code */
|
|
}
|
|
else {
|
|
from_monitor = 1; /* Event from monitor code */
|
|
}
|
|
|
|
/* Interrupts are off naturally here. */
|
|
vm->mon_request = MonReqRedirect;
|
|
vm->redirect_vector = context->vector;
|
|
vm->guest.__mon2host();
|
|
return(from_monitor);
|
|
}
|
|
|
|
|
|
void
|
|
handleGuestFault(guest_context_t *context)
|
|
/* Handle a fault from the guest. Called from the assembly stub
|
|
* __handle_fault.
|
|
*/
|
|
{
|
|
nexus_t *nexus = (nexus_t *) (((Bit32u) context) & 0xfffff000);
|
|
vm_t *vm = (vm_t *) nexus->vm;
|
|
Bit32u cr2 = readCR2();
|
|
Bit64u t1;
|
|
|
|
/* End of elapsed guest execution duration */
|
|
t1 = vm_rdtsc();
|
|
vm->system.cyclesElapsed += (t1 - vm->system.t0);
|
|
|
|
#warning "Delete these checks"
|
|
#if ANAL_CHECKS
|
|
if ( !context->eflags.fields.if_ )
|
|
monpanic(vm, "handleGuestFault: guest IF=0.\n");
|
|
if ( context->eflags.fields.vm )
|
|
monpanic(vm, "handleGuestFault: eflags.VM=1.\n");
|
|
#endif
|
|
|
|
STI();
|
|
|
|
switch ( context->vector ) {
|
|
case ExceptionDB: /* 1 */
|
|
monpanic(vm, "handleGuestFault: #DB, method=%u not coded\n",
|
|
vm->executeMethod);
|
|
#if 0
|
|
if (vm->executeMethod == RunGuestNMethodBreakpoint) {
|
|
/* Breakpoint generated because we requested it via TF=1 */
|
|
}
|
|
else {
|
|
monpanic(vm, "handleGuestFault: #DB, method=%u not coded\n",
|
|
vm->executeMethod);
|
|
}
|
|
#endif
|
|
break;
|
|
|
|
case ExceptionBR: /* 5 */
|
|
monpanic(vm, "handleGuestFault: BR unfinished.\n");
|
|
/* BOUND instruction fault; array index not in bounds */
|
|
monpanic(vm, "handleGuestFault: emulate_exception was here.\n");
|
|
/*emulate_exception(vm, context->vector, 0);*/
|
|
break;
|
|
|
|
case ExceptionDE: /* 0 */
|
|
case ExceptionBP: /* 3 */
|
|
case ExceptionOF: /* 4 */
|
|
case ExceptionNM: /* 7 */
|
|
case ExceptionMF: /* 16 */
|
|
toHostGuestFault(vm, context->vector);
|
|
/*monpanic(vm, "handleGuestFault: DE/BP/OF/NM/MF unfinished.\n");*/
|
|
/*monpanic(vm, "handleGuestFault: %u\n", context->vector);*/
|
|
/* emulate_interrupt(vm, context->vector); */
|
|
break;
|
|
|
|
case ExceptionNP: /* 11 */
|
|
case ExceptionSS: /* 12 */
|
|
case ExceptionAC: /* 17 */
|
|
monpanic(vm, "handleGuestFault: NP/SS/AC unfinished.\n");
|
|
/* use emulate_xyz() */
|
|
/*interrupt(vm, context->vector, 0, 1, context->error); */
|
|
monpanic(vm, "handleGuestFault: %u\n", context->vector);
|
|
break;
|
|
|
|
case ExceptionUD: /* 6 */
|
|
case ExceptionGP: /* 13 */
|
|
toHostGuestFault(vm, context->vector);
|
|
break;
|
|
|
|
case ExceptionPF: /* 14 */
|
|
guestPageFault(vm, context, cr2);
|
|
break;
|
|
|
|
default:
|
|
monpanic(vm, "handleGuestFault: Unhandled Fault: %u\n", context->vector);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void
|
|
handleMonFault(guest_context_t *monContext)
|
|
{
|
|
nexus_t *nexus = (nexus_t *) (((Bit32u) monContext) & 0xfffff000);
|
|
vm_t *vm = (vm_t *) nexus->vm;
|
|
|
|
if (vm->inMonFault)
|
|
monpanic(vm, "handleMonFault called recursively.\n");
|
|
vm->inMonFault = 1;
|
|
monpanic(vm, "handleMonFault: vector=%u\n", monContext->vector);
|
|
|
|
/* Fault occurred inside monitor code. */
|
|
|
|
switch ( monContext->vector ) {
|
|
case ExceptionPF:
|
|
case ExceptionGP:
|
|
{
|
|
Bit32u cr2;
|
|
/*unsigned us, rw;*/
|
|
|
|
cr2 = readCR2();
|
|
STI();
|
|
|
|
if (monContext->error & 0x8) /* If RSVD bits used in PDir */
|
|
monpanic(vm, "handleMF: RSVD\n");
|
|
/*us = G_GetCPL(vm)==3;*/
|
|
/*rw = (monContext->error >> 1) & 1;*/
|
|
monpanic(vm, "handleMF: \n");
|
|
break;
|
|
}
|
|
|
|
default:
|
|
monpanic(vm, "hMF: vector=%u\n", monContext->vector);
|
|
break;
|
|
}
|
|
|
|
/*vm->abort_code = 1;*/
|
|
/*monpanic_nomess(vm);*/
|
|
CLI();
|
|
vm->inMonFault = 0;
|
|
}
|