/*-
* 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 (0);
}
/************************************************************************
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, tempMemory = 0x0;
unsigned short memKb = 8;
unsigned char irq1State, 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 / 4096);
/* 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 (0);
}
/************************************************************************
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 (0);
}
/************************************************************************
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, 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;
}