Main Page   Modules   Alphabetical List   Data Structures   File List   Data Fields   Globals   Related Pages  

paging.c File Reference

Paging manager. More...

#include <const.h>
#include <arch/i386.h>
#include <arch/mem.h>
#include <arch/mmx.h>
#include <kernel/console.h>
#include <kernel/multiboot.h>
#include <kernel/kernel_map.h>
#include <kernel/keyboard.h>
#include <kernel/speaker.h>
#include <kernel/task.h>
#include <arch/paging.h>

Go to the source code of this file.

Functions

dword pop_frame ()
 Pop a free frame from the free frames stack.
Exceptions:
NULL  Out-of-memory.
Note:
The function returns the address expressed in the page number format. This value will be multiplied by PAGE_SIZE to obtain the real physical address of the frame.


void push_frame (dword p_addr)
 Push a free frame into the free frames stack.
Parameters:
p_addr  The page number you want to push.
Note:
The value p_addr must be the page number!
If you know the physical address you have to divide this value to PAGE_SIZE.


bool map_page (dword vir_addr, dword phys_addr, word attribs)
 Map a physical address into a virtual address for the current address space.
Parameters:
vir_addr  The virtual address.
phys_addr  The physical address.
attribs  The attributes mask for this page.
Returns:


void unmap_page (dword vir_addr)
 Unmap a page, but doesn't destroy the physical frame.
Parameters:
vir_addr  The virtual address you want to unmap.


void delete_page (dword vir_addr)
 Unmap a page and destroy the physical frame where the address is mapped.
Parameters:
vir_addr  The virtual address you want to delete.


void * get_temp_page ()
 Get a free virtual page from the temporary memory area.
Returns:
The address of the temporary page or NULL if out of memory.


void free_temp_page (void *p, bool do_delete)
 Free a virtual temporary page allocated by get_temp_page().
Parameters:
p  The temporary page virtual address.
do_delete 
  • TRUE free the page and also the physical frame;
  • FALSE unmap the page only, do not free the physical frame.


dword vir_to_phys (dword vir_addr)
 Translate a virtual address into the physical address.
Parameters:
vir_addr  The virtual address to be translated.


int page_fault_handler (dword err_code, dword cr2)
 This is the page-fault handler. Every time a page-fault occurs, this routine must be invoked.
Parameters:
err_code  The error code from the CPU.
cr2  The address that caused the fault.
Returns:
  • 0 success;
  • < 0 otherwise.


void init_free_frames ()
 Initialize the free frames stack.
Note:
This routine must be called once, when the system is initializing.


void init_paging ()
 Initialize the paging mechanism.
Note:
This routine must be called once, when the system is initializing.
Warning:
This routine must be the first routine of the kernel initialization process!!!


void dump_dirty_pages ()
 Dump the dirty pages to stdout.

void dump_free_frames ()
 Dump the free frames to stdout.

void check_free_frames_integrity ()
 This process checks the free frames stack integrity.
Warning:
This is a process so it must end with auto_kill();.



Variables

volatile dword PHYS_MEM_DIM
 The physical memory dimension. Initialized in paging.c.

dword KERNEL_TOP
 The kernel bounds. These variables must be declared externally into the linker script.

dword KERNEL_END_TEXT
 The kernel bounds. These variables must be declared externally into the linker script.

dwordK_VIR_END
 End of kernel virtual space.

dwordfree_frames = (dword *)&KERNEL_TOP
 Free-frames stack - placed just above kernel memory.

volatile dword K_PDBR [PAGE_SIZE/sizeof(dword)]
 Master page directory.


Detailed Description

Paging manager.

Author:
Andrea Righi <drizzt@inwind.it>
Date:
Last update:
2003-12-16 Andrea Righi: Fixed get_temp_page() and free_temp_page() bugs.
2004-01-24 Andrea Righi: Added the NULL-pointer mapping check in the page fault handler.
2004-02-07 Andrea Righi: Added the fast_clear_page using MMX extensions to clear a page after is mapped.
Note:
Copyright (©) 2003 Andrea Righi
Note:
This is the GNU linker script (LD) where are declared some external variables used by the paging manager.
/* kernel.ld
 *      The GNU linker script for minirighi.
 *      Author: Andrea Righi <drizzt@inwind.it>
 */

OUTPUT_FORMAT("elf32-i386","elf32-i386","elf32-i386")

