Lowlevel
Lowlevel => Lowlevel-Coding => Thema gestartet von: grebaldi am 06. November 2008, 00:12
-
Guten Abend!
Ich habe mich beim Thema OSDev nun bis hin zum GDT durchgeforstet. Ich entwickle nebenbei einen kleinen Kernel in Pascal (dieses, weil ich mich in dieser Sprache am besten auskenne und ja nur OSDev lernen möchte und nicht gleich C). Der Kernel hat erstmal keinen weiteren Sinn, sondern soll mir helfen die Materie zu verstehen.
Als Bootloader verwende ich GRUB, welcher ja gleich den Protected Mode aktiviert und eine GDT schreibt. Diese muss nun überschrieben werden und dies wollte ich realisieren, indem ich innerhalb meines Pascal-Kernels Inline Assembler anwende.
Ich frage mich nun: Ist das überhaupt die richtige Phase der Ausführung, um das zu tun?
Und wenn ja: Wie sieht so ein ganz einfacher Code in Assembler aus, der einerseits den Null-Deskriptor+2 weitere Deskriptoren definiert und diese dann in eine neue GDT schreibt?
Die meisten Tutorials, die ich so gefunden habe beschäftigen sich mit der GDT nur in dem Zusammenhang, dass man sie schriebt, um dann in den Protected Mode zu springen. Bei der Verwendung von GRUB ist das allerdings schon geschehen...
Ich hoffe ich habe verständlich ausgedrückt, was mein Problem ist. Ich bedanke mich im Voraus für alle Antworten.
mfg, grebaldi
-
Ich fang mit den Antworten einfach mal von oben an.
Jo, irgendwo am Anfang des Kernels paßt schon. Aber du willst die Deskriptoren gar nicht in Assembler definieren. Du kannst dir da schöne Pascal-Records dafür basteln (packed nicht vergessen!) und das alles im Pascal-Code initialisieren. Zum Laden der GDT hast du dann eben noch das lgdt in Inline-Assembler, aber das war's.
Ich glaube, dein eigentliches Problem ist, daß du noch nicht verstanden hast, wofür die GDT gut ist. ;) Letztendlich geht es dabei um Segmentierung. Im Real Mode war das einfach, da war ein Segment durch die Basisadresse definiert und fertig. Im Protected Mode ist das etwas mehr (unterschiedliche Längen, Zugriffsberechtigung und solches Zeug) und paßt nicht mehr ganz in ein 16-Bit-Register rein. Deswegen bastelt man sich eine Tabelle, wo die ganzen Informationen drinnen stehen, und die Segmentregister haben nur noch einen Verweis auf die Tabelle (ist nicht hundertprozentig korrekt, die aktuellen Segmente werden tatsächlich in versteckten Registern gehalten, die CPU greift also tatsächlich nicht ständig auf die GDT zu).
So, und damit kommen wir zu der Frage, wann oder warum du eine GDT brauchst: Offensichtlich, sobald du ein Segmentregister neu laden willst, weil du dann den Index in der GDT brauchst - und was GRUB gemachst hast, weißt du laut Spezifikation nicht. Das Segment wechseln wirst du spätestens beim Multitasking müssen. Solange du das nicht tust, brauchst du strenggenommen auch keine neue GDT aufsetzen.
-
Ich bedanke mich erstmal für die Informationen. Ich bin gerade dabei einen entsprechenden Pascal Code zu schreiben. Bis jetzt ist mir auch Code-technisch alles klar bis auf eine Sache, wo es bei mir noch ein wenig hakt. Und dies wäre der Far Jump ins Code Segment, von dem ich keine Ahnung habe, wie ich ihn im Inline Assembler realisieren kann. Vielleicht kriege ich es ja noch von selbst raus, aber wenn jemand bescheid weiß, so würde ich natürlich gern die Erkenntnis teilen wollen^^
Vielen Dank nochmal,
grebaldi
-
Du benutzt FPC? Dessen Inline-Assembler kommt mir jedem Mal, das ich ihn anschaue, kaputter vor. ;)
asm
jmpl .L1, $0x08
.L1:
end;
Das da könnte es tun. Also jmp statt ljmp und die Operanden grad falschrum hinschreiben...
-
hmmm... FPC mag das Label nicht. "Unknown Identifier "L1"" heißt es da im Compiler-Auswurf. Nun bin ich mir nicht ganz sicher, ob Labels irgendwie anders deklariert werden müssen... Das wäre aber sehr schräg...
-
Also meiner mag das. Du hast ihn schon auf AT&T-Syntax gelassen, oder?
Welche Version? Ich habe 2.0.4.
-
Ich hab die 2.2.2 und hab gerade gesehen, dass meine fpc.cfg tot ist (quasi leer), weiß der fuchs warum. Das wird's wohl sein, ich muss sie mal wiederherstellen.
edit: Schon lustig, fpc.cfg ist wieder da, jetz mag er die register nicht. Mit dem Label hat er kein problem, obwohl ich glaube, dass er es sich gar nicht anschaut. Ich werde wohl noch etwas herumprobieren müssen....
edit2: Habe doch wieder auf Intel Syntax zurückgeschalten, da einfacher und gewohnter. Labels müssen dann aber mit vorangestelltem '@' deklariert werden - manchmal ist es zum Haare ausraufen.
Jetz muss ich noch ein wenig Kleinkram fertig stellen und dann müsste es gehen. Ich bedanke mich nochmal vielmals für die Hilfe!
-
So, meine gdt-unit ist fertig und funktioniert natürlich nicht. Ich habe sie von einem passenden C-Beispiel übersetzt, es kann natürlich sein, dass ich in vielerlei hinsicht unrecht hatte (c ist ja nunmal ein wenig anders, und viel davon verstehe ich nicht). Der Code ist aber erstmal eine Basis. Ich dachte mir, ihn während meiner fehlersuche öffentlich zur schau zu stellen (denn vielleicht kommt jemand früher als ich auf den entscheidenden fehler).
unit moloGDT;
interface
type
gdt_entry = packed record
limit_low : Word;
base_low : Word;
base_middle : Byte;
access : Byte;
granularity : Byte;
base_high : Byte;
end;
gdt_ptr = packed record
limit : Word;
base : Word;
end;
procedure gdt_flush; Assembler;
procedure gdt_set_gate(num:Word; base,limit:Cardinal; access,gran:Byte);
procedure gdt_install;
implementation
var gdt : array[0..2] of gdt_entry;
gp : gdt_ptr;
procedure gdt_flush; Assembler;
asm
lgdt gp
movw $0x10, %ax
movw %ax, %ds
movw %ax, %es
movw %ax, %fs
movw %ax, %gs
movw %ax, %ss
jmpl $0x08, .L1
.L1:
end;
procedure gdt_set_gate(num:Word; base,limit:Cardinal; access,gran:Byte);
begin
gdt[num].base_low := (base AND $FFFF);
gdt[num].base_middle := (base SHR 16) AND $FF;
gdt[num].base_high := (base SHR 24) AND $FF;
gdt[num].limit_low := (limit AND $FFFF);
gdt[num].granularity := (limit SHR 16) AND $0F;
gdt[num].granularity := gdt[num].granularity XOR (gran AND $F0);
gdt[num].access := access;
end;
procedure gdt_install;[public , alias: 'gdt_install'];
begin
gdt_set_gate(0,0,0,0,0);
gdt_set_gate(1,0,$FFFFFFFF,$9A,$CF);
gdt_set_gate(2,0,$FFFFFFFF,$92,$CF);
gp.limit := (sizeOf(gdt_entry)*3)-1;
gp.base := word(@gdt);
gdt_flush;
end;
end.
Die Unit wird im Kernelcode eingebunden und gdt_install wird gleich als erstes im Kernel aufgerufen. Ich werde mich auf die Suche machen, aber bin natürlich über Unterstützung immer glücklich^^.
Vielen Dank an taljeth bis hier hin! (Bin doch auf AT&T-Syntax umgestiegen, weil in der Intel-Syntax keine Manipulation von Segment-registern möglich ist, arrrgh.)
mfg, grebaldi
-
gdt_ptr = packed record
limit : Word;
base : Word;
end;
16 Bit sind etwas wenig für einen Pointer, ein dword sollte es schon sein. Oder am besten nimmst du auch als Datentyp einfach das, was es in Wirklichkeit ist, nämlich ein Pointer. Dann bist du auch die Warnung "moloGDT.pas(63,15) Warning: Conversion between ordinals and pointers is not portable" los.
-
Ich habe die words korrigiert und gdt_ptr.base als pointer definiert (bzw. ich habs auch einmal mit gp komplett als pointer probiert), aber das hat's leider nicht gebracht.
Die bitweise Operationen müssten so eigentlich stimmen (wenn ich sie in C richtig interpretiert habe, versteht sich). Wie gesagt, der Code ist einem Tutorial entnommen (hier (http://osdever.net/bkerndev/Docs/gdt.htm) zu finden) und von mir übersetzt. Ich bin etwas stark verwirrt, da es sich ja um ziemlich einfache definitionen handelt.....
-
Funktioniert nicht heißt, die VM semmelt ab? Dreh mal in bochs das Loglevel ganz auf, spätestens dann sagt er dir auch, was kaputt ist. Das Problem ist dann nur noch, zu verstehen, was er eigentlich meint. ;)
Ob die Records so richtig sind, kann ich nicht sagen, ich kenn die nicht auswendig. Vergleichen mit anderem Code kannst du ja selber genauso gut. An dieser Stelle hängt aber irgendwie jeder eine Weile lang, das ist wohl normal.
Edit: Mal kurz mit dem Link verglichen... | ist ein or, ^ wäre xor.
-
Den operator hatte ich schon geändert, ich bin da an eine falsche tabelle geraten.
Aktuell ist es quasi ein OR. (und es funktioniert natürlich trotzdem nicht - aber es ist vrmtl. wie du schon sagst. GDT- eine Art Feuertaufe für jeden (Pascal-)Programmierer^^)
Ich habe gerade einen Code entdeckt, der so ziemlich ähnlich aussieht, wie meiner (also Pascal). Jetzt will ich mal schauen, ob der funktioniert und dann vergleichen, was ich falsch mache.
Ich kann mich nur nochmal für deine Hilfe bedanken.
Edit: Ich bin schockiert und begeistert zugleich, der andere Code funktioniert - sogar mit Intel-Syntax im Inline-Assembler. Jetz werde ich nochmal genauso viel Zeit benötigen um das zu begreifen. Aber so ist wenigstens vorwärtskommen angesagt.
-
Für alle Pascal-Interessierten hier jetzt mal der funktionierende Code meiner Unit:
unit moloGDT;
interface
type
gdt_entry = packed record
limit_low : Word;
base_low : Word;
base_middle : Byte;
access : Byte;
granularity : Byte;
base_high : Byte;
end;
gdt_ptr = packed record
limit : Word;
base : LongWord;
end;
var gdt : array[0..2] of gdt_entry;
gp : gdt_ptr;
procedure gdt_set_gate(num:LongInt; base,limit:LongWord; access,gran:Byte);
procedure gdt_install;
implementation
procedure gdt_flush; Assembler; nostackframe;
label
flush;
asm
lgdt [gp]
mov ax,$10
mov ds,ax
mov es,ax
mov fs,ax
mov gs,ax
mov ss,ax
jmp $08,flush // don't know the correct syntax in FPC inline assembler, but it works!
flush:
end;
procedure gdt_set_gate(num:LongInt; base,limit:LongWord; access,gran:Byte);
begin
gdt[num].base_low := (base AND $FFFF);
gdt[num].base_middle := (base SHR 16) AND $FF;
gdt[num].base_high := (base SHR 24) AND $FF;
gdt[num].limit_low := (limit AND $FFFF);
gdt[num].granularity := (limit SHR 16) AND $0F;
gdt[num].granularity := gdt[num].granularity OR (gran AND $F0);
gdt[num].access := access;
end;
procedure gdt_install;[public , alias: 'gdt_install'];
begin
gdt_set_gate(0,0,0,0,0);
gdt_set_gate(1,0,$FFFFFFFF,$9A,$CF);
gdt_set_gate(2,0,$FFFFFFFF,$92,$CF);
gp.limit := (sizeOf(gdt))-1;
gp.base := LongWord(@gdt);
gdt_flush;
end;
end.