Autor Thema: Problem mit Multitasking [OSDEV - Einsteiger]  (Gelesen 8868 mal)

lucky

  • Beiträge: 5
    • Profil anzeigen
Gespeichert
« am: 29. January 2016, 17:09 »
Hallo Einerseits,

ich bin habe ein Problem mit dem 6. Tutorial (Multitasking) der OS-DEV-Einsteigerserie. Ich habe versucht einen einfachen
Task-Switch hinzu bekommen, doch leider schaffe ich es nicht obwohl ich eigentlich genauso vorgehe wie im Tutorial beschrieben.
Hier ist der Code mit dem ich versuche zwei verschiedene Tasks laufen zu lassen, einer der Tasks gibt in einer Dauerschleife As aus
und der andere ebenfalls in einer Dauerschleife Bs. (Wie im Tutorial). Doch sobald ich den Kernel mit Qemu boote, erscheint noch nicht
einmal ein A oder B. Hier ist mein Code (kernel.c):
#include <stdint.h>
#include "keyboard_map.h"
#include "io.h"
#include "task.h"


/* there are 25 lines each of 80 columns; each element takes 2 bytes */
#define LINES 25
#define COLUMNS_IN_LINE 80
#define BYTES_FOR_EACH_ELEMENT 2
#define SCREENSIZE BYTES_FOR_EACH_ELEMENT * COLUMNS_IN_LINE * LINES

#define KEYBOARD_DATA_PORT 0x60
#define KEYBOARD_STATUS_PORT 0x64
#define IDT_SIZE 256
#define INTERRUPT_GATE 0x8e
#define KERNEL_CODE_SEGMENT_OFFSET 0x08

#define ENTER_KEY_CODE 0x1C

extern unsigned char keyboard_map[128];
extern void keyboard_handler(void);
extern void mouse_handler(void);
extern void irq0_handler(void);
extern char read_port(unsigned short port);
extern void write_port(unsigned short port, unsigned char data);
extern void load_idt(unsigned long *idt_ptr);
extern void some_handler(void);
extern void tim_handler(void);

/* current cursor location */
unsigned int current_loc = 0;
/* video memory begins at address 0xb8000 */
char *vidptr = (char*)0xb8000;

struct IDT_entry {
unsigned short int offset_lowerbits;
unsigned short int selector;
unsigned char zero;
unsigned char type_attr;
unsigned short int offset_higherbits;
};

struct IDT_entry IDT[IDT_SIZE];
void idt_set_entry(unsigned int pos, uint32_t base)
        {
          IDT[pos].offset_lowerbits = base & 0xffff;
          IDT[pos].selector = KERNEL_CODE_SEGMENT_OFFSET;
          IDT[pos].zero = 0x0;
          IDT[pos].type_attr = INTERRUPT_GATE;
          IDT[pos].offset_higherbits = (base >> 16) & 0xffff;
        }



void idt_init(void)
{
       
unsigned long keyboard_address;
       
        unsigned long irq0_handler_address;
unsigned long idt_address;
unsigned long idt_ptr[2];
       
        irq0_handler_address = (unsigned long)irq0_handler;
       

/* populate IDT entry of keyboard's interrupt */
IDT[0x20].offset_lowerbits = irq0_handler_address & 0xffff;
IDT[0x20].selector = KERNEL_CODE_SEGMENT_OFFSET;
IDT[0x20].zero = 0;
IDT[0x20].type_attr = INTERRUPT_GATE;
IDT[0x20].offset_higherbits = (irq0_handler_address & 0xffff0000) >> 16;


        keyboard_address = (unsigned long)keyboard_handler;
IDT[0x21].offset_lowerbits = keyboard_address & 0xffff;
IDT[0x21].selector = KERNEL_CODE_SEGMENT_OFFSET;
IDT[0x21].zero = 0;
IDT[0x21].type_attr = INTERRUPT_GATE;
IDT[0x21].offset_higherbits = (keyboard_address & 0xffff0000) >> 16;

       

        for(int i=16;i<20;i++){
           idt_set_entry(i,(uint32_t)&some_handler);


        }
        for(int d=22;d<257;d++){
         
           idt_set_entry(d,(uint32_t)&some_handler);

        }
     
       


/*     Ports
* PIC1 PIC2
*Command 0x20 0xA0
*Data 0x21 0xA1
*/

/* ICW1 - begin initialization */
write_port(0x20 , 0x11);
write_port(0xA0 , 0x11);

/* ICW2 - remap offset address of IDT */
/*
* In x86 protected mode, we have to remap the PICs beyond 0x20 because
* Intel have designated the first 32 interrupts as "reserved" for cpu exceptions
*/
write_port(0x21 , 0x20);
write_port(0xA1 , 0x28);

/* ICW3 - setup cascading */
outb(0x21, 4);
        outb(0xA1, 2);

/* ICW4 - environment info */
write_port(0x21 , 0x01);
write_port(0xA1 , 0x01);
/* Initialization finished */

/* mask interrupts */
write_port(0x21 , 0xF8);
write_port(0xA1 , 0xEF);


/* fill the IDT descriptor */
idt_address = (unsigned long)IDT ;
idt_ptr[0] = (sizeof (struct IDT_entry) * IDT_SIZE) + ((idt_address & 0xffff) << 16);
idt_ptr[1] = idt_address >> 16 ;

load_idt(idt_ptr);
}

