PIT stops after taskswitch

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
User avatar
osdever
Member
Member
Posts: 492
Joined: Fri Apr 03, 2015 9:41 am
Contact:

Re: PIT stops after taskswitch

Post by osdever »

This code crashes with GPF.

Code: Select all

#include <arch/i686/multitasking.h>
#include <arch/i686/regs.h>
#include <arch/i686/io.h>
#include <memory.h>
#include <stdio.h>
#include <math.h>
#include <tty_old.h>
#include <stddef.h>
struct task *process_list=NULL;
struct task *running_task;
uint64_t system_idle_time = 0;
uint64_t system_in_work_time = 0;
void insertTaskAfter(struct task* a, struct task* b)
{
	b->prev = a;
	b->next = a->next;
	a->next = b;
	b->next->prev = b;
}
//What do you want?
//Arguments. Lots of arguments.
struct task *create_task(void *page_directory,
			             void *start_address,
			             enum task_priority priority)
{
	struct task *t = malloc(sizeof(struct task));
	printf("creating task, structure address=0x%x, code address=0x%x\n", t, start_address);
	t->eax         = 0;
	t->ebx         = 0;
	t->ecx         = 0;
	t->edx         = 0;
	t->esi         = 0;
	t->edi         = 0;
	t->eflags      = 0; //Interrupts are enabled.
	t->stack       = malloc(4096) + 4096; //4kb will be enough
	t->eip         = (uint32_t) start_address;
	t->cr3         = (uint32_t) page_directory;
	t->priority    = priority;
	t->alive       = 1;
	t->exit_status = 0;
	t->time_out    = priority;
	t->next        = t; //It will point to start of process list.
	//Now we will put it into our process list.
	if(process_list)
		insertTaskAfter(process_list->prev, t);
	else //This is first task. There's no process list AT ALL now, so we will create it.
	{
		t->prev = t;
		process_list = t;
	}
	//process_list->prev = t;
	return t;
}
/*
	get_cpu_usage: Return CPU usage in percents.
 */
double get_cpu_usage()
{
	return percentage_relation((double) system_idle_time, (double) system_in_work_time);
}
void pit_do_multitasking_work(regs* r)
{
	//Increment work or idle time.
	if(running_task == process_list)
		system_idle_time++;
	else
		system_in_work_time++;
	if(running_task->time_out) //Yes, it can be zero.
        running_task->time_out--;
    if(running_task->time_out == 0) //Our task's time is out, so we need to switch task.
    {
        running_task->time_out = running_task->priority;
        //Current task state saved. We will call switch_task to actually switch.
        printf("Interrupting current task! New EIP is 0x%x\n", running_task->eip);
        if (r->int_no >= 40)
        {
            outb(0xA0, 0x20);
        }
        outb(0x20, 0x20);
        struct task *old_running_task = running_task;
        running_task = running_task -> next;
        switch_task(old_running_task, r, running_task);
    }
}
void switch_task(struct task *from, regs *r, struct task *to)
{
	if(from != NULL && r != NULL)
	{
    	from->eax    = r->eax        ;
    	from->ebx    = r->ebx        ;
    	from->ecx    = r->ecx        ;
    	from->edx    = r->edx        ;
    	from->esi    = r->esi        ;
    	from->edi    = r->edi        ;
   	 	from->eip    = r->eip        ;
    	from->eflags = r->eflags     ;
	}
	to->eflags |= 0x200;
	asm volatile("mov %0, %%eax;"
		"mov %1, %%ebx;"
		"mov %2, %%ecx;"
		"mov %3, %%edx;"
		"mov %4, %%esi;"
		"mov %5, %%edi;"
		"mov %6, %%ebp;" :
		"=g" (to->eax),
		"=g" (to->ebx),
		"=g" (to->ecx),
		"=g" (to->edx),
		"=g" (to->esi),
		"=g" (to->edi),
		"=g" (to->ebp) : : "memory");
	asm volatile("iret"); //It will pop a lot of registers.
	//If something happened, halt the system.
	while(1);
}
void idle_process()
{
	while(1)
		asm("sti; hlt");
}
void test_process()
{
	while(1)
		tty_putchar('a');
}
void test2_process()
{
	while(1)
		tty_putchar('b');
}
void test3_process()
{
	while(1)
		//tty_putchar('c');
		;
}
void init_multitasking()
{
	create_task(0, (void*) idle_process, PRIORITY_IDLE);
	create_task(0, (void*) test_process, PRIORITY_NORMAL);
	create_task(0, (void*) test2_process, PRIORITY_NORMAL);
	create_task(0, (void*) test3_process, PRIORITY_NORMAL);
	struct task *t;
	for(t = process_list; t->next!=process_list; t=t->next)
		printf("struct: 0x%x, code: 0x%x, priority: %d -> ", t, t->eip, t->priority);
	printf("struct: 0x%x, code: 0x%x, priority: %d", t, t->eip, t->priority);
	running_task = process_list; //Idle process enters the scene! Woo-hoo!
	//But... Khm, we need to switch to it.
	switch_task(NULL, NULL, running_task);   //Now it really enters the scene...for one timer tick.
}

