Autor Thema: Verständnisfragen  (Gelesen 16827 mal)

ChristianF

  • Beiträge: 296
    • Profil anzeigen
    • DeutschOS - Betriebssystem Projekt
Gespeichert
« am: 04. September 2009, 11:06 »
Moin,
ich möchte nun endlich mal einige Dinge geklärt haben, die bisher ungeklärt waren. Ich hatte die ganze Zeit ein Skript für "ld" aus einem Tutorial genutzt. Nun möchte ich dieses aber auch verstehen und nach dem Lesen des Manuals sind noch ein paar Dinge offen.
 
Die einzelnen Sektionen sind mir soweit klar. Nur frage ich mich, warum die Sektionen nach .text (.data und .bss) bei mir und in vielen Tutorials ein ALIGN(0x1000) haben.
Laut Manual heißt dass, das diese Sektionen dann an Page-Aligned werden. Das heißt also, dass .bss an 0x00101000 liegt (laut objdump). Aber was bringt das für Vorteile?
.data ALIGN(0x1000) : { /*...*/ }

Des Weiteren gibt es laut Manual die bss-Sektion für uninitialisierte Daten. Hier gibt es *(.bss) und *(COMMON). Das Manual sagt folgendes:
Zitat
A special notation is needed for common symbols, because in many object file formats common symbols do not have a particular input section.
...
Was sind denn "common symbols"? Ein kleines Beispiel würde ausreichen...
Weiterhin habe ich in verschiedenen Tutorials immer wieder _sbss und _ebss in der Sektion .bss gesehen. Geben diese start und ende der uninitialisierten Daten an, oder wozu werden diese Angaben benötigt?
 
 
Gruß Christian
 
PS: Ich hoffe meine Sätze sind nicht ganz so wirr, wie sie mir gerade erscheinen, und meine Fragen verständlich :P
 
 
EDIT
Noch eine kleine Frage. Allerdings diesmal nicht zu ld...
Und zwar habe ich im Assemblerstub für GRUB folgenden Abschnitt:
[SECTION .bss]
ALIGN 32
stack:
resb OS_STACK_SIZE
Dieser Abschnitt reserviert Daten für den Stack (16 KiB). Die Sektion BSS deswegen, weil es sich ja um uninitialisierte Daten handelt. Nun verstehe ich allerdings nicht, warum darüber ein "ALIGN 32" steht.
« Letzte Änderung: 04. September 2009, 11:14 von ChristianF »

Jidder

  • Administrator
  • Beiträge: 1 625
    • Profil anzeigen
Gespeichert
« Antwort #1 am: 04. September 2009, 11:23 »
Die einzelnen Sektionen sind mir soweit klar. Nur frage ich mich, warum die Sektionen nach .text (.data und .bss) bei mir und in vielen Tutorials ein ALIGN(0x1000) haben.
Laut Manual heißt dass, das diese Sektionen dann an Page-Aligned werden. Das heißt also, dass .bss an 0x00101000 liegt (laut objdump). Aber was bringt das für Vorteile?
.data ALIGN(0x1000) : { /*...*/ }
Page-alignment ist immer dann von Vorteil, wenn Paging im Spiel ist. Falls du deinen Kernel in einen anderen Speicherbereich mappen willst, und nicht mit "halben Seiten" hantieren willst, kann das eventuell deinen Algorithmus vereinfachen.

Wenn du allerdings Programme laden willst, dann ist es von noch größerem Vorteil, dass deren Sektionen page-aligned sind, weil du dann deinen eigenen Lade-Algorithmus ein gutes Stück einfacher gestalten kannst.

Außerdem ist so eine Trennung der Sektionen auf Seitengrenzen Voraussetzung, dass du das No-Execute-Bit effektiv benutzen kannst. (Ich glaube die ersten beiden Gründe sind wichtiger als dieser. Sowas macht eh keiner^^)