void kb_init(void)
{
kprint_newline();
        kprint("Keyboard initialisiert");
}

void kprint(const char *str)
{
unsigned int i = 0;
while (str[i] != '\0') {
vidptr[current_loc++] = str[i++];
vidptr[current_loc++] = 0x07;
}
}

void kprint_newline(void)
{
unsigned int line_size = BYTES_FOR_EACH_ELEMENT * COLUMNS_IN_LINE;
current_loc = current_loc + (line_size - current_loc % (line_size));
}

void clear_screen(void)
{
unsigned int i = 0;
while (i < SCREENSIZE) {
vidptr[i++] = ' ';
vidptr[i++] = 0x07;
}
}

void mouse_handler_main(void)
{

write_port(0x20,0x20);
write_port(0xA0,0x20);
kprint("dd");






}

void timer_handler(void){

kprint("cc");


}

void keyboard_handler_main(void)
{
unsigned char status;
char keycode;

/* write EOI */
write_port(0x20, 0x20);

status = read_port(KEYBOARD_STATUS_PORT);
/* Lowest bit of status will be set if buffer is not empty */
if (status & 0x01) {
keycode = read_port(KEYBOARD_DATA_PORT);
if(keycode < 0)
return;

if(keycode == ENTER_KEY_CODE) {
kprint_newline();
return;
}

vidptr[current_loc++] = keyboard_map[(unsigned char) keycode];
vidptr[current_loc++] = 0x09;
}
}

struct cpu_state* irq_handler(struct cpu_state *r)
{
   struct cpu_state* new_cpu = r;

    /* This is a blank function pointer */
   
   
    if (r->intr == 0x20) {
        new_cpu = schedule(r);
    }

    /* If the IDT entry that was invoked was greater than 40
    *  (meaning IRQ8 - 15), then we need to send an EOI to
    *  the slave controller */
    if (r->intr >= 40)
    {
        outb(0xA0, 0x20);
    }

    /* In either case, we need to send an EOI to the master
    *  interrupt controller too */
    outb(0x20, 0x20);
   
   return new_cpu;
}

void mouse_wait(uint8_t a_type)
{
uint32_t _time_out=100000;
if(a_type==0)
{
while(_time_out--) //Daten
{
if((inb(0x64) & 1)==1)
{
                               
return;
}
}
return;
}
else
{
while(_time_out--) //Signal
{
if((inb(0x64) & 2)==0)
{
   return;
}
}
return;
}
}

void mouse_write(uint8_t a_write)
{
    //Auf Bereitschaft der Maus warten
    mouse_wait(1);
    //Der Maus sagen, dass ein Befehl folgt
    outb(0x64, 0xD4);
    mouse_wait(1);
    //Befehl schreiben
    outb(0x60, a_write);
}

uint8_t mouse_read()
{
    //Antwort der Maus abfragen
    mouse_wait(0);
    return inb(0x60);
}


void mouse_init(void){

    uint8_t _status;
   

    //Mauscontroller aktivieren
    mouse_wait(1);
    outb(0x64, 0xA8);
   

    //Interrupts aktivieren
    mouse_wait(1);
   
    outb(0x64, 0x20);
    mouse_wait(0);
    _status=(inb(0x60) | 2);
   
    mouse_wait(1);
    outb(0x64, 0x60);
    mouse_wait(1);
    outb(0x60, _status);
   

    //Standardeinstellungen der Maus auswählen
    mouse_write(0xF6);
    mouse_read();

    //Maus aktivieren
    mouse_write(0xF4);
    mouse_read();
   


}

void kmain(void)
{
const char *str = "my first kernel with keyboard support";
clear_screen();
kprint(str);
kprint_newline();
kprint_newline();
        init_multitasking();
idt_init();
kb_init();
        mouse_init();
       

while(1);
}

 

Und hier ist die task.h mit den weiteren Funktionen, wie z.B init_multitasking oder init_task und etc.
struct cpu_state {
    // Von Hand gesicherte Register
    uint32_t   eax;
    uint32_t   ebx;
    uint32_t   ecx;
    uint32_t   edx;
    uint32_t   esi;
    uint32_t   edi;
    uint32_t   ebp;
 
    uint32_t   intr;
    uint32_t   error;
 
    // Von der CPU gesichert
    uint32_t   eip;
    uint32_t   cs;
    uint32_t   eflags;
    uint32_t   esp;
    uint32_t   ss;
};
extern void kprint(const char *str);
static int current_task = 1;
static int num_tasks = 2;
static struct cpu_state* task_states[2];
 