K_VIR_START = 0xC0000000;
K_PHYS_START = 0x100000;
K_STACK_SIZE = 0x1000;
PAGE_SIZE = 0x1000;

ENTRY (_start)

SECTIONS
{
        .text K_VIR_START : AT(K_PHYS_START)
        {
                KERNEL_TEXT = .;
                *(.text*)
                *(.rodata*)
                . = ALIGN(PAGE_SIZE);
                KERNEL_END_TEXT = .;
        }

        .data : AT(K_PHYS_START + (KERNEL_DATA - KERNEL_TEXT))
        {
                KERNEL_DATA = .;
                *(.data*)
                . = ALIGN(PAGE_SIZE);
                KERNEL_END_DATA = .;
        }

        .bss : AT(K_PHYS_START + (KERNEL_BSS - KERNEL_TEXT))
        {
                _stack_end = .;
                . += K_STACK_SIZE;
                _stack_start = .;
                K_STACK_START = .;

                KERNEL_BSS = .;
                *(.bss*)
                . = ALIGN(PAGE_SIZE);
                *(COMMON*)
                . = ALIGN(PAGE_SIZE);
                KERNEL_END_BSS = .;
        }
        . = ALIGN(PAGE_SIZE);
        KERNEL_TOP = .;
}

Definition in file paging.c.


Function Documentation

void check_free_frames_integrity  
 

This process checks the free frames stack integrity.

Warning:
This is a process so it must end with auto_kill();.

Definition at line 545 of file paging.c.

00546 {
00547         dword flag, i;
00548         dword *j;
00549 
00550         kprintf("\n\rChecking free frames integrity (%s)...\n\r", get_pname());
00551         for (i=P_ADDR_16MB; i<ADDR_TO_PAGE(PHYS_MEM_DIM); i++)
00552         {
00553                 flag=0;
00554                 for (j=free_frames; j<K_VIR_END; j++)
00555                 {
00556                         if (*j == i) flag++;
00557                 }
00558                 if (flag>1) kprintf("\n\rError! frame:%#010x", i);
00559         }
00560         beep();
00561         kprintf("\n\rCheck done.\n\r");
00562         _exit( 0 );
00563 }

void delete_page dword    vir_addr
 

Unmap a page and destroy the physical frame where the address is mapped.

Parameters:
vir_addr  The virtual address you want to delete.

Definition at line 224 of file paging.c.

00225 {
00226         dword temp;
00227         dword IF = GET_IF();
00228 
00229         // Align address to the page boundary //
00230         vir_addr = PAGE_ALIGN(vir_addr);
00231 
00232         if (*ADDR_TO_PDE(vir_addr) == NULL) return;
00233         if (*ADDR_TO_PTE(vir_addr) == NULL) return;
00234 
00235         disable();
00236 
00237         // Push the physical frame into the free frames stack //
00238         if (
00239                 (vir_to_phys(vir_addr) >= DMA_MEMORY_END) &&
00240                 (vir_to_phys(vir_addr) < PHYS_MEM_DIM)
00241         )
00242                 push_frame(vir_to_phys(vir_addr)/PAGE_SIZE);
00243 
00244         // Unmap the page //
00245         *ADDR_TO_PTE(vir_addr) = NULL;
00246 
00247         // Invalidate the page in the TLB cache //
00248         flush_tlb_single(vir_addr);
00249 
00250         // Check if it is possible to deallocate the frame
00251         // of the page table used to map the address
00252         // So let's examine all entries in the page table
00253         // where the address is mapped.
00254 
00255         for(    temp = PAGE_DIR_ALIGN(vir_addr);
00256                 temp < PAGE_DIR_ALIGN_UP(vir_addr);
00257                 temp += PAGE_SIZE )
00258 
00259                 if (*ADDR_TO_PTE(temp) != NULL)
00260                 {
00261                         SET_IF(IF);
00262                         return;
00263                 }
00264 
00265         // No PTEs found... deallocate the page table! //
00266         push_frame(*ADDR_TO_PDE(vir_addr)/PAGE_SIZE);
00267         *ADDR_TO_PDE(vir_addr) = NULL;
00268 
00269         // Ivalidate the whole TLB cache //
00270         flush_tlb_all();
00271 
00272         // ...and update the master page directory! //
00273         if (vir_addr >= K_VIR_START)
00274                 K_PDBR[vir_addr/(PAGE_SIZE*1024)] = NULL;
00275 
00276         SET_IF(IF);
00277 }

