Newer
Older
UbixOS / sys / vmm / copyvirtualspace.c
@Christopher W. Olsen Christopher W. Olsen on 10 Feb 2022 11 KB Merge branch 'master' of
/*-
 * Copyright (c) 2002-2018 The UbixOS Project.
 * All rights reserved.
 *
 * This was developed by Christopher W. Olsen for the UbixOS Project.
 *
 * Redistribution and use in source and binary forms, with or without modification, are permitted
 * provided that the following conditions are met:
 *
 * 1) Redistributions of source code must retain the above copyright notice, this list of
 *    conditions, the following disclaimer and the list of authors.
 * 2) Redistributions in binary form must reproduce the above copyright notice, this list of
 *    conditions, the following disclaimer and the list of authors in the documentation and/or
 *    other materials provided with the distribution.
 * 3) Neither the name of the UbixOS Project nor the names of its contributors may be used to
 *    endorse or promote products derived from this software without specific prior written
 *    permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <vmm/vmm.h>
#include <vmm/paging.h>
#include <sys/kern_sysctl.h>
#include <ubixos/spinlock.h>
#include <ubixos/kpanic.h>
#include <string.h>

static struct spinLock cvsSpinLock = SPIN_LOCK_INITIALIZER;

/************************************************************************

 Function: void *vmm_copyVirtualSpace(pidType pid);

 Description: Creates A Copy Of A Virtual Space And Set All NON Kernel
 Space To COW For A Fork This Will Also Alter The Parents
 VM Space To Make That COW As Well

 Notes:

 08/02/02 - Added Passing Of pidType pid So We Can Better Keep Track Of
 Which Task Has Which Physical Pages

 ************************************************************************/
