Eigenes OS?



  • Erhard Henkes schrieb:

    Hier hilft - nach meinem bisherigen Wissensstand - nur der typische Ausstieg nach Linux und GRUB.

    das wäre doch doof. die meisten haben windosen. sich extra noch ein OS zu installieren, um dein OS zu bauen, ist nervig.

    Erhard Henkes schrieb:

    Dies werde ich in Teil 3 wohl auch machen müssen. Dann kommen allerdings die ganzen GRUB-Themen (magic numbers, ...) hinzu. Jemand, der auf längere Sicht OS-Development betreibt, muss sich allerdings diese Tool-Basis schaffen, um fremde OS kompilieren/testen zu können.

    es geht bestimmt mit jedem compiler z.b. msvc express edition, watcom, oder dem LCC. natürlich nicht von allein, etwas basteln muss man schon. vorteil: du wärst diese ätzende AT&T-syntax endlich los.
    🙂



  • Erhard Henkes schrieb:

    Das ist ein ganz übles Thema, da ich wegen des eigenen Bootloaders und dieses blöden aout-Formats (16/32-Bit-Code gemischt in kernel.asm) beim Linker immer noch an die Version gcc 3.1, ld 2.13 (in DJGPP) gebunden bin.
    Hier hilft - nach meinem bisherigen Wissensstand - nur der typische Ausstieg nach Linux und GRUB.

    Wieso muss es eigentlich im aout Format sein? Ich habe vor kurzem das OS + Bootloader unter Ubuntu 9.04 mit recht aktuellem gcc 4.3.3, ld 2.19.1 und nasm 2.05.1 compiliert. Dabei habe ich unter anderem das aout in elf geändert, und es lief ohne Probleme. Das aktuelle DJGPP hat ähnliche Versionen, daher sollte das doch eigentlich auch gehen, oder gibt es da noch einen anderen Unterschied?

    Im NASM Manual steht auch, wenn ich das richtig verstehe, dass 16 Bit im ELF Format möglich ist, da NASM die nötigen Informationen übergibt, damit ld weiß was damit zu tun ist: http://www.nasm.us/doc/nasmdoc7.html (7.7.6)

    Unter http://pastebin.com/m3b4d8bcd ist mein Makefile. Das Basis Makefile war die aktuelle Testversion am 20. Mai. Ansonsten habe ich nur noch bei sämtlichen Variablen und Funktionen, die von c benutzt wurden, aber in asm definiert waren, den Unterstrich entfernen müssen. Anscheinend ist es nicht mehr Standard, dass gcc den Unterstrich benutzt und ich habe auch kein Compilerflag dafür gefunden das wieder zu aktivieren.


  • Mod

    @Tobiking2: danke für den Hinweis. Da muss ich doch noch mal probieren.
    Ich kopiere das makefile von 'Tobiking2' mal hier rein:

    all:
          nasm      -f bin file_data.asm -o file_data.dat
          nasm -O32 -f bin boot.asm -o boot.bin            
          nasm -O32 -f elf kernel.asm -o kernel.o
          nasm -O32 -f elf isr.asm -o isr.o
          nasm -O32 -f elf process.asm -o process.o
          nasm -O32 -f elf flush.asm -o flush.o
    
          gcc  -Wall -O -fno-stack-protector -fno-builtin -fno-stack-protector -fno-builtin -nostdlib  -fno-builtin -fno-stack-protector -fstrength-reduce -fomit-frame-pointer -finline-functions -c ckernel.c -o ckernel.o    
    
    usw. 
    
          ld -T kernel.ld kernel.o isr.o ckernel.o video.o flush.o gdt.o idt.o isrs.o irq.o util.o math.o timer.o keyboard.o process.o ordered_array.o paging.o kheap.o descriptor_tables.o task.o  -o ckernel.bin -Map kernel.map
    
          cat boot.bin > MyOS
          cat ckernel.bin >> MyOS
          mv MyOS MyOS.bin
    

    Wenn ich das mit meinem DJGPP (gcc 3.1) ausprobiere erhalte ich vom Linker (ld 2.13) folgende Fehlermeldung:

    kernel.o: file not recognized: File format not recognized

    Im Linker-Skript habe ich übrigens 'rodata' ergänzt, weil einige Compiler das brauchen:

    OUTPUT_FORMAT("binary")
    ENTRY(RealMode)
    phys = 0x00008000;
    SECTIONS
    {
      .text phys  : {
        *(.text)
      }
      .data  : {
        *(.data)
      } 	
      .rodata  : {
        *(.rodata)
      }
      .bss  :  { 					
        *(.bss)
      }
    }
    

  • Mod

    Warum ist in dem Beispiel der Wert vom ESP so extrem groß (0x40205xxx), aber der vom EIP (0x00009Cxx) so niedrig?

    Die Kernel-Stacks für die erzeugten Prozesse wurden jeweils auf dem Heap angelegt, und der beginnt bei mir ab 0x40000000.

    kheap.h:

    #define KHEAP_START         0x40000000 // 1GB
    #define KHEAP_INITIAL_SIZE  0x00200000
    #define KHEAP_MAX           0x4FFFF000
    

    Das Programm selbst ist unten bei 0x00008000 (siehe phys = 0x00008000 im Linker-Skript oben) abgelegt, siehe auch Datei 'kernel.map':

    Linker script and memory map
    
                    0x00008000                phys = 0x8000
    
    .text           0x00008000     0x55d0
     *(.text)
     .text          0x00008000       0xd0 kernel.o
                    0x00008000                RealMode
     .text          0x000080d0      0x1d8 isr.o
                    0x00008178                _isr20
    //...
    
     .text          0x000082b0      0x3c0 ckernel.o
                    0x000082b0                _f2
                    0x000082f0                _f3
                    0x00008430                _main
    //...
    

  • Mod

    das wäre doch doof. die meisten haben windosen. sich extra noch ein OS zu installieren, um dein OS zu bauen, ist nervig.

    Interessante Bemerkung. Wenn ich das mit dem Compiler mit ELF (16 u. 32 Bit gemischt) unter Windows schaffe (es gibt ja auch cross-compiler), dann bleibt nur noch GRUB als Thema. Alternativ kann man den bootloader aufmotzen. Mal sehen. 🙂



  • Erhard Henkes schrieb:

    Wenn ich das mit meinem DJGPP (gcc 3.1) ausprobiere erhalte ich vom Linker (ld 2.13) folgende Fehlermeldung:

    kernel.o: file not recognized: File format not recognized

    Habs grad mal mit dem aktuellen djgpp probiert und das gleiche Problem. DJGPP benutzt nur COFF und unterstützt ELF gar nicht (http://www.delorie.com/djgpp/v2faq/faq22_22.html). Bei coff ist darüber hinaus kein 16 Bit möglich.

    Ich bin beim Suchen auf http://exec.h1.ru/docs/os-devel-faq/os-faq-elf.html gestoßen, einer Anleitung um binutils mit elf support zu compilieren.



  • Erhard Henkes schrieb:

    Wenn ich das mit dem Compiler mit ELF (16 u. 32 Bit gemischt) unter Windows schaffe (es gibt ja auch cross-compiler), dann bleibt nur noch GRUB als Thema.

    was willste mit 16 bit? dachte dein OS schaltet beim start schon in den (flat memory model) 32bit protected mode und bleibt da auch. oder hab' ich was übersehen?
    🙂


  • Mod

    was willste mit 16 bit?

    Schau mal in kernel.asm ... und hier auf das Bild:
    http://www.henkessoft.de/OS_Dev/Bilder/make_process.PNG

    Da liegt der Knackpunkt: 16/32 Bit gemischt in kernel.asm. 🙄

    Vielleicht kann man das umbauen (ich sehe gerade, dass ich org 0x8000 schon auskommentiert habe, das geht offenbar bereits über das Linker-Skript), damit man coff-Format(?) verwenden kann. Vielleicht kann man auch in zwei Hälften (16 Bit u. 32 Bit Teil trennen, den ersten könnte man als bin resultieren lassen, den zweiten als o(bjekt)-Datei wegen des 'call _main')

    Die wating-loop am Ende des 16-Bit-Teils sieht auch nicht sauber aus (war vorher im 32-Bit-Teil, wurde aber aus 16 Bit aufgerufen.



  • Erhard Henkes schrieb:

    Da liegt der Knackpunkt: 16/32 Bit gemischt in kernel.asm.

    aber das ist doch ok so, oder? pc-kisten starten nun mal im real-mode, dein asm-code schaltet in den protected mode und ruft die 'main' des kernels auf. von nun an läuft alles im protected mode ab, egal ob C oder assembler. selbst die portabelsten betriebssysteme haben ein paar plattformabhängige assemblermodule. ich weiss garnicht, was dich daran stört, oder ich hab' heute ne besonders lange leitung.
    🙂


  • Mod

    Vielleicht stehe ich heute auf der Leitung. Ich benötige für kernel.asm das aout-Format (verkraftet beim Linken 16/32 bit gemischt) wegen des Linkers! Also ganz sachlich:

    So klappt es bestens:

    nasmw -O32 -f bin boot.asm -o boot.bin            
    	nasmw -O32 -f aout kernel.asm -o kernel.o
    	nasmw -O32 -f aout isr.asm -o isr.o
    	nasmw -O32 -f aout process.asm -o process.o
    	nasmw -O32 -f aout flush.asm -o flush.o
    
    	gcc  -Wall -O  -c ckernel.c -o ckernel.o    
    ...
    	gcc  -Wall -O  -c syscall.c -o syscall.o  
    
    	ld -T kernel.ld kernel.o ... syscall.o  -o ckernel.bin -Map kernel.map
    
    	cmd /c copy /b boot.bin + ckernel.bin MyOS
    

    Nachteil: Modernere Linker kennen kein aout (UNIX assembler out) mehr, wenn ich das richtig verstanden habe.

    Versuch 1: nasmw -O32 -f coff kernel.asm -o kernel.o

    nasmw -O32 -f coff kernel.asm -o kernel.o
    kernel.asm:21: error: COFF format does not support non-32-bit relocations
    kernel.asm:26: error: COFF format does not support non-32-bit relocations
    kernel.asm:55: error: COFF format does not support non-32-bit relocations
    make.exe: *** [all] Error 1

    Hier streikt nasm(w) wegen 16 bit relocations.

    Versuch 2: nasmw -O32 -f elf kernel.asm -o kernel.o

    kernel.o: file not recognized: File format not recognized
    make.exe: *** [all] Error 1

    Diesmal will der Linker nicht.

    Damit hänge ich irgendwie am aout-Format fest. 😃

    Vielleicht hilft da ein cross-compiler:

    1. http://wiki.osdev.org/GCC_Cross-Compiler
    2. http://lowlevel.brainsware.org/wiki/index.php/Cross-Compiler

    Kennt sich da jemand genau aus? Bin leider (noch) kein Compiler-Experte.

    Übrigens ist abc.w hier auch mit mingw gescheitert:
    http://www.c-plusplus.net/forum/viewtopic-var-p-is-1687893.html

    Ich habe das mit mingw (ist in code::blocks enthalten) noch mal probiert:

    C:\Programme\CodeBlocks\MinGW\bin\ld: cannot perform PE operations on non PE output file 'ckernel.bin'.

    Hmmm, siehe unten!

    Kennt sich da jemand genau aus? Offensichtlich lösen die Cross-Compiler dieses Problem. 😕

    daher bin ich bei meinem DJGPP (gcc 3.1, ld 2.13) und aout geblieben, habe aber das Gefühl, das man da einen Schritt vorwärts kommen sollte.

    Kann mir jemand die Hintergründe, warum man diesen Cross-Compiler bauen muss, mal erklären. Da hat sich doch gegen "früher" etwas verschlechtert, denn ich arbeite doch mit dem DJGPP und dem aout-Format sehr gut! Das Problem ist der Wechsel von aout nach elf. 😉

    http://wiki.osdev.org/GCC_Cross-Compiler



  • Erhard Henkes schrieb:

    So klappt es bestens:

    nasmw -O32 -f bin boot.asm -o boot.bin            
    	nasmw -O32 -f aout kernel.asm -o kernel.o
    	nasmw -O32 -f aout isr.asm -o isr.o
    	nasmw -O32 -f aout process.asm -o process.o
    	nasmw -O32 -f aout flush.asm -o flush.o
    		
    	gcc  -Wall -O  -c ckernel.c -o ckernel.o    
    ...
    	gcc  -Wall -O  -c syscall.c -o syscall.o  
    
    	ld -T kernel.ld kernel.o ... syscall.o  -o ckernel.bin -Map kernel.map
    	
    	cmd /c copy /b boot.bin + ckernel.bin MyOS
    

    ok, du kannst nasm- und gcc-output zusammenlinken, also ist doch alles gut. ich sehe jedenfalls keinen grund, die tools zu wechseln (ein solcher wäre für mich z.b. ein ernsthafter bug des gcc oder nasm).
    btw, aber wenn du unbedingt wechseln willst, kannste das auch zu einem beliebigen späteren zeitpunkt tun. ist also nix, was die weiterentwicklung deines OS grossartig beeinflussen würde.
    🙂



  • Hallo!

    Super Topic und Themenwahl!
    Ich habe ein kleines Problem! Wenn meine Kernel.bin (ASM + C-Kernel) keine Größe hat, die ohne Rest durch 512 Bytes teilbar ist, dann erhalte Ich einen Triple Fault!
    Anfangs habe Ich per "times ??? db 0" die Datei richtig vergrößert, jedoch ist dies bei JEDER Änderung des Kernels notwendig!
    Kennt jemand eine Lösung, um dies zu umgehen oder wenigstens das Ergebnis des Linkens auf eine Größe von ?.00 KB zu bringen?

    Gruß
    Chris

    PS: Ich arbeite mit Windoof Vista und habe DJGPP, wie überall angepriesen installiert und in Gebrauch!



  • Erhard Henkes schrieb:

    Ich benötige für kernel.asm das aout-Format (verkraftet beim Linken 16/32 bit gemischt) wegen des Linkers!

    Der [BITS 16]-Code in der kernel.asm passt noch in die boot.asm. Dann stünde in der kernel.asm i.e. nur noch folgendes:

    [BITS 32]
    [extern _main]
    
     mov    ax, 0x10
     mov    ds, ax      ; data descriptor --> data, stack and extra segment
     mov    ss, ax           
     mov    es, ax
     xor    eax, eax    ; null desriptor --> FS and GS
     mov    fs, ax
     mov    gs, ax
     mov    esp, 0x200000 ; set stack below 2 MB limit
    
     call _main ; ->-> C-Kernel
    (...)
    

    Damit wäre das Linker-Problem vom Tisch. Falls du ernsthaft GRUB in Erwägung ziehst, könntest du konsequenterweise die erste Hälfte von Teil 1 des Tutorials ersatzlos streichen. Das wäre schade. 😞


  • Mod

    Der [BITS 16]-Code in der kernel.asm passt noch in die boot.asm.

    Ja, die Idee hatte ich gestern auch, nachdem der Code da jetzt relativ kurz ist.

    Falls du ernsthaft GRUB in Erwägung ziehst, könntest du konsequenterweise die erste Hälfte von Teil 1 des Tutorials ersatzlos streichen. Das wäre schade.

    Ja, das stimmt. Mir gefällt dieser Weg ohne GRUB und Windows auch sehr gut, vor allem, weil alle es anders machen. GRUB ist eben sehr komfortabel. Dafür ist man im Endeffekt an Linux und diesen Bootloader gebunden. Vielleicht schaffen wir es gemeinsam, den Bootloader bzw. kernel.asm (da ist ja Platz, da keine 512 Byte-Grenze mehr vorhanden) den Anforderungen entsprechend zu gestalten und GRUB zu umgehen.


  • Mod

    @+gjm+: hast Du die Trennung bereits praktisch durchgeführt?

    Ich habe mal versuchsweise getrennt, stürzt aber noch beim Kernel laden ab.

    Ansonsten müsste ich nochmal step-by-step versuchsweise den Code in kleineren Häppchen von kernel.asm nach boot.asm schleppen.
    Dann könnte man nämlich mal coff testen.

    Nachfolgender Code klappt (s.u.).


  • Mod

    Einen Fehler habe ich step-by-step gefunden:

    cli               ; clear interrupts
        lgdt [gdtr]       ; load GDT via GDTR (defined in file "gtd.inc")	
        jmp 0x8:PM        ; http://www.nasm.us/doc/nasmdo10.html#section-10.1
    

    muss im 16-bit-Teil stehen.

    Das macht mir noch ein Problem. So sieht es momentan aus (läuft!):

    boot.asm
    ..

    kernel.asm
    ..

    Frage: wie werde ich den 16 bit Teil in kernel.asm noch los? Ich springe ja vom bootloader nach 0x8000 (Kernelstart).



  • Erhard Henkes schrieb:

    Frage: wie werde ich den 16 bit Teil in kernel.asm noch los? Ich springe ja vom bootloader nach 0x8000 (Kernelstart).

    Ganz einfach: Im Bootloader in den Protected Mode schalten (lgdt und cr0 laden), und dann mit jmp 0x08:0x8000 in den Kernel springen (d.h. im Bootloader ist auch kein 32 Bit Teil). Im Kernel kannst du dann direkt mit 32 Bit Code anfangen, und musst die Segmentregister erstmal laden. Danach solltest du dann nochmal eine GDT im Kernel laden, falls der Bootloader irgendwann überschrieben wird.



  • Der boot.asm muß somit nur folgendes hinzugefügt werden:

    (...)
    .A20_done: 
    
     cli
     lgdt [gdtr]
    
     mov eax, cr0
     or  eax, 1
     mov cr0, eax
    
    ; jmp 0x8000
     jmp 0x8:0x8000    ; Ist zwar ein 16-bit-FAR-Jmp, allerdings
                       ; wird hier CS mit "index" 8 der GDT geladen (da in PM).
                       ; Deshalb wird der Code ab Sprungziel von der CPU
                       ; als 'BITS 32' interpretiert (s. CODE_Desc in der gdt.inc).
    (...)
    %include 'gdt.inc' ; <- Achtung : die gdt.inc ist nun hier und nicht mehr in der kernel.asm.
    
     times 510-($-$$) hlt 
     db 0x55 
     db 0xAA
    

    Nun sollte nur noch in der boot.asm "BITS 16"-Code sein. 🙂


  • Mod

    Danke! Hat gut funktioniert. Der boot sector ist immer noch halb leer, hat also noch Luft für notwendige Ergänzungen. 🙂

    kernel.asm stellt sich nun wie folgt dar (ich lade GDT dort auf Anraten nochmals, ist das so im Code richtig umgesetzt?):

    [Bits 32]
    [global KernelStart]
    KernelStart:
        mov    ax, 0x10
        mov    ds, ax        ; data descriptor --> data, stack and extra segment
        mov    ss, ax           
        mov    es, ax
        xor    eax, eax      ; null desriptor --> FS and GS
        mov    fs, ax
        mov    gs, ax
        mov    esp, 0x200000 ; set stack below 2 MB limit
    
        lgdt [gdtr]          ; load GDT via GDTR (defined in file "gtd.inc")	
    
    [extern _main]           ; entry point in ckernel.c
        call _main           ; ->-> C-Kernel
        jmp $
    
    ;;;;;;;;;;;;
    ; Includes ;
    ;;;;;;;;;;;;
    
        %include "gdt.inc"
    

    Die 16/32-Bit-Trennung ist damit gelungen, endlich. Ein kleiner Meilenstein. 🙂

    Nun geht auch folgendes:

    nasmw -O32 -f bin boot.asm -o boot.bin            
    nasmw -O32 -f coff kernel.asm -o kernel.o
    

    Aber hier gibt es noch ein kleines Problem, auch noch mit KernelStart:

    C:\DJGPP\bin/ld.exe: warning: cannot find entry symbol KernelStart; defaulting to 00008000 <--- Wieso dies?
    ckernel.o(.text+0x1f8):ckernel.c: undefined reference to \_file\_data_end' ckernel.o(.text+0x1fd):ckernel.c: undefined reference to_file_data_start'
    ckernel.o(.text+0x215):ckernel.c: undefined reference to \_file\_data_end' ckernel.o(.text+0x21a):ckernel.c: undefined reference to_file_data_start'
    ckernel.o(.text+0x220):ckernel.c: undefined reference to \_file\_data_start' gdt.o(.text+0xf6):gdt.c: undefined reference to_gdt_flush'
    gdt.o(.text+0xfb):gdt.c: undefined reference to \_tss\_flush' idt.o(.text+0x70):idt.c: undefined reference to_idt_flush'
    isrs.o(.text+0xe):isrs.c: undefined reference to _isr0' isrs.o(.text+0x21):isrs.c: undefined reference to_isr1'
    ...
    isrs.o(.text+0x29e):isrs.c: undefined reference to _isr127' irq.o(.text+0xc5):irq.c: undefined reference to_irq0'
    ...
    irq.o(.text+0x1f7):irq.c: undefined reference to _irq15' paging.o(.text+0x522):paging.c: undefined reference to_copy_page_physical'
    task.o(.text+0x23b):task.c: undefined reference to \_irq\_tail' task.o(.text+0x3c1):task.c: undefined reference to_irq_tail'
    task.o(.text+0x7f2):task.c: undefined reference to `_read_eip'
    make.exe: *** [all] Error 1

    Ich möchte die Syntax mit dem führenden Unterstrich in C behalten. Tipp?



  • ich lade GDT dort auf Anraten nochmals, ...

    😕 Das macht hier keinen Sinn weil gelten würde : "alte GDT == neue GDT". Ausserdem müssten dann die Segmentregister erst wieder mit den Deskriptoren der "neuen" GDT initialisiert werden bevor sie "Wirkung" zeigen.

    C:\DJGPP\bin/ld.exe: warning: cannot find entry symbol KernelStart; defaulting to 00008000 <--- Wieso dies?

    Das Symbol (Label ?) "KernelStart" ist nirgendwo in der kernel.asm definiert. 🙂


Anmelden zum Antworten