Page 1 of 1

Multitasking problem

Posted: Wed May 13, 2009 7:00 pm
by AaronMiller
Hullo all. :)

I've been having a bit of trouble with multitasking. You see, I'm following various JamesM kernel dev. tutorials, but have run into a bit of a snag. The problem appears to be a problem with switching tasks. I assume the problem has something to do with the stack, since it occurs at the RET instruction of my code (C++ return after the EIP "magic number" test.)

I also suspect the problem could just as well be a paging error -- ex: not mapping correctly, or not switching the correct page address -- though I did check for that.

I've been working on solving this problem for quite some time, and it's rather an annoying bug. To be honest, I have got absolutely no idea what piece of code is wrong, what's actually going wrong, or any idea as to how to fix it (aside from not supporting multitasking.) I really need some help here and am hoping someone might be able to lend me a hand.

I've already fixed several potential bugs, and removed several other bugs, but its a bit difficult to tell if any of those were directly related to the problem I'm currently having (not being able to switch tasks.)

-- What happens, more specifically, is the EIP is NOT where it's supposed to be once the RET instruction finishes -- and I get a paging error at an obscure address, 0xF0000FE or something like that -- well, not that exact address (Somewhere in the 0xFXXXXXXX range) but nowhere in my code do I reference memory that high, I don't even put the stack that high! (The stack is being placed so that the very top of it is 0xE0002000, and the bottom of it is 0xE0000000.) Sometimes outputing debug messages causes QEMU to crash, which is rather unhelpful. I'm very certain its not a "heap-alloc'd" memory error, rather a combination of interrupt/stack error that causes QEMU to crash (QEMU becomes completely unresponsive, and outputs no debug information.)

Anyways, this error is bothering me greatly -- I'm so close to having multitasking working -- Once this bug is out of the way (provided there are no hidden bugs, lurking in the dark) I'll have basic multitasking (and then expand on to priorities and such :D). But I can't for the life of me figure out the problem. Here are some source files:

task.c

Code: Select all

/* Includes */
#include <kernel.h>

/* Globals */
// From main.c
extern u32				g_esp;
// From paging.c
extern sPageDir*		g_pKrnlPgDir;
extern sPageDir*		g_pCurPgDir;
// From mm.c
extern sHeap*			g_pKrnlHeap;
// Our own
sTask*					g_pKrnlTask = (sTask*)0;
sTask*					g_pCurTask = (sTask*)0;
sList					g_taskList;
u32						g_freePid = 1;

/* Functions */
// Move the stack
SYS_FUNC void SYS_API tsMoveStack(void)
{
	// Preliminary
	u32		i;
	//u32		addr;
	u32		offset;
	u32		oldEsp, newEsp;
	u32		oldEbp, newEbp;
	u32		tmp, *pTmp;
	// Allocate the stack
	//dbgWrite(" (tsMoveStack) - Allocate the stack\n");
	for(i = STACK_TOP_ADDR; i >= STACK_BASE_ADDR; i -= 0x1000)
	{
		//dbgShowVar(i);
		pgAllocFrame(pgGetPage(g_pCurPgDir, i, B_TRUE), B_FALSE, B_TRUE);
	}
	// Flush the TLB
	//dbgWrite(" (tsMoveStack) - Flush the TLB\n");
	//asm volatile("push eax\n"
	//	"mov eax, cr3\n"
	//	"mov cr3, eax\n"
	//	"pop eax");
	asm volatile("mov eax, cr3" : "=a" (tmp));
	asm volatile("mov cr3, eax" :: "a" (tmp));
	// Grab the old ESP and EBP
	//dbgWrite(" (tsMoveStack) - Grab the OLD ESP & EBP\n");
	asm volatile("mov eax, esp" : "=a" (oldEsp));
	asm volatile("mov edx, ebp" : "=d" (oldEbp));
	// Find the new ESP and EBP
	//dbgWrite(" (tsMoveStack) - Find the new ESP & EBP\n");
	offset = STACK_TOP_ADDR-g_esp;
	newEsp = oldEsp+offset;
	newEbp = oldEbp+offset;
	// -- Extreme debugging
	//dbgShowVar(g_esp);
	//dbgShowVar(offset);
	//dbgShowVar(newEsp);
	//dbgShowVar(newEbp);
	// Copy over the stack
	//dbgWrite(" (tsMoveStack) - Copy over the stack\n");
	kMemCpy((u8*)newEsp, (u8*)oldEsp, g_esp-oldEsp);
	// Backtrace using JamesM's method -- must change
	//dbgWrite(" (tsMoveStack) - Backtrace the stack\n");
	//for(i = addr-size; i < addr; i += 4)
	for(i = STACK_TOP_ADDR; i > STACK_BASE_ADDR; i -= 4)
	{
		tmp = *(u32*)i;
		if ((oldEsp < tmp) && (tmp < g_esp))
		{
			tmp = tmp + offset;
			pTmp = (u32*)i;
			*pTmp = tmp;
			//dbgShowVar(tmp);
		}
		/*
		dbeWrite("--a\n");
		u32* pIterator = (u32*)i;
		dbeWrite("--b\n");
		tmp = *pIterator;
		dbeWrite("--c\n");
		if ((oldEsp < tmp) && (tmp < g_esp))
		{
			dbeWrite("--d\n");
			tmp = tmp + offset;
			dbeWrite("--e\n");
			pIterator[0] = tmp; // u32* tmp2 = (u32*)i; *tmp2 = tmp;
		}
		dbgShowVar(tmp);
		dbgShowVar(pIterator[0]);
		dbeWrite("--f\n");
		*/
	}
	// Finally, change the esp and EBP
	//dbgWrite(" (tsMoveStack) - Change the ESP & EBP\n");
	asm volatile("mov esp, eax" :: "a" (newEsp));
	asm volatile("mov ebp, edx" :: "d" (newEbp));
	//dbgWrite(" (tsMoveStack) - Done\n");
}
// Initialize the use of tasking
SYS_FUNC bool_t SYS_API tsInit(void)
{
	// Stop interrupts
	//dbgWrite(" (tsInit) - Stop interrupts\n");
	asm volatile("cli");
	// Relocate the stack
	//dbgWrite(" (tsInit) - Relocate the stack\n");
	tsMoveStack();
	// Initialize the task list
	//dbgWrite(" (tsInit) - Initialize the task list\n");
	lsInit(&g_taskList);
	// Allocate the kernel task
	//dbgWrite(" (tsInit) - Allocate the kernel task\n");
	g_pKrnlTask = mmKrnlAllocType(sTask);
	if (!g_pKrnlTask)
	{
		errSetError(TS_EC_CANT_ALLOC_TASK, TS_ES_CANT_ALLOC_TASK);
		return B_FALSE;
	}
	// Setup the task
	//dbgWrite(" (tsInit) - Setup the kernel task\n");
	g_pKrnlTask->pProcess = (sTask*)0;	// no parent process - we're the kernel
	g_pKrnlTask->pid = g_freePid++;
	g_pKrnlTask->priority = PL_KERNEL;	// Only the kernel can have this priority
	g_pKrnlTask->pPgDir = g_pKrnlPgDir;
	g_pKrnlTask->pDefHeap = g_pKrnlHeap;
	g_pKrnlTask->esp = 0;
	g_pKrnlTask->ebp = 0;
	g_pKrnlTask->eip = 0;
	// Add the kernel process to the list
	//dbgWrite(" (tsInit) - Add the kernel task to the list\n");
	lsInitItem(&g_pKrnlTask->item, (void*)g_pKrnlTask, 0);
	lsAddItem(&g_taskList, &g_pKrnlTask->item);
	// Set the current task
	//dbgWrite(" (tsInit) - Set the current task\n");
	g_pCurTask = g_pKrnlTask;
	// Set no error
	//dbgWrite(" (tsInit) - Set that no error has occurred\n");
	errSetError(EC_NONE, ES_NONE);
	// Re-enable interrupts
	//dbgWrite(" (tsInit) - Re-enable interrupts and return\n");
	asm volatile("sti");
	return B_TRUE;
}
// Fork the current process
SYS_FUNC u32 SYS_API tsFork(void)
{
	// Preliminary
	sTask*		pParentTask;
	sTask*		pTask;
	sPageDir*	pPgDir;
	u32			esp, ebp, eip;
	// Disable interrupts
	//dbgWrite(" (tsFork) - Disable interrupts\n");
	asm volatile("cli");
	// Get the parent task
	//dbgWrite(" (tsFork) - Get the parent task\n");
	pParentTask = g_pCurTask;
	// Clone the address space
	//dbgWrite(" (tsFork) - Clone the address space\n");
	pPgDir = pgCloneDir(g_pCurPgDir);
	if (!pPgDir)
		return 0;
	// Create a new task
	//dbgWrite(" (tsFork) - Create a new task\n");
	pTask = mmKrnlAllocType(sTask);
	if (!pTask)
		return 0;
	// Setup the task
	//dbgWrite(" (tsFork) - Setup the new task\n");
	pTask->pProcess = pParentTask;
	pTask->pid = g_freePid++;
	pTask->priority = PL_NORMAL;
	pTask->pPgDir = pPgDir;
	pTask->pDefHeap = (sHeap*)0;
	pTask->esp = 0;
	pTask->ebp = 0;
	pTask->eip = 0;
	lsInitItem(&pTask->item, (void*)pTask, 0);
	lsAddItem(&g_taskList, &pTask->item);
	// Grab the EIP
	//dbgWrite(" (tsFork) - Grab the EIP\n");
	eip = kGetEip(); // Save the EIP now
	// Check if the task is the parent or child
	if (g_pCurTask == pParentTask)
	{
		// Grab the ESP and EBP
		//dbgWrite(" (tsFork::parent) - Grab the ESP & EBP\n");
		asm volatile("mov eax, esp\nmov edx, ebp" : "=a" (esp), "=d" (ebp));
		// Set the task's ESP, EBP, and EIP
		//dbgWrite(" (tsFork::parent) - Set the task's ESP, EBP, & EIP\n");
		pTask->esp = esp;
		pTask->ebp = ebp;
		pTask->eip = eip;
		// Done
		//dbgWrite(" (tsFork::parent) - Re-enable interrupts\n");
		asm volatile("sti");
		//dbgWrite(" (tsFork::parent) - Done\n");
		return pTask->pid;
	}
	//dbgWrite(" (tsFork) - Done\n");
	return 0;
}
// Switch the task
SYS_FUNC void SYS_API tsSwitchTask(void)
{
	// Preliminary
	u32 esp, ebp, eip;
	// Don't interrupt me, I'm executing
	asm volatile("cli");
	// Make sure multitasking has been initialized
	if (!g_pCurTask)
		return;
	// Grab some registers
	//dbgWrite(" (tsSwitchTask) - Grab some registers\n");
	asm volatile("mov eax, esp\n"
		"mov edx, ebp" : "=a" (esp), "=d" (ebp));
	//dbgWrite(" (tsSwitchTask) - Grab and check the EIP\n");
	eip = kGetEip();
	// Check if we just switched tasks
	if (eip == 0x12345)
	{
		dbgWrite(" (tsSwitchTask) - Returning\n");
		asm volatile("pop ecx" : "=c" (eip));
		dbgShowVar(eip);
		asm volatile("push ecx" :: "c" (eip));
		return;
	}
	// Set the EIP, ESP, and EBP of the current task
	// -- wouldn't it be better to pass in a sRegs* to tsSwitchTask and do this:
	//	g_pCurTask->eip = pRegs->eip;
	//	g_pCurTask->esp = pRegs->esp;
	//	g_pCurTask->ebp = pRegs->ebp;
	// -- is there some reason why this wouldn't work?
	//dbgWrite(" (tsSwitchTask) - Save some registers and values\n");
	g_pCurTask->eip = eip;
	g_pCurTask->esp = esp;
	g_pCurTask->ebp = ebp;
	// Get the next task
	//dbgWrite(" (tsSwitchTask) - Get the next task\n");
	g_pCurTask = (sTask*)lsAfter(&g_taskList, &g_pCurTask->item);
	if (!g_pCurTask)
		g_pCurTask = (sTask*)lsFirst(&g_taskList);
	// Grab stack info from the new current stack
	//dbgShowVar(g_pCurTask);
	//dbgWrite(" (tsSwitchTask) - Grab stack info\n");
	esp = g_pCurTask->esp;
	ebp = g_pCurTask->ebp;
	dbgShowVar(eip);
	//dbgShowVar(esp);
	//dbgShowVar(ebp);
	// Jump to the new EIP
	dbgWrite(" (tsSwitchTask) - Jump to the new EIP\n");
	asm volatile("mov esp, edx\n"
		"mov ebp, ebx\n"
		"mov cr3, eax\n"
		"mov eax, 0x12345\n"
		"sti\n"
		"jmp dword ptr ecx" :: "c" (eip), "d" (esp), "b" (ebp), "a" (g_pCurTask->pPgDir->physAddr));
}
// Get the PID of the current task
SYS_FUNC u32 SYS_API tsGetPid(void)
{
	if (!g_pCurTask)
	{
		errSetError(EC_NOT_INITIALIZED, ES_NOT_INITIALIZED);
		return 0;
	}
	return g_pCurTask->pid;
}
paging.c

