Lowlevel
Lowlevel => Lowlevel-Coding => Thema gestartet von: micha am 18. March 2012, 18:26
-
Hi
Ich bin grad dabei mir ein Tastaturtreiber zu programmieren.
Da hab ich nur ein Problem:
Wenn ich ein Befehl an die Tastatur sende wird eine #General Protection ausgelöst.
Hier der Code
kbc.h:
/**
* kernel/driver/kbc/kbc.h
* portdefinitionen & send_kbc_command
*
* (C) Copyright 2012 Michael Sippel
*/
#ifndef _KBC_H
#define _KBC_H
#include <stdint.h>
#include <portio.h>
#define KBC_PORT_KBDCOMMAND 0x60 /* Befehl an Tastatur */
#define KBC_PORT_KBCDATA 0x60 /* Daten-Puffer auslesen & schreiben */
#define KBC_PORT_KBCCOMMAND 0x64 /* Befehl an KBC */
#define KBC_PORT_KBCREGISTER 0x64 /* Statusregiser auslesen */
#define KBC_COMM_P1_PORT 0xC0
#define KBC_COMM_READ_OPP 0xD0
#define KBC_COMM_WRITE_OPP 0xD1
#define KBC_COMM_READ_CCB 0x20
#define KBC_COMM_WRITE_CCB 0x60
#define KBC_COMMAND_OK 0xFA
/* ---------- Befehl an KBC senden -------------------- */
static uint8_t send_kbc_command(uint8_t port,uint8_t command){
static int fz = 3; /// @p fz Fehlerzähler
uint8_t ret;/// @p ret Rückgabe vom KBC
//Warten bis Eigabepuffer leer ist
while( inb(KBC_PORT_KBCREGISTER) & 0x2 );
//KBC-Befehl senden
outb(port, command);
//Warten bis Antwort im Ausgabepuffer liegt
while( inb(KBC_PORT_KBCREGISTER) & 0x1 );
//Antwort einlesen
ret = inb(KBC_PORT_KBCDATA);
//Erfolgreich?
if(ret = KBC_COMMAND_OK || fz > 2){
fz = 3;//Fehlerzähler rücksetzen
return ret;//ENDE
}else{
--fz;//Fehlerzähler dekremantieren
send_kbc_command(port, command);//Nochmal versuchen
}
}
/* ---------- Befehl an Tastatur senden --------------- */
static uint8_t send_kbd_command(uint8_t command){
return send_kbc_command(KBC_PORT_KBDCOMMAND,command);
}
/* -- ENDE --- kbc.h -- */
#endif
keyboard.c:
/**
* kernel/driver/kbc/keyboard/init.c
* init_keyboard, keyboard_irq_handler
*
* (C) Copyright 2012 Michael Sippel
*/
#include <stdint.h>
#include <portio.h>
#include <stdbool.h>
#include <console.h>
#include "kbc.h"
#define KBD_COMM_LEDS 0xED /* LEDS ansteuern */
#define KBD_COMM_ECHO 0xEE /* Echo */
#define KBD_COMM_WDHR 0xF3 /* Wiederholrate setzen */
#define KBD_COMM_ACTV 0xF4 /* Tastatur aktivieren */
#define KBD_COMM_DAKV 0xF5 /* Tastatur deaktivieren */
#define KBD_COMM_STDW 0xF6 /* Standarderte setzen */
#define KBD_COMM_KRST 0xFF /* Tastatur-Reset */
#define KBD 0
/* ---------- Tastatur initalisieren ------------------ */
void init_keyboard(void){
color = 0x0F;debug(KBD," \n");
//Warten bis Tastatur nicht mehr im Resetmodus ist
while(! inb(KBC_PORT_KBCREGISTER) & 0x4 );
//Ausgabepuffer leeren
while (inb(KBC_PORT_KBCREGISTER) & 0x1){
//Solange Ausgabepuffer voll ist Datenregister lesen
inb(KBC_PORT_KBCDATA);
}
//LEDs ausschalten
send_kbd_command(KBD_COMM_LEDS);
outb(KBC_PORT_KBCDATA, 0);
//Schnellste Wiederholrate einstellen
send_kbd_command(KBD_COMM_WDHR);
outb(KBC_PORT_KBCDATA, 0);
//Tastatur aktivieren
send_kbd_command(KBD_COMM_ACTV);
}
/* ---------- IRQ handler für Tastatur ---------------- */
void keyboard_irq_handler(void){
printf("Taste gedrückt!\n\n");
}
/* ------- ENDE -- keyboard.c --- */
Grüße
-
Zwei Fehler sind mir beim Überfliegen des Codes aufgefallen:
if(ret = KBC_COMMAND_OK || fz > 2){
Du weist hier ret den Wert zu anstatt ihn zu vergleichen. Vergleiche gehen mit ==. Außerdem solltest du nochmal überlegen, was du dir mit der fz-Variable gedacht hast. Im jetzigen Zustand ist fz > 2 immer wahr, weil du fz mit 3 initialisierst.
while(! inb(KBC_PORT_KBCREGISTER) & 0x4 );
Hier fehlen Klammern um inb(KBC_PORT_KBCREGISTER) & 0x4. Beide Sachen hätte dir der GCC auch gesagt, wenn du ihm -Wall als Parameter übergeben hättest.
Diese beiden Dinge verursachen für sich genommen allerdings noch keinen GPF. Du solltest mal versuchen die genaue Stelle des Absturzes (=Speicheradresse des Befehls) zu lokalisieren. Wenn du deinen Kernel mittels objdump mit den Parametern -dS disassemblierst, kannst du möglicherweise sogar die entsprechende Codezeile feststellen. Dazu musst du den Kernel mit dem Parameter -g kompilieren.
-
Hallo!!
Hab' jetzt mein kernel mit -g kompiliert und dann folgendes gemacht:
ld -dS kernel -o kernel.txt
Diese Datei kann ich mir mit oketa ansehen. Aber da steht nur Assemblierter Code drin.
War das gemeint?
Wenn ja, finde ich mich da nicht rein.
Gruß
-
ld != objdump
-
das lässt sich jetzt dissasemblieren. Doch das passt nichgt alles in mein Terminal rein(der Anfang wird wieder gelöscht). Also kann ich nicht alles sehen. Den Parameter -o kennt objdump nicht.
gibt es da irgendwelche anderen?
-
Wenn du > kernel.txt an den Aufruf anhängst, wird die Ausgabe umgeleitet. Also sowas wie z.B. objdump -dS kernel.bin > kernel.txt
Das muss übrigens zwingend am Ende des Aufrufs stehen und funktioniert mit allen Programmen, die etwas ausgeben.
-
So ich hab jetzt alles zusammen (C/asm)
... maximale zeichen erreicht ... im Anhang ist das zu groß .. und wie jetzt?
-
Pastebin
-
Hier liegts: http://pastebin.com/dgs0FNXK (http://pastebin.com/dgs0FNXK)
-
Damit kann ich erstmal nichts anfangen. Ich wollte, dass du rausfindest, an welcher Stelle der GPF auftritt, um damit den Fehler weiter einzugrenzen. Genauer gesagt, ist der Wert von EIP interessant. Den musst du dann in dem disassemblieren Code suchen. Du kannst den Wert von EIP von deinem eigenen Interrupthandler ausgeben lassen, oder (was zuverlässiger ist) das Log von qemu benutzen. Wenn du qemu mit den Parametern -d int aufrufst, erstellt es eine Datei namens qemu.log im aktuellen Verzeichnis oder in /tmp. Da musst du jetzt von oben angefangen den GPF suchen. Den GPF erkennst du vermutlich daran, dass die Zeile mit "0: v=0d ..." anfängt. In der selben Zeile sollte der Wert für EIP stehen.
-
Eine Zeile, die mit
"0: v=0d ..."
anfängt, gibt es zwar in meinem Logfile nicht, aber so eine ähnliche: 0: v=21 e=0000 i=0 cpl=0 IP=0008:001014fd pc=001014fd SP=0010:00106590 EAX=000000ed
und der Wert von EIP steht in dieser zeile nicht.
EIP steht viermal im logfile (mit verschiedenen werten)
-
Wenn das:
001014fd
EIP sein soll, dann steht da ein nop.
Es steht nach call outb
-
Ja, das ist eip (und das 0008 davor ist cs). v=21 heißt aber, dass es kein GPF ist, sondern Interrupt 0x21, also wahrscheinlich IRQ 1, der Tastaturinterrupt.
-
Dann vertehe ich nicht, warum dann ein GPF ausgelöst wird und nicht mein irq handler.
?????
-
Folgt auf den Tastaturinterrupt eventuell noch ein weiterer Interrupt?
-
Ich hab da eine Vermutung.
Der Tastatur interrupt wird ausgelöst, aber es besitzt niemand die Privilegien, dies zu tun.
Das hab ich festgestellt, weil ich den interrupt auch nicht mit asm("int $0x21");
aufrufen kann. Statt dem IRQ kommt immer ein GPF.
Ich hab schon meine GDT verkleinert, dass ich nur noch ring 0 hab, aber das passiert trozdem.
Außerdem verstehe ich nicht, warum beim initalisieren der Tastatur der IRQ ausgelöst wird.
Gruß
-
Aha!
Ich kann nur die intr. bis 0x1f aufrufen (alle Exeptions) bei alen Anderen interrupts kommt ein GPF.
Ist möglicherweise der PIC falsch programmiert?
pic.c
/**
* kernel/interrupt/pic.c
* init_pic
*
* (C) Copyright 2012 Michael Sippel
*/
#include <portio.h>
#include "interrupt.h"
#define PIC_MASTER_COMM 0x20
#define PIC_MASTER_DATA (PIC_MASTER_COMM) + 1
#define PIC_SLAVE_COMM 0xa0
#define PIC_SLAVE_DATA (PIC_SLAVE_COMM) + 1
#define PIC_INIT 0x11
#define PIC_ICW4 0x01
#define PIC_EOI 0x20
/* ---------- Initalisierung des PIC ------------------ */
void init_pic(void){
// PICs initalisieren
outb(PIC_MASTER_COMM,PIC_INIT);
outb(PIC_SLAVE_COMM,PIC_INIT);
//Erstes IRQ
outb(PIC_MASTER_DATA, FIRST_IRQ); // Master
outb(PIC_SLAVE_DATA, FIRST_IRQ + 8);// Slave
//Slave auf IRQ 2 setzen
outb(PIC_MASTER_DATA, 0x04);
outb(PIC_SLAVE_DATA, 0x02);
//ICW 4
outb(PIC_MASTER_DATA, PIC_ICW4);// Master
outb(PIC_SLAVE_DATA, PIC_ICW4);// Slave
// All IRQs aktivieren / demaskieren
outb(PIC_MASTER_DATA, 0x0);
outb(PIC_SLAVE_DATA, 0x0);
}
Eigentlich dürfte da nix falsch sein.
Was gibt es noch für mögliche fehler?
-
Ich denke, dass die Einträge in der IDT nicht korrekt sind.
-
Denk ich nicht.
Die Testhandler (Hello-World-handler) fubktionieren, ABER nur auf den interrupts bis 0x1f
idt.c
/**
* kernel/interrupt/idt.h
* set_interrupt, init_idt, load_idt
*
* (C) Copyright 2012 Michael Sippel
*/
#include <stdint.h>
#include <stdbool.h>
#include "handlerextern.h"
#include "interrupt.h"
/* ---------- Globale Variablen ----------------------- */
static uint64_t idt[IDT_ENTRIES]; //Interupt Deskriptor Table
/* ---------- Eintrag in die IDT setzen --------------- */
static void set_interrupt(int i, uint32_t offset, uint16_t selector, int type, bool present, int dpl){
idt[i] = 0;
//untere 32-Bit
idt[i] = (offset & 0x0000ffff) | (selector << 16);
//obere 32-Bit
idt[i] |=(
( type << 8 ) |
( (dpl&3) << 13 ) |
( (present&1) << 15)|
( offset & 0xffff0000 )
) * 0x100000000;
}
/* ---------- IDT laden ------------------------------- */
void load_idt(void){
struct {
uint16_t size;
uint64_t pointer;
} __attribute__((packed)) idtp = {
.size = IDT_ENTRIES - 1,
.pointer = &idt,
};
asm("lidt %0" : : "m" (idtp));
}
/* ---------- IDT initalisieren ----------------------- */
void init_idt(void){
/// index, offset, selector, type, present, dpl
set_interrupt(0x00, (uint32_t)&int_handler0x00,0x08,INTERRUPT_GATE,TRUE, 0 );//Exeption
set_interrupt(0x01, (uint32_t)&int_handler0x01,0x08,INTERRUPT_GATE,TRUE, 0 );//Exeption
set_interrupt(0x02, (uint32_t)&int_handler0x02,0x08,INTERRUPT_GATE,TRUE, 0 );//Exeption
set_interrupt(0x03, (uint32_t)&int_handler0x03,0x08,INTERRUPT_GATE,TRUE, 0 );//Exeption
set_interrupt(0x04, (uint32_t)&int_handler0x04,0x08,INTERRUPT_GATE,TRUE, 0 );//Exeption
set_interrupt(0x05, (uint32_t)&int_handler0x05,0x08,INTERRUPT_GATE,TRUE, 0 );//Exeption
set_interrupt(0x06, (uint32_t)&int_handler0x06,0x08,INTERRUPT_GATE,TRUE, 0 );//Exeption
set_interrupt(0x07, (uint32_t)&int_handler0x07,0x08,INTERRUPT_GATE,TRUE, 0 );//Exeption
set_interrupt(0x08, (uint32_t)&int_handler0x08,0x08,INTERRUPT_GATE,TRUE, 0 );//Exeption
set_interrupt(0x09, (uint32_t)&int_handler0x09,0x08,INTERRUPT_GATE,TRUE, 0 );//Exeption
set_interrupt(0x0A, (uint32_t)&int_handler0x0A,0x08,INTERRUPT_GATE,TRUE, 0 );//Exeption
set_interrupt(0x0B, (uint32_t)&int_handler0x0B,0x08,INTERRUPT_GATE,TRUE, 0 );//Exeption
set_interrupt(0x0C, (uint32_t)&int_handler0x0C,0x08,INTERRUPT_GATE,TRUE, 0 );//Exeption
set_interrupt(0x0D, (uint32_t)&int_handler0x0D,0x08,INTERRUPT_GATE,TRUE, 0 );//Exeption
set_interrupt(0x0E, (uint32_t)&int_handler0x0E,0x08,INTERRUPT_GATE,TRUE, 0 );//Exeption
set_interrupt(0x0F, (uint32_t)&int_handler0x0F,0x08,INTERRUPT_GATE,TRUE, 0 );//Exeption
set_interrupt(0x10, (uint32_t)&int_handler0x10,0x08,INTERRUPT_GATE,TRUE, 0 );//Exeption
set_interrupt(0x11, (uint32_t)&int_handler0x11,0x08,INTERRUPT_GATE,TRUE, 0 );//Exeption
set_interrupt(0x12, (uint32_t)&int_handler0x12,0x08,INTERRUPT_GATE,TRUE, 0 );//Exeption
set_interrupt(0x13, (uint32_t)&int_handler0x13,0x08,INTERRUPT_GATE,TRUE, 0 );//Exeption
set_interrupt(0x14, (uint32_t)&int_handler0x14,0x08,INTERRUPT_GATE,TRUE, 0 );//Exeption
set_interrupt(0x15, (uint32_t)&int_handler0x15,0x08,INTERRUPT_GATE,TRUE, 0 );//Exeption
set_interrupt(0x16, (uint32_t)&int_handler0x16,0x08,INTERRUPT_GATE,TRUE, 0 );//Exeption
set_interrupt(0x17, (uint32_t)&int_handler0x17,0x08,INTERRUPT_GATE,TRUE, 0 );//Exeption
set_interrupt(0x18, (uint32_t)&int_handler0x18,0x08,INTERRUPT_GATE,TRUE, 0 );//Exeption
set_interrupt(0x19, (uint32_t)&int_handler0x19,0x08,INTERRUPT_GATE,TRUE, 0 );//Exeption
set_interrupt(0x1A, (uint32_t)&int_handler0x1A,0x08,INTERRUPT_GATE,TRUE, 0 );//Exeption
set_interrupt(0x1B, (uint32_t)&int_handler0x1B,0x08,INTERRUPT_GATE,TRUE, 0 );//Exeption
set_interrupt(0x1C, (uint32_t)&int_handler0x1C,0x08,INTERRUPT_GATE,TRUE, 0 );//Exeption
set_interrupt(0x1D, (uint32_t)&int_handler0x1D,0x08,INTERRUPT_GATE,TRUE, 0 );//Exeption
set_interrupt(0x1E, (uint32_t)&int_handler0x1E,0x08,INTERRUPT_GATE,TRUE, 0 );//Exeption
set_interrupt(0x1F, (uint32_t)&int_handler0x1F,0x08,INTERRUPT_GATE,TRUE, 0 );//Exeption
set_interrupt(0x20, (uint32_t)&int_handler0x20,0x08,INTERRUPT_GATE,TRUE, 0 );//IRQ
set_interrupt(0x21, (uint32_t)&int_handler0x21,0x08,INTERRUPT_GATE,TRUE, 0 );//IRQ
set_interrupt(0x22, (uint32_t)&int_handler0x22,0x08,INTERRUPT_GATE,TRUE, 0 );//IRQ
set_interrupt(0x23, (uint32_t)&int_handler0x23,0x08,INTERRUPT_GATE,TRUE, 0 );//IRQ
set_interrupt(0x24, (uint32_t)&int_handler0x24,0x08,INTERRUPT_GATE,TRUE, 0 );//IRQ
set_interrupt(0x25, (uint32_t)&int_handler0x25,0x08,INTERRUPT_GATE,TRUE, 0 );//IRQ
set_interrupt(0x26, (uint32_t)&int_handler0x26,0x08,INTERRUPT_GATE,TRUE, 0 );//IRQ
set_interrupt(0x27, (uint32_t)&int_handler0x27,0x08,INTERRUPT_GATE,TRUE, 0 );//IRQ
set_interrupt(0x28, (uint32_t)&int_handler0x28,0x08,INTERRUPT_GATE,TRUE, 0 );//IRQ
set_interrupt(0x29, (uint32_t)&int_handler0x29,0x08,INTERRUPT_GATE,TRUE, 0 );//IRQ
set_interrupt(0x2A, (uint32_t)&int_handler0x2A,0x08,INTERRUPT_GATE,TRUE, 0 );//IRQ
set_interrupt(0x2B, (uint32_t)&int_handler0x2B,0x08,INTERRUPT_GATE,TRUE, 0 );//IRQ
set_interrupt(0x2C, (uint32_t)&int_handler0x2C,0x08,INTERRUPT_GATE,TRUE, 0 );//IRQ
set_interrupt(0x2D, (uint32_t)&int_handler0x2D,0x08,INTERRUPT_GATE,TRUE, 0 );//IRQ
set_interrupt(0x2E, (uint32_t)&int_handler0x2E,0x08,INTERRUPT_GATE,TRUE, 0 );//IRQ
set_interrupt(0x2F, (uint32_t)&int_handler0x2F,0x08,INTERRUPT_GATE,TRUE, 0 );//IRQ
set_interrupt(0x30, (uint32_t)&int_handler0x30,0x08,INTERRUPT_GATE,TRUE, 0 );//Software
set_interrupt(0x31, (uint32_t)&test_int_handler00,0x08,INTERRUPT_GATE,TRUE,0);//Software
set_interrupt(0x32, (uint32_t)&test_int_handler01,0x08,INTERRUPT_GATE,TRUE,0);//Software
}
/*----- ENDE idt.c ---- */
-
Entschuldige die vielleicht blöde Frage, aber wie ist IDT_ENTRIES definiert?
-
.size = IDT_ENTRIES - 1,
Hier gehört die Größe in Bytes (minus 1) hin, nicht die Anzahl der Einträge.
@taljeth:
scheinbar ist IDT_ENTRIES = 256
-
Richtig!
IDT_ENTRIES ist 256.
Wenn ich bei size IDT_ENTRIES * 8 nehme starte qemu bei jedem int. neu
-
du hast nicht mit IDT_ENTRIES * 8 - 1 probiert, oder? Die minus 1 war schon richtig.
-
du hast nicht mit IDT_ENTRIES * 8 - 1 probiert, oder? Die minus 1 war schon richtig.
Na klar hab ich das!!!
-
Jetzt nochmal damit das klar ist:
1. Ich nehme .size = IDT_ENTRIES -1
- wenn IDT_ENTRIES = 256 ist funktioniert es
- wenn IDT_ENTRIES = irgendwas anderes ist, startet qemu neu
2. Ich nehme .size = IDT_ENTRIES * 8 -1
- Es funktioniert immer, wenn IDT_ENTRIES größer als die wirkliche Anzahl der interrupts ist
sonst bleibt qemu schwarz
Trotz dem bleibt ein Interrupt der >= 0x20 ist nicht ohne GPF
-
Steht vielleicht was interessantes im qemu.log?
-
wenn ich IDT_ENTRIES auf 256 gestetzt hab und *8 nehme wird der int erst ohne GPF audgelöst, springt in den handler und dann kommt der GPF.
im qemu.log steht eimal:
0: v=21 e=0000 i=0 cpl=0 IP=0008:00101835 pc=00101835 SP=0010:00105f70 EAX=000000ed
und dann:
check_exception old: 0xffffffff new 0xd
1: v=0d e=0204 i=0 cpl=0 IP=0008:00100d66 pc=00100d66 SP=0010:00105f6a EAX=00000000
-
Der nächste Schritt ist dann zu schauen, welcher Code an 0x100d66 steht, der löst den GPF aus. Relativ wahrscheinlich ist, dass es der Rücksprung vom Interrupthandler ist, der schiefgeht.
-
genau der ist es! :-)
und was ist daran jetzt falsch, das ein GPF ausgelöst wird :?
-
Schau dir die Werte auf dem Stack an. In der Regel hast du dort zu viel liegen, weil du vergessen hast, was abzuräumen. Es kann auch mal passieren, dass zu wenig drauf liegt, wenn du mit dem Stackpointer irgendwo durcheinandergekommen bist. Oder selten, der Stackpointer ist richtig und nur der Inhalt stimmt nicht mehr.
-
Der Stackpointer sieht verdächtig aus. Er sollte eigentlich ein Vielfaches von 4 sein. Vielleicht hast du irgendwo ein word gepusht oder gepoppt.
-
Ja, ESP ist nicht durch 4 teilbar. Ich hab nachgeschaut, ich hab immer versucht das `rechnen' mit dem Stackpointer zu vermeiden und bevor die komplette cpu gepopt wird nicht add $4, %esp sondern pop %eax verwendet. (%esp wegnehmen)
Beim wegnehmen der interruptnummer und dem Errorcode geht es ja nicht anders, da ist aber auch nichts (add $8, %esp).
Also ich hab' nicht mit irgendwelchen zahlen, die nicht durch 4 teibar sind, den Stackpointer verändert.
Wie könnten andere solche fehler entstehen?
-
Das kann zum Beispiel passieren, wenn push oder pop mit 16-bit Operanden verwendest. Also pushw/popw oder explizit ein 16-bit Register als Parameter übergeben. Allerdings ist das seltsam, dass der Stack trotzdem nicht ausgeglichen ist, außer du pusht etwas und nimmst es dann mit add/mov (oder anders) vom Stack, oder legst etwas mit sub/mov auf den Stack und poppst es.
-
Eigentlich habe ich kein pushw/popw verwendet...
-
Ich denke dann bleibt dir nichts anderes übrig, als mit dem Debugger da ranzugehen. Du könntest zum Beispiel einen Breakpoint an den Anfang des Interrupthandlers setzen und ab da schrittweise durchgehen.
Dazu musst du GDB und den QEMU Monitor nehmen. Single-Stepping in Assembler kannst du in GDB mit stepi machen. Die Register kannst du dir im QEMU-Monitor mit info registers anzeigen lassen.
Ein paar Links:
http://www.lowlevel.eu/wiki/Debugging#bochs_.2F_QEMU
http://sourceware.org/gdb/current/onlinedocs/gdb/Continuing-and-Stepping.html#Continuing-and-Stepping
http://qemu-buch.de/de/index.php/QEMU-KVM-Buch/_Anhang/_QEMU-Monitor
Alternative wäre den kompletten Code irgendwo hochzuladen. Dann kann ich mir (oder wer anders) sich das mal angucken, und ich muss nicht mehr rumraten.
-
Nun mit dem Debugger komme ich noch nicht wirklich zurecht.
Also ich mache bisjetzt folgendes:
gdb [dann kommt hier das file]
Bla.. BLa ...Bla
(gdb) target remote :1234
[dann kommt hier das file]
soll ich da mein iso-image angeben?
und gleichzeitig:
qemu -S -cdrom image.iso
Und wie komme ich zu dem Qemu-Monitor?
Kann mir jemand den Debugger erklären? :?
-
Das Wichtige steht alles im Wiki-Artikel. GDB kriegt die Kernel-Binary als Parameter, und qemu musst du mit -s -S aufrufen. Du musst in gdb einen Breakpoint auf das Label mit dem Interrupthandler setzen mittels break intr_common_handler oder wie auch immer du das genannt hast. Dann startest du die Ausführung mit cont (oder c). Beim Interrupthandler angekommen, nutzt du stepi (oder si) um Schrittweise weiter zu gehen (oder du setzt halt mehr Breakpoints). Wenn du vorher einmal display/i $pc eingibst, zeigt dir GDB sogar immer den nächsten Assemblerbefehl an. In die Qemu-Konsole kommst du wenn du in Qemu Strg-Alt-2 drückst. Die Dokumentationen dazu hab ich ja bereits verlinkt.
-
Ich kann jetzt mit dem Debugger arbeiten, ich komme auch an den Qemu-Monitor, doch dann ist meine Emulation verschwunden und die Register und der ganze andere kram passt auch nicht auf das kleine 80x25 "kästchen".
Also hab ich breakpoints (ohne Debugger) mit asm("int $3"); gesetzt und mir im qemu-log die Register angesehen:
Vor dem interruptaufruf war ESP ok. Nachher irgendwie falsch (kein vielfaches von vier). Aber mein Handler sieht nur so aus:
.extern keyboard_irq_handler
...
.global int_handler0x21
int_handler0x21:
call keyboard_irq_handler
iret
...
...
und wers wissen will..
void keyboard_irq_handler(void){
printf("Taste gedrueckt!\n");
}
Ich mach nichts mit dem Stack.
könnte möglicherweise der Timerinterrupt dazwischen funken und der Stack verrutscht?
-
Das dir eine C-Funktion ESP verschiebt ist eher unwahrscheinlich. Ich weiß nicht genau wo bei dir vorher und nachher ist. Also direkt nach dem int_handler0x21 label und direkt vor dem iret müssen ESP, (und alle anderen Register auch) gleich sein. Es sieht so aus also würde es hier nicht an esp liegen (darum kümmert sich gcc) sondern an den anderen Registern die in keyboard_irq_handler verändert werden.
Edit: Eigentlich sollten die Fehler wegen falschen Register-Werten erst später auftreten und nicht schon beim iret. Wenn dein printf groben Unfug baut wäre es auch denkbar des es die Rücksprungadresse für den iret manipuliert. (mit var_args hättest du da zumindest die entsprechenden Pointer zu in der Hand)
-
Mein Printf (sprintf) habe ich ja selber implementiert, aber komplett in C. und ich glabe nicht, dass da was schief läuft. die cpu wird doch in einer C-Funktion gesichert.
-
Mein Printf (sprintf) habe ich ja selber implementiert, aber komplett in C. und ich glabe nicht, dass da was schief läuft.
ich habe auch nicht erwartet das du da Assmebler benutzt hast ^^
die cpu wird doch in einer C-Funktion gesichert.
Nein! Nicht alle; EAX z.B. nicht. Und zumindest bei AMD64 gibt es noch mehr Register um deren Sicherung sich der Aufrufer kümmern muss.
[edit]Registers %ebp, %ebx, %edi, %esi, and %esp ‘‘belong’’ to the cal-
ling function. In other words, a called function must preserve these registers’
values for its caller. Remaining registers ‘‘belong’’ to the called function. If a cal-
ling function wants to preserve such a register value across a function call, it must
save the value in its local stack frame.
-
Naja, wenn der Handler leer ist
( (Klammer Auf) int_handler_0x21:
iret
) (Kalmmer zu)
dann kommt die Exeption trotzdem
-
Hi,
das hier ist keine Antwort sondern eine Frage.
Und zwar.. ich hab meine Interrupt Service Routinen geschrieben und soweit funktioniert das auch.
Wenn ich jetzt eine Taste drücke, dann empfange ich diesen Interrupt nur ein einziges Mal.
Jeder weitere Tastendruck wird dann nicht mehr verarbeitet (IRQ 0 wird jedoch permanent weiter
bearbeitet).
void isr_handler(cpu_state_t status)
{
if(status.int_no <= 0x1F) {
dprint("[ISR] Exception captured. Kernel stopped.");
while(1) {
// Stop CPU
asm volatile ("cli; hlt");
}
}else if (status.int_no >= 0x20 && status.int_no <= 0x2f) {
dprint("[ISR] Hardware interrupt received");
if (status.int_no >= 0x28) {
outb(0xA0, 0x20);
}
outb(0x20, 0x20); // EOI
}
dprint("[ISR] Received (but unhandled) interrupt: 0x%X", status.int_no);
}
Hat jemand eine Ahnung woran das liegen könnte?
-
Du musst den Scancode mit inb(0x60) lesen, um den Tastaturpuffer zu leeren.
-
Danke :D
-
Hier mal mein Quälcode :wink: (http://forum.lowlevel.eu/index.php?topic=3011.0 (http://forum.lowlevel.eu/index.php?topic=3011.0))
-
Wie baust du den Kernel? Das mal zu testen zu können würde vermutlich schneller zu Erfolg führen als nur den Quellcode an zu sehen.
Was mir so aufgefallen ist:
cpu.S:
push-/pop_cpu: rufst du zwar nirgends auf, aber das Funktioniert so natürlich nicht. als Makro würde schon eher gehen.
irq/handler.S:
irq_handler0x21: ist weder leer, noch werden die Register ordnungsgemäß wieder hergestellt.
syscall:
int_handler0x30: ignoriert den rückgabe wert von syscall
testhandler:
das geht so nicht, einfach asm("iret") in ne C-Funktion reinhauen. Guck dir mal `objdum -d testhandler.o` an. Da siehst du dass der stack erst noch dem iret, mit leave wieder aufgeräumt wird.
[Edit]
sprintfr:
du legst nirgends einen puffer für dein rückgabe string an! du verwendest rs uninitialisiert. das ist pures Glück wenn das mal gut geht.
-
irq/handler.S:
irq_handler0x21: ist weder leer, noch werden die Register ordnungsgemäß wieder hergestellt.
Das war ja auch nur zum rumprobieren..
syscall:
int_handler0x30: ignoriert den rückgabe wert von syscall
Das ist ja noch garni fertig
testhandler:
das geht so nicht, einfach asm("iret") in ne C-Funktion reinhauen. Guck dir mal `objdum -d testhandler.o` an. Da siehst du dass der stack erst noch dem iret, mit leave wieder aufgeräumt wird.
Da hab ich auch bloß probiert (ich probiere gern :wink:; vergessen wieder raußzunehmen)
sprintfr:
du legst nirgends einen puffer für dein rückgabe string an! du verwendest rs uninitialisiert. das ist pures Glück wenn das mal gut geht.
Eingentlich ging das immer
-
Wie baust du den Kernel? Das mal zu testen zu können würde vermutlich schneller zu Erfolg führen als nur den Quellcode an zu sehen.
src/kernel/Makefile
SRCS = $(shell find -name '*.[cS]')
OBJS = $(addsuffix .o,$(basename $(SRCS)))
CC = gcc
LD = ld
SRC = ..
KERNELSRC = .
IMGSRC = ../../img
KERNEL = $(IMGSRC)/sys/kernel/kernel
INC = -I $(SRC)/include
KERNINC = -I $(KERNELSRC)/include
ASFLAGS = -m32 $(INC)
CFLAGS = -m32 -Wall -fno-stack-protector -nostdinc $(INC)
LDFLAGS = -melf_i386 -Ttext=0x100000
KERNELCF = $(CFLAGS) $(KERNINC)
KERNELASF= $(ASFLAGS) $(KERNINC)
pseudolink: $(OBJS)
#Kernel kompiliert.
%.o: %.c
$(CC) $(KERNELCF) -c -o $@ $^
%.o: %.S
$(CC) $(KERNELASF) -c -o $@ $^
clean:
rm $(OBJS)
.PHONY: clean
Makefile
SRC = ./src
KERNELSRC = $(SRC)/kernel
SRCS = $(shell find -name '*.[cS]')
OBJS = $(addsuffix .o,$(basename $(SRCS)))
CC = gcc
LD = ld
INC = -I $(SRC)/include
IMGSRC = ./img
KERNEL = $(IMGSRC)/sys/kernel/kernel
IMGNAME= OrangePalm0-0-1winterBeta3
ASFLAGS = -m32 $(INC)
CFLAGS = -m32 -Wall -fno-stack-protector -nostdinc $(INC)
LDFLAGS = -melf_i386 -Ttext=0x100000
$(KERNEL): $(OBJS)
$(LD) $(LDFLAGS) -o $@ $^
iso-img:
mkisofs -R -b boot/grub/stage2_eltorito -no-emul-boot -boot-load-size 4 -boot-info-table -o $(IMGNAME).iso $(IMGSRC)
start-iso-qemu:
qemu -d int -cdrom $(IMGNAME).iso
print-src:
echo $(shell find -name '*.[chS]')
clean:
rm $(OBJS)
.PHONY: clean
mkall
# Kernel kompiliern
cd src/kernel
make
cd ../..
# Rest kompilieren & linken
make
# iso-image erstellen
make iso-img
# iso-image mit qemu starten
make start-iso-qemu
-
Du hast 16-Bit Interrupt Desptoren angelegt.
diff --git a/src/kernel/interrupt/idt.c b/src/kernel/interrupt/idt.c
--- a/src/kernel/interrupt/idt.c
+++ b/src/kernel/interrupt/idt.c
@@ -21,6 +21,7 @@ static void set_interrupt(int i, uint32_
//obere 32-Bit
idt[i] |=(
( type << 8 ) |
+ ( 1 << 11) |
( (dpl&3) << 13 ) |
( (present&1) << 15)|
( offset & 0xffff0000 )
Ich würde dir übrigens raten sämtliche Compiler-Warnungen auszumerzen. Dein set_gdt_entry z.B. enthält nen fehler auf den du sogar hingewiesen wirst.
-
Du hast 16-Bit Interrupt Desptoren angelegt.
Was hab ich da falsch gemacht? Wie mach ich's richtig?
-
Du hast 16-Bit Interrupt Desptoren angelegt.
Was hab ich da falsch gemacht? Wie mach ich's richtig?
Du hast vergessen das Flag für 32-Bit Descriptoren zu setzen.
Füge die mit '+' gekennzeichnete Zeile in deinen Code ein.
-
Ich hab jetzt die zeile eingefügt
( 0x800 ) | // 1 << 11
nur leider geht es immernoch nicht :-(
Liegt der Fehler in der GDT? Es kommt ja ein GPF und es werden Segmente übergeben (als Errorcode), die ich garnicht hab!
-
Also die Zeile ist bei mir im Moment der einzige Unterschied zu dem von dir hochgeladenen Code. Und es gibt kein GPF.
Stimmt jetzt wenigsten dein eigener CPU dump mit dem von QEMU überein?
Deine GDT ist zwar falsch(64bit segmente im Protected mode), aber wenn QEMU das stören würde müsste es schon beim neuladen der Segmentregister meckern.
-
Also EAX,EBX,ECX und EDX stimmen überein. Nur die anderen (ESP,CS...) nicht.
-
Die sollten jetzt auch übereinstimmen. Testest eventuell noch die alte Version? ('make clean')
-
Jetzt wird qemu nach init_idt schwarz :|
-
komisch.. :| mit deinem iso gehts
-
Jetzt wird qemu nach init_idt schwarz :|
Dann hast du wohl noch andere Änderungen gemacht. Versuch es doch mal mit dem Quellcode den du hier hochgeladen hast.
-
Super!!!!! mit dem gehts :-) :-) :-D
-
Jetzt komme ich dem Tastaturtreiber schon näher.
Wenn ich einen Befehl an die Tasratur sende, wird der IRQ 0x21 ausgelöst. Ist das richtig?
Außerdem wird bei Tastendruck kein Interrupt ausgelöst.
-
OK. es geht :lol: :lol:
Ich hab vergessen in init ein while(1); zu setzten nach init wird ja der prozessor angehalten :-)
Danke für die Hilfe