PAGE ,132 TITLE DXUTIL.ASM -- Dos Extender Miscellaneous Routines ; Copyright (c) Microsoft Corporation 1988-1991. All Rights Reserved. ;**************************************************************** ;* * ;* DXUTIL.ASM - Dos Extender Miscellaneous * ;* * ;**************************************************************** ;* * ;* Module Description: * ;* * ;* This module contains miscellaneous routines for the Dos * ;* Extender. * ;* * ;**************************************************************** ;* Revision History: * ;* * ;* 08/08/90 earleh DOSX and client privilege ring determined * ;* by equate in pmdefs.inc * ;* 04/09/90 jimmat If 286 with 287, put 287 into pMode too. * ;* 08/20/89 jimmat Removed local A20 code since HIMEM 2.07 * ;* works properly across processor resets * ;* 07/28/89 jimmat Added A20 check/set routines, added * ;* SelOff2SegOff & Lma2SegOff routines. * ;* 06/19/89 jimmat Set direction flag before REP MOVS * ;* 05/25/89 jimmat Added GetSegmentAccess routine * ;* 03/30/89 jimmat Set IOPL = 3 when entering protect mode * ;* 03/16/89 jimmat Added more debug sanity checks * ;* 03/15/89 jimmat Minor changes to run child in ring 1 * ;* 03/13/89 jimmat Added support for LDT & TSS * ;* 02/10/89 (GeneA): changed Dos Extender from small model to * ;* medium model. Also added MoveMemBlock function. * ;* 01/25/89 (GeneA): changed initialization of real mode code * ;* segment address in EnterRealMode. caused by adding * ;* new method of relocationg dos extender for PM operation * ;* 12/13/88 (GeneA): moved EnterProtectedMode and EnterReal- * ;* Mode here from dxinit.asm * ;* 09/16/88 (GeneA): created by extracting code from the * ;* SST debugger modules DOSXTND.ASM, VIRTMD.ASM, * ;* VRTUTIL.ASM, and INTERRPT.ASM * ;* 18-Dec-1992 sudeepb Changed cli/sti to faster FCLI/FSTI * ;* 24-Jan-1992 v-simonf Added WOW callout when INT 8 hooked * ;* * ;**************************************************************** .286p .287 ; ------------------------------------------------------- ; INCLUDE FILE DEFINITIONS ; ------------------------------------------------------- ; .sall ; .xlist include segdefs.inc include gendefs.inc include pmdefs.inc include dpmi.inc include intmac.inc .list ; ------------------------------------------------------- ; GENERAL SYMBOL DEFINITIONS ; ------------------------------------------------------- SHUT_DOWN = 8Fh ;address in CMOS ram of the shutdown code CMOS_ADDR = 70h ;i/o address of the cmos ram address register CMOS_DATA = 71h ;i/o address of the cmos ram data register DMAServiceSegment equ 040h ;40:7B bit 5 indicates DMA services DMAServiceByte equ 07Bh ; are currently required DMAServiceBit equ 020h ; ------------------------------------------------------- ; EXTERNAL SYMBOL DEFINITIONS ; ------------------------------------------------------- ; ------------------------------------------------------- ; DATA SEGMENT DEFINITIONS ; ------------------------------------------------------- DXDATA segment extrn segGDT:WORD extrn segIDT:WORD extrn selGDT:WORD extrn selIDT:WORD extrn selGDTFree:WORD extrn bpGDT:FWORD extrn bpIDT:FWORD extrn bpRmIVT:FWORD extrn rgbXfrBuf1:BYTE extrn PMFaultVector:DWORD extrn lpfnXMSFunc:DWORD extrn pbReflStack:WORD bIntMask db 0 bpBogusIDT df 0 ;This is loaded into the IDT register to ; force a bogus IDT to be defined. When we ; then do an interrupt a triple fault will ; occur forcing the processor to reset. This ; is when doing a mode switch to real mode. IDTSaveArea dw 3 DUP (?) ;save area for IDT during mode switch public A20EnableCount A20EnableCount dw 0 ShutDownSP dw 0 ;stack pointer during 286 reset public f286_287 f286_287 db 0 ;NZ if this is a 286 with 287 coprocessor if DEBUG ;------------------------------------------------------------ extrn fTraceA20:WORD extrn fTraceMode:WORD public fA20 fA20 db 0 endif ;DEBUG -------------------------------------------------------- selPmodeFS dw 0 selPmodeGS dw 0 public HighestSel HighestSel dw 0 ifndef WOW_x86 public IretBopTable IretBopTable label byte irp x,<0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15> db 0c4h, 0c4h, 05dh, x endm else public FastBop FastBop df 0 IretBopTable label byte irp x,<0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15> db 02eh, 066h, 0FFh, 01eh, 00h, 00h, 05dh, x endm NullSel dd 0 dd 0 endif extrn DpmiFlags:WORD DXDATA ends ; ------------------------------------------------------- ; CODE SEGMENT VARIABLES ; ------------------------------------------------------- DXCODE segment extrn segDXData:WORD extrn segDXCode:WORD extrn selDgroup:WORD DXCODE ends DXPMCODE segment extrn selDgroupPM:WORD DXPMCODE ends ; ------------------------------------------------------- subttl Real/Protected Mode Switch Routines page ; ------------------------------------------------------- DXCODE segment assume cs:DXCODE ; ------------------------------------------------------- ; REAL/PROTECTED MODE SWITCH ROUTINES ; ------------------------------------------------------- ; ; EnterProtectedMode -- This routine will switch the processor ; into protected mode. It will return with the processor ; in protected mode and all of the segment registers loaded ; with the selectors for the protected mode segments. ; (CS with the selector for DXCODE and DS,ES,SS with the ; selector for DXDATA) ; It will also switch mode dependent memory variables. ; It assumes that InitGlobalDscrTable and InitIntrDscrTable ; have been called to set up the descriptor tables appropriately. ; ; Note: Except for a very brief time in this routine and in ; EnterRealMode, the DOS Extender runs in the same ring along ; with it's child app. This has the benefit of eliminating ; ring transitions on hardware and software interrupts. ; It also makes it possible for the child to hook their ; own interrupt routine into the IDT. ; ; Input: none ; Output: none ; Errors: none ; Uses: AX, DS, ES, SS, CS modified, all others preserved ; ; NOTE: This routine turns interrupts of and does not turn them ; back on. assume ds:DGROUP,es:NOTHING,ss:NOTHING public EnterProtectedMode EnterProtectedMode proc near FCLI ; Update the mode dependent variables. mov ax,SEL_DXDATA or STD_RING mov selDgroup,ax ; Set the DMA services required bit for pMode users. mov ax,DMAServiceSegment mov es,ax or byte ptr es:[DMAServiceByte],DMAServiceBit ; 'local enable' the A20 line via HIMEM before switching to pMode. ; This is more complicated than you might think. Some real mode code ; (like old versions of SMARTDRV.SYS) may diddle with A20 on their own. ; These programs may not want us to change A20 on them. RMIntrReflector ; may do a XMS 'local enable' to turn A20 back on for one of these pgms. ; Also, on a 386 where we actually do the mode switch, we try to leave ; A20 enabled so as to not waste time diddling for nothing. The ; A20EnabledCount variable tracks if we've 'local enabled' A20 or not. ; Since we can't really trust real mode to leave A20 alone, we double ; check that it's really really on when we think it should be. push bx ;save bx around XMS calls cmp A20EnableCount,0 ;should A20 already be enabled? jz enpm10 ; no, (normal 4 286) just go enable it xmssvc 7 ; yes, is it really enabled? or ax,ax jnz enpm15 ; yes, we be done! if DEBUG ;------------------------------------------------------------ or fA20,1 ; somebody done us wrong endif ;--------------------------------------------------------------- xmssvc 6 ;keep enable/disable calls balanced dec A20EnableCount enpm10: xmssvc 5 ;local enable A20 inc A20EnableCount if DEBUG ;------------------------------------------------------------ or ax,ax jnz @f or fA20,2 ;enable failed! @@: cmp fTraceA20,0 jz @f xmssvc 7 ;in debug mode, make double sure or ax,ax ; A20 was enabled. Slows things jnz @f ; down, but it's better to know. or fA20,2 @@: endif ;DEBUG -------------------------------------------------------- enpm15: pop bx ifndef WOW_x86 DPMIBOP SetAltRegs ; Make sure that the nested task flag is clear pushf pop ax and ax,NOT 4000h push ax npopf ; Make sure that we have the appropriate descriptor tables in effect, ; and switch the machine into protected mode enpr20: smsw ax ;get current machine state or ax,1 ;set the protected mode bit lgdt bpGDT lidt bpIDT lmsw ax ;and away we go ; Flush the instruction queue and load the code segment selector ; by doing a far jump. db 0EAh ;jump far opcode dw offset enpm40 ;offset of far pointer dw SEL_DXCODE0 ;selector part of PM far pointer (ring 0) ; Load the other segment registers with valid selectors (not under VCPI) enpm40: mov ax,SEL_DXDATA0 ;stack has gotta be ring 0 also mov ss,ax ; Load the LDT register and the Task Register mov ax,SEL_LDT lldt ax ;load the LDT register mov ax,SEL_DXDATA or STD_RING mov ds,ax ;ds to our DGROUP mov ax,SEL_GDT mov es,ax ;es to GDT push si ;make sure busy bit is off mov si,SEL_TSS ; in the TSS descriptor mov es:[si].arbSegAccess,STD_TSS ; before trying to load it ltr si ;now load the task register pop si else .386p push ebp mov ebp,esp push SEL_DXCODE or STD_RING ; new cs push 0 ; high half eip push offset epmwow ; new eip push SEL_DXDATA or STD_RING ; new ss push ebp push SEL_DXDATA or STD_RING ; new ds DPMIBOP DPMISwitchToProtectedMode epmwow: pop ebp .286p endif push ds ;point es to DGROUP pop es ; If this is a 286 machine with a 287 math coprocessor, put the coprocessor ; into protected mode also. cmp f286_287,0 ;286 and 287? jz @f ifndef NEC_98 xor al,al ; yup, clear co-processor busy line out 0F0h,al endif ;!NEC_98 fsetpm ; and put it in pMode @@: ; We're currently running in ring 0. Setup an interlevel iret frame ; to switch to our normal ring, and also force IOPL=3. I spent 1+ day ; debugging on a 286 system (with no debugger!) because the 286 seemed ; switch into protected mode with IOPL=0, and once we got to an outer ; ring, we would fault on things like CLI instructions. enpmSwitchRing: ifndef WOW_x86 mov ax,sp ;still points to return address push SEL_DXDATA or STD_RING ;new ss push ax ;new sp pushf pop ax or ah,30h push ax ;new flags, with IOPL=3 push SEL_DXCODE or STD_RING ;new cs push offset DXCODE:epm_ret ;new ip iret endif ; When we get here, we are now in an outer ring. epm_ret: .386 mov ax, selPmodeFS mov fs, ax mov ax, selPmodeGS mov gs, ax .286p ret ;near return to caller in pMode EnterProtectedMode endp ; ------------------------------------------------------- ; EnterRealMode -- This routine will switch the processor ; from protected mode back into real mode. It will also ; reset the various mode dependent variables to their ; real mode values and load the segment registers with ; the real mode segment addresses. ; ; Input: none ; Output: none ; Errors: none ; Uses: AX, DS, ES, SS, CS modified ; ; NOTE: This routine must be called with the stack segment set ; to the Dos Extender data segment, as it resets the stack ; segment register to the Dos Extender real mode data segment ; but does not modify the stack pointer. ; NOTE: This routine turns interrupts off and and does not turn ; them back on. assume ds:DGROUP,es:NOTHING,ss:NOTHING public EnterRealMode EnterRealMode proc near .386 mov ax,fs mov selPmodeFS, ax mov ax,gs mov selPmodeGS, ax .286p FCLI mov es,selDgroup push SegDxCode push offset DXCODE:enrmwow push SegDxData push sp push SegDxData .386p DPMIBOP DPMISwitchToRealMode .286p enrmwow: add sp,6 ; remove rest of parameters push ds pop es ; es not set by mode switch enrm70: push es ;clear DMA services required mov ax,DMAServiceSegment ; bit for real mode mov es,ax and byte ptr es:[DMAServiceByte],not DMAServiceBit pop es mov ax,segDXData mov selDgroup,ax ret EnterRealMode endp ; ------------------------------------------------------- public RmUnsimulateProc RmUnsimulateProc proc far BOP BOP_UNSIMULATE RmUnsimulateProc endp ; ------------------------------------------------------- DXCODE ends DXPMCODE segment assume cs:DXPMCODE public PmUnsimulateProc PmUnsimulateProc proc far BOP BOP_UNSIMULATE PmUnsimulateProc endp ; ------------------------------------------------------- ; RAW MODE SWITCH ROUTINES ; ------------------------------------------------------- ; ------------------------------------------------------ ; PmRawModeSwitch -- This routine performs a raw mode switch from ; protected mode to real mode. NOTE: applications will JUMP at this ; routine ; ; Input: ax - new DS ; cx - new ES ; dx - new SS ; bx - new sp ; si - new CS ; di - new ip ; Output: DS, ES, SS, sp, CS, ip contain new values ; Errors: none ; Uses: ; ; ; assume ds:nothing, ss:nothing, es:nothing public PmRawModeSwitch PmRawModeSwitch proc far push ss pop ds push bx .386p mov bx,ss movzx ebx,bx lar ebx,ebx test ebx,(AB_BIG SHL 8) mov ebx,esp jnz prms10 movzx ebx,bx prms10: .286p ; Switch to dosx stack (since switch to real mode will do that to us anyway ; NOTE: no-one can call EnterIntHandler or ExitIntHandler until we switch to ; the user's new stack. If they do, they will use the area we stored ; the parameters for this call for a stack frame rpushf FCLI push SEL_DXDATA OR STD_RING pop ss assume ss:DGROUP .386p movzx esp,word ptr pbReflStack .286p ; Save user registers push dx ; ss .386p push word ptr [ebx] push word ptr [ebx - 2]; flags pushed before cli .286p push si ; cs push di ; ip push ax ; ds push cx ; es ; switch modes mov ax,SEL_DXDATA OR STD_RING mov ds,ax SwitchToRealMode ; set the registers, switch stacks, and return to the user pop es pop ds pop ax ; ip pop bx ; cs pop cx ; flags pop si ; sp pop ss assume ss:nothing mov sp,si push cx popf push bx push ax ret PmRawModeSwitch endp ; NOTE: this is now the DXCODE segment, NOT the DXPMCODE segment (courtesy ; of SwitchToRealMode ; ------------------------------------------------------ ; RmRawModeSwitch -- This routine performs a raw mode switch from ; protected mode to real mode. NOTE: applications will JUMP at this ; routine ; ; Input: ax - new DS ; cx - new ES ; dx - new SS ; bx - new sp ; si - new CS ; di - new ip ; Output: DS, ES, SS, sp, CS, ip contain new values ; Errors: none ; Uses: ; ; ; assume ds:nothing, ss:nothing, es:nothing public RmRawModeSwitch RmRawModeSwitch proc far push ss pop ds push bx mov bx,sp ; Switch to dosx stack (since switch to real mode will do that to us anyway ; NOTE: no-one can call EnterIntHandler or ExitIntHandler until we switch to ; the user's new stack. If they do, they will use the area we stored ; the parameters for this call for a stack frame pushf FCLI push segDxData pop ss assume ss:DGROUP mov sp,pbReflStack ; Save user registers push dx ; ss push word ptr [bx] ; sp push word ptr [bx - 2] ; flags from before cli push si ; cs push di ; ip push ax ; ds push cx ; es ; switch modes mov ax,segDxData mov ds,ax SwitchToProtectedMode ; set the registers, switch stacks, and return to the user pop es pop ds .386p test DpmiFlags,DPMI_32BIT jnz rrms10 xor eax,eax ; clear high 16 bits xor edi,edi ; clear high 16 bits .286p rrms10: pop di ; ip pop ax ; cs pop cx ; flags from before cli pop bx ; sp assume ss:nothing pop ss .386p mov esp,ebx .286p push cx rpopf .386p push eax push edi db 066h retf .286p RmRawModeSwitch endp DXPMCODE ENDS DXCODE SEGMENT ; ------------------------------------------------------- ; STATE SAVE/RESTORE ROUTINES ; ------------------------------------------------------- ; ------------------------------------------------------- ; RmSaveRestoreState -- This routine exists as a placeholder. It ; is not currently necessary to perform any state saving/restoring ; for raw mode switch. The DPMI spec states that the user can call ; this routine with no adverse effect. ; ; Input: none ; Output: none ; Errors: none ; Uses: none ; assume ds:nothing, ss:nothing, es:nothing public RmSaveRestoreState RmSaveRestoreState proc far ret RmSaveRestoreState endp DXCODE ends ; ------------------------------------------------------- DXPMCODE segment assume cs:DXPMCODE ; ------------------------------------------------------- ; RmSaveRestoreState -- This routine exists as a placeholder. It ; is not currently necessary to perform any state saving/restoring ; for raw mode switch. The DPMI spec states that the user can call ; this routine with no adverse effect. ; ; Input: none ; Output: none ; Errors: none ; Uses: none ; assume ds:DGROUP, ss:nothing, es:nothing public PmSaveRestoreState PmSaveRestoreState proc far push ax push ds mov ax, SEL_DXDATA or STD_RING mov ds, ax test DpmiFlags,DPMI_32BIT pop ds pop ax jnz short @f ; 32-bit return ret @@: db 66h ret PmSaveRestoreState endp ifdef NEC_98 assume ds:DGROUP,es:NOTHING,ss:NOTHING public IncInBios public DecInBios ; ; IncInBios / DecInBios ; ; IN_BIOS Inc/Dec SubRoutine ; IncInBios proc near push ax push es mov ax, 0040h mov es, ax test byte ptr es:[0], 20h jz @f inc byte ptr es:[056h] @@: pop es pop ax ret IncInBios endp DecInBios proc near push ax push es mov ax, 0040h mov es, ax test byte ptr es:[0], 20h jz @f dec byte ptr es:[056h] @@: pop es pop ax ret DecInBios endp endif ;NEC_98 ; ------------------------------------------------------- ; GTPARA -- This routine will return the real mode paragraph ; address of the specified protected mode memory segment. ; ; Input: SI - selector of the segment ; Output: returns ZR true if segment is in lower 1MB range ; AX - real mode paragraph address ; returns ZR false if segment is in extended memory ; Errors: returns CY true if selector isn't valid ; Uses: AX modified, all else preserved assume ds:DGROUP,es:NOTHING,ss:NOTHING public gtpara gtpara: push cx push es push si push bx mov bx,selGDT ;selector for the GDT segment mov es,bx lsl bx,bx and bx,SELECTOR_INDEX and si,SELECTOR_INDEX cmp si,bx ;check the given selector against ; the GDT segment limit pop bx jc gtpr20 ; The given selector is beyond the end of the GDT, so return error. or sp,sp stc Debug_Out "gtpara: invalid selector (#si)" jmp short gtpr90 ; The selector specified is inside the range of defined descriptors in ; the GDT. Get the address from the descriptor. gtpr20: mov cl,es:[si].adrBaseHigh test cl,0F0h jnz gtpr90 shl cl,4 mov ax,es:[si].adrBaseLow if DEBUG ;------------------------------------------------------------ test al,0Fh jz @f Debug_Out "gtpara: segment not on para boundry, sel #si at #cl#ax" @@: endif ;DEBUG -------------------------------------------------------- shr ax,4 or ah,cl cmp ax,ax ; gtpr90: pop si pop es pop cx ret ; ------------------------------------------------------- ; SelOff2SegOff -- This routine will return will translate a ; protected mode selector:offset address to the corresponding ; real mode segment:offset address. ; ; Input: AX - PM selector ; DX - PM offset ; Output: if Z set: ; AX - RM segment ; DX - RM offset ; if NZ set, address is not in conventional memory, and ; cannot be translated ; ; Errors: none ; Uses: AX, DX; all else preserved ; ; Note: This routine is very similar to gtpara, and could replace it! assume ds:DGROUP,es:NOTHING,ss:NOTHING public SelOff2SegOff SelOff2SegOff proc near push bx push cx push dx call GetSegmentAddress ;bx:dx = lma of segment pop cx ;cx = offset test bl,0f0h ;above 1 Meg line? jnz @f ; yes, cut out now add dx,cx adc bx,0 ;bx:dx = lma of segment:offset call Lma2SegOff ;bx:dx = seg:off mov ax,bx ;dx:ax = seg:off cmp ax,ax ;under 1 Meg, set Z flag @@: pop cx pop bx ret SelOff2SegOff endp ; ------------------------------------------------------ ; Lma2SegOff -- This routine converts a linear memory address ; in BX:DX to a normalized SEG:OFF in BX:DX. ; ; Input: BX:DX = lma ; Output: BX:DX = normalized SEG:OFF ; Uses: none public Lma2SegOff Lma2SegOff proc near push dx shl bx,12 shr dx,4 or bx,dx pop dx and dx,0fh ret Lma2SegOff endp ; ------------------------------------------------------- ; GetSegmentAddress -- This routine will return with ; the linear address of the specified segment. ; ; Input: AX - segment selector ; Output: DX - low word of segment address ; BX - high word of segment address ; Errors: none ; Uses: BX, DX, all else preserved assume ds:DGROUP,es:NOTHING,ss:NOTHING public GetSegmentAddress GetSegmentAddress: push es push di mov es,selGDT mov di,ax and di,SELECTOR_INDEX mov dx,es:[di].adrBaseLow mov bl,es:[di].adrBaseHigh mov bh,es:[di].adrbBaseHi386 pop di pop es ret ; ------------------------------------------------------- ; SetSegmentAddress -- This routine will modify the ; segment base address of the specified segment. ; ; Input: AX - segment selector ; Output: DX - low word of segment address ; BX - high word of segment address ; Errors: None ; Uses: All registers preserved assume ds:DGROUP,es:NOTHING,ss:NOTHING public SetSegmentAddress SetSegmentAddress: push si push es mov es,selGDT mov si,ax and si,SELECTOR_INDEX mov es:[si].adrBaseLow,dx mov es:[si].adrBaseHigh,bl mov es:[si].adrbBaseHi386,bh push ax push bx push cx mov ax,si mov cx,1 mov bx,si .386p FBOP BOP_DPMI,,FastBop .286p pop cx pop bx pop ax pop es pop si ret ; ------------------------------------------------------- ; NSetSegmentAccess -- This routine will modify the ; access rights byte of a specified segment. ; ; Input: Selector - segment selector ; Access - Access rights byte value ; Output: none ; Errors: Carry set, AX = error code ; Uses: All registers preserved assume ds:DGROUP,es:NOTHING,ss:NOTHING cProc NSetSegmentAccess,, parmW Selector parmW Access cBegin mov es,selGDT mov si,Selector and si,SELECTOR_INDEX mov ax,Access mov es:[si].arbSegAccess,al ; Set access byte. and ah,0F0h ; Mask off reserved bits. and es:[si].cbLimitHi386,0fh ; Clear old extended bits. or es:[si].cbLimitHi386,ah ; Set new extended bits. IFDEF WOW_x86 push ax push bx push cx mov ax,si mov cx,1 mov bx,si .386p FBOP BOP_DPMI,,FastBop .286p pop cx pop bx pop ax ENDIF cEnd ; ------------------------------------------------------- ; ParaToLDTSelector -- This routine will convert a segment ; address relative to the start of the exe file into the ; corresponding selector for the segment. It searches the ; LDT to see if a segment is already defined at that address. ; If so, its selector is returned. If not, a new segment ; descriptor will be defined. ; ; Note: The LDT and GDT are currently one and the same. ; ; Input: AX - paragraph aaddress of real mode segment ; BX - access rights byte for the segment ; Output: AX - selector for the segment ; Errors: returns CY set if unable to define a new segment ; Uses: AX, all other registers preserved assume ds:DGROUP,es:NOTHING,ss:NOTHING public ParaToLDTSelector ParaToLDTSelector proc near push bx push cx push dx ; Convert the paragraph address to a linear address and see if there ; is a segment defined at that address. mov dx,ax call FindLowSelector jnc @f ;if so, we don't need to make one ; This segment isn't defined, so we need to create a descriptor for it. mov ax,dx call MakeLowSegment if DEBUG ;------------------------------------------------------------ jnc ptos80 Debug_Out "ParaToLDTSelector: can't make selector!" ptos80: endif ;DEBUG -------------------------------------------------------- jc ptos90 @@: or al,SELECTOR_TI or STD_RING ;look like LDT selector ; All done ptos90: pop dx pop cx pop bx ret ParaToLDTSelector endp public FarParaToLDTSelector FarParaToLDTSelector proc far call ParaToLDTSelector ret FarParaToLDTSelector endp ; ------------------------------------------------------- page ; ------------------------------------------------------- ; DESCRIPTOR TABLE MANIPULATION ROUTINES ; ------------------------------------------------------- ; ------------------------------------------------------- ; AllocateSelector -- This function will obtain the ; next free descriptor in the global descriptor table. ; The descriptors in the GDT are stored on a linked list ; of free descriptors. The cbLimit field of the descriptor ; is used as the link to the next element of the list. In ; addition, free descriptors have the access rights byte ; set to 0. ; ; Note: The function InitGlobalDscrTable must have been ; called before calling this function. ; ; Input: none ; Output: AX - selector if one is available ; Errors: CY clear if successful, AX=0 and CY set if not free selectors ; Uses: AX, all else preserved assume ds:DGROUP,es:NOTHING,ss:NOTHING public AllocateSelector AllocateSelector proc near ; Get the next free descriptor on the list. push cx mov cx, 1 ; # of selectors needed mov ax, 0 push ds FBOP BOP_DPMI,Int31Call,FastBop pop cx ret AllocateSelector endp ; ------------------------------------------------------- ; FreeSelector -- This routine will mark the segment ; descriptor for the specified selector as free. This ; is used to release a temporary selector when no longer ; needed. The descriptor is marked as free by setting the ; access rights byte to 0 and placing it on the free list. ; ; Note: This routine can only be called in protected mode. ; ; Input: AX - selector to free ; Output: none ; Errors: CY clear if no error, set if selector is invalid ; Uses: AX used, all other registers preserved assume ds:DGROUP,es:NOTHING,ss:NOTHING public FreeSelector FreeSelector proc near push bx mov bx,ax mov ax,1 push ds FBOP BOP_DPMI,Int31Call,FastBop pop bx ret FreeSelector endp ; ------------------------------------------------------- ; FindLowSelector -- This function will search the global ; descriptor table for a descriptor matching the given ; address. ; ; Input: AX - real mode paragraph to search for ; BX - access rights byte for the segment ; Output: AX - selector corresponding to input paragraph address ; Errors: returns CY set if specified descriptor not found ; Uses: AX, all else preserved assume ds:DGROUP,es:NOTHING,ss:NOTHING public FindLowSelector FindLowSelector: push bx push dx ; mov dx,ax push bx call ParaToLinear pop ax mov bh,al call FindSelector ; pop dx pop bx ret ; ------------------------------------------------------- ; FindSelector -- This function will search the global ; descriptor table for a segment descriptor matching ; the specified linear byte address. ; ; Note that this routine cannot be used to find ; selectors pointing to addresses above 16 Megabytes. ; This is not really a problem, since the routine ; is used to find selectors in real mode DOS land ; most of the time. ; ; Input: DX - low word of linear byte address ; BL - high byte of linear address ; BH - access rights byte for the segment ; Output: AX - selector of corresponding segment ; Errors: returns CY set if specified descriptor not found ; Uses: AX used, all other registers preserved assume ds:DGROUP,es:NOTHING,ss:NOTHING public FindSelector FindSelector proc near push si push di push es push cx ; Get segment limit of the GDT to use as a limit on the search. lsl di,selGDT mov es,selGDT ; Look for a descriptor matching the address in BL:AX mov si,SEL_USER ;search starting here if 0 fnds20: cmp es:[si].arbSegAccess,0 else fnds20: cmp word ptr es:[si].arbSegAccess,0 endif jz fnds28 ;skip if unused descriptor cmp bl,es:[si].adrBaseHigh jnz fnds28 cmp dx,es:[si].adrBaseLow jnz fnds28 if 0 cmp es:[si].cbLimit,0 jz fnds28 ;skip if dscr has 0 limit else cmp es:[si].cbLimit,0ffffh jnz fnds28 ;skip unless dscr has 64k limit endif mov cl,bh xor cl,es:[si].arbSegAccess and cl,NOT AB_ACCESSED jz fnds90 fnds28: add si,8 ;bump to next descriptor jc fnds80 cmp si,di ;check against end of GDT jc fnds20 ;if still less, continue on. ; Hit the end of the GDT and didn't find one. So return error. fnds80: stc jmp short fnds99 ; We found it, so return the selector fnds90: mov ax,si fnds99: pop cx pop es pop di pop si ret FindSelector endp ; ------------------------------------------------------- ; DupSegmentDscr -- This function will duplicate the specified ; segment descriptor into the specified destination descriptor. The ; end result is a second segment descriptor pointing to the same place ; in memory as the first. ; ; Input: AX - selector of segment descriptor to duplicate ; BX - selector of the segment descriptor to receive duplicate ; Output: none ; Errors: none ; Uses: All registers preserved. Modifies the segment ; descriptor for the specified segment. If this selector happens ; to be in a segment register when this routine is called, that ; segment register may end up pointing to the new location. assume ds:DGROUP,es:NOTHING public DupSegmentDscr DupSegmentDscr proc near push cx push si push di push ds push es mov si,ax mov di,bx and si,SELECTOR_INDEX and di,SELECTOR_INDEX mov es,selGDT mov ds,selGDT assume ds:NOTHING mov cx,4 cld rep movs word ptr [di],word ptr [si] pop es pop ds pop di pop si pop cx ret DupSegmentDscr endp IFDEF ROM ; ------------------------------------------------------- DXPMCODE ends ; ------------------------------------------------------- DXCODE segment assume cs:DXCODE ENDIF ; ------------------------------------------------------- ; NSetSegmentDscr -- This function will initialize the ; specified descriptor table entry with the specified data. ; ; This function can be called in real mode or protected mode. ; ; Input: ; Param1 - WORD segment selector ; Param2 - DWORD 32-bit segment base address ; Param3 - DWORD 32-bit segment limit ; param4 - WORD segment access/type ; Output: returns selector for the segment ; Errors: none ; Uses: Flags assume ds:DGROUP,es:NOTHING,ss:NOTHING cProc NSetSegmentDscr,, parmW Selector parmD Base parmD Limit parmW Access cBegin mov es,selGDT mov di,Selector and di,SELECTOR_INDEX mov ax,off_Base ; Set segment base mov es:[di].adrBaseLow,ax mov ax,seg_Base mov es:[di].adrBaseHigh,al mov es:[di].adrbBaseHi386,ah mov ax,word ptr Access and ax,070ffh ; clear 'G' bit and ; extended limit bits mov word ptr es:[di].arbSegAccess,ax ; set access mov ax,seg_Limit mov bx,off_Limit ; AX:BX = segment limit test ax,0fff0h ; big? jz ssd_0 ; No shr bx,12d ; Yes, make it page granular. shl ax,4d or bx,ax mov ax,seg_Limit shr ax,12d or al,080h ; set 'G' bit ssd_0: or es:[di].cbLimitHi386,al ; set high limit mov es:[di].cbLimit,bx ; set low limit push ax push bx push cx mov ax,di mov cx,1 mov bx,di .386p FBOP BOP_DPMI,,FastBop .286p pop cx pop bx pop ax cEnd ifndef WOW_x86 ; ------------------------------------------------------- ; NSetGDTSegmentDscr -- This function will initialize the ; specified descriptor table entry with the specified data. ; ; This function can be called in real mode or protected mode. ; ; Input: ; Param1 - WORD segment selector ; Param2 - DWORD 32-bit segment base address ; Param3 - DWORD 32-bit segment limit ; param4 - WORD segment access/type ; Output: returns selector for the segment ; Errors: none ; Uses: Flags assume ds:DGROUP,es:NOTHING,ss:NOTHING cProc NSetGDTSegmentDscr,, parmW Selector parmD Base parmD Limit parmW Access cBegin mov ax,SEL_GDT mov es,ax mov di,Selector and di,SELECTOR_INDEX mov ax,off_Base ; Set segment base mov es:[di].adrBaseLow,ax mov ax,seg_Base mov es:[di].adrBaseHigh,al mov es:[di].adrbBaseHi386,ah mov ax,word ptr Access and ax,070ffh ; clear 'G' bit and ; extended limit bits mov word ptr es:[di].arbSegAccess,ax ; set access mov ax,seg_Limit mov bx,off_Limit ; AX:BX = segment limit test ax,0fff0h ; big? jz @f ; No shr bx,12d ; Yes, make it page granular. shl ax,4d or bx,ax mov ax,seg_Limit shr ax,12d or al,080h ; set 'G' bit @@: or es:[di].cbLimitHi386,al ; set high limit mov es:[di].cbLimit,bx ; set low limit cEnd endif ; WOW_x86 IFDEF ROM ; ------------------------------------------------------- DXCODE ends ; ------------------------------------------------------- DXPMCODE segment assume cs:DXPMCODE ENDIF ; ------------------------------------------------------- ; MakeLowSegment -- This function will create a segment ; descriptor for the specified low memory paragraph address. ; The segment length will be set to 64k. The difference ; between this and MakeScratchSelector is that this function ; allocates a new segment descriptor in the user area of ; the global descriptor table, thus creating a more or less ; permanent selector. MakeScratchSelector always uses the ; same descriptor location in the descriptor table, thus ; creating a very temporary selector. ; ; Input: AX - paragraph address in low memory ; BX - access rights word for the segment ; Output: AX - selector to use to access the memory ; Errors: returns CY clear if no error, CY set if unable to ; allocate a segment descriptor ; Uses: AX used, all else preserved assume ds:DGROUP,es:NOTHING,ss:NOTHING public MakeLowSegment MakeLowSegment proc near ; We need to allocate a segment descriptor, convert the paragraph address ; to a linear byte address, and then initialize the allocated segment ; descriptor. push dx push cx mov cx,bx mov dx,ax ;paragraph address to DX call AllocateSelector ;get a segment descriptor to use jc mksl90 ;get out if error call ParaToLinear cCall NSetSegmentDscr, clc mksl90: pop cx pop dx ret MakeLowSegment endp ; ------------------------------------------------------- ; ParaToLinear ; ; This function will convert a paragraph address in the lower ; megabyte of memory space into a linear address for use in ; a descriptor table. ; ; Input: DX - paragraph address ; Output: DX - lower word of linear address ; BX - high word of linear address ; Errors: none ; Uses: DX, BL used, all else preserved assume ds:NOTHING,es:NOTHING,ss:NOTHING public ParaToLinear ParaToLinear proc near mov bl,dh shr bl,4 shl dx,4 xor bh,bh ret ParaToLinear endp ; ------------------------------------------------------- ; RZCall -- Utility routine to call a Ring ; Zero procedure. Stack parameter is the NEAR ; address of the routine in the DXPMCODE segment to ; call. The called routine must be declared FAR ; and take no stack parameters. ; ; USES: Whatever Ring 0 routine uses ; +Flags ; RETURNS: Whatever Ring 0 routine returns ; ; NOTE: Assumes that interrupts must be disabled ; for the Ring 0 routine. ; ; History: ; 12-Feb-1991 -- ERH wrote it!!! ; ------------------------------------------------------- My_Call_Gate dd (SEL_SCR0 or STD_TBL_RING) shl 10h public RZCall RZCall proc near pushf FCLI push bp mov bp,sp cCall NSetSegmentDscr, pop bp call dword ptr My_Call_Gate cCall NSetSegmentDscr, npopf retn 2 RZCall endp ; ------------------------------------------------------- ; ------------------------------------------------------- DXPMCODE ends ; ;**************************************************************** end