UNIT NWintr; { DPMI Protected mode calls: Hubert Plattfaut of 2:2447/203.4 Windows Protected Mode calls: -Based on EZDPMI by Julian M. Bucknall [1993: 100116.1572@Compuserve.Com] -Based on the NetCalls and WinDPMI units by Siebrand Dijkstra [1995: 2:512/250.595] -Corrections by Berend de Boer [1995: berend@beard.nest.nl or 2:281/527.23] NwTP Version 0.6, 950301, Copyright 1993,1995 R. Spronk } INTERFACE {$B-,F+,O-,R-,S-,X+} {$DEFINE ProtMode} {$IFDEF MSDOS} {$DEFINE RealMode} {$UNDEF ProtMode} {$ENDIF} uses {$IFDEF RealMode} Dos {$ENDIF} {$IFDEF DPMI} Dos,WinApi { we need the GlobalDosAlloc-Function} {$ENDIF} {$IFDEF WINDOWS} WinTypes,WinDOS,WinProcs {$ENDIF}; CONST VLM_ID_UNKNOWN = $0000; { non-VLM application } VLM_ID_VLM = $0001; VLM_ID_CONN = $0010; VLM_ID_TRAN = $0020; VLM_ID_IPX = $0021; VLM_ID_TCP = $0022; VLM_ID_NWP = $0030; VLM_ID_BIND = $0031; VLM_ID_NDS = $0032; VLM_ID_PNW = $0033; VLM_ID_RSA = $0034; VLM_ID_REDIR = $0040; VLM_ID_FIO = $0041; VLM_ID_PRINT = $0042; VLM_ID_GENR = $0043; VLM_ID_NETX = $0050; VLM_ID_AUTO = $0060; VLM_ID_SECURITY = $0061; VLM_ID_NMR = $0100; VLM_ID_DRVPRN = $09F2; VLM_ID_SAA = $09F5; { SAA Client API for NetWare } VLM_ID_IPXMIB = $09F6; VLM_ID_PNWMIB = $09F7; VLM_ID_PNTRAP = $09F8; VLM_ID_MIB2PROT = $09F9; VLM_ID_MIB2IF = $09FA; VLM_ID_NVT = $09FB; VLM_ID_TRAP = $09FC; VLM_ID_REG = $09FD; VLM_ID_ASN1 = $09FE; VLM_ID_SNMP = $09FF; Type {$ifdef ProtMode} TTregisters= Record {This is the data-structure for the} Case Byte Of {real-mode-interrupts in DPMI-mode} 0: {32 bit registers} (EDI,ESI,EBP,Reserved,EBX,EDX, ECX,EAX:LongInt); 1: {16 bit registers} (DI,DIHigh,SI,SIHigh, BP,BPHigh,ReservedLow,ReservedHigh, BX,BXHigh,DX,DXHigh, CX,CXHigh,AX,AXHigh, Flags,ES,DS,FS,GS,IP, CS,SP,SS:Word); 2: {8 bit registers} (DILowLow,DILowHigh,DIHighLow,DIHighHigh, SILowLow,SILowHigh,SIHighLow,SIHighHigh, BPLowLow,BPLowHigh,BPHighLow,BPHighHigh, ReservedLowLow,ReservedLowHigh,ReservedHighLow,ReservedHighHigh, BL,BH,BXHighLow,BXHighHigh, DL,DH,DXHighLow,DXHighHigh, CL,CH,CXHighLow,CXHighHigh, AL,AH,AXHighLow,AXHighHigh:Byte) End; {$else} {RealMode} TTregisters= Record case Integer of 0: (AX, BX, CX, DX, BP, SI, DI, DS, ES, Flags: Word); 1: (AL, AH, BL, BH, CL, CH, DL, DH: Byte); end; {$endif} TPtrRec=record Ofs,Seg:word end; TintrBuffer=array[1..576] of byte; TPintrBuffer=^TintrBuffer; TVLMheader=record unknown1 :array[1..4] of byte; ptr1ofs,ptr1seg, { pointers to 'procedures' } ptr2ofs,ptr2seg, ptr3ofs,ptr3seg, ptr4ofs,ptr4seg :word; unknown2 :array[1..4] of byte; { 00 00 00 00 } HeaderLen :byte; { 1.11-> 4E; 1.20-> 4E} MultiplexIDstring :array[1..3] of char; { 56 4C 4D 'VLM' } unknown3 :array[1..4] of byte; { 01 00 80 00 } TransientSwitchCount :word; CallCount :word; ControlBlockOfs :word; { in same segment as this header } CurrentVLMID :word; MemoryType :byte; { 04 = XMS } ModulesLoaded :byte; BlockId :word; TransientBlock :word; GlobalSegment :word; AsyncQueue :array[1..3] of record { head, tail, s } pqofs,pqseg:word; end; BusyQueue :array[1..3] of record { head, tail, s } pqofs,pqseg:word; end; ReEntranceLevel :word; FullMapCount :word; unknown5 :word; { 00 00 } end; TVLMcontrolBlockEntry=record Flag :word; ID :word; Func :word; Maps :word; TimesCalled :word; unknown1 :word; { SSeg ? } TransientSeg,GlobalSeg :word; AddressLow,AddressHi :word; TsegSize,GSegSize,SSegSize:word; { in 16 byte paragraphs } VLMname :array[1..9] of char; { null terminated string } end; Var GlobalReqBuf,GlobalReplyBuf:TPintrBuffer; { real-mode only, DPMI: all flags are set to false } VLM_EXE_loaded :Boolean; NETX_VLM_loaded:Boolean; { if true, then VLM_EXE_loaded must also be true. } NETX_EXE_loaded:Boolean; Function RealModeIntr(intNo:byte;Var regs:TTregisters):boolean; Procedure F2SystemCall(subf:byte;req_size,rep_size:word;Var result:word); Procedure nwMsDos(VAR R:ttregisters); Function InRealMode:Boolean; Function MapRealmodeSegment(RSeg:Word):Word; Function nwPtr(s,o:word):Pointer; Procedure GetGlobalBufferAddress(VAR Sreq,Oreq,Srep,Orep:Word); {$IFDEF RealMode} Function GetVLMheader(Var VLMheader:TVLMheader):Boolean; Function GetVLMControlBlock(Entry:Byte; Var ControlBlock:TVLMControlBlockEntry):Boolean; { entry: 0 .. VLMheader.ModulesLoaded } {$ENDIF} IMPLEMENTATION {===========================================================} Var GlobalRegisters:TTregisters; { all Modes ! } VLMCall:Procedure; {$IFDEF RealMode} Var VLMtransientSeg:word; { ---------- Real mode procedures ------------------------------------} {$F+} Var RequesterProc:Procedure(Var regs:Registers); { VLMCall:Procedure; } Procedure VlmSystemCall(Var regs:registers); assembler; asm push ds { check if VLMCall known. If not, return error $FF in fake AL } xor ah,ah mov al,$FF les di,VLMCall mov bx,es cmp bx,$0000 je @1 { move fake regs registers to 'real' registers } { AX, CX, DX, DS, SI, DI, ES only. } les di,regs mov ax,es:[di+16] push ax { push new es } mov ax,es:[di+12] push ax { push new di } mov ds,es:[di+14] mov ax,es:[di] mov cx,es:[di+4] mov dx,es:[di+6] mov si,es:[di+10] pop di pop es { farr call to VLM handler } push bp CALL VLMCall pop bp @1: { move 'real' registers to fake regs registers } {push es push di} les di,regs mov es:[di],ax {mov es:[di+4],cx mov es:[di+6],dx mov es:[di+10],si pop ax ax:= 'di' mov es:[di+12],ax pop ax ax:= 'es' mov es:[di+16],ax } pop ds end; Procedure VLMcheck; CONST DOS_MULTIPLEX =$2F; Var regs:registers; ccode:byte; Function getBinderyAccessLevel:boolean; { to be replaced by a non-bindery call } Type Treq=record len :word; subF :byte; end; Trep=record accLeveL:byte; _objId:longInt; fill:array[1..20] of byte; end; TPreq=^Treq; TPrep=^Trep; Var result:word; BEGIN With TPreq(GlobalReqBuf)^ do begin subF:=$46; len:=sizeOf(Treq)-2; end; F2SystemCall($17,sizeOf(Treq),sizeOf(Trep),result); GetBinderyAccessLevel:=(result=0); end; Var phdr:^TVLMHeader; pVLMcbl:^TVLMcontrolBlockEntry; t:word; begin VLM_EXE_Loaded:=false; Regs.AX:=$7A20; Regs.BX:=$0000; Regs.CX:=$0000; Intr($2F,Regs); if regs.AX=$0000 then begin { OK. AX=0000. All seems well. But is it really the 2F VLM handler? } phdr:=ptr(regs.es,$0000); VLM_EXE_Loaded:=(phdr^.MultiplexIdString[1]='V') and (phdr^.MultiplexIdString[2]='L') and (phdr^.MultiplexIdString[3]='M'); IF VLM_EXE_Loaded then begin NETX_EXE_loaded:=False; { Determine whether netx.vlm is loaded } NETX_VLM_Loaded:=False; t:=0; While t$0000 then begin p:=ptr(VLMtransientSeg,$0000); move(p^,VLMheader,SizeOf(TVLMHeader)); end; GetVLMHeader:=(VLMtransientSeg<>$0000); end; Function GetVLMControlBlock(Entry:Byte; Var ControlBlock:TVLMControlBlockEntry):Boolean; { entry: 0 .. VLMheader.ModulesLoaded } Var ph:^TVLMHeader; pcb:^TVLMControlBlockEntry; begin if VLMtransientSeg<>$0000 then begin ph:=ptr(VLMtransientSeg,$0000); pcb:=ptr(VLMtransientSeg,ph^.ControlBlockOfs+entry*SizeOf(TVLMControlBlockEntry)); move(pcb^,ControlBlock,SizeOf(TVLMControlBlockEntry)); end; GetVLMControlBlock:=(VLMtransientSeg<>$0000); end; Function nwPtr(s,o:word):Pointer; begin nwPtr:=Ptr(s,o); end; Function MapRealmodeSegment(RSeg:Word):Word; begin MapRealmodeSegment:=RSeg; end; {$ENDIF} {------------- end of real-mode procedures -------------------} {$IFDEF ProtMode} Type pRealSegItem=^tRealSegItem; tRealSegItem=record {structure to store information} Seg:word; {about allocated selectors} Sel:Word; prev,next:pRealSegItem; end; {we need to allocate selectors which map real-mode segments.} {all these selectors are stored in an dynamic list} {and are cleand up them at then end of the program} Var GlobalRealReqSeg, GlobalRealReplySeg:Word; SelectorList:pRealSegItem; Function RealModeIntr (IntNo:Byte;VAR Regs:ttregisters):Boolean;Assembler; {Simulate a call to the spectified real mode interrupt. The registers passed to the real mode code are held in RealModeRegisters. This structure contains the register content upon termination of the real mode ISR. Returns False if there was an error.} ASM push di push es mov bh,00 {For DOSX to reset the int controller and A20 line. Windows ingores it.} mov bl,IntNo {Tell DPMI which interrupt to simulate} xor cx,cx {0 bytes to copy to real mode stack} les di,Regs {Get the real mode structure} mov word ptr es:[di+$c],0 {reserved to 0} mov word ptr es:[di+$c+2],0 mov word ptr es:[di+$26],0 {fs to 0} mov word ptr es:[di+$28],0 {gs to 0} mov word ptr es:[di+$2e],0 {sp to 0} mov word ptr es:[di+$30],0 {ss to 0} mov ax,$0300 {Function 0300h is simulate real mode interrupt} int 31h jc @Error {The carry flag was set, so there was an error} mov ax,True {Return no error} jmp @AllDone @Error: mov ax,False {Return false indicating an error} @AllDone: pop es pop di End; Procedure F2SystemCall(subf:byte;req_size,rep_size:word;Var result:word); begin With GlobalRegisters do begin CX := Req_size; DX := rep_size; AH := $f2; AL := subf; DS := GlobalRealReqSeg; {Use then REAL-MODE segments} ES := GlobalRealReplySeg; {of the global buffers} DI := 0; {OFFSET always 0 for} SI := 0; {'GlobalDosAlloc'ated memory} if not RealModeIntr($21,GlobalRegisters) then RUNERROR(217); {DPMI-ERRORS, maybe we should stop the system with the new Errorcode 217} Result:=al; end; end; Procedure nwMsDos(VAR R:ttregisters); begin if not RealModeIntr($21,R) then RUNERROR(217); {DPMI-ERRORS, maybe we should stop then system with the new Errorcode 217} end; Procedure GetGlobalBufferAddress(VAR Sreq,Oreq,Srep,Orep:Word); begin Sreq := GlobalRealReqSeg; {Use the REAL-MODE segments} Srep := GlobalRealReplySeg; {of the global buffers} Oreq := 0; {OFFSET always 0 for} Orep := 0; {'GlobalDosAlloc'ated memory} end; {----- Some low-level functions for DPMI -----------} TYPE os = record o, s : Word; end; {for typecasts} LDTStr = record {Structure of LDT-Elements} limit : Word; base : Word; data : Array[0..1] of Word; end; Procedure Halt218; {runError 218: low-level DPMI-Errors} begin RunError(218); end; {DMPI-Function 0: Allocate LDT Descriptor} function AllocLDTD(var NEWD : Word) : Word; Assembler; asm xor ax,ax mov cx,1 {only 1 descriptor needed} int 31h {Call DPMI} jnc @@ok Call Halt218 {Error on carry} @@ok: les di,NEWD {save descriptor to VAR NEWD} mov es:[di],ax xor ax,ax end; {DMPI-Function 1: Free LDT Descriptor} function FreeLDTD(D : Word) : Word; Assembler; asm mov ax,0001h mov bx,D int 31h jc @@Ex {carry: return Error in ax} xor ax,ax @@Ex: end; {DMPI-Function 7: Set Segment Base Address} function SetSBA(S: Word; BA: LongInt) : Word; Assembler; asm mov ax,0007h mov bx,S mov cx,word ptr BA+2 mov dx,word ptr BA int 31h jc @@Ex {carry: return Error in ax} xor ax,ax @@Ex: end; {DMPI-Function 8: Set Segment Limit} function SetSL(S: Word; L: LongInt) : Word; Assembler; asm mov ax,0008h mov bx,S mov dx,word ptr L mov cx,word ptr L+2 int 31h jc @@Ex {carry: return Error in ax} xor ax,ax @@Ex: end; {DMPI-Function 9: Set Descriptor Access Rights} function SetDAS(S: Word; R: Word) : Word; Assembler; asm mov ax,0009h mov bx,S mov cx,R int 31h jc @@Ex {carry: return Error in ax} xor ax,ax @@Ex: end; {DMPI-Function 11: Get Descriptor} function GetD(S: Word; var D : LDTStr) : Word; Assembler; asm mov ax,000Bh mov bx,S les di,D int 31h jc @@Ex {carry: return Error in ax} xor ax,ax @@Ex: end; {Set then Length of the Descriptor-Segment} function SetLimit(Sele: Word; L: LongInt) : Word; var St,R: Word; Des : LDTStr; begin St:= GetD(Sele, Des); {get the Descriptor-Entry from LDT} if St <> 0 then begin SetLimit:= St; {not in LDT, return Error} Exit; end; with Des do R := (Data[0] shr 8) or ((Data[1] and $00F0) shl 8); {form then rights for the DPMI-9-Call, register cl} if L > $FFFFF then begin {> 1MB: Page aligned} if L and $FFF <> $FFF then begin {Limit=Length-1!} SetLimit := $8021; {return Error: not page aligned} Exit; end; R:= R or $8000; {set Page granularity} end else R:= R and $7FFF; {set Byte granularity} St := SetSL(Sele, 0); {fist set limit to 0} if St = 0 then St := SetDAS(Sele, R); {ok, set the new rights} if St = 0 then St:= SetSL(Sele, L); {ok, set then limit} SetLimit := St; {return errorcode} end; {get a Selector for a part of then real-mode memory} function RealMemSel(RealP : Pointer; Limit : LongInt; var Sele : Word) : Word; function NP(P : Pointer) : LongInt; VAR TC:OS absolute P; begin NP := (LongInt(TC.S) shl 4)+LongInt(TC.O); end; var St : Word; begin St := AllocLDTD(Sele); {get a new Selector} if St = 0 then begin St := SetSBA(Sele, NP(RealP)); {set base addresse to the linear} if St = 0 then begin {address of the Real-Segment} St := SetLimit(Sele, Limit); {set the selector-limit} if St <> 0 then if FreeLDTD(Sele)<>0 then; {on error: free selector} end else if FreeLDTD(Sele)<>0 then; {on error: free selector} end; RealMemSel := St; {return errorcode} end; {check if the required selector is already allocated} Function InSelectorList(S:Word):pRealSegItem; VAR li:pRealSegItem; begin li:=SelectorList; while li<>NIL do begin if li^.Seg=S then begin InSelectorList:=Li; exit; end; li:=li^.Next; end; InSelectorList:=NIL; end; {insert a new SelectorItem at start of the list} Procedure AddToSelectorlist(Segment,Selector:Word); VAR li:pRealSegItem; begin new(li); with li^ do begin Seg:=segment; Sel:=Selector; next:=Selectorlist; prev:=NIL; end; Selectorlist^.prev:=li; Selectorlist:=li; end; {clean up} Procedure FreeSelectorList; VAR li:pRealSegItem; begin while Selectorlist<>NIL do begin li:=selectorlist; selectorlist:=li^.next; if li^.sel<>0 then FreeLDTD(li^.Sel); dispose(li); end; end; Function MapRealmodeSegment(RSeg:Word):Word; VAR sel:Word; li:pRealSegItem; begin li:=InSelectorList(RSeg); if li=NIL then begin if RealMemSel(Ptr(RSeg,0),$ffff,Sel)<>0 then RUNERROR(217); {something's wrong: Errorcode 217} MapRealModeSegment:=Sel; AddToSelectorList(Rseg,Sel); end else MapRealModeSegment:=li^.Sel; end; Function nwPtr(s,o:word):Pointer; begin nwPtr:=Ptr(MapRealModeSegment(s),o); end; {$ENDIF} {----------------- end of protected mode procedures -------------} Var OldExitProc:pointer; Function InRealMode:Boolean; begin {$IFDEF Windows} InRealMode:=(GetWinFlags and wf_PMode)=0; {$ELSE} {$IFDEF ProtMode} InRealMode:=False; {$ELSE} InRealMode:=True; {$ENDIF} {$ENDIF} end; {$F+} Procedure IntrExit; begin ExitProc:=OldExitProc; {$IFDEF ProtMode} if GlobalDosFree(Seg(GlobalReqBuf^))<>0 then; {ignore Errors} if GlobalDosFree(Seg(GlobalReplyBuf^))<>0 then; FreeSelectorList; {$ELSE} {RealMode} FreeMem(GlobalReqBuf,SizeOf(TintrBuffer)); Freemem(GlobalReplyBuf,Sizeof(TintrBuffer)); {$ENDIF} end; {$F-} {$IFDEF ProtMode} VAR w1:Longint absolute GlobalRegisters; { we only need w1 during the initialisation, so we use the static var GlobalRegisters to save 4 bytes of memory :-) } {$ENDIF} begin VLM_EXE_Loaded:=false; NETX_EXE_loaded:=false; NETX_VLM_loaded:=false; {$IFDEF ProtMode} new(SelectorList); fillchar(Selectorlist^,Sizeof(Selectorlist^),0); w1:=GlobalDosAlloc(Sizeof(tIntrBuffer)); {alloc REQ-Buffer} if w1=0 then runerror(217); {DPMI-ERROR, no free Memory} GlobalReqBuf:=Ptr(loWord(w1),0); {buffer-address for protected Mode} GlobalRealReqSeg:=hiWord(w1); {REAL-Mode-Segment of the buffer-address} w1:=GlobalDosAlloc(Sizeof(tIntrBuffer)); {alloc REPLY-Buffer} if w1=0 then runerror(217); GlobalReplyBuf:=Ptr(loWord(w1),0); GlobalRealReplySeg:=hiWord(w1); {$else} {RealMode} new(GlobalReqBuf); if GlobalReqBuf=NIL then RunError(203); {where has all the memory gone?? /Heap-Overflow} new(GlobalReplyBuf); if GlobalReplyBuf=NIL then RunError(203); VLMtransientSeg:=$0000; VLMcheck; {$endif} OldExitProc:=ExitProc; ExitProc:=@IntrExit; end.