Kann ich nicht, der Kernel hat keine Ahnung von PCI (oder was auch immer). Klassische Lösung: Alles was als root läuft (oder sonst irgendeine Art von Recht hat), darf das.
Und genau das möchte ich bei mir nicht, mit Ports ist das noch relativ einfach und wenn ich eine Möglichkeit gefunden habe, bestimmten Speicher nur in meinem Device-Server zuzulassen, dann habe ich auch das Problem gelöst (ich "vererbe" den Speicher dann einfach an die Treiber, das wäre die Lösung für MemoryMapped-IO).
Du traust also dem Treiber zu, dass er nicht DMA auf "irgendwelchen" Speicher in die Hardware schreibt, traust ihm dann aber wieder nicht zu, dass er sich Speicher holen darf, den er braucht?
Dein Memory Manager hat den Überblick über den gemappten physischen Speicher.
Was meinst du genau mit "gemappten physischen Speicher"? Meinst du da welcher Speicher in Benutzung ist und welcher nicht?
Also, die gesamte Speicherverwaltung besteht aus physischem und virtuellem Adressraum, hinter dem jeweils teilweise Speicher steht. Und du musst wissen, welcher Speicher frei ist - getrennt für virtuellen und physischen Speicher, als auch die Verbindung zwischen beiden Adressräumen. Damit weißt du also, welcher Teil des physischen Speichers wo im virtuellen Speicher eines jeden Prozesses gemappt ist.
Außerdem, wenn ich physischen Speicher möchte, ist es mir ja erstmal egal, wo der liegt - die Adresse kann ich dann ja in die Hardware reinprogrammieren.
Wenn wir jetzt über MemoryMapped-IO reden, dann ist es unter Umständen nicht egal wo dieser physische Speicher liegt, bzw. sollte hinter einer MemoryMapped-IO Adresse (im besten Fall, wo also weit weniger als 4GB RAM vorhanden sind) kein physischer Speicher liegen, das Alignment macht dir nämlich einen Strich durch die Rechnung.
Hä?
Teile des physischen Adressraums sind
nicht mit Speicher unterlegt, sondern mit Konfigurationsregistern der Hardware, meist jenseits des realen RAM-Ausbaus (z.B. jenseits der 2GB). Dort muss ein Treiber also unter Umständen physischen Adressraum in seinen eigenen virtuellen Adressraum mappen, der
kein RAM ist.
Die Datenkommunikation zwischen Treiber und Hardware läuft dann wiederum über shared memory, wo die physische Adresse bekannt sein muss (wg. DMA). Also musst du auch dort physischen Adressraum, diesmal mit RAM, in den virtuellen Adressraum mappen können.
Wo die Adressen im virtuellen Adressraum landen, ist egal. Welche Adressen im physischen Adressraum benutzt werden, hängt konkret von der Hardware ab (Konfigurationsregister sind fest) und ist nur teilweise programmierbar (Zieladresse des DMA ist es).
Das gilt für MMIO. Wenn du über Ports zugreifst, hast du einen dritten Adressraum, der mit beiden anderen nichts zu tun hat. Für MMIO und DMA muss es auf jeden Fall die beschriebenen Möglichkeiten geben.
Wenn du nur reines PIO machst, brauchst du beides nicht. Welche dieser Möglichkeiten du brauchst, hängt halt konkret von der Hardware ab.
Soweit kommt es aber bei mir noch nicht, da ich noch keine Möglichkeiten habe, den Speicher für MemoryMapped-IO selbst zu bestimmen. Dazu müsste ich gezielt Speicher aus meinem PMM entfernen können.
Als "belegt" markieren reicht aus. Wie gesagt, Teile des physischen Adressraums, den du benötigst, sind nicht mit RAM unterlegt.
Dann geht das halt nicht, ganz einfach. Wenn mein Gerät DMA machen möchte, muss ich mindestens einen DMA-Block physisch zusammenhängend bereitstellen. Und wenn ich gern "schnell" hätte, muss ich halt mehrere solcher Blöcke haben - im einfachsten Fall als ein großer Block. Ist aber Sache des Treibers, wieviele Blöcke ich welcher Größe wo brauche. In meinem Beispiel sind das einfach 2 Blöcke, die müssen auch nicht hintereinander liegen.
Das ist aber schlecht! Wie willst du nem User folgendes verklickern:
er hat zwar von seinen 2GB RAM noch 1GB frei, aber da der Speicher so fragmentiert ist dass jede 2. Page (physisch gesehen) in Benutzung ist, kann er keine Daten mehr von seiner HDD laden?
Wenn der Treiber physisch 4 MB RAM unterhalb der 16 MB fordert (weil es halt ein popeliger ISA-SCSI-Controller ist), dann wird das wahrscheinlich nicht gehen. Wenn du aber gerne physisch 4 MB RAM als Block irgendwo möchtest, dann geht das wahrscheinlich schon. Ist der RAM voll, dann geht das eben nicht. Wie auch?
Außerdem sollte der Treiber für die Kommunikation mit dem Gerät den Speicher direkt beantragen, oder zumindest einen kleinen Block (z.B. 64 KB). Damit garantierst du auch bei voller Speicherauslastung, dass du die Hardware ansteuern kannst. Alternativ gehst du halt immer weiter runter, bis du einen physischen(!) Block entsprechender Größe bekommst. Dann holst du dir einen Dateisystemblock von der Festplatte halt nur in 4K-Häppchen.
Ich weiß nicht wie dein MemoryManager funktioniert, aber ich habe einen PMM und einen VMM. Der PMM ist einfach nur dafür verantwortlich, das man freien physischen Speicher von ihm bekommt und nicht mehr benötigten physischen (also vorhandener RAM) zurückgeben kann.
Richtig, und was deine Treiber möchten, ist die Möglichkeit, bestimmte
und unbestimmte Bereiche zu bekommen.
Der VMM kümmert sich einfach nur darum, welche virtuelle Adresse frei ist und welche nicht.
Was auch hinreicht.
Der Punkt ist, mein PMM verwaltet nicht immer 4GB sondern nur soviel Speicher wie da ist.
Was nicht ausreicht, da der physische Adressraum halt 4GB groß ist und du dort (z.B. ab 3GB) den Framebuffer der Grafikkarte oder die PCI-Konfigurationsregister findest. Die musst du auch verwalten!
Ja, ich meine aber das:
- Du forderst 64KB an
- bekommst eine virtuelle Adresse zurück
- jetzt machst du für jede 4KB Page einen Syscall um die physische Adresse rauszubekommen
Was man machen könnte, ist einen extra Syscall für Treiber zu implementieren, dem man einen Pointer übergibt, wo man die physischen Adressen des virtuellen Speichers (der angefordert wurde) reinschreibt.
Warum so kompliziert? Was hat dein Treiber davon, wenn er sich jede einzelne Page, die er für die Kommunikation mit der Hardware braucht, einzeln anfordern muss? Außerdem garantierst du ja nicht, dass die Pages zusammenhängend sind.
Erklär du mal dem HDD-Controller, dass er die ersten 4 KB nach 0x3400 0000, die zweiten 4 KB nach 0x3800 0000 und die dritten 4 KB nach 0x3400 8000 schreiben soll. Das heißt, du programmierst die Hardware
dreimal, um 12KB von der Festplatte zu lesen.
Implementier lieber zwei Funktionen:
*getPhysMem( int size, ptr physAddr )
-> gib mir "size" Bytes ab physisch "physAddr" zurück
=> malloc() mit gegebener Adresse für deinen PMM
*getPhysMem( int size, ptr *physAddr )
-> gib mir "size" Bytes Speicher und schreibe in "physAddr" die physische Adresse rein
=> malloc() mit Rückgabe der verwendeten physischen Adresse
Wenn du willst, kannst du noch einen Alignment-Parameter mit einbinden, wenn du z.B. Hardware hast, die ein Alignment >4K braucht.
Was du dir sparst:
- Prozesse müssen die PageTables nicht selbst parsen können (Portabilität, vgl. taljeth)
- Prozesse haben keine Möglichkeit, das Mapping zwischen virt. und phys. Adressraum rauszukriegen (Sicherheit)
- nur ein Syscall pro Treiber und Adressraumblock
Oder sage mir, warum mein Ansatz nicht geht.
Gruß