/*- * 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 <sys/io.h> #include <ubixos/kpanic.h> #include <lib/kprintf.h> #include <lib/kmalloc.h> #include <ubixos/vitals.h> #include <ubixos/spinlock.h> #include <assert.h> //MrOlsen (2016-01-11) NOTE: Need to Seperate Out CPU Specific Stuff Over Time #include <i386/cpu.h> static uint32_t freePages = 0; static struct spinLock vmmSpinLock = SPIN_LOCK_INITIALIZER; //static struct spinLock vmmCowSpinLock = SPIN_LOCK_INITIALIZER; int numPages = 0x0; mMap *vmmMemoryMap = ( mMap * ) VMM_MMAP_ADDR_RMODE; /************************************************************************ Function: void vmm_memMapInit(); Description: This Function Initializes The Memory Map For the System Notes: 02/20/2004 - Made It Report Real And Available Memory ************************************************************************/ int vmm_memMapInit() { int i = 0x0; int memStart = 0x0; // Count System Memory numPages = countMemory(); // Set Memory Map To Point To First Physical Page That We Will Use vmmMemoryMap = ( mMap * ) VMM_MMAP_ADDR_RMODE; // Initialize Map Make All Pages Not Available for( i = 0x0; i < numPages; i++ ) { vmmMemoryMap[i].cowCounter = 0x0; vmmMemoryMap[i].status = memNotavail; vmmMemoryMap[i].pid = vmmID; vmmMemoryMap[i].pageAddr = i * PAGE_SIZE; } // Calculate Start Of Free Memory memStart = ( 0x101000 / 0x1000 ); memStart += ( ( ( sizeof( mMap ) * numPages ) + ( sizeof( mMap ) - 1 ) ) / 0x1000 ); // Initialize All Free Pages To Available vmmMemoryMap[( 0x100000 / 0x1000 )].status = memAvail; freePages++; for( i = memStart; i < numPages; i++ ) { vmmMemoryMap[i].status = memAvail; freePages++; } if( systemVitals ) systemVitals->freePages = freePages; // Print Out Amount Of Memory kprintf( "Real Memory: %iKB\n", numPages * 4 ); kprintf( "Available Memory: %iKB\n", freePages * 4 ); // Return return(0x0); } /************************************************************************ Function: int countMemory(); Description: This Function Counts The Systems Physical Memory Notes: 02/20/2004 - Inspect For Quality And Approved ************************************************************************/ int countMemory() { register uInt32 *mem = 0x0; unsigned long memCount = -1; unsigned long tempMemory = 0x0; unsigned short memKb = 8; unsigned char irq1State; unsigned char irq2State; unsigned long cr0 = 0x0; /* * Save The States Of Both IRQ 1 And 2 So We Can Turn Them Off And Restore * Them Later */ irq1State = inportByte( 0x21 ); irq2State = inportByte( 0xA1 ); // Turn Off IRQ 1 And 2 To Prevent Chances Of Faults While Examining Memory outportByte( 0x21, 0xFF ); outportByte( 0xA1, 0xFF ); // Save The State Of Register CR0 cr0 = rcr0(); /* asm volatile ( "movl %%cr0, %%ebx\n" : "=a" (cr0) : : "ebx" ); */ asm volatile ( "wbinvd" ); load_cr0( cr0 | 0x00000001 | 0x40000000 | 0x20000000 ); /* asm volatile ( "movl %%ebx, %%cr0\n" : : "a" (cr0 | 0x00000001 | 0x40000000 | 0x20000000) : "ebx" ); */ while( memKb < 4096 && memCount != 0 ) { memKb++; if( memCount == -1 ) memCount = 8388608; else memCount += 1024 * 1024; mem = ( uInt32 * ) memCount; tempMemory = *mem; *mem = 0x55AA55AA; asm("": : :"memory"); if( *mem != 0x55AA55AA ) { memCount = 0; } else { *mem = 0xAA55AA55; asm( "": : :"memory" ); if( *mem != 0xAA55AA55 ) { memCount = 0; } } asm( "": : :"memory" ); *mem = tempMemory; } asm( "nop" ); //MrOlsen (2016-01-10) NOTE: I don't like this but I start incrementing form the start. memKb--; asm( "nop" ); load_cr0( cr0 ); /* asm volatile ( "movl %%ebx, %%cr0\n" : : "a" (cr0) : "ebx" ); */ asm("nop"); // Restore States For Both IRQ 1 And 2 outportByte( 0x21, irq1State ); outportByte( 0xA1, irq2State ); asm( "nop" ); // Return Amount Of Memory In Pages return( ( memKb * 1024 * 1024 ) / PAGE_SIZE ); } /************************************************************************ Function: uInt32 vmm_findFreePage(pid_t pid); Description: This Returns A Free Physical Page Address Then Marks It Not Available As Well As Setting The PID To The Proccess Allocating This Page Notes: ************************************************************************/ uint32_t vmm_findFreePage( pidType pid ) { int i = 0x0; // Lets Look For A Free Page if( pid < sysID ) kpanic( "Error: invalid PID %i\n", pid ); spinLock( &vmmSpinLock ); for( i = 0; i <= numPages; i++ ) { /* * If We Found A Free Page Set It To Not Available After That Set Its Own * And Return The Address */ if( ( vmmMemoryMap[i].status == memAvail ) && ( vmmMemoryMap[i].cowCounter == 0 ) ) { vmmMemoryMap[i].status = memNotavail; vmmMemoryMap[i].pid = pid; freePages--; if( systemVitals ) systemVitals->freePages = freePages; spinUnlock( &vmmSpinLock ); return( vmmMemoryMap[i].pageAddr ); } } // If No Free Memory Is Found Return NULL kpanic( "Out Of Memory!!!!" ); return( 0x0 ); } /************************************************************************ Function: int freePage(uInt32 pageAddr); Description: This Function Marks The Page As Free Notes: ************************************************************************/ int freePage( uint32_t pageAddr ) { int pageIndex = 0x0; assert( ( pageAddr & 0xFFF ) == 0x0 ); // Find The Page Index To The Memory Map pageIndex = ( pageAddr / PAGE_SIZE ); // Check If Page COW Is Greater Then 0 If It Is Dec It If Not Free It if( vmmMemoryMap[pageIndex].cowCounter == 0 ) { // Set Page As Avail So It Can Be Used Again spinLock( &vmmSpinLock ); vmmMemoryMap[pageIndex].status = memAvail; vmmMemoryMap[pageIndex].cowCounter = 0x0; vmmMemoryMap[pageIndex].pid = -2; freePages++; systemVitals->freePages = freePages; spinUnlock( &vmmSpinLock ); } else { /* Adjust The COW Counter */ adjustCowCounter( ( ( uint32_t ) vmmMemoryMap[pageIndex].pageAddr ), -1 ); } // Return return(0x0); } /************************************************************************ Function: int adjustCowCounter(uInt32 baseAddr,int adjustment); Description: This Adjust The COW Counter For Page At baseAddr It Will Error If The Count Goes Below 0 Notes: 08/01/02 - I Think If Counter Gets To 0 I Should Free The Page ************************************************************************/ int adjustCowCounter( uInt32 baseAddr, int adjustment ) { int vmmMemoryMapIndex = ( baseAddr / PAGE_SIZE ); assert( ( baseAddr & 0xFFF ) == 0x0 ); spinLock( &vmmSpinLock ); // Adjust COW Counter vmmMemoryMap[vmmMemoryMapIndex].cowCounter += adjustment; if( vmmMemoryMap[vmmMemoryMapIndex].cowCounter <= 0 ) { if( vmmMemoryMap[vmmMemoryMapIndex].cowCounter < 0 ) kprintf( "ERROR: Why is COW less than 0" ); vmmMemoryMap[vmmMemoryMapIndex].cowCounter = 0x0; vmmMemoryMap[vmmMemoryMapIndex].pid = vmmID; vmmMemoryMap[vmmMemoryMapIndex].status = memAvail; freePages++; systemVitals->freePages = freePages; } spinUnlock( &vmmSpinLock ); // Return return(0x0); } /************************************************************************ Function: void vmm_freeProcessPages(pid_t pid); Description: This Function Will Free Up Memory For The Exiting Process Notes: 08/04/02 - Added Checking For COW Pages First ************************************************************************/ /* TODO: This can be greatly improved for performance but it gets the job done */ void vmm_freeProcessPages( pidType pid ) { int i = 0; int x = 0; uint32_t *tmpPageTable = 0x0; uint32_t *tmpPageDir = ( uInt32 * ) PD_BASE_ADDR; spinLock( &vmmSpinLock ); // Check Page Directory For An Avail Page Table // NOTE: This cleans all memory space up to kernel space #ifdef _IGNORE for( i = 0; i < ( PAGE_SIZE - ( PAGE_SIZE / 4 ) ); i++ ) { if( tmpPageDir[i] != 0 ) { // Set Up Page Table Pointer tmpPageTable = ( uint32_t * ) ( PT_BASE_ADDR + ( i * PAGE_SIZE ) ); // Check The Page Table For COW Pages for( x = 0; x < PD_ENTRIES; x++ ) { // If The Page Is COW Adjust COW Counter if( ( ( uint32_t ) tmpPageTable[x] & PAGE_COW ) == PAGE_COW ) { adjustCowCounter( ( ( uint32_t ) tmpPageTable[x] & 0xFFFFF000 ), -1 ); } } } } #endif // Loop Through Pages To Find Pages Owned By Process for( i = 0; i < numPages; i++ ) { if( vmmMemoryMap[i].pid == pid ) { // Check To See If The cowCounter Is Zero If So We Can Ree It if( vmmMemoryMap[i].cowCounter == 0 ) { vmmMemoryMap[i].status = memAvail; vmmMemoryMap[i].cowCounter = 0x0; vmmMemoryMap[i].pid = vmmID; freePages++; systemVitals->freePages = freePages; } else { spinUnlock( &vmmSpinLock ); adjustCowCounter( ( i * PAGE_SIZE ), -1 ); spinLock( &vmmSpinLock ); } } } // Return spinUnlock( &vmmSpinLock ); return; }