EMILE/second/memory.c

242 lines
4.6 KiB
C
Raw Normal View History

2004-02-15 20:46:45 +00:00
/*
*
* (c) 2004 Laurent Vivier <LaurentVivier@wanadoo.fr>
*
* portion from penguin booter
*
*/
#include <stdio.h>
#include "MMU.h"
#include "bank.h"
2004-02-15 20:46:45 +00:00
#include "memory.h"
#include "lowmem.h"
extern unsigned long _start;
2004-02-15 20:46:45 +00:00
/* Memory Allocation information */
#define MAX_MEMORY_AREA 4096
typedef struct memory_area {
unsigned long address;
unsigned long size;
} memory_area_t;
2004-02-15 20:46:45 +00:00
typedef struct memory_pool {
memory_area_t area[MAX_MEMORY_AREA];
unsigned long area_number;
} memory_pool_t;
memory_pool_t* pool = NULL;
static int memory_find_area_by_addr(unsigned long start)
{
int i;
for (i = 0; i < pool->area_number; i++)
{
if ( (pool->area[i].address <= start) &&
(start < pool->area[i].address + pool->area[i].size) )
{
return i;
}
}
return -1;
}
static int memory_find_area_by_size(unsigned long size)
{
int i;
int first_choice = -1;
unsigned long phys_start, phys_end;
2004-02-15 20:46:45 +00:00
for (i = 0; i < pool->area_number; i++)
{
if (size <= pool->area[i].size)
{
if (first_choice == -1)
first_choice = i;
/* try to take all bloc in same memory bank */
logical2physical(pool->area[i].address, &phys_start);
logical2physical(pool->area[i].address + size - 1,
&phys_end);
if (phys_start + size - 1 == phys_end)
return i;
2004-02-15 20:46:45 +00:00
}
}
return first_choice;
2004-02-15 20:46:45 +00:00
}
static void memory_remove(unsigned long start, unsigned long end)
{
int i;
i = memory_find_area_by_addr(start);
if (i == -1)
return;
/* zone to remove is at start of area */
if (start == pool->area[i].address)
{
if (pool->area[i].size == end - start)
{
/* area can be removed */
pool->area_number--;
pool->area[i].address =
pool->area[pool->area_number].address;
pool->area[i].size = pool->area[pool->area_number].size;
}
else
{
/* we guess end < address + size ... */
pool->area[i].address = end;
pool->area[i].size -= end - start;
}
return;
}
/* zone to remove is at end of area */
if ( end == (pool->area[i].address + pool->area[i].size) )
{
/* we guess start >= address */
pool->area[i].size -= end - start;
return;
}
/* zone is in the middle of an area */
/* split in two parts an existing area */
/* second part : end -> original address + orignal size */
pool->area[pool->area_number].address = end;
pool->area[pool->area_number].size = pool->area[i].size - (end - start);
pool->area_number++;
/* first part : original address -> start */
pool->area[i].size = start - pool->area[i].address;
}
static void memory_add(unsigned long start, unsigned long end)
{
int i;
for (i = 0; i < pool->area_number; i++)
{
/* can we add it at end of existing area */
if ( start == (pool->area[i].address + pool->area[i].size) )
{
int j;
pool->area[i].size += end - start;
/* perhaps, now, we can merge with following area ? */
for (j = 0; j < pool->area_number; j++)
{
if ( end == pool->area[j].address )
{
pool->area[i].size += pool->area[j].size;
pool->area_number--;
pool->area[j].address = pool->area[pool->area_number].address;
pool->area[j].size = pool->area[pool->area_number].size;
return;
}
}
return;
}
/* can we add it at begin of existing area */
if ( end == pool->area[i].address )
{
pool->area[i].address = start;
pool->area[i].size += end - start;
return;
}
}
pool->area[pool->area_number].address = start;
pool->area[pool->area_number].size = end;
pool->area_number++;
}
void memory_init()
{
extern char __bootloader_start;
extern char __bootloader_end;
/* we are currently using the MMU to have only one linear memory area */
2004-02-19 13:09:41 +00:00
init_memory_map();
2004-02-17 10:23:55 +00:00
/* we put memory pool array just before us */
2004-02-15 20:46:45 +00:00
pool = (memory_pool_t*) (&_start - sizeof(memory_pool_t));
2004-02-15 20:46:45 +00:00
pool->area_number = 0;
/* add all memory to pool */
memory_add(0, bank_mem_avail());
2004-02-15 20:46:45 +00:00
/* remove the pool array */
memory_remove( (unsigned long)pool,
(unsigned long)pool + sizeof(memory_pool_t));
/* remove the booloader image */
memory_remove( (unsigned long)&__bootloader_start,
(unsigned long)&__bootloader_end);
/* system */
2004-02-15 20:46:45 +00:00
memory_remove(0x0000, 0x8000);
2004-02-15 20:46:45 +00:00
}
void *malloc(size_t size)
{
unsigned long addr;
int area;
size = ((size + 3) & ~0x3L) + 4; /* reserve 4 bytes to store size */
area = memory_find_area_by_size(size);
if (area == -1)
return NULL;
/* remove area from free memory pool */
addr = pool->area[area].address;
memory_remove(addr, addr + size);
*(unsigned long*)addr = size; /* store size of area in first word */
return (void*)(addr + 4);
}
void free(void *ptr)
{
ptr = ptr - 4;
memory_add((unsigned long)ptr, (unsigned long)ptr + *(unsigned long*)ptr);
}