www.pudn.com > COS0.0.1.rar > page.c


/*
 page.c - low level page management
  
 Author:	Paul Barker
 Part of:	COS
 Created:	01/09/04
 Last Modified:	06/11/04

 Copyright (C) 2004 Paul Barker
    
    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 2 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, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

                     (See file "Copying")
*/

#include 

#include 
#include 
#include 
#include 
#include 

// add a new manager
page_manager_t* phys_alloc_manager(page_manager_t* last_man)
{
	page_manager_t* man = sysinfo->page_manager;
	u32_t i = 0;
	page_cluster_t* c = &(man->clusters[i]);
	count_t n = 0;
	
	TRACE(("Allocating a new manager\n"));
	TRACE(("First manager=0x%x, Last manager=0x%x\n", man, last_man));

	// loop to find a cluster representing free memory
	while (c->type != PG_CLUSTER_FREE)
	{
		TRACE(("Loop (%d): cluster=0x%x, type=0x%x\n", n, c,
			c->type));
		TRACE(("\tstart=0x%x, end=0x%x\n", c->start, c->end));
		// handle a bad cluster
		if (c->start >= c->end)
		{
			TRACE(("\tBad cluster, Removing!\n"));
			phys_remove_cluster(c);
			TRACE(("\treturning current manager\n"));
			return man;		// return this manager as it
						// now has a free cluster
		}
		
		if (++i >= 127)
		{
			TRACE(("End of this manager!\n"));
			man = man->next;
			assert(man);
			TRACE(("Going to manager at 0x%x\n", man));
			i = 0;
		}
		c = &(man->clusters[i]);
		n++;
	}
	TRACE(("Found some free memory! cluster=0x%x", c));
	TRACE(("start=0x%x, end=0x%x\n", c->start, c->end));
	
	// one last check for a bad cluster
	if (c->start >= c->end)
	{
		TRACE(("\tBad cluster, Removing!\n"));
		phys_remove_cluster(c);
		TRACE(("\treturning current manager\n"));
		return man;
	}
	
	// nudge this cluster along a page
	TRACE(("Nudging this cluster along a page and"
		" setting up new manager\n"));
	man = (page_manager_t*)c->start;
	c->start += PAGE_SIZE;
	
	// setup new manager
	memzero(man, PAGE_SIZE);
	iptr_t m = (iptr_t)man;
	last_man->next = man;
	
	TRACE(("Adding cluster to describe the new manager\n"));
	phys_add_cluster(m, m + PAGE_SIZE, PG_CLUSTER_MANAGER, 0);
	
	TRACE(("Returning manager at 0x%x\n", man));
	return man;
}

page_cluster_t* phys_add_cluster(iptr_t p_start, iptr_t p_end, u8_t p_type,
				 page_cluster_t* p_next)
{
	page_manager_t* man = sysinfo->page_manager;
	u32_t i = 0;
	page_cluster_t* c = &(man->clusters[i]);
	count_t n = 0;
	
	TRACE(("Adding a new cluster from 0x%x to 0x%x\n", p_start, p_end));
	TRACE(("type=0x%x, next=0x%x\n", p_type, p_next));
	TRACE(("First manager=0x%x\n", man));
	
	assert(!(p_start & ~PAGE_MASK));
	assert(!(p_end & ~PAGE_MASK));	// must already be page aligned

	// loop to find a free cluster
	while (c->start || c->end)
	{
		TRACE(("Loop (%d): cluster=0x%x, type=0x%x\n", n, c,
			c->type));
		TRACE(("\tstart=0x%x, end=0x%x\n", c->start, c->end));
		if (++i >= 127)
		{
			TRACE(("End of this manager!\n"));
			if (!man->next)
			{
				TRACE(("Need to get a new manager\n"));
				man = phys_alloc_manager(man);
				assert(man);
			}
			else
				man = man->next;
			i = 0;
			TRACE(("Going to manager at 0x%x\n", man));
		}
		c = &(man->clusters[i]);
		n++;
	}
	
	TRACE(("Got a free cluster at 0x%x\n", c));
	TRACE(("start=0x%x, end=0x%x, type=0x%x, next=0x%x\n", c->start,
		c->end, c->type, c->next));
	
	c->start = p_start;
	c->end = p_end;
	c->type = p_type;
	c->next = p_next;
	
	return c;
}

void phys_remove_cluster(page_cluster_t* c)
{
	TRACE(("Removing cluster at 0x%x\n", c));
	memzero(c, sizeof(page_cluster_t));
}

