Lowlevel

Lowlevel => Lowlevel-Coding => Thema gestartet von: klaus55 am 25. June 2012, 22:23

Titel: Segmentregister nach GDT Initialisierung reseten
Beitrag von: klaus55 am 25. June 2012, 22:23
Hallo,

nachdem die GDT erstellt wurde und der GDT Pointer über lgdt gesetzt wurde, müssen ja noch die Segmentregister zurückgesetzt werden.

Das geht ja mit folgendem Code:

asm volatile(
           "mov $0x10, %ax;"
           "mov %ax, %ds;"
           "mov %ax, %es;"
           "mov %ax, %ss;"
           "ljmp $0x8, $.1;"
           ".1:"
       );

Nur verstehe ich noch nicht ganz was da gemacht wird.
Also 0x10 ist ja der Offset zu dem Kernel-Datensegment.
Nachdem dieser gesetzt wurde, mache ich dann einen long jump in mein Kernel-Codesegment (wieso genau? wo läuft mein Programm weiter?)

Hier verstehe ich nicht so ganz, was die vorletzte und die letzte Zeile bzw. $.1 und .1: bedeuten.

Vielleicht kann mir ja jemand weiterhelfen.

Gruß
Klaus55

Titel: Re: Segmentregister nach GDT Initialisierung reseten
Beitrag von: kevin am 25. June 2012, 22:36
Also 0x10 ist ja der Offset zu dem Kernel-Datensegment.
Nicht ganz, es ist ein Selektor für das Datensegment.

Zitat
Nachdem dieser gesetzt wurde, mache ich dann einen long jump in mein Kernel-Codesegment (wieso genau? wo läuft mein Programm weiter?)
Du musst alle Segmentregister neu setzen, also auch das Codesegment. cs kann man nicht direkt mit mov befüllen, sondern das geht nur über einen Far Jump.

Das Programm läuft am Sprungziel weiter. Dein Sprungziel besteht aus Segmentselektor 0x08 und Offset $.1. Das .1 ist dabei einfach nur ein Label, und zwar haben Labels in dieser Form (mit Zahlen) die Eigenschaft, dass sie lokal sind und kein extern sichtbares Symbol erzeugen.
Titel: Re: Segmentregister nach GDT Initialisierung reseten
Beitrag von: klaus55 am 25. June 2012, 22:42
Super, danke für die schnelle und ausführliche Antwort zu dieser Zeit noch!  :-)
Titel: Re: Segmentregister nach GDT Initialisierung reseten
Beitrag von: klaus55 am 26. June 2012, 20:52
So, habe nochmal eine Frage.

Auf dieser Seite http://www.lowlevel.eu/wiki/Global_Descriptor_Table wird diese Funktion beschrieben:

static void set_entry(int i, unsigned int base, unsigned int limit, int flags)
{
    gdt[i] = limit & 0xffffLL; // <-----
    gdt[i] |= (base & 0xffffffLL) << 16;
    gdt[i] |= (flags & 0xffLL) << 40;
    gdt[i] |= ((limit >> 16) & 0xfLL) << 48; // <-----
    gdt[i] |= ((flags >> 8 )& 0xffLL) << 52;
    gdt[i] |= ((base >> 24) & 0xffLL) << 56;
}

In dem angegebenen Beispiel wollen wir das Limit auf 0xfffff setzen. Jetzt frage ich mich aber wie das geht, denn
die Funktion bekommt ja nur einen unsigned int = 16 Bit übergeben. Das limit Feld ist aber so aufgeteilt, dass es 20 Bit umfasst.
Wenn man jetzt doch einen Rechtsshift macht, dann wird doch bei unsigned int mit 0en aufgefüllt, oder? dann wären die oberen vier Bit des
Limits aber 0 anstatt 1.

Verstehe ich das richtig oder was verstehe ich da falsch?

