�bersetzung von Philipp Strebl (viele danke!)
Die CPU des 8088 im urspr�nglichen IBM PC war nicht besonders erweiterungsf�hig. Vor allem gab es keinen einfachen Weg, auf mehr als ein MB physischen Speicher zuzugreifen. Um dieses Problem zu beseitigen und trotzdem Abw�rtskompatibilit�t zu wahren, entwickelte Intel die 80286 CPU mit zwei Ausf�hrungsmodi: dem real mode, in dem der 80286 sich wie ein schneller 8088 verh�lt, und den protected mode (heute als 16-bit protected mode bezeichnet). Protected mode erlaubt Programmen den Zugriff auf mehr als ein MB physischen Speicher und sch�tzt gleichzeitig vor falscher Verwendung von Speicher (zum Beispiel der Ausf�hrung eines data segment als code, oder dem Schreiben in ein code segment). Eine verbesserte Version, 32-bit protected mode, wurde mit der 80386 CPU pr�sentiert.
Unterschiede zwischen protected mode und real mode:
real mode | 16-bit protected mode | 32-bit protected mode | |
---|---|---|---|
Segmentbasisaddresse | 20-bit (1 MB range) = 16 * Segmentregister | 24-bit (16 MB range), im descriptor | 32-bit (4 GB range), im descriptor |
Segmentgr��e (h�chstens) | 16-bit, 64 KB (fix) | 16-bit, 1 B - 64 KB | 20-bit, 1 B - 1 MB oder 4 KB - 4 GB |
Segmentschutz | nein | ja | ja |
Segmentregister | segment base adr / 16 | selector | selector |
Die Segmente sind immer noch da, aber im 32-bit protected mode ist es m�glich, das segment limit, d.h. die Grenze f�r die Gr��e eines einzelnen Segments, die im real mode fix bei 64 KB liegt, auf 4 GB zu setzen. Das ist das Maximum an physischem Speicher, das von einer CPU mit 32-bit address bus �berhaupt addressiert werden kann. Dadurch "verschwinden" die Segmente, weil es nur ein (sehr gro�es) Segment mehr gibt (oder geben kann). Trotzdem bleiben andere Schutzmechanismen wirkungsvoll. Einzig und allein diese Tatsache machte 32-bit protected mode beliebt.
Im real mode gibt es wenig, was man �ber die Segmente wissen muss. Jedes ist 64 KB gro� und man kann damit machen, was man will: Daten darin speichern, den Stack hineinplatzieren oder code ausf�hren, der im jeweiligen Segment gespeichert ist. Die base address des Segments ist einfach das 16-fache des Werts in einem der Segmentregister. Im protected mode brauchen wir neben der base address eines Segments auch seine maximale Gr��e und einige flags, die seinen Verwendungszweck angeben. Diese Information kommt in einen 8 bytes gro�en record mit dem klingenden Namen descriptor:
Der Aufbau eines code/data segment descriptor:
Niedrigstes byte | byte 1 | byte 2 | byte 3 | byte 4 | byte 5 | byte 6 | H�chstes byte |
---|---|---|---|---|---|---|---|
limit 7:0 | limit 15:8 | base 7:0 | base 15:8 | base 23:16 | access | flags, limit 19:16 | base 31:24 |
Das ist ein 32-bit (80386) descriptor. Bei 16-bit (80286) descriptors sind die beiden h�chsten bytes (limit 19:16 und base 31:24) auf 0 gesetzt. Das access-byte gibt die Verwendung des Segments an (data segment, stack segment, code segment etc.).
Der Aufbau eines access byte:
H�chstes bit | present | present bit: Muss auf 1 gesetzt sein, um den Zugriff auf das Segment zu gestatten |
---|---|---|
bits 6, 5 | privilege | privilege bits: 0 ist das h�chste Niveau an privilege (Ring 0), 3 ist das niedrigste (Ring 3) |
bit 4 | 1 | f�r code und data/stack segments. Wenn nicht gesetzt, handelt es sich um ein system segment. |
bit 3 | executable | executable bit: Wenn dieses bit als Wert 1 hat, ist dieses Segment code segment, sonst handelt es sich um ein stack/data segment |
bit 2 | expansion direction/conforming | expansion direction (stack/data segment): Wenn dieses bit den Wert 1 hat, w�chst das
Segment (den Adressen nach) nach unten und offsets m�ssen gr��er als das limit sein.
conforming (code segment): steht in Verbindung mit privilege |
bit 1 | writable/readable | writable (stack/data segment): Wenn dieses bit den Wert 1 hat, kann in das Segment
geschrieben werden readable (code segment): Wenn dieses bit den Wert 1 hat, kann von diesem Segment gelesen werden (in code segments kann nicht geschrieben werden) |
Niedrigstes bit | accessed | accessed bit: Dieses bit wird gesetzt wann immer aus dem Segment gelesen oder in das Segment geschrieben wird. |
Der Wert des 4-bit flag in byte 6 eines descriptor ist nur f�r 32-bit Segmente nicht 0:
Highest bit | Bit 6 | Bit 5 | Bit 4 |
Granularity | Default Size | 0 | 0 |
Das granularity bit (G) entscheidet, ob das segment limit in Einheiten zu 4 KB Seiten (G=1) oder ob es in Einheiten zu einem byte (G=0) angegeben ist.
Im Falle der Verwendung eines Segments als stack segment wird das default size bit (D) auch als B (Big) bit bezeichnet und legt fest, ob 16- oder 32-bit Werte auf den Stack gelegt werden (mit den Anweisungen push und pop). Wird das Segment als code segment verwendet, gibt das D bit an, ob Anweisungen prinzipiell in 16-bit (D=0) oder 32-bit (D=1) Einheiten verarbeitet werden. Um etwas n�her darauf einzugehen: Ist das D bit gesetzt, dann ist das Segment USE32, benannt nach der Assemblerdirektive mit demselben Namen. Die folgende Sequenz von hexadezimalen bytes:
B8 90 90 90 90
wird von der CPU als eine 32-bit Anweisung betrachtet und w�rde in Assembler folgenderma�en lauten:
mov eax, 90909090h
Ist das D bit nicht gesetzt, ist das Segment USE16, also 16-bit code, und die gleiche Sequenz von bytes w�rde wie folgt interpretiert:
mov ax, 9090h
nop
nop
Zwei spezielle opcode bytes, n�mlich das operand size prefix und das address length prefix kehren die Wirkung des D bit f�r das Ziel beziehungsweise f�r die Quelle der unmittelbar folgenden Anweisung um.
Bit 4 des access byte ist gesetzt, wenn ein code oder data/stack segment vorliegt. Wenn das bit den Wert 0 hat, handelt es sich um ein system segment. Davon gibt es wiederum verschiedene Varianten:
task state segment (TSS) | Wird zur Vereinfachung des multitasking verwendet. Die CPU vom 80386 aufw�rts kennt vier Untertypen des TSS. |
---|---|
local descriptor table (LDT) | Hier k�nnen tasks anstatt im GDT ihre eigenen privaten descriptors speichern. |
gate | Kontrolliert �berg�nge der CPU von einem privilege-Niveau zu einem anderen. gate descriptors haben einen anderen Aufbau als die �brigen descriptors. |
Aufbau eines gate descriptor:
Niedrigstes byte | byte 1 | byte 2 | byte 3 | byte 4 | byte 5 | byte 6 | H�chstes byte |
---|---|---|---|---|---|---|---|
offset 7:0 | offset 15:8 | selector 7:0 | selector 15:8 | word count 4:0 | access | offset 23:16 | offset 31:24 |
Beachten Sie das selector-Feld. gates arbeiten �ber Umwege und ben�tigen einen eigenen code oder ein eigenes TSS, um zu funktionieren.
Der Aufbau des access byte eines system segment descriptor:
H�chstes bit | bits 6, 5 | bit 4 | bits 3, 2, 1, 0 |
---|---|---|---|
Present | Privilege | 0 | Type |
Die unterschiedlichen system segment-Typen:
0 | ung�ltig | 8 | ung�ltig |
---|---|---|---|
1 | verf�gbares '286 TSS | 9 | verf�gbares '386 TSS |
2 | LDT | 10 | undefiniert, reserviert |
3 | belegtes '286 TSS | 11 | belegtes '386 TSS |
4 | '286 call gate | 12 | '386 call gate |
5 | task gate | 13 | undefiniert, reserviert |
6 | '286 interrupt gate | 14 | '386 interrupt gate |
7 | '286 trap gate | 15 | '386 trap gate |
So. Wenn das Verwirrung stiften sollte: Es reicht vorerst vollauf, wenn Sie sich merken, dass die Haupttypen von system segments TSS (task state segment), LDT (local descriptor table) und gates sind.
Die descriptors sind in einer Tabelle, dem global descriptor table (GDT), dem interrupt descriptor table (IDT) oder in einem der local descriptor tables (LDT) aufgelistet. Die CPU verf�gt �ber 3 Register, n�mlich den GDTR, den IDTR (wenn interrupts benutzt werden) und den LDTR (wenn ein LDT benutzt wird) - das "R" am Ende jeder Bezeichnung steht f�r "Register". Diese Register m�ssen jeweils die Addresse des GDT, des IDT beziehungsweise des LDT beinhalten. Jede descriptor-Tabelle kann bis zu 8192 descriptors enthalten (eine Tabelle ist daher 64 KB gro0).
Im protected mode enthalten die Segmentregister selectors, die auf einen descriptor tables verweisen. Nur die h�chsten 13 bits des selector werden f�r diesen Verweis gebraucht, das n�chstniedrigere bit w�hlt zwischen dem GDT und dem LDT aus. Die beiden niedrigsten bits legen ein privilege-Niveau fest (0-3).
Den protected mode zu aktivieren ist eigentlich recht einfach. Man muss:
Dazu muss man am '386:
Vor dem Zur�ckschalten in den real mode m�ssen CS und SS selectors enthalten, die auf "real mode-geeignete" descriptors verweisen. real mode-geeignet bedeutet, dass sie ein limit von 64 KB haben, byte-granular sind, d.h. das flags nibble=0, das sie nach oben wachsen, beschreibbar (gilt nur f�r data/stack segments) und present sind (access byte=1xx1001xb).
Am '286 kann man nicht einfach das PE bit auf 0 setzen, um den protected mode zu verlassen. Die einzige M�glichkeit ist, die CPU neu zu starten. Das kann geschehen indem man dem keyboard controller die Tastenkombination zum Warmstart �bergibt oder indem man die CPU triple-faulted (siehe die Website von Robert Collins).
Tats�chlich muss das segment limit in DS, ES, FS und GS 0FFFFh oder gr��er sein. Wenn Sie dem Segment ein limit von 0FFFFFh verpassen und es als page-granular ausweisen, k�nnen sie auch im real mode auf bis zu 4 GB Speicher zugreifen. Das ist scherzhaft als unreal mode bezeichnet worden. Wie auch immer, limits, die nicht den Wert 0FFFFh haben (oder page-granularity) verursachen in CS oder SS im real mode gr��te Probleme.
Der GDT (und ebenso LDTs) sollten ihren Platz im RAM haben, weil der Prozessor das accessed bit [im access byte] der descriptors ver�ndert.
Trotzdem stellt eine meiner Quellen (danke, Vinay) fest, dass j�ngere CPUs nicht versuchen, das accessed bit zu setzen, wenn sein Wert schon 1 ist. Dazu lesen Sie bitte die Dokumentation zu der CPU, f�r die Sie schreiben.Wenn Sie einfach beginnen wollen, versuchen Sie es mit diesen Ratschl�gen:
void unhand(void)
{ static const char Msg[]="U n h a n d l e d I n t e r r u p t ";
disable();
movedata(SYS_DATA_SEL, (unsigned)Msg,
LINEAR_SEL, 0xB8000,
sizeof(Msg));
while(1); }