Code: Select all

#ifndef MULTITASKING_H
#define MULTITASKING_H
#include <stdint.h>
#include <arch/i686/regs.h>
enum task_priority {
	PRIORITY_IDLE    = 1,  //Only for idle process.
	PRIORITY_LOW     = 5,
	PRIORITY_NORMAL  = 10, //Recommended for use by all regular processes.
	PRIORITY_HIGH    = 20,
	PRIORITY_MAXIMAL = 40, //It will slow down all processes with other priorities, but will work maximally fast.
};
/*
 * Reserved PIDs:
 * 
 * - 0 is idle process.
 * - 1 is init process. This is every process' parent.
 */
struct task
{
	struct task *prev;
	//We need to check if the process is dead, so on next PIT tick it will be removed from process list.
	uint8_t alive;      //On new process, it's set to 1.
	uint16_t time_out;  //Time before task switch
	int8_t exit_status; //Contains 0 when the process is alive. kill_process() sets it to provided value, nulls all register data, frees stack memory and makes alive field == 1.
	enum task_priority priority;
	uint32_t eax, ebx, ecx, edx, esi, edi, ebp, cr3;
	struct task *next;
	uint32_t ss;
	void *stack; //Stack needs to be separated from kernel and other processes' stack.
	uint32_t eflags;
	uint32_t cs;
	uint32_t eip;
} __attribute__((packed));
extern struct task *process_list;
extern struct task *running_task;
void init_multitasking();
void switch_task(struct task*, regs*, struct task*);
void pit_do_multitasking_work(regs *); //eeeeeeeee long function namesssss
#endif

Code: Select all

void timer_handler(regs* r)
{
    tty_wrstr("Timer interrupt!\n");
    timer_ticks++; //Just increment timer_ticks.
    pit_do_multitasking_work(r);
}
There's 2 variants:
  • I am stupid (99% correct answer)
  • Magic!
Developing U365.
Source:
only testing: http://gitlab.com/bps-projs/U365/tree/testing

OSDev newbies can copy any code from my repositories, just leave a notice that this code was written by U365 development team, not by you.
User avatar
max
Member
Member
Posts: 616
Joined: Mon Mar 05, 2012 11:23 am
Libera.chat IRC: maxdev
Location: Germany
Contact:

Re: PIT stops after taskswitch

Post by max »

catnikita255 wrote:This code crashes with GPF.

Code: Select all

#include <arch/i686/multitasking.h>
#include <arch/i686/regs.h>
#include <arch/i686/io.h>
#include <memory.h>
#include <stdio.h>
#include <math.h>
#include <tty_old.h>
#include <stddef.h>
struct task *process_list=NULL;
struct task *running_task;
uint64_t system_idle_time = 0;
uint64_t system_in_work_time = 0;
void insertTaskAfter(struct task* a, struct task* b)
{
	b->prev = a;
	b->next = a->next;
	a->next = b;
	b->next->prev = b;
}
//What do you want?
//Arguments. Lots of arguments.
struct task *create_task(void *page_directory,
			             void *start_address,
			             enum task_priority priority)
{
	struct task *t = malloc(sizeof(struct task));
	printf("creating task, structure address=0x%x, code address=0x%x\n", t, start_address);
	t->eax         = 0;
	t->ebx         = 0;
	t->ecx         = 0;
	t->edx         = 0;
	t->esi         = 0;
	t->edi         = 0;
	t->eflags      = 0; //Interrupts are enabled.
	t->stack       = malloc(4096) + 4096; //4kb will be enough
	t->eip         = (uint32_t) start_address;
	t->cr3         = (uint32_t) page_directory;
	t->priority    = priority;
	t->alive       = 1;
	t->exit_status = 0;
	t->time_out    = priority;
	t->next        = t; //It will point to start of process list.
	//Now we will put it into our process list.
	if(process_list)
		insertTaskAfter(process_list->prev, t);
	else //This is first task. There's no process list AT ALL now, so we will create it.
	{
		t->prev = t;
		process_list = t;
	}
	//process_list->prev = t;
	return t;
}
/*
	get_cpu_usage: Return CPU usage in percents.
 */