Des Weiteren gibt es laut Manual die bss-Sektion für uninitialisierte Daten. Hier gibt es *(.bss) und *(COMMON). Das Manual sagt folgendes:
Zitat
A special notation is needed for common symbols, because in many object file formats common symbols do not have a particular input section.
...
Was sind denn "common symbols"? Ein kleines Beispiel würde ausreichen...
Das sind auch nur globale uninitalisierte Variablen. Ich glaube dadurch dass sie in verschiedenen Dateien jeweils definiert werden (z.B. wenn jemand das "extern" "vergessen" hat) landen sie in der COMMON-Sektion statt in der .bss-Sektion. Aber ich bin mir nicht ganz sicher.

Weiterhin habe ich in verschiedenen Tutorials immer wieder _sbss und _ebss in der Sektion .bss gesehen. Geben diese start und ende der uninitialisierten Daten an, oder wozu werden diese Angaben benötigt?
Ja, die geben Anfang und Ende an. Diese Variablen werden dazu benutzt um mit memset die .bss-Sektion direkt am Anfang des Programms komplett mit Nullen zu füllen.

Und zwar habe ich im Assemblerstub für GRUB folgenden Abschnitt:
[SECTION .bss]
ALIGN 32
stack:
resb OS_STACK_SIZE
Dieser Abschnitt reserviert Daten für den Stack (16 KiB). Die Sektion BSS deswegen, weil es sich ja um uninitialisierte Daten handelt. Nun verstehe ich allerdings nicht, warum darüber ein "ALIGN 32" steht.
Ein gewisses Alignment für den Stack (und für alle Daten im allgemeinen) ist für die Performance von Vorteil. Man hätte das auch mit anderen Mitteln erreichen können, z.B. die Stackvariable nachträglich anpassen können.

Und ich glaube zur Zeit gibt es keine Register in aktuellen CPUs, die größer als 16 Bytes sind bzw. von mehr als 16 Byte Alignment profitieren. Also ist die 32 vielleicht etwas übertrieben. (Ich glaube der GCC sorgt in der 64 Bit Version auch nur für 16 Byte Alignment.)
« Letzte Änderung: 04. September 2009, 11:28 von PorkChicken »
Dieser Text wird unter jedem Beitrag angezeigt.

ChristianF

  • Beiträge: 296
    • Profil anzeigen
    • DeutschOS - Betriebssystem Projekt
Gespeichert
« Antwort #2 am: 04. September 2009, 11:39 »
Und zwar habe ich im Assemblerstub für GRUB folgenden Abschnitt:
[SECTION .bss]
ALIGN 32
stack:
resb OS_STACK_SIZE
Dieser Abschnitt reserviert Daten für den Stack (16 KiB). Die Sektion BSS deswegen, weil es sich ja um uninitialisierte Daten handelt. Nun verstehe ich allerdings nicht, warum darüber ein "ALIGN 32" steht.
Ein gewisses Alignment für den Stack (und für alle Daten im allgemeinen) ist für die Performance von Vorteil. Man hätte das auch mit anderen Mitteln erreichen können, z.B. die Stackvariable nachträglich anpassen können.

Und ich glaube zur Zeit gibt es keine Register in aktuellen CPUs, die größer als 16 Bytes sind bzw. von mehr als 16 Byte Alignment profitieren. Also ist die 32 vielleicht etwas übertrieben. (Ich glaube der GCC sorgt in der 64 Bit Version auch nur für 16 Byte Alignment.)
Das würde also heißen, das ein "ALIGN 16" auch ausreichen sollte und man dieses, wenn man die performance nicht betrachtet, auch weglassen könnte.
 
Die einzelnen Sektionen sind mir soweit klar. Nur frage ich mich, warum die Sektionen nach .text (.data und .bss) bei mir und in vielen Tutorials ein ALIGN(0x1000) haben.
Laut Manual heißt dass, das diese Sektionen dann an Page-Aligned werden. Das heißt also, dass .bss an 0x00101000 liegt (laut objdump). Aber was bringt das für Vorteile?
.data ALIGN(0x1000) : { /*...*/ }
Page-alignment ist immer dann von Vorteil, wenn Paging im Spiel ist. Falls du deinen Kernel in einen anderen Speicherbereich mappen willst, und nicht mit "halben Seiten" hantieren willst, kann das eventuell deinen Algorithmus vereinfachen.

