Suojattu tila

Tekijä: Chris Giese
Suomentanut Jouni Kähkönen 2. huhtikuuta 2005

Muokattu: 13. syyskuuta 2005

Katso myös:
1. Prashant TR:n laaja-alainen ja havainnollinen suojattua tilaa käsittelevä opas
2. Yariv Kaplanin neljän kappaleen opas suojatusta tilasta


Mikä on suojattu tila?

8088-suoritin, jota käytettiin alkuperäisessä IBM PC:ssä, ei ollut kovin skaalautuva. Siinä ei pystynyt käyttämään – ilman lisäkonsteja – megatavua enempää fyysistä muistia. Ratkaistakseen tämän ongelman yhteensopivuuden kärsimättä taaksepäin Intel suunnitteli 80286-suorittimen, jossa oli käytettävissä kaksi toimintatilaa: reaalitila, jota käytettäessä 286 toimi kuin "nopeutettu 8088", ja suojattu tila (jota nykyisin kutsutaan 16-bittiseksi suojatuksi tilaksi). Suojattua tilaa käytettäessä ohjelmilla on käytössä fyysistä muistia enemmän kuin yksi megatavu ja lisäksi suojattu tila suojaa muistin väärinkäytöltä; ohjelmien ei muun muassa anneta suorittaa koodia datasegmentistä tai kirjoittaa dataa koodisegmenttiin. Kehittyneempi, 32-bittinen suojattu tila, tuli käyttöön 386-suorittimen myötä.

Mitä eroa on reaalitilalla ja suojatulla tilalla?

Taulukko 1: Erot reaali- ja suojatun tilan välillä.

 

Reaalitila

Suojattu tila

32-bittinen suojattu tila

Segmentin kantaosoite

20-bittinen (1 Mt raja-arvo) = 16 * segmenttirekisteri

24-bittinen (16 Mt raja-arvo), deskriptorin kautta

32-bittinen (4 Gt raja-arvo), deskriptorin kautta

Segmentin koko (raja-arvo)

16-bittinen, 64 kt (kiinteä)

16-bittinen, 1 - 64 kt

20-bittinen, 1 kt - 1 Mt tai 4 kt - 4 Gt

Segmentin suojaus

ei

kyllä

kyllä

Segmenttirekisteri

segmentin kantaosoite / 16

valitsin

valitsin



Oletin, ettei suojattu tila käytä segmentoitua muistia...

Segmentit ovat edelleen olemassa 32-bittisessä suojatussa tilassa, mutta segmentin raja-arvon voi niissä asettaa 4 gigatavuun. Tämä on enimmäismäärä fyysistä muistia, jonka 32-bittistä osoitinväylää käyttävä suoritin pystyy enimmillään osoittamaan. Raja-arvomielessä segmentti ikään kuin "katoaa" tällöin (muut suojausjärjestelmät toki jäävät voimaan). Jo pelkästään tämä tekee 32-bittisestä suojatusta tilasta niin suositun.

Mikä on deskriptori?

Reaalitilan segmenteistä täytyy tietää jonkin verran. Ne ovat kukin 64 kilotavun kokoisia, ja niiden avulla pääsee tekemään käytännössä mitä vain: tallettaa siihen dataa, asettaa siihen pino tai suorittaa segmenttiin talletettua koodia. Reaalitilassa segmentin kantaosoite on yksinkertaisesti luku 16 kerrottuna halutun segmenttirekisterin arvolla.

Suojatussa tilassa tarvitsemme paitsi segmentin kantaosoitteen myös segmentin koon (raja-arvon) ja muutaman lipun ilmaisemaan segmentin käyttötarkoitusta. Nämä tiedot ovat 8-tavuisessa tietueessa, jota kutsutaan deskriptoriksi tai kuvaajaksi (eng. descriptor).

Taulukko 2: deskriptorin sisältö sekä koodi- että datasegmentille.

Alin tavu

Tavu 1

Tavu 2

Tavu 3

Tavu 4

Tavu 5

Tavu 6

Ylin tavu

Raja-arvo 7:0

Raja-arvo 15:8

Kanta 7:0

Kanta 15:8

Kanta 23:16

Käyttö

Liput, Raja-arvo 19:16

Kanta 31:24

