about summary refs log blame commit diff stats
path: root/kernel.soso/alloc.c
blob: eceb3aa5dd323361f08d107ed9a85e818ba11ec3 (plain) (tree)
































































































































































































































































                                                                                                                                 
#include "common.h"
#include "screen.h"
#include "alloc.h"
#include "vmm.h"
#include "process.h"
#include "debugprint.h"

#define KMALLOC_MINSIZE		16

extern uint32 *gKernelPageDirectory;

static char *gKernelHeap = NULL;
static uint32 gKernelHeapUsed = 0;


void initializeKernelHeap()
{
    gKernelHeap = (char *) KERN_HEAP_BEGIN;

    ksbrkPage(1);
}

void *ksbrkPage(int n)
{
    struct MallocHeader *chunk;
    char *p_addr;
    int i;

    if ((gKernelHeap + (n * PAGESIZE_4M)) > (char *) KERN_HEAP_END) {
        //Screen_PrintF("ERROR: ksbrk(): no virtual memory left for kernel heap !\n");
        return (char *) -1;
    }

    chunk = (struct MallocHeader *) gKernelHeap;

    for (i = 0; i < n; i++)
    {
        p_addr = getPageFrame4M();

        //Screen_PrintF("DEBUG: ksbrkPage(): got 4M on physical %x\n", p_addr);

        if ((int)(p_addr) < 0)
        {
            PANIC("PANIC: ksbrkPage(): no free page frame available !");
            return (char *) -1;
        }

        addPageToPd(gKernelPageDirectory, gKernelHeap, p_addr, 0); //add PG_USER to allow user programs to read kernel heap

        gKernelHeap += PAGESIZE_4M;
    }

    chunk->size = PAGESIZE_4M * n;
    chunk->used = 0;

    return chunk;
}

void *kmalloc(uint32 size)
{
    if (size==0)
        return 0;

    unsigned long realsize;
    struct MallocHeader *chunk, *other;

    if ((realsize = sizeof(struct MallocHeader) + size) < KMALLOC_MINSIZE)
    {
        realsize = KMALLOC_MINSIZE;
    }

    chunk = (struct MallocHeader *) KERN_HEAP_BEGIN;
    while (chunk->used || chunk->size < realsize)
    {
        if (chunk->size == 0)
        {
            printkf("\nPANIC: kmalloc(): corrupted chunk on %x with null size (heap %x) !\nSystem halted\n", chunk, gKernelHeap);

            PANIC("kmalloc()");

            return 0;
        }

        chunk = (struct MallocHeader *)((char *)chunk + chunk->size);

        if (chunk == (struct MallocHeader *) gKernelHeap)
        {
            if ((int)(ksbrkPage((realsize / PAGESIZE_4M) + 1)) < 0)
            {
                PANIC("kmalloc(): no memory left for kernel !\nSystem halted\n");

                return 0;
            }
        }
        else if (chunk > (struct MallocHeader *) gKernelHeap)
        {
            printkf("\nPANIC: kmalloc(): chunk on %x while heap limit is on %x !\nSystem halted\n", chunk, gKernelHeap);

            PANIC("kmalloc()");

            return 0;
        }
    }


    if (chunk->size - realsize < KMALLOC_MINSIZE)
    {
        chunk->used = 1;
    }
    else
    {
        other = (struct MallocHeader *)((char *) chunk + realsize);
        other->size = chunk->size - realsize;
        other->used = 0;

        chunk->size = realsize;
        chunk->used = 1;
    }

    gKernelHeapUsed += realsize;

    return (char *) chunk + sizeof(struct MallocHeader);
}

void kfree(void *v_addr)
{
    if (v_addr==(void*)0)
        return;

    struct MallocHeader *chunk, *other;

    chunk = (struct MallocHeader *)((uint32)v_addr - sizeof(struct MallocHeader));
    chunk->used = 0;

    gKernelHeapUsed -= chunk->size;

    //Merge free block with next free block
    while ((other = (struct MallocHeader *)((char *)chunk + chunk->size))
           && other < (struct MallocHeader *)gKernelHeap
           && other->used == 0)
    {
        chunk->size += other->size;
    }
}

static void sbrkPage(Process* process, int pageCount)
{
    if (pageCount > 0)
    {
        for (int i = 0; i < pageCount; ++i)
        {
            if ((process->heapNextUnallocatedPageBegin + PAGESIZE_4M) > (char*)USER_OFFSET_END)
            {
                return;
            }

            char * p_addr = getPageFrame4M();

            if ((int)(p_addr) < 0)
            {
                //PANIC("sbrkPage(): no free page frame available !");
                return;
            }

            addPageToPd(process->pd, process->heapNextUnallocatedPageBegin, p_addr, PG_USER);

            process->heapNextUnallocatedPageBegin += PAGESIZE_4M;
        }
    }
    else if (pageCount < 0)
    {
        pageCount *= -1;

        for (int i = 0; i < pageCount; ++i)
        {
            if (process->heapNextUnallocatedPageBegin - PAGESIZE_4M >= process->heapBegin)
            {
                process->heapNextUnallocatedPageBegin -= PAGESIZE_4M;

                //This also releases the page frame
                removePageFromPd(process->pd, process->heapNextUnallocatedPageBegin, TRUE);
            }
        }
    }
}

void initializeProcessHeap(Process* process)
{
    process->heapBegin = (char*) USER_OFFSET;
    process->heapEnd = process->heapBegin;
    process->heapNextUnallocatedPageBegin = process->heapBegin;

    //Userland programs (their code, data,..) start from USER_OFFSET
    //So we should leave some space for them by moving heap pointer.
    //As a result userspace malloc functions start from a forward point (+ USER_EXE_IMAGE).

    sbrk(process, USER_EXE_IMAGE);
}

void *sbrk(Process* process, int nBytes)
{
    printkf("sbrk:1: pid:%d nBytes:%d\n", process->pid, nBytes);

    char* previousBreak = process->heapEnd;
    printkf("before: %x\n", previousBreak);

    if (nBytes > 0)
    {
        int remainingInThePage = process->heapNextUnallocatedPageBegin - process->heapEnd;

        //Screen_PrintF("sbrk:2: remainingInThePage:%d\n", remainingInThePage);

        if (nBytes > remainingInThePage)
        {
            int bytesNeededInNewPages = nBytes - remainingInThePage;
            int neededNewPageCount = (bytesNeededInNewPages / PAGESIZE_4M) + 1;

            //Screen_PrintF("sbrk:3: neededNewPageCount:%d\n", neededNewPageCount);

            uint32 freePages = getFreePageCount();
            if ((uint32)neededNewPageCount + 1 > freePages)
            {
                return (void*)-1;
            }

            sbrkPage(process, neededNewPageCount);
        }
    }
    else if (nBytes < 0)
    {
        char* currentPageBegin = process->heapNextUnallocatedPageBegin - PAGESIZE_4M;

        int remainingInThePage = process->heapEnd - currentPageBegin;

        //Screen_PrintF("sbrk:4: remainingInThePage:%d\n", remainingInThePage);

        if (-nBytes > remainingInThePage)
        {
            int bytesInPreviousPages = -nBytes - remainingInThePage;
            int neededNewPageCount = (bytesInPreviousPages / PAGESIZE_4M) + 1;

            //Screen_PrintF("sbrk:5: neededNewPageCount:%d\n", neededNewPageCount);

            sbrkPage(process, -neededNewPageCount);
        }
    }

    process->heapEnd += nBytes;
    printkf("after: %x\n", process->heapEnd);

    return previousBreak;
}

uint32 getKernelHeapUsed()
{
    return gKernelHeapUsed;
}