Beiträge anzeigen

Diese Sektion erlaubt es dir alle Beiträge dieses Mitglieds zu sehen. Beachte, dass du nur solche Beiträge sehen kannst, zu denen du auch Zugriffsrechte hast.


Nachrichten - oern

Seiten: [1] 2 3
1
Offtopic / Re: Hardware selbstgebaut
« am: 24. November 2013, 18:27 »
Ich habe auch noch einen Z80 mit SRAM und ein paar anderen Bauteilen in einer Kiste, das habe ich mir vor knapp zwei Jahren bestellt und wollte eigentlich eine Art Altair8800-Klon bauen (allerdings noch etwas weniger Komfort, z.B. keine {Examine,Deposit}-Next-Möglichkeit). Ich habe allerdings vorher und nachher nie etwas praktisches mit Elektronik gemacht (außer einen ASURO-Kurs an der örtlichen TU) und irgendwie hat leider nicht einmal das Testen des Z80 funktioniert (NOP an den Datenbus, RS-Flipflop an den Takteingang und LEDs an den Adressbus und dann Single-Steppen), die LEDs taten jedenfalls nicht das, was sie sollten. Seitdem habe ich mich nicht mehr getraut, damit wieder etwas anzufangen... Müsste ich jetzt mal machen.
Wie funktioniert denn die Dekodierlogik bei dir? Wenn ein Slot 32KiB Speicher anbieten kann und es 8 Slots (einschließlich des AVR) gibt, geht meine Rechnung irgendwie nicht auf (64KiB Speicher + 64KiB I/O).
Und wozu genau braucht man diese Bustreiber, durch die alle Signale von/zu der CPU gehen?

oern
2
Lowlevel-Coding / Re: Welche Geräte gehören zu PCI?
« am: 25. May 2013, 11:15 »
Wenn man keinen Wert auf Kompatibilität legt und den PCI-Treiber eh nur benutzt, um an Port- und Speicheradressen und den Interrupt zu kommen bzw. eine Liste der angeschlossenen Geräte zu bekommen, kann man die Werte auch erst einmal hartkodieren, spätestens, wenn man das Betreibssystem auf mehr als einem Rechner laufen lassen will, gibt es dann aber wahrscheinlich Probleme.
3
OS-Design / Re: VFS-Konzept
« am: 14. May 2013, 18:41 »
Hallo,

vielen Dank erstmal für die Antwort!  :-)

Der Kernel ist ein modularer Monolith,
Werden Treiber im Kernel- oder im Userspace ausgeführt? :-)
Ich dachte, das einzige Kriterium für einen monolithischen Kernel sei, dass Treiber im Kernelmodus laufen? Bei mir jedenfalls schon.

Den grundlegenden Teil des VFS bilden die Treiber (Driver). Ein Treiber wird vom System anhand eines menschenlesbaren Namens identifiziert und kann Fähigkeiten (Capabilities) bereitstellen.
Der Begriff "Fähigkeit" passt nicht, weil du hier Geräteklassen identifizierst. Fähigkeiten wären eher "kann gelesen werden", "kann geschrieben werden" oder "kann Audio ausgeben", "kann Bilder einlesen".
OK, ich suchte nur nach irgendeinem Wort, das in etwa beschreibt was ich meine. Ich sehe jetzt auch ein, dass Geräteklassen ein besserer Begriff ist.

dass z.B. ein Treiber für die serielle Schnittstelle ja sowohl HID als auch Video-Gerät ist und man so nicht zwei Treiber zu registrieren muss
Eine serielle Schnittstelle ist eine serielle Schnittstelle, nicht Eingabegerät und nicht Kamera. Es ist auch nicht zielführend, mehrere Treiber an eine serielle Schnittstelle zu binden, weil da nur ein Gerät dranhängen kann. (Wenn mehrere Geräte dranhängen, kann man z.B. einen Multiplex-Treiber dazwischen schieben, der von der Beschaltung abhängt. Beispielsweise serielle IR-Sender, die eine Rx-Leitung anbieten, weil der Pin noch frei war: Der Multiplexer bindet dann an die serielle Schnittstelle und stellt eine "serielle Schnittstelle" (nur lesen) und eine "IR-Sendediode" (nur schreibend) zur Verfügung.)
Heißt das, die serielle Schnittstelle wäre eher der Bus-Klasse zuzuordnen?

