| ;**************************************************************************** |
| ;* |
| ;* SciTech OS Portability Manager Library |
| ;* |
| ;* ======================================================================== |
| ;* |
| ;* The contents of this file are subject to the SciTech MGL Public |
| ;* License Version 1.0 (the "License"); you may not use this file |
| ;* except in compliance with the License. You may obtain a copy of |
| ;* the License at http://www.scitechsoft.com/mgl-license.txt |
| ;* |
| ;* Software distributed under the License is distributed on an |
| ;* "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or |
| ;* implied. See the License for the specific language governing |
| ;* rights and limitations under the License. |
| ;* |
| ;* The Original Code is Copyright (C) 1991-1998 SciTech Software, Inc. |
| ;* |
| ;* The Initial Developer of the Original Code is SciTech Software, Inc. |
| ;* All Rights Reserved. |
| ;* |
| ;* ======================================================================== |
| ;* |
| ;* Based on original code Copyright 1994 Otto Chrons |
| ;* |
| ;* Language: 80386 Assembler, TASM 4.0 or later |
| ;* Environment: IBM PC 32 bit protected mode |
| ;* |
| ;* Description: Low level page fault handler for virtual linear framebuffers. |
| ;* |
| ;**************************************************************************** |
| |
| IDEAL |
| JUMPS |
| |
| include "scitech.mac" ; Memory model macros |
| |
| header _vflat ; Set up memory model |
| |
| VFLAT_START EQU 0F0000000h |
| VFLAT_END EQU 0F03FFFFFh |
| PAGE_PRESENT EQU 1 |
| PAGE_NOTPRESENT EQU 0 |
| PAGE_READ EQU 0 |
| PAGE_WRITE EQU 2 |
| |
| ifdef DOS4GW |
| |
| ;---------------------------------------------------------------------------- |
| ; DOS4G/W flat linear framebuffer emulation. |
| ;---------------------------------------------------------------------------- |
| |
| begdataseg _vflat |
| |
| ; Near pointers to the page directory base and our page tables. All of |
| ; this memory is always located in the first Mb of DOS memory. |
| |
| PDBR dd 0 ; Page directory base register (CR3) |
| accessPageAddr dd 0 |
| accessPageTable dd 0 |
| |
| ; CauseWay page directory & 1st page table linear addresses. |
| |
| CauseWayDIRLinear dd 0 |
| CauseWay1stLinear dd 0 |
| |
| ; Place to store a copy of the original Page Table Directory before we |
| ; intialised our virtual buffer code. |
| |
| pageDirectory: resd 1024 ; Saved page table directory |
| |
| ValidCS dw 0 ; Valid CS for page faults |
| Ring0CS dw 0 ; Our ring 0 code selector |
| LastPage dd 0 ; Last page we mapped in |
| BankFuncBuf: resb 101 ; Place to store bank switch code |
| BankFuncPtr dd offset BankFuncBuf |
| |
| INT14Gate: |
| INT14Offset dd 0 ; eip of original vector |
| INT14Selector dw 0 ; cs of original vector |
| |
| cextern _PM_savedDS,USHORT |
| cextern VF_haveCauseWay,BOOL |
| |
| enddataseg _vflat |
| |
| begcodeseg _vflat ; Start of code segment |
| |
| cextern VF_malloc,FPTR |
| |
| ;---------------------------------------------------------------------------- |
| ; PF_handler64k - Page fault handler for 64k banks |
| ;---------------------------------------------------------------------------- |
| ; The handler below is a 32 bit ring 0 page fault handler. It receives |
| ; control immediately after any page fault or after an IRQ6 (hardware |
| ; interrupt). This provides the fastest possible handling of page faults |
| ; since it jump directly here. If this is a page fault, the number |
| ; immediately on the stack will be an error code, at offset 4 will be |
| ; the eip of the faulting instruction, at offset 8 will be the cs of the |
| ; faulting instruction. If it is a hardware interrupt, it will not have |
| ; the error code and the eflags will be at offset 8. |
| ;---------------------------------------------------------------------------- |
| cprocfar PF_handler64k |
| |
| ; Check if this is a processor exeception or a page fault |
| |
| push eax |
| mov ax,[cs:ValidCS] ; Use CS override to access data |
| cmp [ss:esp+12],ax ; Is this a page fault? |
| jne @@ToOldHandler ; Nope, jump to the previous handler |
| |
| ; Get address of page fault and check if within our handlers range |
| |
| mov eax,cr2 ; EBX has page fault linear address |
| cmp eax,VFLAT_START ; Is the fault less than ours? |
| jb @@ToOldHandler ; Yep, go to previous handler |
| cmp eax,VFLAT_END ; Is the fault more than ours? |
| jae @@ToOldHandler ; Yep, go to previous handler |
| |
| ; This is our page fault, so we need to handle it |
| |
| pushad |
| push ds |
| push es |
| mov ebx,eax ; EBX := page fault address |
| and ebx,invert 0FFFFh ; Mask to 64k bank boundary |
| mov ds,[cs:_PM_savedDS]; Load segment registers |
| mov es,[cs:_PM_savedDS] |
| |
| ; Map in the page table for our virtual framebuffer area for modification |
| |
| mov edi,[PDBR] ; EDI points to page directory |
| mov edx,ebx ; EDX = linear address |
| shr edx,22 ; EDX = offset to page directory |
| mov edx,[edx*4+edi] ; EDX = physical page table address |
| mov eax,edx |
| mov edx,[accessPageTable] |
| or eax,7 |
| mov [edx],eax |
| mov eax,cr3 |
| mov cr3,eax ; Update page table cache |
| |
| ; Mark all pages valid for the new page fault area |
| |
| mov esi,ebx ; ESI := linear address for page |
| shr esi,10 |
| and esi,0FFFh ; Offset into page table |
| add esi,[accessPageAddr] |
| ifdef USE_NASM |
| %assign off 0 |
| %rep 16 |
| or [DWORD esi+off],0000000001h ; Enable pages |
| %assign off off+4 |
| %endrep |
| else |
| off = 0 |
| REPT 16 |
| or [DWORD esi+off],0000000001h ; Enable pages |
| off = off+4 |
| ENDM |
| endif |
| |
| ; Mark all pages invalid for the previously mapped area |
| |
| xchg esi,[LastPage] ; Save last page for next page fault |
| test esi,esi |
| jz @@DoneMapping ; Dont update if first time round |
| ifdef USE_NASM |
| %assign off 0 |
| %rep 16 |
| or [DWORD esi+off],0FFFFFFFEh ; Disable pages |
| %assign off off+4 |
| %endrep |
| else |
| off = 0 |
| REPT 16 |
| and [DWORD esi+off],0FFFFFFFEh ; Disable pages |
| off = off+4 |
| ENDM |
| endif |
| |
| @@DoneMapping: |
| mov eax,cr3 |
| mov cr3,eax ; Flush the TLB |
| |
| ; Now program the new SuperVGA starting bank address |
| |
| mov eax,ebx ; EAX := page fault address |
| shr eax,16 |
| and eax,0FFh ; Mask to 0-255 |
| call [BankFuncPtr] ; Call the bank switch function |
| |
| pop es |
| pop ds |
| popad |
| pop eax |
| add esp,4 ; Pop the error code from stack |
| iretd ; Return to faulting instruction |
| |
| @@ToOldHandler: |
| pop eax |
| ifdef USE_NASM |
| jmp far dword [cs:INT14Gate]; Chain to previous handler |
| else |
| jmp [FWORD cs:INT14Gate]; Chain to previous handler |
| endif |
| |
| cprocend |
| |
| ;---------------------------------------------------------------------------- |
| ; PF_handler4k - Page fault handler for 4k banks |
| ;---------------------------------------------------------------------------- |
| ; The handler below is a 32 bit ring 0 page fault handler. It receives |
| ; control immediately after any page fault or after an IRQ6 (hardware |
| ; interrupt). This provides the fastest possible handling of page faults |
| ; since it jump directly here. If this is a page fault, the number |
| ; immediately on the stack will be an error code, at offset 4 will be |
| ; the eip of the faulting instruction, at offset 8 will be the cs of the |
| ; faulting instruction. If it is a hardware interrupt, it will not have |
| ; the error code and the eflags will be at offset 8. |
| ;---------------------------------------------------------------------------- |
| cprocfar PF_handler4k |
| |
| ; Fill in when we have tested all the 64Kb code |
| |
| ifdef USE_NASM |
| jmp far dword [cs:INT14Gate]; Chain to previous handler |
| else |
| jmp [FWORD cs:INT14Gate]; Chain to previous handler |
| endif |
| |
| cprocend |
| |
| ;---------------------------------------------------------------------------- |
| ; void InstallFaultHandler(void *baseAddr,int bankSize) |
| ;---------------------------------------------------------------------------- |
| ; Installes the page fault handler directly int the interrupt descriptor |
| ; table for maximum performance. This of course requires ring 0 access, |
| ; but none of this stuff will run without ring 0! |
| ;---------------------------------------------------------------------------- |
| cprocstart InstallFaultHandler |
| |
| ARG baseAddr:ULONG, bankSize:UINT |
| |
| enter_c |
| |
| mov [DWORD LastPage],0 ; No pages have been mapped |
| mov ax,cs |
| mov [ValidCS],ax ; Save CS value for page faults |
| |
| ; Put address of our page fault handler into the IDT directly |
| |
| sub esp,6 ; Allocate space on stack |
| ifdef USE_NASM |
| sidt [ss:esp] ; Store pointer to IDT |
| else |
| sidt [FWORD ss:esp] ; Store pointer to IDT |
| endif |
| pop ax ; add esp,2 |
| pop eax ; Absolute address of IDT |
| add eax,14*8 ; Point to Int #14 |
| |
| ; Note that Interrupt gates do not have the high and low word of the |
| ; offset in adjacent words in memory, there are 4 bytes separating them. |
| |
| mov ecx,[eax] ; Get cs and low 16 bits of offset |
| mov edx,[eax+6] ; Get high 16 bits of offset in dx |
| shl edx,16 |
| mov dx,cx ; edx has offset |
| mov [INT14Offset],edx ; Save offset |
| shr ecx,16 |
| mov [INT14Selector],cx ; Save original cs |
| mov [eax+2],cs ; Install new cs |
| mov edx,offset PF_handler64k |
| cmp [UINT bankSize],4 |
| jne @@1 |
| mov edx,offset PF_handler4k |
| @@1: mov [eax],dx ; Install low word of offset |
| shr edx,16 |
| mov [eax+6],dx ; Install high word of offset |
| |
| leave_c |
| ret |
| |
| cprocend |
| |
| ;---------------------------------------------------------------------------- |
| ; void RemoveFaultHandler(void) |
| ;---------------------------------------------------------------------------- |
| ; Closes down the virtual framebuffer services and restores the previous |
| ; page fault handler. |
| ;---------------------------------------------------------------------------- |
| cprocstart RemoveFaultHandler |
| |
| enter_c |
| |
| ; Remove page fault handler from IDT |
| |
| sub esp,6 ; Allocate space on stack |
| ifdef USE_NASM |
| sidt [ss:esp] ; Store pointer to IDT |
| else |
| sidt [FWORD ss:esp] ; Store pointer to IDT |
| endif |
| |
| pop ax ; add esp,2 |
| pop eax ; Absolute address of IDT |
| add eax,14*8 ; Point to Int #14 |
| mov cx,[INT14Selector] |
| mov [eax+2],cx ; Restore original CS |
| mov edx,[INT14Offset] |
| mov [eax],dx ; Install low word of offset |
| shr edx,16 |
| mov [eax+6],dx ; Install high word of offset |
| |
| leave_c |
| ret |
| |
| cprocend |
| |
| ;---------------------------------------------------------------------------- |
| ; void InstallBankFunc(int codeLen,void *bankFunc) |
| ;---------------------------------------------------------------------------- |
| ; Installs the bank switch function by relocating it into our data segment |
| ; and making it into a callable function. We do it this way to make the |
| ; code identical to the way that the VflatD devices work under Windows. |
| ;---------------------------------------------------------------------------- |
| cprocstart InstallBankFunc |
| |
| ARG codeLen:UINT, bankFunc:DPTR |
| |
| enter_c |
| |
| mov esi,[bankFunc] ; Copy the code into buffer |
| mov edi,offset BankFuncBuf |
| mov ecx,[codeLen] |
| rep movsb |
| mov [BYTE edi],0C3h ; Terminate the function with a near ret |
| |
| leave_c |
| ret |
| |
| cprocend |
| |
| ;---------------------------------------------------------------------------- |
| ; int InitPaging(void) |
| ;---------------------------------------------------------------------------- |
| ; Initializes paging system. If paging is not enabled, builds a page table |
| ; directory and page tables for physical memory |
| ; |
| ; Exit: 0 - Successful |
| ; -1 - Couldn't initialize paging mechanism |
| ;---------------------------------------------------------------------------- |
| cprocstart InitPaging |
| |
| push ebx |
| push ecx |
| push edx |
| push esi |
| push edi |
| |
| ; Are we running under CauseWay? |
| |
| mov ax,0FFF9h |
| int 31h |
| jc @@NotCauseway |
| cmp ecx,"CAUS" |
| jnz @@NotCauseway |
| cmp edx,"EWAY" |
| jnz @@NotCauseway |
| |
| mov [BOOL VF_haveCauseWay],1 |
| mov [CauseWayDIRLinear],esi |
| mov [CauseWay1stLinear],edi |
| |
| ; Check for DPMI |
| |
| mov ax,0ff00h |
| push es |
| int 31h |
| pop es |
| shr edi,2 |
| and edi,3 |
| cmp edi,2 |
| jz @@ErrExit ; Not supported under DPMI |
| |
| mov eax,[CauseWayDIRLinear] |
| jmp @@CopyCR3 |
| |
| @@NotCauseway: |
| mov ax,cs |
| test ax,3 ; Which ring are we running |
| jnz @@ErrExit ; Needs zero ring to access |
| ; page tables (CR3) |
| mov eax,cr0 ; Load CR0 |
| test eax,80000000h ; Is paging enabled? |
| jz @@ErrExit ; No, we must have paging! |
| |
| mov eax,cr3 ; Load directory address |
| and eax,0FFFFF000h |
| |
| @@CopyCR3: |
| mov [PDBR],eax ; Save it |
| mov esi,eax |
| mov edi,offset pageDirectory |
| mov ecx,1024 |
| cld |
| rep movsd ; Copy the original page table directory |
| cmp [DWORD accessPageAddr],0; Check if we have allocated page |
| jne @@HaveRealMem ; table already (we cant free it) |
| |
| mov eax,0100h ; DPMI DOS allocate |
| mov ebx,8192/16 |
| int 31h ; Allocate 8192 bytes |
| and eax,0FFFFh |
| shl eax,4 ; EAX points to newly allocated memory |
| add eax,4095 |
| and eax,0FFFFF000h ; Page align |
| mov [accessPageAddr],eax |
| |
| @@HaveRealMem: |
| mov eax,[accessPageAddr] ; EAX -> page table in 1st Mb |
| shr eax,12 |
| and eax,3FFh ; Page table offset |
| shl eax,2 |
| cmp [BOOL VF_haveCauseWay],0 |
| jz @@NotCW0 |
| mov ebx,[CauseWay1stLinear] |
| jmp @@Put1st |
| |
| @@NotCW0: |
| mov ebx,[PDBR] |
| mov ebx,[ebx] |
| and ebx,0FFFFF000h ; Page table for 1st megabyte |
| |
| @@Put1st: |
| add eax,ebx |
| mov [accessPageTable],eax |
| sub eax,eax ; No error |
| jmp @@Exit |
| |
| @@ErrExit: |
| mov eax,-1 |
| |
| @@Exit: pop edi |
| pop esi |
| pop edx |
| pop ecx |
| pop ebx |
| ret |
| |
| cprocend |
| |
| ;---------------------------------------------------------------------------- |
| ; void ClosePaging(void) |
| ;---------------------------------------------------------------------------- |
| ; Closes the paging system |
| ;---------------------------------------------------------------------------- |
| cprocstart ClosePaging |
| |
| push eax |
| push ecx |
| push edx |
| push esi |
| push edi |
| |
| mov eax,[accessPageAddr] |
| call AccessPage ; Restore AccessPage mapping |
| mov edi,[PDBR] |
| mov esi,offset pageDirectory |
| mov ecx,1024 |
| cld |
| rep movsd ; Restore the original page table directory |
| |
| @@Exit: pop edi |
| pop esi |
| pop edx |
| pop ecx |
| pop eax |
| ret |
| |
| cprocend |
| |
| ;---------------------------------------------------------------------------- |
| ; long AccessPage(long phys) |
| ;---------------------------------------------------------------------------- |
| ; Maps a known page to given physical memory |
| ; Entry: EAX - Physical memory |
| ; Exit: EAX - Linear memory address of mapped phys mem |
| ;---------------------------------------------------------------------------- |
| cprocstatic AccessPage |
| |
| push edx |
| mov edx,[accessPageTable] |
| or eax,7 |
| mov [edx],eax |
| mov eax,cr3 |
| mov cr3,eax ; Update page table cache |
| mov eax,[accessPageAddr] |
| pop edx |
| ret |
| |
| cprocend |
| |
| ;---------------------------------------------------------------------------- |
| ; long GetPhysicalAddress(long linear) |
| ;---------------------------------------------------------------------------- |
| ; Returns the physical address of linear address |
| ; Entry: EAX - Linear address to convert |
| ; Exit: EAX - Physical address |
| ;---------------------------------------------------------------------------- |
| cprocstatic GetPhysicalAddress |
| |
| push ebx |
| push edx |
| mov edx,eax |
| shr edx,22 ; EDX is the directory offset |
| mov ebx,[PDBR] |
| mov edx,[edx*4+ebx] ; Load page table address |
| push eax |
| mov eax,edx |
| call AccessPage ; Access the page table |
| mov edx,eax |
| pop eax |
| shr eax,12 |
| and eax,03FFh ; EAX offset into page table |
| mov eax,[edx+eax*4] ; Load physical address |
| and eax,0FFFFF000h |
| pop edx |
| pop ebx |
| ret |
| |
| cprocend |
| |
| ;---------------------------------------------------------------------------- |
| ; void CreatePageTable(long pageDEntry) |
| ;---------------------------------------------------------------------------- |
| ; Creates a page table for specific address (4MB) |
| ; Entry: EAX - Page directory entry (top 10-bits of address) |
| ;---------------------------------------------------------------------------- |
| cprocstatic CreatePageTable |
| |
| push ebx |
| push ecx |
| push edx |
| push edi |
| mov ebx,eax ; Save address |
| mov eax,8192 |
| push eax |
| call VF_malloc ; Allocate page table directory |
| add esp,4 |
| add eax,0FFFh |
| and eax,0FFFFF000h ; Page align (4KB) |
| mov edi,eax ; Save page table linear address |
| sub eax,eax ; Fill with zero |
| mov ecx,1024 |
| cld |
| rep stosd ; Clear page table |
| sub edi,4096 |
| mov eax,edi |
| call GetPhysicalAddress |
| mov edx,[PDBR] |
| or eax,7 ; Present/write/user bit |
| mov [edx+ebx*4],eax ; Save physical address into page directory |
| mov eax,cr3 |
| mov cr3,eax ; Update page table cache |
| pop edi |
| pop edx |
| pop ecx |
| pop ebx |
| ret |
| |
| cprocend |
| |
| ;---------------------------------------------------------------------------- |
| ; void MapPhysical2Linear(ulong pAddr, ulong lAddr, int pages, int flags); |
| ;---------------------------------------------------------------------------- |
| ; Maps physical memory into linear memory |
| ; Entry: pAddr - Physical address |
| ; lAddr - Linear address |
| ; pages - Number of 4K pages to map |
| ; flags - Page flags |
| ; bit 0 = present |
| ; bit 1 = Read(0)/Write(1) |
| ;---------------------------------------------------------------------------- |
| cprocstart MapPhysical2Linear |
| |
| ARG pAddr:ULONG, lAddr:ULONG, pages:UINT, pflags:UINT |
| |
| enter_c |
| |
| and [ULONG pAddr],0FFFFF000h; Page boundary |
| and [ULONG lAddr],0FFFFF000h; Page boundary |
| mov ecx,[pflags] |
| and ecx,11b ; Just two bits |
| or ecx,100b ; Supervisor bit |
| mov [pflags],ecx |
| |
| mov edx,[lAddr] |
| shr edx,22 ; EDX = Directory |
| mov esi,[PDBR] |
| mov edi,[pages] ; EDI page count |
| mov ebx,[lAddr] |
| |
| @@CreateLoop: |
| mov ecx,[esi+edx*4] ; Load page table address |
| test ecx,1 ; Is it present? |
| jnz @@TableOK |
| mov eax,edx |
| call CreatePageTable ; Create a page table |
| @@TableOK: |
| mov eax,ebx |
| shr eax,12 |
| and eax,3FFh |
| sub eax,1024 |
| neg eax ; EAX = page count in this table |
| inc edx ; Next table |
| mov ebx,0 ; Next time we'll map 1K pages |
| sub edi,eax ; Subtract mapped pages from page count |
| jns @@CreateLoop ; Create more tables if necessary |
| |
| mov ecx,[pages] ; ECX = Page count |
| mov esi,[lAddr] |
| shr esi,12 ; Offset part isn't needed |
| mov edi,[pAddr] |
| @@MappingLoop: |
| mov eax,esi |
| shr eax,10 ; EAX = offset to page directory |
| mov ebx,[PDBR] |
| mov eax,[eax*4+ebx] ; EAX = page table address |
| call AccessPage |
| mov ebx,esi |
| and ebx,3FFh ; EBX = offset to page table |
| mov edx,edi |
| add edi,4096 ; Next physical address |
| inc esi ; Next linear page |
| or edx,[pflags] ; Update flags... |
| mov [eax+ebx*4],edx ; Store page table entry |
| loop @@MappingLoop |
| mov eax,cr3 |
| mov cr3,eax ; Update page table cache |
| |
| leave_c |
| ret |
| |
| cprocend |
| |
| endcodeseg _vflat |
| |
| endif |
| |
| END ; End of module |