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.


Themen - Developer30

Seiten: [1]
1
OS-Design / Anfängerfragen zu Paging
« am: 07. August 2013, 17:27 »
Hallo :-)
Ich möchte Paging in meinem Kernel implementieren. Die Grundidee hinter dem virtuellen Adressraum habe ich verstanden, allerdings ergeben sich trotzdem ein paar Fragen.
Auf der Wikiseite zu Paging heißt es bei der Page-Directory Tabelle:

Zitat von: Bit 7 des Page Directory Eintrags
Gibt die Größe der Page an. 0 steht für 4 kB, 1 für 4 MB (oder 2 MB bei Verwendung von PAE). Das S-Bit ist wirkungslos, wenn nicht im Steuerregister CR4 das PSE-Bit (4-MB-Pages) oder PAE-Bit (2-MB-Pages) gesetzt ist.
Ich dachte bisher, dass Pages immer 4kb seien, daher verstehe ich nicht, warum es hier eine Fallunterscheidung gibt :?

Außerdem frage ich mich, ob sich Bit 1 und Bit 2 nur auf den Page Directory Eintrag selbst beziehen oder auch auf die untergeordnete Pagetable. Wenn z.b. Bit 2 nicht gesetzt ist, können Ring 3 Tasks dann trotzdem auf untergeordnete Pagetable Einträge zugreifen, wenn bei diesen dann das 2. Bit gesetzt ist, oder "vererbt" sich das quasi von dem Page Directory auf die untergeordnete Page Table?

Im Tutorial zu Paging steht unter Dynamisches Mapping, dass es einen Trick gäbe, um die Paging-Strukturen zu mappen:
Zitat von: Paging Tutorial - Dynamisches Mapping
Für das Mappen eines Page Directory und allen zugehörigen Page Tables gibt es einen Trick: Wenn ein Eintrag eines Page Directory wieder auf ein Page Directory (z.B. sich selbst) zeigt, dann wird dieses Page Directory an dieser Stelle als Page Table verwendet. Dadurch werden alle enthaltenen Page Tables hintereinander in den virtuellen Speicher gemappt. Dieser Trick wird oft für das aktuelle Page Directory benutzt, d.h. ein bestimmter Eintrag in jedem PD zeigt wieder auf das PD selbst.
Das heißt, wenn ein Page Directory Eintrag auf sich selbst zeigt, dann wird es selbst zum ersten Page Table Eintrag und alle anderen Page Directory Einträge müssten dann ja zu weiteren Page Table Einträge werden, wodurch dann die gesamte Page Directory Tabelle gemappt wird, oder wie? Das heißt, es gäbe Sinn, den ersten Page Directory Eintrag immer auf sich selbst zeigen zu lassen - habe ich das richtig verstanden?

Zitat von: Paging Tutorial - Dynamisches Mapping
Beim Kernelstart müssen einige Bereiche gemappt werden:
  • Der Kernelcode selbst
  • Der Videospeicher von B8000 bis BFFFF
  • Evtl. Multiboot-Strukturen, die noch verwendet werden sollen
  • Zum Zeitpunkt der Paging-Initialisierung schon dynamisch angelegte Kerneldaten, z.B. die Bitmap der physischen Speicherverwaltung
