/*1* Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2008, 20092* The President and Fellows of Harvard College.3*4* Redistribution and use in source and binary forms, with or without5* modification, are permitted provided that the following conditions6* are met:7* 1. Redistributions of source code must retain the above copyright8* notice, this list of conditions and the following disclaimer.9* 2. Redistributions in binary form must reproduce the above copyright10* notice, this list of conditions and the following disclaimer in the11* documentation and/or other materials provided with the distribution.12* 3. Neither the name of the University nor the names of its contributors13* may be used to endorse or promote products derived from this software14* without specific prior written permission.15*16* THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND17* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE18* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE19* ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE20* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL21* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS22* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)23* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT24* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY25* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF26* SUCH DAMAGE.27*/2829#include <types.h>30#include <kern/errno.h>31#include <lib.h>32#include <addrspace.h>33#include <proc.h>34#include <vm.h>35#include <vm/region.h>36#include <vm/page.h>37#include <vm/swap.h>38#include <array.h>39#include <cpu.h>40#include <machine/coremap.h>41#include <machine/tlb.h>42#include <current.h>4344/*45* Note! If OPT_DUMBVM is set, as is the case until you start the VM46* assignment, this file is not compiled or linked or in any way47* used. The cheesy hack versions in dumbvm.c are used instead.48*/4950DEFARRAY_BYTYPE( vm_region_array ,struct vm_region, /* ... */);5152struct addrspace *53as_create(void)54{55struct addrspace *as;5657as = kmalloc(sizeof(struct addrspace));58if (as == NULL) {59return NULL;60}6162//create the array of regions.63as->as_regions = vm_region_array_create();64if( as->as_regions == NULL ) {65kfree( as );66return NULL;67}6869//set the heap start.70as->as_heap_start = 0;71as->as_heap_end = 0;7273return as;74}7576int77as_copy(struct addrspace *old, struct addrspace **ret)78{79struct addrspace *newas;80struct vm_region *vmr;81struct vm_region *newvmr;82unsigned i;83int result;8485newas = as_create();86if (newas==NULL) {87return ENOMEM;88}8990//copy all vm regions that reside in the old addrspace.91for( i = 0; i < vm_region_array_num( old->as_regions ); ++i ) {92vmr = vm_region_array_get( old->as_regions, i );9394//if clone fails, we simply return the reason95//it failed, after destroying newas.96result = vm_region_clone( vmr, &newvmr );97if( result ) {98as_destroy( newas );99return result;100}101102result = vm_region_array_add( newas->as_regions, newvmr, NULL );103if( result ) {104vm_region_destroy( newvmr );105as_destroy( newas );106return result;107}108}109110*ret = newas;111return 0;112}113114void115as_destroy(struct addrspace *as)116{117struct vm_region *vmr;118unsigned i;119120//destroy each vm region associated with this addrspace.121for( i = 0; i < vm_region_array_num( as->as_regions ); ++i ) {122vmr = vm_region_array_get( as->as_regions, i );123vm_region_destroy( vmr );124}125126//reside the regions array to 0, and127//destroy the array.128vm_region_array_setsize( as->as_regions, 0 );129vm_region_array_destroy( as->as_regions );130131kfree( as );132}133134void135as_activate(struct addrspace *as)136{137KASSERT( as != NULL || curthread->t_addrspace == as );138//if the given addrspace is different the one we may have tlb entries for139if( as != curcpu->c_lastas ) {140LOCK_COREMAP();141//set the given addrspace to be the last one142curcpu->c_lastas = as;143//clear the tlb entry.144tlb_clear();145146UNLOCK_COREMAP();147}148}149150static151bool152as_overlaps_region( struct addrspace *as, size_t sz, vaddr_t vaddr ) {153unsigned i;154vaddr_t bottom;155vaddr_t top;156struct vm_region *vmr;157158//loop over all regions.159for( i = 0; i < vm_region_array_num( as->as_regions ); ++i ) {160//get the current region.161vmr = vm_region_array_get( as->as_regions, i );162163//calculate bottom and top virtual addresses.164bottom = vmr->vmr_base;165top = bottom + vm_page_array_num( vmr->vmr_pages ) * PAGE_SIZE;166167//if the tail & head of the new vm_region are inside168//the current address space block,then we have an overlap.169if( vaddr + sz > bottom && vaddr < top )170return true;171}172173return false;174}175/*176* Set up a segment at virtual address VADDR of size MEMSIZE. The177* segment in memory extends from VADDR up to (but not including)178* VADDR+MEMSIZE.179*180* The READABLE, WRITEABLE, and EXECUTABLE flags are set if read,181* write, or execute permission should be set on the segment. At the182* moment, these are ignored. When you write the VM system, you may183* want to implement them.184*/185int186as_define_region(struct addrspace *as, vaddr_t vaddr, size_t sz,187int readable, int writeable, int executable)188{189struct vm_region *vmr;190int res;191192(void) readable;193(void) writeable;194(void) executable;195196//align the virtual address.197vaddr &= PAGE_FRAME;198199//round up the size so it is a multiple of the page size.200sz = ROUNDUP(sz, PAGE_SIZE);201202//if there's an overlap, well we have a problem.203if( as_overlaps_region( as, sz, vaddr ) )204return EINVAL;205206//create a region with the specified amount of pages.207vmr = vm_region_create( sz / PAGE_SIZE );208if( vmr == NULL )209return ENOMEM;210211vmr->vmr_base = vaddr;212//add it to the addresspace.213res = vm_region_array_add( as->as_regions, vmr, NULL );214if( res ) {215vm_region_destroy( vmr );216return res;217}218219//if the new vaddr is larger then heap_size ..220if( vmr->vmr_base > as->as_heap_start && vaddr != USERSTACKBASE )221as->as_heap_start = vmr->vmr_base + sz;222223return 0;224}225226int227as_prepare_load(struct addrspace *as)228{229return as_define_region(230as, as->as_heap_start, 0, 1, 1, 0231);232}233234int235as_complete_load(struct addrspace *as)236{237(void)as;238return 0;239}240241int242as_define_stack(struct addrspace *as, vaddr_t *stackptr)243{244int err;245246//create the stack region.247err = as_define_region( as, USERSTACKBASE, USERSTACKSIZE, 1, 1, 0 );248if( err )249return err;250251*stackptr = USERSTACK;252return 0;253}254255int256as_fault( struct addrspace *as, int fault_type, vaddr_t fault_addr ) {257struct vm_region *vmr;258int ix_page;259struct vm_page *vmp;260int res;261262KASSERT( as != NULL );263264//find the responsible vm_region for the faulty address.265vmr = vm_region_find_responsible( as, fault_addr );266if( vmr == NULL )267return EFAULT;268269//find the responsible vm_page.270ix_page = (fault_addr - vmr->vmr_base) / PAGE_SIZE;271272//get the virtual page.273vmp = vm_page_array_get( vmr->vmr_pages, ix_page );274275//if the virtual page is null, it means we have to zero-fill it.276if( vmp == NULL ) {277//create a new blank page278res = vm_page_new_blank( &vmp );279if( res )280return res;281282//append to to the region.283vm_page_array_set( vmr->vmr_pages, ix_page, vmp );284}285return vm_page_fault( vmp, as, fault_type, fault_addr );286}287288289