Autor Thema: Problem beim Paging implementieren  (Gelesen 3732 mal)

nurnware

  • Beiträge: 5
    • Profil anzeigen
Gespeichert
« am: 18. September 2006, 15:49 »
Hallo!
Schön, dass ich dieses Forum entdeckt habe, ich hoffe ihr könnt mir helfen, deshalb fange ich mal beim Urschleim an, damit auch jeder weiß, was mein Problem ist.

Ich bin dabei einen Microkernel zu schreiben und habe mittlerweile fast alles nötige drin, also GDTs, IDTs, Paging, Multitasking (mit Ring Trasistions) etc.
Nun wollte ich aber, dass jeder Task (alle laufen im Usermode) eine eigene Page Directory hat, der Sicherheit halber. Auf OS-Code können Sie natürlich nicht zugreifen (nur WRITE+SUPERVISOR), aber sonst untereinander, das ist nicht schön.

Mein bisheriges Paging funktionierte so:
Vor dem Paging wurde an eine feste Adresse (4k aligned) die Page directory geladen und direkt danach eine Seite weiter die Page Tables, also 4 MB + 4096 Byte Page Dir insgesamt. Das ist gerade noch zu verkraften. Ruft man malloc() auf wird in der entsprechenden PageDir falls nicht vorhanden die PageTab verlinkt und dann in den Pagetabs (linear, aufeinanderfolgend) die angeforderte Größe auf physikalischen (teils wahllosen Speicher) gemappt. (wahllos = ein Stack der freie phys. Pages zurückgiebt, können also quer verteilt liegen... )
Funktioniert auch alles wunderbar und ist durch den Stack-basierten malloc auch schön schnell. Nach aktiviertem Paging kann man die PageTables natürlich nur über virt. Adressen ändern, da der feste 4MB-Block aber 1:1 gemappt ist, kann ich die Page-Dir-Adressen ohne Übersetzung verwenden.

So nun finde ich aber 4MB für jeden Task einfach mal freizuhalten nicht das Schlauste. Also, meine neue Implementierung:
Vor dem paging:
* Pagedir wird an einer feste Stelle erstellt
* ein Eintrag wird harcoded bei 0xC0000000 (virt.) auf die Page-Dir gemapped.
* Paging an!

soo, nun möchte ich für einen Usermode-Task neunen speicher allozieren. Die Funktion (wird im Kernelmode ausgeführt) dazu funktioniert ungefähr so (ist nicht malloc, nur eine wichtige Unterfunktion)

POINTER VirtualAlloc(UINT32 *pageDirectory, UINT32 reqAllocSize, UINT32 *physAddress, UINT32 Attributes) {
UINT32 reservedVirtAddress;
UINT32 tmpFreeVirtAddress;
UINT32 pageCount = reqAllocSize>>PAGE_BITS;

/* genug phys. Speicher frei? */
if (memFree < reqAllocSize) {
return NULL;
}

/* ist genug virtueller Frei? */
tmpFreeVirtAddress = FindFreeVirtMem(pageDirectory, pageCount);
if (tmpFreeVirtAddress == 0) {
/* no, it isn't */
return NULL;
}

/* jap, also (zufällige) phys. Speicher auf den virtuellen lin. mappen */
reservedVirtAddress = tmpFreeVirtAddress;
while (pageCount--) {
*physAddress = GetFreePhysChunk();
MapPhysToVirt(pageDirectory, reservedVirtAddress, *physAddress, Attributes);
reservedVirtAddress += PAGE_SIZE;
}

return (tmpFreeVirtAddress);
}


Ich denke die Funktionen sind selbsterklärend, wenn nicht poste/erkläre ich sie gern.
Bemerkung: physAddress ist dazu da die letze physikalische Adresse zurückzugeben, ist manchmal sinnvoll (z.B. beim Erstellen von Page Tables, damit man die phys. Adresse in die PageDir einträgt)

