/*++ Copyright (c) 1992 Microsoft Corporation Module Name: Int31.c Abstract: This module provides the int 31 API for dpmi Author: Neil Sandlin (neilsa) 23-Nov-1996 Revision History: --*/ #include "precomp.h" #pragma hdrstop #include "softpc.h" #include "xlathlp.h" VOID Int31NotImplemented(VOID); VOID Int31SelectorManagement(VOID); VOID Int31DOSMemoryManagement(VOID); VOID Int31InterruptManagement(VOID); VOID Int31Translation(VOID); VOID Int31Function4xx(VOID); VOID Int31MemoryManagement(VOID); VOID Int31PageLocking(VOID); VOID Int31DemandPageTuning(VOID); VOID Int31VirtualIntState(VOID); VOID Int31DbgRegSupport(VOID); // // Local constants // #define MAX_DPMI_MAJOR_FUNCTION 0xb typedef VOID (*APIFUNCTION)(VOID); APIFUNCTION DpmiMajorFunctionTable[MAX_DPMI_MAJOR_FUNCTION+1] = { Int31SelectorManagement , // Selector_Management ;[0] Int31DOSMemoryManagement, // DOS_Mem_Mgt ;[1] Int31InterruptManagement, // Int_Serv ;[2] Int31Translation , // Trans_Serv ;[3] Int31Function4xx , // Get_Version ;[4] Int31MemoryManagement , // Mem_Managment ;[5] Int31PageLocking , // Page_Lock ;[6] Int31DemandPageTuning , // Demand_Page_Tuning ;[7] Int31NotImplemented , // Phys_Addr_Mapping ;[8] Int31VirtualIntState , // Virt_Interrrupt_State ;[9] Int31NotImplemented , // Not_Supported ;[A] Int31DbgRegSupport , // Debug_Register_Support ;[B] }; VOID DpmiInt31Entry( VOID ) /*++ Routine Description: This routine is invoked when the caller has issued an int31. Arguments: None Return Value: None. --*/ { DECLARE_LocalVdmContext; ULONG DpmiMajorCode = getAH(); PUCHAR StackPointer; // // Pop ds from stack // StackPointer = VdmMapFlat(getSS(), (*GetSPRegister)(), VDM_PM); setDS(*(PWORD16)StackPointer); (*SetSPRegister)((*GetSPRegister)() + 2); // // Take the iret frame off the stack before we do the operation. This // way we have the stack pointer set up to the same place as we would // if this was a kernel mode dpmi host. // SimulateIret(RESTORE_FLAGS); setCF(0); // assume success if (DpmiMajorCode <= MAX_DPMI_MAJOR_FUNCTION) { (*DpmiMajorFunctionTable[DpmiMajorCode])(); } else { setCF(1); } } VOID DpmiInt31Call( VOID ) /*++ Routine Description: This routine dispatches to the appropriate routine to perform the actual translation of the api Arguments: None Return Value: None. --*/ { DECLARE_LocalVdmContext; ULONG DpmiMajorCode = getAH(); PUCHAR StackPointer; // // Pop ds from stack // StackPointer = VdmMapFlat(getSS(), (*GetSPRegister)(), VDM_PM); setDS(*(PWORD16)StackPointer); (*SetSPRegister)((*GetSPRegister)() + 2); setCF(0); // assume success if (DpmiMajorCode <= MAX_DPMI_MAJOR_FUNCTION) { (*DpmiMajorFunctionTable[DpmiMajorCode])(); } else { setCF(1); } } VOID Int31NotImplemented( VOID ) /*++ Routine Description: This routine handles int 31 functions that aren't implemented on NT. It just returns carry to the app. Arguments: None Return Value: TRUE - The function has been completed --*/ { DECLARE_LocalVdmContext; setCF(1); } VOID Int31SelectorManagement( VOID ) /*++ Routine Description: This routine handles Int31 00xx functions. Arguments: None Return Value: None --*/ { DECLARE_LocalVdmContext; USHORT Sel; USHORT NewSel; UCHAR Func = getAL(); LDT_ENTRY UNALIGNED *Descriptor; USHORT Access; ULONG Base; USHORT Count; ULONG Limit; static UCHAR ReservedSelectors[16] = {0}; // // First, validate the selector // if ((Func >= 4) && (Func <= 0xC)) { Sel = getBX() & SEL_INDEX_MASK; // // Make sure the selector in question is allocated // if (((Sel <= SEL_DPMI_LAST) && (!ReservedSelectors[Sel>>3])) || (Sel > LdtMaxSel) || ((Sel > SEL_DPMI_LAST) && IS_SELECTOR_FREE(Sel))) { setCF(1); return; } } switch(Func) { // // Allocate Selectors // case 0: Count = getCX(); Sel = ALLOCATE_SELECTORS(Count); if (!Sel || !Count) { setCF(1); break; } setAX(Sel); while(Count--) { SetDescriptor(Sel, 0, 0, STD_DATA); Sel+=8; } break; // // Free Selector // case 1: Sel = getBX() & SEL_INDEX_MASK; if (Sel <= SEL_DPMI_LAST) { if (!ReservedSelectors[Sel>>3]) { setCF(1); } else { ReservedSelectors[Sel>>3] = 0; } break; } if (!FreeSelector(Sel)) { setCF(1); } if (getCF() == 0) { // Zero out segment registers if it contains what we just freed // shielint: fs, gs, ss??? kernel will fix fs and gs for us. SS is unlikely // to have the freed selector. If yes, the app is gone anyway. if (getBX() == getDS()) { setDS(0); } if (getBX() == getES()) { setES(0); } } break; // // Segment to Descriptor // case 2: Sel = SegmentToSelector(getBX(), STD_DATA); if (!Sel) { setCF(1); } else { setAX(Sel); } break; // // Get Next Selector Increment value // case 3: setAX(8); break; // // Lock functions unimplemented on NT // case 4: case 5: break; // // Get Descriptor Base // case 6: Base = GET_SELECTOR_BASE(Sel); setDX((USHORT)Base); setCX((USHORT)(Base >> 16)); break; // // Set Descriptor Base // case 7: SetDescriptorBase(Sel, (((ULONG)getCX())<<16) | getDX()); break; // // Set Segment Limit // case 8: Limit = ((ULONG)getCX()) << 16 | getDX(); if (Limit < 0x100000) { // < 1Mb? Ldt[Sel>>3].HighWord.Bits.Granularity = 0; } else { if ((Limit & 0xfff) != 0xfff) { // Limit > 1MB, but not page aligned. Return error setCF(1); break; } Ldt[Sel>>3].HighWord.Bits.Granularity = 1; } SET_SELECTOR_LIMIT(Sel, Limit); SetShadowDescriptorEntries(Sel, 1); FLUSH_SELECTOR_CACHE(Sel, 1); break; // // Set Descriptor Access // case 9: Access = getCX(); // // verify that they aren't setting "System", and that its ring3 // if ((Access & 0x70) != 0x70) { setCF(1); break; } SET_SELECTOR_ACCESS(Sel, Access); SetShadowDescriptorEntries(Sel, 1); FLUSH_SELECTOR_CACHE(Sel, 1); break; // // Create data alias // case 0xA: if (!IS_SELECTOR_READABLE(Sel)) { setCF(1); break; } NewSel = ALLOCATE_SELECTOR(); if (!NewSel) { setCF(1); break; } Ldt[NewSel>>3] = Ldt[Sel>>3]; Ldt[NewSel>>3].HighWord.Bytes.Flags1 &= (AB_PRESENT | AB_DPL3); Ldt[NewSel>>3].HighWord.Bytes.Flags1 |= (AB_DATA | AB_WRITE); SetShadowDescriptorEntries(NewSel, 1); FLUSH_SELECTOR_CACHE(NewSel, 1); setAX(NewSel); break; // // Get Descriptor // case 0xB: Descriptor = VdmMapFlat(getES(), (*GetDIRegister)(), VDM_PM); *Descriptor = Ldt[Sel>>3]; break; // // Set Descriptor // case 0xC: Descriptor = VdmMapFlat(getES(), (*GetDIRegister)(), VDM_PM); // // verify that this isn't a System descriptor, and that its ring3 // if (!(Descriptor->HighWord.Bits.Type & 0x10) || ((Descriptor->HighWord.Bits.Dpl & 3) != 3)) { setCF(1); return; } Ldt[Sel>>3] = *Descriptor; SetShadowDescriptorEntries(Sel, 1); FLUSH_SELECTOR_CACHE(Sel, 1); break; // // Allocate Specific Sel // case 0xD: Sel = getBX() & ~7; if ((Sel > SEL_DPMI_LAST) || ReservedSelectors[Sel>>3]) { setCF(1); } else { ReservedSelectors[Sel>>3] = 1; } break; default: setCF(1); } return; } VOID Int31DOSMemoryManagement( VOID ) /*++ Routine Description: This routine handles Int31 01xx functions. The functionality is implemented in dosmem.c. Arguments: None Return Value: None --*/ { DECLARE_LocalVdmContext; switch(getAL()) { // // Allocate DOS memory block // case 0: DpmiAllocateDosMem(); break; // // Free DOS memory block // case 1: DpmiFreeDosMem(); break; // // Resize DOS memory block // case 2: DpmiSizeDosMem(); break; } } VOID Int31InterruptManagement( VOID ) /*++ Routine Description: This routine handles Int31 02xx functions. Arguments: None Return Value: None --*/ { DECLARE_LocalVdmContext; UCHAR IntNumber = getBL(); PWORD16 pIvtEntry; switch(getAL()) { // // Get Real Mode Interrupt Vector // case 0: pIvtEntry = (PWORD16) (IntelBase + IntNumber*4); setDX(*pIvtEntry++); setCX(*pIvtEntry); break; // // Set Real Mode Interrupt Vector // case 1: pIvtEntry = (PWORD16) (IntelBase + IntNumber*4); *pIvtEntry++ = getDX(); *pIvtEntry = getCX(); break; // // Get exception handler Vector // case 2: { PVDM_FAULTHANDLER Handlers = DpmiFaultHandlers; if (IntNumber >= 32) { setCF(1); break; } setCX(Handlers[IntNumber].CsSelector); (*SetDXRegister)(Handlers[IntNumber].Eip); break; } // // Set exception handler Vector // case 3: if (!SetFaultHandler(IntNumber, getCX(), (*GetDXRegister)())){ setCF(1); } break; // // Get Protect Mode Interrupt Vector // case 4: { PVDM_INTERRUPTHANDLER Handlers = DpmiInterruptHandlers; setCX(Handlers[IntNumber].CsSelector); (*SetDXRegister)(Handlers[IntNumber].Eip); break; } // // Set Protect Mode Interrupt Vector // case 5: if (!SetProtectedModeInterrupt(IntNumber, getCX(), (*GetDXRegister)(), (USHORT)(Frame32 ? VDM_INT_32 : VDM_INT_16))) { setCF(1); } break; } } VOID Int31Translation( VOID ) /*++ Routine Description: This routine handles Int31 03xx functions. The functionality is implemented in modesw.c. Arguments: None Return Value: None --*/ { DECLARE_LocalVdmContext; switch(getAL()) { // // Simulate Real Mode Interrupt // Call Real Mode Procedure with Far Return Frame // Call Real Mode Procedure with Iret Frame // case 0: case 1: case 2: DpmiRMCall(getAL()); break; // // Allocate Real Mode Call-Back Address // case 3: DpmiAllocateRMCallBack(); break; // // Free Real Mode Call-Back Address // case 4: DpmiFreeRMCallBack(); break; // // Get State Save/Restore Addresses // case 5: setAX(0); setBX((USHORT)(DosxRmSaveRestoreState>>16)); setCX((USHORT)DosxRmSaveRestoreState); setSI((USHORT)(DosxPmSaveRestoreState>>16)); (*SetDIRegister)(DosxPmSaveRestoreState & 0x0000FFFF); break; // // Get Raw Mode Switch Addresses // case 6: setBX((USHORT)(DosxRmRawModeSwitch>>16)); setCX((USHORT)DosxRmRawModeSwitch); setSI((USHORT)(DosxPmRawModeSwitch>>16)); (*SetDIRegister)(DosxPmRawModeSwitch & 0x0000FFFF); break; } } VOID Int31Function4xx( VOID ) /*++ Routine Description: This routine handles Int31 04xx functions. Arguments: None Return Value: None --*/ { DECLARE_LocalVdmContext; USHORT Sel; switch(getAL()) { // // Get Version // case 0: setAX(I31VERSION); setBX(I31FLAGS); setCL(idCpuType); setDX((I31MasterPIC << 8) | I31SlavePIC); break; // // INTERNAL NT FUNCTION: WowAllocSelectors // This function is equivalent to DPMI func 0000, // except that it skips the step of initializing the // descriptors. // case 0xf1: Sel = ALLOCATE_WOW_SELECTORS(getCX()); if (!Sel) { setCF(1); // We fall thru to make sure AX is set to 0 in the failure case. } setAX(Sel); break; // // INTERNAL NT FUNCTION: WowSetDescriptor // This function assumes that the local LDT has already // been set in the client. All that needs to be done // is an update of dpmi32 entries, as well as sending // it to the x86 ntoskrnl. // case 0xf2: Sel = getBX() & ~7; if (Sel > LdtMaxSel) { setCF(1); break; } SetShadowDescriptorEntries(Sel, getCX()); // no need to flush the cache on risc since the ldt was changed // from the 16-bit side, and has thus already been flushed break; // // INTERNAL NT FUNCTION: WowSetLowMemFuncs // Wow is passing us the address of GlobalDOSAlloc, GlobalDOSFree // so that we can support the DPMI Dos memory management functions // case 0xf3: WOWAllocSeg = getBX(); WOWAllocFunc = getDX(); WOWFreeSeg = getSI(); WOWFreeFunc = getDI(); break; } } VOID Int31MemoryManagement( VOID ) /*++ Routine Description: This routine handles Int31 05xx functions. Arguments: None Return Value: None --*/ { DECLARE_LocalVdmContext; PMEM_DPMI pMem; switch(getAL()) { // // Get Free Memory Information // case 0: DpmiGetMemoryInfo(); break; // // Allocate Memory Block // case 1: pMem = DpmiAllocateXmem(((ULONG)getBX() << 16) | getCX()); if (!pMem) { setCF(1); break; } // // Return the information about the block // setBX((USHORT)((ULONG)pMem->Address >> 16)); setCX((USHORT)((ULONG)pMem->Address & 0x0000FFFF)); setSI((USHORT)((ULONG)pMem >> 16)); setDI((USHORT)((ULONG)pMem & 0x0000FFFF)); break; // // Free Memory Block // case 2: pMem = (PMEM_DPMI)(((ULONG)getSI() << 16) | getDI()); if (!DpmiIsXmemHandle(pMem) || !DpmiFreeXmem(pMem)) { setCF(1); } break; // // Resize Memory Block // case 3: { ULONG ulMemSize; ulMemSize = ((ULONG)getBX() << 16) | getCX(); // // Not allowed to resize to 0 // if ( ulMemSize != 0 ) { pMem = (PMEM_DPMI)(((ULONG)getSI() << 16) | getDI()); if (!DpmiReallocateXmem(pMem, ulMemSize) ) { setCF(1); break; } // // Return the information about the block // setBX((USHORT)((ULONG)pMem->Address >> 16)); setCX((USHORT)((ULONG)pMem->Address & 0x0000FFFF)); } else { setCF(1); } break; } } } VOID Int31PageLocking( VOID ) /*++ Routine Description: This routine handles Int31 06xx functions. Arguments: None Return Value: None --*/ { DECLARE_LocalVdmContext; switch(getAL()) { // // Lock functions not implemented // case 0: case 1: case 2: case 3: break; // // Get Page Size // case 4: setBX(0); setCX(0x1000); break; } } VOID Int31DemandPageTuning( VOID ) /*++ Routine Description: This routine handles Int31 07xx functions. Arguments: None Return Value: None --*/ { DECLARE_LocalVdmContext; ULONG Addr = (getBX()<<16 | getCX()) + IntelBase; ULONG Count = getSI()<<16 | getDI(); if (Count) { switch(getAL()) { // // Mark Page as Demand Paging Candidate // case 0: // Addr, Count expressed in 4k pages Addr <<= 12; Count <<= 12; case 2: VirtualUnlock((PVOID)Addr, Count); break; // // Discard Page Contents // case 1: // Addr, Count expressed in 4k pages Addr <<= 12; Count <<= 12; case 3: VirtualAlloc((PVOID)Addr, Count, MEM_RESET, PAGE_READWRITE); break; default: setCF(1); } } } VOID Int31VirtualIntState( VOID ) /*++ Routine Description: This routine handles Int31 09xx functions. Arguments: None Return Value: None --*/ { DECLARE_LocalVdmContext; BOOL bVIF = *(ULONG *)(IntelBase+FIXED_NTVDMSTATE_LINEAR) & VDM_VIRTUAL_INTERRUPTS; switch(getAL()) { // // Get and disable Virtual Interrupt State // case 0: setEFLAGS(getEFLAGS() & ~EFLAGS_IF_MASK); break; // // Get and enable Virtual Interrupt State // case 1: setEFLAGS(getEFLAGS() | EFLAGS_IF_MASK); break; case 2: break; default: setCF(1); return; } if (bVIF) { setAL(1); } else { setAL(0); } } VOID Int31DbgRegSupport( VOID ) /*++ Routine Description: This routine handles Int31 0bxx functions. Arguments: None Return Value: None --*/ { DECLARE_LocalVdmContext; #ifndef _X86_ setCF(1); #else ULONG DebugRegisters[6]; USHORT Handle; ULONG Mask; ULONG Size; ULONG Type; UCHAR Func = getAL(); #define DBG_TYPE_EXECUTE 0 #define DBG_TYPE_WRITE 1 #define DBG_TYPE_READWRITE 2 #define DBG_DR6 4 #define DBG_DR7 5 #define DR7_LE 0x100 #define DR7_L0 0x01 #define DR7_L1 0x04 #define DR7_L2 0x10 #define DR7_L3 0x40 // // Debugging ntvdm under NTSD affects the values of the debug register // context, so defining the following value turns on some debugging // code // //#define DEBUGGING_DEBUGREGS 1 if (!DpmiGetDebugRegisters(DebugRegisters)) { setCF(1); return; } #ifdef DEBUGGING_DEBUGREGS { char szMsg[256]; wsprintf(szMsg, " DR0-3=%.8X %.8X %.8X %.8X DR6,7=%.8X %.8X\n", DebugRegisters[0], DebugRegisters[1], DebugRegisters[2], DebugRegisters[3], DebugRegisters[DBG_DR6], DebugRegisters[DBG_DR7]); OutputDebugString(szMsg); } #endif if (Func != 0) { Handle = getBX(); // // point at the local enable bit for this handle in DR7 // Mask = (DR7_L0 << Handle*2); if ((Handle >= 4) || (!(DebugRegisters[DBG_DR7] & Mask))) { // Invalid Handle setCF(1); return; } } switch(Func) { // // Set Debug Watchpoint // case 0: for (Handle = 0, Mask = 3; Handle < 4; Handle++, Mask <<= 2) { if (!(DebugRegisters[DBG_DR7] & Mask)) { // // found a free register // // // Set the linear address // DebugRegisters[Handle] = (((ULONG)getBX()) << 16) + getCX(); Size = getDL(); Type = getDH(); if (Type == DBG_TYPE_EXECUTE) { // force size to be 1 for execute Size = 1; } if ((Size > 4) || (Size == 3) || (!Size) || (Type > 2)) { // error: invalid parameter break; } // // convert size to appropriate bits in DR7 // Size--; Size <<= (18 + Handle*4); // // convert type to appropriate bits in DR7 // if (Type == DBG_TYPE_READWRITE) { Type++; } Type <<= (16 + Handle*4); Mask = 0xf << (16 + Handle*4); // // Set the appropriate Len, R/W, and enable bits in DR7 // Also set the common global and local enable bits. // DebugRegisters[DBG_DR7] &= ~Mask; DebugRegisters[DBG_DR7] |= (Size | Type | (DR7_L0 << Handle*2)); DebugRegisters[DBG_DR7] |= DR7_LE; // // Clear triggered bit for this BP // DebugRegisters[DBG_DR6] &= ~(1 << Handle); #ifdef DEBUGGING_DEBUGREGS { char szMsg[256]; wsprintf(szMsg, "Int31 Setting DBGREG %d, Location %.8X, DR7=%.8X\n", Handle, DebugRegisters[Handle], DebugRegisters[DBG_DR7]); OutputDebugString(szMsg); } #endif if (DpmiSetDebugRegisters(DebugRegisters)) { return; } break; } } setCF(1); break; // // Clear Debug Watchpoint // case 1: // // clear enabled and triggered bits for this BP // DebugRegisters[DBG_DR7] &= ~Mask; DebugRegisters[DBG_DR6] &= (1 << Handle); DebugRegisters[Handle] = 0; // // Check to see if this clears all BP's (all local enable bits // clear), and disable common enable bit if so // if (!(DebugRegisters[DBG_DR7] & (DR7_L0 | DR7_L1 | DR7_L2 | DR7_L3))) { DebugRegisters[DBG_DR7] &= ~DR7_LE; } #ifdef DEBUGGING_DEBUGREGS { char szMsg[256]; wsprintf(szMsg, "Int31 Clearing DBGREG %d, DR7=%.8X\n", Handle, DebugRegisters[DBG_DR7]); OutputDebugString(szMsg); } #endif if (!DpmiSetDebugRegisters(DebugRegisters)) { setCF(1); } break; // // Get State of Debug Watchpoint // case 2: if (DebugRegisters[DBG_DR6] & (1 << Handle)) { setAX(1); } else { setAX(0); } #ifdef DEBUGGING_DEBUGREGS { char szMsg[256]; wsprintf(szMsg, "Int31 Query on DBGREG %d returns %d\n", Handle, getAX()); OutputDebugString(szMsg); } #endif break; // // Reset Debug Watchpoint // case 3: DebugRegisters[DBG_DR6] &= ~(1 << Handle); #ifdef DEBUGGING_DEBUGREGS { char szMsg[256]; wsprintf(szMsg, "Int31 Resetting DBGREG %d\n", Handle); OutputDebugString(szMsg); } #endif if (!DpmiSetDebugRegisters(DebugRegisters)) { setCF(1); } break; default: setCF(1); } #endif }