HUOM: Löydät selkeämmät taulukot sivusta Johdanto suojattuun tilaan.

Tämä on 32-bittinen eli 386-deskriptori. 16-bittisten eli 286-deskriptoreiden tapauksessa kaksi ylintä tavua on asetettava nollaksi, nimittäin Raja-arvo 19:16, Liput ja Kanta 31:24. Käyttö-tavulla ilmaistaan mihin segmenttiä käytetään (datasegmentti, pinosegmentti, koodisegmentti jne.):

Taulukko 3: Käyttö-tavu koodi- ja datasegmenttien deskriptoreissa.

Ylin bitti

Bitit 6, 5

Bitti 4

Bitit 3

Bitti 2

Bitti 1

Alin bitti

Päällä

Etuoikeus

1

Ajettava

Laajenemisen suunta / muoto

Kirjoitettava/luettava

Käytössä

4-bittinen Lippu-arvo on ei-nolla vain 32-bittisillä segmenteillä:

Taulukko 4: Lippu-nippu.

Ylin tavu

Bitti 6

Bitti 5

Bitti 4

Granulariteetti

Oletuskoko

0

0

Granulariteetti-bitti kertoo ilmaistaanko segmentin raja-arvo 4 kilotavun kokoisina sivuina (G = 1) vai tavuissa (G = 0).

Pinosegmenteillä Oletuskoko-bittiä kutsutaan myös B-bitiksi (Big-bitti). Se ohjaa, käytetäänkö pinoille 16-bittisiä vai 32-bittisiä arvoja. Koodisegmenteillä D-bitti ilmaisee, toimivatko käskyt oletusarvoisesti 16-bitin vain 32-bitin erissä. Tarkemmin sanottuna, kun D-bitti on päällä, segmentti on ns. USE32, mikä on saanut nimensä vastaavasta Assembler-direktiivistä. Tällöin suoritin käsittelee heksalukusarjaa

B8 90 90 90 90

32-bittisenä käskynä, eli se kääntyisi Assemblyksi seuraavasti:

mov eax, 90909090h

16-bittisessä (USE16) koodisegmentissä sama lukusarja vastaisi tätä:

mov ax,9090h
nop
nop

Kaksi erityistä käskykoodin tavua nimeltään Operand Size Prefix ja Address Length Prefix kääntävät vastaavasti D-bitin merkityksen, käskyn kohteen ja lähteen suhteen. Nämä etuliitteet vaikuttavat vain käskyyn, joka sijaitsee välittömästi niiden jäljessä.

Koodi- ja data/pino-segmenteissä Käyttö-tavun neljäs bitti on aina 1. Jos tämän bitin arvo on nolla, kyseessä on järjestelmäsegmentti (system segment). Järjestelmäsegmenttejä on usean tyyppisiä:

Taulukko 5: Porttideskriptori.

Alin tavu

Tavu 1

Tavu 2

Tavu 3

Tavu 4

Tavu 5

Tavu 6

Ylin tavu

Siirros 7:0

Siirros 15:8

Valitsin 7:0

Valitsin 15:8

Sanojen määrä 4:0

Käyttö

Siirros 23:16

Siirros 31:24

Huomaa Valitsin-kenttä. Portit toimivat epäsuorasti, eli ne vaativat toimiakseen erillisen koodin tai TSS-deskriptorin.

Taulukko 6: Järjestelmäsegmentin deskriptorin Käyttö-tavu.

Ylin bitti

Bitit 6, 5

Bitti 4

Bitit 3, 2, 1, 0

Päällä

Etuoikeus

0

Tyyppi

Taulukko 7: Järjestelmäsegmenttien tyypit.

Tyyppi

Segmentin tarkoitus

 

Tyyppi

Segmentin tarkoitus

0

(kelvoton)

 

8

(kelvoton)

1

'286 TSS saatavana

 

9

'386 TSS saatavana

2

LDT

 

10

(määrittelemätön, varattu)

3

Aktiivinen '286 TSS

 

11

Aktiivinen '386 TSS

4

'286 kutsuportti (Call Gate)

 

12

'386 kutsuportti

5

Tehtäväportti (Task Port)

 

13

(määrittelemätön, varattu)

6

'286 keskeytysportti

 

14

'386 keskeytysportti

7

'286 Trap-portti

 