So, jetzt das Problem:
Angenommen in der usermode PageDir existiert ein Eintrag für eine PageTab. Nun kommen zu dieser schon existierenden Tab welche hinzu (malloc z.B.)
Also liest der Code die Adresse der Tab aus der Dir und kann natürlich nicht zugreifen, da sie phys. ist. Also eine funktion für die Kernel-Page-Dir namens MapPhysToVirt( bla bla, UINT32 *physAddress); damit man darauf zugreifen kann.
Die Funktion MapPhysToVirt() hingegen muss im Kernel-PageDir/Tab einen Eintrag machen damit der Speicher sichtbar wird. Dazu muss sie die PageTabs ändern,  dazu brauch sie also MapPhysToVirt()...
usw, usw.... bin ich zu blöd? Das kann doch nicht sein?!

Hab schon einiges programmiert, die neue Revision mit mehreren PageDirs aber noch nicht getestet, weil mir aufgefallen ist, dass ich ja ne unendliche Rekursion drin hab.

Ich nehm an es ist ein einfacher Denkfehler, verknüpft mit einem Henne-Ei-Problem, aber ich weiß nicht mehr weiter...

Vllt hilft auch ne Wochen ruhen lassen, nur hab ich da irgendwie keinen Nerv, weil ich grade mal Zeit habe weiterzuprogrammieren.

Vielen Dank schonmal (allein fürs Lesen des vielen Textes ;)

Gruß
nurnware
Hier könnte Ihre Werbung stehen!

nurnware

  • Beiträge: 5
    • Profil anzeigen
Gespeichert
« Antwort #1 am: 19. September 2006, 15:40 »
ich habe es mittlerweile gelöst.