void dump_dirty_pages  
 

Dump the dirty pages to stdout.

Definition at line 493 of file paging.c.

00494 {
00495 // Show all the dirty pages //
00496 
00497         dword vir_addr;
00498         dword display=1;
00499 
00500         // Print all the dirty pages //
00501         kprintf("\n\rDirty pages:\n\r");
00502         for (vir_addr = 0; vir_addr<PAGE_TABLE_MAP; vir_addr += PAGE_SIZE)
00503                 if (*ADDR_TO_PDE(vir_addr) != NULL)
00504                         if ((*ADDR_TO_PTE(vir_addr) & P_DIRTY) == P_DIRTY)
00505                         {
00506                                 if (!(display = display++ % 24))
00507                                         if (kgetchar() == CTRL_C)
00508                                         {
00509                                                 kprintf("\n\r");
00510                                                 return;
00511                                         }
00512                                 kprintf("\n\rvir_addr = %#010x\tpage_entry = %#010x",
00513                                         vir_addr, *(ADDR_TO_PTE(vir_addr)));
00514                         }
00515         kprintf("\n\r");
00516 }

void dump_free_frames  
 

Dump the free frames to stdout.

Definition at line 520 of file paging.c.

00521 {
00522         dword *f = free_frames;
00523         dword display=1;
00524 
00525         kprintf("\n\rFree frames list: (KERNEL_TOP=%#010x)\n\r", (dword)&KERNEL_TOP);
00526         for(;;)
00527         {
00528                 if (*f == NULL) break;
00529                 if (!(display = display++ % 24))
00530                         if (kgetchar() == CTRL_C)
00531                         {
00532                                 kprintf("\n\r");
00533                                 return;
00534                         }
00535                 kprintf("\n\rframe=%#010x &frame=%#010x", *f, (dword)f);
00536                 f++;
00537         }
00538         kprintf("\n\r");
00539 }

void free_temp_page void *    p,
bool    do_delete
 

Free a virtual temporary page allocated by get_temp_page().

Parameters:
p  The temporary page virtual address.
do_delete 
  • TRUE free the page and also the physical frame;
  • FALSE unmap the page only, do not free the physical frame.

Definition at line 333 of file paging.c.

00334 {
00335         // Is the page into the temporary memory range?!                //
00336         if ( (((dword)p >= (K_MEM_TEMP_START))) && ((dword)p < K_MEM_TEMP_END) )
00337         {
00338                 if ( do_delete )
00339                         delete_page( (dword)p );
00340                 else
00341                         unmap_page( (dword)p );
00342         }
00343 }

void* get_temp_page  
 

Get a free virtual page from the temporary memory area.

Returns:
The address of the temporary page or NULL if out of memory.

Definition at line 285 of file paging.c.

00286 {
00287         byte *p = (byte *)K_MEM_TEMP_START;
00288         size_t frame;
00289         dword IF = GET_IF();
00290 
00291         disable();
00292 
00293         while(TRUE)
00294         {
00295                 if ( *ADDR_TO_PDE((size_t)p) )
00296                 {
00297                         if ( *ADDR_TO_PTE((size_t)p) )
00298                         {
00299                                 p += PAGE_SIZE;
00300                                 if (p >= (byte *)K_MEM_TEMP_END)
00301                                 {
00302                                         // Out of temporary memory!     //
00303                                         SET_IF(IF);
00304                                         return( NULL );
00305                                 }
00306                                 continue;
00307                         }
00308                 }
00309                 // OK! A free temporary page has been found!            //
00310                 // Now map this page to a free frame and return the     //
00311                 // virtual address of this page, or NULL if there is    //
00312                 // no free frame.
00313                 frame = ( pop_frame()*PAGE_SIZE );
00314                 if ( frame==NULL )
00315                 {
00316                         p = NULL;
00317                 }
00318                 else if ( !map_page((dword)p, frame, P_PRESENT | P_WRITE) )
00319                 {
00320                         p = NULL;
00321                 }
00322                 SET_IF(IF);
00323                 return( (void *)p );
00324         }
00325 }

void init_free_frames  
 

Initialize the free frames stack.

Note:
This routine must be called once, when the system is initializing.

Definition at line 431 of file paging.c.

