Eigenes OS?



  • Erhard Henkes schrieb:

    Danke für das Feedback. Momentan möchte ich diese Zahlen in den Typen vermeiden. Bei seltsamen Breiten von long kann man ja in os.h im typedef von long auf int umschalten, oders ehe ich das verkehrt?

    Was ist dann der Sinn und Zweck von diesen Typdefinitionen... Man strebt eine gewisse Portabilität, z.B. mit der folgenden Typdefinition:

    typedef long LONG;
    

    weiss man immer noch nicht, wie viele Bits soll denn ein LONG sein? 32, 40 oder 64 Bit? Es geht ja nicht darum, den Typ long irgendwie zu verstecken, sondern "neue" Typen einzuführen, wo man genau weiss, dass sie so und so viele Bits breit sind.

    Erhard Henkes schrieb:

    Ich habe da noch eine Warnung im Compiler, bei der ich nicht ganz sicher bin, wie man sie am saubersten abschaltet...

    Was würde dagegen sprechen, diese Funktion ganz in Assembler zu realisieren. Diese asm im Quellcode ist wie ein Dorn im Auge 🙂 Wieder mal meine Phylosophie...
    Also in Assembler z.B. so:

    .global _fetchESP
    
    .section .text
    
    _fetchESP:
        movl %esp, %eax
        ret
    

    Assemblieren geht so:

    as datei.s -o datei.o
    

    Dann in irgendeiner Header Datei dem Compiler sagen, wie die Funktion aussieht:

    extern uint32_t fetchESP(void);
    

    Und die Objektdatei datei.o noch beim Linken angeben...

    Erhard Henkes schrieb:

    Benötigt man hier movl oder geht auch mov? Ist asm volatile notwendig?

    Bei Befehlen, bei denen der Assembler erkennen kann, wie breit die Operanden sind, kann man l (steht für long) auch weglassen. Ich persönlich mach das nicht, weil defensive Programmierung usw. 🙂


  • Mod

    Diese asm im Quellcode ist wie ein Dorn im Auge

    Dank der AT&T Syntax sicherlich. Dafür hat es den Vorteil, dass man den Code im gleichen Modul betrachten kann. In obigem Fall sicher nicht so kritisch, da sprechende Funktionen.

    In PrettyOS haben wir eine Mischung aus beidem, so dass der geneigte Leser/Entwickler selbst entscheiden kann, wie er es gerne hätte.



  • Erhard Henkes schrieb:

    Diese asm im Quellcode ist wie ein Dorn im Auge

    Dank der AT&T Syntax sicherlich. Dafür hat es den Vorteil, dass man den Code im gleichen Modul betrachten kann.

    Für mich nicht wegen der AT&T Syntax, sondern wegen diesem gcc-spezifischen Inline und der ganzen Schar von Regeln, die man dabei beachten muss. Diese c-Datei ist nun auf den gcc-Compiler festgenagelt, was ja der Philosophie der C-Sprache, im Sinne höhere Sprache, plattformunabhängig, portabel usw., widerspricht...


  • Mod

    Ja, das ist ein Argument.

    ..

    Was mir am meisten fehlt, ist eine standardisierte Diagnose-Funktion für die Memory-Darstellung. Vielleicht muss ich da meine "ODA" noch mit ein paar Daten auf Stand halten, die ich dann bei Bedarf loggen kann. Hauptproblem sind momentan page faults. 🙂

    Das mit dem FIFO (20 Keys) bei den Tasten-Infos hat ja sehr gut geklappt. Danke für den Vorschlag. Die exakte Auswertung der Scans kann man später noch ausgefeilt aufbauen, wenn die Anwendungen (z.B. eine Shell) diese wirklich brauchen.


  • Mod

    ..


  • Mod

    Das hier ist z.B. die "niedliche" Umschaltung auf User Mode. Da hinten dran wühle ich gerade herum. Jetzt fehlen noch brauchbare syscalls usw. Kämpfe noch mit page faults, weil der user nicht einfach auf kernel code zugreifen darf. PM schlägt voll zu (schön!). Endlich passt alles zusammen.
    ..
    Danach hat man allerdings keinen direkten Zugriff mehr auf den eigenen Code in ckernel.c! Wenn man nach dem Umschalten in den user mode nicht per "page fault" ausgehebelt werden will, muss man dem user sozusagen Supervisor-Rechte geben, funktioniert voll!
    ..
    👍 👍


  • Mod

    Fragen:
    - Welche Privilegstufen sollte man einrichten? Nur 0 und 3, oder auch 1 und 2?
    --- Kernel (0) und User (3) ist klar, wozu sollte man den Rest nutzen?
    - Wie würdet ihr die "sys calls" (call to the kernel) praktisch einrichten?
    --- software interrupt (linux: 0x80), ich würde 0x7F bevorzugen wegen der unsigned char Begrenzung in unserem OS?



  • Erhard Henkes schrieb:

    Frage: Wie würdet ihr nun die Syscalls praktisch einrichten?

    x86'er haben spezielle befehle dafür: http://www.ews.uiuc.edu/~cjiang/reference/vc311.htm
    🙂


  • Mod

    Danke für den Link! Was hältst Du von der Software-Interrupt-Methode (software interrupt bei Linux: 0x80), ich würde allerdings 0x7F bevorzugen wegen der aktuellen Begrenzung auf 0..127 in unserem OS?



  • Erhard Henkes schrieb:

    Was hältst Du von der Software-Interrupt-Methode ...

    geht im prinzip auch, aber sysenter ist schneller. diese software-interrupt methode funzt ausserdem sogar auf 368'ern. 'sysenter' gibt's ja erst ab pentium-ähnlichen und späteren intel- und AMD-prozessoren. aber ich bin nicht so der x86-freak, warten wir doch mal ab, was nobuo dazu zu erzählen hat.
    btw: bei syscalls musste extrem drauf achten, dass du keine backdoors einbaust. das problemchen hatten z.b. die ersten versionen von win-nt 4, die man mit zufallsparametern in syscalls crashen, oder sehr einfach code in den kernel einschleusen konnte. daher auch der imageverlust von windoofs, an dem mickrigsoft bis heute zu knabbern hat.
    🙂


  • Mod

    Na, klasse! Bereits die erste Sicherheitsdiskussion. ..

    'sysenter' gibt's ja erst ab pentium-ähnlichen und späteren intel- und AMD-prozessoren.

    Eines meiner Ziele ist, dass PrettyOS ab 80386 lauffähig ist, denn die späteren CPU haben an der prinzipiellen inneren Technik (GDT, IDT, ...) fest gehalten. Daher auch die komplizierte Umschaltung von einem Gate auf das andere, die bei modernen Pentiums hoffentlich leichter geht. 🙂



  • also ich würde euch empfehlen, das ganze über interrupts zu machen. da gibts dann später bei multitasking keine probleme, da bei einem interrupt autamtisch das i-flag in den eflags gelöscht wird, sodass der kernel nicht unterbrochen werden wird, bis er zumindest einen stack gewechselt hat bzw. bis ihr das halt wieder zulasst... Aber nachdem das ja ein Sprung in den Kernel ist, könnte es sein, dass dann zwei Tasks gleichzeitig springen und im Kernel dann Datenstrukturen zerstört werden, weil das nicht synchron geschieht.
    Aber das ist nur meine bescheidene Meinung


  • Mod

    Die Interrupt-Technik habe ich bereits vorbereitet (0x7F = 127).

    ..

    ➡ Danke an alle, die mich bisher soweit getragen haben.
    Jetzt habe ich soviel OS-Blut geleckt, dass ich keinesfalls aufhöre. 😋
    Diesem Assembler-Forum möchte ich für seine konstruktive und offene Haltung ein ganz großes Lob aussprechen. Das findet man sehr selten!



  • blitzmaster schrieb:

    also ich würde euch empfehlen, das ganze über interrupts zu machen. da gibts dann später bei multitasking keine probleme, da bei einem interrupt autamtisch das i-flag in den eflags gelöscht wird, sodass der kernel nicht unterbrochen werden wird...

    interrupts global zu sperren (macht man das mit dem i-flag?) ist, wenn nicht unbedingt erforderlich, eigentlich keine gute idee, weil dann interrupt-anforderungen verloren gehen können, wenn die sperre zu häufig eingesetzt wird oder lange dauert. und ich weiss nicht, ob die pc-architektur 'nested' interrupts und interrupt-prioritäten kennt (ich glaub' die hardware unterstützt es nicht, aber es geht mit software-tricks).

    blitzmaster schrieb:

    Aber nachdem das ja ein Sprung in den Kernel ist, könnte es sein, dass dann zwei Tasks gleichzeitig springen und im Kernel dann Datenstrukturen zerstört werden, weil das nicht synchron geschieht.

    in einem multitasking-os sollten systemfunktionen sowieso immer reentrant-fähig sein. dass mehrere task gleichzeitig im kernel rumschwirren, oder gar die selbe systemfunktion nutzen, ist keine seltenheit.
    🙂


  • Mod

    in einem multitasking-os sollten systemfunktionen sowieso immer reentrant-fähig sein. dass mehrere task gleichzeitig im kernel rumschwirren, oder gar die selbe systemfunktion nutzen, ist keine seltenheit.

    Klingt wirklich herausfordernd, erinnert mich an multithreading. Da lauern neben "malicious codes" bestimmt schon die "deadlocks". 😉

    interrupt-prioritäten

    Je kleiner die Zahl, je höher die Priorität, zumindest habe ich es so gelesen. Also Timer > Keyboard > ...

    Hat eigentlich jemand einen Tipp, wie ich "Files" - ohne GRUB - möglichst einfach in diese RAM disk tranferieren kann, so dass man den VFS-Mechanismus mal vorführen könnte?



  • +fricky schrieb:

    blitzmaster schrieb:

    also ich würde euch empfehlen, das ganze über interrupts zu machen. da gibts dann später bei multitasking keine probleme, da bei einem interrupt autamtisch das i-flag in den eflags gelöscht wird, sodass der kernel nicht unterbrochen werden wird...

    interrupts global zu sperren (macht man das mit dem i-flag?) ist, wenn nicht unbedingt erforderlich, eigentlich keine gute idee, weil dann interrupt-anforderungen verloren gehen können, wenn die sperre zu häufig eingesetzt wird oder lange dauert. und ich weiss nicht, ob die pc-architektur 'nested' interrupts und interrupt-prioritäten kennt (ich glaub' die hardware unterstützt es nicht, aber es geht mit software-tricks).

    Jo, das IF ignoriert praktisch die IRQ-Line der CPU (nicht aber NMIs, exceptions, usw.).
    AFAIR: Selbst der PIC (programmable Interrupt Controller) in den ersten PCs kannte schon eine Prioritaetsbehandlung von IRQs. Das Ding wurde dann zunaechst in typischer x86-manier erweitert, indem man quasi den gleichen Controller nochmal hinten dran geklatscht hat (toll: jetzt mit 16 IRQs!!) und inzwischen gibt es den in die CPU integrierten APIC.
    IAR geht also nichts verloren, wenn das IF geloescht wird ... es sei denn, das bleibt ueber Sekunden so (daher auch meine Ermahnung, schnelle IRQ-handler zu schreiben).
    Dennoch hast du recht: IRQs sollte man nur ausschalten, wenn unbedingt noetig, was bei x86ern mit ihren ganzen atomaren spezial-Befehlen im Prinzip relativ selten vorkommen duerfte.

    +fricky schrieb:

    blitzmaster schrieb:

    Aber nachdem das ja ein Sprung in den Kernel ist, könnte es sein, dass dann zwei Tasks gleichzeitig springen und im Kernel dann Datenstrukturen zerstört werden, weil das nicht synchron geschieht.

    in einem multitasking-os sollten systemfunktionen sowieso immer reentrant-fähig sein. dass mehrere task gleichzeitig im kernel rumschwirren, oder gar die selbe systemfunktion nutzen, ist keine seltenheit.
    🙂

    Richtig. Da kann man entweder alle moeglichen Teile des Kerns einzeln in kritische Sektionen packen, oder man tuetet den ganzen Kern einfach in eben eine solche. Zu Demonstrationszwecken im Rahmen eines Tutorials waere das IMHO uebersichtlicher. Dabei kann man dann auch problemlos IRQs zulassen.

    Int loescht uebrigens nicht das IF. AFAIR sysenter genau so wenig.
    Die Entscheidung gegen sysenter und fuer int befuerworte ich uebrigens (wie ich schon sagte) : Kein neuer Spezial-Befehl, Gebastel mit AMD vs. Intel, einfache Handhabung, abwaertskompatibel. KISS halt. 😃

    BTW ist die Benutzung eines eigenen Stacks fuer Kern-Aufrufe (auch IRQ- und Exception-Handler) wie gesagt dringend zu empfehlen. Praktisch fast wie ein TASK-Switch.



  • Nobuo T schrieb:

    Int loescht uebrigens nicht das IF. AFAIR sysenter genau so wenig.

    So weit ich weiß, wird das IF bei einem Sprung zu einem Interrupt Gate gelöscht.



  • Erhard Henkes schrieb:

    in einem multitasking-os sollten systemfunktionen sowieso immer reentrant-fähig sein. dass mehrere task gleichzeitig im kernel rumschwirren, oder gar die selbe systemfunktion nutzen, ist keine seltenheit.

    Klingt wirklich herausfordernd, erinnert mich an multithreading.

    isses auch. multithreading und -tasking bedeutet, je nach kontext, mal was anderes, mal das selbe. windows-user verstehen unter multitasking, dass gleichzeitig mehrere prozesse möglich sind, von denen jeder mehrere threads (==parallel ausführbare einheiten) haben kann. in der RTOS-terminologie wird das, was unter win 'thread' heisst, oft 'task' oder 'prozess' genannt.

    Nobuo T schrieb:

    BTW ist die Benutzung eines eigenen Stacks fuer Kern-Aufrufe (auch IRQ- und Exception-Handler) wie gesagt dringend zu empfehlen. Praktisch fast wie ein TASK-Switch.

    dann haste aber ein ziemlich schwergewichtiges interface zwischen kernel und userland, das syscalls relativ unperformant macht. eigentlich braucht man doch nur einen privilegwechsel beim sprung in den kernel und bei der rückkehr. natürlich musste noch checken, ob die parameter gültig sind, genug platz auf dem stack ist, pointer sinnvollen inhalt haben und ähnliche kleinigkeiten. einen kompletten, task-switch ähnlichen mechanismus, halte ich für übertrieben. aber vielleicht geht's mit den x86 software-int befehlen nur so (weshalb später 'sysenter' eingeführt wurde).
    🙂


  • Mod

    Die system calls laufen. Man kann also nun z.B. die Funktion puts(...) als syscall_puts(...) aufrufen. Ich benötige mal eure Hilfe, da ich momentan etwas nicht verstehe und nicht sicher bin, ob das genau so richtig ist oder ob im OS etwas nicht stimmt (was mir wahrscheinlicher erscheint).

    Ich habe hier zwei Varianten:
    http://www.henkessoft.de/OS_Dev/Downloads/31&32.zip
    31) nach dem switch_to_user_mode() hat der user privileg 0
    32) nach dem switch_to_user_mode() hat der user privileg 3

    Im ersten Fall:

    switch to user mode
    After set_kernel_stack. tss_entry.esp0: 0000083Ch
    Right before user mode

    IRQ 127<<<Hello, user world!
    behind switch to user mode with ring 0 supervisor privilege!
    SS: 00000010h, ESP: 0018FFE0h, EBP: 0018FFF0h, CS: 00000018h, DS: 00000020h

    Das "Hello, user world!" wird mittels syscall_puts(...) ausgegeben. Daher weiß ich, dass die system calls nun funktionieren. Baut man auf diese Weise ein API auf? (habe das bisher noch nie gemacht 😃 )

    switch_to_user_mode();
        syscall_puts("Hello, user world!\n");
    

    Im zweiten Fall:

    switch to user mode
    After set_kernel_stack. tss_entry.esp0: 0000083Ch
    Right before user mode
    Page Fault

    Exception. System Halted! <<<

    Ich bin etwas irritiert darüber, dass im echten User Mode der Syscall (ring 3 ruft ring 0) vor dem Page Fault (wegen unerlaubtem Zugriff auf Kernel code) nicht durchgegangen ist. Vielleicht liegt es daran, dass auf dem Weg zur Ausführung des Syscalls (... -> IRQ127 -> syscall_handler -> ...) bereits ein "Page Fault" erfolgte.

    Sorry, dass der Code im IRQ-Bereich momentan etwas "gestöpselt" ist. Das muss ich noch aufräumen, wollte jetzt nur erst mal das Tasking und die Syscalls zum Laufen bekommen, denn die Schnittstelle zwischen Kernel und Anwendungen im User Mode sind ja genau das Interessante. 🙂

    Wenn ich das so nebeneinander sehe, wundere ich mich gerade über die Leerzeile zwischen Right before user mode und >>>IRQ 127<<<Hello, user world!.

    Die nächste Aufgabe ist jetzt, den vorhandenen Code vernünftig zu überarbeiten, damit er eine wirklich brauchbare Basis für ein OS ergibt, möchte nicht völlig chaotisch weiter hetzen, weil dann die Didaktik und vor allem das Tutorial auf der Strecke bleibt. Schlechten Code erklärt und zeigt man nicht gerne, sondern ist froh, wenn er läuft. Damit Experimentieren ist zumeist auch nicht gerade lustig. Vielleicht, wenn man die Module noch ordentlich sortiert und hier und da entkoppelt, kann das noch etwas Sinnvolles werden?

    Vielleicht kann sich ein Assembler-Spezialist die IRQ/ISR/exception-Story mal anschauen (z.Z. eine wilde Mischung aus ASM und C). Da blicke ich kaum noch selbst durch. Den int127 (128 ging nicht wegen Überlauf, muss ja auch nicht sein, wäre nur wegen Linux 0x80) habe ich schon an den fault_handler, der eigentlich nur für exceptions gedacht war, "provisorisch angeschraubt", ging irgendwie nicht anders. 🙄 Aber so ist wahrscheinlich auch Windows und Linux entstanden. 😃

    Inzwischen habe ich gehörig Respekt vor der OS-Entwicklung. 👍
    ... und das Schwierigste liegt ja eher noch vor mir. 🕶


  • Mod

    ..

    Gut finde ich bisher,
    - dass nicht der GRUB Loader verwendet wird
    - den sauberen Übergang von Assembler zum C-Kernel (thanks to Nobuo T)
    - die Bitset-Verwaltung beim Paging (effizient)
    - das VFS analog Linux (leider noch nicht richtig in Aktion)

    Schlecht finde ich
    - das Interrupt-Gerödel (richtig zusammen geschustert, da verwende ich sogar noch den alten Aufbau, weil James das auch nicht wirklich auf die Reihe bekam, ist jedoch im Wesentlichen Fleißarbeit, das zu ordnen)
    - die AT&T Syntax innerhalb C (ziemlich unverständlich, das gehört wirklich alles als Intel Syntax ausgelagert, da hat abc.w Recht)

    Unsicher bin ich mir bei
    - der Heap-Erzeugung/-Verwaltung (schwieriges aber langweiliges Thema, Hauptsache läuft erstmal)
    - der Tasking-Steuerung

    Das wird aber alles noch richtig gut, da bin ich ganz sicher.

    Ich musste für mich schnell Zusammenhänge praktisch durchspielen, daher der Rückgriff auf Vorhandenes, um zu sehen, was wirklich voneinander abhängt. Übrigens geht es anderen auch so. Selbst, wenn ich den Sourcecode alleine erfinde, bringt das niemand weiter. Für andere ist der Code immer "fremd". Was zählt, sind gute Erklärungen. Da sieht es in James Molloy's Tutorial allerdings düster aus.

    Was mir bisher noch weitgehend fehlt, ist die Darstellung der Arbeitsweise des OS. Ich möchte kein "unsichtbares" OS, sondern einen "offenen Wecker". Da habe ich bisher überhaupt noch keinen Ansatz gefunden, das ist bei vielen "Educational OS" lauter in sich geschlossenes unverständliches "Gefrickel".

    ..


Anmelden zum Antworten