summaryrefslogtreecommitdiff
path: root/kernel/old/vm.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--kernel/old/vm.c625
1 files changed, 0 insertions, 625 deletions
diff --git a/kernel/old/vm.c b/kernel/old/vm.c
deleted file mode 100644
index 749bed3..0000000
--- a/kernel/old/vm.c
+++ /dev/null
@@ -1,625 +0,0 @@
-/**
-** @file vm.c
-**
-** @author CSCI-452 class of 20245
-**
-** @brief Kernel VM support
-*/
-
-#define KERNEL_SRC
-
-#include <common.h>
-
-#include <vm.h>
-#include <vmtables.h>
-
-#include <kmem.h>
-#include <procs.h>
-#include <x86/arch.h>
-#include <x86/ops.h>
-
-/*
-** PUBLIC GLOBAL VARIABLES
-*/
-
-// created page directory for the kernel
-pde_t *kpdir;
-
-/*
-** PRIVATE FUNCTIONS
-*/
-
-/**
-** Name: vm_isr
-**
-** Description: Page fault handler
-**
-** @param vector Interrupt vector number
-** @param code Error code pushed onto the stack
-*/
-static void vm_isr( int vector, int code ) {
-
- // get whatever information we can from the fault
- pfec_t fault;
- fault.u = (uint32_t) code;
- uint32_t addr = r_cr2();
-
- // report what we found
- sprint( b256,
- "** page fault @ 0x%08x %cP %c %cM %cRSV %c %cPK %cSS %cHLAT %cSGZ",
- addr,
- fault.s.p ? ' ' : '!',
- fault.s.w ? 'W' : 'R',
- fault.s.us ? 'U' : 'S',
- fault.s.rsvd ? ' ' : '!',
- fault.s.id ? 'I' : 'D',
- fault.s.pk ? ' ' : '!',
- fault.s.ss ? ' ' : '!',
- fault.s.hlat ? ' ' : '!',
- fault.s.sgz ? ' ' : '!'
- );
-
- // and give up
- PANIC( 0, b256 );
-}
-
-/**
-** Name: uva2kva
-**
-** Convert a user VA into a kernel address. Works for all addresses -
-** if the address is a page address, the PERMS(va) value will be 0;
-** otherwise, it is the offset into the page.
-**
-** @param pdir Pointer to the page directory to examine
-** @param va Virtual address to check
-*/
-ATTR_UNUSED
-static void *uva2kva( pde_t *pdir, void *va ) {
-
- // find the PMT entry for this address
- pte_t *pte = vm_getpte( pdir, va, false );
- if( pte == NULL ) {
- return NULL;
- }
-
- // get the entry
- pte_t entry = *pte;
-
- // is this a valid address for the user?
- if( IS_PRESENT(entry) ) {
- return NULL;
- }
-
- // is this a system-only page?
- if( IS_SYSTEM(entry) ) {
- return NULL;
- }
-
- // get the physical address
- uint32_t frame = PTE_ADDR(*pte) | PERMS(va);
-
- return (void *) P2V(frame);
-}
-
-
-/*
-** PUBLIC FUNCTIONS
-*/
-
-/**
-** Name: vm_init
-**
-** Description: Initialize the VM module
-*/
-void vm_init( void ) {
-
-#if TRACING_INIT
- cio_puts( " VM" );
-#endif
-
- // set up the kernel's 4K-page directory
- kpdir = vm_mkkvm();
- assert( kpdir != NULL );
-
- // switch to it
- vm_set_kvm();
-
- // install the page fault handler
- install_isr( VEC_PAGE_FAULT, vm_isr );
-}
-
-/**
-** Name: vm_pagedup
-**
-** Duplicate a page of memory
-**
-** @param old Pointer to the first byte of a page
-**
-** @return a pointer to the new, duplicate page, or NULL
-*/
-void *vm_pagedup( void *old ) {
- void *new = (void *) km_page_alloc();
- if( new != NULL ) {
- blkmov( new, old, SZ_PAGE );
- }
- return new;
-}
-
-/**
-** Name: vm_pdedup
-**
-** Duplicate a page directory entry
-**
-** @param dst Pointer to where the duplicate should go
-** @param curr Pointer to the entry to be duplicated
-**
-** @return true on success, else false
-*/
-bool_t vm_pdedup( pde_t *dst, pde_t *curr ) {
-
- assert1( curr != NULL );
- assert1( dst != NULL );
-
-#if TRACING_VM
- cio_printf( "vm_pdedup dst %08x curr %08x\n",
- (uint32_t) dst, (uint32_t) curr );
-#endif
- pde_t entry = *curr;
-
- // simplest case
- if( !IS_PRESENT(entry) ) {
- *dst = 0;
- return true;
- }
-
- // OK, we have an entry; allocate a page table for it
- pte_t *newtbl = (pte_t *) km_page_alloc();
- if( newtbl == NULL ) {
- return false;
- }
-
- // we could clear the new table, but we'll be assigning to
- // each entry anyway, so we'll save the execution time
-
- // address of the page table for this directory entry
- pte_t *old = (pte_t *) PDE_ADDR(entry);
-
- // pointer to the first PTE in the new table
- pte_t *new = newtbl;
-
- for( int i = 0 ; i < N_PTE; ++i ) {
- if( !IS_PRESENT(*old) ) {
- *new = 0;
- } else {
- *new = *old;
- }
- ++old;
- ++new;
- }
-
- // replace the page table address
- // upper 22 bits from 'newtbl', lower 12 from '*curr'
- *dst = (pde_t) ( PTE_ADDR(newtbl) | PERMS(entry) );
-
- return true;
-}
-
-/**
-** Name: vm_getpte
-**
-** Return the address of the PTE corresponding to the virtual address
-** 'va' within the address space controlled by 'pgdir'. If there is no
-** page table for that VA and 'alloc' is true, create the necessary
-** page table entries.
-**
-** @param pdir Pointer to the page directory to be searched
-** @param va The virtual address we're looking for
-** @param alloc Should we allocate a page table if there isn't one?
-**
-** @return A pointer to the page table entry for this VA, or NULL if
-** there isn't one and we're not allocating
-*/
-pte_t *vm_getpte( pde_t *pdir, const void *va, bool_t alloc ) {
- pte_t *ptab;
-
- // sanity check
- assert1( pdir != NULL );
-
- // get the PDIR entry for this virtual address
- pde_t *pde = &pdir[ PDIX(va) ];
-
- // is it already set up?
- if( IS_PRESENT(*pde) ) {
-
- // yes!
- ptab = (pte_t*)P2V(PTE_ADDR(*pde));
-
- } else {
-
- // no - should we create it?
- if( !alloc ) {
- // nope, so just return
- return NULL;
- }
-
- // yes - try to allocate a page table
- ptab = (pte_t *) km_page_alloc();
- if( ptab == NULL ) {
- WARNING( "can't allocate page table" );
- return NULL;
- }
-
- // who knows what was left in this page....
- memclr( ptab, SZ_PAGE );
-
- // add this to the page directory
- //
- // we set this up to allow general access; this could be
- // controlled by setting access control in the page table
- // entries, if necessary.
- //
- // NOTE: the allocator is serving us virtual page addresses,
- // so we must convert them to physical addresses
- *pde = ((uint32_t) V2P(ptab)) | PDE_P | PDE_RW;
- }
-
- // finally, return a pointer to the entry in the
- // page table for this VA
- return &ptab[ PTIX(va) ];
-}
-
-// Set up kernel part of a page table.
-pde_t *vm_mkkvm( void )
-{
- mapping_t *k;
-
- // allocate the page directory
- pde_t *pdir = km_page_alloc();
- if( pdir == NULL ) {
- return NULL;
- }
-
- // clear it out to disable all the entries
- memclr( pdir, SZ_PAGE );
-
- if( P2V(PHYS_TOP) > DEV_BASE ) {
- cio_printf( "PHYS_TOP (%08x -> %08x) > DEV_BASE(%08x)\n",
- PHYS_TOP, P2V(PHYS_TOP), DEV_BASE );
- PANIC( 0, "PHYS_TOP too large" );
- }
-
- // map in all the page ranges
- k = kmap;
- for( int i = 0; i < n_kmap; ++i, ++k ) {
- int stat = vm_map( pdir, ((void *)k->va_start),
- k->pa_end - k->pa_start,
- k->pa_start, k->perm );
- if( stat != SUCCESS ) {
- vm_free( pdir );
- return 0;
- }
- }
-
- return pdir;
-}
-
-/*
-** Creates an initial user VM table hierarchy by copying the
-** system entries into a new page directory.
-**
-** @return a pointer to the new page directory, or NULL
-*/
-pde_t *vm_mkuvm( void ) {
-
- // allocate the directory
- pde_t *new = (pde_t *) km_page_alloc();
- if( new == NULL ) {
- return NULL;
- }
-
- // iterate through the kernel page directory
- pde_t *curr = kpdir;
- pde_t *dst = new;
- for( int i = 0; i < N_PDE; ++i ) {
-
- if( *curr != 0 ) {
- // found an active one - duplicate it
- if( !vm_pdedup(dst,curr) ) {
- return NULL;
- }
- }
-
- ++curr;
- ++dst;
- }
-
- return new;
-
-}
-
-/**
-** Name: vm_set_kvm
-**
-** Switch the page table register to the kernel's page directory.
-*/
-void vm_set_kvm( void ) {
- w_cr3( V2P(kpdir) ); // switch to the kernel page table
-}
-
-/**
-** Name: vm_set_uvm
-**
-** Switch the page table register to the page directory for a user process.
-**
-** @param p PCB of the process we're switching to
-*/
-void vm_set_uvm( pcb_t *p ) {
- assert( p != NULL );
- assert( p->pdir != NULL );
-
- w_cr3( V2P(p->pdir) ); // switch to process's address space
-}
-
-/**
-** Name: vm_add
-**
-** Add pages to the page hierarchy for a process, copying data into
-** them if necessary.
-**
-** @param pdir Pointer to the page directory to modify
-** @param wr "Writable" flag for the PTE
-** @param sys "System" flag for the PTE
-** @param va Starting VA of the range
-** @param size Amount of physical memory to allocate (bytes)
-** @param data Pointer to data to copy, or NULL
-** @param bytes Number of bytes to copy
-**
-** @return status of the allocation attempt
-*/
-int vm_add( pde_t *pdir, bool_t wr, bool_t sys,
- void *va, uint32_t size, char *data, uint32_t bytes ) {
-
- // how many pages do we need?
- uint_t npages = ((size & MOD4K_BITS) ? PGUP(size) : size) >> MOD4K_SHIFT;
-
- // permission set for the PTEs
- uint_t entrybase = PTE_P;
- if( wr ) {
- entrybase |= PTE_RW;
- }
- if( sys ) {
- entrybase |= PTE_US;
- }
-
-#if TRACING_VM
- cio_printf( "vm_add: pdir %08x, %s, va %08x (%u, %u pgs)\n",
- (uint32_t) pdir, wr ? "W" : "!W", (uint32_t) va, size );
- cio_printf( " from %08x, %u bytes, perms %08x\n",
- (uint32_t) data, bytes, entrybase );
-#endif
-
- // iterate through the pages
-
- for( int i = 0; i < npages; ++i ) {
-
- // figure out where this page will go in the hierarchy
- pte_t *pte = vm_getpte( pdir, va, true );
- if( pte == NULL ) {
- // TODO if i > 0, this isn't the first frame - is
- // there anything to do about other frames?
- // POSSIBLE MEMORY LEAK?
- return E_NO_MEMORY;
- }
-
- // allocate the frame
- void *page = km_page_alloc();
- if( page == NULL ) {
- // TODO same question here
- return E_NO_MEMORY;
- }
-
- // clear it all out
- memclr( page, SZ_PAGE );
-
- // create the PTE for this frame
- uint32_t entry = (uint32_t) (PTE_ADDR(page) | entrybase);
- *pte = entry;
-
- // copy data if we need to
- if( data != NULL && bytes > 0 ) {
- // how much to copy
- uint_t num = bytes > SZ_PAGE ? SZ_PAGE : bytes;
- // do it!
- memcpy( (void *)page, (void *)data, num );
- // adjust all the pointers
- data += num; // where to continue
- bytes -= num; // what's left to copy
- }
-
- // bump the virtual address
- va += SZ_PAGE;
- }
-
- return SUCCESS;
-
-}
-
-/**
-** Name: vm_free
-**
-** Deallocate a page table hierarchy and all physical memory frames
-** in the user portion.
-**
-** Works only for 4KB pages.
-**
-** @param pdir Pointer to the page directory
-*/
-void vm_free( pde_t *pdir ) {
-
- // do we have anything to do?
- if( pdir == NULL ) {
- return;
- }
-
- // iterate through the page directory entries, freeing the
- // PMTS and the frames they point to
- pde_t *curr = pdir;
- for( int i = 0; i < N_PDE; ++i ) {
-
- // the entry itself
- pde_t entry = *curr;
-
- // does this entry point to anything useful?
- if( IS_PRESENT(entry) ) {
-
- // yes - large pages make us unhappy
- assert( !IS_LARGE(entry) );
-
- // get the PMT pointer
- pte_t *pmt = (pte_t *) PTE_ADDR(entry);
-
- // walk the PMT
- for( int j = 0; j < N_PTE; ++j ) {
- // does this entry point to a frame?
- if( IS_PRESENT(*pmt) ) {
- // yes - free the frame
- km_page_free( (void *) PTE_ADDR(*pmt) );
- // mark it so we don't get surprised
- *pmt = 0;
- }
- // move on
- ++pmt;
- }
- // now, free the PMT itself
- km_page_free( (void *) PDE_ADDR(entry) );
- *curr = 0;
- }
-
- // move to the next entry
- ++curr;
- }
-
- // finally, free the PDIR itself
- km_page_free( (void *) pdir );
-}
-
-/*
-** Name: vm_map
-**
-** Create PTEs for virtual addresses starting at 'va' that refer to
-** physical addresses in the range [pa, pa+size-1]. We aren't guaranteed
-** that va is page-aligned.
-**
-** @param pdir Page directory for this address space
-** @param va The starting virtual address
-** @param size Length of the range to be mapped
-** @param pa The starting physical address
-** @param perm Permission bits for the PTEs
-**
-** @return the status of the mapping attempt
-*/
-int vm_map( pde_t *pdir, void *va, uint_t size, uint_t pa, int perm ) {
-
- // round the VA down to its page boundary
- char *addr = (char*)PGDOWN((uint_t)va);
-
- // round the end of the range down to its page boundary
- char *last = (char*)PGDOWN(((uint_t)va) + size - 1);
-
- while( addr < last ) {
-
- // get a pointer to the PTE for the current VA
- pte_t *pte = vm_getpte( pdir, addr, true );
- if( pte == NULL ) {
- // couldn't find it
- return E_NO_PTE;
- }
-
- // if this entry has already been mapped, we're in trouble
- if( IS_PRESENT(*pte) ) {
- cio_printf( "vm_map(%08x,%08x,%u,%08x,%03x)\n",
- (uint32_t) pdir, (uint32_t) va, size, pa, perm );
- cio_printf( " addr %08x last %08x pte %08x *pte %08x\n",
- (uint32_t) addr, (uint32_t) last,
- (uint32_t) pte, *pte );
-
- PANIC( 0, "mapping an already-mapped address" );
- }
-
- // ok, set the PTE as requested
- *pte = pa | perm | PTE_P;
-
- // nope - move to the next page
- addr += SZ_PAGE;
- pa += SZ_PAGE;
-
- }
- return SUCCESS;
-}
-
-/**
-** Name: vm_uvmdup
-**
-** Create a duplicate of the user portio of an existing page table
-** hierarchy. We assume that the "new" page directory exists and
-** the system portions of it should not be touched.
-**
-** Note: we do not duplicate the frames in the hierarchy - we just
-** create a duplicate of the hierarchy itself. This means that we
-** now have two sets of page tables that refer to the same physical
-** frames in memory.
-**
-** @param old Existing page directory
-** @param new New page directory
-**
-** @return status of the duplication attempt
-*/
-int vm_uvmdup( pde_t *old, pde_t *new ) {
-
- if( old == NULL || new == NULL ) {
- return E_BAD_PARAM;
- }
-
- // we only want to deal with the "user" half of the address space
- for( int i = 0; i < (N_PDE >> 1); ++i ) {
-
- // the entry to copy
- pde_t entry = *old;
-
- // is this entry in use?
- if( IS_PRESENT(entry) ) {
-
- // yes. if it points to a 4MB page, we just copy it;
- // otherwise, we must duplicate the next level PMT
-
- if( !IS_LARGE(entry) ) {
-
- // it's a 4KB page, so we need to duplicate the PMT
- pte_t *newpt = (pte_t *) vm_pagedup( (void *)PTE_ADDR(entry) );
- if( newpt == NULL ) {
- return E_NO_MEMORY;
- }
-
- uint32_t perms = PERMS(entry);
-
- // create the new PDE entry by replacing the frame #
- entry = ((uint32_t) newpt) | perms;
- }
-
- } else {
-
- // not present, so create an empty entry
- entry = 0;
-
- }
-
- // send it on its way
- *new = entry;
-
- // move on down the line
- ++old;
- ++new;
- }
-
- return SUCCESS;
-}