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

1607 lines
41 KiB
NASM

PAGE ,132
TITLE DXVCPIBT.ASM -- Dos Extender VCPI Support Code (initialization)
; Copyright (c) Microsoft Corporation 1990-1991. All Rights Reserved.
;*** dxvcpibt.asm - vcpi detection/setup code (discardable)
;
; Copyright <C> 1990, Microsoft Corporation
;
; Purpose:
;
; Revision History:
;
;
; 08-07-90 earleh rearranged memory map to allow building full-sized
; Pmode data structures in v86 mode, allow booting to Pmode
; without a LIM 3.2 page frame, fixed up detection code to
; work with various strange interpretations of LIM 4.0 by
; Limulator vendors. Allow use of entire linear space below
; 16 Meg by DOSX and VCPI server.
;
; 05/07/90 jimmat Started incorporating VCPI changes from languages group.
;
; [] 20-Feb-1990 Dans Created
;
;
; note that this should only be called on a 386, except
; CheckForVCPI, which can be called from 286 machines.
;
; this code is for the real mode portion of the dos extender and
; is released back to DOS prior to switching to protect mode the
; first time. NO DATA defined here in either segment as it will
; be discarded.
;
;************************************************************************/
.286p
; -------------------------------------------------------
; INCLUDE FILE DEFINITIONS
; -------------------------------------------------------
.xlist
.sall
include segdefs.inc
include gendefs.inc
include pmdefs.inc
.list
; This entire file is only for VCPI support
if VCPI
.xlist
.sall
include dxvcpi.inc
.list
;
; data
;
DXDATA segment
;
; Externs
;
extrn idCpuType:word
extrn segBootPmode:word
extrn pPteFirst:dword
extrn cPteMax:dword
extrn bpdxsyspages:dword
extrn iddxsystype:byte
extrn hmem_System_Block:word
extrn fEMS:byte
extrn fVCPI:byte
extrn cFreePages:dword
extrn bpGDT:fword
extrn bpGDTbase:dword
extrn bpGDTcb:word
extrn bpIDT:fword
extrn bpIDTbase:dword
extrn bpIDTcb:word
extrn selGDT:word
extrn segGDT:word
extrn selIDT:word
extrn segIDT:word
extrn cdscGDTMax:word
extrn cdscIDTMax:word
extrn segPSP:word
extrn selPSP:word
extrn selGDTFree:word
extrn sysTSS:WORD
ifdef NOT_NTVDM_NOT
extrn fHPVectra:BYTE
endif
extrn lpfnXMSFunc:DWORD
extrn rgbXfrBuf1:BYTE
DMAServiceSegment equ 040h ;40:7B bit 5 indicates DMA services
DMAServiceByte equ 07Bh ; are currently required
DMAServiceBit equ 020h
extrn bDMAServiceBit:BYTE
if DEBUG
extrn fTraceBug:WORD
ifdef CV_TSS
extrn FaultStack:word
endif
endif
EXTRN hmem_XMS_Table:WORD
EXTRN hmem_XMS_Count:WORD
extrn fDebug:BYTE
DXDATA ends
DXSTACK segment
extrn rgw0Stack:WORD
DXSTACK ends
DXCODE segment
extrn CodeEnd:NEAR
extrn CallVCPI:NEAR
extrn ResetVCPI:NEAR
extrn segDXCode:WORD
DXCODE ends
DXPMCODE segment
;
; Externs
;
extrn fnVCPIPMoff:dword
extrn CodeEndPM:near
extrn PMIntr31:NEAR
extrn PMIntr28:NEAR
extrn PMIntr25:NEAR
extrn PMIntr26:NEAR
extrn PMIntr4B:NEAR
extrn PMIntr70:NEAR
extrn PMIntrDos:NEAR
extrn PMIntrMisc:NEAR
extrn PMIntrVideo:NEAR
extrn PMIntrMouse:NEAR
extrn PMIntrIgnore:NEAR
extrn PMInt2FHandler:NEAR
extrn PMIntrEntryVector:NEAR
extrn PMFaultEntryVector:NEAR
extrn HPxBIOS:NEAR
extrn PMFaultReflectorIRET:FAR
if DEBUG
extrn PMDebugInt:NEAR
endif
externFP NSetSegmentDscr
DXPMCODE ends
DXCODE segment
assume cs:DXCODE, ds:DXDATA, es:nothing
extrn WdebVCPI:BYTE
;
; Definitions
;
rgbEMMDrv db 'EMMXXXX0',0 ; emm device driver name
rbgQEMMDrv db 'EMMQXXX0',0 ; QEMM v. 5.0 with "FRAME=NONE"
DebOut_Int equ 41h ;Wdeb386 pMode interface Interrupt
DS_VCPI_Notify equ 005Bh ;Notify Wdeb386 of VCPI interface
extrn ER_QEMM386:BYTE
ERC_QEMM386 equ offset ER_QEMM386
extrn DisplayErrorMsg:FAR
;
; Externs
;
extrn fnVCPIoff:dword
extrn V86ToPm:word
extrn laVTP:dword
;extrn FreeEMSHandle:proc
externNP FreeEMSHandle
extrn SetupHimemDriver:proc
;*** QEMM386Trap
;
; Purpose: A device driver or TSR has just failed the Windows INT 2Fh
; AX = 1605H startup broadcast. Version 5.1 and 5.11 of
; QEMM386 will do this for any version of DOSX which it
; doesn't know how to patch to work with VCPI. Since we
; do know how to work with VCPI now, QEMM386 is behaving in
; an inappropriate manner. Furthermore, QEMM386 is required
; to output an informative message if it does this, and it
; does not.
;
; This routine will ask the user for permission
; to proceed.
;
; Uses:
;
; Input: A process has told DOSX not to load by returning
; non-zero in CX on an INT 2Fh, AX=1605h.
;
; Output: AX = Zero, proceed.
; AX = Non-zero, abort.
;
;************************************************************************/
cProc QEMM386Trap,<NEAR,PUBLIC>,<ds,es>
cBegin
mov dx,cs ;pass msg ptr in DX:AX
mov ax,ERC_QEMM386
call DisplayErrorMsg ; Tell the user something
mov ah, 8 ; Get a character from the keyboard
int 21h
or al,20h ; Force lower case.
cmp al,'y' ; Party if it's a 'y'.
mov ax,0
jne IQ_Abort
jmp IQ_Exit
IQ_Abort:
dec ax
IQ_Exit:
IQ_Not_Open:
or ax,ax
cEnd
;*** CheckForEMS
;
; Purpose: see if an ems driver is loaded
;
; Register
; Usage: ax, cx
;
; Input: ES:BX contains int 67h vector.
;
; Output: none
;
; Returns: carry not set if ems is loaded, set if not
;
; Exceptions: none
;
; Notes: This checks for a LIM 4.0 driver. It also checks
; for a driver named 'EMMQXXX0' which could be QEMM 5.0
; without a page frame.
;
;************************************************************************/
cProc CheckForEMS,<NEAR,PUBLIC>,<di,si,ds>
cBegin
mov di, 0ah ; es:di has string to look at
mov ax, cs
mov ds, ax
assume ds:DXCODE,ss:DGROUP
mov si, offset DXCODE:rgbEMMDrv ; ds:si has driver string
mov cx, CBEMMSTR ; cx has rep count
repe cmpsb
jne short CheckFor_QEMM
; try QEMM driver with
; non-standard name for
; "FRAME=NONE"
emscall GETEMSVER
or ah, ah
jz @F
xor ax, ax
@@:
cmp al, 40h ; LIM 4.0?
jb Failure_Exit ; No, can't use it.
inc fEMS ; set flag
clc ; return success
jmp CheckForEMS_ret
CheckFor_QEMM: ; look for QEMM signature
mov di, 0ah ; es:di has string to look at
mov si, offset DXCODE:rbgQEMMDrv ; ds:si has driver string
mov cx, CBEMMSTR ; cx has rep count
repe cmpsb
jne short Failure_Exit ; if not equal, exit
inc fEMS ; set flag
clc
jmp CheckForEMS_ret
Failure_Exit:
stc ; zero out return value first
CheckForEMS_ret:
cEnd
assume ds:DGROUP
;*** CheckForVCPI
;
; Purpose: to see if a vcpi server is loaded
;
; Register
; Usage: ax, bx, cx
;
; Input: none
;
; Output: none
;
; Returns: ax = vcpi version if it is loaded, 0 otherwise
;
; Exceptions: none
;
; Notes: this calls CheckForEMS first, and EMSVer as well
;
;************************************************************************/
cProc CheckForVCPI,<NEAR,PUBLIC>,<es>
cBegin
cmp idCpuType, 3 ; must be 386 or better to have
jnb @F
jmp novcpi286 ; vcpi server running.
@@:
.386
mov ax, 03500h+EMS_INT ; ah = get interrupt, al = vector to get
int 021h ; returns with es:bx == int vector
mov ax,es ; int vector set?
or ax,bx
jz novcpi ; No.
call CheckForEMS
jc novcpi ; no ems, can't be any vcpi
call SwitchToV86 ; force v86 mode
or al, al
jz novcpi ; failed to allocate, out of here
RMvcpi vcpiVER ; see if vcpi is available
or ah, ah
jnz novcpi
inc fVCPI ; set flag
jc novcpi
movzx eax,segBootPmode
shl eax,4
add eax,GDTOFF
shr eax,4
mov selGDT,ax ; save it for later
mov segGDT,ax ; save it for later
; If this is changed, then GetVCPIInterface must be changed
; to save correct value in pPteFirst.
.ERRE VCPIPTOFF EQ 0
cCall GetVCPIInterface,<segBootPmode,VCPIPTOFF,ax,SEL_VCPI>
call SetupPageTables ;
jc novcpi ; had a problem setting up page table
;
; Load the VDS-implemented bit from the BIOS data area.
;
mov ax,DMAServiceSegment
mov es,ax
mov al,byte ptr es:[DMAServiceByte]
and al,DMAServiceBit
mov [bDMAServiceBit],al
RMvcpi vcpiVER ; refetch VCPI version
mov ax,bx ; put it in AX
jmp CheckForVCPI_exit ; return no error
.286p
novcpi:
call FreeEMSHandle ; free the handle we allocated.
novcpi286: ; (Free routine uses 386 instructions.)
xor ax, ax ; none loaded
CheckForVCPI_exit:
cEnd
DXCODE ends
;**************************************************************
; 386 only code from here on down!!!
;**************************************************************
.386p
include prot386.inc
DXCODE segment
assume cs:DXCODE, ds:DXDATA, es:nothing
;*** SwitchToV86
;
; Purpose: Switch to v86 mode in preparation for vcpi calls
;
; Register
; Usage: eax, bx, ecx, es
;
; Input: none
;
; Output: hEMM is updated, pages are mapped into ems frame
; segBootPmode setup
;
; Returns: al == 1 if successful, al == 0 otherwise
;
; Exceptions: none
;
; Notes: By allocating an ems handle/page, we force the lim 4.0
; emulator to switch from real mode to v86 mode. Do not free
; the ems memory until exit time.
;
;************************************************************************/
cProc SwitchToV86,<NEAR,PUBLIC>,<es,si,edi,cx>
cBegin
;
smsw ax
test al,01h ; In V86 mode?
jz stv_badexit ; We don't know how to turn on
; a VCPI server without allocating
; EMS memory.
mov cx,(offset CodeEnd) + CBPAGE386 - 1
shr cx,4
mov ax,segDXCode
add ax,cx
and ax,0FF00H ; page align DOS block
mov segBootPmode,ax
mov es,ax ; zero it out
mov al,1
jmp stvExit
stv_badexit:
call FreeEMSHandle
xor al, al
stvExit:
cEnd
;*** GetVCPIInterface
;
; Purpose: To retrieve the vcpi protect mode interface data
;
; Register
; Usage: es, ax, edx, ebx
;
; Input: lpPT: far ptr to a page table
; lprggdte: far ptr to a range of 3 gdt entries.
;
; Output: fnVCPIoff updated with 32-bit offset of pm entry point
; for vcpi calls
; *lpPT updated with 4k vcpi 0th page table
; *lprggdte updated with vcpi server code segment, extras
;
;
; Returns: nothing
;
; Exceptions:
;
; Notes: only call in real mode prior to relocation of DosExtender,
; as it sets up a variable in DXPMCODE that needs to be
; copied up when relocated.
;
;************************************************************************/
cProc GetVCPIInterface,<NEAR,PUBLIC>,<es>
parmD lpPT
parmD lprggdte
cBegin
push ds ; save ds
les di, lpPT ; es:di -> vcpi's page table
lds si, lprggdte ; ds:si -> vcpi's gdt entries
assume ds:nothing
RMvcpi vcpiPMINTERFACE
mov word ptr pPteFirst,di
mov ax, CBPAGE386 ; AX = size of page table
sub ax, di ; AX = #bytes left over in zeroth PT
shr ax, 2 ; AX = #PTEs left over
add ax, DXPTMAX shr 2 ; AX = total # user PTEs available,
mov word ptr cPteMax, ax ; counting those in zeroth PT
pop ds ; restore ds
assume ds:DXDATA
mov fnVCPIoff, ebx
mov ax, seg DXPMCODE
mov es, ax
assume es:DXPMCODE
mov fnVCPIPMoff, ebx ; set pm call
;
; Refer to VCPI spec. Version 1.0 for why this call sequence is used to
; find the number of VCPI pages we may allocate.
;
emscall GETNUMOFPAGES
movzx ebx,bx ; BX = unallocated EMS pages
shl ebx, 2 ; EBX = unallocated VCPI pages
mov cFreePages, ebx ; Never allocate more than this amt.
cEnd
assume es:nothing
;*****************************************************************************
; Memory map of system tables buffer prior to switch to protected
; mode under vcpi.
;
; __+-------------------------------+->USERPTOFF
; __+-------------------------------+->LDTTOP
; ~100k | |
; | |
; | LDT, 64k (8190 entries) |
; | |
; | |
; |_______________________________|->
; | |
; | |
; | |
; | DXPMCODE (~18.5k) |
; | Exact size may be found from |
; | codeendPM symbol. |
; | |
; __|_______________________________|
; | |->DXPMCODEOFF (GDTTOP)
; | GDT |
; __|_______________________________|
; | |->GDTOFF
; | TSS space (2 of them for 386) |
; 18k __|_______________________________|
; | IDT, 2k |->TSSOFF
; 16k __|_______________________________|
; | |->IDTOFF
; | |
; | page directory (DXPD) |
; 12k __|_______________________________|
; | |->DXPDOFF
; | DOSX Pmode page table (DXPT2) |
; | (DXLINEARBASE) |
; 8k __|_______________________________|
; | |->DXPTSYSOFF
; | 1st user page table (DXPT1) |
; | (4-8 Meg) |
; 4k __|_______________________________|
; | |->DXPT1OFF = DXTEMPPTOFF
; | 0th page table (VCPIPT) |
; | (0-4 Meg) |
; 0k __|_______________________________|->VCPIPTOFF
; | Wasted for page alignment |
; CodeEnd --->|-------------------------------|
; Label
;
; System data structures and DOSX Pmode code will be located at linear
; addresses pointed to by the page table which begins at DXPTSYSOFF.
; During the first switch to Pmode, these addresses will exist in a
; block of conventional memory allocated from DOS. After the first
; switch, a block of extended memory allocated from VCPI will be used.
; The user page table starting at DXTEMPPTOFF will be used to access the
; second block of memory for initialization purposes.
;
; Note that the first page, which contains the zeroth page table, shared
; with the VCPI server, must use the same physical page of memory before
; and after the switch.
;
; Terms:
; DXPMCODE Dos eXtender Protect Mode CODE
; IDT Interrupt Descriptor Table
; GDT Global Descriptor Table
; DXPTx Dos eXtender Page Table number x
; VCPIPT VCPI Pate Table (we can't touch this one)
; DXPD Dos eXtender Page Directory
;
;*****************************************************************************
;*** SetupPageTables
;
; Purpose: To set up page tables and directory in ems
; frame (as described above)
;
; Register
; Usage: eax, bx, cx, edx, esi, edi
;
; Input: none
;
; Output: DXPD, DXPTx, GDT setup.
;
; Returns: cy set if error
;
; Exceptions:
;
; Notes:
;
;************************************************************************/
cProc SetupPageTables,<NEAR,PUBLIC>,<ds,es>
cBegin
mov cdscGDTMax,8190 ; max out the GDT size
mov bx, segBootPmode
mov es, bx
mov di,DXBOOTPTOFF
shr bx, 8 ; convert to 16-bit page aligned
;
; Our extended memory stuff is located in a single contiguous
; block of DOS memory.
;
mov ecx, DXPMPAGES
physaddrloop:
xchg cx, bx ; put page number in cx, save count
; in bx
RMvcpi vcpiPHYSADDRPAGE ; get the physical address
or ah, ah
jnz badexit
and dx, 0f000h ; mask off 12 lsb's
or dx, NEWPTEMASK ; store with decent page flags
mov es:[ di ], edx ; store physical in DXPT1
add di, 4 ; advance to next
xchg cx, bx ; get loop counter in cx,
inc bx ; advance to next page in ems frame
loop physaddrloop
pagesmapped:
;
; Save away the page directory base for cr3 loading
;
mov eax, es:[DXBOOTPTOFF + (DXPDOFF shr 10)]
; Save the pte (physical addr) of
and ax, 0f000h ; page directory, mask 12 lsb's
mov V86ToPm.zaCr3VTP, eax ; store it
;
; Save the linear address of bpGDT and bpIDT in V86ToPm as well
;
xor eax, eax
mov ax, DXDATA ; dgroup segment, masked hi word
shl eax, 4 ; linearize it
mov ebx, eax ; save it
add eax, offset DGROUP:bpGDT; add in offset
mov V86ToPm.laGdtrVTP, eax ; save it in vcpi v86 to pm structure
mov eax, ebx ; restore linear dgroup
add eax, offset DGROUP:bpIDT; add in offset
mov V86ToPm.laIdtrVTP, eax ; save it in vcpi v86 to pm structure
xor eax, eax
mov ax, cs ; get current code segment
shl eax, 4 ; linear dxcode
add eax, offset DXCODE:V86ToPm ; add in offset of v86 to pm structure
mov laVTP, eax ; save linear ptr of v86 to pm structure
;
; set up the page directory (DXPD)
; with 0-x to be VCPIPT thru DXPTx (x+1 total)
;
; (copy them out of dxpt1, starting with the 2nd entry)
;
mov edi, DXPDOFF
push ds
push es ; make ds point to emspageframe
pop ds ; as well
assume ds:nothing
mov esi, DXBOOTPTOFF + (VCPIPTOFF shr 10) ; point to 2nd entry in DXPT1
; (must be VCPIPT)
mov ecx, CPTDX + 1 ; # of user pt's + vcpi's pt
rep movsd
; point DXLINEARBASE at system area
mov esi,DXBOOTPTOFF + (DXBOOTPTOFF shr 10)
mov edi,DXPDOFF + (DXLINEARBASE shr 20)
movsd
pop es
assume es:DGROUP
;
; Allocate a block of VCPI 4k pages, enough to copy our system tables
; and Pmode code into once we have achieved protected mode operation.
; Map these pages into one of the page tables that is unused until
; heap initialization time.
;
; If there is insufficient VCPI memory to complete this operation, then
; we try to grab XMS memory instead.
;
lea di,bpdxsyspages
cmp cFreePages, DXPMPAGES-1
jc tryXMSServer ; insufficient VCPI pages to
; build this block
mov cx,DXPMPAGES-1
allocate_syspage_from_VCPI:
RMvcpi vcpiALLOCPAGE
or ah,ah
jnz badexit
mov eax,edx
stosd
loop allocate_syspage_from_VCPI
sub cFreePages, DXPMPAGES-1
mov iddxsystype,DXINVCPI
;
; Map these pages into a temporary area, which we use later to move most
; of the stuff in this DOS block up into extended memory.
;
push es
pop ds
assume es:nothing,ds:DGROUP
mov ax, segBootPmode
mov es, ax
lea si,bpdxsyspages
mov cx,DXPMPAGES-1
mov di,DXTEMPPTOFF
mov eax,es:[DXBOOTPTOFF + (VCPIPTOFF shr 10)]
stosd
copy_syspage:
lodsd
or eax,NEWPTEMASK
stosd
loop copy_syspage
jmp goodexit
tryXMSServer:
assume es:nothing,ds:DGROUP
push es
pop ds
call SetupHimemDriver ; Need XMS driver now.
mov dx,DXPMPAGES shl 2 ; kbytes = pages times 4
xmssvc 09h ; allocate an XMS block
cmp ax,1 ; got a block?
jne goodexit ; no, try using DOS mem.
mov hmem_System_Block,dx
xmssvc 0ch ; lock the block
cmp ax,1 ; did it work?
je @F
mov dx,hmem_System_Block ; No, free it up.
mov hmem_System_Block,0
xmssvc 0ah
jmp goodexit ; and try to run in DOS mem
@@:
shl edx,10h ; DX:BX = locked block address
mov dx,bx ; EDX = locked block address
;
; Make sure the locked XMS block is page aligned. If is not, then we
; will have to adjust the bottom address used and shrink the GDT by a
; full 386 page.
;
test dx,MASK allbitsPTE
jz XMSpagealigned
and dx,MASK pfaPTE
sub cdscGDTMax,CBPAGE386 / 8 ; shrink the GDT size
XMSpagealigned:
mov ax, segBootPmode
mov es, ax
mov di,DXTEMPPTOFF
mov eax,es:[DXBOOTPTOFF + (VCPIPTOFF shr 10)]
stosd
mov cx,DXPMPAGES-1
mov eax,edx
or eax,NEWPTEMASK
insertXMSpage:
stosd
add eax,CBPAGE386
loop insertXMSpage
mov iddxsystype,DXINXMS
goodexit:
clc
jmp exit
badexit:
stc
exit:
cEnd
assume ds:DXDATA
;*** InitGDTVCPI - initialize the gdt when running under vcpi
;
; Purpose:
;
; Register
; Usage: uses eax, all others preserved
;
; Input: none
;
; Output: gdt/ldt is initialized
;
; Returns: returns carry set if failure to map EMS pages
;
; Exceptions:
;
; Notes:
;
;************************************************************************/
assume ds:DGROUP,es:DGROUP,ss:NOTHING
public InitGDTVCPI
cProc InitGDTVCPI,<NEAR,PUBLIC>,<ebx,ecx,edx,di,es>
cBegin
mov edx,LADXGDTBASE ; set gdt linear address
mov bpGDTbase,edx
mov ecx,GDT_SIZE - 1 ; set the GDT segment size
mov bpGDTcb,cx
mov bh, STD_DATA ; make it be a data segment
mov ax, SEL_GDT
cCall NSetSegmentDscr,<SEL_GDT,edx,ecx,STD_DATA>
; Set up a descriptor for the LDT and an LDT data alias.
mov edx,LADXLDTBASE
movzx ecx,cdscGDTMax
shl ecx,3
dec ecx
cCall NSetSegmentDscr,<SEL_LDT,edx,ecx,STD_LDT>
;
; Set up the descriptors for the page directory and page tables
;
mov eax,LADXPDBASE
cCall NSetSegmentDscr,<SEL_DXPD,eax,0,CBPAGE386LIM,STD_DATA>
mov edx, LADXPTBASE ; EDX = user page tables linear base
mov ecx, ( (CPTDX + 1) shl 12 ) - 1
; # of user PTE's + vcpi's PTE's minus one
cCall NSetSegmentDscr,<SEL_DXPT,edx,ecx,STD_DATA>
; Setup a selector and data alias for the TSS
mov edx,LADXTSS1BASE ;get base address of TSS
mov ecx,(TYPE TSS386) - 1
cCall NSetSegmentDscr,<SEL_TSS,edx,ecx,STD_TSS386>
mov eax,DXLINEARBASE
xor ecx,ecx
dec cx
cCall NSetSegmentDscr,<SEL_LDT_ALIAS,eax,ecx,STD_DATA>
mov eax,DX_TEMP_LINEARBASE
cCall NSetSegmentDscr,<SEL_TSS_ALIAS,eax,ecx,STD_DATA>
; Set up a descriptor for our protected mode code.
mov dx,cs
movzx edx,dx ;our code segment paragraph address
shl edx,4 ;convert to linear byte address
cCall NSetSegmentDscr,<SEL_DXCODE,edx,0,0ffffh,STD_CODE>
; Set up another one, but ring 0 this time.
cCall NSetSegmentDscr,<SEL_DXCODE0,edx,0,0ffffh,ARB_CODE0>
mov edx,LADXPMCODEBASE ; EDX = LADXPMCODEBASE
mov ecx, offset DXPMCODE:CodeEndPM + 10h
cCall NSetSegmentDscr,<SEL_DXPMCODE,edx,ecx,STD_CODE>
;
; Set up the Ring 0 DXPMCODE alias for handling protected mode processor
; exceptions.
;
cCall NSetSegmentDscr,<SEL_EH,edx,ecx,EH_CODE>
; Set up a descriptor for our protected mode data and stack area.
mov dx,ds
movzx edx,dx ;our data segment paragraph address
shl edx,4 ;convert to linear byte address
mov ecx,0FFFFh
cCall NSetSegmentDscr,<SEL_DXDATA,edx,ecx,STD_DATA>
; And another one of those for ring 0
cCall NSetSegmentDscr,<SEL_DXDATA0,edx,ecx,ARB_DATA0>
; Set up descriptors pointing to our PSP and environment.
movzx edx, segPSP ; segment address of the PSP
shl edx, 4 ; linear address of PSP
cCall NSetSegmentDscr,<SEL_PSP,edx,ecx,STD_DATA>
mov selPSP, SEL_PSP
; set up environment selector
push es
mov es,segPSP
assume es:PSPSEG
movzx edx,segEnviron ;segment addr of environment
shl edx,4 ;linear address of env
mov ecx,7FFFh ;environments can only be 32k
; byte by DOS
cCall NSetSegmentDscr,<SEL_ENVIRON,edx,ecx,STD_DATA>
pop es
assume es:DGROUP
; Set up a descriptor pointing to the BIOS code and data areas
mov edx, 0f0000h ; set to linear f0000 (f000:0000)
mov ecx, 0ffffh ; make it be a 64k segment
cCall NSetSegmentDscr,<SEL_BIOSCODE,edx,ecx,STD_CODE>
mov edx, 40h * 16 ; linear byte addr of BIOS data area
cCall NSetSegmentDscr,<SEL_BIOSDATA,edx,ecx,STD_DATA>
; Set up a descriptor pointing to the real mode interrupt vector table.
xor edx, edx ; the IVT is at linear address 0,
;other registers are still set up
; 64k data segment from last one
cCall NSetSegmentDscr,<SEL_RMIVT,edx,ecx,STD_DATA>
; And, set up a selector for VCPI/WDEB386 to use to access all of memory.
xor edx,edx
mov ecx,edx
dec ecx
cCall NSetSegmentDscr,<SEL_VCPIALLMEM,edx,ecx,ARB_DATA0>
; Set up the call gate descriptor for the reset to V86 mode routine.
mov ecx,offset DXCODE:ResetVCPI
@@: mov edx,SEL_DXCODE0
cCall NSetSegmentDscr,<SEL_RESET,edx,ecx,STD_CALL>
; Set up a call gate to invoke the VCPI pMode interface in ring 0.
mov ecx,offset DXCODE:CallVCPI
cCall NSetSegmentDscr,<SEL_CALLVCPI,edx,ecx,STD_CALL>
; Init call gate descriptors for all the DynaLink services.
if DEBUG ;------------------------------------------------------------
extrn DXOutDebugStr:NEAR
mov ax,SEL_DYNALINK + (OutDebugStr shl 3)
mov ecx,offset DXCODE:DXOutDebugStr
mov edx,(SEL_DXCODE0 or EH_RING) or 00080000h ;copy 8 stack words
cCall NSetSegmentDscr,<ax,edx,ecx,STD_CALL>
extrn DXTestDebugIns:NEAR
mov ax,SEL_DYNALINK + (TestDebugIns shl 3)
movzx edx,dx ;copy 0 stack words
mov ecx,offset DXCODE:DXTestDebugIns
cCall NSetSegmentDscr,<ax,edx,ecx,STD_CALL>
endif ;DEBUG ---------------------------------------------------------
; Set up the fault reflector IRET call gate.
mov ax,offset DXPMCODE:PMFaultReflectorIRET
cCall NSetSegmentDscr,<SEL_RZIRET,5,SEL_EH,0,ax,STD_CALL>
clc
cEnd
;*** InitIDTVCPI ;[ds]
;
; Purpose: to initialize the idt when running under vcpi
;
; Register
; Usage: eax, all others preserved
;
; Input: none
;
; Output: idt is initialized
;
; Returns: can't fail, cy clear
;
; Exceptions:
;
; Notes: only under real mode!!!!!
;
;************************************************************************/
cProc InitIDTVCPI,<NEAR,PUBLIC>,<ebx,ecx,edx,di,es>
cBegin
jnc @F
jmp InitIDTVCPI_ret
@@:
movzx eax, segBootPmode ; set up segIDT,selIDT with
shl eax, 4 ; the RM seg of the idt
add eax, IDTOFF
;
; IDT must be paragraph aligned!!!!! (guarenteed--see dxvcpi.inc)
;
shr eax, 4
mov segIDT, ax ; base of IDT
mov selIDT, ax ;
movzx ecx, cdscIDTMax ; number of descriptors in table
shl cx, 3 ; convert to count of bytes
dec cx ; compute segment size limit
mov bpIDTcb, cx ; set up idtr with limit,
mov edx, LADXIDTBASE ; ...and...
mov bpIDTbase, edx ; ...linear base
cCall NSetSegmentDscr,<SEL_IDT,edx,ecx,STD_DATA>
; Fill the IDT with interrupt gates that point to the interrupt reflector
; entry vector.
mov es, segIDT
xor di,di
mov dx,offset DXPMCODE:PMFaultEntryVector ;the 1st 32 go here
mov cx,32
@@: mov es:[di].offDest,dx
mov es:[di].selDest,SEL_EH or EH_RING
mov es:[di].cwParam,0
mov es:[di].arbGate,STD_INTR
mov es:[di].rsvdGate,0
add dx,3
add di,8
loop @b
mov dx,offset DXPMCODE:PMIntrEntryVector+(32*3) ; the rest go here
mov cx,cdscIDTMax
sub cx,32
@@: mov es:[di].offDest,dx
mov es:[di].selDest,SEL_DXPMCODE or STD_RING
mov es:[di].cwParam,0
mov es:[di].arbGate,STD_INTR
mov es:[di].rsvdGate,0
add dx,3
add di,8
loop @b
; Now, fix up the ones that don't point to the interrupt reflector.
mov es:[21h*8].offDest,offset DXPMCODE:PMIntrDos
mov es:[25h*8].offDest,offset DXPMCODE:PMIntr25
mov es:[26h*8].offDest,offset DXPMCODE:PMIntr26
mov es:[28h*8].offDest,offset DXPMCODE:PMIntr28
mov es:[2Fh*8].offDest,offset DXPMCODE:PMInt2FHandler
mov es:[30h*8].offDest,offset DXPMCODE:PMIntrIgnore
mov es:[31h*8].offDest,offset DXPMCODE:PMIntr31
mov es:[33h*8].offDest,offset DXPMCODE:PMIntrMouse
mov es:[41h*8].offDest,offset DXPMCODE:PMIntrIgnore
if DEBUG ;-------------------------------------------------------------
cmp fTraceBug,0
jz @f
mov es:[41h*8].offDest,offset DXCODE:PMDebugInt
mov es:[41h*8].selDest,SEL_DXCODE or STD_RING
@@:
endif ;DEBUG ---------------------------------------------------------
mov es:[4Bh*8].offDest,offset DXPMCODE:PMIntr4B
ifdef NOT_NTVDM_NOT
; HP Extended BIOS System Call handler
test fHPVectra,0ffh ;only do this for an HP Vectra
jz NoHPBios
; Supposedly the system driver is going to force the HP Bios to
; use interrupt 6Fh while Windows is running, so we don't need to
; search for the moveable HP Bios interrupt--just use Int 6Fh.
mov es:[6Fh*8].offDest,offset HPxBios
NoHPBios:
endif
mov es:[70h*8].offDest,offset DXPMCODE:PMIntr70
clc ; return success
InitIDTVCPI_ret:
cEnd
;*** InitTSSVCPI - initialize the tss for use under vcpi
;
; Purpose:
;
; Register
; Usage: uses eax, all others preserved
;
; Input: none
;
; Output: tss is setup
;
; Returns: none
;
; Exceptions: none
;
; Notes: none
;
;************************************************************************/
cProc InitTSSVCPI,<NEAR,PUBLIC>,<es,di>
cBegin
jnc @F
jmp InitTSSVCPI_ret
@@:
mov ax, segBootPmode
add ax, ( (TSSOFF shr 16d) shl 12d )
mov es, ax
mov di, TSSOFF AND 0FFFFH
mov es:[di + ts3_ldt],SEL_LDT ;set the LDT selector
; Set the ring 0 stack seg/pointer, we don't bother to set the others
; since nothing runs below user privilege level. Currently very little
; code runs ring 0 - just when switching between real/proteted modes.
mov es:[di + ts3_ss0],SEL_DXDATA0
mov word ptr es:[di + ts3_esp0],offset DGROUP:rgw0Stack
clc
InitTSSVCPI_ret:
cEnd
;
;************************************************************************/
;
; The rest of this file is code which is called during protected mode
; initialization.
;
;************************************************************************/
;
;**************************************************************
;*** CallVCPIPM
;
; Utility routine to call VCPI server in protected mode. Masks out
; interrupts during the call because QEMM enables the processor
; interrupt flag when you call it.
;
; Entry: AX = VCPI function code.
; Uses: Depends upon call.
;
; Note: There is a copy of this routine in dxvcpi.asm and another
; in dxvcpibt.asm. This is to allow near calls. The copy
; in dxvcpibt.asm is discarded after initialization time.
;
;**************************************************************
cProc CallVCPIPM,<NEAR>,<si>
cBegin
push ax ; save function code
;
; Shut out all interrupts.
; QEMM 5.0 enables interrupts during this call. All our interrupt
; handlers are in the user code ring. A workaround is to shut off
; hardware interrupts during the call.
;
in al,INTA01
IO_Delay
mov si, ax
mov al,0FFh
out INTA01,al
IO_Delay
pop ax ;restore function code
db 9Ah ;call far SEL_CALLVCPI:0
dw 0,SEL_CALLVCPI or STD_RING
; Restore the state of the interrupt mask register
xchg si, ax
out INTA01,al
IO_Delay
xchg si, ax
cEnd
;**************************************************************
; This variable is used to allow calling NSetSegmentDscr from
; the DXCODE segment in protected mode.
;
NSetSegmentDscrCall label dword
dw OFFSET DXPMCODE:NSetSegmentDscr,SEL_DXPMCODE or STD_RING
extrn EnterProtectedMode:NEAR
extrn EnterRealMode:NEAR
DXCODE ends
DXPMCODE segment
extrn XMScontrol:FAR
DXPMCODE ends
DXCODE segment
XMScontrolCall label dword
dw OFFSET DXPMCODE:XMScontrol,SEL_DXPMCODE or STD_RING
pmxmssvc macro fcn
ifnb <fcn>
mov ah, fcn
endif
call XMScontrolCall
endm
;************************************************************************/
;*** GrowPageTables
;
; Purpose: To grow the user memory page tables large enough to
; accomodate any expected memory allocation request.
;
; Register
; Usage: NONE
;
; Input: NONE
;
; Output: Page tables grown to anticipated demand. cPteMax updated.
; Page table segment limit extended.
;
; Returns:
;
; Exceptions:
;
; Notes: Fails unless there is an XMS block to hold the new page
; tables. Cannot extend the page tables using VCPI memory.
;
;************************************************************************/
cProc GrowPageTables,<NEAR,PUBLIC>,<ebx,ecx,edx,si,di,es>
cBegin
PMvcpi vcpiCFREEPAGES ; Fetch current VCPI free pages
mov ecx,edx
pmxmssvc 08h ; Query free extended memory
shr ax,2 ; convert to pages
movzx eax,ax
add ecx,eax ; ECX = theoretical pages we could
; allocate
sub ecx,cPteMax ; minus what we have room for
jna GPT_ret ; enough space already
;
; So we work on machines with more than 64 Meg. RAM, we should put in a check
; here to see whether it looks like we have more.
;
shr ecx,8 ; ECX = kilobytes to hold this stuff
;
; Instead, we do this quick hack for now. If there appears to be more
; than 60 Mb available, then grow the page tables to 512k.
;
cmp ecx,60
jb @F
mov ecx,512
@@:
mov dx,cx
add dx,3 ; need to page align
pmxmssvc 09h ; Allocate a chunk of XMS to hold it
or ax,ax
jz GPT_ret
lea si, hmem_XMS_Table ; SI points to saved handles buffer
inc [hmem_XMS_Count] ; bump XMS handle count
mov [si],dx ; save handle for later disposal
pmxmssvc 0Ch ; lock the handle
or ax,ax
jnz @F ; jump on success
mov dx,[si] ; couldn't lock handle
pmxmssvc 0Dh ; free it
dec [hmem_XMS_Count] ; decrement count of allocated handles
jmp GPT_ret ; out of here
@@:
shl edx,16 ; EDX = physical address of extended
mov dx,bx ; page tables
shr edx,10
add edx,3 ; begin on a page boundary
shr edx,2
shl edx,12
or dl,NEWPTEMASK ; EDX = first PTE
mov selGDT,SEL_GDT
mov ax,SEL_DXPT
lsl ebx,eax
shl ecx,10 ; ECX = extra space in bytes
add ebx,ecx ; EBX = page table segment limit
mov eax, LADXPTBASE ; EAX = user page tables linear base
cCall [NSetSegmentDscrCall],<SEL_DXPT,eax,ebx,STD_DATA>
mov selGDT,SEL_LDT_ALIAS
mov eax,ecx
shr eax,2 ; ECX = new PTEs
add cPteMax,eax
add ecx,CBPAGE386 - 1
shr ecx,12 ; ECX = pages of page tables
mov ax,SEL_DXPD
mov es,ax
mov edi, ( CPTDX + 1 ) shl 2
push ecx
push edi
mov eax,edx
;
; Map the new page tables into the page directory.
;
GPT_add_PD:
stos dword ptr es:[edi]
add eax,CBPAGE386
dec ecx
jnz GPT_add_PD
;
; Map the new page tables into the page table segment, by copying
; the page directory entries.
;
mov eax, DXLINEARBASE + DXPTSYSOFF + ( USERPT shl 2 )
xor ecx,ecx
dec cx
cCall [NSetSegmentDscrCall],<SEL_SCR0,eax,ecx,STD_DATA>
pop edi
mov esi,edi
pop ecx
push ds
assume ds:nothing
mov ax,es
mov ds,ax
mov ax,SEL_SCR0 or STD_TBL_RING
mov es,ax
rep movsd
pop ds
assume ds:DGROUP
GPT_ret:
cEnd
;************************************************************************/
;*** VCPIBootStrap
;
; Purpose: Move the Pmode memory block into extended memory.
;
; Register Usage:
; EAX, BX, CX, SI, DI, Flags.
;
; Input: System is in Pmode.
;
; Output: If successful, the entire block of memory at DXLINEARBASE
; is copied to DX_TEMP_LINEARBASE. Then the page directory
; and page tables are swapped around, and a new cr3 value is
; loaded, so that the new block is placed at DXLINEARBASE.
; The VCPI server reloads cr3.
;
; Exceptions:
; No defined errors. A bug in this routine will most likely
; crash the program.
;
; Returns: Nothing.
;************************************************************************/
cProc VCPIBootStrap,<NEAR,PUBLIC>,<eax,ecx,si,di>
cBegin
assume ds:DGROUP,ss:DGROUP
call EnterProtectedMode
cmp fDebug,0
jz SkipVCPIDebugINIT
mov ax,SEL_DXCODE or STD_RING
mov es,ax
assume es:NOTHING
mov di,offset DXCODE:WdebVCPI
mov ax,DS_VCPI_Notify
int DebOut_Int
push ds
pop es
assume es:DGROUP
SkipVCPIDebugINIT:
push es
push ds
assume ds:NOTHING,es:NOTHING
mov ax,SEL_LDT_ALIAS
mov ds,ax
mov ax,SEL_TSS_ALIAS
mov es,ax
;
; Copy the block of memory at DXLINEARBASE, which is really in a DOS
; memory block in conventional memory, to DX_TEMP_LINEARBASE.
;
xor si,si
mov di,si
mov cx,LDTOFF shr 2
rep movsd
;
; Manipulate the page tables and page directory in the extended
; memory block, so that all our selectors point to the new area.
;
mov ax,es
mov ds,ax
mov si,DXTEMPPTOFF
mov di,DXPTSYSOFF
mov cx,CBPAGE386 shr 2
rep movsd
;
; Zero out the temporary page table that was used to copy our stuff
; up here.
;
mov di,DXTEMPPTOFF
mov cx,CBPAGE386 shr 2
xor eax,eax
rep stosd
;
; Fill in the new page directory. First, the low memory page tables.
;
mov si,DXPTSYSOFF + (VCPIPTOFF shr 10)
mov di,DXPDOFF
mov cx,CPTDX + 1
rep movsd
;
; Second, the entry for our high memory system area.
;
mov si,DXPTSYSOFF + (DXPTSYSOFF shr 10)
mov di,DXPDOFF + (DXLINEARBASE shr 20)
movsd
;
; Copy the two user page table page table entries to their final location.
;
mov cx,2
mov si,DXPTSYSOFF
mov di,DXPTSYSOFF + ( USERPT shl 2 )
rep movsd
;
; Zero out the original entries.
;
mov cx,2
mov di,DXPTSYSOFF
xor eax,eax
rep stosd
;
; Move our protected mode code segment up.
;
mov di,DXPMCODEOFF
mov ax,SEL_GDT
mov ds,ax
mov si,SEL_LDT_ALIAS
mov dx,DXPMCODE
mov ax,dx
shl ax,4
shr dx,12
mov ds:[si].adrBaseLow,ax
mov ds:[si].adrBaseHigh,dl
mov ds:[si].adrbBaseHi386,dh
mov cx,offset DXPMCODE:CodeEndPM + 10h
shr cx,2
xor si,si
mov ax,SEL_LDT_ALIAS
mov ds,ax
rep movsd
;
; Fetch page directory address for later cr3 loading.
;
mov eax, es:[DXPTSYSOFF + (DXPDOFF shr 10)]
pop ds
assume ds:DGROUP
pop es
push eax
call EnterRealMode
pop eax ; get new page directory address
; Save the pte (physical addr) of
and ax, 0f000h ; page directory, mask 12 lsb's
mov V86ToPm.zaCr3VTP, eax ; store it
;
; VCPI server will load new CR3 value when we do the next real
; to protected mode switch.
;
call EnterProtectedMode
; Set up a descriptor for the LDT data alias.
mov edx,LADXLDTBASE
movzx ecx,cdscGDTMax
shl ecx,3
dec ecx
mov selGDT,SEL_GDT
cCall [NSetSegmentDscrCall],<SEL_LDT_ALIAS,edx,ecx,STD_DATA>
xor eax,eax
cCall [NSetSegmentDscrCall],<SEL_TSS_ALIAS,eax,eax,STD_DATA>
mov selGDT,SEL_LDT_ALIAS
call EnterRealMode
cEnd
;************************************************************************/
;*** AddXMStoVCPIHeap
;
; Purpose: Allocate extended memory for the DOS Extender heap by
; allocating and locking blocks of XMS memory. Fill in
; user page tables to point to the allocated memory.
;
; Register
; Usage: eax, edx
;
; Input: ES:DI points to beginning of user page tables.
;
; Output: ES:DI points to next unused page table entry.
;
; Returns:
;
; Exceptions:
;
; Notes: This function should not be called if memory has already been
; allocated from another source.
;
;************************************************************************/
cProc AddXMStoVCPIHeap,<FAR,PUBLIC>,<bx,cx,si>
localD cVCPIFreePages
cBegin
cCall GrowPageTables
lea si, hmem_XMS_Table ; SI points to saved handles buffer
mov ax,[hmem_XMS_Count] ; Point to the first free one.
shl ax,1
add si,ax
AXVH_begin:
xor eax,eax
pmxmssvc 08h ; Query free extended memory
and ax,NOT 3 ; truncate to page size
or ax,ax ; any available?
jz AXVH_x ; no
mov ecx,cPteMax ; ECX = number of pages that will fit
shl ecx,2 ; ECX = number of kilobytes
cmp eax,ecx ; compare available to space in page tables
jb AXVH_0 ; will fit in page tables
mov ax,cx ; won't, ask for less
AXVH_0:
mov cx,ax ; save copy of size
or cx,cx
jz AXVH_x ; none left
PMvcpi vcpiCFREEPAGES ; Store current VCPI free pages
mov cVCPIFreePages,edx
mov dx,cx ; dx = #kilobytes requested
pmxmssvc 09h ; allocate extended memory block
cmp ax,1 ; got a block?
jne AXVH_x ; no
mov [si],dx ; yes, got one, save handle
PMvcpi vcpiCFREEPAGES ; Fetch VCPI free pages
cmp edx,cVCPIFreePages ; Count still the same?
jne AXVH_0a ; No, allocate the memory from VCPI.
mov dx,[si] ; fetch handle
pmxmssvc 0ch ; lock the block
cmp ax,1 ; did it work?
je AXVH_1 ; yes
AXVH_0a:
mov dx,[si] ; no, fetch handle, free it and exit
pmxmssvc 0ah
jmp AXVH_x ; (can't use unlocked blocks)
AXVH_1: ; handle to locked block is in
; [si], address in DX:BX
movzx eax,dx
shl eax,10h
mov ax,bx ; EAX = linear address of block
shr cx,2 ; CX = number of 386 pages in block
test ax,MASK allbitsPTE ; Page aligned?
jz AXVH_2 ; yes
and ax,NOT (MASK allbitsPTE)
add ax,CBPAGE386
dec cx
jcxz AXVH_x ; none left
AXVH_2:
movzx ecx,cx
sub cPteMax,ecx ; this many PTEs used up
or ax,NEWPTEMASK ; make it a page table entry
mov edx,CBPAGE386
cld
AXVH_AddPage:
stos dword ptr es:[edi]
add eax,edx
loop AXVH_AddPage
inc [hmem_XMS_Count] ; bump XMS handle count
add si,2 ; point to next slot in handle array
cmp si,(OFFSET hmem_XMS_Table) + ( CXMSBLOCKSDX * 2 )
jb AXVH_begin ; jump if more handles fit in array
AXVH_x:
cEnd
DXCODE ends
endif ;VCPI
end