diff --git a/Makefile b/Makefile index 79dbf5c..6029671 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ OBJS = bin/boot.o bin/kernel.o bin/idt.o bin/isr.o bin/screen.o \ bin/utils.o bin/string.o bin/stdio.o bin/disk.o bin/pic.o bin/pit.o \ bin/keyboard.o bin/paging.o bin/page_alloc.o bin/page_tables.o \ - bin/fat.o bin/task.o bin/switch.o bin/sys_exit.o bin/gdt.o bin/isr-asm.o + bin/fat.o bin/task.o bin/switch.o bin/sys_exit.o bin/gdt.o bin/isr-asm.o bin/kheap.o bin/liballoc.o # Define the compiler and assembler #CC = i686-elf-gcc -D__is_katauos @@ -124,6 +124,13 @@ bin/gdt.o: src/kernel/gdt.c bin/isr-asm.o: src/kernel/isr.asm $(AS) $(ASFLAGS) $< -o $@ +bin/kheap.o: src/mm/kheap.c + $(CC) $(CFLAGS) -c $< -o $@ + +bin/liballoc.o: src/mm/liballoc.c + $(CC) $(CFLAGS) -c $< -o $@ + + clean: rm -rf bin/ rm -f $(OUTPUT) diff --git a/include/kheap.h b/include/kheap.h new file mode 100644 index 0000000..9bb3a62 --- /dev/null +++ b/include/kheap.h @@ -0,0 +1,16 @@ +#ifndef KERNEL_HEAP_H +#define KERNEL_HEAP_H + +#include +#include + +#define KHEAP_START 0xE0000000 // Start of kernel heap +#define KHEAP_INITIAL_SIZE 0x100000 // 1 MiB initial size +#define KHEAP_END (KHEAP_START + KHEAP_INITIAL_SIZE) +#define KHEAP_PAGES (KHEAP_INITIAL_SIZE / PAGE_SIZE) + +void kheap_init(); +void* kvalloc(size_t npages); +void kvfree(void* addr, size_t npages); + +#endif \ No newline at end of file diff --git a/include/liballoc.h b/include/liballoc.h new file mode 100644 index 0000000..fc65980 --- /dev/null +++ b/include/liballoc.h @@ -0,0 +1,89 @@ +#ifndef _LIBALLOC_H +#define _LIBALLOC_H + + + +// If we are told to not define our own size_t, then we +// skip the define. +#ifndef _ALLOC_SKIP_DEFINE + +#ifndef _HAVE_SIZE_T +#define _HAVE_SIZE_T +typedef unsigned int size_t; +#endif + + +#ifndef NULL +#define NULL 0 +#endif + +#endif + + +/** This is a boundary tag which is prepended to the + * page or section of a page which we have allocated. It is + * used to identify valid memory blocks that the + * application is trying to free. + */ +struct boundary_tag +{ + unsigned int magic; //< It's a kind of ... + unsigned int size; //< Requested size. + unsigned int real_size; //< Actual size. + int index; //< Location in the page table. + + struct boundary_tag *split_left; //< Linked-list info for broken pages. + struct boundary_tag *split_right; //< The same. + + struct boundary_tag *next; //< Linked list info. + struct boundary_tag *prev; //< Linked list info. +}; + + + + +/** This function is supposed to lock the memory data structures. It + * could be as simple as disabling interrupts or acquiring a spinlock. + * It's up to you to decide. + * + * \return 0 if the lock was acquired successfully. Anything else is + * failure. + */ +extern int liballoc_lock(); + +/** This function unlocks what was previously locked by the liballoc_lock + * function. If it disabled interrupts, it enables interrupts. If it + * had acquiried a spinlock, it releases the spinlock. etc. + * + * \return 0 if the lock was successfully released. + */ +extern int liballoc_unlock(); + +/** This is the hook into the local system which allocates pages. It + * accepts an integer parameter which is the number of pages + * required. The page size was set up in the liballoc_init function. + * + * \return NULL if the pages were not allocated. + * \return A pointer to the allocated memory. + */ +extern void* liballoc_alloc(int); + +/** This frees previously allocated memory. The void* parameter passed + * to the function is the exact same value returned from a previous + * liballoc_alloc call. + * + * The integer value is the number of pages to free. + * + * \return 0 if the memory was successfully freed. + */ +extern int liballoc_free(void*,int); + + + +void *malloc(size_t); //< The standard function. +void *realloc(void *, size_t); //< The standard function. +void *calloc(size_t, size_t); //< The standard function. +void free(void *); //< The standard function. + +#endif + diff --git a/src/kernel/kernel.c b/src/kernel/kernel.c index 4dc7567..f78974b 100644 --- a/src/kernel/kernel.c +++ b/src/kernel/kernel.c @@ -10,6 +10,7 @@ #include "../include/fat.h" #include "../include/task.h" #include "../include/multiboot.h" +#include "../include/liballoc.h" #include @@ -187,7 +188,7 @@ void kmain(unsigned int magic, unsigned int info) //heap_init(); //printf("after heap_init\n"); __asm__ __volatile__ ("sti"); - scheduler_init(); + //scheduler_init(); printf("Kernel init sequence completed\n"); char yooo[256] = "heheh"; @@ -204,8 +205,8 @@ void kmain(unsigned int magic, unsigned int info) char temp1[1024] = "rusya_krutoy"; char temp2[1024] = "katya_tozhe_krutaya"; - huy = alloc_page();//heap_alloc(1024); - huy2 = alloc_page();//heap_alloc(1024); + huy = malloc(64);//alloc_page();//heap_alloc(1024); + huy2 = malloc(64);//alloc_page();//heap_alloc(1024); memcpy(huy, temp1, sizeof(temp1)); memcpy(huy2, temp2, sizeof(temp1)); diff --git a/src/mm/kheap.c b/src/mm/kheap.c new file mode 100644 index 0000000..91337cb --- /dev/null +++ b/src/mm/kheap.c @@ -0,0 +1,136 @@ +// kernel_heap.c +#include "../include/kheap.h" +#include "../include/paging.h" +#include "../include/stdio.h" +#include "../include/string.h" + +#include + +int liballoc_lock() { + // Implement locking (disable interrupts or use spinlock) + return 0; +} + +int liballoc_unlock() { + // Implement unlocking + return 0; +} + +void* liballoc_alloc(int pages) { + return kvalloc(pages); +} + +int liballoc_free(void* ptr, int pages) { + kvfree(ptr, pages); + return 0; +} + +static uint8_t kheap_bitmap[KHEAP_PAGES / 8]; + +// Test a bit in kernel heap bitmap +static int kheap_bitmap_test(uint32_t index) { + uint32_t byte = index / 8; + uint8_t bit = index % 8; + return (kheap_bitmap[byte] >> bit) & 1; +} + +// Set a bit in kernel heap bitmap +static void kheap_bitmap_set(uint32_t index) { + uint32_t byte = index / 8; + uint8_t bit = index % 8; + kheap_bitmap[byte] |= (1 << bit); +} + +// Clear a bit in kernel heap bitmap +static void kheap_bitmap_clear(uint32_t index) { + uint32_t byte = index / 8; + uint8_t bit = index % 8; + kheap_bitmap[byte] &= ~(1 << bit); +} + +// Initialize kernel heap +void kheap_init() { + memset(kheap_bitmap, 0, sizeof(kheap_bitmap)); +} + +// Allocate contiguous virtual pages +void* kvalloc(size_t npages) { + // Limit allocation size for safety + if (npages > 1024 || npages == 0) { + return NULL; + } + + uint32_t start = 0; + uint32_t count = 0; + + // Find contiguous free pages in bitmap + for (uint32_t i = 0; i < KHEAP_PAGES; i++) { + if (!kheap_bitmap_test(i)) { + count++; + if (count >= npages) { + start = i - npages + 1; + break; + } + } else { + count = 0; + } + } + + if (count < npages) { + return NULL; // Not enough contiguous space + } + + // Temporarily store physical addresses + void* phys_addrs[npages]; + uint32_t vaddr = KHEAP_START + start * PAGE_SIZE; + + // Allocate physical pages first + for (uint32_t i = 0; i < npages; i++) { + phys_addrs[i] = alloc_page(); + if (!phys_addrs[i]) { + // Cleanup on failure + for (uint32_t j = 0; j < i; j++) { + free_page(phys_addrs[j]); + } + return NULL; + } + } + + // Mark virtual pages as allocated + for (uint32_t i = start; i < start + npages; i++) { + kheap_bitmap_set(i); + } + + // Map virtual to physical pages + for (uint32_t i = 0; i < npages; i++) { + map_page(phys_addrs[i], (void*)(vaddr + i * PAGE_SIZE), + PAGE_PRESENT | PAGE_RW); + } + + return (void*)vaddr; +} + +// Free allocated pages +void kvfree(void* addr, size_t npages) { + uint32_t vaddr = (uint32_t)addr; + if (vaddr < KHEAP_START || vaddr >= KHEAP_END) { + printf("kvfree: invalid address 0x%x\n", vaddr); + return; + } + + uint32_t start = (vaddr - KHEAP_START) / PAGE_SIZE; + + for (uint32_t i = 0; i < npages; i++) { + uint32_t current_vaddr = vaddr + i * PAGE_SIZE; + void* phys = get_physaddr((void*)current_vaddr); + + if (phys) { + free_page(phys); + unmap_page((void*)current_vaddr); + } else { + printf("kvfree: no physical mapping for 0x%x\n", current_vaddr); + } + + kheap_bitmap_clear(start + i); + } +} \ No newline at end of file diff --git a/src/mm/liballoc.c b/src/mm/liballoc.c new file mode 100644 index 0000000..a728f87 --- /dev/null +++ b/src/mm/liballoc.c @@ -0,0 +1,533 @@ +#include + +/** Durand's Ridiculously Amazing Super Duper Memory functions. */ + +//#define DEBUG + +#define LIBALLOC_MAGIC 0xc001c0de +#define MAXCOMPLETE 5 +#define MAXEXP 32 +#define MINEXP 8 + +#define MODE_BEST 0 +#define MODE_INSTANT 1 + +#define MODE MODE_BEST + +#ifdef DEBUG +#include +#endif + + +struct boundary_tag* l_freePages[MAXEXP]; //< Allowing for 2^MAXEXP blocks +int l_completePages[MAXEXP]; //< Allowing for 2^MAXEXP blocks + + +#ifdef DEBUG +unsigned int l_allocated = 0; //< The real amount of memory allocated. +unsigned int l_inuse = 0; //< The amount of memory in use (malloc'ed). +#endif + + +static int l_initialized = 0; //< Flag to indicate initialization. +static int l_pageSize = 4096; //< Individual page size +static int l_pageCount = 16; //< Minimum number of pages to allocate. + + +// *********** HELPER FUNCTIONS ******************************* + +/** Returns the exponent required to manage 'size' amount of memory. + * + * Returns n where 2^n <= size < 2^(n+1) + */ +static inline int getexp( unsigned int size ) +{ + if ( size < (1< size ) break; + shift += 1; + } + + #ifdef DEBUG + printf("getexp returns %i (%i bytes) for %i size\n", shift - 1, (1<<(shift -1)), size ); + #endif + + return shift - 1; +} + + +static void* liballoc_memset(void* s, int c, size_t n) +{ + int i; + for ( i = 0; i < n ; i++) + ((char*)s)[i] = c; + + return s; +} + +static void* liballoc_memcpy(void* s1, const void* s2, size_t n) +{ + char *cdest; + char *csrc; + unsigned int *ldest = (unsigned int*)s1; + unsigned int *lsrc = (unsigned int*)s2; + + while ( n >= sizeof(unsigned int) ) + { + *ldest++ = *lsrc++; + n -= sizeof(unsigned int); + } + + cdest = (char*)ldest; + csrc = (char*)lsrc; + + while ( n > 0 ) + { + *cdest++ = *csrc++; + n -= 1; + } + + return s1; +} + + + +#ifdef DEBUG +static void dump_array() +{ + int i = 0; + struct boundary_tag *tag = NULL; + + printf("------ Free pages array ---------\n"); + printf("System memory allocated: %i\n", l_allocated ); + printf("Memory in used (malloc'ed): %i\n", l_inuse ); + + for ( i = 0; i < MAXEXP; i++ ) + { + printf("%.2i(%i): ",i, l_completePages[i] ); + + tag = l_freePages[ i ]; + while ( tag != NULL ) + { + if ( tag->split_left != NULL ) printf("*"); + printf("%i", tag->real_size ); + if ( tag->split_right != NULL ) printf("*"); + + printf(" "); + tag = tag->next; + } + printf("\n"); + } + + printf("'*' denotes a split to the left/right of a tag\n"); + fflush( stdout ); +} +#endif + + + +static inline void insert_tag( struct boundary_tag *tag, int index ) +{ + int realIndex; + + if ( index < 0 ) + { + realIndex = getexp( tag->real_size - sizeof(struct boundary_tag) ); + if ( realIndex < MINEXP ) realIndex = MINEXP; + } + else + realIndex = index; + + tag->index = realIndex; + + if ( l_freePages[ realIndex ] != NULL ) + { + l_freePages[ realIndex ]->prev = tag; + tag->next = l_freePages[ realIndex ]; + } + + l_freePages[ realIndex ] = tag; +} + +static inline void remove_tag( struct boundary_tag *tag ) +{ + if ( l_freePages[ tag->index ] == tag ) l_freePages[ tag->index ] = tag->next; + + if ( tag->prev != NULL ) tag->prev->next = tag->next; + if ( tag->next != NULL ) tag->next->prev = tag->prev; + + tag->next = NULL; + tag->prev = NULL; + tag->index = -1; +} + + +static inline struct boundary_tag* melt_left( struct boundary_tag *tag ) +{ + struct boundary_tag *left = tag->split_left; + + left->real_size += tag->real_size; + left->split_right = tag->split_right; + + if ( tag->split_right != NULL ) tag->split_right->split_left = left; + + return left; +} + + +static inline struct boundary_tag* absorb_right( struct boundary_tag *tag ) +{ + struct boundary_tag *right = tag->split_right; + + remove_tag( right ); // Remove right from free pages. + + tag->real_size += right->real_size; + + tag->split_right = right->split_right; + if ( right->split_right != NULL ) + right->split_right->split_left = tag; + + return tag; +} + +static inline struct boundary_tag* split_tag( struct boundary_tag* tag ) +{ + unsigned int remainder = tag->real_size - sizeof(struct boundary_tag) - tag->size; + + struct boundary_tag *new_tag = + (struct boundary_tag*)((unsigned int)tag + sizeof(struct boundary_tag) + tag->size); + + new_tag->magic = LIBALLOC_MAGIC; + new_tag->real_size = remainder; + + new_tag->next = NULL; + new_tag->prev = NULL; + + new_tag->split_left = tag; + new_tag->split_right = tag->split_right; + + if (new_tag->split_right != NULL) new_tag->split_right->split_left = new_tag; + tag->split_right = new_tag; + + tag->real_size -= new_tag->real_size; + + insert_tag( new_tag, -1 ); + + return new_tag; +} + + +// *************************************************************** + + + + +static struct boundary_tag* allocate_new_tag( unsigned int size ) +{ + unsigned int pages; + unsigned int usage; + struct boundary_tag *tag; + + // This is how much space is required. + usage = size + sizeof(struct boundary_tag); + + // Perfect amount of space + pages = usage / l_pageSize; + if ( (usage % l_pageSize) != 0 ) pages += 1; + + // Make sure it's >= the minimum size. + if ( pages < l_pageCount ) pages = l_pageCount; + + tag = (struct boundary_tag*)liballoc_alloc( pages ); + + if ( tag == NULL ) return NULL; // uh oh, we ran out of memory. + + tag->magic = LIBALLOC_MAGIC; + tag->size = size; + tag->real_size = pages * l_pageSize; + tag->index = -1; + + tag->next = NULL; + tag->prev = NULL; + tag->split_left = NULL; + tag->split_right = NULL; + + + #ifdef DEBUG + printf("Resource allocated %x of %i pages (%i bytes) for %i size.\n", tag, pages, pages * l_pageSize, size ); + + l_allocated += pages * l_pageSize; + + printf("Total memory usage = %i KB\n", (int)((l_allocated / (1024))) ); + #endif + + return tag; +} + + + +void *malloc(size_t size) +{ + int index; + void *ptr; + struct boundary_tag *tag = NULL; + + liballoc_lock(); + + if ( l_initialized == 0 ) + { + #ifdef DEBUG + printf("%s\n","liballoc initializing."); + #endif + for ( index = 0; index < MAXEXP; index++ ) + { + l_freePages[index] = NULL; + l_completePages[index] = 0; + } + l_initialized = 1; + } + + index = getexp( size ) + MODE; + if ( index < MINEXP ) index = MINEXP; + + + // Find one big enough. + tag = l_freePages[ index ]; // Start at the front of the list. + while ( tag != NULL ) + { + // If there's enough space in this tag. + if ( (tag->real_size - sizeof(struct boundary_tag)) + >= (size + sizeof(struct boundary_tag) ) ) + { + #ifdef DEBUG + printf("Tag search found %i >= %i\n",(tag->real_size - sizeof(struct boundary_tag)), (size + sizeof(struct boundary_tag) ) ); + #endif + break; + } + + tag = tag->next; + } + + + // No page found. Make one. + if ( tag == NULL ) + { + if ( (tag = allocate_new_tag( size )) == NULL ) + { + liballoc_unlock(); + return NULL; + } + + index = getexp( tag->real_size - sizeof(struct boundary_tag) ); + } + else + { + remove_tag( tag ); + + if ( (tag->split_left == NULL) && (tag->split_right == NULL) ) + l_completePages[ index ] -= 1; + } + + // We have a free page. Remove it from the free pages list. + + tag->size = size; + + // Removed... see if we can re-use the excess space. + + #ifdef DEBUG + printf("Found tag with %i bytes available (requested %i bytes, leaving %i), which has exponent: %i (%i bytes)\n", tag->real_size - sizeof(struct boundary_tag), size, tag->real_size - size - sizeof(struct boundary_tag), index, 1<real_size - size - sizeof( struct boundary_tag ) * 2; // Support a new tag + remainder + + if ( ((int)(remainder) > 0) /*&& ( (tag->real_size - remainder) >= (1<= 0 ) + { + #ifdef DEBUG + printf("Seems to be splittable: %i >= 2^%i .. %i\n", remainder, childIndex, (1<real_size, new_tag->real_size, new_tag->index ); + #endif + } + } + + + + ptr = (void*)((unsigned int)tag + sizeof( struct boundary_tag ) ); + + + + #ifdef DEBUG + l_inuse += size; + printf("malloc: %x, %i, %i\n", ptr, (int)l_inuse / 1024, (int)l_allocated / 1024 ); + dump_array(); + #endif + + + liballoc_unlock(); + return ptr; +} + + + + + +void free(void *ptr) +{ + int index; + struct boundary_tag *tag; + + if ( ptr == NULL ) return; + + liballoc_lock(); + + + tag = (struct boundary_tag*)((unsigned int)ptr - sizeof( struct boundary_tag )); + + if ( tag->magic != LIBALLOC_MAGIC ) + { + liballoc_unlock(); // release the lock + return; + } + + + + #ifdef DEBUG + l_inuse -= tag->size; + printf("free: %x, %i, %i\n", ptr, (int)l_inuse / 1024, (int)l_allocated / 1024 ); + #endif + + + // MELT LEFT... + while ( (tag->split_left != NULL) && (tag->split_left->index >= 0) ) + { + #ifdef DEBUG + printf("Melting tag left into available memory. Left was %i, becomes %i (%i)\n", tag->split_left->real_size, tag->split_left->real_size + tag->real_size, tag->split_left->real_size ); + #endif + tag = melt_left( tag ); + remove_tag( tag ); + } + + // MELT RIGHT... + while ( (tag->split_right != NULL) && (tag->split_right->index >= 0) ) + { + #ifdef DEBUG + printf("Melting tag right into available memory. This was was %i, becomes %i (%i)\n", tag->real_size, tag->split_right->real_size + tag->real_size, tag->split_right->real_size ); + #endif + tag = absorb_right( tag ); + } + + + // Where is it going back to? + index = getexp( tag->real_size - sizeof(struct boundary_tag) ); + if ( index < MINEXP ) index = MINEXP; + + // A whole, empty block? + if ( (tag->split_left == NULL) && (tag->split_right == NULL) ) + { + + if ( l_completePages[ index ] == MAXCOMPLETE ) + { + // Too many standing by to keep. Free this one. + unsigned int pages = tag->real_size / l_pageSize; + + if ( (tag->real_size % l_pageSize) != 0 ) pages += 1; + if ( pages < l_pageCount ) pages = l_pageCount; + + liballoc_free( tag, pages ); + + #ifdef DEBUG + l_allocated -= pages * l_pageSize; + printf("Resource freeing %x of %i pages\n", tag, pages ); + dump_array(); + #endif + + liballoc_unlock(); + return; + } + + + l_completePages[ index ] += 1; // Increase the count of complete pages. + } + + + // .......... + + + insert_tag( tag, index ); + + #ifdef DEBUG + printf("Returning tag with %i bytes (requested %i bytes), which has exponent: %i\n", tag->real_size, tag->size, index ); + dump_array(); + #endif + + liballoc_unlock(); +} + + + + +void* calloc(size_t nobj, size_t size) +{ + int real_size; + void *p; + + real_size = nobj * size; + + p = malloc( real_size ); + + liballoc_memset( p, 0, real_size ); + + return p; +} + + + +void* realloc(void *p, size_t size) +{ + void *ptr; + struct boundary_tag *tag; + int real_size; + + if ( size == 0 ) + { + free( p ); + return NULL; + } + if ( p == NULL ) return malloc( size ); + + if ( liballoc_lock != NULL ) liballoc_lock(); // lockit + tag = (struct boundary_tag*)((unsigned int)p - sizeof( struct boundary_tag )); + real_size = tag->size; + if ( liballoc_unlock != NULL ) liballoc_unlock(); + + if ( real_size > size ) real_size = size; + + ptr = malloc( size ); + liballoc_memcpy( ptr, p, real_size ); + free( p ); + + return ptr; +} + +