///////////////////////////////////////////////////////////////////////// //// $Id: plex86-interface.cc,v 1.7 2006-05-21 20:41:48 sshwarts Exp $ /////////////////////////////////////////////////////////////////////////// //// //// Copyright (C) 2002 Kevin P. Lawton //// //// 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 "bochs.h" #include #include #include #include "plex86-interface.h" #define LOG_THIS genlog-> unsigned plex86State = 0; int plex86FD = -1; asm (".comm plex86PrintBufferPage,4096,4096"); asm (".comm plex86GuestCPUPage,4096,4096"); extern Bit8u plex86PrintBufferPage[]; extern Bit8u plex86GuestCPUPage[]; static Bit8u *plex86MemPtr = 0; static size_t plex86MemSize = 0; static Bit8u *plex86PrintBuffer = plex86PrintBufferPage; static guest_cpu_t *plex86GuestCPU = (guest_cpu_t *) plex86GuestCPUPage; static void copyPlex86StateToBochs(BX_CPU_C *cpu); static void copyBochsDescriptorToPlex86(descriptor_t *, bx_descriptor_t *); static void copyPlex86DescriptorToBochs(BX_CPU_C *, bx_descriptor_t *, descriptor_t *); static int openFD(void); static unsigned faultCount[32]; int openFD(void) { if (plex86State) { // This should be the first operation; no state should be set yet. fprintf(stderr, "plex86: openFD: plex86State = 0x%x\n", plex86State); return(0); // Error. } // Open a new VM. fprintf(stderr, "plex86: opening VM.\n"); fprintf(stderr, "plex86: trying /dev/misc/plex86..."); plex86FD = open("/dev/misc/plex86", O_RDWR); if (plex86FD < 0) { fprintf(stderr, "failed.\n"); // Try the old name. fprintf(stderr, "plex86: trying /dev/plex86..."); plex86FD = open("/dev/plex86", O_RDWR); if (plex86FD < 0) { fprintf(stderr, "failed.\n"); fprintf(stderr, "plex86: did you load the kernel module?" " Read the toplevel README file!\n"); perror ("open"); return(-1); // Error. } } fprintf(stderr, "OK.\n"); return(1); // OK. } unsigned plex86CpuInfo(BX_CPU_C *cpu) { cpuid_info_t bochsCPUID; if (plex86FD < 0) { // If the plex86 File Descriptor has not been opened yet. if ( !openFD() ) { return(0); // Error. } } bochsCPUID.vendorDWord0 = cpu->cpuidInfo.vendorDWord0; bochsCPUID.vendorDWord1 = cpu->cpuidInfo.vendorDWord1; bochsCPUID.vendorDWord2 = cpu->cpuidInfo.vendorDWord2; bochsCPUID.procSignature.raw = cpu->cpuidInfo.procSignature; bochsCPUID.featureFlags.raw = cpu->cpuidInfo.featureFlags; fprintf(stderr, "plex86: passing guest CPUID to plex86.\n"); if ( ioctl(plex86FD, PLEX86_CPUID, &bochsCPUID) ) { perror("ioctl CPUID: "); return(0); // Error. } return(1); // OK. } unsigned plex86TearDown(void) { fprintf(stderr, "plex86: plex86TearDown called.\n"); fprintf(stderr, "plex86: guest Fault Count (FYI):\n"); for (unsigned f=0; f<32; f++) { if (faultCount[f]) fprintf(stderr, "plex86: FC[%u] = %u\n", f, faultCount[f]); } if ( plex86FD < 0 ) { fprintf(stderr, "plex86: plex86TearDown: FD not open.\n"); return(0); } if ( plex86State & Plex86StateMMapPhyMem ) { fprintf(stderr, "plex86: unmapping guest physical memory.\n"); } plex86State &= ~Plex86StateMMapPhyMem; if (plex86State & Plex86StateMMapPrintBuffer) { } plex86State &= ~Plex86StateMMapPrintBuffer; if (plex86State & Plex86StateMMapGuestCPU) { } plex86State &= ~Plex86StateMMapGuestCPU; fprintf(stderr, "plex86: tearing down VM.\n"); if (ioctl(plex86FD, PLEX86_TEARDOWN, 0) == -1) { perror("ioctl TEARDOWN: "); return(0); // Failed. } plex86State &= ~Plex86StateMemAllocated; // Close the connection to the kernel module. fprintf(stderr, "plex86: closing VM device.\n"); if (close(plex86FD) == -1) { perror("close of VM device\n"); return(0); // Failed. } plex86FD = -1; // File descriptor is now closed. plex86State = 0; // For good measure. return(1); // OK. } unsigned plex86ExecuteInVM(BX_CPU_C *cpu) { plex86IoctlExecute_t executeMsg; int ret; if ( plex86State != Plex86StateReady ) { fprintf(stderr, "plex86: plex86ExecuteInVM: not in ready state (0x%x)\n", plex86State); BX_PANIC(("plex86ExecuteInVM: bailing")); return(0); } executeMsg.executeMethod = Plex86ExecuteMethodNative; plex86GuestCPU->edi = cpu->gen_reg[BX_32BIT_REG_EDI].dword.erx; plex86GuestCPU->esi = cpu->gen_reg[BX_32BIT_REG_ESI].dword.erx; plex86GuestCPU->ebp = cpu->gen_reg[BX_32BIT_REG_EBP].dword.erx; plex86GuestCPU->esp = cpu->gen_reg[BX_32BIT_REG_ESP].dword.erx; plex86GuestCPU->ebx = cpu->gen_reg[BX_32BIT_REG_EBX].dword.erx; plex86GuestCPU->edx = cpu->gen_reg[BX_32BIT_REG_EDX].dword.erx; plex86GuestCPU->ecx = cpu->gen_reg[BX_32BIT_REG_ECX].dword.erx; plex86GuestCPU->eax = cpu->gen_reg[BX_32BIT_REG_EAX].dword.erx; plex86GuestCPU->eflags = cpu->eflags.val32; plex86GuestCPU->eip = cpu->dword.eip; // ES/CS/SS/DS/FS/GS for (unsigned s=0; s<6; s++) { plex86GuestCPU->sreg[s].sel.raw = cpu->sregs[s].selector.value; copyBochsDescriptorToPlex86(&plex86GuestCPU->sreg[s].des, &cpu->sregs[s].cache); plex86GuestCPU->sreg[s].valid = cpu->sregs[s].cache.valid; } // LDTR plex86GuestCPU->ldtr.sel.raw = cpu->ldtr.selector.value; copyBochsDescriptorToPlex86(&plex86GuestCPU->ldtr.des, &cpu->ldtr.cache); plex86GuestCPU->ldtr.valid = cpu->ldtr.cache.valid; // TR plex86GuestCPU->tr.sel.raw = cpu->tr.selector.value; copyBochsDescriptorToPlex86(&plex86GuestCPU->tr.des, &cpu->tr.cache); plex86GuestCPU->tr.valid = cpu->tr.cache.valid; // GDTR/IDTR plex86GuestCPU->gdtr.base = cpu->gdtr.base; plex86GuestCPU->gdtr.limit = cpu->gdtr.limit; plex86GuestCPU->idtr.base = cpu->idtr.base; plex86GuestCPU->idtr.limit = cpu->idtr.limit; plex86GuestCPU->dr0 = cpu->dr0; plex86GuestCPU->dr1 = cpu->dr1; plex86GuestCPU->dr2 = cpu->dr2; plex86GuestCPU->dr3 = cpu->dr3; plex86GuestCPU->dr6 = cpu->dr6; plex86GuestCPU->dr7 = cpu->dr7; plex86GuestCPU->tr3 = 0; // Unimplemented in bochs. plex86GuestCPU->tr4 = 0; // Unimplemented in bochs. plex86GuestCPU->tr5 = 0; // Unimplemented in bochs. plex86GuestCPU->tr6 = 0; // Unimplemented in bochs. plex86GuestCPU->tr7 = 0; // Unimplemented in bochs. plex86GuestCPU->cr0.raw = cpu->cr0.val32; plex86GuestCPU->cr1 = cpu->cr1; plex86GuestCPU->cr2 = cpu->cr2; plex86GuestCPU->cr3 = cpu->cr3; plex86GuestCPU->cr4.raw = cpu->cr4.registerValue; plex86GuestCPU->a20Enable = BX_GET_ENABLE_A20(); ret = ioctl(plex86FD, PLEX86_EXECUTE, &executeMsg); if (ret != 0) { fprintf(stderr, "plex86: ioctl(PLEX86_EXECUTE): "); switch (ret) { case Plex86NoExecute_Method: fprintf(stderr, "bad execute method.\n"); break; case Plex86NoExecute_CR0: fprintf(stderr, "bad CR0 value.\n"); break; case Plex86NoExecute_CR4: fprintf(stderr, "bad CR4 value.\n"); break; case Plex86NoExecute_CS: fprintf(stderr, "bad CS value.\n"); break; case Plex86NoExecute_A20: fprintf(stderr, "bad A20 enable value.\n"); break; case Plex86NoExecute_Selector: fprintf(stderr, "bad selector value.\n"); break; case Plex86NoExecute_DPL: fprintf(stderr, "bad descriptor DPL.\n"); break; case Plex86NoExecute_EFlags: fprintf(stderr, "bad EFlags.\n"); break; case Plex86NoExecute_Panic: fprintf(stderr, "panic.\n"); break; case Plex86NoExecute_VMState: fprintf(stderr, "bad VM state.\n"); break; default: fprintf(stderr, "ret = %d\n", ret); } } else { switch ( executeMsg.monitorState.request ) { case MonReqFlushPrintBuf: fprintf(stderr, "plex86: MonReqFlushPrintBuf:\n"); fprintf(stderr, "::%s\n", plex86PrintBuffer); break; case MonReqPanic: fprintf(stderr, "plex86: MonReqPanic:\n"); fprintf(stderr, "::%s\n", plex86PrintBuffer); break; //case MonReqNone: // copyPlex86StateToBochs(cpu); // return(0); /* All OK. */ case MonReqGuestFault: faultCount[ executeMsg.monitorState.guestFaultNo ]++; copyPlex86StateToBochs(cpu); return(0); /* All OK. */ default: fprintf(stderr, "plex86: executeMsg.request = %u\n", executeMsg.monitorState.request); break; } } plex86TearDown(); BX_PANIC(("plex86ExecuteInVM: bailing")); return(0); } void copyPlex86StateToBochs(BX_CPU_C *cpu) { cpu->gen_reg[BX_32BIT_REG_EDI].dword.erx = plex86GuestCPU->edi; cpu->gen_reg[BX_32BIT_REG_ESI].dword.erx = plex86GuestCPU->esi; cpu->gen_reg[BX_32BIT_REG_EBP].dword.erx = plex86GuestCPU->ebp; cpu->gen_reg[BX_32BIT_REG_ESP].dword.erx = plex86GuestCPU->esp; cpu->gen_reg[BX_32BIT_REG_EBX].dword.erx = plex86GuestCPU->ebx; cpu->gen_reg[BX_32BIT_REG_EDX].dword.erx = plex86GuestCPU->edx; cpu->gen_reg[BX_32BIT_REG_ECX].dword.erx = plex86GuestCPU->ecx; cpu->gen_reg[BX_32BIT_REG_EAX].dword.erx = plex86GuestCPU->eax; cpu->eflags.val32 = plex86GuestCPU->eflags; cpu->dword.eip = plex86GuestCPU->eip; // Set fields used for exception processing. cpu->prev_eip = plex86GuestCPU->eip; cpu->prev_esp = plex86GuestCPU->esp; // ES/CS/SS/DS/FS/GS for (unsigned s=0; s<6; s++) { cpu->sregs[s].selector.value = plex86GuestCPU->sreg[s].sel.raw; cpu->sregs[s].cache.valid = plex86GuestCPU->sreg[s].valid; if ( (cpu->sregs[s].selector.value & 0xfffc) == 0 ) { /* Null selector. */ if ( cpu->sregs[s].cache.valid ) { plex86TearDown(); BX_PANIC(("copyPlex86StateToBochs: null descriptor [%u] " "with descriptor cache valid bit set.", s)); } /* valid bit == 0, invalidates a bochs descriptor cache. */ } else { /* Non-null selector. */ if ( cpu->sregs[s].cache.valid==0 ) { plex86TearDown(); BX_PANIC(("copyPlex86StateToBochs: non-null descriptor [%u] " "with descriptor cache valid bit clear.", s)); } copyPlex86DescriptorToBochs(cpu, &cpu->sregs[s].cache, &plex86GuestCPU->sreg[s].des); } } } void copyBochsDescriptorToPlex86(descriptor_t *plex86Desc, bx_descriptor_t *bochsDesc) { // For now this function is a hack to convert from bochs descriptor // cache fields which are parsed out into separate fields, to // a packed descriptor format as stored in a real segment descriptor. // This is user only for code/data segments and the LDTR/TR. // Ideally, bochs would store the 64-bit segment descriptor when // it loads segment registers. if (bochsDesc->valid == 0) { memset(plex86Desc, 0, sizeof(*plex86Desc)); return; } plex86Desc->p = bochsDesc->p; plex86Desc->dpl = bochsDesc->dpl; plex86Desc->type = (bochsDesc->segment<<4) | bochsDesc->type; if (bochsDesc->segment) { // Code/Data segment type. Bit32u limit = bochsDesc->u.segment.limit; plex86Desc->limit_low = limit; // Only lower 16-bits. plex86Desc->limit_high = limit >> 16; Bit32u base = bochsDesc->u.segment.base; plex86Desc->base_low = base; plex86Desc->base_med = base >> 16; plex86Desc->base_high = base >> 24; plex86Desc->avl = bochsDesc->u.segment.avl; plex86Desc->reserved = 0; plex86Desc->d_b = bochsDesc->u.segment.d_b; plex86Desc->g = bochsDesc->u.segment.g; } else if (bochsDesc->type == 2) { // LDT descriptor. plex86Desc->limit_low = bochsDesc->u.ldt.limit; plex86Desc->limit_high = 0; Bit32u base = bochsDesc->u.ldt.base; plex86Desc->base_low = base; plex86Desc->base_med = base >> 16; plex86Desc->base_high = base >> 24; plex86Desc->avl = 0; plex86Desc->reserved = 0; plex86Desc->d_b = 0; plex86Desc->g = 0; } else if ( (bochsDesc->type == 9) || (bochsDesc->type==1) ) { // TSS Bit32u limit = bochsDesc->u.tss.limit; plex86Desc->limit_low = limit; // Only lower 16-bits. plex86Desc->limit_high = limit >> 16; Bit32u base = bochsDesc->u.tss.base; plex86Desc->base_low = base; plex86Desc->base_med = base >> 16; plex86Desc->base_high = base >> 24; plex86Desc->avl = bochsDesc->u.tss.avl; plex86Desc->reserved = 0; plex86Desc->d_b = 0; plex86Desc->g = bochsDesc->u.tss.g; } else { BX_PANIC(("copyBochsDescriptorToPlex86: desc type = %u.", bochsDesc->type)); } } void copyPlex86DescriptorToBochs(BX_CPU_C *cpu, bx_descriptor_t *bochsDesc, descriptor_t *plex86Desc) { Bit32u dword1, dword2, *dwordPtr; dwordPtr = (Bit32u *) plex86Desc; /* We can assume little endian, since we're running an x86 VM. */ dword1 = dwordPtr[0]; dword2 = dwordPtr[1]; cpu->parse_descriptor(dword1, dword2, bochsDesc); } unsigned plex86RegisterGuestMemory(Bit8u *vector, unsigned bytes) { plex86IoctlRegisterMem_t ioctlMsg; if (plex86FD < 0) { // If the plex86 File Descriptor has not been opened yet. if ( !openFD() ) { return(0); // Error. } } if (bytes & 0x3fffff) { // Memory size must be multiple of 4Meg. fprintf(stderr, "plex86: RegisterGuestMemory: memory size of %u bytes" "is not a 4Meg increment.\n", bytes); return(0); // Error. } if ( ((unsigned)vector) & 0xfff ) { // Memory vector must be page aligned. fprintf(stderr, "plex86: RegisterGuestMemory: vector not page aligned."); return(0); // Error. } ioctlMsg.nMegs = bytes >> 20; ioctlMsg.guestPhyMemVector = (Bit32u) vector; ioctlMsg.logBufferWindow = (Bit32u) plex86PrintBuffer; ioctlMsg.guestCPUWindow = (Bit32u) plex86GuestCPU; if (ioctl(plex86FD, PLEX86_REGISTER_MEMORY, &ioctlMsg) == -1) { return(0); // Error. } plex86MemSize = bytes; /* For now... */ plex86State |= Plex86StateMemAllocated; plex86State |= Plex86StateMMapPhyMem; plex86State |= Plex86StateMMapPrintBuffer; plex86State |= Plex86StateMMapGuestCPU; // Zero out printbuffer and guestcpu here? fprintf(stderr, "plex86: RegisterGuestMemory: %uMB succeeded.\n", ioctlMsg.nMegs); return(1); // OK. } unsigned plex86UnregisterGuestMemory(Bit8u *vector, unsigned bytes) { return(1); // OK. }