void *vmm_copyVirtualSpace( pidType pid ) {

    void *newPageDirectoryAddress   = 0x0;

    uint32_t *parentPageDirectory   = 0x0;
    uint32_t *newPageDirectory      = 0x0;
    uint32_t *parentPageTable       = 0x0;
    uint32_t *newPageTable          = 0x0;
    uint32_t *parentStackPage       = 0x0;
    uint32_t *newStackPage          = 0x0;

    uint16_t x                      = 0x0;
    uint16_t i                      = 0x0;
    uint16_t s                      = 0x0;

    spinLock(&cvsSpinLock);

    /* Set Address Of Parent Page Directory */
    parentPageDirectory = (uint32_t *) PD_BASE_ADDR;

    /* Allocate A New Page For The New Page Directory */
    if( ( newPageDirectory = ( uint32_t * ) vmm_getFreeKernelPage( pid, 1 ) ) == 0x0 )
    	kpanic( "Error: newPageDirectory == NULL, File: %s, Line: %i\n", __FILE__, __LINE__ );

    /* Set newPageDirectoryAddress To The Newly Created Page Directories Page */
    newPageDirectoryAddress = ( void * ) vmm_getPhysicalAddr( ( uint32_t ) newPageDirectory );

    /* First Set Up A Flushed Page Directory */
    bzero( newPageDirectory, PAGE_SIZE );

    /* Map Kernel Code Region Entries 0 & 1 */
    newPageDirectory[0] = parentPageDirectory[0];
    //	XXX: We Dont Need This - newPageDirectory[1] = parentPageDirectory[1];

    if( ( newPageTable = ( uint32_t * ) vmm_getFreeKernelPage( pid, 1 ) ) == 0x0 )
    	kpanic("Error: newPageTable == NULL, File: %s, Line: %i\n", __FILE__, __LINE__);

    //	Zero the newPageTable to ensure no garbage
    bzero( newPageTable, PAGE_SIZE );

    parentPageTable = ( uint32_t * ) ( PT_BASE_ADDR + ( PAGE_SIZE * 1 ) );

    for( x = 0; x < PT_ENTRIES; x++ ) {

    	if( ( ( parentPageTable[x] ) & PAGE_PRESENT ) == PAGE_PRESENT ) {

    		/* Set Page To COW In Parent And Child Space */
    		newPageTable[x] = ( ( ( uint32_t ) parentPageTable[x] & 0xFFFFF000 ) | ( KERNEL_PAGE_DEFAULT | PAGE_COW ) );

    		/* Increment The COW Counter For This Page */
    		if( ( ( uint32_t ) parentPageTable[x] & PAGE_COW ) == PAGE_COW ) {

    			adjustCowCounter( ( ( uint32_t ) parentPageTable[x] & 0xFFFFF000 ), 1 );

    		} else {

    			/* Add Two If This Is The First Time Setting To COW */
    			adjustCowCounter( ( (uint32_t ) parentPageTable[x] & 0xFFFFF000 ), 2 );

    			parentPageTable[x] |= PAGE_COW; // newPageTable[i];

    		}

    	} else {

    		newPageTable[x] = parentPageTable[x];

    	}

    }

    newPageDirectory[1] = ( vmm_getPhysicalAddr( ( uint32_t ) newPageTable ) | KERNEL_PAGE_DEFAULT );

    vmm_unmapPage( ( uint32_t ) newPageTable, 1 );

    newPageTable = 0x0;

    /* Map The Kernel Memory Region Entry 770 Address 0xC0800000 */
    for( x = PD_INDEX( VMM_KERN_START ); x <= PD_INDEX( VMM_KERN_END ); x++ ) {

    	newPageDirectory[x] = parentPageDirectory[x];

    }

    /* Map The Kernel Stack Region */
    for( x = PD_INDEX( VMM_KERN_STACK_START ); x <= PD_INDEX( VMM_KERN_STACK_END ); x++ ) {

    	if( ( parentPageDirectory[x] & PAGE_PRESENT ) == PAGE_PRESENT ) {

    		/* Set Parent To Propper Page Table */
    		parentPageTable = (uint32_t *) (PT_BASE_ADDR + (PAGE_SIZE * x));

    		/* Allocate A New Page Table */
    		if( ( newPageTable = ( uint32_t * ) vmm_getFreeKernelPage( pid, 1 ) ) == 0x0 ) {

    			kpanic( "Error: newPageTable == NULL, File: %s, Line: %i\n", __FILE__, __LINE__ );

    		}

    		bzero( newPageTable, PAGE_SIZE );

    		for( i = 0; i < PT_ENTRIES; i++ ) {

    			if( ( parentPageTable[i] & PAGE_PRESENT ) == PAGE_PRESENT ) {

    				/* Alloc A New Page For This Stack Page */
    				if( ( newStackPage = ( uint32_t * ) vmm_getFreeKernelPage( pid, 1 ) ) == 0x0 ) {

    					kpanic( "Error: newStackPage == NULL, File: %s, Line: %i\n", __FILE__, __LINE__ );

    				}

    				/* Set Pointer To Parents Stack Page */
    				parentStackPage = ( uint32_t * ) ( ( ( PAGE_SIZE * PD_ENTRIES ) * x ) + ( PAGE_SIZE * i ) );

    				/* Copy The Stack Byte For Byte (I Should Find A Faster Way) */
    				memcpy( newStackPage, parentStackPage, PAGE_SIZE );

    				/* Insert New Stack Into Page Table */
    				newPageTable[i] = ( vmm_getPhysicalAddr( ( uint32_t ) newStackPage ) | PAGE_DEFAULT | PAGE_STACK );

    				/* Unmap From Kernel Space */
    				vmm_unmapPage( ( uint32_t ) newStackPage, 1 );

    			}

    		}

    		/* Put New Page Table Into New Page Directory */
    		newPageDirectory[x] = ( vmm_getPhysicalAddr( ( uint32_t ) newPageTable) | PAGE_DEFAULT );

    		/* Unmap Page From Kernel Space But Keep It Marked As Not Avail */
    		vmm_unmapPage( ( uint32_t ) newPageTable, 1 );

    	}

    }

    /*
     * Now For The Fun Stuff For Page Tables 2-767 We Must Map These And Set
     * The Permissions On Every Mapped Pages To COW This Will Conserve Memory
     * Because The Two VM Spaces Will Be Sharing Pages Unless an EXECVE Happens
     *
     * We start at the 4MB boundary as the first 4MB is special
     */

    for( x = PD_INDEX( VMM_USER_START ); x <= PD_INDEX( VMM_USER_END ); x++ ) {

    	/* If Page Table Exists Map It */
    	if( ( parentPageDirectory[x] & PAGE_PRESENT ) == PAGE_PRESENT ) {

    		/* Set Parent To Propper Page Table */
    		parentPageTable = ( uint32_t * ) ( PT_BASE_ADDR + ( PAGE_SIZE * x ) );

    		/* Allocate A New Page Table */
    		if( ( newPageTable = ( uint32_t * ) vmm_getFreeKernelPage( pid, 1 ) ) == 0x0 ) {

    			kpanic( "Error: newPageTable == NULL, File: %s, Line: %i\n", __FILE__, __LINE__ );

    		}

    		bzero( newPageTable, PAGE_SIZE );

    		/* Set Parent And New Pages To COW */
    		for( i = 0; i < PD_ENTRIES; i++ ) {


    			/* If Page Is Mapped */
    			if( ( parentPageTable[i] & PAGE_PRESENT ) == PAGE_PRESENT ) {

    				/* Check To See If Its A Stack Page */
    				if( ( ( uint32_t ) parentPageTable[i] & PAGE_STACK ) == PAGE_STACK ) {

    					/* Alloc A New Page For This Stack Page */
    					if( ( newStackPage = (uint32_t *) vmm_getFreeKernelPage( pid, 1 ) ) == 0x0 ) {

    						kpanic( "Error: newStackPage == NULL, File: %s, Line: %i\n", __FILE__, __LINE__ );

    					}

    					bzero( newStackPage, PAGE_SIZE );

    					/* Set Pointer To Parents Stack Page */
    					parentStackPage = ( uint32_t * ) ( ( ( PAGE_SIZE * PD_ENTRIES ) * x ) + ( PAGE_SIZE * i ) );

    					/* Copy The Stack Byte For Byte (I Should Find A Faster Way) */
    					memcpy( newStackPage, parentStackPage, PAGE_SIZE );

    					/* Insert New Stack Into Page Table */
    					newPageTable[i] = ( vmm_getPhysicalAddr( ( uint32_t ) newStackPage ) | PAGE_DEFAULT | PAGE_STACK );

    					/* Unmap From Kernel Space */
    					vmm_unmapPage( ( uint32_t ) newStackPage, 1 );

    				} else {

    					/* Set Page To COW In Parent And Child Space */
    					newPageTable[i] = ( ( ( uint32_t ) parentPageTable[i] & 0xFFFFF000 ) | (PAGE_DEFAULT | PAGE_COW ) );

    					/* Increment The COW Counter For This Page */
    					if( ( ( uint32_t ) parentPageTable[i] & PAGE_COW ) == PAGE_COW ) {

    						adjustCowCounter( ( ( uint32_t ) parentPageTable[i] & 0xFFFFF000 ), 1 );

    					} else {

    						/* Add Two If This Is The First Time Setting To COW */
    						adjustCowCounter( ( ( uint32_t ) parentPageTable[i] & 0xFFFFF000 ), 2 );

    						parentPageTable[i] |= PAGE_COW; // newPageTable[i];

    					}

    				}

    			} else {

    				newPageTable[i] = ( uint32_t ) 0x0;

    			}

    		}

    		/* Put New Page Table Into New Page Directory */
    		newPageDirectory[x] = ( vmm_getPhysicalAddr( ( uint32_t ) newPageTable) | PAGE_DEFAULT );

    		/* Unmap Page From Kernel Space But Keep It Marked As Not Avail */
    		vmm_unmapPage( ( uint32_t ) newPageTable, 1 );

    	}

    }

    /*
     * Allocate A New Page For The The First Page Table Where We Will Map The
     * Lower Region First 4MB
     */

    /*
     *
     * Map Page Directory Into VM Space
     * First Page After Page Tables
     * This must be mapped into the page directory before we map all 1024 page directories into the memory space
     */

    newPageTable = ( uint32_t * ) vmm_getFreePage( pid );

    bzero( newPageTable, PAGE_SIZE );

    newPageDirectory[PD_INDEX(PD_BASE_ADDR)] = ( uint32_t ) ( vmm_getPhysicalAddr( ( uint32_t ) newPageTable ) | PAGE_DEFAULT );

    newPageTable[0] = ( uint32_t ) ( ( uint32_t ) ( newPageDirectoryAddress ) | PAGE_DEFAULT );

    vmm_unmapPage((uint32_t) newPageTable, 1);

  /*
   *
   * Map Page Tables Into VM Space
   * The First Page Table (4MB) Maps To All Page Directories
   *
   */

  newPageTable = (uint32_t *) vmm_getFreePage(pid);

  newPageDirectory[PD_INDEX(PT_BASE_ADDR)] = (uint32_t) (vmm_getPhysicalAddr((uint32_t) newPageTable) | PAGE_DEFAULT);

  /* Flush The Page From Garbage In Memory */
  bzero(newPageTable, PAGE_SIZE);

  for (x = 0; x < PD_ENTRIES; x++)
    newPageTable[x] = newPageDirectory[x];

  /* Unmap Page From Virtual Space */
  vmm_unmapPage((uint32_t) newPageTable, 1);

  /* Now We Are Done With The Page Directory So Lets Unmap That Too */
  vmm_unmapPage((uint32_t) newPageDirectory, 1);

  spinUnlock(&cvsSpinLock);

  /* Return Physical Address Of Page Directory */
  return (newPageDirectoryAddress);
}