Wenn du allerdings Programme laden willst, dann ist es von noch größerem Vorteil, dass deren Sektionen page-aligned sind, weil du dann deinen eigenen Lade-Algorithmus ein gutes Stück einfacher gestalten kannst.

Außerdem ist so eine Trennung der Sektionen auf Seitengrenzen Voraussetzung, dass du das No-Execute-Bit effektiv benutzen kannst. (Ich glaube die ersten beiden Gründe sind wichtiger als dieser. Sowas macht eh keiner^^)
Nun gut, dann ist verständlich, warum die Daten Page-aligned sind. Nun ist es in dem Linkerscript von mir so, dass nur die Daten Page-aligned sind. Sollten da nicht alle Sektionen ein "ALIGN(0x1000)" bekommen?
 
Ich habe mir mal die Arbeit gemacht, ein eigenes Linkerscript zu schreiben. Folgendes ist dabei zustande gekommen:
ENTRY(_start)
OUTPUT_FORMAT(elf32-i386)

/* some variables */
KERNEL_VMA = 0x00100000; /* VMA = virtual memory address */
KERNEL_LMA = 0x00100000; /* LMA = load memory address */
KERNEL_VMA_TO_LMA = KERNEL_VMA - KERNEL_LMA; /* address offset for e.g. higher half */

SECTIONS
{
/* Load kernel at virtual memory address */
. = KERNEL_VMA;

/* define variable for kernels start address */
kernel_start_address = .;

/* code section and read only data (e.g. strings within c-part)*/
.text : AT(ADDR(.text) - KERNEL_VMA_TO_LMA)
{
*(.text)
*(.rodata)
}

/* Initialised data - should be page aligned */
.data ALIGN(0x1000) : AT(ADDR(.data) - KERNEL_VMA_TO_LMA)
{
*(.data)
}

/* Uninitialised data */
.bss : AT(ADDR(.bss) - KERNEL_VMA_TO_LMA)
{
_sbss = .;
*(.bss)
*(COMMON)
_ebss = .;
}

/* define variable for kernels end address */
kernel_end_address = .;
}
Gibt es da noch etwaige Unstimmigkeiten oder Fehler? Irgendwelche Dinge, die ich nicht berücksichtigt habe?
 
Eines habe ich jetzt aber noch...
In manchen Tutorials ist rodata als extra Sektion angegeben. Dient dies einfach nur der Übersicht? Im OSDev-Wiki steht dann, dass rodata nach .text kommt... Was ist da jetzt richtig, bzw. besser, da ja beides richtig zu sein scheint?
 
 
Gruß Christian

Jidder

  • Administrator
  • Beiträge: 1 625
    • Profil anzeigen
Gespeichert
« Antwort #3 am: 04. September 2009, 13:32 »
Das würde also heißen, das ein "ALIGN 16" auch ausreichen sollte und man dieses, wenn man die performance nicht betrachtet, auch weglassen könnte.
Jop. Es gibt natürlich spezielle Instruktionen wie zum Beispiel CMPXCHG16B oder SSE-Instruktionen, die trotzdem ein Alignment fordern. Wenn du die aber nicht benutzt, sollte das kein Problem sein.

Nun gut, dann ist verständlich, warum die Daten Page-aligned sind. Nun ist es in dem Linkerscript von mir so, dass nur die Daten Page-aligned sind. Sollten da nicht alle Sektionen ein "ALIGN(0x1000)" bekommen?
Ja, könnte man machen. Der Grund, dass nur die .data-Sektion page aligned ist, konnte noch ein anderer sein: Wenn im Kernel Page-Directories/-Tables als unsigned char pgdir[4096]; oder so definiert werden. (Die müssen ja genau auf einer Page liegen.) Wenn das der Fall ist, sollte man das aber mit einer eigens für ganze Pages eingerichtete Sektion lösen.
 
