CMSC 412 Programming Assignment #3

Due April 12, 2002 (10:00 AM)

Introduction

The purpose of this project is to add paging to your project.  This will require many small, but difficult changes to your project.

Changing the Project to Use Page Tables

The first step is to modify your project to use page tables and segmentation rather than just segments to provide memory protection.  To enable using page tables, every region of memory your access (both kernel and data segment) must have an entry in a page table.  The way this will work is that there will be a single page table for all kernel only threads, and a page table for each user process.  In addition, the page tables for user mode processes will also contain entries to address the kernel mode memory.  The memory layout for this is shown in Figure 1.

 

VA 0x0000 0000

 

 

 

Kernel Memory

Start of kernel memory

(map all physical memory here)

VA 0x8000 0000

User Memory

 

 

 

 

<gap>

 

 

 

 

 

VA 0xFFFF FFFF

Stack Memory

 

 

 

 

Stack Starts Here

 

 

 

 

Figure 1.

 

The kernel memory should be a one to one mapping of all of the physical memory in the processor (this limits the physical memory of the processor to 2GB, but this is not a critical limit for this project).  The page table entries for this memory should be marked so that this memory is only accessible from kernel mode (i.e. the userMode bit in the page directory and page table should be 0). To make this change, you should start by creating a page directory and page table entries for the kernel threads by writing a function that initializes the page tables and enables paging mode in the processor.  This routine should be called from Init_Mem.

 

To setup page tables, you will need to allocate a page directory (via Alloc_Page) and then allocate page tables for the entire region that will be mapped into this memory context.  You will need to fill out the appropriate fields in the page tables and page directories.  Finally, to enable paging for the first time, you will need to write an assembly routine (Enable_Paging(ptbr)) that will take the base address of your page directory as a parameter and then load the passed ptbr into register cr3, and then set the paging bit in cr0 (the MSB, bit 31). You should be able to do this step and test it by itself (by temporarily giving user mode access to the kernel pages).

 

The next step is to modify your user processes to all use pages in the user region.  This is a two-step process.  First, you need to allocate a page directory for this user space space.  You should copy all of the mappings from the kernel mode page directory for those memory regions in the low range of memory.  Next you need to allocate page table entries for the user processes text and data regions.  Do not allocate extra space for the stack here.  Finally, you should allocate space for one page of stack memory at the end of the virtual address range (i.e. the last entry in the last page table).  For the user space page mappings, make sure to enable the userMode bits in both the page directory and page table entries.

 

You will also need to change some aspects of how the code from project #1 sets things up.  The base address for the user mode process should be 0x8000 0000, and the limit should be 0x8000 0000.  This will allow the user space process to think that it’s virtual location 0 is the 2GB point in the page layout and will greatly simplify your kernel compared to traditional paged systems.  You will also need to add code to switch the PTBR register (cr3) as part of a context switch (where you load the LDT of the user context, you should also load the ptbr which should have an entry in the userContext structure).

 

You will also need to create a second version of Alloc_Page (in mem.c).  This version should be called Alloc_Pageable_Page.  The primary difference is that any page allocated by this routine should have a special flag PAGE_PAGEABLE set in the flags field of its entry in the struct Page data structure (see mem.h).  All pages (but not page directories and page tables) for a user space process should be allocated using this routine.

Handling Page Faults

 

One of the key features of using paging is to have the operating system handle page faults.  To do this you will need to write a page fault interrupt handler.  The first thing the page fault handler will need to do is to figure out the address of the page fault.  It will then need to determine an appropriate action to take.  Possible reasons for a page fault, and the action to take are shown in the table in Figure 2.

 

To determine the faulting address, the fault handler should read register cr2.  Also, the errorCode field of the interrupt state contains information about the faulting access. This information is defined in the pageFaultErrorCode structure defined in paging.h

 

Cause

Indication

Action

Stack growing to new page

Fault is within one page of the current stack limit

Allocate a new page and continue.

Fault for paged out page

Bits in page table indicate page is on disk

Read page from paging device (sector indicated in PTE) and continue.

Fault for invalid address

None of the other conditions apply

Terminate user process

 

Figure 2.

Paging Out Pages

At some point, your operating system will run out of page frames to assign to processes.  In this case, you will need to pick a page to evict from memory and write it to the backing store (paging file). You should implement a version of pseudo-LRU.  Use the reference bit in the page tables to keep track of how frequently pages are accessed. To do this, add a clock field to the struct Page structure in mem.h.  You should update the clock on every page fault.

 

You will also need to manage the use of the paging file. The paging file consists of a group of consecutive 512 bytes disk blocks.  Calling the routine getPagingFileInfo function in pfat.c,  will return the first disk block number of the paging file and the number of disk blocks in the paging file.  Each page will consume 8 consecutive disk blocks.  To read/write the paging file, use the functions IDE_Read and IDE_Write.

 

When a page is paged out, you will need to update the page table entry for that page to clear the valid bit.  You will probably want to put the first disk block that contains the page into the pageBaseAddr field of the page table.  The kernelInfo bits (3 bits holding a number from 0-7) can be used to indicate that the page is on disk rather than not valid.  You should also invalidate the TLB as part of the page out operation.  Although the x86 processor supports a selective invalidate, you can invalidate the entire TLB for this project.  Any move to cr3 (the PTBR) will flush the TLB. To do this, write an assembly language routine invalidateTLB that saves cr3 into a temporary register and then writes the temporary register back to cr3.

Page Ins

When you bring a page in off disk, you may discard its disk version (i.e. free the disk space used by the page).  This will simplify your paging system, but will require that when a page is removed from memory it must always be written to the backing store (since even clean pages no longer have a version on disk).

Process Termination

As part of process termination, you will need to free the memory associated with a process. This includes freeing the page frames used by the process, freeing the page tables and page directories.  In addition, you will need to release the backing store space used by any pages of the terminating process.

Creating a paging file

You will need to create a paging file as part of your harddisk image (hd.img).  The easiest way to do this is to add a rule to the Makefile for userProgs that includes a call to create an empy swapfile and then add the name of the swapfile to the list of files passed to buildFat.  The following rule should do what is required:

            swapfile:

             perl ../zerofile swapfile 8192

 

What to turn in

You should submit a compressed tar file for the project3 directory. 

Files

            paging.h          - header file with information about page table formats.

            pfat.c                - an updated pfat.c with the getPagingFileInfo function.

            rec.c                 - a recursive program that will use all of the free pages and force pagging.