00432 {
00433         // First physical 16MB are reserved for kernel, BIOS & DMA      //
00434         // so let's start with free memory area at 16MB                 //
00435         register dword p_addr=P_ADDR_16MB;
00436 
00437         K_VIR_END = free_frames;
00438         while (p_addr < ADDR_TO_PAGE(PHYS_MEM_DIM))
00439         {
00440                 *K_VIR_END++=p_addr++;
00441         }
00442 
00443         // Last frame is NULL => out of physical memory.                //
00444         // Kernel virtual address space ends here:                      //
00445         *K_VIR_END=NULL;
00446 }

void init_paging  
 

Initialize the paging mechanism.

Note:
This routine must be called once, when the system is initializing.
Warning:
This routine must be the first routine of the kernel initialization process!!!

Definition at line 456 of file paging.c.

00457 {
00458         extern multiboot_info_t *boot_info;
00459         size_t addr;
00460 
00461         if (boot_info->flags & 0x02)
00462         {
00463                 PHYS_MEM_DIM = (boot_info->mem_upper)*1024;
00464         }
00465         else
00466         {
00467                 // Error reading multiboot informations... halting!.
00468                 halt();
00469         }
00470 
00471         // Initialize free frames stack //
00472         init_free_frames();
00473 
00474         // Unmap first 4MB identical-map pages.
00475         *ADDR_TO_PDE(0) = NULL;
00476 
00477         // Flush the whole TLB cache.
00478         flush_tlb_all();
00479 
00480         // Map part of physical memory into the kernel address space //
00481         for (addr=PHYS_MEM_START; addr<PHYS_MEM_END; addr+=PAGE_SIZE)
00482                 map_page(addr, addr-PHYS_MEM_START, P_PRESENT | P_WRITE);
00483 
00484         // Initialize master page directory //
00485         for (addr=0; addr<1024; addr++)
00486                 K_PDBR[addr] = ((dword *)PAGE_DIR_MAP)[addr];
00487 }

bool map_page dword    vir_addr,
dword    phys_addr,
word    attribs
 

Map a physical address into a virtual address for the current address space.

Parameters:
vir_addr  The virtual address.
phys_addr  The physical address.
attribs  The attributes mask for this page.
Returns:

Definition at line 115 of file paging.c.

00116 {
00117         dword *PTE;
00118         dword i;
00119         dword IF = GET_IF();
00120 
00121         // Round virtual & physical address to the page boundary //
00122         vir_addr = PAGE_ALIGN(vir_addr);
00123         phys_addr = PAGE_ALIGN(phys_addr);
00124 
00125         // Get only valid attribs //
00126         attribs &= (PAGE_SIZE-1);
00127 
00128         disable();
00129 
00130         // If the page directory entry is NULL must be created //
00131         if (*ADDR_TO_PDE(vir_addr) == NULL)
00132         {
00133                 // Create a new page table //
00134                 PTE = (dword *)(pop_frame() * PAGE_SIZE);
00135                 if (PTE == NULL)
00136                 {
00137                         // Out of memory!!! //
00138                         SET_IF(IF);
00139                         return(FALSE);
00140                 }
00141 
00142                 // Set the PDE as present, user level, read-write //
00143                 *ADDR_TO_PDE(vir_addr) = (dword)PTE | P_PRESENT | P_USER | P_WRITE;
00144 
00145                 // Flush the whole TLB cache //
00146                 flush_tlb_all();
00147 
00148                 // NULL every PTE entry //
00149                 for (i=PAGE_DIR_ALIGN(vir_addr); i<PAGE_DIR_ALIGN_UP(vir_addr); i+=PAGE_SIZE)
00150                 {
00151                         *ADDR_TO_PTE(i) = NULL;
00152                 }
00153 
00154                 // Update master page directory //
00155                 if (vir_addr >= K_VIR_START)
00156                         K_PDBR[vir_addr/(PAGE_SIZE*1024)] = *ADDR_TO_PDE(vir_addr);
00157         }
00158 
00159         // Store the physical address into the page table entry //
00160         *ADDR_TO_PTE(vir_addr) = (dword)phys_addr | attribs;
00161 
00162         // Invalidate the page in the TLB cache //
00163         flush_tlb_single(vir_addr);
00164 
00165         SET_IF(IF);
00166         return(TRUE);
00167 }

dword pop_frame  
 

Pop a free frame from the free frames stack.