Dass die ganzen Bereiche, die vor der Paging Initialisierung erstellt wurden, gemappt werden müssen erscheint mir ja logisch. Nur bin ich mir nicht sicher, woher ich denn weiß, wo der Kernelcode selbst im Speicher zu finden ist. Also dem Linkscript kann ich entnehmen, dass mein Kernel bei 0x100000 beginnt und der Stack ist zu Beginn an der 2MB Grenze gesetzt. Heißt das denn auch, dass mein Kernelcode also vollständig zwischen 0x100000 und 0x200000 liegt und mit einer Größe von 1MB also 256 Pages beansprucht? (Ich vermute mal: nein :()
Zudem heißt es, dass z.b. Daten, wie die Bitmap der physischen Speicherverwaltung als dynamisch angelegte Kerneldaten gemappt werden müssten. Die Bitmap selbst habe ich aber garnicht dynamisch angelegt und das Tutorial zur physischen Speicherverwaltung legt sie ebenso statisch auf dem Stack an wie ich, was bedeutet, dass sie doch mit dem Kernelcode automatisch mitgemappt werden müsste - korrekt?

Ich hoffe ich habe nicht allzu wenig verstanden :|,

lg
Developer30
2
Lowlevel-Coding / Verständnisproblem zu TSS
« am: 05. August 2013, 16:15 »
Hallo, ich bins mal wieder  :-D

Ich versuche Software-Multitasking zu implementieren. Auf Ring 0 klappt das auch schon super. Jetzt möchte ich das ganze noch zusätzlich für Ring 3 auf die Beine stellen. Ich orientiere mich dabei an diesem Tutorial.

Zitat von: Tutorial
Was ändert sich für uns mit Ring-3-Tasks? An sich ist es nicht viel. Wie oben schon angemerkt, kommt zum bereits Bekannten nur noch ein Stackwechsel dazu.
Das heißt, es gibt jetzt 2 Stacks pro Task, User- und Kernelstack. Soweit, so gut.

Zitat von: Tutorial
Wenn der Prozessor bei einem Interrupt in Ring 3 ist, also einen Ringwechsel durchführen muss, sichert er esp und ss auf den Stack (wie es in struct cpu_state schon vorgesehen ist). Beim Zurückspringen aus dem Interrupt mittels iret stellt er diese Register auch wieder her.
Auf welchen Stack sichert er esp und ss? Wahrscheinlich ja auf den Kernelstack des alten Tasks.

Zitat von: Tutorial
Nachdem er die Register gesichert hat, lädt der Prozessor den Kernel-Stack (bestehend aus ss und esp). Diese beiden Werte kommen aus dem aktiven Task State Segment (TSS).
Jetzt lädt er also den Kernel Stack des nächsten Tasks aus der TSS. An dieser Stelle frage ich mich, wann tut er das? Beim Betreten oder beim Verlassen der ISR? Oder macht er das garnicht automatisch und ich muss das irgendwo tun?

Bei der Implementierung der TSS steht im Tutorial außerdem folgende Codezeile:
Zitat von: Tutorial
tss[1] = (uint32_t) (new_cpu + 1);
Dazu frage ich mich: Wieso wird der Zeiger um 1 erhöht?

Ich hoffe meine Fragen sind (irgendwo) nachvollziehbar.

lg
Developer30
3
Lowlevel-Coding / IRQ1 / Tastatur funktioniert nicht?
« am: 04. August 2013, 12:57 »
Hallo  :-)

Ich möchte gerne Tastatureingaben mit dem IRQ1 abfangen. Dazu habe ich die PICs initialisiert und für jeden IRQ eine Funktion in der IDT eingetragen. Die Funktionen sind über den int Befehl auch alle erreichbar (d.h. meine IDT scheint schonmal korrekt zu sein).

Den PIC habe ich folgendermaßen initialisiert:

// Master-PIC initialisieren
outb(0x20, 0x11); // Initialisierungsbefehl fuer den PIC
outb(0x21, 0x20); // Interruptnummer fuer IRQ 0
outb(0x21, 0x04); // An IRQ 2 haengt der Slave
outb(0x21, 0x01); // ICW 4

// Slave-PIC initialisieren
outb(0xa0, 0x11); // Initialisierungsbefehl fuer den PIC
outb(0xa1, 0x28); // Interruptnummer fuer IRQ 8
outb(0xa1, 0x02); // An IRQ 2 haengt der Slave
outb(0xa1, 0x01); // ICW 4

// Interrupts verknüpfen
IDT::SetGate(0x20, _irq0, 0x08, 0x8E);
IDT::SetGate(0x21, _irq1, 0x08, 0x8E);
IDT::SetGate(0x22, _irq2, 0x08, 0x8E);
IDT::SetGate(0x23, _irq3, 0x08, 0x8E);
IDT::SetGate(0x24, _irq4, 0x08, 0x8E);
IDT::SetGate(0x25, _irq5, 0x08, 0x8E);
IDT::SetGate(0x26, _irq6, 0x08, 0x8E);
IDT::SetGate(0x27, _irq7, 0x08, 0x8E);
IDT::SetGate(0x28, _irq8, 0x08, 0x8E);
IDT::SetGate(0x29, _irq9, 0x08, 0x8E);
IDT::SetGate(0x2A, _irq10, 0x08, 0x8E);
IDT::SetGate(0x2B, _irq11, 0x08, 0x8E);
IDT::SetGate(0x2C, _irq12, 0x08, 0x8E);
IDT::SetGate(0x2D, _irq13, 0x08, 0x8E);
IDT::SetGate(0x2E, _irq14, 0x08, 0x8E);
IDT::SetGate(0x2F, _irq15, 0x08, 0x8E);

// Alle IRQs aktivieren (demaskieren)
outb(0x20, 0x0);
outb(0xa0, 0x0);

Anschließend lade ich die IDT und initialisiere dann die Tastatur:
// Tastaturpuffer leeren
while (inb(0x64) & 0x1)
    inb(0x60);

// Warten bis die Tastatur bereit ist, und der Befehlspuffer leer ist
while ((inb(0x64) & 0x2)) {}
outb(0x60, 0xF4);