Code: Select all

/* Includes */
#include <kernel.h>

/* Globals */
// External
extern u32				g_placementAddr;
// Kernel page directory
sPageDir*				g_pKrnlPgDir;
sPageDir*				g_pCurPgDir;
// Kernel heap
extern sHeap*			g_pKrnlHeap;

/* Internal Functions */
// Allocate a single page table
INT_FUNC void INT_API allocPageTable(sPageDir* pDir, u32 entry, u32 flags)
{
	// Preliminary
	u32	tmp;
	// Allocate the table
	pDir->pEntries[entry] = (sPageTable*)mmKrnlAllocEx(0x1000, B_TRUE, (void**)&tmp);
	kMemSet((u8*)pDir->pEntries[entry], 0, 0x1000);
	pDir->physEntries[entry] = tmp|flags;
	// Debug info
	dbgWrite("Allocated page table at address ");
	dbgWriteHex(tmp);
	dbgWrite(" -- physEnt = ");
	dbgWriteHex(tmp|flags);
	dbgWriteChar('\n');
}
// Copy a page table
INT_FUNC sPageTable* INT_API cloneTable(sPageTable* pSrc, u32* pPhysAddr)
{
	// Preliminary
	sPageTable*	pTable;
	u32			i;
	// Allocate a page table
	pTable = mmKrnlAllocTypeEx(sPageTable, B_TRUE, pPhysAddr);
	if (!pTable)
		return (sPageTable*)0;
	// Zero out the table
	kMemSet((u8*)pTable, 0, sizeof(sPageTable)); // JamesM's was sizeof page directory (sizeof(sPageDir))
	// Set every page table entry
	for(i = 0; i < 1024; i++)
	{
		// Check the base address
		if (!pSrc->pages[i].baseAddr)
			continue;
		// Allocate a new frame
		pgAllocFrame(&pTable->pages[i], B_FALSE, B_FALSE);
		// Copy over certain flags
		pTable->pages[i].flags = 0;
		if (pSrc->pages[i].flags & PG_PRESENT)
			pTable->pages[i].flags |= PG_PRESENT;
		if (pSrc->pages[i].flags & PG_RW)
			pTable->pages[i].flags |= PG_RW;
		if (pSrc->pages[i].flags & PG_USER)
			pTable->pages[i].flags |= PG_USER;
		if (pSrc->pages[i].flags & PG_ACCESSED)
			pTable->pages[i].flags |= PG_ACCESSED;
		if (pSrc->pages[i].flags & PG_DIRTY)
			pTable->pages[i].flags |= PG_DIRTY;
		// Copy the page
		pgCopyPage(pSrc->pages[i].baseAddr*0x1000, pTable->pages[i].baseAddr*0x1000);
		/*
		asm volatile("pushf\n"
			"cli\n"
			"mov edx, cr0\n"
			"and edx, 0x7FFFFFFF\n"
			"mov cr0, edx\n"	// disable paging
			"mov edx, 1024\n"
			".jump_pt:\n"
			"\tmov eax, [edx]\n"
			"\tmov [ecx], eax\n"
			"\tadd ebx, 4\n"
			"\tadd ecx, 4\n"
			"\tdec edx\n"
			"\tjnz .jump_pt\n"
			"mov edx, cr0\n"
			"or edx, 0x80000000\n"
			"mov cr0, edx\n"	// enable paging
			"popf" :: "b" (pSrc->pages[i].baseAddr*PAGE_SIZE), "c" (pTable->pages[i].baseAddr*PAGE_SIZE));
		*/
	}
}

/* Functions */
// Allocate a frame
SYS_FUNC void SYS_API pgAllocFrame(sPage* pPage, bool_t user, bool_t rw)
{
	// Find the first free frame
	u32 index;
	// Check if the frame is already allocated
	if (pPage->baseAddr != 0)
		return;
	// Find the index of the first free frame
	index = pfaAllocFrame();
	pPage->flags = PG_PRESENT | ((user == B_TRUE) ? PG_USER : 0) | ((rw == B_TRUE) ? PG_RW : 0);
	pPage->baseAddr = index;
}
// Deallocate a frame
SYS_FUNC void SYS_API pgFreeFrame(sPage* pPage)
{
	// Preliminary
	u32 frame;
	// Grab the frame
	frame = pPage->baseAddr;
	if (!frame)
		return;
	// Clear the frame and zero out the pages frame address
	pfaFreeFrame(frame);
	pPage->baseAddr = 0;
}
// Get a page from a page directory
SYS_FUNC sPage* SYS_API pgGetPage(sPageDir* pDir, u32 addr, bool_t make)
{
	// Preliminary
	u32			index;
	u32			tableIndex;
	u32			tmp;
	sPageTable*	pTable;
	// Find the table index
	index = addr/0x1000;
	tableIndex = index/1024;
	// Is the table assigned?
	if (pDir->pEntries[tableIndex])
	{
		// Grab the table, and return the page
		pTable = pDir->pEntries[tableIndex];
		return &pTable->pages[index%1024];
	}
	// Should we make the table, considering it doesn't exist?
	if (make)
	{
		// Allocate a new page table, and return the requested page
		allocPageTable(pDir, tableIndex, PT_PRESENT|PT_RW|PT_USER);
		pTable = pDir->pEntries[tableIndex];
		return &pTable->pages[index%1024];
	}
	// Utter failure, return null
	return (sPage*)0;
}
// Clone a page directory
SYS_FUNC sPageDir* SYS_API pgCloneDir(sPageDir* pSrc)
{
	// Preliminary
	sPageDir*	pDir;
	u32			phys;
	u32			offset;
	u32			i;
	// Allocate the page directory
	pDir = mmKrnlAllocTypeEx(sPageDir, B_TRUE, &phys);
	if (!pDir)
		return (sPageDir*)0;
	// Zero out the page directory
	kMemSet((u8*)pDir, 0, sizeof(sPageDir));
	// Grab the offset
	offset = ((u32)pDir->physEntries)-((u32)pDir);
	// Setup the physical address
	pDir->physAddr = offset+phys;
	// Go through the tables
	for(i = 0; i < 1024; i++)
	{
		// Should we link?
		if (g_pKrnlPgDir->pEntries[i] == pSrc->pEntries[i])
		{
			// Link -- Just copy the pointers
			pDir->pEntries[i] = pSrc->pEntries[i];
			pDir->physEntries[i] = pSrc->physEntries[i];
		}
		// Or should we copy?
		else if (pSrc->pEntries[i])
		{
			// Copy the page table
			pDir->pEntries[i] = cloneTable(pSrc->pEntries[i], &phys);
			pDir->physEntries[i] = phys|PT_PRESENT|PT_RW|PT_USER;
		}
	}
	// Return the page directory
	return pDir;
}
// Initialize paging
SYS_FUNC void SYS_API pgInit(void)
{
	// Preliminary
	u32 i, phys;
	// Allocate a page directory entry
	g_pKrnlPgDir = mmKrnlAllocTypeEx(sPageDir, B_TRUE, (void**)&phys);
	kMemSet((u8*)g_pKrnlPgDir, 0, sizeof(sPageDir));
	g_pKrnlPgDir->physAddr = (u32)(g_pKrnlPgDir->physEntries);
	dbgWriteVar(g_pKrnlPgDir);
	// Allocate pages for the heap
	dbgWrite(" - Allocating pages to allow the page table for the kernel heap to be made.\n");
	for(i = KHEAP_START; i < KHEAP_START+KHEAP_DEF_SIZE; i += 0x1000)
		pgGetPage(g_pKrnlPgDir, i, B_TRUE);
	// Identity map
	for(i = 0; i < g_placementAddr+0x1000; i += 0x1000)
		pgAllocFrame(pgGetPage(g_pKrnlPgDir, i, B_TRUE), B_FALSE, B_FALSE);
	// Allocate the pages mapped earlier
	dbgWrite(" - Setting the kernel pages allocated earlier.\n");
	for(i = KHEAP_START; i < KHEAP_START+KHEAP_DEF_SIZE; i += 0x1000)
		pgAllocFrame(pgGetPage(g_pKrnlPgDir, i, B_TRUE), B_FALSE, B_FALSE);
	// Set the page directory
	pgSwitchDir(g_pKrnlPgDir);
	// Show the placement address
	dbgShowVar(g_placementAddr);
#if defined(EXTREMEDEBUG)
	// Debug Preliminary
	u32 cr0, cr3;
	// Grab some values
	asm volatile("mov eax, cr0" : "=a" (cr0));
	asm volatile("mov eax, cr3" : "=a" (cr3));
	// Write the values
	dbeWriteVar(cr0);
	dbeWriteVar(cr3);
#endif
	// Create the heap
	dbgWrite(" - Creating the kernel heap\n");
	g_pKrnlHeap = mmCreateHeap(KHEAP_START, KHEAP_DEF_SIZE, KHEAP_MAX_SIZE, 0);
	dbgWrite(" - Done creating the kernel heap\n");
	// Change the page directory
	g_pCurPgDir = pgCloneDir(g_pKrnlPgDir);
	if (!g_pCurPgDir)
	{
		errKernelPanic(__FILE__, __LINE__, "g_pCurPgDir != (sPageDir*)0", "Unable to clone page directory");
	}
	pgSwitchDir(g_pCurPgDir);
}
// Page fault handler
SYS_FUNC void ASM_API pgFaultHandler(sRegs* pRegs)
{
	// Preliminary
	u32 faultAddr, flags;
	// Ask the processor for the fault address and grab the flags
	asm volatile("mov eax, cr2" : "=a" (faultAddr));
	flags = pRegs->errCode;
	/*
		TODO: Check rather or not the page is allocated.
			  If the page IS allocated, then support swapping from hard disk.
			  If the page isn't allocated, then send the task the exception
			  signal.
	*/
	// Write the error to the screen
	ktmSetColor(0xC, 0);
	ktmWrite("\nPage Fault! - The kernel has died with some information\n");
	ktmSetColor(0xF, 0);
	if (flags & 2)
		ktmWrite("Unable to write memory to address ");
	else
		ktmWrite("Unable to read memory from address ");
	ktmWriteHex(faultAddr); ktmWrite("\n\n");
	ktmSetColor(0xC, 0);
	ktmWrite("Details:\n");
	ktmSetColor(4, 0);
	if (!(flags & 1))
		ktmWrite(" - Page not present\n");
	if (flags & 2)
		ktmWrite(" - Write operation\n");
	else
		ktmWrite(" - Read operation\n");
	if (flags & 4)
		ktmWrite(" - User mode write\n");
	if (flags & 8)
		ktmWrite(" - Overwrote reserved bits\n");
	if (flags & 16)
		ktmWrite(" - Caused by instruction fetch\n");
	errShowInfo();
	ktmSetColor(0xE, 0);
	ktmWrite("\nEAX = "); ktmWriteHex(pRegs->eax);
	ktmWrite(", EDX = "); ktmWriteHex(pRegs->edx);
	ktmWrite(", ECX = "); ktmWriteHex(pRegs->ecx);
	ktmWrite(", EBX = "); ktmWriteHex(pRegs->ebx);
	ktmWrite("\nEIP = "); ktmWriteHex(pRegs->eip);
	ktmWrite(", ESP = "); ktmWriteHex(pRegs->esp);
	ktmWrite(", EBP = "); ktmWriteHex(pRegs->ebp);
	// Halt the system
	while(1) asm volatile("hlt");
}
// Switch the page directory
SYS_FUNC void SYS_API pgSwitchDir(sPageDir* pDir)
{
	// Preliminary
	u32 cr0;
	// Set the current page directory
	g_pCurPgDir = pDir;
	// Set the page directory address to the PDBA register (CR3)
	asm volatile("mov cr3, eax" :: "a" (pDir->physAddr));
	// Set the PG bit of CR0 so that paging may be enabled
	asm volatile("mov eax, cr0" : "=a" (cr0));
	cr0 |= 0x80000000;
	asm volatile("mov cr0, eax" :: "a" (cr0));
	// Inform the world that we are in paging mode!
	dbgWrite("\nSwitched page directory!\n\n");
}
kernel.h

