/* * Copyright (C) 2001-2004 Jakub Jermar * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include spinlock_t zone_head_lock; /**< this lock protects zone_head list */ link_t zone_head; /**< list of all zones in the system */ /** Initialize physical memory management * * Initialize physical memory managemnt. */ void frame_init(void) { if (config.cpu_active == 1) { zone_init(); } frame_arch_init(); if (config.cpu_active == 1) { frame_region_not_free(config.base, config.base + config.kernel_size + CONFIG_STACK_SIZE); } } /** Allocate a frame * * Allocate a frame of physical memory. * * @param flags Flags for host zone selection and address processing. * * @return Allocated frame. */ __address frame_alloc(int flags) { ipl_t ipl; link_t *cur, *tmp; zone_t *z; zone_t *zone = NULL; frame_t *frame = NULL; __address v; loop: ipl = interrupts_disable(); spinlock_lock(&zone_head_lock); /* * First, find suitable frame zone. */ for (cur = zone_head.next; cur != &zone_head; cur = cur->next) { z = list_get_instance(cur, zone_t, link); spinlock_lock(&z->lock); /* * Check if the zone has any free frames. */ if (z->free_count) { zone = z; break; } spinlock_unlock(&z->lock); } if (!zone) { if (flags & FRAME_PANIC) panic("Can't allocate frame.\n"); /* * TODO: Sleep until frames are available again. */ spinlock_unlock(&zone_head_lock); interrupts_restore(ipl); panic("Sleep not implemented.\n"); goto loop; } tmp = zone->free_head.next; frame = list_get_instance(tmp, frame_t, link); frame->refcount++; list_remove(tmp); /* remove frame from free_head */ zone->free_count--; zone->busy_count++; v = zone->base + (frame - zone->frames) * FRAME_SIZE; if (flags & FRAME_KA) v = PA2KA(v); spinlock_unlock(&zone->lock); spinlock_unlock(&zone_head_lock); interrupts_restore(ipl); return v; } /** Free a frame. * * Find respective frame structrue for supplied addr. * Decrement frame reference count. * If it drops to zero, move the frame structure to free list. * * @param addr Address of the frame to be freed. It must be a multiple of FRAME_SIZE. */ void frame_free(__address addr) { ipl_t ipl; link_t *cur; zone_t *z; zone_t *zone = NULL; frame_t *frame; ASSERT(addr % FRAME_SIZE == 0); ipl = interrupts_disable(); spinlock_lock(&zone_head_lock); /* * First, find host frame zone for addr. */ for (cur = zone_head.next; cur != &zone_head; cur = cur->next) { z = list_get_instance(cur, zone_t, link); spinlock_lock(&z->lock); if (IS_KA(addr)) addr = KA2PA(addr); /* * Check if addr belongs to z. */ if ((addr >= z->base) && (addr <= z->base + (z->free_count + z->busy_count) * FRAME_SIZE)) { zone = z; break; } spinlock_unlock(&z->lock); } ASSERT(zone != NULL); frame = &zone->frames[(addr - zone->base)/FRAME_SIZE]; ASSERT(frame->refcount); if (!--frame->refcount) { list_append(&frame->link, &zone->free_head); /* append frame to free_head */ zone->free_count++; zone->busy_count--; } spinlock_unlock(&zone->lock); spinlock_unlock(&zone_head_lock); interrupts_restore(ipl); } /** Mark frame not free. * * Find respective frame structrue for supplied addr. * Increment frame reference count and remove the frame structure from free list. * * @param addr Address of the frame to be marked. It must be a multiple of FRAME_SIZE. */ void frame_not_free(__address addr) { ipl_t ipl; link_t *cur; zone_t *z; zone_t *zone = NULL; frame_t *frame; ASSERT(addr % FRAME_SIZE == 0); ipl = interrupts_disable(); spinlock_lock(&zone_head_lock); /* * First, find host frame zone for addr. */ for (cur = zone_head.next; cur != &zone_head; cur = cur->next) { z = list_get_instance(cur, zone_t, link); spinlock_lock(&z->lock); if (IS_KA(addr)) addr = KA2PA(addr); /* * Check if addr belongs to z. */ if ((addr >= z->base) && (addr <= z->base + (z->free_count + z->busy_count) * FRAME_SIZE)) { zone = z; break; } spinlock_unlock(&z->lock); } ASSERT(zone != NULL); frame = &zone->frames[(addr - zone->base)/FRAME_SIZE]; if (!frame->refcount) { frame->refcount++; list_remove(&frame->link); /* remove frame from free_head */ zone->free_count--; zone->busy_count++; } spinlock_unlock(&zone->lock); spinlock_unlock(&zone_head_lock); interrupts_restore(ipl); } /** Mark frame region not free. * * Mark frame region not free. * * @param start First address. * @param stop Last address. */ void frame_region_not_free(__address start, __address stop) { __address a; start /= FRAME_SIZE; stop /= FRAME_SIZE; for (a = start; a <= stop; a++) frame_not_free(a * FRAME_SIZE); } /** Initialize zonekeeping * * Initialize zonekeeping. */ void zone_init(void) { spinlock_initialize(&zone_head_lock); list_initialize(&zone_head); } /** Create frame zone * * Create new frame zone. * * @param start Physical address of the first frame within the zone. * @param size Size of the zone. Must be a multiple of FRAME_SIZE. * @param flags Zone flags. * * @return Initialized zone. */ zone_t *zone_create(__address start, size_t size, int flags) { zone_t *z; count_t cnt; int i; ASSERT(start % FRAME_SIZE == 0); ASSERT(size % FRAME_SIZE == 0); cnt = size / FRAME_SIZE; z = (zone_t *) early_malloc(sizeof(zone_t)); if (z) { link_initialize(&z->link); spinlock_initialize(&z->lock); z->base = start; z->flags = flags; z->free_count = cnt; list_initialize(&z->free_head); z->busy_count = 0; z->frames = (frame_t *) early_malloc(cnt * sizeof(frame_t)); if (!z->frames) { early_free(z); return NULL; } for (i = 0; iframes[i], z); list_append(&z->frames[i].link, &z->free_head); } } return z; } /** Attach frame zone * * Attach frame zone to zone list. * * @param zone Zone to be attached. */ void zone_attach(zone_t *zone) { ipl_t ipl; ipl = interrupts_disable(); spinlock_lock(&zone_head_lock); list_append(&zone->link, &zone_head); spinlock_unlock(&zone_head_lock); interrupts_restore(ipl); } /** Initialize frame structure * * Initialize frame structure. * * @param frame Frame structure to be initialized. * @param zone Host frame zone. */ void frame_initialize(frame_t *frame, zone_t *zone) { frame->refcount = 0; link_initialize(&frame->link); }