Newer
Older
UbixOS / sys / vmm / vmm_memory.c
/*-
 * 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;
}