uni
Il processore ad ogni istante si trova o a livello di privilegio di sistema, o a livello utente, questa informazione è contenuta nel registro CS (code selector).
Per quanto riguarda la memoria, la parte M1 è accedibile solo tramite livello di sistema, la parte M2 invece anche dal livello utente, l’indirizzo limite che decide dove inizia M2 è scritto dal bootstrap in un registro del processore, e può essere solo cambiato dal sistema.
Il livello di privilegio può essere solo:
- innalzato (o lasciato inalterato) passando attraverso un gate della IDT: istruzione
int $tipo
- abbassato (o lasciato inalterato) tramite l’istruzione IRETQ.
Quando si prova ad eseguire una istruzione senza il privilegio necessario, il processore solleva una interruzione di Protezione. L’utente non può quindi modificare il contenuto della IDT, poiché vorrebbe dire controllare la protezione, ciò viene realizzato caricato la IDT in M1, e rendendo privilegiata l’istruzione lidtr
.
Le routine eseguite tramite int
prendono il nome di primitive di sistema.
IDT
Ogni gate della IDT occupa 16 byte e contiene le seguenti informazioni:
- un bit P (presenza), che indica se il gate è rilevante
- puntatore alla routine a cui saltare (8 byte)
- un bit I/T che indica se il gate è di tipo Interrupt o Trap
- un campo L che indica il livello di privilegio a cui il processore si deve portare dopo aver passato il gate
- un campo DPL (descriptor privilege level) (o GL per gate level) che indica il livello di privilegio necessario per poter attraversare il gate
Svolgimento di Una interruzione
Quando il processore accetta un’interruzione esterna o genera un’eccezione e esegue un’interruzione software esegue il seguente:
- controlla il tipo dell’interruzione
- se è una eccezione il tipo è implicito
- se è interruzione esterna riceve il tipo dall’APIC
- se è interruzione software il tipo è il parametro di
int
usa il tipo come indice della IDT e accedere al corrispondente gate
- se:
- il bit P è zero, il processore genera un’eccezione per gate non presente (tipo 11)
- se è una interruzione software (o una
int3
), se CS è inferiore di DPL genera una eccezione di protezione (tipo 13) - se L è inferiore di CS genera un’interruzione di protezione
Se passa tutti i test salva in un registro SRSP di appoggio il contenuto corrente di %rsp
- se CS è minore stretto di L esegue un cambio di pila, caricando un nuovo valore di %rsp
- salva nella pila corrente (nuova se ha eseguito il cambio) 5 parole lunghe (8 byte), in questo ordine:
- una parola lunga rilevante per la segmentazione
- SRSP, che in caso di cambio di pila punta alla vecchia pila
- RFLAGS
- CS
- %rip (che si trova dunque in cima alla pila (LIFO))
- azzera TF (disabilita una eventuale modalità single step), e azzera IF (interrupt flag) solo se il gate è di tipo Interrupt
- salta all’indirizzo della routine puntata dal gate
Svolgimento di IRETQ
- se il livello di privilegio salvato in pila è superiore a CS genera una eccezione di protezione (
iretq
può solo abbassare il privilegio) - ripristina i valori di %rip, CS, RFLAGS e %rsp, prendendoli dalla pila
Registro TR e Tast State Segment
Strutture dati del sistema associate ad ogni processo:
Il processore cambia Pila quando deve innalzare il livello di privilegio, il puntatore alla nuova pila è prelevato dal Task State Segment (o TSS) corrente.
I TSS sono strutture dati in memoria M1 contenenti spazio per diverse informazioni, tra cui punt_nucleo
, il campo del TSS che contiene il puntatore alla pila di sistema.
In teoria ci dovrebbe essere un TSS per ogni processo, ma ad ogni istante solo uno è quello attivo, indicato dal registro %tr (task register), che può essere caricato attraverso l’istruzione ltr
.
Tutti i TSS sono descritti da una tabella GDT (global descriptor table), puntata dal registro %gtdr, che può essere caricato attraverso l’istruzione lgtdr
.
La GDT oltre a cose rilevanti per la segmentazione contiene una entrata per ogni TSS, contenente il suo indirizzo base (ovvero di partenza) e la sua grandezza in byte.
Il registro %tr contiene l’offset dell’entrata corrispondente al TSS attuale.
Attenzione, noi non useremo questa convenzione, invece avremo un unico TSS, caricando %tr una sola volta durante l’inizializzazione, del quale invece aggiorneremo il campo
punt_nucleo
ad ogni cambio di processo.
Diritto di I/O in Intel
Nell’architettura intel la possibilità o meno di eseguire operazioni come disabilitazione delle interruzioni o utilizzo di I/O è determinata dal campo IOPL (I/O privilege level), che si trova nel registro dei flag ed indica il livello di privilegio minimo necessario per accedere a queste operazioni.
Il campo IOPL può essere ovviamente cambiato solamente a livello di sistema, il tentativo di modifica da parte di un utente viene semplicemente ignorato e non genera eccezione (stessa cosa accade se si tenta di modificare il flag IF tramite popf
).