// this is ONLY to be used in initialisation,
// it is far too slow for normal use
void phys_mark_by_ptr(u32_t p, u32_t len, u8_t type_p)
{
	TRACE(("Marking by pointer at 0x%x, length %d (0x%x)\n", p, len, len));
	TRACE(("Will set type to 0x%x\n", type_p));
	
	if (len == 0)
		return;

	u32_t p_end = ((p + len) & PAGE_MASK) +
		      (((p + len) & ~PAGE_MASK) ? 4096 : 0);
	page_manager_t* man;
	page_cluster_t* c;
	u32_t i;
	count_t n = 0;
	
	p &= PAGE_MASK;
	
	u32_t true_start = p;
	u32_t xx;
	
	TRACE(("Ready to search, true start is 0x%x, end 0x%x\n", p, p_end));

start_search:
	man = sysinfo->page_manager;
	i = 0;
	++n;
	xx = 0;
	
	TRACE(("Starting search %d, p=0x%x\n", n, p));
	
	while (p < p_end)
	{
		// check if end of this manager
		if (i >= 127)
		{
			TRACE(("End of this manager!\n"));
			man = man->next;
			if (!man)
				panic("Could not mark page!");
			i = 0;
			TRACE(("Going to manager at 0x%x\n", man));
		}
		
		c = &(man->clusters[i]);
		
		TRACE(("Loop (%d): cluster at 0x%x\n", xx, c));
		TRACE(("\tstart=0x%x, end=0x%x\n", c->start, c->end));
		TRACE(("\ttype=0x%x, next=0x%x\n", c->type, c->next));
		
		// first check the obvious, p starts below c
		if (p < c->start)
			TRACE(("\tp is below c\n"));
		else if (p > c->end)
			TRACE(("\tp is above c\n"));
		
		// next check if p is at start of c
		else if (p == c->start)
		{
			// now check if c contains p...
			if (p_end < c->end)
			{
				TRACE(("\tc contains p,"
					" start of p is start of c\n"));
				c->start = p_end;
				goto finish;
			}
			
			// or if p contains c (including p is exactly c)
			else
			{
				TRACE(("\tp contains c\n"));
				p = c->end;
				TRACE(("\tRemoving c\n"));
				phys_remove_cluster(c);
				goto start_search;
			}
		}
		
		// finally check if p starts within c
		else if (p < c->end)
		{
			// maybe p is completely within c
			if (p_end <= c->end)
			{
				TRACE(("\tc contains p\n"));
				TRACE(("\tAdding new cluster from end of p"
					" to end of c\n"));
				page_cluster_t* nxt_c = phys_add_cluster(
					p_end, c->end, c->type, c->next);
				c->end = p;
				c->next = nxt_c;
				goto finish;
			}
			
			// else p extends beyond c
			else
			{
				TRACE(("\tp extends beyond c\n"));
				u32_t tmp = p;
				p = c->end;
				c->end = tmp;
				goto start_search;
			}
		}
		
		// otherwise, ignore the bugger
		++i;
		++xx;
	}

finish:
	TRACE(("Finished, adding cluster for p\n"));

	phys_add_cluster(true_start, p_end, type_p, 0);
}

// init physical page manager
void phys_init()
{
	TRACE(("Initialising physical page manager...\n"));

	multiboot_info_t* info = sysinfo->multiboot;
	page_manager_t* pgman = sysinfo->page_manager;
	page_cluster_t* c = 0;
	u32_t n;	// for calculations
	
	TRACE(("sysinfo at 0x%x, first manager at 0x%x\n", info, pgman));
	
	memzero (pgman, 4096);
	
	// setup BIOS data area (page 0)
	c = &(pgman->clusters[0]);
	c->start = 0;
	c->end = PAGE_SIZE;
	c->type = PG_CLUSTER_BIOS;
	
	TRACE(("BIOS data area: start=0x%x, end=0x%x, type=0x%x\n",
		c->start, c->end, c->type));
	
	n = (info->mem_lower << 10) & PAGE_MASK;
	
	// setup remaining low memory
	++c;
	c->start = PAGE_SIZE;
	c->end = n;
	c->type = PG_CLUSTER_FREE_LOWER;
	
	TRACE(("Low memory: start=0x%x, end=0x%x, type=0x%x\n",
		c->start, c->end, c->type));
		
	// setup ISA hole
	++c;
	c->start = n;
	c->end = 0x100000;
	c->type = PG_CLUSTER_ISA_HOLE;
	
	TRACE(("ISA Hole: start=0x%x, end=0x%x, type=0x%x\n",
		c->start, c->end, c->type));
	
	n = ((iptr_t)&end_kernel & PAGE_MASK) + (((iptr_t)&end_kernel & ~PAGE_MASK)
					   ? 4096 : 0);
	
	// setup kernel memory
	++c;
	c->start = (iptr_t)&kernel & PAGE_MASK;
	c->end = n;
	c->type = PG_CLUSTER_KERNEL;
	
	TRACE(("Kernel: start=0x%x, end=0x%x, type=0x%x\n",
		c->start, c->end, c->type));
	
	// setup remaining high memory
	++c;
	c->start = n;
	
	n = ((info->mem_upper + 1024) << 10) & PAGE_MASK;
	
	c->end = n;
	c->type = PG_CLUSTER_FREE;
	
	TRACE(("High memory: start=0x%x, end=0x%x, type=0x%x\n",
		c->start, c->end, c->type));
	
	// setup non present memory
	++c;
	c->start = n;
	c->end = ~0;	// as unsigned gives MAXINT
	c->type = PG_CLUSTER_NOT_PRESENT;
	
	TRACE(("Non-present memory: start=0x%x, end=0x%x, type=0x%x\n",
		c->start, c->end, c->type));
	
/*
  Full 4GB address space is now managed, so we never have to worry
  about a page not being a member of a cluster.
  
  Space used for this method:
	Best case (now) = (6 clusters * 32 bytes + 32 bytes for manager)
			= 224 bytes(4k allocated)
	
	Worst case (1 pg / cluster) = 2^20 clusters in 8257 managers
				    = 32.2519... MB (32.2539... MB allocated)
				    = 0.787... % of a 4GB system
		(This worst case can only happen on a 4GB system)
		(To be honest, it will never happen)
*/
	
	// grab the idata structure
	init_data_t* idata = (init_data_t*)(((iptr_t)sysinfo->multiboot)
					    - PAGE_SIZE);
					    
	TRACE(("idata found at 0x%x\n", idata));
	
	// factor in what has already been alloc'd
	phys_mark_by_ptr((iptr_t)sysinfo->multiboot,
			 idata->multiboot_end - (iptr_t)sysinfo->multiboot,
			 PG_CLUSTER_SYSTEM);
	
	phys_mark_by_ptr((iptr_t)sysinfo, idata->sysinfo_size,
			 PG_CLUSTER_SYSTEM);
	
	TRACE(("Physical page manager ready!\n"));
}

