/*++ Copyright (c) 1992 Microsoft Corporation Module Name: dpmiselr.c Abstract: This is the code for maintaining descriptor data for dpmi32. Author: Dave Hart (davehart) 11-Apr-1993 Notes: Revision History: 09-Feb-1994 (daveh) Moved here from not386.c. 31-Jul-1995 (neilsa) Merged with x86 source 12-Dec-1995 (neilsa) Wrote VdmAddDescriptorMapping(), GetDescriptorMapping --*/ #include "precomp.h" #pragma hdrstop #include "softpc.h" #include "malloc.h" #ifndef _X86_ PDESC_MAPPING pDescMappingHead = NULL; #endif // _X86_ USHORT selLDTFree = 0; VOID DpmiSetDescriptorEntry( VOID ) /*++ Routine Description: This function is called via BOP by dosx to set the flataddress array and, if on x86, the real LDT maintained by the kernel. Arguments: None Return Value: None. --*/ { DECLARE_LocalVdmContext; USHORT SelCount; USHORT SelStart; SelStart = getAX(); if (SelStart % 8) { return; } if (SelStart > LdtMaxSel) { return; } SelCount = getCX(); if ((((LdtMaxSel - SelStart) >> 3) + 1) < SelCount) { return; } SetShadowDescriptorEntries(SelStart, SelCount); // no need to flush the cache on risc since the ldt was changed // from the 16-bit side, and has thus already been flushed } VOID SetDescriptor( USHORT Sel, ULONG Base, ULONG Limit, USHORT Access ) /*++ Routine Description: Arguments: None Return Value: None. --*/ { SET_SELECTOR_ACCESS(Sel, Access); SET_SELECTOR_LIMIT(Sel, Limit); SetDescriptorBase(Sel, Base); } VOID SetDescriptorBase( USHORT Sel, ULONG Base ) /*++ Routine Description: Arguments: None Return Value: None. --*/ { LDT_ENTRY UNALIGNED *Descriptor; // make it qword aligned Sel &= SEL_INDEX_MASK; Descriptor = &Ldt[Sel>>3]; Descriptor->BaseLow = (WORD) Base; Descriptor->HighWord.Bytes.BaseMid = (BYTE) (Base >> 16); Descriptor->HighWord.Bytes.BaseHi = (BYTE) (Base >> 24); SetShadowDescriptorEntries(Sel, 1); FLUSH_SELECTOR_CACHE(Sel, 1); } VOID SetShadowDescriptorEntries( USHORT SelStart, USHORT SelCount ) /*++ Routine Description: This function takes as a parameter an array of descriptors directly out of the LDT in the clients address space. For each descriptor in the array, it does three things: - It extracts the descriptor base and sets it into the FlatAddress array. This value may be adjusted on RISC platforms to account for DIB.DRV (see VdmAddDescriptorMapping). - It extracts the selector limit, and adjusts the limit in the descriptor itself if the values would cause the descriptor to be able to access kernel address space (see note below). On debug builds, the limit is also copied to the Limit array. - On x86 builds, it calls DpmiSetX86Descriptor() to write the descriptor down to the real LDT in the kernel. On RISC builds, it calls down to the emulator to flush compiled LDT entries. Arguments: SelStart - Selector which identifies the first descriptor SelCount - number of descriptors to process Descriptors -> first descriptor in LDT Return Value: None. --*/ { USHORT i; ULONG Base; ULONG Limit; USHORT Sel = SelStart; for (i = 0; i < SelCount; i++, Sel+=8) { // form Base and Limit values Base = GET_SELECTOR_BASE(Sel); Limit = GET_SELECTOR_LIMIT(Sel); // // Do NOT remove the following code. There are several apps that // choose arbitrarily high limits for theirs selectors. This works // under windows 3.1, but NT won't allow us to do that. // The following code fixes the limits for such selectors. // Note: if the base is > 0x7FFEFFFF, the selector set will fail // if ((Limit > 0x7FFEFFFF) || (Base + Limit > 0x7FFEFFFF)) { Limit = 0x7FFEFFFF - (Base + 0xFFF); SET_SELECTOR_LIMIT(Sel, Limit); } if ((Sel >> 3) != 0) { #ifndef _X86_ { ULONG BaseOrig = Base; Base = GetDescriptorMapping(Sel, Base); if (BaseOrig == Base) { Base += (ULONG)IntelBase; } } #endif FlatAddress[Sel >> 3] = Base; #if DBG SelectorLimit[Sel >> 3] = Limit; #endif } // // Sanitize the selector // Ldt[Sel>>3].HighWord.Bits.Dpl = 3; Ldt[Sel>>3].HighWord.Bits.Reserved_0 = 0; } #ifdef _X86_ if (!DpmiSetX86Descriptor(SelStart, SelCount)) { return; } #endif } #ifndef _X86_ VOID FlushSelectorCache( USHORT SelStart, USHORT SelCount ) { DECLARE_LocalVdmContext; USHORT SelEnd; USHORT Sel; USHORT i; VdmTraceEvent(VDMTR_TYPE_DPMI | DPMI_GENERIC, SelStart, SelCount); // // The emulator compiles LDT entries, so we need to flush them // out // for (i = 0, Sel = SelStart; i < SelCount; i++, Sel += 8) { VdmFlushCache(LdtSel, Sel & SEL_INDEX_MASK, 8, VDM_PM); } SelEnd = SelStart + SelCount*8; Sel = getCS(); if ((Sel >= SelStart) && (Sel < SelEnd)) { setCS(Sel); } Sel = getDS(); if ((Sel >= SelStart) && (Sel < SelEnd)) { setDS(Sel); } Sel = getES(); if ((Sel >= SelStart) && (Sel < SelEnd)) { setES(Sel); } Sel = getFS(); if ((Sel >= SelStart) && (Sel < SelEnd)) { setFS(Sel); } Sel = getGS(); if ((Sel >= SelStart) && (Sel < SelEnd)) { setGS(Sel); } Sel = getSS(); if ((Sel >= SelStart) && (Sel < SelEnd)) { setSS(Sel); } } #endif // // Descriptor Mapping functions (RISC ONLY) // #ifndef _X86_ BOOL VdmAddDescriptorMapping( USHORT SelectorStart, USHORT SelectorCount, ULONG LdtBase, ULONG Flat ) /*++ Routine Description: This function was added to support the DIB.DRV implementation on RISC. When an app uses DIB.DRV, then the situation arises where the Intel linear base address + the flat address of the start of the Intel address space does NOT equal the flat address of the memory. This happens when the VdmAddVirtualMemory() api is used to set up an additional layer of indirection for memory addressing in the emulator. But there is more to the story. When app wants to use CreateDIBSection via WinG we also need to map selectors, thus this routine should not depend upon DpmiSetDesctriptorEntry being called afterwards. Thus, we go and zap the flat address table with the new address. Arguments: SelectorStart, Count - range of selectors involved in the mapping LdtBase - Intel base of start of range Flat - True flat address base to be used for these selectors Return Value: This function returns TRUE on success, or FALSE for failure (out of mem) --*/ { PDESC_MAPPING pdm; USHORT i; if ((pdm = (PDESC_MAPPING) malloc(sizeof (DESC_MAPPING))) == NULL) return FALSE; pdm->Sel = SelectorStart &= SEL_INDEX_MASK; pdm->SelCount = SelectorCount; pdm->LdtBase = LdtBase; pdm->FlatBase = Flat; pdm->pNext = pDescMappingHead; pDescMappingHead = pdm; // this code does what essentially desctribed in comment above for (i = 0; i < SelectorCount; ++i) { FlatAddress[(SelectorStart >> 3) + i] = Flat + 65536 * i; } return TRUE; } ULONG GetDescriptorMapping( USHORT sel, ULONG LdtBase ) /*++ Routine Description: Arguments: sel - the selector for which the base should be returned LdtBase - the base for this selector as is set currently in the LDT Return Value: The true flat address for the specified selector. --*/ { PDESC_MAPPING pdm, pdmprev; ULONG Base = LdtBase; sel &= SEL_INDEX_MASK; // and off lower 3 bits pdm = pDescMappingHead; while (pdm) { if ((sel >= pdm->Sel) && (sel < (pdm->Sel + pdm->SelCount*8))) { // // We found a mapping for this selector. Now check to see if // the ldt base still matches the base when the mapping was // created. // if (LdtBase == (pdm->LdtBase + 65536*((sel-pdm->Sel)/8))) { // // The mapping appears still valid. Return the remapped address // return (pdm->FlatBase + 65536*((sel-pdm->Sel)/8)); } else { // // The ldt base doesn't match the mapping, so the mapping // must be obselete. Free the mapping here. // if (pdm == pDescMappingHead) { // // mapping is the first in the list // pDescMappingHead = pdm->pNext; } else { pdmprev->pNext = pdm->pNext; } free(pdm); } break; } pdmprev = pdm; pdm = pdm->pNext; } return Base; } #endif // _X86_ // // LDT Management routines // VOID DpmiInitLDT( VOID ) /*++ Routine Description: This routine stores the flat address for the LDT table in the 16bit land (pointed to by selGDT in 16bit land). It also initializes the free selector chain. Arguments: None Return Value: None. --*/ { DECLARE_LocalVdmContext; USHORT Sel; // // Get the new LDT location // LdtSel = getAX(); Ldt = (PVOID)VdmMapFlat(LdtSel, 0, VDM_PM); LdtMaxSel = getCX(); if (!LdtUserSel) { LdtUserSel = getDI() & SEL_INDEX_MASK; } // // Initialize the LDT free list // selLDTFree = LdtUserSel; for (Sel = selLDTFree; Sel < (LdtMaxSel & SEL_INDEX_MASK); Sel += 8) { NEXT_FREE_SEL(Sel) = Sel+8; MARK_SELECTOR_FREE(Sel); } NEXT_FREE_SEL(Sel) = 0xffff; } VOID DpmiResetLDTUserBase( VOID ) /*++ Routine Description: This routine can hopefully be eliminated at a later date. The flow of dosx initialization has made this necessary. What happens is this: Earlier, dosx has called up to dpmi32 to initialize the LDT (DpmiInitLDT), where it sets the start of the user are of the LDT, and from there, sets up the linked list of free LDT entries. But after that time, and before an app is run, there are pieces of dosx code which allocate selectors that are not transient. In particular, DXNETBIO does an AllocateLowSegment(), which is totally unecessary on NT, but it a bit tricky to rework. So what is happening here is a reset of the start of the user area of the LDT to permanently reserve any selectors that are not free. Arguments: None Return Value: None. --*/ { LdtUserSel = selLDTFree; } VOID DpmiAllocateSelectors( VOID ) // // This routine is called via BOP by those routines in DOSX // that still need to allocate selectors. // { DECLARE_LocalVdmContext; USHORT Sel; Sel = ALLOCATE_SELECTORS(getAX()); if (!Sel) { setCF(1); } else { setAX(Sel); setCF(0); } } VOID DpmiFreeSelector( VOID ) // // This routine is called via BOP by those routines in DOSX // that still need to free selectors. // { DECLARE_LocalVdmContext; if (FreeSelector(getAX())) { setCF(0); } else { setCF(1); } } BOOL RemoveFreeSelector( USHORT Sel ) /*++ Routine Description: This routine removes a specific selector from the free selector chain. Arguments: Sel - the selector to be aquired Return Value: Returns TRUE if the function was successful, FALSE if it was an invalid selector (not free) --*/ { if (!IS_SELECTOR_FREE(Sel)) { return FALSE; } if (Sel == selLDTFree) { // // we are removing the head of the list // selLDTFree = NEXT_FREE_SEL(Sel); } else { USHORT SelTest; USHORT SelPrev = 0; SelTest = selLDTFree; while (SelTest != Sel) { if (SelTest == 0xffff) { // End of list return FALSE; } SelPrev = SelTest; SelTest = NEXT_FREE_SEL(SelTest); } NEXT_FREE_SEL(SelPrev) = NEXT_FREE_SEL(Sel); } MARK_SELECTOR_ALLOCATED(Sel); return TRUE; } USHORT AllocateSelectors( USHORT Count, BOOL bWow ) /*++ Routine Description: This routine allocates selectors from the free selector chain. Arguments: Count - number of selectors needed. If this is more than 1, then all selectors will be contiguous bWow - if true, then use an allocation scheme that is more typical of win31 behavior. This is to avoid problems where winapps accidentally rely on the value of selectors Return Value: Returns the starting selector of the block, or zero if the allocation failed. --*/ { USHORT Sel; if (!Count || (Count>=(LdtMaxSel>>3))) { return 0; } if (Count == 1) { // // Allocating 1 selector // if ((Sel = selLDTFree) != 0xffff) { // Move next selector to head of list selLDTFree = NEXT_FREE_SEL(Sel); MARK_SELECTOR_ALLOCATED(Sel); return (Sel | SEL_LDT3); } } else { // // Allocating a selector block // // ******************************************************* // The strategy of allocating selectors has been modified to // give preference to selector values above 1000h. This is an // attempt to emulate typical values that are returned by win31. // -neilsa // // Some DPMI DOS applications demand that all selectors(no matter it comes // from AllocateLDTSelector or this function) be contiguous, so // the strategy for WOW doesn't work for DPMI DOS applications. // For this reason, a new parameter is added so the caller can control // where to start searching for free selectors. // -williamh // #define SEL_START_HI 0x1000 USHORT SelTest; USHORT SelStart = LdtUserSel; USHORT SelEnd = LdtMaxSel; BOOL bAllFree; if (bWow) { SelStart = SEL_START_HI; } asrestart: for (Sel = SelStart; Sel < (SelEnd - Count*8); Sel += 8) { bAllFree = TRUE; for (SelTest = Sel; SelTest < Sel + Count*8; SelTest += 8) { if (!IS_SELECTOR_FREE(SelTest)) { bAllFree = FALSE; break; } } if (bAllFree) { // // Found a block. Now we need to peel off the chain from // the free list // int i; for (i = 0, SelTest = Sel; i < Count; i++, SelTest+=8) { RemoveFreeSelector(SelTest); } return (Sel | SEL_LDT3); } } if (bWow && (SelEnd == LdtMaxSel)) { // // First pass for WOW complete, do it again // SelStart = LdtUserSel; SelEnd = SEL_START_HI + Count; goto asrestart; } } return 0; } BOOL FreeSelector( USHORT Sel ) /*++ Routine Description: This routine returns a selector to the free selector chain. Arguments: Sel - the selector to be freed Return Value: Returns TRUE if the function was successful, FALSE if it was an invalid selector (already free, reserved selector) --*/ { if ((Sel < LdtUserSel) || (Sel > LdtMaxSel) || IS_SELECTOR_FREE(Sel)) { // // invalid selector // return FALSE; } // // chain selector to head of free list // NEXT_FREE_SEL(Sel) = selLDTFree; selLDTFree = Sel & SEL_INDEX_MASK; MARK_SELECTOR_FREE(Sel); return TRUE; } USHORT FindSelector( ULONG Base, UCHAR Access ) /*++ Routine Description: This routine looks for a selector that matches the base and access rights passed as arguments. Arguments: Base - Base address to compare. Access- Access rights byte to compare. Return Value: Returns the selector that matches, or zero if the allocation failed. --*/ { USHORT Sel; ULONG Limit; for (Sel = LdtUserSel; Sel < LdtMaxSel; Sel+=8) { if (!IS_SELECTOR_FREE(Sel)) { GET_SHADOW_SELECTOR_LIMIT(Sel, Limit); if ((Limit == 0xffff) && (Base == GET_SELECTOR_BASE(Sel)) && ((Access & ~AB_ACCESSED) == (Ldt[Sel>>3].HighWord.Bytes.Flags1 & ~AB_ACCESSED))) { return (Sel | SEL_LDT3); } } } return 0; } USHORT SegmentToSelector( USHORT Segment, USHORT Access ) /*++ Routine Description: This routine either finds or creates selector that can access the specified low memory segment. Arguments: Segment- Paragraph segment address Access - Access rights Return Value: Returns the selector that matches, or zero if the allocation failed. --*/ { ULONG Base = ((ULONG) Segment) << 4; USHORT Sel; if (!(Sel = FindSelector(Base, (UCHAR)Access))) { if (Sel = ALLOCATE_SELECTOR()) { SetDescriptor(Sel, Base, 0xffff, Access); } } return Sel; } VOID SetDescriptorArray( USHORT Sel, ULONG Base, ULONG MemSize ) /*++ Routine Description: This routine allocates a set of descriptors to cover the specified memory block. The descriptors are initialized as follows: The first descriptor points at the whole block, then all subsequent descriptors have a limit of 64k except for the final one, which has a limit of block size MOD 64k. Arguments: Sel, Base, Memsize define the range of the selector array Return Value: none --*/ { USHORT SelCount; if (MemSize) { MemSize--; // now a descriptor limit } SelCount = (USHORT) ((MemSize>>16) + 1); SetDescriptor(Sel, Base, MemSize, STD_DATA); while(--SelCount) { Sel += 8; MemSize -= 0x10000; // subtract 64k Base += 0x10000; SetDescriptor(Sel, Base, (SelCount==1) ? MemSize : 0xffff, STD_DATA); } }