Microsoft KB Archive/105643

From BetaArchive Wiki

INF: Accessing Physical Memory in Windows 3.0 and 3.1 PSS ID Number: Q105643 Article last modified on 05-31-1994

3.00 3.10

WINDOWS

The information in this article applies to:
- Microsoft Windows Software Development Kit (SDK) for Windows, versions 3.0 and 3.1

SUMMARY

Some applications need to access memory at a particular physical address fixed in the address space of the microprocessor. Such applications typically interact directly with memory-mapped hardware device interface cards, or may need to read the computer’s CMOS settings. This article outlines two methods to access physical memory in Windows protected mode (standard mode or 386 enhanced mode).

MORE INFORMATION

Method 1: Exported Selectors (Preferred)

The Windows 3.0 and 3.1 Kernels export several selectors that should be used by applications that require access to physical memory located below the 1 megabyte (MB) boundary. The exported selectors are:

__0000h, __0040h, __A000h, __B000h, __B800h, __C000h, __D000h, __E000h, and __F000h

To use one of these selectors, place it onto a segment register and access the memory or create a long pointer. Here are examples using the Microsoft Macro Assembler (MASM) and Microsoft C:

In ASM: extern __0040h … mov ax, __0040h mov es, ax

In C: extern WORD _0040h; LPSTR lpBIOSDataArea; … /* Note the & and single underscore */ lpBIOSDataArea = (LPSTR)MAKELONG(0, &_0040h);

In C++: extern “C” WORD _0040h; LPSTR lpBIOSDataArea; … /* Note the & and single underscore */ lpBIOSDataArea = (LPSTR)MAKELONG(0, &_0040h);

Each of these data selectors is limited to accessing 64 kilobytes (K). An attempt to read or write to data beyond the limit causes a general protection (GP) fault (evidenced by an unrecoverable application error–UAE) in protected mode. Performing segment arithmetic with these selectors also causes a GP fault. Finally, do not free these selectors once they are no longer needed. Method 1 is always recommended and should be used unless the exported selectors do not provide access to the necessary area of physical memory.

Method 2: Selector Synthesis

Selector synthesis should be used when an application needs to access physical memory that is not addressable with the selectors exported from the Windows Kernel. This method involves allocating a new selector and initializing the associated descriptor with the appropriate values. The functions to do this are provided through the Windows Kernel and the DOS Protected Mode Interface (DPMI) server (which is a part of Windows). The required functions include the following:

Function Description ——– ———–

Map Physical To Linear (DPMI: Interrupt 31h, AX=0800h) Maps a 32-bit physical address to a 32-bit linear address. In 386 enhanced mode this function is required because because linear addresses are different from physical addresses and the selector functions can work only with linear addresses. In standard mode, this function is not required because linear addresses are the same as physical addresses.

AllocSelector(WORD wSelector)

                     (Kernel)  Allocates a new selector or
                     array of tiled selectors and copies the
                     attributes of wSelector to the new
                     selector(s). If the limit of wSelector is
                     less than or equal to 64K, then only one
                     selector is allocated. If the limit of
                     wSelector is larger than 64K, an array of
                     tiled selectors is allocated such that
                     each selector points to one 64K portion of
                     the limit of wSelector.

FreeSelector(WORD wSelector)

                     (Kernel) Frees either a single selector
                     or an array of tiled selectors depending
                     on the limit of wSelector. Frees one
                     selector for each 64K portion of the
                     limit of wSelector. The selector, or
                     array of tiled selectors being freed
                     must have been allocated previously by
                     AllocSelector. Furthermore, the limit
                     of wSelector must be the same as the
                     selector used as a parameter to the call
                     to AllocSelector.

SetSelectorBase(WORD wSelector, DWORD dwBase)

                     (Kernel) Stores the starting linear
                     address of the desired region in the
                     descriptor of wSelector.

SetSelectorLimit(WORD wSelector, DWORD dwLimit)

                     (Kernel) Stores the length of the desired
                     region in the descriptor of wSelector.

GetSelectorLimit(WORD wSelector)

                     (Kernel) Retrieves the length of the
                     region pointed to by wSelector. This
                     length comes from wSelector's descriptor.

Caveats:

  1. These routines do not inform the Windows memory manager that a particular block of memory is in use. It is the responsibility of the caller to ensure that the area of memory will not be accessed or freed by some other process in the system.
  2. Synthesized selectors that alias a memory object allocated by Windows will not be updated if the memory object is moved. To make sure that the memory object will not be moved, call GlobalFix() on it before synthesizing a selector that aliases it. Note that if the synthesized selector points to memory provided by a physical device, there is no need to call GlobalFix() because the device’s memory was not allocated by Windows.
  3. Allocating large numbers of selectors is discouraged because selectors are a limited resource.
  4. Allocating a selector does not actually allocate any memory: it merely creates a pointer that can be used to access existing memory (that was previously allocated or is provided by a memory-mapped hardware device). Do not confuse allocating a selector with allocating memory.

