OSDev.org

The Place to Start for Operating System Developers
It is currently Sun Sep 20, 2020 3:08 pm

All times are UTC - 6 hours




Post new topic Reply to topic  [ 3 posts ] 
Author Message
 Post subject: Multitasking - [Ring 0 / 3] Crashes after a while
PostPosted: Wed Dec 28, 2011 12:06 pm 
Offline
User avatar

Joined: Wed Dec 28, 2011 11:45 am
Posts: 3
Location: Switzerland
Hello there

I'm developing now since a while my own little operating system called cronOS. Till now, I did everything myself. I read documentations, howtos and so on and coded everything from scratch. But now I've got some really annoying problem. I've implemented PMM (physical memory management) and Multitasking.

My multitasking worked great. Task switching worked (scheduler called by PIT / Timer) great and it never crashed. That was my code then:

PIT code - pit.cpp (snippet)
Code:
struct cpu_state* handle(struct cpu_state* cpu) {
        /* Increment counter */
        ticks++;
        if(ticks % CRONOS_PIT_HZ == 0) {
           
        }
       
        /* Handle multitasking */
        cpu = tss::schedule(cpu);
       
        /* Return new CPU */
        return cpu;
    }


Multitasking Code - tss.cpp
Code:
#include "tss.h"
#include "stdint.h"
#include "util.h"
#include "console.h"
#include "debug.h"
#include "gdt.h"
#include "pmm.h"

namespace tss {
   
    struct task {
        struct cpu_state* cpu_state;
        struct task* next;
    };
    static struct task* first_task = (task*) NULL;
    static struct task* current_task = (task*) NULL;
   
    uint8_t init() {
        /* Debug */
        debug::printf("Initializing multitasking...\n");
       
        /* Return */
        return 0;
    }
   
    struct task* init_task(uint32_t entry) {
        /* Get stack space from PMM */
        uint32_t stack = pmm::alloc();
        uint32_t user_stack = pmm::alloc();
       
        debug::printf("Stack: %h / User stack: %h\n", sizeof(uint32_t), stack, sizeof(uint32_t), user_stack);
       
        /* Generate new CPU state */
        struct cpu_state new_state = {
            new_state.eax = 0,
            new_state.ebx = 0,
            new_state.ecx = 0,
            new_state.edx = 0,
            new_state.esi = 0,
            new_state.edi = 0,
            new_state.ebp = 0,
            new_state.intr = 0,
            new_state.error = 0,
            new_state.eip = (uint32_t) entry,
            new_state.cs = 0x08,
            new_state.eflags = 0x202,
            new_state.esp = 0,
            new_state.ss = 0
        };

        /* Copy CPU state to stack */
        struct cpu_state* state = (cpu_state*) (stack + 4096 - sizeof(new_state));
        *state = new_state;
       
        /* Join new task with other tasks */
        struct task* task = (struct task*) pmm::alloc();
        task->cpu_state = state;
        task->next = first_task;
        first_task = task;
       
        debug::printf("CPU State: %h\n", sizeof(task->cpu_state), task->cpu_state);
        debug::printf("First task: %h\n", sizeof(struct task*), first_task);
        debug::printf("Current task: %h\n", sizeof(struct task*), current_task);
       
        /* Return new task */
        return task;
    }
   
    struct cpu_state* schedule(struct cpu_state* cpu) {
        /* No tasks available? */
        if(first_task == 0) {
            return cpu;
        }
       
        /* When another task is running, save CPU state */
        if(current_task > 0) {
            current_task->cpu_state = cpu;
        }
       
        /* First call? */
        if(current_task == 0) {
            current_task = first_task;
        }
       
        /* Choose next task */
        if(current_task->next == 0) {
            current_task = first_task;
        } else {
            current_task = current_task->next;
        }
       
        /* Save new state into CPU */
        cpu = current_task->cpu_state;
       
        debug::printf("Task info: ESP: %h\n", sizeof(cpu->esp), cpu->esp);
       
        /* Return new state */
        return cpu;
    }
   
    void task_a() {
        while(1) {
            console::printf("A");
        }
    }
   
