1 ;****************************************************************************
3 ;* SciTech OS Portability Manager Library
5 ;* ========================================================================
7 ;* The contents of this file are subject to the SciTech MGL Public
8 ;* License Version 1.0 (the "License"); you may not use this file
9 ;* except in compliance with the License. You may obtain a copy of
10 ;* the License at http://www.scitechsoft.com/mgl-license.txt
12 ;* Software distributed under the License is distributed on an
13 ;* "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
14 ;* implied. See the License for the specific language governing
15 ;* rights and limitations under the License.
17 ;* The Original Code is Copyright (C) 1991-1998 SciTech Software, Inc.
19 ;* The Initial Developer of the Original Code is SciTech Software, Inc.
20 ;* All Rights Reserved.
22 ;* ========================================================================
24 ;* Based on original code Copyright 1994 Otto Chrons
26 ;* Language: 80386 Assembler, TASM 4.0 or later
27 ;* Environment: IBM PC 32 bit protected mode
29 ;* Description: Low level page fault handler for virtual linear framebuffers.
31 ;****************************************************************************
36 include "scitech.mac" ; Memory model macros
38 header _vflat ; Set up memory model
40 VFLAT_START EQU 0F0000000h
41 VFLAT_END EQU 0F03FFFFFh
49 ;----------------------------------------------------------------------------
50 ; DOS4G/W flat linear framebuffer emulation.
51 ;----------------------------------------------------------------------------
55 ; Near pointers to the page directory base and our page tables. All of
56 ; this memory is always located in the first Mb of DOS memory.
58 PDBR dd 0 ; Page directory base register (CR3)
62 ; CauseWay page directory & 1st page table linear addresses.
64 CauseWayDIRLinear dd 0
65 CauseWay1stLinear dd 0
67 ; Place to store a copy of the original Page Table Directory before we
68 ; intialised our virtual buffer code.
70 pageDirectory: resd 1024 ; Saved page table directory
72 ValidCS dw 0 ; Valid CS for page faults
73 Ring0CS dw 0 ; Our ring 0 code selector
74 LastPage dd 0 ; Last page we mapped in
75 BankFuncBuf: resb 101 ; Place to store bank switch code
76 BankFuncPtr dd offset BankFuncBuf
79 INT14Offset dd 0 ; eip of original vector
80 INT14Selector dw 0 ; cs of original vector
82 cextern _PM_savedDS,USHORT
83 cextern VF_haveCauseWay,BOOL
87 begcodeseg _vflat ; Start of code segment
89 cextern VF_malloc,FPTR
91 ;----------------------------------------------------------------------------
92 ; PF_handler64k - Page fault handler for 64k banks
93 ;----------------------------------------------------------------------------
94 ; The handler below is a 32 bit ring 0 page fault handler. It receives
95 ; control immediately after any page fault or after an IRQ6 (hardware
96 ; interrupt). This provides the fastest possible handling of page faults
97 ; since it jump directly here. If this is a page fault, the number
98 ; immediately on the stack will be an error code, at offset 4 will be
99 ; the eip of the faulting instruction, at offset 8 will be the cs of the
100 ; faulting instruction. If it is a hardware interrupt, it will not have
101 ; the error code and the eflags will be at offset 8.
102 ;----------------------------------------------------------------------------
103 cprocfar PF_handler64k
105 ; Check if this is a processor exeception or a page fault
108 mov ax,[cs:ValidCS] ; Use CS override to access data
109 cmp [ss:esp+12],ax ; Is this a page fault?
110 jne @@ToOldHandler ; Nope, jump to the previous handler
112 ; Get address of page fault and check if within our handlers range
114 mov eax,cr2 ; EBX has page fault linear address
115 cmp eax,VFLAT_START ; Is the fault less than ours?
116 jb @@ToOldHandler ; Yep, go to previous handler
117 cmp eax,VFLAT_END ; Is the fault more than ours?
118 jae @@ToOldHandler ; Yep, go to previous handler
120 ; This is our page fault, so we need to handle it
125 mov ebx,eax ; EBX := page fault address
126 and ebx,invert 0FFFFh ; Mask to 64k bank boundary
127 mov ds,[cs:_PM_savedDS]; Load segment registers
128 mov es,[cs:_PM_savedDS]
130 ; Map in the page table for our virtual framebuffer area for modification
132 mov edi,[PDBR] ; EDI points to page directory
133 mov edx,ebx ; EDX = linear address
134 shr edx,22 ; EDX = offset to page directory
135 mov edx,[edx*4+edi] ; EDX = physical page table address
137 mov edx,[accessPageTable]
141 mov cr3,eax ; Update page table cache
143 ; Mark all pages valid for the new page fault area
145 mov esi,ebx ; ESI := linear address for page
147 and esi,0FFFh ; Offset into page table
148 add esi,[accessPageAddr]
152 or [DWORD esi+off],0000000001h ; Enable pages
158 or [DWORD esi+off],0000000001h ; Enable pages
163 ; Mark all pages invalid for the previously mapped area
165 xchg esi,[LastPage] ; Save last page for next page fault
167 jz @@DoneMapping ; Dont update if first time round
171 or [DWORD esi+off],0FFFFFFFEh ; Disable pages
177 and [DWORD esi+off],0FFFFFFFEh ; Disable pages
184 mov cr3,eax ; Flush the TLB
186 ; Now program the new SuperVGA starting bank address
188 mov eax,ebx ; EAX := page fault address
190 and eax,0FFh ; Mask to 0-255
191 call [BankFuncPtr] ; Call the bank switch function
197 add esp,4 ; Pop the error code from stack
198 iretd ; Return to faulting instruction
203 jmp far dword [cs:INT14Gate]; Chain to previous handler
205 jmp [FWORD cs:INT14Gate]; Chain to previous handler
210 ;----------------------------------------------------------------------------
211 ; PF_handler4k - Page fault handler for 4k banks
212 ;----------------------------------------------------------------------------
213 ; The handler below is a 32 bit ring 0 page fault handler. It receives
214 ; control immediately after any page fault or after an IRQ6 (hardware
215 ; interrupt). This provides the fastest possible handling of page faults
216 ; since it jump directly here. If this is a page fault, the number
217 ; immediately on the stack will be an error code, at offset 4 will be
218 ; the eip of the faulting instruction, at offset 8 will be the cs of the
219 ; faulting instruction. If it is a hardware interrupt, it will not have
220 ; the error code and the eflags will be at offset 8.
221 ;----------------------------------------------------------------------------
222 cprocfar PF_handler4k
224 ; Fill in when we have tested all the 64Kb code
227 jmp far dword [cs:INT14Gate]; Chain to previous handler
229 jmp [FWORD cs:INT14Gate]; Chain to previous handler
234 ;----------------------------------------------------------------------------
235 ; void InstallFaultHandler(void *baseAddr,int bankSize)
236 ;----------------------------------------------------------------------------
237 ; Installes the page fault handler directly int the interrupt descriptor
238 ; table for maximum performance. This of course requires ring 0 access,
239 ; but none of this stuff will run without ring 0!
240 ;----------------------------------------------------------------------------
241 cprocstart InstallFaultHandler
243 ARG baseAddr:ULONG, bankSize:UINT
247 mov [DWORD LastPage],0 ; No pages have been mapped
249 mov [ValidCS],ax ; Save CS value for page faults
251 ; Put address of our page fault handler into the IDT directly
253 sub esp,6 ; Allocate space on stack
255 sidt [ss:esp] ; Store pointer to IDT
257 sidt [FWORD ss:esp] ; Store pointer to IDT
260 pop eax ; Absolute address of IDT
261 add eax,14*8 ; Point to Int #14
263 ; Note that Interrupt gates do not have the high and low word of the
264 ; offset in adjacent words in memory, there are 4 bytes separating them.
266 mov ecx,[eax] ; Get cs and low 16 bits of offset
267 mov edx,[eax+6] ; Get high 16 bits of offset in dx
269 mov dx,cx ; edx has offset
270 mov [INT14Offset],edx ; Save offset
272 mov [INT14Selector],cx ; Save original cs
273 mov [eax+2],cs ; Install new cs
274 mov edx,offset PF_handler64k
275 cmp [UINT bankSize],4
277 mov edx,offset PF_handler4k
278 @@1: mov [eax],dx ; Install low word of offset
280 mov [eax+6],dx ; Install high word of offset
287 ;----------------------------------------------------------------------------
288 ; void RemoveFaultHandler(void)
289 ;----------------------------------------------------------------------------
290 ; Closes down the virtual framebuffer services and restores the previous
291 ; page fault handler.
292 ;----------------------------------------------------------------------------
293 cprocstart RemoveFaultHandler
297 ; Remove page fault handler from IDT
299 sub esp,6 ; Allocate space on stack
301 sidt [ss:esp] ; Store pointer to IDT
303 sidt [FWORD ss:esp] ; Store pointer to IDT
307 pop eax ; Absolute address of IDT
308 add eax,14*8 ; Point to Int #14
309 mov cx,[INT14Selector]
310 mov [eax+2],cx ; Restore original CS
311 mov edx,[INT14Offset]
312 mov [eax],dx ; Install low word of offset
314 mov [eax+6],dx ; Install high word of offset
321 ;----------------------------------------------------------------------------
322 ; void InstallBankFunc(int codeLen,void *bankFunc)
323 ;----------------------------------------------------------------------------
324 ; Installs the bank switch function by relocating it into our data segment
325 ; and making it into a callable function. We do it this way to make the
326 ; code identical to the way that the VflatD devices work under Windows.
327 ;----------------------------------------------------------------------------
328 cprocstart InstallBankFunc
330 ARG codeLen:UINT, bankFunc:DPTR
334 mov esi,[bankFunc] ; Copy the code into buffer
335 mov edi,offset BankFuncBuf
338 mov [BYTE edi],0C3h ; Terminate the function with a near ret
345 ;----------------------------------------------------------------------------
346 ; int InitPaging(void)
347 ;----------------------------------------------------------------------------
348 ; Initializes paging system. If paging is not enabled, builds a page table
349 ; directory and page tables for physical memory
351 ; Exit: 0 - Successful
352 ; -1 - Couldn't initialize paging mechanism
353 ;----------------------------------------------------------------------------
354 cprocstart InitPaging
362 ; Are we running under CauseWay?
372 mov [BOOL VF_haveCauseWay],1
373 mov [CauseWayDIRLinear],esi
374 mov [CauseWay1stLinear],edi
385 jz @@ErrExit ; Not supported under DPMI
387 mov eax,[CauseWayDIRLinear]
392 test ax,3 ; Which ring are we running
393 jnz @@ErrExit ; Needs zero ring to access
395 mov eax,cr0 ; Load CR0
396 test eax,80000000h ; Is paging enabled?
397 jz @@ErrExit ; No, we must have paging!
399 mov eax,cr3 ; Load directory address
403 mov [PDBR],eax ; Save it
405 mov edi,offset pageDirectory
408 rep movsd ; Copy the original page table directory
409 cmp [DWORD accessPageAddr],0; Check if we have allocated page
410 jne @@HaveRealMem ; table already (we cant free it)
412 mov eax,0100h ; DPMI DOS allocate
414 int 31h ; Allocate 8192 bytes
416 shl eax,4 ; EAX points to newly allocated memory
418 and eax,0FFFFF000h ; Page align
419 mov [accessPageAddr],eax
422 mov eax,[accessPageAddr] ; EAX -> page table in 1st Mb
424 and eax,3FFh ; Page table offset
426 cmp [BOOL VF_haveCauseWay],0
428 mov ebx,[CauseWay1stLinear]
434 and ebx,0FFFFF000h ; Page table for 1st megabyte
438 mov [accessPageTable],eax
439 sub eax,eax ; No error
454 ;----------------------------------------------------------------------------
455 ; void ClosePaging(void)
456 ;----------------------------------------------------------------------------
457 ; Closes the paging system
458 ;----------------------------------------------------------------------------
459 cprocstart ClosePaging
467 mov eax,[accessPageAddr]
468 call AccessPage ; Restore AccessPage mapping
470 mov esi,offset pageDirectory
473 rep movsd ; Restore the original page table directory
484 ;----------------------------------------------------------------------------
485 ; long AccessPage(long phys)
486 ;----------------------------------------------------------------------------
487 ; Maps a known page to given physical memory
488 ; Entry: EAX - Physical memory
489 ; Exit: EAX - Linear memory address of mapped phys mem
490 ;----------------------------------------------------------------------------
491 cprocstatic AccessPage
494 mov edx,[accessPageTable]
498 mov cr3,eax ; Update page table cache
499 mov eax,[accessPageAddr]
505 ;----------------------------------------------------------------------------
506 ; long GetPhysicalAddress(long linear)
507 ;----------------------------------------------------------------------------
508 ; Returns the physical address of linear address
509 ; Entry: EAX - Linear address to convert
510 ; Exit: EAX - Physical address
511 ;----------------------------------------------------------------------------
512 cprocstatic GetPhysicalAddress
517 shr edx,22 ; EDX is the directory offset
519 mov edx,[edx*4+ebx] ; Load page table address
522 call AccessPage ; Access the page table
526 and eax,03FFh ; EAX offset into page table
527 mov eax,[edx+eax*4] ; Load physical address
535 ;----------------------------------------------------------------------------
536 ; void CreatePageTable(long pageDEntry)
537 ;----------------------------------------------------------------------------
538 ; Creates a page table for specific address (4MB)
539 ; Entry: EAX - Page directory entry (top 10-bits of address)
540 ;----------------------------------------------------------------------------
541 cprocstatic CreatePageTable
547 mov ebx,eax ; Save address
550 call VF_malloc ; Allocate page table directory
553 and eax,0FFFFF000h ; Page align (4KB)
554 mov edi,eax ; Save page table linear address
555 sub eax,eax ; Fill with zero
558 rep stosd ; Clear page table
561 call GetPhysicalAddress
563 or eax,7 ; Present/write/user bit
564 mov [edx+ebx*4],eax ; Save physical address into page directory
566 mov cr3,eax ; Update page table cache
575 ;----------------------------------------------------------------------------
576 ; void MapPhysical2Linear(ulong pAddr, ulong lAddr, int pages, int flags);
577 ;----------------------------------------------------------------------------
578 ; Maps physical memory into linear memory
579 ; Entry: pAddr - Physical address
580 ; lAddr - Linear address
581 ; pages - Number of 4K pages to map
584 ; bit 1 = Read(0)/Write(1)
585 ;----------------------------------------------------------------------------
586 cprocstart MapPhysical2Linear
588 ARG pAddr:ULONG, lAddr:ULONG, pages:UINT, pflags:UINT
592 and [ULONG pAddr],0FFFFF000h; Page boundary
593 and [ULONG lAddr],0FFFFF000h; Page boundary
595 and ecx,11b ; Just two bits
596 or ecx,100b ; Supervisor bit
600 shr edx,22 ; EDX = Directory
602 mov edi,[pages] ; EDI page count
606 mov ecx,[esi+edx*4] ; Load page table address
607 test ecx,1 ; Is it present?
610 call CreatePageTable ; Create a page table
616 neg eax ; EAX = page count in this table
618 mov ebx,0 ; Next time we'll map 1K pages
619 sub edi,eax ; Subtract mapped pages from page count
620 jns @@CreateLoop ; Create more tables if necessary
622 mov ecx,[pages] ; ECX = Page count
624 shr esi,12 ; Offset part isn't needed
628 shr eax,10 ; EAX = offset to page directory
630 mov eax,[eax*4+ebx] ; EAX = page table address
633 and ebx,3FFh ; EBX = offset to page table
635 add edi,4096 ; Next physical address
636 inc esi ; Next linear page
637 or edx,[pflags] ; Update flags...
638 mov [eax+ebx*4],edx ; Store page table entry
641 mov cr3,eax ; Update page table cache