Exceptions:
NULL  Out-of-memory.
Note:
The function returns the address expressed in the page number format. This value will be multiplied by PAGE_SIZE to obtain the real physical address of the frame.

Definition at line 61 of file paging.c.

00062 {
00063         dword ret;
00064         dword IF = GET_IF();
00065 
00066         disable();
00067 
00068         if (*free_frames != NULL)
00069         {
00070                 ret = *free_frames++;
00071 
00072                 SET_IF(IF);
00073                 return(ret);
00074         }
00075 
00076         // Out of memory!!! //
00077         SET_IF(IF);
00078         return(NULL);
00079 }

void push_frame dword    p_addr
 

Push a free frame into the free frames stack.

Parameters:
p_addr  The page number you want to push.
Note:
The value p_addr must be the page number!
If you know the physical address you have to divide this value to PAGE_SIZE.

Definition at line 88 of file paging.c.

00089 {
00090         dword IF = GET_IF();
00091 
00092         disable();
00093 
00094         // Push the frame into free frames stack //
00095         if ((dword)free_frames > ((dword)&KERNEL_TOP))
00096         {
00097                 *(--free_frames)=p_addr;
00098         }
00099 
00100         SET_IF(IF);
00101 }

void unmap_page dword    vir_addr
 

Unmap a page, but doesn't destroy the physical frame.

Parameters:
vir_addr  The virtual address you want to unmap.

Definition at line 172 of file paging.c.

00173 {
00174         dword temp;
00175         dword IF = GET_IF();
00176 
00177         // Align address to the page boundary //
00178         vir_addr = PAGE_ALIGN(vir_addr);
00179 
00180         if (*ADDR_TO_PDE(vir_addr) == NULL) return;
00181         if (*ADDR_TO_PTE(vir_addr) == NULL) return;
00182 
00183         disable();
00184 
00185         // Unmap the page //
00186         *ADDR_TO_PTE(vir_addr) = NULL;
00187 
00188         // Invalidate the page in the TLB cache //
00189         flush_tlb_single(vir_addr);
00190 
00191         // Check if it is possible to deallocate the frame
00192         // of the page table used to map the address
00193         // So let's examine all entries in the page table
00194         // where the address is mapped.
00195 
00196         for(    temp = PAGE_DIR_ALIGN(vir_addr);
00197                 temp < PAGE_DIR_ALIGN_UP(vir_addr);
00198                 temp += PAGE_SIZE)
00199 
00200                 if (*ADDR_TO_PTE(temp) != NULL)
00201                 {
00202                         SET_IF(IF);
00203                         return;
00204                 }
00205 
00206         // No PTEs found... deallocate the page table! //
00207         push_frame(*ADDR_TO_PDE(vir_addr)/PAGE_SIZE);
00208         *ADDR_TO_PDE(vir_addr) = NULL;
00209 
00210         // Invalidate the whole TLB cache //
00211         flush_tlb_all();
00212 
00213         // ...and update the master page directory! //
00214         if (vir_addr >= K_VIR_START)
00215                 K_PDBR[vir_addr/(PAGE_SIZE*1024)] = NULL;
00216 
00217         SET_IF(IF);
00218 }

dword vir_to_phys dword    vir_addr
 

Translate a virtual address into the physical address.

Parameters:
vir_addr  The virtual address to be translated.

Definition at line 350 of file paging.c.

00351 {
00352         if (*ADDR_TO_PDE(vir_addr) == NULL) return(NULL);
00353         return ((*ADDR_TO_PTE(vir_addr) & -PAGE_SIZE) + (vir_addr % PAGE_SIZE));
00354 }


Variable Documentation

dword* free_frames = (dword *)&KERNEL_TOP
 

Free-frames stack - placed just above kernel memory.

Definition at line 48 of file paging.c.

volatile dword K_PDBR[PAGE_SIZE/sizeof(dword)]
 

Master page directory.

Definition at line 51 of file paging.c.

dword* K_VIR_END
 

End of kernel virtual space.

Definition at line 45 of file paging.c.

dword KERNEL_END_TEXT
 

The kernel bounds. These variables must be declared externally into the linker script.

Definition at line 42 of file paging.c.

dword KERNEL_TOP
 

The kernel bounds. These variables must be declared externally into the linker script.

Definition at line 42 of file paging.c.


Generated on Fri Feb 20 15:32:19 2004 for Minirighi by doxygen1.2.18