    void task_b() {
        while(1) {
            console::printf("B");
        }
    }
   
}


IRQ handler - loader.asm
Code:
; IRQ handler macro
%macro IRQ 1
global irq_handler%1
irq_handler%1:
  cli
  push byte 0
  push byte %1+32
  jmp irq_stub
%endmacro

; IRQ routines
IRQ     0   ; Timer
IRQ     1   ; Keyboard
IRQ     2   ; ???
IRQ     3   ; ???
IRQ     4   ; ???
IRQ     5   ; ???
IRQ     6   ; ???
IRQ     7   ; ???
IRQ     8   ; RTC
IRQ     9   ; ???
IRQ     10  ; ???
IRQ     11  ; ???
IRQ     12  ; ???
IRQ     13  ; ???
IRQ     14  ; ???
IRQ     15  ; ???

; IRQ stub
extern irq_handle
irq_stub:
    ; Save CPU state
    push ebp
    push edi
    push esi
    push edx
    push ecx
    push ebx
    push eax

    ; Call handler
    push esp
    call irq_handle
    mov esp, eax

    ; Restore CPU state
    pop eax
    pop ebx
    pop ecx
    pop edx
    pop esi
    pop edi
    pop ebp

    ; Remove error code and interrupt number from stack
    add esp, 8

    ; Jump back
    iret


That code works really great! No crashes and everything is fine. Then I added ring switching. To be specific, I changed all these files:

IRQ handler - loader.asm
Code:
...

    ; Load kernel data segments
    mov ax, 0x10
    mov ds, ax
    mov es, ax

    ; Call handler
    push esp
    call irq_handle
    mov esp, eax

    ; Load user data segments
    mov ax, 0x23
    mov ds, ax
    mov es, ax

    ...


As you can see, I added the code for segment loading. I also changed the tss.cpp a bit:

Multitasking - tss.cpp
Code:
...

    static uint32_t tss[32] = {0, 0, 0x10};
   
   ...
   
    uint8_t init() {
        /* Debug */
        debug::printf("Initializing multitasking...\n");
       
        /* Init TSS */
        gdt::set_entry(5, (uint32_t) tss, (uint32_t) sizeof(tss), GDT_FLAG_TSS | GDT_FLAG_PRESENT | GDT_FLAG_RING3);
        __asm__ volatile("ltr %%ax" :: "a" (5 << 3));
       
        /* Return loaded tasks */
        return 0;
    }
   
    void set_tss(uint32_t cpu) {
        /* Set new TSS */
        tss[1] = cpu;
    }
   
    struct task* init_task(uint32_t entry) {
        /* Get stack space from PMM */
        uint32_t stack = pmm::alloc();
        uint32_t user_stack = pmm::alloc();
       
        debug::printf("Stack: %h / User stack: %h\n", sizeof(uint32_t), stack, sizeof(uint32_t), user_stack);
       
        /* Generate new CPU state */
        struct cpu_state new_state = {
            new_state.eax = 0,
            new_state.ebx = 0,
            new_state.ecx = 0,
            new_state.edx = 0,
            new_state.esi = 0,
            new_state.edi = 0,
            new_state.ebp = 0,
            new_state.intr = 0,
            new_state.error = 0,
            new_state.eip = (uint32_t) entry,
            new_state.cs = 0x18 | 0x03,
            new_state.eflags = 0x202,
            new_state.esp = (uint32_t) user_stack + 4096,
            new_state.ss = 0x20 | 0x03
        };

        /* Copy CPU state to stack */
        struct cpu_state* state = (cpu_state*) (stack + 4096 - sizeof(new_state));
        *state = new_state;
       
        /* Join new task with other tasks */
        struct task* task = (struct task*) pmm::alloc();
        task->cpu_state = state;
        task->next = first_task;
        first_task = task;
       
        debug::printf("CPU State: %h\n", sizeof(task->cpu_state), task->cpu_state);
        debug::printf("First task: %h\n", sizeof(struct task*), first_task);
        debug::printf("Current task: %h\n", sizeof(struct task*), current_task);
       
        /* Return new task */
        return task;
    }
   
    ...