Außerdem hat er eine Liste von Geräten, die von diesem Treiber verwaltet werden.
Ich sehe den Treiber als notwendiges Übel, um mit einem Gerät zu kommunizieren. Dann ist es sinnvoller, die Liste der Treiber zu haben, die für die verschiedenen Geräte zuständig sind.
Könnte man natürlich auch machen, ich dachte, dass das einfacher ist, um z.B. Geräte zu erkennen (da dafür der Treiber zuständig ist) oder beim Entfernen des Treibers alle Geräte, die über ihn verwaltet werden, mit zu entfernen. Kommt meiner Meinung nach mehr oder weniger aufs selbe hinaus.

Ein Gerät wird vom System anhand zweier ID-Nummern identifiziert. Diese müssen nicht unbedingt benutzt werden (0 ist default), und ich denke, dass zwei ausreichen. Bei einem ATA-Treiber kann z.B. die erste für die Platte und die zweite für die Partition stehen, bei Grafikkarten eins für die Karte und eins für den Bildschirm usw.
Was machst du mit Subpartitionen (vgl. Minix, BSD, Solaris)? Bei Grafikkarten gibt es ebenfalls mehr Ebenen: Grafikkarte, Framebuffer, CRTC, Encoder, Output. Mit "nur zwei" schränkst du dich da sehr unnötig ein.
Dass das nicht genug ist habe ich schon gewusst, lieber wäre mir ein Array bzw. eine Liste. Jedenfalls will ich die Geräte eigentlich nicht als Dateien im Baum des Treibers verwalten (oder was auch immer ich stattdessen verwenden sollte).

Vollständige Pfade beginnen mit dem Namen des Treibers.
Woher weiß ich, welcher Treiber für ein bestimmtes Gerät zuständig ist?
Das genaue Gegenteil (abgesehen vom Unixkonzept) wären meiner Meinung nach die Laufwerksbuchstaben in Windows. Dort hat der Anwender mehr oder weniger keine Kontrolle darüber, welcher Buchstabe letztlendlich für welches Gerät steht. Ich halte es für besser, wenn der Anwender explizit angibt, was er haben will, was auch den Namen des Treibers mit einschließt. Einen "eindimensionalen" Dateibaum, d.h. es gibt nur eine Wurzel, und Mounten möchte ich eigentlich nicht haben.

An dem ersten Beispiel sieht man auch, dass Dateisysteme nicht über den Dateisystemtreiber, sondern über den Massenspeichertreiber angesprochen werden, der den zu benutzenden Treiber ermittelt (und natürlich zwischenspeichern darf).
Das macht das Layering kaputt:
- Woher weiß der Massenspeichertreiber, welche Dateisystemtreiber es gibt und welche er benutzen darf?
- Warum sollte ein Anwendungsprogramm mit der Festplatte reden wollen, wenn es doch nur Dateien aus dem Dateisystem verarbeiten will?
- Was machst du mit virtuellen Dateisystemen, für die es keine Massenspeicher gibt ("Netzwerkumgebung" als Beispiel)?
Also das Herausfinden des FS-Treibers wäre glaube ich kein großes Problem, da holt sich der Storage-Treiber "einfach" beim ersten Zugriff eine Liste der Treiber der Dateisystemklasse und geht die durch. 2. und 3. sind in der Tat Probleme. Umgekehrt ist die Frage wie das Layering stattfinden soll. Die Pfad-Pipes von tyndur finde ich ehrlich gesagt nicht besonders schön (auch wenn sie ja ihren Zweck erfüllen), denn dann müsste ja der FS-Treiber wissen, von wo er lesen soll.

Die einfachste, aber unschönste Variante wäre wahrscheinlich, für alle Geräteklassen eigene Callbacks und Interfaces zu benutzen. Das wollte ich mit diesem VFS aber ja möglichst vermeiden. Aber die Treiberschnittstelle ist (meiner Meinung nach) eh der zentrale Teil eines Betriebssystems und soll schön werden, daher lohnt es sich auch, mehr Arbeit hineinzustecken.

Gruß,
oern
4
OS-Design / VFS-Konzept
« am: 13. May 2013, 15:54 »
Hallo,

ich entwickle gerade den Treibermanager / ein VFS für mein OS und wollte es hier mal vorstellen. Kritik und Fälle, in denen das Konzept nicht ausreicht, sind erwünscht. :-)

