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


/*
 gdt.c - GDT management
  
 Author:        Paul Barker
 Part of:       COS
 Created:       17/04/04
 Last Modified: 06/10/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 

//////////////////////////////////////////////////////////////////////////
// private functions

// loop through the gdt looking for a free entry
u8_t gdt_get_free()
{
	gdt_entry_t* desc;
	count_t i = 1;	// start with 1st entry, since 0 is left blank
	
	while (i < GDT_NUM_ENTRIES)
	{
		desc = gdt_get(i);
		if (desc->avail == 1)
		{
			return i;
		}
		++i;
	}
	
	// there doesn't seem to be any free GDT entries!
	return 0xFF;
}

//////////////////////////////////////////////////////////////////////////
// public functions

// initialise the kernel GDT
void gdt_init()
{
	TRACE(("Initialising GDT...\n"));
	word_t limitAndBase[3];
	count_t i;

	// firstly, wipe our GDT for safety
	memset((ptr_t)sysinfo->gdt, 0, 4096);

	// initialise the kernel code segment
	gdt_set_simple(1, 0x0A);
		// 1010b: code, !conforming, readable, !accessed
		// (This line taken from GeekOS)

	// initialise the kernel data segment by hand
	gdt_set_simple(2, 0x02);
		// 0010b: data, expand-up, writable, !accessed
		// (This line taken from GeekOS)

	// now set every other entry as available
	i = 3;	// start with 3rd entry
	while (i < GDT_NUM_ENTRIES)
	{
		sysinfo->gdt->entries[i++].avail = 1;
	}

	u32_t addr = (u32_t)sysinfo->gdt;
	u16_t sz = GDT_NUM_ENTRIES * sizeof(gdt_entry_t);
	
	// cruft together a limit and base, then load the GDT.
	//   This bit is taken from GeekOS, with modifications
	limitAndBase[0] = sz;
	limitAndBase[1] = addr & 0xffff;
	limitAndBase[2] = addr >> 16;

	TRACE(("Loading GDT at 0x%x, size %d bytes\n", addr, sz));
	lgdtr(limitAndBase);
	
	TRACE(("GDT ready, %d free entries of %d total entries\n",
	       GDT_NUM_ENTRIES - 3, GDT_NUM_ENTRIES));
}

// add an entry, returns the entry number
// assume interrupts are off otherwise we might have problems
u8_t gdt_add(u8_t type,
	     u32_t base /* = 0 */,
	     u32_t size /* = 0xFFFFFFFF */,
	     u8_t system /* = 1 */,
	     u8_t granularity /* = 1 */,
	     u8_t dbBit /* = 1 */,
	     u8_t dpl /* = 0 */)
{
	TRACE(("gdt_add: Adding GDT entry\n"));
	TRACE(("type=%x, base=%x, size=%x\n", type, base, size));
	TRACE(("system=%d, granularity=%d, dbBit=%d, dpl=%d\n",
		system, granularity, dbBit, dpl));
	
	u32_t i = gdt_get_free();
	gdt_entry_t* desc = &(sysinfo->gdt->entries[i]);
	
	if (i == 0xFF)
		return 0xFF;
	
	TRACE(("found free entry %d\n", i));
	
	gdt_set_size(desc, size);
	gdt_set_base(desc, base);
	desc->system = system;
	desc->present = 1;
	desc->granularity = granularity;
	desc->type = type;
	desc->dbBit = dbBit;
	desc->dpl = dpl;
	desc->avail = 0;
	
	TRACE(("gdt_add() done\n"));
	
	return i;
}

// get an existing entry
gdt_entry_t* gdt_get(u8_t entry)
{
	return &(sysinfo->gdt->entries[entry]);
}

// copy an entry into the table
// I expect this will rarely be used, but could reduce the amount of time
//    interrupts will be disabled for once memcpy() is optimised.
void gdt_put(u8_t entry, gdt_entry_t* desc)
{
	gdt_entry_t* ent = gdt_get(entry);
	TRACE(("gdt_put: copying from %x to descriptor %d at %x\n",
	       desc, entry, ent));
	memcpy(ent, desc, sizeof(gdt_entry_t));
}

// remove an entry
// again interrupts should be disabled, we always assume this will work
void gdt_remove(u8_t entry)
{
	gdt_entry_t* desc = gdt_get(entry);
	TRACE(("gdt_remove: removing entry %d at %x\n", entry, desc));
	memset((ptr_t)desc, 0, sizeof(gdt_entry_t));
	desc->avail = 1;
}

// modify an entry, or add an entry at a certain position
// assume interrupts are disabled yet again
void gdt_set(u8_t entry,
	    u8_t type,
	    u32_t base /* = 0 */,
	    u32_t size /* = 0xFFFFFFFF */,
	    u8_t system /* = 1 */,
	    u8_t granularity /* = 1 */,
	    u8_t dbBit /* = 1 */,
	    u8_t dpl /* = 0 */)
{
	gdt_entry_t* desc = gdt_get(entry);
	
	TRACE(("gdt_set: Setting GDT entry %d at %x\n", entry, desc));
	TRACE(("type=%x, base=%x, size=%x\n", type, base, size));
	TRACE(("system=%d, granularity=%d, dbBit=%d, dpl=%d\n",
		system, granularity, dbBit, dpl));
		
	memset((ptr_t)desc, 0, sizeof(gdt_entry_t));
	gdt_set_size(desc, size);
	gdt_set_base(desc, base);
	desc->system = system;
	desc->present = 1;
	desc->granularity = granularity;
	desc->type = type;
	desc->dbBit = dbBit;
	desc->dpl = dpl;
	desc->avail = 0;
	
	TRACE(("gdt_set() done\n"));
}