Paging & Heap


  • Mod

    Badestrand hat freundlicherweise das Paging- und Heap-Modul komplett neu gestaltet, siehe aktuelle Version: https://prettyos.svn.sourceforge.net/svnroot/prettyos/trunk/Source/
    http://prettyos.svn.sourceforge.net/viewvc/prettyos.tar.gz?view=tar
    (Achtung: im makefile wurde "cp -f CKERNEL.SYS A:\CKERNEL.SYS" auskommentiert. Dafür wird nun "CreateFloppyImage PrettyOS FloppyImage.bin ../_stage1_bootloader/boot.bin ../_stage2_bootloader/BOOT2.SYS CKERNEL.SYS" und "cmd /c bochs.bxrc" ausgeführt.)



  • Es wird jetzt auch die Memory Map vom BIOS berücksichtigt und entsprechende Bereiche werden in der physischen Verwaltung als "reserviert" belegt.

    Es fehlt aber noch die Umwandlung physischen Speichers in virtuellen Speicher (also das Umrechnen der Adressen). Wird noch ein wenig schwierig, weil dazu alle Pagedirectories durchsucht werden müssen. Oder reicht es da, wenn nur das Kernel-Directory durchsucht wird, es also für den User-Speicher keine Umrechnung gibt?


  • Mod

    Oder reicht es da, wenn nur das Kernel-Directory durchsucht wird

    brauche das z.B. für pciScan/BARx hinter Paging-Install. Virtuelle und physische Adresse.



  • Badestrand schrieb:

    Es fehlt aber noch die Umwandlung physischen Speichers in virtuellen Speicher (also das Umrechnen der Adressen).

    Brauchst du das wirklich? Ich habe dafür noch keinen Einsatzzweck gesehen. An manchen Stellen brauchst du die physische Adresse, aber die kannst du ja direkt beim Allozieren in der passenden Datenstruktur speichern statt hinterher die virtuelle Adresse konvertieren zu wollen.


  • Mod

    An manchen Stellen brauchst du die physische Adresse, aber die kannst du ja direkt beim Allozieren in der passenden Datenstruktur speichern statt hinterher die virtuelle Adresse konvertieren zu wollen.

    @taljeth: Beim vorherigen Modul habe ich die von dir angesprochene Umwandlung wie folgt erledigt:

    uint32_t show_physical_address(uint32_t virtual_address)
    {
        page_t* page = get_page(virtual_address, 0, kernel_directory);
        return( (page->frame_addr)*PAGESIZE + (virtual_address&0xFFF) );
    }
    

    Aber nun zu PCI und BAR:
    Bei der Ermittlung der sechs BAR erhalte ich vom PCI-Scan physikalische Adressen, die ich mappen muss. Um das Ergebnis im virtuellen Speicherraum (also pciScan hinter paging_install) zu finden, benötige ich die erzeugte virtuelle Adresse. Dafür hatte ich beim alten Modul von JM keine Bestimmungsmethode, soweit ich das verstanden habe.

    Daher die von mir noch nicht abgelegte Idee der Umrechnung: phys. --> virtuell

    @Badestrand: Wie sind diese beiden Fälle bei deinem Modul bisher gelagert?

    Das hier in pciScan() kann ich nur vor dem Einschalten des Paging ohne Mapping erledigen:

    /// TEST Memory Size Begin
                                    cli();
                                    pci_config_write_dword  ( bus, device, func, PCI_BAR0 + 4*i, 0xFFFFFFFF );
                                    pciBar = pci_config_read( bus, device, func, PCI_BAR0 + 4*i             );
                                    pci_config_write_dword  ( bus, device, func, PCI_BAR0 + 4*i,
                                                              pciDev_Array[number].bar[i].baseAddress       );
                                    sti();
                                    pciDev_Array[number].bar[i].memorySize = (~pciBar | 0x0F) + 1;
                                    printformat("sz:%d ", pciDev_Array[number].bar[i].memorySize );
                                    /// TEST Memory Size End
    


  • Erhard Henkes schrieb:

    @taljeth: Beim vorherigen Modul habe ich die von dir angesprochene Umwandlung wie folgt erledigt:

    uint32_t show_physical_address(uint32_t virtual_address)
    {
        page_t* page = get_page(virtual_address, 0, kernel_directory);
        return( (page->frame_addr)*PAGESIZE + (virtual_address&0xFFF) );
    }
    

    Ja, es war zu früh. Ich hab's genau falschrum gesagt. Ändert aber nichts an der Tatsache, dass man eigentlich nie anhand der physischen die virtuelle Adresse rausfinden muss. 😉

    Bei der Ermittlung der sechs BAR erhalte ich vom PCI-Scan physikalische Adressen, die ich mappen muss. Um das Ergebnis im virtuellen Speicherraum (also pciScan hinter paging_install) zu finden, benötige ich die erzeugte virtuelle Adresse.

    Die Mappingfunktion sollte dir einfach die virtuelle Adresse zurückgeben, an die sie gemappt hat.


  • Mod

    So sieht unsere Funktion zur Ermittlung der physikalischen Adresse zur Zeit aus:

    static uint32_t paging_get_phys_addr( page_directory_t* pd, void* virt_addr )
    {
        // Find the page table
        uint32_t pagenr = (uint32_t)virt_addr / PAGESIZE;
        page_table_t* pt = pd->tables[pagenr/1024];
        ASSERT( pt != 0 );
    
        // Read the address, cut off the flags, append the address' odd part
        return (pt->pages[pagenr%1024]&0xFFFF000) + (((uint32_t)virt_addr)&0x00000FFF);
    }
    

    Die Mappingfunktion sollte dir einfach die virtuelle Adresse zurückgeben, an die sie gemappt hat.

    @Badestrand: Da solltest Du etwas dazu schreiben.


  • Mod

    Hier die Funktionen, die Paging und Heap bisher bieten:

    paging.h:

    bool paging_alloc( page_directory_t* pd, void* addr, uint32_t size, uint32_t flags );
    void paging_free ( page_directory_t* pd, void* addr, uint32_t size );
    void paging_switch( page_directory_t* pd  );
    page_directory_t* paging_create_user_pd();
    void paging_destroy_user_pd( page_directory_t* pd );
    uint32_t paging_install();
    

    kheap.h:

    void heap_install();
    void* k_malloc( uint32_t size, uint32_t alignment );
    void k_free( void* mem );
    


  • Die Mappingfunktion sollte dir einfach die virtuelle Adresse zurückgeben, an die sie gemappt hat.

    Naja, beim Mapping gibt man ja eigentlich schon die virtuelle Adresse an (so wie ich das kenne, sonst skizziere bitte mal).

    Wie ist das eigentlich mit dem physischen Speicher, der bei PCI verwendet wird: Ist der in der Memory-Map als "nicht frei" angegeben?
    Ansonsten bringt ein Mapping phys->virt auch nur mäßig was, weil man muss ja u.U. allokieren. Wie wär's mit

    // Gibt den Anfang der virtuellen Adresse zurück, die
    //  auf den angegebenen physischen Bereich gemappt ist.
    void* paging_get_phys( uint32_t phys_start, uint32_t phys_end );
    

    ?

    Und was machen wir, wenn der entsprechende physische Speicher schon belegt ist? In welchem Adressbereich befinden sich typischerweise die PCI-Adressen? 0MB-20MB haben wir ja identity-gemappt.


  • Mod

    In welchem Adressbereich befinden sich typischerweise die PCI-Adressen?

    Ich habe mir mal einige Screenshots angesehen:

    USB UHCI:
    6000h - 7000h

    USB OHCI und EHCI:
    CDBFF800h
    CFFFD000h - CFFFF000h
    F0804000h - F0805000h

    Das sind physikalische Adressen vor dem Paging.
    Wenn ich diese auf virtuelle Adressen "mappen" muss, brauche ich eigentlich so etwas:
    virtual_address = paging_alloc(...,phys_address)
    ... oder sehe ich das falsch?

    Mit dem alten Modul hatte ich das gleiche Problem. Nach dem Mappen wusste ich nicht, auf welcher virtuellen Adresse die physikalische BAR-Adresse gelandet ist. Also hätte ich GetVirtualAddress(phys-Address) benötigt, gab es aber nur umgekehrt.


  • Mod

    Und was machen wir, wenn der entsprechende physische Speicher schon belegt ist?

    Ist das überhaupt nutzbares RAM?



  • Erhard Henkes schrieb:

    Das sind physikalische Adressen vor dem Paging.
    Wenn ich diese auf virtuelle Adressen "mappen" muss, brauche ich eigentlich so etwas:
    virtual_address = paging_alloc(...,phys_address)
    ... oder sehe ich das falsch?

    Richtig, ich sehe mal zu, könnte diese Woche noch klappen (aber nicht vor morgen Abend).

    Erhard Henkes schrieb:

    Ist das überhaupt nutzbares RAM?

    Gute Frage, ich checke dann auch gleich mal die Memory Map..


  • Mod

    checke dann auch gleich mal die Memory Map

    Thx. So ein paar Analysefunktionen wären didaktisch nicht schlecht. Ins alte Modul habe ich ein paar rein geschrieben. Vielleicht kann man da was übertragen. 🙂


  • Mod

    taljeth im IRC: <taljeth> In eurem Pagingcode wollt ihr vermutlich den TLB gelegentlich invalidieren, übrigens.



  • Erhard Henkes schrieb:

    taljeth im IRC: <taljeth> In eurem Pagingcode wollt ihr vermutlich den TLB gelegentlich invalidieren, übrigens.

    An welchen Stellen denn genau? Oh, wahrscheinlich z.B. wenn der Heap erweitert wird. Bzw generell bei paging_alloc. Guter Punkt, muss ich mich drum kümmern, ich hatte aber merkwürdigerweise noch keine Probleme damit.



  • Badestrand schrieb:

    Erhard Henkes schrieb:

    Ist das überhaupt nutzbares RAM?

    Gute Frage, ich checke dann auch gleich mal die Memory Map..

    Ich hab mal ein wenig geforstet, hier Ausgabe auf meinem Läppi:

    Memory Map:
    Begin       Ende     Typ (ungleich 1 heißt "belegt")
    00000000 -> 0009F800 frei
    0009F800 -> 000A0000    2
    000E0000 -> 00100000    2
    00100000 -> 1F680000 frei
    1F680000 -> 1F68E000    4
    1F68E000 -> 1F700000    4
    1F700000 -> 1F800000    2
    1F800000 -> 20000000    2
    [Hier noch viele Einträge für Bereich 0xE0000000 - 0xFFFFFFFF; und das bei 512 MB RAM!]
    
    USB-PciScan-Ausgabe:
    I/O Adresse 0x000050A0, Size -65504
    I/O Adresse 0x000050C0, Size -65504
    I/O Adresse 0x000050E0, Size -65504
    Mem Adresse 0xD0444000, Size 1024
    

    Darauf komme ich nicht ganz klar. Einmal wegen der Memory Map Einträge bei über 512 MB RAM, zum Zweiten der USB-Mem in so einem hohen Bereich, zum Dritten sind die USB-I/O-Adressen nicht von der Memory-Map als "belegt" markiert.



  • Badestrand schrieb:

    An welchen Stellen denn genau? Oh, wahrscheinlich z.B. wenn der Heap erweitert wird. Bzw generell bei paging_alloc. Guter Punkt, muss ich mich drum kümmern, ich hatte aber merkwürdigerweise noch keine Probleme damit.

    Probleme gibt es erst dann, wenn du eine schonmal benutzte virtuelle Adresse erneut vergibst und woanders hinmappst. Im allgemeinen also nachdem du Paging fertig getestet hast und überall mit Fehlern rechnest, nur nicht dort. Und TLB-Bugs sind hässlich zu debuggen. Mach's also besser gleich richtig.

    Generell in paging_alloc klingt nach einer guten Idee. Beim Freigeben kann es auch nicht schaden. Wenn es dort fehlt, sollten korrekte Programme zwar keine Probleme machen, aber für das Debuggen nicht ganz korrekter Programme ist es besser, wenn sie sofort auf die Schnauze fliegen und nicht erst irgendwann später.



  • Falls, ich mich mal einmischen darf 😉

    @Badestrand

    Also die USB I/O Ports werden auch nicht in der Memory Map auftauchen, da es I/O-Ports sind und keine Speicheradressen! Es ist ganz normal das in der Memory Map sehr viele Bereiche am oberen Ende des Adressraumes kommen, da dort die ganzen PCI Sachen (welche Memory Mapped I/O verwenden) hingemappt werden.

    @all

    Ich habe noch keinen Blick auf euren Source geworfen, aber wie funktioniert denn eure Funktion, wenn ihr eine Page alloziert? Ich meine woher wisst ihr welche virtuelle Adresse im aktuellen PageDir noch frei ist? Ich würde ich empfehlen eine Funktion zu schreiben, die einfach nur einen Bereich des virtuellen Adressraumes alloziert und in die ihr dann nach belieben physikalischen Speicher mappen könnt.



  • taljeth schrieb:

    Probleme gibt es erst dann, wenn du eine schonmal benutzte virtuelle Adresse erneut vergibst und woanders hinmappst. Im allgemeinen also nachdem du Paging fertig getestet hast und überall mit Fehlern rechnest, nur nicht dort. Und TLB-Bugs sind hässlich zu debuggen. Mach's also besser gleich richtig.

    Generell in paging_alloc klingt nach einer guten Idee. Beim Freigeben kann es auch nicht schaden. Wenn es dort fehlt, sollten korrekte Programme zwar keine Probleme machen, aber für das Debuggen nicht ganz korrekter Programme ist es besser, wenn sie sofort auf die Schnauze fliegen und nicht erst irgendwann später.

    Cool, danke! Ich will mir auch gar nicht vorstellen, wie das zu Debuggen wäre 🙂 Ich bau's dann also in paging_alloc und paging_free ein. Ist es besser, die einzelnen Adressen zu updaten oder gleich cr3 neu zu laden (was wohl einfacher ist)?

    FlashBurn schrieb:

    Also die USB I/O Ports werden auch nicht in der Memory Map auftauchen, da es I/O-Ports sind und keine Speicheradressen! Es ist ganz normal das in der Memory Map sehr viele Bereiche am oberen Ende des Adressraumes kommen, da dort die ganzen PCI Sachen (welche Memory Mapped I/O verwenden) hingemappt werden.

    Ok, also müssen wir uns da wohl keine Sorgen machen..? Aber müsste der USB-Adressbereich ab 0xD0444000 nicht in der Memory Map auftauchen? Oder wird der nur nicht eingetragen, weil mein Speicher sowieso nur bis 0x20000000 geht?

    FlashBurn schrieb:

    Ich habe noch keinen Blick auf euren Source geworfen, aber wie funktioniert denn eure Funktion, wenn ihr eine Page alloziert? Ich meine woher wisst ihr welche virtuelle Adresse im aktuellen PageDir noch frei ist? Ich würde ich empfehlen eine Funktion zu schreiben, die einfach nur einen Bereich des virtuellen Adressraumes alloziert und in die ihr dann nach belieben physikalischen Speicher mappen könnt.

    Bis jetzt haben wir afaik gar keine Gefahr der Überschneidung. Der Bereich 0-20MB ist Identity gemappt. Ab 3 GB (0xC0000000) beginnt der Heap, der alloziert beim Wachsen die fortfolgenden Adressen und sonst sollte/darf in den beiden Bereichen niemand anders allozieren.
    Sonst an Allokationen gibt's nur das Laden der ELF, wobei wir im Moment eine Basis-Adresse von 0x01400000 (20 MB) voraussetzen, dort den Speicher im User-Page-Directory allokieren und den ELF-Code hinkopieren. Da ist vorher auch nichts und sonst haben wir nichts an Allokationen.
    Wie wird das denn "normalerweise" gemacht?



  • Ok, also müssen wir uns da wohl keine Sorgen machen..? Aber müsste der USB-Adressbereich ab 0xD0444000 nicht in der Memory Map auftauchen? Oder wird der nur nicht eingetragen, weil mein Speicher sowieso nur bis 0x20000000 geht?

    Da muss ich ehrlich passen, soweit bin ich auch noch nicht (ich müsste mir jetzt auch nochmal die Memorymap auf meinem PC angucken). Auf der einen Seite würde ich sagen, er sollte eigentlich in der Memorymap drin stehen, aber soweit ich weiß kann der Bereich ja auch geändert werden (wird über die BARs im PCI CFG Space gemacht). Probleme gibt es erst, wenn du 4GiB Ram hast, und der Speicher eines Gerätes auf eine Adresse gemappt (physikalisch nicht virtuell) wird, der in eurem PMM als frei markiert ist.

    Wie wird das denn "normalerweise" gemacht?

    Also ich kann dir sagen, wie ich es mache 😉 Wie es im Allgemeinen Funktioniert kann ich dir so genau auch nicht sagen.

    Wenn ich dich richtig verstanden habe, sieht euer Adressraum so aus:

    0-20MiB Kernel Space
    20MiB - 3GiB User Space
    3GiB - 4GiB Kernel Heap
    

    Um auf deine Frage wegen dem TLB zurück zu kommen, es ist aus Performancesicht besser ein "invlpg" zu machen, denn da wird nur der Eintrag für diese Page im TLB "gelöscht". Sobald CR3 neu geschrieben wird, wird der gesamte TLB gelöscht, mit einer Ausnahme, die Pages die das Globalflag gesetzt haben, werden nicht gelöscht. Im Endeffekt heißt das, dass du die Pages die das Flag gesetzt haben nur mit dem "invlpg" Befehl aus dem TLB löschen kannst.

    Was macht ihr denn wenn mein Programm mehr als eine Page bräuchte und der Speicher zusammenhängend sein muss?

    Ich würde euch empfehlen folgendes zu machen (ich mache es so und bisher funktioniert es 😉 ):

    • Eine Datenstruktur in der steht welche Adressbereiche noch frei sind (wenn man das ganze dann noch nach Größe und Basisadresse sortieren kann, wird es performanter).
    • Eine Funktion die euch physikalischen Speicher an eine virtuelle Adresse mappt.
    • Der VMM (virtuelle Memory Manager) sucht dann nach einem Adressbereich der für die Anforderung groß genug ist und löscht ihn aus der Datenstruktur. Dann ruft er den PMM (physikalischen Memory Manager) auf und mapped die Physikalische Page an die Basis des Bereichs den ihr vorher "alloziert" habt, das macht er so oft bis der ganze Bereich gemappt ist.

    Diese Variante hat den Vorteil, das man auch einfach nur Adressbereiche "allozieren" kann und dann in diesen Adressbereich den Speicher von den Geräten mappen kann. Denn wie wollt ihr dieses Problem lösen?

    Auch solltet ihr euch fragen, was passiert wenn im Kernelspace (>=3GiB) eine neue PageTable/Page gemappt wird, wie erfahren das die anderen PageDirs?

    Mir fällt gerade auf, das ich noch nicht alles erfasst habe, wie ihr es macht. Ich meine wo werden eure PageDirs von den anderen User Prozessen gespeichert?

    Ich hoffe ich war einigermaßen verständlich 😉 Wenn nicht einfach nachfragen was unklar ist!


Anmelden zum Antworten