Code: Select all

#ifndef	KERNEL_H
#define	KERNEL_H

/* Includes */
#define sObject_defined
#include "dragon.h"

/* Defines */
// Version states
#define VS_ALPHA							0
#define VS_BETA								1
#define VS_RELEASE_CANDIDATE				2
#define VS_RELEASE							3
// Kernel version
#define KERNEL_VERSION_MAJOR				0
#define KERNEL_VERSION_MINOR				0
#define KERNEL_VERSION_MINOR_MINOR			5
#define KERNEL_VERSION_STATE				VS_ALPHA
#if defined(DEBUG)
#	define KERNEL_VERSION					"dragon-kernel-0.05-alpha(debug)"
#else
#	define KERNEL_VERSION					"dragon-kernel-0.05-alpha"
#endif
// Multiboot defines
#define MULTIBOOT_FLAG_MEM					0x001
#define MULTIBOOT_FLAG_DEVICE				0x002
#define MULTIBOOT_FLAG_CMDLINE				0x004
#define MULTIBOOT_FLAG_MODS					0x008
#define MULTIBOOT_FLAG_AOUT					0x010
#define MULTIBOOT_FLAG_ELF					0x020
#define MULTIBOOT_FLAG_MMAP					0x040
#define MULTIBOOT_FLAG_CONFIG				0x080
#define MULTIBOOT_FLAG_LOADER				0x100
#define MULTIBOOT_FLAG_APM					0x200
#define MULTIBOOT_FLAG_VBE					0x400
// Kernel boot modes
#define KBM_GUI								1 // boot with a GUI
#define KBM_NETWORK							2 // boot allowing internet/network connections
#define KBM_SAFE_MODE						4 // boot in safe mode
// Screen (ktm)
#define	TAB_WIDTH							8
// Size of a page
#define	PAGE_SIZE							0x1000
// Kernel heap information
#define KHEAP_START							0xC0000000
#define KHEAP_DEF_SIZE						0x700000
#define KHEAP_MAX_SIZE						0x20000000
// Page table flags
#define PT_PRESENT							1
#define PT_RW								2
#define PT_USER								4
#define PT_WT								8
#define PT_CD								16
#define PT_ACCESSED							32
#define PT_RESERVED							64
#define PT_SIZE								128
#define PT_GLOBAL							256
// Page flags
#define PG_PRESENT							1
#define PG_RW								2
#define	PG_USER								4
#define PG_WT								8
#define PG_CD								16
#define PG_ACCESSED							32
#define PG_DIRTY							64
#define PG_PTAI								128
#define PG_GLOBAL							256
// Heap magic values
#define HEAP_MAGIC							0xFACEBEEF
#define HEAP_ENTRY_OCCUPIED					0xCBC0BCB1
#define HEAP_ENTRY_VACANT					0x5456763C
#define	HEAP_ENTRY_FOOTER					0xFAD0F001
// Heap values
#define HEAP_INDEX_SIZE						0x20000
#define HEAP_MIN_SIZE						0x50000
// Heap flags
#define HF_KERNEL							1
#define HF_READ_ONLY						2
// Heap entry flags
#define HEF_PAGE_ALIGN						1
// Node masks
#define FSM_VIRTUAL							0	// virtual node (implies directory styled access)
#define FSM_FILE							1	// file
#define FSM_DIR								2	// directory
#define FSM_CHAR_DEVICE						3	// device; ex, stdio, stdlib
#define FSM_BLOCK_DEVICE					4	// device; ex, hdd, fdd, cd, etc
#define FSM_PIPE							5	// process pipe
#define FSM_SYM_LINK						6	// symbolic link/shortcut
// Node flags
#define FSF_SYSTEM							1	// system file (cannot be moved)
#define FSF_READ_ONLY						2	// read-only file
#define FSF_HIDDEN							4	// hidden file
#define FSF_EXECUTABLE						8	// executable file
#define FSF_COMPRESSED						16	// compressed file
#define FSF_ENCRYPTED						32	// encrypted file
// Open flags
#define OPN_WRITE							1	// write access for self
#define OPN_READ							2	// read access for self
#define OPN_SHARE_WRITE						4	// write access for others
#define OPN_SHARE_READ						8	// read access for others
// File block size
#define FILE_BLOCK_SIZE						0x1000
// Object magic header
#define OBJ_MAGIC							0xBABAF00D
// InitRD header magic values
#define IRD_MAGIC_1							0x04305478
#define IRD_MAGIC_2							0xFEEDBEEF
// File seek relative location
#define FSR_START							0
#define FSR_CURRENT							1
#define FSR_END								2
// Priority levels
#define PL_IDLE								0
#define PL_VERY_LOW							1
#define PL_LOW								2
#define PL_NORMAL							3
#define PL_HIGH								4
#define PL_VERY_HIGH						5
#define PL_REAL_TIME						6
#define PL_KERNEL							0xFFFFFFFF
// Priority times
#define PT_IDLE								4	// 4 milliseconds run time - useful for apps that are waiting for a resource to become available
#define PT_VERY_LOW							7	// 7 milliseconds run time - useful for apps that do very little work
#define PT_LOW								13	// 13 milliseconds run time - useful for apps that do little work
#define PT_NORMAL							25	// 25 milliseconds run time - useful for normal apps
#define PT_HIGH								50	// 50 milliseconds run time - useful for video editing software
#define PT_VERY_HIGH						100	// 100 milliseconds run time - useful for games
#define PT_REAL_TIME						200	// 200 milliseconds run time - useful for high-end games
#define PT_KERNEL							0xFFFFFFFF
// Shared data area
#define STACK_BASE_ADDR						0xE0000000
#define STACK_TOP_ADDR						0xE0002000
#define STACK_SIZE							(STACK_TOP_ADDR-STACK_BASE_ADDR)

/* Macros */
// Page frame allocator (PFA) macros
#define MakeFrameId(i,b)					(((i)*32)+(b))
#define GetFrameIndex(f)					((f)/32)
#define GetFrameBit(f)						((f)%8)
// Assertion
#define assert(x)							{\
												if (!(x))\
													errKernelPanic(__FILE__, __LINE__, #x, "Kernel Assertion Failed");\
											}
// Debug macros
#if defined(DEBUG)
#	define dbgWrite(s)						{ ktmWrite(s); errSetDebugInfo(__LINE__, __FILE__); }
#	define dbgWriteChar(c)					{ ktmWriteChar(c); errSetDebugInfo(__LINE__, __FILE__); }
#	define dbgWriteDec(s)					{ ktmWriteDec(s); errSetDebugInfo(__LINE__, __FILE__); }
#	define dbgWriteHex(s)					{ ktmWriteHex(s); errSetDebugInfo(__LINE__, __FILE__); }
#	define dbgWriteOct(s)					{ ktmWriteOct(s); errSetDebugInfo(__LINE__, __FILE__); }
#	define dbgWriteBin(s)					{ ktmWriteBin(s); errSetDebugInfo(__LINE__, __FILE__); }
#	define dbgShowVar(v)					{\
												ktmWrite(#v " = ");\
												ktmWriteDec((s32)(v));\
												ktmWrite(" (");\
												ktmWriteHex((u32)(v));\
												ktmWrite(")\n");\
												errSetDebugInfo(__LINE__, __FILE__);\
											}
#else
#	define dbgWrite(s)						// not in debug mode...
#	define dbgWriteChar(c)					// not in debug mode...
#	define dbgWriteDec(s)					// not in debug mode...
#	define dbgWriteHex(s)					// not in debug mode...
#	define dbgWriteOct(s)					// not in debug mode...
#	define dbgWriteBin(s)					// not in debug mode...
#	define dbgShowVar(v)					// not in debug mode...
#endif
#if defined(EXTREMEDEBUG)
#	define dbeWrite(s)						{ ktmWrite(s); errSetDebugInfo(__LINE__, __FILE__); }
#	define dbeWriteChar(c)					{ ktmWriteChar(c); errSetDebugInfo(__LINE__, __FILE__); }
#	define dbeWriteDec(s)					{ ktmWriteDec(s); errSetDebugInfo(__LINE__, __FILE__); }
#	define dbeWriteHex(s)					{ ktmWriteHex(s); errSetDebugInfo(__LINE__, __FILE__); }
#	define dbeWriteOct(s)					{ ktmWriteOct(s); errSetDebugInfo(__LINE__, __FILE__); }
#	define dbeWriteBin(s)					{ ktmWriteBin(s); errSetDebugInfo(__LINE__, __FILE__); }
#	define dbeShowVar(v)					{\
												ktmWrite(#v " = ");\
												ktmWriteDec((s32)(v));\
												ktmWrite(" (");\
												ktmWriteHex((u32)(v));\
												ktmWrite(")\n");\
												errSetDebugInfo(__LINE__, __FILE__);\
											}
