Eigenes OS?



  • Erhard Henkes schrieb:

    1. zur AT&T-Syntax: %eax %%eax *%%eax
      wo muss man genau was verwenden?

    Man muss unterscheiden zwischen Assembler-Syntax für den GNU Assembler und dem Assembler-Inline Formalismus für den gcc Compiler.

    Für den GNU Assembler gilt: Alle Register-Bezeichnungen, weil sie was besonderes sind, im Sinne, keine Variablen uns so was, bekommen ein % Zeichen. Man kann ruhig Variablen anlegen, die eax, ebx und sonst wie heissen, und auf diese Variablen zugreifen:

    .section .data
    eax: .long 0        # Variable eax
    ebx: .long 0        # Variable ebx
    ecx: .long 0, 0, 0  # Ein Array ecx[3]
    
    .section .text
    _Helper:
        movl %eax, eax      # Speichere Inhalt des CPU-Registers EAX (%eax) in der Variable eax (eax)
        movl %ebx, ebx      # Speichere Inhalt des CPU-Registers EBX (%ebx) in der Variable ebx (ebx)
        movl $eax, %eax     # Lade Addresse der Variable eax ($eax) ins CPU-Register EAX (%eax)
        movl $ecx, %ecx     # Lade Anfangsadresse des Arrays ecx ($ecx) ins CPU-Register ECX (%ecx)
        movl %eax, (%ecx)   # Speichere Inhalt des CPU-Registers EAX (%eax) im Array-Element ecx[0]
        movl %ebx, 4(%ecx)  # Speichere Inhalt des CPU-Registers EBX (%ebx) im Array-Element ecx[1]
        movl %ecx, 8(%ecx)  # Speichere Inhalt des CPU-Registers ECX (%ecx) im Array-Element ecx[2]
        ret
    

    Bei indirekten Sprüngen ist es so, dass ein Sternchen benützt werden muss/soll:

    _Sprung:
        jmpl *1000      # Effektive Adresse 1000
        jmpl *%eax      # Effektive Adresse im CPU-Register EAX (%eax)
        jmpl *4(%eax)   # Effektive Adresse im CPU-Register EAX (%eax) und Displacement = 4
        jmpl *(%eax, %ebx)  # Basis im CPU-Register EAX (%eax), Index im CPU-Register EBX (%ebx)
        jmpl *32(%eax, %ebx, 4) # Basis im CPU-Register EAX (%eax), Index im CPU-Register EBX, Skalierung = 4, Displacement = 32
        ret
    

    Vergisst man ein Sternchen zu setzen, kommt vom gas eine Warnung:

    ... Warning: indirect jmp without `*'

    Warum das so ist, weiss ich ehrlich gesagt nicht, habe im Manual dazu nichts gefunden und habe ehrlich gesagt, nicht intensiv danach gesucht.

    Bezüglich Assembler-Inline im C-Code kann ich leider nicht viel sagen. Ich weiss nur, dass man bei Registern zwei % Zeichen setzen muss. Ansonsten lässt sich, denke ich, obige Info auch so übernehmen. Und es gibt noch eine weitere Schar von Regeln für den Inline, die man kennen sollte...

    Ich hoffe, ich konnte bisschen Licht auf die AT&T Syntax werfen (die ja zu unrecht verteufelt wird)... 😉


  • Mod

    die ja zu unrecht verteufelt wird

    Ich fange an, mich daran zu gewöhnen. Die C-Syntax ist ja auch nicht jedes Coders Lieblingsspeise.

    Momentan kämpfe ich gerade mit dem Thema Paging und versuche die Dinge so weit wie möglich zu visualisieren.

    Dabei habe ich eine Frage zum Alignment: Wenn die memory_placement_address nach einem k_malloc bei exakt 200000h liegt bei PAGESIZE = 1000h und man belegt nun weitere 2000h, wo muss es dann weiter gehen, bei 202000h oder 203000h? Der Algo, den ich von JM übernommen habe, macht momentan bei 203000h weiter, kommt mir verschwenderisch vor. Aber vielleicht habe ich da etwas noch nicht ganz verstanden. 😃

    if( align == 1 && (placement_address & 0xFFFFF000) )
    {
        placement_address &= 0xFFFFF000;
        placement_address += PAGESIZE;
    }
    

    Wenn ( placement_address == (placement_address & 0xFFFFF000) ) könnte man das Addieren der PAGESIZE doch sparen. Aufrunden muss man doch nur bei placement_address > (placement_address & 0xFFFFF000) ?

    Beispiel:
    placement_address startet mit 200000h. Wegen align=0 startet es auch genau dort. Bei align=1 hätte es bei 201000h begonnen. Ist das korrekt oder muss ich obige Abfrage auf die letzten 3 Hex-Nuller ergänzen?

    setup bitset:
    old: 00200000h sz: 00002000h a: 0 new: 00202000h

    make a page directory:
    old: 00203000h sz: 00002004h a: 1 new: 00205004h

    Daher halte ich diese verfeinerte Version für richtig:

    if( align == 1 /*&& (placement_address & 0xFFFFF000)*/ )
    {
        if( !(placement_address == (placement_address & 0xFFFFF000) ) ) // New !!!
        {
            placement_address &= 0xFFFFF000;
            placement_address += PAGESIZE;
        }
    }
    

    Please comment. 🙂

    Das && (placement_address & 0xFFFFF000) von JM verstehe ich auch nicht. Das kann man doch genau so gut weg lassen? 😕



  • The Intel manuals tells everything about the x86(-64) architecture, including paging. Why would you do that stuff in ASM? But that's you own choice, what I posted is how I would go work.

    use:

    if(aligned == 1 && (placement_address & 0x00000fff))
    {
       placement_address &= 0xfffff000;
       placement_address += 0x1000;
    }
    

    Open the WIndows calulcator, science mode, and do something in it like this. Just putting in some number, anding with 0x00000fff or 0xfffff000.

    Something everybody here should know: each part of a hexidecimal number (0x12345678, 1 is a part, 2 a part, etc.) is 4 bits in length. so 0xfffff000 makes 12 bits zero 🙂

    Success

    // PHPnerd


  • Mod

    Ah, that's o.k. It rounds up to next 1000h, if it is not xxxxx000h. Good.



  • No, if address is xxxxx000h, it wont do anything. If it is not, it will make it so. And add a page, so no data will be overwritten.

    Calculate it 😛

    // PHPnerd


  • Mod

    Please excuse, at the first view I thought it's the same as JM's code. But it's the opposite. 🙂 Follows the same logic I proposed. Right?


  • Mod

    OK, let's compare the three codes:

    PHPnerd:

    if(aligned == 1 && (placement_address & 0x00000fff))
    {
       placement_address &= 0xfffff000;
       placement_address += PAGESIZE;
    }
    

    My proposal:

    if( align == 1  )
    {
        if( !(placement_address == (placement_address & 0xFFFFF000) ) ) 
        {
            placement_address &= 0xFFFFF000;
            placement_address += PAGESIZE;
        }
    }
    

    JM's code wasting memory - very bad!

    if( align == 1 && (placement_address & 0xFFFFF000) )
    {
        placement_address &= 0xFFFFF000;
        placement_address += PAGESIZE;
    }
    

    I have to admit that your code is elegant, but my code might be the best to understand. 🙂



  • Yeah, results the same, but my way will be lots faster. (some cycles, so some billions of a second). Mine is better to understand, i dont understand yours

    // PHPnerd


  • Mod

    OK, let's do it our own way! I will use my code. 👍

    I have compared the objdump of both kmalloc (w/o optimization).
    There is only a tiny difference in these asm lines:

    //PHPnerd’s Code:  
    
      31:	a1 90 00 00 00       	mov    0x90,%eax
      36:	a9 ff 0f 00 00       	test   $0xfff,%eax
      3b:	74 0f                	je     4c <_k_malloc+0x2e>
      3d:	25 00 f0 ff ff       	and    $0xfffff000,%eax
    
    //My Code:
    
      31:	a1 90 00 00 00       	mov    0x90,%eax
      36:	25 00 f0 ff ff       	and    $0xfffff000,%eax
      3b:	39 05 90 00 00 00    	cmp    %eax,0x90
      41:	74 0a                	je     4d <_k_malloc+0x2f>
    

    I need 18 and PHPnerd 17 bytes. Your code is really better? 👍

    I think that's a good example for the tutorial: How to compare your own code with other solutions? 🙂

    Well, the topic "alignment" now is clarified. 😋



  • Intel's code? No, it is my code 😛

    I said it was better :P, I think you miss something with the objdump, because your code didnt actually do everything you wrote in C. (do a check on alignment, A == (A AND 0xfffff000) THEN A AND 0xfffff000, ADD A, 0x1000)

    // PHPnerd


  • Mod

    Intel's code? No, it is my code

    Please excuse. You were talking so much about Intel that I thought they have published it in there manual. 😃
    I corrected it in the thread (Intel -> PHPnerd).

    I think you miss something with the objdump

    I have checked both dumps, the rest is identical AFAIK: http://www.henkessoft.de/OS_Dev/Downloads/kmalloc_objdump_comparison.zip

    Ich habe mal vorsichtig begonnen die Themen:

    Eigene Betriebssystementwicklung am PC  -  Teil 2
        - Key Queue und Key Handler
        - Speicher Management
              - Physical Memory Management (PMM)
                    - k_malloc( size, align, phys )
              - Virtual Memory Management (VMM)
                    - Virtueller Speicher und Swapping
                    - Paging
    

    zu verarbeiten. Code zum Downloaden biete ich noch keinen an, weil ich mit PHPnerd noch über die Funktion set_page(...) diskutiere:

    page_t* set_page(ULONG address, page_directory_t* dir)
    {
        //ULONG tmp = address;
        address>>=12; // /= PAGESIZE        // index <== address
        ULONG table_index = (address>>10);  // ==> page table containing this address
    
        //TODO: check it against Intel Manual 3A !!!
        if(
            ( ( (ULONG)(  dir->tablesPhysical[table_index] ) ) & 0x1 )
                      && (dir->tables[table_index])
          )
            putch('!');
        else
        {
            putch('?'); /*printformat(" address: %x\n",tmp)*/;
            ULONG phys;
            dir->tables[table_index] = (page_table_t*) k_malloc( sizeof(page_table_t), 1, &phys );
            k_memset(dir->tables[table_index], 0, PAGESIZE);
            dir->tablesPhysical[table_index] = phys | 0x7; // 111b meaning: PRESENT=1, RW=1, USER=1
        }
        return &dir->tables[table_index]->pages[address%1024];
    }
    

    Da gibt es noch Unklarheiten bei der Abfrage, ob eine page table wirklich schon assigned ist. JM fragt hier mittels if(dir->tables[table_index]) ab, während PHPnerd der Meinung ist, dass dies nicht reicht und man auch das Present Bit abfragen sollte. Ich habe mal das der Physical Tables beid er Abfrage hinzu gefügt, weil das im else-Zweig auf 1 gesetzt wird. Sollte soweit gehen.

    Da bin ich noch nicht ganz sicher in diesem Bereich des Pagings, auch was die Visualisierung der Baumstruktur CR3 -> PD mit PDE -> PT mit PTE -> Page mit Offset -> physikalische Adresse und der jeweils zugehörigen Zustands-Bits von PD, PT und Page angeht.

    http://www.henkessoft.de/OS_Dev/OS_Dev2.htm

    PHPnerd ist der Meinung, JM's Code im VMM-Bereich sei sehr schlecht und empfiehlt eine "Neukonstruktion" auf Basis Intel Manual 3A (da steht aber nichts drinnen, was ich nicht schon wusste, die Sekundärliteratur ist inzwischen ziemlich gut). Ich verwende zunächst mal obige set_page(...), bis ich etwas Besseres habe. 🙂


  • Mod

    Nochmal eine ganz andere Frage an die Assembler-Spezialisten zum Teil 1:

    http://www.henkessoft.de/OS_Dev/OS_Dev1.htm#mozTocId412221
    Dort finden sich [Bits 16] und [Bits 32] in einer Übersetzungseinheit (Übergang von RM nach PM). Vielleicht kann man das so verändern, so dass man COFF (32bit, stringentes Format, akzeptiert keinen 16 bit Bereich) anstelle aout (16 und 32 bit gemischt, tolles Format!) verwenden kann? Benötigen wir diesen Eintrag [Bits 16] wirklich? Andere erzählen mir, man sollte den 16/32 Bit Zirkus im Bootloader abwickeln und den Kernel komplett in 32 Bit halten. Ich weiß, dass ich dies versucht habe (man kann kernel.asm ja als verlängerten Bootloader ansehen), aber ein Sprung aus dem Binär-Format nach C-Funktionen war nicht erlaubt.


  • Mod

    PMM (Bit Array) und VMM (Paging) ist nun im Tutorial umgesetzt:
    http://www.henkessoft.de/OS_Dev/OS_Dev2.htm#mozTocId918986
    http://www.henkessoft.de/OS_Dev/Downloads/20090503_PrettyOS_104.zip

    Allerdings bin ich didaktisch noch nicht zufrieden.

    EDIT(5.5.): Ich habe nun noch folgende Funktion zur Sichtbarmachung der Paging-Zusammenhänge eingeführt:

    void analyze_PD_PT_Pages(ULONG PDE, ULONG PTE, page_directory_t* dir)
    {
        k_clear_screen();
        ULONG cr3;
        asm volatile("mov %%cr3, %0": "=r"(cr3)); // read cr3
        printformat( "PDBR cr3:       %x\n", cr3 );
        printformat( "PD phys. addr.: %x\n", dir->physicalAddr );
    
        int i,j;
        for(i=0;i<PDE;++i)
        {
            printformat( "  PDE %d: phys: %x virt: %x\n", i, dir->tablesPhysical[i], dir->tables[i] );
            for(j=0;j<PTE;++j)
            {
                printformat( "    PTE %d: addr: %x frame-addr: %x\n", j,
                                    &(dir->tables[i]->pages[j]),
                             (ULONG)( dir->tables[i]->pages[j].frame_addr)<<12
                           );
            }
        }
    }
    

    in kernel.c:

    analyze_PD_PT_Pages(1,10,kernel_directory);
    

    screen output:

    PDBR cr3:       00202000h
    PD phys. addr.: 00202000h
      PDE 0: phys: 00205027h virt: 00205000h
        PTE 0: addr: 00205000h frame-addr: 00000000h
        PTE 1: addr: 00205004h frame-addr: 00001000h
        PTE 2: addr: 00205008h frame-addr: 00002000h
        PTE 3: addr: 0020500Ch frame-addr: 00003000h
        PTE 4: addr: 00205010h frame-addr: 00004000h
        PTE 5: addr: 00205014h frame-addr: 00005000h
        PTE 6: addr: 00205018h frame-addr: 00006000h
        PTE 7: addr: 0020501Ch frame-addr: 00007000h
        PTE 8: addr: 00205020h frame-addr: 00008000h
        PTE 9: addr: 00205024h frame-addr: 00009000h
    

    Jetzt versteht man die Zusammenhänge besser und z.B. auch, warum der Bereich von 205000h bis 206000h noch gemappt wurde:
    map phys addr to virt addr from 0x0 to the end of used memory:
    ?old: 00205000h sz: 00001000h a: 1 new: 00206000h

    Nun ist schon besser. Das werde ich noch ins Tut einbauen, vielleicht noch mit den wichtigsten Bits der Pages hinter den Adressen.



  • übrigend funktioniert der source code vom c-kernel bei mir auch nicht.
    weder mit make.exe noch mit einer batch
    die gcc.exe und ld.exe habe ich aber ins verzeichniss reinkopiert, die fehlen im zip


  • Mod

    gcc.exe und ld.exe habe ich aber ins verzeichniss reinkopiert

    Das solltest Du nicht machen, sondern diese nach C:\djgpp installieren. Pfad und Env korrekt setzen wie beschrieben. Nimm genau den DJGPP aus dem im Tutorial angegebenen Link wegen dem aout-Format. Falls dann etwas nicht klappt, bitte erst Fehlermeldung lesen und probieren. Wenn Du nicht mehr weiter kommst, genaue Meldung posten. Hast Du schon mit Assembler und C-Compiler gearbeitet?



  • upps
    stimmt ja *boing*
    ich probiers demnächst aus 😃


  • Mod

    Das Thema Heap ist nun ebenfalls im Tutorial grundlegend verarbeitet. Wer sich als Leser tiefer hinein bohren möchte, kann es mit den Links machen, für PrettyOS ist nur die Anwendung wichtig.


  • Mod

    Ich wollte das Ende des Kernelbereichs bestimmen. Dazu veränderte ich das Linker-Skript wie folgt:

    OUTPUT_FORMAT("binary")
    ENTRY(RealMode)
    phys = 0x00008000;
    SECTIONS
    {
      .text phys  : {
        *(.text)
      }
      .data  : {
        *(.data)
      }
      .bss : {
        *(.bss)   
      }
       _endkernel = .;   
    }
    
    //os.c
    extern ULONG* endkernel;
    ULONG get_end_kernel(){ return *endkernel; }
    
    //ckernel.c
    int main()
    {
        k_clear_screen();
        printformat("end of kernel %x\n", get_end_kernel());
        //...
    }
    

    liefert:

    end of kernel F000FF53h

    Nach meinen Berechnungen sollte natürlich ein deutlich niedrigerer Wert heraus kommen. 0x8000 (start) + 0x3540 (Hexeditor MyOS.bin) - 0x0200 (bootloader sector) = 0xB340
    Auch wenn ich _endkernel = .; hinter data setze, kommt das Gleiche heraus. Was mache ich verkehrt? 😕



  • so wie ich das sehe, deklarierst du mit

    extern ULONG* endkernel;
    

    die Variable als Zeiger; das ist noch korrekt, aber du willst da die Adresse, und nicht den Inhalt an dieser Stelle, also

    ULONG get_end_kernel(){ return endkernel; }
    

    (beachte: ohne stern!) somit kannst du den Pointer auch vom Typ void* oder char* machen, da du ja die Daten, die am Ende deines Kernels sind nicht auslesen möchtest, oder vl. schon^^


  • Mod

    Klar Adresse der Variablen, nicht Inhalt. Habe da beides durchmischt.
    So klappt es:

    extern ULONG endkernel;
    ULONG get_end_kernel()
    {
        return (ULONG) &endkernel;
    }
    

    und _endkernel vor bss (Block Started by Symbol)

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

    ergeben:

    end of kernel 0000B320h

    Das passt gut zu meiner Schätzung.

    Hinter dem .bss Block ist der Wert höher:

    end of kernel 0000BC00h

    bss: http://en.wikipedia.org/wiki/Block_Started_by_Symbol

    Was ist das wirkliche Ende des Kernels? B320h (ohne bss) oder BC00h (incl. bss)?
    Ich denke incl. bss ist korrekt.


Anmelden zum Antworten