#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; }