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


/*
 sysinit.c - Initialise system info
  
 Author:        Paul Barker
 Part of:       COS
 Created:       05/10/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 

u32_t mmap_640[5];	// map of memory from 0 to 640k
root_sysinfo_t* sysinfo;

#define setbit(addr, bit) (addr |= (1 << (bit)))

u32_t mark_pg(u32_t start, u32_t len)
{
	u32_t ret = len;
	u32_t page = (start & PAGE_MASK) / PAGE_SIZE;
	len = (len & PAGE_MASK) + ((len & ~PAGE_MASK) ? PAGE_SIZE : 0);
	len /= PAGE_SIZE;
	
	while (len--)
	{
		if (page > 160)
			return ret;

		setbit(mmap_640[page / 32], page % 32);
		TRACE(("marking page %d, at 0x%x\n", page, page * PAGE_SIZE));
		page++;
	}
	
	return ret;
}

// does not update mmap_640 so this can only be called once
void* get_free(u32_t c)
{
	count_t n = 0;
	count_t x;
	c /= PAGE_SIZE;
	ptr_t a_start = 0;
	u32_t i = c;
	
	while (n < 5) // loop through each byte of the page table
	{
		x = 0;
		while (x < 32)
		{
			if (!(mmap_640[n] & (1 << x)))
						// check allocated flag
			{
				if (!a_start)
					a_start = (ptr_t)
						  (4096 * (x + n * 32));
				if (--i == 0)
					return a_start;
			}
			else
			{
				i = c;
				a_start = 0;
			}
			++x;
		}
		++n;
	}
	return NULL;
}

inline u32_t do_copy(u32_t* src, ptr_t dest, count_t sz)
{
	if (*src)
		*src = (u32_t)memcpy(dest, (ptr_t)*src, sz);
	return sz;
}

void sys_init(multiboot_info_t* info)
{
	count_t i;
	module_t* mptr;
	u32_t sum = 0;
	
	TRACE(("sysinit()\n"));
	
	/*
	   First things first we need some memory in which to place our
	   system objects, avoiding the multiboot structure and bios memory.
	   We know that no kernel info is below 1M.
	   
	   For now we ignore the elf or aout symbol/section tables.
	*/
	memzero(mmap_640, 20);
	setbit(mmap_640[0], 0);		// mark first page in use (BIOS)
	setbit(mmap_640[4], 31);	// last page incase we have 639k
					//   instead of 640k
	
	// sum the amount of memory used by the multiboot info,
	// marking pages as we go
	sum += mark_pg((u32_t)info, sizeof(multiboot_info_t));
	sum += mark_pg(info->cmdline, strlen((string_t)info->cmdline));
	sum += mark_pg(info->mods_addr, info->mods_count * 16);
	
	// keep the modules table but ignore the modules
	for (i = 0; i < info->mods_count; i++)
	{
		mptr = (module_t*)(info->mods_addr + (16 * i));
		sum += mark_pg(mptr->string, strlen((string_t)mptr->string));
	}
	
	sum += mark_pg(info->mmap_addr, info->mmap_length);
	
	// find a free block large enuff to fit this memory,
	// and copy the lot to there, adjusting pointers as necessary.
	// we also reserve a page for some other stuff
	sum = (sum & PAGE_MASK) + ((sum & ~PAGE_MASK) ? PAGE_SIZE : 0);
	u8_t* p_start = get_free(sum + PAGE_SIZE);
	u8_t* p = p_start + PAGE_SIZE;
	
	p += do_copy((u32_t*)&(info), p, sizeof(multiboot_info_t));
	p += do_copy(&(info->cmdline), p, strlen((string_t)info->cmdline));
	p += do_copy(&(info->mods_addr), p, info->mods_count * 16);
	
	for (i = 0; i < info->mods_count; i++)
	{
		mptr = (module_t*)(info->mods_addr + (16 * i));
		p += do_copy(&(mptr->string), p,
			     strlen((string_t)mptr->string));
	}
	
	p += do_copy(&(info->mmap_addr), p, info->mmap_length);
	
	// store some stuff in the reserved page
	init_data_t* idata = (init_data_t*)p_start;
	idata->multiboot_end = (iptr_t)p;
	idata->multiboot_end = (idata->multiboot_end & PAGE_MASK)
			       + ((idata->multiboot_end & ~PAGE_MASK)
			          ? PAGE_SIZE : 0);
	
	// We now want to set up the IDT so we can handle errors and the
	// physical page manager so we can manage memory
	// IDT requires GDT and TSS.
	// So we need 5 pages for sysinfo, IDT, GDT, TSS and page_manager
#define NEEDED_SIZE (5 * PAGE_SIZE)
	
	// check below multiboot info
	if (((iptr_t)p_start) - PAGE_SIZE >= NEEDED_SIZE)
		sysinfo = (root_sysinfo_t*)PAGE_SIZE;
	
	// check above
	else if ((640 * 1024) - PAGE_SIZE - idata->multiboot_end
		 >= NEEDED_SIZE)
		sysinfo = (root_sysinfo_t*)
			  (idata->multiboot_end); // will be page aligned
	
	else
		panic("No memory for sysinfo!");
	
	idata->sysinfo_size = NEEDED_SIZE;
	memzero(sysinfo, NEEDED_SIZE);
	
#undef NEEDED_SIZE

	// now set the lot up
	iptr_t ptr = (iptr_t)sysinfo;
	sysinfo->gdt = (gdt_t*)(ptr + PAGE_SIZE);
	sysinfo->tss_seg = (tss_segment_t*)(ptr + 2 * PAGE_SIZE);
	sysinfo->interrupts = (interrupt_data_t*)(ptr + 3 * PAGE_SIZE);
	sysinfo->page_manager = (page_manager_t*)(ptr + 4 * PAGE_SIZE);
	
	sysinfo->multiboot = info;
}

/*
  TODO:	Protect the sysinfo struct with a lock which can allow reading but
		deny writing while it is being read and deny both reading and
		writing while it is being written to.
*/