static uint8_t stack_a[4096];
static uint8_t stack_b[4096];

void task_a(void)
{
    while (1) {
        kprint("A");
    }
}
 
void task_b(void)
{
    while (1) {
        kprint("B");
    }
}



struct cpu_state* init_task(char* stack, void* entry)
{
    /*
     * CPU-Zustand fuer den neuen Task festlegen
     */
    struct cpu_state new_state = {
        .eax = 0,
        .ebx = 0,
        .ecx = 0,
        .edx = 0,
        .esi = 0,
        .edi = 0,
        .ebp = 0,
        //.esp = unbenutzt (kein Ring-Wechsel)
        .eip = (unsigned long) entry,
 
        /* Ring-0-Segmentregister */
        .cs  = 0x08,
        //.ss  = unbenutzt (kein Ring-Wechsel)
 
        /* IRQs einschalten (IF = 1) */
        .eflags = 0x202,
    };
 
    /*
     * Den angelegten CPU-Zustand auf den Stack des Tasks kopieren, damit es am
     * Ende so aussieht als waere der Task durch einen Interrupt unterbrochen
     * worden. So kann man dem Interrupthandler den neuen Task unterschieben
     * und er stellt einfach den neuen Prozessorzustand "wieder her".
     */
    struct cpu_state* state = (void*) (stack + 4096 - sizeof(new_state));
    *state = new_state;
 
    return state;
}


 
void init_multitasking(void)
{
    task_states[0] = init_task(stack_a, task_a);
    task_states[1] = init_task(stack_b, task_b);
}

struct cpu_state* schedule(struct cpu_state* cpu)
{
    /*
     * Wenn schon ein Task laeuft, Zustand sichern. Wenn nicht, springen wir
     * gerade zum ersten Mal in einen Task. Diesen Prozessorzustand brauchen
     * wir spaeter nicht wieder.
     */
    if (current_task >= 0) {
        task_states[current_task] = cpu;
    }
 
    /*
     * Naechsten Task auswaehlen. Wenn alle durch sind, geht es von vorne los
     */
    current_task++;
 
    /* Prozessorzustand des neuen Tasks aktivieren */
    cpu = task_states[current_task];
 
    return cpu;
}



Und der Interrupt-Handler des Irq's 0 (Timer):
irq0_handler:     
        push ebp
        push edi
        push esi
        push edx
        push ecx
        push ebx
        push eax
 
        ; // Handler aufrufen
        ; // Der Rueckgabewert ist der Prozessorzustand des moeglicherweise
        ; // gewechselten Tasks. Wir muessen also den Stack dorthin wechseln
        ; // um die Register wiederherzustellen.
        push esp
        call irq_handler
        mov esp, eax
 
        ; // CPU-Zustand wiederherstellen

        pop eax
        pop ebx
        pop ecx
        pop edx
        pop esi
        pop edi
        pop ebp
 
        ; // Fehlercode und Interruptnummer vom Stack nehmen
        add esp, 8
 
        ; // Ruecksprung zum unterbrochenen Code
        iretd


Vielleicht kann man hier jemand weiterhelfen. :-)

Jidder

  • Administrator
  • Beiträge: 1 625
    • Profil anzeigen
Gespeichert
« Antwort #1 am: 29. January 2016, 17:21 »
Das erste was mir auffällt, ist dass du die Interrupts nirgendwo aktivierst. Bevor du in kmain in die Endlosschleife gehst, solltest du das mit asm volatile("sti"); tun.
Dieser Text wird unter jedem Beitrag angezeigt.

lucky

  • Beiträge: 5
    • Profil anzeigen
Gespeichert
« Antwort #2 am: 29. January 2016, 17:31 »
Ja, könnte daran liegen.  Doch eigentlich wurde dies schon durch die Funktion zur Initialisierun der IDT getan.
Nachdem die IDT geladen wird. Trotzdem Vielen Dank  :-)

lucky

  • Beiträge: 5
    • Profil anzeigen
Gespeichert
« Antwort #3 am: 29. January 2016, 17:55 »
Hat jemand dieses Thema (Multitasking) schon bearbeitet oder den Sourcecode von dem Kernel welcher in diesem Tutorial benutzt oder gearbeitet wurde?

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #4 am: 29. January 2016, 17:59 »
Tut es denn mit sti immer noch nicht? Das wäre auch das, worauf ich getippt hätte, und ich sehe es in deinem Code beim Initialisieren der IDT nicht.

Den Tutorialkernel zum Stand direkt nach der Implementierung von Multitasking findest du hier.
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

lucky

  • Beiträge: 5
    • Profil anzeigen
Gespeichert
« Antwort #5 am: 29. January 2016, 19:26 »
Ja, stimmt ihr hattet recht, die Interrupts mussten noch aktiviert werden und jetzt gehts! :-)
Ich musste dann trotzdem noch einige kleine Probleme beheben, aber zum Schluss hat's funktioniert!

Vielen Dank, Jidder und Kevin! :-)

 

Einloggen