{{Navigation |Zurück = [[ARM-OS-Dev Teil 8 – Ein erstes Programm]] }} == Ziel == Zwar können wir nun ELF-Programme laden, doch das wird natürlich nicht mehr so toll funktionieren, wenn wir mehrere verschiedene auf einmal laden wollen und alle die gleiche Ladeadresse verwenden. Um diesem Problem zu begegnen, kann man den Pagingmechanismus verwenden: Jedes Programm erhält seinen eigenen virtuellen Adressraum, sodass es gar nicht mit anderen Programmen ins Gehege kommen kann, weil es auf deren Daten gar nicht zugreifen kann. == Paging == Zur Einführung ist es sinnvoll, zunächst den [[Paging|Paging-Artikel]] zu lesen, da dieser eine solche am Beispiel x86 liefert. Der auf ARM-Prozessoren verwendete Pagingmechanismus ist dem von x86 recht ähnlich, sodass es nach dem Verständnis des Artikels nicht schwerfallen sollte, auch das ARM-Paging nachzuvollziehen. An dieser Stelle sei erwähnt, dass es zwei sich leicht voneinander unterscheidende Pagingmethoden auf ARM-Prozessoren gibt: Das VMSAv6-Paging, das mit ARMv6 eingeführt wurde, und ein nicht näher benanntes, welches davor in einigen ARMv5-CPUs implementiert war (daneben gibt es noch eine ganz andere Methode des Speicherschutzes, die sogenannte ''Memory Protection Unit'' (MPU, im Gegensatz zur MMU), welche hier aber nicht weiter betrachtet wird). Vom Prinzip her unterscheiden sich beide Varianten nicht. Unterschiede sind die, dass man vor VMSAv6 einzelne Teile einer Page verschieden schützen konnte und es 1-kB-Pages gab, während es in VMSAv6 globale Pages (s. x86-Paging) und die Möglichkeit gibt, Codeausführung je nach Page zu erlauben oder zu verbieten. Da auch ARM gewissen Wert auf Abwärtskompatibilität legt, gibt es die vor-VMSAv6-Pagingvariante auch in VMSAv6, dort heißt sie „VMSAv6, subpages enabled“ (aufgrund der erwähnten Möglichkeit, Teile einer Page einzeln schützen zu lassen). Diese Variante sollte auf allen ARM-CPUs funktionieren, welche eine MMU besitzen, sodass diese hier verwendet wird. === Translation Table === Wenn die MMU der CPU versucht, eine virtuelle Adresse aufzulösen, nimmt sie die oberen zwölf Bit als Index eines Eintrags in der sogenannten ''Translation Table''. Diese hat 4096 Einträge zu je vier Byte, ist also insgesamt 16 kB groß. Die unteren zwei Bit des Eintrags geben den Typ an: Bei 0b00 ist der Eintrag frei, beim Adresszugriff wird eine Abort-Exception ausgelöst. 0b01 ist ein Verweis auf eine weitere Tabelle (eine sogenannte ''Coarse Page Table''), 0b10 ein Verweis auf eine Section (sozusagen eine 1-MB-Page); 0b11 ist als Typ reserviert (stand vor VMSAv6 für ''Fine Page Tables''). Ein Coarse-Page-Table-Verweis sieht folgendermaßen aus: {| {{prettytable}} ! Bit || Beschreibung |- | 0 – 1 || Ist 0b01 (Typ des Eintrags). |- | 2 – 4 || Sollte auf 0 gesetzt werden. |- | 5 – 8 || Gibt die Domain an, der diese Page Table angehört (s. u.). |- | 9 || Möglichst 0. |- | 10 – 31 || Bits 10 bis 31 der Basisadresse der Page Table (muss an einer 1-kB-Grenze liegen). |} Da hier im Tutorial keine Sections verwendet werden, ist es hier unnötig, den Aufbau eines Sectioneintrags darzulegen. === Domains === Zusätzlich zur Möglichkeit, jeder einzelnen Page spezielle Zugriffsrechte zuzuordnen, bietet ARM auch sogenannte Domains. Eine Domain ist eine Gruppe verschiedener Speicherregionen, auf die man dadurch relativ schnell Zugriff verbieten oder erlauben kann, indem man einfach die Domainzugriffsrechte ändert. Wie man gesehen hat, erfolgt die Zuordnung von Speicher zu Domain in der Translation Table und kann nur für Pages (nicht für Sections) durchgeführt werden. Es gibt drei Zugriffsarten: Gar kein Zugriff, sodass bei Zugriff immer ein Abort ausgelöst wird; ''Client'', hierbei werden die Zugriffsrechte, die in der Page Table angegeben sind, beachtet; ''Server'', bei welcher jeder Zugriff erlaubt wird (wenn an der virtuellen Adresse eine Page vorhanden ist). In diesem Tutorial werden wir Domains ignorieren, sodass alle 16 Domains, die es gibt, auf ''Client'' gestellt werden und alle Page Tables in die Domain 0 eingetragen werden (da aber alle ''Client'' sind, ist es auch egal, in welche Domain sie kommen). === Page Tables === Vor VMSAv6 gab es zwei Page-Table-Arten: Coarse und Fine Page Tables, erstere mit 256 Einträgen, letztere mit 1024. Mit VMSAv6 wurden letztere abgeschafft. Hat die MMU also beim Zugriff auf die Translation Table festgestellt, dass auf eine Page Table verwiesen wird, so verwendet sie die Bits 12 bis 20 der virtuellen Adresse als Index in der angegebenen (Coarse) Page Table. Auch hier gibt es wieder 4-Byte-Einträge, deren niederwertigste zwei Bit den Typ des Eintrags angeben: 0b00 zeigt wieder einen unbenutzten Eintrag an, 0b01 steht für eine 64-kB-Page, 0b10 für eine 4-kB-Page mit Subpages und 0b11 für eine ohne. Da hier nur 4-kB-Pages mit Subpages verwendet werden (ohne Gebrauch von den Subpages zu machen), sei auch hier nur der Aufbau dieses einen Eintragtyps dargestellt: {| {{prettytable}} ! Bit || Beschreibung |- | 0 – 1 || Ist 0b10 (4-kB-Page mit Subpages). |- | 2 || B-Bit: Bufferable |- | 3 || C-Bit: Cachable |- | 4 – 5 || Zugriffsrechte für das erste Kilobyte. |- | 6 – 7 || Zugriffsrechte für das zweite Kilobyte. |- | 8 – 9 || Zugriffsrechte für das dritte Kilobyte. |- | 10 – 11 || Zugriffsrechte für das vierte Kilobyte. |- | 12 – 31 || Bits 12 bis 31 der Basisadresse der Page (muss an einer 4-kB-Grenze liegen). |} Wenn jemand die genaue Bedeutung von B- und C-Bit verstanden hat, möge er sie hier bitte eintragen. Soweit ich verstanden habe, sollte man beide für MMIO auf 0 und für normalen Speicher auf 1 setzen. Um sicher zu gehen, werden sie in diesem Tutorial einfach auf 0 gesetzt. Für die Zugriffsrechte gilt: {| {{prettytable}} ! Wert || Bedeutung |- | 0b00 || * Bei gelöschtem R- und S-Bit: Kein Zugriff * Bei gesetztem R-Bit (und gelöschtem S): Von allen Modi aus nur lesbar * Bei gesetztem S-Bit (und gelöschtem R): Von privilegierten Modi aus lesbar, vom Usermodus kein Zugriff |- | 0b01 || Vollzugriff für privilegierte Modi, keiner für Usermodus |- | 0b10 || Vollzugriff für privilegierte Modi, vom Usermodus aus lesbar |- | 0b11 || Vollzugriff für alle Modi |} Privilegierte Modi sind alle nicht-Usermodi. Die Bits R und S befinden sich in einem Kontrollregister der MMU (die Namen stehen für ''ROM Protection'' respektive ''System Protection''). == CP15 == Im Gegensatz zu x86-CPUs befindet sich die MMU aus Programmierersicht bei ARMs nicht direkt in der CPU (bei x86 können Kontrollregister wie CR0 oder CR3, die auch das Paging steuern, wie normale Register mit dem MOV-Befehl gesetzt werden), sondern in einem Koprozessor. Dieser Speicherverwaltungskoprozessor trägt den Namen CP15 (CP für Coprocessor), da seine ID, mit der er in den Koprozessorbefehlen angesprochen wird, 15 ist. Er übernimmt die Speicherverwaltung der CPU (und auch andere Aufgaben, er heißt offiziell ''System Control Coprocessor''), das heißt, er kann entweder als MPU oder MMU agieren. Um den CP15 zu steuern, werden mit den Befehle MCR und MRC bestimmte Werte in seine Register geschrieben bzw. daraus gelesen. MCR- und MRC-Befehle haben die folgende Form:
mcr/mrcDabei ist '', , , , , [op2]