double get_cpu_usage()
{
	return percentage_relation((double) system_idle_time, (double) system_in_work_time);
}
void pit_do_multitasking_work(regs* r)
{
	//Increment work or idle time.
	if(running_task == process_list)
		system_idle_time++;
	else
		system_in_work_time++;
	if(running_task->time_out) //Yes, it can be zero.
        running_task->time_out--;
    if(running_task->time_out == 0) //Our task's time is out, so we need to switch task.
    {
        running_task->time_out = running_task->priority;
        //Current task state saved. We will call switch_task to actually switch.
        printf("Interrupting current task! New EIP is 0x%x\n", running_task->eip);
        if (r->int_no >= 40)
        {
            outb(0xA0, 0x20);
        }
        outb(0x20, 0x20);
        struct task *old_running_task = running_task;
        running_task = running_task -> next;
        switch_task(old_running_task, r, running_task);
    }
}
void switch_task(struct task *from, regs *r, struct task *to)
{
	if(from != NULL && r != NULL)
	{
    	from->eax    = r->eax        ;
    	from->ebx    = r->ebx        ;
    	from->ecx    = r->ecx        ;
    	from->edx    = r->edx        ;
    	from->esi    = r->esi        ;
    	from->edi    = r->edi        ;
   	 	from->eip    = r->eip        ;
    	from->eflags = r->eflags     ;
	}
	to->eflags |= 0x200;
	asm volatile("mov %0, %%eax;"
		"mov %1, %%ebx;"
		"mov %2, %%ecx;"
		"mov %3, %%edx;"
		"mov %4, %%esi;"
		"mov %5, %%edi;"
		"mov %6, %%ebp;" :
		"=g" (to->eax),
		"=g" (to->ebx),
		"=g" (to->ecx),
		"=g" (to->edx),
		"=g" (to->esi),
		"=g" (to->edi),
		"=g" (to->ebp) : : "memory");
	asm volatile("iret"); //It will pop a lot of registers.
	//If something happened, halt the system.
	while(1);
}
void idle_process()
{
	while(1)
		asm("sti; hlt");
}
void test_process()
{
	while(1)
		tty_putchar('a');
}
void test2_process()
{
	while(1)
		tty_putchar('b');
}
void test3_process()
{
	while(1)
		//tty_putchar('c');
		;
}
void init_multitasking()
{
	create_task(0, (void*) idle_process, PRIORITY_IDLE);
	create_task(0, (void*) test_process, PRIORITY_NORMAL);
	create_task(0, (void*) test2_process, PRIORITY_NORMAL);
	create_task(0, (void*) test3_process, PRIORITY_NORMAL);
	struct task *t;
	for(t = process_list; t->next!=process_list; t=t->next)
		printf("struct: 0x%x, code: 0x%x, priority: %d -> ", t, t->eip, t->priority);
	printf("struct: 0x%x, code: 0x%x, priority: %d", t, t->eip, t->priority);
	running_task = process_list; //Idle process enters the scene! Woo-hoo!
	//But... Khm, we need to switch to it.
	switch_task(NULL, NULL, running_task);   //Now it really enters the scene...for one timer tick.
}

Code: Select all

#ifndef MULTITASKING_H
#define MULTITASKING_H
#include <stdint.h>
#include <arch/i686/regs.h>
enum task_priority {
	PRIORITY_IDLE    = 1,  //Only for idle process.
	PRIORITY_LOW     = 5,
	PRIORITY_NORMAL  = 10, //Recommended for use by all regular processes.
	PRIORITY_HIGH    = 20,
	PRIORITY_MAXIMAL = 40, //It will slow down all processes with other priorities, but will work maximally fast.
};
/*
 * Reserved PIDs:
 * 
 * - 0 is idle process.
 * - 1 is init process. This is every process' parent.
 */
struct task
{
	struct task *prev;
	//We need to check if the process is dead, so on next PIT tick it will be removed from process list.
	uint8_t alive;      //On new process, it's set to 1.
	uint16_t time_out;  //Time before task switch
	int8_t exit_status; //Contains 0 when the process is alive. kill_process() sets it to provided value, nulls all register data, frees stack memory and makes alive field == 1.
	enum task_priority priority;
	uint32_t eax, ebx, ecx, edx, esi, edi, ebp, cr3;
	struct task *next;
	uint32_t ss;
	void *stack; //Stack needs to be separated from kernel and other processes' stack.
	uint32_t eflags;
	uint32_t cs;
	uint32_t eip;
} __attribute__((packed));
extern struct task *process_list;
extern struct task *running_task;
void init_multitasking();
void switch_task(struct task*, regs*, struct task*);
void pit_do_multitasking_work(regs *); //eeeeeeeee long function namesssss
#endif

Code: Select all

void timer_handler(regs* r)
{
    tty_wrstr("Timer interrupt!\n");
    timer_ticks++; //Just increment timer_ticks.
    pit_do_multitasking_work(r);
}
There's 2 variants:
  • I am stupid (99% correct answer)
  • Magic!
Did you at all read and comprehend what I wrote for you?
onlyonemac
Member
Member
Posts: 1146
Joined: Sat Mar 01, 2014 2:59 pm

Re: PIT stops after taskswitch

Post by onlyonemac »

Are you sending EOI *before* switching task. It's easy to get confused and end up sending EOI afterwards (i.e. at the end of your interrupt handler, after whatever line performs the task switching), except that in that case EOI won't be sent until the current task starts again (which will never happen unless you send EOI). If you can, see if you're receiving other hardware interrupts in the second task (e.g. keyboard interrupt), and if not then check your EOI.
When you start writing an OS you do the minimum possible to get the x86 processor in a usable state, then you try to get as far away from it as possible.

Syntax checkup:
Wrong: OS's, IRQ's, zero'ing
Right: OSes, IRQs, zeroing
Post Reply