#pypp 0 // Iris: micro-kernel for a capability-based operating system. // alloc.ccp: Allocation of kernel structures. // 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" // Memory model used for kernel structure storage // Each Memory object has several pointers, one for each type of objects it contains. These pointers are the start of double-linked lists. // Each object also has a NEXT and PREV pointer, which point to the next and previous object in the same page. These pointers are 0 for the first (or last) object in a page. There is no pointer to the first object, it always starts at page_base + SIZE. // The PREV/NEXT-list contains all objects in the page, including Free objects, in the order they appear in the page. // The prev/next-lists contain only objects of one type, unsorted. // All pointers are to the start of the object. There is a header of size SIZE before it, containing NEXT and PREV. #define PREV(x) (((Object_base **)(x))[-2]) #define NEXT(x) (((Object_base **)(x))[-1]) #define SIZE (2 * sizeof (Object_base *)) bool Memory::use (unsigned num): // Go up to parents, incrementing used. for Memory *m = this; m; m = m->address_space: if used + num > limit: // Not allowed. Restore used for all children. for Memory *r = this; r != m; r = r->address_space: r->used -= num return false m->used += num return true void Memory::unuse (unsigned num): for Memory *m = this; m; m = m->address_space: m->used -= num // This allocates a new block of memory for use by the kernel. // size is the requires size of the block (excluding SIZE) // first is a pointer to the first object pointer of this type. // The result is a block of size at least size, which is linked as an object in the list of first. void *Memory::search_free (unsigned size, void **first): Free *f unsigned s = 0 // Let's see if there already is a Free chunk which is large enough. for f = frees; f; f = f->next: if NEXT (f): s = (unsigned)NEXT (f) - (unsigned)f else: s = PAGE_SIZE - ((unsigned)f & ~PAGE_MASK) + SIZE // s is now the size of the current free block, including SIZE. // The requirement is to fit a block of size, plus its SIZE header. if s >= size + SIZE: break if !f: // No chunk was found; allocate a new page and add a chunk in it. It is always large enough. unsigned p = palloc () if !p: dbg_log ("no free space: kernel allocation failed") return NULL f = (Free *)(p + SIZE) // Mark it as a Free object. f->marker = ~0 // Link it in the Free list. f->next = frees f->prev = NULL frees = f if f->next: f->next->prev = f // There are no other objects in this page. NEXT (f) = NULL PREV (f) = NULL // The size of this block is the entire page. s = PAGE_SIZE // We have a free block, possibly too large. The block is linked in frees, and in the page. if s >= size + sizeof (Free) + 2 * SIZE: // Create the new object at the end and keep the Free. // f is the start of the free block // f + (s - SIZE) is the end of the free block, compensated for the header of the next block. // f + (s - SIZE) - size is the address where the new block should start. Free *obj = (Free *)((unsigned)f + (s - SIZE) - size) // Link the new object in the page. NEXT (obj) = NEXT (f) if NEXT (obj): PREV (NEXT (obj)) = obj PREV (obj) = f NEXT (f) = obj // Set f to the new object, because it is used by that name later. f = obj else: // The block was only just large enough: turn it into a new type. It is already linked into the page. // Unlink it from the free list. if f->prev: f->prev->next = f->next else: frees = f->next if f->next: f->next->prev = f->prev // f is now a block which is linked in the page, but not in any list. Link it into first. f->next = (Free *)*first f->prev = NULL if f->next: f->next->prev = f *first = f // Set common initial values. f->address_space = this f->refs = NULL return f // Free an object; it is still in its list, and it is still in the page list. void Memory::free_obj (Object_base *obj, void **first): Free *self = (Free *)obj // Free it from its list. if self->prev: self->prev->next = self->next else: *(Object_base **)first = self->next if self->next: self->next->prev = self->prev // Merge with previous, if it exists and is a Free. if PREV (self) && PREV (self)->is_free (): self = (Free *)PREV (self) // Remove the object from the page list. NEXT (self) = NEXT (obj) if NEXT (self): PREV (NEXT (self)) = self else: // The previous object is not a Free, so create a new one. // It is already linked in the page, but needs to be linked into the free list. self->next = frees self->prev = NULL if self->next: self->next->prev = self frees = self // Mark it as a Free. self->marker = ~0 // Merge with next, if it exists and is a Free. if NEXT (self) && NEXT (self)->is_free (): // Unlink the next from the frees list. Free *n = (Free *)NEXT (self) if n->prev: n->prev->next = n->next else: frees = n->next if n->next: n->next->prev = n->prev // Unlink the next from the page list. NEXT (self) = NEXT (NEXT (self)) if NEXT (self): PREV (NEXT (self)) = self // Free page if the resulting object is the only thing in it. if !PREV (self) && !NEXT (self): if self->next: self->next->prev = self->prev if self->prev: self->prev->next = self->next else: frees = self->next pfree ((unsigned)self - SIZE) Page *Memory::alloc_page (): Page *ret = (Page *)search_free (sizeof (Page), (void **)&pages) if !ret: return NULL ret->data.frame = 0 ret->data.flags = 0 return ret Thread *Memory::alloc_thread (): Thread *ret = (Thread *)search_free (sizeof (Thread), (void **)&threads) if !ret: return NULL ret->address_space = this ret->pc = 0 ret->sp = 0 Thread_arch_init (ret) ret->flags = 0 ret->schedule_prev = NULL ret->schedule_next = NULL ret->receivers = NULL top_memory.alloc_capability (NULL, NULL, NULL, 0, &ret->exception) return ret Message *Memory::alloc_message (Receiver *target): Message *ret = (Message *)search_free (sizeof (Message), (void **)&target->messages) if !ret: return NULL if !ret->next: target->last_message = ret return ret Receiver *Memory::alloc_receiver (): Receiver *ret = (Receiver *)search_free (sizeof (Receiver), (void **)&receivers) if !ret: return NULL ret->owner = NULL ret->prev_owned = NULL ret->next_owned = NULL ret->alarm_count = ~0 ret->capabilities = NULL ret->messages = NULL ret->last_message = NULL ret->reply_protected_data = ~0 ret->protected_only = false return ret Capability *Memory::alloc_capability (Receiver *target, Capability *parent, Capability **parent_ptr, unsigned protected_data, Capability *ret): if !ret: ret = (Capability *)search_free (sizeof (Capability), (void **)&capabilities) if !ret: return NULL ret->target = target ret->protected_data = protected_data ret->parent = parent ret->children = NULL ret->sibling_prev = NULL if parent: ret->sibling_next = parent->children parent->children = ret else: if parent_ptr: ret->sibling_next = *parent_ptr else: ret->sibling_next = NULL if ret->sibling_next: ret->sibling_next->sibling_prev = ret return ret Capability *Memory::clone_capability (Capability *source, bool copy, Capability *ret): if copy: if source->parent: return alloc_capability (source->target, source->parent, &source->parent->children, source->protected_data, ret) else if (unsigned)source->target & ~KERNEL_MASK: return alloc_capability (source->target, source->parent, &source->target->capabilities, source->protected_data, ret) else: return alloc_capability (source->target, source->parent, &((Object_base *)source->protected_data)->refs, source->protected_data, ret) else: return alloc_capability (source->target, source, &source->children, source->protected_data, ret) Cappage *Memory::alloc_cappage (): Cappage *ret = (Cappage *)search_free (sizeof (Cappage), (void **)&cappages) if !ret: return NULL ret->data.frame = zalloc () if !ret->data.frame: free_cappage (ret) return NULL ret->data.flags = 0 return ret Memory *Memory::alloc_memory (): Memory *ret = (Memory *)search_free (sizeof (Memory), (void **)&memories) if !ret: return NULL ret->frees = NULL ret->pages = NULL ret->threads = NULL ret->memories = NULL ret->limit = ~0 ret->used = 0 Memory_arch_init (ret) return ret void Memory::free_page (Page *page): if page->data.flags & PAGE_FLAG_PAYING: unuse () if page->data.frame: pfree (page->data.frame) free_obj (page, (void **)&pages) void Memory::free_thread (Thread *thread): thread->unrun () while thread->receivers: thread->receivers->orphan () thread->exception.invalidate () free_obj (thread, (void **)&threads) void Memory::free_message (Receiver *owner, Message *message): if !message->next: owner->last_message = message->prev for unsigned i = 0; i < 4; ++i: if message->capabilities[i]: free_capability (message->capabilities[i]) free_obj (message, (void **)&owner->messages) void Memory::free_receiver (Receiver *receiver): receiver->orphan () while receiver->capabilities: receiver->capabilities->invalidate () while receiver->messages: free_message (receiver, receiver->messages) free_obj (receiver, (void **)&receivers) void Receiver::orphan (): if prev_owned: prev_owned->next_owned = next_owned else: owner->receivers = next_owned if next_owned: next_owned->prev_owned = prev_owned owner = NULL void Receiver::own (Thread *o): if owner: orphan () owner = o next_owned = o->receivers if next_owned: next_owned->prev_owned = this o->receivers = this void Memory::free_capability (Capability *capability): capability->invalidate () free_obj (capability, (void **)&capabilities) void Capability::invalidate (): if !target: return if sibling_prev: sibling_prev->sibling_next = sibling_next else if (unsigned)target & ~KERNEL_MASK: target->capabilities = sibling_next else: ((Object_base *)protected_data)->refs = sibling_next if sibling_next: sibling_next->sibling_prev = sibling_prev parent = NULL sibling_prev = NULL sibling_next = NULL Capability *c = this while c->children: c = c->children while c: Capability *next = c->sibling_next if !next: next = c->parent c->target = NULL c->parent = NULL c->children = NULL c->sibling_prev = NULL c->sibling_next = NULL c->protected_data = 0 c = next void Memory::free_cappage (Cappage *p): for unsigned i = 0; i < CAPPAGE_SIZE; ++i: ((Capability *)p->data.frame)[i].invalidate () zfree (p->data.frame) free_obj (p, (void **)&cappages) void Memory::free_memory (Memory *mem): while mem->pages: free_page (mem->pages) while mem->cappages: free_cappage (mem->cappages) while mem->threads: free_thread (mem->threads) while mem->memories: free_memory (mem->memories) while mem->receivers: free_receiver (mem->receivers) while mem->capabilities: free_capability (mem->capabilities) Memory_arch_free (mem) if mem->frees: panic (0, "kernel memory leak: memory still in use") free_obj (mem, (void **)&memories) void Page::forget (): if data.share_prev || data.share_next: if data.share_prev: ((Page *)data.share_prev)->data.share_next = data.share_next if data.share_next: ((Page *)data.share_next)->data.share_prev = data.share_prev data.share_prev = NULL data.share_next = NULL else: // If the page has a frame and should be freed, free it. if !((data.flags ^ PAGE_FLAG_FRAME) & (PAGE_FLAG_PHYSICAL | PAGE_FLAG_FRAME)): raw_pfree (data.frame) data.frame = 0 data.flags &= ~(PAGE_FLAG_FRAME | PAGE_FLAG_SHARED | PAGE_FLAG_PHYSICAL | PAGE_FLAG_UNCACHED) Page_arch_update_mapping (this) void Cappage::forget (): if data.share_prev || data.share_next: if data.share_prev: ((Cappage *)data.share_prev)->data.share_next = data.share_next if data.share_next: ((Cappage *)data.share_next)->data.share_prev = data.share_prev data.share_prev = NULL data.share_next = NULL else: for unsigned i = 0; i < CAPPAGE_SIZE; ++i: ((Capability *)data.frame)[i].invalidate () raw_pfree (data.frame) data.frame = 0 data.flags &= ~(PAGE_FLAG_FRAME | PAGE_FLAG_SHARED)