#pypp 0 // Iris: micro-kernel for a capability-based operating system. // memory.ccp: Page allocation system. // Copyright 2009 Bas Wijnen // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . #include "kernel.hh" //#define DEBUG_ALLOC extern unsigned _end static void clear_page (unsigned page, unsigned num = 1): #ifdef DEBUG_ALLOC kdebug ("clearing page ") kdebug_num (page) kdebug ("+") kdebug_num (num << PAGE_BITS) kdebug ('\n') #endif page = (page & ~0xc0000000) | 0xa0000000 for unsigned p = 0; p < num; ++p: arch_uncache_page (page - 0x20000000 + (p << PAGE_BITS)) for unsigned i = 0; i < (1 << (PAGE_BITS - 2)); ++i: // Clear page. ((unsigned *)page)[(p << PAGE_BITS - 2) + i] = 0 if *(unsigned *)page != 0 || ((unsigned *)page)[(num << (PAGE_BITS - 2)) - 1] != 0: kdebug_num ((num << (PAGE_BITS - 2)) - 1) kdebug ("/") kdebug_num (((unsigned *)page)[(num << (PAGE_BITS - 2)) - 1]) kdebug (",") kdebug_num (*(unsigned *)page) kdebug ("\n") dpanic (0, "clear_page didn't work") #if 0 static unsigned free_begin static unsigned free_end unsigned init_memory (unsigned mem): free_begin = ((unsigned)&_end + ~PAGE_MASK) & PAGE_MASK free_end = (0x80000000 + mem) & PAGE_MASK return (free_end - free_begin) >> PAGE_BITS unsigned phys_alloc (unsigned num): if free_begin + num * PAGE_SIZE > free_end: kdebug ("memory allocation failed\n") return 0 unsigned ret = free_begin free_begin += num * PAGE_SIZE clear_page (ret, num) return ret void phys_free (unsigned page, unsigned num): // Not supported. #ifndef NDEBUG /*void check_impl (kObject *o, unsigned num, char const *msg): // for ; o; o = (kObject *)o->next: // if (unsigned)o >= (unsigned)free_begin && (unsigned)o < (unsigned)free_end: */ // panic (num, msg) bool check_free (kObject *o, unsigned size): return true void print_free (): #endif #else struct kFreePages: kFreePages *prev, *next unsigned num static kFreePages *first_free unsigned init_memory (unsigned mem): first_free = (kFreePages *)(((unsigned)&_end + ~PAGE_MASK) & PAGE_MASK) first_free->prev = NULL first_free->next = NULL first_free->num = ((mem & PAGE_MASK) - ((unsigned)first_free & ~0xc0000000)) >> PAGE_BITS #ifdef DEBUG_ALLOC kdebug ("initial memory: ") kdebug_num ((unsigned)first_free & ~0xc0000000) kdebug ("+") kdebug_num (first_free->num) kdebug ("\n") #endif return first_free->num #ifdef DEBUG_ALLOC static bool is_free (unsigned page): for kFreePages *p = first_free; p; p = p->next: if page >= (unsigned)p && page < (unsigned)p + (p->num << PAGE_BITS): return true return false #endif unsigned phys_alloc (unsigned num): kFreePages *choice = NULL for kFreePages *p = first_free; p; p = p->next: if p->num < num: continue if choice && choice->num < p->num: continue if p->num == num: if p->prev: p->prev->next = p->next else: first_free = p->next if p->next: p->next->prev = p->prev clear_page ((unsigned)p, num) #ifdef DEBUG_ALLOC kdebug ("allocating ") kdebug_num ((unsigned)p & ~0xc0000000) kdebug ("+") kdebug_num (num << PAGE_BITS) kdebug ("\n") if is_free ((unsigned)p): panic ((unsigned)p, "page still free after allocation") #endif return (unsigned)p choice = p if !choice: // TODO: reorganizing may work to allow allocation. dpanic (0x03948859, "range memory allocation failed") return 0 choice->num -= num unsigned ret = (unsigned)choice + (choice->num << PAGE_BITS) clear_page (ret, num) #ifdef DEBUG_ALLOC kdebug ("allocating ") kdebug_num (ret & ~0xc0000000) kdebug ("+") kdebug_num (num << PAGE_BITS) kdebug ("\n") if is_free (ret): panic (ret, "page still free after allocation") #endif return ret void phys_free (unsigned page, unsigned num): #ifdef DEBUG_ALLOC kdebug ("free ") kdebug_num (page & ~0xc0000000) kdebug ("+") kdebug_num (num << PAGE_BITS) kdebug ("\n") if is_free (page): panic (page, "page already free") #endif unsigned size = num << PAGE_BITS if !first_free || (unsigned)first_free > page: // This is the first free block. kFreePages *n = (kFreePages *)page n->prev = NULL if (unsigned)first_free == page + size: // It fits exactly before the previous first free block. n->next = first_free->next n->num = num + first_free->num else: // It is a new block. n->next = first_free n->num = num first_free = n if n->next: n->next->prev = n return kFreePages *p for p = first_free; p->next && (unsigned)p->next < page; p = p->next: // Do nothing. if p == p->next: dpanic (0, "page is its own next") // The new block should be inserted directly after p. if (unsigned)p->next == page + size: // It can be merged with the block after it: do that. kFreePages *n = (kFreePages *)page n->prev = p n->next = p->next->next if n->next: n->next->prev = n n->num = num + p->next->num p->next = n if (unsigned)p + (p->num << PAGE_BITS) == (unsigned)n: // It can also be merged with the page before it: do that as well. p->num += n->num p->next = n->next if p->next: p->next->prev = p return // The new block cannot be merged with the block after it. if (unsigned)p + (p->num << PAGE_BITS) == page: // But it can be merged with the one before it: do that. p->num += num return // The new block cannot be merged at all. kFreePages *n = (kFreePages *)page n->next = p->next n->prev = p if n->next: n->next->prev = n p->next = n n->num = num #ifndef NDEBUG bool check_free (kObject *o, unsigned size): for kFreePages *p = first_free; p; p = p->next: if (unsigned)o + size > (unsigned)p && (unsigned)o < (unsigned)p + p->num * PAGE_SIZE: return false return true void print_free (): kdebug ("Free pages: ") for kFreePages *p = first_free; p; p = p->next: kdebug_num ((unsigned)p) kdebug (":") kdebug_num (p->num, 4) kdebug ("->") kdebug ("NULL\n") #endif #endif #ifndef NDEBUG void check_memory (kMemory *mem, unsigned num, char const *msg): check_impl (mem->pages, num, msg) check_impl (mem->threads, num, msg) check_impl (mem->receivers, num, msg) for kReceiver *r = mem->receivers; r; r = (kReceiver *)r->next: check_impl (r->messages, num, msg) check_impl (mem->capses, num, msg) check_impl (mem->memories, num, msg) for kMemory *m = mem->memories; m; m = (kMemory *)m->next: check_memory (m, num, msg) void check (unsigned num, char const *msg): check_memory (&top_memory, num, msg) top_memory.check (num) #endif unsigned raw_zalloc (): return phys_alloc (1) void raw_pfree (unsigned page): return phys_free (page, 1) unsigned kMemory::zalloc (): if !use (): dpanic (0x34638920, "limit reached: allocation not allowed") kdebug ("limit reached: allocation not allowed\n") return NULL return raw_zalloc () void kMemory::pfree (unsigned page): unuse () return raw_pfree (page) unsigned kMemory::palloc (): return zalloc () void kMemory::zfree (unsigned page): pfree (page)