Page 1 of 1

Task Switching (General protection fault)

Posted: Mon Feb 03, 2014 5:22 pm
by gardinirafael
Good day

I have a little problem with task switching (I hope).

My system works without paging on ring 0 only and using 32 bits architecture.

The objective is create a task management for kernel in this first time. The paging and user-mode will be in the future.

My problem appear when the task will change for the new context on schedule function.

After the schedule function execute all tasks and assign the new context, BOCHS show me a (general protection fault) message. task's and schedule function doesn't execute anymore.

Bellow you can find the main functions about this problem.

I no have ideia how to solve this problem, someone can help-me to fix?

Thank in advance.

k_main.c

Code: Select all

/////////////////////////////////////////////////
// Author: 	Rafael Angelo Gardini
// File: 	k_main.c
// Description:	Kernel main
// Last update:	21/06/2012
/////////////////////////////////////////////////

#include "../headers/io/stdio.h"
#include "../headers/io/video.h"
#include "../headers/memory/mm.h"
#include "../headers/process/process.h"
#include "../headers/system/fpu.h"
#include "../headers/system/gdt.h"
#include "../headers/system/idt.h"
#include "../headers/system/irq.h"
#include "../headers/system/isr.h"
#include "../headers/system/kernel.h"
#include "../headers/system/timer.h"

#ifndef K_MAIN

void task0(void)
{
	k_printf("task0\n");

	for(;;);

}

void task1(void)
{
	k_printf("task1\n");

	for(;;);
}

void task2(void)
{
	k_printf("task2\n");

	for(;;);
}

void task3(void)
{
	k_printf("task3\n");

	for(;;);
}

void k_main() 
{
	k_cli_irq();

	k_install_memory();

	k_install_video();

	k_install_gdt();	
	
	k_install_idt();

	k_install_irq();

	k_install_isr();

	k_install_fpu();

	k_install_timer();

	k_install_process();

	k_info_kernel();

	k_sti_irq();

	//---------------------------------------------
	k_create_process(task0);
	k_create_process(task1);
	k_create_process(task2);
	k_create_process(task3);
	//---------------------------------------------

	k_enable_schedule_process();

	k_loop_kernel();
}

#endif
process.c

Code: Select all

/////////////////////////////////////////////////
// Author: 	Rafael Angelo Gardini
// File: 	process.h
// Description:	Process functions
// Last update:	30/09/2013
/////////////////////////////////////////////////

#include "../../headers/io/stdio.h"
#include "../../headers/memory/mm.h"
#include "../../headers/process/process.h"
#include "../../headers/system/irq.h"

#ifndef PROCESS
#define PROCESS

void k_install_process()
{
	process.storepid = 0;
	process.enable = 0;

	process.running = MEMORY_NULL;
	process.task = MEMORY_NULL;

	k_printf("Install process management sucess...\n");
}

int k_create_stack(void (*task)(void))
{
	int stackptr = (int)k_malloc(STACK_SIZE) + STACK_SIZE ;

	unsigned int * stack = (unsigned int*)stackptr;

	*--stack = 0x202;

	*--stack = 0x08;

	*--stack = (unsigned int) task;

	*--stack = 0;
	*--stack = 0;
	*--stack = 0;
	*--stack = 0;
	*--stack = 0;
	*--stack = 0;
	*--stack = 0;
	*--stack = 0;

	*--stack = 0x10;
	*--stack = 0x10;
	*--stack = 0x10;
	*--stack = 0x10;

	return (unsigned int) stack;
}

void k_create_process(void (*task)(void))
{
	k_cli_irq();

	TASK * newtask = (TASK *) k_malloc(sizeof(TASK));

	newtask->stack = k_create_stack(task);

	newtask->status = STOPPED;
	newtask->task = task;
	newtask->pid = process.storepid;

	k_printf("CREATE PID [%d] STACK: [%d] TASK: [%d]\n", newtask->pid, newtask->stack, newtask->task);

	if (process.task == MEMORY_NULL)
	{
		process.task = newtask;
		process.task->next = MEMORY_NULL;
	}
	else
	{
		newtask->next = process.task;
		process.task = newtask;
	}

	process.storepid++;

	k_sti_irq();
}

void k_enable_schedule_process()
{
	process.enable = 1;
}

int k_schedule_process(int context)
{
	if (process.task == MEMORY_NULL)
	{
		return context;
	}

	if (process.running == MEMORY_NULL)
	{
		process.running = process.task;
	}
	else
	{
		process.running->stack = context;

		if (process.running->next != MEMORY_NULL)
		{
			process.running = process.running->next;
		}
		else
		{
			process.running = process.task;
		}
	}

	k_printf("PID %d CONTEXT: %d STACK %d\n", process.running->pid, context, process.running->stack);

	return process.running->stack;
}