15

'386 Trap-portti

Huh! Nyt vain muistat, että TSS:t, LDT:t ja portit ovat järjestelmäsegmentin kolme päätyyppiä.

Missä deskriptorit sijaitsevat?

Deskriptorit ovat talletettuna muistissa olevassa taulussa: Global Descriptor -taulussa (GDT), Interrupt Descriptor -taulussa (IDT) tai yhdessä Local Descriptor -tauluista. Suoritin sisältää kolme rekisteriä: GDTR jonka pitää osoittaa GDT:hen, IDTR jonka pitää osoittaa IDT:hen (jos keskeytykset on käytössä), ja LDTR jonka pitää osoittaa LDT:hen (jos LDT on käytössä). Jokainen näistä tauluista voi säilöä 8192 deskriptoria.

Mikä on valitsin?

Suojatussa tilassa segmenttirekisterit sisältävät valitsimen (engl. selector), joka osoittaa yhteen deskriptoritauluun. Tätä indeksiä varten käytetään valitsimesta vain 13 ylintä bittiä. Seuraava alempi bitti valitsee joko GDT:n tai LDT:n. Valitsimen kaksi alinta bittiä asettavat ns. etuoikeusarvon.

Miten suojattu tila otetaan käyttöön?

Suojatun tilan käynnistäminen on periaatteessa hyvin yksinkertaista, ja siitä kerrotaan useissa muissakin oppaissa. Suojattu tila käynnistetään:

Miten pääsen takaisin reaalitilaan?

386-suorittimilla:

Ennen palaamista reaalitilaan CS:n ja SS:n tulee sisältää valitsimet, jotka osoittavat reaalitilalle sopiviin deskriptoreihin. Niillä on 64 kilotavun raja-arvo, niiden granulariteetti eli rakeisuus on 1 tavu (Liput-nippu=0), ne ovat ylöspäin laajenevia, kirjoitettavia (ainoastaan data/pino-segmenttien tapauksessa) ja käytössä (Käyttö-tavu=1xx1001x).

286-suorittimissa suojatusta tilasta ei voi noin vain poistua pyyhkimällä PE-bitin arvon. Ainoa tapa on nollata suoritin. Tämän voi tehdä käskemällä näppäimistöohjainta lähettämään impulssin suorittimen reset-linjalle tai ns. "triple-faulttaamalla" suoritinta (katso Robert Collins’in kotisivut: www.x86.org).

Mitä ongelmia olet kohdannut?

Itse asiassa DS-, ES-, FS- ja GS-rekistereillä segmenttiraja-arvon täytyy olla 0xFFFF tai suurempi. Jos asetat segmenttiraja-arvoksi 0xFFFFF ja teet siitä sivu-granulaarisen, voit käyttää reaalitilasta käsin muistia 4 Gt. Tätä on alettu kutsua epäreaalitilaksi. Joka tapauksessa muut raja-arvot kuin 0xFFFF (tai sivu-granularisuus) CS- tai SS-rekistereissö voivat aiheuttaa suuria ongelmia reaalitilassa.

GDT:n (niin kuin LDT-taulujenkin) pitäisi jäädä RAM:iin, koska suoritin muuttaa deskriptoreiden accessed- eli käytössä-bittiä.

Yksi lähteeni kertoo kuitenkin (kiitos, Vinay), että uudemmat suorittimet eivät yritä asettaa deskriptorin käytössä-bittiä jos bitti on jo asetettu. Tarkista asia käyttämäsi suorittimen dokumentaatiosta.

 

Jos haluat aloittaa yksinkertaisesta, ota huomioon seuraavat seikat:

 
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);
}

Viestissä vuorottelevia välimerkkejä PC-videolaitteisto käsittelee attribuuttitavuina. Näin viesti näkyy huomiota herättävänä musta-vihreänä tekstinä. Aseta sopivaan IDT-taulun deskriptoriin (jokaiseen?) keskeytysportti siten, että trap-portin valitsinkenttä viittaa käsittelijän koodisegmenttiin ja siirroskenttä tämän rutiinin muistiosoitteeseen.

Suomentanut: Jouni Kähkönen, 23. tammikuuta 2005

Tämä suomalainen versio on julkaistu Chris Giese'n myöntämällä luvalla.