Titel: Re: Segmentregister nach GDT Initialisierung reseten
Beitrag von: Jidder am 26. June 2012, 21:14
Der Typ int ist beim GCC 32-Bit groß. Nur bei 16-Bit Systemen ist er 16-Bit groß.
Titel: Re: Segmentregister nach GDT Initialisierung reseten
Beitrag von: klaus55 am 29. June 2012, 17:43
Ich habe nochmal eine Frage zu dem ljmp.
Wenn ich mir irgendwelche instruction sets anschaue z.B. von Intel, dann finde ich immer nur ein jmp, aber kein ljmp.
Übersetzt der compiler das einfach in ein jump x:y? Wenn ja, wo finde ich eine Tabelle wo all diese Instruktionen aufgelistet sind?

Titel: Re: Segmentregister nach GDT Initialisierung reseten
Beitrag von: Jidder am 29. June 2012, 18:23
Der GCC (bzw. GNU AS) verwendet standardmäßig die AT&T Syntax. Diese Syntax folgt relativ konsistenten Regeln, deswegen gibt es keine direkte Gegenüberstellung der Mnenomics. Allerdings gibt es im AS Manual eine Seite, die die Regeln auflistet (http://sourceware.org/binutils/docs/as/i386_002dVariations.html#i386_002dVariations). ljmp gehört also zu den wenigen Sonderfällen, weil es einen far pointer als immediate operand hat. ljmp $x, $y ist in der Tat dasselbe wie jmp x:y.
Titel: Re: Segmentregister nach GDT Initialisierung reseten
Beitrag von: klaus55 am 01. July 2012, 13:02
Ich hätte jetzt nochmal eine Frage.
Also ich hab sozusagen den ersten Prototypen meines Kernels fertig. Bis jetzt habe ich ihn immer mit qemu getestet (qemu-system-x86_64 -kernel kernel).
Wie muss ich den Kernel jetzt installieren/kompilieren, damit ich ihn beim Start des Computers aus dem Boot-Menü auswählen und starten kann?
Ich verwende Linux und Grub.
Titel: Re: Segmentregister nach GDT Initialisierung reseten
Beitrag von: Svenska am 02. July 2012, 06:32
Du trägst die Kernel-Datei in die grub.cfg ein, als "multiboot". Fertig.  :-)
Beachte, dass die Distro-Upgrades die grub.cfg dynamisch erzeugen, also musst du dich u.U. woanders umschauen, z.B. /etc/defaults/* oder /boot/grub/*.
Titel: Re: Segmentregister nach GDT Initialisierung reseten
Beitrag von: klaus55 am 02. July 2012, 14:37
Ja, das hab ich versucht, aber es klappt nicht so ganz wie ich es mache.

Also ich hab das Tutorial durchgemacht und benutze entsprechend das makefile von dort:

SRCS = $(shell find -name '*.[cS]')
OBJS = $(addsuffix .o,$(basename $(SRCS)))

CC = gcc
LD = ld

ASFLAGS = -m32
CFLAGS = -m32 -Wall -g -fno-stack-protector -nostdinc
LDFLAGS = -melf_i386 -Tkernel.ld

INC=-I/usr/include

kernel-0.1: $(OBJS)
  $(LD) $(LDFLAGS) -o $@ $^
 
%.o: %.c
  $(CC) $(INC) $(CFLAGS) -c -o $@ $^

%.o: %.S
  $(CC) $(ASFLAGS) -c -o $@ $^
 
clean:
  rm $(OBJS)
 
.PHONY: clean

In der grub.cfg hab ich den Eintrag hinzugefügt:

Zitat
menuentry "MyKernel" {
   multiboot kernel-0.1
}


Die Datei kernel-0.1 befindet sich im übergeordneten Ordner /boot.

Den Kernel kann ich beim Start auswählen, aber dann erscheint ein Fehler: error: couldn't open file
oder so ähnlich
Titel: Re: Segmentregister nach GDT Initialisierung reseten
Beitrag von: kevin am 02. July 2012, 14:44
Eventuell musst du das multiboot-Modul von GRUB erst noch laden?

(Ja, GRUB 2 ist ein tolles Betriebssystem. Leider haben sie sich nicht die Mühe gemacht, einen anständigen Bootloader mitzuliefern...)
Titel: Re: Segmentregister nach GDT Initialisierung reseten
Beitrag von: klaus55 am 02. July 2012, 14:49
Okay, kannst du mir sagen wie das geht oder hast du nen passenden Link dazu?

Hab noch nicht groß Erfahrung mit GRUB.
Titel: Re: Segmentregister nach GDT Initialisierung reseten
Beitrag von: kevin am 02. July 2012, 14:55
Probier mal insmod multiboot. Aber ich kenne mich mit GRUB 2 auch nicht wirklich aus, bis jetzt habe ich noch überall GRUB 1 drauf.
Titel: Re: Segmentregister nach GDT Initialisierung reseten
Beitrag von: klaus55 am 02. July 2012, 15:35
Okay, wenn ich jetzt den Kernel beim Starten auswähle, verschwindet das Menü kurz (vielleicht 5 ms oder so) und erscheint dann wieder.
Wenn ich meinen Kernel mit qemu ausführe schreibt er aber ein paar Sachen auf den Bildschirm, so wie es eigentlich sein sollte.
Titel: Re: Segmentregister nach GDT Initialisierung reseten
Beitrag von: OsDevNewbie am 02. July 2012, 17:55
Hallo,
also ich verwende module [Name und Pfad des Moduls] (ohne Klammern), und das funktioniert bei mir prima und ich verwende auch GRUB2.
Ich hoffe ich konnte helfen.
Titel: Re: Segmentregister nach GDT Initialisierung reseten
Beitrag von: klaus55 am 02. July 2012, 19:10
Hm..wie meinst du das? Kannst du mir vielleicht ein Beispiel zeigen?
Titel: Re: Segmentregister nach GDT Initialisierung reseten
Beitrag von: OsDevNewbie am 02. July 2012, 20:15
Hier ist ein Beispiel. Dabei wird der Kernel gebootet und das Modul "tastaturtreiber" wird geladen. Ich hoffe du hast das so gemeint mit
...Kannst du mir vielleicht ein Beispiel zeigen?
set timeout=10
set default=0

menuentry "OS"{
multiboot /kernel
module /tastaturtreiber
boot
}
Titel: Re: Segmentregister nach GDT Initialisierung reseten
Beitrag von: oern am 02. July 2012, 21:42
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.
Titel: Re: Segmentregister nach GDT Initialisierung reseten
Beitrag von: OsDevNewbie am 02. July 2012, 22:02
Also bei mir funktioniert das mit "multiboot kernel" super. Ich wüsste nicht warum man es dann noch als zusätzliches Modul laden muss.
Titel: Re: Segmentregister nach GDT Initialisierung reseten
Beitrag von: klaus55 am 02. July 2012, 22:48
Vielen Dank für eure Hilfe! Es funktioniert jetzt.
Das Problem war, dass ich nur

menuentry "My Kernel" {
multiboot kernel-0.1
}

angegeben habe. Richtig ist aber
menuentry "My Kernel" {
multiboot /boot/kernel-0.1
}

Also ich hab das Verzeichnis nicht richtig angegeben. Wenn ich

menuentry "My Kernel" {
insmod multiboot kernel-0.1
}

mache kommt keine Fehlermeldung, aber er läd den Kernel auch nicht.

Naja, ich bin froh, dass es jetzt funktioniert  :-)

Danke nochmal an alle! Ist echt super, dass immer so schnell Leute antworten, auch wenn es so aussieht, als wär hier nicht viel los.

Edit: Und ich hab jetzt auch endlich verstanden wofür das multiboot gut ist.  :-)