; =============================================================================
;
; Litos - Memory pages
;
; =============================================================================
CODE_SECTION 32
; -----------------------------------------------------------------------------
; Lock/unlock page cache
; -----------------------------------------------------------------------------
; NOTES: Use macro PGCLOCK to lock, PGCUNLOCK to unlock.
; -----------------------------------------------------------------------------
; ------------- Page cache lock function
%ifdef SMP
LOCK_LockFnc PGCLock,PageCacheLock ; page cache lock function
%endif
; ------------- Macro - call page cache lock function
%macro PGCLOCK 0
%ifdef SMP
call PGCLock ; call page cache lock function
%endif
%endmacro
; ------------- Macro - page cache unlock
%macro PGCUNLOCK 0
%ifdef SMP
LOCK_Unlock PageCacheLock ; unlock page cache lock
%endif
%endmacro
; -----------------------------------------------------------------------------
; Initialize memory page map
; -----------------------------------------------------------------------------
; DESTROYS: All registers
; NOTES: It must be called before SysMemInit and after InitPDE.
; If no continuous space found (very unlikely), it halts.
; -----------------------------------------------------------------------------
; ------------- Prepare number of physical pages
PageMapInit: mov eax,[MemoryMax] ; EAX <- end of physical memory
add eax,PAGE_SIZE-1 ; round up to page size
shr eax,PAGE_SHIFT ; EAX <- number of pages
mov [PageMapNum],eax ; number of page descriptors
; ------------- Prepare size of page map
mov edx,PAGEDESC_size ; EDX <- page descriptor size
mul edx ; EAX <- size of page map
mov [PageMapSize],eax ; size of page map
; ------------- Prepare size of page map in memory bitmap
add eax,PAGE_SIZE-1 ; round up to page size
shr eax,PAGE_SHIFT ; EAX <- number of pages
mov ebx,eax ; EBX <- save number of pages
add eax,byte 7 ; round up to byte
shr eax,3 ; EAX <- size of page map in bytes
; ------------- Prepare to find continuous memory for page map
mov esi,PageMemMap+SYSTEM_SIZE/PAGE_SIZE/8 ; end of map
sub esi,eax ; ESI <- start of possible page map
xchg eax,edx ; EDX <- size of page map in bytes
xor eax,eax ; EAX <- 0
dec eax ; EAX <- -1, available flags
; ------------- Check one possible space
PageMapInit2: mov edi,esi ; EDI <- address of the space
mov ecx,edx ; ECX <- size of the space
repe scasb ; check one space
je PageMapInit4 ; suitable space found
; ------------- Try another space (it will be located below PDE table)
dec esi ; ESI <- lower address
cmp esi,PageMemMap ; is address OK?
jae PageMapInit2 ; it is OK
jmp short $ ; emergency halt
; ------------- Initialize found space
PageMapInit4: mov edi,esi ; EDI <- address of the space
xor eax,eax ; EAX <- 0, used flags
mov ecx,edx ; ECX <- size of the space
rep stosb ; mark space as used
; ------------- Correct last byte of the map
and bl,7 ; pages in last byte
jz PageMapInit5 ; no pages remain
mov cl,bl ; CL <- number of pages
mov bh,-1 ; BH <- mask, pages are free
shl bh,cl ; BH <- new mask of used pages
mov [edi-1],bh ; set new mask of used pages
; ------------- Address of page map
PageMapInit5: xchg eax,esi ; EAX <- address in memory map
sub eax,PageMemMap ; EAX <- offset in memory map
shl eax,PAGE_SHIFT+3 ; EAX <- offset in system memory
add eax,SYSTEM_ADDR ; EAX <- address in memory
mov [PageMap],eax ; address of page map
; ------------- Initialize page map
xchg eax,ebx ; EBX <- address of page map
mov ecx,[PageMapNum] ; ECX <- number of pages
xor edx,edx ; EDX <- 0
mov eax,SYSTEM_ADDR ; EAX <- system address
PageMapInit6: mov [ebx+PG_Flags],edx ; initialize flags
mov [ebx+PG_Ref],edx ; reference counter
mov [ebx+PG_Address],eax ; address in system memory
add ebx,byte PAGEDESC_size ; EBX <- next descriptor
loop PageMapInit6 ; next descriptor
; ------------- Initialize zero page descriptor
mov eax,PageEmpty ; EAX <- zero page
call GetPageDesc ; get page descriptor
mov dword [ebx+PG_Address],eax ; system address
mov dword [ebx+PG_Flags],PG_ZERO ; flags
inc dword [ebx+PG_Ref] ; reference counter
mov [PageZero],ebx ; store zero page descriptor
ret
; -----------------------------------------------------------------------------
; Copy page on a write
; -----------------------------------------------------------------------------
; INPUT: EAX = source page descriptor
; EBX = destination page descriptor
; NOTES: We should use MMS instructions, but we would need to save FPU state.
; -----------------------------------------------------------------------------
; ------------- Push registers
PageCopyWrite: push ecx ; push ECX
push edi ; push EDI
; ------------- Check destination address and page size
mov edi,[ebx+PG_Address] ; EDI <- destination address
mov ecx,PAGE_SIZE/4 ; ECX <- page size / 4
; ------------- Check if source page is zero page
test byte [eax+PG_Flags],PG_ZERO ; is it zero page?
jnz PageCopyWrite4 ; it is zero page
; ------------- Copy the page
push esi ; push ESI
mov esi,[eax+PG_Address] ; ESI <- source address
rep movsd ; copy page
pop esi ; pop ESI
; ------------- Pop registers
pop edi ; pop EDI
pop ecx ; pop ECX
ret
; ------------- Clear the page
PageCopyWrite4: push eax ; push EAX
xor eax,eax ; EAX <- 0
rep stosd ; clear page
pop eax ; pop EAX
; ------------- Pop registers
pop edi ; pop EDI
pop ecx ; pop ECX
ret
; -----------------------------------------------------------------------------
; Get page descriptor
; -----------------------------------------------------------------------------
; INPUT: EAX = address in system memory (address must be valid)
; OUTPUT: EBX = page descriptor
; NOTES: Bits 0 to 11 of the address are ignored.
; -----------------------------------------------------------------------------
; ------------- Push registers
GetPageDesc: mov ebx,eax ; EBX <- push EAX
push edx ; push EDX
; ------------- Calculate address of page descriptor
sub eax,SYSTEM_ADDR ; EAX <- offset in system memory
shr eax,PAGE_SHIFT ; EAX <- page number
mov edx,PAGEDESC_size ; EDX <- size of page descriptor
mul edx ; EAX <- offset of page descriptor
add eax,[PageMap] ; EAX <- address of page descriptor
; ------------- Pop registers
pop edx ; pop EDX
xchg eax,ebx ; pop EAX, EBX <- page descriptor
ret
; -----------------------------------------------------------------------------
; Get page descriptor with check
; -----------------------------------------------------------------------------
; INPUT: EAX = address in system memory
; OUTPUT: EBX = page descriptor (EBX = 0 on CY)
; CY = invalid address (EBX = 0)
; NOTES: Bits 0 to 11 of the address are ignored.
; -----------------------------------------------------------------------------
; ------------- Push registers
GetPageDescChk: mov ebx,eax ; EBX <- push EAX
; ------------- Calculate address of page descriptor
sub eax,SYSTEM_ADDR ; EAX <- offset in system memory
jc GetPageDescChk8 ; invalid address
shr eax,PAGE_SHIFT ; EAX <- page number
cmp eax,[PageMapNum] ; check number of page descriptor
jae GetPageDescChk8 ; invalid page number
push edx ; push EDX
mov edx,PAGEDESC_size ; EDX <- size of page descriptor
mul edx ; EAX <- offset of page descriptor
pop edx ; pop EDX
add eax,[PageMap] ; EAX <- address of page descriptor
; ------------- OK: Pop registers (here is NC)
xchg eax,ebx ; pop EAX, EBX <- page descriptor
ret
; ------------- ERROR: Pop registers
GetPageDescChk8:xor eax,eax ; EAX <- 0, invalid address
stc ; set error flag
xchg eax,ebx ; pop EAX, EBX <- 0
ret
; -----------------------------------------------------------------------------
; Add page to the dirty page list
; -----------------------------------------------------------------------------
; INPUT: EBX = address of page descriptor
; -----------------------------------------------------------------------------
; ------------- Push registers
PageSetDirty: push eax ; push EAX
push edx ; push EDX
; ------------- Set dirty flag
bts dword [ebx+PG_Flags],PG_DIRTY_BIT ; set dirty flag
jc PageSetDirty9 ; dirty flag was alreaday set
; ------------- Fast check ig page space is valid
mov edx,[ebx+PG_Space] ; EDX <- page space
or edx,edx ; is page space valid?
jz PageSetDirty9 ; page space is not valid
; ------------- Lock page cache
PGCLOCK ; lock page cache
; ------------- Get page space (-> EDX)
mov edx,[ebx+PG_Space] ; EDX <- page space
or edx,edx ; is page space valid?
jz PageSetDirty4 ; page space is not valid
; ------------- Detach page from current page list
call ListDelEBX ; delete page from current page list
; ------------- Attach page to the dirty page list
lea eax,[edx+PS_Dirty] ; EAX <- list of dirty pages
xchg eax,ebx ; EAX <- page, EBX <- dirty list head
call ListAdd ; add page to the dirty page list
xchg eax,ebx ; EAX <- dirty list head, EBX <- page
; ------------- Unlock page cache
PageSetDirty4: PGCUNLOCK ; unlock page cache
; TODO: Mark inode dirty pages !!!!!!!!!!
; ------------- Pop registers
PageSetDirty9: pop edx ; pop EDX
pop eax ; pop EAX
ret
; -----------------------------------------------------------------------------
; Free pages from the swap
; -----------------------------------------------------------------------------
; INPUT: EBX = page descriptor
; -----------------------------------------------------------------------------
PageSwapFree:
; TODO !!!!!!!!!!
ret
; -----------------------------------------------------------------------------
; Remove pages from the user space
; -----------------------------------------------------------------------------
; INPUT: EAX = start address of the interval (page aligned)
; ECX = size of the interval (page aligned)
; EDX = virtual memory descriptor
; NOTES: Virtual memory descriptor must be locked.
; -----------------------------------------------------------------------------
; ------------- Push registers
PageRemove: push eax ; push EAX
push ebx ; push EBX
push ecx ; push ECX
push esi ; push ESI
push edi ; push EDI
; ------------- Prepare number of pages (-> EDI)
shr ecx,PAGE_SHIFT ; ECX <- number of pages
jecxz PageRemove9 ; no pages
mov edi,ecx ; EDI <- number of pages
; ------------- Prepare first page directory entry (-> ESI)
mov esi,eax ; ESI <- start address of the interval
shr esi,PAGEDIR_SHIFT-2 ; ESI <- offset of PDE
and esi,byte ~(B0|B1) ; ESI <- align offset of PDE
mov ebx,[edx+VMD_PGD] ; EBX <- global page directory
or ebx,ebx ; is global page directory valid?
jz PageRemove9 ; global page directory is not valid
add esi,ebx ; ESI <- first page directory entry
; ------------- Prepare offset of first PTE (->EBX) and number of pages (->ECX)
shr eax,PAGE_SHIFT ; EAX <- absolute page index
and eax,PAGEDIR_NUM-1 ; EAX <- page index in one directory
mov ecx,PAGEDIR_NUM ; ECX <- entries per page directory
sub ecx,eax ; ECX <- remaining pages
shl eax,2 ; EAX <- offset of first PTE
xchg eax,ebx ; EBX <- offset of first PTE
; ------------- Prepare address of first page table entry (-> EBX)
PageRemove2: mov eax,[esi] ; EAX <- page directory entry
or eax,eax ; is page directory entry valid?
jz PageRemove6 ; page directory is not valid
add ebx,eax
; ------------- Limit number of pages (-> ECX)
cmp ecx,edi ; check number of pages
jbe PageRemove3 ; number of pages is OK
mov ecx,edi ; ECX <- limit number of pages
; ------------- Get page table entry (-> EAX)
PageRemove3: mov eax,[ebx] ; EAX <- page table entry
or eax,eax ; is page table entry valid?
jz PageRemove5 ; page table entry is not valid
; ------------- Check if page is present
test al,PE_PRESENT ; is page present?
jz PageRemove4 ; page is not present
; ------------- Page si present, get page descriptor (-> EBX)
push ebx ; push EBX
call GetPageDescChk ; get page descriptor
jc PageRemove36 ; invalid address
; ------------- Check if page is reserved
test byte [ebx+PG_Flags],PG_RESERVE ; page reserved?
jnz PageRemove36 ; page is reserved (not commited)
; ------------- Decrease number of used pages
CURRENT eax ; EBX <- current task
%ifdef SMP
lock ; CPU instruction lock
%endif
dec dword [eax+TASK_PagesUsed] ; decrease used pages
; ------------- Free page from the swap
call PageSwapFree ; free page from the swap
PageRemove36: pop ebx ; pop EBX
; ------------- Clear page table entry
PageRemove4: and dword [ebx],byte 0 ; clear page table entry
; ------------- Next page directory entry
PageRemove5: add ebx,byte 4 ; EBX <- next page directory entry
dec edi ; decrease number of pages
loop PageRemove3 ; next page
; ------------- Next page directory entry
PageRemove6: add esi,byte 4 ; ESI <- next page directory entry
xor ebx,ebx ; EBX <- 0, offset of next first PTE
sub edi,ecx ; decrease number of pages
mov ecx,PAGEDIR_NUM ; ECX <- new number of entries
ja PageRemove2 ; next page directory
; ------------- Pop registers
PageRemove9: pop edi ; pop EDI
pop esi ; pop ESI
pop ecx ; pop ECX
pop ebx ; pop EBX
pop eax ; pop EAX
ret
; -----------------------------------------------------------------------------
; Free one page table entry
; -----------------------------------------------------------------------------
; INPUT: EAX = address of page table entry
; -----------------------------------------------------------------------------
; ------------- Push registers
PTEFree: push ebx ; push EBX
push edx ; push EDX
; ------------- Get page descriptor (-> EBX)
xchg eax,edx ; EDX <- address of page table entry
mov eax,[edx] ; EAX <- page table entry
call GetPageDescChk ; get page descriptor (-> EBX)
jc PTEFree9 ; invalid address
; ------------- Check if page is reserved (it is not commited with any data)
test byte [ebx+PG_Flags],PG_RESERVE ; reserved?
jnz PTEFree9 ; page is reserved (not commited)
; ------------- Set "Dirty" state of the flag (page is modified)
test byte [edx],PE_DIRTY ; is page dirty?
jz PTEFree2 ; page is not dirty
call PageSetDirty ; add a page to the dirty page list
; ------------- Free page and swap cache
PTEFree2:
; ------------- Pop registers
PTEFree9: pop edx ; pop EDX
pop ebx ; pop EBX
ret
; -----------------------------------------------------------------------------
; Int 14: #PF Page fault service (interrupt gate)
; -----------------------------------------------------------------------------
; Page fault was caused by:
; - Accessed page is not present in memory (P present flag is clear).
; - Corresponding table entry is null.
; - The procedure does not have sufficient privilege to access the page.
; - Code running in user mode attempts to write to a read-only page.
;
; Page fault error code:
; B0: (P) 0=The fault was caused by a nonpresent page.
; 1=The fault was caused by a page-level protection violation.
; B1: (W/R) 0=The access causing the fault was a read.
; 1=The access causing the fault was a write.
; B2: (U/S) 0=Processor was executing in supervisor mode.
; 1=Processor was executing in user mode.
; B3: (RSVD) 0=The fault was not caused by a reserved bit violation.
; 1=The page fault occured because a 1 was detected in one
; of the reserved bit positions of a page table entry or
; directory entry that was marked present.
; -----------------------------------------------------------------------------
; ------------- Push registers (it must correspond with the TRAPSTACK)
PageFault: pusha ; push all registers
push ds ; push DS
push es ; push ES
cld ; direction up
; ------------- Initialize kernel segments
mov eax,SYSTEM_DS ; EBX <- system DS
mov ds,eax ; DS <- system DS
mov es,eax ; ES <- system DS
mov ebp,esp ; EBP <- trap stack frame TRAPSTACK
; ------------- Get fault address (-> ESI)
mov esi,cr2 ; ESI <- get fault address
; ------------- Check fault address if it is not in system memory
cmp esi,SYSTEM_ADDR ; is fault address in system memory?
jae PageFault8 ; yes, system error
; ------------- Handle system exception
test byte [ebp+TRAP_Int+REGI_CS],3 ; privilege level?
jnz PageFault1 ; user trap
call DoException ; handle system exception
jmp PageFault9
; ------------- Re-enable interrupts
PageFault1: test byte [ebp+TRAP_Int+REGI_Flags+1],EFLAGS_IF>>8; enabled?
jz PageFault2 ; interrupts were not enabled
sti ; re-enable interrupts
; ------------- Get current task (-> EDI)
PageFault2: CURRENT edi ; EDI <- get current task
; ------------- Find nearest higher region in virtual memory (-> EBX)
mov edx,[edi+TASK_VirtMem] ; EDX<-virtual memory descriptor
mov eax,esi ; EAX <- fault address
call VirtMemFindAddr ; find region in virtual memory
jc PageFault5 ; region not found
; ------------- Check if region is OK
cmp eax,[ebx+VMR_Start] ; is region OK?
jae PageFault4 ; region is OK
; ------------- Address is below region, check if it can grow down
test byte [ebx+VMR_Flags+1],VMR_GROWSDOWN>>8 ; grows down?
jz PageFault5 ; invalid address
; ------------- Region can grow down, check access distance of user stack
test byte [ebp+TRAP_Code],B2 ; user mode?
jz PageFault3 ; no, it came from system mode
add eax,10000h+32*4 ; reserve for "enter 65535,31"
cmp eax,[ebp+TRAP_ESP] ; check distance of stack
jb PageFault5 ; invalid distance
; ------------- Expand stack
PageFault3:
; good area
PageFault4:
; Bad area
PageFault5:
; ------------- TODO!!!!! System error
PageFault8: mov al,[VideoRows]
sub al,2
mov [VideoRow],al
mov byte [VideoPos],0
push esi ; push fault address
mov esi,PageFaultTxt
call DebOutText ; display text
; ------------- Task index
mov eax,[edi+TASK_TSSIndex] ; ESI <- error code
call DebOutNum
mov esi,PageFaultTxt1
call DebOutText ; display text
; ------------- Get error code (-> EAX)
mov eax,[ebp+TRAP_Code] ; ESI <- error code
call DebOutNum
mov esi,PageFaultTxt2
call DebOutText ; display text
; ------------- Get fault address
pop eax ; EAX <- fault address
call DebOutHexD
mov esi,PageFaultTxt3
call DebOutText ; display text
; ------------- Get instruction address
mov eax,[ebp+TRAP_Int+REGI_EIP]
call DebOutHexD
mov esi,PageFaultTxt4
call DebOutText ; display text
; ------------- Pop registers
PageFault9: pop es ; pop ES
pop ds ; pop DS
popa ; pop all registers
add esp,byte 4 ; skip error code
iret
; -----------------------------------------------------------------------------
; Data
; -----------------------------------------------------------------------------
DATA_SECTION
PageFaultTxt: db ">>>>> Task ",0
PageFaultTxt1: db ", Page fault ",0
PageFaultTxt2: db " at ",0
PageFaultTxt3: db " called from ",0
PageFaultTxt4: db " <<<<<",10,0
; ------------- Memory page map of physical pages
PageMap: dd 0 ; array of page descriptors
PageMapNum: dd 0 ; number of page descriptors
PageMapSize: dd 0 ; size of array of page descriptors
PageZero: dd 0 ; pointer to zero page descriptor
; ------------- Page cache lock
%ifdef SMP
PageCacheLock: SPINLOCK ; page cache lock
%endif
|