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-prosessori, jota käytettiin alkuperäisessä IBM PC:ssä, ei ollut kovin skaalattava. 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-prosessorin, jossa oli käytettävissä kaksi toimintatilaa: reaalitila, jota käytettäessä 286 toimii kuin ’nopeutettu 8088’, ja suojattu tila (jota nykyisin kutsutaan 16-bittiseksi suojatuksi tilaksi). Suojattua tilaa käytettäessä ohjelmat saavat käyttöönsä fyysistä muistia enemmän kuin yhden megatavun, ja lisäksi se suojaa muistin käyttöä väärin (esim. ohjelmien ei anneta suorittaa koodia datasegmentistä, tai kirjoittaa dataa koodisegmenttiin). Kehittyneempi versio, 32-bittinen suojattu tila, tuli käyttöön 386-prosessorin 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 (1M tavun raja) = 16 * segmenttirekisteri

24-bittinen (16M tavun raja), deskriptorilta

32-bittinen (4G tavun raja), deskriptorilta

Segmentin koko (raja)

16-bittinen, 64K tavua (fixed)

16-bittinen, 1-64K tavua

20-bittinen, 1-1M tavua tai 4K-4G vua

Segmentin suojaus

ei

kyllä

kyllä

Segmenttirekisteri

segmenttikantaosoite / 16

selektori

selektori



Oletin, että suojattu tila ei käytä segmentoitua muistia...

Segmenttejä käytetään toki myös suojatussa tilassa. 32-bittisessä suojatussa tilassa voidaan asettaa segmentin raja 4 gigatavuksi (4,0 Gt). Tämä on suurin määrä fyysistä muistia, joka on osoitettavissa 32-bittistä osoiteväylää käyttävällä prosessorilla. Tällöin segmentti "katoaa" (tosin muut suojausjärjestelmät jäävät vaikuttamaan). Yksinään jo tämä syy tekee 32-bittisestä suojatusta tilasta suositun.

Mikä on deskriptori?