Ich habe mir mal die Arbeit gemacht, ein eigenes Linkerscript zu schreiben. Folgendes ist dabei zustande gekommen:
...
Gibt es da noch etwaige Unstimmigkeiten oder Fehler? Irgendwelche Dinge, die ich nicht berücksichtigt habe?
Ich finde an denen jetzt nichts auffälliges. Wenn du dir mit objdump den Kernel und die Objekt-Dateien anschaust, kannst du manchmal hinweise bekommen, ob du Sektionen vergessen hast. Zum Beispiel legt der Compiler unter Umständen sachen in Untersektionen wie ".rodata.1" und so weiter ab.

Eines habe ich jetzt aber noch...
In manchen Tutorials ist rodata als extra Sektion angegeben. Dient dies einfach nur der Übersicht? Im OSDev-Wiki steht dann, dass rodata nach .text kommt... Was ist da jetzt richtig, bzw. besser, da ja beides richtig zu sein scheint?
Ich denke mal, dass es im Allgemeinen egal ist.
Dieser Text wird unter jedem Beitrag angezeigt.

ChristianF

  • Beiträge: 296
    • Profil anzeigen
    • DeutschOS - Betriebssystem Projekt
Gespeichert
« Antwort #4 am: 07. September 2009, 11:06 »
Gut...
 
Ich bin da auf noch etwas anderes gestoßen. Wenn ich unter der Sektion ".bss" kernel_end_address mit einem Wert befülle, so kann ich innerhalb des Kernels nur darauf zugreifen, in dem ich dieses per Funktionsdeklaration einbinde:
extern void kernel_end_address(void);Bei einem anderen Tutorial stand am Ende des Linkerscripts folgendes:
end = .; _end = .; __end = .;
Schreibt man das so, kann man anstatt einer externen Funktionsdeklaration eine Variablendeklaration nutzen:
extern unsigned int end;Aber warum steht da insgesamt dreimal end? Und welches würde dann im Endeffekt genutzt werden?

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #5 am: 07. September 2009, 12:37 »
Du kannst es immer als Variable deklarieren. Aber du definierst damit nur ein Symbol, du reservierst keinen Platz. Was also in der Variablen drinsteht, ist nicht aussagekräftig. Du müsstest auf die Adresse der Variablen zugreifen, also &kernel_end. Der Trick beim Benutzen einer Funktion ist dass &func == func ist und man deswegen das & weglassen kann.
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

ChristianF

  • Beiträge: 296
    • Profil anzeigen
    • DeutschOS - Betriebssystem Projekt
Gespeichert
« Antwort #6 am: 07. September 2009, 13:20 »
Mmmhhh...
Okay, das muss ich wohl nochmal ausprobieren....
Ich habe es mal ausprobiert und die letzten beiden weggelassen (_end, sowie __end). Als ich dann den Kernel kompilieren und linken wollte, kam eine Nachricht der Art "end nicht definiert". Deswegen denke ich, dass eines der letzten beiden ends genutzt wird, wenn man diese Adresse per Variable abfragen will... (myend = &end).
 
Damit ich bei einer späteren Portierung nichts mehr umändern muss und ich auch die komplette Makefile verstehe, habe ich beschlossen, diese neuzuschreiben.
Nun habe ich beim linken mittels ld (64Bit System) immer die Option "-melf_i386" genutzt, da das sonst nicht funktioniert hat. Gibt es auch ein äquivalent dazu, wie z.B. "-melf_x86-64"? Wenn nicht, muss ich eben eine Makefile mit den jeweiligen Parametern anlegen, die je nach gewünschter Architektur eingebunden wird...
 
Dann ist da noch eine Frage. Ich suche meine C- und Asm-Dateien per find zusammen. Nun würde ich allerdings, dass ein bestimmtes Verzeichnis nicht durchsucht wird. Kann man das irgendwie per Parameter mitgeben? Hier noch die betreffenden Zeilen der Makefile:
KERNEL_SRC_FOLDER := src/kernel/src
KERNEL_SRC_ARCH_FOLDER := src/kernel/src/arch/$(ARCH)
KERNEL_ARCH_CFILES := $(shell find $(KERNEL_SRC_ARCH_FOLDER) -mindepth 1 -maxdepth 10 -name "*.c")
KERNEL_NONARCH_CFILES := $(shell find $(KERNEL_SRC_FOLDER) -mindepth 1 -maxdepth 10 -name "*.c")
Konkret möchte ich, dass beim Suchen nach nicht Architekturabhängigen Dateien (C wie Assembler) das Verzeichnis "arch" nicht berücksichtigt wird, nur weiß ich nicht wie...
« Letzte Änderung: 07. September 2009, 13:44 von ChristianF »