Der Kernel ist ein modularer Monolith, d.h. die Kerneldatei beinhaltet nur den Prozess (mit Threads)-, Speicher-, Interrupt-, Modul- und Treiber/Dateimanager. Die Treiber werden von der Modulverwaltung entweder als Multibootmodul (für Boottreiber) oder aus einer Datei geladen. Dabei verwende ich ELF-Relocatables (wie der Linuxkernel). Wenn ein Modul geladen wird, wird (ebenfalls wie im Linuxkernel) eine Funktion mod_init aufgerufen, in der der Treiber z.B. Caches anlegen und sich beim Treibermanager anmelden kann. Beim Entfernen wird mod_destroy aufgerufen, die u.a. Aktionen aus mod_init rückgängig macht. Abhängigkeitsverwaltung wird es vorerst nicht geben.

Den grundlegenden Teil des VFS bilden die Treiber (Driver). Ein Treiber wird vom System anhand eines menschenlesbaren Namens identifiziert und kann Fähigkeiten (Capabilities) bereitstellen. Als Fähigkeiten sind erst mal geplant: Speichermedium, Dateisystem, Bus, HID (Human Interface Device) und Video (dass das nicht ewig reicht, ist mir klar, z.B. Netzwerk). Dies hat den Vorteil (gegenüber einem System, das nur Typen unterstützt), dass z.B. ein Treiber für die serielle Schnittstelle ja sowohl HID als auch Video-Gerät ist und man so nicht zwei Treiber zu registrieren muss. Ein Treiber stellt außerdem ein einziges Callback bereit, das als Parameter eine IO-Anfrage bekommt (dazu später mehr). Außerdem hat er eine Liste von Geräten, die von diesem Treiber verwaltet werden.

Ein Gerät wird vom System anhand zweier ID-Nummern identifiziert. Diese müssen nicht unbedingt benutzt werden (0 ist default), und ich denke, dass zwei ausreichen. Bei einem ATA-Treiber kann z.B. die erste für die Platte und die zweite für die Partition stehen, bei Grafikkarten eins für die Karte und eins für den Bildschirm usw. Das Gerät hat auch eine Fähigkeiten-Maske, d.h. ein Gerät, das ein Treiber registriert, muss nicht alle Funktionen unterstützen, die der Treiber bereitstellen kann. Es beinhaltet außerdem den Root-Knoten des Dateibaums des Gerätes.

Ein Knoten wird anhand eines menschenlesbaren Namens identifiziert (d.h. Verzeichniseinträge und Knoten werden nicht getrennt betrachtet). Ein Knoten hat Attribute (z.B. Zugriffzeit, ACLs, Größe) und einen Typ (nur Datei, Symlink oder Verzeichnis).

Um eine IO-Operation mit einem Knoten durchzuführen, muss man das oben genannte Callback des Treibers benutzen und eine IO-Anfrage vorbereiten. Die Anfrage enthält den Knoten, auf den sie sich bezieht, die Funktion, in der das Gerät benutzt wird (entspricht den Capabilities, es darf aber nur ein Bit gesetzt sein), den Typ der Anfrage (Lesen, Schreiben, Attribute lesen, Attribute Schreiben, Erstellen, Entfernen, etc.), Daten und die Größe der Daten.

Vollständige Pfade beginnen mit dem Namen des Treibers. Darauf folgen ggf. in Klammern bis zu zwei ID-Nummern (mit Komma getrennt) und ein Doppelpunkt. Danach folgt (ohne führendes '/', da ja sowieso von der Wurzel ausgegangen wird) der Pfad innerhalb des Gerätes, ganz "normal" mit Slashes abgetrennt. Beispiel: ata(0,1):foo/bar.txt oder vga:modeinfo.
An dem ersten Beispiel sieht man auch, dass Dateisysteme nicht über den Dateisystemtreiber, sondern über den Massenspeichertreiber angesprochen werden, der den zu benutzenden Treiber ermittelt (und natürlich zwischenspeichern darf).
Andere Treibertypen sollen vom Usermode nicht direkt über das VFS angesprochen werden, sondern es soll einheitliche Schnittstellen für die jeweiligen Fähigkeiten geben, z.B. hat ein Grafik/Videogerät standardmäßig eine Datei "modeinfo" in seinem Wurzelverzeichnis, aus der die verfügbaren Modi (vermutlich nur maschinenlesbar) ausgelesen werden können, und "control", in die (vermutlich auch nur maschinenlesbar) sozusagen "male ein Rechteck vom Punkt (a;b) zum Punkt (c;d)" geschrieben werden kann.

