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

paging.c

Go to the documentation of this file.
00001 /*!     \file arch/i386/kernel/paging.c
00002  *      \brief Paging manager.
00003  *      \author Andrea Righi <drizzt@inwind.it>
00004  *      \date Last update:\n
00005  *              2003-12-16 Andrea Righi:
00006  *                      Fixed get_temp_page() and free_temp_page()
00007  *                      bugs.\n
00008  *              2004-01-24 Andrea Righi:
00009  *                      Added the NULL-pointer mapping check in the
00010  *                      page fault handler.\n
00011  *              2004-02-07 Andrea Righi:
00012  *                      Added the fast_clear_page using MMX extensions
00013  *                      to clear a page after is mapped.\n
00014  *      \note Copyright (&copy;) 2003 Andrea Righi
00015  *
00016  *      \note
00017  *              This is the GNU linker script (LD) where are declared
00018  *              some external variables used by the paging manager.
00019  *              \include kernel.ld
00020  */
00021 
00022 #include <const.h>
00023 
00024 #include <arch/i386.h>
00025 #include <arch/mem.h>
00026 #include <arch/mmx.h>
00027 
00028 #include <kernel/console.h>
00029 #include <kernel/multiboot.h>
00030 #include <kernel/kernel_map.h>
00031 #include <kernel/keyboard.h>
00032 #include <kernel/speaker.h>
00033 #include <kernel/task.h>
00034 
00035 #include <arch/paging.h>
00036 
00037 //! Total amount of physical memory.
00038 volatile dword PHYS_MEM_DIM;
00039 
00040 //! The kernel bounds. These variables must be declared externally into
00041 //! the linker script.
00042 extern dword KERNEL_TOP, KERNEL_END_TEXT;
00043 
00044 //! End of kernel virtual space.
00045 dword *K_VIR_END;
00046 
00047 //! Free-frames stack - placed just above kernel memory.
00048 dword *free_frames = (dword *)&KERNEL_TOP;
00049 
00050 //! Master page directory.
00051 volatile dword K_PDBR[PAGE_SIZE/sizeof(dword)];
00052 
00053 // --- Free frames stack operators ------------------------------------ //
00054 
00055 //! \brief Pop a free frame from the free frames stack.
00056 //! \exception NULL Out-of-memory.
00057 //! \note
00058 //!     The function returns the address expressed in the page number
00059 //!     format. This value will be multiplied by #PAGE_SIZE to obtain
00060 //!     the real physical address of the frame.
00061 dword pop_frame()
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 }
00080 
00081 //! \brief Push a free frame into the free frames stack.
00082 //! \param p_addr
00083 //!     The page number you want to push.
00084 //! \note
00085 //!     The value \p p_addr must be the page number! \n
00086 //!     If you know the physical address you have to divide this
00087 //!     value to #PAGE_SIZE.
00088 void push_frame(dword p_addr)
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 }
00102 
00103 // --- Mapping operators ---------------------------------------------- //
00104 
00105 //! \brief
00106 //!     Map a physical address into a virtual address for the
00107 //!     current address space.
00108 //! \param vir_addr The virtual address.
00109 //! \param phys_addr The physical address.
00110 //! \param attribs
00111 //!     The attributes mask for this page.
00112 //! \return
00113 //!     \li #TRUE on success;
00114 //!     \li #FALSE if out-of-memory.
00115 bool map_page(dword vir_addr, dword phys_addr, word attribs)
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 }
00168 
00169 //! \brief
00170 //!     Unmap a page, but doesn't destroy the physical frame.
00171 //! \param vir_addr The virtual address you want to unmap.
00172 void unmap_page(dword vir_addr)
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 }
00219 
00220 //! \brief
00221 //!     Unmap a page and destroy the physical frame where the address
00222 //!     is mapped.
00223 //! \param vir_addr The virtual address you want to delete.
00224 void delete_page(dword vir_addr)
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 }
00278 
00279 // --- Temporary pages ------------------------------------------------ //
00280 
00281 //! \brief
00282 //!     Get a free virtual page from the temporary memory area.
00283 //! \return
00284 //!     The address of the temporary page or NULL if out of memory.
00285 void *get_temp_page()
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 }
00326 
00327 //! \brief
00328 //!     Free a virtual temporary page allocated by get_temp_page().
00329 //! \param p The temporary page virtual address.
00330 //! \param do_delete
00331 //!     \li TRUE free the page and also the physical frame;
00332 //!     \li FALSE unmap the page only, do not free the physical frame.
00333 void free_temp_page(void *p, bool do_delete)
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 }
00344 
00345 // --- Generic operatators -------------------------------------------- //
00346 
00347 //! \brief
00348 //!     Translate a virtual address into the physical address.
00349 //! \param vir_addr The virtual address to be translated.
00350 dword vir_to_phys(dword vir_addr)
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 }
00355 
00356 // --- Page fault handler --------------------------------------------- //
00357 
00358 /*! \ingroup Handlers */
00359 //! \brief
00360 //!     This is the page-fault handler. Every time a page-fault
00361 //!     occurs, this routine must be invoked.
00362 //! \param err_code The error code from the CPU.
00363 //! \param cr2 The address that caused the fault.
00364 //! \return
00365 //!     \li 0 success;
00366 //!     \li < 0 otherwise.
00367 int page_fault_handler(dword err_code, dword cr2)
00368 {
00369         dword phys_addr;
00370 
00371 #ifdef PAGE_DEBUG
00372         kprintf("\n\rPAGE_FAULT : %#010x", cr2);
00373 #endif
00374         if( cr2==NULL )
00375         {
00376                 // Cannot map the NULL pointer.
00377                 return( -1 );
00378         }
00379 
00380         // Get a physical free frame.
00381         phys_addr = pop_frame()*PAGE_SIZE;
00382 
00383         // If out of memory => return!
00384         if (phys_addr == NULL)
00385         {
00386                 set_color(LIGHT_RED);
00387                 kprintf("\n\rPage fault handler panic: Out of memory!!!");
00388                 set_color(DEFAULT_COLOR);
00389                 return(-1);
00390         }
00391 
00392         // Get only valid err_code //
00393         err_code &= (P_PRESENT | P_WRITE | P_USER);
00394 
00395         // Map page with correct attributes //
00396         if (cr2 >= K_VIR_START)
00397         {
00398                 if (!(map_page(cr2, phys_addr, P_PRESENT | P_WRITE)))
00399                 {
00400                         // Out of memory!!! //
00401                         set_color(LIGHT_RED);
00402                         kprintf("\n\rPage fault handler panic: Out of memory!!!");
00403                         set_color(DEFAULT_COLOR);
00404                         return( -1 );
00405                 }
00406         }
00407         else
00408         {
00409                 if (!(map_page(cr2, phys_addr, P_PRESENT | P_WRITE | P_USER)))
00410                 {
00411                         // Out of memory!!! //
00412                         set_color(LIGHT_RED);
00413                         kprintf("\n\rPage fault handler panic: Out of memory!!!");
00414                         set_color(DEFAULT_COLOR);
00415                         return( -1 );
00416                 }
00417         }
00418         // Null the new page.
00419         // memsetl((void *)PAGE_ALIGN(cr2), 0, PAGE_SIZE/sizeof(uint32_t));
00420         fast_clear_page((void *)PAGE_ALIGN(cr2));
00421         return( 0 );
00422 }
00423 
00424 // --- Initialization routines ---------------------------------------- //
00425 
00426 //! \brief
00427 //!     Initialize the free frames stack.
00428 //! \note
00429 //!     This routine must be called once, when the system is
00430 //!     initializing.
00431 void init_free_frames()
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 }
00447 
00448 //! \brief
00449 //!     Initialize the paging mechanism.
00450 //! \note
00451 //!     This routine must be called once, when the system is
00452 //!     initializing.
00453 //! \warning
00454 //!     This routine must be the first routine of the kernel
00455 //!     initialization process!!!
00456 void init_paging()
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 }
00488 
00489 // --- Debug functions ------------------------------------------------ //
00490 
00491 //! \brief
00492 //!     Dump the dirty pages to stdout.
00493 void dump_dirty_pages()
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 }
00517 
00518 //! \brief
00519 //!     Dump the free frames to stdout.
00520 void dump_free_frames()
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 }
00540 
00541 //! \brief
00542 //!     This process checks the free frames stack integrity.
00543 //! \warning
00544 //!     This is a process so it must end with auto_kill();
00545 void check_free_frames_integrity()
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 }

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