#else
#	define dbeWrite(s)						// not in extreme debug mode...
#	define dbeWriteChar(c)					// not in extreme debug mode...
#	define dbeWriteDec(s)					// not in extreme debug mode...
#	define dbeWriteHex(s)					// not in extreme debug mode...
#	define dbeWriteOct(s)					// not in extreme debug mode...
#	define dbeWriteBin(s)					// not in extreme debug mode...
#	define dbeShowVar(v)					// not in extreme debug mode...
#endif
#define dbgWriteVar(v)						dbgShowVar(v)
#define dbeWriteVar(v)						dbeShowVar(v)

/* Typedefs (Pre Structures) */
// Callback function for when an item is deleted in a linked list
typedef void (*deleteListItem_t) (void* pObj);
typedef deleteListItem_t deleteFunc_t;
// Sorting callback
typedef bool_t (*sortFunc_t) (void* pA, void* pB);
// Handle types
typedef struct sHandle_t* handle_t;
typedef handle_t file_t; // more handle types here

/* Structures */
// Linked list base
typedef struct sList_t
{
	struct sListItem_t	*pFirst, *pLast;
} sList;
// Linked list item
typedef struct sListItem_t
{
	void*				pObj;
	deleteListItem_t	pfnDelete;
	struct sListItem_t	*pPrev, *pNext;
} sListItem;
// Multiboot structure
typedef struct sMultiBoot_t
{
	u32										flags;
	u32										memLower;
	u32										memUpper;
	u32										bootDevice;
	u32										cmdline;
	u32										numMods;
	u32*									pMods;
	u32										num;
	u32										size;
	u32										addr;
	u32										shndx;
	u32										mmapLength;
	u32										mmapAddr;
	u32										drivesLength;
	u32										drivesAddr;
	u32										configTable;
	u32										bootLoaderName;
	u32										apmTable;
	u32										vbeControlInfo;
	u32										vbeModeInfo;
	u32										vbeMode;
	u32										vbeInterfaceSeg;
	u32										vbeInterfaceOff;
	u32										vbeInterfaceLen;
} __attribute__((packed)) sMultiBoot;
// Kernel initialization structure
typedef struct sKernelInit_t
{
	sMultiBoot*								pMultiBoot;
	u32										kernelStack;
	u32										kernelStackBase;
	u32										mode;
} __attribute__((packed)) sKernelInit;
// Screen character
typedef struct sScrChar_t
{
	u8										c;	// Character
	u8										a;	// Attribute
} __attribute__((packed)) sScrChar;
// GDT Entry
typedef	struct sGdtEntry_t
{
	u16										limitLow;
	u16										baseLow;
	u8										baseMiddle;
	u8										access;
	u8										granularity;
	u8										baseHigh;
} __attribute__((packed)) sGdtEntry;
// GDT Pointer
typedef	struct sGdtPtr_t
{
	u16										limit;
	u32										base;
} __attribute__((packed)) sGdtPtr;
// IDT Entry
typedef struct sIdtEntry_t
{
	u16										baseLow;
	u16										selector;
	u8										poison;
	u8										flags;
	u16										baseHigh;
} __attribute__((packed)) sIdtEntry;
// IDT Pointer
typedef struct sIdtPtr_t
{
	u16										limit;
	u32										base;
} __attribute__((packed)) sIdtPtr;
// Registers
typedef struct sRegs_t
{
	u32										gs, fs, es, ds;
	u32										edi, esi, ebp, esp, ebx, edx, ecx, eax;
	u32										intNum, errCode;
	u32										eip, cs, eflags, useResp, ss;
} __attribute__((packed)) sRegs;
// Page table entry (page)
typedef struct sPage_t
{
	unsigned			flags : 9;			// Flags
	unsigned			avail : 3;			// Available for the kernel's use
	unsigned			baseAddr : 20;		// Page base address
} __attribute__((packed)) sPage;
// An actual page table
typedef struct sPageTable_t
{
	sPage				pages[1024];
} __attribute__((packed)) sPageTable;
// Page directory
typedef struct sPageDir_t
{
	sPageTable*			pEntries[1024];		// Page table pointers
	// end-of-page-table-page (0x1000 bytes)
	u32					physEntries[1024];	// Physical addresses of the page tables
	u32					physAddr;			// Physical address of the page directory
} __attribute__((packed)) sPageDir;
// Ordered array
typedef struct sOrderedArray_t
{
	void**									ppArray;
	u32										size;
	u32										maxSize;
	sortFunc_t								pfnLessThan;
} sOrderedArray;
// The heap
typedef struct sHeap_t
{
	u32										magic; // HEAP_MAGIC
	sOrderedArray							index;
	u32										flags; // HF_*
	u32										baseAddr;
	u32										size;
	u32										maxSize;
} __attribute__((packed)) sHeap;
// An entry in the heap (header)
typedef struct sHeapEntryHeader_t
{
	u32										magic; // HEAP_ENTRY_OCCUPIED or HEAP_ENTRY_VACANT
	u32										size;
} __attribute__((packed)) sHeapEntryHeader;
// An entry in the heap (footer)
typedef struct sHeapEntryFooter_t
{
	u32										magic; // HEAP_ENTRY_FOOTER
	sHeapEntryHeader*						pHeader;
} __attribute__((packed)) sHeapEntryFooter;
// Individual handle
typedef struct sHandle_t
{
	u32										magic;
	u32										typeValue;
	struct sHandleType_t*					pType;
	u32										refCount;
	void*									pData;
	u32										userData;
	sListItem								item;
} __attribute__((packed)) sHandle;
// Handle type
typedef struct sHandleType_t
{
	u32										typeValue;
	u32										typeSize;
	u32										numHandles;
	sList									handleList;
	sListItem								item;
	void*									pData;				// shared data between all handles of this type
	bool_t (*pfnConstructor) (sHandle* pHandle);
	void (*pfnDestructor) (sHandle* pHandle);
} __attribute__((packed)) sHandleType;
// Type-Descriptor
typedef struct HANDLE_TYPE_DESC_T
{
	u32										typeValue;
	u32										typeSize;
	u32										sharedDataSize;
	bool_t (*pfnConstructor) (sHandle* pHandle);
	void (*pfnDestructor) (sHandle* pHandle);
} HANDLE_TYPE_DESC;
// Virtual file system node
typedef struct sVfsNode_t
{
	char									szName[MAX_PATH];	// name of the file
	sHandle*								pFileHandle;		// handle currently attached to this file
	u32										mask;				// one of the FSM values
	u32										flags;				// a combination of FSF flags
	struct sVfsNode_t*						pMountPoint;		// if this is a mount point, this is a pointer to the mounted node, otherwise null
	u32										uid;				// user identifier
	u32										gid;				// group identifier
	u32										inode;				// node identifier -- reserved for FS driver use
	u32										privateData;		// private data -- reserved for FS driver use
	struct sVfsNode_t*						pParent;			// parent node
	sList									dirList;			// list of opened subnodes to this node
	sListItem								item;				// list item to parent
	bool_t (*pfnOpen) (struct sVfsNode_t* pNode, u32 flags);
	bool_t (*pfnClose) (struct sVfsNode_t* pNode);
	u32 (*pfnWrite) (struct sVfsNode_t* pNode, void* pData, u32 dataLen);
	u32 (*pfnRead) (struct sVfsNode_t* pNode, void* pData, u32 dataLen);
	bool_t (*pfnSeek) (struct sVfsNode_t* pNode, u32 location);
	u32 (*pfnTell) (struct sVfsNode_t* pNode);
	u32 (*pfnGetLength) (struct sVfsNode_t* pNode);
	bool_t (*pfnReadDir) (struct sVfsNode_t* pNode); // causes dirList to repopulate
	struct sVfsNode_t* (*pfnFindDir) (struct sVfsNode_t* pNode, char* pszFileName); // check to see if pszPath specifies a file from here
	struct sVfsNode_t* (*pfnCreateFile) (struct sVfsNode_t* pNode, char* pszFileName, u32 mask, u32 flags);
	void (*pfnDelete) (struct sVfsNode_t* pNode);
} sVfsNode;
// File system descriptor
typedef struct sFileSystem_t
{
	// TODO: Add stuff
	bool_t									readOnly;			// is this a read-only fs?
	u32										uid;				// default user identifier
	u32										gid;				// default group identifier
	u32										privateData;		// private data for the file system
	bool_t (*pfnOpen) (sVfsNode* pNode, u32 flags);
	bool_t (*pfnClose) (sVfsNode* pNode);
	u32 (*pfnWrite) (sVfsNode* pNode, void* pData, u32 dataLen);
	u32 (*pfnRead) (sVfsNode* pNode, void* pData, u32 dataLen);
	bool_t (*pfnSeek) (sVfsNode* pNode, u32 location);
	u32 (*pfnTell) (sVfsNode* pNode);
	u32 (*pfnGetLength) (sVfsNode* pNode);
	bool_t (*pfnReadDir) (sVfsNode* pNode);
	sVfsNode* (*pfnFindDir) (sVfsNode* pNode, char* pszFileName);
	sVfsNode* (*pfnCreateFile) (sVfsNode* pNode, char* pszFileName, u32 mask, u32 flags);
	void (*pfnDelete) (sVfsNode* pNode);
} sFileSystem;
// Path information structure
typedef struct PATH_INFO_T
{
	sVfsNode*								pDirNode;
	sVfsNode*								pFileNode;
	char									szDirName[MAX_PATH];
	char									szFileName[MAX_PATH];
} PATH_INFO;
// A task (process or thread)
typedef struct sTask_t
{
	struct sTask_t*						pProcess;	// parent process
	sVfsNode*								pCurDir;	// current directory for file access
	u32										pid;		// process identifier
	u32										priority;	// the process' priority
	sPageDir*								pPgDir;		// process page directory
	sHeap*									pDefHeap;	// default heap for process
	u32										esp, ebp;	// ESP and EBP regs
	u32										eip;		// EIP reg
	sListItem								item;		// list item for process list
} sTask;

/* Typedefs (Post Structs) */
// ISR handler callback
typedef void (*isr_t) (sRegs* pRegs);