ptr_t phys_alloc(count_t n, u8_t use_type, u8_t from_type)
{
	TRACE(("Allocating physical memory, %d pages long\n", n));
	TRACE(("From type 0x%x to type 0x%x\n", from_type, use_type));
	
	if (n == 0)
		return NULL;

	page_manager_t* man = sysinfo->page_manager;
	u32_t i = 0;
	page_cluster_t* c;
	u32_t sz = n * PAGE_SIZE;
	count_t xx = 0;
	
	TRACE(("First manager at 0x%x, sz=0x%x\n", man, sz));
	
	// find cluster of at least n pages, of type from_type
	while (1)
	{
		// check if end of this manager
		if (i >= 127)
		{
			TRACE(("End of this manager!\n"));
			man = man->next;
			if (!man)
				panic("Out of memory");
			i = 0;
			TRACE(("Going to manager at 0x%x\n", man));
		}
		
		c = &(man->clusters[i]);
		
		TRACE(("Loop (%d): cluster at 0x%x\n", xx, c));
		TRACE(("\tstart=0x%x, end=0x%x\n", c->start, c->end));
		TRACE(("\ttype=0x%x, next=0x%x\n", c->type, c->next));
		
		// check type
		if (c->type == from_type)
		{
			TRACE(("Correct type\n"));
			// check size
			if ((c->end - c->start) >= sz)
			{
				TRACE(("It's large enough!\n"));
				// add a cluster to describe remainder
				if ((c->end - c->start) > sz)
				{
					TRACE(("Adding a cluster to describe"
						" remainder\n"));
					phys_add_cluster(c->start + sz,
							 c->end, c->type,
							 c->next);
				}
				
				// modify this cluster to describe allocated
				//  region
				c->end = c->start + sz;
				c->type = use_type;
				c->next = 0;
				
				TRACE(("Returning found pages\n"));
				TRACE(("\tstart=0x%x, end=0x%x\n",
					c->start, c->end));
				TRACE(("\ttype=0x%x, next=0x%x\n",
					c->type, c->next));
				
				return (ptr_t)c->start;
			}
		}
		++xx;
		++i;
	}
}

void phys_free(ptr_t p, u8_t to_type)
{
	TRACE(("Freeing physical memory at 0x%x, to type 0x%x\n",
		p, to_type));
	page_manager_t* man = sysinfo->page_manager;
	u32_t i = 0;
	page_cluster_t* c;
	
	TRACE(("First manager at 0x%x\n", man));
	count_t xx = 0;
	
	// find cluster starting at p
	while (1)
	{
		// check if end of this manager
		if (i >= 127)
		{
			TRACE(("End of this manager!\n"));
			man = man->next;
			if (!man)
				panic("Could not find pointer to free");
			i = 0;
			TRACE(("Going to manager at 0x%x\n", man));
		}
		
		c = &(man->clusters[i]);
		
		TRACE(("Loop (%d): cluster at 0x%x\n", xx, c));
		TRACE(("\tstart=0x%x, end=0x%x\n", c->start, c->end));
		TRACE(("\ttype=0x%x, next=0x%x\n", c->type, c->next));
		
		if (c->start == (iptr_t)p)
		{
			TRACE(("Found correct pointer, changing type and"
				" returning\n"));
			c->type = to_type;
			return;
		}
		++xx;
		++i;
	}
}

/*
  TODO: Validation / Compaction functions.
	Manage a free list during alloc / free.
	Preserve next pointer in clusters when removing a cluster.
	Protect the lot with mutexes.
	Test, test and test again!
	Preserve modules passed by bootloader.
*/