windows-nt/Source/XPSP1/NT/base/mvdm/dpmi/dxutil.asm
2020-09-26 16:20:57 +08:00

1561 lines
45 KiB
NASM

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,<SetDescriptorTableEntries>,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,<PUBLIC,NEAR>,<es,si>
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,<SetDescriptorTableEntries>,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,<PUBLIC,FAR>,<es,di,ax,bx>
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,<SetDescriptorTableEntries>,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,<PUBLIC,FAR>,<es,di,ax,bx>
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,<ax,bx,dx,0,0FFFFh,cx>
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,<SEL_SCR0,0,SEL_EH,0,[bp+6],STD_CALL>
pop bp
call dword ptr My_Call_Gate
cCall NSetSegmentDscr,<SEL_SCR0,0,0,0,-1,STD_DATA>
npopf
retn 2
RZCall endp
; -------------------------------------------------------
; -------------------------------------------------------
DXPMCODE ends
;
;****************************************************************
end