/* Functions */
// Text mode screen
SYS_FUNC void SYS_API ktmScrollScreen(void);
SYS_FUNC void SYS_API ktmWriteChar(char c);
SYS_FUNC void SYS_API ktmWrite(const char* pszText);
SYS_FUNC void SYS_API ktmWriteDec(s32 d);
SYS_FUNC void SYS_API ktmWriteHex(u32 h);
SYS_FUNC void SYS_API ktmWriteOct(u32 o);
SYS_FUNC void SYS_API ktmWriteBin(u32 b);
SYS_FUNC void SYS_API ktmClear(void);
SYS_FUNC void SYS_API ktmSetColor(u8 fg, u8 bg);
SYS_FUNC void SYS_API ktmSetCursorPos(u16 x, u16 y);
SYS_FUNC void SYS_API ktmGetCursorPos(u16* pX, u16* pY);
SYS_FUNC u16 SYS_API ktmGetCursorX(void);
SYS_FUNC u16 SYS_API ktmGetCursorY(void);
SYS_FUNC u8 SYS_API ktmGetColors(void);
// Global descriptor table
ASM_FUNC void ASM_API gdtInit(void);
// Interrupt descriptor table
SYS_FUNC void SYS_API idtSetGate(u32 index, u32 base, u16 selector, u8 flags);
SYS_FUNC void SYS_API idtInit(void);
ASM_FUNC void ASM_API idtFlush(void);
// Interrupt service routines
SYS_FUNC void SYS_API isrInit(void);
SYS_FUNC void ASM_API isrFaultHandler(sRegs* pRegs);
SYS_FUNC void SYS_API isrSetHandler(u32 index, isr_t pfnHandlerCallback);
// Interrupt request routines
ASM_FUNC void ASM_API irqRemap(void);
SYS_FUNC void SYS_API irqInit(void);
SYS_FUNC void SYS_API irqSetHandler(u32 index, isr_t pfnHandlerCallback);
SYS_FUNC void ASM_API irqFaultHandler(sRegs* pRegs);
// Timer
SYS_FUNC void ASM_API timIrqHandler(sRegs* pRegs);
SYS_FUNC void SYS_API timSetPhase(u32 hz);
SYS_FUNC void SYS_API timInit(void);
// Memory manager
SYS_FUNC void* SYS_API mmKrnlAlloc(u32 size);
SYS_FUNC void* SYS_API mmKrnlAllocEx(u32 size, bool_t align, void** ppPhysAddr);
SYS_FUNC sHeap* SYS_API mmCreateHeap(u32 start, u32 size, u32 maxSize, u32 flags);
SYS_FUNC void* SYS_API mmAlloc(sHeap* pHeap, u32 flags, u32 size);
SYS_FUNC bool_t SYS_API mmIsPointerValidEx(void* pPtr, sHeapEntryHeader** ppOutHeader, sHeapEntryFooter** ppOutFooter);
#define mmIsPointerValid(p) mmIsPointerValidEx(p,(sHeapEntryHeader**)0,(sHeapEntryFooter**)0)
SYS_FUNC void SYS_API mmFree(sHeap* pHeap, void* pPtr);
#define mmKrnlAllocType(t) ((t*)mmKrnlAlloc(sizeof(t)))
#define mmKrnlAllocTypeEx(t,a,p) ((t*)mmKrnlAllocEx(sizeof(t),(a),(void**)(p)))
#define mmAllocType(t,h,f) ((t*)mmAlloc((h),(f),sizeof(t)))
// Page frame allocator
SYS_FUNC bool_t SYS_API pfaInitFrames(u32 numFrames);
SYS_FUNC u32 SYS_API pfaFindFreeFrame(void);
SYS_FUNC u32 SYS_API pfaAllocFrame(void);
SYS_FUNC void SYS_API pfaFreeFrame(u32 frame);
SYS_FUNC bool_t SYS_API pfaFrameExist(u32 frame);
// Paging
SYS_FUNC void SYS_API pgAllocFrame(sPage* pPage, bool_t user, bool_t rw);
SYS_FUNC void SYS_API pgFreeFrame(sPage* pPage);
SYS_FUNC sPage* SYS_API pgGetPage(sPageDir* pDir, u32 addr, bool_t make);
SYS_FUNC sPageDir* SYS_API pgCloneDir(sPageDir* pSrc);
ASM_FUNC void ASM_API pgCopyPage(u32 a, u32 b);
SYS_FUNC void SYS_API pgInit(void);
SYS_FUNC void ASM_API pgFaultHandler(sRegs* pRegs);
SYS_FUNC void SYS_API pgSwitchDir(sPageDir* pDir);
// Errors
SYS_FUNC void SYS_API errSetErrorEx(u32 error, char* pszErrorMsg, char* pszFile, u32 line);
#define errSetError(err,msg) errSetErrorEx((err),(msg),__FILE__,__LINE__)
SYS_FUNC u32 SYS_API errGetError(void);
SYS_FUNC char* SYS_API errGetErrorMsg(char* pszOutBuff, u32 buffSize);
SYS_FUNC void SYS_API errKernelPanic(char* pszFile, u32 line, char* pszExpression, char* pszDetails);
SYS_FUNC void SYS_API errShowInfo(void);
SYS_FUNC void SYS_API errSetDebugInfo(u32 debugValue, char* pszDebugString);
// Ordered array
SYS_FUNC bool_t SYS_API oaCreate(sOrderedArray* pArray, u32 maxSize, sortFunc_t pfnLessThan);
SYS_FUNC bool_t SYS_API oaCreateEx(sOrderedArray* pArray, void* pAddr, u32 maxSize, sortFunc_t pfnLessThan);
SYS_FUNC void SYS_API oaFree(sOrderedArray* pArray);
SYS_FUNC bool_t SYS_API oaInsert(sOrderedArray* pArray, void* pItem);
SYS_FUNC void SYS_API oaRemove(sOrderedArray* pArray, u32 i);
SYS_FUNC void* SYS_API oaGet(sOrderedArray* pArray, u32 i);
// Virtual file system
SYS_FUNC void SYS_API vfsInit(void);
SYS_FUNC sVfsNode* SYS_API vfsAddVirtualNode(sVfsNode* pParent, char* pszName);
SYS_FUNC sVfsNode* SYS_API vfsAddDevice(char* pszName, sFileSystem* pFsDesc, u32 mask);
SYS_FUNC sVfsNode* SYS_API vfsMount(char* pszName, sVfsNode* pDevice);
SYS_FUNC sVfsNode* SYS_API vfsAddFile(sVfsNode* pParent, char* pszName, u32 mask, u32 flags);
SYS_FUNC bool_t SYS_API vfsOpen(sVfsNode* pNode, u32 flags);
SYS_FUNC bool_t SYS_API vfsClose(sVfsNode* pNode);
SYS_FUNC u32 SYS_API vfsWrite(sVfsNode* pNode, void* pData, u32 dataLen);
SYS_FUNC u32 SYS_API vfsRead(sVfsNode* pNode, void* pData, u32 dataLen);
SYS_FUNC bool_t SYS_API vfsSeek(sVfsNode* pNode, u32 location);
SYS_FUNC u32 SYS_API vfsTell(sVfsNode* pNode);
SYS_FUNC u32 SYS_API vfsGetLength(sVfsNode* pNode);
SYS_FUNC bool_t SYS_API vfsReadDir(sVfsNode* pNode);
SYS_FUNC sVfsNode* SYS_API vfsFindDir(sVfsNode* pNode, char* pszFileName);
SYS_FUNC sVfsNode* SYS_API vfsCreateFile(sVfsNode* pNode, char* pszFileName, u32 mask, u32 flags);
SYS_FUNC void SYS_API vfsDelete(sVfsNode* pNode);
SYS_FUNC sVfsNode* SYS_API vfsFindPathNode(char* pszName);
// Object system
SYS_FUNC void SYS_API obInit(void);
SYS_FUNC bool_t SYS_API obIsTypeValid(u32 typeValue);
SYS_FUNC bool_t SYS_API obMakeType(HANDLE_TYPE_DESC* pDesc);
SYS_FUNC sHandle* SYS_API obAlloc(u32 typeValue);
SYS_FUNC bool_t SYS_API obIsValid(sHandle* pHandle);
SYS_FUNC bool_t SYS_API obIsType(sHandle* pHandle, u32 typeValue);
SYS_FUNC void SYS_API obCapture(sHandle* pHandle);
SYS_FUNC bool_t SYS_API obRelease(sHandle* pHandle);
SYS_FUNC void SYS_API obSetUserData(sHandle* pHandle, u32 userData);
SYS_FUNC u32 SYS_API obGetUserData(sHandle* pHandle, u32 userData);
// The ramdisk file system
SYS_FUNC void SYS_API ramfsInit(void);
// The initial ramdisk
SYS_FUNC bool_t SYS_API irdInit(u32 addr);
// File system API
/*
SYS_FUNC bool_t SYS_API fsInit(void);
SYS_FUNC sHandle* SYS_API fsOpen(char* pszFileName, u32 flags);
SYS_FUNC void SYS_API fsClose(sHandle* pHandle);
SYS_FUNC u32 SYS_API fsWrite(sHandle* pHandle, void* pData, u32 dataLen);
SYS_FUNC u32 SYS_API fsRead(sHandle* pHandle, void* pData, u32 dataLen);
SYS_FUNC bool_t SYS_API fsSeek(sHandle* pHandle, u32 location, u32 rel);
SYS_FUNC u32 SYS_API fsTell(sHandle* pHandle);
SYS_FUNC bool_t SYS_API fsEndOfFile(sHandle* pHandle);
SYS_FUNC void SYS_API fsDeleteFile(char* pszFileName);
SYS_FUNC bool_t SYS_API fsCopyFile(char* pszSrc, char* pszDest, bool_t overwrite);
SYS_FUNC bool_t SYS_API fsMoveFile(char* pszSrc, char* pszDest, bool_t overwrite);
*/
// Multitasking
ASM_FUNC u32 ASM_API kGetEip(void);
SYS_FUNC void SYS_API tsMoveStack(void);
SYS_FUNC bool_t SYS_API tsInit(void);
SYS_FUNC u32 SYS_API tsFork(void);
SYS_FUNC void SYS_API tsSwitchTask(void);
// External ISRs
ASM_FUNC void ASM_API isr0(void);
ASM_FUNC void ASM_API isr1(void);
ASM_FUNC void ASM_API isr2(void);
ASM_FUNC void ASM_API isr3(void);
ASM_FUNC void ASM_API isr4(void);
ASM_FUNC void ASM_API isr5(void);
ASM_FUNC void ASM_API isr6(void);
ASM_FUNC void ASM_API isr7(void);
ASM_FUNC void ASM_API isr8(void);
ASM_FUNC void ASM_API isr9(void);
ASM_FUNC void ASM_API isr10(void);
ASM_FUNC void ASM_API isr11(void);
ASM_FUNC void ASM_API isr12(void);
ASM_FUNC void ASM_API isr13(void);
ASM_FUNC void ASM_API isr14(void);
ASM_FUNC void ASM_API isr15(void);
ASM_FUNC void ASM_API isr16(void);
ASM_FUNC void ASM_API isr17(void);
ASM_FUNC void ASM_API isr18(void);
ASM_FUNC void ASM_API isr19(void);
ASM_FUNC void ASM_API isr20(void);
ASM_FUNC void ASM_API isr21(void);
ASM_FUNC void ASM_API isr22(void);
ASM_FUNC void ASM_API isr23(void);
ASM_FUNC void ASM_API isr24(void);
ASM_FUNC void ASM_API isr25(void);
ASM_FUNC void ASM_API isr26(void);
ASM_FUNC void ASM_API isr27(void);
ASM_FUNC void ASM_API isr28(void);
ASM_FUNC void ASM_API isr29(void);
ASM_FUNC void ASM_API isr30(void);
ASM_FUNC void ASM_API isr31(void);
// External IRQs
ASM_FUNC void ASM_API irq0(void);
ASM_FUNC void ASM_API irq1(void);
ASM_FUNC void ASM_API irq2(void);
ASM_FUNC void ASM_API irq3(void);
ASM_FUNC void ASM_API irq4(void);
ASM_FUNC void ASM_API irq5(void);
ASM_FUNC void ASM_API irq6(void);
ASM_FUNC void ASM_API irq7(void);
ASM_FUNC void ASM_API irq8(void);
ASM_FUNC void ASM_API irq9(void);
ASM_FUNC void ASM_API irq10(void);
ASM_FUNC void ASM_API irq11(void);
ASM_FUNC void ASM_API irq12(void);
ASM_FUNC void ASM_API irq13(void);
ASM_FUNC void ASM_API irq14(void);
ASM_FUNC void ASM_API irq15(void);