Reaalitilan segmenteistä täytyy tietää jonkin verran. Jokainen niistä on 64 kilotavun kokoinen, ja niille voi tehdä käytännössä mitä vain: tallettaa dataa sinne, asettaa pino sinne 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 (rajan) ja muutaman lipun ilmaisemaan segmentin käyttötarkoitusta. Tämä informaatio on 8-tavuisessa datatietueessa, jota kutsumme deskriptoriksi (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 7:0

Raja 15:8

Kanta 7:0

Kanta 15:8

Kanta 23:16

Access

Liput, Raja 19:16

Kanta 31:24

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

Tämä on 32-bittinen (386) deskriptori. 16-bittisten (286) deskriptoreiden on asetettava kaksi tavua (Raja 19:16, Liput ja Kanta 31:24) nollaksi. Access-tavu ilmaisee segmentin käytön (datasegmentti, pinosegmentti, koodisegmentti, jne.):

Taulukko 3: Access-tavu koodi- ja datasegmenttien deskriptoreissa.

Ylin bitti

Bitit 6, 5

Bitti 4

Bitit 3

Bitti 2

Bitti 1

Alin bitti

Esitys

Etuoikeus

1

Suoritettava

Laajenemisen suunta / conforming

Kirjoitettava/ luettava

Käytössä (accessed)

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

Taulukko 4: Lippu-nippu.

Ylin tavu

Bitti 6

Bitti 5

Bitti 4

Granularity

Oletuskoko

0

0

Granularity-bitti kertoo, ilmaistaanko segmentin raja 4Kt:n kokoisina sivuina (kun G==1) vai yhden tavun kokoisina palasina (kun G==0).

Pinosegmenteillä Oletuskoko-bitti tunnetaan myös nimellä B-bitti (lyh. Big). Se ohjaa sitä, että käytetäänkö pinoille 16-bittisiä vai 32-bittisiä arvoja. Koodisegmenteillä D-bitti ilmaisee, toimivatko käskyt oletusarvoisesti 16-bitin vain 32-bitin erinä. Laajentavaa tietoa tästä asiasta: Kun D-bitti on päällä, segmentti on USE32, saman nimisen assembler-direktiivin perusteella nimettynä. Seuraavaa heksalukusarjaa:

B8 90 90 90 90

prosessori käsittelee 32-bittisenä käskynä eli se käännettäisiin assemblyksi seuraavasti:

mov eax, 90909090h

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

mov ax,9090h
nop
nop

Kaksi erityistä opkooditavua nimeltä Operand Size Prefix ja Address Length Prefix kääntävät D-bitin merkityksen kyseessä olevan käskyn kohteelle ja lähteelle. Nämä etuliitteet vaikuttavat vain käskyyn, joka sijatisee välittömästi niiden perässä.

Koodi- ja data/pino-segmenteissä Access-tavun neljäs bitti on aina 1. Jos tämän bitin arvo on nolla, kyseessä on järjestelmäsegmentti (system segment). Niitä on monta eri tyyppiä:

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

Selektori 7:0

Selektori 15:8

Sanojen määrä 4:0

Access

Siirros 23:16

Siirros 31:24

Huomaa Selektori-kentät. Portit toimivat epäsuorasti (through indirection), ja vaativat toimiakseen erillistä koodia tai TSS-deskriptoria.

Taulukko 6: Access-tavu järjestelmäregmentin deskriptorissa.

Ylin bitti

Bitit 6, 5

Bitti 4

Bitit 3, 2, 1, 0

Esitys

Etuoikeus

0

Tyyppi

Taulukko 7: Järjestelmäsegmenttien tyypit.

Tyyppi

Segmentin tarkoitus

 

Tyyppi

Segmentin tarkoitus

0

(kelvoton)

 

8

(kelvoton)

1

'286 TSS saatavissa

 

9

'386 TSS saatavissa

2

LDT

 

10

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

3

Aktiivinen '286 TSS

 

11

Aktiivinen '386 TSS

4

'286 kutsuntaportti (Call Gate)

 

12

'386 kutsuntaportti

5

Työtehtäväportti

 

13

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

6

'286 keskeytysportti

 

14

'386 keskeytysportti

7

'286 Trap-portti

 

15

'386 Trap-portti

Vau! Tässä vaiheessa muista vain, että TSS:t, LDT:t ja portit ovat järjestelmäsegmentin kolme päätyyppiä.

Missä deskriptorit sijaitsevat?

Deskriptorit on talletettuna muistissa olevassa taulukkossa: Global Descriptor -taulukossa (GDT), Interrupt Descriptor -taulukossa (IDT) tai yhdessä Local Descriptor -taulukoista. Prosessori 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ä taulukoista voi pitää sisällään 8192 deskriptoria.

Mikä on selektori?

Suojatussa tilassa segmenttirekisterit sisältävät selektorin, joka osoittaa yhteen deskriptoritaulukoista. Tätä indeksiä varten käytetään selektorista vain 13 ylintä bittiä. Seuraava alempi bitti valitsee jomman kumman GDT:stä ja LDT:stä. Selektorin 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 myös useissa muissa oppaissa. Suojatun tilan käynistämiseksi sinun on:

Miten pääsen takaisin reaalitilaan?

386-prosessoreilla:

Ennen palaamista reaalitilaan CS:n ja SS:n on sisällettävä selektorit, jotka osoittavat “reaalitilalle sopiviin” deskriptoreihin. Niillä on 64Kt:n raja, ne ovat tavu-granulaarisia (Liput-nippu=0), ylöspäin laajenevia, kirjoitettavia (data/pino-segmentti ainoastaan) ja nykyisiä (Access-tavu=1xx1001x).

286-prosessoreissa ei voi tuosta noin vain pyyhkiä PE-bitin arvoa poistuakseen suojatusta tilasta. Ainoa tapa on resetoida prosessorin tila. Tämä voidaan tehdä käskemällä näppäimistöohjainta lähettämään impulssin prosessorin reset-linjalle, tai ns. triple-häiriöimällä (triple-faulting) prosessoria (katso Robert Collins’in kotisivut: www.x86.org).

Mitä ongelmia olet kohdannut?

Oikeastaan DS-, ES-, FS- ja GS-rekistereille segmenttirajan täytyy olla 0xFFFF tai suurempi. Jos asetat segmenttirajaksi 0xFFFFF ja teet siitä sivu-granulaarisen, voit käyttää reaalitilasta käsin 4Gt muistia. Tätä on alettu kutsua unreaalitilaksi. Joka tapauksessa muut rajat kuin 0xFFFF (tai sivu-granularisuus) CS- tai SS-rekistereille voi aiheuttaa isoja ongelmia reaalitilassa.

GDT:n (niin kuin LDT-taulukoidenkin) pitäisi jäädä RAM:iin, koska prosessori muuttaa deskriptoreiden accessed-bittiä.

Kuitenkin, yksi minun lähteistäni (kiitos, Vinay) ilmoittaa, että myöhemmät prosessorit eivät yritä asettaa deskriptorin Accessed-bittiä jos tuo bitti on jo asetettu. Tarkista käyttämäsi prosessorin 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ä vuoron perään olevia välilyöntejä PC-videolaitteisto käsittelee attribuuttitavuina. Näin viesti näkyy huomiota herättävänä musta-vihreänä tekstinä. Pane keskeytysportti IDT-taulukolle sopivaan (kaikkiin?) deskriptoriin niin, että mukana on selekori koodisegmenttiisi trap-portin (engl. trap port) selektorikentässä, ja tämän rutiinin osoite sen siirros-kenttään.

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

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