#include #include #include #include #include #include #include #define MALLOC_MAGIC 0x616c6c6f63686472 /* "allochdr" */ #define DESCLEN 8 typedef struct Allocation Allocation; struct Allocation { uint64_t magic; uint64_t len; Allocation *next, *prev; void *stacktrace[4]; char desc[DESCLEN]; }; static Allocation *malloc_last = NULL; extern uint8_t pbitmap[]; /* linker.ld */ static size_t pbitmap_len; /* in bytes */ static void *firstfreepage; /* not necessarily actually free */ static void *memtop; static size_t toindex(void *p) { assert((void*)pbitmap <= p); return ((uintptr_t)p - (uintptr_t)pbitmap) / PAGE_SIZE; } static bool pbitmap_get(void *p) { size_t i = toindex(p); size_t b = i / 8; uint8_t m = 1 << (i&7); assert(b < pbitmap_len); // TODO the bitmap should be a tad longer return (pbitmap[b]&m) != 0; } static bool pbitmap_set(void *p, bool v) { size_t i = toindex(p); size_t b = i / 8; uint8_t m = 1 << (i&7); assert(b < pbitmap_len); bool prev = (pbitmap[b]&m) != 0; if (v) { pbitmap[b] |= m; } else { pbitmap[b] &= ~m; } return prev; } static size_t page_amt(size_t bytes) { return (bytes + PAGE_SIZE - 1) / PAGE_SIZE; } void mem_init(void *p) { memtop = p; kprintf("memory %8x -> %8x\n", &_bss_end, memtop); pbitmap_len = toindex(memtop) / 8; memset(pbitmap, 0, pbitmap_len); mem_reserve(pbitmap, pbitmap_len); firstfreepage = pbitmap; } void mem_reserve(void *addr, size_t len) { kprintf("reserved %8x -> %8x\n", addr, addr + len); void *top = min(addr + len, memtop); addr = (void*)((uintptr_t)addr & ~PAGE_MASK); /* round down to page */ for (void *p = max(addr, (void*)pbitmap); p < top; p += PAGE_SIZE) { pbitmap_set(p, true); } } void mem_debugprint(void) { size_t count = 0, bytes = 0, pages = 0; kprintf("[kern] current allocations:\n"); for (Allocation *iter = malloc_last; iter; iter = iter->prev) { kprintf( "%08p %6dB %.8s ", ((void*)iter) + sizeof(Allocation), iter->len - sizeof(Allocation), iter->desc ); for (size_t i = 0; i < 4; i++) { kprintf(" k/%08x", iter->stacktrace[i]); } kprintf("\n"); count++; bytes += iter->len; pages += page_amt(iter->len); } kprintf( "%d in total, %d bytes, %d pages = %dB used\n", count, bytes, pages, pages*PAGE_SIZE ); } void * page_alloc(size_t pages) { assert(pages == 1); for (void *p = firstfreepage; p < memtop; p += PAGE_SIZE) { if (!pbitmap_get(p)) { pbitmap_set(p, true); firstfreepage = p + PAGE_SIZE; return p; } } kprintf("we ran out of memory :(\ngoodbye.\n"); panic_unimplemented(); } void * page_zalloc(size_t pages) { void *p = page_alloc(pages); memset(p, 0, pages * PAGE_SIZE); return p; } void page_free(void *addr, size_t pages) { assert((void*)pbitmap <= addr); for (size_t i = 0; i < pages; i++) { if (pbitmap_set(addr + i*PAGE_SIZE, false) == false) { panic_invalid_state(); } } } static void kmalloc_sanity(const void *addr) { assert(addr); const Allocation *hdr = addr - sizeof(Allocation); assert(hdr->magic == MALLOC_MAGIC); if (hdr->next) assert(hdr->next->prev == hdr); if (hdr->prev) assert(hdr->prev->next == hdr); } void * kmalloc(size_t len, const char *desc) { Allocation *hdr; void *addr; if (KMALLOC_MAX < len) { panic_invalid_state(); } len += sizeof(Allocation); assert(len <= PAGE_SIZE); hdr = page_alloc(page_amt(len)); hdr->magic = MALLOC_MAGIC; hdr->len = len; memset(hdr->desc, ' ', DESCLEN); if (desc) { for (int i = 0; i < DESCLEN; i++) { if (desc[i] == '\0') break; hdr->desc[i] = desc[i]; } } hdr->next = NULL; hdr->prev = malloc_last; if (hdr->prev) { assert(!hdr->prev->next); hdr->prev->next = hdr; } for (size_t i = 0; i < 4; i++) hdr->stacktrace[i] = debug_caller(i); malloc_last = hdr; addr = (void*)hdr + sizeof(Allocation); #ifndef NDEBUG memset(addr, 0xCC, len); #endif kmalloc_sanity(addr); return addr; } void kfree(void *ptr) { Allocation *hdr; size_t pages; if (ptr == NULL) return; hdr = ptr - sizeof(Allocation); kmalloc_sanity(ptr); pages = page_amt(hdr->len); hdr->magic = ~MALLOC_MAGIC; // (hopefully) detect double frees if (hdr->next) hdr->next->prev = hdr->prev; if (hdr->prev) hdr->prev->next = hdr->next; if (malloc_last == hdr) malloc_last = hdr->prev; #ifndef NDEBUG memset(hdr, 0xC0, pages * PAGE_SIZE); #endif page_free(hdr, pages); }