/* Inlined Functions */
// Read a byte from a port
INLINE u8 INT_API kInB(u16 port)
{
	u8	ret = 0;
	asm volatile("in al, dx" : "=a" (ret) : "d" (port));
	return	ret;
}
// Write a byte to a port
INLINE void INT_API kOutB(u16 port, u8 value)
{
	asm volatile("out dx, al" :: "d" (port), "a" (value));
}
// Read a word from a port
INLINE u16 INT_API kInW(u16 port)
{
	u16	ret = 0;
	asm volatile("in ax, dx" : "=a" (ret) : "d" (port));
	return	ret;
}
// Write a word to a port
INLINE void INT_API kOutW(u16 port, u16 value)
{
	asm volatile("out dx, ax" :: "d" (port), "a" (value));
}
/*
	TODO: Convert kMemSet to an assembly function
	TODO: Convert kMemCpy to an assembly function
	TODO: Convert kStrLen to an assembly function
	TODO: Add a kMemCmp function
	TODO: Add a kToUpper macro
	TODO: Add a kToLower macro
	TODO: Add a kIsDigit macro
	TODO: Add a kIsAlpha macro
	TODO: Add a kIsAlphaNum macro
	TODO: Add a kStrCaseCmp function (Ignores case)
*/
// Set a block of memory to a specific byte
INLINE void INT_API kMemSet(u8* pDest, u8 value, u32 size)
{
	u32 i;
	for(i = 0; i < size; i++)
		pDest[i] = value;
}
// Copy one block of memory to another
INLINE void INT_API kMemCpy(u8* pDest, u8* pSrc, u32 size)
{
	u32 i;
	for(i = 0; i < size; i++)
		pDest[i] = pSrc[i];
}
// Find the length of a string
INLINE u32 INT_API kStrLen(char* pszString)
{
	if (!pszString)
		return	0;
	char* pszDelta = pszString;
	while(*pszString)
		pszString++;
	return	(u32)(pszString-pszDelta);
}
// Copy a string into another string
INLINE char* INT_API kStrCpy(char* pszDest, char* pszSrc)
{
	u32 i = 0;
	if (!pszSrc[0])
	{
		pszDest[0] = 0;
		return pszDest;
	}
	while(pszSrc[i])
	{
		pszDest[i] = pszSrc[i];
		i++;
	}
	pszDest[i] = 0;
	return pszDest;
}
// Copy a string into another string -- extended version
INLINE char* INT_API kStrCpyEx(char* pszDest, char* pszSrc, u32 maxLen)
{
	u32 i = 0;
	if (!pszSrc[0])
	{
		pszDest[0] = 0;
		return pszDest;
	}
	while(pszSrc[i] && i < maxLen)
	{
		pszDest[i] = pszSrc[i];
		i++;
	}
	pszDest[i] = 0;
	return pszDest;
}
// Add to a string
INLINE char* INT_API kStrCat(char* pszDest, char* pszSrc)
{
	u32 i = 0, l = kStrLen(pszDest);
	if (!kStrCpy(&pszDest[l], pszSrc))
		return (char*)0;
	return pszDest;
}
// Add a certain amount of characters to the string
INLINE char* INT_API kStrCatEx(char* pszDest, char* pszSrc, u32 maxLen)
{
	u32 i = 0, l = kStrLen(pszDest);
	if (!kStrCpyEx(&pszDest[l], pszSrc, maxLen-l))
		return (char*)0;
	return pszDest;
}
// Compare one string to another string
INLINE s32 INT_API kStrCmp(char* pszTestA, char* pszTestB)
{
	u32	i = 0;
	s32	delta = 0;
	// Test for stupid occurrances
	if (pszTestA == pszTestB)
		return 0;
	// Test if either pszTestA or pszTestB is null
	if (!pszTestA || !pszTestB)
		return 1;
	// Compare the strings
	while(pszTestA[i] && pszTestB[i])
	{
		delta = delta + (pszTestA[i]-pszTestB[i]);
		i++;
	}
	// Check if either of these strings are different
	delta = delta + (pszTestA[i]-pszTestB[i]);
	// Return the delta
	return delta;
}
// Find the address of a single character in a string
INLINE char* INT_API kStrChr(char* pszTest, char c)
{
	while(*pszTest)
	{
		if (*pszTest == c)
			return pszTest;
		pszTest++;
	}
	return (char*)0;
}
// Find the address of a single character from a string in a string
INLINE char* INT_API kStrBrk(char* pszTest, char* pszDelims)
{
	u32 i;
	while(*pszTest)
	{
		for(i = 0; pszDelims[i]; i++)
		{
			if (*pszTest == pszDelims[i])
				return pszTest;
		}
		pszTest++;
	}
	return (char*)0;
}
// Signed integer to string
INLINE char* INT_API kSiToStr(s32 value, char* pszString, u32 radix, bool_t upper, char** ppRsvd)
{
	// Preliminary
	static	char* pszUpperDigits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
	static	char* pszLowerDigits = "0123456789abcdefghijklmnopqrstuvwxyz";
	char*	pszTmp = pszString;
	char*	pszDigits = (upper > 0) ? pszUpperDigits : pszLowerDigits;
	// Check for a negative
	if (value < 0)
	{
		// Write the negative character
		*pszString = '-';
		pszString++;
		// Convert the value to positive
		value = (-value);
	}
	// Do a recursive loop until we're at a single digit
	if (value >= radix)
	{
		// Iterate
		kSiToStr(value/radix, pszString, radix, upper, &pszString);
		value = value % radix;
	}
	// Set the value
	*pszString = pszDigits[value];
	pszString++;
	*pszString = 0;
	// Check the reserved value
	if (ppRsvd)
		*ppRsvd = pszString;
	// Return the string
	return	pszTmp;
}
// Unsigned integer to string
INLINE char* INT_API kUiToStr(u32 value, char* pszString, u32 radix, bool_t upper, char** ppRsvd)
{
	// Preliminary
	static	char* pszUpperDigits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
	static	char* pszLowerDigits = "0123456789abcdefghijklmnopqrstuvwxyz";
	char*	pszTmp = pszString;
	char*	pszDigits = (upper > 0) ? pszUpperDigits : pszLowerDigits;
	// Do a recursive loop until we're at a single digit
	if (value >= radix)
	{
		// Iterate
		kSiToStr(value/radix, pszString, radix, upper, &pszString);
		value = value % radix;
	}
	// Set the value
	*pszString = pszDigits[value];
	pszString++;
	*pszString = 0;
	// Check the reserved value
	if (ppRsvd)
		*ppRsvd = pszString;
	// Return the string
	return	pszTmp;
}
// Initialize a linked list
INLINE void INT_API lsInit(sList* pList)
{
	pList->pFirst = pList->pLast = (sListItem*)0;
}
// Initialize a list item
INLINE void INT_API lsInitItem(sListItem* pItem, void* pObj, deleteListItem_t pfnDelFunc)
{
	pItem->pObj = pObj;
	pItem->pfnDelete = pfnDelFunc;
	pItem->pPrev = (sListItem*)0;
	pItem->pNext = (sListItem*)0;
}
// Add an item to a list
INLINE void INT_API lsAddItem(sList* pList, sListItem* pItem)
{
	if (pList->pLast)
		pList->pLast->pNext = pItem;
	pItem->pPrev = pList->pLast;
	pList->pLast = pItem;
	if (!pList->pFirst)
		pList->pFirst = pItem;
}
// Insert an item before another item
INLINE void INT_API lsInsertBefore(sList* pList, sListItem* pNew, sListItem* pOld)
{
	if (pOld->pPrev)
		pOld->pPrev->pNext = pNew;
	pNew->pPrev = pOld->pPrev;
	pNew->pNext = pOld;
	pOld->pPrev = pNew;
	if (pOld == pList->pFirst)
		pList->pFirst = pNew;
}
// Insert an item after another item
INLINE void INT_API lsInsertAfter(sList* pList, sListItem* pNew, sListItem* pOld)
{
	if (pOld->pNext)
		pOld->pNext->pPrev = pNew;
	pNew->pNext = pOld->pNext;
	pNew->pPrev = pOld;
	pOld->pNext = pNew;
	if (pOld == pList->pLast)
		pList->pLast = pNew;
}
// Remove an item from the list
INLINE void INT_API lsRemove(sList* pList, sListItem* pItem)
{
	if (pItem->pPrev)
		pItem->pPrev->pNext = pItem->pNext;
	if (pItem->pNext)
		pItem->pNext->pPrev = pItem->pPrev;
	if (pItem == pList->pFirst)
		pList->pFirst = pItem->pNext;
	if (pItem == pList->pLast)
		pList->pLast = pItem->pPrev;
	pItem->pPrev = pItem->pNext = (sListItem*)0;
	if (pItem->pfnDelete)
		pItem->pfnDelete(pItem->pObj);
}
// Remove all items
INLINE void INT_API lsRemoveAll(sList* pList)
{
	while(pList->pFirst)
		lsRemove(pList, pList->pFirst);
}
// Retrieve the first object in a list
INLINE void* INT_API lsFirst(sList* pList)
{
	return (pList->pFirst) ? pList->pFirst->pObj : (void*)0;
}
// Retrieve the last object in a list
INLINE void* INT_API lsLast(sList* pList)
{
	return (pList->pLast) ? pList->pLast->pObj : (void*)0;
}
// Retrieve the previous object in a list
INLINE void* INT_API lsBefore(sList* pList, sListItem* pItem)
{
	return (pItem->pPrev) ? pItem->pPrev->pObj : (void*)0;
}
// Retrieve the next object in a list
INLINE void* INT_API lsAfter(sList* pList, sListItem* pItem)
{
	return (pItem->pNext) ? pItem->pNext->pObj : (void*)0;
}

#endif

dragon.h

Code: Select all

#ifndef DRAGON_H
#define DRAGON_H