In meiner _irq1 Prozedur erzeuge ich eine Bildschirmausgabe, sodass ich ja sehen sollte, wenn eine Taste gedrückt wird. Außerdem lade ich mit inb(0x60) den Scancode und setze danach EOI für den IRQ1.

Wenn ich meinen Kernel mit bochs ausführe, dann erscheint zu beginn direkt einmal, dass eine Taste gedrückt wurde, obwohl ich noch gar nichts gedrückt habe. Wenn ich dann weitere Tasten auf der Tastatur drücke passiert nichts. Irgendwann steht dann auch in der bochs Konsole:

00103620000i[KBD  ] internal keyboard buffer full, ignoring scancode.(1e)
00105448000i[KBD  ] internal keyboard buffer full, ignoring scancode.(1f)
00106000000i[KBD  ] internal keyboard buffer full, ignoring scancode.(20)
00106516000i[KBD  ] internal keyboard buffer full, ignoring scancode.(9e)

Es ist mir bald schon peinlich, dass das, woran ich hier scheitere, auf der Tutorialseite mit einem Stern als "einfach" eingestuft wird. Ich hoffe dennoch auf Hilfe und Verständnis für Anfängerfehler.

lg
Developer30
4
Lowlevel-Coding / PIC initialisieren
« am: 02. August 2013, 17:40 »
Hallo,

was für Könner wahrscheinlich eine Sache von einer Minute wäre, bringt mich gerade zum grübeln. Ich möchte die beiden PICs initialisieren, damit ich den IRQ1 für Tastaturereignisse benutzen kann. Also habe ich versucht, die Tabelle auf http://www.lowlevel.eu/wiki/Programmable_Interrupt_Controller#Initialisierung in Code umzusetzen:

// master
outb(0x20, 0x10); // Initialisierung einleiten
outb(0x21, 0x04); // IRQ0 soll auf Interrupt 32 sein (4*8)
outb(0x21, 0x04); // Bitmaske

// slave
outb(0xA0, 0x10); // Initialisierung einleiten
outb(0xA1, 0x04); // IRQ0 immernoch auf 32
outb(0xA1, 2);    // Verbindung zum Master

// kein Interrupt maskieren
outb(0x21, 0);
outb(0xA1, 0);

Die Tastatur habe ich folgendermaßen initialisiert:
// Puffer leeren
while (inb(0x64) & 0x1)
inb(0x60);

// Tastatur aktivieren
while((inb(0x64) & 0x2));
outb(0x60, command);

IDT ist auch initialisiert und ich habe eine Funktion für Interrupt 0x21 eingestellt, die über int $0x21 auch aufgerufen wird.
Wenn ich meinen Kernel mit bochs ausführe, dann passiert auf Tastendruck nichts, obwohl ich in meiner IRQ1 Funktion eine Bildschirmausgabe programmiert habe. Über den manuellen Aufruf mit int $0x21 erscheint die Bildschirmausgabe. Meine Vermutung ist jetzt, dass die PIC Controller nicht richtig eingestellt wurden. Kann mir da jemand weiterhelfen?

lg
Developer30

5
Hallo Allerseits,

das ganze ist noch Neuland für mich, also habe ich mich an dem Artikel http://www.lowlevel.eu/wiki/C%2B%2B-Kernel_mit_GRUB orientiert. Ich habe alle Anweisungen auf der Seite befolgt und das ganze mal kompiliert und mit bochs ausgeführt. Ich musste feststellen, dass das screen Objekt, was global in der Video.cpp erstellt wird, scheinbar nicht über den Konstruktor initialisiert wurde, weil auf dem Bildschirm nichts passiert. Wenn ich ein Video Objekt hingegen in der kernelMain Funktion erstelle, dann funktioniert die Bildschirmausgabe. Ich weiß nicht, was ich falsch gemacht haben soll. Eigentlich sollten doch alle Konstruktoren von globalen und statischen Objekten mit der initialiseConstructors Funktion aufgerufen werden. Die Startup.cpp sieht bei mir so aus:

typedef void (*constructor)();
 
// Im Linkerskript definiert
extern "C" constructor start_ctors;
extern "C" constructor end_ctors;
 
extern "C" void initialiseConstructors();
 
// Ruft die Konstruktoren für globale/statische Objekte auf
void initialiseConstructors()
{
  for (constructor* i = &start_ctors;i != &end_ctors;++i)
    (*i)();
}

Das einzig Auffällige ist die Warnung, die beim kompilieren erscheint (ich glaube, mit dem Problem hat die Warnung allerdings nicht viel zutun):

Kernel.cpp:8:6: Warnung: unbenutzter Parameter »multiboot_structur« [-Wunused-parameter]

lg
Developer30
Seiten: [1]

Einloggen