2009-07-16 10:15:15 +03:00
|
|
|
#pypp 0
|
2010-02-06 00:15:58 +02:00
|
|
|
// Iris: micro-kernel for a capability-based operating system.
|
|
|
|
// memory.ccp: Page allocation system.
|
|
|
|
// Copyright 2009 Bas Wijnen <wijnen@debian.org>
|
|
|
|
//
|
|
|
|
// 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 <http://www.gnu.org/licenses/>.
|
|
|
|
|
2009-07-16 10:15:15 +03:00
|
|
|
#include "kernel.hh"
|
2010-01-24 22:34:24 +02:00
|
|
|
//#define DEBUG_ALLOC
|
2009-07-16 10:15:15 +03:00
|
|
|
|
|
|
|
extern unsigned _end
|
2009-07-20 01:23:45 +03:00
|
|
|
|
2009-07-23 13:06:32 +03:00
|
|
|
static void clear_page (unsigned page, unsigned num = 1):
|
2010-01-24 22:34:24 +02:00
|
|
|
#ifdef DEBUG_ALLOC
|
|
|
|
kdebug ("clearing page ")
|
|
|
|
kdebug_num (page)
|
|
|
|
kdebug ("+")
|
|
|
|
kdebug_num (num)
|
|
|
|
kdebug ('\n')
|
|
|
|
#endif
|
2009-12-18 10:00:38 +02:00
|
|
|
page = (page & ~0xc0000000) | 0xa0000000
|
2010-05-03 17:46:14 +03:00
|
|
|
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)[i] = 0
|
2009-12-11 10:43:42 +02:00
|
|
|
if *((unsigned *)page) != 0 || ((unsigned *)page)[(num << (PAGE_BITS - 2)) - 1] != 0:
|
|
|
|
dpanic (0, "clear_page didn't work")
|
2009-07-20 01:23:45 +03:00
|
|
|
|
2009-12-27 01:12:35 +02:00
|
|
|
#if 0
|
2009-07-23 13:06:32 +03:00
|
|
|
|
2009-07-20 01:23:45 +03:00
|
|
|
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:
|
2010-01-24 22:34:24 +02:00
|
|
|
kdebug ("memory allocation failed\n")
|
2009-07-24 15:25:53 +03:00
|
|
|
return 0
|
2009-07-20 01:23:45 +03:00
|
|
|
unsigned ret = free_begin
|
|
|
|
free_begin += num * PAGE_SIZE
|
2009-07-23 13:06:32 +03:00
|
|
|
clear_page (ret, num)
|
2009-07-20 01:23:45 +03:00
|
|
|
return ret
|
|
|
|
|
|
|
|
void phys_free (unsigned page, unsigned num):
|
|
|
|
// Not supported.
|
|
|
|
|
2009-08-24 22:02:35 +03:00
|
|
|
#ifndef NDEBUG
|
2009-12-26 15:17:06 +02:00
|
|
|
/*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
|
2009-10-10 02:31:10 +03:00
|
|
|
void print_free ():
|
2009-08-24 22:02:35 +03:00
|
|
|
#endif
|
2009-07-23 13:06:32 +03:00
|
|
|
#else
|
|
|
|
|
2009-08-18 00:11:15 +03:00
|
|
|
struct kFreePages:
|
|
|
|
kFreePages *prev, *next
|
2009-07-20 01:23:45 +03:00
|
|
|
unsigned num
|
|
|
|
|
2009-08-18 00:11:15 +03:00
|
|
|
static kFreePages *first_free
|
2009-07-20 01:23:45 +03:00
|
|
|
|
2009-07-16 10:15:15 +03:00
|
|
|
unsigned init_memory (unsigned mem):
|
2009-08-18 00:11:15 +03:00
|
|
|
first_free = (kFreePages *)(((unsigned)&_end + ~PAGE_MASK) & PAGE_MASK)
|
2009-07-20 01:23:45 +03:00
|
|
|
first_free->prev = NULL
|
|
|
|
first_free->next = NULL
|
2009-12-11 10:43:42 +02:00
|
|
|
first_free->num = ((mem & PAGE_MASK) - ((unsigned)first_free & ~0xc0000000)) >> PAGE_BITS
|
2009-12-30 23:41:45 +02:00
|
|
|
#ifdef DEBUG_ALLOC
|
2010-01-24 22:34:24 +02:00
|
|
|
kdebug ("initial memory: ")
|
|
|
|
kdebug_num ((unsigned)first_free & ~0xc0000000)
|
|
|
|
kdebug ("+")
|
|
|
|
kdebug_num (first_free->num)
|
|
|
|
kdebug ("\n")
|
2009-12-30 23:41:45 +02:00
|
|
|
#endif
|
2009-07-20 01:23:45 +03:00
|
|
|
return first_free->num
|
|
|
|
|
2010-01-24 22:34:24 +02:00
|
|
|
#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
|
|
|
|
|
2009-07-20 01:23:45 +03:00
|
|
|
unsigned phys_alloc (unsigned num):
|
2009-08-18 00:11:15 +03:00
|
|
|
kFreePages *choice = NULL
|
|
|
|
for kFreePages *p = first_free; p; p = p->next:
|
2009-07-20 01:23:45 +03:00
|
|
|
if p->num < num:
|
|
|
|
continue
|
|
|
|
if choice && choice->num < p->num:
|
|
|
|
continue
|
2009-08-24 22:02:35 +03:00
|
|
|
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)
|
2009-12-30 23:41:45 +02:00
|
|
|
#ifdef DEBUG_ALLOC
|
2010-01-24 22:34:24 +02:00
|
|
|
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")
|
2009-12-30 23:41:45 +02:00
|
|
|
#endif
|
2009-08-24 22:02:35 +03:00
|
|
|
return (unsigned)p
|
2009-07-20 01:23:45 +03:00
|
|
|
choice = p
|
|
|
|
if !choice:
|
|
|
|
// TODO: reorganizing may work to allow allocation.
|
2009-08-24 22:02:35 +03:00
|
|
|
dpanic (0x03948859, "range memory allocation failed")
|
2009-07-20 01:23:45 +03:00
|
|
|
return 0
|
|
|
|
choice->num -= num
|
|
|
|
unsigned ret = (unsigned)choice + (choice->num << PAGE_BITS)
|
|
|
|
clear_page (ret, num)
|
2009-12-30 23:41:45 +02:00
|
|
|
#ifdef DEBUG_ALLOC
|
2010-01-24 22:34:24 +02:00
|
|
|
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")
|
2009-12-30 23:41:45 +02:00
|
|
|
#endif
|
2009-07-20 01:23:45 +03:00
|
|
|
return ret
|
|
|
|
|
|
|
|
void phys_free (unsigned page, unsigned num):
|
2009-12-30 23:41:45 +02:00
|
|
|
#ifdef DEBUG_ALLOC
|
2010-01-24 22:34:24 +02:00
|
|
|
kdebug ("free ")
|
|
|
|
kdebug_num (page & ~0xc0000000)
|
|
|
|
kdebug ("+")
|
|
|
|
kdebug_num (num << PAGE_BITS)
|
|
|
|
kdebug ("\n")
|
|
|
|
if is_free (page):
|
|
|
|
panic (page, "page already free")
|
2009-12-30 23:41:45 +02:00
|
|
|
#endif
|
2009-07-20 01:23:45 +03:00
|
|
|
unsigned size = num << PAGE_BITS
|
|
|
|
if !first_free || (unsigned)first_free > page:
|
|
|
|
// This is the first free block.
|
2009-08-18 00:11:15 +03:00
|
|
|
kFreePages *n = (kFreePages *)page
|
2009-07-20 01:23:45 +03:00
|
|
|
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
|
2009-08-18 00:11:15 +03:00
|
|
|
kFreePages *p
|
2009-07-20 01:23:45 +03:00
|
|
|
for p = first_free; p->next && (unsigned)p->next < page; p = p->next:
|
|
|
|
// Do nothing.
|
2010-05-01 00:13:49 +03:00
|
|
|
if p == p->next:
|
|
|
|
dpanic (0, "page is its own next")
|
2009-07-20 01:23:45 +03:00
|
|
|
// 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.
|
2009-08-18 00:11:15 +03:00
|
|
|
kFreePages *n = (kFreePages *)page
|
2009-07-20 01:23:45 +03:00
|
|
|
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.
|
2009-08-18 00:11:15 +03:00
|
|
|
kFreePages *n = (kFreePages *)page
|
2009-07-20 01:23:45 +03:00
|
|
|
n->next = p->next
|
|
|
|
n->prev = p
|
|
|
|
if n->next:
|
|
|
|
n->next->prev = n
|
|
|
|
p->next = n
|
|
|
|
n->num = num
|
2009-08-24 22:02:35 +03:00
|
|
|
|
|
|
|
#ifndef NDEBUG
|
2009-10-10 02:31:10 +03:00
|
|
|
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 ():
|
2010-01-24 22:34:24 +02:00
|
|
|
kdebug ("Free pages: ")
|
2009-10-10 02:31:10 +03:00
|
|
|
for kFreePages *p = first_free; p; p = p->next:
|
2010-01-24 22:34:24 +02:00
|
|
|
kdebug_num ((unsigned)p)
|
|
|
|
kdebug (":")
|
|
|
|
kdebug_num (p->num, 4)
|
|
|
|
kdebug ("->")
|
|
|
|
kdebug ("NULL\n")
|
2009-08-24 22:02:35 +03:00
|
|
|
#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)
|
2009-10-10 02:31:10 +03:00
|
|
|
for kReceiver *r = mem->receivers; r; r = (kReceiver *)r->next:
|
|
|
|
check_impl (r->messages, num, msg)
|
2009-08-24 22:02:35 +03:00
|
|
|
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)
|
2009-10-10 02:31:10 +03:00
|
|
|
top_memory.check (num)
|
2009-07-20 01:23:45 +03:00
|
|
|
#endif
|
2009-07-16 10:15:15 +03:00
|
|
|
|
|
|
|
unsigned raw_zalloc ():
|
2009-07-20 01:23:45 +03:00
|
|
|
return phys_alloc (1)
|
2009-07-16 10:15:15 +03:00
|
|
|
|
|
|
|
void raw_pfree (unsigned page):
|
2009-07-20 01:23:45 +03:00
|
|
|
return phys_free (page, 1)
|
2009-07-16 10:15:15 +03:00
|
|
|
|
2009-08-18 00:11:15 +03:00
|
|
|
unsigned kMemory::zalloc ():
|
2009-07-16 10:15:15 +03:00
|
|
|
if !use ():
|
2009-08-24 22:02:35 +03:00
|
|
|
dpanic (0x34638920, "limit reached: allocation not allowed")
|
2010-01-24 22:34:24 +02:00
|
|
|
kdebug ("limit reached: allocation not allowed\n")
|
2009-07-16 10:15:15 +03:00
|
|
|
return NULL
|
|
|
|
return raw_zalloc ()
|
|
|
|
|
2009-08-18 00:11:15 +03:00
|
|
|
void kMemory::pfree (unsigned page):
|
2009-07-16 10:15:15 +03:00
|
|
|
unuse ()
|
|
|
|
return raw_pfree (page)
|
|
|
|
|
2009-08-18 00:11:15 +03:00
|
|
|
unsigned kMemory::palloc ():
|
2009-07-20 01:23:45 +03:00
|
|
|
return zalloc ()
|
2009-07-16 10:15:15 +03:00
|
|
|
|
2009-08-18 00:11:15 +03:00
|
|
|
void kMemory::zfree (unsigned page):
|
2009-07-20 01:23:45 +03:00
|
|
|
pfree (page)
|