/* Defines */
// Compilers
#define C_GCC								100
#define C_MSVC								200
// Architectures
#define A_I386								100
#define A_IA64								110
#define A_PPC								200
#define A_ARM								300
// C language version
#define CV_C								100	// C : .c
#define CV_CPP								200	// C++ : .cpp
#define CV_CS								300	// C# (C-Sharp) : .cs
// Is C in use, or is C++ in use?
#if defined(__cplusplus) || defined(cplusplus)
#	define USING_CPP
#	define CVER								CV_CPP
#else
#	define USING_C
#	define CVER								CV_C
#endif
// Determine which compiler is in use
#if defined(__GNUC__) || defined(__MINGW32_VERSION)
#	define USING_GCC
#	define COMPILER							C_GCC
#elif defined(_MSC_VER)
#	define USING_MSVC
#	define COMPILER							C_MSVC
#endif
// Determine which architecture is in use
#if defined(__i386__) || defined(_M_IX86)
#	define USING_I386
#	define ARCH								A_I386
#	define BITS								32
#elif defined(__amd64__) || defined(__ia64__) || defined(_M_IA64)
#	define USING_IA64
#	define ARCH								A_IA64
#	define BITS								64
#elif defined(__powerpc__) || defined(_M_PPC) || defined(__PPC__)
#	define USING_PPC
#	define ARCH								A_PPC
#	define BITS								32
#elif defined(__arm__) || defined(_M_ARM) || defined(__ARM__)
#	define USING_ARM
#	define ARCH								A_ARM
#	define BITS								32
#endif
// Booleans
#define B_TRUE								1
#define B_FALSE								0
// Results
#define R_SUCCESS							1		// Generic success
#define R_FAILURE							0		// Generic failure
#define R_NO_MEMORY							(-1)	// Not enough memory to complete call
#define R_INVALID_CALL						(-2)	// One of the arguments to the function was invalid
#define R_INTERNAL_ERROR					(-3)	// Internal error occured
#define R_NOT_INITIALIZED					(-4)	// One of the components that the function requires wasn't initialized properly
#define R_BAD_MEMORY						(-5)	// A bad memory location was passed in
#define R_INVALID_FILE						(-6)	// Either the file/stream requested doesn't exist or isn't of the right type
// Invalid values
#define INVALID_INDEX_VALUE					0xFFFFFFFF
// Inline
#if CVER == CV_CPP || CVER == CV_CS
#	if COMPILER == C_MSVC
#		define INLINE						__forceinline
#	else
#		define INLINE						inline
#	endif
#else
#	define INLINE							static
#endif
// Function modifiers
#if CVER == CV_CPP || CVER == CV_CS
#	define SYS_FUNC							extern "C"
#else
#	define SYS_FUNC							extern
#endif
#if COMPILER == C_GCC
#	define SYS_API							__attribute__((stdcall))
#else
#	define SYS_API							__stdcall
#endif
#define INT_FUNC							static
#if COMPILER == C_GCC
#	define INT_API							__attribute__((fastcall))
#else
#	define INT_API							__fastcall
#endif
#define ASM_FUNC							SYS_FUNC
#define	ASM_API
// OS limits
#define MAX_PATH							256 // includes null character
// Handle types
#define HT_FILE								0xF0324CC8
// Error codes
#define EC_NONE								0
#define EC_NO_MEMORY						1
#define EC_INVALID_CALL						2
#define EC_INTERNAL_ERROR					3
#define EC_NOT_INITIALIZED					4
#define EC_BAD_MEMORY						5
#define EC_INVALID_FILE						6
// Task error codes
#define TS_EC_CANT_ALLOC_TASK				0x100
#define TS_EC_CANT_ALLOC_SHARED_HEAP		0x101
// Error code messages
#define ES_NONE								"No error, or unknown error."
#define ES_NO_MEMORY						"Not enough memory for call."
#define ES_INVALID_CALL						"Bad parameter (invalid pointer?)"
#define ES_NOT_INITIALIZED					"The component hasn't been initialized."
#define ES_BAD_MEMORY						"'Bad' memory."
#define ES_INVALID_FILE						"The referenced file doesn't exist or is inaccessible."
// Task error messages
#define TS_ES_CANT_ALLOC_TASK				"Unable to allocate memory for the task."
#define TS_ES_CANT_ALLOC_SHARED_HEAP		"Unable to allocate memory for the shared heap."

/* Macros */
// Bit Management
#define Bits8To16(a,b)						((u16)(((u8)(a))|(((u16)((u8)(b)))<<8)))
#define Bits16To32(a,b)						((u32)(((u16)(a))|(((u32)((u16)(b)))<<16)))
#define Bits8To32(a,b,c,d)					(Bits16To32(Bits8To16(a,b),Bits8To16(c,d)))
#define Bits32To16L(a)						((u16)((u32)(a)))
#define Bits32To16H(a)						((u16)(((u32)(a)>>16)&0xFFFF))
#define Bits16To8L(a)						((u8)(a))
#define Bits16To8H(a)						((u8)(((u16)(a)>>8)&0xFF))
#define Bits32To8LL(a)						(Bits16To8L(Bits32To16L(a)))
#define Bits32To8LH(a)						(Bits16To8L(Bits32To16H(a)))
#define Bits32To8HL(a)						(Bits16To8H(Bits32To16L(a)))
#define Bits32To8HH(a)						(Bits16To8H(Bits32To16H(a)))
// Bit Management (Windows Equivalents)
#define LOWORD(a)							(Bits32To16L(a))
#define HIWORD(a)							(Bits32To16H(a))
#define LOBYTE(a)							(Bits16To8L(a))
#define HIBYTE(a)							(Bits16To8H(a))
// Flags & Bits
#define MakeFlag(id)						(((u32)1)<<(id))
#define ClearBit(value,bit)					(value &= ~(1<<bit))
#define SetBit(value,bit)					(value |= (1<<bit))
#define GetBit(value,bit)					(value & (1<<bit))

/* Typedefs */
// 32-bit processors
#if BITS == 32
typedef unsigned char						u8;
typedef unsigned short						u16;
typedef unsigned int						u32;
typedef signed char							s8;
typedef signed short						s16;
typedef signed int							s32;
typedef unsigned long long int				u64;
typedef signed long long int				s64;
// 64-bit processors
#elif BITS == 64 // redundant here, I suppose
typedef unsigned char						u8;
typedef unsigned short						u16;
typedef unsigned int						u32;
typedef signed char							s8;
typedef signed short						s16;
typedef signed int							s32;
typedef unsigned long long int				u64; // should just be 'long'?
typedef signed long long int				s64; // should just be 'long'?
#endif
// OS types
typedef s32									result_t;	// One of the 'R_' values
typedef u32									bool_t;		// One of the 'B_' values
typedef u32									bitfield_t;	// Use the flag macros on this type (ClearBit, SetBit, GetBit)
// Character mode
#if defined(UNICODE) || defined(_UNICODE)
typedef short								char_t, tchar;
#else
typedef char								char_t, tchar;
#endif
// Handles
#if !defined(sObject_defined)
typedef struct sHandle_t { u32 __unused; }	sHandle, *handle_t;
typedef handle_t							file_t;
#endif

#endif


If you need to see more code, or use the build kit I used to compile all this, go to http://code.google.com/p/dragon-os

The current packages would be the dragon-0.05-alpha-unstable and dragonkit


Thanks for your time.

Cheers,

-naota

Re: Multitasking problem

Posted: Thu May 14, 2009 6:25 pm
by NickJohnson
Oookay... that's a lot of code. If you have any ideas about where the issue could be, it would be very helpful to narrow it down a bit. I mean, that is probably more code than my *entire* kernel, although at least it appears well commented.

Also, JamesM's tutorial uses a sort of odd multitasking setup. It is many many times easier, as well as more secure, to make all kernel operations happen on a stack in the kernel. When you switch contexts, the stack stays where it is... you just have to make sure to jump back to the proper user stack location. The tutorial setup is like trying to pull a rug out from under yourself, but this is more like pulling a rug out from under someone else: not trivial, but much less tricky. You can take a look at how my kernel does it if you like: it's in kernel/src/trap/int.s mostly.

EDIT: By a "stack in the kernel", I mean a *separate* stack in the kernel used only for handling interrupts. You can't reuse the stack you use for initialization.

Re: Multitasking problem

Posted: Thu May 14, 2009 6:29 pm
by AaronMiller
task.c :: tsSwitchTask is where the error occurs.
That'd be very helpful, thanks. :)

When I was reading about JamesM's method it seemed like it was a good way to do it. I'll look into other tutorials though for multitasking, thanks. :)

Cheers,

-naota

Re: Multitasking problem

Posted: Thu May 14, 2009 6:57 pm
by NickJohnson
I can't see any immediate problems with that function. However, using that much inline assembly, especially stuff that manipulates the stack (push/pop), makes things pretty hard to debug. It's usually best to try and move all the assembly into the actual interrupt handlers if possible. That's another reason to not use the tutorial's technique - no more inline asm, read_eip(), and especially no more weird semi-recursion and instruction pointer kludges.

If I had to guess, there is probably some sort of mismatch in stack positions, which makes the kernel reload different things into esp, ebp, and eip. That could easily turn into a "heisenbug" - something that would appear or disappear if you use debugging functions or not - because it is messing with the stack.

Re: Multitasking problem

Posted: Thu May 14, 2009 7:24 pm
by AaronMiller
Oh dear, so that's what you call those. :/
Alright, I'll change my method -- start over, read a bit more first, do a bit more research.
Thank you guys so much for your help, it is greatly appreciated. :)


Cheers,

-naota

Re: Multitasking problem

Posted: Fri May 15, 2009 8:11 am
by frank
This is the closest thing I've found to how mine works

http://www.osdever.net/tutorials/multitasking.php

This method completely takes all assembly out of the scheduler and is very easy to setup and use user mode tasks with it.

Re: Multitasking problem

Posted: Fri May 15, 2009 4:38 pm
by AaronMiller
Thanks all. :)

@Frank
I solved the problem now, I followed another tutorial, but I have that one saved to my HD. :)


Cheers,

-naota

Re: Multitasking problem

