page 60,132 ;----------------------------------------------------------------------------- ; ; 4MPAGES.ASM ; ; Copyright (c) 1995-Present Robert Collins ; ; You have my permission to copy and distribute this software for ; non-commercial purposes. Any commercial use of this software or ; source code is allowed, so long as the appropriate copyright ; attributions (to me) are intact, *AND* my email address is properly ; displayed. ; ; Basically, give me credit, where credit is due, and show my email ; address. ; ;----------------------------------------------------------------------------- ; ; Robert R. Collins email: rcollins@x86.org ; ;----------------------------------------------------------------------------- ;----------------------------------------------------------------------------- ; Assembler directives ;----------------------------------------------------------------------------- .xlist ; disable list file .586P ;----------------------------------------------------------------------------- ; Structures ;----------------------------------------------------------------------------- Desc_cache STRUC db ? _Type db ? _CS32 db ? db ? _Addr dd ? _Limit dd ? Desc_cache ENDS Descriptor STRUC Seg_limit dw ? ; Segment limit Base_A15_A00 dw ? ; A00..A15 of base address Base_A23_A16 db ? ; A16..A23 of base address Access_rights db ? ; Segment access rights GDLimit_A19_A16 db ? ; Granularity, Op-size, Limit A16..A19 Base_A31_A24 db ? ; A24..A31 of base address Descriptor ENDS ;----------------------------------------------------------------------------- ; Equates & macros ;----------------------------------------------------------------------------- CRLF$ equ 0dh,0ah,24h PSE equ 10h MSRTR6 equ 8 MSRTR7 equ 9 PDBR equ 80000h ; base address of page directory FARCS equ 400000h ; base of far CS address Signature equ 55aa55aah JMPFAR MACRO destination,selector ; dynamic JMP FAR SEG:OFF db 0eah ;; jmp instruction dw offset destination ;; offset word dw selector ;; segment selector word endm CALLFAR MACRO destination,selector ; dynamic CALL FAR SEG:OFF db 09ah ;; jmp instruction dw offset destination ;; offset word dw selector ;; segment selector word endm IO_Delay MACRO out 0e7h,ax ENDM ICEBP MACRO db 0f1h ENDM .list ;----------------------------------------------------------------------------- ; Dummy segments ;----------------------------------------------------------------------------- INTSEG segment at 0 int0 dd ? INTSEG ends _DATA segment para public use16 'DATA' ;----------------------------------------------------------------------------- ; Data segment ;----------------------------------------------------------------------------- GDT_386 label fword GDT_PTR Descriptor <> SEL_RMCS equ $-GDT_386 GDT_RMCS Descriptor <-1,,,9bh,0,> ; DS Descriptor SEL_RMDS equ $-GDT_386 GDT_RMDS Descriptor <-1,,,93h,0,> ; DS Descriptor SEL_4G equ $-GDT_386 GDT_4G Descriptor <-1h,0,0h,93h,8fh,0h> ; 4G Descriptor GDT_Len equ ($-GDT_386) - 1 ;----------------------------------------------------------------------------- ; All other data ;----------------------------------------------------------------------------- PG_Msg db "Can't run test...already in paging mode.",CRLF$ PE_Msg db "Can't run test...already in protected mode.",CRLF$ Not4M_Msg db "This processor doesn't support 4M pages.",CRLF$ Failure1_Msg db "4M page translation didn't work.",CRLF$ Failure2_Msg db "Unknown page translation (this should never occur).",CRLF$ Passed_Msg db "4M page translation behaves as expected.",CRLF$ ext_mem_blocks dw 0 align OrigPTE dd 0 OrigINT0 dd 0 OrigSentinal dd 0 _DATA ENDS _TEXT segment para public use16 'CODE' ASSUME CS:_TEXT, DS:_DATA, ES:_DATA, SS:STACK ;----------------------------------------------------------------------------- ; Code starts here ;----------------------------------------------------------------------------- _4MPAGES proc far mov ax,seg STACK ; setup stack segment mov ss,ax mov sp,sizeof StackPtr xor ax,ax ; clear it pushf push ds ; save far return on stack push ax ;----------------------------------------------------------------------------- ; Set segments to normal data segment ;----------------------------------------------------------------------------- @@: mov ax,seg _DATA ; get original data segment mov ds,ax mov es,ax ;----------------------------------------------------------------------------- ; Check that this processor supports 4M pages. ;----------------------------------------------------------------------------- xor eax,eax ; clear it db 66h ; were're going to do something smsw ax ; undocumented here, don't look test eax,80000001h ; page mode or pretected mode enabled? jz @F ; nope, continue mov dx,offset PG_Msg ; shl eax,1 ; check for PE jc @ErrorExit ; oops mov dx,offset PE_Msg @ErrorExit: mov ah,9 int 21h ; print message mov ax,4c01h ; set error code int 21h iret ; go split, just in case @@: mov eax,1 ; get CPUID flags cpuid ; test dx,1000y ; does this processor support 4M pages? mov dx,offset Not4M_Msg ; set message for failure jz @ErrorExit ; nope, go split ;----------------------------------------------------------------------------- ; Setup descriptor table ;----------------------------------------------------------------------------- mov eax,ds ; make pointer to GDT table shl eax,4 ; have physical address of segment add eax,offset GDT_386 ; now have physical addr of table mov GDT_PTR.Seg_limit,GDT_Len ; set length mov GDT_PTR.Base_A15_A00,ax shr eax,10h ; get other address bits mov GDT_PTR.Base_A23_A16,al mov GDT_PTR.Access_rights,ah mov eax,cs ; get CS shl eax,4 ; now have physical address mov GDT_RMCS.Base_A15_A00,ax shr eax,10h ; get other address bits mov GDT_RMCS.Base_A23_A16,al mov GDT_RMCS.Base_A31_A24,ah mov eax,ds ; get DS shl eax,4 ; now have physical address mov GDT_RMDS.Base_A15_A00,ax shr eax,10h ; get other address bits mov GDT_RMDS.Base_A23_A16,al mov GDT_RMDS.Base_A31_A24,ah ;----------------------------------------------------------------------------- ; Initialize page mode ;----------------------------------------------------------------------------- mov al,18h out 70h,al IO_Delay in al,71h mov ah,al mov al,17h out 70h,al IO_Delay in al,71h shr ax,6 mov ext_mem_blocks,ax call Init_page_mode cli lgdt GDT_386 mov eax,cr0 ; get control register or al,1 ; mov cr0,eax push cs ; push return selector on stack push offset PMRET ; set return offset JMPFAR @F,SEL_RMCS @@: mov ax,SEL_RMDS ; get DS selector mov ds,ax mov ax,SEL_4G ; get GS selector mov gs,ax ;----------------------------------------------------------------------------- ; Enable page mode ;----------------------------------------------------------------------------- mov eax,cr0 ; get 386 control register or eax,80000000h ; set PG bit mov cr0,eax ; now we're in protected mode jmp short @F ;----------------------------------------------------------------------------- ;----------------------------------------------------------------------------- @@: mov esi,FARCS mov edi,gs:Int0[esi] ; get original memory contents mov OrigSentinal,edi ; save it mov gs:Int0[esi],Signature ; save signature in memory mov edi,gs:Int0 ; get original interrupt vector mov OrigINT0,edi ; save it mov edx,SEL_4G ; get selector lea eax,Int0[esi] ; get INT0 offset call GetLinear ; get linear address mov dword ptr gs:[edx],87h ; modify to 4M PDE mov edi,gs:[eax] ; get original PTE mov OrigPTE,edi ; save it mov gs:Int0,edi ; save it mov ecx,cr3 ; get CR3 mov ebx,cr4 ; get CR4 or bl,PSE ; enable 4M pages mov cr4,ebx mov cr3,ecx ; flush TLB mov ebp,gs:Int0[esi] ; try to read from 4M mov ecx,cr3 ; clear TLB by loading mov ebx,cr4 ; get PSE and bl,not PSE ; turn off PSE mov cr4,ebx mov cr3,ecx ; CR3 with any value mov edi,OrigSentinal mov gs:Int0[esi],edi ; restore sentinal mov edi,OrigINT0 mov gs:Int0,edi ; restore original INT0 handler ;----------------------------------------------------------------------------- ; Split from this program ;----------------------------------------------------------------------------- mov cx,SEL_RMDS ; get DS selector mov gs,cx mov ecx,cr3 ; clear TLB by loading mov ebx,cr0 ; get 386 control register and ebx,not 80000001h ; clear paging bit mov cr0,ebx ; and store in CR0 mov cr3,ecx ; CR3 with any value retf PMRET: mov ax,seg _DATA mov ds,ax mov gs,ax mov dx,offset Failure1_Msg ; cmp ebp,Signature ; did we get a bogus signature? je @ErrorExit ; yep mov dx,offset Failure2_Msg cmp ebp,OrigPTE ; was our signature our original PDE? jne @ErrorExit ; nope mov dx,offset Passed_Msg mov ah,9 int 21h ; print message mov ax,4c00h ; set error code int 21h iret ; go split, just in case _4MPAGES endp ;----------------------------------------------------------------------------- GetLinear proc near ; Convert selector:offset to linear address ;----------------------------------------------------------------------------- ; Input: EDX:EAX = Selector:Offset ; Output: EBX = Linear address ; EDX = Linear address of page directory entry ; EAX = Linear address of page table entry ;----------------------------------------------------------------------------- push ecx mov bh,GDT_PTR.Base_A31_A24[edx] mov bl,GDT_PTR.Base_A23_A16[edx] shl ebx,10h mov bx,GDT_PTR.Base_A15_A00[edx] add ebx,eax ; now have linear address mov edx,ebx ; make a copy of linear address ; AAAR PP ; 33222222222211111111VVVSP CW ; 10987654321098765432LLLVSDADTUWP and edx,not 00000000001111111111111111111111y shr edx,20d ; make into index into page directory add edx,PDBR ; add page directory base mov eax,dword ptr gs:[edx] ; get base address of page table ; AAAR PP ; 33222222222211111111VVVSP CW ; 10987654321098765432LLLVSDADTUWP and eax,not 00000000000000000000111111111111y mov ecx,ebx ; make a copy of linear address ; AAAR PP ; 33222222222211111111VVVSP CW ; 10987654321098765432LLLVSDADTUWP and ecx,not 11111111110000000000111111111111y shr ecx,10d add eax,ecx pop ecx ret GetLinear endp ;----------------------------------------------------------------------------- Init_page_mode proc near ; initialize paging directory and ; ; requisite tables. ;----------------------------------------------------------------------------- ; Input: AX = Input status ; [B0,B1]=00 == Unity mapping ; =01 == Random mapping ; PSP_ADDR = segment address of PSP ; EXT_MEM_BLOCKS = # of 64K-byte blocks of extended memory ; Output: None ; Register(s) modified: AX, BX, CX, DX, EDI, DS ;----------------------------------------------------------------------------- psp_addr equ [bp-4] push es push bp mov bp,sp sub sp,4 mov [bp-2],ax ; save input status mov edi,PDBR SHR 4 ; set fixed location for page directory mov es,di ; prepare to make a physical address shl edi,4 ; make into physical address mov cr3,edi ; load page directory cache register lea eax,[edi+7] mov cx,ds:ext_mem_blocks ; get size of Extended memory in system shl cx,6 add cx,400h ; compensate for 0-1M ram addresses xor edi,edi ;----------------------------------------------------------------------------- ; Initialize page directory 0 ;----------------------------------------------------------------------------- ;----------------------------------------------------------------------------- ; If there are any more than 1 page directory (more than 4M in the system), ; Then go through this loop initializing all subsequent directory entries. ;----------------------------------------------------------------------------- init_page_directory: add eax,1000h ; point to next page table stosd ; fill in this entry of the page directory sub cx,1000h ; are we done yet? ja init_page_directory ; nope ;----------------------------------------------------------------------------- ; Clear all unused page directory entries, and make them not present. ;----------------------------------------------------------------------------- xor eax,eax ; clear it xor cx,cx sub cx,ds:ext_mem_blocks ; get size of Extended memory in system sub cx,10h ; align to next 4M boundry shr cx,6 rep stosd ;----------------------------------------------------------------------------- ; Prepare to initialize page tables for unity mapping. ;----------------------------------------------------------------------------- mov eax,7 ; initialize page table pointer w/ access mov cx,ds:ext_mem_blocks ; get size of Extended memory in system shl cx,6 add cx,400h ; compensate for 0-1M ram addresses shr cx,2 ; make CX=4K pages in system mov edi,1000h ; point to first page table init_page_table: stosd ; initialize page table entry add eax,1000h ; adjust pointer to next 4K block loop init_page_table ; continue until finished mov sp,bp pop bp pop es ; restore segment register ret init_page_mode endp _TEXT ends STACK segment para public 'STACK' ;----------------------------------------------------------------------------- ; Stack segment ;----------------------------------------------------------------------------- StackPtr db 400h dup (?) STACK ends ZSEG segment para public ZSEG ends end _4MPAGES