First of all, sorry for my poor english.
I'm also developing an OS, using a monolithic approach. For now, I'm working on multitasking, more specifically, on a task switch function. It's better to inform previously that I have only one task that it's executing in the system (the kernel task), and only ring0 is enabled. Ok here is my theory for task switching:
1) IRQ 0 interrupt fires and loads an hardware interrupt handler hook - _clock_interrupt() - which is responsible for save general registers, load kernel data segment registers, switch to an interrupt stack and call the real handler function - do_clock_tick().
2) do_clock_tick() just decrement the current task's quantum, and increment the global variable ticks, witch only counts clock ticks since system has been powered on. When a task run out its quantum, do_clock_tick() calls schedule().
3) schedule(), just chooses a new task for execute by taking the next task in ready_task queue. Once a task was choosen, schedule() calls _switch_task(struct task_t* next_task).
4) _switch_task(struct task_t* next_task) is an assembly function, so it first saves the pointer passed to it as parameter. Next it saves ebp, esp, eip and cr3 registers, updates the global pointer curr_task to next_task, and finally loads the new task registers.
The problem is that, when system is switching to the new task for the first time all that stuff works, but, when comes the next clock tick, a page fault rises, when _switch_task(struct task_t* next_task) tries to load the new task registers.
Here it's the debug information that I have:
- When the page fault comes, I've got:
Code: Select all
[present] [fault_address : 0xFFFFFF08] [fault instruction: 0x0010152E]
Code: Select all
mov (%edx),%eax
Code: Select all
load db "loading...", 0
Here it's the code:
Code: Select all
/* This structure describes a task. */
struct task_t
{
unsigned int esp;
unsigned int ebp;
unsigned int eip;
unsigned int cr3;
unsigned int kstack;
unsigned int ustack;
struct page_dir_t* dir;
unsigned int pid;
unsigned char* name;
unsigned char quantum;
struct task_t* next;
};
Code: Select all
; Clock interrupt.
[GLOBAL _clock_interrupt] ; (void)(void)
_clock_interrupt:
cli
cld
; Save registers.
pushad
push ds
push es
push fs
push gs
; Load kernel data segment register.
mov ax, ss
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
; Switch to interrupt handler's stack.
mov ecx, esp ; Before switching to another stack, save esp.
mov edx, [curr_task] ; Loads a new stack for the interrupt handler.
mov eax, [edx + 0x10]
mov esp, eax
push ecx ; Save old esp into interrupt stack.
call do_clock_tick
; Switch to task's stack.
pop ecx
mov esp, ecx
; Send reset signal to master.
mov al, 0x20
out 0x20, al
; Restore registers.
pop gs
pop fs
pop es
pop ds
popad
iretd
Code: Select all
/* Number of clock ticks since system has been powered on. */
PRIVATE unsigned int ticks = 0;
/* Handles a clock interrupt. */
PUBLIC void do_clock_tick()
{
ticks++;
curr_task->quantum--;
if(curr_task->quantum == 0)
{
curr_task->quantum = 100;
schedule();
}
}
Code: Select all
/* Switch between tasks using a primitive roud robin algorithm. */
PUBLIC void schedule()
{
struct task_t* next_task = curr_task->next;
if (next_task == NULL) next_task = ready_task;
_switch_task(next_task);
}
Code: Select all
[EXTERN curr_task]
[EXTERN ready_task]
next_task dw 0
;load db "loading...", 0 --> IF I UNCOMMENT THIS, EVERYTHING GOES WELL.
; Swtiches to given task.
[GLOBAL _switch_task] ; (void)(struct task_t*)
_switch_task:
push ebp
mov ebp, esp
mov eax, [esp + 8]
lea ebx, [next_task]
mov [ebx], eax
; Reads previously esp, ebp and eip for later use.
call _read_eip
mov ebx, esp
mov ecx, ebp
cmp eax, 0xDEADBEEF
jne .save_and_switch
mov esp, ebp
pop ebp
ret
.save_and_switch:
; Save current task registers.
mov edx, [curr_task]
mov [edx], ebx ; Saves esp.
mov [edx + 0x4], ecx ; Saves ebp.
mov [edx + 0x8], eax ; Saves eip.
; Load next task.
mov ecx, [next_task]
lea edx, [curr_task]
mov [edx], ecx
; Loads the current task registers.
mov edx, [curr_task]
mov eax, [edx] ; --> HER EIS THE PROBLEM.
mov esp, eax ; Load esp.
mov eax, [edx + 0x4]
mov ebp, eax ; Load ebp.
mov ecx, [edx + 0x8] ; Load eip.
mov eax, [edx + 0xC]
mov cr3, eax ; Load cr3.
; Swiches to current task.
mov eax, 0xDEADBEEF
jmp ecx
Thanks for your attention.