#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 **)(x))[-2]) #define NEXT(x) (((Object **)(x))[-1]) #define SIZE (2 * sizeof (Object *)) 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 = (Free *)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: ((Free *)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: ((Free *)f->prev)->next = f->next else: frees = (Free *)f->next if f->next: ((Free *)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: ((Free *)f->next)->prev = f *first = f // Set common initial values. f->address_space = this f->refs.reset () return f // Free an object; it is still in its list, and it is still in the page list. void Memory::free_obj (Object *obj, Pointer *first): Free *self = (Free *)obj // Invalidate references. while self->refs: self->refs->invalidate () // Free it from its list. if self->prev: ((Free *)self->prev)->next = self->next else: *(Pointer *)first = (Pointer)self->next if self->next: ((Free *)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: ((Free *)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: ((Free *)n->prev)->next = n->next else: frees = (Free *)n->next if n->next: ((Free *)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: ((Free *)self->next)->prev = self->prev if self->prev: ((Free *)self->prev)->next = self->next else: frees = (Free *)self->next pfree ((unsigned)self - SIZE) Page *Memory::alloc_page (): Page *ret = (Page *)search_free (sizeof (Page), (void **)&pages) if !ret: return NULL ret->frame = 0 ret->flags = 0 return ret Thread *Memory::alloc_thread (unsigned size): Thread *ret = (Thread *)search_free (sizeof (Thread) + (size - 1) * sizeof (CapsP), (void **)&threads) if !ret: return NULL ret->receivers = NULL ret->pc = 0 ret->sp = 0 Thread_arch_init (ret) ret->flags = 0 ret->schedule_prev = NULL ret->schedule_next = NULL for unsigned i = 0; i < size; ++i: ret->caps[i] = NULL 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->caps = NULL ret->capabilities.reset () ret->messages = NULL ret->last_message = NULL ret->reply_protected_data = ~0 ret->protected_only = false return ret Caps *Memory::alloc_caps (unsigned size): Caps *ret = (Caps *)search_free (sizeof (Caps) + (size - 1) * sizeof (Capability), (void **)&capses) if !ret: return NULL ret->size = size for unsigned i = 0; i < size; ++i: ret->set (i, NULL, 0, CapRef (), NULL) 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->capses = NULL ret->receivers = NULL ret->memories = NULL ret->limit = ~0 ret->used = 0 Memory_arch_init (ret) return ret void Caps::set (unsigned index, Receiver *target, Protected pdata, CapRef parent, CapRef *parent_ptr): caps[index].target = target caps[index].protected_data = pdata caps[index].parent = parent caps[index].children.reset () caps[index].sibling_prev.reset () if parent: caps[index].sibling_next = parent->children parent->children = CapRef (this, index) else: if parent_ptr: caps[index].sibling_next = *parent_ptr *parent_ptr = CapRef (this, index) else: caps[index].sibling_next.reset () if caps[index].sibling_next: caps[index].sibling_next->sibling_prev = CapRef (this, index) void Caps::clone (unsigned index, CapRef source, bool copy): if copy: if source->parent: set (index, source->target, source->protected_data, source->parent) else if (unsigned)source->target & ~KERNEL_MASK: set (index, source->target, source->protected_data, CapRef (), &source->target->capabilities) else: set (index, source->target, source->protected_data, CapRef (), &((Object *)source->protected_data)->refs) else: set (index, source->target, source->protected_data, source) void Memory::free_page (Page *page): if page->flags & PAGE_FLAG_PAYING: unuse () if page->frame: pfree (page->frame) free_obj (page, (Pointer *)&pages) void Memory::free_thread (Thread *thread): thread->unrun () while thread->receivers: thread->receivers->orphan () free_obj (thread, (void **)&threads) void Memory::free_message (Receiver *owner, Message *message): if !message->next: owner->last_message = (MessageP)message->prev 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 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 *)protected_data)->refs = sibling_next if sibling_next: sibling_next->sibling_prev = sibling_prev parent.reset () sibling_prev.reset () sibling_next.reset () Capability *c = this while c: while c->children: c = c->children.deref () Capability *next = c->sibling_next.deref () if !next: next = c->parent.deref () c->target = NULL c->parent.reset () c->children.reset () c->sibling_prev.reset () c->sibling_next.reset () c->protected_data = 0 c = next void Memory::free_caps (Caps *c): for unsigned i = 0; i < c->size; ++i: c->caps[i].invalidate () free_obj (c, (void **)&capses) void Memory::free_memory (Memory *mem): while mem->pages: free_page (mem->pages) while mem->capses: free_caps (mem->capses) while mem->threads: free_thread (mem->threads) while mem->memories: free_memory (mem->memories) while mem->receivers: free_receiver (mem->receivers) 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 share_prev || share_next: if share_prev: share_prev->share_next = share_next if share_next: share_next->share_prev = share_prev share_prev = NULL share_next = NULL else: // If the page has a frame and should be freed, free it. if !((flags ^ PAGE_FLAG_FRAME) & (PAGE_FLAG_PHYSICAL | PAGE_FLAG_FRAME)): raw_pfree (frame) frame = 0 flags &= ~(PAGE_FLAG_FRAME | PAGE_FLAG_SHARED | PAGE_FLAG_PHYSICAL | PAGE_FLAG_UNCACHED) Page_arch_update_mapping (this)