The following code fragment has been written using the inline assembly feature of the Microsoft C and C++ compilers. This code illustrates the technique of selector synthesis.

Sample Code

DWORD MapPhysicalToLinear(DWORD, DWORD); WORD SynthSelector(DWORD, DWORD); LPSTR GetSelectorPointer(WORD);

  /* These prototypes are needed only for the Windows     */
  /* Software Development Kit (SDK) version 3.0.          */
  /* They are not needed in the Windows 3.1 SDK, because  */
  /* they are included in WINDOWS.H.                      */

int FAR PASCAL SetSelectorBase(WORD, DWORD); int FAR PASCAL SetSelectorLimit(WORD, DWORD); DWORD FAR PASCAL GetSelectorLimit(WORD);

LPSTR lpPhys; // Could be declared a huge ptr instead. DWORD dwPhysical, dwLinear, dwLength; WORD wSelector; WORD segment, offset; // These variables are necessary only // if the target memory is addressed // with a “real-mode” style SEG:OFFSET // pointer.

/————————————————————–/ /* Sample Code to Synthesize a Selector / /————————————————————–/ / Create a linear address. The method to do this depends upon / / where the memory is located. Both methods are shown below, / / but only one must be used. / / If the selector will point to memory below 1 MB, create a / / linear address as follows (yes, this really is a linear / / address): / dwLinear = ((DWORD)segment << 4L) + offset; / Otherwise, if the selector will point to memory above 1 MB,/ / dwPhysical should contain the 32-bit physical address. / / Call DPMI to convert dwPhysical to a linear address. / / Note that you must pass the physical address and the / / length (limit) to DPMI. / dwPhysical = 0xC00000; // This is physical 12 MB address, // (for example purposes only). dwLinear = MapPhysicalToLinear(dwPhysical, dwLength); if (!dwLinear) { // error… } / Now that dwLinear contains the linear address, it’s time / / to allocate the selector and then the far pointer from / / the selector. For memory regions larger than 64K, a huge / / pointer should be created instead of a far pointer. */ wSelector = SynthSelector(dwLinear, dwLength); // Create selector. lpPhys = GetSelectorPointer(wSelector); // Make a pointer.

  /* Use the pointer lpPhys to access memory...                */
  /* Free the selector when finished with it.  Don't need to   */
  /* make sure the selector's limit is less than 64K because   */
  /* SynthSelector creates an array of tiled selectors and we  */
  /* need to free them all.                                    */

FreeSelector(wSelector); /* Rest of program… / /————————————————————–/ / This function is a shell for DPMI Map Physical To Linear. / / Returns 0 if it failed or the physical address is below / / 1 MB. Returns the linear address if DPMI call succeeded. / /————————————————————–*/

DWORD MapPhysicalToLinear(DWORD dwPhysical, DWORD dwLength) { DWORD dwLinear = 0L; // In case memory below 1 MB, we // don’t want to return garbage. if (dwPhysical >= 0x100000L) // Use only if above 1 MB. { _asm { push di push si mov bx, WORD PTR [dwPhysical+2] ; Load arguments. mov cx, WORD PTR [dwPhysical] mov si, WORD PTR [dwLength+2] mov di, WORD PTR [dwLength] mov ax, 800h int 31h ; Issue DPMI call. jc short error_return mov dx, bx mov ax, cx jmp short fine_return error_return: xor ax, ax mov dx, ax fine_return: mov WORD PTR [dwLinear+2], dx ; Return value. mov WORD PTR [dwLinear], ax pop si pop di } } return dwLinear; }

/————————————————————–/ /* This function will allocate and initialize a selector. / /————————————————————–*/

WORD SynthSelector(DWORD dwLinearAddress, DWORD dwLength) { WORD tempSelector, selector = NULL; /* Allocate one temporary selector by using value of DS / / (DS contains selector of app’s or DLL’s DGROUP, which / / is less than 64K.) Then set the selector’s base / / address and limit to the real values, which may be / / larger than 64K. Because the memory must be accessed by / / 16-bit code, it is necessary to allocate an array of / / tiled selectors. The temporary selector is used to / / force AllocSelector() to allocate an array with the / / proper number of tiled selectors each with the proper / / base and limit. Then, we free the single temporary / / selector. */

  _asm {
       push     ds
       call     AllocSelector
       mov      tempSelector, ax
       }

  if (tempSelector)   /* AllocSelector returns NULL on error. */
     {
     SetSelectorBase(tempSelector, dwLinearAddress);
     SetSelectorLimit(tempSelector, dwLength);
     selector = AllocSelector(tempSelector);
     SetSelectorLimit(tempSelector, 100L);
     FreeSelector(tempSelector);
     }
  return selector;

}

/————————————————————–/ /* This function builds a pointer to the memory referenced / / by the selector. / /————————————————————–*/

LPSTR GetSelectorPointer(WORD selector)

{ return (LPSTR)MAKELONG(0, selector); }

Additional reference words: 3.00 3.10 KBCategory: Prg KBSubcategory: KrMm

============================================================================= Copyright Microsoft Corporation 1994.