Eigenes OS?


  • Mod

    Beim Recherchieren habe ich eine ausführliche neue Tutorial-Serie (bisher 19 Teile) gefunden, didaktisch allerdings nicht geschickt gemacht, dafür in englisch und vollgestopft mit Detail-Infos:
    http://brokenthorn.com/Resources/OSDev1.html



  • @Erhard: Bezüglich Linken mit mingw hab ich es leider nicht hingegriegt. Benutze nun DJGPP. Es ist nur so, dass meine Pfade auf mingw Verzeichnisse eingestellt sind und wenn ich make in der Eingabeaufforderung aufrufe, mingw make ausgeführt wird. Und da kamen wieder die komischen Fehlermeldungen. Und ich wollte wissen, woran es liegt...

    Bezüglich des nächsten logischen Themas über Interrupts: Ich halte grade ein älteres Buch von Klaus-Dieter Thies "Die Innovativen 80286/80386 Architekturen" Teil 2. Im ersten Kapitel Basis-Programmier-Modell gibt es einen Unterkapitel "Ausnahmen und Interrupts". Vielleicht wäre das nächste logische Thema nicht nur über Interrupts alleine (als asynchrone Ereignisse), sondern auch über Ausnahmen (als synchrone Ereignisse) sinnvoll...

    Erhard Henkes schrieb:

    @abc.w:

    Ich habe mich auch mal mit einem eigenen OS in Assembler beschäftigt zwecks endlich mal den Protected Mode der Intel CPUs zu verstehen. Habe aber diesbezüglich versagt und daraus wurde nichts. Hm, vertane Lebenszeit... hätte lieber in was anderes und sinnvolleres investieren sollen.

    Ich hoffe, Du bist dem Protected Mode schon etwas näher gekommen. 😉

    Hm, ja, bisschen 🙂 Aber noch weit davon entfernt, mal aus dem Stegreif eigene Tasks anlegen zu können, die sich nicht gegenseitig kaputtmachen könnten 🙂

    +fricky schrieb:

    abc.w schrieb:

    Gibt es irgendwo einen freien x86 IP ab 80386, am Besten in VHDL?

    http://www.opencores.org/?do=project&who=zet86

    16 bit Zet processor equivalent to an Intel 80186. Ich hätte gerne einen ab 80386... 🙂 Ich hatte auch mal danach recherchiert, es gibt irgendwie keine, nicht mal "kostenpflichtige", warum auch immer.


  • Mod

    Vielleicht wäre das nächste logische Thema nicht nur über Interrupts alleine (als asynchrone Ereignisse), sondern auch über Ausnahmen (als synchrone Ereignisse) sinnvoll...

    Ja, das gehört zusammen: Gates, Interrupts, Traps, Exceptions.
    Ich bin am nachdenken, wie man dieses Kapitel am besten anpackt.
    Mir war es nur wichtig zu zeigen, dass man auch ohne Interrupts Daten per Polling aus dem Tastaturpuffer abholen kann. Das ist aber, als würde man ständig an der Tür stehen, um zu öffnen, falls jemand kommt (eben weitgehend unnötiger Aufwand). Nun wird nach dem Umzug nach PM wieder die "Klingel" eingeführt, damit wir asynchron auf "echten" Bedarf reagieren können. 😉



  • Erhard Henkes schrieb:

    Ja, das gehört zusammen: Gates, Interrupts, Traps, Exceptions.
    Ich bin am nachdenken, wie man dieses Kapitel am besten anpackt.

    diese 'call gates' sind eine x86-spezialität. die würde ich getrennt erwähnen.
    🙂


  • Mod

    Ich habe eine Nomenklatur-Frage. Man benötigt in den verschiedenen C-Modulen immer wieder eine ganze Menge extern definierter Funktionen. Dies erfolgt oft durch Inkludieren einer Header-Datei namens "system.h", in der man dann alle "externen" Funktionen und einiges andere deklariert/definiert. Mir gefällt der Name "system.h" nicht besonders, weil er nicht wirklich verständlich ist. System kann alles und nichts sein. 🙄

    Folgende Ideen habe ich bisher:

    functions.h
    function_prototypes.h
    kernel.h
    operating_system.h
    os.h

    Wie würdet ihr diesen Header nennen, der dann fast in jedem Modul am Anfang zu finden ist? Mein Favorit ist bisher "os.h". Lässt man geistig das "o" weg, landet man tatsächlich bei "system.h".
    Vielleicht hat jemand eine viel bessere Idee. 🙂



  • #include <PrettyOS.h> ?


  • Mod

    Die Idee ist o.k. Ich wollte den Namen aber noch ändern können, ist nur der Entwicklungs-Deckname. 😉
    Habe mich vorerst für os.h entschieden.


  • Mod

    Ich benötige eure Unterstützung, weil ich die Lücke nicht finde. 🙄

    Exceptions und Timer Interrupt IRQ 0 gehen.
    Wenn ich den keyboard_handler (polling geht ja bestens) zum Testen auf den IRQ0 (18,222 Ticks/sec) umlege, funktioniert er wie beabsichtigt.

    Allerdings kommt der Keyboard Interrupt IRQ1 nicht an. 😕 EDIT: Er wird nicht ausgelöst, weil der Tastatur-Puffer nicht leer ist (das berühmte "flushen" hat mich wieder eingeholt).

    Hier der gesamte - noch nicht funktionierende - Sourcecode: http://www.henkessoft.de/OS_Dev/Downloads/14 C Test IRQ1 funktioniert nicht.zip

    Ich denke, dass der C-Code für die Interrupt-Behandlung in Ordnung ist, habe ihn mehrfach gecheckt. Vielleicht liegt es am Assemblercode isr.asm (Stack?) oder am Linker-Script? Hier ein Screenshot vom OS (timer IRQ geht, habe ihn auf eine Minute im Bildschirm-Ausdruck eingestellt, Zeit stimmt unter Bochs aber nicht): http://www.henkessoft.de/OS_Dev/Downloads/IRQ_Test.PNG

    Ich hoffe, das jemand den blöden Fehler bezüglich IRQ1 und vielleicht auch der anderen (welchen kann man am einfachsten testen?) findet, damit wir rasch weiter kommen. Wie gesagt, IRQ0 geht bestens (einfach mal auf key_handler umlegen).
    Wenn man mit

    __asm__("int $0x21");
    

    im Kernel Programm main()
    den IRQ1 (--> 33 = 0x21) selbst auslöst, kommt auch der keyboard_handler ins Spiel.

    Das heißt, warum wird der IRQ1 nicht weiter geleitet? (IDT Problem?)

    @Nobuo T, abc.w, Bitsy, +fricky et.al.:
    Lasst mich bitte nicht hängen! Wer diesen Mist-Fehler findet, wird im Tutorial extrem gelobt. EDIT: Hauptproblem inzwischen gelöst, aber ich würde mich freuen, wenn ihr trotzdem mal über den Code schaut. Das Thema IDT, IDTR, ISR, Exceptions, ... ist wichtig.

    EDIT: In einem anderen Forum habe ich erfahren, dass man den Tastatur-Puffer leeren muss, damit neue Interrupts gesendet werden. Klingt logisch. Daher habe ich einfach folgendes gemacht:

    Ein einmaliges

    __asm__("int $0x21");
    

    reicht bereits aus, um den Prozess zu starten.
    Soll ich ein

    keyboard_init(){__asm__("int $0x21");}
    

    schreiben, das ich einmal zu Beginn der main() aufrufe??

    Es gibt noch eine zweite - evtl. bessere - Lösung:

    void keyboard_init()
    {
        /* Wait until buffer is empty */
        while (inportb(0x64) & 0x01)
          inportb(0x60);
    
        // __asm__("int $0x21"); // ? 
    };
    

    Hier ist jetzt der funktionierende Code, allerdings alles noch im Experimentierstadium:

    http://www.henkessoft.de/OS_Dev/Downloads/14_C_Test_IRQ1_funktioniert.zip

    Welchen Interrupt würdet ihr nach IRQ0 (Zeit-Ticks) und IRQ1 (Tastatur) als nächstes probieren?

    Speichermanagement, Multitasking, ... warten. Allerdings muss ich zugeben, dass das gesamte Thema OSDEV ziemlich knifflig ist und man die Tools wirklich beherrschen muss. meine Tool-Schwachpunkte sind AT&T Syntax und Linker-Skript. Der verwendete DJGPP mit gcc 3.1 ist auf jeden Fall die richtige Wahl.



  • Probiere mal irgendeinen von 3-8, wobei man das anzusteuernde Device auch auf einen der oberen Interrupts legen können sollte, und dann kümmere Dich um den 2er, weil Du den brauchst, wenn Du an die oberen 8 ran willst. Wenn das Gerät dann auch z.B. mit dem 10er ansprechbar ist, stehen überhaupt erst mal alle IRQs zur Verfügung! Also - erst eine einfache Interruptgeschichte 'unten', und dann schauen, dass sie auch 'oben' läuft.

    Wenn der 2er Probleme macht - ich sollte noch einen Sourcecode haben, bei dem ich eine vierfache Serielle angesteuert habe, wobei der letzte Kanal eben nur über IRQ 10 erreichbar war. Die waren damals sehr dankbar, dass es überhaupt mal ein Stück Software gab, mit dem man den überhaupt ansprechen konnte 😉

    Freut mich übrigens, dass der 3er DJGPP soweit funktioniert - hatte Dir ja in der Mail die Problematik erklärt.


  • Mod

    @Bitsy:
    Das sind die Mechanismen, die versuchsweise eingebaut sind, muss aber noch prüfen, ob alles wirklich funktioniert, bisher wurden ja nur IRQ0 (clock) und IRQ1 (keyboard) behandelt:

    ..

    Daher kann ich mit

    __asm__("int $0x21"); // 0x21 = 33, gemappt auf IRQ1 (Keyboard)
    

    den Keyboard-Interrupt selbst anstoßen.

    Das mit den Interrupts finde ich ganz lustig. Die Keyboard-Ports und der Keyboard-Puffer haben mich fast aus der OS-Kreisbahn geworfen, weil man diesbezüglich viel unausgegorenes und verwirrendes Zeug im Internet findet.

    @Bitsy: Hast Du eine Idee, wie man das Linker-Problem (mit dem a.out Format) des mingw überwinden könnte? abc.w präferiert diese Toolchain. 😕



  • Bezüglich Linker-Problem mit mingw würde mich zwar interessieren, wie man es irgendwie hinkriegen könnte. Aber, wenn es nicht geht, aus welchen Gründen auch immer, ist auch nicht schlimm. Ich habe ja noch DJGPP.
    Ich habe mir den Quellcode 14_C_Test_IRQ1_funktioniert geholt und ausprobiert. Eines stört mich jetzt ein wenig. Zunächst, auf der einen Seite haben wir Assembler-Dateien, die mit nasm assembliert werden. Auf der anderen Seite gibt es c-Dateien, die aber hie und da eine Funktion mit Assembler Inline-Code enthalten. Das ist irgendwie nicht konsistent. Einmal nasm, dann gcc, dann inline-gcc. Wäre vielleicht sinnvoll, wenn c-Datei, dann C, wenn Assembler-Datei, dann Assembler nasm. Und die Funktionen mit Assembler Inline-Code rein in Assembler implementieren? Nur als Vorschlag. Optimal wäre natürlich, statt nasm GNU as zu verwenden, weiss aber jetzt nicht genau, ob man 16-Bit Assembler Quellcode mit as assemblieren kann. Vorteil davon wäre noch, dass man nur ein Tool bräuchte: DJGGP. GNU as ist nämlich in DJGPP mitenthalten.



  • Eine andere Sache, wollte ein Paar Ausnahmen provozieren mit diesem Code:

    .global _k_zero_divide
    .global _k_undefined_op
    .global _k_null_pointer
    
    .section .text
    
    _k_zero_divide:
        movl $0, %eax
        div %eax
        ret
    
    _k_undefined_op:
        ud2
        ret
    
    _k_null_pointer:
        movl $0, %eax
        movl %eax, (%eax)
        ret
    

    Division durch 0 und undefinierter Op 👍
    Die Funktion k_null_pointer() wir aber ausgeführt und das System läuft 😕 Oder ist Zugriff mit Offset Null ok 😕



  • Erhard:
    Irgendwie habe ich inzwischen ein wenig den Ueberblick verloren. Kannst du mal irgendwie ein Verzeichnis oder eine Seite einrichten, wo man waehrend der Entwicklung immer mal die aktuellen Codes runterladen kann?

    Bei deinem ersten KB-Polling hat mich ehrlich gesagt schon etwas gewundert, dass das ueberhaupt funktionierte. Klassischerweise macht man das AFAIR so, dass man einen Handler fuer IRQ 1 installiert. Dort checkt man erstmal, ob etwas im input buffer ist (port 64h, Bit0). Falls was dran liegt, port 60h auslesen und irgendwie verarbeiten (scancodes oder evtl. sonstige spezial-codes).
    Zum Abschluss immer ACK an das keyboard, indem ein reset ausgefuehrt wird: Kurz aus- und wieder einschalten ueber Port 61h, Bit7. Zum Ende natuerlich noch EOI an den PIC. Ist dann zwar immer noch ziemlich primitiv, sollte so aber zumindest keine Probleme geben...

    abc.w:
    Ich kann nur vermuten, dass einfach (noch?) kein Paging aktiviert ist, mit dem eine Ausnahme beim Zugriff auf einen 0-Pointer ausgeloest wuerde. An der physikalischen Adresse 0 ist nun mal einfach normaler RAM, also an sich kein Problem.


  • Mod

    nasm, dann gcc, dann inline-gcc. Wäre vielleicht sinnvoll, wenn c-Datei, dann C, wenn Assembler-Datei, dann Assembler nasm. Und die Funktionen mit Assembler Inline-Code rein in Assembler implementieren?

    Du hast völlig Recht. Die bisherige "gemischte" Lösung liegt an meinen Problemen mit der AT&T Syntax. Auf der einen Seite ist es wichtig, dass Einsteiger sehen, wie einfach man zwischen C- und ASM-Code hin und her springen kann. Man benötigt nur global, extern und den führenden Unterstrich. Auf der anderen Seite führt dies zu einem Hin- und Hergehüpfe. Ich werde daher versuchen, Deine Idee aufzunehmen. Vielleicht kann Nobuo T da etwas unterstützen. 🙂

    ... statt nasm GNU as zu verwenden, weiss aber jetzt nicht genau, ob man 16-Bit Assembler Quellcode mit as assemblieren kann. Vorteil davon wäre noch, dass man nur ein Tool bräuchte: DJGGP. GNU as ist nämlich in DJGPP mitenthalten.

    Habe mir die Syntax von as bisher noch nicht angeschaut. AT&T?

    Kannst du mal irgendwie ein Verzeichnis oder eine Seite einrichten, wo man waehrend der Entwicklung immer mal die aktuellen Codes runterladen kann?

    @Nobuo T:
    Ja, wird sofort gemacht: Den letzten Code erhält man ab jetzt immer unter diesem Link (wurde aber im Tutorial noch nicht beschrieben, weil er sich im Entwicklungs-/Testzustand befindet; daher sind gute Ideen - wie oben von abc.w zum Thema Exception - immer gerne gesehen. Bitte ausreichend kommentieren.):

    ➡ ➡ ➡ Link zur jeweils aktuellsten Test-Version:
    http://www.henkessoft.de/OS_Dev/Downloads/PrettyOS_last_version.zip

    Was abc.w anspricht ist mein Hin- und Herhüpfen zwischen C-Code (vor allem zum Thema Interrupts) und isr.asm.


  • Mod

    Die Funktion k_null_pointer() wird aber ausgeführt und das System läuft 😕 Oder ist Zugriff mit Offset Null ok 😕

    Ich kann nur vermuten, dass einfach (noch?) kein Paging aktiviert ist, mit dem eine Ausnahme beim Zugriff auf einen 0-Pointer ausgeloest wuerde. An der physikalischen Adresse 0 ist nun mal einfach normaler RAM, also an sich kein Problem.

    Stimmt, Paging wurde noch nicht aktiviert. Das gehört sinnvollerweise als Vorteil zu Multitasking, wenn ich das richtig sehe.

    Klassischerweise macht man das AFAIR so, dass man einen Handler fuer IRQ 1 installiert.

    Richtig, klassischerweise. Aber es geht eben auch ohne Interrupt. Dieses ineffiziente Polling wollte ich zuerst zeigen, damit die Notwendigkeit der Interrupts verständlich wird.

    Diese beiden Punkte bestätigen mich in meiner Vorgehensweise. Ich versuche die didaktische Reihenfolge im Tutorial so aufzubauen, wie man Funktionen/Strukturen in ASM oder C für die beabsichtigte Umsetzung wirklich benötigt. Also pyramidal.

    Die bisherige Reihenfolge war deshalb:

    - Bootbarer Minikernel
    - Bootloader + nachgeladenem Kernel
    - Auf Instruktionen im RM reagieren (dank BIOS einfache Handhabung)
    - A20-Gate und PM aktivieren, GDT/GDTR (das gehört wohl als Minimum zusammen)
    - Sprung vom ASM- zum C-Kernel
    - Rudimentäre Textausgabe auf Bildschirm
    - Rudimentäre Texteingabe mit Tastatur
    - IDT/IDTR, Interrupts, Exceptions allgemein
    - IRQ 0 (System Clock Tick jede 18,22 sec)
    - IRQ 1 (Keyboard, durch Port 0x60 u. 0x64 etwas komplizierter)
    - ... (immer spannend halten) 😉

    Ich kann nur hoffen, dass dieses Konzept sinnvoll ist. 😕

    Was würdet ihr als notwendige nächste "Blöcke" auf die Pyramide wuchten?



  • Erhard Henkes schrieb:

    Klassischerweise macht man das AFAIR so, dass man einen Handler fuer IRQ 1 installiert.

    Richtig, klassischerweise. Aber es geht eben auch ohne Interrupt. Dieses ineffiziente Polling wollte ich zuerst zeigen, damit die Notwendigkeit der Interrupts verständlich wird.

    ehrlich gesagt, würde ich als noob nicht einsehen, wozu man einen dedizierten keyboard-interrupt braucht, wenn man die tastatur auch pollen kann (auch wenn ich verstanden habe, wie interrupts funktionieren). warum sollte polling ineffizient sein? so lange ich die tastatur häufig genug polle, so daß ich mit der tastenfrequenz eines schnellschreibers klar komme, ist die welt doch in ordnung. und wenn mir einer erzählen würde, dass die cpu beim pollen sinnlos zyklen verballert, würde ich antworten: "na und? dann polle ich eben in der timer-isr. ca. 18 anschläge/s als samplingrate sollte doch reichen".
    🙂



  • Erhard Henkes schrieb:

    Was würdet ihr als notwendige nächste "Blöcke" auf die Pyramide wuchten?

    schwer zu sagen. der vollständigkeit halber vielleicht weitere hardwarekomponenten eines 0815-pc's behandeln, wie z.b. ansteuerung/benutzung der RTC, des piezo-piepsers, bustreiber bauen usw. allerdings besteht die gefahr, dass das alles langwierig, kompliziert und langweilig werden kann. vielleicht, um die spannung zu erhalten, das os soweit ausbauen, dass die erste, einfache anwendung laufen kann, z.b ein tetris clone oder so.
    🙂



  • Naja, PIT (und damit auch den speaker) und PIC vielleicht mal kurz anschneiden. Das sollte zur Standard-Peripherie IMHO dann aber wirklich erstmal reichen. Was die OS-Thematik betrifft hast du ja bis jetzt eigentlich wirklich nur ganz vorsichtig an der Oberflaeche gekratzt, weitere Nebensaechlichkeiten wie die RTC oder irgendwelche BUS-Geschichten sollte man da IMHO vermeiden.

    Mein Vorschlag waere als Anschluss an deine Pyramide dann mal langsam in die abstrakteren, eigentlich interessanten Gefilde des OS-Designs einzusteigen. Da ist das Grundlegenste als naechstes vermutlich die dynamische Verwaltung des RAM: Knappe Einfuehrung in die Problematik und Vorstellung einfacher Loesungsansaetze wie Unterteilung des Speichers in Bloecke statischer Groesse (vielleicht praktischerweise gleich 4KB gross 😉 ) und Verwaltung in statischen Arrays, Bitmaps oder dynamischen Free-Lists, Reservierungsstrategie next fit, evtl. ganz knapp noch Wiedereingliederungsproblematik (das weiter auszuwaelzen waere dann IMHO wirklich eher Stoff fuer ein richtiges Buch).
    Weitermachen kannst du dann evtl. mit einer knappen Einfuehrung in Paging. Das spielt schliesslich eigentlich auf fast jeder Multitaskingmaschine eine Rolle.

    Danach koenntest du dich dann vorsichtig an den Komplex Programme, Prozesse und Multitasking herantasten, mit einem knappen Ueberblick zum Thema Prozesserzeugung und Kontextwechsel, Scheduling und Verdraengung.
    Dann vielleicht quasi zum Abschluss noch einen Abstecher zu Privilegien, Adressraumtrennung/Schutzmechanismen und Micro- vs Macro-Kern sowie IPC.


  • Mod

    "na und? dann polle ich eben in der timer-isr. ca. 18 anschläge/s als samplingrate sollte doch reichen".

    EDIT: Die korrekte Frequenz wurde eingefügt, um keinen Verwirrung zu stiften. (thanks to +fricky)
    Der Timer "feuert" mit einer Frequenz von 1193182 Hz / 65536 = 18,2065 Hz. Wenn man nichts anderes machen will als Tippen, ist das Polling sicher o.k. 😃
    Dann stößt man den Keyboard_handler einfach beim Timer_handler mit an. Ich habe das in der Tat genau so gemacht, als ich blöderweise den Tastaturpuffer nicht gelöscht hatte und dadurch kein IRQ1 kam, um zu sehen, ob der Keyboard_handler korrekt funktioniert. Ging tadellos. Von daher könnte man ein IRQ0-gesteuertes Keyboard-Polling als Alternative in Betracht ziehen.
    Wirklichen Sinn macht es aber keinen, weil der Interrupt am Master PIC bereits fest vergeben ist. 😉


  • Mod

    Ich fasse mal die wirklich interessante Taskliste von Nobuo T zusammen:

    - PIC (die beiden PICs wurden bereits beim remapping von IRQ 0-15 verarbeitet)
    - PIT (programmable interval timer)
    - Speaker (ja, BEEP (frequence)! Seit Win NT kaputt wegen Behinderung.)
    - (video.c vernünftig ausbauen, bisher nur rudimentär)
    - OS-Thematik (generell)
    - Dynamische RAM Verwaltung:
    - Unterteilung des Speichers in Blöcke statischer Groesse (4KB)
    - Verwaltung (stat. Arrays, Bitmaps, dynam. Free-Lists)
    - Reservierungsstrategie next fit
    - Wiedereingliederungsproblematik
    - Paging
    - Programme & Prozesse
    - Multitasking vs Singletasking
    - Prozesserzeugung, Kontextwechsel, Scheduling, Verdrängung
    - Privilegien / Schutzmechanismen / Adressraumtrennung
    - Micro- vs Macro-Kernel
    - inter-process communication

    Das wird aber noch einige Zeit brauchen, denn ich möchte dies ja auch praktisch fassbar machen.

    Kleines Übersichtsbild aus Internet:
    http://ezs.kr.hs-niederrhein.de/TreiberBuch/html/os_aufbau.png


Anmelden zum Antworten