void k_kill_process(int pid)
{
	TASK * index = MEMORY_NULL;

	for (index = process.task; index != MEMORY_NULL; index = index->next)
	{
		if (pid == index->pid)
		{
			index->status = KILLED;
		}
	}
}

#endif
process.asm

Code: Select all

[BITS 32]
extern k_schedule_process

global k_switch_process

k_switch_process:
                pusha
                push ds
                push es
                push fs
                push gs

                mov eax, 0x10
                mov ds, eax
                mov es, eax
                mov fs, eax
                mov gs, eax

                mov eax, esp

                push eax
                call k_schedule_process
                mov esp, eax

                mov al, 0x20
                out 0x20, al

                pop gs
                pop fs
                pop es
                pop ds
                popa

                iret

bochs.img
Image

Re: Task Switching (General protection fault)

Posted: Mon Feb 03, 2014 6:51 pm
by Combuster
My crystal ball claims it's a corrupt stack.

But there's a lot more you can do to narrow the cause - you can print the registers at the time of the crash, as well as the error location and error code. That saves you a lot of guesswork.

Re: Task Switching (General protection fault)

Posted: Mon Feb 03, 2014 8:23 pm
by gardinirafael
Combuster wrote:My crystal ball claims it's a corrupt stack.

But there's a lot more you can do to narrow the cause - you can print the registers at the time of the crash, as well as the error location and error code. That saves you a lot of guesswork.
I printed the registers, but i don't have idea whats the problem.

does memory manager have something rule to create stack? or some place to begin?

because I am using the end of kernel to begin the address (from TLINK file) and I dont use anything rule.

Thank you

Image

Re: Task Switching (General protection fault)

Posted: Mon Feb 03, 2014 10:22 pm
by eryjus
One way to go is to look at your linker map:

For example:

Code: Select all

LD = i586-elf-ld -L bin -T $(LNKS) -Map kernel.map
The file kernel.map will have the starting address of each function. You can look at your EIP (0x100289) and fund the file/function you are in. You will have an offset from your starting function to your IP, which you can find by disassembling your file (i586-elf-objdump or similar). Once you have the exact asm instruction creating the issue, your error code and the Intel software development guides will be really helpful.

Also, change your register output to hex and get used to working with hex.

Good luck!

Re: Task Switching (General protection fault)

Posted: Tue Feb 04, 2014 1:14 am
by iansjack
The value in SS is nonsense. Either you haven't set it correctly or something has changed it. Without a sane stack you are going to have problems.

Re: Task Switching (General protection fault)

Posted: Tue Feb 04, 2014 3:27 am
by Combuster
Since the GPF comes from kernel land, there'll be no SS/ESP pushed by the exception, and you're looking at other variables on the stack as a result.

Re: Task Switching (General protection fault)

Posted: Wed Feb 05, 2014 4:39 am
by gardinirafael
Hi guys,
I fix this issue finally.
when kernel.map was generated I could see my mistake.

Between irq and schedule function, I had a sub-function to call my schedule function, so the return address is ever to sub-function and not to new context from my task.

Exemple (with error):

irq_handle() -------call------> sub-function() -------call------> schedule()

I removed the sub-function, the problem was solved.

Exemple (working perfect):

irq_handle() -------call------> schedule()


Guys thank you for all. :lol:
Sorry by my bad english.

Re: Task Switching (General protection fault)

Posted: Wed Feb 05, 2014 6:09 am
by Combuster
gardinirafael wrote:Between irq and schedule function, I had a sub-function to call my schedule function, so the return address is ever to sub-function and not to new context from my task.
This would be perfectly correct behaviour on my watch. If you saved the state correctly, that subfunction would be just as able to return to its caller after being rescheduled and you wouldn't depend on an exact stack layout.

By the sound of it you've successfully hidden a bug so that it can breed and swarm you later.

Re: Task Switching (General protection fault)

Posted: Wed Feb 05, 2014 7:50 am
by gardinirafael
Combuster wrote:
gardinirafael wrote:Between irq and schedule function, I had a sub-function to call my schedule function, so the return address is ever to sub-function and not to new context from my task.
This would be perfectly correct behaviour on my watch. If you saved the state correctly, that subfunction would be just as able to return to its caller after being rescheduled and you wouldn't depend on an exact stack layout.

By the sound of it you've successfully hidden a bug so that it can breed and swarm you later.
You are correct and my fault was exactly doesn't save and restore the context correctly.

I had two steps to save the context, first on before to call sub-function by IRQ, second on before to call a task by sub-function and restore all in the end. I didnt do that.

I only saved and restored context from sub-function and task, so the new context stored on my task was trash.

;)