Noch weiß ich nicht genau, wie ich das Businterface gestalten soll, da damit sowohl z.B. PCI als auch USB verwaltet werden soll, ebenso wie das HID-Interface da man damit auch sowohl Zeigegeräte als auch Tastaturen etc. verwenden können soll.

Falls jemand bis hier gelesen haben sollte (oder auch sonst), vielen Dank, ich freue mich auf Kritik.
Viele Grüße,

oern
5
Softwareentwicklung / Re: Paging Error 14
« am: 11. May 2013, 14:09 »
Mappst du das Prozessimage denn auch mit dem Userflag? Ansonsten löst die CPU die Exception aus, weil der Task im Usermode laufen soll und die Seiten so gemappt sind, dass nur Kernelmode-Code darauf zugreifen darf. Das Problem und eine sehr provisorische Lösung wird im Tutorial beschrieben (kurz vor "Aufteilung des Adressraums").
6
Das Wiki / Re: EU und Forum
« am: 06. May 2013, 22:07 »
Der Autor meinte mit an Sicherheit grenzender Wahrscheinlichkeit den MASM (ein Assembler von Microsoft). Das erste M darin steht (wenn nicht für Microsoft) für Macro, die structs sind also keine Maschinenbefehle sondern Anweisungen an den Assembler, an bestimmten Stellen Platz frei zu lassen bzw. mit Labels auf bestimmte Speicherstellen zugreifen zu können, um dem Programmierer die Arbeit zu erleichtern.
7
OS-Design / Re: IRQs im Kernel
« am: 01. May 2013, 17:03 »
Oh... Ok, Danke. Da hätte ich auch selbst drauf kommen müssen.
Und falls der nächste oder übernächste Task irgendwo im Kernel sein sollte, programmiert man ein Timeout, oder? Langfristig (falls es dazu kommen sollte; siehe http://xkcd.com/844/), sollte ich mir eh Gedanken machen, wo ich Locks brauche, besonders bei der Taskverwaltung und der virtuellen Speicherverwaltung scheint das recht kompliziert zu werden.

oern
8
OS-Design / IRQs im Kernel
« am: 01. May 2013, 14:41 »
Hallo,

ich habe mal wieder einen Kernel begonnen (modularer Monolith). Dabei habe ich mir folgendes Problem überlegt:
Der Kernel nicht unterbrechbar ist, d.h. alle Interrupts sind in der IDT als "Interrupt Gate" eingetragen. Ein Programm ruft eine Kernelfunktion per Syscall auf (danach sind HW-Interrupts deaktiviert). In dieser Kernelfunktion (z.B. Lesen aus einer Datei) muss auf einen Block eines Massenspeichermediums zugegriffen werden, der nicht im Cache ist. Dies soll per DMA geschehen, d.h. der Storage-Controller löst nach Abschluss des Transfers einen IRQ aus. Wie soll der Kernel davon erfahren?
Die Lösung wäre natürlich, zumindest den Syscall und eventuell die IRQs unterbrechbar zu machen, aber das erfordert ja sehr viel Locking (was für SMP aber ja eh notwendig wird). Gibt es einfachere Lösungen oder existiert dieses Problem überhaupt nicht?

oern
9
Offtopic / Re: Welchen Desktop benutzt ihr?
« am: 12. July 2012, 20:34 »
Ich benutze (im Moment) Xfce auf Arch Linux mit gdm als Login-Manager und bin damit relativ zufrieden (auch wenn ich es irgendwie nicht hinbekomme, dass wenn ich das Terminal über den Desktop starte, es den Fokus bekommt; bei allen anderen Programmen geht das).
Vorher habe ich kurze Zeit nur Openbox mit tint2 benutzt und die restlichen Komponenten aus verschiedenen DEs zusammengesucht.
Noch vorher (insgesamt wohl die längste Zeit) hatte ich KDE4 (teilw. Kubuntu und Arch), aber das war mir doch zu viel.
Gruß,
oern
10
Lowlevel-Coding / Re: «push ax» Problem
« am: 10. July 2012, 21:38 »
Falls du die C-Calling-Convention verwendest (was du anscheinend tust), musst du den Operanden auf den Stack legen (und zwar den, den du in C als erstes angeben würdest als letztes).

Gruß,
oern
11
Mit Multiboot-Modul ist gemeint, dass GRUB2 selber modularisiert ist. Die Fähigkeit, Multibootkernel zu laden ist dort in ein Modul namens multiboot ausgelagert, und das kann man wie taljeth schon sagte mit "insmod multiboot" irgendwo oben in der grub.cfg laden.
12
Hallo,

ich wollte zufällig auch heute hier mal nach anderen Leuten fragen, die eine Linux-Distribution mitentwickeln wollen.
Ich habe da auch schon halbwegs Vorstellungen von, was das werden soll, und auch schon mal ein LFS (Linux from Scratch) gebaut.
Die Distri soll auf dpkg aufbauen. Davon soll es 4 Versionen geben: Desktop (mit KDE oder GNOME; für Anfänger gedacht), Programmierer (mit Programmiertools, einem WM z.B. openbox oder blackbox, und Quellpaketen, die zwar nicht zum Kompilieren gedacht sind, aber das Mitliefern des Quellcodes unterstützen), "Geek" (ohne GUI, sonst wie die Programmier-Version) und eventuell noch eine Server-Variante. Diese Einzeldistributionen sollen aber nicht untereinander kompatibel sein, dafür sollen diese aber besser an ihr Einsatzgebiet angepasst sein (ungefähr das, was Gentoo mit den USE-Flags erreicht). Ich habe auch schon ein paar Pakete kompiliert und die dpkg-Dateien erstellt, aber für 4 Versionen (oder auch schon nur eine) bräuchte ich natürlich Helfer.

Wenn du Linux als Kernel benutzen willst, würde ich dir auch Linux from Scratch empfehlen, da baust du dir das gesamte System aus dem Quellcode. Natürlich musst du dich dafür mit Linux auskennen, z.B. weil du deinen eigenen Kernel konfigurieren musst und erstmal keine grafische Oberfläche hast.
Die anderen freien Betriebssysteme sind wahrscheinlich auch alle Unix-Klone, aber GNU ist ja noch nicht wirklich fertig und die BSD-Systeme haben ja meistens ihr eigenes Userland, d.h. es ist nicht vorgesehen, etwas eigenes darauf aufzubauen (wenn auch möglich).

Gruß,
oern
13
Lowlevel-Coding / Re: Tutoral Teil 6: GPF-Problem
« am: 08. May 2012, 16:14 »
Aha, offenbar passiert der GPF, wenn du zum zweiten Task (der mit den Bs) wechselst, aber schon im Scheduler. Daher kommen wohl auch die Werte für cs und ss und das nicht gesetzte IE-Flag.
Vermutlich vergisst du aber im Interruptstub, ds und es auf 0x10 zu setzen, denn das wird dir nicht von der CPU abgenommen.
Außerdem würde ich mir mit objdump anschauen, was an der Addresse 0x10140e für Code steht, denn dort scheint ja das Problem zu liegen. Wie gesagt, der Quellcode davon (auf pastebin), wäre nicht schlecht.

Unterschiede zwischen GRUB und qemu sollte es eigentlich nicht geben. Ich verwende mittlerweile lieber qemu direkt, weil ich es mir dann sparen kann, ein GRUB-Image zu erstellen.

Gruß,
oern
14
Lowlevel-Coding / Re: Tutoral Teil 6: GPF-Problem
« am: 08. May 2012, 15:48 »
Erstens sind die Interrupts nur dann aus, wenn der behandelte Interrupt als Interrupt Gate in der IDT eingetragen ist.
Zweitens sollte der GPF-Handler die Registerwerte ausgeben, die der Prozessor vor dem Interrupt hatte, also die aus dem Userspace, und dort sollten Interrupts eingeschaltet sein.

Womit versuchst du denn, das "Verschieben in den Userspace" zu erreichen?

Vielleicht wäre der Code von der GDT, dem Interrupthandler bzw. Assemblerstub und dem Multitasking nützlich.
Außerdem kannst du qemu mit dem Parameter "-d int,cpu_reset" aufrufen und dann mal die Datei /tmp/qemu.log posten. Daran könnte man zumindest mal sehen, ob der GPF im Scheduler kommt oder ob der Scheduler überhaupt aufgerufen wird.

Gruß,
oern
15
Lowlevel-Coding / Re: Tutoral Teil 6: GPF-Problem
« am: 07. May 2012, 20:57 »
Hallo,

cs sollte im Userspace vermutlich 0x1b sein und ss 0x23, wenn du die GDT aus dem Tutorial übernommen hast (auf dem Bild ist cs ja 0x08 => kein Userspace und ss ist 0x1b => vermutlich Code-Segment).
Error-Code 0 sagt eigentlich, dass der Fehler im Segment 0 aufgetreten ist.
Außerdem ist in den eflags das IE-Bit (0x200) nicht gesetzt, d.h., die Interrupts sind ausgeschaltet (zum Taskwechsel durch den Timerinterrupt sollten sie aber natürlich an sein).

Das ist zwar immer noch keine Lösung für dein Problem, aber wie Svenska schon sagte, sind das zu wenig Informationen.

Gruß,
oern
16
OS-Design / Re: Global Descriptor Table (GDT)
« am: 25. March 2012, 14:03 »
Hallo,

wie "Disassembly of section .bss" sagt, liegen die Symbole in der .bss Section, die uninitialisierte Daten beinhaltet. Also werden sie erst zur Laufzeit mit Daten gefüllt, weshalb objdump nicht sagen kann, welche Werte dort liegen. Dies ist zum Beispiel in Binärformaten wie ELF nützlich, da dort nur die initialisierten Daten in der Datei liegen müssen und der Rest alloziiert und auf 0 gesetzt wird (kann man mit "readelf -l" sehen, dort ist in der zweiten LOAD-Sektion meistens filesz < memsz).

Grüße,
oern
17
Lowlevel-Coding / Re: Problem mit Interrupts
« am: 16. February 2012, 16:55 »
Es geht jetzt!!!

Das Problem war, dass der Assembler aus den unteren Interruptstubs relative Jumps gemacht hat, die nur zwei Bytes verbrauchen, während ich davon ausging, dass alle Jump-Befehle 5 Bytes brauchen. Hier war es aber wichtig, dass alle Stubs gleich groß sind, da ich ja auf den Anfang der Handler immer 9 addiere (zu den 5 Bytes für den Sprung kommen noch die nops, die ich ja schon extra zum Alignment eingebaut hatte, bzw. eben die push-Befehle). Also wurden mir da praktisch ungültige Addressen in die IDT reingeschoben.
Hätte eben doch C nehmen sollen und die Addressen der Stubs jedes mal einzeln nehmen sollen.
Ich habe jetzt als Zwischenlösung einfach die absolute Addresse statt common_int_handler geschrieben, aber das geht natürlich auch nicht mehr, wenn sich der Code verschiebt (zur Zeit ist die idt.o immer ganz am Anfang, andere Dateien stören also nicht).

Grüße,
oern
18
Lowlevel-Coding / Re: Problem mit Interrupts
« am: 15. February 2012, 16:43 »
Ok, du wolltest ja Code  :-D:

idt.S:
#define idt_sys_gate(n) .quad 0x00008e0000080000
#define idt_usr_trap(n) .quad 0x0000ef0000080000
#define idt_no_entry() .quad 0x0000000000000000

#define int_handler(n) \
  push $0; \
  push $##n; \
  jmp common_int_handler;

#define int_handler_error(n) \
  nop; \
  nop; \
  push $##n; \
  jmp common_int_handler;

.section .text
.global idt_init
idt_init:
  push %edi
  push %ecx
  mov $handler, %eax
  xor %ecx, %ecx
  mov $idt, %edi
.loop:
  cmp $0x31, %ecx
  je .loop_end
  mov %ax, (%edi,%ecx,8)
  ror $0x10, %eax
  mov %ax, 0x06(%edi,%ecx,8)
  ror $0x10, %eax
  add $0x09, %eax
  inc %ecx
  jmp .loop
.loop_end:
  pop %ecx
  pop %edi
  lidt idtp
  mov $0x11, %al
  out %al, $0x20
  out %al, $0xa0
  mov $0x20, %al
  out %al, $0x21
  mov $0x28, %al
  out %al, $0xa1
  mov $0x04, %al
  out %al, $0x21
  mov $0x02, %al
  out %al, $0xa1
  mov $0x01, %al
  out %al, $0x21
  out %al, $0xa1
  ret

handler:
int_handler(0)
int_handler(1)
int_handler(2)
int_handler(3)
int_handler(4)
int_handler(5)
int_handler(6)
int_handler(7)
int_handler_error(8)
int_handler(9)
int_handler_error(10)
int_handler_error(11)
int_handler_error(12)
int_handler_error(13)
int_handler_error(14)
int_handler(15)
int_handler(16)
int_handler_error(17)
int_handler(18)
...
int_handler(48)

.extern int_handler

common_int_handler:
  push %gs
  push %fs
  push %es
  push %ds
  push %ebp
  push %edi
  push %esi
  push %edx
  push %ecx
  push %ebx
  push %eax
  mov $0x10, %ax
  mov %ax, %ds
  mov %ax, %es
  mov %ax, %fs
  mov %ax, %gs
  push %esp
  call int_handler
  mov %eax, %esp
  pop %eax
  pop %ebx
  pop %ecx
  pop %edx
  pop %esi
  pop %edi
  pop %ebp
  pop %ds
  pop %es
  pop %fs
  pop %gs
  add $0x08, %esp
  iretl

.section .data
idtp:
.word 0x7ff
.int idt
idt:
idt_sys_gate(0)
idt_sys_gate(1)
...
idt_sys_gate(47)
idt_usr_trap(48)
idt_no_entry()
...

cpu-Struktur:
struct cpu {
    unsigned int eax;
    unsigned int ebx;
    unsigned int ecx;
    unsigned int edx;
    unsigned int esi;
    unsigned int edi;
    unsigned int ebp;
    unsigned int ds;
    unsigned int es;
    unsigned int fs;
    unsigned int gs;
    unsigned int intr;
    unsigned int error;
    unsigned int eip;
    unsigned int cs;
    unsigned int eflags;
    unsigned int esp;
    unsigned int ss;
} __attribute__((packed));

handler.c:
struct cpu *int_handler(struct cpu *cpu)
{
    if (cpu->intr <= 0x1f) {
printk("Exception %d\n", cpu->intr);
printk("EAX = 0x%x  EBX = 0x%x  ECX = 0x%x  EDX = 0x%x\n", \
    cpu->eax, cpu->ebx, cpu->ecx, cpu->edx);
if (cpu->cs != 0x08) {
    printk("ESI = 0x%x  EDI = 0x%x  EBP = 0x%x  ESP = 0x%x\n", \
cpu->esi, cpu->edi, cpu->ebp, cpu->esp);
} else {
    printk("ESI = 0x%x  EDI = 0x%x  EBP = 0x%x\n", \
cpu->esi, cpu->edi, cpu->ebp);
}
printk("CS = 0x%x  DS = 0x%x   ES = 0x%x   FS = 0x%x\n", \
    cpu->cs, cpu->es, cpu->fs, cpu->gs);
if (cpu->cs != 0x08) {
    printk("GS = 0x%x   SS = 0x%x   EFL = 0x%x  EIP = 0x%x\n", \
cpu->gs, cpu->ss, cpu->eflags, cpu->eip);
} else {
    printk("GS = 0x%x  EFL = 0x%x  EIP = 0x%x\n", \
cpu->gs, cpu->eflags, cpu->eip);
}
printk("ERR = 0x%x\n", cpu->error);
asm volatile("cli; hlt");
    } else if (cpu->intr <= 0x2f) {
printk("IRQ %d\n", cpu->intr - 0x20);
printk("EAX = 0x%x  EBX = 0x%x  ECX = 0x%x  EDX = 0x%x\n", \
    cpu->eax, cpu->ebx, cpu->ecx, cpu->edx);
if (cpu->cs != 0x08) {
    printk("ESI = 0x%x  EDI = 0x%x  EBP = 0x%x  ESP = 0x%x\n", \
cpu->esi, cpu->edi, cpu->ebp, cpu->esp);
} else {
    printk("ESI = 0x%x  EDI = 0x%x  EBP = 0x%x\n", \
cpu->esi, cpu->edi, cpu->ebp);
}
printk("CS = 0x%x  DS = 0x%x   ES = 0x%x   FS = 0x%x\n", \
    cpu->cs, cpu->es, cpu->fs, cpu->gs);
if (cpu->cs != 0x08) {
    printk("GS = 0x%x   SS = 0x%x   EFL = 0x%x  EIP = 0x%x\n", \
cpu->gs, cpu->ss, cpu->eflags, cpu->eip);
} else {
    printk("GS = 0x%x  EFL = 0x%x  EIP = 0x%x\n", \
cpu->gs, cpu->eflags, cpu->eip);
}
handle_irq(cpu->intr - 0x20);
    } else {
cpu = handle_syscall(cpu);
    }
    return cpu;
}

unsigned int irq_table[0x10] = { 0, 0, 0, 0, 0, 0, 0, 0, \
    0, 0, 0, 0, 0, 0, 0, 0 };

void set_irq(unsigned char num, unsigned int handler)
{
    irq_table[num] = handler;
}

static void handle_irq(unsigned char num)
{
    if (irq_table[num]) {
void (*handler)(void) = (void*)irq_table[num];
handler();
    }
    outb(0x20, 0x20);
    if (num > 7) {
outb(0x20, 0xa0);
    }
}

Ich kann auch noch den genauen QEMU-Log und die dazugehörige Ausgabe schreiben.
Und ich meinte eigentlich gar nicht GRUB  :oops:, sondern qemu mit -kernel, ich wollte damit nur sagen, dass der Fehler nicht von einem eigenen Bootloader kommen kann.
In dem Moment, in dem das passiert ist schon Paging an (das sollte auch nicht der Fehler sein, der Page Fault kommt an Addresse 0x430, die wirklich nicht gemappt sein sollte) und die neue GDT geladen.
Wie schon gesagt, die Registerwerte sind (teilweise) verschoben, andere kommen scheibar von irgendwo her. Ich habe schon drin, dass er ss und esp nur ausgibt, wenn der Interrupt nicht vom Kernelspace aufgerufen wird.

oern
19
Lowlevel-Coding / Problem mit Interrupts
« am: 14. February 2012, 18:06 »
Hallo,

ich habe mal wieder angefangen, einen neuen Kernel zu schreiben weil meine alten alle zu kaputt waren.
Jetzt hänge ich bei der IDT / den Interrupts.
Ich habe so ziemlich das gleiche Problem wie manche andere hier auch schon hatten, aber die Fehler die es da waren können es eigentlich nicht sein.
Offenbar wird der Stack, wo die Registerwerte beim Sprung in den Handler drauf sind, kaputt gemacht. Zum Beispiel kommen wenn ich (nach Laden der IDT) die Interrupts einschalte zuerst (laut QEMU-Logs) ein Int 0x21 (wieso der kommt weiß ich nicht, ist doch Tastatur?), danach ein Int 0x20 und dann ein Int 0x0e. Ich habe einen Registerdump auch bei IRQs. Der behauptet aber beim ersten IRQ, dass IRQ 4 (statt 1) und beim zweiten, dass IRQ 3 (statt 0), aufgerufen wurde. Beim zweiten IRQ stimmen auch die Registerwerte vom Stack nicht mehr mit den aus den QEMU-Logs überein (z.B. steht in CS der Wert von EIP, in ESP die EFLAGS etc.). Beim Page Fault ist dann von den Werten her alles in Ordnung. Der Code, der die IDT einrichtet, ist in Assembler geschrieben und schreibt nur die Handleraddressen in die IDT; der Rest (Flags, Segment) steht da schon vorher drinnen, damit so wenig wie möglich zur Laufzeit gemacht wird (mir kommt es unnötig vor, die Flags und das Codesegment zur Laufzeit zu bestimmen, obwohl die schon zur Kompilierzeit feststehen, mache ich bei der GDT genau so).
Außerdem wird der PIC auf die entsprechenden Nummern gemappt. Bootloader ist GRUB und kein eigener.

Alles in allem: Interrupts funktionieren nicht. Wieso nicht? :roll:

Ich könnte noch den Code posten, das mache ich aber erst, falls überhaupt jemand sich das hier durchliest. Ich hoffe, dass die restliche Beschreibung genau genug war, aber sonst sagt ihr das ja.

Grüße,

oern
20
Offtopic / Re: Panikmache wegen DNS-Changer
« am: 14. January 2012, 12:57 »
Ich meinte mit "Panikmache" eigentlich auch eher die meisten anderen Seiten im Internet, wo die so schreiben "Und dann ist das ganze Internet im März weg" und solche Sachen.
Seiten: [1] 2 3

Einloggen