MNemo

  • Beiträge: 547
    • Profil anzeigen
Gespeichert
« Antwort #7 am: 07. September 2009, 16:48 »
Nun habe ich beim linken mittels ld (64Bit System) immer die Option "-melf_i386" genutzt, da das sonst nicht funktioniert hat. Gibt es auch ein äquivalent dazu, wie z.B. "-melf_x86-64"?
-melf_x86_64  :-D (eine vollständige Liste kannst du dir mit ld -V ausgeben lassen)

Zitat
Konkret möchte ich, dass beim Suchen nach nicht Architekturabhängigen Dateien (C wie Assembler) das Verzeichnis "arch" nicht berücksichtigt wird, nur weiß ich nicht wie...
$ man find:
 find … -name "*.c" -and -not -path "*/arch/*"

Welches der drei end genutzt wird, hängt davon ab, ob du mit oder ohne leading-underscore compilieren lässt(gcc option: -f{no-}leading-underscore), und ob du end oder _end in deinem Code verwendest.
„Wichtig ist nicht, besser zu sein als alle anderen. Wichtig ist, besser zu sein als du gestern warst!“

ChristianF

  • Beiträge: 296
    • Profil anzeigen
    • DeutschOS - Betriebssystem Projekt
Gespeichert
« Antwort #8 am: 08. September 2009, 11:26 »
Nun ja, unter dem Debian und mit dem Crosscompiler immer ohne den Untersstrich :D
 
Ich habe auch mal etwas weiter gesucht und schließlich das ganze wie folgt gelöst:
$(shell find $(KERNEL_SRC_FOLDER) -mindepth 1 -maxdepth 10 -name "*.c" -type f | grep -v "^$(KERNEL_SRC_FOLDER)/arch/")

ChristianF

  • Beiträge: 296
    • Profil anzeigen
    • DeutschOS - Betriebssystem Projekt
Gespeichert
« Antwort #9 am: 11. September 2009, 08:20 »
So so, da bin ich mal wieder.
Dieses mal hat es allerdings nicht direkt mit dem zu tun, was ich zuvor gepostet habe...
 
Und zwar bin ich am Überlegen, was vom Kernel Architekturabhängig ist und was nicht. Ich bin nun zu dem Schluss gekommen, dass eigentlich fast alles Abhängig von der gewählten Architektur ist, wenn man einen I386, einen amd64 und nen alten PowerPC (ich habe keine Ahnung von diesem) Prozessor betrachtet.
 
Was übrig bleibt sind in meinen Augen kleine Funktionen im Kernel wie memset oder memcpy, die man ja eigentlich nicht ändern müsste...
 
Ist das wirklich so, oder habe ich da eventuell etwas übersehen oder falsch gedacht?

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #10 am: 11. September 2009, 10:28 »
Ich würde mal mit einem klaren "kommt drauf an" antworten.

Bei einem Mikrokernel wirst du relativ viele prozessorspezifische Sachen haben, während bei einem Monolithen der allergrößte Teil unabhängig von der Prozessorarchitektur sein wird. Unabhängige Sachen, die du in beiden Fällen haben wirst, sind zum Beispiel der Scheduler und allgemein recht viel von der Taskverwaltung, evtl. die physische Speicherverwaltung, die Verarbeitung von Syscalls (auch wenn der Aufrufmechanismus wieder plattformabhängig ist), sofern vorhanden wahrscheinlich auch noch die IPC.

Reicht das für den Anfang? ;)
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

bluecode

  • Beiträge: 1 391
    • Profil anzeigen
    • lightOS