in der Funktion MapPhysToVirt(...) hab ich einfach harcoded wenn die pageDirectory == KERNEL_PAGE_DIR ist, dann rechnet er die Virtuelle Adresse aus (die ist auch fest eingetragen nun und wird von KernelEnablePaging gleich fest gemappt und enthält die kompletten Page Tables. Macht dann zwar wieder 4MB weggeschmissen, aber zum Glück nicht für jeden Task, da jeder Task nur mit leerer PageDir (4K) startet und die Tables dynamisch alloziert wenn malloc() aus dem Usermode aufgerufen wurde.

Für nen anderen Vorschlag bin ich aber natürlich weiterhin offen!
Hier könnte Ihre Werbung stehen!

maumo

  • Beiträge: 182
    • Profil anzeigen
    • http://maumo.50webs.com/
Gespeichert
« Antwort #2 am: 19. September 2006, 16:15 »
Zitat
aber zum Glück nicht für jeden Task, da jeder Task nur mit leerer PageDir (4K) startet und die Tables dynamisch alloziert wenn malloc() aus dem Usermode aufgerufen wurde.

wie soll denn dann malloc aufgerufen werden? muss ja ein system call (z.b. durch interrupt) sein und dazu muss ja der kernel in die pagedir gemappt sein, oder?

nurnware

  • Beiträge: 5
    • Profil anzeigen
Gespeichert
« Antwort #3 am: 20. September 2006, 13:11 »
ja sorry, sollte auch heißen "mit leerer PageDir startet + KernelSpace"
Den mappe ich natürlich auch noch mit hin. Aber im Kernel hab nun im Moment aufgrund der sonst entstehenden Schwierigkeiten die Page Tables an einer festen Adresse mit einer festen Länge freigehalten. Bei den Usermode-Pagetables mach ich das nicht, die werden dynamisch alloziert. Nur der KErnelspeicher ist anfangs natürlich gemappt, nicht nur wegen Systemcalls, auch wegen des Multitaskings. Der Kernelstack muss ja sichbar sein in der ersten Anweisung des INT0, denn da ist ja der esp schon auf den Kernelstack eingestellt dank TSS-Eintrag. Sonst würde ein Pagefault auftreteten, wenn der Speicher gar nicht in User-Pagetable wäre.
Hier könnte Ihre Werbung stehen!

nurnware

  • Beiträge: 5
    • Profil anzeigen
Gespeichert
« Antwort #4 am: 12. October 2006, 15:08 »
Hallo,
habe noch ein Problem. Hoffe mir kann jemand helfen, denn hier weiß ich absolut nicht weiter:
Und zwar scheint der Prozessor beim Ändern einer Pagetable den TLB nicht zu aktualisieren. Das heißt genauer folgendes.
(Mit aktiviertem Paging das Ganze)
POINTER OpenPhysMemory(POINTER physAddress); sei eine Funktion, die nen Pointer (unsigned int *) zurückliefert im Virtuellen Adressraum und vorher einen Pagetable-Eintrag gemacht hat hinter dem die gewünschte physikalische Adresse steht (physAddress).

Nun teste ich das ganze, indem ich auf den Bildschirm schreibe:
UINT32 *myScreen = OpenPhysMemory((UINT32*) 0xB8000);
*myScreen = 'A';
wunderbar, auf dem Bildschirm kommt oben in der Ecke ein A!

nun änder ich den Code folgendermaßen:
UINT32 *myScreen = OpenPhysMemory((UINT32*) 0x123456);
*myScreen = 42;
myScreen = OpenPhysMemory((UINT32*)0xB8000);
*myScreen = 'A';
Ich schreibe also vorher an eine andere Adresse, dann erst auf den Bildsch. Dazu muss man sagen, dass OpenPhysMemory immer dieselbe virtuelle Adresse nimmt. Nun kommt nichts mehr auf dem Bildschirm an, vielmehr schreibt an die vorher gemappte Adresse 0x123456!
Wenn ich jedesmal ne neue virt. Adresse benutze geht es wunderbar, nur wenn er alte ändert, scheint ers zu ignorieren!
Wenn ich den GDB an Bochs oder QEmu hänge lässt sich feststellen, dass die physikalischen Adressen (xp /4xw 0x123456) verändert wurden, obwohl die virtuelle auf eine ganz andere zeigt!

Wie kann das sein? Ich weiß ich nicht mehr weiter.
Tritt übrigens in VMWare nicht auf, da geht alles problemlos! Hab es bisher aber nur auf einem einzigen echten Prozessor getestet, einem AMD Geode LX-800, der reagiert wie Qemu/Bochs und scheint die alten Einträge zu verwerden statt der Neuen.

PS: Auf die Pagetable kann ich natürlich schreiben, die ist virtuell gemappt. Sieht man ja auch daran, dass das erste Bsp. funktioniert. Laut GDB (und auch integrietem Qemu-Debugger) ist auch an der Adresse der PageDir sowie entspr. PageTable die gewollte phys. Adresse eingetragen. Also am Code liegts denk ich wirklich nicht, was ja auch VMware bestätigt.

Muss man irngedwie ein TLB Flush durchführen (CR3 neu schreiben?!) ???

Edit: Wenn man folgenden Code einfügt gehts:
POINTER OpenPhysMemory(POINTER physAddress) {
 ...
ProcessorWriteCR3(ProcessorReadCR3());
}
Das kanns ja aber wohl bitte nicht sein?!?! Das muss doch anders gehen...?!
« Letzte Änderung: 12. October 2006, 15:20 von nurnware »
Hier könnte Ihre Werbung stehen!

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #5 am: 12. October 2006, 17:08 »
cr3 neu laden ist eine mögliche Lösung, ja. Allerdings geht dann der gesamte Cache drauf. In LOST wird per Inline-Assembler INVLPG benutzt, das den TLB-Eintrag für genau eine virtuelle Adresse invalidiert.
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

nurnware

  • Beiträge: 5
    • Profil anzeigen
Gespeichert
« Antwort #6 am: 12. October 2006, 17:35 »
Ah, die Instuktion kannte ich nichtmal. Vielen Dank! Habs gleich implementiert, jetzt gehts auch ohne CR3-Rewrite.
Hier könnte Ihre Werbung stehen!

 

Einloggen