↑ Back to main site.

EPOS

Experimental Protected-mode Operating System

paging.c
/*
EPOS
http://www.atanaslaskov.com/epos/

FILE:        paging.c
DESCRIPTION: Implementation of IA32 paging

BSD LICENSE
Copyright (c) 2006, Atanas Laskov
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
   1. Redistributions of source code must retain the above copyright notice,
   this list of conditions and the following disclaimer.
   2. Redistributions in binary form must reproduce the above copyright notice,
   this list of conditions and the following disclaimer in the documentation
   and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL ATANAS LASKOV BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include "..\access\access.h"
#include "..\init\init.h"
#include "..\service\service.h"
#include "..\gdt\gdt.h"
#include "..\memory\memory.h"
#include "..\process\process.h"

#include "paging.h"

// Data structures
//
struct PageTable{
    struct PageTableEntire arEntires[PAGE_ENTIRE_CNT];
};

struct PageDir{
    struct PageDirectoryEntire arEntires[PAGE_ENTIRE_CNT];
};

struct PageDirInfo{
    unsigned long dwCurrentPageTableCnt;
    struct PageTable *arPageTables;
    HMEMORY hMem;
};

struct PageDir       *g_arPageDirs;
struct PageDirInfo    g_arPageDirsI[MAX_PROCESS_CNT+1];
HMEMORY g_hMemDir;

// Convert kernel memory pointer to a global
// physical memory pointer
//
unsigned long knlptr2physic(void *a){
    return ((unsigned long)a)+0x600;
}

// Initialize paging
//
void initPaging()
{
    struct MemBlock Info;
    unsigned long dwPageDirStart;
    unsigned long dir;

    // Allocate memory for page directories
    //
    if(memAlloc(&g_hMemDir,  sizeof(struct PageDir)*(MAX_PROCESS_CNT+1) )
        == MEMRET_OUTOFMEM) {
       
        // Halt disgracefully
        puts("\nKRN: Out of memory");
        while(1);
    }
   
    g_arPageDirs = memFormPointer(g_hMemDir);
   
    memBlockInfo(g_hMemDir,&Info);
    dwPageDirStart = Info.dwStart;

    // Initialize page directories
    //
    for(dir=0; dir<MAX_PROCESS_CNT+1; dir++)
        g_arPageDirsI[dir].dwCurrentPageTableCnt = 0;

    // Enable the kernel page directory able to address the maximal amount
    // of phisical memory
    //
    initExpandDirectoryTables(memGetPhysicalMemorySize(), PAGING_DIR_KERNEL);
    initFormatDirectory(PAGING_DIR_KERNEL);
    pageMapPages(PAGING_DIR_KERNEL, 0, 0, memGetPhysicalMemorySize());

    // Enable paging and select the kernel page directory
    //
    access_cli;
    access_loadPDBR(dwPageDirStart);   // load CR3
    access_enablePaging();             // update CR0
    access_sti;
}

// Free up memory used by page tables
//
void initFreeDirectoryTables(unsigned long nDirectoryIndex)
{
    if( g_arPageDirsI[nDirectoryIndex].dwCurrentPageTableCnt==0 )return;
   
    memFree(g_arPageDirsI[nDirectoryIndex].hMem);
    g_arPageDirsI[nDirectoryIndex].dwCurrentPageTableCnt=0;
}

// Expand paging directory tables
//
void initExpandDirectoryTables(unsigned long dwNewSize,
                               unsigned long nDirectoryIndex)
{
    unsigned long dwNewPgCnt = dwNewSize/(PSTEP*1024)+1;
   
    unsigned long dwMinSize =
       dwNewPgCnt<g_arPageDirsI[nDirectoryIndex].dwCurrentPageTableCnt ?
       dwNewPgCnt:g_arPageDirsI[nDirectoryIndex].dwCurrentPageTableCnt;
       
    HMEMORY hMem;
    struct PageTable *arNewPageTables;
    MEMRET r;

    // Re-alocate memory (bigger blocks)
    //
    if( memAlloc(&hMem, dwNewPgCnt*PSTEP) != MEMRET_OK )
        // Halt disgracefully...
        tmhalt("Kernel out of memory. Unable to resize page directory.");

    arNewPageTables = memFormPointer(hMem);

    // Transfer the content of old page directory tables
    //
    if(g_arPageDirsI[nDirectoryIndex].dwCurrentPageTableCnt!=0)
    {
        kMemCpy(
            arNewPageTables,
            g_arPageDirsI[nDirectoryIndex].arPageTables,
            dwMinSize*PSTEP
        );
           
        memFree(g_arPageDirsI[nDirectoryIndex].hMem);
    }

    // Point the page table array to the new memory
    //
    g_arPageDirsI[nDirectoryIndex].hMem = hMem;
    g_arPageDirsI[nDirectoryIndex].dwCurrentPageTableCnt = dwNewPgCnt;
    g_arPageDirsI[nDirectoryIndex].arPageTables = arNewPageTables;
}

// Initialize a page directory
//
void initFormatDirectory(unsigned long nDirI)
{
    unsigned long e=0;
    struct PageDirectoryEntire *direntire;

    //Format directory entities
    //
    for(e=0; e<g_arPageDirsI[nDirI].dwCurrentPageTableCnt; e++)
    {
        direntire = g_arPageDirs[nDirI].arEntires+e;
        direntire->present     = PAGE_PRESENT;
        direntire->rw          = PAGE_RW;
        direntire->us          = PAGE_USER;
        direntire->pwt         = PAGE_PWT_WRITEBACK; // ?
        direntire->cashdisable = PAGE_ENABLECASH;
        direntire->accessed    = 0;
        direntire->reserved    = 0;
        direntire->pgsize      = PAGE_4K;
        direntire->global      = 0;
        direntire->available   = 0;
        direntire->page_base   =
        knlptr2physic(g_arPageDirsI[nDirI].arPageTables[e].arEntires)>>12;
    }
}

// Map pages to memory
//
void initMapPage(unsigned long dwPageIndex, unsigned long dwTableIndex,
                 unsigned long dwDirI, unsigned long dwPhysAdr)
{
    struct PageTableEntire     *tblentire;

    tblentire =
     g_arPageDirsI[dwDirI].arPageTables[dwTableIndex].arEntires+dwPageIndex;
     
    tblentire->present     = 1;
    tblentire->rw          = PAGE_RW;
    tblentire->us          = PAGE_USER;
    tblentire->pwt         = PAGE_PWT_WRITEBACK; //?
    tblentire->cashdisable = PAGE_ENABLECASH;    //PAGE_ENABLECASH;
    tblentire->accessed    = 0;
    tblentire->dirty       = 0;
    tblentire->reserved    = 0;
    tblentire->global      = 0;
    tblentire->available   = 0;
    tblentire->page_base   = dwPhysAdr>>12;
}

void initMapAddress(unsigned long dwLogAddress, unsigned long dwPhysAdr,
                    unsigned long dwDirectoryIndex)
{
    unsigned long dwTableIndex = dwLogAddress>>22;
    unsigned long dwPageIndex  = (dwLogAddress&0x3FFFFF)>>12;

    initMapPage(dwPageIndex, dwTableIndex, dwDirectoryIndex, dwPhysAdr);
}


// Get page directory size
//
void pageDirectorySize(unsigned long dwDirectoryIndex, unsigned long dwNewSize)
{
    unsigned long dwOldPgTbCnt =
        g_arPageDirsI[PAGING_DIR_KERNEL].dwCurrentPageTableCnt;
   
    unsigned long dwNewPgTbCnt = dwNewSize/(PSTEP*1024)+1;

    initExpandDirectoryTables(dwNewSize, dwDirectoryIndex);
    initFormatDirectory(dwDirectoryIndex);

    if(dwNewPgTbCnt>dwOldPgTbCnt)
        pageMapPages(
            dwDirectoryIndex,
            dwOldPgTbCnt*PSTEP*1024,
            dwOldPgTbCnt*PSTEP*1024,
            (dwNewPgTbCnt-dwOldPgTbCnt)*PSTEP*1024
        );
   
    //intiMapTables(dwOldPgTbCnt, dwNewPgTbCnt-dwOldPgTbCnt,
    //dwOldPgTbCnt, dwDirectoryIndex);

}

void pageDirectoryFree(unsigned long dwDirI) {
    initFreeDirectoryTables(dwDirI);
}

void pageDirectorySwitch(unsigned long dwDirI) {
    access_loadPDBR( knlptr2physic(g_arPageDirs[dwDirI].arEntires) );
}

void pageMapPages(unsigned long dwDirectory, unsigned long dwLogbase,
                  unsigned long dwPhysbase, unsigned long dwSize)
{
    unsigned long i;

    for(i=dwLogbase; i<dwLogbase+dwSize; i+=PSTEP)
    {
        initMapAddress(i, dwPhysbase, dwDirectory);
        dwPhysbase += PSTEP;
    }
}

unsigned pageGetPhysicalAddress(unsigned long dwDir, unsigned long dwLogAdr,
                                unsigned long *pPhysicalAdr)
{
    unsigned long dwTableI = dwLogAdr>>22;
    unsigned long dwPageI  = (dwLogAdr&0x3FFFFF)>>12;
    struct PageTableEntire     *tblentire;

    if(dwTableI>=g_arPageDirsI[dwDir].dwCurrentPageTableCnt)
    {
        putc('*');
        return PAGE_OUT_OF_DIRECTORY;
    }

    tblentire = g_arPageDirsI[dwDir].arPageTables[dwTableI].arEntires+dwPageI;
    *pPhysicalAdr = tblentire->page_base<<12;
    return PAGE_OK;
}