Gespeichert
« Antwort #11 am: 11. September 2009, 15:34 »
Was übrig bleibt sind in meinen Augen kleine Funktionen im Kernel wie memset oder memcpy, die man ja eigentlich nicht ändern müsste...
Wenn du davon wirklich optimierte Implementierungen haben willst, ist das nicht nur von der Architektur, sondern eventuell sogar von der Prozessorgeneration abhängig.
lightOS
"Überlegen sie mal 'nen Augenblick, dann lösen sich die ganzen Widersprüche auf. Die Wut wird noch größer, aber die intellektuelle Verwirrung lässt nach.", Georg Schramm

ChristianF

  • Beiträge: 296
    • Profil anzeigen
    • DeutschOS - Betriebssystem Projekt
Gespeichert
« Antwort #12 am: 14. September 2009, 13:51 »
Was übrig bleibt sind in meinen Augen kleine Funktionen im Kernel wie memset oder memcpy, die man ja eigentlich nicht ändern müsste...
Wenn du davon wirklich optimierte Implementierungen haben willst, ist das nicht nur von der Architektur, sondern eventuell sogar von der Prozessorgeneration abhängig.
Nun, ich habe die Funktionen jetzt so unoptimiert geschrieben, dass diese Plattformunabhängig sein sollten.
 
Ich würde mal mit einem klaren "kommt drauf an" antworten.

Bei einem Mikrokernel wirst du relativ viele prozessorspezifische Sachen haben, während bei einem Monolithen der allergrößte Teil unabhängig von der Prozessorarchitektur sein wird. Unabhängige Sachen, die du in beiden Fällen haben wirst, sind zum Beispiel der Scheduler und allgemein recht viel von der Taskverwaltung, evtl. die physische Speicherverwaltung, die Verarbeitung von Syscalls (auch wenn der Aufrufmechanismus wieder plattformabhängig ist), sofern vorhanden wahrscheinlich auch noch die IPC.

Reicht das für den Anfang? ;)
Fürs erste ja.  :-D
Ich bin nun soweit, dass mein neugeschriebener Kernel bereits eine GDT lädt...
Diese, sowie auch der IDT-Code und der IRQ-Code sind in meinen Augen Plattformabhängig. Wenn man jetzt nur i386 und x86_64 betrachtet, könnte man hier auch nochmal einen geteilten Ordner erstellen, da diese teilweise ähnliche, bzw. gleiche Funktionen haben. Allerdings werde ich das lassen, weil ich nicht wirklich Lust habe dafür das Makefile nochmals anzupassen...
 
Was mir noch eingefallen ist, was eigentlich auch Größtenteils Plattformunabhängig ist, ist die Heapverwaltung. Abhängig von der Plattform ist da nur der Code, der den Heap erweitert, verkleinert und initialisiert, da das auf dem 386er ja gemappt werden muss, sobald das Paging aktiv ist.
Wie das bei anderen Prozessoren ist, weiß ich nicht genau, aber ich glaube kaum, dass jeder Prozessor Paging unterstützt. ;)

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #13 am: 14. September 2009, 13:54 »
Jeder Prozessor, der es wert ist, unterstützt zu werden, hat eine MMU. Ohne macht es nicht viel Spaß. Allerdings gibt es da ein paar lustige andere Konzepte, wie so eine MMU funktioniert - das mit PD und PT ist nicht universell.
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

ChristianF

  • Beiträge: 296
    • Profil anzeigen
    • DeutschOS - Betriebssystem Projekt
Gespeichert
« Antwort #14 am: 15. September 2009, 15:37 »
Genau das meinte ich ja. Es besteht ja schon ein Unterschied beim Paging zwischen 32 und 64 Bit...
 
Die folgende Frage wird nur gestellt, um sicher zu gehen:
Ich habe den kompletten Code für die Textausgabe mal im Ordner für plattformabhängige Parts abgelegt. Ist das so okay, oder gibt es den CGA-Krempel überall und wenn ja, ist der dann überall gleich?

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #15 am: 15. September 2009, 16:49 »
Das eigentliche Ausgeben eines Zeichens ist natürlich geräteabhängig (nicht unbedingt plattformabhängig). Du wirst vermutlich schon für PCs zwei verschiedene Ausgabefunktionen für VGA und serielle Schnittstelle haben wollen.

