about summary refs log blame commit diff stats
path: root/tools/iso/kernel.soso/alloc.c
blob: 6e81b13c3ccd36f139dc565c13a22e2dc6e3302a (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15














                                    
                             




                                           
                        




                                                                     
                                                                                




                                                
                             

                                  
                                                                         
 
                                














                                                                                                                           
                            





                                       
                                                                            



                                                    

                                                   








                                                                                                                                 

                                                                     




                                                                                 
                                                               








                                                                                                                        
                                                   

                        
          












                                                                   
                          












                                                                                  
                                 



                                   



                                                                                                 




                                             
                                    








                                                                                             
                             

                        

                                                                                            








                                                                                           
                                              










                                                                                           
                                          




                                                                
                     

                                                                                          
                                                                         
 
                                          


                                                                               
                                                                             

                                                  
                                                             





                                                  
                          



                                                                                     
                                                                         
 
                                           


                                                                              
                                                                             










                                                   
                            

                           
#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) {
        //printkf("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();

        //printkf("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;

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

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

            //printkf("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;

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

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

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

            sbrkPage(process, -neededNewPageCount);
        }
    }

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

    return previousBreak;
}

uint32 getKernelHeapUsed() {
    return gKernelHeapUsed;
}