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 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,, 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,, 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,, 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, 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,, 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,, 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,, 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,, 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, ; 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, ; ; Set up the descriptors for the page directory and page tables ; mov eax,LADXPDBASE cCall NSetSegmentDscr, 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, ; Setup a selector and data alias for the TSS mov edx,LADXTSS1BASE ;get base address of TSS mov ecx,(TYPE TSS386) - 1 cCall NSetSegmentDscr, mov eax,DXLINEARBASE xor ecx,ecx dec cx cCall NSetSegmentDscr, mov eax,DX_TEMP_LINEARBASE cCall NSetSegmentDscr, ; 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, ; Set up another one, but ring 0 this time. cCall NSetSegmentDscr, mov edx,LADXPMCODEBASE ; EDX = LADXPMCODEBASE mov ecx, offset DXPMCODE:CodeEndPM + 10h cCall NSetSegmentDscr, ; ; Set up the Ring 0 DXPMCODE alias for handling protected mode processor ; exceptions. ; cCall NSetSegmentDscr, ; 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, ; And another one of those for ring 0 cCall NSetSegmentDscr, ; 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, 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, 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, mov edx, 40h * 16 ; linear byte addr of BIOS data area cCall NSetSegmentDscr, ; 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, ; 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, ; Set up the call gate descriptor for the reset to V86 mode routine. mov ecx,offset DXCODE:ResetVCPI @@: mov edx,SEL_DXCODE0 cCall NSetSegmentDscr, ; Set up a call gate to invoke the VCPI pMode interface in ring 0. mov ecx,offset DXCODE:CallVCPI cCall NSetSegmentDscr, ; 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, extrn DXTestDebugIns:NEAR mov ax,SEL_DYNALINK + (TestDebugIns shl 3) movzx edx,dx ;copy 0 stack words mov ecx,offset DXCODE:DXTestDebugIns cCall NSetSegmentDscr, endif ;DEBUG --------------------------------------------------------- ; Set up the fault reflector IRET call gate. mov ax,offset DXPMCODE:PMFaultReflectorIRET cCall NSetSegmentDscr, 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,, 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, ; 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,, 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,, 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 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,, 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], 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], 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,, 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], xor eax,eax cCall [NSetSegmentDscrCall], 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,, 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