I've only added the TSS and changed the function "init_task" to specify the new registers. The last change I did was in my PIT code:

PIT code - pit.cpp (snippet)
Code:
struct cpu_state* handle(struct cpu_state* cpu) {
        /* Increment counter */
        ticks++;
        if(ticks % CRONOS_PIT_HZ == 0) {
           
        }
       
        /* Handle multitasking */
        cpu = tss::schedule(cpu);
        tss::set_tss((uint32_t) cpu + 1);
       
        /* Return new CPU */
        return cpu;
    }


I didn't change more. Now my problem is: Multitasking still works fine. But after a while, QEMU just crashes and Bochs prints: "00520473404e[CPU0 ] read_RMW_virtual_dword_32(): segment limit violation" In my debug console (serial output via COM port 1) I can't see much more: https://www.snapserv.net/screenshots/20 ... 8_1715.png

I've got no idea where my error is. Can you see anything wrong? Thanks for your answer!

_________________
Regards
NeoXiD

Actual projects:
cronOS - Simple operating system written in C++
easyDebug - Small and fast PHP debugger - written in PHP!


Last edited by NeoXiD on Wed Dec 28, 2011 4:00 pm, edited 1 time in total.

Top
 Profile  
 
 Post subject: Re: Multitasking - [Ring 0 / 3] Crashes after a while
PostPosted: Wed Dec 28, 2011 3:57 pm 
Offline
User avatar

Joined: Wed Dec 28, 2011 11:45 am
Posts: 3
Location: Switzerland
berkus wrote:
Didn't read after
Code:
/* When another task is running, save CPU state */
        if(current_task >= 0) {


Care to explain?


Okay, there was some little fault in it but that didn't change the problem. (It should be > 0, but that wasn't the problem) Actually the scheduler checks there if some other task is active, and if yes, the scheduler saves the CPU state into the task. After that the next task would be loaded and it continues with loading the user segment registers. All that works but after a while it just crashes. (After around 100-200 cycles) But when I remove all the "Ring-3" stuff, it works without any problems...

_________________
Regards
NeoXiD

Actual projects:
cronOS - Simple operating system written in C++
easyDebug - Small and fast PHP debugger - written in PHP!


Top
 Profile  
 
 Post subject: Re: Multitasking - [Ring 0 / 3] Crashes after a while
PostPosted: Thu Dec 29, 2011 8:17 am 
Offline
User avatar

Joined: Wed Dec 28, 2011 11:45 am
Posts: 3
Location: Switzerland
berkus wrote:
Ok, I looked further. You don't provide enough info for debugging but there are a lot of things that catch the eye.
1. Are you running with identical (1-1) mapping? Does pmm::alloc return physical or virtual addresses?
2. You didn't set ss/esp properly in the first (working) case - which memory were you trashing then? You set ESP to physical or virtual address returned by pmm::alloc now - which memory you trash in this case?
3. AFAIK you don't reload TSS in set_tss() but you should (I may be wrong about this, anyway).
4. Did you set CPU exception handlers for all exceptions? They would tell you more if you did.
5. Use bochs with debugger enabled (--enable-debugger --enable-disasm) and debug.

Code:
push esp
call irq_handle
mov esp, eax

Erm, what? Nor can I see the code for irq_handle.


I tried now so many ways this morning and then I tried that:

Code:
void set_tss() {
        /* Set new TSS */
        tss.ss0 = 0x10;
        tss.esp0 = current_task->stack + 4096 - 4;
        tss.iopb = 0;
    }


And it worked. Stack is a pointer to the kernel stack and now everything works perfectly. It looks like I filled "esp0" with some wrong kernel stack pointer and then I've got a stack overflow everytime. Now with that new code everything works. (And thanks to that bug my TSS is now an structure and not just an array.)

Thanks you for your time to read through my code and all your thoughts. Have a nice day!

_________________
Regards
NeoXiD

Actual projects:
cronOS - Simple operating system written in C++
easyDebug - Small and fast PHP debugger - written in PHP!


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 3 posts ] 

All times are UTC - 6 hours


Who is online

Users browsing this forum: Bing [Bot], Google [Bot] and 8 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group