Aber ein printf ist ein printf, egal womit es sein Zeichen am Ende ausgibt. Interpretieren des Formatstrings, Ausgabe von Zahlen usw. ist alles generisch.
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

ChristianF

  • Beiträge: 296
    • Profil anzeigen
    • DeutschOS - Betriebssystem Projekt
Gespeichert
« Antwort #16 am: 15. October 2009, 09:47 »
Es wird mal wieder Zeit eine Frage zu posten... :roll:
 
Ich bin nun dabei, "Interrupt Service Routines" ISRs einzubauen. Es gibt 256 Interrupts und so weiter. Ich habe mir 2 Makros geschrieben, die mir die Tipparbeit im Assemblercode abnehmen...
 
Nun zur Frage:
Der Prozessor schiebt ja die folgenden Register auf den Stack:
SS, ESP, EFLAGS, CS, EIP, Error Code. Zusätzlich dazu schiebe ich noch die Interruptnummer auf den Stack.
 
Das ganze sieht dann wie folgt aus:
; Abschnitt
; Macro for isr stub without an already pushed error code
%macro INT_STUB 1
global intr_stub%1
intr_stub%1:
cli
push byte %1 ; ISR-Number
push byte 0 ; Dummy error code for a uniform stack frame
jmp interrupt_handler_stub
%endmacro

; Macro for isr stub with an already pushed error code
%macro INT_STUB_ERROR_CODE 1
global intr_stub%1
intr_stub%1:
cli
push byte %1 ; ISR-Number
jmp interrupt_handler_stub
%endmacro

INT_STUB 0
INT_STUB 1
INT_STUB 2
INT_STUB 3
INT_STUB 4
INT_STUB 5
INT_STUB 6
INT_STUB 7
INT_STUB_ERROR_CODE 8

extern interrupt_handler
interrupt_handler_stub:

; Push created stack
mov eax, esp
push eax
; Call C-Function
mov eax, interrupt_handler
call eax
pop eax

; remove isr number and error code
add esp, 8
; restores EIP, CS, EFLAGS, ESP and SS
iret

Was ich noch machen müsste, wäre die Segmente des Kernels laden, falls ein Ring-Wechsel auftritt.
Aber welchen Sinn hat es, die kompletten Register an den Interrupthandler zu übergeben? So wird das ja in vielen Tutorials gemacht...

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #17 am: 15. October 2009, 10:06 »
Übergeben musst du sie nicht zwingend (wird aber oft gemacht, weil dann z.B. die Syscallnummer und Parameter in Registern übergeben werden können), aber zumindest sichern und wiederherstellen wäre schon nett. Das Userspace-Programm wird sich bedanken, wenn du ihm seine Registerwerte überschreibst, sobald der Timer feuert. ;)
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

ChristianF

  • Beiträge: 296
    • Profil anzeigen
    • DeutschOS - Betriebssystem Projekt
Gespeichert
« Antwort #18 am: 15. October 2009, 10:26 »
Stimmt...
Wo habe ich nur meinen Kopf...  :-P
 
Da ist noch was unklar.
pusha schiebt die general purpose register auf den Stack und ignoriert dabei esp. Im Intel Manual 2B steht folgendes:
EDI ← Pop();
ESI ← Pop();
EBP ← Pop();
Increment ESP by 4; (* Skip next 4 bytes of stack *)
EBX ← Pop();
EDX ← Pop();
ECX ← Pop();
EAX ← Pop();

Heißt das, dass der Wert von ESP nicht auf den Stack gepusht wird, es aber trotzdem ein Platz von 4 Byte mit irgendwelchem Inhalt angelegt wird? Wenn dem so ist, müsste ich ja esp oder zumindest einen Platzhalter noch in meine register-struktur einbauen...

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #19 am: 15. October 2009, 11:40 »
Gepusht wird es vermutlich schon, aber gepopt halt nicht.
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

 

Einloggen