Posted: Sat May 16, 2009 1:36 am
by ehenkes
JamesM's tutorial uses a sort of odd multitasking setup.
I agree, because I had the same problem. :(
Hence, I completely changed the task switch methodology to the way tyndur is using, and it is understandable now, and it works. :(
http://www.henkessoft.de/OS_Dev/OS_Dev2 ... ocId114565

But I have to admit that by JM's code I really learned how to debug. :D
http://www.henkessoft.de/OS_Dev/OS_Dev2 ... ocId939575

isr.asm - before irq_handler
The trick is that esp is not pushed to the stack, but delivered as an argument to the irq_handler function which takes it away from the stack and delivers the correct next esp back to eax (return value) which is moved to esp.

Code: Select all

irq_common_stub:
   
    push eax
    push ecx
    push edx
    push ebx
    push ebp
    push esi
    push edi
   
    push ds
    push es
    push fs
    push gs

    mov ax, 0x10
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax
   
    push esp              ; parameter of _irq_handler1
    call _irq_handler1    


irq.c - the direct way to the task switch in the interrupt handler

Code: Select all

ULONG irq_handler1(ULONG esp)
{
    ULONG retVal;
    struct regs* r = (struct regs*)esp;

    if(!pODA->ts_flag)
        retVal = esp;
    else
        retVal = task_switch1(esp); //new task's esp

    //...

    return retVal;
}   
task.c - the task_switch: linked list of tasks - switch of esp

Code: Select all

ULONG task_switch1(ULONG esp)
{
    // ...
    current_task->esp = esp;   // save esp
   
    current_task = current_task->next; // task switch
    if(!current_task) current_task = ready_queue;

    // new_task
    current_directory = current_task->page_directory;
    asm volatile("mov %0, %%cr3" : : "r" (current_directory->physicalAddr));

    tss_entry.cr3 = current_directory->physicalAddr;
    tss_entry.esp = current_task->esp;
    tss_entry.esp0 = (current_task->kernel_stack)+KERNEL_STACK_SIZE;

    return current_task->esp;  // return new task's esp
}
isr.asm behind the irq_handler

Code: Select all

global _irq_tail  
_irq_tail:
    mov esp, eax          ; return value: changed or unchanged esp

    pop gs
    pop fs
    pop es
    pop ds
       
    pop edi
    pop esi
    pop ebp
    pop ebx
    pop edx
    pop ecx
    pop eax
   
    add esp, 8
    iret
I found the following text regarding the use of the TSS during software task switch:
When an interrupt happens in protected (32-bit) mode, the x86 CPU will look in the TSS for SS0 and ESP0 and load their values into SS and ESP respectively. This allows for the kernel to use a different stack than the user program, and also have this stack be unique for each user program.
Very important is that you use the upper end of the kernel stack. Thus, you have to add the kernel size to the meory address provided by kmalloc:

Code: Select all

new_task->kernel_stack        = k_malloc(KERNEL_STACK_SIZE,1,0)+KERNEL_STACK_SIZE;
Here is the way I create the task meaning setting up the kernel stack, the eip, esp0 and the code and data segments:

Code: Select all

void create_task(void* entry)
{
    page_directory_t* directory   = clone_directory(current_directory);
    task_t* new_task              = (task_t*)k_malloc(sizeof(task_t),0,0);

    new_task->id                  = next_pid++;
    new_task->page_directory      = directory;
    new_task->kernel_stack        = k_malloc(KERNEL_STACK_SIZE,1,0)+KERNEL_STACK_SIZE;
    new_task->next                = 0;

    task_t* tmp_task = (task_t*)ready_queue;
    while (tmp_task->next)
        tmp_task = tmp_task->next;
    tmp_task->next = new_task;

    ULONG* kernel_stack = (ULONG*) new_task->kernel_stack;

    *(--kernel_stack) = 0x0200;       // eflags = interrupts aktiviert und iopl = 0
    *(--kernel_stack) = 0x08;         // cs
    *(--kernel_stack) = (ULONG)entry; // eip

    *(--kernel_stack) = 0;            // interrupt nummer
    *(--kernel_stack) = 0;            // error code

    // general purpose registers without esp
    *(--kernel_stack) = 0;
    *(--kernel_stack) = 0;
    *(--kernel_stack) = 0;
    *(--kernel_stack) = 0;
    *(--kernel_stack) = 0;
    *(--kernel_stack) = 0;
    *(--kernel_stack) = 0;

    // data segment registers
    *(--kernel_stack) = 0x10;
    *(--kernel_stack) = 0x10;
    *(--kernel_stack) = 0x10;
    *(--kernel_stack) = 0x10;

    new_task->esp = (ULONG)kernel_stack;
    new_task->eip = (ULONG)irq_tail;
}
But one question: Can anybody fully explain this

Code: Select all

add esp, 8
please? I am not sure, if I really understand why it is shifted by two 4-byte values.
Which are these values? I have read that you have to take the error code and IRQ number off the stack.

Re: Multitasking problem

Posted: Sat May 16, 2009 2:50 pm
by AaronMiller
@ehankes
Its CDECL calling convention, have to add ESP back up.
There's one parameter -- ESP
And then there's the return value after the function returned, the original EIP. I dunno if its supposed to be like that in this case, but for normal apps I believe that's the case -- I could have that wrong though.

This is my current task.c :)

Code: Select all

/* Includes */
#include <kernel.h>

/* Globals */
// mm.c
extern sHeap* g_pKrnlHeap;
// paging.c
extern sPageDir* g_pKrnlPgDir;
// Ours
sList		g_processList;
sProcess*	g_pCurrentProcess = (sProcess*)0;
sThread*	g_pCurrentThread = (sThread*)0;
sProcess*	g_pKernelProcess = (sProcess*)0;
u32			g_nextPid = 0;

/* Internal Functions */
// Delete a thread
INT_FUNC void ASM_API deleteThread(sThread* pThread)
{
	sProcess* pProcess = pThread->pParentProcess;
	mmKrnlFree((void*)pThread->esp0);
	mmKrnlFree((void*)pThread);
	if (!lsFirst(&pProcess->threadList) && pProcess != g_pKernelProcess)
	{
		lsRemove(&g_processList, &pProcess->item);
	}
}

/* Functions */
// Initialize multitasking
SYS_FUNC bool_t SYS_API tsInit(sKernelInit* pInit)
{
	// Preliminary
	u32*		pStack;
	sThread*	pThread;
	// Stop interrupts
	asm volatile("cli");
	// Initialize the process list
	lsInit(&g_processList);
	// Allocate the kernel process
	g_pKernelProcess = mmKrnlAllocType(sProcess);
	if (!g_pKernelProcess)
		return B_FALSE;
	// Setup the process
	kStrCpy(g_pKernelProcess->szName, "kernel (system)");
	g_pKernelProcess->pid = g_nextPid++;
	g_pKernelProcess->pPgDir = g_pKrnlPgDir;
	g_pKernelProcess->pHeap = g_pKrnlHeap;
	g_pKernelProcess->flags = 0;
	lsInit(&g_pKernelProcess->threadList);
	lsInitItem(&g_pKernelProcess->item, (void*)g_pKernelProcess, 0);
	// Allocate the kernel processes stack
	pStack = (u32*)pInit->kernelStack;
	// Put the stuff pushed by the process
	*--pStack = 0x0202;		// EFLAGS
	*--pStack = 0x08;		// CS
	*--pStack = (u32)kIdle;	// EIP
	// Things pushed by PUSHA
	*--pStack = 0;			// EDI
	*--pStack = 0;			// ESI
	*--pStack = 0;			// EBP
	*--pStack = 0;			// reserved
	*--pStack = 0;			// EBX
	*--pStack = 0;			// EDX
	*--pStack = 0;			// ECX
	*--pStack = 0;			// EAX
	// Data segments
	*--pStack = 0x10;		// DS
	*--pStack = 0x10;		// ES
	*--pStack = 0x10;		// FS
	*--pStack = 0x10;		// GS
	// Allocate the main kernel thread
	pThread = mmKrnlAllocType(sThread);
	if (!pThread)
		return B_FALSE;
	// Setup the kernel thread
	pThread->pParentProcess = g_pKernelProcess;
	pThread->flags = 0;
	pThread->esp0 = (u32)pStack;
	pThread->esp3 = 0;
	lsInitItem(&pThread->item, (void*)pThread, 0);
	// Add the thread to the kernel processes thread list
	lsAddItem(&g_pKernelProcess->threadList, &pThread->item);
	// Add the kernel process to the list
	lsAddItem(&g_processList, &g_pKernelProcess->item);
	// Set the current process
	g_pCurrentProcess = g_pKernelProcess;
	// Enable interrupts
	asm volatile("sti");
	// Everything appears to have been a success
	return B_TRUE;
}
// Create a thread
SYS_FUNC sThread* SYS_API tsMakeThread(sProcess* pProcess, thread_t pfnThread)
{
	// Preliminary
	u32*		pStack;
	sThread*	pThread;
	// Return if no process was specified
	if (!pProcess)
		return (sThread*)0;
	// Create the thread
	pThread = mmKrnlAllocType(sThread);
	if (!pThread)
		return (sThread*)0;
	// Allocate a stack
	pThread->pStack = mmAlloc(pProcess->pHeap, 0, 0x1000);
	if (!pThread->pStack)
	{
		mmKrnlFree((void*)pThread);
		return (sThread*)0;
	}
	// Save the stack
	pStack = (u32*)(((u32)pThread->pStack)+0x1000);
	// Put the stuff pushed by the process
	*--pStack = 0x0202;			// EFLAGS
	*--pStack = 0x08;			// CS
	*--pStack = (u32)pfnThread;	// EIP
	// Things pushed by PUSHA
	*--pStack = 0;				// EDI
	*--pStack = 0;				// ESI
	*--pStack = 0;				// EBP
	*--pStack = 0;				// reserved
	*--pStack = 0;				// EBX
	*--pStack = 0;				// EDX
	*--pStack = 0;				// ECX
	*--pStack = 0;				// EAX
	// Data segments
	*--pStack = 0x10;			// DS
	*--pStack = 0x10;			// ES
	*--pStack = 0x10;			// FS
	*--pStack = 0x10;			// GS
	// Setup the thread
	pThread->pParentProcess = pProcess;
	pThread->flags = 0;
	pThread->esp0 = (u32)pStack;
	pThread->esp3 = 0;
	lsInitItem(&pThread->item, (void*)pThread, (deleteListItem_t)deleteThread);
	// Add the thread to the processes thread list
	lsAddItem(&pProcess->threadList, &pThread->item);
	// Return the thread
	return pThread;
}
// Switch the task -- returns the new stack
SYS_FUNC u32 ASM_API tsSwitchTask(u32 oldEsp)
{
	// Preliminary
	sProcess* pOldProcess = g_pCurrentProcess;
	// Return if we haven't been initialized yet
	if (!g_pCurrentProcess)
		return oldEsp;
	// Check rather or not we just started multitasking
	if (g_pCurrentThread)
		// We've been multitasking
		g_pCurrentThread->esp0 = oldEsp;
	else
		// We just started multitasking
		g_pCurrentThread = (sThread*)lsFirst(&g_pCurrentProcess->threadList);
	// Switch the thread
	g_pCurrentThread = (sThread*)lsAfter(&g_pCurrentProcess->threadList, &g_pCurrentThread->item);
	// Check if we instead need to change the process
	if (!g_pCurrentThread)
	{
		// Grab the next process
		g_pCurrentProcess = (sProcess*)lsAfter(&g_processList, &g_pCurrentProcess->item);
		// First process?
		if (!g_pCurrentProcess)
			g_pCurrentProcess = (sProcess*)lsFirst(&g_processList);
		// Grab the first thread of the process
		g_pCurrentThread = (sThread*)lsFirst(&g_pCurrentProcess->threadList);
		// Switch the page directory
		if (pOldProcess != g_pCurrentProcess)
			pgSwitchDir(g_pCurrentProcess->pPgDir);
	}
	// Return the thread's new stack
	return g_pCurrentThread->esp0;
}
Cheers,

-naota

Re: Multitasking problem

Posted: Sat May 16, 2009 6:13 pm
by ehenkes
Looks pretty good. Any problems? :)

Re: Multitasking problem

Posted: Mon May 18, 2009 6:39 pm
by AaronMiller
Nope, no problems. :) If anyone wants to use that code in their own OS, they're free to. :)


Cheers,

-naota